Skip to content

Commit 4f3c32d

Browse files
committed
feat: Add configurable hotkey label overlay for command bar buttons on macOS
1 parent 7651828 commit 4f3c32d

9 files changed

Lines changed: 139 additions & 1 deletion

File tree

Core/GameEngine/Include/Common/OptionPreferences.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class OptionPreferences : public UserPreferences
8282
Bool getScreenEdgeScrollEnabledInWindowedApp() const;
8383
Bool getScreenEdgeScrollEnabledInFullscreenApp() const;
8484
ScreenEdgeScrollMode getScreenEdgeScrollMode() const;
85+
Bool getShowHotKeyLabels() const;
8586
Bool getSendDelay();
8687
Int getFirewallBehavior();
8788
Short getFirewallPortAllocationDelta();

Core/GameEngine/Include/GameClient/GameWindow.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ enum
186186
WIN_STATUS_ALWAYS_COLOR = 0x01000000, // Never render these buttons using greyscale renderer when button disabled.
187187
WIN_STATUS_ON_MOUSE_DOWN = 0x02000000, // Pushbutton triggers on mouse down.
188188
WIN_STATUS_SHORTCUT_BUTTON = 0x04000000, // Oh god... this is a total hack for shortcut buttons to handle rendering text top left corner...
189+
#ifdef __APPLE__
190+
WIN_STATUS_HOTKEY_LABEL = 0x08000000, // Mac: hotkey-letter overlay on command-bar cameos (bold + semi-transparent bg). Runtime-only, like SHORTCUT_BUTTON (not in WindowStatusNames[]).
191+
#endif
189192
// when you edit this, remember to edit WindowStatusNames[]
190193

191194
};

Core/GameEngine/Source/Common/OptionPreferences.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,18 @@ ScreenEdgeScrollMode OptionPreferences::getScreenEdgeScrollMode() const
430430
return mode;
431431
}
432432

