From dca4ea1cb4a727e733f20c08955d8b6cc9f1337a Mon Sep 17 00:00:00 2001 From: Relintai Date: Sat, 31 Jul 2021 15:09:54 +0200 Subject: [PATCH] Keyboard/controller navigation for the main menu. --- game/menu/Menu.gd | 36 +++++++++- game/menu/Menu.tscn | 58 +++++++++++++--- game/scenes/CharacterCreationMenu.gd | 8 +++ game/scenes/CharacterSelectorMenu.gd | 11 +++ game/scenes/Main.tscn | 6 +- game/ui/Window.gd | 75 +++++++++++++++++++++ game/ui/about/About.tscn | 6 +- game/ui/options/Options.tscn | 5 +- game/ui/theme/button_bg_stylebox_focus.tres | 2 +- 9 files changed, 191 insertions(+), 16 deletions(-) create mode 100644 game/ui/Window.gd diff --git a/game/menu/Menu.gd b/game/menu/Menu.gd index 8280c9d7..f2a3531c 100644 --- a/game/menu/Menu.gd +++ b/game/menu/Menu.gd @@ -25,27 +25,59 @@ class_name Menu export(int, "Character Select", "Character Create") var start_menu : int = 0 export (NodePath) var character_selector_scene : NodePath export (NodePath) var charcer_creation_scenes : NodePath +export (NodePath) var option_buttons_path : NodePath enum StartMenuTypes { CHARACTER_SELECT, CHARACTER_CREATE } -func _ready(): +var _menu : int = 0 +var _viewport : Viewport = null + +func _ready() -> void: switch_to_menu(start_menu) + +func _enter_tree() -> void: + #find the viewport + var n : Node = self + + while n: + n = n.get_parent() + + if n is Viewport: + _viewport = n as Viewport + _viewport.connect("gui_focus_changed", self, "_on_control_focus_changed") + break + +func _exit_tree(): + if _viewport: + _viewport.disconnect("gui_focus_changed", self, "_on_control_focus_changed") + + _viewport = null +func _on_control_focus_changed(node : Control) -> void: + if !node: + if _menu == StartMenuTypes.CHARACTER_SELECT: + get_node(character_selector_scene).focus() + else: + get_node(character_selector_scene).focus() + func switch_to_menu(menu : int) -> void: + _menu = menu if menu == StartMenuTypes.CHARACTER_SELECT: get_node(character_selector_scene).show() + get_node(option_buttons_path).show() else: get_node(character_selector_scene).hide() if menu == StartMenuTypes.CHARACTER_CREATE: get_node(charcer_creation_scenes).show() + get_node(option_buttons_path).hide() else: get_node(charcer_creation_scenes).hide() -func _on_About_pressed(): +func _on_About_pressed() -> void: pass # Replace with function body. diff --git a/game/menu/Menu.tscn b/game/menu/Menu.tscn index 1b99f47e..63822e57 100644 --- a/game/menu/Menu.tscn +++ b/game/menu/Menu.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=35 format=2] +[gd_scene load_steps=36 format=2] [ext_resource path="res://ui/theme/ui_theme.tres" type="Theme" id=1] [ext_resource path="res://ui/menu/CharacterEntry.tscn" type="PackedScene" id=2] @@ -21,6 +21,7 @@ [ext_resource path="res://scripts/settings/DirectionalLightSettings.gd" type="Script" id=19] [ext_resource path="res://ui/about/About.tscn" type="PackedScene" id=20] [ext_resource path="res://menu/ExitButton.gd" type="Script" id=21] +[ext_resource path="res://ui/Window.gd" type="Script" id=22] [sub_resource type="TerraChunkBlocky" id=1] resource_name = "Chunk[-2,0]" @@ -263,6 +264,7 @@ __meta__ = { } character_selector_scene = NodePath("CharacterSelectorMenu") charcer_creation_scenes = NodePath("CharacterCreationMenu") +option_buttons_path = NodePath("OptionsButton") [node name="GameName" type="PanelContainer" parent="."] anchor_left = 0.5 @@ -348,24 +350,35 @@ size_flags_horizontal = 3 margin_top = 458.0 margin_right = 277.0 margin_bottom = 484.57 +focus_neighbour_left = NodePath("../../../../../../OptionsButton/Control/VBoxContainer/About") +focus_neighbour_bottom = NodePath("../New") +focus_next = NodePath("../New") +focus_previous = NodePath("../../../../../../OptionsButton/Control/VBoxContainer/ExitButton") text = "Load" [node name="New" type="Button" parent="CharacterSelectorMenu/CharacterSelector/VBoxContainer/CharacterSelector/VBoxContainer"] margin_top = 492.0 margin_right = 277.0 margin_bottom = 518.57 +focus_neighbour_left = NodePath("../../../../../../OptionsButton/Control/VBoxContainer/OptionsOpen") +focus_neighbour_top = NodePath("../Load") +focus_neighbour_bottom = NodePath("../Delete") +focus_next = NodePath("../Delete") +focus_previous = NodePath("../Load") text = "New" [node name="Delete" type="Button" parent="CharacterSelectorMenu/CharacterSelector/VBoxContainer/CharacterSelector/VBoxContainer"] margin_top = 526.0 margin_right = 277.0 margin_bottom = 552.57 +focus_neighbour_left = NodePath("../../../../../../OptionsButton/Control/VBoxContainer/ExitButton") +focus_neighbour_top = NodePath("../New") +focus_previous = NodePath("../New") text = "Delete" [node name="PlayerDisplays" type="Node" parent="CharacterSelectorMenu"] [node name="CharacterCreationMenu" type="Control" parent="."] -visible = false anchor_right = 1.0 anchor_bottom = 1.0 script = ExtResource( 6 ) @@ -457,9 +470,11 @@ visible = false anchor_right = 1.0 anchor_bottom = 1.0 mouse_filter = 2 +script = ExtResource( 22 ) __meta__ = { "_edit_use_anchors_": false } +focus_button_path = NodePath("PanelContainer/VBoxContainer/ConnectButton") [node name="PanelContainer" type="PanelContainer" parent="ConnectMenu"] anchor_left = 0.5 @@ -547,9 +562,11 @@ visible = false anchor_right = 1.0 anchor_bottom = 1.0 mouse_filter = 2 +script = ExtResource( 22 ) __meta__ = { "_edit_use_anchors_": false } +focus_button_path = NodePath("PanelContainer/VBoxContainer/HostButton") [node name="PanelContainer" type="PanelContainer" parent="HostMenu"] anchor_left = 0.5 @@ -659,6 +676,9 @@ margin_top = 13.0 margin_right = 120.0 margin_bottom = 39.5702 rect_min_size = Vector2( 120, 0 ) +focus_neighbour_right = NodePath("../../../../CharacterSelectorMenu/CharacterSelector/VBoxContainer/CharacterSelector/VBoxContainer/Load") +focus_neighbour_bottom = NodePath("../About") +focus_next = NodePath("../About") text = "Disconnect" script = ExtResource( 13 ) __meta__ = { @@ -670,6 +690,9 @@ margin_top = 13.0 margin_right = 120.0 margin_bottom = 39.5702 rect_min_size = Vector2( 120, 0 ) +focus_neighbour_right = NodePath("../../../../CharacterSelectorMenu/CharacterSelector/VBoxContainer/CharacterSelector/VBoxContainer/Load") +focus_neighbour_bottom = NodePath("../Host") +focus_next = NodePath("../Host") text = "Connect" script = ExtResource( 11 ) __meta__ = { @@ -681,6 +704,11 @@ margin_top = 47.0 margin_right = 120.0 margin_bottom = 73.5702 rect_min_size = Vector2( 120, 0 ) +focus_neighbour_top = NodePath("../Connect") +focus_neighbour_right = NodePath("../../../../CharacterSelectorMenu/CharacterSelector/VBoxContainer/CharacterSelector/VBoxContainer/Load") +focus_neighbour_bottom = NodePath("../About") +focus_next = NodePath("../About") +focus_previous = NodePath("../Connect") text = "Host" script = ExtResource( 12 ) __meta__ = { @@ -689,9 +717,9 @@ __meta__ = { [node name="Button2" type="Button" parent="OptionsButton/Control/VBoxContainer"] visible = false -margin_top = 81.0 +margin_top = 68.0 margin_right = 120.0 -margin_bottom = 107.57 +margin_bottom = 94.5702 rect_min_size = Vector2( 120, 0 ) text = "Login" __meta__ = { @@ -700,9 +728,9 @@ __meta__ = { [node name="Button3" type="Button" parent="OptionsButton/Control/VBoxContainer"] visible = false -margin_top = 81.0 +margin_top = 68.0 margin_right = 120.0 -margin_bottom = 107.57 +margin_bottom = 94.5702 rect_min_size = Vector2( 120, 0 ) text = "Register" __meta__ = { @@ -714,16 +742,26 @@ margin_top = 81.0 margin_right = 120.0 margin_bottom = 107.57 rect_min_size = Vector2( 120, 0 ) +focus_neighbour_top = NodePath("../Host") +focus_neighbour_right = NodePath("../../../../CharacterSelectorMenu/CharacterSelector/VBoxContainer/CharacterSelector/VBoxContainer/Load") +focus_neighbour_bottom = NodePath("../OptionsOpen") +focus_next = NodePath("../OptionsOpen") +focus_previous = NodePath("../Host") text = "About" __meta__ = { "_edit_use_anchors_": false } -[node name="Button" type="Button" parent="OptionsButton/Control/VBoxContainer"] +[node name="OptionsOpen" type="Button" parent="OptionsButton/Control/VBoxContainer"] margin_top = 115.0 margin_right = 120.0 margin_bottom = 141.57 rect_min_size = Vector2( 120, 0 ) +focus_neighbour_top = NodePath("../About") +focus_neighbour_right = NodePath("../../../../CharacterSelectorMenu/CharacterSelector/VBoxContainer/CharacterSelector/VBoxContainer/New") +focus_neighbour_bottom = NodePath("../ExitButton") +focus_next = NodePath("../ExitButton") +focus_previous = NodePath("../About") text = "Options" __meta__ = { "_edit_use_anchors_": false @@ -734,6 +772,10 @@ margin_top = 149.0 margin_right = 120.0 margin_bottom = 175.57 rect_min_size = Vector2( 120, 0 ) +focus_neighbour_top = NodePath("../OptionsOpen") +focus_neighbour_right = NodePath("../../../../CharacterSelectorMenu/CharacterSelector/VBoxContainer/CharacterSelector/VBoxContainer/Delete") +focus_next = NodePath("../../../../CharacterSelectorMenu/CharacterSelector/VBoxContainer/CharacterSelector/VBoxContainer/Load") +focus_previous = NodePath("../OptionsOpen") text = "Exit" script = ExtResource( 21 ) __meta__ = { @@ -811,4 +853,4 @@ script = ExtResource( 19 ) [connection signal="pressed" from="OptionsButton/Control/VBoxContainer/Button2" to="Login" method="show"] [connection signal="pressed" from="OptionsButton/Control/VBoxContainer/Button3" to="Register" method="show"] [connection signal="pressed" from="OptionsButton/Control/VBoxContainer/About" to="About" method="show"] -[connection signal="pressed" from="OptionsButton/Control/VBoxContainer/Button" to="Options" method="show"] +[connection signal="pressed" from="OptionsButton/Control/VBoxContainer/OptionsOpen" to="Options" method="show"] diff --git a/game/scenes/CharacterCreationMenu.gd b/game/scenes/CharacterCreationMenu.gd index 0ffe542f..2f078540 100644 --- a/game/scenes/CharacterCreationMenu.gd +++ b/game/scenes/CharacterCreationMenu.gd @@ -59,6 +59,8 @@ func _ready(): if fb != null: fb.pressed = true + + connect("visibility_changed", self, "on_visibility_changed") func create() -> void: if name_line_edit.text == "": @@ -84,3 +86,9 @@ func create() -> void: get_node(menu_path).switch_to_menu(Menu.StartMenuTypes.CHARACTER_SELECT) +func focus() -> void: + name_line_edit.grab_focus() + +func on_visibility_changed(): + if visible: + focus() diff --git a/game/scenes/CharacterSelectorMenu.gd b/game/scenes/CharacterSelectorMenu.gd index bec8c448..d259aacc 100644 --- a/game/scenes/CharacterSelectorMenu.gd +++ b/game/scenes/CharacterSelectorMenu.gd @@ -207,9 +207,20 @@ func load_character() -> void: # else: get_node("/root/Main").load_character(b.file_name) +func focus() -> void: + var loadb : Control = get_node(load_button_path) as Control + + if loadb.visible: + loadb.grab_focus() + else: + get_node(create_button_path).grab_focus() + + + func visibility_changed() -> void: if visible: refresh() + focus() func character_selection_changed() -> void: var b : BaseButton = character_button_group.get_pressed_button() diff --git a/game/scenes/Main.tscn b/game/scenes/Main.tscn index 0f9e9409..b8ab5bdf 100644 --- a/game/scenes/Main.tscn +++ b/game/scenes/Main.tscn @@ -63,7 +63,7 @@ Broken Seals [wave]LOADING[/wave] [/center]" -text = " +text = "[center] @@ -90,7 +90,7 @@ Broken Seals -LOADING -" +[wave]LOADING[/wave] +[/center]" [node name="DebugInfo" parent="." instance=ExtResource( 5 )] diff --git a/game/ui/Window.gd b/game/ui/Window.gd new file mode 100644 index 00000000..8570e7fb --- /dev/null +++ b/game/ui/Window.gd @@ -0,0 +1,75 @@ +extends Control + +# Copyright (c) 2019-2021 Péter Magyar +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +export(NodePath) var focus_button_path : NodePath = "" + +var _previous : Control = null +var _current_focus : Control = null + +var _viewport : Viewport = null + + +func _ready(): + connect("visibility_changed", self, "on_visibility_changed") + +func _enter_tree() -> void: + #find the viewport + var n : Node = self + + while n: + n = n.get_parent() + + if n is Viewport: + _viewport = n as Viewport + _viewport.connect("gui_focus_changed", self, "_on_control_focus_changed") + break + +func _exit_tree(): + if _viewport: + _viewport.disconnect("gui_focus_changed", self, "_on_control_focus_changed") + + _viewport = null + +func _on_control_focus_changed(node : Control) -> void: + _current_focus = node + + +func on_visibility_changed() -> void: + if visible: + focus() + else: + unfocus() + +func focus(): + _previous = _current_focus + + var n : Control = get_node(focus_button_path) + + if n: + n.grab_focus() + +func unfocus(): + if _previous: + _previous.grab_focus() + _previous = null + else: + release_focus() diff --git a/game/ui/about/About.tscn b/game/ui/about/About.tscn index ae870310..aafc82c3 100644 --- a/game/ui/about/About.tscn +++ b/game/ui/about/About.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=8 format=2] +[gd_scene load_steps=9 format=2] [ext_resource path="res://ui/about/GodotAuthors.gd" type="Script" id=1] [ext_resource path="res://ui/about/GodotDonors.gd" type="Script" id=2] @@ -7,15 +7,18 @@ [ext_resource path="res://ui/about/GodotThirdPartyLicenses.gd" type="Script" id=5] [ext_resource path="res://ui/about/Authors.gd" type="Script" id=6] [ext_resource path="res://ui/about/Third-Party Licenses.gd" type="Script" id=7] +[ext_resource path="res://ui/Window.gd" type="Script" id=8] [node name="About" type="Control"] anchor_right = 1.0 anchor_bottom = 1.0 mouse_filter = 2 theme = ExtResource( 3 ) +script = ExtResource( 8 ) __meta__ = { "_edit_use_anchors_": false } +focus_button_path = NodePath("PanelContainer/VBoxContainer/Close") [node name="PanelContainer" type="PanelContainer" parent="."] anchor_left = 0.5 @@ -260,4 +263,5 @@ margin_top = 439.0 margin_right = 728.0 margin_bottom = 465.57 text = "Close" + [connection signal="pressed" from="PanelContainer/VBoxContainer/Close" to="." method="hide"] diff --git a/game/ui/options/Options.tscn b/game/ui/options/Options.tscn index 7e2cc375..8127bef1 100644 --- a/game/ui/options/Options.tscn +++ b/game/ui/options/Options.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=8 format=2] +[gd_scene load_steps=9 format=2] [ext_resource path="res://ui/theme/ui_theme.tres" type="Theme" id=1] [ext_resource path="res://ui/player_ui/RemoveProfile.gd" type="Script" id=2] @@ -6,6 +6,7 @@ [ext_resource path="res://ui/options/OptionCheckboxRow.tscn" type="PackedScene" id=4] [ext_resource path="res://ui/options/OptionCheckBox.gd" type="Script" id=5] [ext_resource path="res://ui/options/OptionEnumRow.tscn" type="PackedScene" id=6] +[ext_resource path="res://ui/Window.gd" type="Script" id=7] [ext_resource path="res://ui/options/OptionsSpinboxRow.tscn" type="PackedScene" id=8] [node name="Options" type="Control"] @@ -13,9 +14,11 @@ anchor_right = 1.0 anchor_bottom = 1.0 mouse_filter = 2 theme = ExtResource( 1 ) +script = ExtResource( 7 ) __meta__ = { "_edit_use_anchors_": false } +focus_button_path = NodePath("PanelContainer/VBoxContainer/Close") [node name="PanelContainer" type="PanelContainer" parent="."] anchor_left = 0.5 diff --git a/game/ui/theme/button_bg_stylebox_focus.tres b/game/ui/theme/button_bg_stylebox_focus.tres index 91ef13d0..f014d34e 100644 --- a/game/ui/theme/button_bg_stylebox_focus.tres +++ b/game/ui/theme/button_bg_stylebox_focus.tres @@ -11,4 +11,4 @@ margin_top = 5.82727 margin_bottom = 5.0562 axis_stretch_horizontal = 2 axis_stretch_vertical = 2 -modulate_color = Color( 0.807843, 0.807843, 0.807843, 1 ) +modulate_color = Color( 0.552941, 0.552941, 0.552941, 1 )