diff --git a/src/gfx.cpp b/src/gfx.cpp index 5010874810..8cdfaba8df 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -36,6 +36,8 @@ byte _support8bpp; CursorVars _cursor; bool _ctrl_pressed; ///< Is Ctrl pressed? bool _shift_pressed; ///< Is Shift pressed? +bool _invert_ctrl; +bool _invert_shift; byte _fast_forward; bool _left_button_down; ///< Is left mouse button pressed? bool _left_button_clicked; ///< Is left mouse button clicked? diff --git a/src/gfx_func.h b/src/gfx_func.h index 75a8459aff..4e12a4d946 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -56,6 +56,8 @@ extern byte _support8bpp; extern CursorVars _cursor; extern bool _ctrl_pressed; ///< Is Ctrl pressed? extern bool _shift_pressed; ///< Is Shift pressed? +extern bool _invert_ctrl; +extern bool _invert_shift; extern byte _fast_forward; extern bool _left_button_down; @@ -73,6 +75,7 @@ extern Palette _cur_palette; ///< Current palette void HandleKeypress(uint keycode, WChar key); void HandleTextInput(const char *str, bool marked = false, const char *caret = nullptr, const char *insert_location = nullptr, const char *replacement_end = nullptr); void HandleCtrlChanged(); +void HandleShiftChanged(); void HandleMouseEvents(); void UpdateWindows(); diff --git a/src/gui.h b/src/gui.h index 39e80a5b66..3bb78c4078 100644 --- a/src/gui.h +++ b/src/gui.h @@ -59,6 +59,8 @@ void ShowEstimatedCostOrIncome(Money cost, int x, int y); void ShowExtraViewPortWindow(TileIndex tile = INVALID_TILE); void ShowExtraViewPortWindowForTileUnderCursor(); +void ShowModifierKeyToggleWindow(); + /* bridge_gui.cpp */ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transport_type, byte bridge_type); diff --git a/src/lang/english.txt b/src/lang/english.txt index 1955ce42c0..b88b470757 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -488,6 +488,7 @@ STR_ABOUT_MENU_ZOOMIN_SCREENSHOT :Fully zoomed in STR_ABOUT_MENU_DEFAULTZOOM_SCREENSHOT :Default zoom screenshot STR_ABOUT_MENU_GIANT_SCREENSHOT :Whole map screenshot STR_ABOUT_MENU_SHOW_FRAMERATE :Show frame rate +STR_ABOUT_MENU_SHOW_TOGGLE_MODIFIER_KEYS :Modifier key window STR_ABOUT_MENU_ABOUT_OPENTTD :About 'OpenTTD' STR_ABOUT_MENU_SPRITE_ALIGNER :Sprite aligner STR_ABOUT_MENU_TOGGLE_BOUNDING_BOXES :Toggle bounding boxes @@ -6144,3 +6145,9 @@ STR_SCHDISPATCH_SUMMARY_L2 :{BLACK}This sch STR_SCHDISPATCH_SUMMARY_L3 :{BLACK}Maximum delay of {STRING3} is allowed before the slot is skipped. STR_SCHDISPATCH_SUMMARY_NOT_ENABLED :{BLACK}This schedule is not active. +# Modifier key toggle window +STR_MODIFIER_KEY_TOGGLE_CAPTION :{WHITE}Modifier keys +STR_SHIFT_KEY_NAME :{BLACK}Shift +STR_CTRL_KEY_NAME :{BLACK}Ctrl +STR_MODIFIER_TOGGLE_SHIFT_TOOLTIP :{BLACK}Click to invert state of Shift key +STR_MODIFIER_TOGGLE_CTRL_TOOLTIP :{BLACK}Click to invert state of Ctrl key diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index e2a5169bc3..20c2947e70 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -1315,3 +1315,88 @@ void ShowQuery(StringID caption, StringID message, Window *parent, QueryCallback new QueryWindow(&_query_desc, caption, message, parent, callback); } + +static const NWidgetPart _modifier_key_toggle_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_MODIFIER_KEY_TOGGLE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_SHADEBOX, COLOUR_GREY), + NWidget(WWT_STICKYBOX, COLOUR_GREY), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(NWID_SPACER), SetMinimalSize(0, 2), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(2, 0, 2), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MKT_SHIFT), SetMinimalSize(78, 12), SetFill(1, 0), + SetDataTip(STR_SHIFT_KEY_NAME, STR_MODIFIER_TOGGLE_SHIFT_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MKT_CTRL), SetMinimalSize(78, 12), SetFill(1, 0), + SetDataTip(STR_CTRL_KEY_NAME, STR_MODIFIER_TOGGLE_CTRL_TOOLTIP), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 2), + EndContainer(), +}; + +struct ModifierKeyToggleWindow : Window { + ModifierKeyToggleWindow(WindowDesc *desc, WindowNumber window_number) : + Window(desc) + { + this->InitNested(window_number); + this->UpdateButtons(); + } + + ~ModifierKeyToggleWindow() + { + _invert_shift = false; + _invert_ctrl = false; + } + + void UpdateButtons() + { + this->SetWidgetLoweredState(WID_MKT_SHIFT, _shift_pressed); + this->SetWidgetLoweredState(WID_MKT_CTRL, _ctrl_pressed); + this->SetDirty(); + } + + EventState OnCTRLStateChange() override + { + this->UpdateButtons(); + return ES_NOT_HANDLED; + } + + void OnShiftStateChange() override + { + this->UpdateButtons(); + } + + void OnClick(Point pt, int widget, int click_count) override + { + switch (widget) { + case WID_MKT_SHIFT: + _invert_shift = !_invert_shift; + UpdateButtons(); + break; + + case WID_MKT_CTRL: + _invert_ctrl = !_invert_ctrl; + UpdateButtons(); + break; + } + } + + void OnInvalidateData(int data = 0, bool gui_scope = true) override + { + if (!gui_scope) return; + this->UpdateButtons(); + } +}; + +static WindowDesc _modifier_key_toggle_desc( + WDP_AUTO, "modifier_key_toggle", 0, 0, + WC_MODIFIER_KEY_TOGGLE, WC_NONE, + WDF_NO_FOCUS, + _modifier_key_toggle_widgets, lengthof(_modifier_key_toggle_widgets) +); + +void ShowModifierKeyToggleWindow() +{ + AllocateWindowDescFront(&_modifier_key_toggle_desc, 0); +} diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index 1eb818f079..4ed6ac0d46 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -1070,7 +1070,7 @@ static CallBackFunction PlaceLandBlockInfo() static CallBackFunction ToolbarHelpClick(Window *w) { - PopupMainToolbMenu(w, _game_mode == GM_EDITOR ? (int)WID_TE_HELP : (int)WID_TN_HELP, STR_ABOUT_MENU_LAND_BLOCK_INFO, _settings_client.gui.newgrf_developer_tools ? 13 : 10); + PopupMainToolbMenu(w, _game_mode == GM_EDITOR ? (int)WID_TE_HELP : (int)WID_TN_HELP, STR_ABOUT_MENU_LAND_BLOCK_INFO, _settings_client.gui.newgrf_developer_tools ? 14 : 11); return CBF_NONE; } @@ -1173,10 +1173,11 @@ static CallBackFunction MenuClickHelp(int index) case 6: MenuClickLargeWorldScreenshot(SC_DEFAULTZOOM); break; case 7: MenuClickLargeWorldScreenshot(SC_WORLD); break; case 8: ShowFramerateWindow(); break; - case 9: ShowAboutWindow(); break; - case 10: ShowSpriteAlignerWindow(); break; - case 11: ToggleBoundingBoxes(); break; - case 12: ToggleDirtyBlocks(); break; + case 9: ShowModifierKeyToggleWindow(); break; + case 10: ShowAboutWindow(); break; + case 11: ShowSpriteAlignerWindow(); break; + case 12: ToggleBoundingBoxes(); break; + case 13: ToggleDirtyBlocks(); break; } return CBF_NONE; } diff --git a/src/video/allegro_v.cpp b/src/video/allegro_v.cpp index 3d4aea8f11..38a72ba1f9 100644 --- a/src/video/allegro_v.cpp +++ b/src/video/allegro_v.cpp @@ -499,9 +499,10 @@ void VideoDriver_Allegro::MainLoop() next_tick = cur_ticks + MILLISECONDS_PER_TICK; bool old_ctrl_pressed = _ctrl_pressed; + bool old_shift_pressed = _shift_pressed; - _ctrl_pressed = !!(key_shifts & KB_CTRL_FLAG); - _shift_pressed = !!(key_shifts & KB_SHIFT_FLAG); + _ctrl_pressed = !!(key_shifts & KB_CTRL_FLAG) != _invert_ctrl; + _shift_pressed = !!(key_shifts & KB_SHIFT_FLAG) != _invert_shift; /* determine which directional keys are down */ _dirkeys = @@ -511,6 +512,7 @@ void VideoDriver_Allegro::MainLoop() (key[KEY_DOWN] ? 8 : 0); if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged(); + if (old_shift_pressed != _shift_pressed) HandleShiftChanged(); GameLoop(); diff --git a/src/video/cocoa/event.mm b/src/video/cocoa/event.mm index 9af2fa5c4c..0e067840a1 100644 --- a/src/video/cocoa/event.mm +++ b/src/video/cocoa/event.mm @@ -695,11 +695,13 @@ void QZ_GameLoop() next_tick = cur_ticks + MILLISECONDS_PER_TICK; bool old_ctrl_pressed = _ctrl_pressed; + bool old_shift_pressed = _shift_pressed; - _ctrl_pressed = !!(_current_mods & ( _settings_client.gui.right_mouse_btn_emulation != RMBE_CONTROL ? NSControlKeyMask : NSCommandKeyMask)); - _shift_pressed = !!(_current_mods & NSShiftKeyMask); + _ctrl_pressed = !!(_current_mods & ( _settings_client.gui.right_mouse_btn_emulation != RMBE_CONTROL ? NSControlKeyMask : NSCommandKeyMask)) != _invert_ctrl; + _shift_pressed = !!(_current_mods & NSShiftKeyMask) != _invert_shift; if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged(); + if (old_shift_pressed != _shift_pressed) HandleShiftChanged(); GameLoop(); diff --git a/src/video/sdl_v.cpp b/src/video/sdl_v.cpp index fdaac36605..983bcd923f 100644 --- a/src/video/sdl_v.cpp +++ b/src/video/sdl_v.cpp @@ -728,9 +728,10 @@ void VideoDriver_SDL::MainLoop() next_tick = cur_ticks + MILLISECONDS_PER_TICK; bool old_ctrl_pressed = _ctrl_pressed; + bool old_shift_pressed = _shift_pressed; - _ctrl_pressed = !!(mod & KMOD_CTRL); - _shift_pressed = !!(mod & KMOD_SHIFT); + _ctrl_pressed = !!(mod & KMOD_CTRL) != _invert_ctrl; + _shift_pressed = !!(mod & KMOD_SHIFT) != _invert_shift; /* determine which directional keys are down */ _dirkeys = @@ -746,6 +747,7 @@ void VideoDriver_SDL::MainLoop() (keys[SDLK_DOWN] ? 8 : 0); #endif if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged(); + if (old_shift_pressed != _shift_pressed) HandleShiftChanged(); /* The gameloop is the part that can run asynchronously. The rest * except sleeping can't. */ diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 387b32b002..a0bb6bd755 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -1243,9 +1243,10 @@ void VideoDriver_Win32::MainLoop() next_tick = cur_ticks + MILLISECONDS_PER_TICK; bool old_ctrl_pressed = _ctrl_pressed; + bool old_shift_pressed = _shift_pressed; - _ctrl_pressed = _wnd.has_focus && GetAsyncKeyState(VK_CONTROL)<0; - _shift_pressed = _wnd.has_focus && GetAsyncKeyState(VK_SHIFT)<0; + _ctrl_pressed = (_wnd.has_focus && GetAsyncKeyState(VK_CONTROL) < 0) != _invert_ctrl; + _shift_pressed = (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0) != _invert_shift; /* determine which directional keys are down */ if (_wnd.has_focus) { @@ -1259,6 +1260,7 @@ void VideoDriver_Win32::MainLoop() } if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged(); + if (old_shift_pressed != _shift_pressed) HandleShiftChanged(); /* Flush GDI buffer to ensure we don't conflict with the drawing thread. */ GdiFlush(); diff --git a/src/widgets/misc_widget.h b/src/widgets/misc_widget.h index fb7940722e..c139e04d49 100644 --- a/src/widgets/misc_widget.h +++ b/src/widgets/misc_widget.h @@ -57,4 +57,10 @@ enum TextfileWidgets { WID_TF_HSCROLLBAR, ///< Horizontal scrollbar to scroll through the textfile left-to-right. }; +/** Widgets of the #TextfileWindow class. */ +enum ModifierKeyToggleWidgets { + WID_MKT_SHIFT, + WID_MKT_CTRL, +}; + #endif /* WIDGETS_MISC_WIDGET_H */ diff --git a/src/window.cpp b/src/window.cpp index c8dee0379e..e4d43847f0 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2743,6 +2743,17 @@ void HandleCtrlChanged() } } +/** + * State of SHIFT key has changed + */ +void HandleShiftChanged() +{ + Window *w; + FOR_ALL_WINDOWS_FROM_FRONT(w) { + w->OnShiftStateChange(); + } +} + /** * Insert a text string at the cursor position into the edit box widget. * @param wid Edit box widget. diff --git a/src/window_gui.h b/src/window_gui.h index 6c3638de20..636c06e3fd 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -667,6 +667,10 @@ public: */ virtual EventState OnCTRLStateChange() { return ES_NOT_HANDLED; } + /** + * The state of the shift key has changed + */ + virtual void OnShiftStateChange() {} /** * A click with the left mouse button has been made on the window. diff --git a/src/window_type.h b/src/window_type.h index 96743dee32..75bceaace0 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -755,6 +755,11 @@ enum WindowClass { WC_BUILD_VIRTUAL_TRAIN, WC_CREATE_TEMPLATE, + /** + * Modifier key toggle window. + */ + WC_MODIFIER_KEY_TOGGLE, + WC_INVALID = 0xFFFF, ///< Invalid window. };