From 20fdf410d6da498595ac6550ed00c12e01702c1c Mon Sep 17 00:00:00 2001 From: Micael Dias Date: Tue, 26 Sep 2023 01:03:01 +0100 Subject: [PATCH 1/2] Add: gui setting to allow controlling if toolbar dropdown menus close instantly on mouse release --- src/lang/english.txt | 3 +++ src/settings_gui.cpp | 1 + src/settings_type.h | 1 + src/table/settings/gui_settings.ini | 8 ++++++++ src/toolbar_gui.cpp | 24 ++++++++++++------------ src/window.cpp | 17 ++++++++++++----- 6 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index dddadd8f93..b8db23ce70 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1650,6 +1650,9 @@ STR_CONFIG_SETTING_SCROLLMODE_LMB :Move map with L STR_CONFIG_SETTING_SMOOTH_SCROLLING :Smooth viewport scrolling: {STRING2} STR_CONFIG_SETTING_SMOOTH_SCROLLING_HELPTEXT :Control how the main view scrolls to a specific position when clicking on the smallmap or when issuing a command to scroll to a specific object on the map. If enabled, the viewport scrolls smoothly, if disabled it jumps directly to the targeted spot +STR_CONFIG_SETTING_TOOLBAR_DROPDOWNS_INSTANT_CLOSE :Toolbar dropdown menus instant-close: {STRING2} +STR_CONFIG_SETTING_TOOLBAR_DROPDOWNS_INSTANT_CLOSE_HELPTEXT :Controls whether the the main toolbar dropdown menus close instantly on mouse button release + STR_CONFIG_SETTING_MEASURE_TOOLTIP :Show a measurement tooltip when using various build-tools: {STRING2} STR_CONFIG_SETTING_MEASURE_TOOLTIP_HELPTEXT :Display tile-distances and height differences when dragging during construction operations diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 3ead59aa20..fe3b5fdd68 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -2014,6 +2014,7 @@ static SettingsContainer &GetSettingsTree() general->Add(new SettingEntry("gui.window_snap_radius")); general->Add(new SettingEntry("gui.window_soft_limit")); general->Add(new SettingEntry("gui.right_click_wnd_close")); + general->Add(new SettingEntry("gui.toolbar_dropdowns_instant_close")); } SettingsPage *viewports = interface->Add(new SettingsPage(STR_CONFIG_SETTING_INTERFACE_VIEWPORTS)); diff --git a/src/settings_type.h b/src/settings_type.h index ae6f22c42e..fd1fed5698 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -178,6 +178,7 @@ struct GUISettings { uint8_t scrollwheel_multiplier; ///< how much 'wheel' per incoming event from the OS? bool timetable_arrival_departure; ///< show arrivals and departures in vehicle timetables RightClickClose right_click_wnd_close; ///< close window with right click + bool toolbar_dropdowns_instant_close; ///< whether toolbar dropdown menus instant close on mouse release. bool pause_on_newgame; ///< whether to start new games paused or not SignalGUISettings signal_gui_mode; ///< select which signal types are shown in the signal GUI SignalCycleSettings cycle_signal_types; ///< Which signal types to cycle with the build signal tool. diff --git a/src/table/settings/gui_settings.ini b/src/table/settings/gui_settings.ini index 78dadf1c44..7f758ae2a9 100644 --- a/src/table/settings/gui_settings.ini +++ b/src/table/settings/gui_settings.ini @@ -146,6 +146,14 @@ strhelp = STR_CONFIG_SETTING_RIGHT_MOUSE_WND_CLOSE_HELPTEXT strval = STR_CONFIG_SETTING_RIGHT_MOUSE_WND_CLOSE_NO cat = SC_BASIC +[SDTC_BOOL] +var = gui.toolbar_dropdowns_instant_close +flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC +def = true +str = STR_CONFIG_SETTING_TOOLBAR_DROPDOWNS_INSTANT_CLOSE +strhelp = STR_CONFIG_SETTING_TOOLBAR_DROPDOWNS_INSTANT_CLOSE_HELPTEXT +cat = SC_BASIC + ; We might need to emulate a right mouse button on mac [SDTC_VAR] ifdef = __APPLE__ diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index c1aa2ff267..cfa70950d7 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -115,7 +115,7 @@ public: */ static void PopupMainToolbarMenu(Window *w, WidgetID widget, DropDownList &&list, int def) { - ShowDropDownList(w, std::move(list), def, widget, 0, true); + ShowDropDownList(w, std::move(list), def, widget, 0, _settings_client.gui.toolbar_dropdowns_instant_close); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); } @@ -279,7 +279,7 @@ static CallBackFunction ToolbarOptionsClick(Window *w) list.push_back(MakeDropDownListCheckedItem(IsTransparencySet(TO_HOUSES), STR_SETTINGS_MENU_TRANSPARENT_BUILDINGS, OME_TRANSPARENTBUILDINGS)); list.push_back(MakeDropDownListCheckedItem(IsTransparencySet(TO_SIGNS), STR_SETTINGS_MENU_TRANSPARENT_SIGNS, OME_SHOW_STATIONSIGNS)); - ShowDropDownList(w, std::move(list), 0, WID_TN_SETTINGS, 140, true); + ShowDropDownList(w, std::move(list), 0, WID_TN_SETTINGS, 140, _settings_client.gui.toolbar_dropdowns_instant_close); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); return CBF_NONE; } @@ -656,7 +656,7 @@ static CallBackFunction ToolbarGraphsClick(Window *w) if (_toolbar_mode != TB_NORMAL) AddDropDownLeagueTableOptions(list); - ShowDropDownList(w, std::move(list), GRMN_OPERATING_PROFIT_GRAPH, WID_TN_GRAPHS, 140, true); + ShowDropDownList(w, std::move(list), GRMN_OPERATING_PROFIT_GRAPH, WID_TN_GRAPHS, 140, _settings_client.gui.toolbar_dropdowns_instant_close); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); return CBF_NONE; @@ -669,7 +669,7 @@ static CallBackFunction ToolbarLeagueClick(Window *w) AddDropDownLeagueTableOptions(list); int selected = list[0]->result; - ShowDropDownList(w, std::move(list), selected, WID_TN_LEAGUE, 140, true); + ShowDropDownList(w, std::move(list), selected, WID_TN_LEAGUE, 140, _settings_client.gui.toolbar_dropdowns_instant_close); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); return CBF_NONE; @@ -850,7 +850,7 @@ static CallBackFunction ToolbarZoomOutClick(Window *w) static CallBackFunction ToolbarBuildRailClick(Window *w) { - ShowDropDownList(w, GetRailTypeDropDownList(), _last_built_railtype, WID_TN_RAILS, 140, true); + ShowDropDownList(w, GetRailTypeDropDownList(), _last_built_railtype, WID_TN_RAILS, 140, _settings_client.gui.toolbar_dropdowns_instant_close); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); return CBF_NONE; } @@ -872,7 +872,7 @@ static CallBackFunction MenuClickBuildRail(int index) static CallBackFunction ToolbarBuildRoadClick(Window *w) { - ShowDropDownList(w, GetRoadTypeDropDownList(RTTB_ROAD), _last_built_roadtype, WID_TN_ROADS, 140, true); + ShowDropDownList(w, GetRoadTypeDropDownList(RTTB_ROAD), _last_built_roadtype, WID_TN_ROADS, 140, _settings_client.gui.toolbar_dropdowns_instant_close); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); return CBF_NONE; } @@ -894,7 +894,7 @@ static CallBackFunction MenuClickBuildRoad(int index) static CallBackFunction ToolbarBuildTramClick(Window *w) { - ShowDropDownList(w, GetRoadTypeDropDownList(RTTB_TRAM), _last_built_tramtype, WID_TN_TRAMS, 140, true); + ShowDropDownList(w, GetRoadTypeDropDownList(RTTB_TRAM), _last_built_tramtype, WID_TN_TRAMS, 140, _settings_client.gui.toolbar_dropdowns_instant_close); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); return CBF_NONE; } @@ -918,7 +918,7 @@ static CallBackFunction ToolbarBuildWaterClick(Window *w) { DropDownList list; list.push_back(MakeDropDownListIconItem(SPR_IMG_BUILD_CANAL, PAL_NONE, STR_WATERWAYS_MENU_WATERWAYS_CONSTRUCTION, 0)); - ShowDropDownList(w, std::move(list), 0, WID_TN_WATER, 140, true); + ShowDropDownList(w, std::move(list), 0, WID_TN_WATER, 140, _settings_client.gui.toolbar_dropdowns_instant_close); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); return CBF_NONE; } @@ -940,7 +940,7 @@ static CallBackFunction ToolbarBuildAirClick(Window *w) { DropDownList list; list.push_back(MakeDropDownListIconItem(SPR_IMG_AIRPORT, PAL_NONE, STR_AIRCRAFT_MENU_AIRPORT_CONSTRUCTION, 0)); - ShowDropDownList(w, std::move(list), 0, WID_TN_AIR, 140, true); + ShowDropDownList(w, std::move(list), 0, WID_TN_AIR, 140, _settings_client.gui.toolbar_dropdowns_instant_close); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); return CBF_NONE; } @@ -964,7 +964,7 @@ static CallBackFunction ToolbarForestClick(Window *w) list.push_back(MakeDropDownListIconItem(SPR_IMG_LANDSCAPING, PAL_NONE, STR_LANDSCAPING_MENU_LANDSCAPING, 0)); list.push_back(MakeDropDownListIconItem(SPR_IMG_PLANTTREES, PAL_NONE, STR_LANDSCAPING_MENU_PLANT_TREES, 1)); list.push_back(MakeDropDownListIconItem(SPR_IMG_SIGN, PAL_NONE, STR_LANDSCAPING_MENU_PLACE_SIGN, 2)); - ShowDropDownList(w, std::move(list), 0, WID_TN_LANDSCAPE, 100, true); + ShowDropDownList(w, std::move(list), 0, WID_TN_LANDSCAPE, 100, _settings_client.gui.toolbar_dropdowns_instant_close); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); return CBF_NONE; } @@ -1237,7 +1237,7 @@ static CallBackFunction ToolbarScenGenIndustry(Window *w) static CallBackFunction ToolbarScenBuildRoadClick(Window *w) { - ShowDropDownList(w, GetScenRoadTypeDropDownList(RTTB_ROAD), _last_built_roadtype, WID_TE_ROADS, 140, true); + ShowDropDownList(w, GetScenRoadTypeDropDownList(RTTB_ROAD), _last_built_roadtype, WID_TE_ROADS, 140, _settings_client.gui.toolbar_dropdowns_instant_close); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); return CBF_NONE; } @@ -1257,7 +1257,7 @@ static CallBackFunction ToolbarScenBuildRoad(int index) static CallBackFunction ToolbarScenBuildTramClick(Window *w) { - ShowDropDownList(w, GetScenRoadTypeDropDownList(RTTB_TRAM), _last_built_tramtype, WID_TE_TRAMS, 140, true); + ShowDropDownList(w, GetScenRoadTypeDropDownList(RTTB_TRAM), _last_built_tramtype, WID_TE_TRAMS, 140, _settings_client.gui.toolbar_dropdowns_instant_close); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); return CBF_NONE; } diff --git a/src/window.cpp b/src/window.cpp index b2b44e114c..6b15bcfbbf 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -614,11 +614,18 @@ static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count) bool focused_widget_changed = false; /* If clicked on a window that previously did not have focus */ - if (_focused_window != w && // We already have focus, right? - (w->window_desc.flags & WDF_NO_FOCUS) == 0 && // Don't lose focus to toolbars - widget_type != WWT_CLOSEBOX) { // Don't change focused window if 'X' (close button) was clicked - focused_widget_changed = true; - SetFocusedWindow(w); + if (_focused_window != w) { + bool allowed_focus_change = + (w->window_desc.flags & WDF_NO_FOCUS) == 0 && // Don't lose focus to toolbar + widget_type != WWT_CLOSEBOX; // Don't change focused window if 'X' (close button) was clicked + // if we're switching away from a dropdown menu always let it get the lost focus notification + if (_focused_window != nullptr && _focused_window->window_class == WC_DROPDOWN_MENU) { + allowed_focus_change = true; + } + if (allowed_focus_change) { + focused_widget_changed = true; + SetFocusedWindow(w); + } } if (nw == nullptr) return; // exit if clicked outside of widgets From daf96d4c4f2b301c51c0b4e223bb84bd5b61243c Mon Sep 17 00:00:00 2001 From: Micael Dias Date: Fri, 27 Sep 2024 22:50:51 +0100 Subject: [PATCH 2/2] Codechange: improve dropdown mouse interactions to fix hovering when not using instant-close and also make logic more readable --- src/dropdown.cpp | 68 +++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/src/dropdown.cpp b/src/dropdown.cpp index 3fde9ef74f..2d83db8139 100644 --- a/src/dropdown.cpp +++ b/src/dropdown.cpp @@ -74,7 +74,6 @@ struct DropdownWindow : Window { Rect wi_rect; ///< Rect of the button that opened the dropdown. DropDownList list; ///< List with dropdown menu items. int selected_result; ///< Result value of the selected item in the list. - uint8_t click_delay = 0; ///< Timer to delay selection. bool drag_mode = true; bool instant_close; ///< Close the window when the mouse button is raised. bool persist; ///< Persist dropdown menu. @@ -281,12 +280,7 @@ struct DropdownWindow : Window { void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override { if (widget != WID_DM_ITEMS) return; - int item; - if (this->GetDropDownItem(item)) { - this->click_delay = 4; - this->selected_result = item; - this->SetDirty(); - } + this->drag_mode = true; } /** Rate limit how fast scrolling happens. */ @@ -300,43 +294,41 @@ struct DropdownWindow : Window { void OnMouseLoop() override { - if (this->click_delay != 0 && --this->click_delay == 0) { - /* Close the dropdown, so it doesn't affect new window placement. - * Also mark it dirty in case the callback deals with the screen. (e.g. screenshots). */ - if (!this->persist) this->Close(); - this->parent->OnDropdownSelect(this->parent_button, this->selected_result); - return; + /* Scrolling logic */ + if (_cursor.pos.y <= this->top + WidgetDimensions::scaled.dropdownlist.top) { + /* Cursor is above the list, set scroll up */ + this->scrolling = -1; + } else if (_cursor.pos.y >= this->top + this->height - WidgetDimensions::scaled.dropdownlist.bottom) { + /* Cursor is below list, set scroll down */ + this->scrolling = 1; } - if (this->drag_mode) { - int item; - - if (!_left_button_clicked) { - this->drag_mode = false; - if (!this->GetDropDownItem(item)) { - if (this->instant_close) this->Close(); - return; - } - this->click_delay = 2; - } else { - if (_cursor.pos.y <= this->top + WidgetDimensions::scaled.dropdownlist.top) { - /* Cursor is above the list, set scroll up */ - this->scrolling = -1; - return; - } else if (_cursor.pos.y >= this->top + this->height - WidgetDimensions::scaled.dropdownlist.bottom) { - /* Cursor is below list, set scroll down */ - this->scrolling = 1; - return; - } - - if (!this->GetDropDownItem(item)) return; - } - - if (this->selected_result != item) { + /* Hover item under the cursor */ + int item = -1; + const bool item_is_valid = this->GetDropDownItem(item); + if (item_is_valid) { + if (item != this->selected_result) { this->selected_result = item; this->SetDirty(); } } + + /* Select option logic */ + if (!_left_button_clicked) { + bool released_on_item = false; + + if (this->drag_mode) { + this->drag_mode = false; + + if ((instant_close || item_is_valid) && !this->persist) { + this->Close(); + } + + if (item_is_valid) { + this->parent->OnDropdownSelect(this->parent_button, this->selected_result); + } + } + } } void ReplaceList(DropDownList &&list)