diff options
| author | aura <nw@moneybot.cc> | 2026-03-17 12:04:30 +0100 |
|---|---|---|
| committer | day <day@national.shitposting.agency> | 2026-03-20 22:52:49 +0100 |
| commit | 34d410da50e76f12c3d011293f4b544330e8ba3e (patch) | |
| tree | c8335b5699dc40e5de6233891a3cbe154612a224 | |
| parent | 7cc07134a9759ed196b0fe5ea26c18d76e232952 (diff) | |
multi select
| -rw-r--r-- | src/editor/editor.cpp | 62 | ||||
| -rw-r--r-- | src/editor/editor.h | 12 | ||||
| -rw-r--r-- | src/editor/editor_infobox.cpp | 14 | ||||
| -rw-r--r-- | src/editor/properties.cpp | 221 | ||||
| -rw-r--r-- | src/editor/properties.h | 54 | ||||
| -rw-r--r-- | src/editor/view2d.cpp | 138 | ||||
| -rw-r--r-- | src/game/physics/movement.cpp | 2 | ||||
| -rw-r--r-- | src/gui/base.h | 5 | ||||
| -rw-r--r-- | src/gui/floatinput.cpp | 12 | ||||
| -rw-r--r-- | src/gui/vectorinput.cpp | 12 | ||||
| -rw-r--r-- | src/util/string.h | 48 | ||||
| -rw-r--r-- | src/util/typedef.h | 4 |
12 files changed, 412 insertions, 172 deletions
diff --git a/src/editor/editor.cpp b/src/editor/editor.cpp index ba61961..c96f908 100644 --- a/src/editor/editor.cpp +++ b/src/editor/editor.cpp @@ -31,7 +31,7 @@ void editor_clear_gui_state_refs( GAME_EDITOR* e ) { e->gui.header_viewtype = 0; } -static void editor_push_undo_action( GAME_EDITOR* e, const GAME_EDITOR::EDITOR_UNDO_ACTION& action ) { +void editor_push_undo_action( GAME_EDITOR* e, const GAME_EDITOR::EDITOR_UNDO_ACTION& action ) { if( !e ) return; @@ -39,7 +39,7 @@ static void editor_push_undo_action( GAME_EDITOR* e, const GAME_EDITOR::EDITOR_U e->undo_actions.push( action ); } -static void editor_refresh_after_map_change( GAME_EDITOR* e ) { +void editor_refresh_after_map_change( GAME_EDITOR* e ) { if( !e || !e->map ) return; @@ -54,13 +54,13 @@ static U8 editor_clr_eq( const CLR& a, const CLR& b ) { return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; } -static U8 editor_vertex_eq( const MAP_VERTEX& a, const MAP_VERTEX& b ) { +U8 editor_vertex_eq( const MAP_VERTEX& a, const MAP_VERTEX& b ) { return a.pos == b.pos && a.uv == b.uv && editor_clr_eq( a.clr, b.clr ); } -static U8 editor_wall_eq( const MAP_WALL& a, const MAP_WALL& b ) { +U8 editor_wall_eq( const MAP_WALL& a, const MAP_WALL& b ) { return a.start == b.start && a.end == b.end && a.uvstart == b.uvstart @@ -68,7 +68,7 @@ static U8 editor_wall_eq( const MAP_WALL& a, const MAP_WALL& b ) { && a.propid == b.propid; } -static U8 editor_poly_eq( const MAP_POLYGON& a, const MAP_POLYGON& b ) { +U8 editor_poly_eq( const MAP_POLYGON& a, const MAP_POLYGON& b ) { if( a.type != b.type || a.propid != b.propid ) return 0; if( !( a.mins == b.mins ) || !( a.maxs == b.maxs ) ) @@ -83,19 +83,19 @@ static U8 editor_poly_eq( const MAP_POLYGON& a, const MAP_POLYGON& b ) { return 1; } -static U8 editor_sprite_eq( const MAP_SPRITE& a, const MAP_SPRITE& b ) { +U8 editor_sprite_eq( const MAP_SPRITE& a, const MAP_SPRITE& b ) { return a.pos == b.pos && a.size == b.size && editor_clr_eq( a.clr, b.clr ) && a.tex == b.tex; } -static U8 editor_entity_eq( const MAP_ENTITY& a, const MAP_ENTITY& b ) { +U8 editor_entity_eq( const MAP_ENTITY& a, const MAP_ENTITY& b ) { return a.pos == b.pos && a.classid == b.classid; } -static I32 editor_clamp_valid_idx( I32 idx, I32 size ) { +I32 editor_clamp_valid_idx( I32 idx, I32 size ) { if( size <= 0 ) return -1; if( idx < 0 ) @@ -105,12 +105,12 @@ static I32 editor_clamp_valid_idx( I32 idx, I32 size ) { return idx; } -static I32 editor_idx_distance( I32 a, I32 b ) { +I32 editor_idx_distance( I32 a, I32 b ) { return a > b ? a - b : b - a; } template <typename MATCH_FN> -static I32 editor_find_nearest_idx( I32 size, I32 expected_idx, MATCH_FN match ) { +I32 editor_find_nearest_idx( I32 size, I32 expected_idx, MATCH_FN match ) { if( size <= 0 ) return -1; @@ -134,7 +134,7 @@ static I32 editor_find_nearest_idx( I32 size, I32 expected_idx, MATCH_FN match ) return best; } -static I32 editor_find_wall_idx( WORLD_MAP* map, const MAP_WALL& want, I32 expected_idx ) { +I32 editor_find_wall_idx( WORLD_MAP* map, const MAP_WALL& want, I32 expected_idx ) { if( !map ) return -1; @@ -143,7 +143,7 @@ static I32 editor_find_wall_idx( WORLD_MAP* map, const MAP_WALL& want, I32 expec } ); } -static I32 editor_find_poly_idx( WORLD_MAP* map, const MAP_POLYGON& want, I32 expected_idx ) { +I32 editor_find_poly_idx( WORLD_MAP* map, const MAP_POLYGON& want, I32 expected_idx ) { if( !map ) return -1; @@ -152,7 +152,7 @@ static I32 editor_find_poly_idx( WORLD_MAP* map, const MAP_POLYGON& want, I32 ex } ); } -static I32 editor_find_sprite_idx( WORLD_MAP* map, const MAP_SPRITE& want, I32 expected_idx ) { +I32 editor_find_sprite_idx( WORLD_MAP* map, const MAP_SPRITE& want, I32 expected_idx ) { if( !map ) return -1; @@ -161,7 +161,7 @@ static I32 editor_find_sprite_idx( WORLD_MAP* map, const MAP_SPRITE& want, I32 e } ); } -static I32 editor_find_entity_idx( WORLD_MAP* map, const MAP_ENTITY& want, I32 expected_idx ) { +I32 editor_find_entity_idx( WORLD_MAP* map, const MAP_ENTITY& want, I32 expected_idx ) { if( !map ) return -1; @@ -170,14 +170,9 @@ static I32 editor_find_entity_idx( WORLD_MAP* map, const MAP_ENTITY& want, I32 e } ); } -static void editor_mark_clear_wall_refs( GAME_EDITOR* e, MAP_WALL* w, U8* clear_props_select, U8* clear_view_select, U8* clear_view_drag ) { - if( e->gui.props ) { - if( e->gui.props->seltype == EDITOR_SELECT_WALL && e->gui.props->curselect == w ) - *clear_props_select = 1; - if( e->gui.props->seltype == EDITOR_SELECT_WVERTEX - && ( e->gui.props->curselect == &w->start || e->gui.props->curselect == &w->end ) ) - *clear_props_select = 1; - } +void editor_mark_clear_wall_refs( GAME_EDITOR* e, MAP_WALL* w, U8* clear_props_select, U8* clear_view_select, U8* clear_view_drag ) { + if( gui_editor_propview_is_selected( e->gui.props, w, EDITOR_SELECT_WALL ) ) + *clear_props_select = 1; if( e->gui.v2d ) { if( e->gui.v2d->seltype == EDITOR_SELECT_WALL && e->gui.v2d->curselect == w ) @@ -194,16 +189,12 @@ static void editor_mark_clear_wall_refs( GAME_EDITOR* e, MAP_WALL* w, U8* clear_ } } -static void editor_mark_clear_poly_refs( GAME_EDITOR* e, MAP_POLYGON* p, U8* clear_props_select, U8* clear_view_select, U8* clear_view_drag ) { - if( e->gui.props ) { - if( e->gui.props->seltype == EDITOR_SELECT_POLY && e->gui.props->curselect == p ) +void editor_mark_clear_poly_refs( GAME_EDITOR* e, MAP_POLYGON* p, U8* clear_props_select, U8* clear_view_select, U8* clear_view_drag ) { + if( gui_editor_propview_is_selected( e->gui.props, p, EDITOR_SELECT_POLY ) ) + *clear_props_select = 1; + for( auto& it : p->vertices ) { + if( gui_editor_propview_is_selected( e->gui.props, &it, EDITOR_SELECT_PVERTEX ) ) *clear_props_select = 1; - if( e->gui.props->seltype == EDITOR_SELECT_PVERTEX ) { - p->vertices.each( fn( MAP_VERTEX* v ) { - if( e->gui.props->curselect == v ) - *clear_props_select = 1; - } ); - } } if( e->gui.v2d ) { @@ -227,8 +218,9 @@ static void editor_mark_clear_poly_refs( GAME_EDITOR* e, MAP_POLYGON* p, U8* cle } } -static void editor_mark_clear_sprite_refs( GAME_EDITOR* e, MAP_SPRITE* s, U8* clear_props_select, U8* clear_view_select, U8* clear_view_drag ) { - if( e->gui.props && e->gui.props->seltype == EDITOR_SELECT_SPRITE && e->gui.props->curselect == s ) +void editor_mark_clear_sprite_refs( GAME_EDITOR* e, MAP_SPRITE* s, U8* clear_props_select, U8* clear_view_select, U8* clear_view_drag ) { + // todo : only clear the selected item + if( gui_editor_propview_is_selected( e->gui.props, s, EDITOR_SELECT_SPRITE ) ) *clear_props_select = 1; if( e->gui.v2d ) { @@ -239,8 +231,8 @@ static void editor_mark_clear_sprite_refs( GAME_EDITOR* e, MAP_SPRITE* s, U8* cl } } -static void editor_mark_clear_entity_refs( GAME_EDITOR* e, MAP_ENTITY* ent, U8* clear_props_select, U8* clear_view_select, U8* clear_view_drag ) { - if( e->gui.props && e->gui.props->seltype == EDITOR_SELECT_ENT && e->gui.props->curselect == ent ) +void editor_mark_clear_entity_refs( GAME_EDITOR* e, MAP_ENTITY* ent, U8* clear_props_select, U8* clear_view_select, U8* clear_view_drag ) { + if( gui_editor_propview_is_selected( e->gui.props, ent, EDITOR_SELECT_ENT ) ) *clear_props_select = 1; if( e->gui.v2d ) { diff --git a/src/editor/editor.h b/src/editor/editor.h index 33396df..4050029 100644 --- a/src/editor/editor.h +++ b/src/editor/editor.h @@ -264,9 +264,13 @@ struct GUI_EDITOR_2DVIEW : GUI_VIEW { }; struct GUI_EDITOR_PROPVIEW : GUI_VIEW { - void* curselect; - U8 seltype; + struct PROPVIEW_SELECT { + void* obj; + U8 seltype; + }; + LIST<PROPVIEW_SELECT> curselect; + LIST<EDITOR_PROP*> curprops; GUI_VIEW* itemview; }; @@ -299,6 +303,7 @@ struct GUI_EDITOR_TEXTUREPICKER : GUI_WINDOW { GUI_LABEL* densitylabel; GUI_CALLBACK cb; + void* cbextra; }; @@ -330,7 +335,8 @@ extern GUI_EDITORWINDOW* gui_editorwindow( I32 w, I32 h ); extern GUI_EDITOR_2DVIEW* gui_editor_2dview( I32 x, I32 y, I32 w, I32 h ); extern GUI_EDITOR_3DVIEW* gui_editor_3dview( I32 x, I32 y, I32 w, I32 h ); extern GUI_EDITOR_PROPVIEW* gui_editor_propview( I32 x, I32 y, I32 w, I32 h ); -extern void gui_editor_propview_select( GUI_EDITOR_PROPVIEW* e, void* what, U8 seltype ); +extern void gui_editor_propview_select( GUI_EDITOR_PROPVIEW* e, void* what, U8 seltype, U8 clearall = 1 ); +extern U8 gui_editor_propview_is_selected( GUI_EDITOR_PROPVIEW* e, void* what, U8 seltype ); extern void gui_editor_propview_update( GUI_EDITOR_PROPVIEW* e ); extern GUI_EDITOR_TOOLVIEW* gui_editor_toolview( I32 x, I32 y, I32 w, I32 h ); extern void gui_editor_toolview_update( GUI_EDITOR_TOOLVIEW* view ); diff --git a/src/editor/editor_infobox.cpp b/src/editor/editor_infobox.cpp index 479b59d..173e849 100644 --- a/src/editor/editor_infobox.cpp +++ b/src/editor/editor_infobox.cpp @@ -70,12 +70,14 @@ static void gui_editor_infobox_draw_assets( GUI_EDITOR_INFOBOX* box, I32 panel_x SURF_PROPS* p = map_entry ? 0 : &map->props[idx - 1]; U8 selected = 0; if( editor->gui.props ) { - if( map_entry ) { - selected = editor->gui.props->seltype == EDITOR_SELECT_ORIGIN - && editor->gui.props->curselect == editor->map; - } else { - selected = editor->gui.props->seltype == EDITOR_SELECT_SURFPROPS - && editor->gui.props->curselect == p; + if( editor->gui.props->curselect.size == 1 ) { + if( map_entry ) { + selected = editor->gui.props->curselect.data->seltype == EDITOR_SELECT_ORIGIN + && editor->gui.props->curselect.data->obj == editor->map; + } else { + selected = editor->gui.props->curselect.data->seltype == EDITOR_SELECT_SURFPROPS + && editor->gui.props->curselect.data->obj == p; + } } } diff --git a/src/editor/properties.cpp b/src/editor/properties.cpp index c9eb991..0cc0e56 100644 --- a/src/editor/properties.cpp +++ b/src/editor/properties.cpp @@ -45,30 +45,106 @@ I32 propview_action2_x( GUI_EDITOR_PROPVIEW* view ) { ); } -void gui_editor_propview_select( GUI_EDITOR_PROPVIEW* view, void* what, U8 seltype ) { +void gui_editor_propview_select( GUI_EDITOR_PROPVIEW* view, void* what, U8 seltype, U8 clearall ) { + using PROPSELECT = GUI_EDITOR_PROPVIEW::PROPVIEW_SELECT; if( !editor->map ) return; + view->curprops.clear(); + if( clearall ) + view->curselect.clear(); + + PROPSELECT sel{ + .obj = what, + .seltype = seltype + }; + if( seltype == EDITOR_SELECT_WVERTEX ) { MAP_WALL* s = editor->map->walls.where( fn( MAP_WALL* s ) { return ( &s->start == what || &s->end == what ); } ); if( !s ) - return gui_editor_propview_select( view, editor->map, EDITOR_SELECT_ORIGIN ); + return gui_editor_propview_select( view, editor->map, EDITOR_SELECT_ORIGIN, 1 ); - view->curselect = s; - view->seltype = EDITOR_SELECT_WALL; - return gui_editor_propview_update( view ); + sel.obj = s; + sel.seltype = EDITOR_SELECT_WALL; } - view->curselect = what; - view->seltype = seltype; + if( view->curselect.idx_where( fn( PROPSELECT* p ) { return p->obj == sel.obj; } ) != -1 ) + return; + + view->curselect.push( sel ); gui_editor_propview_update( view ); } +U8 gui_editor_propview_is_selected( GUI_EDITOR_PROPVIEW *e, void *what, U8 seltype ) { + for( auto& it : e->curselect ) { + if( it.obj == what && it.seltype == seltype ) + return 1; + } + return 0; +} + +void gui_editor_propview_sync_props_value( GUI_EDITOR_PROPVIEW* view, EDITOR_PROP* prop ) { + void* valuep = eprop_ptr( prop ); + for( auto& it : view->curselect ) { + EOBJECT* eobj = (EOBJECT*)it.obj; + EDITOR_PROP* otherp = eprop_from_name( eobj, prop->displayname ); + if( otherp == prop ) continue; + + if( otherp && otherp->type == prop->type ) { + void* targetp = eprop_ptr( otherp ); + if( targetp != valuep ) + memcpy( targetp, valuep, prop->size ); + } + } +} + +void gui_editor_propview_sync_props_value( GUI_EDITOR_PROPVIEW* view, const char* propname ) { + if( view->curselect.size < 2 ) return; + EDITOR_PROP** pptr = view->curprops.where( fn( EDITOR_PROP** pptr ) { return (*pptr)->displayname == propname; } ); + if( !pptr ) + return; + gui_editor_propview_sync_props_value( view, *pptr ); +} + +void gui_editor_propview_sync_props_fdiff( GUI_EDITOR_PROPVIEW* view, EDITOR_PROP* prop, F32* fdiff, U32 valc ) { + for( auto& it : view->curselect ) { + EOBJECT* eobj = (EOBJECT*)it.obj; + EDITOR_PROP* otherp = eprop_from_name( eobj, prop->displayname ); + if( otherp == prop ) continue; + + if( otherp && otherp->type == prop->type ) { + F32* targetp = (F32*)eprop_ptr( otherp ); + if( targetp == eprop_ptr( prop ) ) + continue; + + for( U32 i = 0; i < valc; ++i ) + targetp[i] += fdiff[i]; + } + } +} + +void gui_editor_propview_sync_props_fdiff( GUI_EDITOR_PROPVIEW* view, const char* propname, F32* fdiff, U32 valc ) { + if( view->curselect.size < 2 ) return; + EDITOR_PROP** pptr = view->curprops.where( fn( EDITOR_PROP** pptr ) { return (*pptr)->displayname == propname; } ); + if( !pptr ) + return; + + gui_editor_propview_sync_props_fdiff( view, *pptr, fdiff, valc ); +} + +// todo : int diff + +struct TEXBTN_CB { + EDITOR_PROP* prop; + GL_TEX2D** texp; +}; + +void gui_editor_propview_create_eobj_prop_ro( GUI_EDITOR_PROPVIEW* view, EDITOR_PROP* prop, I32* x, I32* y, I32 w, I32 space ); void gui_editor_propview_create_eobj_props( GUI_EDITOR_PROPVIEW* view, EOBJECT* e, I32* x, I32* y, I32 w ); void gui_editor_propview_create_eobj_prop( GUI_EDITOR_PROPVIEW* view, EDITOR_PROP* prop, I32* x, I32* y, I32 w, I32 space ) { F32 action_x = propview_action_x( view ); @@ -82,11 +158,29 @@ void gui_editor_propview_create_eobj_prop( GUI_EDITOR_PROPVIEW* view, EDITOR_PRO else step = editor->propgrid ? editor->grid : 0.25f; switch( prop->type ) { - case EPROP_F32: gui_floatinput( *x, *y, w, n, (F32*)eprop_ptr( prop ), min, max, step ); *y += (space+18); break; - case EPROP_VEC2: gui_vectorinput( *x, *y, w, n, (F32*)eprop_ptr( prop ), 2, min, max, step, "xy", "%.03f" ); *y += (space+18); break; - case EPROP_VEC3: gui_vectorinput( *x, *y, w, n, (F32*)eprop_ptr( prop ), 3, min, max, step, "xyz", "%.02f" ); *y += (space+18); break; - case EPROP_VEC4: gui_vectorinput( *x, *y, w, n, (F32*)eprop_ptr( prop ), 3, min, max, step, "xyzw", "%0.02f" ); *y += (space+18); break; - case EPROP_CLR: gui_colorinput( *x, *y, w, n, (CLR*)eprop_ptr( prop ) ); *y += (space+18); break; + case EPROP_F32: { GUI_FLOATINPUT* in = gui_floatinput( *x, *y, w, n, (F32*)eprop_ptr( prop ), min, max, step ); + in->cb = pfn( void* ptr ) { + GUI_FLOATINPUT* fin = (GUI_FLOATINPUT*)ptr; + gui_editor_propview_sync_props_fdiff( editor->gui.props, (EDITOR_PROP*)fin->cbextra, &fin->lastchange, 1 ); + }; in->cbextra = prop; + *y += (space+18); + } break; + case EPROP_VEC2: case EPROP_VEC3: case EPROP_VEC4: { U32 c = prop->type - EPROP_VEC2 + 2; + GUI_VECTORINPUT* in = gui_vectorinput( *x, *y, w, n, (F32*)eprop_ptr( prop ), c, min, max, step, "xyzw", c == 2 ? "%.03f" : "%0.02f" ); + in->cb = pfn( void* ptr ) { + GUI_VECTORINPUT* vin = (GUI_VECTORINPUT*)ptr; + gui_editor_propview_sync_props_fdiff( editor->gui.props, (EDITOR_PROP*)vin->cbextra, vin->lastchange.data, vin->inputs.size ); + }; in->cbextra = prop; + + *y += (space+18); + } break; + case EPROP_CLR: { GUI_COLORINPUT* cin = gui_colorinput( *x, *y, w, n, (CLR*)eprop_ptr( prop ) ); + cin->cb = pfn( void* ptr ) { + GUI_COLORINPUT* cin = (GUI_COLORINPUT*)ptr; + gui_editor_propview_sync_props_fdiff( editor->gui.props, (EDITOR_PROP*)cin->cbextra, cin->lastchange.data, cin->inputs.size ); + }; cin->cbextra = prop; + *y += (space+18); + }break; case EPROP_MAPPROP: { MAP_PROPREF* ref = (MAP_PROPREF*)eprop_ptr( prop ); SURF_PROPS* props = map_get_props( editor->map, *ref ); @@ -96,11 +190,14 @@ void gui_editor_propview_create_eobj_prop( GUI_EDITOR_PROPVIEW* view, EDITOR_PRO GUI_BUTTON* btn = (GUI_BUTTON*)ptr; SURF_PROPS props{ .tex = 0, .clr = CLR::WHITE() }; editor->map->props.push( props ); + EDITOR_PROP* eprop = (EDITOR_PROP*)btn->extra; + MAP_PROPREF* ref = (MAP_PROPREF*)eprop_ptr( eprop ); - ((MAP_PROPREF*)btn->extra)->id = editor->map->props.size - 1; + (ref)->id = editor->map->props.size - 1; editor->gui.assets_scroll = ( editor->map->props.size + 1 ) * 28; + gui_editor_propview_sync_props_value( editor->gui.props, eprop ); gui_editor_propview_update( editor->gui.props ); - } ); newprop->extra = ref; + } ); newprop->extra = prop; GUI_BUTTON* goprop = gui_button( action_x, *y, 20, 20, "\x1A", pfn( void* ptr ) { GUI_BUTTON* btn = (GUI_BUTTON*)ptr; @@ -113,11 +210,15 @@ void gui_editor_propview_create_eobj_prop( GUI_EDITOR_PROPVIEW* view, EDITOR_PRO GL_TEX2D** tex = (GL_TEX2D**)eprop_ptr( prop ); if( *tex ) gui_label( *x, *y, "%s: %s", n, (*tex)->name ); else gui_label( *x, *y, "%s: none", n ); - GUI_BUTTON* btn = gui_button( action_x, *y, 20, 20, "\x1A", pfn( void* ptr ) { + GUI_BUTTON* btn = gui_button( action_x, *y, 20, 20, "\x1A", cfn( void* ptr ) { GUI_BUTTON* btn = (GUI_BUTTON*)ptr; GL_TEX2D** ptex = (GL_TEX2D**)btn->extra; GUI_EDITOR_TEXTUREPICKER* picker = gui_editor_texturepicker( 200, 100, 400, 400, ptex ); - picker->cb = pfn( void* ) { gui_editor_propview_update( editor->gui.props ); }; + picker->cb = pfn( void* p ) { + GUI_EDITOR_TEXTUREPICKER* picker = (GUI_EDITOR_TEXTUREPICKER*)p; + gui_editor_propview_sync_props_value( editor->gui.props, (EDITOR_PROP*)picker->cbextra ); + gui_editor_propview_update( editor->gui.props ); + }; picker->cbextra = prop; } ); btn->extra = tex; *y += space; } break; case EPROP_VERTEX_LIST: { @@ -132,7 +233,7 @@ void gui_editor_propview_create_eobj_prop( GUI_EDITOR_PROPVIEW* view, EDITOR_PRO } ); btn->extra = v; *y += space; } } break; - default: break; + default: return gui_editor_propview_create_eobj_prop_ro( view, prop, x, y, w, space ); break; } } @@ -152,6 +253,7 @@ void gui_editor_propview_create_eobj_prop_ro( GUI_EDITOR_PROPVIEW* view, EDITOR_ case EPROP_I32: str += to_str( *(I32*)ptr ); break; case EPROP_I64: str += to_str( *(I64*)ptr ); break; case EPROP_STRING: str += to_str( *(CLR*)ptr ); break; + default: str += STR("UNIMPL_TYPE : ") + to_str( prop->type ); return; } str += "]"; @@ -172,7 +274,7 @@ void gui_editor_propview_create_eobj_props( GUI_EDITOR_PROPVIEW* view, EOBJECT* } void gui_editor_propview_create_wallprops( GUI_EDITOR_PROPVIEW* view ) { - MAP_WALL* s = (MAP_WALL*)view->curselect; + MAP_WALL* s = (MAP_WALL*)view->curselect.data[0].obj; WORLD_MAP* m = editor->map; I32 x = 10, y = 10; @@ -187,7 +289,7 @@ void gui_editor_propview_create_wallprops( GUI_EDITOR_PROPVIEW* view ) { } void gui_editor_propview_create_polyprops( GUI_EDITOR_PROPVIEW* view ) { - MAP_POLYGON* p = (MAP_POLYGON*)view->curselect; + MAP_POLYGON* p = (MAP_POLYGON*)view->curselect.data[0].obj; WORLD_MAP* m = editor->map; I32 x = 10, y = 10; I32 space = 20; @@ -201,7 +303,7 @@ void gui_editor_propview_create_polyprops( GUI_EDITOR_PROPVIEW* view ) { } void gui_editor_propview_create_mapprops( GUI_EDITOR_PROPVIEW* view ) { - WORLD_MAP* m = (WORLD_MAP*)view->curselect; + WORLD_MAP* m = (WORLD_MAP*)view->curselect.data[0].obj; I32 x = 10, y = 10; I32 space = 20; @@ -236,7 +338,7 @@ void gui_editor_propview_create_mapprops( GUI_EDITOR_PROPVIEW* view ) { } void gui_editor_propview_create_pvertexprops( GUI_EDITOR_PROPVIEW* view ) { - MAP_VERTEX* v = (MAP_VERTEX*)view->curselect; + MAP_VERTEX* v = (MAP_VERTEX*)view->curselect.data[0].obj; WORLD_MAP* m = editor->map; I32 x = 10, y = 10; @@ -254,7 +356,7 @@ void gui_editor_propview_create_pvertexprops( GUI_EDITOR_PROPVIEW* view ) { gui_label( x, y, "polygon idx: %d", poly_idx ); gui_button( action_x, y - 2, 20, 20, "\x1A", pfn( void* ) { GUI_EDITOR_PROPVIEW* view = editor->gui.props; - MAP_VERTEX* v = (MAP_VERTEX*)view->curselect; + MAP_VERTEX* v = (MAP_VERTEX*)view->curselect.data[0].obj; WORLD_MAP* m = editor->map; MAP_POLYGON* polygon = m->polygons.where( fn( MAP_POLYGON* p ) { I32 vert_idx = p->vertices.idx_where( fn( MAP_VERTEX* pv ) { return (pv == v); } ); @@ -270,7 +372,7 @@ void gui_editor_propview_create_pvertexprops( GUI_EDITOR_PROPVIEW* view ) { void gui_editor_propview_create_spriteprops( GUI_EDITOR_PROPVIEW* view ) { - MAP_SPRITE* s = (MAP_SPRITE*)view->curselect; + MAP_SPRITE* s = (MAP_SPRITE*)view->curselect.data[0].obj; WORLD_MAP* m = editor->map; I32 x = 10, y = 10; @@ -285,7 +387,7 @@ void gui_editor_propview_create_spriteprops( GUI_EDITOR_PROPVIEW* view ) { } void gui_editor_propview_create_surfprops( GUI_EDITOR_PROPVIEW* view ) { - SURF_PROPS* p = (SURF_PROPS*)view->curselect; + SURF_PROPS* p = (SURF_PROPS*)view->curselect.data[0].obj; WORLD_MAP* m = editor->map; I32 x = 10, y = 10; @@ -300,7 +402,7 @@ void gui_editor_propview_create_surfprops( GUI_EDITOR_PROPVIEW* view ) { } void gui_editor_propview_create_entprops( GUI_EDITOR_PROPVIEW* view ) { - MAP_ENTITY* e = (MAP_ENTITY*)view->curselect; + MAP_ENTITY* e = (MAP_ENTITY*)view->curselect.data[0].obj; WORLD_MAP* m = editor->map; I32 x = 10, y = 10; @@ -317,6 +419,41 @@ void gui_editor_propview_create_entprops( GUI_EDITOR_PROPVIEW* view ) { gui_vectorinput( x, y, propview_input_width( view ), "position", (F32*)&e->pos, 3, -INFINITY, INFINITY, step ); y += (space+18); } +void gui_editor_propview_filter_props( GUI_EDITOR_PROPVIEW* view ) { + if( view->curselect.size < 2 ) return view->curprops.clear(); + + EOBJECT* first = (EOBJECT*)view->curselect.data[0].obj; + for( auto& it : first->eprops ) { + U8 add = 1; + EDITOR_PROP* prop = eprop_from_ref( first, it ); + for( U32 i = 1; i < view->curselect.size; ++i ) { + EOBJECT* obj = (EOBJECT*)view->curselect.data[i].obj; + if( !eprop_from_name( obj, prop->displayname ) ) { + add = 0; + break; + } + } + + if( add ) + view->curprops.push( prop ); + } +} + +void gui_editor_propview_create_groupprops( GUI_EDITOR_PROPVIEW* view ) { + if( !view->curprops.size ) + gui_editor_propview_filter_props( view ); + + I32 x = 10, y = 10, w = propview_input_width( view ); + U32 space = 20; + + for( auto& it : view->curprops ) { + if( it->readonly ) + gui_editor_propview_create_eobj_prop_ro( view, it, &x, &y, w, space ); + else + gui_editor_propview_create_eobj_prop( view, it, &x, &y, w, space ); + } +} + void gui_editor_propview_update( GUI_EDITOR_PROPVIEW* view ) { if( !editor->map ) return; @@ -327,20 +464,31 @@ void gui_editor_propview_update( GUI_EDITOR_PROPVIEW* view ) { gui_set_view( view->itemview ); view->itemview->initheld = 1; - switch( view->seltype ) { - case EDITOR_SELECT_WALL: gui_editor_propview_create_wallprops( view ); break; - case EDITOR_SELECT_POLY: gui_editor_propview_create_polyprops( view ); break; - case EDITOR_SELECT_ORIGIN: gui_editor_propview_create_mapprops( view ); break; - case EDITOR_SELECT_ENT: gui_editor_propview_create_entprops( view ); break; - case EDITOR_SELECT_PVERTEX: gui_editor_propview_create_pvertexprops( view ); break; - case EDITOR_SELECT_SPRITE: gui_editor_propview_create_spriteprops( view ); break; - case EDITOR_SELECT_SURFPROPS: gui_editor_propview_create_surfprops( view ); break; - default: break; + if( !view->curselect.size ) + return; + + if( view->curselect.size > 1 ) { + gui_editor_propview_create_groupprops( view ); + } else { + switch( view->curselect.data->seltype ) { + case EDITOR_SELECT_WALL: gui_editor_propview_create_wallprops( view ); break; + case EDITOR_SELECT_POLY: gui_editor_propview_create_polyprops( view ); break; + case EDITOR_SELECT_ORIGIN: gui_editor_propview_create_mapprops( view ); break; + case EDITOR_SELECT_ENT: gui_editor_propview_create_entprops( view ); break; + case EDITOR_SELECT_PVERTEX: gui_editor_propview_create_pvertexprops( view ); break; + case EDITOR_SELECT_SPRITE: gui_editor_propview_create_spriteprops( view ); break; + case EDITOR_SELECT_SURFPROPS: gui_editor_propview_create_surfprops( view ); break; + default: break; + } } } void gui_editor_propview_get_title( GUI_EDITOR_PROPVIEW* view, char* buf ) { - switch( view->seltype ) { + if( view->curselect.size > 1 ) { + sprintf( buf, "group properties" ); return; + } + + switch( view->curselect.data->seltype ) { case EDITOR_SELECT_NONE: sprintf( buf, "properties" ); break; case EDITOR_SELECT_POLY: sprintf( buf, "polygon properties" ); break; case EDITOR_SELECT_ORIGIN: sprintf( buf, "properties" ); break; @@ -389,8 +537,7 @@ GUI_EDITOR_PROPVIEW* gui_editor_propview( I32 x, I32 y, I32 w, I32 h ) { view->draw_fn = gui_editor_propview_draw_fn; view->input_fn = gui_base_input_fn; - view->curselect = 0; - view->seltype = 0; + view->curselect = {}; GUI_VIEW* parent = gui_get_view(); parent->children.push( view ); diff --git a/src/editor/properties.h b/src/editor/properties.h index d663a6b..ef59b0d 100644 --- a/src/editor/properties.h +++ b/src/editor/properties.h @@ -5,14 +5,15 @@ #if IS_EDITOR #include <math.h> #include "../util/string.h" +#include "../util/fnv.h" #define EPROP( _type, name, value, display ) \ _type name = value; \ - EDITOR_PROP name##_prop{ &name, eprop_type<_type>(), display, this, 0 } + EDITOR_PROP name##_prop{ &name, eprop_type<_type>(), display, this, sizeof(_type), 0 } #define EPROP_RO( _type, name, value, display ) \ _type name = value; \ - EDITOR_PROP name##_prop{ &name, eprop_type<_type>(), display, this, 1 } + EDITOR_PROP name##_prop{ &name, eprop_type<_type>(), display, this, sizeof(_type), 1 } #define EPROP_RANGED( _type, name, value, display, _min, _max ) \ _type name = value; \ @@ -23,6 +24,7 @@ this, \ _min, \ _max, \ + sizeof(_type), \ 0 \ } @@ -34,6 +36,7 @@ display, \ step, \ this, \ + sizeof(_type), \ 0 \ } @@ -47,6 +50,7 @@ this, \ min, \ max, \ + sizeof(_type), \ 0 \ } @@ -90,13 +94,15 @@ concept __eobject_base = __is_base_of(EOBJECT, T); // editor object property struct EDITOR_PROP { - EDITOR_PROP( void* _pdata, U8 _type, STR _displayname, struct EOBJECT* _parent, U8 _readonly = 0 ) { + EDITOR_PROP( void* _pdata, U8 _type, STR _displayname, struct EOBJECT* _parent, U32 _size, U8 _readonly = 0 ) { type = _type; displayname = _displayname; min = -INFINITY; max = INFINITY; step = 0; readonly = _readonly; + hash = fnv1a( _displayname ); + size = _size; U64 _this = (U64)this; U64 pdata = (U64)_pdata; @@ -109,13 +115,15 @@ struct EDITOR_PROP { dataoff = offset; } - EDITOR_PROP( void* _pdata, U8 _type, STR _displayname, F32 _step, struct EOBJECT* _parent, U8 _readonly = 0 ) { + EDITOR_PROP( void* _pdata, U8 _type, STR _displayname, F32 _step, struct EOBJECT* _parent, U32 _size, U8 _readonly = 0 ) { type = _type; displayname = _displayname; min = -INFINITY; max = INFINITY; step = _step; readonly = _readonly; + hash = fnv1a( _displayname ); + size = _size; U64 _this = (U64)this; U64 pdata = (U64)_pdata; @@ -128,13 +136,15 @@ struct EDITOR_PROP { dataoff = offset; } - EDITOR_PROP( void* _pdata, U8 _type, STR _displayname, struct EOBJECT* _parent, F32 _min, F32 _max, U8 _readonly = 0 ) { + EDITOR_PROP( void* _pdata, U8 _type, STR _displayname, struct EOBJECT* _parent, F32 _min, F32 _max, U32 _size, U8 _readonly = 0 ) { type = _type; displayname = _displayname; min = _min; max = _max; step = 0; readonly = _readonly; + hash = fnv1a( _displayname ); + size = _size; U64 _this = (U64)this; U64 pdata = (U64)_pdata; @@ -147,13 +157,15 @@ struct EDITOR_PROP { dataoff = offset; } - EDITOR_PROP( void* _pdata, U8 _type, STR _displayname, F32 _step, struct EOBJECT* _parent, F32 _min, F32 _max, U8 _readonly = 0 ) { + EDITOR_PROP( void* _pdata, U8 _type, STR _displayname, F32 _step, struct EOBJECT* _parent, F32 _min, F32 _max, U32 _size, U8 _readonly = 0 ) { type = _type; displayname = _displayname; min = _min; max = _max; step = _step; readonly = _readonly; + hash = fnv1a( _displayname ); + size = _size; U64 _this = (U64)this; U64 pdata = (U64)_pdata; @@ -173,6 +185,8 @@ struct EDITOR_PROP { F64 max{ INFINITY }; F32 step; U8 readonly; + FNV1A hash; + U32 size; template <typename T> struct __eprop_type { static const U8 type = EPROP_INVALID; @@ -209,6 +223,18 @@ inline void* eprop_ptr( EDITOR_PROP* prop ) { return (void*)( (U64)prop - prop->dataoff); } +inline EDITOR_PROP* eprop_from_name( EOBJECT* obj, STR name ) { + FNV1A fnv = fnv1a( name ); + + for( auto& it : obj->eprops ) { + EDITOR_PROP* prop = eprop_from_ref( obj, it ); + if( fnv == prop->hash ) + return prop; + } + + return 0; +} + template <typename T> const U8 eprop_type() { return EDITOR_PROP::__eprop_type<T>::type; @@ -216,6 +242,18 @@ const U8 eprop_type() { #else struct EOBJECT {}; -#define EPROP( type, name, value, displayname ) \ - type name{ value }; +#define EPROP( _type, name, value, display ) \ + _type name = value; + +#define EPROP_RO( _type, name, value, display ) \ + _type name = value; + +#define EPROP_RANGED( _type, name, value, display, _min, _max ) \ + _type name = value; + +#define EPROP_STEP( _type, name, value, display, step ) \ + _type name = value; + +#define EPROP_RANGED_STEP( _type, name, value, display, min, max, step ) \ + _type name = value; #endif diff --git a/src/editor/view2d.cpp b/src/editor/view2d.cpp index f1b5b17..8bcd588 100644 --- a/src/editor/view2d.cpp +++ b/src/editor/view2d.cpp @@ -1,4 +1,5 @@ #include <math.h> +#include "SDL_scancode.h" #include "editor.h" #include "../render/gl_2d.h" #include "../game/object.h" @@ -187,13 +188,15 @@ void gui_editor_2dview_select( GUI_EDITOR_2DVIEW* view, void* what, U8 seltype ) U8 gui_editor_2dview_is_gizmo_active( GUI_EDITOR_2DVIEW* view, void* what, U8 seltype ) { GUI_EDITOR_PROPVIEW* props = editor->gui.props; - if( props->seltype == EDITOR_SELECT_WALL && (seltype == EDITOR_SELECT_WVERTEX) ) { - MAP_WALL* s = (MAP_WALL*)props->curselect; - if( what == &s->start || what == &s->end ) + for( auto& it : props->curselect ) { + if( it.seltype == EDITOR_SELECT_WALL && (seltype == EDITOR_SELECT_WVERTEX) ) { + MAP_WALL* s = (MAP_WALL*)it.obj; + if( what == &s->start || what == &s->end ) + return 1; + } + else if( it.seltype == seltype && it.obj == what ) { return 1; - } - else if( props->seltype == seltype && props->curselect == what ) { - return 1; + } } if( view->curdrag ) { @@ -830,9 +833,11 @@ void gui_editor_2dview_input_select_onmove( GUI_EDITOR_2DVIEW* view ) { GUI_EDITOR_PROPVIEW* props = editor->gui.props; // special case for dragging wall vertices, just always update - U8 iswallv = props->seltype == EDITOR_SELECT_WALL && view->dragtype == EDITOR_SELECT_WVERTEX; - if( props->curselect == view->curdrag || iswallv ) - gui_editor_propview_update( editor->gui.props ); + for( auto& it : props->curselect ) { + U8 iswallv = it.seltype == EDITOR_SELECT_WALL && view->dragtype == EDITOR_SELECT_WVERTEX; + if( it.obj == view->curdrag || iswallv ) + gui_editor_propview_update( editor->gui.props ); + } } void gui_editor_2dview_input_select_drag_wall( GUI_EDITOR_2DVIEW* view ) { @@ -955,7 +960,7 @@ void gui_editor_2dview_input_select_drag_origin( GUI_EDITOR_2DVIEW* view ) { void gui_editor_2dview_input_select_ondrop( GUI_EDITOR_2DVIEW* view ) { if( !view->dragmoved ) - gui_editor_propview_select( editor->gui.props, view->curdrag, view->dragtype ); + gui_editor_propview_select( editor->gui.props, view->curdrag, view->dragtype, !input.keys[SDL_SCANCODE_LCTRL] ); view->curdrag = 0; } @@ -1389,76 +1394,77 @@ void gui_editor_2dview_input_tool_draw( GUI_EDITOR_2DVIEW* view ) { } void gui_editor_view2d_delete_obj( GUI_EDITOR_2DVIEW* view ) { - void* it; - U8 type; - + LIST<GUI_EDITOR_PROPVIEW::PROPVIEW_SELECT> sel{}; if( view->curdrag && view->dragtype ) { - it = view->curdrag; - type = view->dragtype; + sel.push( { + .obj = view->curdrag, + .seltype = view->dragtype + } ); view->curdrag = 0; view->dragtype = EDITOR_SELECT_NONE; - } else if( editor->gui.props->curselect ) { - it = editor->gui.props->curselect; - type = editor->gui.props->seltype; + } else if( editor->gui.props->curselect.size ) { view->curselect = 0; view->seltype = EDITOR_SELECT_NONE; + sel = editor->gui.props->curselect; + editor->gui.props->curselect.clear(); + + gui_editor_propview_select( editor->gui.props, 0, 0 ); } else return; - U8 cleared = 1; - switch( type ) { - case EDITOR_SELECT_POLY: { - I32 idx = editor->map->polygons.idx_of( (MAP_POLYGON*)it ); - if( idx != -1 ) - editor->map->polygons.erase( idx ); - } break; - case EDITOR_SELECT_WALL: { - I32 idx = editor->map->walls.idx_of( (MAP_WALL*)it ); - if( idx != -1 ) - editor->map->walls.erase( idx ); - }; break; - case EDITOR_SELECT_SPRITE: { - I32 idx = editor->map->sprites.idx_of( (MAP_SPRITE*)it ); - if( idx != -1 ) - editor->map->sprites.erase( idx ); - }; break; - case EDITOR_SELECT_ENT: { - I32 idx = editor->map->entities.idx_of( (MAP_ENTITY*)it ); - if( idx != -1 ) - editor->map->entities.erase( idx ); - }; break; - case EDITOR_SELECT_PVERTEX: { - I32 vidx = -1, idx = editor->map->polygons.idx_where( fn( MAP_POLYGON* p ) { - vidx = p->vertices.idx_where( fn( MAP_VERTEX* v ) { - return v == it; + for( auto& _it: sel ) { + void* it = _it.obj; + U8 type = _it.seltype; + switch( type ) { + case EDITOR_SELECT_POLY: { + I32 idx = editor->map->polygons.idx_of( (MAP_POLYGON*)it ); + if( idx != -1 ) + editor->map->polygons.erase( idx ); + } break; + case EDITOR_SELECT_WALL: { + I32 idx = editor->map->walls.idx_of( (MAP_WALL*)it ); + if( idx != -1 ) + editor->map->walls.erase( idx ); + }; break; + case EDITOR_SELECT_SPRITE: { + I32 idx = editor->map->sprites.idx_of( (MAP_SPRITE*)it ); + if( idx != -1 ) + editor->map->sprites.erase( idx ); + }; break; + case EDITOR_SELECT_ENT: { + I32 idx = editor->map->entities.idx_of( (MAP_ENTITY*)it ); + if( idx != -1 ) + editor->map->entities.erase( idx ); + }; break; + case EDITOR_SELECT_PVERTEX: { + I32 vidx = -1, idx = editor->map->polygons.idx_where( fn( MAP_POLYGON* p ) { + vidx = p->vertices.idx_where( fn( MAP_VERTEX* v ) { + return v == it; + } ); + return vidx != -1; } ); - return vidx != -1; - } ); - if( idx != -1 && vidx != -1 ) { - MAP_POLYGON* p = &editor->map->polygons[idx]; - if( p->vertices.size <= 3 ) { - editor->map->polygons.erase( idx ); - break; + if( idx != -1 && vidx != -1 ) { + MAP_POLYGON* p = &editor->map->polygons[idx]; + if( p->vertices.size <= 3 ) { + editor->map->polygons.erase( idx ); + break; + } + editor->map->polygons[idx].vertices.erase( vidx ); } - editor->map->polygons[idx].vertices.erase( vidx ); - } - }; break; - case EDITOR_SELECT_WVERTEX: { - I32 idx = editor->map->walls.idx_where( fn( MAP_WALL* w ) { - return &w->end == it || &w->start == it; - } ); + }; break; + case EDITOR_SELECT_WVERTEX: { + I32 idx = editor->map->walls.idx_where( fn( MAP_WALL* w ) { + return &w->end == it || &w->start == it; + } ); - if( idx != -1 ) - editor->map->walls.erase( idx ); - }; break; - default: - cleared = 0; break; + if( idx != -1 ) + editor->map->walls.erase( idx ); + }; break; + default: break; + } } - if( cleared && it == editor->gui.props->curselect ) { - gui_editor_propview_select( editor->gui.props, 0, 0 ); - } } void gui_editor_2dview_key_input( GUI_EDITOR_2DVIEW* view ) { diff --git a/src/game/physics/movement.cpp b/src/game/physics/movement.cpp index 094bc81..838c8a9 100644 --- a/src/game/physics/movement.cpp +++ b/src/game/physics/movement.cpp @@ -616,8 +616,6 @@ void gmove_full_walk_move() { gmove_start_gravity(); // todo: - // jump, - // categorizepos // more if( gmove->input->jump ) diff --git a/src/gui/base.h b/src/gui/base.h index 293de74..0418afd 100644 --- a/src/gui/base.h +++ b/src/gui/base.h @@ -229,6 +229,8 @@ struct GUI_FLOATINPUT : GUI_BASE { F32 max; F32 step; + F32 lastchange; + I32 lastmx; U8 heldoutbounds; U8 held; @@ -239,6 +241,7 @@ struct GUI_FLOATINPUT : GUI_BASE { U8 wraparound; GUI_CALLBACK cb; + void* cbextra; }; struct GUI_VECTORINPUT : GUI_VIEW { @@ -253,8 +256,10 @@ struct GUI_VECTORINPUT : GUI_VIEW { GUI_VIEW* inputview; LIST<GUI_FLOATINPUT*> inputs; + LIST<F32> lastchange; GUI_CALLBACK cb; + void* cbextra; }; struct GUI_COLORINPUT : GUI_VECTORINPUT {}; diff --git a/src/gui/floatinput.cpp b/src/gui/floatinput.cpp index 5746758..5ce92fe 100644 --- a/src/gui/floatinput.cpp +++ b/src/gui/floatinput.cpp @@ -166,6 +166,7 @@ void gui_floatinput_input_bound( GUI_FLOATINPUT* input ) { if( !gui_mbutton_down( GUI_MBTNLEFT ) ) return; + F32 oldval = *input->pval; I32 x = gui_relx( input ); I32 w = gui_floatinput_content_w( input ); @@ -182,6 +183,8 @@ void gui_floatinput_input_bound( GUI_FLOATINPUT* input ) { F32 nval = min + (max - min) * progress; F32 rmn = remainderf( nval, input->step ); *input->pval = nval - rmn; + if( oldval != *input->pval ) + input->lastchange = *input->pval - oldval; } void gui_floatinput_input_unbound( GUI_FLOATINPUT* input ) { @@ -191,6 +194,7 @@ void gui_floatinput_input_unbound( GUI_FLOATINPUT* input ) { I32 mx, my; gui_cursor_pos( &mx, &my ); + F32 oldval = *input->pval; I32 dx = mx - input->lastmx; if( dx ) *input->pval += dx * input->step; @@ -205,6 +209,8 @@ void gui_floatinput_input_unbound( GUI_FLOATINPUT* input ) { F32 rmn = remainderf( *input->pval, input->step ); *input->pval -= rmn; + if( oldval != *input->pval ) + input->lastchange = *input->pval - oldval; } void gui_floatinput_input_scroll( GUI_FLOATINPUT* input ) { @@ -229,6 +235,7 @@ void gui_floatinput_input_scroll( GUI_FLOATINPUT* input ) { F32 rmn = remainderf( nval, input->step ); *input->pval = nval - rmn; + input->lastchange = *input->pval - oldval; if( input->cb ) input->cb( input ); } @@ -261,13 +268,15 @@ void gui_floatinput_input_fn( void* ptr ) { return; } - if( !input->held ) { + if( !input->held && !input->heldoutbounds ) { U8 fine = !!m2; I32 split_y = y + 1 + ( h - 2 ) / 2; U8 inc = my < split_y; F32 step = fine ? 0.1f : 1.f; + F32 val = *input->pval; *input->pval += inc ? step : -step; gui_floatinput_clamp_and_snap( input ); + input->lastchange = *input->pval - val; if( input->cb ) input->cb( input ); input->held = 1; @@ -320,6 +329,7 @@ struct GUI_FLOATINPUT* gui_floatinput( I32 x, I32 y, I32 w, const char* title, F input->input_fn = gui_floatinput_input_fn; input->draw_fn = gui_floatinput_draw_fn; + input->lastchange = 0; input->cb = 0; input->pval = pval; input->min = min; diff --git a/src/gui/vectorinput.cpp b/src/gui/vectorinput.cpp index 9ebb0fa..daed540 100644 --- a/src/gui/vectorinput.cpp +++ b/src/gui/vectorinput.cpp @@ -25,6 +25,16 @@ void gui_vectorinput_child_cb( void* ptr ) { GUI_FLOATINPUT* child = (GUI_FLOATINPUT*)ptr; // slider -> child view -> vectorinput GUI_VECTORINPUT* parent = (GUI_VECTORINPUT*)child->parent->parent; + for( auto& it : parent->lastchange ) + it = 0.f; + + for( U32 i = 0; i < parent->inputs.size; ++i ) { + if( parent->inputs.data[i] == child ) { + parent->lastchange.data[i] = child->lastchange; + break; + } + } + if( parent->cb ) parent->cb( parent ); } @@ -54,6 +64,7 @@ void __gui_internal_vectorinput_init( GUI_VIEW* parent = gui_get_view(); parent->children.push( input ); input->parent = parent; + input->lastchange.resize( valc ); gui_set_view( input ); @@ -77,6 +88,7 @@ void __gui_internal_vectorinput_init( printfmt ); + input->inputs.push( slider ); slider->cb = gui_vectorinput_child_cb; } diff --git a/src/util/string.h b/src/util/string.h index 672fb5d..2725fd2 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -1,5 +1,6 @@ #pragma once #include <cstdio> +#include <wchar.h> #include <stdarg.h> #include <string.h> @@ -65,12 +66,21 @@ struct __str : public LIST<CT> { va_start( args, fmt ); va_list args2; va_copy( args2, args ); - U32 c = vsnprintf( 0, 0, fmt, args ); - va_end( args ); - this->reserve( c * 2 ); - vsnprintf( this->data, c + 1, fmt, args2 ); - this->data[c] = 0; - this->size = c; + if constexpr( sizeof( CT ) == 1 ) { + U32 c = vsnprintf( 0, 0, fmt, args ); + va_end( args ); + this->reserve( c * 2 ); + vsnprintf( this->data, c + 1, fmt, args2 ); + this->data[c] = 0; + this->size = c; + } else { + U32 c = vswprintf( 0, 0, fmt, args ); + va_end( args ); + this->reserve( c * 2 ); + vswprintf( this->data, c + 1, fmt, args2 ); + this->data[c] = 0; + this->size = c; + } va_end( args2 ); } @@ -138,18 +148,28 @@ struct __str : public LIST<CT> { return 1; } - __str<CT>& fmt( const char* fmt, ... ) { + __str<CT>& fmt( const CT* fmt, ... ) { va_list args; va_start( args, fmt ); va_list args2; va_copy( args2, args ); - U32 c = this->size + vsnprintf( 0, 0, fmt, args ); - va_end( args ); - if( c > this->capacity ) - this->reserve( c * 2 ); - vsnprintf( this->data + this->size, c + 1, fmt, args2 ); - this->data[c] = 0; - this->size = c; + if constexpr( sizeof( CT ) == 1 ) { + U32 c = this->size + vsnprintf( 0, 0, fmt, args ); + va_end( args ); + if( c > this->capacity ) + this->reserve( c * 2 ); + vsnprintf( this->data + this->size, c + 1, fmt, args2 ); + this->data[c] = 0; + this->size = c; + } else { + U32 c = this->size + vswprintf( 0, 0, fmt, args ); + va_end( args ); + if( c > this->capacity ) + this->reserve( c * 2 ); + vswprintf( this->data + this->size, c + 1, fmt, args2 ); + this->data[c] = 0; + this->size = c; + } va_end( args2 ); return *this; diff --git a/src/util/typedef.h b/src/util/typedef.h index 53c4010..4a28624 100644 --- a/src/util/typedef.h +++ b/src/util/typedef.h @@ -29,7 +29,11 @@ typedef double F64; typedef unsigned long PTR; +// lambda macro - captures all by ref #define fn( ... ) [&]( __VA_ARGS__ ) +// lambda macro - captures all by copy +#define cfn( ... ) [=]( __VA_ARGS__ ) +// lambda macro - raw pointer func #define pfn( ... ) []( __VA_ARGS__ ) template <typename T> |
