Skip to content

Commit

Permalink
Merge pull request #320 from Spartan322/add/keyboard-shortcuts
Browse files Browse the repository at this point in the history
Add shortcut GUI support
  • Loading branch information
Spartan322 authored Dec 10, 2024
2 parents 8e2b288 + 6979312 commit d663d8e
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 11 deletions.
98 changes: 96 additions & 2 deletions extension/src/openvic-extension/utility/UITools.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include "UITools.hpp"
#include <cctype>

#include <godot_cpp/classes/color_rect.hpp>
#include <godot_cpp/classes/line_edit.hpp>
#include <godot_cpp/classes/os.hpp>
#include <godot_cpp/classes/panel.hpp>
#include <godot_cpp/classes/style_box_empty.hpp>
#include <godot_cpp/classes/style_box_texture.hpp>
Expand All @@ -22,6 +24,10 @@
#include "openvic-extension/singletons/AssetManager.hpp"
#include "openvic-extension/singletons/GameSingleton.hpp"
#include "openvic-extension/utility/Utilities.hpp"
#include "godot_cpp/classes/global_constants.hpp"
#include "godot_cpp/classes/input_event_key.hpp"
#include "godot_cpp/classes/shortcut.hpp"
#include "godot_cpp/core/error_macros.hpp"

using namespace godot;
using namespace OpenVic;
Expand Down Expand Up @@ -60,6 +66,63 @@ GUI::Position const* UITools::get_gui_position(String const& gui_scene, String c
return position;
}

static Array get_events_from_shortcut_key(String const& key) {
Array events;
if (key.length() == 0) {
return events;
}

Key key_value = OS::get_singleton()->find_keycode_from_string(key);
if (key_value == Key::KEY_UNKNOWN) {
if (key.nocasecmp_to("DEL") == 0) {
key_value = Key::KEY_DELETE;
} else if (key.nocasecmp_to("PAGE_UP") == 0) {
key_value = Key::KEY_PAGEUP;
} else if (key.nocasecmp_to("PAGE_DOWN") == 0) {
key_value = Key::KEY_PAGEDOWN;
} else if (key == "+") {
// on most keyboards + and = are on the same key, Godot does not see them as different
key_value = Key::KEY_EQUAL;
} else if (key == "-") {
key_value = Key::KEY_MINUS;
} else if (key == ">") {
// on many keyboards > and . are on the same key, Godot does not see them as different
key_value = Key::KEY_PERIOD;
} else if (key == "<") {
// on many keyboards < and , are on the same key, Godot does not see them as different
key_value = Key::KEY_COMMA;
}
}

if (key_value == Key::KEY_UNKNOWN) {
return events;
}

Ref<InputEventKey> event;
event.instantiate();
event->set_pressed(true);
events.append(event);

if (key.length() == 1) {
if (key_value >= Key::KEY_A && key_value <= Key::KEY_Z) {
event->set_shift_pressed(std::isupper(key[0]));
} else if (key == "+") {
event->set_shift_pressed(true);

Ref<InputEventKey> second_event;
second_event.instantiate();
second_event->set_pressed(true);
second_event->set_key_label(godot::KEY_KP_ADD);
events.append(second_event);
} else if (key == ">") {
event->set_shift_pressed(true);
}
}

event->set_key_label(key_value);
return events;
}

/* GUI::Element tree -> godot::Control tree conversion code below: */

namespace OpenVic {
Expand Down Expand Up @@ -217,9 +280,15 @@ static bool generate_icon(generate_gui_args_t&& args) {
static bool generate_button(generate_gui_args_t&& args) {
GUI::Button const& button = static_cast<GUI::Button const&>(args.element);

// TODO - shortcut, clicksound, rotation (?)
// TODO - clicksound, rotation (?)
const String button_name = Utilities::std_to_godot_string(button.get_name());
const String shortcut_key_name = Utilities::std_to_godot_string(button.get_shortcut());
Array event_array = get_events_from_shortcut_key(shortcut_key_name);

ERR_FAIL_COND_V_MSG(
shortcut_key_name.length() != 0 && event_array.size() == 0, false,
vformat("Unknown shortcut key '%s' for GUI button %s", shortcut_key_name, button_name)
);
ERR_FAIL_NULL_V_MSG(button.get_sprite(), false, vformat("Null sprite for GUI button %s", button_name));

GUIButton* gui_button = nullptr;
Expand Down Expand Up @@ -266,16 +335,31 @@ static bool generate_button(generate_gui_args_t&& args) {
ret &= gui_button->set_gfx_font(button.get_font()) == OK;
}

if (shortcut_key_name.length() != 0) {
Ref<Shortcut> shortcut;
shortcut.instantiate();
shortcut->set_events(event_array);
gui_button->set_shortcut(shortcut);
}

gui_button->set_shortcut_feedback(false);
gui_button->set_shortcut_in_tooltip(false);

args.result = gui_button;
return ret;
}

static bool generate_checkbox(generate_gui_args_t&& args) {
GUI::Checkbox const& checkbox = static_cast<GUI::Checkbox const&>(args.element);

// TODO - shortcut
const String checkbox_name = Utilities::std_to_godot_string(checkbox.get_name());
const String shortcut_key_name = Utilities::std_to_godot_string(checkbox.get_shortcut());
Array event_array = get_events_from_shortcut_key(shortcut_key_name);

ERR_FAIL_COND_V_MSG(
shortcut_key_name.length() != 0 && event_array.size() == 0, false,
vformat("Unknown shortcut key '%s' for GUI checkbox %s", shortcut_key_name, checkbox_name)
);
ERR_FAIL_NULL_V_MSG(checkbox.get_sprite(), false, vformat("Null sprite for GUI checkbox %s", checkbox_name));

GFX::IconTextureSprite const* texture_sprite = checkbox.get_sprite()->cast_to<GFX::IconTextureSprite>();
Expand Down Expand Up @@ -306,6 +390,16 @@ static bool generate_checkbox(generate_gui_args_t&& args) {
ret &= gui_icon_button->set_gfx_font(checkbox.get_font()) == OK;
}

if (shortcut_key_name.length() != 0) {
Ref<Shortcut> shortcut;
shortcut.instantiate();
shortcut->set_events(event_array);
gui_icon_button->set_shortcut(shortcut);
}

gui_icon_button->set_shortcut_feedback(false);
gui_icon_button->set_shortcut_in_tooltip(false);

args.result = gui_icon_button;
return ret;
}
Expand Down
13 changes: 9 additions & 4 deletions game/project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -71,25 +71,25 @@ theme/custom="res://assets/graphics/theme/default_theme.tres"
map_north={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":87,"physical_keycode":0,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":87,"physical_keycode":0,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
]
}
map_east={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":4194321,"key_label":4194321,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":68,"physical_keycode":68,"key_label":68,"unicode":100,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":68,"physical_keycode":68,"key_label":68,"unicode":100,"location":0,"echo":false,"script":null)
]
}
map_south={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194322,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":83,"physical_keycode":0,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":83,"physical_keycode":0,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
]
}
map_west={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":4194319,"key_label":4194319,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":65,"physical_keycode":65,"key_label":65,"unicode":97,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":65,"physical_keycode":65,"key_label":65,"unicode":97,"location":0,"echo":false,"script":null)
]
}
map_zoom_in={
Expand Down Expand Up @@ -136,6 +136,11 @@ time_speed_decrease={
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194435,"physical_keycode":0,"key_label":0,"unicode":45,"location":0,"echo":false,"script":null)
]
}
menu_pause={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}