433+
Bool OptionPreferences::getShowHotKeyLabels() const
434+
{
435+
OptionPreferences::const_iterator it = find("ShowHotKeyLabels");
436+
if (it == end())
437+
return FALSE;
438+
439+
if (stricmp(it->second.str(), "yes") == 0)
440+
return TRUE;
441+
442+
return FALSE;
443+
}
444+
433445
Bool OptionPreferences::usesSystemMapDir()
434446
{
435447
OptionPreferences::const_iterator it = find("UseSystemMapDir");

Core/GameEngineDevice/Source/W3DDevice/GameClient/GUI/Gadget/W3DPushButton.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@
5656
#include "W3DDevice/GameClient/W3DGameWindow.h"
5757
#include "W3DDevice/GameClient/W3DDisplay.h"
5858
#include "W3DDevice/GameClient/W3DGadget.h"
59+
#ifdef __APPLE__
60+
#include "GameClient/GameFont.h"
61+
#include "GameClient/Color.h"
62+
#endif
5963

6064

6165

@@ -117,6 +121,39 @@ static void drawButtonText( GameWindow *window, WinInstanceData *instData )
117121
if( text->getFont() != window->winGetFont() )
118122
text->setFont( window->winGetFont() );
119123

124+
#ifdef __APPLE__
125+
if( BitIsSet( window->winGetStatus(), WIN_STATUS_HOTKEY_LABEL ) )
126+
{
127+
const Int badgeSize = ( (size.x < size.y) ? size.x : size.y ) / 4;
128+
129+
if( TheFontLibrary )
130+
{
131+
GameFont *winFont = window->winGetFont();
132+
AsciiString fontName = winFont ? winFont->nameString : AsciiString( "Arial" );
133+
Int fontSize = ( badgeSize * 3 ) / 4;
134+
if( fontSize < 8 )
135+
fontSize = 8;
136+
GameFont *badgeFont = TheFontLibrary->getFont( fontName, fontSize, TRUE );
137+
if( badgeFont )
138+
text->setFont( badgeFont );
139+
}
140+
141+
text->getSize( &width, &height );
142+
143+
ICoord2D badgeOrigin;
144+
badgeOrigin.x = origin.x;
145+
badgeOrigin.y = origin.y + size.y - badgeSize;
146+
147+
TheDisplay->drawFillRect( badgeOrigin.x, badgeOrigin.y, badgeSize, badgeSize, GameMakeColor( 0, 0, 0, 160 ) );
148+
149+
ICoord2D letterPos;
150+
letterPos.x = badgeOrigin.x + ( badgeSize - width ) / 2;
151+
letterPos.y = badgeOrigin.y + ( badgeSize - height ) / 2;
152+
text->draw( letterPos.x, letterPos.y, textColor, dropColor );
153+
return;
154+
}
155+
#endif
156+
120157
// get text size
121158
text->getSize( &width, &height );
122159

GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@
7878
#include "GameClient/ControlBarResizer.h"
7979
#include "GameClient/GadgetListBox.h"
8080
#include "GameClient/HotKey.h"
81+
#ifdef __APPLE__
82+
#include "Common/OptionPreferences.h"
83+
#endif
8184
#include "GameClient/GameWindowTransitions.h"
8285
#include "GameClient/GUICallbacks.h"
8386

@@ -2445,6 +2448,16 @@ void ControlBar::setCommandBarBorder( GameWindow *button, CommandButtonMappedBor
24452448
}
24462449

24472450

2451+
#ifdef __APPLE__
2452+
static UnicodeString toUpperHotKeyLabel( const AsciiString &hotKey )
2453+
{
2454+
UnicodeString label;
2455+
for (const char *c = hotKey.str(); c && *c; ++c)
2456+
label.concat( (WideChar)toupper( (unsigned char)*c ) );
2457+
return label;
2458+
}
2459+
#endif
2460+
24482461
//-------------------------------------------------------------------------------------------------
24492462
/** Set the command data into the control */
24502463
//-------------------------------------------------------------------------------------------------
@@ -2522,7 +2535,22 @@ void ControlBar::setControlCommand( GameWindow *button, const CommandButton *com
25222535
AsciiString hotKey = TheHotKeyManager->searchHotKey(commandButton->getTextLabel());
25232536
if(hotKey.isNotEmpty())
25242537
TheHotKeyManager->addHotKey(button, hotKey);
2538+
2539+
#ifdef __APPLE__
2540+
static const Bool s_showHotKeyLabels = OptionPreferences().getShowHotKeyLabels();
2541+
2542+
Bool isSpecialPowerShortcutButton = FALSE;
2543+
for (Int i = 0; i < MAX_SPECIAL_POWER_SHORTCUTS; ++i)
2544+
isSpecialPowerShortcutButton |= (m_specialPowerShortcutButtons[i] == button);
2545+
2546+
if (s_showHotKeyLabels && hotKey.isNotEmpty() && !isSpecialPowerShortcutButton)
2547+
{
2548+
GadgetButtonSetText(button, toUpperHotKeyLabel(hotKey));
2549+
button->winSetStatus(WIN_STATUS_HOTKEY_LABEL);
2550+
}
2551+
#endif
25252552
}
2553+
25262554
GadgetButtonSetAltSound(button, "GUICommandBarClick");
25272555

25282556
}

Platform/MacOS/Launcher/Sources/LauncherViewModel.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ class LauncherViewModel: ObservableObject {
2626
])
2727
}
2828
}
29+
@Published var showHotkeyLabels: Bool = SettingsDefaults.showHotkeyLabels {
30+
didSet {
31+
OptionsIniHelper.writeValue(value: showHotkeyLabels ? "yes" : "no", forKey: "ShowHotKeyLabels")
32+
}
33+
}
2934
@Published var gameLanguage: String = SettingsDefaults.gameLanguage {
3035
didSet {
3136
guard !isInitializing else { return }
@@ -100,6 +105,7 @@ class LauncherViewModel: ObservableObject {
100105
])
101106
}
102107
self.isWindowedEdgeScrollEnabled = OptionsIniHelper.readValue(forKey: "ScreenEdgeScrollEnabledInWindowedApp") == "yes"
108+
self.showHotkeyLabels = OptionsIniHelper.readValue(forKey: "ShowHotKeyLabels") == "yes"
103109
self.gameLanguage = OptionsIniHelper.readValue(forKey: "Language") ?? SettingsDefaults.gameLanguage
104110

