summaryrefslogtreecommitdiff
path: root/src/editor/editor_layout.cpp
diff options
context:
space:
mode:
authorkasull <qsullian@gmail.com>2026-03-05 11:38:03 -0500
committerkasull <qsullian@gmail.com>2026-03-05 11:38:03 -0500
commit7c8b5072d8441aa6a7da9bd7560c47ba8c41270b (patch)
tree7f3a076dd4c651047dfcea82dbf05aa90ea83ed0 /src/editor/editor_layout.cpp
parent54bcabc374b438ee288964d6f6314f5da2121a0e (diff)
split UI components and make menubar data-driven
replace monolithic gui.cpp with focused editor UI component files shared editor_gui_internal.h for layout constants and cross-component helpers modular menubar rendering/input
Diffstat (limited to 'src/editor/editor_layout.cpp')
-rw-r--r--src/editor/editor_layout.cpp283
1 files changed, 283 insertions, 0 deletions
diff --git a/src/editor/editor_layout.cpp b/src/editor/editor_layout.cpp
new file mode 100644
index 0000000..f98ef00
--- /dev/null
+++ b/src/editor/editor_layout.cpp
@@ -0,0 +1,283 @@
+#include "editor_gui_internal.h"
+
+static I32 editor_layout_props_w_max( GUI_EDITORWINDOW* wnd ) {
+ I32 max_w = wnd->w - ( EDITOR_LAYOUT_MARGIN * 2 ) - EDITOR_LAYOUT_COLUMN_GAP - EDITOR_LAYOUT_RIGHT_MIN_W;
+ return max( EDITOR_LAYOUT_PROPS_MIN_W, max_w );
+}
+
+static I32 editor_layout_content_bottom( GUI_EDITORWINDOW* wnd ) {
+ return wnd->h - EDITOR_LAYOUT_MARGIN - EDITOR_LAYOUT_STATUS_H - EDITOR_LAYOUT_STATUS_GAP;
+}
+
+static I32 editor_layout_left_available_h( GUI_EDITORWINDOW* wnd ) {
+ return max( 1, editor_layout_content_bottom( wnd ) - EDITOR_LAYOUT_CONTENT_Y );
+}
+
+static I32 editor_layout_view_h_max( GUI_EDITORWINDOW* wnd ) {
+ I32 max_h = editor_layout_content_bottom( wnd )
+ - EDITOR_LAYOUT_CONTENT_Y
+ - EDITOR_LAYOUT_VIEW_TOOL_GAP
+ - EDITOR_LAYOUT_TOOL_PANEL_H
+ - ( EDITOR_LAYOUT_TITLE_OFFSET * 2 );
+ return max( EDITOR_LAYOUT_VIEW_MIN_H, max_h );
+}
+
+static void editor_layout_clamp_custom_sizes( GAME_EDITOR* e ) {
+ if( !e || !e->wnd )
+ return;
+
+ GAME_EDITOR::EDITOR_GUI* egui = &e->gui;
+ if( egui->props_w <= 0 ) egui->props_w = EDITOR_LAYOUT_PROPS_DEFAULT_W;
+ if( egui->props_h <= 0 ) egui->props_h = EDITOR_LAYOUT_PROPS_DEFAULT_H;
+ if( egui->view_h <= 0 ) egui->view_h = EDITOR_LAYOUT_VIEW_DEFAULT_H;
+
+ egui->props_w = min( max( egui->props_w, EDITOR_LAYOUT_PROPS_MIN_W ), editor_layout_props_w_max( e->wnd ) );
+
+ I32 left_avail_h = editor_layout_left_available_h( e->wnd );
+ I32 max_props_h = left_avail_h - EDITOR_LAYOUT_LEFT_BOX_GAP - EDITOR_LAYOUT_ASSETS_MIN_H - ( EDITOR_LAYOUT_TITLE_OFFSET * 2 );
+ max_props_h = max( EDITOR_LAYOUT_PROPS_MIN_H, max_props_h );
+ egui->props_h = min( max( egui->props_h, EDITOR_LAYOUT_PROPS_MIN_H ), max_props_h );
+
+ egui->view_h = editor_layout_view_h_max( e->wnd );
+}
+
+static EDITOR_LAYOUT editor_calc_layout( GAME_EDITOR* e ) {
+ GUI_EDITORWINDOW* wnd = e->wnd;
+ editor_layout_clamp_custom_sizes( e );
+
+ EDITOR_LAYOUT l{};
+ l.left_x = EDITOR_LAYOUT_MARGIN;
+ l.left_w = e->gui.props_w;
+ l.right_x = l.left_x + l.left_w + EDITOR_LAYOUT_COLUMN_GAP;
+ l.right_w = max( 1, wnd->w - EDITOR_LAYOUT_MARGIN - l.right_x );
+ l.tool_btn_x = l.right_x;
+ l.tool_btn_w = 45;
+ l.tool_panel_x = l.tool_btn_x + l.tool_btn_w + EDITOR_LAYOUT_TOOL_PANEL_GAP;
+ l.tool_panel_w = EDITOR_LAYOUT_TOOL_PANEL_W;
+
+ l.top_y = EDITOR_LAYOUT_NAV_Y;
+ l.props_y = EDITOR_LAYOUT_CONTENT_Y;
+ l.view_y = EDITOR_LAYOUT_CONTENT_Y;
+ l.props_h = e->gui.props_h;
+
+ I32 left_total_h = max( 1, editor_layout_content_bottom( wnd ) - l.props_y );
+ I32 props_max_h = left_total_h - EDITOR_LAYOUT_LEFT_BOX_GAP - EDITOR_LAYOUT_ASSETS_MIN_H - ( EDITOR_LAYOUT_TITLE_OFFSET * 2 );
+ props_max_h = max( EDITOR_LAYOUT_PROPS_MIN_H, props_max_h );
+ l.props_h = min( max( l.props_h, EDITOR_LAYOUT_PROPS_MIN_H ), props_max_h );
+ l.assets_y = l.props_y + EDITOR_LAYOUT_TITLE_OFFSET + l.props_h + EDITOR_LAYOUT_LEFT_BOX_GAP;
+ l.assets_h = max( EDITOR_LAYOUT_ASSETS_MIN_H, editor_layout_content_bottom( wnd ) - l.assets_y - EDITOR_LAYOUT_TITLE_OFFSET );
+
+ l.view_h = e->gui.view_h;
+ l.tool_y = l.view_y + EDITOR_LAYOUT_TITLE_OFFSET + l.view_h + EDITOR_LAYOUT_VIEW_TOOL_GAP;
+ l.tool_btn_y = l.tool_y + EDITOR_LAYOUT_TOOL_BTN_TOP_GAP;
+
+ I32 bottom_content = editor_layout_content_bottom( wnd );
+ l.tool_h = max( 1, bottom_content - l.tool_y - EDITOR_LAYOUT_TITLE_OFFSET );
+ l.tool_panel_h = l.tool_h;
+ l.tool_btn_step = 23;
+
+ I32 required_w = l.tool_btn_w + EDITOR_LAYOUT_TOOL_PANEL_GAP + l.tool_panel_w;
+ if( required_w > l.right_w ) {
+ I32 overflow = required_w - l.right_w;
+ I32 shrink_panel = min( overflow, max( 0, l.tool_panel_w - 180 ) );
+ l.tool_panel_w -= shrink_panel;
+ }
+ l.tool_panel_x = l.tool_btn_x + l.tool_btn_w + EDITOR_LAYOUT_TOOL_PANEL_GAP;
+
+ l.status_x = EDITOR_LAYOUT_MARGIN;
+ l.status_w = max( 1, wnd->w - EDITOR_LAYOUT_MARGIN * 2 );
+ l.status_h = EDITOR_LAYOUT_STATUS_H;
+ l.status_y = wnd->h - EDITOR_LAYOUT_MARGIN - l.status_h;
+ return l;
+}
+
+void editor_set_bounds( GUI_BASE* node, I32 x, I32 y, I32 w, I32 h ) {
+ if( !node )
+ return;
+
+ I32 x_extra = node->xbound - node->w;
+ I32 y_extra = node->ybound - node->h;
+ if( x_extra < 0 ) x_extra = 0;
+ if( y_extra < 0 ) y_extra = 0;
+
+ node->x = x;
+ node->y = y;
+ node->w = max( 1, w );
+ node->h = max( 1, h );
+ node->xbound = node->w + x_extra;
+ node->ybound = node->h + y_extra;
+}
+
+void editor_resize_base_view( GUI_EDITORWINDOW* wnd ) {
+ GUI_VIEW* base = (GUI_VIEW*)gui_find_node( wnd, "BASE_VIEW" );
+ if( !base )
+ return;
+
+ editor_set_bounds( base, 1, 1, wnd->w - 2, wnd->h - 2 );
+}
+
+void editor_layout_start_menu( GAME_EDITOR* e ) {
+ GUI_EDITORWINDOW* wnd = e->wnd;
+ GUI_LIST* list = (GUI_LIST*)gui_find_node( wnd, "map list" );
+ GUI_BUTTON* new_map = (GUI_BUTTON*)gui_find_node( wnd, "new map" );
+ GUI_BUTTON* load_map = (GUI_BUTTON*)gui_find_node( wnd, "load map" );
+ if( !list || !new_map || !load_map )
+ return;
+
+ const I32 min_margin = 10;
+ const I32 list_title_offset = 15;
+ const I32 button_gap = 10;
+ const I32 button_h = 25;
+ const I32 list_button_gap = 5;
+ I32 list_w = 200;
+ I32 list_h = 200;
+ I32 max_list_w = max( 80, wnd->w - min_margin * 2 );
+ I32 start_y = EDITOR_LAYOUT_CONTENT_Y;
+ I32 max_list_h = max( 80, wnd->h - ( start_y + list_title_offset + list_button_gap + button_h + min_margin ) );
+ if( list_w > max_list_w ) list_w = max_list_w;
+ if( list_h > max_list_h ) list_h = max_list_h;
+
+ I32 x = ( wnd->w - list_w ) / 2;
+ I32 y = start_y;
+ I32 bottom = y + list_title_offset + list_h + list_button_gap + button_h;
+ if( bottom > wnd->h - min_margin )
+ y = max( min_margin, wnd->h - min_margin - ( list_title_offset + list_h + list_button_gap + button_h ) );
+
+ editor_set_bounds( list, x, y, list_w, list_h );
+ I32 bw = max( 50, ( list_w - button_gap ) / 2 );
+ I32 by = y + list_title_offset + list_h + list_button_gap;
+ editor_set_bounds( new_map, x, by, bw, button_h );
+ editor_set_bounds( load_map, x + bw + button_gap, by, bw, button_h );
+}
+
+static void editor_layout_tool_buttons( GUI_EDITORWINDOW* wnd, const EDITOR_LAYOUT& l ) {
+ const char* names[] = { "none", "select", "wall", "poly", "sprite", "ent" };
+ I32 y = l.tool_btn_y;
+ for( U32 i = 0; i < sizeof( names ) / sizeof( names[0] ); ++i ) {
+ GUI_BUTTON* btn = (GUI_BUTTON*)gui_find_node( wnd, names[i] );
+ editor_set_bounds( btn, l.tool_btn_x, y, l.tool_btn_w, 20 );
+ y += l.tool_btn_step;
+ }
+}
+
+static void editor_layout_header_controls( GAME_EDITOR* e, const EDITOR_LAYOUT& l ) {
+ GAME_EDITOR::EDITOR_GUI* egui = &e->gui;
+ GUI_BASE* toolbar = egui->header_toolbar;
+ GUI_BUTTON* back = egui->header_back;
+ GUI_BUTTON* v2btn = egui->header_mode_2d;
+ GUI_BUTTON* v3btn = egui->header_mode_3d;
+ GUI_BUTTON* simbtn = egui->header_mode_sim;
+ GUI_LABEL* view_type_lbl = egui->header_viewtype_label;
+ GUI_BASE* view_type_seg = egui->header_viewtype;
+
+ const I32 top_btn_h = 20;
+ const I32 nav_left_x = 10;
+ const I32 nav_btn_w = 92;
+ const I32 nav_gap = 8;
+ const I32 mode_btn_w = 92;
+ I32 mode_group_w = mode_btn_w * 3 + nav_gap * 2;
+ I32 mode_x = e->wnd->w - EDITOR_LAYOUT_MARGIN - mode_group_w;
+ I32 type_seg_x = l.right_x;
+ I32 type_lbl_w = 64;
+ I32 type_lbl_x = type_seg_x - type_lbl_w - 6;
+ I32 max_type_w = mode_x - type_seg_x - 8;
+ I32 type_seg_w = min( 174, max( 120, max_type_w ) );
+
+ editor_set_bounds( toolbar, 1, EDITOR_LAYOUT_MENU_Y, e->wnd->w - 3, EDITOR_LAYOUT_MENU_H );
+ editor_set_bounds( back, nav_left_x, l.top_y, nav_btn_w, top_btn_h );
+
+ GUI_BUTTON* mode_btns[] = { v2btn, v3btn, simbtn };
+ const char* mode_names[] = { "[ 2d view ]", "[ 3d view ]", "[ simulation ]" };
+ for( I32 i = 0; i < 3; ++i ) {
+ I32 bx = mode_x + ( mode_btn_w + nav_gap ) * i;
+ editor_set_bounds( mode_btns[i], bx, l.top_y, mode_btn_w, top_btn_h );
+ if( mode_btns[i] )
+ snprintf( mode_btns[i]->name, GUI_NAME_LEN, "%s", mode_names[i] );
+ }
+
+ U8 show_viewtype = e->gui.view_mode == EDITOR_VIEWMODE_2D;
+ if( view_type_lbl ) {
+ view_type_lbl->x = type_lbl_x;
+ view_type_lbl->y = l.top_y + 4;
+ view_type_lbl->enabled = show_viewtype;
+ }
+ if( view_type_seg ) {
+ editor_set_bounds( view_type_seg, type_seg_x, l.top_y, type_seg_w, top_btn_h );
+ view_type_seg->enabled = show_viewtype;
+ }
+}
+
+static void editor_layout_left_column( GAME_EDITOR* e, const EDITOR_LAYOUT& l ) {
+ editor_set_bounds( e->gui.props, l.left_x, l.props_y, l.left_w, l.props_h );
+ if( e->gui.props->itemview ) {
+ editor_set_bounds( e->gui.props->itemview, 0, EDITOR_LAYOUT_TITLE_OFFSET, e->gui.props->w, e->gui.props->h );
+ }
+
+ if( e->gui.assets )
+ editor_set_bounds( e->gui.assets, l.left_x, l.assets_y, l.left_w, l.assets_h );
+}
+
+static void editor_layout_view_panels( GAME_EDITOR* e, const EDITOR_LAYOUT& l ) {
+ editor_set_bounds( e->gui.v2d, l.right_x, l.view_y, l.right_w, l.view_h );
+ e->gui.v2d->ybound = e->gui.v2d->h + EDITOR_LAYOUT_TITLE_OFFSET;
+ editor_set_bounds( e->gui.v3d, l.right_x, l.view_y, l.right_w, l.view_h );
+ e->gui.v3d->ybound = e->gui.v3d->h + EDITOR_LAYOUT_TITLE_OFFSET;
+}
+
+static void editor_layout_tool_panel( GAME_EDITOR* e, const EDITOR_LAYOUT& l ) {
+ editor_layout_tool_buttons( e->wnd, l );
+ editor_set_bounds( e->gui.tool, l.tool_panel_x, l.tool_y, l.tool_panel_w, l.tool_panel_h );
+ if( e->gui.tool->itemview ) {
+ editor_set_bounds( e->gui.tool->itemview, 0, EDITOR_LAYOUT_TITLE_OFFSET, e->gui.tool->w, e->gui.tool->h );
+ }
+}
+
+static void editor_layout_2d_footer( GAME_EDITOR* e ) {
+ if( !e->gui.gridlabel )
+ return;
+
+ I32 gx = 150;
+ I32 gy = e->gui.v2d->h - 4;
+ e->gui.gridlabel->x = gx;
+ e->gui.gridlabel->y = gy + 1;
+
+ GUI_BUTTON* plus = (GUI_BUTTON*)gui_find_node( e->gui.v2d, "+" );
+ GUI_BUTTON* minus = (GUI_BUTTON*)gui_find_node( e->gui.v2d, "-" );
+ GUI_CHECKBOX* propgrid = (GUI_CHECKBOX*)gui_find_node( e->gui.v2d, "properties grid" );
+ GUI_CHECKBOX* wireframe = (GUI_CHECKBOX*)gui_find_node( e->gui.v2d, "wireframe" );
+ editor_set_bounds( plus, gx + 70, gy, 18, 18 );
+ editor_set_bounds( minus, gx + 93, gy, 18, 18 );
+ editor_set_bounds( propgrid, gx + 120, gy, propgrid ? propgrid->w : 120, 18 );
+ editor_set_bounds( wireframe, gx + 240, gy, wireframe ? wireframe->w : 80, 18 );
+}
+
+static void editor_layout_3d_footer( GAME_EDITOR* e ) {
+ GUI_BUTTON* compile = (GUI_BUTTON*)gui_find_node( e->gui.v3d, "compile bsp" );
+ GUI_CHECKBOX* drawbsp = (GUI_CHECKBOX*)gui_find_node( e->gui.v3d, "draw bsp" );
+ I32 gy3d = e->gui.v3d->h - 4;
+ editor_set_bounds( compile, 1, gy3d, 90, 18 );
+ editor_set_bounds( drawbsp, 101, gy3d, drawbsp ? drawbsp->w : 80, 18 );
+}
+
+void editor_layout_map_view( GAME_EDITOR* e ) {
+ if( !e->gui.v2d || !e->gui.v3d || !e->gui.props || !e->gui.tool )
+ return;
+
+ EDITOR_LAYOUT l = editor_calc_layout( e );
+ editor_raise_header_toolbar( e );
+
+ editor_layout_header_controls( e, l );
+ editor_layout_left_column( e, l );
+ editor_layout_view_panels( e, l );
+ editor_layout_tool_panel( e, l );
+ if( e->gui.status )
+ editor_set_bounds( e->gui.status, l.status_x, l.status_y, l.status_w, l.status_h );
+
+ editor_apply_view_mode( e );
+ editor_layout_2d_footer( e );
+ editor_layout_3d_footer( e );
+
+ editor_update_properties_column( e );
+ gui_editor_toolview_update( e->gui.tool );
+ editor_notify_grid_change( e );
+}