summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/editor/editor.cpp343
-rw-r--r--src/editor/editor.h24
-rw-r--r--src/editor/gui.cpp352
-rw-r--r--src/editor/view2d.cpp29
-rw-r--r--src/gui/button.cpp5
-rw-r--r--src/gui/list.cpp32
6 files changed, 756 insertions, 29 deletions
diff --git a/src/editor/editor.cpp b/src/editor/editor.cpp
index 3c67ecd..63360d1 100644
--- a/src/editor/editor.cpp
+++ b/src/editor/editor.cpp
@@ -4,6 +4,347 @@
GAME_EDITOR* editor = 0;
+static void editor_push_undo_action( GAME_EDITOR* e, const GAME_EDITOR::EDITOR_UNDO_ACTION& action ) {
+ if( !e )
+ return;
+
+ e->redo_actions.clear();
+ e->undo_actions.push( action );
+}
+
+static void editor_refresh_after_map_change( GAME_EDITOR* e ) {
+ if( !e || !e->map )
+ return;
+
+ map_check_bounds( e->map );
+ if( e->gui.props && e->propgrid )
+ editor_update_properties_column( e );
+ if( e->gui.tool )
+ gui_editor_toolview_update( e->gui.tool );
+}
+
+static U8 editor_clr_eq( const CLR& a, const CLR& b ) {
+ return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
+}
+
+static U8 editor_vertex_eq( const MAP_VERTEX& a, const MAP_VERTEX& b ) {
+ return a.pos == b.pos
+ && a.normal == b.normal
+ && a.uv == b.uv
+ && editor_clr_eq( a.clr, b.clr );
+}
+
+static U8 editor_wall_eq( const MAP_WALL& a, const MAP_WALL& b ) {
+ return a.start == b.start
+ && a.end == b.end
+ && a.uvstart == b.uvstart
+ && a.uvend == b.uvend
+ && a.propid == b.propid;
+}
+
+static U8 editor_poly_eq( const MAP_POLYGON& a, const MAP_POLYGON& b ) {
+ if( a.type != b.type || a.propid != b.propid )
+ return 0;
+ if( !( a.mins == b.mins ) || !( a.maxs == b.maxs ) )
+ return 0;
+ if( a.vertices.size != b.vertices.size )
+ return 0;
+
+ for( U32 i = 0; i < a.vertices.size; ++i ) {
+ if( !editor_vertex_eq( a.vertices.data[i], b.vertices.data[i] ) )
+ return 0;
+ }
+ return 1;
+}
+
+static I32 editor_clamp_valid_idx( I32 idx, I32 size ) {
+ if( size <= 0 )
+ return -1;
+ if( idx < 0 )
+ return 0;
+ if( idx >= size )
+ return size - 1;
+ return idx;
+}
+
+static I32 editor_idx_distance( I32 a, I32 b ) {
+ return a > b ? a - b : b - a;
+}
+
+template <typename MATCH_FN>
+static I32 editor_find_nearest_idx( I32 size, I32 expected_idx, MATCH_FN match ) {
+ if( size <= 0 )
+ return -1;
+
+ expected_idx = editor_clamp_valid_idx( expected_idx, size );
+ if( match( expected_idx ) )
+ return expected_idx;
+
+ I32 best = -1;
+ I32 best_dist = 0x7fffffff;
+ for( I32 i = 0; i < size; ++i ) {
+ if( !match( i ) )
+ continue;
+
+ I32 dist = editor_idx_distance( i, expected_idx );
+ if( best == -1 || dist < best_dist ) {
+ best = i;
+ best_dist = dist;
+ }
+ }
+
+ return best;
+}
+
+static I32 editor_find_wall_idx( WORLD_MAP* map, const MAP_WALL& want, I32 expected_idx ) {
+ if( !map )
+ return -1;
+
+ return editor_find_nearest_idx( (I32)map->walls.size, expected_idx, fn( I32 i ) {
+ return editor_wall_eq( map->walls[i], want );
+ } );
+}
+
+static I32 editor_find_poly_idx( WORLD_MAP* map, const MAP_POLYGON& want, I32 expected_idx ) {
+ if( !map )
+ return -1;
+
+ return editor_find_nearest_idx( (I32)map->polygons.size, expected_idx, fn( I32 i ) {
+ return editor_poly_eq( map->polygons[i], want );
+ } );
+}
+
+static void editor_mark_clear_wall_refs( GAME_EDITOR* e, MAP_WALL* w, U8* clear_props_select, U8* clear_view_select, U8* clear_view_drag ) {
+ if( e->gui.props ) {
+ if( e->gui.props->seltype == EDITOR_SELECT_WALL && e->gui.props->curselect == w )
+ *clear_props_select = 1;
+ if( e->gui.props->seltype == EDITOR_SELECT_WVERTEX
+ && ( e->gui.props->curselect == &w->start || e->gui.props->curselect == &w->end ) )
+ *clear_props_select = 1;
+ }
+
+ if( e->gui.v2d ) {
+ if( e->gui.v2d->seltype == EDITOR_SELECT_WALL && e->gui.v2d->curselect == w )
+ *clear_view_select = 1;
+ if( e->gui.v2d->seltype == EDITOR_SELECT_WVERTEX
+ && ( e->gui.v2d->curselect == &w->start || e->gui.v2d->curselect == &w->end ) )
+ *clear_view_select = 1;
+
+ if( e->gui.v2d->dragtype == EDITOR_SELECT_WALL && e->gui.v2d->curdrag == w )
+ *clear_view_drag = 1;
+ if( e->gui.v2d->dragtype == EDITOR_SELECT_WVERTEX
+ && ( e->gui.v2d->curdrag == &w->start || e->gui.v2d->curdrag == &w->end ) )
+ *clear_view_drag = 1;
+ }
+}
+
+static void editor_mark_clear_poly_refs( GAME_EDITOR* e, MAP_POLYGON* p, U8* clear_props_select, U8* clear_view_select, U8* clear_view_drag ) {
+ if( e->gui.props ) {
+ if( e->gui.props->seltype == EDITOR_SELECT_POLY && e->gui.props->curselect == p )
+ *clear_props_select = 1;
+ if( e->gui.props->seltype == EDITOR_SELECT_PVERTEX ) {
+ p->vertices.each( fn( MAP_VERTEX* v ) {
+ if( e->gui.props->curselect == v )
+ *clear_props_select = 1;
+ } );
+ }
+ }
+
+ if( e->gui.v2d ) {
+ if( e->gui.v2d->seltype == EDITOR_SELECT_POLY && e->gui.v2d->curselect == p )
+ *clear_view_select = 1;
+ if( e->gui.v2d->seltype == EDITOR_SELECT_PVERTEX ) {
+ p->vertices.each( fn( MAP_VERTEX* v ) {
+ if( e->gui.v2d->curselect == v )
+ *clear_view_select = 1;
+ } );
+ }
+
+ if( e->gui.v2d->dragtype == EDITOR_SELECT_POLY && e->gui.v2d->curdrag == p )
+ *clear_view_drag = 1;
+ if( e->gui.v2d->dragtype == EDITOR_SELECT_PVERTEX ) {
+ p->vertices.each( fn( MAP_VERTEX* v ) {
+ if( e->gui.v2d->curdrag == v )
+ *clear_view_drag = 1;
+ } );
+ }
+ }
+}
+
+void editor_undo_clear( GAME_EDITOR* e ) {
+ if( !e )
+ return;
+
+ e->undo_actions.clear();
+ e->redo_actions.clear();
+}
+
+void editor_undo_record_create_walls( GAME_EDITOR* e, I32 start_idx, I32 count ) {
+ if( !e || !e->map || count <= 0 )
+ return;
+
+ WORLD_MAP* map = e->map;
+ if( start_idx < 0 || start_idx >= (I32)map->walls.size )
+ return;
+
+ I32 available = (I32)map->walls.size - start_idx;
+ if( count > available )
+ count = available;
+ if( count <= 0 )
+ return;
+
+ GAME_EDITOR::EDITOR_UNDO_ACTION action{};
+ action.type = EDITOR_UNDO_CREATE_WALLS;
+ action.start_idx = start_idx;
+ for( I32 i = 0; i < count; ++i )
+ action.walls.push( map->walls[start_idx + i] );
+
+ editor_push_undo_action( e, action );
+}
+
+void editor_undo_record_create_poly( GAME_EDITOR* e, I32 start_idx ) {
+ if( !e || !e->map )
+ return;
+
+ WORLD_MAP* map = e->map;
+ if( start_idx < 0 || start_idx >= (I32)map->polygons.size )
+ return;
+
+ GAME_EDITOR::EDITOR_UNDO_ACTION action{};
+ action.type = EDITOR_UNDO_CREATE_POLY;
+ action.start_idx = start_idx;
+ action.polys.push( map->polygons[start_idx] );
+
+ editor_push_undo_action( e, action );
+}
+
+static U8 editor_apply_undo_action_reverse( GAME_EDITOR* e, GAME_EDITOR::EDITOR_UNDO_ACTION& action ) {
+ if( !e || !e->map )
+ return 0;
+
+ WORLD_MAP* map = e->map;
+ U8 clear_props_select = 0;
+ U8 clear_view_select = 0;
+ U8 clear_view_drag = 0;
+
+ switch( action.type ) {
+ case EDITOR_UNDO_CREATE_WALLS: {
+ I32 count = (I32)action.walls.size;
+ for( I32 i = count - 1; i >= 0; --i ) {
+ if( !map->walls.size )
+ break;
+
+ I32 expected = action.start_idx + i;
+ I32 idx = editor_find_wall_idx( map, action.walls[i], expected );
+ if( idx < 0 )
+ idx = editor_clamp_valid_idx( expected, (I32)map->walls.size );
+
+ MAP_WALL* w = &map->walls[idx];
+ editor_mark_clear_wall_refs( e, w, &clear_props_select, &clear_view_select, &clear_view_drag );
+
+ map->walls.erase( idx );
+ }
+ } break;
+ case EDITOR_UNDO_CREATE_POLY: {
+ I32 count = (I32)action.polys.size;
+ if( count <= 0 )
+ return 0;
+ for( I32 i = count - 1; i >= 0; --i ) {
+ if( !map->polygons.size )
+ break;
+
+ I32 expected = action.start_idx + i;
+ I32 idx = i < (I32)action.polys.size ? editor_find_poly_idx( map, action.polys[i], expected ) : -1;
+ if( idx < 0 )
+ idx = editor_clamp_valid_idx( expected, (I32)map->polygons.size );
+
+ MAP_POLYGON* p = &map->polygons[idx];
+ editor_mark_clear_poly_refs( e, p, &clear_props_select, &clear_view_select, &clear_view_drag );
+
+ map->polygons.erase( idx );
+ }
+ } break;
+ default:
+ return 0;
+ }
+
+ if( e->gui.props && clear_props_select )
+ gui_editor_propview_select( e->gui.props, 0, EDITOR_SELECT_NONE );
+ if( e->gui.v2d ) {
+ if( clear_view_select ) {
+ e->gui.v2d->curselect = 0;
+ e->gui.v2d->seltype = EDITOR_SELECT_NONE;
+ }
+ if( clear_view_drag ) {
+ e->gui.v2d->curdrag = 0;
+ e->gui.v2d->dragtype = EDITOR_SELECT_NONE;
+ }
+ }
+
+ editor_refresh_after_map_change( e );
+
+ return 1;
+}
+
+static U8 editor_apply_undo_action_forward( GAME_EDITOR* e, GAME_EDITOR::EDITOR_UNDO_ACTION& action ) {
+ if( !e || !e->map )
+ return 0;
+
+ WORLD_MAP* map = e->map;
+ switch( action.type ) {
+ case EDITOR_UNDO_CREATE_WALLS: {
+ if( !action.walls.size )
+ return 0;
+
+ action.walls.each( fn( MAP_WALL* w ) {
+ map->walls.push( *w );
+ } );
+ } break;
+ case EDITOR_UNDO_CREATE_POLY: {
+ if( !action.polys.size )
+ return 0;
+
+ action.polys.each( fn( MAP_POLYGON* p ) {
+ map->polygons.push( *p );
+ } );
+ } break;
+ default:
+ return 0;
+ }
+
+ editor_refresh_after_map_change( e );
+
+ return 1;
+}
+
+U8 editor_undo( GAME_EDITOR* e ) {
+ if( !e || !e->map || !e->undo_actions.size )
+ return 0;
+
+ GAME_EDITOR::EDITOR_UNDO_ACTION action = e->undo_actions.pop();
+ if( !editor_apply_undo_action_reverse( e, action ) ) {
+ e->undo_actions.push( action );
+ return 0;
+ }
+
+ e->redo_actions.push( action );
+ return 1;
+}
+
+U8 editor_redo( GAME_EDITOR* e ) {
+ if( !e || !e->map || !e->redo_actions.size )
+ return 0;
+
+ GAME_EDITOR::EDITOR_UNDO_ACTION action = e->redo_actions.pop();
+ if( !editor_apply_undo_action_forward( e, action ) ) {
+ e->redo_actions.push( action );
+ return 0;
+ }
+
+ e->undo_actions.push( action );
+ return 1;
+}
+
void editor_register_grid_dependency( GAME_EDITOR* e, void* tag, EDITOR_GRID_DEP_CALLBACK cb ) {
if( !e || !tag || !cb )
return;
@@ -60,6 +401,7 @@ GAME_EDITOR* editor_create( GAME_DATA* game ) {
}
STAT editor_close( GAME_EDITOR* e ) {
+ editor_undo_clear( e );
game_unload_map( e->game );
e->map = 0;
@@ -87,6 +429,7 @@ STAT editor_load_map( GAME_EDITOR* e, const char* mapname ) {
return STAT_ERR;
e->game->state.map = e->map = m;
+ editor_undo_clear( e );
gui_push_callback( e, pfn( void* d ) {
editor_create_map_view( (GAME_EDITOR*)d );
} );
diff --git a/src/editor/editor.h b/src/editor/editor.h
index 0fee667..2067df5 100644
--- a/src/editor/editor.h
+++ b/src/editor/editor.h
@@ -35,6 +35,12 @@ enum EditorSelectType_t {
EDITOR_SELECT_SURFPROPS
};
+enum EditorUndoType_t {
+ EDITOR_UNDO_NONE = 0,
+ EDITOR_UNDO_CREATE_WALLS = 1,
+ EDITOR_UNDO_CREATE_POLY = 2
+};
+
struct GAME_EDITOR;
typedef void( *EDITOR_GRID_DEP_CALLBACK )( GAME_EDITOR* e );
@@ -81,9 +87,9 @@ struct GAME_EDITOR {
I32 view_mode{};
I32 view2d_type{};
+ GUI_BASE* header_toolbar{};
GUI_LABEL* header_viewtype_label{};
GUI_BUTTON* header_back{};
- GUI_BUTTON* header_save{};
GUI_BUTTON* header_mode_2d{};
GUI_BUTTON* header_mode_3d{};
GUI_BUTTON* header_mode_sim{};
@@ -105,6 +111,15 @@ struct GAME_EDITOR {
LIST<GRID_DEPENDENCY> grid_dependencies{};
LIST<GUI_LIST_ENTRY> map_list{};
+
+ struct EDITOR_UNDO_ACTION {
+ U8 type{};
+ I32 start_idx{};
+ LIST<MAP_WALL> walls{};
+ LIST<MAP_POLYGON> polys{};
+ };
+ LIST<EDITOR_UNDO_ACTION> undo_actions{};
+ LIST<EDITOR_UNDO_ACTION> redo_actions{};
};
extern GAME_EDITOR* editor_create( struct GAME_DATA* game );
@@ -124,6 +139,11 @@ 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 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 );
+extern U8 editor_undo( GAME_EDITOR* e );
+extern U8 editor_redo( GAME_EDITOR* e );
struct GUI_EDITORWINDOW : GUI_WINDOW {};
struct GUI_EDITOR_3DVIEW : GUI_VIEW {
@@ -153,6 +173,8 @@ struct GUI_EDITOR_2DVIEW : GUI_VIEW {
U8 poly_drag;
VEC2 poly_start;
VEC2 poly_end;
+
+ I32 pending_wall_undo_idx;
};
struct GUI_EDITOR_PROPVIEW : GUI_VIEW {
diff --git a/src/editor/gui.cpp b/src/editor/gui.cpp
index 92f3f7c..04e69f1 100644
--- a/src/editor/gui.cpp
+++ b/src/editor/gui.cpp
@@ -6,8 +6,10 @@
const I32 EDITOR_LAYOUT_MARGIN = 10;
const I32 EDITOR_LAYOUT_TITLE_OFFSET = 15;
-const I32 EDITOR_LAYOUT_NAV_Y = 10;
-const I32 EDITOR_LAYOUT_CONTENT_Y = 38;
+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;
@@ -40,6 +42,13 @@ struct GUI_EDITOR_VIEWTYPE_SEGMENT : GUI_BASE {
I32 held_seg{-1};
};
+struct GUI_EDITOR_TOOLBAR : GUI_BASE {
+ U8 held{};
+ I32 held_item{-1};
+ U8 file_open{};
+ U8 edit_open{};
+};
+
enum EditorViewMode_t {
EDITOR_VIEWMODE_2D = 0,
EDITOR_VIEWMODE_3D = 1,
@@ -53,6 +62,27 @@ enum Editor2DViewType_t {
};
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;
+
+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
+};
+
+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 )
@@ -70,14 +100,12 @@ static void editor_apply_view_mode( GAME_EDITOR* e ) {
}
static void editor_header_back_cb( void* ) {
+ editor_toolbar_close_menu();
editor_close( editor );
}
-static void editor_header_save_cb( void* ) {
- editor_save_map( 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 );
@@ -138,7 +166,7 @@ static void gui_editor_viewtype_segment_draw_fn( void* ptr ) {
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] = { "top down", "side", "front" };
+ const char* txts[3] = { "x/y", "x/z", "y/z" };
for( I32 i = 0; i < 3; ++i ) {
U8 selected = i == active_seg;
@@ -213,6 +241,250 @@ static GUI_EDITOR_VIEWTYPE_SEGMENT* gui_editor_viewtype_segment( I32 x, I32 y, I
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->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 );
+}
+
struct EDITOR_LAYOUT {
I32 left_x;
I32 left_w;
@@ -260,8 +532,9 @@ static void editor_set_bounds( GUI_BASE* node, I32 x, I32 y, I32 w, I32 h ) {
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_save = gui_button( 110, EDITOR_LAYOUT_NAV_Y, 92, 20, "[ save ]", editor_header_save_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" );
@@ -333,7 +606,7 @@ 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 = 8;
+const I32 EDITOR_ASSETS_SCROLLBAR_W = 7;
const I32 EDITOR_ASSETS_SCROLLBAR_GAP = 4;
const I32 EDITOR_ASSETS_SCROLL_STEP = 20;
@@ -368,7 +641,7 @@ static EDITOR_ASSETS_LAYOUT editor_assets_layout( GUI_EDITOR_INFOBOX* box, I32 p
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;
+ 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;
@@ -407,11 +680,9 @@ static void gui_editor_infobox_draw_assets( GUI_EDITOR_INFOBOX* box, I32 panel_x
CLR rowbg = idx % 2 ? ui_clr.bg : ui_clr.bg_alt;
if( selected )
- rowbg = CLR::blend( (CLR){ 0.f, 1.f, 0.f, 1.f }, ui_clr.bg, 0.85f );
+ 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 );
- if( selected )
- gui_draw_rect( l.row_x, row_y, l.row_w, EDITOR_ASSETS_ROW_H - 2, (CLR){ 0.f, 1.f, 0.f, 1.f } );
I32 tx = l.row_x + 3;
I32 ty = row_y + 3;
@@ -426,7 +697,7 @@ static void gui_editor_infobox_draw_assets( GUI_EDITOR_INFOBOX* box, I32 panel_x
}
I32 text_x = tx + EDITOR_ASSETS_THUMB + 8;
- CLR txt = selected ? (CLR){ 0.8f, 1.f, 0.8f, 1.f } : ui_clr.txt;
+ 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 ) {
@@ -445,7 +716,7 @@ static void gui_editor_infobox_draw_assets( GUI_EDITOR_INFOBOX* box, I32 panel_x
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 + 1, thumb_y + 1, EDITOR_ASSETS_SCROLLBAR_W - 2, max( 1, thumb_h - 2 ), ui_clr.txt );
+ gui_draw_frect( l.track_x, thumb_y + 1, EDITOR_ASSETS_SCROLLBAR_W, max( 1, thumb_h - 1 ), ui_clr.txt );
}
}
@@ -683,16 +954,18 @@ static void editor_layout_map_view( GAME_EDITOR* e ) {
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* save = egui->header_save;
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;
@@ -707,8 +980,9 @@ static void editor_layout_map_view( GAME_EDITOR* e ) {
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 );
- editor_set_bounds( save, nav_left_x + nav_btn_w + nav_gap, 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 ]" };
@@ -796,6 +1070,21 @@ static void editor_layout_map_view( GAME_EDITOR* e ) {
void gui_editorwindow_draw_fn( void* ptr ) {
GUI_EDITORWINDOW* wnd = (GUI_EDITORWINDOW*)ptr;
+ editor_raise_header_toolbar( editor );
+ GUI_BASE* toolbar = editor ? 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 );
@@ -808,6 +1097,32 @@ void gui_editorwindow_draw_fn( void* ptr ) {
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->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 ) {
@@ -818,6 +1133,7 @@ GUI_EDITORWINDOW* gui_editorwindow_create( I32 w, I32 h ) {
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 );
@@ -933,9 +1249,9 @@ void editor_create_map_view( GAME_EDITOR* e ) {
e->gui.assets = 0;
e->gui.status = 0;
e->gui.assets_scroll = 0;
+ e->gui.header_toolbar = 0;
e->gui.header_viewtype_label = 0;
e->gui.header_back = 0;
- e->gui.header_save = 0;
e->gui.header_mode_2d = 0;
e->gui.header_mode_3d = 0;
e->gui.header_mode_sim = 0;
diff --git a/src/editor/view2d.cpp b/src/editor/view2d.cpp
index 6814fd2..b68846e 100644
--- a/src/editor/view2d.cpp
+++ b/src/editor/view2d.cpp
@@ -433,7 +433,9 @@ void gui_editor_2dview_create_poly_from_drag( GUI_EDITOR_2DVIEW* view ) {
}
map_polygon_calc_bounds( &newp );
+ I32 poly_idx = editor->map->polygons.size;
editor->map->polygons.push( newp );
+ editor_undo_record_create_poly( editor, poly_idx );
gui_editor_2dview_check_bounds_preserve_view( view );
}
@@ -443,6 +445,7 @@ void gui_editor_2dview_create_wallpoly_from_drag( GUI_EDITOR_2DVIEW* view ) {
return;
I32 propid = gui_editor_2dview_find_or_create_wallpoly_propid();
+ I32 first_wall_idx = editor->map->walls.size;
F32 depth = gui_editor_2dview_placement_z();
F32 wallheight = gui_editor_2dview_wall_height();
@@ -459,6 +462,8 @@ void gui_editor_2dview_create_wallpoly_from_drag( GUI_EDITOR_2DVIEW* view ) {
wall.propid = propid;
editor->map->walls.push( wall );
}
+ I32 new_wall_count = editor->map->walls.size - first_wall_idx;
+ editor_undo_record_create_walls( editor, first_wall_idx, new_wall_count );
gui_editor_2dview_check_bounds_preserve_view( view );
}
@@ -1141,8 +1146,11 @@ void gui_editor_2dview_input_tool_wall( GUI_EDITOR_2DVIEW* view ) {
U8 m1 = gui_mbutton_down( 0 );
if( !m1 ) {
if( view->held ) {
+ if( view->pending_wall_undo_idx >= 0 )
+ editor_undo_record_create_walls( editor, view->pending_wall_undo_idx, 1 );
gui_editor_2dview_check_bounds_preserve_view( view );
}
+ view->pending_wall_undo_idx = -1;
view->held = 0;
view->curdrag = 0;
return;
@@ -1168,6 +1176,7 @@ void gui_editor_2dview_input_tool_wall( GUI_EDITOR_2DVIEW* view ) {
I32 idx = editor->map->walls.size;
editor->map->walls.push( neww );
+ view->pending_wall_undo_idx = idx;
view->curdrag = &editor->map->walls[idx].end;
view->held = 1;
@@ -1338,12 +1347,31 @@ void gui_editor_view2d_delete_obj( GUI_EDITOR_2DVIEW* view ) {
void gui_editor_2dview_key_input( GUI_EDITOR_2DVIEW* view ) {
static U8 del_held = 0;
+ static U8 undo_held = 0;
+ static U8 redo_held = 0;
if( kb_down( SDLK_DELETE ) && !input.mouselock ) {
if( !del_held )
gui_editor_view2d_delete_obj( view );
del_held = 1;
} else del_held = 0;
+
+ U8 ctrl = kb_down( SDLK_LCTRL ) || kb_down( SDLK_RCTRL );
+ U8 z = kb_down( SDLK_z );
+ if( ctrl && z && !input.mouselock ) {
+ if( !undo_held )
+ editor_undo( editor );
+
+ undo_held = 1;
+ } else undo_held = 0;
+
+ U8 r = kb_down( SDLK_r );
+ if( ctrl && r && !input.mouselock ) {
+ if( !redo_held )
+ editor_redo( editor );
+
+ redo_held = 1;
+ } else redo_held = 0;
}
void gui_editor_2dview_input_fn( void* ptr ) {
@@ -1469,6 +1497,7 @@ GUI_EDITOR_2DVIEW* gui_editor_2dview( I32 x, I32 y, I32 w, I32 h ) {
view->poly_drag = 0;
view->poly_start = { 0.f, 0.f };
view->poly_end = { 0.f, 0.f };
+ view->pending_wall_undo_idx = -1;
GUI_BASE* parent = gui_get_view();
if( !parent )
diff --git a/src/gui/button.cpp b/src/gui/button.cpp
index 1548e0e..e66130b 100644
--- a/src/gui/button.cpp
+++ b/src/gui/button.cpp
@@ -12,11 +12,12 @@ void gui_button_draw_fn( void* ptr ) {
U8 hover = inbounds && !active;
CLR border = gui_is_fg_window( btn )? ui_clr.border : ui_clr.border_inactive;
- if( active )
- border = { 0.f, 1.f, 0.f, 1.f };
CLR fill = ui_clr.bg_sec;
CLR txt = ui_clr.txt;
+ if( active ) {
+ fill = CLR::blend( ui_clr.bg_sec, ui_clr.border, 0.22f );
+ }
if( hover ) {
fill = ui_clr.border;
txt = CLR::BLACK();
diff --git a/src/gui/list.cpp b/src/gui/list.cpp
index 90f2f49..2feff47 100644
--- a/src/gui/list.cpp
+++ b/src/gui/list.cpp
@@ -10,18 +10,33 @@ void gui_list_draw_fn( void* ptr ) {
I32 y = gui_rely( list );
gui_draw_str( x, y, ALIGN_L, FNT_JPN12, ui_clr.txt, list->name );
- y += LIST_TITLE_OFFSET;
+ I32 panel_y = y + LIST_TITLE_OFFSET;
CLR col = gui_is_fg_window( list )? ui_clr.border : ui_clr.border_inactive;
- gui_draw_frect( x, y, list->w, list->h, col );
- gui_draw_frect( x+1, y+1, list->w-2, list->h-2, ui_clr.bg_sec );
+ gui_draw_frect( x, panel_y, list->w, list->h, col );
+ gui_draw_frect( x + 1, panel_y + 1, list->w - 2, list->h - 2, ui_clr.bg_sec );
+
+ I32 txt_h = 12;
+ gui_draw_get_str_bounds( 0, &txt_h, FNT_JPN12, "ag" );
I32 yoff = 0;
list->plist->each( fn( GUI_LIST_ENTRY* e ) {
U8 selected = e->val == *list->pval;
- CLR col = selected? ui_clr.txt : ui_clr.txt_inactive;
+ I32 row_x = x + 1;
+ I32 row_y = panel_y + 1 + yoff;
+ I32 row_w = list->w - 2;
+ if( row_w < 1 ) row_w = 1;
+ I32 row_h = LIST_ITEM_HEIGHT;
+
+ if( selected ) {
+ CLR sel_bg = CLR::blend( ui_clr.border, ui_clr.bg, 0.70f );
+ gui_draw_frect( row_x, row_y, row_w, row_h, sel_bg );
+ }
+
+ CLR col = selected ? ui_clr.txt : ui_clr.txt_inactive;
+ I32 txt_y = row_y + ( row_h - txt_h ) / 2;
- gui_draw_str( x + 4, y + yoff + 6, ALIGN_L, FNT_JPN12, col, e->title );
+ gui_draw_str( row_x + 3, txt_y, ALIGN_L, FNT_JPN12, col, e->title );
yoff += LIST_ITEM_HEIGHT;
} );
}
@@ -31,6 +46,7 @@ void gui_list_input_fn( void* ptr ) {
I32 x = gui_relx( list );
I32 y = gui_rely( list );
+ I32 panel_y = y + LIST_TITLE_OFFSET;
I32 w = list->w;
I32 h = list->h;
@@ -38,12 +54,12 @@ void gui_list_input_fn( void* ptr ) {
I32 mx, my;
gui_cursor_pos( &mx, &my );
- U8 inbounds = mx >= x && mx <= x + w && my >= y && my <= y + h;
+ U8 inbounds = mx >= x && mx <= x + w && my >= panel_y && my <= panel_y + h;
if( !inbounds )
return;
- I32 diff = my - y;
- I32 idx = diff / LIST_ITEM_HEIGHT - 1;
+ I32 diff = my - panel_y;
+ I32 idx = diff / LIST_ITEM_HEIGHT;
if( idx >= list->plist->size ) idx = list->plist->size - 1;
if( idx < 0 ) idx = 0;