From a156bc15880e9e250c9c40f0dde431e077109dc1 Mon Sep 17 00:00:00 2001 From: aura Date: Tue, 17 Mar 2026 12:04:30 +0100 Subject: multi select --- src/editor/properties.cpp | 221 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 184 insertions(+), 37 deletions(-) (limited to 'src/editor/properties.cpp') 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 ); -- cgit v1.2.3