#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 ); }