diff --git a/stb_tilemap_editor.h b/stb_tilemap_editor.h index 4007d67..b4ce92b 100644 --- a/stb_tilemap_editor.h +++ b/stb_tilemap_editor.h @@ -1,4 +1,4 @@ -// stb_tilemap_editor.h - v0.10 - Sean Barrett - http://nothings.org/stb +// stb_tilemap_editor.h - v0.20 - Sean Barrett - http://nothings.org/stb // placed in the public domain - not copyrighted - first released 2014-09 // // Embeddable tilemap editor for C/C++ @@ -70,9 +70,13 @@ // stbte_tilemap keeps its own undo state. (The clipboard is global, so // either approach allows cut&pasting between levels.) // +// REVISION HISTORY +// +// 0.20 - 2014-09-27 - eraser tool, bugfixes, new colorscheme +// 0.10 - 2014-09-23 - initial release +// // TODO // -// Eraser!!! // Separate scroll state for each category // Implement paint bucket // Support STBTE_HITTEST_TILE above @@ -263,45 +267,138 @@ extern void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layern #error "Undo buffer size must be a power of 2" #endif -#define STBTE_COLOR_TOOLBAR_BACKGROUND 0x606060 // stbte__color[STBTE__ctoolbar][STBTE__base][STBTE__idle] -#define STBTE_COLOR_TILEMAP_BACKGROUND 0x000000 -#define STBTE_COLOR_TILEMAP_BORDER 0x203060 -#define STBTE_COLOR_TILEMAP_HIGHLIGHT 0xffffff -#define STBTE_COLOR_PANEL_BACKGROUND 0x403010 -#define STBTE_COLOR_PANEL_OUTLINE 0xc08040 -#define STBTE_COLOR_PANEL_TEXT 0xffffff -#define STBTE_COLOR_BUTTON_BACKGROUND 0x703870 -#define STBTE_COLOR_BUTTON_OUTLINE 0xc060c0 -#define STBTE_COLOR_BUTTON_TEXT 0xffffff -#define STBTE_COLOR_BUTTON_DOWN 0xe080e0 -#define STBTE_COLOR_BUTTON_OVER 0xffc0ff -#define STBTE_COLOR_BUTTON_TEXT_SELECTED 0x000000 -#define STBTE_COLOR_MICROBUTTON 0x40c040 -#define STBTE_COLOR_MICROBUTTON_DOWN 0xc0ffc0 -#define STBTE_COLOR_MICROBUTTON_FRAME 0x00ff00 -#define STBTE_COLOR_MICROBUTTON_OVER 0x80ff80 -#define STBTE_COLOR_TILEPALETTE_OUTLINE 0xffffff -#define STBTE_COLOR_TILEPALETTE_BACKGROUND 0x000000 -#define STBTE_COLOR_MINIBUTTON_ICON 0xffffff -#define STBTE_COLOR_SELECTION_OUTLINE1 0xdfdfdf -#define STBTE_COLOR_SELECTION_OUTLINE2 0x303030 -#define STBTE_COLOR_GRID 0x404040 +static int *stbte__colors; -#define STBTE_COLOR_LAYERCONTROL 0x6f6f6f -#define STBTE_COLOR_LAYERCONTROL_OVER 0xcfcfcf -#define STBTE_COLOR_LAYERCONTROL_DOWN 0xffffff -#define STBTE_COLOR_LAYERCONTROL_TOGGLED 0xbfbfbf -#define STBTE_COLOR_LAYERCONTROL_DISABLED 0x404040 -#define STBTE_COLOR_LAYERCONTROL_OUTLINE 0xffffff -#define STBTE_COLOR_LAYERCONTROL_OUTLINE_DISABLED 0x202020 -#define STBTE_COLOR_LAYERCONTROL_TEXT 0xffffff -#define STBTE_COLOR_LAYERCONTROL_TEXT_DOWN 0x5f5f5f -#define STBTE_COLOR_LAYERCONTROL_TEXT_TOGGLED 0x000000 -#define STBTE_COLOR_LAYERCONTROL_TEXT_DISABLED 0x606060 +enum +{ + STBTE__base, + STBTE__outline, + STBTE__text, -#define STBTE_COLOR_LAYERMASK_HIDE 0xffff55 -#define STBTE_COLOR_LAYERMASK_LOCK 0x5f55ff -#define STBTE_COLOR_LAYERMASK_SOLO 0xff5f55 + STBTE__num_color_aspects, +}; + +enum +{ + STBTE__idle, + STBTE__over, + STBTE__down, + STBTE__over_down, + STBTE__selected, + STBTE__selected_over, + STBTE__disabled, + STBTE__num_color_states, +}; + +enum +{ + STBTE__cexpander, + STBTE__ctoolbar, + STBTE__ctoolbar_button, + STBTE__cpanel, + STBTE__cpanel_sider, + STBTE__cpanel_sizer, + STBTE__cscrollbar, + STBTE__cmapsize, + STBTE__clayer_button, + STBTE__clayer_hide, + STBTE__clayer_lock, + STBTE__clayer_solo, + STBTE__ccategory_button, + + STBTE__num_color_modes, +}; + +#ifdef STBTE__COLORPICKER +static char *stbte__color_names[] = +{ + "expander", "toolbar", "tool button", "panel", + "panel c1", "panel c2", "scollbar", "map button", + "layer", "hide", "lock", "solo", + "category", +}; +#endif // STBTE__COLORPICKER + + // idle, over, down, over&down, selected, sel&over, disabled +static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] = +{ + { + { 0x000000, 0x84987c, 0xdcdca8, 0xdcdca8, 0x40c040, 0x60d060, 0x505050, }, + { 0xa4b090, 0xe0ec80, 0xffffc0, 0xffffc0, 0x80ff80, 0x80ff80, 0x606060, }, + { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0x808890, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, }, + { 0x605860, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, }, + { 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, }, + }, { + { 0x3c5068, 0x7088a8, 0x647488, 0x94b4dc, 0x8890c4, 0x9caccc, 0x404040, }, + { 0x889cb8, 0x889cb8, 0x889cb8, 0x889cb8, 0x84c4e8, 0xacc8ff, 0x0c0c08, }, + { 0xbcc4cc, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x707074, }, + }, { + { 0x403848, 0x403010, 0x403010, 0x403010, 0x403010, 0x403010, 0x303024, }, + { 0x68546c, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0x605030, }, + { 0xf4e4ff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0xb4b04c, 0xacac60, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050, }, + { 0xa0a04c, 0xd0d04c, 0xffff80, 0xffff80, 0x80ff80, 0x80ff80, 0x606060, }, + { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0x40c440, 0x60d060, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050, }, + { 0x40c040, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x606060, }, + { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0x9090ac, 0xa0a0b8, 0xbcb8cc, 0xbcb8cc, 0x909040, 0x909040, 0x909040, }, + { 0xa0a0b8, 0xb0b4d0, 0xa0a0b8, 0xa0a0b8, 0xa0a050, 0xa0a050, 0xa0a050, }, + { 0x808088, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, }, + }, { + { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828, }, + { 0x646064, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838, }, + { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828, }, + { 0xb09cb4, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838, }, + { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0x646494, 0x888cb8, 0xb0b0b0, 0xb0b0cc, 0x9c9cf4, 0x8888b0, 0x50506c, }, + { 0x9090a4, 0xb0b4d4, 0xb0b0dc, 0xb0b0cc, 0xd0d0fc, 0xd0d4f0, 0x606060, }, + { 0xb4b4d4, 0xe4e4ff, 0xffffff, 0xffffff, 0xe0e4ff, 0xececff, 0x909090, }, + }, { + { 0x646444, 0x888c64, 0xb0b0b0, 0xb0b088, 0xaca858, 0x88886c, 0x505050, }, + { 0x88886c, 0xb0b490, 0xb0b0b0, 0xb0b088, 0xd8d898, 0xd0d4b0, 0x606060, }, + { 0xb4b49c, 0xffffd8, 0xffffff, 0xffffd4, 0xffffdc, 0xffffcc, 0x909090, }, + }, { + { 0x906464, 0xb48c8c, 0xd4b0b0, 0xdcb0b0, 0xff9c9c, 0xc88888, 0x505050, }, + { 0xb47c80, 0xd4b4b8, 0xc4a8a8, 0xdcb0b0, 0xffc0c0, 0xfce8ec, 0x606060, }, + { 0xe0b4b4, 0xffdcd8, 0xffd8d4, 0xffe0e4, 0xffece8, 0xffffff, 0x909090, }, + }, { + { 0x403848, 0x403848, 0x403848, 0x886894, 0x7c80c8, 0x7c80c8, 0x302828, }, + { 0x403848, 0x403848, 0x403848, 0x403848, 0x7c80c8, 0x7c80c8, 0x403838, }, + { 0xc8c4c8, 0xffffff, 0xffffff, 0xffffff, 0xe8e8ec, 0xffffff, 0x909090, }, + }, +}; + +#define STBTE_COLOR_TILEMAP_BACKGROUND 0x000000 +#define STBTE_COLOR_TILEMAP_BORDER 0x203060 +#define STBTE_COLOR_TILEMAP_HIGHLIGHT 0xffffff +#define STBTE_COLOR_GRID 0x404040 +#define STBTE_COLOR_SELECTION_OUTLINE1 0xdfdfdf +#define STBTE_COLOR_SELECTION_OUTLINE2 0x303030 +#define STBTE_COLOR_TILEPALETTE_OUTLINE 0xffffff +#define STBTE_COLOR_TILEPALETTE_BACKGROUND 0x000000 + +// disabled, selected, down, over +static unsigned char stbte__state_to_index[2][2][2][2] = +{ + { + { { STBTE__idle , STBTE__over }, { STBTE__down , STBTE__over_down }, }, + { { STBTE__selected, STBTE__selected_over }, { STBTE__down , STBTE__over_down }, }, + },{ + { { STBTE__disabled, STBTE__disabled }, { STBTE__disabled, STBTE__disabled }, }, + { { STBTE__selected, STBTE__selected_over }, { STBTE__disabled, STBTE__disabled }, }, + } +}; +#define STBTE__INDEX_FOR_STATE(disable,select,down,over) stbte__state_to_index[disable][select][down][over] +#define STBTE__INDEX_FOR_ID(id,disable,select) STBTE__INDEX_FOR_STATE(disable,select,STBTE__IS_ACTIVE(id),STBTE__IS_HOT(id)) #define STBTE__FONT_HEIGHT 9 static short stbte__font_offset[95+16]; @@ -358,6 +455,7 @@ typedef short stbte__tiledata; enum { STBTE__panel_toolbar, + STBTE__panel_colorpick, STBTE__panel_info, STBTE__panel_layers, STBTE__panel_categories, @@ -515,7 +613,8 @@ static void stbte__init_gui(void) stbte__ui.panel[i].delta_height = 0; stbte__ui.panel[i].side = STBTE__side_left; } - stbte__ui.panel[STBTE__panel_toolbar].side = STBTE__side_top; + stbte__ui.panel[STBTE__panel_toolbar ].side = STBTE__side_top; + stbte__ui.panel[STBTE__panel_colorpick].side = STBTE__side_right; if (stbte__ui.left_width == 0) stbte__ui.left_width = 80; @@ -1070,7 +1169,33 @@ static int stbte__button_core(int id) return 0; } -static int stbte__button(char *label, int x, int y, int textoff, int width, int id, int toggled) +static void stbte__draw_box(int x0, int y0, int x1, int y1, int colormode, int colorindex) +{ + stbte__draw_rect (x0,y0,x1,y1, stbte__color_table[colormode][STBTE__base ][colorindex]); + stbte__draw_frame(x0,y0,x1,y1, stbte__color_table[colormode][STBTE__outline][colorindex]); +} + +static void stbte__draw_textbox(int x0, int y0, int x1, int y1, char *text, int xoff, int yoff, int colormode, int colorindex) +{ + stbte__draw_box(x0,y0,x1,y1,colormode,colorindex); + stbte__draw_text(x0+xoff,y0+yoff, text, x1-x0-xoff-1, stbte__color_table[colormode][STBTE__text][colorindex]); +} + +static int stbte__button(int colormode, char *label, int x, int y, int textoff, int width, int id, int toggled, int disabled) +{ + int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT; + int s = STBTE__BUTTON_INTERNAL_SPACING; + + int over = !disabled && stbte__hittest(x0,y0,x1,y1,id); + + if (stbte__ui.event == STBTE__paint) + stbte__draw_textbox(x0,y0,x1,y1, label,s+textoff,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled)); + if (disabled) + return 0; + return (stbte__button_core(id) == 1); +} + +static int stbte__button_icon(int colormode, char ch, int x, int y, int width, int id, int toggled) { int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT; int s = STBTE__BUTTON_INTERNAL_SPACING; @@ -1078,104 +1203,55 @@ static int stbte__button(char *label, int x, int y, int textoff, int width, int int over = stbte__hittest(x0,y0,x1,y1,id); if (stbte__ui.event == STBTE__paint) { - stbte__draw_rect (x0, y0, x1, y1, STBTE__IS_ACTIVE(id) || toggled ? STBTE_COLOR_BUTTON_DOWN : STBTE_COLOR_BUTTON_BACKGROUND); - stbte__draw_frame(x0, y0, x1, y1, STBTE__IS_HOT(id) || toggled ? STBTE_COLOR_BUTTON_OVER : STBTE_COLOR_BUTTON_OUTLINE); - stbte__draw_text (x0+s+textoff, y0+s, label ,width-s*2, toggled ? STBTE_COLOR_BUTTON_TEXT : STBTE_COLOR_BUTTON_TEXT_SELECTED); + char label[2] = { ch, 0 }; + int pad = (9 - stbte__get_char_width(ch))/2; + stbte__draw_textbox(x0,y0,x1,y1, label,s+pad,s, colormode, STBTE__INDEX_FOR_ID(id,0,toggled)); } return (stbte__button_core(id) == 1); } -static int stbte__button_icon(char ch, int x, int y, int width, int id, int toggled) -{ - int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT; - int s = STBTE__BUTTON_INTERNAL_SPACING, pad; - char label[2] = { ch, 0 }; - - int over = stbte__hittest(x0,y0,x1,y1,id); - - if (stbte__ui.event == STBTE__paint) { - stbte__draw_rect (x0, y0, x1, y1, STBTE__IS_ACTIVE(id) || toggled ? STBTE_COLOR_BUTTON_DOWN : STBTE_COLOR_BUTTON_BACKGROUND); - stbte__draw_frame(x0, y0, x1, y1, STBTE__IS_HOT(id) || toggled ? STBTE_COLOR_BUTTON_OVER : STBTE_COLOR_BUTTON_OUTLINE); - pad = (9 - stbte__get_char_width(ch))/2; - stbte__draw_text (x0+s+pad, y0+s, label ,9, toggled ? STBTE_COLOR_BUTTON_TEXT : STBTE_COLOR_BUTTON_TEXT_SELECTED); - } - return (stbte__button_core(id) == 1); -} - -static int stbte__minibutton(int x, int y, int ch, int id) +static int stbte__minibutton(int colormode, int x, int y, int ch, int id) { int x0 = x, y0 = y, x1 = x+8, y1 = y+7; int over = stbte__hittest(x0,y0,x1,y1,id); if (stbte__ui.event == STBTE__paint) { char str[2] = { ch,0 }; - stbte__draw_rect (x0,y0,x1,y1, STBTE__IS_ACTIVE(id) ? STBTE_COLOR_MICROBUTTON_DOWN : STBTE_COLOR_MICROBUTTON); - stbte__draw_frame(x0,y0,x1,y1, STBTE__IS_HOT(id) ? STBTE_COLOR_MICROBUTTON_OVER : STBTE_COLOR_MICROBUTTON_FRAME); - stbte__draw_text (x0+1,y0,str,99, STBTE_COLOR_MINIBUTTON_ICON); + stbte__draw_textbox(x0,y0,x1,y1, str,1,0,colormode, STBTE__INDEX_FOR_ID(id,0,0)); } return stbte__button_core(id); } -static int stbte__layerbutton(int x, int y, int ch, int id, int toggled, int disabled, int color) +static int stbte__layerbutton(int x, int y, int ch, int id, int toggled, int disabled, int colormode) { int x0 = x, y0 = y, x1 = x+10, y1 = y+11; - int over = stbte__hittest(x0,y0,x1,y1,id); + int over = !disabled && stbte__hittest(x0,y0,x1,y1,id); if (stbte__ui.event == STBTE__paint) { - int rc = STBTE_COLOR_LAYERCONTROL; - int rf = STBTE_COLOR_LAYERCONTROL_OUTLINE; - int rt = STBTE_COLOR_LAYERCONTROL_TEXT; - if (toggled) { - rc = STBTE_COLOR_LAYERCONTROL_TOGGLED; - rt = STBTE_COLOR_LAYERCONTROL_TEXT_TOGGLED; - } - if (STBTE__IS_HOT(id)) { - rc = STBTE_COLOR_LAYERCONTROL_OVER; - } - if (STBTE__IS_ACTIVE(id)) { - rc = STBTE_COLOR_LAYERCONTROL_DOWN; - rt = STBTE_COLOR_LAYERCONTROL_TEXT_DOWN; - } - rc &= color; - rf &= color; - rt &= color; - if (disabled) { - rc = STBTE_COLOR_LAYERCONTROL_DISABLED; - rf = STBTE_COLOR_LAYERCONTROL_OUTLINE_DISABLED; - rt = STBTE_COLOR_LAYERCONTROL_TEXT_DISABLED; - } - - stbte__draw_rect (x0,y0,x1,y1, rc); - stbte__draw_frame(x0,y0,x1,y1, rf); - { - char str[2] = { ch,0 }; - int off = (9-stbte__get_char_width(ch))/2; - stbte__draw_text (x0+1+off,y0+2,str,99, rt); - } + char str[2] = { ch,0 }; + int off = (9-stbte__get_char_width(ch))/2; + stbte__draw_textbox(x0,y0,x1,y1, str, off+1,2, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled)); } if (disabled) return 0; return stbte__button_core(id); } - -static int stbte__microbutton(int x, int y, int size, int id, int c1, int c2, int toggled) +static int stbte__microbutton(int x, int y, int size, int id, int colormode) { int x0 = x, y0 = y, x1 = x+size, y1 = y+size; int over = stbte__hittest(x0,y0,x1,y1,id); if (stbte__ui.event == STBTE__paint) { - stbte__draw_rect (x0,y0,x1,y1, STBTE__IS_ACTIVE(id) || toggled ? c2 : c1 ); - stbte__draw_frame(x0,y0,x1,y1, STBTE__IS_HOT(id) ? STBTE_COLOR_MICROBUTTON_OVER : STBTE_COLOR_MICROBUTTON_FRAME); + stbte__draw_box(x0,y0,x1,y1, colormode, STBTE__INDEX_FOR_ID(id,0,0)); } return stbte__button_core(id); } -static int stbte__microbutton_dragger(int x, int y, int size, int id, int c1, int c2, int toggled, int *pos) +static int stbte__microbutton_dragger(int x, int y, int size, int id, int *pos) { int x0 = x, y0 = y, x1 = x+size, y1 = y+size; int over = stbte__hittest(x0,y0,x1,y1,id); switch (stbte__ui.event) { case STBTE__paint: - stbte__draw_rect (x0,y0,x1,y1, STBTE__IS_ACTIVE(id) || toggled ? c2 : c1 ); - stbte__draw_frame(x0,y0,x1,y1, STBTE__IS_HOT(id) ? STBTE_COLOR_MICROBUTTON_OVER : STBTE_COLOR_MICROBUTTON_FRAME); + stbte__draw_box(x0,y0,x1,y1, STBTE__cexpander, STBTE__INDEX_FOR_ID(id,0,0)); break; case STBTE__leftdown: if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { @@ -1205,17 +1281,12 @@ static int stbte__category_button(char *label, int x, int y, int width, int id, int over = stbte__hittest(x0,y0,x1,y1,id); - if (stbte__ui.event == STBTE__paint) { - stbte__draw_rect (x0, y0, x1, y1, toggled ? STBTE_COLOR_BUTTON_DOWN : STBTE_COLOR_BUTTON_BACKGROUND); - stbte__draw_text (x0+s, y0+s, label ,width-s*2, STBTE__IS_HOT(id) ? STBTE_COLOR_BUTTON_TEXT : STBTE_COLOR_BUTTON_TEXT_SELECTED); - } + if (stbte__ui.event == STBTE__paint) + stbte__draw_textbox(x0,y0,x1,y1, label, s,s, STBTE__ccategory_button, STBTE__INDEX_FOR_ID(id,0,toggled)); return (stbte__button_core(id) == 1); } -#define STBTE_COLOR_SCROLLBAR_TRACK 0x808030 -#define STBTE_COLOR_SCROLLBAR_THUMB 0x909040 - static void stbte__scrollbar(int x, int y0, int y1, int *val, int v0, int v1, int num_vis, int id) { int over; @@ -1230,8 +1301,8 @@ static void stbte__scrollbar(int x, int y0, int y1, int *val, int v0, int v1, in over = stbte__hittest(x-1,y0,x+2,y1,id); switch (stbte__ui.event) { case STBTE__paint: - stbte__draw_rect(x,y0,x+1,y1, STBTE_COLOR_SCROLLBAR_TRACK); - stbte__draw_rect(x-1,thumbpos-3,x+2,thumbpos+4, STBTE_COLOR_SCROLLBAR_THUMB); + stbte__draw_rect(x,y0,x+1,y1, stbte__color_table[STBTE__cscrollbar][STBTE__text][STBTE__idle]); + stbte__draw_box(x-1,thumbpos-3,x+2,thumbpos+4, STBTE__cscrollbar, STBTE__INDEX_FOR_ID(id,0,0)); break; case STBTE__leftdown: if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { @@ -1289,9 +1360,9 @@ static void stbte__compute_panel_locations(stbte_tilemap *tm) int i, limit, w, k; int window_width = stbte__ui.x1 - stbte__ui.x0; int window_height = stbte__ui.y1 - stbte__ui.y0; - int min_width[STBTE__num_panel]={0,0,0,0,0}; - int height[STBTE__num_panel]={0,0,0,0,0}; - int panel_active[STBTE__num_panel]={1,1,1,1,1}; + int min_width[STBTE__num_panel]={0,0,0,0,0,0}; + int height[STBTE__num_panel]={0,0,0,0,0,0}; + int panel_active[STBTE__num_panel]={1,0,1,1,1,1}; int vpos[4] = { 0,0,0,0 }; stbte__panel *p = stbte__ui.panel; stbte__panel *pt = &p[STBTE__panel_toolbar]; @@ -1308,9 +1379,13 @@ static void stbte__compute_panel_locations(stbte_tilemap *tm) // determine which panels are active panel_active[STBTE__panel_categories] = tm->num_categories != 0; panel_active[STBTE__panel_layers ] = tm->num_layers > 1; +#ifdef STBTE__COLORPICKER + panel_active[STBTE__panel_colorpick ] = 1; +#endif // compute minimum widths for each panel (assuming they're on sides not top) min_width[STBTE__panel_info ] = 8 + 11 + 7*tm->digits+17+7; // estimate min width of "w:0000" + min_width[STBTE__panel_colorpick ] = 120; min_width[STBTE__panel_tiles ] = 4 + tm->palette_spacing_x + 5; // 5 for scrollbar min_width[STBTE__panel_categories] = 4 + 42 + 5; // 42 is enough to show ~7 chars; 5 for scrollbar min_width[STBTE__panel_layers ] = 4 + 54 + 30*tm->has_layer_names; // 2 digits plus 3 buttons plus scrollbar @@ -1352,6 +1427,9 @@ static void stbte__compute_panel_locations(stbte_tilemap *tm) stbte__region[i].x = (i == STBTE__side_left) ? stbte__ui.x0 - anim : stbte__ui.x1 - stbte__region[i].width + anim; } + // color picker + height[STBTE__panel_colorpick] = 300; + // info panel w = stbte__region[p[STBTE__panel_info].side].width; p[STBTE__panel_info].mode = (w >= 8 + (11+7*tm->digits+17)*2 + 4); @@ -1418,6 +1496,7 @@ enum STBTE__panel_mover, // p1 is panel ID, p2 is destination side STBTE__panel_sizer, // param panel ID STBTE__scrollbar_id, + STBTE__colorpick_id, }; // id is: [ 24-bit data : 7-bit identifer ] @@ -1754,11 +1833,10 @@ static void stbte__paste_stack(stbte_tilemap *tm, short result[], short dest[], for (i=0; i < tm->num_layers; ++i) { result[i] = dest[i]; - if (src[i] != STBTE__NO_TILE) { + if (src[i] != STBTE__NO_TILE) if (!tm->layerinfo[i].hidden && tm->layerinfo[i].locked != STBTE__locked) if (tm->layerinfo[i].locked == STBTE__unlocked || (!dragging && dest[i] == STBTE__BG(tm,i))) result[i] = src[i]; - } } } @@ -1770,14 +1848,12 @@ static void stbte__clear_stack(stbte_tilemap *tm, short result[]) i = tm->cur_layer; if (tm->solo_layer >= 0) i = tm->solo_layer; - if (i >= 0) { - result[i] = (i == 0 ? tm->background_tile : STBTE__NO_TILE); - } else { - for (i=0; i < tm->num_layers; ++i) { + if (i >= 0) + result[i] = STBTE__BG(tm,i); + else + for (i=0; i < tm->num_layers; ++i) if (!tm->layerinfo[i].hidden && tm->layerinfo[i].locked == STBTE__unlocked) - result[i] = (i == 0 ? tm->background_tile : STBTE__NO_TILE); - } - } + result[i] = STBTE__BG(tm,i); } // check if some map square is active @@ -2275,7 +2351,7 @@ static void stbte__toolbar(stbte_tilemap *tm, int x0, int y0, int w, int h) highlight=1; if (i == STBTE__tool_fill) continue; - if (stbte__button_icon(toolchar[i], x, y, 13, STBTE__ID(STBTE__toolbarA, i), highlight)) { + if (stbte__button_icon(STBTE__ctoolbar_button, toolchar[i], x, y, 13, STBTE__ID(STBTE__toolbarA, i), highlight)) { switch (i) { case STBTE__tool_eyedrop: stbte__ui.eyedrop_last_layer = tm->num_layers; // flush eyedropper state @@ -2299,17 +2375,17 @@ static void stbte__toolbar(stbte_tilemap *tm, int x0, int y0, int w, int h) } x += 8; - if (stbte__button("cut" , x, y,10, 40, STBTE__ID(STBTE__toolbarB,0), 0)) { + if (stbte__button(STBTE__ctoolbar_button, "cut" , x, y,10, 40, STBTE__ID(STBTE__toolbarB,0), 0, !stbte__ui.has_selection)) { if (stbte__ui.has_selection) stbte__copy_cut(tm, 1); } x += 42; - if (stbte__button("copy" , x, y, 5, 40, STBTE__ID(STBTE__toolbarB,1), 0)) { + if (stbte__button(STBTE__ctoolbar_button, "copy" , x, y, 5, 40, STBTE__ID(STBTE__toolbarB,1), 0, !stbte__ui.has_selection)) { if (stbte__ui.has_selection) stbte__copy_cut(tm, 0); } x += 42; - if (stbte__button("paste", x, y, 0, 40, STBTE__ID(STBTE__toolbarB,2), stbte__ui.pasting)) { + if (stbte__button(STBTE__ctoolbar_button, "paste", x, y, 0, 40, STBTE__ID(STBTE__toolbarB,2), stbte__ui.pasting, !stbte__ui.has_copy)) { if (stbte__ui.has_copy) { stbte__ui.pasting = 1; stbte__activate(STBTE__ID(STBTE__toolbarB,3)); @@ -2317,20 +2393,22 @@ static void stbte__toolbar(stbte_tilemap *tm, int x0, int y0, int w, int h) } } +#define STBTE__TEXTCOLOR(n) stbte__color_table[n][STBTE__text][STBTE__idle] + static int stbte__info_value(char *label, int x, int y, int val, int digits, int id) { if (stbte__ui.event == STBTE__paint) { int off = 9-stbte__get_char_width(label[0]); char text[16]; sprintf(text, label, digits, val); - stbte__draw_text_core(x+off,y, text, 999, STBTE_COLOR_PANEL_TEXT,1); + stbte__draw_text_core(x+off,y, text, 999, STBTE__TEXTCOLOR(STBTE__cpanel),1); } if (id) { x += 9+7*digits+4; - if (stbte__minibutton(x,y, '+', id + (0<<19))) + if (stbte__minibutton(STBTE__cmapsize, x,y, '+', STBTE__ID2(id,1,0))) val += (stbte__ui.shift ? 10 : 1); x += 9; - if (stbte__minibutton(x,y, '-', id + (1<<19))) + if (stbte__minibutton(STBTE__cmapsize, x,y, '-', STBTE__ID2(id,2,0))) val -= (stbte__ui.shift ? 10 : 1); if (val < 1) val = 1; else if (val > 4096) val = 4096; } @@ -2363,7 +2441,7 @@ static void stbte__info(stbte_tilemap *tm, int x0, int y0, int w, int h) stbte__info_value(in_region ? "y:%*d" : "y:",x,y, (stbte__ui.hot_id>> 7)&4095, tm->digits, 0); y += 15; x = x0+2; - stbte__draw_text(x,y,"brush:",40,STBTE_COLOR_PANEL_TEXT); + stbte__draw_text(x,y,"brush:",40,STBTE__TEXTCOLOR(STBTE__cpanel)); if (tm->cur_tile >= 0) STBTE_DRAW_TILE(x+43,y-3,tm->tiles[tm->cur_tile].id,1); } @@ -2379,7 +2457,7 @@ static void stbte__layers(stbte_tilemap *tm, int x0, int y0, int w, int h) y0 += 5; if (!tm->has_layer_names) { if (stbte__ui.event == STBTE__paint) { - stbte__draw_text(x0,y0, "Layers", w-4, STBTE_COLOR_PANEL_TEXT); + stbte__draw_text(x0,y0, "Layers", w-4, STBTE__TEXTCOLOR(STBTE__cpanel)); } y0 += 11; } @@ -2393,13 +2471,13 @@ static void stbte__layers(stbte_tilemap *tm, int x0, int y0, int w, int h) if (i-tm->layer_scroll >= 0 && i-tm->layer_scroll < num_rows) { if (str == NULL) sprintf(str=text, "%2d", i+1); - if (stbte__button(str, x0,y,(i+1<10)*2,xoff-2, STBTE__ID(STBTE__layer,i), tm->cur_layer==i)) + if (stbte__button(STBTE__clayer_button, str, x0,y,(i+1<10)*2,xoff-2, STBTE__ID(STBTE__layer,i), tm->cur_layer==i,0)) tm->cur_layer = (tm->cur_layer == i ? -1 : i); - if (stbte__layerbutton(x0+xoff + 0,y+1,'H',STBTE__ID(STBTE__hide,i), tm->layerinfo[i].hidden,disabled,STBTE_COLOR_LAYERMASK_HIDE)) + if (stbte__layerbutton(x0+xoff + 0,y+1,'H',STBTE__ID(STBTE__hide,i), tm->layerinfo[i].hidden,disabled,STBTE__clayer_hide)) tm->layerinfo[i].hidden = !tm->layerinfo[i].hidden; - if (stbte__layerbutton(x0+xoff + 12,y+1,lockedchar[locked],STBTE__ID(STBTE__lock,i), locked!=0,disabled,STBTE_COLOR_LAYERMASK_LOCK)) + if (stbte__layerbutton(x0+xoff + 12,y+1,lockedchar[locked],STBTE__ID(STBTE__lock,i), locked!=0,disabled,STBTE__clayer_lock)) tm->layerinfo[i].locked = (locked+1)%3; - if (stbte__layerbutton(x0+xoff + 24,y+1,'S',STBTE__ID(STBTE__solo,i), tm->solo_layer==i,0,STBTE_COLOR_LAYERMASK_SOLO)) + if (stbte__layerbutton(x0+xoff + 24,y+1,'S',STBTE__ID(STBTE__solo,i), tm->solo_layer==i,0,STBTE__clayer_solo)) tm->solo_layer = (tm->solo_layer == i ? -1 : i); y += 15; } @@ -2496,6 +2574,140 @@ static void stbte__palette_of_tiles(stbte_tilemap *tm, int x0, int y0, int w, in stbte__scrollbar(x1-4, y0+6, y1-2, &tm->palette_scroll, 0, num_total_rows, num_vis_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__palette)); } +static int stbte__cp_mode, stbte__cp_aspect, stbte__cp_state, stbte__cp_index, stbte__save, stbte__cp_altered, stbte__color_copy; + +#ifdef STBTE__COLORPICKER +static void stbte__dump_colorstate(void) +{ + int i,j,k; + printf("static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] =\n"); + printf("{\n"); + printf(" {\n"); + for (k=0; k < STBTE__num_color_modes; ++k) { + for (j=0; j < STBTE__num_color_aspects; ++j) { + printf(" { "); + for (i=0; i < STBTE__num_color_states; ++i) { + printf("0x%06x, ", stbte__color_table[k][j][i]); + } + printf("},\n"); + } + if (k+1 < STBTE__num_color_modes) + printf(" }, {\n"); + else + printf(" },\n"); + } + printf("};\n"); +} + +static void stbte__slider(int x0, int w, int y, int *value, int id) +{ + int x1 = x0+w; + int pos = *value * w / 256; + int over = stbte__hittest(x0,y-2,x1,y+3,id); + switch (stbte__ui.event) { + case STBTE__paint: + stbte__draw_rect(x0,y,x1,y+1, 0x808080); + stbte__draw_rect(x0+pos-1,y-1,x0+pos+2,y+2, 0xffffff); + break; + case STBTE__leftdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) + stbte__activate(id); + // fall through + case STBTE__mousemove: + if (STBTE__IS_ACTIVE(id)) { + int v = (stbte__ui.mx-x0)*256/w; + if (v < 0) v = 0; else if (v > 255) v = 255; + *value = v; + } + break; + case STBTE__leftup: + if (STBTE__IS_ACTIVE(id)) { + stbte__activate(0); + stbte__dump_colorstate(); + } + break; + } +} + +static void stbte__colorpicker(int x0, int y0, int w, int h) +{ + int x1 = x0+w, y1 = y0+h, x,y, i; + + x = x0+2; y = y0+6; + + y += 5; + x += 8; + + + { + int color = stbte__color_table[stbte__cp_mode][stbte__cp_aspect][stbte__cp_index]; + int rgb[3]; + if (stbte__cp_altered && stbte__cp_index == STBTE__idle) + color = stbte__save; + + if (stbte__minibutton(STBTE__cmapsize, x1-20,y+ 5, 'C', STBTE__ID2(STBTE__colorpick_id,4,0))) + stbte__color_copy = color; + if (stbte__minibutton(STBTE__cmapsize, x1-20,y+15, 'P', STBTE__ID2(STBTE__colorpick_id,4,1))) + color = stbte__color_copy; + + rgb[0] = color >> 16; rgb[1] = (color>>8)&255; rgb[2] = color & 255; + for (i=0; i < 3; ++i) { + stbte__slider(x+8,64, y, rgb+i, STBTE__ID2(STBTE__colorpick_id,3,i)); + y += 15; + } + if (stbte__ui.event != STBTE__paint && stbte__ui.event != STBTE__tick) + stbte__color_table[stbte__cp_mode][stbte__cp_aspect][stbte__cp_index] = (rgb[0]<<16)|(rgb[1]<<8)|(rgb[2]); + } + + y += 5; + + // states + x = x0+2+35; + if (stbte__ui.event == STBTE__paint) { + static char *states[] = { "idle", "over", "down", "down&over", "selected", "selected&over", "disabled" }; + stbte__draw_text(x, y+1, states[stbte__cp_index], x1-x-1, 0xffffff); + } + + x = x0+24; y += 12; + + for (i=3; i >= 0; --i) { + int state = 0 != (stbte__cp_state & (1 << i)); + if (stbte__layerbutton(x,y, "OASD"[i], STBTE__ID2(STBTE__colorpick_id, 0,i), state,0, STBTE__clayer_button)) { + stbte__cp_state ^= (1 << i); + stbte__cp_index = stbte__state_to_index[0][0][0][stbte__cp_state]; + } + x += 16; + } + x = x0+2; y += 18; + + for (i=0; i < 3; ++i) { + static char *labels[] = { "Base", "Edge", "Text" }; + if (stbte__button(STBTE__ctoolbar_button, labels[i], x,y,0,36, STBTE__ID2(STBTE__colorpick_id,1,i), stbte__cp_aspect==i,0)) + stbte__cp_aspect = i; + x += 40; + } + + y += 18; + x = x0+2; + + for (i=0; i < STBTE__num_color_modes; ++i) { + if (stbte__button(STBTE__ctoolbar_button, stbte__color_names[i], x, y, 0,80, STBTE__ID2(STBTE__colorpick_id,2,i), stbte__cp_mode == i,0)) + stbte__cp_mode = i; + y += 12; + } + + // make the currently selected aspect flash, unless we're actively dragging color slider etc + if (stbte__ui.event == STBTE__tick) { + stbte__save = stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle]; + if ((stbte__ui.active_id & 127) != STBTE__colorpick_id) { + if ((stbte__ui.ms_time & 2047) < 200) { + stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle] ^= 0x1f1f1f; + stbte__cp_altered = 1; + } + } + } +} +#endif static void stbte__editor_traverse(stbte_tilemap *tm) { @@ -2560,15 +2772,14 @@ static void stbte__editor_traverse(stbte_tilemap *tm) for (i=0; i < STBTE__num_panel; ++i) { stbte__panel *p = &stbte__ui.panel[i]; if (stbte__ui.event == STBTE__paint) { - stbte__draw_rect (p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE_COLOR_PANEL_BACKGROUND); - stbte__draw_frame(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE_COLOR_PANEL_OUTLINE); + stbte__draw_box(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE__cpanel, STBTE__idle); } // obscure tilemap data underneath panel stbte__hittest(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE__ID2(STBTE__panel, i, 0)); switch (i) { case STBTE__panel_toolbar: if (stbte__ui.event == STBTE__paint) - stbte__draw_rect(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE_COLOR_TOOLBAR_BACKGROUND); + stbte__draw_rect(p->x0,p->y0,p->x0+p->width,p->y0+p->height, stbte__color_table[STBTE__ctoolbar][STBTE__base][STBTE__idle]); stbte__toolbar(tm,p->x0,p->y0,p->width,p->height); break; case STBTE__panel_info: @@ -2580,10 +2791,15 @@ static void stbte__editor_traverse(stbte_tilemap *tm) case STBTE__panel_categories: stbte__categories(tm,p->x0,p->y0,p->width,p->height); break; + case STBTE__panel_colorpick: +#ifdef STBTE__COLORPICKER + stbte__colorpicker(p->x0,p->y0,p->width,p->height); +#endif + break; case STBTE__panel_tiles: // erase boundary between categories and tiles if they're on same side if (stbte__ui.event == STBTE__paint && p->side == stbte__ui.panel[STBTE__panel_categories].side) - stbte__draw_rect(p->x0+1,p->y0-1,p->x0+p->width-1,p->y0+1, STBTE_COLOR_PANEL_BACKGROUND); + stbte__draw_rect(p->x0+1,p->y0-1,p->x0+p->width-1,p->y0+1, stbte__color_table[STBTE__cpanel][STBTE__base][STBTE__idle]); stbte__palette_of_tiles(tm,p->x0,p->y0,p->width,p->height); break; } @@ -2591,7 +2807,7 @@ static void stbte__editor_traverse(stbte_tilemap *tm) for (j=0; j < 2; ++j) { int result; if (i == STBTE__panel_toolbar) continue; - result = stbte__microbutton(p->x0+p->width - 1 - 2*4 + 4*j,p->y0+2,3, STBTE__ID2(STBTE__panel, i, j+1), 0x808080,0xc0c0c0, 0); + result = stbte__microbutton(p->x0+p->width - 1 - 2*4 + 4*j,p->y0+2,3, STBTE__ID2(STBTE__panel, i, j+1), STBTE__cpanel_sider+j); if (result) { switch (j) { case 0: p->side = result > 0 ? STBTE__side_left : STBTE__side_right; break; @@ -2614,7 +2830,7 @@ static void stbte__editor_traverse(stbte_tilemap *tm) width = stbte__ui.left_width , x += stbte__region[i].width + 1; else width = -stbte__ui.right_width, x -= 6; - if (stbte__microbutton_dragger(x, stbte__region[i].y+2, 5, STBTE__ID(STBTE__region,i), 0x206020,0xffffff, 0, &width)) { + if (stbte__microbutton_dragger(x, stbte__region[i].y+2, 5, STBTE__ID(STBTE__region,i), &width)) { // if non-0, it is expanding, so retract it if (stbte__region[i].retracted == 0.0) stbte__region[i].retracted = 0.01f; @@ -2638,7 +2854,7 @@ static void stbte__editor_traverse(stbte_tilemap *tm) if (stbte__ui.event == STBTE__paint && stbte__ui.alert_msg) { int w = stbte__text_width(stbte__ui.alert_msg); int x = (stbte__ui.x0+stbte__ui.x1)/2; - int y = (stbte__ui.y0+stbte__ui.y1)/2; + int y = (stbte__ui.y0+stbte__ui.y1)*5/6; stbte__draw_rect (x-w/2-4,y-8, x+w/2+4,y+8, 0x604020); stbte__draw_frame(x-w/2-4,y-8, x+w/2+4,y+8, 0x906030); stbte__draw_text (x-w/2,y-4, stbte__ui.alert_msg, w+1, 0xff8040); @@ -2651,6 +2867,11 @@ static void stbte__editor_traverse(stbte_tilemap *tm) stbte__ui.alert_msg = 0; } } + + if (stbte__ui.event == STBTE__paint) { + stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle] = stbte__save; + stbte__cp_altered = 0; + } } static void stbte__do_event(stbte_tilemap *tm)