summaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/editor/editor.h29
-rw-r--r--src/editor/editor_gui_internal.h55
-rw-r--r--src/editor/editor_infobox.cpp238
-rw-r--r--src/editor/editor_layout.cpp283
-rw-r--r--src/editor/editor_menubar.cpp430
-rw-r--r--src/editor/editor_viewtype_segment.cpp119
-rw-r--r--src/editor/editor_window.cpp285
-rw-r--r--src/editor/gui.cpp1280
8 files changed, 1427 insertions, 1292 deletions
diff --git a/src/editor/editor.h b/src/editor/editor.h
index b000816..6735d47 100644
--- a/src/editor/editor.h
+++ b/src/editor/editor.h
@@ -58,15 +58,15 @@ enum Editor2DViewType_t {
EDITOR_2DVIEW_FRONT_ELEVATION = 2
};
-enum EditorToolbarHit_t {
- EDITOR_TOOLBAR_HIT_NONE = -1,
- EDITOR_TOOLBAR_HIT_FILE = 0,
- EDITOR_TOOLBAR_HIT_EDIT = 1,
- EDITOR_TOOLBAR_HIT_VIEW = 2,
- EDITOR_TOOLBAR_HIT_TOOLS = 3,
- EDITOR_TOOLBAR_HIT_SAVE = 4,
- EDITOR_TOOLBAR_HIT_UNDO = 5,
- EDITOR_TOOLBAR_HIT_REDO = 6
+enum EditorMenubarEntryType_t {
+ EDITOR_MENUBAR_ENTRY_FUNCTION = 0,
+ EDITOR_MENUBAR_ENTRY_SUBMENU = 1
+};
+
+struct EDITOR_MENUBAR_ENTRY {
+ char text[64]{};
+ U8 type{};
+ LIST<EDITOR_MENUBAR_ENTRY> entries{};
};
struct GAME_EDITOR;
@@ -195,6 +195,10 @@ extern void editor_new_map_cb( void* );
extern void editor_create_map_view( GAME_EDITOR* e );
extern void editor_update_properties_column( GAME_EDITOR* e );
+extern const char* editor_tool_name();
+extern void editor_set_view_mode( I32 mode );
+extern void editor_toolbar_set_open( struct GUI_EDITOR_TOOLBAR* bar, U8 file_open, U8 edit_open );
+extern U8 editor_toolbar_menu_open( const struct GUI_EDITOR_TOOLBAR* bar );
extern void editor_undo_clear( GAME_EDITOR* e );
extern void editor_undo_record_create_walls( GAME_EDITOR* e, I32 start_idx, I32 count );
extern void editor_undo_record_create_poly( GAME_EDITOR* e, I32 start_idx );
@@ -281,9 +285,10 @@ struct GUI_EDITOR_VIEWTYPE_SEGMENT : GUI_BASE {
struct GUI_EDITOR_TOOLBAR : GUI_BASE {
U8 held{};
- I32 held_item{-1};
- U8 file_open{};
- U8 edit_open{};
+ I32 held_root{-1};
+ I32 held_sub{-1};
+ I32 open_root{-1};
+ LIST<EDITOR_MENUBAR_ENTRY> entries{};
};
extern GUI_EDITORWINDOW* gui_editorwindow( I32 w, I32 h );
diff --git a/src/editor/editor_gui_internal.h b/src/editor/editor_gui_internal.h
new file mode 100644
index 0000000..ff9b140
--- /dev/null
+++ b/src/editor/editor_gui_internal.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include "editor.h"
+#include "../util.h"
+
+constexpr I32 EDITOR_LAYOUT_MARGIN = 10;
+constexpr I32 EDITOR_LAYOUT_TITLE_OFFSET = 15;
+constexpr I32 EDITOR_LAYOUT_MENU_Y = 1;
+constexpr I32 EDITOR_LAYOUT_MENU_H = 22;
+constexpr I32 EDITOR_LAYOUT_NAV_Y = EDITOR_LAYOUT_MENU_Y + EDITOR_LAYOUT_MENU_H + 4;
+constexpr I32 EDITOR_LAYOUT_CONTENT_Y = EDITOR_LAYOUT_NAV_Y + 28;
+constexpr I32 EDITOR_LAYOUT_COLUMN_GAP = 10;
+constexpr I32 EDITOR_LAYOUT_VIEW_TOOL_GAP = 17;
+constexpr I32 EDITOR_LAYOUT_TOOL_BTN_TOP_GAP = 3;
+constexpr I32 EDITOR_LAYOUT_TOOL_PANEL_GAP = 5;
+constexpr I32 EDITOR_LAYOUT_LEFT_BOX_GAP = 10;
+constexpr I32 EDITOR_LAYOUT_STATUS_H = 22;
+constexpr I32 EDITOR_LAYOUT_STATUS_GAP = 6;
+constexpr I32 EDITOR_LAYOUT_TOOL_PANEL_W = 300;
+constexpr I32 EDITOR_LAYOUT_TOOL_PANEL_H = 150;
+constexpr I32 EDITOR_LAYOUT_PROPS_DEFAULT_W = 300;
+constexpr I32 EDITOR_LAYOUT_PROPS_DEFAULT_H = 300;
+constexpr I32 EDITOR_LAYOUT_VIEW_DEFAULT_H = 370;
+constexpr I32 EDITOR_LAYOUT_PROPS_MIN_W = 200;
+constexpr I32 EDITOR_LAYOUT_PROPS_MIN_H = 120;
+constexpr I32 EDITOR_LAYOUT_ASSETS_MIN_H = 120;
+constexpr I32 EDITOR_LAYOUT_VIEW_MIN_H = 140;
+constexpr I32 EDITOR_LAYOUT_RIGHT_MIN_W = 260;
+
+constexpr I32 EDITOR_TOOLBAR_DROPDOWN_W = 120;
+
+extern U8 editor_menu_hover_mask_active;
+extern I32 editor_menu_hover_real_x;
+extern I32 editor_menu_hover_real_y;
+
+extern GUI_EDITOR_VIEWTYPE_SEGMENT* gui_editor_viewtype_segment( I32 x, I32 y, I32 w, I32 h, const char* name );
+extern GUI_EDITOR_TOOLBAR* gui_editor_toolbar( I32 x, I32 y, I32 w, I32 h, const char* name );
+extern GUI_EDITOR_INFOBOX* gui_editor_infobox( I32 x, I32 y, I32 w, I32 h, U8 type, const char* name );
+
+extern void editor_apply_view_mode( GAME_EDITOR* e );
+extern void editor_toolbar_close_menu();
+extern void editor_create_header_controls( GAME_EDITOR* e );
+extern void editor_raise_header_toolbar( GAME_EDITOR* e );
+
+extern void editor_set_bounds( GUI_BASE* node, I32 x, I32 y, I32 w, I32 h );
+extern void editor_resize_base_view( GUI_EDITORWINDOW* wnd );
+extern void editor_layout_start_menu( GAME_EDITOR* e );
+extern void editor_layout_map_view( GAME_EDITOR* e );
+
+extern void editor_create_properties_column( GAME_EDITOR* e );
+extern void editor_create_auxiliary_panels( GAME_EDITOR* e );
+extern void editor_update_toolview( GAME_EDITOR* e );
+extern void editor_create_toolview_column( GAME_EDITOR* e );
+extern void editor_create_game_view_column( GAME_EDITOR* e );
+extern void settool( U8 t );
diff --git a/src/editor/editor_infobox.cpp b/src/editor/editor_infobox.cpp
new file mode 100644
index 0000000..8e455d9
--- /dev/null
+++ b/src/editor/editor_infobox.cpp
@@ -0,0 +1,238 @@
+#include "editor_gui_internal.h"
+
+#include "../game.h"
+#include "../render/gl_2d.h"
+
+constexpr I32 EDITOR_ASSETS_ROW_H = 28;
+constexpr I32 EDITOR_ASSETS_THUMB = 20;
+constexpr I32 EDITOR_ASSETS_INNER_PAD_X = 8;
+constexpr I32 EDITOR_ASSETS_INNER_PAD_Y = 6;
+constexpr I32 EDITOR_ASSETS_SCROLLBAR_W = 7;
+constexpr I32 EDITOR_ASSETS_SCROLLBAR_GAP = 4;
+constexpr I32 EDITOR_ASSETS_SCROLL_STEP = 20;
+
+struct EDITOR_ASSETS_LAYOUT {
+ I32 inner_x{};
+ I32 inner_y{};
+ I32 inner_w{};
+ I32 list_h{};
+ I32 row_x{};
+ I32 row_w{};
+ I32 count{};
+ I32 content_h{};
+ I32 max_scroll{};
+ U8 show_scroll{};
+ I32 track_x{};
+ I32 track_y{};
+ I32 track_h{};
+};
+
+static EDITOR_ASSETS_LAYOUT editor_assets_layout( GUI_EDITOR_INFOBOX* box, I32 panel_x, I32 panel_y, I32 prop_count ) {
+ EDITOR_ASSETS_LAYOUT l{};
+ l.inner_x = panel_x + EDITOR_ASSETS_INNER_PAD_X;
+ l.inner_y = panel_y + EDITOR_ASSETS_INNER_PAD_Y;
+ l.inner_w = max( 1, box->w - ( EDITOR_ASSETS_INNER_PAD_X * 2 ) );
+ l.list_h = max( 1, box->h - ( EDITOR_ASSETS_INNER_PAD_Y * 2 ) );
+ l.count = prop_count + 1;
+ l.content_h = l.count * EDITOR_ASSETS_ROW_H;
+ l.max_scroll = max( 0, l.content_h - l.list_h );
+ l.show_scroll = l.content_h > l.list_h;
+ l.row_x = l.inner_x + 1;
+ l.row_w = l.inner_w - 2;
+ if( l.show_scroll )
+ l.row_w -= EDITOR_ASSETS_SCROLLBAR_W + EDITOR_ASSETS_SCROLLBAR_GAP;
+ l.row_w = max( 1, l.row_w );
+ l.track_x = l.inner_x + l.inner_w - EDITOR_ASSETS_SCROLLBAR_W - 1 - ( EDITOR_ASSETS_SCROLLBAR_GAP / 2 );
+ l.track_y = l.inner_y;
+ l.track_h = l.list_h - 2;
+ return l;
+}
+
+static void editor_assets_clamp_scroll( const EDITOR_ASSETS_LAYOUT& l ) {
+ editor->gui.assets_scroll = min( max( 0, editor->gui.assets_scroll ), l.max_scroll );
+}
+
+static void gui_editor_infobox_draw_assets( GUI_EDITOR_INFOBOX* box, I32 panel_x, I32 panel_y ) {
+ if( !editor->map )
+ return;
+
+ WORLD_MAP* map = editor->map;
+ EDITOR_ASSETS_LAYOUT l = editor_assets_layout( box, panel_x, panel_y, (I32)map->props.size );
+ editor_assets_clamp_scroll( l );
+ I32 yoff = -editor->gui.assets_scroll;
+
+ for( I32 idx = 0; idx < l.count; ++idx ) {
+ I32 row_y = l.inner_y + idx * EDITOR_ASSETS_ROW_H + yoff;
+ if( row_y + EDITOR_ASSETS_ROW_H - 2 <= l.inner_y || row_y >= l.inner_y + l.list_h )
+ continue;
+
+ U8 map_entry = idx == 0;
+ 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;
+ }
+ }
+
+ CLR rowbg = idx % 2 ? ui_clr.bg : ui_clr.bg_alt;
+ if( selected )
+ rowbg = CLR::blend( ui_clr.border, ui_clr.bg, 0.70f );
+
+ gui_draw_frect( l.row_x, row_y, l.row_w, EDITOR_ASSETS_ROW_H - 2, rowbg );
+
+ I32 tx = l.row_x + 3;
+ I32 ty = row_y + 3;
+ gui_draw_frect( tx, ty, EDITOR_ASSETS_THUMB, EDITOR_ASSETS_THUMB, ui_clr.border );
+ if( map_entry ) {
+ gui_draw_frect( tx + 1, ty + 1, EDITOR_ASSETS_THUMB - 2, EDITOR_ASSETS_THUMB - 2, ui_clr.bg_alt );
+ gui_draw_str( tx + EDITOR_ASSETS_THUMB / 2, ty + 4, ALIGN_C, FNT_JPN12, ui_clr.txt, "m" );
+ } else if( p->tex ) {
+ gl_2d_textured_frect( _gui.gl2d_font, { (F32)(tx + 1), (F32)(ty + 1) }, { (F32)(EDITOR_ASSETS_THUMB - 2), (F32)(EDITOR_ASSETS_THUMB - 2) }, p->tex );
+ } else {
+ gui_draw_frect( tx + 1, ty + 1, EDITOR_ASSETS_THUMB - 2, EDITOR_ASSETS_THUMB - 2, p->clr );
+ }
+
+ I32 text_x = tx + EDITOR_ASSETS_THUMB + 8;
+ CLR txt = ui_clr.txt;
+ if( map_entry ) {
+ gui_draw_str( text_x, row_y + 7, ALIGN_L, FNT_JPN12, txt, "[map] -> %s", map->name );
+ } else if( p->tex ) {
+ gui_draw_str( text_x, row_y + 7, ALIGN_L, FNT_JPN12, txt, "[%d] -> %s", idx - 1, p->tex->name );
+ } else {
+ gui_draw_str( text_x, row_y + 7, ALIGN_L, FNT_JPN12, txt, "[%d] -> %.2f, %.2f, %.2f", idx - 1, p->clr.r, p->clr.g, p->clr.b );
+ }
+ }
+
+ if( !l.show_scroll )
+ return;
+
+ gui_draw_frect( l.track_x, l.track_y, EDITOR_ASSETS_SCROLLBAR_W, l.track_h, ui_clr.bg_alt );
+ gui_draw_rect( l.track_x, l.track_y, EDITOR_ASSETS_SCROLLBAR_W, l.track_h, ui_clr.border );
+
+ I32 thumb_h = max( 18, ( l.track_h * l.list_h ) / max( 1, l.content_h ) );
+ thumb_h = min( thumb_h, l.track_h );
+ I32 travel = max( 1, l.track_h - thumb_h );
+ I32 thumb_y = l.track_y + ( travel * editor->gui.assets_scroll ) / max( 1, l.max_scroll );
+ gui_draw_frect( l.track_x, thumb_y + 1, EDITOR_ASSETS_SCROLLBAR_W, max( 1, thumb_h - 1 ), ui_clr.txt );
+}
+
+static void gui_editor_infobox_draw_fn( void* ptr ) {
+ GUI_EDITOR_INFOBOX* box = (GUI_EDITOR_INFOBOX*)ptr;
+ I32 x = gui_relx( box );
+ I32 y = gui_rely( box );
+ I32 w = box->w;
+ I32 h = box->h;
+
+ if( box->type == EDITOR_INFOBOX_STATUS ) {
+ CLR col = gui_is_fg_window( box ) ? 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_draw_str(
+ x + 6,
+ y + 4,
+ ALIGN_L,
+ FNT_JPN12,
+ ui_clr.txt,
+ "selected tool: %s. click in viewport to create/edit.",
+ editor_tool_name()
+ );
+
+ if( editor->game && editor->game->gl ) {
+ GL_DATA* gl = editor->game->gl;
+ gui_draw_str(
+ x + w - 6,
+ y + 4,
+ ALIGN_R,
+ FNT_JPN12,
+ ui_clr.txt,
+ "fps: %.3f (%.5f ms) %dx%d",
+ gl->fps,
+ gl->frametime,
+ gl->canvas_size[0],
+ gl->canvas_size[1]
+ );
+ }
+ return;
+ }
+
+ gui_draw_str( x, y, ALIGN_L, FNT_JPN12, ui_clr.txt, "assets" );
+ y += EDITOR_LAYOUT_TITLE_OFFSET;
+
+ CLR col = gui_is_fg_window( box ) ? 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_draw_push_clip( x + 2, y + 2, w - 4, h - 4 );
+ gui_editor_infobox_draw_assets( box, x, y );
+ gui_draw_pop_clip();
+}
+
+static void gui_editor_infobox_input_fn( void* ptr ) {
+ GUI_EDITOR_INFOBOX* box = (GUI_EDITOR_INFOBOX*)ptr;
+ static U8 assets_click_held = 0;
+ if( box->type == EDITOR_INFOBOX_ASSETS ) {
+ I32 x = gui_relx( box );
+ I32 y = gui_rely( box ) + EDITOR_LAYOUT_TITLE_OFFSET;
+ I32 w = box->w;
+ I32 h = box->h;
+ I32 prop_count = editor->map ? (I32)editor->map->props.size : 0;
+ EDITOR_ASSETS_LAYOUT l = editor_assets_layout( box, x, y, prop_count );
+ editor_assets_clamp_scroll( l );
+ 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_capture_scroll();
+ if( scroll == 1 ) editor->gui.assets_scroll -= EDITOR_ASSETS_SCROLL_STEP;
+ else if( scroll == (U8)-1 ) editor->gui.assets_scroll += EDITOR_ASSETS_SCROLL_STEP;
+ editor_assets_clamp_scroll( l );
+ }
+
+ U8 m1 = gui_mbutton_down( GUI_MBTNLEFT );
+ if( m1 && !assets_click_held && editor->map ) {
+ if( mx >= l.row_x && mx < l.row_x + l.row_w ) {
+ I32 idx = ( my - l.inner_y + editor->gui.assets_scroll ) / EDITOR_ASSETS_ROW_H;
+ if( idx >= 0 && idx < l.count && editor->gui.props ) {
+ if( idx == 0 ) gui_editor_propview_select( editor->gui.props, editor->map, EDITOR_SELECT_ORIGIN );
+ else gui_editor_propview_select( editor->gui.props, &editor->map->props[idx - 1], EDITOR_SELECT_SURFPROPS );
+ }
+ }
+ }
+ assets_click_held = m1;
+ }
+
+ if( !gui_mbutton_down( GUI_MBTNLEFT ) )
+ assets_click_held = 0;
+ }
+
+ gui_base_input_fn( box );
+}
+
+GUI_EDITOR_INFOBOX* gui_editor_infobox( I32 x, I32 y, I32 w, I32 h, U8 type, const char* name ) {
+ if( !gui_check_target() )
+ return 0;
+
+ GUI_EDITOR_INFOBOX* box = new GUI_EDITOR_INFOBOX;
+ box->x = x;
+ box->y = y;
+ box->w = w;
+ box->h = h;
+ box->xbound = w;
+ box->ybound = h + ( type == EDITOR_INFOBOX_STATUS ? 0 : EDITOR_LAYOUT_TITLE_OFFSET );
+ box->draw_fn = gui_editor_infobox_draw_fn;
+ box->input_fn = gui_editor_infobox_input_fn;
+ box->type = type;
+ strcpy( box->name, name );
+
+ GUI_VIEW* parent = gui_get_view();
+ box->parent = parent;
+ parent->children.push( box );
+ return box;
+}
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 );
+}
diff --git a/src/editor/editor_menubar.cpp b/src/editor/editor_menubar.cpp
new file mode 100644
index 0000000..5491c05
--- /dev/null
+++ b/src/editor/editor_menubar.cpp
@@ -0,0 +1,430 @@
+#include "editor_gui_internal.h"
+
+U8 editor_menu_hover_mask_active = 0;
+I32 editor_menu_hover_real_x = 0;
+I32 editor_menu_hover_real_y = 0;
+
+static void editor_header_back_cb( void* ) {
+ editor_toolbar_close_menu();
+ editor_close( editor );
+}
+
+static void editor_header_mode_2d_cb( void* ) {
+ editor_set_view_mode( EDITOR_VIEWMODE_2D );
+}
+
+static void editor_header_mode_3d_cb( void* ) {
+ editor_set_view_mode( EDITOR_VIEWMODE_3D );
+}
+
+static void editor_header_mode_sim_cb( void* ) {
+ editor_set_view_mode( EDITOR_VIEWMODE_SIM );
+}
+
+void editor_apply_view_mode( GAME_EDITOR* e ) {
+ if( !e || !e->gui.v2d || !e->gui.v3d )
+ return;
+
+ if( e->gui.view_mode == EDITOR_VIEWMODE_2D ) {
+ e->gui.v2d->enabled = 1;
+ e->gui.v3d->enabled = 0;
+ return;
+ }
+
+ e->gui.v2d->enabled = 0;
+ e->gui.v3d->enabled = 1;
+}
+
+U8 editor_toolbar_menu_open( const GUI_EDITOR_TOOLBAR* bar ) {
+ return bar && bar->open_root >= 0;
+}
+
+static I32 editor_toolbar_find_root( GUI_EDITOR_TOOLBAR* bar, const char* text ) {
+ if( !bar || !text )
+ return -1;
+
+ for( I32 i = 0; i < (I32)bar->entries.size; ++i ) {
+ if( !strcmp( bar->entries[i].text, text ) )
+ return i;
+ }
+
+ return -1;
+}
+
+void editor_toolbar_set_open( GUI_EDITOR_TOOLBAR* bar, U8 file_open, U8 edit_open ) {
+ if( !bar )
+ return;
+
+ bar->open_root = -1;
+ if( file_open )
+ bar->open_root = editor_toolbar_find_root( bar, "file" );
+ else if( edit_open )
+ bar->open_root = editor_toolbar_find_root( bar, "edit" );
+}
+
+void editor_toolbar_close_menu() {
+ if( !editor )
+ return;
+
+ GUI_EDITOR_TOOLBAR* bar = (GUI_EDITOR_TOOLBAR*)editor->gui.header_toolbar;
+ editor_toolbar_set_open( bar, 0, 0 );
+}
+
+void editor_set_view_mode( I32 mode ) {
+ if( !editor )
+ return;
+
+ editor_toolbar_close_menu();
+ editor->gui.view_mode = mode;
+ editor_apply_view_mode( editor );
+ editor_layout_map_view( editor );
+}
+
+void editor_raise_header_toolbar( GAME_EDITOR* e ) {
+ if( !e || !e->map || !e->wnd || !e->gui.header_toolbar )
+ return;
+
+ GUI_BASE* bar = e->gui.header_toolbar;
+ GUI_BASE* parent = bar->parent;
+ if( !parent )
+ return;
+
+ I32 idx = parent->children.idx_of( bar );
+ if( idx == -1 || idx == (I32)parent->children.size - 1 )
+ return;
+
+ parent->children.erase( idx );
+ parent->children.push( bar );
+}
+
+static void editor_menubar_set_entry( EDITOR_MENUBAR_ENTRY* entry, const char* text, U8 type ) {
+ if( !entry )
+ return;
+
+ entry->entries.clear();
+ snprintf( entry->text, sizeof( entry->text ), "%s", text );
+ entry->type = type;
+}
+
+static void editor_toolbar_init_entries( GUI_EDITOR_TOOLBAR* bar ) {
+ if( !bar )
+ return;
+
+ bar->entries.clear();
+ bar->entries.resize( 4 );
+
+ editor_menubar_set_entry( &bar->entries[0], "file", EDITOR_MENUBAR_ENTRY_SUBMENU );
+ bar->entries[0].entries.resize( 1 );
+ editor_menubar_set_entry( &bar->entries[0].entries[0], "save", EDITOR_MENUBAR_ENTRY_FUNCTION );
+
+ editor_menubar_set_entry( &bar->entries[1], "edit", EDITOR_MENUBAR_ENTRY_SUBMENU );
+ bar->entries[1].entries.resize( 2 );
+ editor_menubar_set_entry( &bar->entries[1].entries[0], "undo", EDITOR_MENUBAR_ENTRY_FUNCTION );
+ editor_menubar_set_entry( &bar->entries[1].entries[1], "redo", EDITOR_MENUBAR_ENTRY_FUNCTION );
+
+ editor_menubar_set_entry( &bar->entries[2], "view", EDITOR_MENUBAR_ENTRY_SUBMENU );
+ bar->entries[2].entries.resize( 3 );
+ editor_menubar_set_entry( &bar->entries[2].entries[0], "2d view", EDITOR_MENUBAR_ENTRY_FUNCTION );
+ editor_menubar_set_entry( &bar->entries[2].entries[1], "3d view", EDITOR_MENUBAR_ENTRY_FUNCTION );
+ editor_menubar_set_entry( &bar->entries[2].entries[2], "simulation", EDITOR_MENUBAR_ENTRY_FUNCTION );
+
+ editor_menubar_set_entry( &bar->entries[3], "tools", EDITOR_MENUBAR_ENTRY_SUBMENU );
+ bar->entries[3].entries.resize( 6 );
+ editor_menubar_set_entry( &bar->entries[3].entries[0], "none", EDITOR_MENUBAR_ENTRY_FUNCTION );
+ editor_menubar_set_entry( &bar->entries[3].entries[1], "select", EDITOR_MENUBAR_ENTRY_FUNCTION );
+ editor_menubar_set_entry( &bar->entries[3].entries[2], "wall", EDITOR_MENUBAR_ENTRY_FUNCTION );
+ editor_menubar_set_entry( &bar->entries[3].entries[3], "poly", EDITOR_MENUBAR_ENTRY_FUNCTION );
+ editor_menubar_set_entry( &bar->entries[3].entries[4], "sprite", EDITOR_MENUBAR_ENTRY_FUNCTION );
+ editor_menubar_set_entry( &bar->entries[3].entries[5], "ent", EDITOR_MENUBAR_ENTRY_FUNCTION );
+}
+
+static I32 editor_toolbar_root_width( EDITOR_MENUBAR_ENTRY* entry ) {
+ if( !entry )
+ return 44;
+
+ I32 text_w = 0;
+ gui_draw_get_str_bounds( &text_w, 0, FNT_JPN12, "%s", entry->text );
+ return max( 44, text_w + 16 );
+}
+
+static I32 editor_toolbar_dropdown_width( EDITOR_MENUBAR_ENTRY* entry ) {
+ if( !entry || entry->type != EDITOR_MENUBAR_ENTRY_SUBMENU )
+ return EDITOR_TOOLBAR_DROPDOWN_W;
+
+ I32 width = EDITOR_TOOLBAR_DROPDOWN_W;
+ for( I32 i = 0; i < (I32)entry->entries.size; ++i ) {
+ I32 text_w = 0;
+ gui_draw_get_str_bounds( &text_w, 0, FNT_JPN12, "%s", entry->entries[i].text );
+ width = max( width, text_w + 20 );
+ }
+
+ return width;
+}
+
+static void editor_toolbar_root_rect( GUI_EDITOR_TOOLBAR* bar, I32 idx, I32* rx, I32* ry, I32* rw, I32* rh ) {
+ I32 x = gui_relx( bar );
+ I32 y = gui_rely( bar );
+ I32 cx = x + 8;
+ for( I32 i = 0; i < idx; ++i )
+ cx += editor_toolbar_root_width( &bar->entries[i] ) + 4;
+
+ I32 w = editor_toolbar_root_width( &bar->entries[idx] );
+ *rx = cx;
+ *ry = y + 2;
+ *rw = w;
+ *rh = max( 1, bar->h - 4 );
+}
+
+static void editor_toolbar_sub_rect( GUI_EDITOR_TOOLBAR* bar, I32 root_idx, I32 row, I32* rx, I32* ry, I32* rw, I32* rh ) {
+ I32 root_x = 0, root_y = 0, root_w = 0, root_h = 0;
+ editor_toolbar_root_rect( bar, root_idx, &root_x, &root_y, &root_w, &root_h );
+ I32 row_h = max( 18, root_h );
+
+ *rx = root_x;
+ *ry = gui_rely( bar ) + bar->h + row * row_h;
+ *rw = editor_toolbar_dropdown_width( &bar->entries[root_idx] );
+ *rh = row_h;
+}
+
+static U8 editor_toolbar_pt_in_rect( I32 mx, I32 my, I32 x, I32 y, I32 w, I32 h ) {
+ return mx >= x && mx < x + w && my >= y && my < y + h;
+}
+
+struct EDITOR_MENUBAR_HIT {
+ I32 root{-1};
+ I32 sub{-1};
+};
+
+static EDITOR_MENUBAR_HIT editor_toolbar_hit_test( GUI_EDITOR_TOOLBAR* bar, I32 mx, I32 my ) {
+ EDITOR_MENUBAR_HIT hit{};
+ for( I32 i = 0; i < (I32)bar->entries.size; ++i ) {
+ I32 x = 0, y = 0, w = 0, h = 0;
+ editor_toolbar_root_rect( bar, i, &x, &y, &w, &h );
+ if( editor_toolbar_pt_in_rect( mx, my, x, y, w, h ) ) {
+ hit.root = i;
+ return hit;
+ }
+ }
+
+ if( bar->open_root >= 0 && bar->open_root < (I32)bar->entries.size ) {
+ EDITOR_MENUBAR_ENTRY* root = &bar->entries[bar->open_root];
+ if( root->type == EDITOR_MENUBAR_ENTRY_SUBMENU ) {
+ for( I32 i = 0; i < (I32)root->entries.size; ++i ) {
+ I32 x = 0, y = 0, w = 0, h = 0;
+ editor_toolbar_sub_rect( bar, bar->open_root, i, &x, &y, &w, &h );
+ if( editor_toolbar_pt_in_rect( mx, my, x, y, w, h ) ) {
+ hit.root = bar->open_root;
+ hit.sub = i;
+ return hit;
+ }
+ }
+ }
+ }
+
+ return hit;
+}
+
+static U8 editor_toolbar_entry_enabled( const EDITOR_MENUBAR_ENTRY* root, const EDITOR_MENUBAR_ENTRY* entry ) {
+ if( !root || !entry )
+ return 0;
+
+ if( !strcmp( root->text, "edit" ) && !strcmp( entry->text, "undo" ) )
+ return editor && editor->undo_actions.size > 0;
+ if( !strcmp( root->text, "edit" ) && !strcmp( entry->text, "redo" ) )
+ return editor && editor->redo_actions.size > 0;
+
+ return 1;
+}
+
+static void editor_toolbar_invoke( const EDITOR_MENUBAR_ENTRY* root, const EDITOR_MENUBAR_ENTRY* entry ) {
+ if( !root || !entry || !editor )
+ return;
+ if( !editor_toolbar_entry_enabled( root, entry ) )
+ return;
+
+ if( !strcmp( root->text, "file" ) && !strcmp( entry->text, "save" ) ) {
+ editor_save_map( editor );
+ return;
+ }
+ if( !strcmp( root->text, "edit" ) && !strcmp( entry->text, "undo" ) ) {
+ editor_undo( editor );
+ return;
+ }
+ if( !strcmp( root->text, "edit" ) && !strcmp( entry->text, "redo" ) ) {
+ editor_redo( editor );
+ return;
+ }
+ if( !strcmp( root->text, "view" ) && !strcmp( entry->text, "2d view" ) ) {
+ editor_set_view_mode( EDITOR_VIEWMODE_2D );
+ return;
+ }
+ if( !strcmp( root->text, "view" ) && !strcmp( entry->text, "3d view" ) ) {
+ editor_set_view_mode( EDITOR_VIEWMODE_3D );
+ return;
+ }
+ if( !strcmp( root->text, "view" ) && !strcmp( entry->text, "simulation" ) ) {
+ editor_set_view_mode( EDITOR_VIEWMODE_SIM );
+ return;
+ }
+ if( !strcmp( root->text, "tools" ) ) {
+ if( !strcmp( entry->text, "none" ) ) settool( EDITOR_TOOL_NONE );
+ else if( !strcmp( entry->text, "select" ) ) settool( EDITOR_TOOL_SELECT );
+ else if( !strcmp( entry->text, "wall" ) ) settool( EDITOR_TOOL_WALL );
+ else if( !strcmp( entry->text, "poly" ) ) settool( EDITOR_TOOL_POLY );
+ else if( !strcmp( entry->text, "sprite" ) ) settool( EDITOR_TOOL_SPRITE );
+ else if( !strcmp( entry->text, "ent" ) ) settool( EDITOR_TOOL_ENT );
+ }
+}
+
+static void gui_editor_toolbar_draw_fn( void* ptr ) {
+ GUI_EDITOR_TOOLBAR* bar = (GUI_EDITOR_TOOLBAR*)ptr;
+ I32 x = gui_relx( bar );
+ I32 y = gui_rely( bar );
+ I32 mx = 0, my = 0;
+ if( editor_menu_hover_mask_active ) {
+ mx = editor_menu_hover_real_x;
+ my = editor_menu_hover_real_y;
+ } else {
+ gui_cursor_pos( &mx, &my );
+ }
+
+ U8 m1 = gui_mbutton_down( GUI_MBTNLEFT );
+ EDITOR_MENUBAR_HIT hit = editor_toolbar_hit_test( bar, mx, my );
+ CLR hover_fill = CLR::blend( ui_clr.border, ui_clr.bg, 0.70f );
+ CLR active_fill = CLR::blend( ui_clr.bg_sec, ui_clr.border, 0.22f );
+
+ CLR border = gui_is_fg_window( bar ) ? ui_clr.border : ui_clr.border_inactive;
+ gui_draw_frect( x, y, bar->w, bar->h, ui_clr.bg_sec );
+
+ for( I32 i = 0; i < (I32)bar->entries.size; ++i ) {
+ I32 rx = 0, ry = 0, rw = 0, rh = 0;
+ editor_toolbar_root_rect( bar, i, &rx, &ry, &rw, &rh );
+ U8 is_hover = hit.root == i && hit.sub == -1;
+ U8 is_active = bar->held && bar->held_root == i && bar->held_sub == -1 && m1;
+ U8 is_open = bar->open_root == i;
+
+ CLR fill = ui_clr.bg_sec;
+ if( is_open || is_hover )
+ fill = hover_fill;
+ if( is_active )
+ fill = active_fill;
+
+ if( is_open || is_active || is_hover )
+ gui_draw_frect( rx, ry, rw, rh, fill );
+ gui_draw_str( rx + 6, y + bar->h / 2 - 8, ALIGN_L, FNT_JPN12, ui_clr.txt, "%s", bar->entries[i].text );
+ }
+
+ if( bar->open_root < 0 || bar->open_root >= (I32)bar->entries.size )
+ return;
+
+ EDITOR_MENUBAR_ENTRY* root = &bar->entries[bar->open_root];
+ if( root->type != EDITOR_MENUBAR_ENTRY_SUBMENU )
+ return;
+
+ if( !root->entries.size )
+ return;
+
+ I32 panel_x = 0, panel_y = 0, panel_w = 0, panel_h = 0;
+ editor_toolbar_sub_rect( bar, bar->open_root, 0, &panel_x, &panel_y, &panel_w, &panel_h );
+ I32 row_h = panel_h;
+ gui_draw_frect( panel_x, panel_y, panel_w, row_h * root->entries.size, border );
+ gui_draw_frect( panel_x + 1, panel_y + 1, max( 1, panel_w - 2 ), max( 1, row_h * (I32)root->entries.size - 2 ), ui_clr.bg_sec );
+
+ for( I32 i = 0; i < (I32)root->entries.size; ++i ) {
+ EDITOR_MENUBAR_ENTRY* entry = &root->entries[i];
+ I32 rx = 0, ry = 0, rw = 0, rh = 0;
+ editor_toolbar_sub_rect( bar, bar->open_root, i, &rx, &ry, &rw, &rh );
+
+ U8 enabled = editor_toolbar_entry_enabled( root, entry );
+ U8 is_hover = hit.root == bar->open_root && hit.sub == i;
+ U8 is_active = bar->held && bar->held_root == bar->open_root && bar->held_sub == i && m1;
+
+ CLR fill = ui_clr.bg_sec;
+ if( enabled && is_active )
+ fill = active_fill;
+ else if( is_hover )
+ fill = hover_fill;
+ gui_draw_frect( rx + 1, ry + 1, max( 1, rw - 2 ), max( 1, rh - 2 ), fill );
+
+ CLR txt_clr = enabled ? ui_clr.txt : ui_clr.txt_inactive;
+ gui_draw_str( rx + 8, ry + rh / 2 - 8, ALIGN_L, FNT_JPN12, txt_clr, "%s", entry->text );
+ }
+}
+
+static void gui_editor_toolbar_input_fn( void* ptr ) {
+ GUI_EDITOR_TOOLBAR* bar = (GUI_EDITOR_TOOLBAR*)ptr;
+ I32 mx, my;
+ gui_cursor_pos( &mx, &my );
+ U8 m1 = gui_mbutton_down( GUI_MBTNLEFT );
+ EDITOR_MENUBAR_HIT hit = editor_toolbar_hit_test( bar, mx, my );
+
+ if( m1 ) {
+ if( !bar->held ) {
+ bar->held = 1;
+ bar->held_root = hit.root;
+ bar->held_sub = hit.sub;
+ if( hit.root == -1 && editor_toolbar_menu_open( bar ) )
+ bar->open_root = -1;
+ }
+ return;
+ }
+
+ if( bar->held && bar->held_root == hit.root && bar->held_sub == hit.sub ) {
+ if( hit.sub >= 0 ) {
+ EDITOR_MENUBAR_ENTRY* root = &bar->entries[hit.root];
+ EDITOR_MENUBAR_ENTRY* sub = &root->entries[hit.sub];
+ editor_toolbar_invoke( root, sub );
+ bar->open_root = -1;
+ } else if( hit.root >= 0 ) {
+ EDITOR_MENUBAR_ENTRY* root = &bar->entries[hit.root];
+ if( root->type == EDITOR_MENUBAR_ENTRY_SUBMENU ) {
+ if( bar->open_root == hit.root ) bar->open_root = -1;
+ else bar->open_root = hit.root;
+ } else {
+ editor_toolbar_invoke( root, root );
+ bar->open_root = -1;
+ }
+ }
+ }
+
+ bar->held = 0;
+ bar->held_root = -1;
+ bar->held_sub = -1;
+}
+
+GUI_EDITOR_TOOLBAR* gui_editor_toolbar( I32 x, I32 y, I32 w, I32 h, const char* name ) {
+ if( !gui_check_target() )
+ return 0;
+
+ GUI_EDITOR_TOOLBAR* bar = new GUI_EDITOR_TOOLBAR;
+ bar->x = x;
+ bar->y = y;
+ bar->w = w;
+ bar->h = h;
+ bar->xbound = w;
+ bar->ybound = h + 120;
+ bar->draw_fn = gui_editor_toolbar_draw_fn;
+ bar->input_fn = gui_editor_toolbar_input_fn;
+ bar->held = 0;
+ bar->held_root = -1;
+ bar->held_sub = -1;
+ bar->open_root = -1;
+ strcpy( bar->name, name );
+ editor_toolbar_init_entries( bar );
+
+ GUI_VIEW* parent = gui_get_view();
+ bar->parent = parent;
+ parent->children.push( bar );
+ return bar;
+}
+
+void editor_create_header_controls( GAME_EDITOR* e ) {
+ GAME_EDITOR::EDITOR_GUI* egui = &e->gui;
+ egui->header_toolbar = gui_editor_toolbar( 1, EDITOR_LAYOUT_MENU_Y, e->wnd->w - 3, EDITOR_LAYOUT_MENU_H, "editor toolbar" );
+ egui->header_back = gui_button( 10, EDITOR_LAYOUT_NAV_Y, 92, 20, "[ back ]", editor_header_back_cb );
+ egui->header_viewtype_label = gui_label( 250, EDITOR_LAYOUT_NAV_Y + 4, "view type:" );
+ egui->header_viewtype = gui_editor_viewtype_segment( 320, EDITOR_LAYOUT_NAV_Y, 186, 20, "viewtype segment" );
+ egui->header_mode_2d = gui_button( 500, EDITOR_LAYOUT_NAV_Y, 92, 20, "[ 2d view ]", editor_header_mode_2d_cb );
+ egui->header_mode_3d = gui_button( 600, EDITOR_LAYOUT_NAV_Y, 92, 20, "[ 3d view ]", editor_header_mode_3d_cb );
+ egui->header_mode_sim = gui_button( 700, EDITOR_LAYOUT_NAV_Y, 92, 20, "[ simulation ]", editor_header_mode_sim_cb );
+}
diff --git a/src/editor/editor_viewtype_segment.cpp b/src/editor/editor_viewtype_segment.cpp
new file mode 100644
index 0000000..7cd5c2d
--- /dev/null
+++ b/src/editor/editor_viewtype_segment.cpp
@@ -0,0 +1,119 @@
+#include "editor_gui_internal.h"
+
+static void editor_header_viewtype_segment_widths( I32 w, I32* w0, I32* w1, I32* w2 ) {
+ I32 base = max( 1, w / 3 );
+ I32 rem = max( 0, w - base * 3 );
+ *w0 = base + ( rem > 0 ? 1 : 0 );
+ *w1 = base + ( rem > 1 ? 1 : 0 );
+ *w2 = max( 1, w - *w0 - *w1 );
+}
+
+static I32 editor_header_viewtype_segment_index( GUI_EDITOR_VIEWTYPE_SEGMENT* seg, I32 mx, I32 my ) {
+ I32 x = gui_relx( seg );
+ I32 y = gui_rely( seg );
+ if( mx < x || mx >= x + seg->w || my < y || my >= y + seg->h )
+ return -1;
+
+ I32 w0 = 0, w1 = 0, w2 = 0;
+ editor_header_viewtype_segment_widths( seg->w, &w0, &w1, &w2 );
+ I32 rel = mx - x;
+ if( rel < w0 ) return 0;
+ if( rel < w0 + w1 ) return 1;
+ return 2;
+}
+
+static void gui_editor_viewtype_segment_draw_fn( void* ptr ) {
+ GUI_EDITOR_VIEWTYPE_SEGMENT* seg = (GUI_EDITOR_VIEWTYPE_SEGMENT*)ptr;
+ I32 x = gui_relx( seg );
+ I32 y = gui_rely( seg );
+ I32 mx, my;
+ gui_cursor_pos( &mx, &my );
+ I32 hover_seg = editor_header_viewtype_segment_index( seg, mx, my );
+ I32 active_seg = editor->gui.view2d_type;
+ U8 down = gui_mbutton_down( GUI_MBTNLEFT );
+
+ CLR border = gui_is_fg_window( seg ) ? ui_clr.border : ui_clr.border_inactive;
+ I32 inner_x = x + 1;
+ I32 inner_y = y + 1;
+ I32 inner_w = max( 1, seg->w - 2 );
+ I32 inner_h = max( 1, seg->h - 2 );
+ gui_draw_frect( inner_x, inner_y, inner_w, inner_h, ui_clr.bg_sec );
+
+ I32 w0 = 0, w1 = 0, w2 = 0;
+ editor_header_viewtype_segment_widths( inner_w, &w0, &w1, &w2 );
+ I32 segx[3] = { inner_x, inner_x + w0, inner_x + w0 + w1 };
+ I32 segw[3] = { w0, w1, w2 };
+ const char* txts[3] = { "x/y", "x/z", "y/z" };
+
+ for( I32 i = 0; i < 3; ++i ) {
+ U8 selected = i == active_seg;
+ U8 hover = hover_seg == i;
+
+ CLR fill = ui_clr.bg_sec;
+ if( selected )
+ fill = CLR::blend( ui_clr.bg_alt, ui_clr.border, 0.15f );
+ else if( hover )
+ fill = CLR::blend( ui_clr.bg_alt, ui_clr.bg_sec, 0.5f );
+
+ if( selected && down && hover )
+ fill = CLR::blend( fill, ui_clr.bg_sec, 0.35f );
+
+ gui_draw_frect( segx[i], inner_y, max( 1, segw[i] ), inner_h, fill );
+
+ CLR txt = selected ? ui_clr.txt : CLR::blend( ui_clr.txt, ui_clr.bg_sec, 0.45f );
+ gui_draw_str( segx[i] + segw[i] / 2, y + ( seg->h / 2 ) - 7, ALIGN_C, FNT_JPN12, txt, txts[i] );
+ }
+
+ I32 split1_x = inner_x + w0;
+ I32 split2_x = inner_x + w0 + w1;
+ gui_draw_line( split1_x, inner_y, split1_x, inner_y + inner_h - 1, border );
+ gui_draw_line( split2_x, inner_y, split2_x, inner_y + inner_h - 1, border );
+ gui_draw_rect( x, y, seg->w, seg->h, border );
+}
+
+static void gui_editor_viewtype_segment_input_fn( void* ptr ) {
+ GUI_EDITOR_VIEWTYPE_SEGMENT* seg = (GUI_EDITOR_VIEWTYPE_SEGMENT*)ptr;
+ I32 mx, my;
+ gui_cursor_pos( &mx, &my );
+ U8 m1 = gui_mbutton_down( GUI_MBTNLEFT );
+ I32 cur_seg = editor_header_viewtype_segment_index( seg, mx, my );
+
+ if( m1 ) {
+ if( !seg->held ) {
+ seg->held = 1;
+ seg->held_seg = cur_seg;
+ }
+ return;
+ }
+
+ if( seg->held && seg->held_seg >= 0 && seg->held_seg == cur_seg ) {
+ editor->gui.view2d_type = seg->held_seg;
+ editor_layout_map_view( editor );
+ }
+
+ seg->held = 0;
+ seg->held_seg = -1;
+}
+
+GUI_EDITOR_VIEWTYPE_SEGMENT* gui_editor_viewtype_segment( I32 x, I32 y, I32 w, I32 h, const char* name ) {
+ if( !gui_check_target() )
+ return 0;
+
+ GUI_EDITOR_VIEWTYPE_SEGMENT* seg = new GUI_EDITOR_VIEWTYPE_SEGMENT;
+ seg->x = x;
+ seg->y = y;
+ seg->w = w;
+ seg->h = h;
+ seg->xbound = w;
+ seg->ybound = h;
+ seg->draw_fn = gui_editor_viewtype_segment_draw_fn;
+ seg->input_fn = gui_editor_viewtype_segment_input_fn;
+ seg->held = 0;
+ seg->held_seg = -1;
+ strcpy( seg->name, name );
+
+ GUI_VIEW* parent = gui_get_view();
+ seg->parent = parent;
+ parent->children.push( seg );
+ return seg;
+}
diff --git a/src/editor/editor_window.cpp b/src/editor/editor_window.cpp
new file mode 100644
index 0000000..51ca67d
--- /dev/null
+++ b/src/editor/editor_window.cpp
@@ -0,0 +1,285 @@
+#include "editor_gui_internal.h"
+
+#include "../game.h"
+
+void gui_editorwindow_draw_fn( void* ptr ) {
+ GUI_EDITORWINDOW* wnd = (GUI_EDITORWINDOW*)ptr;
+ editor_raise_header_toolbar( editor );
+
+ GUI_BASE* toolbar = ( editor && editor->map ) ? editor->gui.header_toolbar : 0;
+ GUI_EDITOR_TOOLBAR* tbar = (GUI_EDITOR_TOOLBAR*)toolbar;
+ U8 menu_open = editor_toolbar_menu_open( tbar );
+
+ F32 saved_mx = input.mouse.pos.x;
+ F32 saved_my = input.mouse.pos.y;
+ if( menu_open ) {
+ editor_menu_hover_mask_active = 1;
+ editor_menu_hover_real_x = (I32)saved_mx;
+ editor_menu_hover_real_y = (I32)saved_my;
+ input.mouse.pos.x = -100000.f;
+ input.mouse.pos.y = -100000.f;
+ } else {
+ editor_menu_hover_mask_active = 0;
+ }
+
+ CLR clr = gui_is_fg_window( wnd ) ? ui_clr.border : ui_clr.border_inactive;
+ gui_draw_frect( wnd->x, wnd->y, wnd->w, wnd->h, clr );
+ gui_draw_frect( wnd->x + 1, wnd->y + 1, wnd->w - 2, wnd->h - 2, ui_clr.bg );
+
+ wnd->children.each( fn( GUI_BASE** ptr ) {
+ GUI_BASE* it = *ptr;
+ if( !it->enabled ) return;
+ if( it->draw_fn ) it->draw_fn( it );
+ else dlog( "gui_editorwindow_draw_fn(): child %p has no draw_fn", it );
+ } );
+
+ input.mouse.pos.x = saved_mx;
+ input.mouse.pos.y = saved_my;
+ editor_menu_hover_mask_active = 0;
+}
+
+static void gui_editorwindow_input_fn( void* ptr ) {
+ GUI_EDITORWINDOW* wnd = (GUI_EDITORWINDOW*)ptr;
+ editor_raise_header_toolbar( editor );
+
+ GUI_BASE* toolbar = ( editor && editor->map ) ? editor->gui.header_toolbar : 0;
+ if( toolbar && toolbar->enabled && toolbar->input_fn )
+ toolbar->input_fn( toolbar );
+
+ GUI_EDITOR_TOOLBAR* tbar = (GUI_EDITOR_TOOLBAR*)toolbar;
+ if( editor_toolbar_menu_open( tbar ) )
+ return;
+
+ wnd->children.each( fn( GUI_BASE** childptr ) {
+ GUI_BASE* child = *childptr;
+ if( !child || child == toolbar || !child->enabled || !child->input_fn )
+ return;
+ child->input_fn( child );
+ } );
+}
+
+GUI_EDITORWINDOW* gui_editorwindow_create( I32 w, I32 h ) {
+ GUI_EDITORWINDOW* wnd = new GUI_EDITORWINDOW;
+ wnd->x = 0;
+ wnd->y = 0;
+ wnd->xbound = wnd->w = w;
+ wnd->ybound = wnd->h = h;
+ wnd->locked = 1;
+ wnd->draw_fn = gui_editorwindow_draw_fn;
+ wnd->input_fn = gui_editorwindow_input_fn;
+ strcpy( wnd->name, "EDITORWINDOW" );
+
+ _gui.windows.push( wnd );
+ gui_set_window( wnd );
+ gui_set_view( 0 );
+ gui_view( 1, 1, w - 2, h - 2 );
+ return wnd;
+}
+
+GUI_EDITORWINDOW* gui_editorwindow( I32 w, I32 h ) {
+ GUI_EDITORWINDOW* wnd = gui_editorwindow_create( w, h );
+ GAME_EDITOR* e = editor;
+ LIST<GUI_LIST_ENTRY>* map_list = editor_get_map_list( e );
+ gui_list( wnd->w / 2 - 100, EDITOR_LAYOUT_CONTENT_Y, 200, 200, "map list", map_list, &editor->gui.map_select );
+ gui_button( wnd->w / 2 - 100, EDITOR_LAYOUT_CONTENT_Y + 220, 95, 25, "new map", editor_new_map_cb );
+ gui_button( wnd->w / 2 + 5, EDITOR_LAYOUT_CONTENT_Y + 220, 95, 25, "load map", editor_load_map_cb );
+ return wnd;
+}
+
+void editor_update_properties_column( GAME_EDITOR* e ) {
+ GAME_EDITOR::EDITOR_GUI* egui = &e->gui;
+ gui_editor_propview_update( egui->props );
+}
+
+void editor_create_properties_column( GAME_EDITOR* e ) {
+ GAME_EDITOR::EDITOR_GUI* egui = &e->gui;
+ egui->props = gui_editor_propview( 10, EDITOR_LAYOUT_CONTENT_Y, 300, 460 );
+ gui_editor_propview_select( egui->props, e->map, EDITOR_SELECT_ORIGIN );
+}
+
+void editor_create_auxiliary_panels( GAME_EDITOR* e ) {
+ GAME_EDITOR::EDITOR_GUI* egui = &e->gui;
+ egui->assets = gui_editor_infobox( 10, EDITOR_LAYOUT_CONTENT_Y + 310, 300, 180, EDITOR_INFOBOX_ASSETS, "EDITOR_ASSETS_VIEW" );
+ egui->status = gui_editor_infobox( 10, 568, 780, EDITOR_LAYOUT_STATUS_H, EDITOR_INFOBOX_STATUS, "EDITOR_STATUS_VIEW" );
+}
+
+void editor_update_toolview( GAME_EDITOR* e ) {
+ GAME_EDITOR::EDITOR_GUI* egui = &e->gui;
+ gui_editor_toolview_update( egui->tool );
+}
+
+void settool( U8 t ) {
+ editor->tool.type = t;
+ if( editor->gui.v2d )
+ editor->gui.v2d->poly_drag = 0;
+ if( editor->gui.tool )
+ editor_update_toolview( editor );
+}
+
+const char* editor_tool_name() {
+ switch( editor->tool.type ) {
+ case EDITOR_TOOL_SELECT: return "select";
+ case EDITOR_TOOL_WALL: return "wall";
+ case EDITOR_TOOL_POLY: return "poly";
+ case EDITOR_TOOL_SPRITE: return "sprite";
+ case EDITOR_TOOL_ENT: return "ent";
+ default: return "none";
+ }
+}
+
+static U8 EDITOR_TOOL_BUTTON_TYPES[] = {
+ EDITOR_TOOL_NONE,
+ EDITOR_TOOL_SELECT,
+ EDITOR_TOOL_WALL,
+ EDITOR_TOOL_POLY,
+ EDITOR_TOOL_SPRITE,
+ EDITOR_TOOL_ENT
+};
+
+static void settool_btn_cb( void* ptr ) {
+ GUI_BUTTON* btn = (GUI_BUTTON*)ptr;
+ if( !btn || !btn->extra )
+ return;
+
+ settool( *(U8*)btn->extra );
+}
+
+void editor_create_toolview_column( GAME_EDITOR* e ) {
+ GAME_EDITOR::EDITOR_GUI* egui = &e->gui;
+
+ I32 x = 320;
+ I32 y = EDITOR_LAYOUT_CONTENT_Y + EDITOR_LAYOUT_VIEW_DEFAULT_H + EDITOR_LAYOUT_VIEW_TOOL_GAP + EDITOR_LAYOUT_TOOL_BTN_TOP_GAP;
+ I32 off = 23;
+ const char* labels[] = { "none", "select", "wall", "poly", "sprite", "ent" };
+
+ for( U32 i = 0; i < sizeof( labels ) / sizeof( labels[0] ); ++i ) {
+ GUI_BUTTON* btn = gui_button( x, y, 45, 20, labels[i], settool_btn_cb );
+ btn->extra = &EDITOR_TOOL_BUTTON_TYPES[i];
+ y += off;
+ }
+
+ egui->tool = gui_editor_toolview( 370, y - EDITOR_LAYOUT_TOOL_BTN_TOP_GAP, 300, 150 );
+ gui_editor_toolview_update( egui->tool );
+}
+
+void editor_create_game_view_column( GAME_EDITOR* e ) {
+ GAME_EDITOR::EDITOR_GUI* egui = &e->gui;
+ I32 x = 320, y = EDITOR_LAYOUT_CONTENT_Y;
+ egui->v2d = gui_editor_2dview( x, y, 468, 370 );
+ egui->v3d = gui_editor_3dview( x, y, 468, 370 );
+ egui->v2d->enabled = 1;
+ egui->v3d->enabled = 0;
+}
+
+void editor_create_map_view( GAME_EDITOR* e ) {
+ if( !e->map ) {
+ dlog( "editor_create_map_views() : no map loaded\n" );
+ return;
+ }
+
+ GUI_EDITORWINDOW* w = e->wnd;
+ w->children.each( fn( GUI_BASE** ptr ) {
+ gui_free( *ptr );
+ } );
+ w->children.clear();
+ editor_clear_gui_state_refs( e );
+
+ e->gui.view2d_type = EDITOR_2DVIEW_TOP_DOWN;
+ e->gui.view_mode = EDITOR_VIEWMODE_2D;
+
+ gui_set_window( w );
+ gui_set_view( 0 );
+ gui_view( 1, 1, w->w - 2, w->h - 2 );
+
+ editor_create_header_controls( e );
+ editor_create_properties_column( e );
+ editor_create_toolview_column( e );
+ editor_create_game_view_column( e );
+ editor_create_auxiliary_panels( e );
+ editor_layout_map_view( e );
+}
+
+void editor_resize( GAME_EDITOR* e, I32 w, I32 h ) {
+ if( !e || !e->wnd )
+ return;
+
+ w = max( 1, w );
+ h = max( 1, h );
+
+ GUI_EDITORWINDOW* wnd = e->wnd;
+ if( wnd->w == w && wnd->h == h )
+ return;
+
+ editor_set_bounds( wnd, 0, 0, w, h );
+ editor_resize_base_view( wnd );
+
+ if( e->map ) editor_layout_map_view( e );
+ else editor_layout_start_menu( e );
+
+ if( !e->gui.new_map_popup )
+ return;
+
+ GUI_WINDOW* popup = e->gui.new_map_popup;
+ if( popup->x > w - 5 ) popup->x = w - 5;
+ if( popup->x + popup->w < 5 ) popup->x = 5 - popup->w;
+ if( popup->y > h - 5 ) popup->y = h - 5;
+ if( popup->y + popup->h < 5 ) popup->y = 5 - popup->h;
+}
+
+void close_new_map_popup( void* ) {
+ gui_push_callback( pfn( void* ) {
+ GUI_WINDOW* popup = editor->gui.new_map_popup;
+ if( !popup )
+ return;
+
+ gui_free( popup );
+ editor->gui.new_map_popup = 0;
+ } );
+}
+
+void editor_new_map_cb( void* ptr ) {
+ if( editor->gui.new_map_popup )
+ return;
+
+ GUI_WINDOW* cwnd = gui_get_window();
+ GUI_VIEW* view = gui_get_view();
+
+ I32 wx = 250;
+ I32 wy = 140;
+ I32 ww = 300;
+ I32 wh = 200;
+
+ editor->gui.new_map_popup = gui_window( wx, wy, ww, wh );
+ gui_title( "new map" );
+ GUI_TEXTBOX* tb = gui_textbox( 10, 20, ww - 20, 20, "name", 32 );
+ tb->active = 1;
+
+ gui_button( ww - 50 - 70, wh - 20 - 10, 50, 20, "ok", pfn( void* ptr ) {
+ GUI_BUTTON* btn = (GUI_BUTTON*)ptr;
+ GUI_TEXTBOX* name = (GUI_TEXTBOX*)gui_find_node( btn->parent, "name" );
+ if( !name )
+ return;
+
+ editor_new_map( editor, name->value );
+ close_new_map_popup( 0 );
+ } );
+
+ gui_button( ww - 50 - 10, wh - 20 - 10, 50, 20, "cancel", close_new_map_popup );
+ gui_set_window( cwnd );
+ gui_set_view( view );
+}
+
+void editor_load_map_cb( void* ptr ) {
+ GUI_LIST* list = (GUI_LIST*)gui_find_node( editor->wnd, "map list" );
+ GUI_LIST_ENTRY* e = gui_list_get_selected( list );
+ if( !e )
+ return;
+
+ char full_path[256];
+ sprintf( full_path, "../assets/maps/%s", e->title );
+
+ if( editor->map )
+ editor_close( editor );
+
+ editor_load_map( editor, full_path );
+}
diff --git a/src/editor/gui.cpp b/src/editor/gui.cpp
deleted file mode 100644
index 088421d..0000000
--- a/src/editor/gui.cpp
+++ /dev/null
@@ -1,1280 +0,0 @@
-#include "editor.h"
-#include "../game/world/bsp.h"
-#include "../game/assets.h"
-#include "../game.h"
-#include "../render/gl_2d.h"
-
-const I32 EDITOR_LAYOUT_MARGIN = 10;
-const I32 EDITOR_LAYOUT_TITLE_OFFSET = 15;
-const I32 EDITOR_LAYOUT_MENU_Y = 1;
-const I32 EDITOR_LAYOUT_MENU_H = 22;
-const I32 EDITOR_LAYOUT_NAV_Y = EDITOR_LAYOUT_MENU_Y + EDITOR_LAYOUT_MENU_H + 4;
-const I32 EDITOR_LAYOUT_CONTENT_Y = EDITOR_LAYOUT_NAV_Y + 28;
-const I32 EDITOR_LAYOUT_COLUMN_GAP = 10;
-const I32 EDITOR_LAYOUT_VIEW_TOOL_GAP = 17;
-const I32 EDITOR_LAYOUT_TOOL_BTN_TOP_GAP = 3;
-const I32 EDITOR_LAYOUT_TOOL_PANEL_GAP = 5;
-const I32 EDITOR_LAYOUT_LEFT_BOX_GAP = 10;
-const I32 EDITOR_LAYOUT_STATUS_H = 22;
-const I32 EDITOR_LAYOUT_STATUS_GAP = 6;
-const I32 EDITOR_LAYOUT_TOOL_PANEL_W = 300;
-const I32 EDITOR_LAYOUT_TOOL_PANEL_H = 150;
-const I32 EDITOR_LAYOUT_PROPS_DEFAULT_W = 300;
-const I32 EDITOR_LAYOUT_PROPS_DEFAULT_H = 300;
-const I32 EDITOR_LAYOUT_VIEW_DEFAULT_H = 370;
-const I32 EDITOR_LAYOUT_PROPS_MIN_W = 200;
-const I32 EDITOR_LAYOUT_PROPS_MIN_H = 120;
-const I32 EDITOR_LAYOUT_ASSETS_MIN_H = 120;
-const I32 EDITOR_LAYOUT_VIEW_MIN_H = 140;
-const I32 EDITOR_LAYOUT_RIGHT_MIN_W = 260;
-
-static void editor_layout_map_view( GAME_EDITOR* e );
-static U8 editor_menu_hover_mask_active = 0;
-static I32 editor_menu_hover_real_x = 0;
-static I32 editor_menu_hover_real_y = 0;
-
-static void editor_toolbar_set_open( GUI_EDITOR_TOOLBAR* bar, U8 file_open, U8 edit_open );
-
-static void editor_toolbar_close_menu() {
- GUI_EDITOR_TOOLBAR* bar = (GUI_EDITOR_TOOLBAR*)editor->gui.header_toolbar;
- editor_toolbar_set_open( bar, 0, 0 );
-}
-
-static void editor_apply_view_mode( GAME_EDITOR* e ) {
- if( !e || !e->gui.v2d || !e->gui.v3d )
- return;
-
- U8 mode = e->gui.view_mode;
- if( mode == EDITOR_VIEWMODE_2D ) {
- e->gui.v2d->enabled = 1;
- e->gui.v3d->enabled = 0;
- return;
- }
-
- e->gui.v2d->enabled = 0;
- e->gui.v3d->enabled = 1;
-}
-
-static void editor_header_back_cb( void* ) {
- editor_toolbar_close_menu();
- editor_close( editor );
-}
-
-static void editor_set_view_mode( I32 mode ) {
- editor_toolbar_close_menu();
- editor->gui.view_mode = mode;
- editor_apply_view_mode( editor );
- editor_layout_map_view( editor );
-}
-
-static void editor_header_mode_2d_cb( void* ) {
- editor_set_view_mode( EDITOR_VIEWMODE_2D );
-}
-
-static void editor_header_mode_3d_cb( void* ) {
- editor_set_view_mode( EDITOR_VIEWMODE_3D );
-}
-
-static void editor_header_mode_sim_cb( void* ) {
- editor_set_view_mode( EDITOR_VIEWMODE_SIM );
-}
-
-static void editor_header_viewtype_segment_widths( I32 w, I32* w0, I32* w1, I32* w2 ) {
- I32 base = max( 1, w / 3 );
- I32 rem = max( 0, w - base * 3 );
- *w0 = base + ( rem > 0 ? 1 : 0 );
- *w1 = base + ( rem > 1 ? 1 : 0 );
- *w2 = max( 1, w - *w0 - *w1 );
-}
-
-static I32 editor_header_viewtype_segment_index( GUI_EDITOR_VIEWTYPE_SEGMENT* seg, I32 mx, I32 my ) {
- I32 x = gui_relx( seg );
- I32 y = gui_rely( seg );
- if( mx < x || mx >= x + seg->w || my < y || my >= y + seg->h )
- return -1;
-
- I32 w0 = 0, w1 = 0, w2 = 0;
- editor_header_viewtype_segment_widths( seg->w, &w0, &w1, &w2 );
- I32 rel = mx - x;
- if( rel < w0 ) return 0;
- if( rel < w0 + w1 ) return 1;
- return 2;
-}
-
-static void gui_editor_viewtype_segment_draw_fn( void* ptr ) {
- GUI_EDITOR_VIEWTYPE_SEGMENT* seg = (GUI_EDITOR_VIEWTYPE_SEGMENT*)ptr;
- I32 x = gui_relx( seg );
- I32 y = gui_rely( seg );
- I32 mx, my;
- gui_cursor_pos( &mx, &my );
- I32 hover_seg = editor_header_viewtype_segment_index( seg, mx, my );
- I32 active_seg = editor->gui.view2d_type;
- U8 down = gui_mbutton_down( GUI_MBTNLEFT );
-
- CLR border = gui_is_fg_window( seg ) ? ui_clr.border : ui_clr.border_inactive;
- I32 inner_x = x + 1;
- I32 inner_y = y + 1;
- I32 inner_w = max( 1, seg->w - 2 );
- I32 inner_h = max( 1, seg->h - 2 );
- gui_draw_frect( inner_x, inner_y, inner_w, inner_h, ui_clr.bg_sec );
-
- I32 w0 = 0, w1 = 0, w2 = 0;
- editor_header_viewtype_segment_widths( inner_w, &w0, &w1, &w2 );
- I32 segx[3] = { inner_x, inner_x + w0, inner_x + w0 + w1 };
- I32 segw[3] = { w0, w1, w2 };
- const char* txts[3] = { "x/y", "x/z", "y/z" };
-
- for( I32 i = 0; i < 3; ++i ) {
- U8 selected = i == active_seg;
- U8 hover = hover_seg == i;
-
- CLR fill = ui_clr.bg_sec;
- if( selected )
- fill = CLR::blend( ui_clr.bg_alt, ui_clr.border, 0.15f );
- else if( hover )
- fill = CLR::blend( ui_clr.bg_alt, ui_clr.bg_sec, 0.5f );
-
- if( selected && down && hover )
- fill = CLR::blend( fill, ui_clr.bg_sec, 0.35f );
-
- gui_draw_frect( segx[i], inner_y, max( 1, segw[i] ), inner_h, fill );
-
- CLR txt = selected ? ui_clr.txt : CLR::blend( ui_clr.txt, ui_clr.bg_sec, 0.45f );
- gui_draw_str( segx[i] + segw[i] / 2, y + ( seg->h / 2 ) - 7, ALIGN_C, FNT_JPN12, txt, txts[i] );
- }
-
- I32 split1_x = inner_x + w0;
- I32 split2_x = inner_x + w0 + w1;
- gui_draw_line( split1_x, inner_y, split1_x, inner_y + inner_h - 1, border );
- gui_draw_line( split2_x, inner_y, split2_x, inner_y + inner_h - 1, border );
- gui_draw_rect( x, y, seg->w, seg->h, border );
-}
-
-static void gui_editor_viewtype_segment_input_fn( void* ptr ) {
- GUI_EDITOR_VIEWTYPE_SEGMENT* seg = (GUI_EDITOR_VIEWTYPE_SEGMENT*)ptr;
- I32 mx, my;
- gui_cursor_pos( &mx, &my );
- U8 m1 = gui_mbutton_down( GUI_MBTNLEFT );
- I32 cur_seg = editor_header_viewtype_segment_index( seg, mx, my );
-
- if( m1 ) {
- if( !seg->held ) {
- seg->held = 1;
- seg->held_seg = cur_seg;
- }
- return;
- }
-
- if( seg->held && seg->held_seg >= 0 && seg->held_seg == cur_seg ) {
- editor->gui.view2d_type = seg->held_seg;
- editor_layout_map_view( editor );
- }
-
- seg->held = 0;
- seg->held_seg = -1;
-}
-
-static GUI_EDITOR_VIEWTYPE_SEGMENT* gui_editor_viewtype_segment( I32 x, I32 y, I32 w, I32 h, const char* name ) {
- if( !gui_check_target() )
- return 0;
-
- GUI_EDITOR_VIEWTYPE_SEGMENT* seg = new GUI_EDITOR_VIEWTYPE_SEGMENT;
- seg->x = x;
- seg->y = y;
- seg->w = w;
- seg->h = h;
- seg->xbound = w;
- seg->ybound = h;
- seg->draw_fn = gui_editor_viewtype_segment_draw_fn;
- seg->input_fn = gui_editor_viewtype_segment_input_fn;
- seg->held = 0;
- seg->held_seg = -1;
- strcpy( seg->name, name );
-
- GUI_VIEW* parent = gui_get_view();
- seg->parent = parent;
- parent->children.push( seg );
- return seg;
-}
-
-const I32 EDITOR_TOOLBAR_DROPDOWN_W = 74;
-
-static U8 editor_toolbar_pt_in_rect( I32 mx, I32 my, I32 x, I32 y, I32 w, I32 h ) {
- return mx >= x && mx < x + w && my >= y && my < y + h;
-}
-
-static void editor_toolbar_set_open( GUI_EDITOR_TOOLBAR* bar, U8 file_open, U8 edit_open ) {
- if( !bar )
- return;
-
- bar->file_open = file_open;
- bar->edit_open = edit_open;
-}
-
-static void editor_toolbar_item_rect( GUI_EDITOR_TOOLBAR* bar, I32 idx, I32* rx, I32* ry, I32* rw, I32* rh ) {
- static const I32 widths[4] = { 44, 44, 44, 52 };
- I32 x = gui_relx( bar );
- I32 y = gui_rely( bar );
- I32 cx = x + 8;
- for( I32 i = 0; i < idx; ++i )
- cx += widths[i] + 4;
-
- *rx = cx;
- *ry = y + 2;
- *rw = widths[idx];
- *rh = max( 1, bar->h - 4 );
-}
-
-static void editor_toolbar_dropdown_row_rect( GUI_EDITOR_TOOLBAR* bar, I32 anchor_idx, I32 row, I32* rx, I32* ry, I32* rw, I32* rh ) {
- I32 x = 0, y = 0, w = 0, h = 0;
- editor_toolbar_item_rect( bar, anchor_idx, &x, &y, &w, &h );
- *rx = x;
- *rh = max( 18, h );
- *ry = gui_rely( bar ) + bar->h + row * *rh;
- *rw = EDITOR_TOOLBAR_DROPDOWN_W;
-}
-
-static I32 editor_toolbar_hit_test( GUI_EDITOR_TOOLBAR* bar, I32 mx, I32 my ) {
- for( I32 i = 0; i < 4; ++i ) {
- I32 x = 0, y = 0, w = 0, h = 0;
- editor_toolbar_item_rect( bar, i, &x, &y, &w, &h );
- if( editor_toolbar_pt_in_rect( mx, my, x, y, w, h ) )
- return i;
- }
-
- if( bar->file_open ) {
- I32 x = 0, y = 0, w = 0, h = 0;
- editor_toolbar_dropdown_row_rect( bar, EDITOR_TOOLBAR_HIT_FILE, 0, &x, &y, &w, &h );
- if( editor_toolbar_pt_in_rect( mx, my, x, y, w, h ) )
- return EDITOR_TOOLBAR_HIT_SAVE;
- }
-
- if( bar->edit_open ) {
- for( I32 i = 0; i < 2; ++i ) {
- I32 x = 0, y = 0, w = 0, h = 0;
- editor_toolbar_dropdown_row_rect( bar, EDITOR_TOOLBAR_HIT_EDIT, i, &x, &y, &w, &h );
- if( editor_toolbar_pt_in_rect( mx, my, x, y, w, h ) )
- return i == 0 ? EDITOR_TOOLBAR_HIT_UNDO : EDITOR_TOOLBAR_HIT_REDO;
- }
- }
-
- return EDITOR_TOOLBAR_HIT_NONE;
-}
-
-static void gui_editor_toolbar_draw_fn( void* ptr ) {
- GUI_EDITOR_TOOLBAR* bar = (GUI_EDITOR_TOOLBAR*)ptr;
- I32 x = gui_relx( bar );
- I32 y = gui_rely( bar );
- I32 mx, my;
- if( editor_menu_hover_mask_active ) {
- mx = editor_menu_hover_real_x;
- my = editor_menu_hover_real_y;
- } else {
- gui_cursor_pos( &mx, &my );
- }
- U8 m1 = gui_mbutton_down( GUI_MBTNLEFT );
- I32 hover = editor_toolbar_hit_test( bar, mx, my );
- CLR hover_fill = CLR::blend( ui_clr.border, ui_clr.bg, 0.70f );
- CLR active_fill = CLR::blend( ui_clr.bg_sec, ui_clr.border, 0.22f );
-
- CLR border = gui_is_fg_window( bar ) ? ui_clr.border : ui_clr.border_inactive;
- gui_draw_frect( x, y, bar->w, bar->h, ui_clr.bg_sec );
-
- static const char* labels[4] = { "file", "edit", "view", "tools" };
- for( I32 i = 0; i < 4; ++i ) {
- I32 rx = 0, ry = 0, rw = 0, rh = 0;
- editor_toolbar_item_rect( bar, i, &rx, &ry, &rw, &rh );
- U8 is_hover = hover == i;
- U8 is_active = bar->held && bar->held_item == i && m1;
- U8 is_open = ( bar->file_open && i == EDITOR_TOOLBAR_HIT_FILE )
- || ( bar->edit_open && i == EDITOR_TOOLBAR_HIT_EDIT );
-
- CLR fill = ui_clr.bg_sec;
- if( is_open )
- fill = hover_fill;
- else if( is_active )
- fill = active_fill;
- else if( is_hover )
- fill = hover_fill;
-
- if( is_open || is_active || is_hover )
- gui_draw_frect( rx, ry, rw, rh, fill );
- gui_draw_str( rx + 6, y + bar->h / 2 - 8, ALIGN_L, FNT_JPN12, ui_clr.txt, labels[i] );
- }
-
- if( bar->file_open ) {
- I32 rx = 0, ry = 0, rw = 0, rh = 0;
- editor_toolbar_dropdown_row_rect( bar, EDITOR_TOOLBAR_HIT_FILE, 0, &rx, &ry, &rw, &rh );
- U8 is_hover = hover == EDITOR_TOOLBAR_HIT_SAVE;
- U8 is_active = bar->held && bar->held_item == EDITOR_TOOLBAR_HIT_SAVE && m1;
-
- gui_draw_frect( rx, ry, rw, rh, border );
- CLR fill = ui_clr.bg_sec;
- if( is_active )
- fill = active_fill;
- else if( is_hover )
- fill = hover_fill;
-
- gui_draw_frect( rx + 1, ry + 1, max( 1, rw - 2 ), max( 1, rh - 2 ), fill );
- gui_draw_str( rx + 8, ry + rh / 2 - 8, ALIGN_L, FNT_JPN12, ui_clr.txt, "save" );
- }
-
- if( bar->edit_open ) {
- const U8 enabled[2] = { editor->undo_actions.size > 0, editor->redo_actions.size > 0 };
- const char* labels[2] = { "undo", "redo" };
- const I32 hit_id[2] = { EDITOR_TOOLBAR_HIT_UNDO, EDITOR_TOOLBAR_HIT_REDO };
-
- I32 panel_x = 0, panel_y = 0, panel_w = 0, panel_h = 0;
- editor_toolbar_dropdown_row_rect( bar, EDITOR_TOOLBAR_HIT_EDIT, 0, &panel_x, &panel_y, &panel_w, &panel_h );
- I32 row_h = panel_h;
- gui_draw_frect( panel_x, panel_y, panel_w, row_h * 2, border );
- gui_draw_frect( panel_x + 1, panel_y + 1, max( 1, panel_w - 2 ), max( 1, row_h * 2 - 2 ), ui_clr.bg_sec );
-
- for( I32 i = 0; i < 2; ++i ) {
- I32 rx = 0, ry = 0, rw = 0, rh = 0;
- editor_toolbar_dropdown_row_rect( bar, EDITOR_TOOLBAR_HIT_EDIT, i, &rx, &ry, &rw, &rh );
- U8 is_hover = hover == hit_id[i];
- U8 is_active = bar->held && bar->held_item == hit_id[i] && m1;
-
- CLR fill = ui_clr.bg_sec;
- if( enabled[i] && is_active )
- fill = active_fill;
- else if( is_hover )
- fill = hover_fill;
- gui_draw_frect( rx + 1, ry + 1, max( 1, rw - 2 ), max( 1, rh - 2 ), fill );
-
- CLR txt_clr = enabled[i] ? ui_clr.txt : ui_clr.txt_inactive;
- gui_draw_str( rx + 8, ry + rh / 2 - 8, ALIGN_L, FNT_JPN12, txt_clr, labels[i] );
- }
- }
-}
-
-static void gui_editor_toolbar_input_fn( void* ptr ) {
- GUI_EDITOR_TOOLBAR* bar = (GUI_EDITOR_TOOLBAR*)ptr;
- I32 mx, my;
- gui_cursor_pos( &mx, &my );
- U8 m1 = gui_mbutton_down( GUI_MBTNLEFT );
- I32 hit = editor_toolbar_hit_test( bar, mx, my );
-
- if( m1 ) {
- if( !bar->held ) {
- bar->held = 1;
- bar->held_item = hit;
- if( hit == EDITOR_TOOLBAR_HIT_NONE && ( bar->file_open || bar->edit_open ) )
- editor_toolbar_set_open( bar, 0, 0 );
- }
- return;
- }
-
- if( bar->held && bar->held_item == hit ) {
- switch( hit ) {
- case EDITOR_TOOLBAR_HIT_FILE:
- editor_toolbar_set_open( bar, !bar->file_open, 0 );
- break;
- case EDITOR_TOOLBAR_HIT_EDIT:
- editor_toolbar_set_open( bar, 0, !bar->edit_open );
- break;
- case EDITOR_TOOLBAR_HIT_SAVE:
- editor_toolbar_set_open( bar, 0, 0 );
- editor_save_map( editor );
- break;
- case EDITOR_TOOLBAR_HIT_UNDO:
- editor_toolbar_set_open( bar, 0, 0 );
- editor_undo( editor );
- break;
- case EDITOR_TOOLBAR_HIT_REDO:
- editor_toolbar_set_open( bar, 0, 0 );
- editor_redo( editor );
- break;
- case EDITOR_TOOLBAR_HIT_VIEW:
- case EDITOR_TOOLBAR_HIT_TOOLS:
- editor_toolbar_set_open( bar, 0, 0 );
- break;
- default:
- break;
- }
- }
-
- bar->held = 0;
- bar->held_item = EDITOR_TOOLBAR_HIT_NONE;
-}
-
-static GUI_EDITOR_TOOLBAR* gui_editor_toolbar( I32 x, I32 y, I32 w, I32 h, const char* name ) {
- if( !gui_check_target() )
- return 0;
-
- GUI_EDITOR_TOOLBAR* bar = new GUI_EDITOR_TOOLBAR;
- bar->x = x;
- bar->y = y;
- bar->w = w;
- bar->h = h;
- bar->xbound = w;
- bar->ybound = h + 52;
- bar->draw_fn = gui_editor_toolbar_draw_fn;
- bar->input_fn = gui_editor_toolbar_input_fn;
- bar->held = 0;
- bar->held_item = EDITOR_TOOLBAR_HIT_NONE;
- bar->file_open = 0;
- bar->edit_open = 0;
- strcpy( bar->name, name );
-
- GUI_VIEW* parent = gui_get_view();
- bar->parent = parent;
- parent->children.push( bar );
- return bar;
-}
-
-static void editor_raise_header_toolbar( GAME_EDITOR* e ) {
- if( !e || !e->map || !e->wnd || !e->gui.header_toolbar )
- return;
-
- GUI_BASE* bar = e->gui.header_toolbar;
- GUI_BASE* parent = bar->parent;
- if( !parent )
- return;
-
- I32 idx = parent->children.idx_of( bar );
- if( idx == -1 || idx == (I32)parent->children.size - 1 )
- return;
-
- parent->children.erase( idx );
- parent->children.push( bar );
-}
-
-static 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;
-}
-
-static void editor_create_header_controls( GAME_EDITOR* e ) {
- GAME_EDITOR::EDITOR_GUI* egui = &e->gui;
-
- egui->header_toolbar = gui_editor_toolbar( 1, EDITOR_LAYOUT_MENU_Y, e->wnd->w - 3, EDITOR_LAYOUT_MENU_H, "editor toolbar" );
-
- egui->header_back = gui_button( 10, EDITOR_LAYOUT_NAV_Y, 92, 20, "[ back ]", editor_header_back_cb );
-
- egui->header_viewtype_label = gui_label( 250, EDITOR_LAYOUT_NAV_Y + 4, "view type:" );
- egui->header_viewtype = gui_editor_viewtype_segment( 320, EDITOR_LAYOUT_NAV_Y, 186, 20, "viewtype segment" );
-
- egui->header_mode_2d = gui_button( 500, EDITOR_LAYOUT_NAV_Y, 92, 20, "[ 2d view ]", editor_header_mode_2d_cb );
- egui->header_mode_3d = gui_button( 600, EDITOR_LAYOUT_NAV_Y, 92, 20, "[ 3d view ]", editor_header_mode_3d_cb );
- egui->header_mode_sim = gui_button( 700, EDITOR_LAYOUT_NAV_Y, 92, 20, "[ simulation ]", editor_header_mode_sim_cb );
-}
-
-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 );
-
- I32 view_max = editor_layout_view_h_max( e->wnd );
- egui->view_h = view_max;
-}
-
-static const char* editor_tool_name() {
- switch( editor->tool.type ) {
- case EDITOR_TOOL_SELECT: return "select";
- case EDITOR_TOOL_WALL: return "wall";
- case EDITOR_TOOL_POLY: return "poly";
- case EDITOR_TOOL_SPRITE: return "sprite";
- case EDITOR_TOOL_ENT: return "ent";
- default: return "none";
- }
-}
-
-const I32 EDITOR_ASSETS_ROW_H = 28;
-const I32 EDITOR_ASSETS_THUMB = 20;
-const I32 EDITOR_ASSETS_INNER_PAD_X = 8;
-const I32 EDITOR_ASSETS_INNER_PAD_Y = 6;
-const I32 EDITOR_ASSETS_SCROLLBAR_W = 7;
-const I32 EDITOR_ASSETS_SCROLLBAR_GAP = 4;
-const I32 EDITOR_ASSETS_SCROLL_STEP = 20;
-
-struct EDITOR_ASSETS_LAYOUT {
- I32 inner_x{};
- I32 inner_y{};
- I32 inner_w{};
- I32 list_h{};
- I32 row_x{};
- I32 row_w{};
- I32 count{};
- I32 content_h{};
- I32 max_scroll{};
- U8 show_scroll{};
- I32 track_x{};
- I32 track_y{};
- I32 track_h{};
-};
-
-static EDITOR_ASSETS_LAYOUT editor_assets_layout( GUI_EDITOR_INFOBOX* box, I32 panel_x, I32 panel_y, I32 prop_count ) {
- EDITOR_ASSETS_LAYOUT l{};
- l.inner_x = panel_x + EDITOR_ASSETS_INNER_PAD_X;
- l.inner_y = panel_y + EDITOR_ASSETS_INNER_PAD_Y;
- l.inner_w = max( 1, box->w - ( EDITOR_ASSETS_INNER_PAD_X * 2 ) );
- l.list_h = max( 1, box->h - ( EDITOR_ASSETS_INNER_PAD_Y * 2 ) );
- l.count = prop_count + 1;
- l.content_h = l.count * EDITOR_ASSETS_ROW_H;
- l.max_scroll = max( 0, l.content_h - l.list_h );
- l.show_scroll = l.content_h > l.list_h;
- l.row_x = l.inner_x + 1;
- l.row_w = l.inner_w - 2;
- if( l.show_scroll )
- l.row_w -= EDITOR_ASSETS_SCROLLBAR_W + EDITOR_ASSETS_SCROLLBAR_GAP;
- l.row_w = max( 1, l.row_w );
- l.track_x = l.inner_x + l.inner_w - EDITOR_ASSETS_SCROLLBAR_W - 1 - ( EDITOR_ASSETS_SCROLLBAR_GAP / 2 );
- l.track_y = l.inner_y;
- l.track_h = l.list_h - 2;
- return l;
-}
-
-static void editor_assets_clamp_scroll( const EDITOR_ASSETS_LAYOUT& l ) {
- editor->gui.assets_scroll = min( max( 0, editor->gui.assets_scroll ), l.max_scroll );
-}
-
-static void gui_editor_infobox_draw_assets( GUI_EDITOR_INFOBOX* box, I32 panel_x, I32 panel_y ) {
- if( !editor->map )
- return;
-
- WORLD_MAP* map = editor->map;
- EDITOR_ASSETS_LAYOUT l = editor_assets_layout( box, panel_x, panel_y, (I32)map->props.size );
- editor_assets_clamp_scroll( l );
- I32 yoff = -editor->gui.assets_scroll;
-
- for( I32 idx = 0; idx < l.count; ++idx ) {
- I32 row_y = l.inner_y + idx * EDITOR_ASSETS_ROW_H + yoff;
- if( row_y + EDITOR_ASSETS_ROW_H - 2 <= l.inner_y || row_y >= l.inner_y + l.list_h )
- continue;
-
- U8 map_entry = idx == 0;
- 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;
- }
- }
-
- CLR rowbg = idx % 2 ? ui_clr.bg : ui_clr.bg_alt;
- if( selected )
- rowbg = CLR::blend( ui_clr.border, ui_clr.bg, 0.70f );
-
- gui_draw_frect( l.row_x, row_y, l.row_w, EDITOR_ASSETS_ROW_H - 2, rowbg );
-
- I32 tx = l.row_x + 3;
- I32 ty = row_y + 3;
- gui_draw_frect( tx, ty, EDITOR_ASSETS_THUMB, EDITOR_ASSETS_THUMB, ui_clr.border );
- if( map_entry ) {
- gui_draw_frect( tx + 1, ty + 1, EDITOR_ASSETS_THUMB - 2, EDITOR_ASSETS_THUMB - 2, ui_clr.bg_alt );
- gui_draw_str( tx + EDITOR_ASSETS_THUMB / 2, ty + 4, ALIGN_C, FNT_JPN12, ui_clr.txt, "m" );
- } else if( p->tex ) {
- gl_2d_textured_frect( _gui.gl2d_font, { (F32)(tx + 1), (F32)(ty + 1) }, { (F32)(EDITOR_ASSETS_THUMB - 2), (F32)(EDITOR_ASSETS_THUMB - 2) }, p->tex );
- } else {
- gui_draw_frect( tx + 1, ty + 1, EDITOR_ASSETS_THUMB - 2, EDITOR_ASSETS_THUMB - 2, p->clr );
- }
-
- I32 text_x = tx + EDITOR_ASSETS_THUMB + 8;
- CLR txt = ui_clr.txt;
- if( map_entry ) {
- gui_draw_str( text_x, row_y + 7, ALIGN_L, FNT_JPN12, txt, "[map] -> %s", map->name );
- } else if( p->tex ) {
- gui_draw_str( text_x, row_y + 7, ALIGN_L, FNT_JPN12, txt, "[%d] -> %s", idx - 1, p->tex->name );
- } else {
- gui_draw_str( text_x, row_y + 7, ALIGN_L, FNT_JPN12, txt, "[%d] -> %.2f, %.2f, %.2f", idx - 1, p->clr.r, p->clr.g, p->clr.b );
- }
-
- }
-
- if( l.show_scroll ) {
- gui_draw_frect( l.track_x, l.track_y, EDITOR_ASSETS_SCROLLBAR_W, l.track_h, ui_clr.bg_alt );
- gui_draw_rect( l.track_x, l.track_y, EDITOR_ASSETS_SCROLLBAR_W, l.track_h, ui_clr.border );
-
- I32 thumb_h = max( 18, ( l.track_h * l.list_h ) / max( 1, l.content_h ) );
- thumb_h = min( thumb_h, l.track_h );
- I32 travel = max( 1, l.track_h - thumb_h );
- I32 thumb_y = l.track_y + ( travel * editor->gui.assets_scroll ) / max( 1, l.max_scroll );
- gui_draw_frect( l.track_x, thumb_y + 1, EDITOR_ASSETS_SCROLLBAR_W, max( 1, thumb_h - 1 ), ui_clr.txt );
- }
-}
-
-static void gui_editor_infobox_draw_fn( void* ptr ) {
- GUI_EDITOR_INFOBOX* box = (GUI_EDITOR_INFOBOX*)ptr;
-
- I32 x = gui_relx( box );
- I32 y = gui_rely( box );
- I32 w = box->w;
- I32 h = box->h;
-
- if( box->type == EDITOR_INFOBOX_STATUS ) {
- CLR col = gui_is_fg_window( box )? 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_draw_str(
- x + 6,
- y + 4,
- ALIGN_L,
- FNT_JPN12,
- ui_clr.txt,
- "selected tool: %s. click in viewport to create/edit.",
- editor_tool_name()
- );
-
- if( editor->game && editor->game->gl ) {
- GL_DATA* gl = editor->game->gl;
- gui_draw_str(
- x + w - 6,
- y + 4,
- ALIGN_R,
- FNT_JPN12,
- ui_clr.txt,
- "fps: %.3f (%.5f ms) %dx%d",
- gl->fps,
- gl->frametime,
- gl->canvas_size[0],
- gl->canvas_size[1]
- );
- }
- return;
- }
-
- gui_draw_str( x, y, ALIGN_L, FNT_JPN12, ui_clr.txt, "assets" );
- y += EDITOR_LAYOUT_TITLE_OFFSET;
-
- CLR col = gui_is_fg_window( box )? 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_draw_push_clip( x + 2, y + 2, w - 4, h - 4 );
- gui_editor_infobox_draw_assets( box, x, y );
- gui_draw_pop_clip();
-}
-
-static void gui_editor_infobox_input_fn( void* ptr ) {
- GUI_EDITOR_INFOBOX* box = (GUI_EDITOR_INFOBOX*)ptr;
- static U8 assets_click_held = 0;
- if( box->type == EDITOR_INFOBOX_ASSETS ) {
- I32 x = gui_relx( box );
- I32 y = gui_rely( box ) + EDITOR_LAYOUT_TITLE_OFFSET;
- I32 w = box->w;
- I32 h = box->h;
- I32 prop_count = editor->map ? (I32)editor->map->props.size : 0;
- EDITOR_ASSETS_LAYOUT l = editor_assets_layout( box, x, y, prop_count );
- editor_assets_clamp_scroll( l );
- 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_capture_scroll();
- if( scroll == 1 )
- editor->gui.assets_scroll -= EDITOR_ASSETS_SCROLL_STEP;
- else if( scroll == (U8)-1 )
- editor->gui.assets_scroll += EDITOR_ASSETS_SCROLL_STEP;
-
- editor_assets_clamp_scroll( l );
- }
-
- U8 m1 = gui_mbutton_down( GUI_MBTNLEFT );
- if( m1 && !assets_click_held && editor->map ) {
- if( mx >= l.row_x && mx < l.row_x + l.row_w ) {
- I32 idx = ( my - l.inner_y + editor->gui.assets_scroll ) / EDITOR_ASSETS_ROW_H;
- if( idx >= 0 && idx < l.count && editor->gui.props ) {
- if( idx == 0 )
- gui_editor_propview_select( editor->gui.props, editor->map, EDITOR_SELECT_ORIGIN );
- else
- gui_editor_propview_select( editor->gui.props, &editor->map->props[idx - 1], EDITOR_SELECT_SURFPROPS );
- }
- }
- }
- assets_click_held = m1;
- }
- if( !gui_mbutton_down( GUI_MBTNLEFT ) )
- assets_click_held = 0;
- }
-
- gui_base_input_fn( box );
-}
-
-static GUI_EDITOR_INFOBOX* gui_editor_infobox( I32 x, I32 y, I32 w, I32 h, U8 type, const char* name ) {
- if( !gui_check_target() )
- return 0;
-
- GUI_EDITOR_INFOBOX* box = new GUI_EDITOR_INFOBOX;
- box->x = x;
- box->y = y;
- box->w = w;
- box->h = h;
- box->xbound = w;
- box->ybound = h + ( type == EDITOR_INFOBOX_STATUS ? 0 : EDITOR_LAYOUT_TITLE_OFFSET );
- box->draw_fn = gui_editor_infobox_draw_fn;
- box->input_fn = gui_editor_infobox_input_fn;
- box->type = type;
- strcpy( box->name, name );
-
- GUI_VIEW* parent = gui_get_view();
- box->parent = parent;
- parent->children.push( box );
- return box;
-}
-
-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;
-
- l.tool_panel_w = EDITOR_LAYOUT_TOOL_PANEL_W;
- 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;
-}
-
-static 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 );
-}
-
-static 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_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 );
-
- 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 menu_btn_y = EDITOR_LAYOUT_MENU_Y;
- const I32 top_btn_y = l.top_y;
- 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, menu_btn_y, e->wnd->w - 3, EDITOR_LAYOUT_MENU_H );
-
- editor_set_bounds( back, nav_left_x, top_btn_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, top_btn_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 = top_btn_y + 4;
- view_type_lbl->enabled = show_viewtype;
- }
- if( view_type_seg ) {
- editor_set_bounds( view_type_seg, type_seg_x, top_btn_y, type_seg_w, top_btn_h );
- view_type_seg->enabled = show_viewtype;
- }
-
- 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 );
- }
-
- 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;
-
- 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
- );
- }
- 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 );
-
- if( e->gui.gridlabel ) {
- 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 );
- }
-
- 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 );
-
- editor_update_properties_column( e );
- gui_editor_toolview_update( e->gui.tool );
- editor_notify_grid_change( e );
-}
-
-void gui_editorwindow_draw_fn( void* ptr ) {
- GUI_EDITORWINDOW* wnd = (GUI_EDITORWINDOW*)ptr;
- editor_raise_header_toolbar( editor );
- GUI_BASE* toolbar = ( editor && editor->map ) ? editor->gui.header_toolbar : 0;
- GUI_EDITOR_TOOLBAR* tbar = (GUI_EDITOR_TOOLBAR*)toolbar;
- U8 menu_open = tbar && ( tbar->file_open || tbar->edit_open );
- F32 saved_mx = input.mouse.pos.x;
- F32 saved_my = input.mouse.pos.y;
- if( menu_open ) {
- editor_menu_hover_mask_active = 1;
- editor_menu_hover_real_x = (I32)saved_mx;
- editor_menu_hover_real_y = (I32)saved_my;
- input.mouse.pos.x = -100000.f;
- input.mouse.pos.y = -100000.f;
- } else {
- editor_menu_hover_mask_active = 0;
- }
-
- CLR clr = gui_is_fg_window( wnd )? ui_clr.border : ui_clr.border_inactive;
- gui_draw_frect( wnd->x, wnd->y, wnd->w, wnd->h, clr );
- gui_draw_frect( wnd->x + 1, wnd->y + 1, wnd->w - 2, wnd->h - 2, ui_clr.bg );
-
- wnd->children.each( fn( GUI_BASE** ptr ) {
- GUI_BASE* it = *ptr;
- if( !it->enabled ) return;
-
- if( it->draw_fn ) it->draw_fn( it );
- else dlog( "gui_editorwindow_draw_fn(): child %p has no draw_fn", it );
- } );
-
- input.mouse.pos.x = saved_mx;
- input.mouse.pos.y = saved_my;
- editor_menu_hover_mask_active = 0;
-}
-
-static void gui_editorwindow_input_fn( void* ptr ) {
- GUI_EDITORWINDOW* wnd = (GUI_EDITORWINDOW*)ptr;
- editor_raise_header_toolbar( editor );
-
- GUI_BASE* toolbar = ( editor && editor->map ) ? editor->gui.header_toolbar : 0;
- if( toolbar && toolbar->enabled && toolbar->input_fn )
- toolbar->input_fn( toolbar );
-
- GUI_EDITOR_TOOLBAR* tbar = (GUI_EDITOR_TOOLBAR*)toolbar;
- U8 menu_open = tbar && ( tbar->file_open || tbar->edit_open );
- if( menu_open )
- return;
-
- wnd->children.each( fn( GUI_BASE** childptr ) {
- GUI_BASE* child = *childptr;
- if( !child || child == toolbar || !child->enabled || !child->input_fn )
- return;
-
- child->input_fn( child );
- } );
-}
-
-GUI_EDITORWINDOW* gui_editorwindow_create( I32 w, I32 h ) {
- GUI_EDITORWINDOW* wnd = new GUI_EDITORWINDOW;
- wnd->x = 0;
- wnd->y = 0;
- wnd->xbound = wnd->w = w;
- wnd->ybound = wnd->h = h;
- wnd->locked = 1;
- wnd->draw_fn = gui_editorwindow_draw_fn;
- wnd->input_fn = gui_editorwindow_input_fn;
- strcpy( wnd->name, "EDITORWINDOW" );
-
- _gui.windows.push( wnd );
- gui_set_window( wnd );
-
- gui_set_view( 0 );
- gui_view( 1, 1, w - 2, h - 2 );
- return wnd;
-}
-
-GUI_EDITORWINDOW* gui_editorwindow( I32 w, I32 h ) {
- GUI_EDITORWINDOW* wnd = gui_editorwindow_create( w, h );
-
- GAME_EDITOR* e = editor;
- LIST<GUI_LIST_ENTRY>* map_list = editor_get_map_list( e );
-
- gui_list( wnd->w / 2 - 100, EDITOR_LAYOUT_CONTENT_Y, 200, 200, "map list", map_list, &editor->gui.map_select );
- gui_button( wnd->w / 2 - 100, EDITOR_LAYOUT_CONTENT_Y + 220, 95, 25, "new map", editor_new_map_cb );
- gui_button( wnd->w / 2 + 5, EDITOR_LAYOUT_CONTENT_Y + 220, 95, 25, "load map", editor_load_map_cb );
- return wnd;
-}
-
-
-void editor_update_properties_column( GAME_EDITOR* e ) {
- GAME_EDITOR::EDITOR_GUI* egui = &e->gui;
- gui_editor_propview_update( egui->props );
-}
-
-void editor_create_properties_column( GAME_EDITOR* e ) {
- GAME_EDITOR::EDITOR_GUI* egui = &e->gui;
- egui->props = gui_editor_propview( 10, EDITOR_LAYOUT_CONTENT_Y, 300, 460 );
- gui_editor_propview_select( egui->props, e->map, EDITOR_SELECT_ORIGIN );
-}
-
-void editor_create_auxiliary_panels( GAME_EDITOR* e ) {
- GAME_EDITOR::EDITOR_GUI* egui = &e->gui;
- egui->assets = gui_editor_infobox( 10, EDITOR_LAYOUT_CONTENT_Y + 310, 300, 180, EDITOR_INFOBOX_ASSETS, "EDITOR_ASSETS_VIEW" );
- egui->status = gui_editor_infobox( 10, 568, 780, EDITOR_LAYOUT_STATUS_H, EDITOR_INFOBOX_STATUS, "EDITOR_STATUS_VIEW" );
-}
-
-void editor_update_toolview( GAME_EDITOR* e ) {
- GAME_EDITOR::EDITOR_GUI* egui = &e->gui;
- gui_editor_toolview_update( egui->tool );
-}
-
-void settool( U8 t ) {
- editor->tool.type = t;
-
- if( editor->gui.v2d ) {
- editor->gui.v2d->poly_drag = 0;
- }
-
- if( editor->gui.tool ) {
- editor_update_toolview( editor );
- }
-}
-
-static U8 EDITOR_TOOL_BUTTON_TYPES[] = {
- EDITOR_TOOL_NONE,
- EDITOR_TOOL_SELECT,
- EDITOR_TOOL_WALL,
- EDITOR_TOOL_POLY,
- EDITOR_TOOL_SPRITE,
- EDITOR_TOOL_ENT
-};
-
-static void settool_btn_cb( void* ptr ) {
- GUI_BUTTON* btn = (GUI_BUTTON*)ptr;
- if( !btn || !btn->extra )
- return;
-
- settool( *(U8*)btn->extra );
-}
-
-void editor_create_toolview_column( GAME_EDITOR* e ) {
- GAME_EDITOR::EDITOR_GUI* egui = &e->gui;
-
- I32 x = 320;
- I32 y = EDITOR_LAYOUT_CONTENT_Y + EDITOR_LAYOUT_VIEW_DEFAULT_H + EDITOR_LAYOUT_VIEW_TOOL_GAP + EDITOR_LAYOUT_TOOL_BTN_TOP_GAP;
- I32 off = 23;
- const char* labels[] = { "none", "select", "wall", "poly", "sprite", "ent" };
-
- for( U32 i = 0; i < sizeof( labels ) / sizeof( labels[0] ); ++i ) {
- GUI_BUTTON* btn = gui_button( x, y, 45, 20, labels[i], settool_btn_cb );
- btn->extra = &EDITOR_TOOL_BUTTON_TYPES[i];
- y += off;
- }
-
- egui->tool = gui_editor_toolview( 370, y - EDITOR_LAYOUT_TOOL_BTN_TOP_GAP, 300, 150 );
- gui_editor_toolview_update( egui->tool );
-}
-
-void editor_create_game_view_column( GAME_EDITOR* e ) {
- GAME_EDITOR::EDITOR_GUI* egui = &e->gui;
- I32 x = 320, y = EDITOR_LAYOUT_CONTENT_Y;
- egui->v2d = gui_editor_2dview( x, y, 468, 370 );
- egui->v3d = gui_editor_3dview( x, y, 468, 370 );
- egui->v2d->enabled = 1;
- egui->v3d->enabled = 0;
-}
-
-void editor_create_map_view( GAME_EDITOR* e ) {
- if( !e->map ) {
- dlog( "editor_create_map_views() : no map loaded\n" );
- return;
- }
-
- GUI_EDITORWINDOW* w = e->wnd;
- w->children.each( fn( GUI_BASE** ptr ) {
- gui_free( *ptr );
- } );
- w->children.clear();
- editor_clear_gui_state_refs( e );
- e->gui.view2d_type = EDITOR_2DVIEW_TOP_DOWN;
- e->gui.view_mode = EDITOR_VIEWMODE_2D;
-
- gui_set_window( w );
- gui_set_view( 0 );
-
- gui_view( 1, 1, w->w - 2, w->h - 2 );
-
- editor_create_header_controls( e );
- editor_create_properties_column( e );
- editor_create_toolview_column( e );
- editor_create_game_view_column( e );
- editor_create_auxiliary_panels( e );
- editor_layout_map_view( e );
-}
-
-void editor_resize( GAME_EDITOR* e, I32 w, I32 h ) {
- if( !e || !e->wnd )
- return;
-
- w = max( 1, w );
- h = max( 1, h );
-
- GUI_EDITORWINDOW* wnd = e->wnd;
- if( wnd->w == w && wnd->h == h )
- return;
-
- editor_set_bounds( wnd, 0, 0, w, h );
- editor_resize_base_view( wnd );
-
- if( e->map )
- editor_layout_map_view( e );
- else
- editor_layout_start_menu( e );
-
- if( e->gui.new_map_popup ) {
- GUI_WINDOW* popup = e->gui.new_map_popup;
- if( popup->x > w - 5 ) popup->x = w - 5;
- if( popup->x + popup->w < 5 ) popup->x = 5 - popup->w;
- if( popup->y > h - 5 ) popup->y = h - 5;
- if( popup->y + popup->h < 5 ) popup->y = 5 - popup->h;
- }
-}
-
-void close_new_map_popup( void* ) {
- gui_push_callback( pfn( void* ) {
- GUI_WINDOW* popup = editor->gui.new_map_popup;
- if( !popup )
- return;
-
- gui_free( popup );
- editor->gui.new_map_popup = 0;
- } );
-}
-
-void editor_new_map_cb( void* ptr ) {
- if( editor->gui.new_map_popup ) return;
-
- GUI_WINDOW* cwnd = gui_get_window();
- GUI_VIEW* view = gui_get_view();
-
- I32 wx = 250;
- I32 wy = 140;
- I32 ww = 300;
- I32 wh = 200;
-
- editor->gui.new_map_popup = gui_window( wx, wy, ww, wh );
- gui_title( "new map" );
- GUI_TEXTBOX* tb = gui_textbox( 10, 20, ww - 20, 20, "name", 32 );
- tb->active = 1;
-
- gui_button( ww - 50 - 70, wh - 20 - 10, 50, 20, "ok", pfn( void* ptr ) {
- GUI_BUTTON* btn = (GUI_BUTTON*)ptr;
- GUI_TEXTBOX* name = (GUI_TEXTBOX*)gui_find_node( btn->parent, "name" );
- if( !name )
- return;
-
- editor_new_map( editor, name->value );
- close_new_map_popup( 0 );
- } );
-
- gui_button( ww - 50 - 10, wh - 20 - 10, 50, 20, "cancel", close_new_map_popup );
-
- gui_set_window( cwnd );
- gui_set_view( view );
-}
-
-void editor_load_map_cb( void* ptr ) {
- GUI_LIST* list = (GUI_LIST*)gui_find_node( editor->wnd, "map list" );
-
- GUI_LIST_ENTRY* e = gui_list_get_selected( list );
- if( !e )
- return;
-
- char full_path[256];
- sprintf( full_path, "../assets/maps/%s", e->title );
-
- if( editor->map )
- editor_close( editor );
-
- editor_load_map( editor, full_path );
-}