[internationalization]

Expand Down
14 changes: 10 additions & 4 deletions game/src/Game/GameSession/Menubar.gd
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ signal zoom_in_button_pressed
signal zoom_out_button_pressed
signal minimap_clicked(pos_clicked : Vector2)

var _menu_button : GUIIconButton

var _mapmode_button_group : ButtonGroup
# We use this instead of the ButtonGroup's get_buttons() as we can add null
# entries for any missing buttons, ensuring each button is at the right index.
Expand Down Expand Up @@ -53,10 +55,10 @@ func _ready() -> void:

# TODO: add keyboard shortcuts (and shortcut tooltips) where vanilla does by default + use key bindings in settings

var menu_button : GUIIconButton = get_gui_icon_button_from_nodepath(^"./menubar/menu_button")
if menu_button:
menu_button.tooltip_string = "M_MENU_BUTTON"
menu_button.pressed.connect(_on_game_session_menu_button_pressed)
_menu_button = get_gui_icon_button_from_nodepath(^"./menubar/menu_button")
if _menu_button:
_menu_button.tooltip_string = "M_MENU_BUTTON"
_menu_button.pressed.connect(_on_game_session_menu_button_pressed)

# TODO: implement ledger
var ledger_button : GUIIconButton = get_gui_icon_button_from_nodepath(^"./menubar/ledger_button")
Expand Down Expand Up @@ -93,6 +95,10 @@ func _ready() -> void:
# This will set the mapmode in GameSingleton which in turn updates the buttons so that the right one is pressed
_mapmode_pressed(0)

func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("menu_pause"):
_menu_button.pressed.emit()

# REQUIREMENTS:
# * UIFUN-10
func _on_game_session_menu_button_pressed() -> void:
Expand Down
12 changes: 12 additions & 0 deletions game/src/Game/GameSession/Topbar.gd
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,25 @@ func _ready() -> void:
if _speed_up_button:
_speed_up_button.pressed.connect(_on_increase_speed_button_pressed)
_speed_up_button.set_tooltip_string("TOPBAR_INC_SPEED")
var speed_up_action := InputEventAction.new()
speed_up_action.action = "time_speed_increase"
_speed_up_button.shortcut = Shortcut.new()
_speed_up_button.shortcut.events.append(speed_up_action)
_speed_down_button = get_gui_icon_button_from_nodepath(^"./topbar/button_speeddown")
if _speed_down_button:
_speed_down_button.pressed.connect(_on_decrease_speed_button_pressed)
_speed_down_button.set_tooltip_string("TOPBAR_DEC_SPEED")
var speed_down_action := InputEventAction.new()
speed_down_action.action = "time_speed_decrease"
_speed_down_button.shortcut = Shortcut.new()
_speed_down_button.shortcut.events.append(speed_down_action)
_pause_bg_button = get_gui_icon_button_from_nodepath(^"./topbar/pause_bg")
if _pause_bg_button:
_pause_bg_button.pressed.connect(_on_play_pause_button_pressed)
var time_pause_action := InputEventAction.new()
time_pause_action.action = "time_pause"
_pause_bg_button.shortcut = Shortcut.new()
_pause_bg_button.shortcut.events.append(time_pause_action)
_speed_indicator_button = get_gui_icon_button_from_nodepath(^"./topbar/speed_indicator")
if _speed_indicator_button:
_speed_indicator_button.pressed.connect(_on_play_pause_button_pressed)
Expand Down

0 comments on commit d663d8e

Please sign in to comment.