From 679f95d1de75ac97dddcb5ace8653f155954558a Mon Sep 17 00:00:00 2001 From: Colin Caine Date: Mon, 16 Jun 2025 20:16:38 +0100 Subject: [PATCH 1/5] Codechange: make more hotkey ids match widgets --- src/order_gui.cpp | 16 ++++----- src/toolbar_gui.cpp | 38 ++++++++++---------- src/toolbar_gui.h | 67 +++++++++++++++++++----------------- src/widgets/order_widget.h | 1 + src/widgets/toolbar_widget.h | 50 ++++++++++++++------------- 5 files changed, 91 insertions(+), 81 deletions(-) diff --git a/src/order_gui.cpp b/src/order_gui.cpp index 5b47c97897..707dc72040 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -469,14 +469,14 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile) /** Hotkeys for order window. */ enum OrderHotKeys : int32_t { - OHK_SKIP, - OHK_DELETE, - OHK_GOTO, - OHK_NONSTOP, - OHK_FULLLOAD, - OHK_UNLOAD, - OHK_NEAREST_DEPOT, - OHK_ALWAYS_SERVICE, + OHK_SKIP = WID_O_SKIP, + OHK_DELETE = WID_O_DELETE, + OHK_GOTO = WID_O_GOTO, + OHK_NONSTOP = WID_O_NON_STOP, + OHK_FULLLOAD = WID_O_FULL_LOAD, + OHK_UNLOAD = WID_O_UNLOAD, + OHK_ALWAYS_SERVICE = WID_O_DEPOT_ACTION, + OHK_NEAREST_DEPOT = WID_O_END, OHK_TRANSFER, OHK_NO_UNLOAD, OHK_NO_LOAD, diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index a82d581990..c41a30aae7 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -2305,29 +2305,29 @@ static ToolbarButtonProc * const _scen_toolbar_button_procs[] = { }; enum MainToolbarEditorHotkeys : int32_t { - MTEHK_PAUSE, - MTEHK_FASTFORWARD, - MTEHK_SETTINGS, - MTEHK_SAVEGAME, - MTEHK_GENLAND, - MTEHK_GENTOWN, - MTEHK_GENINDUSTRY, - MTEHK_BUILD_ROAD, - MTEHK_BUILD_TRAM, - MTEHK_BUILD_DOCKS, - MTEHK_BUILD_TREES, - MTEHK_SIGN, - MTEHK_MUSIC, - MTEHK_LANDINFO, + MTEHK_PAUSE = WID_TE_PAUSE, + MTEHK_FASTFORWARD = WID_TE_FAST_FORWARD, + MTEHK_SETTINGS = WID_TE_SETTINGS, + MTEHK_SAVEGAME = WID_TE_SAVE, + MTEHK_GENLAND = WID_TE_LAND_GENERATE, + MTEHK_GENTOWN = WID_TE_TOWN_GENERATE, + MTEHK_GENINDUSTRY = WID_TE_INDUSTRY, + MTEHK_BUILD_ROAD = WID_TE_ROADS, + MTEHK_BUILD_TRAM = WID_TE_TRAMS, + MTEHK_BUILD_DOCKS = WID_TE_WATER, + MTEHK_BUILD_TREES = WID_TE_TREES, + MTEHK_SIGN = WID_TE_SIGNS, + MTEHK_MUSIC = WID_TE_MUSIC_SOUND, + MTEHK_LANDINFO = WID_TE_HELP, + MTEHK_ZOOM_IN = WID_TE_ZOOM_IN, + MTEHK_ZOOM_OUT = WID_TE_ZOOM_OUT, + MTEHK_SMALLMAP = WID_TE_SMALL_MAP, + MTEHK_TERRAFORM = WID_TE_END, + MTEHK_EXTRA_VIEWPORT, MTEHK_SMALL_SCREENSHOT, MTEHK_ZOOMEDIN_SCREENSHOT, MTEHK_DEFAULTZOOM_SCREENSHOT, MTEHK_GIANT_SCREENSHOT, - MTEHK_ZOOM_IN, - MTEHK_ZOOM_OUT, - MTEHK_TERRAFORM, - MTEHK_SMALLMAP, - MTEHK_EXTRA_VIEWPORT, }; struct ScenarioEditorToolbarWindow : Window { diff --git a/src/toolbar_gui.h b/src/toolbar_gui.h index 37cc7fb886..5c152cbfd1 100644 --- a/src/toolbar_gui.h +++ b/src/toolbar_gui.h @@ -10,47 +10,52 @@ #ifndef TOOLBAR_GUI_H #define TOOLBAR_GUI_H +#include "widgets/toolbar_widget.h" + +// TODO: Replace all instances of "MTHK_blah" with "WID_blah" where we can, +// then redefine this as AdditionalMainToolbarHotkeys, or something like that. enum MainToolbarHotkeys : int32_t { - MTHK_PAUSE, - MTHK_FASTFORWARD, - MTHK_SETTINGS, - MTHK_SAVEGAME, - MTHK_LOADGAME, - MTHK_SMALLMAP, - MTHK_TOWNDIRECTORY, - MTHK_SUBSIDIES, - MTHK_STATIONS, - MTHK_FINANCES, - MTHK_COMPANIES, - MTHK_STORY, - MTHK_GOAL, - MTHK_GRAPHS, - MTHK_LEAGUE, - MTHK_INDUSTRIES, - MTHK_TRAIN_LIST, - MTHK_ROADVEH_LIST, - MTHK_SHIP_LIST, - MTHK_AIRCRAFT_LIST, - MTHK_ZOOM_IN, - MTHK_ZOOM_OUT, - MTHK_BUILD_RAIL, - MTHK_BUILD_ROAD, - MTHK_BUILD_TRAM, - MTHK_BUILD_DOCKS, - MTHK_BUILD_AIRPORT, + MTHK_PAUSE = WID_TN_PAUSE, + MTHK_FASTFORWARD = WID_TN_FAST_FORWARD, + MTHK_SETTINGS = WID_TN_SETTINGS, + MTHK_SAVEGAME = WID_TN_SAVE, + MTHK_SMALLMAP = WID_TN_SMALL_MAP, + MTHK_TOWNDIRECTORY = WID_TN_TOWNS, + MTHK_SUBSIDIES = WID_TN_SUBSIDIES, + MTHK_STATIONS = WID_TN_STATIONS, + MTHK_FINANCES = WID_TN_FINANCES, + MTHK_COMPANIES = WID_TN_COMPANIES, + MTHK_STORY = WID_TN_STORY, + MTHK_GOAL = WID_TN_GOAL, + MTHK_GRAPHS = WID_TN_GRAPHS, + MTHK_LEAGUE = WID_TN_LEAGUE, + MTHK_INDUSTRIES = WID_TN_INDUSTRIES, + MTHK_TRAIN_LIST = WID_TN_TRAINS, + MTHK_ROADVEH_LIST = WID_TN_ROADVEHS, + MTHK_SHIP_LIST = WID_TN_SHIPS, + MTHK_AIRCRAFT_LIST = WID_TN_AIRCRAFT, + MTHK_ZOOM_IN = WID_TN_ZOOM_IN, + MTHK_ZOOM_OUT = WID_TN_ZOOM_OUT, + MTHK_BUILD_RAIL = WID_TN_RAILS, + MTHK_BUILD_ROAD = WID_TN_ROADS, + MTHK_BUILD_TRAM = WID_TN_TRAMS, + MTHK_BUILD_DOCKS = WID_TN_WATER, + MTHK_BUILD_AIRPORT = WID_TN_AIR, + MTHK_TERRAFORM = WID_TN_LANDSCAPE, + MTHK_MUSIC = WID_TN_MUSIC_SOUND, + MTHK_LANDINFO = WID_TN_HELP, + // Hotkeys without associated widgets. + MTHK_LOADGAME = WID_TN_END, MTHK_BUILD_TREES, - MTHK_MUSIC, - MTHK_LANDINFO, MTHK_SCRIPT_DEBUG, MTHK_SMALL_SCREENSHOT, MTHK_ZOOMEDIN_SCREENSHOT, MTHK_DEFAULTZOOM_SCREENSHOT, MTHK_GIANT_SCREENSHOT, MTHK_CHEATS, - MTHK_TERRAFORM, MTHK_EXTRA_VIEWPORT, MTHK_CLIENT_LIST, - MTHK_SIGN_LIST + MTHK_SIGN_LIST, }; void AllocateToolbar(); diff --git a/src/widgets/order_widget.h b/src/widgets/order_widget.h index b0399ebda6..dc3ad0cc44 100644 --- a/src/widgets/order_widget.h +++ b/src/widgets/order_widget.h @@ -37,6 +37,7 @@ enum OrderWidgets : WidgetID { WID_O_SEL_TOP_ROW, ///< #NWID_SELECTION widget for the top row of the 'your non-trains' order window. WID_O_SEL_BOTTOM_MIDDLE, ///< #NWID_SELECTION widget for the middle part of the bottom row of the 'your train' order window. WID_O_SHARED_ORDER_LIST, ///< Open list of shared vehicles. + WID_O_END, ///< End of the list of widgets. }; #endif /* WIDGETS_ORDER_WIDGET_H */ diff --git a/src/widgets/toolbar_widget.h b/src/widgets/toolbar_widget.h index 36b53516af..4241200dc4 100644 --- a/src/widgets/toolbar_widget.h +++ b/src/widgets/toolbar_widget.h @@ -50,29 +50,33 @@ enum ToolbarNormalWidgets : WidgetID { /** Widgets of the #ScenarioEditorToolbarWindow class. */ enum ToolbarEditorWidgets : WidgetID { - WID_TE_PAUSE, ///< Pause the game. - WID_TE_FAST_FORWARD, ///< Fast forward the game. - WID_TE_SETTINGS, ///< Settings menu. - WID_TE_SAVE, ///< Save menu. - WID_TE_SPACER, ///< Spacer with "scenario editor" text. - WID_TE_DATE, ///< The date of the scenario. - WID_TE_DATE_BACKWARD, ///< Reduce the date of the scenario. - WID_TE_DATE_FORWARD, ///< Increase the date of the scenario. - WID_TE_SMALL_MAP, ///< Small map menu. - WID_TE_ZOOM_IN, ///< Zoom in the main viewport. - WID_TE_ZOOM_OUT, ///< Zoom out the main viewport. - WID_TE_LAND_GENERATE, ///< Land generation. - WID_TE_TOWN_GENERATE, ///< Town building window. - WID_TE_INDUSTRY, ///< Industry building window. - WID_TE_ROADS, ///< Road building menu. - WID_TE_TRAMS, ///< Tram building menu. - WID_TE_WATER, ///< Water building toolbar. - WID_TE_TREES, ///< Tree building toolbar. - WID_TE_SIGNS, ///< Sign building. - WID_TE_DATE_PANEL, ///< Container for the date widgets. - WID_TE_MUSIC_SOUND, ///< Music/sound configuration menu. - WID_TE_HELP, ///< Help menu. - WID_TE_SWITCH_BAR, ///< Only available when toolbar has been split to switch between different subsets. + // TODO: Clean this up. The idea here was to see which buttons are + // common between the two toolbars so that they can perhaps be + // de-duplicated. + WID_TE_PAUSE = WID_TN_PAUSE, ///< Pause the game. + WID_TE_FAST_FORWARD = WID_TN_FAST_FORWARD, ///< Fast forward the game. + WID_TE_SETTINGS = WID_TN_SETTINGS, ///< Settings menu. + WID_TE_SAVE = WID_TN_SAVE, ///< Save menu. + WID_TE_SMALL_MAP = WID_TN_SMALL_MAP, ///< Small map menu. + WID_TE_ZOOM_IN = WID_TN_ZOOM_IN, ///< Zoom in the main viewport. + WID_TE_ZOOM_OUT = WID_TN_ZOOM_OUT, ///< Zoom out the main viewport. + WID_TE_ROADS = WID_TN_ROADS, ///< Road building menu. + WID_TE_TRAMS = WID_TN_TRAMS, ///< Tram building menu. + WID_TE_WATER = WID_TN_WATER, ///< Water building toolbar. + WID_TE_MUSIC_SOUND = WID_TN_MUSIC_SOUND, ///< Music/sound configuration menu. + WID_TE_HELP = WID_TN_HELP, ///< Help menu. + WID_TE_SWITCH_BAR = WID_TN_SWITCH_BAR, ///< Only available when toolbar has been split to switch between different subsets. + WID_TE_SPACER = WID_TN_END, ///< Spacer with "scenario editor" text. + WID_TE_DATE, ///< The date of the scenario. + WID_TE_DATE_BACKWARD, ///< Reduce the date of the scenario. + WID_TE_DATE_FORWARD, ///< Increase the date of the scenario. + WID_TE_LAND_GENERATE, ///< Land generation. + WID_TE_TOWN_GENERATE, ///< Town building window. + WID_TE_INDUSTRY, ///< Industry building window. + WID_TE_TREES, ///< Tree building toolbar. + WID_TE_SIGNS, ///< Sign building. + WID_TE_DATE_PANEL, ///< Container for the date widgets. + WID_TE_END, }; #endif /* WIDGETS_TOOLBAR_WIDGET_H */ From a92619be8966d1f5d123acbaf5a3f91d31d0db00 Mon Sep 17 00:00:00 2001 From: Colin Caine Date: Mon, 16 Jun 2025 20:17:25 +0100 Subject: [PATCH 2/5] Feature: WIP: Draw hotkey hints over some widgets --- src/hotkeys.cpp | 43 +++++++++++++++++++++++++++++++++ src/hotkeys.h | 2 ++ src/widget.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++++++ src/widget_type.h | 1 + 4 files changed, 106 insertions(+) diff --git a/src/hotkeys.cpp b/src/hotkeys.cpp index 59a623a968..9db098f315 100644 --- a/src/hotkeys.cpp +++ b/src/hotkeys.cpp @@ -196,6 +196,40 @@ static std::string KeycodeToString(uint16_t keycode) return str; } +/** + * A short representation of the keycode, for printing as a hotkey hint. + * @param keycode The keycode to convert to a string. + * @return A string representation of this keycode. + */ +std::string KeycodeToShortString(uint16_t keycode) +{ + std::string str; + if (keycode & WKC_SHIFT) { + // TODO + str += "ยป"; + } + if (keycode & WKC_CTRL) { + str += "^"; + } + if (keycode & WKC_ALT) { + str += "A+"; + } + if (keycode & WKC_META) { + str += "M+"; + } + keycode = keycode & ~WKC_SPECIAL_KEYS; + + for (const auto &kn : _keycode_to_name) { + if (kn.keycode == keycode) { + str += kn.name; + return str; + } + } + assert(keycode < 128); + str.push_back(keycode); + return str; +} + /** * Convert all keycodes attached to a hotkey to a single string. If multiple * keycodes are attached to the hotkey they are split by a comma. @@ -350,3 +384,12 @@ void HandleGlobalHotkeys([[maybe_unused]] char32_t key, uint16_t keycode) } } +const Hotkey* HotkeyList::GetHotkeyByNum(int num) const +{ + for (const Hotkey &hotkey : this->items) { + if (hotkey.num == num) { + return &hotkey; + } + } + return (const Hotkey*) nullptr; +} diff --git a/src/hotkeys.h b/src/hotkeys.h index 88b714590b..436dc70f78 100644 --- a/src/hotkeys.h +++ b/src/hotkeys.h @@ -44,6 +44,7 @@ struct HotkeyList { void Save(IniFile &ini) const; int CheckMatch(uint16_t keycode, bool global_only = false) const; + const Hotkey* GetHotkeyByNum(int num) const; GlobalHotkeyHandlerFunc global_hotkey_handler; private: @@ -64,5 +65,6 @@ void SaveHotkeysToConfig(); void HandleGlobalHotkeys(char32_t key, uint16_t keycode); +std::string KeycodeToShortString(uint16_t keycode); #endif /* HOTKEYS_H */ diff --git a/src/widget.cpp b/src/widget.cpp index a9d68d2669..0b82f623dd 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -27,6 +27,9 @@ #include "safeguards.h" +#include "widgets/toolbar_widget.h" +#include "hotkeys.h" + WidgetDimensions WidgetDimensions::scaled = {}; static std::string GetStringForWidget(const Window *w, const NWidgetCore *nwid, bool secondary = false) @@ -3108,6 +3111,63 @@ void NWidgetLeaf::Draw(const Window *w) } DrawOutline(w, this); + + // TODO: Draw hints only if Alt is being held. + if (!this->IsDisabled()) { + this->DrawHotkeyHint(w); + } +} + +void NWidgetLeaf::DrawHotkeyHint(const Window* w) { + // TODO: Use global hotkey for autoroads for rail, road, tram, etc. if + // they have been set. + + // TODO: Rect is wrong for edit boxes, and the kind of hint we're showing + // will look bad anyway. + Rect r = this->GetCurrentRect().Shrink(1); + if (w->window_desc.cls == WC_MAIN_TOOLBAR && this->index == WID_TN_FAST_FORWARD) { + // Special-case hint text for Fast-forwards because it's not really a hotkey + DrawStringMultiLine(r, "Tab", TC_WHITE, SA_LEFT | SA_BOTTOM, false, FS_NORMAL); + } else if (w->window_desc.hotkeys != nullptr) { + // Widget IDs can coincidentally overlap with hotkey IDs if + // they aren't assigned properly. Avoid this. + auto hk = w->window_desc.hotkeys->GetHotkeyByNum(this->index); + if (hk != nullptr) { + if (hk->keycodes.size()) { + // Find the "best" of the available keycodes + auto keycode = *(hk->keycodes.begin()); + for (auto k : hk->keycodes) { + if (!(keycode & WKC_GLOBAL_HOTKEY) && (k & WKC_GLOBAL_HOTKEY)) { + keycode = k; + } else if ('A' <= (k & ~WKC_SPECIAL_KEYS) && (k & ~WKC_SPECIAL_KEYS) <= 'Z') { + keycode = k; + } + } + + // Convert to a string + // TODO: Glyphs for Shift, Alt, etc. Colour for global. + // - On screen keyboard has a sprite for shift. + auto s = KeycodeToShortString(keycode); + + // Choose the font-size + auto availableSize = Dimension(r.right - r.left, r.bottom - r.top); + auto desiredSize = GetStringBoundingBox(s, FS_NORMAL); + auto fontsize = FS_NORMAL; + if (availableSize < desiredSize) { + fontsize = FS_SMALL; + } + + // Display the hints! + // TODO: not as readable as my mockup :( + // - Outline or a bolder font could help. + auto colour = TC_WHITE; + if (keycode & WKC_GLOBAL_HOTKEY) { + colour = TC_YELLOW; + } + DrawStringMultiLine(r, s, colour, SA_LEFT | SA_BOTTOM, false, fontsize); + } + } + } } /** diff --git a/src/widget_type.h b/src/widget_type.h index b7bb0ce925..09791892d1 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -922,6 +922,7 @@ public: void SetupSmallestSize(Window *w) override; void Draw(const Window *w) override; + void DrawHotkeyHint(const Window* window); bool ButtonHit(const Point &pt); From 016970154dea82fcdf28da3da0ab00f41e86308e Mon Sep 17 00:00:00 2001 From: Colin Caine Date: Mon, 16 Jun 2025 22:43:43 +0200 Subject: [PATCH 3/5] Codefix: undo changes to scenario editor widget ids --- src/widgets/toolbar_widget.h | 49 +++++++++++++++++------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/src/widgets/toolbar_widget.h b/src/widgets/toolbar_widget.h index 4241200dc4..a7a4b66f67 100644 --- a/src/widgets/toolbar_widget.h +++ b/src/widgets/toolbar_widget.h @@ -50,32 +50,29 @@ enum ToolbarNormalWidgets : WidgetID { /** Widgets of the #ScenarioEditorToolbarWindow class. */ enum ToolbarEditorWidgets : WidgetID { - // TODO: Clean this up. The idea here was to see which buttons are - // common between the two toolbars so that they can perhaps be - // de-duplicated. - WID_TE_PAUSE = WID_TN_PAUSE, ///< Pause the game. - WID_TE_FAST_FORWARD = WID_TN_FAST_FORWARD, ///< Fast forward the game. - WID_TE_SETTINGS = WID_TN_SETTINGS, ///< Settings menu. - WID_TE_SAVE = WID_TN_SAVE, ///< Save menu. - WID_TE_SMALL_MAP = WID_TN_SMALL_MAP, ///< Small map menu. - WID_TE_ZOOM_IN = WID_TN_ZOOM_IN, ///< Zoom in the main viewport. - WID_TE_ZOOM_OUT = WID_TN_ZOOM_OUT, ///< Zoom out the main viewport. - WID_TE_ROADS = WID_TN_ROADS, ///< Road building menu. - WID_TE_TRAMS = WID_TN_TRAMS, ///< Tram building menu. - WID_TE_WATER = WID_TN_WATER, ///< Water building toolbar. - WID_TE_MUSIC_SOUND = WID_TN_MUSIC_SOUND, ///< Music/sound configuration menu. - WID_TE_HELP = WID_TN_HELP, ///< Help menu. - WID_TE_SWITCH_BAR = WID_TN_SWITCH_BAR, ///< Only available when toolbar has been split to switch between different subsets. - WID_TE_SPACER = WID_TN_END, ///< Spacer with "scenario editor" text. - WID_TE_DATE, ///< The date of the scenario. - WID_TE_DATE_BACKWARD, ///< Reduce the date of the scenario. - WID_TE_DATE_FORWARD, ///< Increase the date of the scenario. - WID_TE_LAND_GENERATE, ///< Land generation. - WID_TE_TOWN_GENERATE, ///< Town building window. - WID_TE_INDUSTRY, ///< Industry building window. - WID_TE_TREES, ///< Tree building toolbar. - WID_TE_SIGNS, ///< Sign building. - WID_TE_DATE_PANEL, ///< Container for the date widgets. + WID_TE_PAUSE, ///< Pause the game. + WID_TE_FAST_FORWARD, ///< Fast forward the game. + WID_TE_SETTINGS, ///< Settings menu. + WID_TE_SAVE, ///< Save menu. + WID_TE_SPACER, ///< Spacer with "scenario editor" text. + WID_TE_DATE, ///< The date of the scenario. + WID_TE_DATE_BACKWARD, ///< Reduce the date of the scenario. + WID_TE_DATE_FORWARD, ///< Increase the date of the scenario. + WID_TE_SMALL_MAP, ///< Small map menu. + WID_TE_ZOOM_IN, ///< Zoom in the main viewport. + WID_TE_ZOOM_OUT, ///< Zoom out the main viewport. + WID_TE_LAND_GENERATE, ///< Land generation. + WID_TE_TOWN_GENERATE, ///< Town building window. + WID_TE_INDUSTRY, ///< Industry building window. + WID_TE_ROADS, ///< Road building menu. + WID_TE_TRAMS, ///< Tram building menu. + WID_TE_WATER, ///< Water building toolbar. + WID_TE_TREES, ///< Tree building toolbar. + WID_TE_SIGNS, ///< Sign building. + WID_TE_DATE_PANEL, ///< Container for the date widgets. + WID_TE_MUSIC_SOUND, ///< Music/sound configuration menu. + WID_TE_HELP, ///< Help menu. + WID_TE_SWITCH_BAR, ///< Only available when toolbar has been split to switch between different subsets. WID_TE_END, }; From 2f4c5e6737c4b139e2a30564306f93792a6385b5 Mon Sep 17 00:00:00 2001 From: Colin Caine Date: Tue, 17 Jun 2025 01:09:06 +0200 Subject: [PATCH 4/5] Codefix: outlined font and use shorter names --- src/hotkeys.cpp | 67 +++++++++++++++++++++----------------- src/widget.cpp | 86 +++++++++++++++++++++++++++++++------------------ 2 files changed, 92 insertions(+), 61 deletions(-) diff --git a/src/hotkeys.cpp b/src/hotkeys.cpp index 9db098f315..523e2f6fdd 100644 --- a/src/hotkeys.cpp +++ b/src/hotkeys.cpp @@ -27,8 +27,16 @@ static std::vector *_hotkey_lists = nullptr; /** String representation of a keycode */ struct KeycodeNames { - const std::string_view name; ///< Name of the keycode + std::string_view name; ///< Name of the keycode WindowKeyCodes keycode; ///< The keycode + std::string_view short_name; + + KeycodeNames(const std::string_view name, const WindowKeyCodes keycode, const std::string_view short_name = "") + { + this->name = name; + this->keycode = keycode; + this->short_name = short_name.empty() ? name : short_name; + } }; /** Array of non-standard keycodes that can be used in the hotkeys config file. */ @@ -38,16 +46,17 @@ static const std::initializer_list _keycode_to_name = { {"ALT", WKC_ALT}, {"META", WKC_META}, {"GLOBAL", WKC_GLOBAL_HOTKEY}, - {"ESC", WKC_ESC}, - {"BACKSPACE", WKC_BACKSPACE}, - {"INS", WKC_INSERT}, - {"DEL", WKC_DELETE}, - {"PAGEUP", WKC_PAGEUP}, - {"PAGEDOWN", WKC_PAGEDOWN}, - {"END", WKC_END}, - {"HOME", WKC_HOME}, - {"RETURN", WKC_RETURN}, - {"SPACE", WKC_SPACE}, + {"ESC", WKC_ESC, "Esc"}, + {"TAB", WKC_TAB, "Tab"}, + {"BACKSPACE", WKC_BACKSPACE, "BS"}, + {"INS", WKC_INSERT, "Ins"}, + {"DEL", WKC_DELETE, "Del"}, + {"PAGEUP", WKC_PAGEUP, "Page up"}, + {"PAGEDOWN", WKC_PAGEDOWN, "Page down"}, + {"END", WKC_END, "End"}, + {"HOME", WKC_HOME, "Home"}, + {"RETURN", WKC_RETURN, "Enter"}, + {"SPACE", WKC_SPACE, "Space"}, {"F1", WKC_F1}, {"F2", WKC_F2}, {"F3", WKC_F3}, @@ -61,31 +70,31 @@ static const std::initializer_list _keycode_to_name = { {"F11", WKC_F11}, {"F12", WKC_F12}, {"BACKQUOTE", WKC_BACKQUOTE}, - {"PAUSE", WKC_PAUSE}, - {"NUM_DIV", WKC_NUM_DIV}, - {"NUM_MUL", WKC_NUM_MUL}, - {"NUM_MINUS", WKC_NUM_MINUS}, - {"NUM_PLUS", WKC_NUM_PLUS}, - {"NUM_ENTER", WKC_NUM_ENTER}, - {"NUM_DOT", WKC_NUM_DECIMAL}, - {"SLASH", WKC_SLASH}, + {"PAUSE", WKC_PAUSE, "Pause"}, + {"NUM_DIV", WKC_NUM_DIV, "Num /"}, + {"NUM_MUL", WKC_NUM_MUL, "Num *"}, + {"NUM_MINUS", WKC_NUM_MINUS, "Num -"}, + {"NUM_PLUS", WKC_NUM_PLUS, "Num +"}, + {"NUM_ENTER", WKC_NUM_ENTER, "Num Enter"}, + {"NUM_DOT", WKC_NUM_DECIMAL, "Num ."}, + {"SLASH", WKC_SLASH, "/"}, {"/", WKC_SLASH}, /* deprecated, use SLASH */ - {"SEMICOLON", WKC_SEMICOLON}, + {"SEMICOLON", WKC_SEMICOLON, ";"}, {";", WKC_SEMICOLON}, /* deprecated, use SEMICOLON */ - {"EQUALS", WKC_EQUALS}, + {"EQUALS", WKC_EQUALS, "="}, {"=", WKC_EQUALS}, /* deprecated, use EQUALS */ - {"L_BRACKET", WKC_L_BRACKET}, + {"L_BRACKET", WKC_L_BRACKET, "["}, {"[", WKC_L_BRACKET}, /* deprecated, use L_BRACKET */ - {"BACKSLASH", WKC_BACKSLASH}, + {"BACKSLASH", WKC_BACKSLASH, "\\"}, {"\\", WKC_BACKSLASH}, /* deprecated, use BACKSLASH */ - {"R_BRACKET", WKC_R_BRACKET}, + {"R_BRACKET", WKC_R_BRACKET, "]"}, {"]", WKC_R_BRACKET}, /* deprecated, use R_BRACKET */ - {"SINGLEQUOTE", WKC_SINGLEQUOTE}, + {"SINGLEQUOTE", WKC_SINGLEQUOTE, "'"}, {"'", WKC_SINGLEQUOTE}, /* deprecated, use SINGLEQUOTE */ - {"COMMA", WKC_COMMA}, - {"PERIOD", WKC_PERIOD}, + {"COMMA", WKC_COMMA, ","}, + {"PERIOD", WKC_PERIOD, "."}, {".", WKC_PERIOD}, /* deprecated, use PERIOD */ - {"MINUS", WKC_MINUS}, + {"MINUS", WKC_MINUS, "-"}, {"-", WKC_MINUS}, /* deprecated, use MINUS */ }; @@ -221,7 +230,7 @@ std::string KeycodeToShortString(uint16_t keycode) for (const auto &kn : _keycode_to_name) { if (kn.keycode == keycode) { - str += kn.name; + str += kn.short_name; return str; } } diff --git a/src/widget.cpp b/src/widget.cpp index 0b82f623dd..7261c7a8f0 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -3124,50 +3124,72 @@ void NWidgetLeaf::DrawHotkeyHint(const Window* w) { // TODO: Rect is wrong for edit boxes, and the kind of hint we're showing // will look bad anyway. - Rect r = this->GetCurrentRect().Shrink(1); + const uint o = ScaleGUITrad(1); + Rect r = this->GetCurrentRect().Translate(o, 0); + std::string hint; + auto fontsize = FS_NORMAL; + uint16_t keycode = 0; + if (w->window_desc.cls == WC_MAIN_TOOLBAR && this->index == WID_TN_FAST_FORWARD) { // Special-case hint text for Fast-forwards because it's not really a hotkey - DrawStringMultiLine(r, "Tab", TC_WHITE, SA_LEFT | SA_BOTTOM, false, FS_NORMAL); + hint = "Tab"; } else if (w->window_desc.hotkeys != nullptr) { // Widget IDs can coincidentally overlap with hotkey IDs if // they aren't assigned properly. Avoid this. auto hk = w->window_desc.hotkeys->GetHotkeyByNum(this->index); - if (hk != nullptr) { - if (hk->keycodes.size()) { - // Find the "best" of the available keycodes - auto keycode = *(hk->keycodes.begin()); - for (auto k : hk->keycodes) { - if (!(keycode & WKC_GLOBAL_HOTKEY) && (k & WKC_GLOBAL_HOTKEY)) { - keycode = k; - } else if ('A' <= (k & ~WKC_SPECIAL_KEYS) && (k & ~WKC_SPECIAL_KEYS) <= 'Z') { + if (hk != nullptr && hk->keycodes.size()) { + // Find the "best" of the available keycodes + keycode = *(hk->keycodes.begin()); + hint = KeycodeToShortString(keycode); + for (auto k : hk->keycodes) { + if (!(keycode & WKC_GLOBAL_HOTKEY) && (k & WKC_GLOBAL_HOTKEY)) { + keycode = k; + } else { + auto h = KeycodeToShortString(k); + if (hint.length() > h.length()) { + Debug(misc, 1, "shorter hint: {}; old hint {}", h, hint); keycode = k; + hint = h; } } - - // Convert to a string - // TODO: Glyphs for Shift, Alt, etc. Colour for global. - // - On screen keyboard has a sprite for shift. - auto s = KeycodeToShortString(keycode); - - // Choose the font-size - auto availableSize = Dimension(r.right - r.left, r.bottom - r.top); - auto desiredSize = GetStringBoundingBox(s, FS_NORMAL); - auto fontsize = FS_NORMAL; - if (availableSize < desiredSize) { - fontsize = FS_SMALL; - } - - // Display the hints! - // TODO: not as readable as my mockup :( - // - Outline or a bolder font could help. - auto colour = TC_WHITE; - if (keycode & WKC_GLOBAL_HOTKEY) { - colour = TC_YELLOW; - } - DrawStringMultiLine(r, s, colour, SA_LEFT | SA_BOTTOM, false, fontsize); } + + // Convert to a string + // TODO: Glyphs for Shift, Alt, etc. Colour for global. + // - On screen keyboard has a sprite for shift. + hint = KeycodeToShortString(keycode); } } + + if (hint.empty()) { + return; + } + + // Choose the font-size + auto availableSize = Dimension(r.right - r.left, r.bottom - r.top); + auto desiredSize = GetStringBoundingBox(hint, FS_NORMAL); + if (availableSize < desiredSize) { + fontsize = FS_SMALL; + } + + // Display the hints! + // TODO: not as readable as my mockup :( + auto colour = TC_WHITE; + if (keycode & WKC_GLOBAL_HOTKEY) { + colour = TC_LIGHT_BLUE; + } + // Draw a slightly shoddy outline + DrawStringMultiLine(r.Translate( o, o), hint, TC_BLACK | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); + DrawStringMultiLine(r.Translate( o, -o), hint, TC_BLACK | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); + DrawStringMultiLine(r.Translate(-o, o), hint, TC_BLACK | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); + DrawStringMultiLine(r.Translate(-o, -o), hint, TC_BLACK | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); + DrawStringMultiLine(r.Translate( o, 0), hint, TC_BLACK | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); + DrawStringMultiLine(r.Translate(-o, 0), hint, TC_BLACK | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); + DrawStringMultiLine(r.Translate( 0, o), hint, TC_BLACK | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); + DrawStringMultiLine(r.Translate( 0, -o), hint, TC_BLACK | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); + + // Draw the hint text. + DrawStringMultiLine(r, hint, colour | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); } /** From 7b68c184fbcabadb8c2516c54aeb77f70f0f40b8 Mon Sep 17 00:00:00 2001 From: Colin Caine Date: Tue, 17 Jun 2025 18:00:40 +0200 Subject: [PATCH 5/5] WIP --- src/widget.cpp | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/widget.cpp b/src/widget.cpp index 7261c7a8f0..2aedb77220 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -3113,7 +3113,8 @@ void NWidgetLeaf::Draw(const Window *w) DrawOutline(w, this); // TODO: Draw hints only if Alt is being held. - if (!this->IsDisabled()) { + // Don't draw hotkey hints on disabled widgets or editboxes with focus. + if (!(this->IsDisabled() || (this->type == WWT_EDITBOX && w->nested_focus == this))) { this->DrawHotkeyHint(w); } } @@ -3122,12 +3123,9 @@ void NWidgetLeaf::DrawHotkeyHint(const Window* w) { // TODO: Use global hotkey for autoroads for rail, road, tram, etc. if // they have been set. - // TODO: Rect is wrong for edit boxes, and the kind of hint we're showing - // will look bad anyway. const uint o = ScaleGUITrad(1); - Rect r = this->GetCurrentRect().Translate(o, 0); + Rect r = this->GetCurrentRect().Shrink(o); std::string hint; - auto fontsize = FS_NORMAL; uint16_t keycode = 0; if (w->window_desc.cls == WC_MAIN_TOOLBAR && this->index == WID_TN_FAST_FORWARD) { @@ -3139,6 +3137,7 @@ void NWidgetLeaf::DrawHotkeyHint(const Window* w) { auto hk = w->window_desc.hotkeys->GetHotkeyByNum(this->index); if (hk != nullptr && hk->keycodes.size()) { // Find the "best" of the available keycodes + // TODO: maybe don't repeat this work so much keycode = *(hk->keycodes.begin()); hint = KeycodeToShortString(keycode); for (auto k : hk->keycodes) { @@ -3147,7 +3146,6 @@ void NWidgetLeaf::DrawHotkeyHint(const Window* w) { } else { auto h = KeycodeToShortString(k); if (hint.length() > h.length()) { - Debug(misc, 1, "shorter hint: {}; old hint {}", h, hint); keycode = k; hint = h; } @@ -3166,30 +3164,34 @@ void NWidgetLeaf::DrawHotkeyHint(const Window* w) { } // Choose the font-size - auto availableSize = Dimension(r.right - r.left, r.bottom - r.top); - auto desiredSize = GetStringBoundingBox(hint, FS_NORMAL); - if (availableSize < desiredSize) { - fontsize = FS_SMALL; - } + auto fontsize = FS_SMALL; + // It's better to have a single consistent size. + /*auto availableSize = Dimension(r.right - r.left, r.bottom - r.top);*/ + /*auto desiredSize = GetStringBoundingBox(hint, FS_NORMAL);*/ + /*if (availableSize < desiredSize) {*/ + /* fontsize = FS_SMALL;*/ + /*}*/ - // Display the hints! - // TODO: not as readable as my mockup :( + // Choose hint colour auto colour = TC_WHITE; if (keycode & WKC_GLOBAL_HOTKEY) { colour = TC_LIGHT_BLUE; } - // Draw a slightly shoddy outline - DrawStringMultiLine(r.Translate( o, o), hint, TC_BLACK | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); - DrawStringMultiLine(r.Translate( o, -o), hint, TC_BLACK | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); - DrawStringMultiLine(r.Translate(-o, o), hint, TC_BLACK | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); - DrawStringMultiLine(r.Translate(-o, -o), hint, TC_BLACK | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); - DrawStringMultiLine(r.Translate( o, 0), hint, TC_BLACK | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); - DrawStringMultiLine(r.Translate(-o, 0), hint, TC_BLACK | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); - DrawStringMultiLine(r.Translate( 0, o), hint, TC_BLACK | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); - DrawStringMultiLine(r.Translate( 0, -o), hint, TC_BLACK | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); - // Draw the hint text. - DrawStringMultiLine(r, hint, colour | TC_NO_SHADE, SA_LEFT | SA_BOTTOM, false, fontsize); + auto alignment = SA_LEFT | SA_BOTTOM; + + // Draw a slightly shoddy outline + DrawStringMultiLine(r.Translate( o, o), hint, TC_BLACK | TC_NO_SHADE, alignment, false, fontsize); + DrawStringMultiLine(r.Translate( o, -o), hint, TC_BLACK | TC_NO_SHADE, alignment, false, fontsize); + DrawStringMultiLine(r.Translate(-o, o), hint, TC_BLACK | TC_NO_SHADE, alignment, false, fontsize); + DrawStringMultiLine(r.Translate(-o, -o), hint, TC_BLACK | TC_NO_SHADE, alignment, false, fontsize); + DrawStringMultiLine(r.Translate( o, 0), hint, TC_BLACK | TC_NO_SHADE, alignment, false, fontsize); + DrawStringMultiLine(r.Translate(-o, 0), hint, TC_BLACK | TC_NO_SHADE, alignment, false, fontsize); + DrawStringMultiLine(r.Translate( 0, o), hint, TC_BLACK | TC_NO_SHADE, alignment, false, fontsize); + DrawStringMultiLine(r.Translate( 0, -o), hint, TC_BLACK | TC_NO_SHADE, alignment, false, fontsize); + + // Draw the hint text + DrawStringMultiLine(r, hint, colour | TC_NO_SHADE, alignment, false, fontsize); } /**