#include "editor.h" #include "../util/string.h" #include "../game/object.h" const U32 TOOLVIEW_TITLE_OFFSET = 15; const I32 TOOLVIEW_INNER_X = 10; const I32 TOOLVIEW_INNER_PAD = 20; const I32 TOOLVIEW_ROW_HEIGHT = 20; const I32 TOOLVIEW_ROW_GAP = 4; const I32 TOOLVIEW_WALL_SHAPE_LIST_HEIGHT = 54; const I32 TOOLVIEW_OBJECT_TYPE_LIST_HEIGHT = 36; const I32 TOOLVIEW_ENTITY_CLASS_LIST_HEIGHT = 54; const I32 TOOLVIEW_SCROLL_STEP = 20; const I32 TOOLVIEW_SCROLLBAR_W = 8; const I32 TOOLVIEW_SCROLLBAR_MIN_H = 18; GUI_EDITOR_TOOLVIEW* gui_editor_toolview_from_item_child( GUI_BASE* child ) { if( !child || !child->parent || !child->parent->parent ) return 0; return (GUI_EDITOR_TOOLVIEW*)child->parent->parent; } I32 gui_editor_toolview_visible_h( GUI_EDITOR_TOOLVIEW* view ) { return max( 1, view->h - 4 ); } I32 gui_editor_toolview_max_scroll( GUI_EDITOR_TOOLVIEW* view ) { return max( 0, view->content_h - gui_editor_toolview_visible_h( view ) ); } void gui_editor_toolview_clamp_scroll( GUI_EDITOR_TOOLVIEW* view ) { if( !view ) return; I32 max_scroll = gui_editor_toolview_max_scroll( view ); view->scroll = min( max( 0, view->scroll ), max_scroll ); if( view->itemview ) { view->itemview->y = TOOLVIEW_TITLE_OFFSET - view->scroll; } } void gui_editor_toolview_sanitize_float_input( void* ptr ) { GUI_FLOATINPUT* input = (GUI_FLOATINPUT*)ptr; F32* pval = input->pval; if( pval == &editor->tool.polysides ) { if( !isfinite( *pval ) ) *pval = EDITOR_DEFAULT_POLY_SIDES; F32 rounded = floorf( *pval + 0.5f ); if( rounded < EDITOR_POLY_SIDES_MIN ) rounded = EDITOR_POLY_SIDES_MIN; *pval = rounded; return; } if( pval == &editor->tool.wallheight ) { if( !isfinite( *pval ) ) *pval = EDITOR_DEFAULT_WALL_HEIGHT; if( *pval < 1.f ) *pval = 1.f; return; } if( pval == &editor->tool.placementheight ) { if( !isfinite( *pval ) ) *pval = EDITOR_DEFAULT_PLACEMENT_HEIGHT; } } I32 gui_editor_toolview_create_float_input( GUI_EDITOR_TOOLVIEW* view, I32 y, const char* title, F32* pval, F32 min, F32 max, F32 step, const char* fmt ) { GUI_FLOATINPUT* input = gui_floatinput( TOOLVIEW_INNER_X, y, view->w - TOOLVIEW_INNER_PAD, title, pval, min, max, step, fmt ); input->cb = gui_editor_toolview_sanitize_float_input; if( input->cb ) input->cb( input ); return y + TOOLVIEW_ROW_HEIGHT + TOOLVIEW_ROW_GAP; } void gui_editor_toolview_queue_refresh( GUI_EDITOR_TOOLVIEW* view ) { if( !view ) return; gui_push_callback( view, pfn( void* ptr ) { gui_editor_toolview_update( (GUI_EDITOR_TOOLVIEW*)ptr ); } ); } void gui_editor_toolview_wallshape_dropdown_toggle_cb( void* ptr ) { GUI_BUTTON* btn = (GUI_BUTTON*)ptr; GUI_EDITOR_TOOLVIEW* view = gui_editor_toolview_from_item_child( btn ); if( !view ) return; view->wallshape_dropdown_open = !view->wallshape_dropdown_open; gui_editor_toolview_queue_refresh( view ); } void gui_editor_toolview_wallshape_select_cb( void* ptr ) { GUI_LIST* list = (GUI_LIST*)ptr; GUI_EDITOR_TOOLVIEW* view = gui_editor_toolview_from_item_child( list ); if( !view ) return; view->wallshape_dropdown_open = 0; gui_editor_toolview_queue_refresh( view ); } void gui_editor_toolview_objecttype_dropdown_toggle_cb( void* ptr ) { GUI_BUTTON* btn = (GUI_BUTTON*)ptr; GUI_EDITOR_TOOLVIEW* view = gui_editor_toolview_from_item_child( btn ); if( !view ) return; view->objecttype_dropdown_open = !view->objecttype_dropdown_open; view->entclass_dropdown_open = 0; gui_editor_toolview_queue_refresh( view ); } void gui_editor_toolview_objecttype_select_cb( void* ptr ) { GUI_LIST* list = (GUI_LIST*)ptr; GUI_EDITOR_TOOLVIEW* view = gui_editor_toolview_from_item_child( list ); if( !view ) return; view->objecttype_dropdown_open = 0; view->entclass_dropdown_open = 0; gui_editor_toolview_queue_refresh( view ); } void gui_editor_toolview_entclass_dropdown_toggle_cb( void* ptr ) { GUI_BUTTON* btn = (GUI_BUTTON*)ptr; GUI_EDITOR_TOOLVIEW* view = gui_editor_toolview_from_item_child( btn ); if( !view ) return; view->entclass_dropdown_open = !view->entclass_dropdown_open; gui_editor_toolview_queue_refresh( view ); } void gui_editor_toolview_entclass_select_cb( void* ptr ) { GUI_LIST* list = (GUI_LIST*)ptr; GUI_EDITOR_TOOLVIEW* view = gui_editor_toolview_from_item_child( list ); if( !view ) return; view->entclass_dropdown_open = 0; gui_editor_toolview_queue_refresh( view ); } I32 gui_editor_toolview_create_wallshape_dropdown( GUI_EDITOR_TOOLVIEW* view, I32 y ) { static LIST shape_entries{}; if( !shape_entries.size ) { GUI_LIST_ENTRY line{}; line.val = EDITOR_WALLSHAPE_LINE; strcpy( line.title, "line" ); shape_entries.push( line ); GUI_LIST_ENTRY polygon{}; polygon.val = EDITOR_WALLSHAPE_POLYGON; strcpy( polygon.title, "polygon" ); shape_entries.push( polygon ); } const char* shape_name = editor->tool.wallshape == EDITOR_WALLSHAPE_POLYGON ? "polygon" : "line"; char dropdown_title[64]; sprintf( dropdown_title, "shape: %s v", shape_name ); gui_button( TOOLVIEW_INNER_X, y, view->w - TOOLVIEW_INNER_PAD, TOOLVIEW_ROW_HEIGHT, dropdown_title, gui_editor_toolview_wallshape_dropdown_toggle_cb ); y += TOOLVIEW_ROW_HEIGHT + TOOLVIEW_ROW_GAP; if( !view->wallshape_dropdown_open ) return y; GUI_LIST* shape = gui_list( TOOLVIEW_INNER_X, y, view->w - TOOLVIEW_INNER_PAD, TOOLVIEW_WALL_SHAPE_LIST_HEIGHT, "shape", &shape_entries, &editor->tool.wallshape ); shape->cb = gui_editor_toolview_wallshape_select_cb; y += TOOLVIEW_WALL_SHAPE_LIST_HEIGHT + TOOLVIEW_ROW_GAP; return y; } I32 gui_editor_toolview_create_objecttype_dropdown( GUI_EDITOR_TOOLVIEW* view, I32 y ) { static LIST object_entries{}; if( !object_entries.size ) { GUI_LIST_ENTRY sprite{}; sprite.val = EDITOR_OBJECT_SPRITE; strcpy( sprite.title, "sprite" ); object_entries.push( sprite ); GUI_LIST_ENTRY entity{}; entity.val = EDITOR_OBJECT_ENTITY; strcpy( entity.title, "entity" ); object_entries.push( entity ); } const char* object_name = editor->tool.objecttype == EDITOR_OBJECT_ENTITY ? "entity" : "sprite"; char dropdown_title[64]; sprintf( dropdown_title, "kind: %s v", object_name ); gui_button( TOOLVIEW_INNER_X, y, view->w - TOOLVIEW_INNER_PAD, TOOLVIEW_ROW_HEIGHT, dropdown_title, gui_editor_toolview_objecttype_dropdown_toggle_cb ); y += TOOLVIEW_ROW_HEIGHT + TOOLVIEW_ROW_GAP; if( !view->objecttype_dropdown_open ) return y; GUI_LIST* object_type = gui_list( TOOLVIEW_INNER_X, y, view->w - TOOLVIEW_INNER_PAD, TOOLVIEW_OBJECT_TYPE_LIST_HEIGHT, "object type", &object_entries, &editor->tool.objecttype ); object_type->cb = gui_editor_toolview_objecttype_select_cb; y += TOOLVIEW_OBJECT_TYPE_LIST_HEIGHT + TOOLVIEW_ROW_GAP; return y; } I32 gui_editor_toolview_create_entclass_dropdown( GUI_EDITOR_TOOLVIEW* view, I32 y ) { static LIST entclass_entries{}; if( !entclass_entries.size ) { GUI_LIST_ENTRY trigger{}; trigger.val = OBJCLASS_TRIGGER; strcpy( trigger.title, "trigger" ); entclass_entries.push( trigger ); GUI_LIST_ENTRY player{}; player.val = OBJCLASS_PLAYER; strcpy( player.title, "player" ); entclass_entries.push( player ); GUI_LIST_ENTRY npc{}; npc.val = OBJCLASS_BASENPC; strcpy( npc.title, "base_npc" ); entclass_entries.push( npc ); } char dropdown_title[64]; sprintf( dropdown_title, "class: %s v", obj_classid_to_name( editor->tool.entclass ) ); gui_button( TOOLVIEW_INNER_X, y, view->w - TOOLVIEW_INNER_PAD, TOOLVIEW_ROW_HEIGHT, dropdown_title, gui_editor_toolview_entclass_dropdown_toggle_cb ); y += TOOLVIEW_ROW_HEIGHT + TOOLVIEW_ROW_GAP; if( !view->entclass_dropdown_open ) return y; GUI_LIST* entclass = gui_list( TOOLVIEW_INNER_X, y, view->w - TOOLVIEW_INNER_PAD, TOOLVIEW_ENTITY_CLASS_LIST_HEIGHT, "entity class", &entclass_entries, (I32*)&editor->tool.entclass ); entclass->cb = gui_editor_toolview_entclass_select_cb; y += TOOLVIEW_ENTITY_CLASS_LIST_HEIGHT + TOOLVIEW_ROW_GAP; return y; } I32 gui_editor_toolview_create_poly_sides_input( GUI_EDITOR_TOOLVIEW* view, I32 y ) { return gui_editor_toolview_create_float_input( view, y, "sides", &editor->tool.polysides, (F32)EDITOR_POLY_SIDES_MIN, INFINITY, 1.f, "%.0f" ); } I32 gui_editor_toolview_create_wall_height_input( GUI_EDITOR_TOOLVIEW* view, I32 y ) { return gui_editor_toolview_create_float_input( view, y, "wall height", &editor->tool.wallheight, 1.f, 1024.f, 1.f, "%.0f" ); } I32 gui_editor_toolview_create_placement_height_input( GUI_EDITOR_TOOLVIEW* view, I32 y ) { F32 step = isfinite( editor->grid ) && editor->grid > 0.f ? editor->grid : 1.f; return gui_editor_toolview_create_float_input( view, y, "placement z", &editor->tool.placementheight, -INFINITY, INFINITY, step, "%.0f" ); } void gui_editor_toolview_get_title( GUI_EDITOR_TOOLVIEW* view, char* out ) { switch( editor->tool.type ) { case EDITOR_TOOL_OBJECT: memcpy( out, "tool: object", 12 ); break; case EDITOR_TOOL_WALL: memcpy( out, "tool: wall", 10 ); break; case EDITOR_TOOL_POLY: memcpy( out, "tool: polygon", 14 ); break; case EDITOR_TOOL_SELECT: memcpy( out, "tool: select", 12 ); break; default: memcpy( out, "tool: none", 10 ); break; } } void gui_editor_toolview_update( GUI_EDITOR_TOOLVIEW* view ) { if( !view ) return; GUI_VIEW* oldview = gui_get_view(); gui_set_view( view->itemview ); gui_empty_children( view->itemview ); view->itemview->initheld = 1; I32 y = 10; if( editor->tool.type == EDITOR_TOOL_WALL ) { y = gui_editor_toolview_create_wallshape_dropdown( view, y ); if( view->wallshape_dropdown_open ) { view->content_h = y + 6; gui_editor_toolview_clamp_scroll( view ); gui_set_view( oldview ); return; } if( editor->tool.wallshape == EDITOR_WALLSHAPE_POLYGON ) { y = gui_editor_toolview_create_poly_sides_input( view, y ); } y = gui_editor_toolview_create_wall_height_input( view, y ); y = gui_editor_toolview_create_placement_height_input( view, y ); } else if( editor->tool.type == EDITOR_TOOL_POLY ) { y = gui_editor_toolview_create_poly_sides_input( view, y ); y = gui_editor_toolview_create_placement_height_input( view, y ); } else if( editor->tool.type == EDITOR_TOOL_OBJECT ) { y = gui_editor_toolview_create_objecttype_dropdown( view, y ); if( view->objecttype_dropdown_open ) { view->content_h = y + 6; gui_editor_toolview_clamp_scroll( view ); gui_set_view( oldview ); return; } if( editor->tool.objecttype == EDITOR_OBJECT_ENTITY ) { y = gui_editor_toolview_create_entclass_dropdown( view, y ); if( view->entclass_dropdown_open ) { view->content_h = y + 6; gui_editor_toolview_clamp_scroll( view ); gui_set_view( oldview ); return; } } y = gui_editor_toolview_create_placement_height_input( view, y ); } view->content_h = y + 6; gui_editor_toolview_clamp_scroll( view ); gui_set_view( oldview ); } void gui_editor_toolview_draw_scrollbar( GUI_EDITOR_TOOLVIEW* view, I32 x, I32 y, I32 h ) { I32 max_scroll = gui_editor_toolview_max_scroll( view ); if( max_scroll <= 0 ) return; I32 track_x = x + view->w - TOOLVIEW_SCROLLBAR_W - 3; I32 track_y = y + 2; I32 track_h = max( 8, h - 4 ); gui_draw_frect( track_x, track_y, TOOLVIEW_SCROLLBAR_W, track_h, ui_clr.bg_alt ); gui_draw_rect( track_x, track_y, TOOLVIEW_SCROLLBAR_W, track_h, ui_clr.border ); I32 visible_h = gui_editor_toolview_visible_h( view ); I32 thumb_h = max( TOOLVIEW_SCROLLBAR_MIN_H, ( track_h * visible_h ) / max( 1, view->content_h ) ); thumb_h = min( thumb_h, track_h ); I32 travel = max( 1, track_h - thumb_h ); I32 thumb_y = track_y + ( travel * view->scroll ) / max( 1, max_scroll ); gui_draw_frect( track_x + 1, thumb_y + 1, TOOLVIEW_SCROLLBAR_W - 2, max( 1, thumb_h - 2 ), ui_clr.txt ); } void gui_editor_toolview_draw_fn( void* ptr ) { if( !editor->map ) return; GUI_EDITOR_TOOLVIEW* view = (GUI_EDITOR_TOOLVIEW*)ptr; I32 x = gui_relx( view ); I32 y = gui_rely( view ); I32 w = view->w; I32 h = view->h; gui_draw_str( x, y, ALIGN_L, FNT_JPN12, ui_clr.txt, "tool options" ); y += TOOLVIEW_TITLE_OFFSET; CLR col = gui_is_fg_window( view )? ui_clr.border : ui_clr.border_inactive; gui_draw_frect( x, y, w, h, col ); gui_draw_frect( x+1, y+1, w-2, h-2, ui_clr.bg_sec ); gui_editor_toolview_clamp_scroll( view ); I32 max_scroll = gui_editor_toolview_max_scroll( view ); I32 clip_w = w - 4; if( max_scroll > 0 ) clip_w -= TOOLVIEW_SCROLLBAR_W + 2; gui_draw_push_clip( x + 2, y + 2, max( 1, clip_w ), max( 1, h - 4 ) ); view->itemview->draw_fn( view->itemview ); gui_draw_pop_clip(); gui_editor_toolview_draw_scrollbar( view, x, y, h ); } void gui_editor_toolview_input_fn( void* ptr ) { GUI_EDITOR_TOOLVIEW* view = (GUI_EDITOR_TOOLVIEW*)ptr; gui_base_input_fn( ptr ); I32 x = gui_relx( view ); I32 y = gui_rely( view ) + TOOLVIEW_TITLE_OFFSET; I32 w = view->w; I32 h = view->h; I32 mx, my; gui_cursor_pos( &mx, &my ); U8 inbounds = mx >= x && mx <= x + w && my >= y && my <= y + h; if( inbounds ) { U8 scroll = gui_mbutton_down( GUI_MBTNSCROLL ); if( scroll && gui_editor_toolview_max_scroll( view ) > 0 ) { if( scroll == 1 ) view->scroll -= TOOLVIEW_SCROLL_STEP; else if( scroll == (U8)-1 ) view->scroll += TOOLVIEW_SCROLL_STEP; gui_editor_toolview_clamp_scroll( view ); gui_capture_scroll(); } } gui_editor_toolview_clamp_scroll( view ); } GUI_EDITOR_TOOLVIEW* gui_editor_toolview( I32 x, I32 y, I32 w, I32 h ) { if( !gui_check_target() ) return 0; GUI_EDITOR_TOOLVIEW* view = new GUI_EDITOR_TOOLVIEW; view->x = x; view->y = y; view->w = w; view->h = h; view->xbound = view->w; view->ybound = view->h + TOOLVIEW_TITLE_OFFSET; strcpy( view->name, "EDITOR_PROP_VIEW" ); view->draw_fn = gui_editor_toolview_draw_fn; view->input_fn = gui_editor_toolview_input_fn; view->wallshape_dropdown_open = 0; view->objecttype_dropdown_open = 0; view->entclass_dropdown_open = 0; view->scroll = 0; view->content_h = 0; GUI_VIEW* parent = gui_get_view(); parent->children.push( view ); view->parent = parent; gui_set_view( view ); view->itemview = gui_view( 0, TOOLVIEW_TITLE_OFFSET, w, h ); view->itemview->initheld = 1; gui_set_view( parent ); return view; }