Selaa lähdekoodia

colorpicker;
new color scheme

Sean Barrett 11 vuotta sitten
vanhempi
commit
b46b49d935
1 muutettua tiedostoa jossa 361 lisäystä ja 140 poistoa
  1. 361 140
      stb_tilemap_editor.h

+ 361 - 140
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
-
-#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
-
-#define STBTE_COLOR_LAYERMASK_HIDE       0xffff55
-#define STBTE_COLOR_LAYERMASK_LOCK       0x5f55ff
-#define STBTE_COLOR_LAYERMASK_SOLO       0xff5f55
+static int *stbte__colors;
+
+enum
+{
+   STBTE__base,
+   STBTE__outline,
+   STBTE__text,
+
+   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,112 +1169,89 @@ 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 = stbte__hittest(x0,y0,x1,y1,id);
+   int over = !disabled && 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);
-   }
+   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(char ch, int x, int y, int width, int id, int toggled)
+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, pad;
-   char label[2] = { ch, 0 };
+   int s = STBTE__BUTTON_INTERNAL_SPACING;
 
    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);
+      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__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)