diff --git a/stb_tilemap_editor.h b/stb_tilemap_editor.h index b4ce92b..4d328e1 100644 --- a/stb_tilemap_editor.h +++ b/stb_tilemap_editor.h @@ -4,6 +4,11 @@ // Embeddable tilemap editor for C/C++ // // +// REVISION HISTORY +// 0.30 properties +// - +// 0.20 initial release +// // COMPILING // // This header file contains both the header file and the @@ -19,18 +24,62 @@ // // color = (r<<16)|(g<<8)|(b) // // void STBTE_DRAW_TILE(int x0, int y0, -// unsigned short id, int highlight); +// unsigned short id, int highlight, stbte_props *data); // // this draws the tile image identified by 'id' in one of several -// // highlight modes (see STBTE_drawmode_* in the header section, -// // x0,y0,highlight:int; id:unsigned short +// // highlight modes (see STBTE_drawmode_* in the header section); +// // if 'data' is NULL, it's drawing the tile in a palette; if 'data' +// // is not NULL, it's drawing a tile on the map, and that is the data +// // associated with that map tile // // #include "stb_tilemap_editor.h" // // Optionally you can define the following functions before the include; // note these must be macros (which can just call a single function) so -// we can detect if you've defined them: +// this library can #ifdef to detect if you've defined them: // -// [[ support for these is not implemented yet ]] +// #define STBTE_PROP_TYPE(int n, short *tiledata, float *params) ... +// // Returns the type of the n'th property of a given tile, which +// // controls how it is edited. Legal types are: +// // 0 /* no editable property in this slot */ +// // STBTE_PROP_int /* uses a slider to adjust value */ +// // STBTE_PROP_float /* uses a weird multi-axis control */ +// // STBTE_PROP_bool /* uses a checkbox to change value */ +// // And you can bitwise-OR in the following flags: +// // STBTE_PROP_disabled +// // Note that all of these are stored as floats in the param array. +// // +// // Since the tiledata is passed to you, you can choose which property +// // is bound to that slot based on that data. +// // +// // Changing the type of a parameter does not cause the underlying +// // value to be clamped to the type min/max except when the tile is +// // explicitly selected. +// +// #define STBTE_PROP_NAME(int n, short *tiledata, float *params) ... +// // these return a string with the name for slot #n in the float +// // property list for the tile. +// // (if any), or +// // based on the values of other params. +// +// #define STBTE_PROP_MIN(int n, short *tiledata) ...your code here... +// #define STBTE_PROP_MAX(int n, short *tiledata) ...your code here... +// // These return the allowable range for the property values for +// // the specified slot. It is never called for boolean types. +// +// #define STBTE_ALLOW_LINK(short *src, float *src_data, \ +// short *dest, float *dest_data) ...your code... +// // this returns true or false depending on whether you allow a link +// // to be drawn from a tile 'src' to a tile 'dest'. if you don't +// // define this, linking will not be supported +// +// #define STBTE_LINK_COLOR(short *src, float *src_data, \ +// short *dest, float *dest_data) ...your code... +// // return a color encoded as a 24-bit unsigned integer in the +// // form 0xRRGGBB. If you don't define this, default colors will +// // be used. +// +// +// [[ support for those below is not implemented yet ]] // // #define STBTE_HITTEST_TILE(x0,y0,id,mx,my) ...your code here... // // this returns true or false depending on whether the mouse @@ -39,11 +88,6 @@ // // this hittest based on the tile geometry, but if you have // // tiles whose images extend out of the tile, you'll need this. // -// #define STBTE_DRAW_ICON(x0,y0,id,highlight) ...your code here... -// // this must draw a tile image identified by 'id' but now the -// // tile image must be drawn to fit in the tile palette, which -// // means it cannot exceed your specified palette spacing. -// // ADDITIONAL CONFIGURATION // // The following symbols set static limits which determine how much @@ -56,6 +100,7 @@ // #define STBTE_MAX_CATEGORIES 100 // #define STBTE_UNDO_BUFFER_BYTES (1 << 20) // 1MB // #define STBTE_MAX_COPY 90000 // e.g. 300x300 +// #define STBTE_MAX_PROP 10 // max properties per tile // // API // @@ -113,6 +158,13 @@ enum STBTE_drawmode_emphasize = 1, }; +// these are the property types +#define STBTE_PROP_none 0 +#define STBTE_PROP_int 1 +#define STBTE_PROP_float 2 +#define STBTE_PROP_bool 3 +#define STBTE_PROP_disabled 4 + //////// // // creation @@ -255,6 +307,22 @@ extern void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layern #define STBTE_UNDO_BUFFER_BYTES (1 << 20) // 1MB #endif +#ifndef STBTE_MAX_PROP +#define STBTE_MAX_PROP 10 +#endif + +#ifndef STBTE_PROP_MIN +#define STBTE_PROP_MIN(n,td,tp) 0 +#endif + +#ifndef STBTE_PROP_MAX +#define STBTE_PROP_MAX(n,td,tp) 100.0 +#endif + +#ifndef STBTE_PROP_FLOAT_SCALE +#define STBTE_PROP_FLOAT_SCALE(n,td,tp) 1 // default scale size +#endif + #define STBTE__UNDO_BUFFER_COUNT (STBTE_UNDO_BUFFER_BYTES>>1) #if STBTE_MAX_TILEMAP_X > 4096 || STBTE_MAX_TILEMAP_Y > 4096 @@ -269,6 +337,11 @@ extern void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layern static int *stbte__colors; +typedef struct +{ + short x,y; +} stbte__link; + enum { STBTE__base, @@ -386,6 +459,19 @@ static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][ #define STBTE_COLOR_TILEPALETTE_OUTLINE 0xffffff #define STBTE_COLOR_TILEPALETTE_BACKGROUND 0x000000 +#ifndef STBTE_LINK_COLOR +#define STBTE_LINK_COLOR(src,sp,dest,dp) 0x5030ff +#endif + +#ifndef STBTE_LINK_COLOR_DRAWING +#define STBTE_LINK_COLOR_DRAWING 0xff40ff +#endif + +#ifndef STBTE_LINK_COLOR_DISALLOWED +#define STBTE_LINK_COLOR_DISALLOWED 0x602060 +#endif + + // disabled, selected, down, over static unsigned char stbte__state_to_index[2][2][2][2] = { @@ -402,40 +488,40 @@ static unsigned char stbte__state_to_index[2][2][2][2] = #define STBTE__FONT_HEIGHT 9 static short stbte__font_offset[95+16]; -static short stbte__fontdata[762] = +static short stbte__fontdata[769] = { - 4,4,4,9,9,9,9,8,9,8,4,9,7,7,7,7,4,2,6,8,6,6,7,3,4,4,8,6,3,6,2,6,6,6,6,6,6, + 4,9,6,9,9,9,9,8,9,8,4,9,7,7,7,7,4,2,6,8,6,6,7,3,4,4,8,6,3,6,2,6,6,6,6,6,6, 6,6,6,6,6,2,3,5,4,5,6,6,6,6,6,6,6,6,6,6,6,6,7,6,7,7,7,6,7,6,6,6,6,7,7,6,6, 6,4,6,4,7,7,3,6,6,5,6,6,5,6,6,4,5,6,4,7,6,6,6,6,6,6,6,6,6,7,6,6,6,5,2,5,8, - 0,0,0,0,0,0,0,0,0,0,0,0,146,511,146,146,511,146,146,511,146,511,257,341,297, - 341,297,341,257,511,16,56,124,16,16,16,124,56,16,96,144,270,261,262,136,80, - 48,224,192,160,80,40,22,14,15,3,448,496,496,240,232,20,10,5,2,112,232,452, - 450,225,113,58,28,63,30,60,200,455,257,257,0,0,0,257,257,455,120,204,132, - 132,159,14,4,4,14,159,132,132,204,120,8,24,56,120,56,24,8,32,48,56,60,56, - 48,32,0,0,0,0,111,111,7,7,0,0,7,7,34,127,127,34,34,127,127,34,36,46,107,107, - 58,18,99,51,24,12,102,99,48,122,79,93,55,114,80,4,7,3,62,127,99,65,65,99, - 127,62,8,42,62,28,28,62,42,8,8,8,62,62,8,8,128,224,96,8,8,8,8,8,8,96,96,96, - 48,24,12,6,3,62,127,89,77,127,62,64,66,127,127,64,64,98,115,89,77,71,66,33, - 97,73,93,119,35,24,28,22,127,127,16,39,103,69,69,125,57,62,127,73,73,121, - 48,1,1,113,121,15,7,54,127,73,73,127,54,6,79,73,105,63,30,54,54,128,246,118, - 8,28,54,99,65,20,20,20,20,65,99,54,28,8,2,3,105,109,7,2,30,63,33,45,47,46, - 124,126,19,19,126,124,127,127,73,73,127,54,62,127,65,65,99,34,127,127,65, - 99,62,28,127,127,73,73,73,65,127,127,9,9,9,1,62,127,65,73,121,121,127,127, - 8,8,127,127,65,65,127,127,65,65,32,96,64,64,127,63,127,127,8,28,54,99,65, - 127,127,64,64,64,64,127,127,6,12,6,127,127,127,127,6,12,24,127,127,62,127, - 65,65,65,127,62,127,127,9,9,15,6,62,127,65,81,49,127,94,127,127,9,25,127, - 102,70,79,73,73,121,49,1,1,127,127,1,1,63,127,64,64,127,63,15,31,48,96,48, - 31,15,127,127,48,24,48,127,127,99,119,28,28,119,99,7,15,120,120,15,7,97,113, - 89,77,71,67,127,127,65,65,3,6,12,24,48,96,65,65,127,127,8,12,6,3,6,12,8,64, - 64,64,64,64,64,64,3,7,4,32,116,84,84,124,120,127,127,68,68,124,56,56,124, - 68,68,68,56,124,68,68,127,127,56,124,84,84,92,24,8,124,126,10,10,56,380,324, - 324,508,252,127,127,4,4,124,120,72,122,122,64,256,256,256,506,250,126,126, - 16,56,104,64,66,126,126,64,124,124,24,56,28,124,120,124,124,4,4,124,120,56, - 124,68,68,124,56,508,508,68,68,124,56,56,124,68,68,508,508,124,124,4,4,12, - 8,72,92,84,84,116,36,4,4,62,126,68,68,60,124,64,64,124,124,28,60,96,96,60, - 28,28,124,112,56,112,124,28,68,108,56,56,108,68,284,316,352,320,508,252,68, - 100,116,92,76,68,8,62,119,65,65,127,127,65,65,119,62,8,16,24,12,12,24,24, - 12,4, + 0,0,0,0,2,253,130,456,156,8,72,184,64,2,125,66,64,160,64,146,511,146,146, + 511,146,146,511,146,511,257,341,297,341,297,341,257,511,16,56,124,16,16,16, + 124,56,16,96,144,270,261,262,136,80,48,224,192,160,80,40,22,14,15,3,448,496, + 496,240,232,20,10,5,2,112,232,452,450,225,113,58,28,63,30,60,200,455,257, + 257,0,0,0,257,257,455,120,204,132,132,159,14,4,4,14,159,132,132,204,120,8, + 24,56,120,56,24,8,32,48,56,60,56,48,32,0,0,0,0,111,111,7,7,0,0,7,7,34,127, + 127,34,34,127,127,34,36,46,107,107,58,18,99,51,24,12,102,99,48,122,79,93, + 55,114,80,4,7,3,62,127,99,65,65,99,127,62,8,42,62,28,28,62,42,8,8,8,62,62, + 8,8,128,224,96,8,8,8,8,8,8,96,96,96,48,24,12,6,3,62,127,89,77,127,62,64,66, + 127,127,64,64,98,115,89,77,71,66,33,97,73,93,119,35,24,28,22,127,127,16,39, + 103,69,69,125,57,62,127,73,73,121,48,1,1,113,121,15,7,54,127,73,73,127,54, + 6,79,73,105,63,30,54,54,128,246,118,8,28,54,99,65,20,20,20,20,65,99,54,28, + 8,2,3,105,109,7,2,30,63,33,45,47,46,124,126,19,19,126,124,127,127,73,73,127, + 54,62,127,65,65,99,34,127,127,65,99,62,28,127,127,73,73,73,65,127,127,9,9, + 9,1,62,127,65,73,121,121,127,127,8,8,127,127,65,65,127,127,65,65,32,96,64, + 64,127,63,127,127,8,28,54,99,65,127,127,64,64,64,64,127,127,6,12,6,127,127, + 127,127,6,12,24,127,127,62,127,65,65,65,127,62,127,127,9,9,15,6,62,127,65, + 81,49,127,94,127,127,9,25,127,102,70,79,73,73,121,49,1,1,127,127,1,1,63,127, + 64,64,127,63,15,31,48,96,48,31,15,127,127,48,24,48,127,127,99,119,28,28,119, + 99,7,15,120,120,15,7,97,113,89,77,71,67,127,127,65,65,3,6,12,24,48,96,65, + 65,127,127,8,12,6,3,6,12,8,64,64,64,64,64,64,64,3,7,4,32,116,84,84,124,120, + 127,127,68,68,124,56,56,124,68,68,68,56,124,68,68,127,127,56,124,84,84,92, + 24,8,124,126,10,10,56,380,324,324,508,252,127,127,4,4,124,120,72,122,122, + 64,256,256,256,506,250,126,126,16,56,104,64,66,126,126,64,124,124,24,56,28, + 124,120,124,124,4,4,124,120,56,124,68,68,124,56,508,508,68,68,124,56,56,124, + 68,68,508,508,124,124,4,4,12,8,72,92,84,84,116,36,4,4,62,126,68,68,60,124, + 64,64,124,124,28,60,96,96,60,28,28,124,112,56,112,124,28,68,108,56,56,108, + 68,284,316,352,320,508,252,68,100,116,92,76,68,8,62,119,65,65,127,127,65, + 65,119,62,8,16,24,12,12,24,24,12,4, }; typedef struct @@ -458,6 +544,7 @@ enum STBTE__panel_colorpick, STBTE__panel_info, STBTE__panel_layers, + STBTE__panel_props, STBTE__panel_categories, STBTE__panel_tiles, @@ -480,8 +567,11 @@ enum STBTE__tool_rect, STBTE__tool_eyedrop, STBTE__tool_fill, + STBTE__tool_link, + + STBTE__tool_showgrid, + STBTE__tool_showlinks, - STBTE__tool_grid, STBTE__tool_undo, STBTE__tool_redo, // copy/cut/paste aren't included here because they're displayed differently @@ -490,7 +580,7 @@ enum }; // icons are stored in the 0-31 range of ASCII in the font -static int toolchar[] = { 26,24,25,20,23,22, 19,29,28, }; +static int toolchar[] = { 26,24,25,20,23,22,18, 19,17, 29,28, }; enum { @@ -520,23 +610,30 @@ typedef struct int x0,y0,x1,y1,color; } stbte__colorrect; +#define STBTE__MAX_DELAYRECT 256 +#define STBTE__MAX_DELAYLINK 4096 + typedef struct { int tool, active_event; int active_id, hot_id, next_hot_id; int event; - int mx,my; + int mx,my, dx,dy; int ms_time; int shift, scrollkey; int initted; int side_extended[2]; - stbte__colorrect delayrect[1024]; - int delaycount; - int show_grid; + stbte__colorrect delayrect[STBTE__MAX_DELAYRECT]; + stbte__colorrect delaylink[STBTE__MAX_DELAYLINK]; + int delaycount, delaylinkcount; + int show_grid, show_links; int brush_state; // used to decide which kind of erasing int eyedrop_x, eyedrop_y, eyedrop_last_layer; int pasting, paste_x, paste_y; int scrolling, start_x, start_y; + int last_mouse_x, last_mouse_y; + int accum_x, accum_y; + int linking; int dragging; int drag_x, drag_y, drag_w, drag_h; int drag_offx, drag_offy, drag_dest_x, drag_dest_y; @@ -579,6 +676,11 @@ enum struct stbte_tilemap { stbte__tiledata data[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_LAYERS]; + float props[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_PROP]; + #ifdef STBTE_ALLOW_LINK + stbte__link link[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X]; + int linkcount[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X]; + #endif int max_x, max_y, num_layers; int spacing_x, spacing_y; int palette_spacing_x, palette_spacing_y; @@ -588,6 +690,10 @@ struct stbte_tilemap int num_categories, category_scroll; stbte__tileinfo *tiles; int num_tiles, max_tiles, digits; + unsigned char undo_available_valid; + unsigned char undo_available; + unsigned char redo_available; + unsigned char padding; int cur_palette_count; int palette_scroll; int tileinfo_dirty; @@ -608,6 +714,7 @@ static void stbte__init_gui(void) int i,n; stbte__ui.initted = 1; // init UI state + stbte__ui.show_links = 1; for (i=0; i < STBTE__num_panel; ++i) { stbte__ui.panel[i].expanded = 1; // visible if not autohidden stbte__ui.panel[i].delta_height = 0; @@ -668,6 +775,7 @@ stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacin tm->category_scroll = 0; tm->layer_scroll = 0; tm->has_layer_names = 0; + tm->undo_available_valid = 0; for (i=0; i < tm->num_layers; ++i) { tm->layerinfo[i].hidden = 0; @@ -782,6 +890,13 @@ void stbte_clear_map(stbte_tilemap *tm) tm->data[0][i][0] = tm->background_tile; for (j=1; j < tm->num_layers; ++j) tm->data[0][i][j] = STBTE__NO_TILE; + for (j=0; j < STBTE_MAX_PROP; ++j) + tm->props[0][i][j] = 0; + #ifdef STBTE_ALLOW_LINK + tm->link[0][i].x = -1; + tm->link[0][i].y = -1; + tm->linkcount[0][i] = 0; + #endif } } @@ -865,11 +980,18 @@ static void stbte__prepare_tileinfo(stbte_tilemap *tm) // 2) end_of_redo_record // -2:short // -// 2) tile update +// 3) tile update // tile_id:short (-1..32767) +// x_coord:short +// y_coord:short +// layer:short (0..31) +// +// 4) property update (also used for links) +// value_hi:short +// value_lo:short // y_coord:short // x_coord:short -// layer:short (0..31) +// property:short (256+prop#) // // Since we use a circular buffer, we might overwrite the undo storage. // To detect this, before playing back commands we scan back and see @@ -877,7 +999,17 @@ static void stbte__prepare_tileinfo(stbte_tilemap *tm) // it's wholly contained. // // When we read back through, we see them in reverse order, so -// we'll see the layer number first +// we'll see the layer number or property number first +// +// To be clearer about the circular buffer, there are two cases: +// 1. a single record is larger than the whole buffer. +// this is caught because the end_of_undo_record will +// get overwritten. +// 2. multiple records written are larger than the whole +// buffer, so some of them have been overwritten by +// the later ones. this is handled by explicitly tracking +// the undo length; we never try to parse the data that +// got overwritten // given two points, compute the length between them #define stbte__wrap(pos) ((pos) & (STBTE__UNDO_BUFFER_COUNT-1)) @@ -893,6 +1025,7 @@ static void stbte__write_undo(stbte_tilemap *tm, short value) tm->undo_pos = stbte__wrap(pos+1); tm->undo_len += (tm->undo_len < STBTE__UNDO_BUFFER_COUNT-2); tm->redo_len -= (tm->redo_len > 0); + tm->undo_available_valid = 0; } static void stbte__write_redo(stbte_tilemap *tm, short value) @@ -902,6 +1035,7 @@ static void stbte__write_redo(stbte_tilemap *tm, short value) tm->undo_pos = stbte__wrap(pos-1); tm->redo_len += (tm->redo_len < STBTE__UNDO_BUFFER_COUNT-2); tm->undo_len -= (tm->undo_len > 0); + tm->undo_available_valid = 0; } static void stbte__begin_undo(stbte_tilemap *tm) @@ -949,19 +1083,73 @@ static void stbte__redo_record(stbte_tilemap *tm, int x, int y, int i, int v) stbte__write_redo(tm, i); } -static void stbte__undo(stbte_tilemap *tm) +static float stbte__extract_float(short s0, short s1) +{ + union { float f; short s[2]; } converter; + converter.s[0] = s0; + converter.s[1] = s1; + return converter.f; +} + +static short stbte__extract_short(float f, int slot) +{ + union { float f; short s[2]; } converter; + converter.f = f; + return converter.s[slot]; +} + +static void stbte__undo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1) +{ + STBTE_ASSERT(stbte__ui.undoing); + if (stbte__ui.undoing) { + stbte__write_undo(tm, s1); + stbte__write_undo(tm, s0); + stbte__write_undo(tm, x); + stbte__write_undo(tm, y); + stbte__write_undo(tm, 256+i); + } +} + +static void stbte__undo_record_prop_float(stbte_tilemap *tm, int x, int y, int i, float f) +{ + stbte__undo_record_prop(tm, x,y,i, stbte__extract_short(f,0), stbte__extract_short(f,1)); +} + +static void stbte__redo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1) +{ + stbte__write_redo(tm, s1); + stbte__write_redo(tm, s0); + stbte__write_redo(tm, x); + stbte__write_redo(tm, y); + stbte__write_redo(tm, 256+i); +} + + +static int stbte__undo_find_end(stbte_tilemap *tm) { // first scan through for the end record - int i, pos = stbte__wrap(tm->undo_pos-1), endpos; - for (i=0; i < tm->undo_len; i += 4) { + int i, pos = stbte__wrap(tm->undo_pos-1); + for (i=0; i < tm->undo_len;) { STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk); if (tm->undo_buffer[pos] == STBTE__undo_record) break; - pos = stbte__wrap(pos-4); + if (tm->undo_buffer[pos] >= 255) + pos = stbte__wrap(pos-5), i += 5; + else + pos = stbte__wrap(pos-4), i += 4; } if (i >= tm->undo_len) + return -1; + return pos; +} + +static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y); +static void stbte__undo(stbte_tilemap *tm) +{ + int i, pos, endpos; + endpos = stbte__undo_find_end(tm); + if (endpos < 0) return; - endpos = pos; // we found a complete undo record pos = stbte__wrap(tm->undo_pos-1); @@ -978,29 +1166,62 @@ static void stbte__undo(stbte_tilemap *tm) y = tm->undo_buffer[stbte__wrap(pos-1)]; x = tm->undo_buffer[stbte__wrap(pos-2)]; v = tm->undo_buffer[stbte__wrap(pos-3)]; - pos = stbte__wrap(pos-4); - // write the redo entry - stbte__redo_record(tm, x, y, n, tm->data[y][x][n]); - // apply the undo entry - tm->data[y][x][n] = (short) v; + if (n >= 255) { + short s0=0,s1=0; + int v2 = tm->undo_buffer[stbte__wrap(pos-4)]; + pos = stbte__wrap(pos-5); + if (n > 255) { + float vf = stbte__extract_float(v, v2); + s0 = stbte__extract_short(tm->props[y][x][n-256], 0); + s1 = stbte__extract_short(tm->props[y][x][n-256], 1); + tm->props[y][x][n-256] = vf; + } else { +#ifdef STBTE_ALLOW_LINK + s0 = tm->link[y][x].x; + s1 = tm->link[y][x].y; + stbte__set_link(tm, x,y, v, v2); +#endif + } + // write the redo entry + stbte__redo_record_prop(tm, x, y, n-256, s0,s1); + // apply the undo entry + } else { + pos = stbte__wrap(pos-4); + // write the redo entry + stbte__redo_record(tm, x, y, n, tm->data[y][x][n]); + // apply the undo entry + tm->data[y][x][n] = (short) v; + } } // overwrite undo record with junk tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk; } -static void stbte__redo(stbte_tilemap *tm) +static int stbte__redo_find_end(stbte_tilemap *tm) { // first scan through for the end record - int i, pos = stbte__wrap(tm->undo_pos+1), endpos; - for (i=0; i < tm->redo_len; i += 4) { + int i, pos = stbte__wrap(tm->undo_pos+1); + for (i=0; i < tm->redo_len;) { STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk); if (tm->undo_buffer[pos] == STBTE__redo_record) break; - pos = stbte__wrap(pos+4); + if (tm->undo_buffer[pos] >= 255) + pos = stbte__wrap(pos+5), i += 5; + else + pos = stbte__wrap(pos+4), i += 4; } if (i >= tm->redo_len) - return; // this should only ever happen if redo buffer is empty - endpos = pos; + return -1; // this should only ever happen if redo buffer is empty + return pos; +} + +static void stbte__redo(stbte_tilemap *tm) +{ + // first scan through for the end record + int i, pos, endpos; + endpos = stbte__redo_find_end(tm); + if (endpos < 0) + return; // we found a complete redo record pos = stbte__wrap(tm->undo_pos+1); @@ -1014,22 +1235,112 @@ static void stbte__redo(stbte_tilemap *tm) y = tm->undo_buffer[stbte__wrap(pos+1)]; x = tm->undo_buffer[stbte__wrap(pos+2)]; v = tm->undo_buffer[stbte__wrap(pos+3)]; - pos = stbte__wrap(pos+4); - // don't use stbte__undo_record because it's guarded - stbte__write_undo(tm, tm->data[y][x][n]); - stbte__write_undo(tm, x); - stbte__write_undo(tm, y); - stbte__write_undo(tm, n); - tm->data[y][x][n] = (short) v; + if (n >= 255) { + int v2 = tm->undo_buffer[stbte__wrap(pos+4)]; + short s0=0,s1=0; + pos = stbte__wrap(pos+5); + if (n > 255) { + float vf = stbte__extract_float(v, v2); + s0 = stbte__extract_short(tm->props[y][x][n-256],0); + s1 = stbte__extract_short(tm->props[y][x][n-256],1); + tm->props[y][x][n-256] = vf; + } else { +#ifdef STBTE_ALLOW_LINK + s0 = tm->link[y][x].x; + s1 = tm->link[y][x].y; + stbte__set_link(tm, x,y,v,v2); +#endif + } + // don't use stbte__undo_record_prop because it's guarded + stbte__write_undo(tm, s1); + stbte__write_undo(tm, s0); + stbte__write_undo(tm, x); + stbte__write_undo(tm, y); + stbte__write_undo(tm, n); + } else { + pos = stbte__wrap(pos+4); + // don't use stbte__undo_record because it's guarded + stbte__write_undo(tm, tm->data[y][x][n]); + stbte__write_undo(tm, x); + stbte__write_undo(tm, y); + stbte__write_undo(tm, n); + tm->data[y][x][n] = (short) v; + } } tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk; } +// because detecting that undo is available +static void stbte__recompute_undo_available(stbte_tilemap *tm) +{ + tm->undo_available = (stbte__undo_find_end(tm) >= 0); + tm->redo_available = (stbte__redo_find_end(tm) >= 0); +} + +static int stbte__undo_available(stbte_tilemap *tm) +{ + if (!tm->undo_available_valid) + stbte__recompute_undo_available(tm); + return tm->undo_available; +} + +static int stbte__redo_available(stbte_tilemap *tm) +{ + if (!tm->undo_available_valid) + stbte__recompute_undo_available(tm); + return tm->undo_available; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef STBTE_ALLOW_LINK +static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y) +{ + stbte__link *a; + STBTE_ASSERT(src_x >= 0 && src_x < STBTE_MAX_TILEMAP_X && src_y >= 0 && src_y < STBTE_MAX_TILEMAP_Y); + a = &tm->link[src_y][src_x]; + // check if it's a do nothing + if (a->x == dest_x && a->y == dest_y) + return; + // otherwise, undo + stbte__begin_undo(tm); + stbte__undo_record_prop(tm, src_x, src_y, -1, a->x, a->y); + stbte__end_undo(tm); + // check if there's an existing link + if (a->x >= 0) { + // decrement existing link refcount + STBTE_ASSERT(tm->linkcount[a->y][a->x] > 0); + --tm->linkcount[a->y][a->x]; + } + // increment new dest + if (dest_x >= 0) { + ++tm->linkcount[dest_y][dest_x]; + } + a->x = dest_x; + a->y = dest_y; +} +#endif + + static void stbte__draw_rect(int x0, int y0, int x1, int y1, unsigned int color) { STBTE_DRAW_RECT(x0,y0,x1,y1, color); } +static void stbte__draw_line(int x0, int y0, int x1, int y1, unsigned int color) +{ + int temp; + if (x1 < x0) temp=x0,x0=x1,x1=temp; + if (y1 < y0) temp=y0,y0=y1,y1=temp; + stbte__draw_rect(x0,y0,x1+1,y1+1,color); +} + +static void stbte__draw_link(int x0, int y0, int x1, int y1, unsigned int color) +{ + stbte__draw_line(x0,y0,x0,y1, color); + stbte__draw_line(x0,y1,x1,y1, color); +} + static void stbte__draw_frame(int x0, int y0, int x1, int y1, unsigned int color) { stbte__draw_rect(x0,y0,x1-1,y0+1,color); @@ -1110,25 +1421,40 @@ static int stbte__text_width(const char *str) static void stbte__draw_frame_delayed(int x0, int y0, int x1, int y1, int color) { - if (stbte__ui.delaycount < 1024) { + if (stbte__ui.delaycount < STBTE__MAX_DELAYRECT) { stbte__colorrect r = { x0,y0,x1,y1,color }; stbte__ui.delayrect[stbte__ui.delaycount++] = r; } } +static void stbte__draw_link_delayed(int x0, int y0, int x1, int y1, int color) +{ + if (stbte__ui.delaycount < STBTE__MAX_DELAYRECT) { + stbte__colorrect r = { x0,y0,x1,y1,color }; + stbte__ui.delaylink[stbte__ui.delaylinkcount++] = r; + } +} + static void stbte__flush_delay(void) { - stbte__colorrect *r = stbte__ui.delayrect; + stbte__colorrect *r; int i; + r = stbte__ui.delayrect; for (i=0; i < stbte__ui.delaycount; ++i,++r) stbte__draw_frame(r->x0,r->y0,r->x1,r->y1,r->color); + r = stbte__ui.delaylink; + for (i=0; i < stbte__ui.delaylinkcount; ++i,++r) + stbte__draw_link(r->x0,r->y0,r->x1,r->y1,r->color); stbte__ui.delaycount = 0; + stbte__ui.delaylinkcount = 0; } static void stbte__activate(int id) { stbte__ui.active_id = id; stbte__ui.active_event = stbte__ui.event; + stbte__ui.accum_x = 0; + stbte__ui.accum_y = 0; } static int stbte__hittest(int x0, int y0, int x1, int y1, int id) @@ -1195,7 +1521,7 @@ static int stbte__button(int colormode, char *label, int x, int y, int textoff, 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) +static int stbte__button_icon(int colormode, char ch, int x, int y, 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; @@ -1205,8 +1531,10 @@ static int stbte__button_icon(int colormode, char ch, int x, int y, int width, i if (stbte__ui.event == STBTE__paint) { 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)); + stbte__draw_textbox(x0,y0,x1,y1, label,s+pad,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled)); } + if (disabled) + return 0; return (stbte__button_core(id) == 1); } @@ -1287,6 +1615,104 @@ static int stbte__category_button(char *label, int x, int y, int width, int id, return (stbte__button_core(id) == 1); } +enum +{ + STBTE__none, + STBTE__begin, + STBTE__end, + STBTE__change, +}; + +// returns -1 if value changes, 1 at end of drag +static int stbte__slider(int x0, int w, int y, int range, int *value, int id) +{ + int x1 = x0+w; + int pos = *value * w / (range+1); + int over = stbte__hittest(x0,y-2,x1,y+3,id); + int event_mouse_move = STBTE__change; + 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); + event_mouse_move = STBTE__begin; + } + // fall through + case STBTE__mousemove: + if (STBTE__IS_ACTIVE(id)) { + int v = (stbte__ui.mx-x0)*(range+1)/w; + if (v < 0) v = 0; else if (v > range) v = range; + *value = v; + return event_mouse_move; + } + break; + case STBTE__leftup: + if (STBTE__IS_ACTIVE(id)) { + stbte__activate(0); + return STBTE__end; + } + break; + } + return STBTE__none; +} + +static int stbte__float_control(int x0, int y0, int w, float minv, float maxv, float scale, char *fmt, float *value, int colormode, int id) +{ + int x1 = x0+w; + int y1 = y0+11; + int over = stbte__hittest(x0,y0,x1,y1,id); + switch (stbte__ui.event) { + case STBTE__paint: { + char text[32]; + sprintf(text, fmt ? fmt : "%6.2f", *value); + stbte__draw_textbox(x0,y0,x1,y1, text, 1,2, colormode, STBTE__INDEX_FOR_ID(id,0,0)); + break; + } + case STBTE__leftdown: + case STBTE__rightdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) + stbte__activate(id); + return STBTE__begin; + break; + case STBTE__leftup: + case STBTE__rightup: + if (STBTE__IS_ACTIVE(id)) { + stbte__activate(0); + return STBTE__end; + } + break; + case STBTE__mousemove: + if (STBTE__IS_ACTIVE(id)) { + float v = *value, delta; + int ax = stbte__ui.accum_x/4; + int ay = stbte__ui.accum_y/4; + stbte__ui.accum_x -= ax*4; + stbte__ui.accum_y -= ay*4; + if (stbte__ui.shift) { + if (stbte__ui.active_event == STBTE__leftdown) + delta = ax * 1 + ay / 16.0; + else + delta = ax / 64.0 + ay / 1024.0; + } else { + if (stbte__ui.active_event == STBTE__leftdown) + delta = ax + ay * 0.1; + else + delta = ax * 0.01 + ay * 0.001; + } + v += delta * scale; + if (v < minv) v = minv; + if (v > maxv) v = maxv; + *value = v; + return STBTE__change; + } + break; + } + return STBTE__none; +} + static void stbte__scrollbar(int x, int y0, int y1, int *val, int v0, int v1, int num_vis, int id) { int over; @@ -1339,6 +1765,13 @@ static void stbte__compute_digits(stbte_tilemap *tm) tm->digits = 2; } +static int stbte__is_single_selection(void) +{ + return stbte__ui.has_selection + && stbte__ui.select_x0 == stbte__ui.select_x1 + && stbte__ui.select_y0 == stbte__ui.select_y1; +} + typedef struct { int width, height; @@ -1347,7 +1780,7 @@ typedef struct float retracted; } stbte__region_t; -stbte__region_t stbte__region[4]; +static stbte__region_t stbte__region[4]; #define STBTE__TOOLBAR_ICON_SIZE (9+2*2) #define STBTE__TOOLBAR_PASTE_SIZE (34+2*2) @@ -1360,9 +1793,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,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 min_width[STBTE__num_panel]={0,0,0,0,0,0,0}; + int height[STBTE__num_panel]={0,0,0,0,0,0,0}; + int panel_active[STBTE__num_panel]={1,0,1,1,1,1,1}; int vpos[4] = { 0,0,0,0 }; stbte__panel *p = stbte__ui.panel; stbte__panel *pt = &p[STBTE__panel_toolbar]; @@ -1382,6 +1815,7 @@ static void stbte__compute_panel_locations(stbte_tilemap *tm) #ifdef STBTE__COLORPICKER panel_active[STBTE__panel_colorpick ] = 1; #endif + panel_active[STBTE__panel_props ] = stbte__is_single_selection(); // 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" @@ -1390,6 +1824,7 @@ static void stbte__compute_panel_locations(stbte_tilemap *tm) 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 min_width[STBTE__panel_toolbar ] = 4 + STBTE__TOOLBAR_PASTE_SIZE; // wide enough for 'Paste' button + min_width[STBTE__panel_props ] = 80; // narrowest info panel // compute minimum widths for left & right panels based on the above stbte__region[0].width = stbte__ui.left_width; @@ -1453,6 +1888,9 @@ static void stbte__compute_panel_locations(stbte_tilemap *tm) if (k == 0) k = 1; height[STBTE__panel_tiles] = ((tm->num_tiles+k-1)/k) * tm->palette_spacing_y + 8; + // properties panel + height[STBTE__panel_props] = 9 + STBTE_MAX_PROP*14; + // now compute the locations of all the panels for (i=0; i < STBTE__num_panel; ++i) { if (panel_active[i]) { @@ -1497,6 +1935,9 @@ enum STBTE__panel_sizer, // param panel ID STBTE__scrollbar_id, STBTE__colorpick_id, + STBTE__prop_flag, + STBTE__prop_float, + STBTE__prop_int, }; // id is: [ 24-bit data : 7-bit identifer ] @@ -1998,11 +2439,12 @@ static void stbte__drag_update(stbte_tilemap *tm, int mapx, int mapy) stbte__paste_stack(tm, data, data, tm->data[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox], !stbte__ui.shift); } if (data) { - for (i=0; i < tm->num_layers; ++i) + for (i=0; i < tm->num_layers; ++i) { if (tm->data[mapy][mapx][i] != data[i]) { stbte__undo_record(tm, mapx, mapy, i, tm->data[mapy][mapx][i]); tm->data[mapy][mapx][i] = data[i]; } + } } } @@ -2036,7 +2478,6 @@ static void stbte__drag_place(stbte_tilemap *tm, int mapx, int mapy) stbte__ui.select_y1 = stbte__ui.select_y0 + stbte__ui.drag_h; } - static void stbte__tile(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy) { int tool = stbte__ui.tool; @@ -2111,12 +2552,14 @@ static void stbte__tile(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy) } } - for (i=0; i < tm->num_layers; ++i) { - if (i == tm->solo_layer || (!tm->layerinfo[i].hidden && tm->solo_layer < 0)) - if (data[i] >= 0) - STBTE_DRAW_TILE(x0,y0, (unsigned short) data[i], 0); - if (i == 0 && stbte__ui.show_grid==1) - stbte__draw_halfframe(x0,y0,x0+tm->spacing_x, y0+tm->spacing_y, STBTE_COLOR_GRID); + { + for (i=0; i < tm->num_layers; ++i) { + if (i == tm->solo_layer || (!tm->layerinfo[i].hidden && tm->solo_layer < 0)) + if (data[i] >= 0) + STBTE_DRAW_TILE(x0,y0, (unsigned short) data[i], 0, tm->props[mapy][mapx]); + if (i == 0 && stbte__ui.show_grid==1) + stbte__draw_halfframe(x0,y0,x0+tm->spacing_x, y0+tm->spacing_y, STBTE_COLOR_GRID); + } } if (stbte__ui.pasting || stbte__ui.dragging || stbte__ui.scrolling) break; @@ -2141,6 +2584,20 @@ static void stbte__tile(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy) if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { stbte__draw_frame_delayed(x0-1,y0-1,x1+1,y1+1, STBTE_COLOR_TILEMAP_HIGHLIGHT); } +#ifdef STBTE_ALLOW_LINK + if (stbte__ui.show_links && tm->link[mapy][mapx].x >= 0) { + int tx = tm->link[mapy][mapx].x; + int ty = tm->link[mapy][mapx].y; + int lx0,ly0,lx1,ly1; + lx0 = x0 + (tm->spacing_x >> 1) - 1; + ly0 = y0 + (tm->spacing_y >> 1) - 1; + lx1 = lx0 + (tx - mapx) * tm->spacing_x + 2; + ly1 = ly0 + (ty - mapy) * tm->spacing_y + 2; + stbte__draw_link_delayed(lx0,ly0,lx1,ly1, + STBTE_LINK_COLOR(tm->data[mapy][mapx], tm->props[mapy][mapx], + tm->data[ty ][tx ], tm->props[ty ][tx])); + } +#endif break; } } @@ -2232,6 +2689,41 @@ static void stbte__tile(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy) } break; +#ifdef STBTE_ALLOW_LINK + case STBTE__tool_link: + switch (stbte__ui.event) { + case STBTE__leftdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + stbte__activate(id); + stbte__ui.linking = 1; + stbte__ui.sx = mapx; + stbte__ui.sy = mapy; + // @TODO: undo + } + break; + case STBTE__leftup: + if (STBTE__IS_HOT(id) && STBTE__IS_MAP_ACTIVE()) { + if ((mapx != stbte__ui.sx || mapy != stbte__ui.sy) && + STBTE_ALLOW_LINK(tm->data[stbte__ui.sy][stbte__ui.sx], tm->props[stbte__ui.sy][stbte__ui.sx], + tm->data[mapy][mapx], tm->props[mapy][mapx])) + stbte__set_link(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy); + else + stbte__set_link(tm, stbte__ui.sx, stbte__ui.sy, -1,-1); + stbte__ui.linking = 0; + stbte__activate(0); + } + break; + + case STBTE__rightdown: + if (STBTE__IS_ACTIVE(id)) { + stbte__activate(0); + stbte__ui.linking = 0; + } + break; + } + break; +#endif + case STBTE__tool_erase: switch (stbte__ui.event) { case STBTE__mousemove: @@ -2338,20 +2830,30 @@ static void stbte__tile(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy) static void stbte__toolbar(stbte_tilemap *tm, int x0, int y0, int w, int h) { int i; - int estimated_width = 13 * STBTE__num_tool + 8+8+ 120+4; + int estimated_width = 13 * STBTE__num_tool + 8+8+ 120+4 - 30; int x = x0 + w/2 - estimated_width/2; int y = y0+1; for (i=0; i < STBTE__num_tool; ++i) { - int highlight=0; + int highlight=0, disable=0; highlight = (stbte__ui.tool == i); - if (i == STBTE__tool_undo || i == STBTE__tool_grid) + if (i == STBTE__tool_undo || i == STBTE__tool_showgrid) x += 8; - if (i == STBTE__tool_grid && stbte__ui.show_grid) - highlight=1; + if (i == STBTE__tool_showgrid && stbte__ui.show_grid) + highlight = 1; + if (i == STBTE__tool_showlinks && stbte__ui.show_links) + highlight = 1; if (i == STBTE__tool_fill) continue; - if (stbte__button_icon(STBTE__ctoolbar_button, toolchar[i], x, y, 13, STBTE__ID(STBTE__toolbarA, i), highlight)) { + #ifndef STBTE_ALLOW_LINK + if (i == STBTE__tool_link || i == STBTE__tool_showlinks) + disable = 1; + #endif + if (i == STBTE__tool_undo && !stbte__undo_available(tm)) + disable = 1; + if (i == STBTE__tool_redo && !stbte__redo_available(tm)) + disable = 1; + if (stbte__button_icon(STBTE__ctoolbar_button, toolchar[i], x, y, 13, STBTE__ID(STBTE__toolbarA, i), highlight, disable)) { switch (i) { case STBTE__tool_eyedrop: stbte__ui.eyedrop_last_layer = tm->num_layers; // flush eyedropper state @@ -2360,7 +2862,10 @@ static void stbte__toolbar(stbte_tilemap *tm, int x0, int y0, int w, int h) stbte__ui.tool = i; stbte__ui.has_selection = 0; break; - case STBTE__tool_grid: + case STBTE__tool_showlinks: + stbte__ui.show_links = !stbte__ui.show_links; + break; + case STBTE__tool_showgrid: stbte__ui.show_grid = (stbte__ui.show_grid+1)%3; break; case STBTE__tool_undo: @@ -2443,7 +2948,7 @@ static void stbte__info(stbte_tilemap *tm, int x0, int y0, int w, int h) x = x0+2; 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); + STBTE_DRAW_TILE(x+43,y-3,tm->tiles[tm->cur_tile].id,1,0); } static void stbte__layers(stbte_tilemap *tm, int x0, int y0, int w, int h) @@ -2521,7 +3026,7 @@ static void stbte__tile_in_palette(stbte_tilemap *tm, int x, int y, int slot) switch (stbte__ui.event) { case STBTE__paint: stbte__draw_rect(x,y,x+tm->palette_spacing_x-1,y+tm->palette_spacing_x-1, STBTE_COLOR_TILEPALETTE_BACKGROUND); - STBTE_DRAW_TILE(x,y,t->id, slot == tm->cur_tile); + STBTE_DRAW_TILE(x,y,t->id, slot == tm->cur_tile,0); if (slot == tm->cur_tile) stbte__draw_frame_delayed(x-1,y-1,x+tm->palette_spacing_x,y+tm->palette_spacing_y, STBTE_COLOR_TILEPALETTE_OUTLINE); break; @@ -2574,8 +3079,107 @@ 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; +static float stbte__linear_remap(float n, float x0, float x1, float y0, float y1) +{ + return (n-x0)/(x1-x0)*(y1-y0) + y0; +} +static float stbte__saved; +static void stbte__props_panel(stbte_tilemap *tm, int x0, int y0, int w, int h) +{ + int x1 = x0+w, y1 = y0+h; + int i; + int y = y0 + 5, x = x0+2; + int slider_width = 60; + int mx,my; + float *p; + short *data; + if (!stbte__is_single_selection()) + return; + mx = stbte__ui.select_x0; + my = stbte__ui.select_y0; + p = tm->props[my][mx]; + data = tm->data[my][mx]; + for (i=0; i < STBTE_MAX_PROP; ++i) { + unsigned int n = STBTE_PROP_TYPE(i, data, p); + if (n) { + char *s = STBTE_PROP_NAME(i, data, p); + if (s == NULL) s = ""; + switch (n & 3) { + case STBTE_PROP_bool: { + int flag = (int) p[i]; + if (stbte__layerbutton(x,y, flag ? 'x' : ' ', STBTE__ID(STBTE__prop_flag,i), flag, 0, 2)) { + stbte__begin_undo(tm); + stbte__undo_record_prop_float(tm,mx,my,i,flag); + p[i] = !flag; + stbte__end_undo(tm); + } + stbte__draw_text(x+13,y+1,s,x1-(x+13)-2,STBTE__TEXTCOLOR(STBTE__cpanel)); + y += 13; + break; + } + case STBTE_PROP_int: { + int a = (int) STBTE_PROP_MIN(i,data,p); + int b = (int) STBTE_PROP_MAX(i,data,p); + int v = (int) p[i] - a; + if (a+v != p[i] || v < 0 || v > b-a) { + if (v < 0) v = 0; + if (v > b-a) v = b-a; + p[i] = a+v; // @TODO undo + } + switch (stbte__slider(x, slider_width, y+7, b-a, &v, STBTE__ID(STBTE__prop_int,i))) + { + case STBTE__begin: + stbte__saved = p[i]; + // fallthrough + case STBTE__change: + p[i] = a+v; // @TODO undo + break; + case STBTE__end: + if (p[i] != stbte__saved) { + stbte__begin_undo(tm); + stbte__undo_record_prop_float(tm,mx,my,i,stbte__saved); + stbte__end_undo(tm); + } + break; + } + stbte__draw_text(x+slider_width+2,y+2, s, x1-1-(x+slider_width+2), STBTE__TEXTCOLOR(STBTE__cpanel)); + y += 12; + break; + } + case STBTE_PROP_float: { + float a = (float) STBTE_PROP_MIN(i, data,p); + float b = (float) STBTE_PROP_MAX(i, data,p); + float c = STBTE_PROP_FLOAT_SCALE(i, data, p); + float old; + if (p[i] < a || p[i] > b) { + // @TODO undo + if (p[i] < a) p[i] = a; + if (p[i] > b) p[i] = b; + } + old = p[i]; + switch (stbte__float_control(x, y, 50, a, b, c, "%8.4f", &p[i], STBTE__layer,STBTE__ID(STBTE__prop_float,i))) { + case STBTE__begin: + stbte__saved = old; + break; + case STBTE__end: + if (stbte__saved != p[i]) { + stbte__begin_undo(tm); + stbte__undo_record_prop_float(tm,mx,my,i, stbte__saved); + stbte__end_undo(tm); + } + break; + } + stbte__draw_text(x+53,y+1, s, x1-1-(x+53), STBTE__TEXTCOLOR(STBTE__cpanel)); + y += 12; + break; + } + } + } + } +} + +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) { @@ -2599,36 +3203,6 @@ static void stbte__dump_colorstate(void) 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; @@ -2652,7 +3226,8 @@ static void stbte__colorpicker(int x0, int y0, int w, int h) 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)); + if (stbte__slider(x+8,64, y, 255, rgb+i, STBTE__ID2(STBTE__colorpick_id,3,i)) > 0) + stbte__dump_colorstate(); y += 15; } if (stbte__ui.event != STBTE__paint && stbte__ui.event != STBTE__tick) @@ -2765,6 +3340,26 @@ static void stbte__editor_traverse(stbte_tilemap *tm) y1 = stbte__ui.y0 + (stbte__ui.select_y1 + 1) * tm->spacing_y - tm->scroll_y + 1; stbte__draw_frame(x0,y0,x1,y1, (stbte__ui.ms_time & 256 ? STBTE_COLOR_SELECTION_OUTLINE1 : STBTE_COLOR_SELECTION_OUTLINE2)); } + + stbte__flush_delay(); // draw a dynamic link on top of the queued links + + #ifdef STBTE_ALLOW_LINK + if (stbte__ui.linking && STBTE__IS_MAP_HOT()) { + int x0,y0,x1,y1; + int color; + int ex = ((stbte__ui.hot_id >> 19) & 4095); + int ey = ((stbte__ui.hot_id >> 7) & 4095); + x0 = stbte__ui.x0 + (stbte__ui.sx ) * tm->spacing_x - tm->scroll_x + (tm->spacing_x>>1)+1; + y0 = stbte__ui.y0 + (stbte__ui.sy ) * tm->spacing_y - tm->scroll_y + (tm->spacing_y>>1)+1; + x1 = stbte__ui.x0 + (ex ) * tm->spacing_x - tm->scroll_x + (tm->spacing_x>>1)-1; + y1 = stbte__ui.y0 + (ey ) * tm->spacing_y - tm->scroll_y + (tm->spacing_y>>1)-1; + if (STBTE_ALLOW_LINK(tm->data[stbte__ui.sy][stbte__ui.sx], tm->props[stbte__ui.sy][stbte__ui.sx], tm->data[ey][ex], tm->props[ey][ex])) + color = STBTE_LINK_COLOR_DRAWING; + else + color = STBTE_LINK_COLOR_DISALLOWED; + stbte__draw_link(x0,y0,x1,y1, color); + } + #endif } stbte__flush_delay(); @@ -2802,6 +3397,9 @@ static void stbte__editor_traverse(stbte_tilemap *tm) 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; + case STBTE__panel_props: + stbte__props_panel(tm,p->x0,p->y0,p->width,p->height); + break; } // draw the panel side selectors for (j=0; j < 2; ++j) { @@ -2890,6 +3488,7 @@ static void stbte__do_event(stbte_tilemap *tm) stbte__end_undo(tm); stbte__ui.scrolling = 0; stbte__ui.dragging = 0; + stbte__ui.linking = 0; } } } @@ -2917,6 +3516,12 @@ static void stbte__set_event(int event, int x, int y) stbte__ui.event = event; stbte__ui.mx = x; stbte__ui.my = y; + stbte__ui.dx = x - stbte__ui.last_mouse_x; + stbte__ui.dy = y - stbte__ui.last_mouse_y; + stbte__ui.last_mouse_x = x; + stbte__ui.last_mouse_y = y; + stbte__ui.accum_x += stbte__ui.dx; + stbte__ui.accum_y += stbte__ui.dy; } void stbte_draw(stbte_tilemap *tm)