105111
// Load settings.json
@@ -152,6 +158,7 @@ class LauncherViewModel: ObservableObject {
152158

153159
func resetAllSettings() {
154160
isWindowedEdgeScrollEnabled = SettingsDefaults.isWindowedEdgeScrollEnabled
161+
showHotkeyLabels = SettingsDefaults.showHotkeyLabels
155162
gameLanguage = SettingsDefaults.gameLanguage
156163
cameraMinHeight = SettingsDefaults.cameraMinHeight
157164
cameraMaxHeight = SettingsDefaults.cameraMaxHeight

Platform/MacOS/Launcher/Sources/Localization.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ enum L10n {
149149
var about: String { resolve("settings.about") }
150150
var aboutButton: String { resolve("settings.aboutButton") }
151151
var cameraSection: String { resolve("settings.cameraSection") }
152+
var interfaceSection: String { resolve("settings.interfaceSection") }
153+
var showHotkeyLabels: String { resolve("settings.showHotkeyLabels") }
154+
var showHotkeyLabelsDesc: String { resolve("settings.showHotkeyLabelsDesc") }
152155
var cameraMaxHeight: String { resolve("settings.cameraMaxHeight") }
153156
var cameraMinHeight: String { resolve("settings.cameraMinHeight") }
154157
var cameraSpeed: String { resolve("settings.cameraSpeed") }
@@ -239,6 +242,9 @@ enum L10n {
239242
"settings.about": "System",
240243
"settings.aboutButton": "About Generals Online",
241244
"settings.cameraSection": "Camera",
245+
"settings.interfaceSection": "Interface",
246+
"settings.showHotkeyLabels": "Show Hotkey Labels",
247+
"settings.showHotkeyLabelsDesc": "Displays each command's hotkey letter on its command-bar icon.",
242248
"settings.cameraMaxHeight": "Max Height (Zoom)",
243249
"settings.cameraMinHeight": "Min Height (Close)",
244250
"settings.cameraSpeed": "Camera Speed",
@@ -319,6 +325,9 @@ enum L10n {
319325
"settings.about": "Система",
320326
"settings.aboutButton": "О программе",
321327
"settings.cameraSection": "Камера",
328+
"settings.interfaceSection": "Интерфейс",
329+
"settings.showHotkeyLabels": "Показывать буквы хоткеев",
330+
"settings.showHotkeyLabelsDesc": "Отображает букву горячей клавиши на иконке команды в панели.",
322331
"settings.cameraMaxHeight": "Макс. высота (Зум)",
323332
"settings.cameraMinHeight": "Мин. высота (Приближение)",
324333
"settings.cameraSpeed": "Скорость камеры",
@@ -399,6 +408,9 @@ enum L10n {
399408
"settings.about": "Система",
400409
"settings.aboutButton": "Про програму",
401410
"settings.cameraSection": "Камера",
411+
"settings.interfaceSection": "Інтерфейс",
412+
"settings.showHotkeyLabels": "Показувати літери гарячих клавіш",
413+
"settings.showHotkeyLabelsDesc": "Відображає літеру гарячої клавіші на іконці команди в панелі.",
402414
"settings.cameraMaxHeight": "Макс. висота (Зум)",
403415
"settings.cameraMinHeight": "Мін. висота (Наближення)",
404416
"settings.cameraSpeed": "Швидкість камери",
@@ -479,6 +491,9 @@ enum L10n {
479491
"settings.about": "सिस्टम (System)",
480492
"settings.aboutButton": "Generals Online के बारे में",
481493
"settings.cameraSection": "कैमरा (Camera)",
494+
"settings.interfaceSection": "इंटरफ़ेस (Interface)",
495+
"settings.showHotkeyLabels": "हॉटकी अक्षर दिखाएं",
496+
"settings.showHotkeyLabelsDesc": "कमांड बार के आइकन पर प्रत्येक कमांड की हॉटकी का अक्षर दिखाता है।",
482497
"settings.cameraMaxHeight": "अधिकतम ऊंचाई (Zoom)",
483498
"settings.cameraMinHeight": "न्यूनतम ऊंचाई (Close)",
484499
"settings.cameraSpeed": "कैमरा गति",
@@ -559,6 +574,9 @@ enum L10n {
559574
"settings.about": "系统",
560575
"settings.aboutButton": "关于 Generals Online",
561576
"settings.cameraSection": "相机",
577+
"settings.interfaceSection": "界面",
578+
"settings.showHotkeyLabels": "显示快捷键字母",
579+
"settings.showHotkeyLabelsDesc": "在指令栏图标上显示每个指令的快捷键字母。",
562580
"settings.cameraMaxHeight": "最大高度 (缩放)",
563581
"settings.cameraMinHeight": "最小高度 (靠近)",
564582
"settings.cameraSpeed": "相机速度",
@@ -639,6 +657,9 @@ enum L10n {
639657
"settings.about": "النظام (System)",
640658
"settings.aboutButton": "حول Generals Online",
641659
"settings.cameraSection": "الكاميرا",
660+
"settings.interfaceSection": "الواجهة",
661+
"settings.showHotkeyLabels": "إظهار أحرف مفاتيح الاختصار",
662+
"settings.showHotkeyLabelsDesc": "يعرض حرف مفتاح الاختصار لكل أمر على أيقونته في شريط الأوامر.",
642663
"settings.cameraMaxHeight": "الارتفاع الأقصى (التقريب)",
643664
"settings.cameraMinHeight": "الارتفاع الأدنى (التبعيد)",
644665
"settings.cameraSpeed": "سرعة الكاميرا",
@@ -719,6 +740,9 @@ enum L10n {
719740
"settings.about": "Жүйе (System)",
720741
"settings.aboutButton": "Generals Online туралы",
721742
"settings.cameraSection": "Камера",
743+
"settings.interfaceSection": "Интерфейс",
744+
"settings.showHotkeyLabels": "Жылдам пернелер әріптерін көрсету",
745+
"settings.showHotkeyLabelsDesc": "Командалар тақтасындағы белгішеде команданың жылдам перне әрпін көрсетеді.",
722746
"settings.cameraMaxHeight": "Максималды биіктік (Зум)",
723747
"settings.cameraMinHeight": "Минималды биіктік (Жақындату)",
724748
"settings.cameraSpeed": "Камера жылдамдығы",
@@ -799,6 +823,9 @@ enum L10n {
799823
"settings.about": "Hệ thống",
800824
"settings.aboutButton": "Về Generals Online",
801825
"settings.cameraSection": "Camera",
826+
"settings.interfaceSection": "Giao diện",
827+
"settings.showHotkeyLabels": "Hiển thị chữ phím tắt",
828+
"settings.showHotkeyLabelsDesc": "Hiển thị chữ phím tắt của mỗi lệnh trên biểu tượng ở thanh lệnh.",
802829
"settings.cameraMaxHeight": "Chiều cao tối đa (Zoom)",
803830
"settings.cameraMinHeight": "Chiều cao tối thiểu (Gần)",
804831
"settings.cameraSpeed": "Tốc độ camera",
@@ -879,6 +906,9 @@ enum L10n {
879906
"settings.about": "System",
880907
"settings.aboutButton": "O Generals Online",
881908
"settings.cameraSection": "Kamera",
909+
"settings.interfaceSection": "Interfejs",
910+
"settings.showHotkeyLabels": "Pokaż litery skrótów",
911+
"settings.showHotkeyLabelsDesc": "Wyświetla literę skrótu klawiszowego na ikonie polecenia na pasku.",
882912
"settings.cameraMaxHeight": "Maks. wysokość (Zoom)",
883913
"settings.cameraMinHeight": "Min. wysokość (Zbliżenie)",
884914
"settings.cameraSpeed": "Prędkość kamery",
@@ -959,6 +989,9 @@ enum L10n {
959989
"settings.about": "System",
960990
"settings.aboutButton": "Über Generals Online",
961991
"settings.cameraSection": "Kamera",
992+
"settings.interfaceSection": "Benutzeroberfläche",
993+
"settings.showHotkeyLabels": "Tastenkürzel-Buchstaben anzeigen",
994+
"settings.showHotkeyLabelsDesc": "Zeigt den Tastenkürzel-Buchstaben jedes Befehls auf seinem Symbol in der Befehlsleiste an.",
962995
"settings.cameraMaxHeight": "Maximale Höhe (Zoom)",
963996
"settings.cameraMinHeight": "Minimale Höhe (Nah)",
964997
"settings.cameraSpeed": "Kamerageschwindigkeit",
@@ -1039,6 +1072,9 @@ enum L10n {
10391072
"settings.about": "Sistema",
10401073
"settings.aboutButton": "Acerca de Generals Online",
10411074
"settings.cameraSection": "Cámara",
1075+
"settings.interfaceSection": "Interfaz",
1076+
"settings.showHotkeyLabels": "Mostrar letras de atajos",
1077+
"settings.showHotkeyLabelsDesc": "Muestra la letra del atajo de cada comando en su icono de la barra de comandos.",
10421078
"settings.cameraMaxHeight": "Altura máxima (Zoom)",
10431079
"settings.cameraMinHeight": "Altura mínima (Acercar)",
10441080
"settings.cameraSpeed": "Velocidad de cámara",
@@ -1119,6 +1155,9 @@ enum L10n {
11191155
"settings.about": "Sistem",
11201156
"settings.aboutButton": "Generals Online Hakkında",
11211157
"settings.cameraSection": "Kamera",
1158+
"settings.interfaceSection": "Arayüz",
1159+
"settings.showHotkeyLabels": "Kısayol Harflerini Göster",
1160+
"settings.showHotkeyLabelsDesc": "Her komutun kısayol harfini komut çubuğundaki simgesinde gösterir.",
11221161
"settings.cameraMaxHeight": "Maks. Yükseklik (Zoom)",
11231162
"settings.cameraMinHeight": "Min. Yükseklik (Yakınlaşma)",
11241163
"settings.cameraSpeed": "Kamera Hızı",

Platform/MacOS/Launcher/Sources/SettingsDefaults.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Foundation
22

33
struct SettingsDefaults {
44
static let isWindowedEdgeScrollEnabled = false
5+
static let showHotkeyLabels = false
56
static let gameLanguage = "english"
67

78
static let cameraMinHeight: Double = 210.0

Platform/MacOS/Launcher/Sources/SidebarView.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,17 @@ struct SidebarView: View {
8080
defaultValue: SettingsDefaults.cameraMoveSpeed,
8181
scope: .global
8282
)
83-
83+
84+
// --- INTERFACE SECTION ---
85+
_buildSectionHeader(title: L10n.settings.interfaceSection)
86+
87+
_buildSidebarSettingToggle(
88+
title: L10n.settings.showHotkeyLabels,
89+
description: L10n.settings.showHotkeyLabelsDesc,
90+
isOn: $viewModel.showHotkeyLabels,
91+
scope: .global
92+
)
93+
8494
// --- PERFORMANCE SECTION ---
8595
_buildSectionHeader(title: L10n.settings.fpsSection)
8696

0 commit comments

Comments
 (0)