diff --git a/Makefile b/Makefile
index c3fde88e..c0764927 100644
--- a/Makefile
+++ b/Makefile
@@ -186,7 +186,7 @@ $(EXPORT_TEMPLATE):
.PHONY: debug
debug: $(IMPORT_DIR) ## Run the project in debug mode in gamescope
- $(GAMESCOPE) --xwayland-count 2 -- \
+ $(GAMESCOPE) -e --xwayland-count 2 -- \
$(GODOT) --path $(PWD) --remote-debug tcp://127.0.0.1:6007 \
--position 320,140 res://entrypoint.tscn
diff --git a/assets/editor-icons/fluent--brain-circuit-24-filled.svg b/assets/editor-icons/fluent--brain-circuit-24-filled.svg
new file mode 100644
index 00000000..a63f851b
--- /dev/null
+++ b/assets/editor-icons/fluent--brain-circuit-24-filled.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/editor-icons/fluent--brain-circuit-24-filled.svg.import b/assets/editor-icons/fluent--brain-circuit-24-filled.svg.import
new file mode 100644
index 00000000..b69c5471
--- /dev/null
+++ b/assets/editor-icons/fluent--brain-circuit-24-filled.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dumpjvnmyvle0"
+path="res://.godot/imported/fluent--brain-circuit-24-filled.svg-deafc844fbbb2acb1300ed1eab205035.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/editor-icons/fluent--brain-circuit-24-filled.svg"
+dest_files=["res://.godot/imported/fluent--brain-circuit-24-filled.svg-deafc844fbbb2acb1300ed1eab205035.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/assets/editor-icons/fluent--draw-text-24-filled.svg b/assets/editor-icons/fluent--draw-text-24-filled.svg
new file mode 100644
index 00000000..f3346aeb
--- /dev/null
+++ b/assets/editor-icons/fluent--draw-text-24-filled.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/editor-icons/fluent--draw-text-24-filled.svg.import b/assets/editor-icons/fluent--draw-text-24-filled.svg.import
new file mode 100644
index 00000000..cdeda7a3
--- /dev/null
+++ b/assets/editor-icons/fluent--draw-text-24-filled.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b37mlly16qbny"
+path="res://.godot/imported/fluent--draw-text-24-filled.svg-62a66cb94069dc7c979f10a171be9c30.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/editor-icons/fluent--draw-text-24-filled.svg"
+dest_files=["res://.godot/imported/fluent--draw-text-24-filled.svg-62a66cb94069dc7c979f10a171be9c30.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/assets/state/state_machines/global_state_machine.tres b/assets/state/state_machines/global_state_machine.tres
index 39c6c6df..468c96e2 100644
--- a/assets/state/state_machines/global_state_machine.tres
+++ b/assets/state/state_machines/global_state_machine.tres
@@ -1,7 +1,13 @@
-[gd_resource type="Resource" script_class="StateMachine" load_steps=2 format=3 uid="uid://cr544el0cqjlm"]
+[gd_resource type="Resource" script_class="StateMachine" load_steps=6 format=3 uid="uid://cr544el0cqjlm"]
+[ext_resource type="Resource" uid="uid://c640rq4e1xmrn" path="res://assets/state/states/in_game.tres" id="1_dtppc"]
[ext_resource type="Script" path="res://core/systems/state/state_machine.gd" id="1_dw1uo"]
+[ext_resource type="Resource" uid="uid://bmgs1ngma1523" path="res://assets/state/states/in_game_menu.tres" id="2_pg6ge"]
+[ext_resource type="Resource" uid="uid://cv3vduo0ojk1u" path="res://assets/state/states/menu.tres" id="3_4n6bo"]
+[ext_resource type="Resource" uid="uid://dgbe422crufa4" path="res://assets/state/states/popup.tres" id="4_j3a4g"]
[resource]
script = ExtResource("1_dw1uo")
logger_name = "GlobalStateMachine"
+minimum_states = 1
+allowed_states = Array[Resource("res://core/systems/state/state.gd")]([ExtResource("1_dtppc"), ExtResource("2_pg6ge"), ExtResource("3_4n6bo"), ExtResource("4_j3a4g")])
diff --git a/assets/state/state_machines/menu_state_machine.tres b/assets/state/state_machines/menu_state_machine.tres
new file mode 100644
index 00000000..e5e2caf5
--- /dev/null
+++ b/assets/state/state_machines/menu_state_machine.tres
@@ -0,0 +1,8 @@
+[gd_resource type="Resource" script_class="StateMachine" load_steps=2 format=3 uid="uid://bcr6c0281lb5b"]
+
+[ext_resource type="Script" path="res://core/systems/state/state_machine.gd" id="1_18avi"]
+
+[resource]
+script = ExtResource("1_18avi")
+logger_name = "MenuStateMachine"
+minimum_states = 0
diff --git a/assets/state/state_machines/popup_state_machine.tres b/assets/state/state_machines/popup_state_machine.tres
new file mode 100644
index 00000000..77866321
--- /dev/null
+++ b/assets/state/state_machines/popup_state_machine.tres
@@ -0,0 +1,14 @@
+[gd_resource type="Resource" script_class="StateMachine" load_steps=7 format=3 uid="uid://cadriyl38ny5y"]
+
+[ext_resource type="Resource" uid="uid://e7bbebwf7guj" path="res://assets/state/states/main_menu.tres" id="1_7fvmm"]
+[ext_resource type="Resource" uid="uid://bp807nlks8eq1" path="res://assets/state/states/quick_bar_menu.tres" id="2_detpa"]
+[ext_resource type="Resource" uid="uid://bw0mtk7sso8m2" path="res://assets/state/states/power_menu.tres" id="3_0mhn6"]
+[ext_resource type="Resource" uid="uid://dja3m1mevv6xw" path="res://assets/state/states/osk.tres" id="4_qu5fi"]
+[ext_resource type="Resource" uid="uid://db5gbdl3xgwlq" path="res://assets/state/states/help_menu.tres" id="5_5ytlq"]
+[ext_resource type="Script" path="res://core/systems/state/state_machine.gd" id="6_82te1"]
+
+[resource]
+script = ExtResource("6_82te1")
+logger_name = "PopupStateMachine"
+minimum_states = 0
+allowed_states = Array[Resource("res://core/systems/state/state.gd")]([ExtResource("1_7fvmm"), ExtResource("2_detpa"), ExtResource("3_0mhn6"), ExtResource("4_qu5fi"), ExtResource("5_5ytlq")])
diff --git a/assets/state/states/menu.tres b/assets/state/states/menu.tres
new file mode 100644
index 00000000..fb1878ea
--- /dev/null
+++ b/assets/state/states/menu.tres
@@ -0,0 +1,8 @@
+[gd_resource type="Resource" script_class="State" load_steps=2 format=3 uid="uid://cv3vduo0ojk1u"]
+
+[ext_resource type="Script" path="res://core/systems/state/state.gd" id="1_8q8t2"]
+
+[resource]
+script = ExtResource("1_8q8t2")
+name = "menu"
+data = {}
diff --git a/assets/state/states/popup.tres b/assets/state/states/popup.tres
new file mode 100644
index 00000000..32e2341d
--- /dev/null
+++ b/assets/state/states/popup.tres
@@ -0,0 +1,8 @@
+[gd_resource type="Resource" script_class="State" load_steps=2 format=3 uid="uid://dgbe422crufa4"]
+
+[ext_resource type="Script" path="res://core/systems/state/state.gd" id="1_t0m3s"]
+
+[resource]
+script = ExtResource("1_t0m3s")
+name = "popup"
+data = {}
diff --git a/assets/styles/dracula/quick_bar_panel.tres b/assets/styles/dracula/quick_bar_panel.tres
index 3de48999..f1cb30b3 100644
--- a/assets/styles/dracula/quick_bar_panel.tres
+++ b/assets/styles/dracula/quick_bar_panel.tres
@@ -1,7 +1,7 @@
[gd_resource type="StyleBoxFlat" format=3 uid="uid://br2w1fnanqkfa"]
[resource]
-bg_color = Color(0.0823529, 0.0862745, 0.105882, 1)
+bg_color = Color(0.156863, 0.164706, 0.211765, 1)
corner_radius_bottom_left = 10
shadow_size = 5
shadow_offset = Vector2(-4, 4)
diff --git a/assets/themes/card_ui-dracula.tres b/assets/themes/card_ui-dracula.tres
index c3716d32..0c10fd7a 100644
--- a/assets/themes/card_ui-dracula.tres
+++ b/assets/themes/card_ui-dracula.tres
@@ -16,7 +16,7 @@
[ext_resource type="StyleBox" uid="uid://y3qbwgrt2wai" path="res://assets/styles/dracula/settings_menu_panel.tres" id="10_rxp5w"]
[ext_resource type="StyleBox" uid="uid://dj0ysj0o6beba" path="res://assets/styles/dracula/power_menu_panel.tres" id="10_u1qhh"]
[ext_resource type="StyleBox" uid="uid://bifp73vg5vmau" path="res://assets/styles/dracula/plugin_store_card_panel.tres" id="11_83cpl"]
-[ext_resource type="StyleBox" uid="uid://daavpt58e7jlj" path="res://assets/styles/darksoul/quick_bar_panel.tres" id="14_fm0xr"]
+[ext_resource type="StyleBox" uid="uid://br2w1fnanqkfa" path="res://assets/styles/dracula/quick_bar_panel.tres" id="15_354e6"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_s2sy6"]
bg_color = Color(0.133333, 0.137255, 0.180392, 1)
@@ -112,14 +112,14 @@ MainMenu/styles/panel = ExtResource("7_n80rp")
Notification/base_type = &"PanelContainer"
Notification/styles/panel = ExtResource("9_sy73e")
Panel/styles/panel = ExtResource("2_f7nj8")
-PanelContainer/styles/panel = ExtResource("2_f7nj8")
+PanelContainer/styles/panel = ExtResource("4_wqh56")
PluginStoreCard/base_type = &"PanelContainer"
PluginStoreCard/styles/panel = ExtResource("11_83cpl")
PowerMenu/base_type = &"PanelContainer"
PowerMenu/styles/panel = ExtResource("10_u1qhh")
ProgressBar/styles/fill = SubResource("StyleBoxFlat_4nys6")
QuickBar/base_type = &"PanelContainer"
-QuickBar/styles/panel = ExtResource("14_fm0xr")
+QuickBar/styles/panel = ExtResource("15_354e6")
RoundedPanel/base_type = &"PanelContainer"
RoundedPanel/styles/panel = SubResource("StyleBoxFlat_knf7f")
SearchBar/base_type = &"PanelContainer"
diff --git a/core/global/gamescope.gd b/core/global/gamescope.gd
index 8def04e7..d8ae55f7 100644
--- a/core/global/gamescope.gd
+++ b/core/global/gamescope.gd
@@ -248,7 +248,7 @@ func get_window_pid(window_id: int, display: XWAYLAND) -> int:
## window was found.
func get_window_id(pid: int, display: XWAYLAND) -> int:
var display_name := get_display_name(display)
- logger.debug("Getting Window ID for {0} on display {1}".format([pid, display_name]))
+ logger.trace("Getting Window ID for {0} on display {1}".format([pid, display_name]))
var xwayland := get_xwayland(display)
if not xwayland:
return -1
@@ -270,7 +270,7 @@ func get_window_id(pid: int, display: XWAYLAND) -> int:
func get_window_ids(pid: int, display: XWAYLAND) -> PackedInt32Array:
var window_ids := PackedInt32Array()
var display_name := get_display_name(display)
- logger.debug("Getting Window ID for {0} on display {1}".format([pid, display_name]))
+ logger.trace("Getting Window ID for {0} on display {1}".format([pid, display_name]))
var xwayland := get_xwayland(display)
if not xwayland:
return window_ids
diff --git a/core/global/launch_manager.gd b/core/global/launch_manager.gd
index d2cb28b0..7d65adff 100644
--- a/core/global/launch_manager.gd
+++ b/core/global/launch_manager.gd
@@ -55,8 +55,10 @@ var _apps_by_name: Dictionary = {}
var _data_dir: String = ProjectSettings.get_setting("OpenGamepadUI/data/directory")
var _persist_path: String = "/".join([_data_dir, "launcher.json"])
var _persist_data: Dictionary = {"version": 1}
-var logger := Log.get_logger("LaunchManager", Log.LEVEL.INFO)
+var _ogui_window_id := gamescope.get_window_id(PID, gamescope.XWAYLAND.OGUI)
var should_manage_overlay := true
+var logger := Log.get_logger("LaunchManager", Log.LEVEL.INFO)
+
# Connect to Gamescope signals
func _init() -> void:
@@ -64,19 +66,18 @@ func _init() -> void:
var on_focus_changed := func(from: int, to: int):
logger.info("Window focus changed from " + str(from) + " to: " + str(to))
var last_app := _current_app
- _current_app = get_running_from_window_id(to)
- # If there is no _current_app then another process opened this window. Find it.
- if _current_app == null:
- _current_app = _detect_running_app(to)
+ _current_app = _detect_running_app(to)
logger.debug("Last app: " + str(last_app) + " current_app: " + str(_current_app))
app_switched.emit(last_app, _current_app)
+
# If the app has a gamepad profile, set it
- set_app_gamepad_profile(_current_app)
+ if to != _ogui_window_id and _current_app:
+ set_app_gamepad_profile(_current_app)
- # If we don't want LaunchManager to manage overlay (I.E. overlay mode), return false always.
- if not should_manage_overlay:
- return
+ # If we don't want LaunchManager to manage overlay (I.E. overlay mode), return false always.
+ if not should_manage_overlay:
+ return
gamescope.focused_window_updated.connect(on_focus_changed)
@@ -94,21 +95,19 @@ func _init() -> void:
if not should_manage_overlay:
return
- logger.debug("Enabling STEAM_OVERLAY.")
- var ogui_window_id = gamescope.get_window_id(PID, gamescope.XWAYLAND.OGUI)
- gamescope.set_overlay(ogui_window_id, 0)
-
+ logger.debug("Enabling STEAM_OVERLAY atom")
+ gamescope.set_overlay(_ogui_window_id, 0)
+
var on_game_state_exited := func(_to: State):
# Set the gamepad profile to the global profile
set_gamepad_profile("")
-
+
# If we don't want LaunchManager to manage overlay (I.E. overlay mode), return false always.
if not should_manage_overlay:
return
- logger.debug("Removing STEAM_OVERLAY.")
- var ogui_window_id = gamescope.get_window_id(PID, gamescope.XWAYLAND.OGUI)
- gamescope.set_overlay(ogui_window_id, 1)
+ logger.debug("Disabling STEAM_OVERLAY atom")
+ gamescope.set_overlay(_ogui_window_id, 1)
in_game_state.state_entered.connect(on_game_state_entered)
in_game_state.state_exited.connect(on_game_state_exited)
@@ -118,12 +117,12 @@ func _init() -> void:
func _load_persist_data():
# Create the data directory if it doesn't exist
DirAccess.make_dir_absolute(_data_dir)
-
+
# Create our data file if it doesn't exist
if not FileAccess.file_exists(_persist_path):
logger.debug("LaunchManager: Launcher data does not exist. Creating it.")
_save_persist_data()
-
+
# Read our persistent data and parse it
var file: FileAccess = FileAccess.open(_persist_path, FileAccess.READ)
var data: String = file.get_as_text()
@@ -168,12 +167,12 @@ func launch(app: LibraryLaunchItem) -> RunningApp:
env = user_env
var sandboxing_key := ".".join(["use_sandboxing", app._provider_id])
var use_sandboxing := settings_manager.get_value(section, sandboxing_key, true) as bool
-
+
# Set the display environment if one was not set.
if not "DISPLAY" in env:
env["DISPLAY"] = gamescope.get_display_name(Gamescope.XWAYLAND.GAME)
var display := env["DISPLAY"] as String
-
+
# Set the OGUI ID environment variable
env["OGUI_ID"] = app.name
@@ -309,7 +308,7 @@ func set_gamepad_profile(path: String, target_gamepad: String = "") -> void:
var profile_path := settings_manager.get_value("input", "gamepad_profile", InputPlumber.DEFAULT_GLOBAL_PROFILE) as String
if not profile_path.ends_with(".json") or not FileAccess.file_exists(profile_path):
profile_path = InputPlumber.DEFAULT_GLOBAL_PROFILE
- logger.debug("Loading global gamepad profile: " + profile_path)
+ logger.info("Loading global gamepad profile: " + profile_path)
for gamepad in input_plumber.composite_devices:
gamepad.target_modify_profile(profile_path, profile_modifier)
@@ -322,12 +321,12 @@ func set_gamepad_profile(path: String, target_gamepad: String = "") -> void:
target_devices.append("touchpad")
_:
logger.debug(target_gamepad, "needs no additional target devices.")
- logger.debug("Setting target devices to: ", target_devices)
+ logger.info("Setting target devices to: ", target_devices)
gamepad.set_target_devices(target_devices)
return
- logger.debug("Loading gamepad profile: " + path)
+ logger.info("Loading gamepad profile: " + path)
if not FileAccess.file_exists(path):
logger.warn("Gamepad profile not found: " + path)
return
@@ -350,7 +349,7 @@ func set_gamepad_profile(path: String, target_gamepad: String = "") -> void:
target_devices.append("touchpad")
_:
logger.debug(target_gamepad, "needs no additional target devices.")
- logger.debug("Setting target devices to: ", target_devices)
+ logger.info("Setting target devices to: ", target_devices)
gamepad.set_target_devices(target_devices)
var notify := Notification.new("Using gamepad profile: " + profile.name)
@@ -409,7 +408,7 @@ func _remove_running(app: RunningApp):
# Checks for running apps and updates our state accordingly
-func _check_running() -> void:
+func check_running() -> void:
# Find the root window
var root_id := gamescope.get_root_window_id(Gamescope.XWAYLAND.GAME)
if root_id < 0:
diff --git a/core/systems/input/back_input_handler.gd b/core/systems/input/back_input_handler.gd
index 0cadd6ca..a2770823 100644
--- a/core/systems/input/back_input_handler.gd
+++ b/core/systems/input/back_input_handler.gd
@@ -2,6 +2,8 @@
extends Node
class_name BackInputHandler
+## DEPRECATED: Use [InputWatcher] with [StateUpdater] instead
+
## The state machine to use to update when back input is pressed
@export var state_machine: StateMachine = preload(
"res://assets/state/state_machines/global_state_machine.tres"
diff --git a/core/systems/input/focus_group.gd b/core/systems/input/focus_group.gd
index 9814a0b6..a477dc7a 100644
--- a/core/systems/input/focus_group.gd
+++ b/core/systems/input/focus_group.gd
@@ -11,10 +11,13 @@ class_name FocusGroup
@export_category("Focus Control")
## The current focus of the focus group
@export var current_focus: Control
+## DEPRECATED: Use [InputWatcher] nodes with [FocusSetter] to handle back input.
## Menus with multiple levels of focus groups can be part of a chain of focus
@export var focus_stack: FocusStack
## The InputEvent that will trigger focusing a parent focus group
@export var back_action := "ogui_east"
+## Whether or not to wrap around focus chains
+@export var wrap_focus: bool = true
@export_category("Focus Group Neighbors")
@export var focus_neighbor_bottom: FocusGroup
@@ -144,7 +147,7 @@ func _input(event: InputEvent) -> void:
return
# Only handle back button pressed and when the guide button is not held
- if not event.is_action_pressed(back_action) or Input.is_action_pressed("ogui_guide"):
+ if not event.is_action_released(back_action) or Input.is_action_pressed("ogui_guide"):
return
# Only handle input if a focus stack is defined
@@ -421,12 +424,21 @@ func _vbox_set_focus_tree(control_children: Array[Control]) -> void:
child.focus_next = control_children[i + 1].get_path()
child.focus_neighbor_bottom = control_children[i + 1].get_path()
else:
- child.focus_next = control_children[0].get_path()
- child.focus_neighbor_bottom = control_children[0].get_path()
+ # If wrap focus is enabled, then link focus of the last node to the first node.
+ if wrap_focus:
+ child.focus_next = control_children[0].get_path()
+ child.focus_neighbor_bottom = control_children[0].get_path()
+ else:
+ child.focus_next = control_children[i].get_path()
+ child.focus_neighbor_bottom = control_children[i].get_path()
# Index -1
- child.focus_previous = control_children[i - 1].get_path()
- child.focus_neighbor_top = control_children[i - 1].get_path()
+ if i == 0 and not wrap_focus:
+ child.focus_previous = control_children[i].get_path()
+ child.focus_neighbor_top = control_children[i].get_path()
+ else:
+ child.focus_previous = control_children[i - 1].get_path()
+ child.focus_neighbor_top = control_children[i - 1].get_path()
# Block leaving the UI element unless B button is pressed.
child.focus_neighbor_left = control_children[i].get_path()
diff --git a/core/systems/input/input_manager.gd b/core/systems/input/input_manager.gd
index 82a0d88a..7925a935 100644
--- a/core/systems/input/input_manager.gd
+++ b/core/systems/input/input_manager.gd
@@ -13,21 +13,25 @@ class_name InputManager
## Reference to the on-screen keyboard instance to open when the OSK action is
## pressed.
-const osk := preload("res://core/global/keyboard_instance.tres")
+var osk := load("res://core/global/keyboard_instance.tres") as KeyboardInstance
## The audio manager to use to adjust the audio when audio input events happen.
var audio_manager := load("res://core/global/audio_manager.tres") as AudioManager
## InputPlumber receives and sends DBus input events.
var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumber
-
+## LaunchManager provides context on the currently running app so we can switch profiles
+var launch_manager := load("res://core/global/launch_manager.tres") as LaunchManager
+## The Global State Machine
+var state_machine := load("res://assets/state/state_machines/global_state_machine.tres") as StateMachine
## State machine to use to switch menu states in response to input events.
-var state_machine := (
- preload("res://assets/state/state_machines/global_state_machine.tres") as StateMachine
+var popup_state_machine := (
+ preload("res://assets/state/state_machines/popup_state_machine.tres") as StateMachine
)
var in_game_menu_state := preload("res://assets/state/states/in_game_menu.tres") as State
var main_menu_state := preload("res://assets/state/states/main_menu.tres") as State
var quick_bar_state := preload("res://assets/state/states/quick_bar_menu.tres") as State
var osk_state := preload("res://assets/state/states/osk.tres") as State
+var popup_state := preload("res://assets/state/states/popup.tres") as State
## Map of pressed actions to prevent double inputs
var actions_pressed := {}
@@ -135,6 +139,8 @@ func _input(event: InputEvent) -> void:
# Handle inputs when the guide button is being held
if Input.is_action_pressed("ogui_guide"):
+ # Prevent ALL input from propagating if guide is held!
+ get_viewport().set_input_as_handled()
logger.debug("Additional action while guide wad pressed.")
# OSK
if event.is_action_pressed("ogui_north"):
@@ -146,9 +152,6 @@ func _input(event: InputEvent) -> void:
logger.debug("Trigger QB")
action_press(dbus_path, "ogui_qb")
action_press(dbus_path, "ogui_guide_action")
-
- # Prevent ALL input from propagating if guide is held!
- get_viewport().set_input_as_handled()
return
if event.is_action_pressed("ogui_south"):
@@ -158,6 +161,13 @@ func _input(event: InputEvent) -> void:
logger.debug("South released on its own.")
action_release(dbus_path, "ui_accept")
+ if event.is_action_pressed("ogui_north"):
+ logger.debug("North pressed on its own.")
+ action_press(dbus_path, "ogui_search")
+ elif event.is_action_released("ogui_north"):
+ logger.debug("North released on its own.")
+ action_release(dbus_path, "ogui_search")
+
## Handle guide button events and determine whether this is a guide action
## (e.g. guide + A to open the Quick Bar), or if it's just a normal guide button press.
@@ -165,7 +175,11 @@ func _guide_input(event: InputEvent) -> void:
var dbus_path := event.get_meta("dbus_path", "") as String
# Only act on release events
if event.is_pressed():
- logger.debug("Guide pressed. Waiting for additional events.")
+ logger.warn("Guide pressed. Waiting for additional events.")
+ # Set the gamepad profile to the global default so we can capture button events.
+ # This ensures that we use the global profile and not the game's input profile for
+ # processing guide button combos and navigating the menu.
+ launch_manager.set_gamepad_profile("")
return
# If a guide action combo was pressed and we released the guide button,
@@ -188,15 +202,14 @@ func _main_menu_input(event: InputEvent) -> void:
return
# Open the main menu
- var state := state_machine.current_state()
- var menu_state := main_menu_state
+ var state := popup_state_machine.current_state()
- if state == menu_state:
- state_machine.pop_state()
+ if state == main_menu_state:
+ popup_state_machine.pop_state()
elif state in [quick_bar_state, osk_state]:
- state_machine.replace_state(menu_state)
+ popup_state_machine.replace_state(main_menu_state)
else:
- state_machine.push_state(menu_state)
+ popup_state_machine.push_state(main_menu_state)
## Handle quick bar menu events to open the quick bar menu
@@ -205,13 +218,13 @@ func _on_quick_bar_open(event: InputEvent) -> void:
if not event.is_pressed():
return
- var state := state_machine.current_state()
+ var state := popup_state_machine.current_state()
if state == quick_bar_state:
- state_machine.pop_state()
+ popup_state_machine.pop_state()
elif state in [main_menu_state, in_game_menu_state, osk_state]:
- state_machine.replace_state(quick_bar_state)
+ popup_state_machine.replace_state(quick_bar_state)
else:
- state_machine.push_state(quick_bar_state)
+ popup_state_machine.push_state(quick_bar_state)
## Handle OSK events for bringing up the on-screen keyboard
@@ -220,18 +233,22 @@ func _osk_input(event: InputEvent) -> void:
if not event.is_pressed():
return
+ var state := popup_state_machine.current_state()
+ if state == osk_state:
+ osk.close()
+ popup_state_machine.pop_state()
+ return
+
+ if state in [main_menu_state, in_game_menu_state, quick_bar_state]:
+ popup_state_machine.replace_state(osk_state)
+ else:
+ popup_state_machine.push_state(osk_state)
+ state_machine.push_state(popup_state)
+
var context := KeyboardContext.new()
context.type = KeyboardContext.TYPE.X11
osk.open(context)
- var state := state_machine.current_state()
- if state == osk_state:
- state_machine.pop_state()
- elif state in [main_menu_state, in_game_menu_state, quick_bar_state]:
- state_machine.replace_state(osk_state)
- else:
- state_machine.push_state(osk_state)
-
## Handle audio input events such as mute, volume up, and volume down
func _audio_input(event: InputEvent) -> void:
@@ -285,8 +302,12 @@ func _on_dbus_input_event(event: String, value: float, dbus_path: String) -> voi
action = "ogui_osk"
"ui_r1":
action = "ogui_tab_right"
+ "ui_r2":
+ action = "ogui_right_trigger"
"ui_l1":
action = "ogui_tab_left"
+ "ui_l2":
+ action = "ogui_left_trigger"
if pressed:
action_press(dbus_path, action)
diff --git a/core/systems/input/input_watcher.gd b/core/systems/input/input_watcher.gd
index 765dc9e2..f1e8543e 100644
--- a/core/systems/input/input_watcher.gd
+++ b/core/systems/input/input_watcher.gd
@@ -16,20 +16,43 @@ signal input_released
## If true, consumes the event, marking it as handled so no other nodes
## try to handle this input event.
@export var stop_propagation: bool
+## Always process inputs or only when parent node is visible
+@export_flags("When visible", "When child focused") var process_input_mode: int = 3
## Name of the input action in the InputMap to watch for
var action: String
+@onready var logger := Log.get_logger("InputWatcher", Log.LEVEL.INFO)
+
func _ready() -> void:
if action.is_empty():
set_process_input(false)
+ if process_input_mode == 0:
+ return
+
+ # Only process input if the parent node is visible
+ var parent := get_parent()
+ if parent is Control:
+ var control := parent as Control
+ var on_visibility := func():
+ set_process_input(control.is_visible_in_tree())
+ control.visibility_changed.connect(on_visibility)
+ set_process_input(control.is_visible_in_tree())
+
func _input(event: InputEvent) -> void:
if not event.is_action(action):
return
+ # Only process input if a child node has focus
+ if (process_input_mode & 2):
+ var focus_owner := get_viewport().gui_get_focus_owner()
+ var parent := get_parent()
+ if not parent.is_ancestor_of(focus_owner):
+ return
+
if event.is_pressed():
input_pressed.emit()
elif event.is_released():
@@ -37,6 +60,8 @@ func _input(event: InputEvent) -> void:
# Stop the event from propagating
if stop_propagation:
+ var parent := get_parent()
+ logger.debug("Consuming input event '{action}' for node {n}".format({"action": action, "n": str(parent)}))
get_viewport().set_input_as_handled()
diff --git a/core/systems/input/overlay_mode_input_manager.gd b/core/systems/input/overlay_mode_input_manager.gd
index cf5a84a9..f060c8fe 100644
--- a/core/systems/input/overlay_mode_input_manager.gd
+++ b/core/systems/input/overlay_mode_input_manager.gd
@@ -15,11 +15,15 @@ class_name OverlayInputManager
var audio_manager := load("res://core/global/audio_manager.tres") as AudioManager
## InputPlumber receives and sends DBus input events.
var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumber
-
+## LaunchManager provides context on the currently running app so we can switch profiles
+var launch_manager := load("res://core/global/launch_manager.tres") as LaunchManager
+## The Global State Machine
+var state_machine := load("res://assets/state/state_machines/global_state_machine.tres") as StateMachine
## State machine to use to switch menu states in response to input events.
-var state_machine := (
- preload("res://assets/state/state_machines/global_state_machine.tres") as StateMachine
+var popup_state_machine := (
+ preload("res://assets/state/state_machines/popup_state_machine.tres") as StateMachine
)
+var menu_state_machine := preload("res://assets/state/state_machines/menu_state_machine.tres") as StateMachine
var in_game_menu_state := preload("res://assets/state/states/in_game_menu.tres") as State
var main_menu_state := preload("res://assets/state/states/main_menu.tres") as State
var quick_bar_state := preload("res://assets/state/states/quick_bar_menu.tres") as State
@@ -67,7 +71,6 @@ func _send_input(dbus_path: String, action: String, pressed: bool, strength: flo
## https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html#how-does-it-work
func _input(event: InputEvent) -> void:
logger.debug("Got input event to handle: " + str(event))
- _trace_input_status("START")
var dbus_path := event.get_meta("dbus_path", "") as String
# Consume double inputs for controllers with DPads that have TRIGGER_HAPPY events
@@ -88,26 +91,22 @@ func _input(event: InputEvent) -> void:
if event.is_action("ogui_guide_ov"):
_guide_input(event)
get_viewport().set_input_as_handled()
- _trace_input_status("END")
return
# Steam chord events
if _send_steam_chord(event):
get_viewport().set_input_as_handled()
- _trace_input_status("END")
return
# Quick Bar open events
if event.is_action("ogui_qb_ov"):
_quick_bar_input(event)
get_viewport().set_input_as_handled()
- _trace_input_status("END")
return
# Handle audio events
if event.is_action("ogui_volume_down") or event.is_action("ogui_volume_up") or event.is_action("ogui_volume_mute"):
_audio_input(event)
- _trace_input_status("END")
return
# Handle guide action release events
@@ -117,39 +116,37 @@ func _input(event: InputEvent) -> void:
if event.is_action_released("ogui_north_ov"):
action_release(dbus_path, "ogui_osk_ov")
get_viewport().set_input_as_handled()
- _trace_input_status("END")
return
# Steam QAM
if event.is_action_released("ogui_south_ov"):
action_release(dbus_path, "ogui_qam_ov")
get_viewport().set_input_as_handled()
- _trace_input_status("END")
return
# Steam Video Capture
if event.is_action_released("ogui_west_ov"):
action_release(dbus_path, "ogui_vc_ov")
get_viewport().set_input_as_handled()
- _trace_input_status("END")
return
# Steam Screenshot
if event.is_action_released("ogui_rb_ov"):
action_release(dbus_path, "ogui_sc_ov")
get_viewport().set_input_as_handled()
- _trace_input_status("END")
return
# Quick Bar
if event.is_action_released("ogui_east_ov"):
action_release(dbus_path, "ogui_qb_ov")
get_viewport().set_input_as_handled()
- _trace_input_status("END")
return
# Handle inputs when the guide button is being held
if Input.is_action_pressed("ogui_guide_ov"):
+ # Prevent ALL input from propagating if guide is held!
+ get_viewport().set_input_as_handled()
+
if event.is_pressed():
logger.debug("Additional action while guide wad pressed.")
# Steam OSK
@@ -177,7 +174,6 @@ func _input(event: InputEvent) -> void:
action_press(dbus_path, "ogui_guide_action")
action_press(dbus_path, "ogui_qb_ov")
-
elif event.is_released():
# Steam OSK
if event.is_action_released("ogui_north_ov"):
@@ -199,9 +195,6 @@ func _input(event: InputEvent) -> void:
if event.is_action_released("ogui_east_ov"):
action_release(dbus_path, "ogui_qb_ov")
- # Prevent ALL input from propagating if guide is held!
- get_viewport().set_input_as_handled()
- _trace_input_status("END")
return
# Handle events in the UI while it is open.
@@ -245,8 +238,6 @@ func _input(event: InputEvent) -> void:
action_release(dbus_path, "ogui_tab_left")
get_viewport().set_input_as_handled()
- _trace_input_status("END")
-
## Handle guide button events and determine whether this is a guide action
## (e.g. guide + A to open the Quick Bar), or if it's just a normal guide button press.
@@ -255,6 +246,10 @@ func _guide_input(event: InputEvent) -> void:
# Only act on release events
if event.is_pressed():
logger.debug("Guide pressed. Waiting for additional events.")
+ # Set the gamepad profile to the global default so we can capture button events.
+ # This ensures that we use the global profile and not the game's input profile for
+ # processing guide button combos and navigating the menu.
+ launch_manager.set_gamepad_profile("")
return
# If a guide action combo was pressed and we released the guide button,
@@ -272,22 +267,17 @@ func _guide_input(event: InputEvent) -> void:
## Handle quick bar menu events to open the quick bar menu
func _quick_bar_input(event: InputEvent) -> void:
- logger.debug("Trigger Quick Bar")
-
# Only act on press events
if not event.is_pressed():
- logger.debug("Event is up event, ignore.")
return
- var state := state_machine.current_state()
- logger.debug("Current State: " +str(state))
- if state != base_state:
- logger.debug("Close Quick Bar")
- _close_focused_window()
- return
-
- logger.debug("Push Quick Bar")
- state_machine.push_state(quick_bar_state)
+ var state := popup_state_machine.current_state()
+ if state == quick_bar_state:
+ popup_state_machine.pop_state()
+ elif state in [main_menu_state, in_game_menu_state]:
+ popup_state_machine.replace_state(quick_bar_state)
+ else:
+ popup_state_machine.push_state(quick_bar_state)
## Handle Steam chord events. Returns true if this was a Steam chord.
@@ -399,16 +389,5 @@ func _on_dbus_input_event(event: String, value: float, dbus_path: String) -> voi
# Closes all windows until we return to the base_state
func _close_focused_window() -> void:
- while state_machine.stack_length() > state_machine.minimum_states:
- state_machine.pop_state()
-
-
-# Does a trace print of the status of all events.
-func _trace_input_status(point: String) -> void:
- logger.trace(point +": Guide Pressed: " + str(Input.is_action_pressed("ogui_guide_ov")))
- logger.trace(point +": Guide Used: " + str(Input.is_action_pressed("ogui_guide_action")))
- logger.trace(point +": QAM Pressed: " + str(Input.is_action_pressed("ogui_qam_ov")))
- logger.trace(point +": OSK Pressed: " + str(Input.is_action_pressed("ogui_osk_ov")))
- logger.trace(point +": SC Pressed: " + str(Input.is_action_pressed("ogui_sc_ov")))
- logger.trace(point +": VC Pressed: " + str(Input.is_action_pressed("ogui_vc_ov")))
- logger.trace(point +": QB Pressed: " + str(Input.is_action_pressed("ogui_qb_ov")))
+ popup_state_machine.clear_states()
+ menu_state_machine.clear_states()
diff --git a/core/systems/launcher/launch_manager.gd b/core/systems/launcher/launch_manager.gd
index c9accfa2..b7e36fa9 100644
--- a/core/systems/launcher/launch_manager.gd
+++ b/core/systems/launcher/launch_manager.gd
@@ -10,10 +10,6 @@ func _init() -> void:
launch_manager._load_persist_data()
-func _ready() -> void:
- # Set a timer that will update our state based on if anything is running.
- var running_timer = Timer.new()
- running_timer.timeout.connect(launch_manager._check_running)
- running_timer.wait_time = 1
- add_child(running_timer)
- running_timer.start()
+# TODO: Replace this with dbus signaling. This is super shitty.
+func _process(delta) -> void:
+ launch_manager.check_running()
diff --git a/core/systems/launcher/running_app.gd b/core/systems/launcher/running_app.gd
index 8b20cb65..145fefff 100644
--- a/core/systems/launcher/running_app.gd
+++ b/core/systems/launcher/running_app.gd
@@ -122,10 +122,10 @@ func update() -> void:
# Ensure that the running app has a corresponding window ID
var has_valid_window := false
if needs_window_id():
- logger.debug("App needs a valid window id")
+ logger.trace("App needs a valid window id")
var id := _discover_window_id()
if id > 0 and window_id != id:
- logger.debug("Setting window ID " + str(id) + " for " + launch_item.name)
+ logger.trace("Setting window ID " + str(id) + " for " + launch_item.name)
window_id = id
else:
has_valid_window = true
@@ -155,13 +155,13 @@ func update() -> void:
STATE.STOPPING: "stopping",
STATE.STOPPED: "stopped"
}
- logger.debug(launch_item.name + " current state: " + state_str[state])
+ logger.trace(launch_item.name + " current state: " + state_str[state])
# TODO: Check all windows for STEAM_GAME prop
# If this was launched by Steam, try and detect if the game closed
# so we can kill Steam gracefully
if is_steam_app() and state == STATE.MISSING_WINDOW and is_ogui_managed:
- logger.debug(launch_item.name + " is a Steam game and has no valid window ID. It may have closed.")
+ logger.trace(launch_item.name + " is a Steam game and has no valid window ID. It may have closed.")
# Don't try closing Steam immediately. Wait a few more ticks before attempting
# to close Steam.
if steam_close_tries < 4:
@@ -204,7 +204,7 @@ func get_all_window_ids() -> PackedInt32Array:
var window_ids := PackedInt32Array()
var pids := get_child_pids()
pids.append(pid)
- logger.debug(app_name + " found related PIDs: " + str(pids))
+ logger.trace(app_name + " found related PIDs: " + str(pids))
for process_id in pids:
var windows := Gamescope.get_window_ids(process_id, display_type)
@@ -214,7 +214,7 @@ func get_all_window_ids() -> PackedInt32Array:
if window in window_ids:
continue
window_ids.append(window)
- logger.debug(app_name + " found related window IDs: " + str(window_ids))
+ logger.trace(app_name + " found related window IDs: " + str(window_ids))
return window_ids
@@ -227,19 +227,19 @@ func is_running() -> bool:
return true
# If that failed, check if reaper can get the status.
- logger.debug("Reaper pid State: " + Reaper.get_pid_state(pid))
+ logger.trace("Reaper pid State: " + Reaper.get_pid_state(pid))
if Reaper.get_pid_state(pid) in ["R (running)", "S (sleeping)"]:
return true
- logger.debug("Original process not running. Checking child PID's...")
+ logger.trace("Original process not running. Checking child PID's...")
# If it's not running, let's check to make sure it's REALLY not running
# and hasn't re-parented itself
var children := get_child_pids()
if children.size() > 0:
var pids := Array(children)
- logger.debug("{0} is not running, but lives on in {1}".format([pid, ",".join(pids)]))
+ logger.trace("{0} is not running, but lives on in {1}".format([pid, ",".join(pids)]))
return true
- logger.debug("Process " + str(pid) + " has died and no child PID's could be found.")
+ logger.trace("Process " + str(pid) + " has died and no child PID's could be found.")
return false
@@ -348,18 +348,18 @@ func _ensure_app_id() -> void:
## Returns whether or not the window id of the running app needs to be discovered
func needs_window_id() -> bool:
if window_id <= 0:
- logger.debug(launch_item.name + " has a bad window ID: " + str(window_id))
+ logger.trace(launch_item.name + " has a bad window ID: " + str(window_id))
return true
var focusable_windows := Gamescope.get_focusable_windows()
if not window_id in focusable_windows:
- logger.debug(str(window_id) + " is not in the list of focusable windows")
+ logger.trace(str(window_id) + " is not in the list of focusable windows")
return true
# Check if the current window ID exists in the list of open windows
var root_window := Gamescope.get_root_window_id(Gamescope.XWAYLAND.GAME)
var all_windows := Gamescope.get_all_windows(root_window, Gamescope.XWAYLAND.GAME)
if not window_id in all_windows:
- logger.debug(str(window_id) + " is not in the list of all windows")
+ logger.trace(str(window_id) + " is not in the list of all windows")
return true
# If this is a Steam app, the only acceptable window will have its STEAM_GAME
@@ -368,10 +368,10 @@ func needs_window_id() -> bool:
var display_type := Gamescope.get_display_type(display)
var steam_app_id := get_meta("steam_app_id") as int
if not Gamescope.has_app_id(window_id, display_type):
- logger.debug(str(window_id) + " does not have an app ID already set by Steam")
+ logger.trace(str(window_id) + " does not have an app ID already set by Steam")
return true
if Gamescope.get_app_id(window_id) != steam_app_id:
- logger.debug(str(window_id) + " has an app ID but it does not match " + str(steam_app_id))
+ logger.trace(str(window_id) + " has an app ID but it does not match " + str(steam_app_id))
return true
# Track that a window has been successfully detected at least once.
@@ -387,7 +387,7 @@ func _discover_window_id() -> int:
# If there's a window directly associated with the PID, return that
var win_id := get_window_id_from_pid()
if win_id > 0:
- logger.debug("Found window ID for {0} from PID: {1}".format([launch_item.name, window_id]))
+ logger.trace("Found window ID for {0} from PID: {1}".format([launch_item.name, window_id]))
return win_id
# Get all windows associated with the running app
@@ -427,7 +427,7 @@ func find_steam() -> int:
continue
var process_name := pid_info["Name"] as String
if process_name == "steam":
- logger.debug("Found steam PID: " + str(child_pid))
+ logger.trace("Found steam PID: " + str(child_pid))
return child_pid
return -1
diff --git a/core/systems/state/state.gd b/core/systems/state/state.gd
index 54a93e51..e76304cc 100644
--- a/core/systems/state/state.gd
+++ b/core/systems/state/state.gd
@@ -2,9 +2,38 @@
extends Resource
class_name State
+## Object for tracking the current state of a [StateMachine]
+##
+## A [State] represents some state of the application, such as the currently
+## focused menu. Together with a [StateMachine], a [State] can be used to listen
+## for signals whenever the state of the application changes.
+##
+## A [State] takes advantage of the fact that Godot resources are globally
+## unique. This allows you to load a [State] resource from anywhere in the project
+## to subscribe to state changes.
+
+## Emitted whenever a [StateMachine] has set this [State] as the current state.
+## The "from" [State] will be populated with the last [State] the [StateMachine]
+## was in.
signal state_entered(from: State)
+## Emitted whenever a [StateMachine] has left this [State]. The "to" [State] will
+## be populated with the new current [State] of the [StateMachine].
signal state_exited(to: State)
+## Emitted whenever a [StateMachine] has added this [State] to its state stack.
+signal state_added
+## Emitted whenever a [StateMachine] has removed this [State] from its state stack.
signal state_removed
+## Emitted whenever a [StateMachine] calls "refresh" on the current [State]
+signal refreshed
+## Optional human-readable name for the state
@export var name: String
+## DEPRECATED: Use 'set_meta()' or 'get_meta()' instead
@export var data: Dictionary
+
+
+func _to_string() -> String:
+ if not name.is_empty():
+ return "".format({"name": name})
+ return "".format({"rid": get_rid()})
+
diff --git a/core/systems/state/state_machine.gd b/core/systems/state/state_machine.gd
index a78424ef..49689563 100644
--- a/core/systems/state/state_machine.gd
+++ b/core/systems/state/state_machine.gd
@@ -1,15 +1,38 @@
-# https://www.reddit.com/r/godot/comments/vodp2a/comment/iegv4fs/?utm_source=reddit&utm_medium=web2x&context=3
-# The state machine takes advantage of the fact that resources are globally unique.
-# This allows you to load a state machine resource from anywhere to subscribe to
-# state changes.
@icon("res://assets/editor-icons/state-machine.svg")
extends Resource
class_name StateMachine
+## Manages the current [State] for some part of the application.
+##
+## A [StateMachine] is responsible for managing an arbitrary number of [State]
+## objects. The [StateMachine] keeps a "stack" of states that can be set, pushed,
+## popped, removed, or cleared, and will fire signals for each kind of change.
+## This can allow the application to update and respond to different states of
+## the [StateMachine].
+##
+## Only one [State] is considered the "current" state in a [StateMachine]: the
+## last state in the stack. A [State] will fire the "entered" signal whenever it
+## becomes the "current" state, and fires the "exited" signal whenever it leaves
+## the "current" state.
+##
+## The [StateMachine] takes advantage of the fact that Godot resources are globally
+## unique. This allows you to load a [StateMachine] resource from anywhere in the
+## project to subscribe to state changes.
+
+## Emitted whenever this [StateMachine] has changed states
signal state_changed(from: State, to: State)
+## Emitted whenever all states have been removed from the state machine
+signal emptied
+## Name of the state machine to use for logging purposes
@export var logger_name := "StateMachine"
+## The minimum number of states that this [StateMachine] must have. This parameter
+## can be used to ensure that states cannot be popped below this number of states.
@export var minimum_states: int = 1
+## If set, only the given [State] objects will be allowed to be added to the [StateMachine].
+## Will panic if an invalid state is added to the stack. If empty, all [State]
+## objects will be allowed.
+@export var allowed_states: Array[State] = []
var _state_stack: Array[State] = []
var logger := Log.get_logger(logger_name)
@@ -25,16 +48,16 @@ func _on_state_changed(from: State, to: State) -> void:
var to_str = ""
if from != null:
from_str = from.name
- from.state_exited.emit(to)
if to != null:
to_str = to.name
- to.state_entered.emit(from)
if logger.get_name() != logger_name:
logger = Log.get_logger(logger_name)
logger.info("Switched from state {0} to {1}".format([from_str, to_str]))
var state_names := PackedStringArray()
for state in _state_stack:
state_names.append(state.name)
+ if not to:
+ emptied.emit()
logger.info("Stack: " + "-> ".join(state_names))
@@ -46,20 +69,42 @@ func current_state() -> State:
return _state_stack[length-1]
+## Emits the 'refreshed' signal on the current [State]. This can be used to
+## trigger hand-offs between multiple state machines.
+func refresh() -> void:
+ var current := current_state()
+ if not current:
+ return
+ current.refreshed.emit()
+
+
## Set state will set the entire state stack to the given array of states
-func set_state(stack: Array[State]) -> void:
- if null in stack:
+func set_state(new_stack: Array[State]) -> void:
+ if null in new_stack:
logger.warn("Invalid NULL state pushed.")
return
- var cur := current_state()
- var old_stack := _state_stack
- _state_stack = stack
- for s in old_stack:
- var state := s as State
- if has_state(state):
- continue
+ var states_added: Array[State] = []
+ var states_removed: Array[State] = []
+ for state: State in new_stack:
+ _ensure_state_allowed(state)
+ if not has_state(state):
+ states_added.push_back(state)
+ for state: State in _state_stack:
+ if not state in new_stack:
+ states_removed.push_back(state)
+ var last_current := current_state()
+ _state_stack = new_stack
+ var new_current := current_state()
+ if last_current != new_current:
+ if last_current:
+ last_current.state_exited.emit(new_current)
+ if new_current:
+ new_current.state_entered.emit(last_current)
+ state_changed.emit(last_current, new_current)
+ for state: State in states_removed:
state.state_removed.emit()
- state_changed.emit(cur, stack[-1])
+ for state: State in states_added:
+ state.state_added.emit()
## Push state will push the given state to the top of the state stack.
@@ -67,9 +112,18 @@ func push_state(state: State) -> void:
if state == null:
logger.warn("Invalid NULL state pushed.")
return
- var cur := current_state()
+ _ensure_state_allowed(state)
+ var current := current_state()
+ if state == current:
+ return
+ var is_new := not has_state(state)
_push_unique(state)
- state_changed.emit(cur, state)
+ if is_new:
+ state.state_added.emit()
+ if current:
+ current.state_exited.emit(state)
+ state.state_entered.emit(current)
+ state_changed.emit(current, state)
## Pushes the given state to the front of the stack
@@ -77,17 +131,32 @@ func push_state_front(state: State) -> void:
if state == null:
logger.warn("Invalid NULL state pushed.")
return
- var cur = current_state()
- _state_stack.push_front(state)
- state_changed.emit(cur, current_state())
+ _ensure_state_allowed(state)
+ var is_new := not has_state(state)
+ var last_current := current_state()
+ _push_front_unique(state)
+ var new_current := current_state()
+ if is_new:
+ state.state_added.emit()
+ if last_current != new_current:
+ if last_current:
+ last_current.state_exited.emit(new_current)
+ if new_current:
+ new_current.state_entered.emit(last_current)
+ state_changed.emit(last_current, new_current)
## Pop state will remove the last state from the stack and return it.
func pop_state() -> State:
if self.stack_length() > minimum_states:
- var popped = _state_stack.pop_back()
- var cur = current_state()
- state_changed.emit(popped, cur)
+ var popped := _state_stack.pop_back() as State
+ var current := current_state()
+ if popped:
+ popped.state_exited.emit(current)
+ popped.state_removed.emit()
+ if current:
+ current.state_entered.emit(popped)
+ state_changed.emit(popped, current)
return popped
return current_state()
@@ -97,29 +166,54 @@ func replace_state(state: State) -> void:
if state == null:
logger.warn("Invalid NULL state pushed.")
return
+ _ensure_state_allowed(state)
+ var current := current_state()
+ if state == current:
+ return
+ var is_new := not has_state(state)
var popped := _state_stack.pop_back() as State
_push_unique(state)
- if popped != null:
+ if is_new:
+ state.state_added.emit()
+ if popped:
+ popped.state_exited.emit(state)
popped.state_removed.emit()
+ state.state_entered.emit(popped)
state_changed.emit(popped, state)
## Removes all instances of the given state from the stack
func remove_state(state: State) -> void:
- var cur := current_state()
+ if not state:
+ return
+ if not has_state(state):
+ return
+ var last_current := current_state()
var new_state_stack: Array[State] = []
- for i in range(0, len(_state_stack)):
- var s := _state_stack[i]
- if state != s:
- new_state_stack.push_back(s)
+ for existing_state: State in _state_stack:
+ if state == existing_state:
+ continue
+ new_state_stack.push_back(existing_state)
_state_stack = new_state_stack
+ if last_current == state:
+ state.state_exited.emit(current_state())
state.state_removed.emit()
- state_changed.emit(cur, current_state())
+ var new_current := current_state()
+ if last_current != new_current:
+ if new_current:
+ new_current.state_entered.emit(last_current)
+ state_changed.emit(last_current, new_current)
## Removes all states
func clear_states() -> void:
+ var current := current_state()
+ if current:
+ current.state_exited.emit(null)
+ for state: State in _state_stack:
+ state.state_removed.emit()
_state_stack.clear()
+ state_changed.emit(current, null)
## Returns the length of the state stack
@@ -140,8 +234,29 @@ func has_state(state: State) -> bool:
func _push_unique(state: State) -> void:
- var i = _state_stack.find(state)
+ var i := _state_stack.find(state)
if i >= 0:
_state_stack.remove_at(i)
_state_stack.push_back(state)
+
+func _push_front_unique(state: State) -> void:
+ var i := _state_stack.find(state)
+ if i >= 0:
+ _state_stack.remove_at(i)
+ _state_stack.push_front(state)
+
+
+func _ensure_state_allowed(state: State) -> void:
+ if allowed_states.is_empty():
+ return
+ if state in allowed_states:
+ return
+ var msg := "State '{name}' is not in allowed states for state machine '{sm}'. Allowed: {list}".format(
+ {
+ "name": str(state),
+ "sm": str(logger_name),
+ "list": str(allowed_states)
+ }
+ )
+ assert(false, msg)
diff --git a/core/systems/state/state_machine_test.gd b/core/systems/state/state_machine_test.gd
index 2041e481..528bcbaf 100644
--- a/core/systems/state/state_machine_test.gd
+++ b/core/systems/state/state_machine_test.gd
@@ -9,17 +9,174 @@ func before_each() -> void:
watch_signals(state_machine)
+func test_set_state() -> void:
+ var state1 := State.new()
+ state1.name = "State1"
+ watch_signals(state1)
+ var state2 := State.new()
+ state2.name = "State2"
+ watch_signals(state2)
+ var state3 := State.new()
+ state3.name = "State3"
+ watch_signals(state3)
+ state_machine.set_state([state1, state2, state3])
+ assert_eq(state_machine.stack_length(), 3, "should have three states in the stack")
+ assert_signal_emitted_with_parameters(state_machine, "state_changed", [null, state3])
+ assert_signal_emitted(state1, "state_added", "should have emitted state_added signal")
+ assert_signal_emitted(state2, "state_added", "should have emitted state_added signal")
+ assert_signal_emitted(state3, "state_added", "should have emitted state_added signal")
+ assert_signal_emitted_with_parameters(state3, "state_entered", [null])
+ assert_signal_not_emitted(state2, "state_entered", "state2 should not have been entered")
+ assert_signal_not_emitted(state1, "state_entered", "state1 should not have been entered")
+
+ state_machine.set_state([state2])
+ assert_eq(state_machine.stack_length(), 1, "should have one state in the stack")
+ assert_signal_emitted_with_parameters(state_machine, "state_changed", [state3, state2])
+ assert_signal_emitted(state1, "state_removed", "should have emitted state_removed signal")
+ assert_signal_emitted(state3, "state_removed", "should have emitted state_removed signal")
+ assert_signal_emitted_with_parameters(state2, "state_entered", [state3])
+ assert_signal_emitted_with_parameters(state3, "state_exited", [state2])
+
+
func test_push_state() -> void:
- var state := State.new()
- state_machine.push_state(state)
+ var state1 := State.new()
+ state1.name = "State1"
+ watch_signals(state1)
+ state_machine.push_state(state1)
assert_eq(state_machine.stack_length(), 1, "should have one state in the stack")
- assert_signal_emitted_with_parameters(state_machine, "state_changed", [null, state])
+ assert_signal_emitted_with_parameters(state_machine, "state_changed", [null, state1])
+ assert_signal_emitted(state1, "state_added", "should have emitted state_added signal")
+ assert_signal_emitted_with_parameters(state1, "state_entered", [null])
+
+ var state2 := State.new()
+ state2.name = "State2"
+ watch_signals(state2)
+ state_machine.push_state(state2)
+ assert_eq(state_machine.stack_length(), 2, "should have two states in the stack")
+ assert_signal_emitted_with_parameters(state_machine, "state_changed", [state1, state2])
+ assert_signal_emitted_with_parameters(state1, "state_exited", [state2])
+ assert_signal_emitted(state2, "state_added", "should have emitted state_added signal")
+ assert_signal_emitted_with_parameters(state2, "state_entered", [state1])
+
+
+func test_push_state_front() -> void:
+ var state1 := State.new()
+ state1.name = "State1"
+ watch_signals(state1)
+ var state2 := State.new()
+ state2.name = "State2"
+ watch_signals(state2)
+ state_machine.push_state_front(state1)
+ state_machine.push_state_front(state2)
+ assert_eq(state_machine.stack_length(), 2, "should have two states in the stack")
+ assert_eq(state_machine.current_state(), state1, "state1 should be the current state")
+ assert_signal_emitted_with_parameters(state_machine, "state_changed", [null, state1])
+ assert_signal_emitted(state1, "state_added")
+ assert_signal_emitted(state2, "state_added")
+ assert_signal_not_emitted(state2, "state_entered", "state2 should not have been entered")
+ assert_signal_emitted_with_parameters(state1, "state_entered", [null])
func test_pop_state() -> void:
var state := State.new()
+ state.name = "State1"
state_machine.set_state([state])
+ watch_signals(state)
var popped := state_machine.pop_state()
assert_signal_emitted_with_parameters(state_machine, "state_changed", [state, null])
+ assert_signal_emitted_with_parameters(state, "state_exited", [null])
+ assert_signal_emitted(state, "state_removed", "should have emitted state_removed signal")
assert_eq(state_machine.stack_length(), 0, "should have no state in the stack")
assert_same(state, popped, "popped state is original state")
+
+ popped = state_machine.pop_state()
+ assert_eq(state_machine.stack_length(), 0, "should have no state in the stack")
+ assert_null(popped)
+
+ state_machine.minimum_states = 1
+ state_machine.push_state(state)
+ state_machine.pop_state()
+ assert_eq(state_machine.stack_length(), 1, "should have not have popped state")
+
+
+func test_replace_state() -> void:
+ var state1 := State.new()
+ state1.name = "State1"
+ watch_signals(state1)
+ var state2 := State.new()
+ state2.name = "State2"
+ watch_signals(state2)
+ state_machine.replace_state(state1)
+ assert_eq(state_machine.stack_length(), 1, "should have one state in the stack")
+ assert_eq(state_machine.current_state(), state1, "state1 should be the current state")
+ assert_signal_emitted_with_parameters(state_machine, "state_changed", [null, state1])
+ assert_signal_emitted(state1, "state_added")
+ assert_signal_emitted_with_parameters(state1, "state_entered", [null])
+
+ state_machine.replace_state(state2)
+ assert_eq(state_machine.stack_length(), 1, "should have one state in the stack")
+ assert_eq(state_machine.current_state(), state2, "state2 should be the current state")
+ assert_signal_emitted_with_parameters(state_machine, "state_changed", [state1, state2])
+ assert_signal_emitted(state2, "state_added")
+ assert_signal_emitted_with_parameters(state1, "state_exited", [state2])
+ assert_signal_emitted_with_parameters(state2, "state_entered", [state1])
+
+ var state3 := State.new()
+ state3.name = "State3"
+ state_machine.replace_state(state3)
+ watch_signals(state3)
+ state_machine.replace_state(state3)
+ assert_signal_not_emitted(state3, "state_entered", "should not have re-entered state")
+ assert_signal_not_emitted(state3, "state_exited", "should not have exited state")
+ assert_signal_not_emitted(state3, "state_removed", "should not have removed state")
+ assert_signal_not_emitted(state3, "state_added", "should not have added state")
+
+
+func test_remove_state() -> void:
+ var state1 := State.new()
+ state1.name = "State1"
+ watch_signals(state1)
+ var state2 := State.new()
+ state2.name = "State2"
+ watch_signals(state2)
+ var state3 := State.new()
+ state3.name = "State3"
+ watch_signals(state3)
+ state_machine.set_state([state1, state2, state3])
+ assert_eq(state_machine.stack_length(), 3, "should have three states in the stack")
+
+ state_machine.remove_state(state3)
+ assert_eq(state_machine.stack_length(), 2, "should have two states in the stack")
+ assert_eq(state_machine.current_state(), state2, "state2 should be the current state")
+ assert_signal_emitted_with_parameters(state2, "state_entered", [state3])
+ assert_signal_emitted_with_parameters(state3, "state_exited", [state2])
+ assert_signal_emitted(state3, "state_removed")
+ assert_signal_not_emitted(state1, "state_entered")
+ assert_signal_not_emitted(state1, "state_exited")
+
+ state_machine.remove_state(state1)
+ assert_eq(state_machine.stack_length(), 1, "should have one state in the stack")
+ assert_eq(state_machine.current_state(), state2, "state2 should be the current state")
+ assert_signal_emitted(state1, "state_removed")
+ assert_signal_not_emitted(state1, "state_entered")
+ assert_signal_not_emitted(state1, "state_exited")
+
+
+func test_clear_state() -> void:
+ var state1 := State.new()
+ state1.name = "State1"
+ var state2 := State.new()
+ state2.name = "State2"
+ var state3 := State.new()
+ state3.name = "State3"
+
+ state_machine.set_state([state1, state2, state3])
+ watch_signals(state1)
+ watch_signals(state2)
+ watch_signals(state3)
+ state_machine.clear_states()
+ assert_eq(state_machine.stack_length(), 0, "should have no states in the stack")
+ assert_signal_emitted(state1, "state_removed")
+ assert_signal_emitted(state2, "state_removed")
+ assert_signal_emitted(state3, "state_removed")
+ assert_signal_emitted_with_parameters(state3, "state_exited", [null])
diff --git a/core/systems/state/state_machine_watcher.gd b/core/systems/state/state_machine_watcher.gd
new file mode 100644
index 00000000..fcfe3161
--- /dev/null
+++ b/core/systems/state/state_machine_watcher.gd
@@ -0,0 +1,41 @@
+@tool
+@icon("res://assets/editor-icons/visible.svg")
+extends Node
+class_name StateMachineWatcher
+
+## Fires signals based on [StateMachine] changes
+##
+## The [StateMachineWatcher] fires signals based changes to the given
+## [StateMachine]. This enables other nodes to react to state changes.
+
+## Emitted when the configured [StateMachine] changes states
+signal state_changed(to: State, from: State)
+## Emitted when the configured [StateMachine] no longer has any [State] objects
+## in its stack
+signal emptied
+
+## Fire signals when this state machine changes
+@export var state_machine: StateMachine:
+ set(v):
+ state_machine = v
+ if Engine.is_editor_hint():
+ update_configuration_warnings()
+
+
+func _ready() -> void:
+ if not state_machine:
+ return
+ if Engine.is_editor_hint():
+ return
+ var on_changed := func(from: State, to: State):
+ state_changed.emit(from, to)
+ state_machine.state_changed.connect(on_changed)
+ var on_emptied := func():
+ emptied.emit()
+ state_machine.emptied.connect(on_emptied)
+
+
+func _get_configuration_warnings() -> PackedStringArray:
+ if not state_machine:
+ return ["No state machine configured!"]
+ return []
diff --git a/core/systems/state/state_updater.gd b/core/systems/state/state_updater.gd
index 1f330abc..09670971 100644
--- a/core/systems/state/state_updater.gd
+++ b/core/systems/state/state_updater.gd
@@ -15,13 +15,20 @@ const in_game := preload("res://assets/state/states/in_game.tres")
## Possible state actions to take
enum ACTION {
PUSH, ## Pushes the state on top of the state stack
+ PUSH_FRONT, ## Pushes the state to the beginning of the state stack
POP, ## Removes the state at the top of the state stack
REPLACE, ## Replaces the state at the top of the state stack
SET, ## Removes all states and sets the given state
+ REFRESH, ## Calls the 'refresh' signal on the current state of the [StateMachine]
+ CLEAR, ## Clear all states from the [StateMachine]
}
## The state machine instance to use for managing state changes
-@export var state_machine: StateMachine
+@export var state_machine: StateMachine:
+ set(v):
+ state_machine = v
+ if Engine.is_editor_hint():
+ update_configuration_warnings()
## Signal on our parent to connect to. When this signal fires, the [StateUpdater]
## will change the state machine to the given state.
var on_signal: String
@@ -32,6 +39,9 @@ var on_signal: String
func _ready() -> void:
+ # Don't run in the editor
+ if Engine.is_editor_hint():
+ return
notify_property_list_changed()
get_parent().connect(on_signal, _on_signal)
@@ -46,6 +56,8 @@ func _on_signal(metakey: String = "", metadata: Variant = null):
match action:
ACTION.PUSH:
sm.push_state(state)
+ ACTION.PUSH_FRONT:
+ sm.push_state_front(state)
ACTION.POP:
sm.pop_state()
ACTION.REPLACE:
@@ -56,6 +68,10 @@ func _on_signal(metakey: String = "", metadata: Variant = null):
if sm.has_state(in_game):
states.push_front(in_game)
sm.set_state(states)
+ ACTION.REFRESH:
+ sm.refresh()
+ ACTION.CLEAR:
+ sm.clear_states()
# Customize editor properties that we expose. Here we dynamically look up
@@ -85,3 +101,9 @@ func _get_property_list():
)
return properties
+
+
+func _get_configuration_warnings() -> PackedStringArray:
+ if not state_machine:
+ return ["No state machine configured!"]
+ return []
diff --git a/core/systems/state/state_updater.tscn b/core/systems/state/state_updater.tscn
index fdb20c19..1e06f1f2 100644
--- a/core/systems/state/state_updater.tscn
+++ b/core/systems/state/state_updater.tscn
@@ -3,5 +3,6 @@
[ext_resource type="Script" path="res://core/systems/state/state_updater.gd" id="1_anggw"]
[node name="StateUpdater" type="Node"]
+editor_description = "Update a state machine when a node signal fires"
script = ExtResource("1_anggw")
on_signal = ""
diff --git a/core/systems/state/state_watcher.gd b/core/systems/state/state_watcher.gd
index 093529d7..9f362041 100644
--- a/core/systems/state/state_watcher.gd
+++ b/core/systems/state/state_watcher.gd
@@ -10,6 +10,8 @@ class_name StateWatcher
signal state_entered
signal state_exited
signal state_removed
+signal state_added
+signal state_refreshed
## Fire signals when this state is switched to
@export var state: State
@@ -17,12 +19,18 @@ signal state_removed
func _ready() -> void:
assert(state != null)
- var on_entered := func(from: State):
+ var on_entered := func(_from: State):
state_entered.emit()
state.state_entered.connect(on_entered)
- var on_exited := func(to: State):
+ var on_exited := func(_to: State):
state_exited.emit()
state.state_exited.connect(on_exited)
var on_removed := func():
state_removed.emit()
state.state_removed.connect(on_removed)
+ var on_added := func():
+ state_added.emit()
+ state.state_added.connect(on_added)
+ var on_refresh := func():
+ state_refreshed.emit()
+ state.refreshed.connect(on_refresh)
diff --git a/core/systems/state/states_watcher.gd b/core/systems/state/states_watcher.gd
new file mode 100644
index 00000000..4ead2259
--- /dev/null
+++ b/core/systems/state/states_watcher.gd
@@ -0,0 +1,40 @@
+@icon("res://assets/editor-icons/visible.svg")
+extends Node
+class_name StatesWatcher
+
+## Fires signals based on [State] changes to a [StateMachine]
+##
+## The [StatesWatcher] fires signals based on the current [State] of a
+## [StateMachine]. This enables other nodes to react to state changes.
+
+signal state_entered
+signal state_exited
+signal state_removed
+signal state_added
+signal state_refreshed
+
+## Fire signals when this state is switched to
+@export var states: Array[State]
+
+
+func _ready() -> void:
+ for state in states:
+ var on_entered := func(from: State):
+ if from in states:
+ return
+ state_entered.emit()
+ state.state_entered.connect(on_entered)
+ var on_exited := func(to: State):
+ if to in states:
+ return
+ state_exited.emit()
+ state.state_exited.connect(on_exited)
+ var on_removed := func():
+ state_removed.emit()
+ state.state_removed.connect(on_removed)
+ var on_added := func():
+ state_added.emit()
+ state.state_added.connect(on_added)
+ var on_refresh := func():
+ state_refreshed.emit()
+ state.refreshed.connect(on_refresh)
diff --git a/core/systems/state/states_watcher.tscn b/core/systems/state/states_watcher.tscn
new file mode 100644
index 00000000..054a9a60
--- /dev/null
+++ b/core/systems/state/states_watcher.tscn
@@ -0,0 +1,6 @@
+[gd_scene load_steps=2 format=3 uid="uid://bfiia7vnbfw3s"]
+
+[ext_resource type="Script" path="res://core/systems/state/states_watcher.gd" id="1_ob7ue"]
+
+[node name="StatesWatcher" type="Node"]
+script = ExtResource("1_ob7ue")
diff --git a/core/systems/threading/shared_thread.gd b/core/systems/threading/shared_thread.gd
index 1c508fb1..ffd820f1 100644
--- a/core/systems/threading/shared_thread.gd
+++ b/core/systems/threading/shared_thread.gd
@@ -55,6 +55,9 @@ func _init(options: int = Option.WATCHDOG_ENABLE as int) -> void:
func _notification(what: int):
if what == NOTIFICATION_PREDELETE:
+ if not self:
+ logger.error("Unable to stop thread, lost reference to self... Somehow...")
+ return
stop()
diff --git a/core/systems/user_interface/behavior_node.gd b/core/systems/user_interface/behavior_node.gd
new file mode 100644
index 00000000..521c07e8
--- /dev/null
+++ b/core/systems/user_interface/behavior_node.gd
@@ -0,0 +1,72 @@
+@tool
+@icon("res://assets/editor-icons/fluent--brain-circuit-24-filled.svg")
+extends Node
+class_name BehaviorNode
+
+## Base class for defining signal-based behavior
+##
+## A [BehaviorNode] is a node that follows a signaling pattern. These nodes can
+## be added as a child of any node and can be configured to listen for and react
+## to signals from its parent. This can allow developers to attach behaviors
+## to nodes in the scene tree from the editor in a compositional way.
+
+
+## The signal to connect to on this behavior's parent node. This behavior will
+## execute whenever this signal is fired.
+var on_signal: String:
+ set(v):
+ on_signal = v
+ if Engine.is_editor_hint():
+ update_configuration_warnings()
+
+func _init() -> void:
+ ready.connect(_on_ready)
+
+
+## Automatically connect to the configured parent signal on ready
+func _on_ready() -> void:
+ notify_property_list_changed()
+ # Don't run in the editor
+ if Engine.is_editor_hint():
+ return
+ if on_signal != "":
+ get_parent().connect(on_signal, _on_signal)
+
+
+## Invoked whenever the configured parent signal fires. This should be overridden
+## in a child class.
+func _on_signal(arg1: Variant = null, arg2: Variant = null, arg3: Variant = null, arg4: Variant = null):
+ pass
+
+
+# Customize editor properties that we expose. Here we dynamically look up
+# the parent node's signals so we can display them in a list.
+func _get_property_list():
+ # By default, `on_signal` is not visible in the editor.
+ var property_usage := PROPERTY_USAGE_NO_EDITOR
+
+ var parent_signals := []
+ if get_parent() != null:
+ property_usage = PROPERTY_USAGE_DEFAULT
+ for sig in get_parent().get_signal_list():
+ parent_signals.push_back(sig["name"])
+
+ var properties := []
+ properties.append(
+ {
+ "name": "on_signal",
+ "type": TYPE_STRING,
+ "usage": property_usage, # See above assignment.
+ "hint": PROPERTY_HINT_ENUM,
+ "hint_string": ",".join(parent_signals)
+ }
+ )
+
+ return properties
+
+
+func _get_configuration_warnings() -> PackedStringArray:
+ var warnings := PackedStringArray()
+ if on_signal.is_empty():
+ warnings.append("No parent signal selected!")
+ return warnings
diff --git a/core/systems/user_interface/tab_setter.gd b/core/systems/user_interface/tab_setter.gd
new file mode 100644
index 00000000..094514ad
--- /dev/null
+++ b/core/systems/user_interface/tab_setter.gd
@@ -0,0 +1,32 @@
+@tool
+extends BehaviorNode
+class_name TabSetter
+
+## Set the current tab on a [TabContainer] in reaction to a parent signal
+##
+## This [BehaviorNode] can be added as a child to any node and configured to
+## listen for a signal. When the parent signal fires, this behavior will set
+## the current tab on the given target [TabContainer].
+
+## The target [TabContainer] to update the current tab in response to a signal
+@export var target: TabContainer:
+ set(v):
+ target = v
+ if Engine.is_editor_hint():
+ update_configuration_warnings()
+## The current tab number to switch to
+@export var tab_number: int = 0
+
+
+## Set the current tab on the target node
+func _on_signal(_arg1: Variant = null, _arg2: Variant = null, _arg3: Variant = null, _arg4: Variant = null):
+ if not target:
+ return
+ target.current_tab = tab_number
+
+
+func _get_configuration_warnings() -> PackedStringArray:
+ var warnings := super()
+ if not target:
+ warnings.append("No target tab container configured!")
+ return warnings
diff --git a/core/systems/user_interface/text_setter.gd b/core/systems/user_interface/text_setter.gd
new file mode 100644
index 00000000..5d3b627e
--- /dev/null
+++ b/core/systems/user_interface/text_setter.gd
@@ -0,0 +1,33 @@
+@tool
+@icon("res://assets/editor-icons/fluent--draw-text-24-filled.svg")
+extends BehaviorNode
+class_name TextSetter
+
+## Set text on the target [Label] node in reaction to a parent signal
+##
+## This [BehaviorNode] can be added as a child to any node and configured to
+## listen for a signal. When the parent signal fires, this behavior will set
+## the text on the given target [Label].
+
+## The target [Label] to update with the given text when a parent signal fires
+@export var target: Label:
+ set(v):
+ target = v
+ if Engine.is_editor_hint():
+ update_configuration_warnings()
+## The text to set on the target label
+@export var text: String = ""
+
+
+## Set the current tab on the target node
+func _on_signal(_arg1: Variant = null, _arg2: Variant = null, _arg3: Variant = null, _arg4: Variant = null):
+ if not target:
+ return
+ target.text = tr(text)
+
+
+func _get_configuration_warnings() -> PackedStringArray:
+ var warnings := super()
+ if not target:
+ warnings.append("No target label configured!")
+ return warnings
diff --git a/core/systems/utility/theme_utils.gd b/core/systems/utility/theme_utils.gd
new file mode 100644
index 00000000..b25b2d7c
--- /dev/null
+++ b/core/systems/utility/theme_utils.gd
@@ -0,0 +1,19 @@
+extends RefCounted
+class_name ThemeUtils
+
+
+## Returns the effective theme of the node. This will visit each parent node
+## until it finds a theme and returns it. If no theme is found, null will be
+## returned.
+static func get_effective_theme(node: Control) -> Theme:
+ if not node:
+ return null
+ var parent := node.get_parent()
+ if not parent is Control:
+ return null
+
+ var parent_control := parent as Control
+ if parent_control.theme:
+ return parent_control.theme
+
+ return get_effective_theme(parent_control)
diff --git a/core/ui/card_ui/card_ui.gd b/core/ui/card_ui/card_ui.gd
index acc7964e..44327f30 100644
--- a/core/ui/card_ui/card_ui.gd
+++ b/core/ui/card_ui/card_ui.gd
@@ -9,6 +9,10 @@ var input_plumber := load("res://core/systems/input/input_plumber.tres") as Inpu
var state_machine := (
preload("res://assets/state/state_machines/global_state_machine.tres") as StateMachine
)
+var menu_state_machine := preload("res://assets/state/state_machines/menu_state_machine.tres") as StateMachine
+var popup_state_machine := preload("res://assets/state/state_machines/popup_state_machine.tres") as StateMachine
+var menu_state := preload("res://assets/state/states/menu.tres") as State
+var popup_state := preload("res://assets/state/states/popup.tres") as State
var first_boot_state := preload("res://assets/state/states/first_boot_menu.tres") as State
var home_state := preload("res://assets/state/states/home.tres") as State
var in_game_state := preload("res://assets/state/states/in_game.tres") as State
@@ -16,21 +20,21 @@ var osk_state := preload("res://assets/state/states/osk.tres") as State
var power_state := preload("res://assets/state/states/power_menu.tres") as State
var PID: int = OS.get_process_id()
-var overlay_window_id = gamescope.get_window_id(PID, gamescope.XWAYLAND.OGUI)
+var overlay_window_id := gamescope.get_window_id(PID, gamescope.XWAYLAND.OGUI)
-@onready var panel := $%Panel
-@onready var ui_container := $%MenuContent
-@onready var boot_video := $%BootVideoPlayer
-@onready var fade_transition := $%FadeTransitionPlayer
-@onready var fade_texture := $%FadeTexture
-@onready var power_timer := $%PowerTimer
-@onready var settings_menu := $%SettingsMenu
+@onready var panel := $%Panel as Panel
+@onready var ui_container := $%MenuContent as MarginContainer
+@onready var boot_video := $%BootVideoPlayer as VideoStreamPlayer
+@onready var fade_transition := $%FadeTransitionPlayer as AnimationPlayer
+@onready var fade_texture := $%FadeTexture as TextureRect
+@onready var power_timer := $%PowerTimer as Timer
+@onready var settings_menu := $%SettingsMenu as Control
var logger = Log.get_logger("Main", Log.LEVEL.INFO)
func _init() -> void:
# Tell gamescope that we're an overlay
- if overlay_window_id < 0:
+ if overlay_window_id <= 0:
logger.error("Unable to detect Window ID. Overlay is not going to work!")
logger.debug("Found primary window id: {0}".format([overlay_window_id]))
_setup(overlay_window_id)
@@ -38,7 +42,7 @@ func _init() -> void:
# Lets us run as an overlay in gamescope
func _setup(window_id: int) -> void:
- if window_id < 0:
+ if window_id <= 0:
logger.error("Unable to configure gamescope atoms")
return
# Pretend to be Steam
@@ -68,8 +72,36 @@ func _ready() -> void:
# Set the FPS limit
Engine.max_fps = settings_manager.get_value("general", "max_fps", 60) as int
- # Listen for global state changes
- state_machine.state_changed.connect(_on_state_changed)
+ # Always push the menu state if we end up with an empty stack.
+ var on_states_emptied := func():
+ state_machine.push_state.call_deferred(menu_state)
+ state_machine.emptied.connect(on_states_emptied)
+
+ # Whenever the menu state is refreshed, refresh the menu state machine to
+ # re-grab focus.
+ var on_menu_refreshed := func():
+ menu_state_machine.refresh()
+ menu_state.refreshed.connect(on_menu_refreshed)
+
+ # Whenever the menu state is entered, refresh the menu state machine to
+ # re-grab focus
+ var on_menu_state_entered := func(_from: State):
+ menu_state_machine.refresh()
+ menu_state.state_entered.connect(on_menu_state_entered)
+ var on_menu_state_removed := func():
+ menu_state_machine.clear_states()
+ menu_state.state_removed.connect(on_menu_state_removed)
+ var on_menu_states_empty := func():
+ state_machine.remove_state(menu_state)
+ menu_state_machine.emptied.connect(on_menu_states_empty)
+
+ # Whenever an popup state is pushed, update the global state
+ var on_popup_state_changed := func(_from: State, to: State):
+ if to:
+ state_machine.push_state(popup_state)
+ else:
+ state_machine.remove_state(popup_state)
+ popup_state_machine.state_changed.connect(on_popup_state_changed)
# Show/hide the overlay when we enter/exit the in-game state
in_game_state.state_entered.connect(_on_game_state_entered)
@@ -88,21 +120,13 @@ func _ready() -> void:
input_plumber.composite_device_changed.connect(on_device_changed)
# Set the theme if one was set
- var theme_path := settings_manager.get_value("general", "theme", "") as String
- if theme_path == "":
- logger.debug("No theme set. Using default theme.")
-
- var current_theme = get_theme()
- if theme_path != "" && current_theme.resource_path != theme_path:
- logger.debug("Setting theme to: " + theme_path)
- var loaded_theme = load(theme_path)
- if loaded_theme != null:
- # TODO: This is a workaround, themes aren't properly set the first time.
- call_deferred("set_theme", loaded_theme)
- call_deferred("set_theme", current_theme)
- call_deferred("set_theme", loaded_theme)
- else:
- logger.debug("Unable to load theme")
+ var theme_path := settings_manager.get_value("general", "theme", "res://assets/themes/card_ui-dracula.tres") as String
+ logger.debug("Setting theme to: " + theme_path)
+ var loaded_theme = load(theme_path)
+ if loaded_theme != null:
+ set_theme(loaded_theme)
+ else:
+ logger.debug("Unable to load theme")
func _on_focus_changed(control: Control) -> void:
@@ -110,12 +134,6 @@ func _on_focus_changed(control: Control) -> void:
logger.debug("Focus changed to: " + control.get_parent().name + " | " + control.name)
-# Always push the home state if we end up with an empty stack.
-func _on_state_changed(_from: State, _to: State) -> void:
- if state_machine.stack_length() == 0:
- state_machine.push_state.call_deferred(home_state)
-
-
## Invoked when the in-game state was entered
func _on_game_state_entered(_from: State) -> void:
# Pass all gamepad input to the game
@@ -131,7 +149,12 @@ func _on_game_state_entered(_from: State) -> void:
# Ensure panel is invisible
panel.visible = false
for child in ui_container.get_children():
- child.visible = false
+ if not child is Control:
+ continue
+ (child as Control).visible = false
+
+ # Clear all menu states
+ menu_state_machine.clear_states()
## Invoked when the in-game state is exited
@@ -143,22 +166,30 @@ func _on_game_state_exited(to: State) -> void:
#gamepad_manager.set_gamepads_profile(null)
# Set gamescope input focus to on so the user can interact with the UI
+ if to == popup_state:
+ var current_popup := popup_state_machine.current_state()
+ if current_popup == osk_state:
+ return
+
if gamescope.set_input_focus(overlay_window_id, 1) != OK:
logger.error("Unable to set STEAM_INPUT_FOCUS atom!")
# If the in-game state still exists in the stack, set the blur state.
if state_machine.has_state(in_game_state):
panel.visible = false
- if to != osk_state:
- # Only blur if the focused GFX app is set
- if gamescope.get_focused_app_gfx() != Gamescope.OVERLAY_GAME_ID:
- _set_blur(gamescope.BLUR_MODE.ALWAYS)
+ # Only blur if the focused GFX app is set
+ var should_blur := settings_manager.get_value("display", "enable_overlay_blur", true) as bool
+ if should_blur and gamescope.get_focused_app_gfx() != Gamescope.OVERLAY_GAME_ID:
+ _set_blur(gamescope.BLUR_MODE.ALWAYS)
+
else:
_on_game_state_removed()
# Un-hide all UI elements
for child in ui_container.get_children():
- child.visible = true
+ if not child is Control:
+ continue
+ (child as Control).visible = true
## Invoked when the in-game state is removed
@@ -169,9 +200,11 @@ func _on_game_state_removed() -> void:
# Un-hide the background panel
panel.visible = true
- # Reset the state stack if no home state exists
- if not state_machine.has_state(home_state):
- state_machine.set_state([home_state])
+ # Reset the state stack if no menu state exists
+ if not state_machine.has_state(menu_state):
+ state_machine.set_state([menu_state])
+ if not menu_state_machine.has_state(home_state):
+ menu_state_machine.set_state([home_state])
# Sets the blur mode in gamescope
@@ -192,13 +225,16 @@ func _on_boot_video_player_finished() -> void:
fade_transition.play("fade")
boot_video.visible = false
+ # Set the initial global state
+ state_machine.push_state(menu_state)
+
# If this is the first boot, enter the first-boot menu state. Otherwise,
# go to the home state.
if settings_manager.get_value("general", "first_boot", true):
- state_machine.push_state(first_boot_state)
+ menu_state_machine.push_state(first_boot_state)
else:
# Initialize the state machine with its initial state
- state_machine.push_state(home_state)
+ menu_state_machine.push_state(home_state)
## Called when any unhandled input reaches the main node
@@ -210,7 +246,7 @@ func _input(event: InputEvent) -> void:
if event.is_action_pressed("ogui_power"):
var open_power_menu := func():
logger.info("Power menu requested")
- state_machine.push_state(power_state)
+ popup_state_machine.push_state(power_state)
power_timer.timeout.connect(open_power_menu, CONNECT_ONE_SHOT)
power_timer.start()
return
diff --git a/core/ui/card_ui/card_ui.tscn b/core/ui/card_ui/card_ui.tscn
index 45a1044c..870a3dab 100644
--- a/core/ui/card_ui/card_ui.tscn
+++ b/core/ui/card_ui/card_ui.tscn
@@ -1,7 +1,6 @@
-[gd_scene load_steps=39 format=3 uid="uid://fhriwlhm0lcj"]
+[gd_scene load_steps=38 format=3 uid="uid://fhriwlhm0lcj"]
[ext_resource type="PackedScene" uid="uid://n83wlhmmsu3j" path="res://core/systems/input/input_manager.tscn" id="1_34t85"]
-[ext_resource type="Theme" uid="uid://ehplgpp70vxa" path="res://assets/themes/card_ui-dracula.tres" id="1_ajgj2"]
[ext_resource type="Script" path="res://core/ui/card_ui/card_ui.gd" id="1_f8851"]
[ext_resource type="PackedScene" uid="uid://dlegwm7jqfe2i" path="res://core/systems/boxart/boxart_local.tscn" id="2_600i0"]
[ext_resource type="PackedScene" uid="uid://ch6qw6obetalo" path="res://core/systems/library/library_desktop.tscn" id="3_68bes"]
@@ -83,7 +82,6 @@ anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
-theme = ExtResource("1_ajgj2")
script = ExtResource("1_f8851")
[node name="InputManager" parent="." instance=ExtResource("1_34t85")]
@@ -176,7 +174,6 @@ visible = false
layout_mode = 2
[node name="TopMargin" type="MarginContainer" parent="MenuContent"]
-z_index = 20
layout_mode = 2
mouse_filter = 2
theme_override_constants/margin_left = 30
@@ -264,6 +261,8 @@ size_flags_vertical = 0
unique_name_in_owner = true
visible = false
layout_mode = 2
+size_flags_horizontal = 4
+size_flags_vertical = 4
[node name="OnScreenKeyboard" parent="AlwaysVisibleContent" instance=ExtResource("18_462u5")]
visible = false
@@ -306,7 +305,7 @@ mouse_filter = 2
[node name="FadeTexture" type="TextureRect" parent="."]
unique_name_in_owner = true
-z_index = 20
+z_index = 21
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
@@ -323,7 +322,7 @@ libraries = {
[node name="BootVideoPlayer" type="VideoStreamPlayer" parent="."]
unique_name_in_owner = true
-z_index = 20
+z_index = 21
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
diff --git a/core/ui/card_ui/gamepad/gamepad_mapper.tscn b/core/ui/card_ui/gamepad/gamepad_mapper.tscn
index 5abedf6e..a208cd53 100644
--- a/core/ui/card_ui/gamepad/gamepad_mapper.tscn
+++ b/core/ui/card_ui/gamepad/gamepad_mapper.tscn
@@ -1,12 +1,13 @@
-[gd_scene load_steps=22 format=3 uid="uid://b3o3wo40sfih1"]
+[gd_scene load_steps=23 format=3 uid="uid://b3o3wo40sfih1"]
[ext_resource type="Script" path="res://core/ui/card_ui/gamepad/gamepad_mapper.gd" id="1_alala"]
[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="2_4osef"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_o3mis"]
+[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="3_2kcao"]
[ext_resource type="Resource" uid="uid://c4er7pfmn7x50" path="res://assets/state/state_machines/gamepad_settings_state_machine.tres" id="3_wgp64"]
[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="4_scwwv"]
[ext_resource type="Resource" uid="uid://46cu324n427u" path="res://assets/state/states/gamepad_change_input.tres" id="4_sv30y"]
[ext_resource type="PackedScene" uid="uid://uljtdvmuol3l" path="res://core/systems/input/focus_group_setter.tscn" id="5_i6uqd"]
-[ext_resource type="PackedScene" uid="uid://ccd4sw84h1qbc" path="res://core/systems/input/back_input_handler.tscn" id="5_roi3c"]
[ext_resource type="Texture2D" uid="uid://dyemqkvdtk43e" path="res://assets/images/gamepad/xbox/xbox_button_color_a.svg" id="8_5jaa6"]
[ext_resource type="PackedScene" uid="uid://cgmb4kr2ec4ha" path="res://core/ui/components/tabs_header.tscn" id="8_okgql"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="9_an8os"]
@@ -34,6 +35,16 @@ grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_alala")
+[node name="InputWatcher" parent="." instance=ExtResource("2_o3mis")]
+stop_propagation = true
+process_input_mode = 1
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("3_2kcao")]
+state_machine = ExtResource("3_wgp64")
+action = 2
+on_signal = "input_released"
+
[node name="StateWatcher" parent="." instance=ExtResource("2_4osef")]
state = ExtResource("4_sv30y")
@@ -47,11 +58,6 @@ on_signal = "state_entered"
target = NodePath("../../VBoxContainer/TabContainer/Gamepad/MarginContainer/HBoxContainer/PanelContainer/MarginContainer/ScrollContainer/GamepadInputContainer/GamepadFocusGroup")
on_signal = "state_entered"
-[node name="BackInputHandler" parent="." instance=ExtResource("5_roi3c")]
-state_machine = ExtResource("3_wgp64")
-process_input_during = Array[Resource("res://core/systems/state/state.gd")]([ExtResource("4_sv30y")])
-minimum_states = 0
-
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 15
diff --git a/core/ui/card_ui/gamepad/gamepad_settings.gd b/core/ui/card_ui/gamepad/gamepad_settings.gd
index 177591ab..d5dbdab0 100644
--- a/core/ui/card_ui/gamepad/gamepad_settings.gd
+++ b/core/ui/card_ui/gamepad/gamepad_settings.gd
@@ -10,7 +10,7 @@ var in_game_state := load("res://assets/state/states/in_game.tres") as State
var launch_manager := load("res://core/global/launch_manager.tres") as LaunchManager
var notification_manager := load("res://core/global/notification_manager.tres") as NotificationManager
var settings_manager := load("res://core/global/settings_manager.tres") as SettingsManager
-var global_state_machine := load("res://assets/state/state_machines/global_state_machine.tres") as StateMachine
+var global_state_machine := load("res://assets/state/state_machines/menu_state_machine.tres") as StateMachine
var state_machine := load("res://assets/state/state_machines/gamepad_settings_state_machine.tres") as StateMachine
var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumber
var input_icons := load("res://core/systems/input/input_icon_manager.tres") as InputIconManager
@@ -428,6 +428,9 @@ func _update_mapping_elements() -> void:
mappings = profile.get_mappings_by_source_capability(capability)
else:
var mapping := InputPlumberMapping.from_source_capability(capability)
+ if not mapping:
+ logger.error("Failed to create Mapping from Capability", capability)
+ continue
var target_event = InputPlumberEvent.from_capability(capability)
logger.debug("Adding", capability, "to mappings as:", mapping, " with event:", target_event)
mapping.target_events = [target_event]
diff --git a/core/ui/card_ui/gamepad/gamepad_settings.tscn b/core/ui/card_ui/gamepad/gamepad_settings.tscn
index 039ec89f..8bd77f19 100644
--- a/core/ui/card_ui/gamepad/gamepad_settings.tscn
+++ b/core/ui/card_ui/gamepad/gamepad_settings.tscn
@@ -1,14 +1,16 @@
-[gd_scene load_steps=22 format=3 uid="uid://cwarv58ju0sow"]
+[gd_scene load_steps=24 format=3 uid="uid://cwarv58ju0sow"]
[ext_resource type="Script" path="res://core/ui/card_ui/gamepad/gamepad_settings.gd" id="1_o4hh5"]
[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="2_3x6ad"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_ck60w"]
[ext_resource type="Resource" uid="uid://cx8u1y5j7vyss" path="res://assets/state/states/gamepad_settings.tres" id="3_wyv83"]
+[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="3_yygaq"]
[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="4_8f1hr"]
+[ext_resource type="Resource" uid="uid://bcr6c0281lb5b" path="res://assets/state/state_machines/menu_state_machine.tres" id="4_wxnal"]
[ext_resource type="PackedScene" uid="uid://uljtdvmuol3l" path="res://core/systems/input/focus_group_setter.tscn" id="5_7qogg"]
[ext_resource type="PackedScene" uid="uid://cr83fmlociwko" path="res://core/ui/components/card_icon_button.tscn" id="6_lxfig"]
[ext_resource type="Resource" uid="uid://46cu324n427u" path="res://assets/state/states/gamepad_change_input.tres" id="6_oe11l"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="7_fyh3u"]
-[ext_resource type="PackedScene" uid="uid://ccd4sw84h1qbc" path="res://core/systems/input/back_input_handler.tscn" id="9_5ao07"]
[ext_resource type="Texture2D" uid="uid://cfpaw2eck4ftr" path="res://assets/ui/icons/save-fill.svg" id="10_y71gk"]
[ext_resource type="PackedScene" uid="uid://dbll03tbmw3ps" path="res://core/ui/components/card_mapping_button.tscn" id="11_unkgl"]
[ext_resource type="Texture2D" uid="uid://dj1ohb74chydb" path="res://assets/ui/icons/round-delete-forever.svg" id="12_64hbv"]
@@ -32,6 +34,16 @@ grow_vertical = 2
mouse_filter = 2
script = ExtResource("1_o4hh5")
+[node name="InputWatcher" parent="." instance=ExtResource("2_ck60w")]
+stop_propagation = true
+process_input_mode = 1
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("3_yygaq")]
+state_machine = ExtResource("4_wxnal")
+action = 2
+on_signal = "input_released"
+
[node name="StateWatcher" parent="." instance=ExtResource("2_3x6ad")]
state = ExtResource("3_wyv83")
@@ -45,9 +57,6 @@ on_signal = "state_entered"
target = NodePath("../../MainContainer/TopLevelContainer/HBoxContainer/VBoxContainer/MainFocusGroup")
on_signal = "state_entered"
-[node name="BackInputHandler" parent="." instance=ExtResource("9_5ao07")]
-process_input_during = Array[Resource("res://core/systems/state/state.gd")]([ExtResource("3_wyv83")])
-
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 1
anchors_preset = 15
diff --git a/core/ui/card_ui/help/help_menu.tscn b/core/ui/card_ui/help/help_menu.tscn
index 3f7aa1c7..1234be16 100644
--- a/core/ui/card_ui/help/help_menu.tscn
+++ b/core/ui/card_ui/help/help_menu.tscn
@@ -1,15 +1,20 @@
-[gd_scene load_steps=9 format=3 uid="uid://dj1fooc3gh13l"]
+[gd_scene load_steps=13 format=3 uid="uid://dj1fooc3gh13l"]
[ext_resource type="Script" path="res://core/ui/card_ui/help/help_menu.gd" id="1_7fti5"]
-[ext_resource type="PackedScene" uid="uid://orey8uxm7v6v" path="res://core/systems/state/visibility_manager.tscn" id="2_pli5u"]
-[ext_resource type="PackedScene" uid="uid://ccd4sw84h1qbc" path="res://core/systems/input/back_input_handler.tscn" id="3_effq8"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_cxu4x"]
+[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="3_10mqr"]
[ext_resource type="Resource" uid="uid://db5gbdl3xgwlq" path="res://assets/state/states/help_menu.tres" id="3_hidel"]
+[ext_resource type="Resource" uid="uid://cadriyl38ny5y" path="res://assets/state/state_machines/popup_state_machine.tres" id="4_x24i3"]
+[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_1uaoh"]
[ext_resource type="PackedScene" uid="uid://b0cyl6fdqxevn" path="res://core/systems/input/scroller_joystick.tscn" id="5_uocjd"]
[ext_resource type="PackedScene" uid="uid://dithv38oqgy58" path="res://core/ui/components/section_label.tscn" id="5_wfsen"]
[ext_resource type="PackedScene" uid="uid://d0u3rsa5qpj57" path="res://core/ui/components/subsection_label.tscn" id="7_15bya"]
+[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="7_dlovl"]
[ext_resource type="Script" path="res://core/ui/components/input_icon.gd" id="8_swg2f"]
+[ext_resource type="PackedScene" uid="uid://ekhjpmat02f8" path="res://core/systems/effects/slide_effect.tscn" id="8_ut6gk"]
[node name="HelpMenu" type="Control"]
+z_index = 20
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
@@ -18,11 +23,32 @@ grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_7fti5")
-[node name="VisibilityManager" parent="." instance=ExtResource("2_pli5u")]
+[node name="InputWatcher" parent="." instance=ExtResource("2_cxu4x")]
+stop_propagation = true
+process_input_mode = 1
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("3_10mqr")]
+state_machine = ExtResource("4_x24i3")
+action = 2
+on_signal = "input_released"
+
+[node name="StateWatcher" parent="." instance=ExtResource("5_1uaoh")]
state = ExtResource("3_hidel")
-[node name="BackInputHandler" parent="." instance=ExtResource("3_effq8")]
-process_input_during = Array[Resource("res://core/systems/state/state.gd")]([ExtResource("3_hidel")])
+[node name="FadeEffect" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("7_dlovl")]
+target = NodePath("../..")
+on_signal = "state_entered"
+fade_out_signal = "state_exited"
+on_signal = "state_entered"
+
+[node name="SlideEffect" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("8_ut6gk")]
+target = NodePath("../../MarginContainer")
+slide_speed = 0.25
+direction = "up"
+on_signal = "state_entered"
+slide_out_signal = "state_exited"
+on_signal = "state_entered"
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 1
diff --git a/core/ui/card_ui/home/cardui_home.gd b/core/ui/card_ui/home/cardui_home.gd
index 2884a53e..41ba2809 100644
--- a/core/ui/card_ui/home/cardui_home.gd
+++ b/core/ui/card_ui/home/cardui_home.gd
@@ -6,7 +6,8 @@ var LaunchManager := preload("res://core/global/launch_manager.tres") as LaunchM
var InstallManager := preload("res://core/global/install_manager.tres")
var BoxArtManager := load("res://core/global/boxart_manager.tres") as BoxArtManager
var LibraryManager := load("res://core/global/library_manager.tres") as LibraryManager
-var state_machine := preload("res://assets/state/state_machines/global_state_machine.tres") as StateMachine
+var state_machine := preload("res://assets/state/state_machines/menu_state_machine.tres") as StateMachine
+var popup_state_machine := preload("res://assets/state/state_machines/popup_state_machine.tres") as StateMachine
var home_state := preload("res://assets/state/states/home.tres") as State
var main_menu_state := preload("res://assets/state/states/main_menu.tres") as State
var launcher_state := preload("res://assets/state/states/game_launcher.tres") as State
@@ -19,13 +20,13 @@ var recent_apps: Array
var tween: Tween
var logger := Log.get_logger("HomeMenu", Log.LEVEL.INFO)
-@onready var container: HBoxContainer = $%CardContainer
-@onready var banner: TextureRect = $%BannerTexture
-@onready var library_banner := $%LibraryBanner
-@onready var player: AnimationPlayer = $%AnimationPlayer
-@onready var scroll_container: ScrollContainer = $%ScrollContainer
-@onready var library_deck: LibraryDeck = $%LibraryDeck
-@onready var end_spacer := $%EndSpacer
+@onready var container := $%CardContainer as HBoxContainer
+@onready var banner := $%BannerTexture as TextureRect
+@onready var library_banner := $%LibraryBanner as Control
+@onready var player := $%AnimationPlayer as AnimationPlayer
+@onready var scroll_container := $%ScrollContainer as ScrollContainer
+@onready var library_deck := $%LibraryDeck as LibraryDeck
+@onready var end_spacer := $%EndSpacer as Control
# Called when the node enters the scene tree for the first time.
@@ -33,6 +34,7 @@ func _ready() -> void:
# Connect to state entered/exited signals
home_state.state_entered.connect(_on_state_entered)
home_state.state_exited.connect(_on_state_exited)
+ home_state.refreshed.connect(_on_state_refreshed)
# Clear any example grid items
for child in container.get_children():
@@ -54,8 +56,6 @@ func _ready() -> void:
# Show the library banner when the library deck is focused
var on_library_focused := func():
- if state_machine.current_state() != home_state:
- return
player.stop()
player.play("fade_in")
library_banner.visible = true
@@ -83,7 +83,7 @@ func refresh() -> void:
refresh.call_deferred()
-func _on_state_entered(from: State) -> void:
+func _on_state_entered(_from: State) -> void:
set_process_input(true)
library_banner.visible = false
_grab_focus()
@@ -93,17 +93,21 @@ func _on_state_exited(_to: State) -> void:
set_process_input(false)
+func _on_state_refreshed() -> void:
+ _grab_focus()
+
+
# Push the main menu state when the back button is pressed
func _input(event: InputEvent) -> void:
- # Only handle back button pressed and when the guide button is not held
- if not event.is_action_pressed("ogui_east") or Input.is_action_pressed("ogui_guide"):
+ # Only handle back button released and when the guide button is not held
+ if not event.is_action_released("ogui_east") or Input.is_action_pressed("ogui_guide"):
return
# Stop the event from propagating
get_viewport().set_input_as_handled()
# Push the main menu state when the back button is pressed
- state_machine.push_state(main_menu_state)
+ popup_state_machine.push_state(main_menu_state)
# When an install is queued, connect signals to show a progress bar on the library
@@ -193,8 +197,6 @@ func _grab_focus() -> void:
# Called when a card is focused
func _on_card_focused(item: LibraryItem, card: Control) -> void:
- if state_machine.current_state() != home_state:
- return
player.stop()
player.play("fade_in")
banner.texture = await BoxArtManager.get_boxart_or_placeholder(item, BoxArtProvider.LAYOUT.BANNER)
diff --git a/core/ui/card_ui/home/cardui_home.tscn b/core/ui/card_ui/home/cardui_home.tscn
index be4aeb2a..eea547d8 100644
--- a/core/ui/card_ui/home/cardui_home.tscn
+++ b/core/ui/card_ui/home/cardui_home.tscn
@@ -1,21 +1,19 @@
-[gd_scene load_steps=20 format=3 uid="uid://o70x5igrlq30"]
+[gd_scene load_steps=18 format=3 uid="uid://o70x5igrlq30"]
[ext_resource type="Script" path="res://core/ui/card_ui/home/cardui_home.gd" id="1_a4a3j"]
-[ext_resource type="PackedScene" uid="uid://orey8uxm7v6v" path="res://core/systems/state/visibility_manager.tscn" id="2_u18ot"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_d1wwx"]
+[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="2_eo81q"]
[ext_resource type="Resource" uid="uid://oaavalv0wcoa" path="res://assets/state/states/home.tres" id="3_x1rvr"]
-[ext_resource type="Resource" uid="uid://bmgs1ngma1523" path="res://assets/state/states/in_game_menu.tres" id="4_d0dnr"]
+[ext_resource type="Resource" uid="uid://cadriyl38ny5y" path="res://assets/state/state_machines/popup_state_machine.tres" id="4_mrwia"]
+[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="4_pbfsf"]
[ext_resource type="Resource" uid="uid://e7bbebwf7guj" path="res://assets/state/states/main_menu.tres" id="5_gbh30"]
-[ext_resource type="Resource" uid="uid://bp807nlks8eq1" path="res://assets/state/states/quick_bar_menu.tres" id="6_jlesr"]
-[ext_resource type="Resource" uid="uid://dja3m1mevv6xw" path="res://assets/state/states/osk.tres" id="7_ao7st"]
-[ext_resource type="Resource" uid="uid://bfoequ6xb7csn" path="res://assets/state/states/quick_bar_button_submenu.tres" id="8_dx6pe"]
-[ext_resource type="Resource" uid="uid://bw0mtk7sso8m2" path="res://assets/state/states/power_menu.tres" id="9_kvuqp"]
[ext_resource type="Texture2D" uid="uid://d1mksukdkqorr" path="res://assets/images/placeholder-grid-banner.png" id="10_mmfgs"]
[ext_resource type="PackedScene" uid="uid://rosd00fxjrs8" path="res://core/ui/components/library_banner.tscn" id="11_16gcd"]
[ext_resource type="PackedScene" uid="uid://b0cyl6fdqxevn" path="res://core/systems/input/scroller_joystick.tscn" id="12_h5dxg"]
[ext_resource type="PackedScene" uid="uid://bkhrcemal7uxo" path="res://core/ui/components/card.tscn" id="12_m30ge"]
[ext_resource type="PackedScene" uid="uid://crsu0vpicq0vh" path="res://core/ui/components/library_deck.tscn" id="13_rxwf5"]
+[ext_resource type="Resource" uid="uid://bcr6c0281lb5b" path="res://assets/state/state_machines/menu_state_machine.tres" id="14_8pw3l"]
[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="14_wdgux"]
-[ext_resource type="Resource" uid="uid://cr544el0cqjlm" path="res://assets/state/state_machines/global_state_machine.tres" id="15_484n7"]
[ext_resource type="Resource" uid="uid://boq501bigx8kl" path="res://assets/state/states/library.tres" id="16_6odlo"]
[sub_resource type="Animation" id="Animation_638a6"]
@@ -48,9 +46,23 @@ grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_a4a3j")
-[node name="VisibilityManager" parent="." instance=ExtResource("2_u18ot")]
+[node name="InputWatcher" parent="." instance=ExtResource("2_d1wwx")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("14_wdgux")]
+state_machine = ExtResource("4_mrwia")
+state = ExtResource("5_gbh30")
+on_signal = "input_released"
+
+[node name="StateWatcher" parent="." instance=ExtResource("2_eo81q")]
state = ExtResource("3_x1rvr")
-visible_during = Array[Resource]([ExtResource("4_d0dnr"), ExtResource("5_gbh30"), ExtResource("6_jlesr"), ExtResource("7_ao7st"), ExtResource("8_dx6pe"), ExtResource("9_kvuqp")])
+
+[node name="FadeEffect" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("4_pbfsf")]
+target = NodePath("../..")
+on_signal = "state_entered"
+fade_out_signal = "state_exited"
+on_signal = "state_entered"
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 1
@@ -137,7 +149,7 @@ unique_name_in_owner = true
layout_mode = 2
[node name="StateUpdater" parent="VBoxContainer/PanelContainer/MarginContainer/ScrollContainer/CardContainer/LibraryDeck" instance=ExtResource("14_wdgux")]
-state_machine = ExtResource("15_484n7")
+state_machine = ExtResource("14_8pw3l")
state = ExtResource("16_6odlo")
on_signal = "button_up"
diff --git a/core/ui/card_ui/launch/game_launch_menu.tscn b/core/ui/card_ui/launch/game_launch_menu.tscn
index 44c01073..430a0b74 100644
--- a/core/ui/card_ui/launch/game_launch_menu.tscn
+++ b/core/ui/card_ui/launch/game_launch_menu.tscn
@@ -1,23 +1,18 @@
-[gd_scene load_steps=25 format=3 uid="uid://bcdk1lj6enq3l"]
+[gd_scene load_steps=20 format=3 uid="uid://bcdk1lj6enq3l"]
[ext_resource type="Script" path="res://core/ui/card_ui/launch/game_launch_menu.gd" id="1_u3ehs"]
-[ext_resource type="PackedScene" uid="uid://orey8uxm7v6v" path="res://core/systems/state/visibility_manager.tscn" id="2_135m3"]
[ext_resource type="Texture2D" uid="uid://d1mksukdkqorr" path="res://assets/images/placeholder-grid-banner.png" id="2_oae7b"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_slsfk"]
[ext_resource type="PackedScene" uid="uid://2tdbi1v6qb6h" path="res://core/ui/components/loading02.tscn" id="3_dp3a3"]
[ext_resource type="Resource" uid="uid://dav2b3n384cso" path="res://assets/state/states/game_launcher.tres" id="3_w7th1"]
-[ext_resource type="Resource" uid="uid://e7bbebwf7guj" path="res://assets/state/states/main_menu.tres" id="4_le731"]
+[ext_resource type="Resource" uid="uid://bcr6c0281lb5b" path="res://assets/state/state_machines/menu_state_machine.tres" id="4_aqvfw"]
+[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_4ja2v"]
[ext_resource type="Texture2D" uid="uid://bec6fhiswf6ve" path="res://assets/ui/icons/gamepad-bold.svg" id="5_ewj68"]
-[ext_resource type="Resource" uid="uid://bmgs1ngma1523" path="res://assets/state/states/in_game_menu.tres" id="5_tdidy"]
-[ext_resource type="Resource" uid="uid://bp807nlks8eq1" path="res://assets/state/states/quick_bar_menu.tres" id="6_gnrnf"]
-[ext_resource type="Resource" uid="uid://dja3m1mevv6xw" path="res://assets/state/states/osk.tres" id="7_qt0bt"]
-[ext_resource type="Resource" uid="uid://bw0mtk7sso8m2" path="res://assets/state/states/power_menu.tres" id="8_3e46i"]
-[ext_resource type="PackedScene" uid="uid://ccd4sw84h1qbc" path="res://core/systems/input/back_input_handler.tscn" id="9_hnl5c"]
-[ext_resource type="PackedScene" uid="uid://bo077a5mwi7xl" path="res://core/ui/components/transition_fade_in.tscn" id="9_k6q8g"]
+[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="7_ch3qu"]
[ext_resource type="Texture2D" uid="uid://d2ipfga47yjju" path="res://assets/images/empty-grid-logo.png" id="12_01uv6"]
[ext_resource type="PackedScene" uid="uid://c71ayw7pcw6u6" path="res://core/ui/components/card_button.tscn" id="14_3h1st"]
[ext_resource type="PackedScene" uid="uid://cr83fmlociwko" path="res://core/ui/components/card_icon_button.tscn" id="15_f3ktw"]
[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="15_lat8h"]
-[ext_resource type="Resource" uid="uid://cr544el0cqjlm" path="res://assets/state/state_machines/global_state_machine.tres" id="16_m8g5y"]
[ext_resource type="Resource" uid="uid://cx8u1y5j7vyss" path="res://assets/state/states/gamepad_settings.tres" id="17_7ydn0"]
[ext_resource type="Resource" uid="uid://3vw3bk76d88w" path="res://assets/state/states/game_settings.tres" id="19_b21vy"]
[ext_resource type="Texture2D" uid="uid://dj1ohb74chydb" path="res://assets/ui/icons/round-delete-forever.svg" id="21_agq5k"]
@@ -58,15 +53,23 @@ grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_u3ehs")
-[node name="VisibilityManager" parent="." instance=ExtResource("2_135m3")]
-state = ExtResource("3_w7th1")
-visible_during = Array[Resource]([ExtResource("4_le731"), ExtResource("5_tdidy"), ExtResource("6_gnrnf"), ExtResource("7_qt0bt"), ExtResource("8_3e46i")])
+[node name="InputWatcher" parent="." instance=ExtResource("2_slsfk")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("15_lat8h")]
+state_machine = ExtResource("4_aqvfw")
+action = 2
+on_signal = "input_released"
-[node name="TransitionFadeIn" parent="VisibilityManager" instance=ExtResource("9_k6q8g")]
-root_node = NodePath("../..")
+[node name="StateWatcher" parent="." instance=ExtResource("5_4ja2v")]
+state = ExtResource("3_w7th1")
-[node name="BackInputHandler" parent="." instance=ExtResource("9_hnl5c")]
-process_input_during = Array[Resource("res://core/systems/state/state.gd")]([ExtResource("3_w7th1")])
+[node name="FadeEffect" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("7_ch3qu")]
+target = NodePath("../..")
+on_signal = "state_entered"
+fade_out_signal = "state_exited"
+on_signal = "state_entered"
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 1
@@ -162,11 +165,12 @@ layout_mode = 2
texture = ExtResource("5_ewj68")
[node name="StateUpdater" parent="CenterContainer/HBoxContainer/ExtraMenu/MarginContainer/HBoxContainer/GamepadButton" instance=ExtResource("15_lat8h")]
-state_machine = ExtResource("16_m8g5y")
+state_machine = ExtResource("4_aqvfw")
state = ExtResource("17_7ydn0")
on_signal = "player_button_up"
[node name="VSeparator" type="VSeparator" parent="CenterContainer/HBoxContainer/ExtraMenu/MarginContainer/HBoxContainer"]
+visible = false
custom_minimum_size = Vector2(0, 24)
layout_mode = 2
size_flags_vertical = 4
@@ -177,7 +181,7 @@ custom_minimum_size = Vector2(28, 28)
layout_mode = 2
[node name="StateUpdater" parent="CenterContainer/HBoxContainer/ExtraMenu/MarginContainer/HBoxContainer/SettingsButton" instance=ExtResource("15_lat8h")]
-state_machine = ExtResource("16_m8g5y")
+state_machine = ExtResource("4_aqvfw")
state = ExtResource("19_b21vy")
on_signal = "button_up"
@@ -190,6 +194,7 @@ layout_mode = 2
layout_mode = 2
[node name="VSeparator" type="VSeparator" parent="CenterContainer/HBoxContainer/ExtraMenu/MarginContainer/HBoxContainer/DeleteContainer/HBoxContainer"]
+visible = false
custom_minimum_size = Vector2(0, 24)
layout_mode = 2
size_flags_vertical = 4
diff --git a/core/ui/card_ui/launch/game_settings.tscn b/core/ui/card_ui/launch/game_settings.tscn
index 97e6319d..1e03f2d0 100644
--- a/core/ui/card_ui/launch/game_settings.tscn
+++ b/core/ui/card_ui/launch/game_settings.tscn
@@ -1,14 +1,12 @@
-[gd_scene load_steps=26 format=3 uid="uid://58qlqqbh58im"]
+[gd_scene load_steps=24 format=3 uid="uid://58qlqqbh58im"]
[ext_resource type="Script" path="res://core/ui/card_ui/launch/game_settings.gd" id="1_vrr6s"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_1eqgx"]
[ext_resource type="PackedScene" uid="uid://orey8uxm7v6v" path="res://core/systems/state/visibility_manager.tscn" id="2_pga6u"]
[ext_resource type="Resource" uid="uid://3vw3bk76d88w" path="res://assets/state/states/game_settings.tres" id="3_s80bx"]
-[ext_resource type="Resource" uid="uid://bmgs1ngma1523" path="res://assets/state/states/in_game_menu.tres" id="4_qjhtj"]
-[ext_resource type="Resource" uid="uid://dja3m1mevv6xw" path="res://assets/state/states/osk.tres" id="5_t4x4x"]
-[ext_resource type="Resource" uid="uid://e7bbebwf7guj" path="res://assets/state/states/main_menu.tres" id="6_c6jr8"]
-[ext_resource type="Resource" uid="uid://bp807nlks8eq1" path="res://assets/state/states/quick_bar_menu.tres" id="7_7cc3x"]
-[ext_resource type="Resource" uid="uid://bw0mtk7sso8m2" path="res://assets/state/states/power_menu.tres" id="8_j5oy4"]
-[ext_resource type="PackedScene" uid="uid://ccd4sw84h1qbc" path="res://core/systems/input/back_input_handler.tscn" id="9_e1xsr"]
+[ext_resource type="Resource" uid="uid://bcr6c0281lb5b" path="res://assets/state/state_machines/menu_state_machine.tres" id="4_5bv80"]
+[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_0v30m"]
+[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="7_5qmye"]
[ext_resource type="PackedScene" uid="uid://d0u3rsa5qpj57" path="res://core/ui/components/subsection_label.tscn" id="11_u6frn"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="12_86g2h"]
[ext_resource type="Resource" uid="uid://cc6i4i264dmqd" path="res://core/ui/card_ui/launch/game_settings_focus.tres" id="12_e0uun"]
@@ -35,12 +33,23 @@ grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_vrr6s")
-[node name="VisibilityManager" parent="." instance=ExtResource("2_pga6u")]
+[node name="InputWatcher" parent="." instance=ExtResource("2_1eqgx")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("15_lmmqq")]
+state_machine = ExtResource("4_5bv80")
+action = 2
+on_signal = "input_released"
+
+[node name="StateWatcher" parent="." instance=ExtResource("5_0v30m")]
state = ExtResource("3_s80bx")
-visible_during = Array[Resource]([ExtResource("4_qjhtj"), ExtResource("5_t4x4x"), ExtResource("6_c6jr8"), ExtResource("7_7cc3x"), ExtResource("8_j5oy4")])
-[node name="BackInputHandler" parent="." instance=ExtResource("9_e1xsr")]
-process_input_during = Array[Resource("res://core/systems/state/state.gd")]([ExtResource("3_s80bx")])
+[node name="FadeEffect" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("7_5qmye")]
+target = NodePath("../..")
+on_signal = "state_entered"
+fade_out_signal = "state_exited"
+on_signal = "state_entered"
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 1
diff --git a/core/ui/card_ui/library/library_menu.gd b/core/ui/card_ui/library/library_menu.gd
index 8021998d..b032739c 100644
--- a/core/ui/card_ui/library/library_menu.gd
+++ b/core/ui/card_ui/library/library_menu.gd
@@ -5,7 +5,7 @@ signal refresh_completed
var settings_manager := load("res://core/global/settings_manager.tres") as SettingsManager
var library_manager := load("res://core/global/library_manager.tres") as LibraryManager
var install_manager := load("res://core/global/install_manager.tres") as InstallManager
-var state_machine := load("res://assets/state/state_machines/global_state_machine.tres") as StateMachine
+var menu_state_machine := load("res://assets/state/state_machines/menu_state_machine.tres") as StateMachine
var library_state := load("res://assets/state/states/library.tres") as State
var launcher_state := load("res://assets/state/states/game_launcher.tres") as State
var osk_state := load("res://assets/state/states/osk.tres") as State
@@ -35,6 +35,9 @@ func _ready() -> void:
# Connect to state entered signals
library_state.state_entered.connect(_on_state_entered)
+ var on_refresh := func():
+ _on_state_entered(null)
+ library_state.refreshed.connect(on_refresh)
# Listen for tab container changes
tabs_state.tab_changed.connect(_on_tab_container_tab_changed)
@@ -87,8 +90,6 @@ func _on_state_entered(_from: State):
# Handle searches
func _on_search(text: String):
- if not state_machine.current_state() in [library_state, osk_state]:
- return
text = text.to_lower()
# If the text is empty, set all items to visible
@@ -157,7 +158,7 @@ func _build_card(item: LibraryItem) -> GameCard:
# Listen for button presses and pass the library item with the state
var on_button_up := func():
launcher_state.data = {"item": item}
- state_machine.push_state(launcher_state)
+ menu_state_machine.push_state(launcher_state)
card.button_up.connect(on_button_up)
return card
diff --git a/core/ui/card_ui/library/library_menu.tscn b/core/ui/card_ui/library/library_menu.tscn
index 8063421d..71934b98 100644
--- a/core/ui/card_ui/library/library_menu.tscn
+++ b/core/ui/card_ui/library/library_menu.tscn
@@ -1,15 +1,13 @@
-[gd_scene load_steps=14 format=3 uid="uid://uqkwpeq7f1o"]
+[gd_scene load_steps=12 format=3 uid="uid://uqkwpeq7f1o"]
[ext_resource type="Script" path="res://core/ui/card_ui/library/library_menu.gd" id="1_7invr"]
[ext_resource type="Resource" uid="uid://cjda3nse6s3n1" path="res://core/ui/card_ui/library/library_tabs_state.tres" id="2_4hmwr"]
-[ext_resource type="PackedScene" uid="uid://orey8uxm7v6v" path="res://core/systems/state/visibility_manager.tscn" id="3_2qh2a"]
+[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="3_7wtey"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="3_l8ff2"]
[ext_resource type="Resource" uid="uid://boq501bigx8kl" path="res://assets/state/states/library.tres" id="4_dffbv"]
-[ext_resource type="Resource" uid="uid://e7bbebwf7guj" path="res://assets/state/states/main_menu.tres" id="5_i24wn"]
-[ext_resource type="Resource" uid="uid://bmgs1ngma1523" path="res://assets/state/states/in_game_menu.tres" id="6_gwkmd"]
-[ext_resource type="Resource" uid="uid://bp807nlks8eq1" path="res://assets/state/states/quick_bar_menu.tres" id="7_oigb4"]
-[ext_resource type="Resource" uid="uid://dja3m1mevv6xw" path="res://assets/state/states/osk.tres" id="8_s5vy4"]
-[ext_resource type="Resource" uid="uid://bw0mtk7sso8m2" path="res://assets/state/states/power_menu.tres" id="9_swp14"]
-[ext_resource type="PackedScene" uid="uid://ccd4sw84h1qbc" path="res://core/systems/input/back_input_handler.tscn" id="10_brww5"]
+[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="4_qje1c"]
+[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="5_ctjfn"]
+[ext_resource type="Resource" uid="uid://bcr6c0281lb5b" path="res://assets/state/state_machines/menu_state_machine.tres" id="5_nj5st"]
[ext_resource type="PackedScene" uid="uid://b0cyl6fdqxevn" path="res://core/systems/input/scroller_joystick.tscn" id="11_27umd"]
[ext_resource type="PackedScene" uid="uid://bkhrcemal7uxo" path="res://core/ui/components/card.tscn" id="11_bj8cj"]
@@ -26,12 +24,23 @@ grow_vertical = 2
script = ExtResource("1_7invr")
tabs_state = ExtResource("2_4hmwr")
-[node name="VisibilityManager" parent="." instance=ExtResource("3_2qh2a")]
+[node name="InputWatcher" parent="." instance=ExtResource("3_l8ff2")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("4_qje1c")]
+state_machine = ExtResource("5_nj5st")
+action = 2
+on_signal = "input_released"
+
+[node name="StateWatcher" parent="." instance=ExtResource("3_7wtey")]
state = ExtResource("4_dffbv")
-visible_during = Array[Resource]([ExtResource("5_i24wn"), ExtResource("6_gwkmd"), ExtResource("7_oigb4"), ExtResource("8_s5vy4"), ExtResource("9_swp14")])
-[node name="BackInputHandler" parent="." instance=ExtResource("10_brww5")]
-process_input_during = Array[Resource("res://core/systems/state/state.gd")]([ExtResource("4_dffbv")])
+[node name="FadeEffect" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("5_ctjfn")]
+target = NodePath("../..")
+on_signal = "state_entered"
+fade_out_signal = "state_exited"
+on_signal = "state_entered"
[node name="TabContainer" type="TabContainer" parent="."]
unique_name_in_owner = true
diff --git a/core/ui/card_ui/main-menu/main_menu.tscn b/core/ui/card_ui/main-menu/main_menu.tscn
index adddd5f6..62ca824c 100644
--- a/core/ui/card_ui/main-menu/main_menu.tscn
+++ b/core/ui/card_ui/main-menu/main_menu.tscn
@@ -1,14 +1,14 @@
-[gd_scene load_steps=20 format=3 uid="uid://jfacx7uys32r"]
+[gd_scene load_steps=23 format=3 uid="uid://jfacx7uys32r"]
[ext_resource type="Script" path="res://core/ui/card_ui/main-menu/main_menu.gd" id="1_3looj"]
[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="2_5xlnh"]
-[ext_resource type="PackedScene" uid="uid://ccd4sw84h1qbc" path="res://core/systems/input/back_input_handler.tscn" id="2_jmie1"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_bcynd"]
[ext_resource type="Resource" uid="uid://e7bbebwf7guj" path="res://assets/state/states/main_menu.tres" id="3_3xh32"]
[ext_resource type="PackedScene" uid="uid://ekhjpmat02f8" path="res://core/systems/effects/slide_effect.tscn" id="4_hfk5i"]
-[ext_resource type="Resource" uid="uid://bmgs1ngma1523" path="res://assets/state/states/in_game_menu.tres" id="4_maija"]
+[ext_resource type="Resource" uid="uid://cadriyl38ny5y" path="res://assets/state/state_machines/popup_state_machine.tres" id="4_w4cpu"]
[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="5_88bts"]
+[ext_resource type="PackedScene" uid="uid://uljtdvmuol3l" path="res://core/systems/input/focus_group_setter.tscn" id="6_r2ip5"]
[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="6_ris1l"]
-[ext_resource type="Resource" uid="uid://cr544el0cqjlm" path="res://assets/state/state_machines/global_state_machine.tres" id="7_n0655"]
[ext_resource type="Resource" uid="uid://boq501bigx8kl" path="res://assets/state/states/library.tres" id="8_iv515"]
[ext_resource type="PackedScene" uid="uid://4t4jt26o2fbr" path="res://core/ui/card_ui/navigation/system_container.tscn" id="8_p8fdd"]
[ext_resource type="Resource" uid="uid://ba34axjjpixwc" path="res://assets/state/states/store.tres" id="9_2jho2"]
@@ -17,8 +17,11 @@
[ext_resource type="Resource" uid="uid://bytra6gw0dr4c" path="res://core/ui/card_ui/main-menu/main_menu_focus.tres" id="10_v2rc1"]
[ext_resource type="Resource" uid="uid://bw0mtk7sso8m2" path="res://assets/state/states/power_menu.tres" id="11_e52bm"]
[ext_resource type="PackedScene" uid="uid://c71ayw7pcw6u6" path="res://core/ui/components/card_button.tscn" id="11_obt01"]
+[ext_resource type="Resource" uid="uid://bcr6c0281lb5b" path="res://assets/state/state_machines/menu_state_machine.tres" id="14_8hmym"]
[ext_resource type="Resource" uid="uid://oaavalv0wcoa" path="res://assets/state/states/home.tres" id="14_lxab3"]
[ext_resource type="PackedScene" uid="uid://b0cyl6fdqxevn" path="res://core/systems/input/scroller_joystick.tscn" id="19_b6ano"]
+[ext_resource type="Resource" uid="uid://cr544el0cqjlm" path="res://assets/state/state_machines/global_state_machine.tres" id="19_vkv5c"]
+[ext_resource type="Resource" uid="uid://cv3vduo0ojk1u" path="res://assets/state/states/menu.tres" id="20_vym73"]
[node name="MainMenu" type="Control"]
z_index = 20
@@ -28,6 +31,15 @@ anchor_bottom = 1.0
grow_vertical = 2
script = ExtResource("1_3looj")
+[node name="InputWatcher" parent="." instance=ExtResource("2_bcynd")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("6_ris1l")]
+state_machine = ExtResource("4_w4cpu")
+action = 2
+on_signal = "input_released"
+
[node name="StateWatcher" parent="." instance=ExtResource("2_5xlnh")]
state = ExtResource("3_3xh32")
@@ -46,8 +58,9 @@ on_signal = "state_entered"
slide_out_signal = "state_exited"
on_signal = "state_entered"
-[node name="BackInputHandler" parent="." instance=ExtResource("2_jmie1")]
-process_input_during = Array[Resource("res://core/systems/state/state.gd")]([ExtResource("3_3xh32"), ExtResource("4_maija")])
+[node name="FocusGroupSetter" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("6_r2ip5")]
+target = NodePath("../../PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer/FocusGroup")
+on_signal = "state_entered"
[node name="PanelContainer" type="PanelContainer" parent="."]
custom_minimum_size = Vector2(280, 0)
@@ -105,9 +118,19 @@ text = "Home"
click_focuses = false
[node name="StateUpdater" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer/HomeButton" instance=ExtResource("6_ris1l")]
-state_machine = ExtResource("7_n0655")
+state_machine = ExtResource("14_8hmym")
state = ExtResource("14_lxab3")
-action = 3
+action = 4
+on_signal = "button_up"
+
+[node name="StateUpdater2" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer/HomeButton" instance=ExtResource("6_ris1l")]
+state_machine = ExtResource("19_vkv5c")
+state = ExtResource("20_vym73")
+on_signal = "button_up"
+
+[node name="ClearOverlay" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer/HomeButton" instance=ExtResource("6_ris1l")]
+state_machine = ExtResource("4_w4cpu")
+action = 6
on_signal = "button_up"
[node name="LibraryButton" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer" instance=ExtResource("11_obt01")]
@@ -116,9 +139,18 @@ text = "Library"
click_focuses = false
[node name="StateUpdater" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer/LibraryButton" instance=ExtResource("6_ris1l")]
-state_machine = ExtResource("7_n0655")
+state_machine = ExtResource("14_8hmym")
state = ExtResource("8_iv515")
-action = 2
+on_signal = "button_up"
+
+[node name="StateUpdater2" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer/LibraryButton" instance=ExtResource("6_ris1l")]
+state_machine = ExtResource("19_vkv5c")
+state = ExtResource("20_vym73")
+on_signal = "button_up"
+
+[node name="ClearOverlay" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer/LibraryButton" instance=ExtResource("6_ris1l")]
+state_machine = ExtResource("4_w4cpu")
+action = 6
on_signal = "button_up"
[node name="StoreButton" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer" instance=ExtResource("11_obt01")]
@@ -128,9 +160,18 @@ text = "Store"
click_focuses = false
[node name="StateUpdater" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer/StoreButton" instance=ExtResource("6_ris1l")]
-state_machine = ExtResource("7_n0655")
+state_machine = ExtResource("14_8hmym")
state = ExtResource("9_2jho2")
-action = 2
+on_signal = "button_up"
+
+[node name="StateUpdater2" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer/StoreButton" instance=ExtResource("6_ris1l")]
+state_machine = ExtResource("19_vkv5c")
+state = ExtResource("20_vym73")
+on_signal = "button_up"
+
+[node name="ClearOverlay" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer/StoreButton" instance=ExtResource("6_ris1l")]
+state_machine = ExtResource("4_w4cpu")
+action = 6
on_signal = "button_up"
[node name="SettingsButton" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer" instance=ExtResource("11_obt01")]
@@ -139,9 +180,18 @@ text = "Settings"
click_focuses = false
[node name="StateUpdater" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer/SettingsButton" instance=ExtResource("6_ris1l")]
-state_machine = ExtResource("7_n0655")
+state_machine = ExtResource("14_8hmym")
state = ExtResource("10_irj46")
-action = 2
+on_signal = "button_up"
+
+[node name="StateUpdater2" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer/SettingsButton" instance=ExtResource("6_ris1l")]
+state_machine = ExtResource("19_vkv5c")
+state = ExtResource("20_vym73")
+on_signal = "button_up"
+
+[node name="ClearOverlay" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer/SettingsButton" instance=ExtResource("6_ris1l")]
+state_machine = ExtResource("4_w4cpu")
+action = 6
on_signal = "button_up"
[node name="PowerButton" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer" instance=ExtResource("11_obt01")]
@@ -150,7 +200,7 @@ text = "Power"
click_focuses = false
[node name="StateUpdater" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/ButtonContainer/PowerButton" instance=ExtResource("6_ris1l")]
-state_machine = ExtResource("7_n0655")
+state_machine = ExtResource("4_w4cpu")
state = ExtResource("11_e52bm")
on_signal = "button_up"
diff --git a/core/ui/card_ui/navigation/running_game_card.gd b/core/ui/card_ui/navigation/running_game_card.gd
index 4400ada4..e5302252 100644
--- a/core/ui/card_ui/navigation/running_game_card.gd
+++ b/core/ui/card_ui/navigation/running_game_card.gd
@@ -16,17 +16,26 @@ var launch_manager := load("res://core/global/launch_manager.tres") as LaunchMan
var boxart_manager := load("res://core/global/boxart_manager.tres") as BoxArtManager
var library_manager := load("res://core/global/library_manager.tres") as LibraryManager
var state_machine := load("res://assets/state/state_machines/global_state_machine.tres") as StateMachine
+var menu_state_machine := load("res://assets/state/state_machines/menu_state_machine.tres") as StateMachine
+var popup_state_machine := load("res://assets/state/state_machines/popup_state_machine.tres") as StateMachine
var in_game_state := load("res://assets/state/states/in_game.tres") as State
var button_scene := load("res://core/ui/components/card_button.tscn") as PackedScene
@export_category("Card")
-@export var is_toggled := false
+@export var is_toggled := false:
+ set(v):
+ is_toggled = v
+ if is_toggled:
+ toggled_on.emit()
+ else:
+ toggled_off.emit()
+ toggled.emit(is_toggled)
@onready var content_container := $%ContentContainer
@onready var game_logo := $%GameLogo
@onready var game_label := $%GameLabel
@onready var resume_button := $%ResumeButton as CardButton
-@onready var suspend_button := $%SuspendButton as CardButton
+@onready var pause_button := $%PauseButton as CardButton
@onready var exit_button := $%ExitButton as CardButton
@onready var highlight_rect := $%HighlightTextureRect
@onready var focus_group := $%FocusGroup as FocusGroup
@@ -39,11 +48,18 @@ var logger := Log.get_logger("RunningGameCard", Log.LEVEL.INFO)
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
+ if Engine.is_editor_hint():
+ return
+
focus_entered.connect(_on_focus)
focus_exited.connect(_on_unfocus)
- pressed.connect(_on_pressed)
+ button_up.connect(_on_button_up)
theme_changed.connect(_on_theme_changed)
- _on_theme_changed()
+
+ # Find the parent theme and update if required
+ var effective_theme := ThemeUtils.get_effective_theme(self)
+ if effective_theme:
+ _on_theme_changed()
# Auto-close when visibility is lost
var on_visibility_changed := func():
@@ -55,19 +71,20 @@ func _ready() -> void:
# Connect sub-buttons
var on_resume_game := func():
state_machine.set_state([in_game_state])
+ menu_state_machine.clear_states()
+ popup_state_machine.clear_states()
resume_button.pressed.connect(on_resume_game)
var on_exit_game := func():
- # TODO: Handle this better
+ # TODO: Handle "this" better?
launch_manager.stop(running_app)
- state_machine.pop_state()
exit_button.pressed.connect(on_exit_game)
var on_suspend := func():
running_app.suspend(not running_app.is_suspended)
if running_app.is_suspended:
- suspend_button.text = "Continue"
+ pause_button.text = "Unpause"
else:
- suspend_button.text = "Suspend"
- suspend_button.pressed.connect(on_suspend)
+ pause_button.text = "Pause"
+ pause_button.pressed.connect(on_suspend)
func _on_theme_changed() -> void:
@@ -134,7 +151,7 @@ func _on_focus() -> void:
# the user has focused outside the card, and we should shrink to hide the
# content
if is_toggled:
- _on_pressed()
+ _on_button_up()
func _on_unfocus() -> void:
@@ -148,7 +165,7 @@ func _on_unfocus() -> void:
return
-func _on_pressed() -> void:
+func _on_button_up() -> void:
is_toggled = !is_toggled
if is_toggled:
toggled_on.emit()
@@ -159,9 +176,34 @@ func _on_pressed() -> void:
func _gui_input(event: InputEvent) -> void:
+ var is_valid := [event is InputEventAction, event is InputEventKey]
+ if not true in is_valid:
+ return
if event.is_action("ui_accept"):
if event.is_pressed():
button_down.emit()
pressed.emit()
else:
button_up.emit()
+
+
+func _input(event: InputEvent) -> void:
+ if not is_toggled:
+ return
+ if not event.is_action("ogui_east"):
+ return
+ if not event.is_released():
+ return
+
+ # Only process input if a child node has focus
+ #var focus_owner := get_viewport().gui_get_focus_owner()
+ #if not self.is_ancestor_of(focus_owner):
+ # return
+
+ # Handle back input
+ is_toggled = false
+
+ # Stop the event from propagating
+ #logger.debug("Consuming input event '{action}' for node {n}".format({"action": action, "n": str(self)}))
+ get_viewport().set_input_as_handled()
+ self.grab_focus()
diff --git a/core/ui/card_ui/navigation/running_game_card.tscn b/core/ui/card_ui/navigation/running_game_card.tscn
index da72739b..279c8c8a 100644
--- a/core/ui/card_ui/navigation/running_game_card.tscn
+++ b/core/ui/card_ui/navigation/running_game_card.tscn
@@ -1,4 +1,4 @@
-[gd_scene load_steps=17 format=3 uid="uid://dlouq0b0bnm41"]
+[gd_scene load_steps=19 format=3 uid="uid://dlouq0b0bnm41"]
[ext_resource type="Texture2D" uid="uid://d2ipfga47yjju" path="res://assets/images/empty-grid-logo.png" id="1_4m4go"]
[ext_resource type="Script" path="res://core/ui/card_ui/navigation/running_game_card.gd" id="1_vgpef"]
@@ -10,12 +10,14 @@
[ext_resource type="PackedScene" uid="uid://hbgypx7p3gyw" path="res://core/systems/effects/grower_effect.tscn" id="4_f5kjd"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="5_smmwr"]
[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="6_s452c"]
-[ext_resource type="Resource" uid="uid://bytra6gw0dr4c" path="res://core/ui/card_ui/main-menu/main_menu_focus.tres" id="6_xmlue"]
-[ext_resource type="Resource" uid="uid://cr544el0cqjlm" path="res://assets/state/state_machines/global_state_machine.tres" id="7_yrgt0"]
[ext_resource type="PackedScene" uid="uid://c71ayw7pcw6u6" path="res://core/ui/components/card_button.tscn" id="8_ixs6g"]
[ext_resource type="Resource" uid="uid://cx8u1y5j7vyss" path="res://assets/state/states/gamepad_settings.tres" id="8_nyd1y"]
+[ext_resource type="Resource" uid="uid://bcr6c0281lb5b" path="res://assets/state/state_machines/menu_state_machine.tres" id="13_aslkr"]
+[ext_resource type="Resource" uid="uid://cadriyl38ny5y" path="res://assets/state/state_machines/popup_state_machine.tres" id="15_70rxc"]
+[ext_resource type="Resource" uid="uid://cr544el0cqjlm" path="res://assets/state/state_machines/global_state_machine.tres" id="15_p4kr0"]
+[ext_resource type="Resource" uid="uid://cv3vduo0ojk1u" path="res://assets/state/states/menu.tres" id="16_vmedb"]
-[sub_resource type="Image" id="Image_blvbp"]
+[sub_resource type="Image" id="Image_2yqt6"]
data = {
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
"format": "RGBA8",
@@ -25,7 +27,7 @@ data = {
}
[sub_resource type="ImageTexture" id="ImageTexture_osglk"]
-image = SubResource("Image_blvbp")
+image = SubResource("Image_2yqt6")
[node name="RunningGameCard" type="PanelContainer"]
anchors_preset = 10
@@ -143,7 +145,6 @@ size_flags_vertical = 3
[node name="FocusGroup" parent="MarginContainer/VBoxContainer/ContentContainer" node_paths=PackedStringArray("current_focus") instance=ExtResource("5_smmwr")]
unique_name_in_owner = true
current_focus = NodePath("../ResumeButton")
-focus_stack = ExtResource("6_xmlue")
[node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer/ContentContainer"]
layout_mode = 2
@@ -151,23 +152,38 @@ layout_mode = 2
[node name="ResumeButton" parent="MarginContainer/VBoxContainer/ContentContainer" instance=ExtResource("8_ixs6g")]
unique_name_in_owner = true
layout_mode = 2
-text = "Resume"
+text = "Return"
-[node name="SuspendButton" parent="MarginContainer/VBoxContainer/ContentContainer" instance=ExtResource("8_ixs6g")]
+[node name="PauseButton" parent="MarginContainer/VBoxContainer/ContentContainer" instance=ExtResource("8_ixs6g")]
unique_name_in_owner = true
layout_mode = 2
-text = "Suspend"
+text = "Pause"
[node name="GamepadButton" parent="MarginContainer/VBoxContainer/ContentContainer" instance=ExtResource("8_ixs6g")]
layout_mode = 2
text = "Gamepad"
[node name="StateUpdater" parent="MarginContainer/VBoxContainer/ContentContainer/GamepadButton" instance=ExtResource("6_s452c")]
-state_machine = ExtResource("7_yrgt0")
+state_machine = ExtResource("13_aslkr")
state = ExtResource("8_nyd1y")
on_signal = "player_button_up"
+[node name="StateUpdater2" parent="MarginContainer/VBoxContainer/ContentContainer/GamepadButton" instance=ExtResource("6_s452c")]
+state_machine = ExtResource("15_p4kr0")
+state = ExtResource("16_vmedb")
+on_signal = "button_up"
+
+[node name="ClearOverlay" parent="MarginContainer/VBoxContainer/ContentContainer/GamepadButton" instance=ExtResource("6_s452c")]
+state_machine = ExtResource("15_70rxc")
+action = 6
+on_signal = "button_up"
+
[node name="ExitButton" parent="MarginContainer/VBoxContainer/ContentContainer" instance=ExtResource("8_ixs6g")]
unique_name_in_owner = true
layout_mode = 2
text = "Exit"
+
+[node name="ClearOverlay" parent="MarginContainer/VBoxContainer/ContentContainer/ExitButton" instance=ExtResource("6_s452c")]
+state_machine = ExtResource("15_70rxc")
+action = 6
+on_signal = "button_up"
diff --git a/core/ui/card_ui/navigation/search_bar_menu.tscn b/core/ui/card_ui/navigation/search_bar_menu.tscn
index 8999d533..433e6f4d 100644
--- a/core/ui/card_ui/navigation/search_bar_menu.tscn
+++ b/core/ui/card_ui/navigation/search_bar_menu.tscn
@@ -1,11 +1,11 @@
[gd_scene load_steps=12 format=3 uid="uid://d4bmkauhrlhq0"]
-[ext_resource type="Script" path="res://core/systems/state/visibility_manager.gd" id="1_3s0wp"]
[ext_resource type="Script" path="res://core/ui/card_ui/navigation/search_bar_menu.gd" id="1_518qs"]
[ext_resource type="PackedScene" uid="uid://bx0fnuxd8mm51" path="res://core/ui/components/search_bar.tscn" id="2_gqstr"]
[ext_resource type="Resource" uid="uid://oaavalv0wcoa" path="res://assets/state/states/home.tres" id="2_kr4pi"]
+[ext_resource type="PackedScene" uid="uid://bfiia7vnbfw3s" path="res://core/systems/state/states_watcher.tscn" id="2_nb2d7"]
[ext_resource type="Resource" uid="uid://boq501bigx8kl" path="res://assets/state/states/library.tres" id="3_sj5or"]
-[ext_resource type="PackedScene" uid="uid://bo077a5mwi7xl" path="res://core/ui/components/transition_fade_in.tscn" id="4_r83xd"]
+[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="4_1043g"]
[ext_resource type="Texture2D" uid="uid://8pmccsyfv3u7" path="res://assets/ui/icons/search.svg" id="5_pq07x"]
[ext_resource type="PackedScene" uid="uid://cr83fmlociwko" path="res://core/ui/components/card_icon_button.tscn" id="6_dilc0"]
[ext_resource type="PackedScene" uid="uid://cgmb4kr2ec4ha" path="res://core/ui/components/tabs_header.tscn" id="8_uixir"]
@@ -13,20 +13,21 @@
[ext_resource type="Script" path="res://core/ui/components/input_icon.gd" id="9_qnoau"]
[node name="SearchBarMenu" type="PanelContainer"]
-z_index = 20
+z_index = 19
custom_minimum_size = Vector2(0, 48)
size_flags_horizontal = 0
size_flags_vertical = 0
theme_type_variation = &"SearchBar"
script = ExtResource("1_518qs")
-[node name="VisibilityManager" type="Node" parent="."]
-script = ExtResource("1_3s0wp")
-state = ExtResource("2_kr4pi")
-visible_during = Array[Resource]([ExtResource("2_kr4pi"), ExtResource("3_sj5or")])
+[node name="StatesWatcher" parent="." instance=ExtResource("2_nb2d7")]
+states = Array[Resource("res://core/systems/state/state.gd")]([ExtResource("2_kr4pi"), ExtResource("3_sj5or")])
-[node name="TransitionFadeIn" parent="VisibilityManager" instance=ExtResource("4_r83xd")]
-root_node = NodePath("..")
+[node name="FadeEffect" parent="StatesWatcher" node_paths=PackedStringArray("target") instance=ExtResource("4_1043g")]
+target = NodePath("../..")
+on_signal = "state_entered"
+fade_out_signal = "state_exited"
+on_signal = "state_entered"
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 2
diff --git a/core/ui/card_ui/navigation/top_button_menu.tscn b/core/ui/card_ui/navigation/top_button_menu.tscn
index f6fb739f..2539834e 100644
--- a/core/ui/card_ui/navigation/top_button_menu.tscn
+++ b/core/ui/card_ui/navigation/top_button_menu.tscn
@@ -1,15 +1,15 @@
[gd_scene load_steps=13 format=3 uid="uid://by0i08fw1fwty"]
+[ext_resource type="PackedScene" uid="uid://bfiia7vnbfw3s" path="res://core/systems/state/states_watcher.tscn" id="1_onljq"]
[ext_resource type="Texture2D" uid="uid://bjscvn2us6tal" path="res://assets/ui/icons/bell.svg" id="1_te2kv"]
-[ext_resource type="Script" path="res://core/systems/state/visibility_manager.gd" id="1_ue0mf"]
[ext_resource type="Texture2D" uid="uid://dq32os2qn6atc" path="res://assets/ui/icons/help.svg" id="2_1q5o3"]
[ext_resource type="Resource" uid="uid://oaavalv0wcoa" path="res://assets/state/states/home.tres" id="2_a2ifc"]
-[ext_resource type="Resource" uid="uid://boq501bigx8kl" path="res://assets/state/states/library.tres" id="3_5xjwl"]
[ext_resource type="Resource" uid="uid://dav2b3n384cso" path="res://assets/state/states/game_launcher.tres" id="4_g6or7"]
-[ext_resource type="PackedScene" uid="uid://bo077a5mwi7xl" path="res://core/ui/components/transition_fade_in.tscn" id="4_lk3ix"]
+[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="4_m8oxr"]
[ext_resource type="PackedScene" uid="uid://cr83fmlociwko" path="res://core/ui/components/card_icon_button.tscn" id="6_b4g8u"]
+[ext_resource type="Resource" uid="uid://bcr6c0281lb5b" path="res://assets/state/state_machines/menu_state_machine.tres" id="9_74mn5"]
+[ext_resource type="Resource" uid="uid://cadriyl38ny5y" path="res://assets/state/state_machines/popup_state_machine.tres" id="9_atx4m"]
[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="9_nhibw"]
-[ext_resource type="Resource" uid="uid://cr544el0cqjlm" path="res://assets/state/state_machines/global_state_machine.tres" id="10_5h6sh"]
[ext_resource type="Resource" uid="uid://d3gp85f35oiw6" path="res://assets/state/states/settings.tres" id="11_eo1bd"]
[ext_resource type="Resource" uid="uid://db5gbdl3xgwlq" path="res://assets/state/states/help_menu.tres" id="11_q3ls8"]
@@ -20,13 +20,14 @@ size_flags_horizontal = 8
size_flags_vertical = 0
theme_type_variation = &"ButtonMenu"
-[node name="VisibilityManager" type="Node" parent="."]
-script = ExtResource("1_ue0mf")
-state = ExtResource("2_a2ifc")
-visible_during = Array[Resource]([ExtResource("2_a2ifc"), ExtResource("3_5xjwl"), ExtResource("4_g6or7")])
+[node name="StatesWatcher" parent="." instance=ExtResource("1_onljq")]
+states = Array[Resource("res://core/systems/state/state.gd")]([ExtResource("2_a2ifc"), ExtResource("4_g6or7")])
-[node name="TransitionFadeIn" parent="VisibilityManager" instance=ExtResource("4_lk3ix")]
-root_node = NodePath("..")
+[node name="FadeEffect" parent="StatesWatcher" node_paths=PackedStringArray("target") instance=ExtResource("4_m8oxr")]
+target = NodePath("../..")
+on_signal = "state_entered"
+fade_out_signal = "state_exited"
+on_signal = "state_entered"
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 2
@@ -37,6 +38,7 @@ theme_override_constants/margin_right = 10
layout_mode = 2
[node name="NotifyButton" parent="MarginContainer/HBoxContainer" instance=ExtResource("6_b4g8u")]
+visible = false
custom_minimum_size = Vector2(36, 36)
layout_mode = 2
texture = ExtResource("1_te2kv")
@@ -47,7 +49,7 @@ layout_mode = 2
texture = ExtResource("2_1q5o3")
[node name="StateUpdater" parent="MarginContainer/HBoxContainer/HelpButton" instance=ExtResource("9_nhibw")]
-state_machine = ExtResource("10_5h6sh")
+state_machine = ExtResource("9_atx4m")
state = ExtResource("11_q3ls8")
on_signal = "button_up"
@@ -56,6 +58,6 @@ custom_minimum_size = Vector2(36, 36)
layout_mode = 2
[node name="StateUpdater" parent="MarginContainer/HBoxContainer/SettingsButton" instance=ExtResource("9_nhibw")]
-state_machine = ExtResource("10_5h6sh")
+state_machine = ExtResource("9_74mn5")
state = ExtResource("11_eo1bd")
on_signal = "button_up"
diff --git a/core/ui/card_ui/ootbe/first_boot_menu.tscn b/core/ui/card_ui/ootbe/first_boot_menu.tscn
index b054b16a..e52ce622 100644
--- a/core/ui/card_ui/ootbe/first_boot_menu.tscn
+++ b/core/ui/card_ui/ootbe/first_boot_menu.tscn
@@ -1,7 +1,6 @@
-[gd_scene load_steps=13 format=3 uid="uid://b30stcxjwk3od"]
+[gd_scene load_steps=14 format=3 uid="uid://b30stcxjwk3od"]
[ext_resource type="Script" path="res://core/ui/card_ui/ootbe/first_boot_menu.gd" id="1_37dam"]
-[ext_resource type="PackedScene" uid="uid://orey8uxm7v6v" path="res://core/systems/state/visibility_manager.tscn" id="2_bh7el"]
[ext_resource type="PackedScene" uid="uid://cwdf3dyj7grrk" path="res://core/ui/card_ui/ootbe/language_select.tscn" id="2_djjvb"]
[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="2_ikevm"]
[ext_resource type="Resource" uid="uid://bj8aa7rky3u36" path="res://assets/state/state_machines/first_boot_state_machine.tres" id="3_0m4gw"]
@@ -10,7 +9,9 @@
[ext_resource type="PackedScene" uid="uid://b64v61ut4e1qv" path="res://core/ui/card_ui/ootbe/network_select.tscn" id="4_mx76i"]
[ext_resource type="Resource" uid="uid://cawipbut6fx5o" path="res://assets/state/states/first_boot_language.tres" id="4_pe1rt"]
[ext_resource type="PackedScene" uid="uid://cq5do4hbuevrd" path="res://core/ui/card_ui/ootbe/plugin_select.tscn" id="5_8uql2"]
+[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_ipx1n"]
[ext_resource type="PackedScene" uid="uid://cx0pugv62qwkw" path="res://core/ui/card_ui/ootbe/plugin_setup.tscn" id="6_jawcd"]
+[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="7_4d0j5"]
[ext_resource type="PackedScene" uid="uid://lbf0ycha2pav" path="res://core/ui/card_ui/ootbe/finished_menu.tscn" id="12_5m0q4"]
[node name="FirstBootMenu" type="Control"]
@@ -27,9 +28,15 @@ state_machine = ExtResource("3_0m4gw")
state = ExtResource("4_pe1rt")
on_signal = "ready"
-[node name="VisibilityManager" parent="." instance=ExtResource("2_bh7el")]
+[node name="StateWatcher" parent="." instance=ExtResource("5_ipx1n")]
state = ExtResource("3_yp1h6")
+[node name="FadeEffect" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("7_4d0j5")]
+target = NodePath("../..")
+on_signal = "state_entered"
+fade_out_signal = "state_exited"
+on_signal = "state_entered"
+
[node name="ContentContainer" type="MarginContainer" parent="."]
layout_mode = 1
anchors_preset = 15
diff --git a/core/ui/card_ui/power/power_menu.gd b/core/ui/card_ui/power/power_menu.gd
index ad56c0b6..8e62e954 100644
--- a/core/ui/card_ui/power/power_menu.gd
+++ b/core/ui/card_ui/power/power_menu.gd
@@ -1,7 +1,7 @@
extends Control
var state_machine := (
- preload("res://assets/state/state_machines/global_state_machine.tres") as StateMachine
+ preload("res://assets/state/state_machines/popup_state_machine.tres") as StateMachine
)
var power_state := load("res://assets/state/states/power_menu.tres") as State
var logger := Log.get_logger("PowerMenu")
@@ -50,10 +50,12 @@ func _on_state_entered(_from: State) -> void:
func _on_systemctl_cmd(command: String) -> void:
- state_machine.pop_state()
- var output: Array = []
- if OS.execute("systemctl", [command], output) != OK:
- logger.warn("Failed to " + command + ": '" + output[0] + "'")
+ state_machine.clear_states()
+ var exec := func():
+ var output: Array = []
+ if OS.execute("systemctl", [command], output) != OK:
+ logger.warn("Failed to " + command + ": '" + output[0] + "'")
+ exec.call_deferred()
func _on_exit() -> void:
diff --git a/core/ui/card_ui/power/power_menu.tscn b/core/ui/card_ui/power/power_menu.tscn
index eaf69499..b67c488e 100644
--- a/core/ui/card_ui/power/power_menu.tscn
+++ b/core/ui/card_ui/power/power_menu.tscn
@@ -1,12 +1,13 @@
-[gd_scene load_steps=16 format=3 uid="uid://lfm3ps837gy5"]
+[gd_scene load_steps=17 format=3 uid="uid://lfm3ps837gy5"]
[ext_resource type="Script" path="res://core/ui/card_ui/power/power_menu.gd" id="2_0vc46"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="3_1whtm"]
[ext_resource type="Shader" path="res://assets/shaders/simple_blur.gdshader" id="3_oe8pc"]
-[ext_resource type="PackedScene" uid="uid://ccd4sw84h1qbc" path="res://core/systems/input/back_input_handler.tscn" id="4_7v8tn"]
+[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="4_4rhfl"]
+[ext_resource type="Resource" uid="uid://cadriyl38ny5y" path="res://assets/state/state_machines/popup_state_machine.tres" id="5_kxixy"]
[ext_resource type="Resource" uid="uid://bw0mtk7sso8m2" path="res://assets/state/states/power_menu.tres" id="5_u1rly"]
-[ext_resource type="PackedScene" uid="uid://orey8uxm7v6v" path="res://core/systems/state/visibility_manager.tscn" id="6_j6s3o"]
-[ext_resource type="PackedScene" uid="uid://b6p2lrkowintj" path="res://core/ui/components/transition_container.tscn" id="7_7o4m4"]
-[ext_resource type="PackedScene" uid="uid://bo077a5mwi7xl" path="res://core/ui/components/transition_fade_in.tscn" id="8_tx8jn"]
+[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="6_34b1q"]
+[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="8_xohsw"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="9_vlbso"]
[ext_resource type="Script" path="res://core/systems/input/focus_stack.gd" id="10_6ywv6"]
[ext_resource type="PackedScene" uid="uid://dithv38oqgy58" path="res://core/ui/components/section_label.tscn" id="10_y3iyf"]
@@ -48,15 +49,23 @@ grow_horizontal = 2
grow_vertical = 2
texture = SubResource("GradientTexture1D_nu0jx")
-[node name="BackInputHandler" parent="." instance=ExtResource("4_7v8tn")]
-process_input_during = Array[Resource("res://core/systems/state/state.gd")]([ExtResource("5_u1rly")])
+[node name="InputWatcher" parent="." instance=ExtResource("3_1whtm")]
+stop_propagation = true
+action = "ogui_east"
-[node name="VisibilityManager" parent="." instance=ExtResource("6_j6s3o")]
-state = ExtResource("5_u1rly")
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("4_4rhfl")]
+state_machine = ExtResource("5_kxixy")
+action = 2
+on_signal = "input_released"
-[node name="TransitionContainer" parent="." instance=ExtResource("7_7o4m4")]
+[node name="StateWatcher" parent="." instance=ExtResource("6_34b1q")]
+state = ExtResource("5_u1rly")
-[node name="TransitionFadeIn" parent="TransitionContainer" instance=ExtResource("8_tx8jn")]
+[node name="FadeEffect" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("8_xohsw")]
+target = NodePath("../..")
+on_signal = "state_entered"
+fade_out_signal = "state_exited"
+on_signal = "state_entered"
[node name="PanelContainer" type="PanelContainer" parent="."]
custom_minimum_size = Vector2(200, 150)
diff --git a/core/ui/card_ui/quick_bar/qb_card.gd b/core/ui/card_ui/quick_bar/qb_card.gd
index de5ad674..7fc45969 100644
--- a/core/ui/card_ui/quick_bar/qb_card.gd
+++ b/core/ui/card_ui/quick_bar/qb_card.gd
@@ -3,7 +3,7 @@
extends Container
class_name QuickBarCard
-# DEPRECATED
+# DEPRECATED: Change this to [ExpandableCard]
signal pressed
signal button_up
@@ -15,7 +15,14 @@ signal nonchild_focused
@export_category("Card")
@export var title := "Section"
-@export var is_toggled := false
+@export var is_toggled := false:
+ set(v):
+ is_toggled = v
+ if is_toggled:
+ toggled_on.emit()
+ else:
+ toggled_off.emit()
+ toggled.emit(is_toggled)
@onready var header_container := $%HeaderContainer as VBoxContainer
@onready var label := $%SectionLabel as Label
@@ -31,17 +38,24 @@ var logger := Log.get_logger("QBCard", Log.LEVEL.INFO)
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
- focus_entered.connect(_on_focus)
- focus_exited.connect(_on_unfocus)
- pressed.connect(_on_pressed)
- theme_changed.connect(_on_theme_changed)
- _on_theme_changed()
label.text = title
-
+
# Do nothing if running in the editor
if Engine.is_editor_hint():
return
+ var on_focus_exited := func():
+ self._on_unfocus.call_deferred()
+ focus_exited.connect(on_focus_exited)
+ focus_entered.connect(_on_focus)
+ button_up.connect(_on_button_up)
+ theme_changed.connect(_on_theme_changed)
+
+ # Find the parent theme and update if required
+ var effective_theme := ThemeUtils.get_effective_theme(self)
+ if effective_theme:
+ _on_theme_changed()
+
# Try to find a scroll container to do smooth scrolling on expansion
var scroll_container := find_parent("ScrollContainer")
if scroll_container and scroll_container is ScrollContainer:
@@ -51,11 +65,9 @@ func _ready() -> void:
grower.effect_finished.connect(on_grow_finished)
# Auto-close when visibility is lost
- var on_visibility_changed := func():
- var grower := get_node("GrowerEffect") as GrowerEffect
- grower.shrink()
+ var on_hidden := func():
is_toggled = false
- hidden.connect(on_visibility_changed)
+ hidden.connect(on_hidden)
# Resize any children that are Control nodes
for child in content_container.get_children():
@@ -132,23 +144,47 @@ func _on_focus() -> void:
# the user has focused outside the card, and we should shrink to hide the
# content
if is_toggled:
- _on_pressed()
+ _on_button_up()
func _on_unfocus() -> void:
- # If a child focus group is focused, don't do anything. That means that
- if not focus_group:
- logger.warn("No focus group defined!")
+ # Get the new focus owner
+ var focus_owner := get_viewport().gui_get_focus_owner()
+
+ # Emit a signal if a non-child node grabs focus
+ if not self.is_ancestor_of(focus_owner):
+ nonchild_focused.emit()
+ is_toggled = false
return
- # a child node is focused and we want the card to remain "selected"
- if focus_group.is_in_focus_stack():
+ # If a child has focus, listen for focus changes until a non-child has focus
+ get_viewport().gui_focus_changed.connect(_on_focus_change)
+
+
+func _on_focus_change(focused: Control) -> void:
+ # Don't do anything if the focused node is a child
+ if self.is_ancestor_of(focused):
return
-
+
+ # If the focus owner is a child of an on-screen keyboard, don't do anything
+ # so the card remains open.
+ var virtual_keyboards := get_tree().get_nodes_in_group("osk")
+ for kb in virtual_keyboards:
+ if not kb:
+ continue
+ if kb.is_ancestor_of(focused):
+ return
+
+ # If a non-child has focus, emit a signal to indicate that this node and none
+ # of its children have focus.
nonchild_focused.emit()
+ is_toggled = false
+ var viewport := get_viewport()
+ if viewport.gui_focus_changed.is_connected(_on_focus_change):
+ viewport.gui_focus_changed.disconnect(_on_focus_change)
-func _on_pressed() -> void:
+func _on_button_up() -> void:
is_toggled = !is_toggled
if is_toggled:
toggled_on.emit()
@@ -168,3 +204,24 @@ func _gui_input(event: InputEvent) -> void:
pressed.emit()
else:
button_up.emit()
+
+
+func _input(event: InputEvent) -> void:
+ if not is_toggled:
+ return
+ if not event.is_action("ogui_east"):
+ return
+ if not event.is_released():
+ return
+
+ # Only process input if a child node has focus
+ #var focus_owner := get_viewport().gui_get_focus_owner()
+ #if not self.is_ancestor_of(focus_owner):
+ # return
+
+ # Handle back input
+ is_toggled = false
+
+ # Stop the event from propagating
+ #logger.debug("Consuming input event '{action}' for node {n}".format({"action": action, "n": str(self)}))
+ get_viewport().set_input_as_handled()
diff --git a/core/ui/card_ui/quick_bar/qb_card.tscn b/core/ui/card_ui/quick_bar/qb_card.tscn
index ca3e085d..aa4ebd1d 100644
--- a/core/ui/card_ui/quick_bar/qb_card.tscn
+++ b/core/ui/card_ui/quick_bar/qb_card.tscn
@@ -7,18 +7,8 @@
[ext_resource type="PackedScene" uid="uid://uljtdvmuol3l" path="res://core/systems/input/focus_group_setter.tscn" id="5_2hiwt"]
[ext_resource type="PackedScene" uid="uid://btg3dylh0tqoi" path="res://core/systems/effects/smooth_scroll_effect.tscn" id="6_hklca"]
[ext_resource type="PackedScene" uid="uid://d0u3rsa5qpj57" path="res://core/ui/components/subsection_label.tscn" id="6_qh1ai"]
-
-[sub_resource type="Image" id="Image_6pbp1"]
-data = {
-"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
-"format": "RGBA8",
-"height": 16,
-"mipmaps": false,
-"width": 16
-}
-
-[sub_resource type="ImageTexture" id="ImageTexture_sa6yd"]
-image = SubResource("Image_6pbp1")
+[ext_resource type="Script" path="res://core/systems/input/input_watcher.gd" id="8_7kulb"]
+[ext_resource type="Script" path="res://core/systems/input/focus_setter.gd" id="9_wwxqw"]
[node name="QuickBarCard" type="PanelContainer"]
anchors_preset = 10
@@ -69,7 +59,6 @@ theme_type_variation = &"ExpandableCard"
unique_name_in_owner = true
visible = false
layout_mode = 2
-texture = SubResource("ImageTexture_sa6yd")
expand_mode = 1
stretch_mode = 6
@@ -113,3 +102,13 @@ layout_mode = 2
unique_name_in_owner = true
visible = false
layout_mode = 2
+
+[node name="InputWatcher" type="Node" parent="MarginContainer/CardVBoxContainer/ContentContainer"]
+script = ExtResource("8_7kulb")
+stop_propagation = true
+action = "ogui_east"
+
+[node name="FocusSetter" type="Node" parent="MarginContainer/CardVBoxContainer/ContentContainer/InputWatcher" node_paths=PackedStringArray("target")]
+script = ExtResource("9_wwxqw")
+target = NodePath("../../../../..")
+on_signal = "input_released"
diff --git a/core/ui/card_ui/quick_bar/quick_bar_menu.tscn b/core/ui/card_ui/quick_bar/quick_bar_menu.tscn
index 71f49464..c8944223 100644
--- a/core/ui/card_ui/quick_bar/quick_bar_menu.tscn
+++ b/core/ui/card_ui/quick_bar/quick_bar_menu.tscn
@@ -1,18 +1,18 @@
-[gd_scene load_steps=26 format=3 uid="uid://hroo3ll4inrb"]
+[gd_scene load_steps=29 format=3 uid="uid://hroo3ll4inrb"]
[ext_resource type="Script" path="res://core/ui/card_ui/quick_bar/quick_bar_menu.gd" id="1_56jo7"]
[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="2_6rvrx"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_kmwe3"]
[ext_resource type="Resource" uid="uid://bp807nlks8eq1" path="res://assets/state/states/quick_bar_menu.tres" id="3_cxjne"]
[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="4_l64vu"]
+[ext_resource type="Resource" uid="uid://cadriyl38ny5y" path="res://assets/state/state_machines/popup_state_machine.tres" id="4_n117t"]
[ext_resource type="PackedScene" uid="uid://ekhjpmat02f8" path="res://core/systems/effects/slide_effect.tscn" id="5_i1kur"]
-[ext_resource type="PackedScene" uid="uid://ccd4sw84h1qbc" path="res://core/systems/input/back_input_handler.tscn" id="5_ro2hl"]
[ext_resource type="PackedScene" uid="uid://4t4jt26o2fbr" path="res://core/ui/card_ui/navigation/system_container.tscn" id="7_xtcf0"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="8_5082k"]
[ext_resource type="PackedScene" uid="uid://cr83fmlociwko" path="res://core/ui/components/card_icon_button.tscn" id="9_6qs1m"]
[ext_resource type="Texture2D" uid="uid://bjscvn2us6tal" path="res://assets/ui/icons/bell.svg" id="10_4yppf"]
[ext_resource type="Texture2D" uid="uid://dq32os2qn6atc" path="res://assets/ui/icons/help.svg" id="11_a0ma3"]
[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="12_ldp5y"]
-[ext_resource type="Resource" uid="uid://cr544el0cqjlm" path="res://assets/state/state_machines/global_state_machine.tres" id="13_u3r8o"]
[ext_resource type="Resource" uid="uid://d3gp85f35oiw6" path="res://assets/state/states/settings.tres" id="14_didkb"]
[ext_resource type="Resource" uid="uid://db5gbdl3xgwlq" path="res://assets/state/states/help_menu.tres" id="14_gr3i0"]
[ext_resource type="Texture2D" uid="uid://c8pq5h4uim4pj" path="res://assets/ui/icons/game-controller.svg" id="15_0l0p5"]
@@ -21,6 +21,9 @@
[ext_resource type="Resource" uid="uid://cx8u1y5j7vyss" path="res://assets/state/states/gamepad_settings.tres" id="16_74m7a"]
[ext_resource type="PackedScene" uid="uid://b0cyl6fdqxevn" path="res://core/systems/input/scroller_joystick.tscn" id="17_qgen2"]
[ext_resource type="Resource" uid="uid://dpc1o781f43ef" path="res://core/ui/card_ui/quick_bar/quick_bar_menu_focus.tres" id="18_4nxly"]
+[ext_resource type="Resource" uid="uid://bcr6c0281lb5b" path="res://assets/state/state_machines/menu_state_machine.tres" id="18_lj3fv"]
+[ext_resource type="Resource" uid="uid://cr544el0cqjlm" path="res://assets/state/state_machines/global_state_machine.tres" id="18_q6xfk"]
+[ext_resource type="Resource" uid="uid://cv3vduo0ojk1u" path="res://assets/state/states/menu.tres" id="19_orchh"]
[ext_resource type="PackedScene" uid="uid://bjy50kdrebgre" path="res://core/ui/card_ui/quick_bar/notifications_card.tscn" id="19_pppbi"]
[ext_resource type="PackedScene" uid="uid://dxaeufuk7ump2" path="res://core/ui/card_ui/quick_bar/quick_settings_card.tscn" id="20_17ks0"]
[ext_resource type="PackedScene" uid="uid://dycb7m0oj13ly" path="res://core/ui/card_ui/quick_bar/performance_card.tscn" id="21_uw510"]
@@ -37,6 +40,15 @@ grow_horizontal = 0
grow_vertical = 2
script = ExtResource("1_56jo7")
+[node name="InputWatcher" parent="." instance=ExtResource("2_kmwe3")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("12_ldp5y")]
+state_machine = ExtResource("4_n117t")
+action = 2
+on_signal = "input_released"
+
[node name="StateWatcher" parent="." instance=ExtResource("2_6rvrx")]
state = ExtResource("3_cxjne")
@@ -54,9 +66,6 @@ on_signal = "state_entered"
slide_out_signal = "state_exited"
on_signal = "state_entered"
-[node name="BackInputHandler" parent="." instance=ExtResource("5_ro2hl")]
-process_input_during = Array[Resource("res://core/systems/state/state.gd")]([ExtResource("3_cxjne")])
-
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 1
anchors_preset = 11
@@ -95,6 +104,7 @@ focus_neighbor_bottom = NodePath("../../ScrollContainer/Viewport/FocusGroup")
[node name="NotifyButton" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ButtonContainer" instance=ExtResource("9_6qs1m")]
unique_name_in_owner = true
+visible = false
custom_minimum_size = Vector2(26, 26)
layout_mode = 2
texture = ExtResource("10_4yppf")
@@ -105,7 +115,7 @@ layout_mode = 2
texture = ExtResource("11_a0ma3")
[node name="StateUpdater" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ButtonContainer/HelpButton" instance=ExtResource("12_ldp5y")]
-state_machine = ExtResource("13_u3r8o")
+state_machine = ExtResource("4_n117t")
state = ExtResource("14_gr3i0")
on_signal = "button_up"
@@ -115,10 +125,20 @@ layout_mode = 2
texture = ExtResource("15_0l0p5")
[node name="StateUpdater" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ButtonContainer/GamepadButton" instance=ExtResource("12_ldp5y")]
-state_machine = ExtResource("13_u3r8o")
+state_machine = ExtResource("18_lj3fv")
state = ExtResource("16_74m7a")
on_signal = "player_button_up"
+[node name="StateUpdater2" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ButtonContainer/GamepadButton" instance=ExtResource("12_ldp5y")]
+state_machine = ExtResource("18_q6xfk")
+state = ExtResource("19_orchh")
+on_signal = "button_up"
+
+[node name="ClearOverlay" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ButtonContainer/GamepadButton" instance=ExtResource("12_ldp5y")]
+state_machine = ExtResource("4_n117t")
+action = 6
+on_signal = "button_up"
+
[node name="Spacer" type="Control" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ButtonContainer"]
layout_mode = 2
size_flags_horizontal = 3
@@ -128,10 +148,20 @@ custom_minimum_size = Vector2(26, 26)
layout_mode = 2
[node name="StateUpdater" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ButtonContainer/SettingsButton" instance=ExtResource("12_ldp5y")]
-state_machine = ExtResource("13_u3r8o")
+state_machine = ExtResource("18_lj3fv")
state = ExtResource("14_didkb")
on_signal = "button_up"
+[node name="StateUpdater2" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ButtonContainer/SettingsButton" instance=ExtResource("12_ldp5y")]
+state_machine = ExtResource("18_q6xfk")
+state = ExtResource("19_orchh")
+on_signal = "button_up"
+
+[node name="ClearOverlay" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ButtonContainer/SettingsButton" instance=ExtResource("12_ldp5y")]
+state_machine = ExtResource("4_n117t")
+action = 6
+on_signal = "button_up"
+
[node name="PlayingNowContainer" type="HBoxContainer" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer"]
unique_name_in_owner = true
visible = false
diff --git a/core/ui/card_ui/quick_bar/quick_settings_card.tscn b/core/ui/card_ui/quick_bar/quick_settings_card.tscn
index 68481fda..a6360fe3 100644
--- a/core/ui/card_ui/quick_bar/quick_settings_card.tscn
+++ b/core/ui/card_ui/quick_bar/quick_settings_card.tscn
@@ -1,26 +1,11 @@
-[gd_scene load_steps=5 format=3 uid="uid://dxaeufuk7ump2"]
+[gd_scene load_steps=3 format=3 uid="uid://dxaeufuk7ump2"]
[ext_resource type="PackedScene" uid="uid://b5xnora73yd8x" path="res://core/ui/card_ui/quick_bar/qb_card.tscn" id="1_xq1be"]
[ext_resource type="PackedScene" uid="uid://gjrgimao0edi" path="res://core/ui/common/quick_bar/quick_settings_menu.tscn" id="2_gwynk"]
-[sub_resource type="Image" id="Image_n6cge"]
-data = {
-"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
-"format": "RGBA8",
-"height": 16,
-"mipmaps": false,
-"width": 16
-}
-
-[sub_resource type="ImageTexture" id="ImageTexture_ca0vc"]
-image = SubResource("Image_n6cge")
-
[node name="QuickSettingsCard" instance=ExtResource("1_xq1be")]
title = "Quick Settings"
-[node name="HighlightTexture" parent="PanelContainer" index="0"]
-texture = SubResource("ImageTexture_ca0vc")
-
[node name="SectionLabel" parent="MarginContainer/CardVBoxContainer" index="0"]
text = "Quick Settings"
diff --git a/core/ui/card_ui/settings/bluetooth_settings_menu.tscn b/core/ui/card_ui/settings/bluetooth_settings_menu.tscn
index a26e3046..92d5e7c4 100644
--- a/core/ui/card_ui/settings/bluetooth_settings_menu.tscn
+++ b/core/ui/card_ui/settings/bluetooth_settings_menu.tscn
@@ -1,9 +1,14 @@
-[gd_scene load_steps=5 format=3 uid="uid://6ygxdkvh1hib"]
+[gd_scene load_steps=10 format=3 uid="uid://6ygxdkvh1hib"]
[ext_resource type="Script" path="res://core/ui/card_ui/settings/bluetooth_settings_menu.gd" id="1_7sjpd"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="2_36ef6"]
-[ext_resource type="Resource" uid="uid://dgi16frh3mgj8" path="res://core/ui/card_ui/settings/settings_menu_focus.tres" id="3_rx42r"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_p7et2"]
+[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="3_2trn0"]
[ext_resource type="PackedScene" uid="uid://d1qb7euwlu7bh" path="res://core/ui/components/toggle.tscn" id="4_isnhb"]
+[ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="4_l428g"]
+[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_ivwga"]
+[ext_resource type="Resource" uid="uid://5r7ehi4t5mri" path="res://assets/state/states/settings_bluetooth.tres" id="6_2ogat"]
+[ext_resource type="Script" path="res://core/systems/input/focus_group_setter.gd" id="7_klxjh"]
[node name="BluetoothSettingsMenu" type="Control"]
layout_mode = 3
@@ -14,6 +19,23 @@ grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_7sjpd")
+[node name="InputWatcher" parent="." instance=ExtResource("2_p7et2")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("3_2trn0")]
+state_machine = ExtResource("4_l428g")
+action = 2
+on_signal = "input_released"
+
+[node name="StateWatcher" parent="." instance=ExtResource("5_ivwga")]
+state = ExtResource("6_2ogat")
+
+[node name="FocusGroupSetter" type="Node" parent="StateWatcher" node_paths=PackedStringArray("target")]
+script = ExtResource("7_klxjh")
+target = NodePath("../../MarginContainer/ScrollContainer/AvailableContainer/VBoxContainer/FocusGroup")
+on_signal = "state_entered"
+
[node name="DiscoverTimer" type="Timer" parent="."]
unique_name_in_owner = true
@@ -65,21 +87,18 @@ layout_mode = 2
[node name="FocusGroup" parent="MarginContainer/ScrollContainer/AvailableContainer/VBoxContainer" node_paths=PackedStringArray("current_focus") instance=ExtResource("2_36ef6")]
current_focus = NodePath("../EnableToggle")
-focus_stack = ExtResource("3_rx42r")
[node name="EnableToggle" parent="MarginContainer/ScrollContainer/AvailableContainer/VBoxContainer" instance=ExtResource("4_isnhb")]
unique_name_in_owner = true
layout_mode = 2
text = "Enabled"
separator_visible = false
-button_pressed = false
[node name="DiscoverToggle" parent="MarginContainer/ScrollContainer/AvailableContainer/VBoxContainer" instance=ExtResource("4_isnhb")]
unique_name_in_owner = true
layout_mode = 2
text = "Enable Discovery"
separator_visible = false
-button_pressed = false
[node name="Tree" type="Tree" parent="MarginContainer/ScrollContainer/AvailableContainer/VBoxContainer"]
unique_name_in_owner = true
diff --git a/core/ui/card_ui/settings/disks_settings_menu.gd b/core/ui/card_ui/settings/disks_settings_menu.gd
index 2c86791b..3fe3439c 100644
--- a/core/ui/card_ui/settings/disks_settings_menu.gd
+++ b/core/ui/card_ui/settings/disks_settings_menu.gd
@@ -7,7 +7,7 @@ const drive_card_scene: PackedScene = preload("res://core/ui/components/drive_ca
var logger := Log.get_logger("DisksMenu", Log.LEVEL.INFO)
-@onready var container: HFlowContainer = $%DriveCardContainer
+@onready var container: VBoxContainer = $%DriveCardContainer
@onready var focus_group: FocusGroup = $%FocusGroup
@onready var no_drive_label: Label = $%NoDisksLabel
@@ -42,6 +42,12 @@ func _on_drives_updated(devices: Array[BlockDevice]) -> void:
# Poplulate drives
var last_focus: FocusGroup
for drive in devices:
+ var drive_type = drive.dbus_path.trim_prefix(steam_disks.BLOCK_PREFIX)
+
+ # Ignore loop devices
+ if drive_type.contains("loop"):
+ continue
+
# Create Drive Card
var drive_card := drive_card_scene.instantiate()
container.add_child(drive_card)
diff --git a/core/ui/card_ui/settings/disks_settings_menu.tscn b/core/ui/card_ui/settings/disks_settings_menu.tscn
index 9cb27349..babb7c5b 100644
--- a/core/ui/card_ui/settings/disks_settings_menu.tscn
+++ b/core/ui/card_ui/settings/disks_settings_menu.tscn
@@ -1,12 +1,14 @@
-[gd_scene load_steps=9 format=3 uid="uid://clbapexwfoqtu"]
+[gd_scene load_steps=11 format=3 uid="uid://clbapexwfoqtu"]
[ext_resource type="Script" path="res://core/ui/card_ui/settings/disks_settings_menu.gd" id="1_ct0k3"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_6shyw"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="2_8gros"]
-[ext_resource type="Resource" uid="uid://dgi16frh3mgj8" path="res://core/ui/card_ui/settings/settings_menu_focus.tres" id="3_8w0tf"]
+[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="3_lwwgd"]
[ext_resource type="PackedScene" uid="uid://caeaxm6st4a4u" path="res://core/ui/components/drive_card.tscn" id="4_brwek"]
+[ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="4_vkilo"]
[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_hkv7e"]
[ext_resource type="Resource" uid="uid://cn236k0ajm0uw" path="res://assets/state/states/settings_disks.tres" id="6_txokm"]
-[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="7_kihn7"]
+[ext_resource type="Script" path="res://core/systems/input/focus_group_setter.gd" id="7_lonxh"]
[ext_resource type="Script" path="res://core/systems/input/scroller_joystick.gd" id="8_x4sfx"]
[node name="DisksMenu" type="ScrollContainer"]
@@ -17,6 +19,23 @@ grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_ct0k3")
+[node name="InputWatcher" parent="." instance=ExtResource("2_6shyw")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("3_lwwgd")]
+state_machine = ExtResource("4_vkilo")
+action = 2
+on_signal = "input_released"
+
+[node name="StateWatcher" parent="." instance=ExtResource("5_hkv7e")]
+state = ExtResource("6_txokm")
+
+[node name="FocusGroupSetter" type="Node" parent="StateWatcher" node_paths=PackedStringArray("target")]
+script = ExtResource("7_lonxh")
+target = NodePath("../../MarginContainer/DriveCardContainer/FocusGroup")
+on_signal = "state_entered"
+
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
@@ -29,14 +48,13 @@ Verify your media device is plugged in and that it is not mounted to a protected
horizontal_alignment = 1
uppercase = true
-[node name="DriveCardContainer" type="HFlowContainer" parent="MarginContainer"]
+[node name="DriveCardContainer" type="VBoxContainer" parent="MarginContainer"]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="FocusGroup" parent="MarginContainer/DriveCardContainer" instance=ExtResource("2_8gros")]
unique_name_in_owner = true
-focus_stack = ExtResource("3_8w0tf")
[node name="DriveCard" parent="MarginContainer/DriveCardContainer" instance=ExtResource("4_brwek")]
layout_mode = 2
@@ -47,14 +65,5 @@ layout_mode = 2
[node name="DriveCard3" parent="MarginContainer/DriveCardContainer" instance=ExtResource("4_brwek")]
layout_mode = 2
-[node name="StateWatcher" parent="." instance=ExtResource("5_hkv7e")]
-state = ExtResource("6_txokm")
-
-[node name="FadeEffect" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("7_kihn7")]
-target = NodePath("../..")
-on_signal = "state_entered"
-fade_out_signal = "state_exited"
-on_signal = "state_entered"
-
[node name="ScrollerJoystick" type="Node" parent="."]
script = ExtResource("8_x4sfx")
diff --git a/core/ui/card_ui/settings/general_controller_settings_menu.tscn b/core/ui/card_ui/settings/general_controller_settings_menu.tscn
index 8ff4d89f..8530ea27 100644
--- a/core/ui/card_ui/settings/general_controller_settings_menu.tscn
+++ b/core/ui/card_ui/settings/general_controller_settings_menu.tscn
@@ -1,14 +1,15 @@
-[gd_scene load_steps=11 format=3 uid="uid://cgxl1qiu50h15"]
+[gd_scene load_steps=12 format=3 uid="uid://cgxl1qiu50h15"]
[ext_resource type="Script" path="res://core/ui/card_ui/settings/general_controller_settings_menu.gd" id="1_r712q"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="2_fdccl"]
[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="2_siloa"]
-[ext_resource type="Resource" uid="uid://dgi16frh3mgj8" path="res://core/ui/card_ui/settings/settings_menu_focus.tres" id="3_6p4qa"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_w65oc"]
[ext_resource type="Resource" uid="uid://bcekyu20uvkxv" path="res://assets/state/states/settings_general_controller.tres" id="3_yy4fy"]
-[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="4_g1xc4"]
+[ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="4_s8rkr"]
+[ext_resource type="Script" path="res://core/systems/input/focus_group_setter.gd" id="7_28fjg"]
[ext_resource type="PackedScene" uid="uid://c71ayw7pcw6u6" path="res://core/ui/components/card_button.tscn" id="7_ynn8u"]
[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="8_1egj0"]
-[ext_resource type="Resource" uid="uid://cr544el0cqjlm" path="res://assets/state/state_machines/global_state_machine.tres" id="9_3kuiy"]
+[ext_resource type="Resource" uid="uid://bcr6c0281lb5b" path="res://assets/state/state_machines/menu_state_machine.tres" id="10_ij44t"]
[ext_resource type="Resource" uid="uid://cx8u1y5j7vyss" path="res://assets/state/states/gamepad_settings.tres" id="10_uoc6p"]
[node name="GeneralControllerSettings" type="Control"]
@@ -20,13 +21,21 @@ grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_r712q")
+[node name="InputWatcher" parent="." instance=ExtResource("2_w65oc")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("8_1egj0")]
+state_machine = ExtResource("4_s8rkr")
+action = 2
+on_signal = "input_released"
+
[node name="StateWatcher" parent="." instance=ExtResource("2_siloa")]
state = ExtResource("3_yy4fy")
-[node name="FadeEffect" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("4_g1xc4")]
-target = NodePath("../..")
-on_signal = "state_entered"
-fade_out_signal = "state_exited"
+[node name="FocusGroupSetter" type="Node" parent="StateWatcher" node_paths=PackedStringArray("target")]
+script = ExtResource("7_28fjg")
+target = NodePath("../../MarginContainer/VBoxContainer/FocusGroup")
on_signal = "state_entered"
[node name="MarginContainer" type="MarginContainer" parent="."]
@@ -45,13 +54,12 @@ theme_override_constants/margin_bottom = 5
layout_mode = 2
[node name="FocusGroup" parent="MarginContainer/VBoxContainer" instance=ExtResource("2_fdccl")]
-focus_stack = ExtResource("3_6p4qa")
[node name="CardButton" parent="MarginContainer/VBoxContainer" instance=ExtResource("7_ynn8u")]
layout_mode = 2
text = "Gamepad Settings"
[node name="StateUpdater" parent="MarginContainer/VBoxContainer/CardButton" instance=ExtResource("8_1egj0")]
-state_machine = ExtResource("9_3kuiy")
+state_machine = ExtResource("10_ij44t")
state = ExtResource("10_uoc6p")
-on_signal = "player_button_down"
+on_signal = "player_button_up"
diff --git a/core/ui/card_ui/settings/general_settings_menu.tscn b/core/ui/card_ui/settings/general_settings_menu.tscn
index 2399dc7e..83869bf0 100644
--- a/core/ui/card_ui/settings/general_settings_menu.tscn
+++ b/core/ui/card_ui/settings/general_settings_menu.tscn
@@ -1,12 +1,17 @@
-[gd_scene load_steps=18 format=3 uid="uid://dsgrw1grwef4m"]
+[gd_scene load_steps=23 format=3 uid="uid://dsgrw1grwef4m"]
[ext_resource type="Script" path="res://core/ui/card_ui/settings/general_settings_menu.gd" id="1_if7xt"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_b5gbi"]
[ext_resource type="PackedScene" uid="uid://bnhxravpuheh2" path="res://core/systems/updater/software_updater.tscn" id="2_ogwq4"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="3_36sdt"]
-[ext_resource type="Resource" uid="uid://dgi16frh3mgj8" path="res://core/ui/card_ui/settings/settings_menu_focus.tres" id="4_fyrmg"]
+[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="3_bxo72"]
+[ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="4_31ujl"]
+[ext_resource type="Script" path="res://core/systems/state/state_watcher.gd" id="5_7jxnc"]
[ext_resource type="PackedScene" uid="uid://dithv38oqgy58" path="res://core/ui/components/section_label.tscn" id="5_caxj0"]
[ext_resource type="PackedScene" uid="uid://d1qb7euwlu7bh" path="res://core/ui/components/toggle.tscn" id="6_ghdw1"]
+[ext_resource type="Resource" uid="uid://4n3376qdy3y3" path="res://assets/state/states/settings_general.tres" id="6_vwiy7"]
[ext_resource type="PackedScene" uid="uid://c71ayw7pcw6u6" path="res://core/ui/components/card_button.tscn" id="7_qenel"]
+[ext_resource type="Script" path="res://core/systems/input/focus_group_setter.gd" id="7_sb10a"]
[ext_resource type="PackedScene" uid="uid://d0u3rsa5qpj57" path="res://core/ui/components/subsection_label.tscn" id="8_2m3jw"]
[ext_resource type="PackedScene" uid="uid://xei5afwefxud" path="res://core/ui/components/dropdown.tscn" id="8_r15pa"]
[ext_resource type="Texture2D" uid="uid://ckbrrln8fngno" path="res://assets/images/platform/ayaneoair-ctr_v1a_1172023.png" id="9_00g0t"]
@@ -30,6 +35,24 @@ follow_focus = true
horizontal_scroll_mode = 0
script = ExtResource("1_if7xt")
+[node name="InputWatcher" parent="." instance=ExtResource("2_b5gbi")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("3_bxo72")]
+state_machine = ExtResource("4_31ujl")
+action = 2
+on_signal = "input_released"
+
+[node name="StateWatcher" type="Node" parent="."]
+script = ExtResource("5_7jxnc")
+state = ExtResource("6_vwiy7")
+
+[node name="FocusGroupSetter" type="Node" parent="StateWatcher" node_paths=PackedStringArray("target")]
+script = ExtResource("7_sb10a")
+target = NodePath("../../MarginContainer/VBoxContainer/VBoxContainer/FocusGroup")
+on_signal = "state_entered"
+
[node name="SoftwareUpdater" parent="." instance=ExtResource("2_ogwq4")]
[node name="UpdateTimer" type="Timer" parent="."]
@@ -56,7 +79,7 @@ theme_override_constants/separation = 10
[node name="FocusGroup" parent="MarginContainer/VBoxContainer/VBoxContainer" node_paths=PackedStringArray("current_focus", "focus_neighbor_bottom") instance=ExtResource("3_36sdt")]
current_focus = NodePath("../AutoUpdateToggle")
-focus_stack = ExtResource("4_fyrmg")
+wrap_focus = false
focus_neighbor_bottom = NodePath("../../ThemeButtonContainer/FocusGroup")
[node name="UpdatesLabel" parent="MarginContainer/VBoxContainer/VBoxContainer" instance=ExtResource("5_caxj0")]
@@ -110,7 +133,7 @@ theme_override_constants/v_separation = 10
[node name="FocusGroup" parent="MarginContainer/VBoxContainer/ThemeButtonContainer" node_paths=PackedStringArray("current_focus", "focus_neighbor_bottom", "focus_neighbor_top") instance=ExtResource("3_36sdt")]
current_focus = NodePath("../ThemeDraculaButton")
-focus_stack = ExtResource("4_fyrmg")
+wrap_focus = false
focus_neighbor_bottom = NodePath("../../VBoxContainer3/FocusGroup")
focus_neighbor_top = NodePath("../../VBoxContainer/FocusGroup")
@@ -160,7 +183,7 @@ theme_override_constants/separation = 10
[node name="FocusGroup" parent="MarginContainer/VBoxContainer/VBoxContainer3" node_paths=PackedStringArray("current_focus", "focus_neighbor_top") instance=ExtResource("3_36sdt")]
current_focus = NodePath("../ClientVersionText")
-focus_stack = ExtResource("4_fyrmg")
+wrap_focus = false
focus_neighbor_top = NodePath("../../ThemeButtonContainer/FocusGroup")
[node name="HSeparator3" type="HSeparator" parent="MarginContainer/VBoxContainer/VBoxContainer3"]
diff --git a/core/ui/card_ui/settings/library_settings_menu.tscn b/core/ui/card_ui/settings/library_settings_menu.tscn
index 8e703fcb..39d3547d 100644
--- a/core/ui/card_ui/settings/library_settings_menu.tscn
+++ b/core/ui/card_ui/settings/library_settings_menu.tscn
@@ -1,13 +1,15 @@
-[gd_scene load_steps=9 format=3 uid="uid://drbp6ec8646v3"]
+[gd_scene load_steps=11 format=3 uid="uid://drbp6ec8646v3"]
[ext_resource type="Script" path="res://core/ui/card_ui/settings/library_settings_menu.gd" id="1_0w1vu"]
[ext_resource type="PackedScene" uid="uid://cemxrvvjgm4g" path="res://core/ui/components/slider.tscn" id="1_obgkx"]
[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="2_guba0"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_oc808"]
+[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="3_k7tqg"]
[ext_resource type="PackedScene" uid="uid://dithv38oqgy58" path="res://core/ui/components/section_label.tscn" id="3_x5lfl"]
[ext_resource type="Resource" uid="uid://blcfrofi5oawd" path="res://assets/state/states/settings_library.tres" id="3_ykj28"]
-[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="4_148sd"]
+[ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="4_cd7pj"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="5_sh522"]
-[ext_resource type="Resource" uid="uid://dgi16frh3mgj8" path="res://core/ui/card_ui/settings/settings_menu_focus.tres" id="6_ou60g"]
+[ext_resource type="Script" path="res://core/systems/input/focus_group_setter.gd" id="7_5nxbl"]
[node name="LibrarySettingsMenu" type="ScrollContainer"]
anchors_preset = 15
@@ -18,13 +20,21 @@ grow_vertical = 2
follow_focus = true
script = ExtResource("1_0w1vu")
+[node name="InputWatcher" parent="." instance=ExtResource("2_oc808")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("3_k7tqg")]
+state_machine = ExtResource("4_cd7pj")
+action = 2
+on_signal = "input_released"
+
[node name="StateWatcher" parent="." instance=ExtResource("2_guba0")]
state = ExtResource("3_ykj28")
-[node name="FadeEffect" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("4_148sd")]
-target = NodePath("../..")
-on_signal = "state_entered"
-fade_out_signal = "state_exited"
+[node name="FocusGroupSetter" type="Node" parent="StateWatcher" node_paths=PackedStringArray("target")]
+script = ExtResource("7_5nxbl")
+target = NodePath("../../MarginContainer/VBoxContainer/FocusGroup")
on_signal = "state_entered"
[node name="MarginContainer" type="MarginContainer" parent="."]
@@ -42,7 +52,6 @@ layout_mode = 2
[node name="FocusGroup" parent="MarginContainer/VBoxContainer" node_paths=PackedStringArray("current_focus") instance=ExtResource("5_sh522")]
current_focus = NodePath("../MaxRecentAppsSlider")
-focus_stack = ExtResource("6_ou60g")
[node name="HomeLabel" parent="MarginContainer/VBoxContainer" instance=ExtResource("3_x5lfl")]
layout_mode = 2
diff --git a/core/ui/card_ui/settings/plugin_settings_content.tscn b/core/ui/card_ui/settings/plugin_settings_content.tscn
index b092323b..e58c9afd 100644
--- a/core/ui/card_ui/settings/plugin_settings_content.tscn
+++ b/core/ui/card_ui/settings/plugin_settings_content.tscn
@@ -1,8 +1,7 @@
-[gd_scene load_steps=5 format=3 uid="uid://by6gr07lw0gw3"]
+[gd_scene load_steps=4 format=3 uid="uid://by6gr07lw0gw3"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="1_kkfns"]
[ext_resource type="PackedScene" uid="uid://cmjjyqx1pl23d" path="res://core/ui/components/text.tscn" id="1_qr2e8"]
-[ext_resource type="Resource" uid="uid://dgi16frh3mgj8" path="res://core/ui/card_ui/settings/settings_menu_focus.tres" id="2_132sf"]
[ext_resource type="PackedScene" uid="uid://d1qb7euwlu7bh" path="res://core/ui/components/toggle.tscn" id="2_sphif"]
[node name="PluginSettingsContent" type="MarginContainer"]
@@ -24,7 +23,7 @@ size_flags_vertical = 0
[node name="FocusGroup" parent="ContentLayout" node_paths=PackedStringArray("current_focus") instance=ExtResource("1_kkfns")]
unique_name_in_owner = true
current_focus = NodePath("../PluginNameText")
-focus_stack = ExtResource("2_132sf")
+wrap_focus = false
[node name="PluginNameText" parent="ContentLayout" instance=ExtResource("1_qr2e8")]
unique_name_in_owner = true
diff --git a/core/ui/card_ui/settings/plugin_settings_menu.gd b/core/ui/card_ui/settings/plugin_settings_menu.gd
index 005697ff..a181dd54 100644
--- a/core/ui/card_ui/settings/plugin_settings_menu.gd
+++ b/core/ui/card_ui/settings/plugin_settings_menu.gd
@@ -108,7 +108,6 @@ func _config_settings_menu(plugin_settings: Control, plugin_content_container: C
# Create a focus group for the plugin settings
var plugin_focus_group := FocusGroup.new()
plugin_focus_group.name = "FocusGroup"
- plugin_focus_group.focus_stack = load("res://core/ui/card_ui/settings/settings_menu_focus.tres")
plugin_settings.add_child(plugin_focus_group)
for child in plugin_settings.get_children():
if not child is Control:
diff --git a/core/ui/card_ui/settings/plugin_settings_menu.tscn b/core/ui/card_ui/settings/plugin_settings_menu.tscn
index b821e324..4706c2d7 100644
--- a/core/ui/card_ui/settings/plugin_settings_menu.tscn
+++ b/core/ui/card_ui/settings/plugin_settings_menu.tscn
@@ -1,17 +1,18 @@
-[gd_scene load_steps=12 format=3 uid="uid://cliqk7lo4t8ao"]
+[gd_scene load_steps=13 format=3 uid="uid://cliqk7lo4t8ao"]
[ext_resource type="Script" path="res://core/ui/card_ui/settings/plugin_settings_menu.gd" id="1_1p4dc"]
-[ext_resource type="PackedScene" uid="uid://orey8uxm7v6v" path="res://core/systems/state/visibility_manager.tscn" id="2_d8jy7"]
+[ext_resource type="Script" path="res://core/systems/input/input_watcher.gd" id="2_5h71d"]
[ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="3_a1fhd"]
+[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="3_q7mwh"]
[ext_resource type="Resource" uid="uid://doxatxmp2mlty" path="res://assets/state/states/settings_plugins.tres" id="4_m0d4b"]
-[ext_resource type="PackedScene" uid="uid://bo077a5mwi7xl" path="res://core/ui/components/transition_fade_in.tscn" id="5_0c8si"]
+[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_ipise"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="6_fo0yk"]
-[ext_resource type="Resource" uid="uid://dgi16frh3mgj8" path="res://core/ui/card_ui/settings/settings_menu_focus.tres" id="7_8k476"]
+[ext_resource type="Script" path="res://core/systems/input/focus_group_setter.gd" id="7_wmvad"]
[ext_resource type="PackedScene" uid="uid://dithv38oqgy58" path="res://core/ui/components/section_label.tscn" id="8_rs83g"]
[ext_resource type="PackedScene" uid="uid://o0equu1tyr4s" path="res://core/ui/components/expandable_card.tscn" id="8_veuxu"]
[ext_resource type="PackedScene" uid="uid://b0cyl6fdqxevn" path="res://core/systems/input/scroller_joystick.tscn" id="10_nbqrj"]
-[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_oda2h"]
+[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xcwv0"]
resource_local_to_scene = true
bg_color = Color(0.105882, 0.109804, 0.141176, 1)
border_blend = true
@@ -30,11 +31,23 @@ follow_focus = true
horizontal_scroll_mode = 0
script = ExtResource("1_1p4dc")
-[node name="VisibilityManager" parent="." instance=ExtResource("2_d8jy7")]
+[node name="InputWatcher" type="Node" parent="."]
+script = ExtResource("2_5h71d")
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("3_q7mwh")]
state_machine = ExtResource("3_a1fhd")
+action = 2
+on_signal = "input_released"
+
+[node name="StateWatcher" parent="." instance=ExtResource("5_ipise")]
state = ExtResource("4_m0d4b")
-[node name="TransitionFadeIn" parent="VisibilityManager" instance=ExtResource("5_0c8si")]
+[node name="FocusGroupSetter" type="Node" parent="StateWatcher" node_paths=PackedStringArray("target")]
+script = ExtResource("7_wmvad")
+target = NodePath("../../MarginContainer/ContentContainer/FocusGroup")
+on_signal = "state_entered"
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 2
@@ -50,7 +63,6 @@ layout_mode = 2
[node name="FocusGroup" parent="MarginContainer/ContentContainer" instance=ExtResource("6_fo0yk")]
unique_name_in_owner = true
-focus_stack = ExtResource("7_8k476")
[node name="NoPluginsLabel" parent="MarginContainer/ContentContainer" instance=ExtResource("8_rs83g")]
unique_name_in_owner = true
@@ -60,6 +72,6 @@ horizontal_alignment = 1
[node name="ExpandableCard" parent="MarginContainer/ContentContainer" instance=ExtResource("8_veuxu")]
layout_mode = 2
-theme_override_styles/panel = SubResource("StyleBoxFlat_oda2h")
+theme_override_styles/panel = SubResource("StyleBoxFlat_xcwv0")
[node name="ScrollerJoystick" parent="." instance=ExtResource("10_nbqrj")]
diff --git a/core/ui/card_ui/settings/plugin_store_menu.gd b/core/ui/card_ui/settings/plugin_store_menu.gd
index 44626e11..0d1bfea7 100644
--- a/core/ui/card_ui/settings/plugin_store_menu.gd
+++ b/core/ui/card_ui/settings/plugin_store_menu.gd
@@ -3,26 +3,20 @@ extends ScrollContainer
signal plugin_store_loaded(plugin_items: Dictionary)
const plugin_store_card_scene: PackedScene = preload("res://core/ui/components/plugin_store_card.tscn")
-var PluginLoader := load("res://core/global/plugin_loader.tres") as PluginLoader
-var plugin_store_state := preload("res://assets/state/states/settings_plugin_store.tres") as State
-var NotificationManager := load("res://core/global/notification_manager.tres") as NotificationManager
+var plugin_loader := load("res://core/global/plugin_loader.tres") as PluginLoader
+var notification_manager := load("res://core/global/notification_manager.tres") as NotificationManager
var plugin_nodes := {}
-@onready var container := $%HFlowContainer
-@onready var focus_group := $%FocusGroup
-@onready var http_image := $HTTPImageFetcher
-@onready var settings_menu := $"../../../.." #verbose?
+@onready var container := $%HFlowContainer as HFlowContainer
+@onready var focus_group := $%FocusGroup as FocusGroup
+@onready var http_image := $HTTPImageFetcher as HTTPImageFetcher
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
plugin_store_loaded.connect(_on_plugin_store_loaded)
- plugin_store_state.state_entered.connect(_on_state_entered)
- PluginLoader.plugin_upgradable.connect(_on_plugin_upgradable)
- load_plugin_store_items()
-
-
-func _on_state_entered(_from: State) -> void:
+ plugin_loader.plugin_upgradable.connect(_on_plugin_upgradable)
+ visibility_changed.connect(load_plugin_store_items)
load_plugin_store_items()
@@ -30,7 +24,7 @@ func _on_state_entered(_from: State) -> void:
# signal with the loaded items
func load_plugin_store_items():
# Fetch available plugins from the plugin store
- var plugin_items = await PluginLoader.get_plugin_store_items()
+ var plugin_items = await plugin_loader.get_plugin_store_items()
plugin_store_loaded.emit(plugin_items)
@@ -89,4 +83,4 @@ func _on_plugin_upgradable(plugin_id: String, update_type: int) -> void:
if update_type == PluginLoader.update_type.UPDATE:
notify.text=("Plugin upgrade available: {0}".format([plugin_id]))
- NotificationManager.show(notify)
+ notification_manager.show(notify)
diff --git a/core/ui/card_ui/settings/plugin_store_menu.tscn b/core/ui/card_ui/settings/plugin_store_menu.tscn
index 48b4d32c..3d2ce7c8 100644
--- a/core/ui/card_ui/settings/plugin_store_menu.tscn
+++ b/core/ui/card_ui/settings/plugin_store_menu.tscn
@@ -1,14 +1,14 @@
[gd_scene load_steps=11 format=3 uid="uid://cf5bdr4sh2irb"]
[ext_resource type="Script" path="res://core/ui/card_ui/settings/plugin_store_menu.gd" id="1_o3w0b"]
-[ext_resource type="PackedScene" uid="uid://orey8uxm7v6v" path="res://core/systems/state/visibility_manager.tscn" id="2_vfjyq"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_stwp6"]
[ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="3_7o5pg"]
+[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="3_o8kc3"]
[ext_resource type="Resource" uid="uid://u6fyqt1ogrie" path="res://assets/state/states/settings_plugin_store.tres" id="4_moq21"]
[ext_resource type="Script" path="res://core/systems/network/http_image_fetcher.gd" id="5_5l7jt"]
-[ext_resource type="PackedScene" uid="uid://bo077a5mwi7xl" path="res://core/ui/components/transition_fade_in.tscn" id="5_ghad7"]
+[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_gh7kv"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="6_oyayo"]
-[ext_resource type="Resource" uid="uid://dgi16frh3mgj8" path="res://core/ui/card_ui/settings/settings_menu_focus.tres" id="7_qbq53"]
-[ext_resource type="PackedScene" uid="uid://cc7a35n2pqmmf" path="res://core/ui/components/plugin_store_card.tscn" id="8_g4q4p"]
+[ext_resource type="Script" path="res://core/systems/input/focus_group_setter.gd" id="7_u7s1j"]
[ext_resource type="PackedScene" uid="uid://b0cyl6fdqxevn" path="res://core/systems/input/scroller_joystick.tscn" id="10_c2505"]
[node name="PluginStoreMenu" type="ScrollContainer"]
@@ -22,11 +22,22 @@ horizontal_scroll_mode = 0
vertical_scroll_mode = 2
script = ExtResource("1_o3w0b")
-[node name="VisibilityManager" parent="." instance=ExtResource("2_vfjyq")]
+[node name="InputWatcher" parent="." instance=ExtResource("2_stwp6")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("3_o8kc3")]
state_machine = ExtResource("3_7o5pg")
+action = 2
+on_signal = "input_released"
+
+[node name="StateWatcher" parent="." instance=ExtResource("5_gh7kv")]
state = ExtResource("4_moq21")
-[node name="TransitionFadeIn" parent="VisibilityManager" instance=ExtResource("5_ghad7")]
+[node name="FocusGroupSetter" type="Node" parent="StateWatcher" node_paths=PackedStringArray("target")]
+script = ExtResource("7_u7s1j")
+target = NodePath("../../MarginContainer/HFlowContainer/FocusGroup")
+on_signal = "state_entered"
[node name="HTTPImageFetcher" type="Node" parent="."]
script = ExtResource("5_5l7jt")
@@ -47,18 +58,5 @@ theme_override_constants/v_separation = 42
[node name="FocusGroup" parent="MarginContainer/HFlowContainer" instance=ExtResource("6_oyayo")]
unique_name_in_owner = true
-focus_stack = ExtResource("7_qbq53")
-
-[node name="PluginStoreCard" parent="MarginContainer/HFlowContainer" instance=ExtResource("8_g4q4p")]
-layout_mode = 2
-
-[node name="PluginStoreCard2" parent="MarginContainer/HFlowContainer" instance=ExtResource("8_g4q4p")]
-layout_mode = 2
-
-[node name="PluginStoreCard3" parent="MarginContainer/HFlowContainer" instance=ExtResource("8_g4q4p")]
-layout_mode = 2
-
-[node name="PluginStoreCard4" parent="MarginContainer/HFlowContainer" instance=ExtResource("8_g4q4p")]
-layout_mode = 2
[node name="ScrollerJoystick" parent="." instance=ExtResource("10_c2505")]
diff --git a/core/ui/card_ui/settings/settings_menu.gd b/core/ui/card_ui/settings/settings_menu.gd
index d0b9a5e2..96105346 100644
--- a/core/ui/card_ui/settings/settings_menu.gd
+++ b/core/ui/card_ui/settings/settings_menu.gd
@@ -1,31 +1,10 @@
extends Control
-var state_machine := (
- preload("res://assets/state/state_machines/global_state_machine.tres") as StateMachine
-)
-var settings_state_machine := preload("res://assets/state/state_machines/settings_state_machine.tres") as StateMachine
-var settings_state := preload("res://assets/state/states/settings.tres") as State
var version := preload("res://core/global/version.tres") as Version
-@onready var version_label := $%VersionLabel
-@onready var setting_buttons_container: VBoxContainer = $%SettingButtonsContainer
-@onready var focus_group := $%FocusGroup as FocusGroup
-@onready var section_label := $%SectionLabel
+@onready var version_label := $%VersionLabel as Label
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
version_label.text = "v" + str(version.core)
- settings_state_machine.state_changed.connect(_on_settings_state_changed)
- settings_state.state_entered.connect(_on_state_entered)
-
-
-func _on_state_entered(_from: State) -> void:
- focus_group.grab_focus()
-
-
-func _on_settings_state_changed(_from: State, to: State) -> void:
- var text := to.name
- text = text.capitalize()
- text = text.replace("_", " ")
- section_label.text = text
diff --git a/core/ui/card_ui/settings/settings_menu.tscn b/core/ui/card_ui/settings/settings_menu.tscn
index daf0d20a..b6a2ff31 100644
--- a/core/ui/card_ui/settings/settings_menu.tscn
+++ b/core/ui/card_ui/settings/settings_menu.tscn
@@ -1,24 +1,25 @@
-[gd_scene load_steps=48 format=3 uid="uid://d2jiecrd5sw4s"]
+[gd_scene load_steps=47 format=3 uid="uid://d2jiecrd5sw4s"]
[ext_resource type="Script" path="res://core/ui/card_ui/settings/settings_menu.gd" id="1_x5bjx"]
-[ext_resource type="PackedScene" uid="uid://orey8uxm7v6v" path="res://core/systems/state/visibility_manager.tscn" id="2_xwevg"]
+[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="2_xfs0b"]
[ext_resource type="Resource" uid="uid://d3gp85f35oiw6" path="res://assets/state/states/settings.tres" id="3_an6bd"]
-[ext_resource type="Resource" uid="uid://e7bbebwf7guj" path="res://assets/state/states/main_menu.tres" id="4_3yyw8"]
-[ext_resource type="Resource" uid="uid://bmgs1ngma1523" path="res://assets/state/states/in_game_menu.tres" id="5_t0lm2"]
-[ext_resource type="Resource" uid="uid://bp807nlks8eq1" path="res://assets/state/states/quick_bar_menu.tres" id="6_l4ywh"]
-[ext_resource type="Resource" uid="uid://dja3m1mevv6xw" path="res://assets/state/states/osk.tres" id="7_mmjyu"]
-[ext_resource type="Resource" uid="uid://bw0mtk7sso8m2" path="res://assets/state/states/power_menu.tres" id="8_u3uua"]
-[ext_resource type="PackedScene" uid="uid://ccd4sw84h1qbc" path="res://core/systems/input/back_input_handler.tscn" id="9_op1y2"]
+[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="4_0rio2"]
+[ext_resource type="Script" path="res://core/systems/state/state_machine_watcher.gd" id="5_wwlng"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="9_rn3rv"]
+[ext_resource type="Script" path="res://core/systems/state/state_updater.gd" id="9_xkw8n"]
[ext_resource type="Texture2D" uid="uid://djy4rejy21s6g" path="res://icon.svg" id="10_k4kqs"]
+[ext_resource type="Resource" uid="uid://bcr6c0281lb5b" path="res://assets/state/state_machines/menu_state_machine.tres" id="11_5dh5k"]
[ext_resource type="PackedScene" uid="uid://d0u3rsa5qpj57" path="res://core/ui/components/subsection_label.tscn" id="11_s3f5p"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="13_atk08"]
[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="14_cid64"]
[ext_resource type="PackedScene" uid="uid://c71ayw7pcw6u6" path="res://core/ui/components/card_button.tscn" id="14_obber"]
[ext_resource type="Resource" uid="uid://dgi16frh3mgj8" path="res://core/ui/card_ui/settings/settings_menu_focus.tres" id="14_t8wf4"]
[ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="15_cb33u"]
+[ext_resource type="Script" path="res://core/systems/user_interface/tab_setter.gd" id="16_l8xyu"]
[ext_resource type="Resource" uid="uid://4n3376qdy3y3" path="res://assets/state/states/settings_general.tres" id="16_sfk74"]
[ext_resource type="PackedScene" uid="uid://uljtdvmuol3l" path="res://core/systems/input/focus_group_setter.tscn" id="18_axp35"]
[ext_resource type="Resource" uid="uid://bfx7p4n4fhjwd" path="res://assets/state/states/settings_display.tres" id="18_ljeud"]
+[ext_resource type="Script" path="res://core/systems/user_interface/text_setter.gd" id="18_wbniw"]
[ext_resource type="Resource" uid="uid://2efht48q7i6v" path="res://assets/state/states/settings_network.tres" id="19_l8vuu"]
[ext_resource type="Resource" uid="uid://blcfrofi5oawd" path="res://assets/state/states/settings_library.tres" id="19_wexln"]
[ext_resource type="Resource" uid="uid://bdvbnao6j0o18" path="res://assets/state/states/settings_audio.tres" id="20_5v03o"]
@@ -27,16 +28,14 @@
[ext_resource type="Resource" uid="uid://u6fyqt1ogrie" path="res://assets/state/states/settings_plugin_store.tres" id="22_ntyf3"]
[ext_resource type="Resource" uid="uid://cffkiat2h10ms" path="res://assets/state/states/settings_processes.tres" id="23_ilbjf"]
[ext_resource type="Resource" uid="uid://cn236k0ajm0uw" path="res://assets/state/states/settings_disks.tres" id="24_oep0o"]
-[ext_resource type="Resource" uid="uid://0f3pirade1ok" path="res://assets/state/states/settings_windows.tres" id="24_oscnu"]
[ext_resource type="Resource" uid="uid://cakuo0qwrrkk8" path="res://assets/state/states/settings_logging.tres" id="25_nijem"]
[ext_resource type="PackedScene" uid="uid://b0cyl6fdqxevn" path="res://core/systems/input/scroller_joystick.tscn" id="27_krt45"]
[ext_resource type="PackedScene" uid="uid://dithv38oqgy58" path="res://core/ui/components/section_label.tscn" id="27_x4tym"]
[ext_resource type="Resource" uid="uid://bcekyu20uvkxv" path="res://assets/state/states/settings_general_controller.tres" id="28_nmfnj"]
[ext_resource type="PackedScene" uid="uid://dsgrw1grwef4m" path="res://core/ui/card_ui/settings/general_settings_menu.tscn" id="29_8hwjo"]
-[ext_resource type="PackedScene" uid="uid://bo077a5mwi7xl" path="res://core/ui/components/transition_fade_in.tscn" id="29_grs7x"]
[ext_resource type="PackedScene" uid="uid://521da7e2cdxd" path="res://core/ui/common/settings/display_settings_menu.tscn" id="30_22tgj"]
[ext_resource type="PackedScene" uid="uid://cpss2bhdwm8t7" path="res://core/ui/common/settings/network_settings_menu.tscn" id="31_35x2h"]
-[ext_resource type="PackedScene" path="res://core/ui/common/settings/audio_settings_menu.tscn" id="32_dqi6v"]
+[ext_resource type="PackedScene" uid="uid://b4uvvjgnbxj25" path="res://core/ui/common/settings/audio_settings_menu.tscn" id="32_dqi6v"]
[ext_resource type="PackedScene" uid="uid://6ygxdkvh1hib" path="res://core/ui/card_ui/settings/bluetooth_settings_menu.tscn" id="33_a7xb2"]
[ext_resource type="PackedScene" uid="uid://drbp6ec8646v3" path="res://core/ui/card_ui/settings/library_settings_menu.tscn" id="34_lrrl0"]
[ext_resource type="PackedScene" uid="uid://cf5bdr4sh2irb" path="res://core/ui/card_ui/settings/plugin_store_menu.tscn" id="35_0u4bv"]
@@ -54,14 +53,7 @@ grow_begin = -10.0
grow_end = -10.0
vertical = true
-[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2j6m7"]
-resource_local_to_scene = true
-bg_color = Color(0.105882, 0.109804, 0.141176, 1)
-border_blend = true
-corner_radius_top_left = 10
-corner_radius_top_right = 10
-corner_radius_bottom_right = 10
-corner_radius_bottom_left = 10
+[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_a60ci"]
[node name="SettingsMenu" type="Control"]
layout_mode = 3
@@ -72,12 +64,45 @@ grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_x5bjx")
-[node name="VisibilityManager" parent="." instance=ExtResource("2_xwevg")]
+[node name="InputWatcher" parent="." instance=ExtResource("9_rn3rv")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("14_cid64")]
+state_machine = ExtResource("11_5dh5k")
+action = 2
+on_signal = "input_released"
+
+[node name="StateWatcher" parent="." instance=ExtResource("2_xfs0b")]
state = ExtResource("3_an6bd")
-visible_during = Array[Resource]([ExtResource("4_3yyw8"), ExtResource("5_t0lm2"), ExtResource("6_l4ywh"), ExtResource("7_mmjyu"), ExtResource("8_u3uua")])
-[node name="BackInputHandler" parent="." instance=ExtResource("9_op1y2")]
-process_input_during = Array[Resource("res://core/systems/state/state.gd")]([ExtResource("3_an6bd")])
+[node name="FadeEffect" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("4_0rio2")]
+target = NodePath("../..")
+on_signal = "state_entered"
+fade_out_signal = "state_exited"
+on_signal = "state_entered"
+
+[node name="FocusGroupSetter" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("18_axp35")]
+target = NodePath("../../MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/FocusGroup")
+on_signal = "state_entered"
+
+[node name="RefreshFocusGroupSetter" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("18_axp35")]
+target = NodePath("../../MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/FocusGroup")
+on_signal = "state_refreshed"
+
+[node name="StateUpdater" type="Node" parent="StateWatcher"]
+script = ExtResource("9_xkw8n")
+state_machine = ExtResource("15_cb33u")
+action = 6
+on_signal = "state_removed"
+
+[node name="StateMachineWatcher" type="Node" parent="."]
+script = ExtResource("5_wwlng")
+state_machine = ExtResource("15_cb33u")
+
+[node name="FocusGroupSetter" parent="StateMachineWatcher" node_paths=PackedStringArray("target") instance=ExtResource("18_axp35")]
+target = NodePath("../../MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/FocusGroup")
+on_signal = "emptied"
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 1
@@ -186,11 +211,18 @@ text = "General"
state_machine = ExtResource("15_cb33u")
state = ExtResource("16_sfk74")
action = 3
+on_signal = "button_up"
+
+[node name="TabSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/GeneralButton" node_paths=PackedStringArray("target")]
+script = ExtResource("16_l8xyu")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer")
on_signal = "focus_entered"
-[node name="FocusGroupSetter" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/GeneralButton" node_paths=PackedStringArray("target") instance=ExtResource("18_axp35")]
-target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/GeneralSettings/MarginContainer/VBoxContainer/VBoxContainer/FocusGroup")
-on_signal = "button_up"
+[node name="TextSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/GeneralButton" node_paths=PackedStringArray("target")]
+script = ExtResource("18_wbniw")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/TitleContainer/MarginContainer/HBoxContainer/SectionLabel")
+text = "General"
+on_signal = "focus_entered"
[node name="LibraryButton" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer" instance=ExtResource("14_obber")]
layout_mode = 2
@@ -200,11 +232,19 @@ text = "Library"
state_machine = ExtResource("15_cb33u")
state = ExtResource("19_wexln")
action = 3
+on_signal = "button_up"
+
+[node name="TabSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/LibraryButton" node_paths=PackedStringArray("target")]
+script = ExtResource("16_l8xyu")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer")
+tab_number = 1
on_signal = "focus_entered"
-[node name="FocusGroupSetter" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/LibraryButton" node_paths=PackedStringArray("target") instance=ExtResource("18_axp35")]
-target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/LibrarySettingsMenu/MarginContainer/VBoxContainer/FocusGroup")
-on_signal = "button_up"
+[node name="TextSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/LibraryButton" node_paths=PackedStringArray("target")]
+script = ExtResource("18_wbniw")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/TitleContainer/MarginContainer/HBoxContainer/SectionLabel")
+text = "Library"
+on_signal = "focus_entered"
[node name="HSeparator3" type="HSeparator" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer"]
layout_mode = 2
@@ -217,11 +257,19 @@ text = "Display"
state_machine = ExtResource("15_cb33u")
state = ExtResource("18_ljeud")
action = 3
+on_signal = "button_up"
+
+[node name="TabSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/DisplayButton" node_paths=PackedStringArray("target")]
+script = ExtResource("16_l8xyu")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer")
+tab_number = 2
on_signal = "focus_entered"
-[node name="FocusGroupSetter" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/DisplayButton" node_paths=PackedStringArray("target") instance=ExtResource("18_axp35")]
-target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/DisplaySettings/VBoxContainer/FocusGroup")
-on_signal = "pressed"
+[node name="TextSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/DisplayButton" node_paths=PackedStringArray("target")]
+script = ExtResource("18_wbniw")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/TitleContainer/MarginContainer/HBoxContainer/SectionLabel")
+text = "Display"
+on_signal = "focus_entered"
[node name="NetworkButton" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer" instance=ExtResource("14_obber")]
layout_mode = 2
@@ -231,11 +279,19 @@ text = "Network"
state_machine = ExtResource("15_cb33u")
state = ExtResource("19_l8vuu")
action = 3
+on_signal = "button_up"
+
+[node name="TabSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/NetworkButton" node_paths=PackedStringArray("target")]
+script = ExtResource("16_l8xyu")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer")
+tab_number = 3
on_signal = "focus_entered"
-[node name="FocusGroupSetter" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/NetworkButton" node_paths=PackedStringArray("target") instance=ExtResource("18_axp35")]
-target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/NetworkSettings/MarginContainer/VBoxContainer/FocusGroup")
-on_signal = "pressed"
+[node name="TextSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/NetworkButton" node_paths=PackedStringArray("target")]
+script = ExtResource("18_wbniw")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/TitleContainer/MarginContainer/HBoxContainer/SectionLabel")
+text = "Network"
+on_signal = "focus_entered"
[node name="BluetoothButton" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer" instance=ExtResource("14_obber")]
layout_mode = 2
@@ -245,11 +301,19 @@ text = "Bluetooth"
state_machine = ExtResource("15_cb33u")
state = ExtResource("21_cbayw")
action = 3
+on_signal = "button_up"
+
+[node name="TabSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/BluetoothButton" node_paths=PackedStringArray("target")]
+script = ExtResource("16_l8xyu")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer")
+tab_number = 4
on_signal = "focus_entered"
-[node name="FocusGroupSetter" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/BluetoothButton" node_paths=PackedStringArray("target") instance=ExtResource("18_axp35")]
-target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/BluetoothSettingsMenu/MarginContainer/ScrollContainer/AvailableContainer/VBoxContainer/FocusGroup")
-on_signal = "pressed"
+[node name="TextSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/BluetoothButton" node_paths=PackedStringArray("target")]
+script = ExtResource("18_wbniw")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/TitleContainer/MarginContainer/HBoxContainer/SectionLabel")
+text = "Bluetooth"
+on_signal = "focus_entered"
[node name="AudioButton" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer" instance=ExtResource("14_obber")]
layout_mode = 2
@@ -259,11 +323,19 @@ text = "Audio"
state_machine = ExtResource("15_cb33u")
state = ExtResource("20_5v03o")
action = 3
+on_signal = "button_up"
+
+[node name="TabSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/AudioButton" node_paths=PackedStringArray("target")]
+script = ExtResource("16_l8xyu")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer")
+tab_number = 5
on_signal = "focus_entered"
-[node name="FocusGroupSetter" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/AudioButton" node_paths=PackedStringArray("target") instance=ExtResource("18_axp35")]
-target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/AudioSettingsMenu/VBoxContainer/FocusGroup")
-on_signal = "pressed"
+[node name="TextSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/AudioButton" node_paths=PackedStringArray("target")]
+script = ExtResource("18_wbniw")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/TitleContainer/MarginContainer/HBoxContainer/SectionLabel")
+text = "Audio"
+on_signal = "focus_entered"
[node name="DisksButton" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer" instance=ExtResource("14_obber")]
layout_mode = 2
@@ -273,11 +345,19 @@ text = "Disks"
state_machine = ExtResource("15_cb33u")
state = ExtResource("24_oep0o")
action = 3
+on_signal = "button_up"
+
+[node name="TabSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/DisksButton" node_paths=PackedStringArray("target")]
+script = ExtResource("16_l8xyu")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer")
+tab_number = 6
on_signal = "focus_entered"
-[node name="FocusGroupSetter" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/DisksButton" node_paths=PackedStringArray("target") instance=ExtResource("18_axp35")]
-target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/DisksMenu/MarginContainer/DriveCardContainer/FocusGroup")
-on_signal = "pressed"
+[node name="TextSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/DisksButton" node_paths=PackedStringArray("target")]
+script = ExtResource("18_wbniw")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/TitleContainer/MarginContainer/HBoxContainer/SectionLabel")
+text = "Disks"
+on_signal = "focus_entered"
[node name="HSeparator4" type="HSeparator" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer"]
layout_mode = 2
@@ -290,11 +370,19 @@ text = "Plugins"
state_machine = ExtResource("15_cb33u")
state = ExtResource("21_6lg2v")
action = 3
+on_signal = "button_up"
+
+[node name="TabSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/PluginsButton" node_paths=PackedStringArray("target")]
+script = ExtResource("16_l8xyu")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer")
+tab_number = 7
on_signal = "focus_entered"
-[node name="FocusGroupSetter" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/PluginsButton" node_paths=PackedStringArray("target") instance=ExtResource("18_axp35")]
-target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/PluginSettingsMenu/MarginContainer/ContentContainer/FocusGroup")
-on_signal = "pressed"
+[node name="TextSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/PluginsButton" node_paths=PackedStringArray("target")]
+script = ExtResource("18_wbniw")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/TitleContainer/MarginContainer/HBoxContainer/SectionLabel")
+text = "Plugins"
+on_signal = "focus_entered"
[node name="PluginStoreButton" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer" instance=ExtResource("14_obber")]
layout_mode = 2
@@ -304,11 +392,19 @@ text = "Plugin Store"
state_machine = ExtResource("15_cb33u")
state = ExtResource("22_ntyf3")
action = 3
+on_signal = "button_up"
+
+[node name="TabSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/PluginStoreButton" node_paths=PackedStringArray("target")]
+script = ExtResource("16_l8xyu")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer")
+tab_number = 8
on_signal = "focus_entered"
-[node name="FocusGroupSetter" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/PluginStoreButton" node_paths=PackedStringArray("target") instance=ExtResource("18_axp35")]
-target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/PluginStoreMenu/MarginContainer/HFlowContainer/FocusGroup")
-on_signal = "pressed"
+[node name="TextSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/PluginStoreButton" node_paths=PackedStringArray("target")]
+script = ExtResource("18_wbniw")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/TitleContainer/MarginContainer/HBoxContainer/SectionLabel")
+text = "Plugin Store"
+on_signal = "focus_entered"
[node name="HSeparator5" type="HSeparator" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer"]
layout_mode = 2
@@ -321,24 +417,19 @@ text = "Processes"
state_machine = ExtResource("15_cb33u")
state = ExtResource("23_ilbjf")
action = 3
-on_signal = "focus_entered"
-
-[node name="FocusGroupSetter" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/ProcessesButton" instance=ExtResource("18_axp35")]
-on_signal = "pressed"
-
-[node name="WindowsButton" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer" instance=ExtResource("14_obber")]
-visible = false
-layout_mode = 2
-text = "Windows"
+on_signal = "button_up"
-[node name="StateUpdater" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/WindowsButton" instance=ExtResource("14_cid64")]
-state_machine = ExtResource("15_cb33u")
-state = ExtResource("24_oscnu")
-action = 3
+[node name="TabSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/ProcessesButton" node_paths=PackedStringArray("target")]
+script = ExtResource("16_l8xyu")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer")
+tab_number = 9
on_signal = "focus_entered"
-[node name="FocusGroupSetter" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/WindowsButton" instance=ExtResource("18_axp35")]
-on_signal = "pressed"
+[node name="TextSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/ProcessesButton" node_paths=PackedStringArray("target")]
+script = ExtResource("18_wbniw")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/TitleContainer/MarginContainer/HBoxContainer/SectionLabel")
+text = "Processes"
+on_signal = "focus_entered"
[node name="LoggingButton" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer" instance=ExtResource("14_obber")]
layout_mode = 2
@@ -348,11 +439,19 @@ text = "Logging"
state_machine = ExtResource("15_cb33u")
state = ExtResource("25_nijem")
action = 3
+on_signal = "button_up"
+
+[node name="TabSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/LoggingButton" node_paths=PackedStringArray("target")]
+script = ExtResource("16_l8xyu")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer")
+tab_number = 10
on_signal = "focus_entered"
-[node name="FocusGroupSetter" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/LoggingButton" node_paths=PackedStringArray("target") instance=ExtResource("18_axp35")]
-target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/LoggingSettings/MarginContainer/VBoxContainer/FocusGroup")
-on_signal = "pressed"
+[node name="TextSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/LoggingButton" node_paths=PackedStringArray("target")]
+script = ExtResource("18_wbniw")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/TitleContainer/MarginContainer/HBoxContainer/SectionLabel")
+text = "Logging"
+on_signal = "focus_entered"
[node name="ControllerButton" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer" instance=ExtResource("14_obber")]
layout_mode = 2
@@ -362,11 +461,19 @@ text = "Controllers"
state_machine = ExtResource("15_cb33u")
state = ExtResource("28_nmfnj")
action = 3
+on_signal = "button_up"
+
+[node name="TabSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/ControllerButton" node_paths=PackedStringArray("target")]
+script = ExtResource("16_l8xyu")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer")
+tab_number = 11
on_signal = "focus_entered"
-[node name="FocusGroupSetter" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/ControllerButton" node_paths=PackedStringArray("target") instance=ExtResource("18_axp35")]
-target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/ContentContainer/MarginContainer/GeneralControllerSettings/MarginContainer/VBoxContainer/FocusGroup")
-on_signal = "pressed"
+[node name="TextSetter" type="Node" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer/MarginContainer/SettingButtonsContainer/ControllerButton" node_paths=PackedStringArray("target")]
+script = ExtResource("18_wbniw")
+target = NodePath("../../../../../../../../../ContentContainer/VBoxContainer/TitleContainer/MarginContainer/HBoxContainer/SectionLabel")
+text = "Controllers"
+on_signal = "focus_entered"
[node name="ScrollerJoystick" parent="MarginContainer/HBoxContainer/MenuContainer/VBoxContainer/ButtonContainer/MarginContainer/ScrollContainer" instance=ExtResource("27_krt45")]
@@ -411,113 +518,55 @@ theme_override_constants/margin_top = 5
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 5
-[node name="GeneralSettings" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer" instance=ExtResource("29_8hwjo")]
+[node name="TabContainer" type="TabContainer" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer"]
layout_mode = 2
+size_flags_horizontal = 3
+theme_override_styles/panel = SubResource("StyleBoxEmpty_a60ci")
+tabs_visible = false
-[node name="VisibilityManager" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/GeneralSettings" instance=ExtResource("2_xwevg")]
-state_machine = ExtResource("15_cb33u")
-state = ExtResource("16_sfk74")
-
-[node name="TransitionFadeIn" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/GeneralSettings/VisibilityManager" instance=ExtResource("29_grs7x")]
+[node name="GeneralSettings" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("29_8hwjo")]
+layout_mode = 2
-[node name="LibrarySettingsMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer" instance=ExtResource("34_lrrl0")]
+[node name="LibrarySettingsMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("34_lrrl0")]
visible = false
layout_mode = 2
-[node name="DisplaySettings" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer" instance=ExtResource("30_22tgj")]
+[node name="DisplaySettings" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("30_22tgj")]
visible = false
layout_mode = 2
-[node name="VisibilityManager" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/DisplaySettings" instance=ExtResource("2_xwevg")]
-state_machine = ExtResource("15_cb33u")
-state = ExtResource("18_ljeud")
-
-[node name="TransitionFadeIn" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/DisplaySettings/VisibilityManager" instance=ExtResource("29_grs7x")]
-
-[node name="NetworkSettings" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer" instance=ExtResource("31_35x2h")]
+[node name="NetworkSettings" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("31_35x2h")]
visible = false
layout_mode = 2
-[node name="VisibilityManager" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/NetworkSettings" instance=ExtResource("2_xwevg")]
-state_machine = ExtResource("15_cb33u")
-state = ExtResource("19_l8vuu")
-
-[node name="TransitionFadeIn" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/NetworkSettings/VisibilityManager" instance=ExtResource("29_grs7x")]
-
-[node name="BluetoothSettingsMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer" instance=ExtResource("33_a7xb2")]
+[node name="BluetoothSettingsMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("33_a7xb2")]
visible = false
layout_mode = 2
-[node name="EnableToggle" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/BluetoothSettingsMenu/MarginContainer/ScrollContainer/AvailableContainer/VBoxContainer" index="1"]
-button_pressed = true
-
-[node name="DiscoverToggle" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/BluetoothSettingsMenu/MarginContainer/ScrollContainer/AvailableContainer/VBoxContainer" index="2"]
-button_pressed = true
-
-[node name="VisibilityManager" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/BluetoothSettingsMenu" instance=ExtResource("2_xwevg")]
-state_machine = ExtResource("15_cb33u")
-state = ExtResource("21_cbayw")
-
-[node name="TransitionFadeIn" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/BluetoothSettingsMenu/VisibilityManager" instance=ExtResource("29_grs7x")]
-
-[node name="AudioSettingsMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer" instance=ExtResource("32_dqi6v")]
+[node name="AudioSettingsMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("32_dqi6v")]
visible = false
layout_mode = 2
-[node name="VisibilityManager" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/AudioSettingsMenu" instance=ExtResource("2_xwevg")]
-state_machine = ExtResource("15_cb33u")
-state = ExtResource("20_5v03o")
-
-[node name="TransitionFadeIn" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/AudioSettingsMenu/VisibilityManager" instance=ExtResource("29_grs7x")]
-
-[node name="DisksMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer" instance=ExtResource("40_lyyu6")]
+[node name="DisksMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("40_lyyu6")]
visible = false
layout_mode = 2
-[node name="PluginSettingsMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer" instance=ExtResource("35_dooq8")]
+[node name="PluginSettingsMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("35_dooq8")]
visible = false
layout_mode = 2
-[node name="ExpandableCard" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/PluginSettingsMenu/MarginContainer/ContentContainer" index="2"]
-theme_override_styles/panel = SubResource("StyleBoxFlat_2j6m7")
-
-[node name="PluginStoreMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer" instance=ExtResource("35_0u4bv")]
+[node name="PluginStoreMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("35_0u4bv")]
visible = false
layout_mode = 2
-[node name="ProcessesMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer" instance=ExtResource("36_hnokx")]
+[node name="ProcessesMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("36_hnokx")]
visible = false
layout_mode = 2
-[node name="VisibilityManager" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/ProcessesMenu" instance=ExtResource("2_xwevg")]
-state_machine = ExtResource("15_cb33u")
-state = ExtResource("23_ilbjf")
-
-[node name="TransitionFadeIn" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/ProcessesMenu/VisibilityManager" instance=ExtResource("29_grs7x")]
-
-[node name="LoggingSettings" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer" instance=ExtResource("37_iwtxk")]
+[node name="LoggingSettings" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("37_iwtxk")]
visible = false
layout_mode = 2
-[node name="VisibilityManager" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/LoggingSettings" instance=ExtResource("2_xwevg")]
-state_machine = ExtResource("15_cb33u")
-state = ExtResource("25_nijem")
-
-[node name="TransitionFadeIn" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/LoggingSettings/VisibilityManager" instance=ExtResource("29_grs7x")]
-
-[node name="GeneralControllerSettings" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer" instance=ExtResource("40_uqw2r")]
+[node name="GeneralControllerSettings" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("40_uqw2r")]
visible = false
layout_mode = 2
-
-[editable path="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/GeneralSettings"]
-[editable path="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/LibrarySettingsMenu"]
-[editable path="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/DisplaySettings"]
-[editable path="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/NetworkSettings"]
-[editable path="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/BluetoothSettingsMenu"]
-[editable path="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/AudioSettingsMenu"]
-[editable path="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/DisksMenu"]
-[editable path="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/PluginSettingsMenu"]
-[editable path="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/PluginStoreMenu"]
-[editable path="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/ProcessesMenu"]
-[editable path="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/LoggingSettings"]
-[editable path="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/GeneralControllerSettings"]
diff --git a/core/ui/card_ui_overlay_mode/card_ui_overlay_mode.gd b/core/ui/card_ui_overlay_mode/card_ui_overlay_mode.gd
index dc837468..c83c7c43 100644
--- a/core/ui/card_ui_overlay_mode/card_ui_overlay_mode.gd
+++ b/core/ui/card_ui_overlay_mode/card_ui_overlay_mode.gd
@@ -8,6 +8,10 @@ var input_plumber := preload("res://core/systems/input/input_plumber.tres") as I
var state_machine := (
preload("res://assets/state/state_machines/global_state_machine.tres") as StateMachine
)
+var menu_state_machine := preload("res://assets/state/state_machines/menu_state_machine.tres") as StateMachine
+var popup_state_machine := preload("res://assets/state/state_machines/popup_state_machine.tres") as StateMachine
+var menu_state := preload("res://assets/state/states/menu.tres") as State
+var popup_state := preload("res://assets/state/states/popup.tres") as State
var quick_bar_state = preload("res://assets/state/states/quick_bar_menu.tres") as State
var settings_state = preload("res://assets/state/states/settings.tres") as State
var gamepad_state = preload("res://assets/state/states/gamepad_settings.tres") as State
@@ -18,8 +22,7 @@ var PID: int = OS.get_process_id()
var args := OS.get_cmdline_user_args()
var cmdargs := OS.get_cmdline_args()
var display := Gamescope.XWAYLAND.OGUI
-var game_running: bool = false
-var overlay_window_id: int
+var overlay_window_id := gamescope.get_window_id(PID, display)
var underlay_log: FileAccess
var underlay_process: int
var underlay_window_id: int
@@ -31,16 +34,13 @@ var logger := Log.get_logger("Main", Log.LEVEL.INFO)
## Sets up overlay mode.
func _init():
-
- logger.debug("Overlay start _init")
- # Tell gamescope that we're an overlay
- if overlay_window_id < 0:
+ # Discover the OpenGamepadUI window ID
+ if overlay_window_id <= 0:
logger.error("Unable to detect Window ID. Overlay is not going to work!")
- logger.debug("Found primary window id: {0}".format([overlay_window_id]))
+ logger.info("Found primary window id: {0}".format([overlay_window_id]))
# Back button wont close windows without this. OverlayInputManager prevents poping the last state.
state_machine.push_state(base_state)
- state_machine.state_changed.connect(_on_no_state)
# Ensure LaunchManager doesn't override our custom overlay management l
launch_manager.should_manage_overlay = false
@@ -53,15 +53,9 @@ func _init():
var plugin_manager := plugin_manager_scene.instantiate()
add_child(plugin_manager)
- # Listen for home state changes
- base_state.state_entered.connect(_on_base_state_entered)
- base_state.state_exited.connect(_on_base_state_exited)
- logger.debug("Overlay finish _init")
-
## Starts the --overlay-mode session.
func _ready() -> void:
- logger.debug("Overlay start _ready")
# Workaround old versions that don't pass launch args via update pack
# TODO: Parse the parent PID's CLI args and use those instead.
if "--skip-update-pack" in cmdargs and args.size() == 0:
@@ -90,7 +84,6 @@ func _ready() -> void:
# Set up the session
logger.debug("Setup Overlay Mode")
_setup_overlay_mode(args)
- launch_manager.app_switched.connect(_on_app_switched)
# Set the theme if one was set
logger.debug("Setup Theme")
@@ -114,10 +107,40 @@ func _ready() -> void:
## Finds needed PID's and global vars, Starts the user defined program as an
## underlay process.
func _setup_overlay_mode(args: Array) -> void:
- overlay_window_id = gamescope.get_window_id(PID, display)
- for state in managed_states:
- state.state_entered.connect(_on_window_open)
- state.state_exited.connect(_on_window_closed)
+ # Always push the base state if we end up with an empty stack.
+ var on_states_emptied := func():
+ state_machine.push_state.call_deferred(base_state)
+ state_machine.emptied.connect(on_states_emptied)
+
+ # Whenever the menu state is refreshed, refresh the menu state machine to
+ # re-grab focus.
+ var on_menu_refreshed := func():
+ menu_state_machine.refresh()
+ menu_state.refreshed.connect(on_menu_refreshed)
+
+ # Whenever the menu state is entered, refresh the menu state machine to
+ # re-grab focus
+ var on_menu_state_entered := func(_from: State):
+ menu_state_machine.refresh()
+ menu_state.state_entered.connect(on_menu_state_entered)
+ var on_menu_state_removed := func():
+ menu_state_machine.clear_states()
+ menu_state.state_removed.connect(on_menu_state_removed)
+ var on_menu_states_empty := func():
+ state_machine.remove_state(menu_state)
+ menu_state_machine.emptied.connect(on_menu_states_empty)
+
+ # Whenever an popup state is pushed, update the global state
+ var on_popup_state_changed := func(_from: State, to: State):
+ if to:
+ state_machine.push_state(popup_state)
+ else:
+ state_machine.remove_state(popup_state)
+ popup_state_machine.state_changed.connect(on_popup_state_changed)
+
+ # Show/hide the overlay when we enter/exit the in-game state
+ base_state.state_entered.connect(_on_base_state_entered)
+ base_state.state_exited.connect(_on_base_state_exited)
# Don't crash if we're not launching another program.
if args == []:
@@ -130,9 +153,6 @@ func _setup_overlay_mode(args: Array) -> void:
var log_path := OS.get_environment("HOME") + "/.underlay-stdout.log"
_start_underlay_process(args, log_path)
- # Establish overlay focus in gamescope.
- gamescope.set_overlay(overlay_window_id, 1, display)
-
# Remove unneeded/conflicting elements from default menues
var remove_list: PackedStringArray = ["PerformanceCard", "NotifyButton", "HelpButton", "VolumeSlider", "BrightnessSlider", "PerGameToggle"]
_remove_children(remove_list, quick_bar_menu)
@@ -218,13 +238,15 @@ func _find_underlay_window_id() -> void:
for window in all_windows:
if window == overlay_window_id:
continue
- if gamescope.has_xprop(window, "STEAM_OVERLAY", display):
+ if gamescope.has_xprop(window, "STEAM_NOTIFICATION", display):
underlay_window_id = window
- logger.debug("Found steam! " + str(underlay_window_id))
+ logger.info("Found steam! " + str(underlay_window_id))
+ gamescope.focused_app_updated.connect(_on_app_focus_changed)
break
# If we didn't find the window_id, set up a tiemr to loop back and try again.
- if not underlay_window_id:
+ if underlay_window_id <= 0:
+ logger.debug("Unable to find steam PID. Checking again in 1 second...")
var underlay_window_timer := Timer.new()
underlay_window_timer.set_one_shot(true)
underlay_window_timer.set_timer_process_callback(Timer.TIMER_PROCESS_IDLE)
@@ -233,37 +255,28 @@ func _find_underlay_window_id() -> void:
underlay_window_timer.start()
-## Called when "quick_bar_state" is entered.
-func _on_window_open(from: State) -> void:
- if from:
- logger.info("Quick bar open state: " + from.name)
- if game_running:
- gamescope.set_overlay(overlay_window_id, 1, display)
- gamescope.set_overlay(underlay_window_id, 0, display)
-
-
-## Called when "quick_bar_state" is exited.
-func _on_window_closed(to: State) -> void:
- if to:
- logger.info("Quick bar closed state: " + to.name)
- if game_running:
- gamescope.set_overlay(overlay_window_id, 0, display)
- gamescope.set_overlay(underlay_window_id, 1, display)
-
-
-## Called when a RunningApp is changed to another RunningApp, or to no app.
-## Changes the overlay focus to the overlay if no game is running or to the
-## underlay process if a game is running.
-func _on_app_switched(_from: RunningApp, to: RunningApp) -> void:
- if to == null:
- # Establish overlay focus in gamescope.
- gamescope.set_overlay(overlay_window_id, 1, display)
- gamescope.set_overlay(underlay_window_id, 0, display)
- game_running = false
- return
+## Called when the base state is entered.
+func _on_base_state_entered(from: State) -> void:
+ # Manage input focus
+ input_plumber.set_intercept_mode(InputPlumber.INTERCEPT_MODE.PASS)
+ if gamescope.set_input_focus(overlay_window_id, 0) != OK:
+ logger.error("Unable to set STEAM_INPUT_FOCUS atom!")
+
+ # Manage overlay
gamescope.set_overlay(overlay_window_id, 0, display)
gamescope.set_overlay(underlay_window_id, 1, display)
- game_running = true
+
+
+## Called when a the base state is exited.
+func _on_base_state_exited(to: State) -> void:
+ # Manage input focus
+ input_plumber.set_intercept_mode(InputPlumber.INTERCEPT_MODE.ALL)
+ if gamescope.set_input_focus(overlay_window_id, 1) != OK:
+ logger.error("Unable to set STEAM_INPUT_FOCUS atom!")
+
+ # Manage overlay
+ gamescope.set_overlay(overlay_window_id, 1, display)
+ gamescope.set_overlay(underlay_window_id, 0, display)
## Verifies steam is still running by checking for the steam overlay, closes otherwise.
@@ -276,21 +289,16 @@ func _check_exit() -> void:
get_tree().quit()
-## Sets gamescope input focus to on so the user can interact with the UI
-func _on_base_state_exited(_to: State) -> void:
- input_plumber.set_intercept_mode(InputPlumber.INTERCEPT_MODE.ALL)
- if gamescope.set_input_focus(overlay_window_id, 1) != OK:
- logger.error("Unable to set STEAM_INPUT_FOCUS atom!")
-
-
-## Sets gamescope input focus to off so the user can interact with the game
-func _on_base_state_entered(_from: State) -> void:
- input_plumber.set_intercept_mode(InputPlumber.INTERCEPT_MODE.PASS)
- if gamescope.set_input_focus(overlay_window_id, 0) != OK:
- logger.error("Unable to set STEAM_INPUT_FOCUS atom!")
-
+func _on_app_focus_changed(_from: int, to: int) -> void:
+ # On focus to the steam overlay, ensure the default profile is used.
+ logger.warn("Changed window focus to app ID:", to)
+ if to in [Gamescope.OVERLAY_GAME_ID, 0]:
+ launch_manager.set_gamepad_profile("")
+ return
-## Ensures there is always a state on the state stack.
-func _on_no_state(_from: State, to: State) -> void:
- if state_machine.stack_length() == 0:
- state_machine.push_state(base_state)
+ # On focus back to the game, ensure the game profile is set
+ var _current_app := launch_manager.get_current_app()
+ if not _current_app:
+ logger.error("Unable to set gamepad profile. Current app is NULL and we aren't focused")
+ return
+ launch_manager.set_app_gamepad_profile(_current_app)
diff --git a/core/ui/common/debug/processes_menu.tscn b/core/ui/common/debug/processes_menu.tscn
index 5821b3ce..363393f6 100644
--- a/core/ui/common/debug/processes_menu.tscn
+++ b/core/ui/common/debug/processes_menu.tscn
@@ -1,10 +1,15 @@
-[gd_scene load_steps=6 format=3 uid="uid://bfu4edkk5dqt2"]
+[gd_scene load_steps=11 format=3 uid="uid://bfu4edkk5dqt2"]
[ext_resource type="Script" path="res://core/ui/common/debug/processes_menu.gd" id="1_i5gve"]
[ext_resource type="PackedScene" uid="uid://dithv38oqgy58" path="res://core/ui/components/section_label.tscn" id="2_t6r8e"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_tbk5a"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="3_2ttlw"]
-[ext_resource type="Resource" uid="uid://dgi16frh3mgj8" path="res://core/ui/card_ui/settings/settings_menu_focus.tres" id="4_1h6bm"]
+[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="3_10x0l"]
+[ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="4_dva4v"]
+[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_cqojd"]
[ext_resource type="Script" path="res://core/systems/input/focus_group.gd" id="5_nqvca"]
+[ext_resource type="Resource" uid="uid://cffkiat2h10ms" path="res://assets/state/states/settings_processes.tres" id="6_kfvag"]
+[ext_resource type="Script" path="res://core/systems/input/focus_group_setter.gd" id="7_use1k"]
[node name="ProcessesMenu" type="Control"]
layout_mode = 3
@@ -15,6 +20,23 @@ grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_i5gve")
+[node name="InputWatcher" parent="." instance=ExtResource("2_tbk5a")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("3_10x0l")]
+state_machine = ExtResource("4_dva4v")
+action = 2
+on_signal = "input_released"
+
+[node name="StateWatcher" parent="." instance=ExtResource("5_cqojd")]
+state = ExtResource("6_kfvag")
+
+[node name="FocusGroupSetter" type="Node" parent="StateWatcher" node_paths=PackedStringArray("target")]
+script = ExtResource("7_use1k")
+target = NodePath("../../MarginContainer/VBoxContainer/HBoxContainer/FocusGroup")
+on_signal = "state_entered"
+
[node name="RefreshTimer" type="Timer" parent="."]
[node name="MarginContainer" type="MarginContainer" parent="."]
@@ -38,7 +60,6 @@ size_flags_vertical = 3
[node name="FocusGroup" parent="MarginContainer/VBoxContainer/HBoxContainer" node_paths=PackedStringArray("current_focus", "focus_neighbor_right") instance=ExtResource("3_2ttlw")]
current_focus = NodePath("../PIDInspector")
-focus_stack = ExtResource("4_1h6bm")
focus_neighbor_right = NodePath("../MarginContainer/VBoxContainer/FocusGroup")
[node name="PIDInspector" type="Tree" parent="MarginContainer/VBoxContainer/HBoxContainer"]
diff --git a/core/ui/common/osk/on_screen_keyboard.gd b/core/ui/common/osk/on_screen_keyboard.gd
index b332bb36..0d26da6d 100644
--- a/core/ui/common/osk/on_screen_keyboard.gd
+++ b/core/ui/common/osk/on_screen_keyboard.gd
@@ -10,6 +10,12 @@ signal closed
const Gamescope := preload("res://core/global/gamescope.tres")
const key_scene := preload("res://core/ui/components/button.tscn")
+
+## State machine to use to switch menu states in response to input events.
+var popup_state_machine := (
+ preload("res://assets/state/state_machines/popup_state_machine.tres") as StateMachine
+)
+var osk_state := preload("res://assets/state/states/osk.tres") as State
var input_icons := load("res://core/systems/input/input_icon_manager.tres") as InputIconManager
# Different states of mode shift (i.e. when the user presses "shift" or "caps")
@@ -28,6 +34,9 @@ var logger := Log.get_logger("OSK")
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
+ # Don't run in the editor
+ if Engine.is_editor_hint():
+ return
if not layout:
logger.warn("No keyboard layout was defined")
return
@@ -155,8 +164,6 @@ func _nearest_neighbor(idx: int, from_size: int, to_size: int) -> int:
# Opens the OSK with the given context. The keyboard context determines where
# keyboard inputs should go, and how to handle submits.
func open() -> void:
- visible = true
-
# Grab focus on the first key in the first row
for r in rows_container.get_children():
var row := r as HBoxContainer
@@ -190,7 +197,7 @@ func open() -> void:
# Closes the OSK
func close() -> void:
- visible = false
+ popup_state_machine.remove_state(osk_state)
closed.emit()
@@ -209,13 +216,16 @@ func set_mode_shift(mode: MODE_SHIFT) -> void:
# Handle gamepad keyboard shortcuts
func _input(event: InputEvent) -> void:
- if not visible:
+ if not is_visible_in_tree():
return
- if event.is_action_pressed("ogui_east"):
+ # Block propagation of any input if guide is held, avoids conflicts.
+ if Input.is_action_pressed("ogui_guide"):
+ return
+ if event.is_action_released("ogui_east"):
instance.close()
get_viewport().set_input_as_handled()
return
- if event.is_action_pressed("ogui_north"):
+ if event.is_action_released("ogui_north"):
var key := KeyboardKeyConfig.new()
key.input = InputEventKey.new()
key.input.keycode = KEY_BACKSPACE
diff --git a/core/ui/common/settings/audio_settings_menu.tscn b/core/ui/common/settings/audio_settings_menu.tscn
index 078e257a..5cf0ea86 100644
--- a/core/ui/common/settings/audio_settings_menu.tscn
+++ b/core/ui/common/settings/audio_settings_menu.tscn
@@ -1,11 +1,16 @@
-[gd_scene load_steps=7 format=3]
+[gd_scene load_steps=12 format=3 uid="uid://b4uvvjgnbxj25"]
[ext_resource type="Script" path="res://core/ui/common/settings/audio_settings_menu.gd" id="1_hjnkk"]
[ext_resource type="PackedScene" uid="uid://dithv38oqgy58" path="res://core/ui/components/section_label.tscn" id="1_x5cm4"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="2_73ivm"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_n36nh"]
[ext_resource type="PackedScene" uid="uid://cemxrvvjgm4g" path="res://core/ui/components/slider.tscn" id="2_qsi2y"]
-[ext_resource type="Resource" uid="uid://dgi16frh3mgj8" path="res://core/ui/card_ui/settings/settings_menu_focus.tres" id="3_pfahh"]
[ext_resource type="PackedScene" uid="uid://xei5afwefxud" path="res://core/ui/components/dropdown.tscn" id="3_pixnq"]
+[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="3_r3dmc"]
+[ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="4_sfspm"]
+[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_06jyg"]
+[ext_resource type="Resource" uid="uid://bdvbnao6j0o18" path="res://assets/state/states/settings_audio.tres" id="6_v2pqc"]
+[ext_resource type="Script" path="res://core/systems/input/focus_group_setter.gd" id="7_l6bmd"]
[node name="AudioSettingsMenu" type="MarginContainer"]
anchors_preset = 15
@@ -19,13 +24,29 @@ theme_override_constants/margin_right = 5
theme_override_constants/margin_bottom = 5
script = ExtResource("1_hjnkk")
+[node name="InputWatcher" parent="." instance=ExtResource("2_n36nh")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("3_r3dmc")]
+state_machine = ExtResource("4_sfspm")
+action = 2
+on_signal = "input_released"
+
+[node name="StateWatcher" parent="." instance=ExtResource("5_06jyg")]
+state = ExtResource("6_v2pqc")
+
+[node name="FocusGroupSetter" type="Node" parent="StateWatcher" node_paths=PackedStringArray("target")]
+script = ExtResource("7_l6bmd")
+target = NodePath("../../VBoxContainer/FocusGroup")
+on_signal = "state_entered"
+
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
[node name="FocusGroup" parent="VBoxContainer" node_paths=PackedStringArray("current_focus") instance=ExtResource("2_73ivm")]
current_focus = NodePath("../VolumeSlider")
-focus_stack = ExtResource("3_pfahh")
[node name="AudioLabel" parent="VBoxContainer" instance=ExtResource("1_x5cm4")]
layout_mode = 2
diff --git a/core/ui/common/settings/display_settings_menu.gd b/core/ui/common/settings/display_settings_menu.gd
index 32aa33ca..09f45a3b 100644
--- a/core/ui/common/settings/display_settings_menu.gd
+++ b/core/ui/common/settings/display_settings_menu.gd
@@ -1,17 +1,26 @@
extends Control
-var SettingsManager := load("res://core/global/settings_manager.tres") as SettingsManager
-@onready var scale_slider := $%ScaleSlider
+var settings_manager := load("res://core/global/settings_manager.tres") as SettingsManager
+@onready var scale_slider := $%ScaleSlider as ValueSlider
+@onready var blur_toggle := $%BlurToggle as Toggle
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
- var display_scale := SettingsManager.get_value("display", "scale", 1.0) as float
+ var display_scale := settings_manager.get_value("display", "scale", 1.0) as float
scale_slider.value = display_scale
get_window().content_scale_factor = display_scale
scale_slider.value_changed.connect(_on_scale_changed)
+
+ var blur_enabled := settings_manager.get_value("display", "enable_overlay_blur", true) as bool
+ blur_toggle.button_pressed = blur_enabled
+ blur_toggle.toggled.connect(_on_blur_toggled)
func _on_scale_changed(value: float) -> void:
get_window().content_scale_factor = value
- SettingsManager.set_value("display", "scale", value)
+ settings_manager.set_value("display", "scale", value)
+
+
+func _on_blur_toggled(pressed: bool) -> void:
+ settings_manager.set_value("display", "enable_overlay_blur", pressed)
diff --git a/core/ui/common/settings/display_settings_menu.tscn b/core/ui/common/settings/display_settings_menu.tscn
index ad9ac155..8f095b57 100644
--- a/core/ui/common/settings/display_settings_menu.tscn
+++ b/core/ui/common/settings/display_settings_menu.tscn
@@ -1,11 +1,17 @@
-[gd_scene load_steps=7 format=3 uid="uid://521da7e2cdxd"]
+[gd_scene load_steps=13 format=3 uid="uid://521da7e2cdxd"]
[ext_resource type="Script" path="res://core/ui/common/settings/display_settings_menu.gd" id="1_w4xaq"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_jrm6t"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="2_oj3r7"]
+[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="3_aiw87"]
[ext_resource type="PackedScene" uid="uid://dithv38oqgy58" path="res://core/ui/components/section_label.tscn" id="3_nmfgp"]
-[ext_resource type="Resource" uid="uid://dgi16frh3mgj8" path="res://core/ui/card_ui/settings/settings_menu_focus.tres" id="3_rrkak"]
[ext_resource type="PackedScene" uid="uid://cemxrvvjgm4g" path="res://core/ui/components/slider.tscn" id="4_fpepf"]
+[ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="4_m2akb"]
+[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_m7ryc"]
+[ext_resource type="Resource" uid="uid://bfx7p4n4fhjwd" path="res://assets/state/states/settings_display.tres" id="6_5275c"]
[ext_resource type="PackedScene" uid="uid://b0cyl6fdqxevn" path="res://core/systems/input/scroller_joystick.tscn" id="6_hgrqi"]
+[ext_resource type="Script" path="res://core/systems/input/focus_group_setter.gd" id="7_tlkhc"]
+[ext_resource type="PackedScene" uid="uid://d1qb7euwlu7bh" path="res://core/ui/components/toggle.tscn" id="11_a21ac"]
[node name="DisplaySettings" type="ScrollContainer"]
anchors_preset = 15
@@ -17,6 +23,23 @@ size_flags_horizontal = 3
size_flags_vertical = 3
script = ExtResource("1_w4xaq")
+[node name="InputWatcher" parent="." instance=ExtResource("2_jrm6t")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("3_aiw87")]
+state_machine = ExtResource("4_m2akb")
+action = 2
+on_signal = "input_released"
+
+[node name="StateWatcher" parent="." instance=ExtResource("5_m7ryc")]
+state = ExtResource("6_5275c")
+
+[node name="FocusGroupSetter" type="Node" parent="StateWatcher" node_paths=PackedStringArray("target")]
+script = ExtResource("7_tlkhc")
+target = NodePath("../../VBoxContainer/FocusGroup")
+on_signal = "state_entered"
+
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
@@ -24,7 +47,6 @@ size_flags_vertical = 3
[node name="FocusGroup" parent="VBoxContainer" node_paths=PackedStringArray("current_focus") instance=ExtResource("2_oj3r7")]
current_focus = NodePath("../ScaleSlider")
-focus_stack = ExtResource("3_rrkak")
[node name="DisplayLabel" parent="VBoxContainer" instance=ExtResource("3_nmfgp")]
layout_mode = 2
@@ -37,9 +59,24 @@ layout_mode = 2
unique_name_in_owner = true
layout_mode = 2
text = "Scale"
-value = 1.2
-max_value = 5.0
+value = 1.0
+max_value = 3.0
min_value = 0.2
-step = 0.2
+step = 0.02
+
+[node name="OverlayLabel" parent="VBoxContainer" instance=ExtResource("3_nmfgp")]
+layout_mode = 2
+text = "Overlay"
+
+[node name="HSeparator2" type="HSeparator" parent="VBoxContainer"]
+layout_mode = 2
+
+[node name="BlurToggle" parent="VBoxContainer" instance=ExtResource("11_a21ac")]
+unique_name_in_owner = true
+layout_mode = 2
+text = "Blur"
+separator_visible = false
+description = "Blur background game when overlay is open"
+button_pressed = true
[node name="ScrollerJoystick" parent="." instance=ExtResource("6_hgrqi")]
diff --git a/core/ui/common/settings/logging_settings_menu.tscn b/core/ui/common/settings/logging_settings_menu.tscn
index 4dfabc1b..8093e0ea 100644
--- a/core/ui/common/settings/logging_settings_menu.tscn
+++ b/core/ui/common/settings/logging_settings_menu.tscn
@@ -1,11 +1,16 @@
-[gd_scene load_steps=8 format=3 uid="uid://csor0e54svgja"]
+[gd_scene load_steps=13 format=3 uid="uid://csor0e54svgja"]
[ext_resource type="Script" path="res://core/ui/common/settings/logging_settings_menu.gd" id="1_qleso"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="2_5va5m"]
-[ext_resource type="Resource" uid="uid://dgi16frh3mgj8" path="res://core/ui/card_ui/settings/settings_menu_focus.tres" id="3_qmfyc"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_w6ipu"]
+[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="3_86i0s"]
[ext_resource type="PackedScene" uid="uid://dithv38oqgy58" path="res://core/ui/components/section_label.tscn" id="3_udmnv"]
+[ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="4_hqn56"]
[ext_resource type="PackedScene" uid="uid://d0u3rsa5qpj57" path="res://core/ui/components/subsection_label.tscn" id="5_6uj1s"]
+[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_ufggl"]
+[ext_resource type="Resource" uid="uid://cakuo0qwrrkk8" path="res://assets/state/states/settings_logging.tres" id="6_3ewnc"]
[ext_resource type="PackedScene" uid="uid://xei5afwefxud" path="res://core/ui/components/dropdown.tscn" id="6_ljvn8"]
+[ext_resource type="Script" path="res://core/systems/input/focus_group_setter.gd" id="7_2p4q3"]
[ext_resource type="PackedScene" uid="uid://b0cyl6fdqxevn" path="res://core/systems/input/scroller_joystick.tscn" id="7_fbxhf"]
[node name="LoggingSettings" type="ScrollContainer"]
@@ -19,6 +24,23 @@ size_flags_vertical = 3
follow_focus = true
script = ExtResource("1_qleso")
+[node name="InputWatcher" parent="." instance=ExtResource("2_w6ipu")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("3_86i0s")]
+state_machine = ExtResource("4_hqn56")
+action = 2
+on_signal = "input_released"
+
+[node name="StateWatcher" parent="." instance=ExtResource("5_ufggl")]
+state = ExtResource("6_3ewnc")
+
+[node name="FocusGroupSetter" type="Node" parent="StateWatcher" node_paths=PackedStringArray("target")]
+script = ExtResource("7_2p4q3")
+target = NodePath("../../MarginContainer/VBoxContainer/FocusGroup")
+on_signal = "state_entered"
+
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
@@ -37,7 +59,6 @@ size_flags_vertical = 3
[node name="FocusGroup" parent="MarginContainer/VBoxContainer" node_paths=PackedStringArray("current_focus") instance=ExtResource("2_5va5m")]
unique_name_in_owner = true
current_focus = NodePath("../GlobalDropdown")
-focus_stack = ExtResource("3_qmfyc")
[node name="LoggingLabel" parent="MarginContainer/VBoxContainer" instance=ExtResource("3_udmnv")]
layout_mode = 2
diff --git a/core/ui/common/settings/network_settings_menu.tscn b/core/ui/common/settings/network_settings_menu.tscn
index c6df6cd2..8ea0ed6d 100644
--- a/core/ui/common/settings/network_settings_menu.tscn
+++ b/core/ui/common/settings/network_settings_menu.tscn
@@ -1,12 +1,17 @@
-[gd_scene load_steps=10 format=3 uid="uid://cpss2bhdwm8t7"]
+[gd_scene load_steps=15 format=3 uid="uid://cpss2bhdwm8t7"]
[ext_resource type="Script" path="res://core/ui/common/settings/network_settings_menu.gd" id="1_2qdps"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="2_8jmkq"]
+[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_yj01k"]
[ext_resource type="PackedScene" uid="uid://dithv38oqgy58" path="res://core/ui/components/section_label.tscn" id="3_ddyy1"]
-[ext_resource type="Resource" uid="uid://dgi16frh3mgj8" path="res://core/ui/card_ui/settings/settings_menu_focus.tres" id="3_emyir"]
+[ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="3_hbsyi"]
[ext_resource type="PackedScene" uid="uid://d0u3rsa5qpj57" path="res://core/ui/components/subsection_label.tscn" id="4_t7br0"]
+[ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="4_tf04q"]
+[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_jobhc"]
[ext_resource type="PackedScene" uid="uid://df5o1o2dsik84" path="res://core/ui/components/button.tscn" id="5_round"]
[ext_resource type="Theme" uid="uid://bko0q7gp1hwjp" path="res://assets/themes/dracula.tres" id="6_i7u1a"]
+[ext_resource type="Resource" uid="uid://2efht48q7i6v" path="res://assets/state/states/settings_network.tres" id="6_ke40f"]
+[ext_resource type="Script" path="res://core/systems/input/focus_group_setter.gd" id="7_gnxgo"]
[ext_resource type="PackedScene" uid="uid://d1rjdfxxrdccf" path="res://core/ui/components/text_input.tscn" id="7_qx5i7"]
[ext_resource type="PackedScene" uid="uid://b0cyl6fdqxevn" path="res://core/systems/input/scroller_joystick.tscn" id="9_v6vet"]
@@ -20,6 +25,23 @@ size_flags_horizontal = 3
size_flags_vertical = 3
script = ExtResource("1_2qdps")
+[node name="InputWatcher" parent="." instance=ExtResource("2_yj01k")]
+stop_propagation = true
+action = "ogui_east"
+
+[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("3_hbsyi")]
+state_machine = ExtResource("4_tf04q")
+action = 2
+on_signal = "input_released"
+
+[node name="StateWatcher" parent="." instance=ExtResource("5_jobhc")]
+state = ExtResource("6_ke40f")
+
+[node name="FocusGroupSetter" type="Node" parent="StateWatcher" node_paths=PackedStringArray("target")]
+script = ExtResource("7_gnxgo")
+target = NodePath("../../MarginContainer/VBoxContainer/FocusGroup")
+on_signal = "state_entered"
+
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
@@ -34,7 +56,6 @@ size_flags_vertical = 3
[node name="FocusGroup" parent="MarginContainer/VBoxContainer" node_paths=PackedStringArray("current_focus") instance=ExtResource("2_8jmkq")]
current_focus = NodePath("../RefreshButton")
-focus_stack = ExtResource("3_emyir")
[node name="NoNetworkLabel" parent="MarginContainer/VBoxContainer" instance=ExtResource("4_t7br0")]
unique_name_in_owner = true
diff --git a/core/ui/components/card_button.gd b/core/ui/components/card_button.gd
index e3c19967..8ece6bef 100644
--- a/core/ui/components/card_button.gd
+++ b/core/ui/components/card_button.gd
@@ -71,7 +71,10 @@ func _ready() -> void:
label.vertical_alignment = vertical_alignment
label.autowrap_mode = autowrap_mode
label.uppercase = uppercase
-
+
+ if Engine.is_editor_hint():
+ return
+
# Connect signals
pressed.connect(_play_sound.bind(select_audio_stream))
focus_entered.connect(_on_focus)
@@ -79,7 +82,11 @@ func _ready() -> void:
mouse_entered.connect(_on_focus)
mouse_exited.connect(_on_unfocus)
theme_changed.connect(_on_theme_changed)
- _on_theme_changed()
+
+ # Find the parent theme and update if required
+ var effective_theme := ThemeUtils.get_effective_theme(self)
+ if effective_theme:
+ _on_theme_changed()
func _on_theme_changed() -> void:
diff --git a/core/ui/components/card_button.tscn b/core/ui/components/card_button.tscn
index bfa1138f..51a3f885 100644
--- a/core/ui/components/card_button.tscn
+++ b/core/ui/components/card_button.tscn
@@ -1,19 +1,7 @@
-[gd_scene load_steps=4 format=3 uid="uid://c71ayw7pcw6u6"]
+[gd_scene load_steps=2 format=3 uid="uid://c71ayw7pcw6u6"]
[ext_resource type="Script" path="res://core/ui/components/card_button.gd" id="1_62nf7"]
-[sub_resource type="Image" id="Image_u87yt"]
-data = {
-"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
-"format": "RGBA8",
-"height": 16,
-"mipmaps": false,
-"width": 16
-}
-
-[sub_resource type="ImageTexture" id="ImageTexture_ax6fu"]
-image = SubResource("Image_u87yt")
-
[node name="CardButton" type="PanelContainer"]
clip_children = 2
offset_right = 103.0
@@ -26,7 +14,6 @@ script = ExtResource("1_62nf7")
unique_name_in_owner = true
visible = false
layout_mode = 2
-texture = SubResource("ImageTexture_ax6fu")
expand_mode = 1
stretch_mode = 6
diff --git a/core/ui/components/card_icon_button.gd b/core/ui/components/card_icon_button.gd
index d596f723..6f7cdbb1 100644
--- a/core/ui/components/card_icon_button.gd
+++ b/core/ui/components/card_icon_button.gd
@@ -35,7 +35,10 @@ var select_audio_stream = load(select_audio)
func _ready() -> void:
# Configure the icon
icon.texture = texture
-
+
+ if Engine.is_editor_hint():
+ return
+
# Connect signals
pressed.connect(_play_sound.bind(select_audio_stream))
focus_entered.connect(_on_focus)
@@ -43,7 +46,11 @@ func _ready() -> void:
mouse_entered.connect(_on_focus)
mouse_exited.connect(_on_unfocus)
theme_changed.connect(_on_theme_changed)
- _on_theme_changed()
+
+ # Find the parent theme and update if required
+ var effective_theme := ThemeUtils.get_effective_theme(self)
+ if effective_theme:
+ _on_theme_changed()
func _on_theme_changed() -> void:
diff --git a/core/ui/components/card_input_icon_button.gd b/core/ui/components/card_input_icon_button.gd
index 668478d7..1897d0ec 100644
--- a/core/ui/components/card_input_icon_button.gd
+++ b/core/ui/components/card_input_icon_button.gd
@@ -22,7 +22,11 @@ var logger := Log.get_logger("CardInputIconButton")
func _ready() -> void:
# Connect signals
theme_changed.connect(_on_theme_changed)
- _on_theme_changed()
+
+ # Find the parent theme and update if required
+ var effective_theme := ThemeUtils.get_effective_theme(self)
+ if effective_theme:
+ _on_theme_changed()
func _on_theme_changed() -> void:
diff --git a/core/ui/components/card_mapping_button.gd b/core/ui/components/card_mapping_button.gd
index f65ff86c..d8ca680c 100644
--- a/core/ui/components/card_mapping_button.gd
+++ b/core/ui/components/card_mapping_button.gd
@@ -66,10 +66,17 @@ func _ready() -> void:
target_label.vertical_alignment = vertical_alignment
target_label.autowrap_mode = autowrap_mode
target_label.uppercase = uppercase
-
+
+ if Engine.is_editor_hint():
+ return
+
# Connect signals
theme_changed.connect(_on_theme_changed)
- _on_theme_changed()
+
+ # Find the parent theme and update if required
+ var effective_theme := ThemeUtils.get_effective_theme(self)
+ if effective_theme:
+ _on_theme_changed()
## Set the source input icon's icon mapping
diff --git a/core/ui/components/card_mapping_button.tscn b/core/ui/components/card_mapping_button.tscn
index 75a65eec..639f7f80 100644
--- a/core/ui/components/card_mapping_button.tscn
+++ b/core/ui/components/card_mapping_button.tscn
@@ -1,4 +1,4 @@
-[gd_scene load_steps=8 format=3 uid="uid://dbll03tbmw3ps"]
+[gd_scene load_steps=6 format=3 uid="uid://dbll03tbmw3ps"]
[ext_resource type="Script" path="res://core/ui/components/card_mapping_button.gd" id="1_qi6wi"]
[ext_resource type="Script" path="res://core/ui/components/input_icon.gd" id="2_d8vnj"]
@@ -6,18 +6,6 @@
[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="3_h1m7y"]
[ext_resource type="Texture2D" uid="uid://da6kcs5mhssov" path="res://assets/ui/icons/arrow-right-bold.svg" id="4_gjhmn"]
-[sub_resource type="Image" id="Image_6pbp1"]
-data = {
-"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
-"format": "RGBA8",
-"height": 16,
-"mipmaps": false,
-"width": 16
-}
-
-[sub_resource type="ImageTexture" id="ImageTexture_sa6yd"]
-image = SubResource("Image_6pbp1")
-
[node name="CardMappingButton" type="MarginContainer"]
editor_description = "Button used for mapping gamepad inputs"
anchors_preset = 15
@@ -57,7 +45,6 @@ theme_type_variation = &"CardButton"
unique_name_in_owner = true
visible = false
layout_mode = 2
-texture = SubResource("ImageTexture_sa6yd")
expand_mode = 1
stretch_mode = 6
diff --git a/core/ui/components/dialog.gd b/core/ui/components/dialog.gd
index 660d01a1..55f499df 100644
--- a/core/ui/components/dialog.gd
+++ b/core/ui/components/dialog.gd
@@ -37,11 +37,11 @@ signal choice_selected(accepted: bool)
@export var close_on_selected := true
@onready var label := $%Label as Label
-@onready var confirm_button := $%ConfirmButton as Button
-@onready var cancel_button := $%CancelButton as Button
+@onready var confirm_button := $%ConfirmButton as CardButton
+@onready var cancel_button := $%CancelButton as CardButton
@onready var fade_effect := $%FadeEffect as Effect
-
+var _return_node: Control = null
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
confirm_button.button_up.connect(_on_selected.bind(true))
@@ -53,17 +53,20 @@ func _on_selected(accepted: bool) -> void:
if close_on_selected:
closed.emit()
choice_selected.emit(accepted)
+ if _return_node:
+ _return_node.grab_focus.call_deferred()
+ _return_node = null
## Opens the dialog box with the given settings
-func open(message: String = "", confirm_txt: String = "", cancel_txt: String = "") -> void:
+func open(return_node: Control, message: String = "", confirm_txt: String = "", cancel_txt: String = "") -> void:
if message != "":
text = message
if confirm_txt != "":
confirm_text = confirm_txt
if cancel_txt != "":
cancel_text = cancel_txt
-
+ _return_node = return_node
opened.emit()
await fade_effect.effect_finished
confirm_button.grab_focus.call_deferred()
diff --git a/core/ui/components/dialog.tscn b/core/ui/components/dialog.tscn
index be8a4137..158d6a68 100644
--- a/core/ui/components/dialog.tscn
+++ b/core/ui/components/dialog.tscn
@@ -1,9 +1,10 @@
-[gd_scene load_steps=5 format=3 uid="uid://eqqk1uve143x"]
+[gd_scene load_steps=6 format=3 uid="uid://eqqk1uve143x"]
[ext_resource type="Script" path="res://core/ui/components/dialog.gd" id="1_yqaeg"]
[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="2_q0yos"]
[ext_resource type="PackedScene" uid="uid://ekhjpmat02f8" path="res://core/systems/effects/slide_effect.tscn" id="3_hbrr5"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="4_vp527"]
+[ext_resource type="PackedScene" uid="uid://c71ayw7pcw6u6" path="res://core/ui/components/card_button.tscn" id="5_clb01"]
[node name="Dialog" type="Control" groups=["dialog", "popup"]]
editor_description = "Opens a dialog choice to the user."
@@ -80,26 +81,10 @@ alignment = 1
[node name="FocusGroup" parent="Spacer/CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/HBoxContainer" instance=ExtResource("4_vp527")]
-[node name="CancelButton" type="Button" parent="Spacer/CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/HBoxContainer"]
+[node name="CancelButton" parent="Spacer/CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/HBoxContainer" instance=ExtResource("5_clb01")]
unique_name_in_owner = true
layout_mode = 2
-size_flags_horizontal = 3
-focus_neighbor_left = NodePath("../ConfirmButton")
-focus_neighbor_top = NodePath(".")
-focus_neighbor_right = NodePath("../ConfirmButton")
-focus_neighbor_bottom = NodePath(".")
-focus_next = NodePath("../ConfirmButton")
-focus_previous = NodePath("../ConfirmButton")
-text = "No"
-[node name="ConfirmButton" type="Button" parent="Spacer/CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/HBoxContainer"]
+[node name="ConfirmButton" parent="Spacer/CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/HBoxContainer" instance=ExtResource("5_clb01")]
unique_name_in_owner = true
layout_mode = 2
-size_flags_horizontal = 3
-focus_neighbor_left = NodePath("../CancelButton")
-focus_neighbor_top = NodePath(".")
-focus_neighbor_right = NodePath("../CancelButton")
-focus_neighbor_bottom = NodePath(".")
-focus_next = NodePath("../CancelButton")
-focus_previous = NodePath("../CancelButton")
-text = "Yes"
diff --git a/core/ui/components/drive_card.gd b/core/ui/components/drive_card.gd
index 33790888..12cce13f 100644
--- a/core/ui/components/drive_card.gd
+++ b/core/ui/components/drive_card.gd
@@ -15,6 +15,10 @@ var device: BlockDevice
var device_path: String
var highlight_tween: Tween
+signal pressed
+signal button_up
+signal button_down
+signal nonchild_focused
signal format_drive(device: BlockDevice)
signal format_sd_card
signal init_partition(device: PartitionDevice)
@@ -24,8 +28,9 @@ signal init_partition(device: PartitionDevice)
@onready var drive_icon: TextureRect = $%IconTextureRect
@onready var drive_focus_group: FocusGroup = $%DriveFocusGroup
@onready var format_button: CardButton = $%FormatButton
-@onready var partitions_container: VBoxContainer = $%PartitionsVBox
+@onready var partitions_container: HBoxContainer = $%PartitionsHBox
@onready var partitions_focus_group: FocusGroup = $%PartitionsFocusGroup
+@onready var highlight := $%HighlightTexture as TextureRect
var logger: Logger
var log_level:= Log.LEVEL.INFO
@@ -36,6 +41,7 @@ func setup(device: BlockDevice) -> void:
# Setup UDisks2 information
self.device = device
self.device_path = "/dev" + self.device.dbus_path.trim_prefix(steam_disks.BLOCK_PREFIX)
+
logger = Log.get_logger("DriveCard|"+self.device_path, log_level)
logger.debug("Setup Drive Card")
drive_name_label.text = self.device_path
@@ -52,20 +58,30 @@ func setup(device: BlockDevice) -> void:
format_button.pressed.disconnect(_srm_format_drive)
format_button.visible = true
+ var on_focus_exited := func():
+ self._on_unfocus.call_deferred()
+ focus_exited.connect(on_focus_exited)
+ theme_changed.connect(_on_theme_changed)
+
+ # Find the parent theme and update if required
+ var effective_theme := ThemeUtils.get_effective_theme(self)
+ if effective_theme:
+ _on_theme_changed()
+
func _srm_format_drive() -> void:
+ if steam_disks.block_operations:
+ logger.debug("Format operation blocked.")
+ return
+
var dialog := get_tree().get_first_node_in_group("dialog") as Dialog
var msg := "WARNING: All data on " + device_path + " device will be wiped. " + \
"This action cannot be undone. Do you wish to continue?"
- dialog.open(msg, "Cancel", "Continue Format")
+ dialog.open(format_button, msg, "Cancel", "Continue Format")
var cancel := await dialog.choice_selected as bool
if cancel:
return
- if steam_disks.block_operations:
- logger.debug("Format operation blocked.")
- return
-
logger.debug("Format Drive", device_path)
_clear_partitions()
_on_format_started()
@@ -76,18 +92,18 @@ func _srm_format_drive() -> void:
func _srm_format_sd_card() -> void:
+ if steam_disks.block_operations:
+ logger.debug("Format operation blocked.")
+ return
+
var dialog := get_tree().get_first_node_in_group("dialog") as Dialog
var msg := "WARNING: All data on " + device_path + " device will be wiped. " + \
"This action cannot be undone. Do you wish to continue?"
- dialog.open(msg, "Cancel", "Continue Format")
+ dialog.open(format_button, msg, "Cancel", "Continue Format")
var cancel := await dialog.choice_selected as bool
if cancel:
return
- if steam_disks.block_operations:
- logger.debug("Format operation blocked.")
- return
-
logger.debug("Format SD Card", device_path)
_clear_partitions()
_on_format_started()
@@ -116,7 +132,6 @@ func _srm_init_drive(partition: PartitionDevice) -> void:
init_partition.emit(partition)
-
func _clear_partitions() -> void:
# Clear the current grid of items
var keep_nodes := [partitions_focus_group]
@@ -150,9 +165,54 @@ func _populate_partitions() -> void:
_clear_partitions()
var last_focus: FocusGroup
for partition in self.device.partitions:
+
+ # Ignore loop devices
+ if partition.partition_name.contains("/dev/loop"):
+ continue
+
logger.debug("Drive has partition to set up:", partition.dbus_path)
var partition_card := partition_card_scene.instantiate() as PartitionCard
partitions_container.add_child(partition_card)
partition_card.setup(partition)
partition_card.visible = true
partition_card.init_partition.connect(_srm_init_drive)
+
+
+# Update the highlight texture on theme change
+func _on_theme_changed() -> void:
+ # Configure the highlight texture from the theme
+ var highlight_texture := get_theme_icon("highlight", "ExpandableCard")
+ if highlight_texture:
+ highlight.texture = highlight_texture
+
+
+func _gui_input(event: InputEvent) -> void:
+ if event.is_action("ui_accept"):
+ if event.is_pressed():
+ button_down.emit()
+ pressed.emit()
+ else:
+ button_up.emit()
+
+func _on_unfocus() -> void:
+ # Emit a signal if a non-child node grabs focus
+ var focus_owner := get_viewport().gui_get_focus_owner()
+ if not self.is_ancestor_of(focus_owner):
+ nonchild_focused.emit()
+ return
+
+ # If a child has focus, listen for focus changes until a non-child has focus
+ get_viewport().gui_focus_changed.connect(_on_focus_change)
+
+
+func _on_focus_change(focused: Control) -> void:
+ # Don't do anything if the focused node is a child
+ if self.is_ancestor_of(focused):
+ return
+
+ # If a non-child has focus, emit a signal to indicate that this node and none
+ # of its children have focus.
+ nonchild_focused.emit()
+ var viewport := get_viewport()
+ if viewport.gui_focus_changed.is_connected(_on_focus_change):
+ viewport.gui_focus_changed.disconnect(_on_focus_change)
diff --git a/core/ui/components/drive_card.tscn b/core/ui/components/drive_card.tscn
index 5a3b7066..a1a06426 100644
--- a/core/ui/components/drive_card.tscn
+++ b/core/ui/components/drive_card.tscn
@@ -1,16 +1,74 @@
-[gd_scene load_steps=6 format=3 uid="uid://caeaxm6st4a4u"]
+[gd_scene load_steps=13 format=3 uid="uid://caeaxm6st4a4u"]
[ext_resource type="Script" path="res://core/ui/components/drive_card.gd" id="1_jlhco"]
+[ext_resource type="PackedScene" uid="uid://c5sfkhrfbao71" path="res://core/systems/effects/play_audio_effect.tscn" id="2_kr6y1"]
[ext_resource type="Texture2D" uid="uid://bidhj1jikg827" path="res://assets/icons/interface-hdd.svg" id="2_uf6p1"]
[ext_resource type="PackedScene" uid="uid://c71ayw7pcw6u6" path="res://core/ui/components/card_button.tscn" id="3_84mee"]
+[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="3_aof0c"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="3_c72py"]
[ext_resource type="PackedScene" uid="uid://bkdsn268g6hx7" path="res://core/ui/components/partition_card.tscn" id="4_wgas0"]
+[ext_resource type="PackedScene" uid="uid://uljtdvmuol3l" path="res://core/systems/input/focus_group_setter.tscn" id="4_y8uw0"]
+[ext_resource type="Script" path="res://core/systems/input/input_watcher.gd" id="5_7gf3k"]
+[ext_resource type="Script" path="res://core/systems/input/focus_setter.gd" id="6_gomk5"]
+
+[sub_resource type="Gradient" id="Gradient_2e33l"]
+colors = PackedColorArray(0.741176, 0.576471, 0.976471, 1, 1, 0.47451, 0.776471, 1)
+
+[sub_resource type="GradientTexture2D" id="GradientTexture2D_ag6ju"]
+gradient = SubResource("Gradient_2e33l")
+fill = 1
+fill_to = Vector2(1, 2)
[node name="DriveCard" type="MarginContainer"]
custom_minimum_size = Vector2(340, 200)
+anchors_preset = 10
+anchor_right = 1.0
+offset_bottom = 209.0
+grow_horizontal = 2
+focus_mode = 2
script = ExtResource("1_jlhco")
-[node name="BackgroundPanel" type="PanelContainer" parent="."]
+[node name="PlayFocusAudioEffect" parent="." instance=ExtResource("2_kr6y1")]
+on_signal = "focus_entered"
+
+[node name="PlaySelectedAudioEffect" parent="." instance=ExtResource("2_kr6y1")]
+audio = "res://assets/audio/interface/select_002.ogg"
+on_signal = "pressed"
+
+[node name="HighlightFadeEffect" parent="." node_paths=PackedStringArray("target") instance=ExtResource("3_aof0c")]
+target = NodePath("../PanelContainer/HighlightTexture")
+on_signal = "focus_entered"
+fade_out_signal = "nonchild_focused"
+on_signal = "focus_entered"
+
+[node name="FocusGroupSetter" parent="." node_paths=PackedStringArray("target") instance=ExtResource("4_y8uw0")]
+target = NodePath("../EdgeMarginContainer/LayoutHBox/DriveDataVBox/DriveFocusGroup")
+on_signal = "button_up"
+
+[node name="PanelContainer" type="PanelContainer" parent="."]
+clip_children = 2
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+theme_type_variation = &"PluginStoreCard"
+
+[node name="HighlightTexture" type="TextureRect" parent="PanelContainer"]
+unique_name_in_owner = true
+visible = false
+layout_mode = 2
+texture = SubResource("GradientTexture2D_ag6ju")
+expand_mode = 1
+stretch_mode = 6
+
+[node name="InsidePanelMargin" type="MarginContainer" parent="."]
+layout_mode = 2
+theme_override_constants/margin_left = 4
+theme_override_constants/margin_top = 4
+theme_override_constants/margin_right = 4
+theme_override_constants/margin_bottom = 4
+
+[node name="InsidePanel" type="Panel" parent="InsidePanelMargin"]
+unique_name_in_owner = true
layout_mode = 2
theme_type_variation = &"PluginStoreCard"
@@ -25,6 +83,16 @@ theme_override_constants/margin_bottom = 20
layout_mode = 2
theme_override_constants/separation = 20
+[node name="InputWatcher" type="Node" parent="EdgeMarginContainer/LayoutHBox"]
+script = ExtResource("5_7gf3k")
+stop_propagation = true
+action = "ogui_east"
+
+[node name="FocusSetter" type="Node" parent="EdgeMarginContainer/LayoutHBox/InputWatcher" node_paths=PackedStringArray("target")]
+script = ExtResource("6_gomk5")
+target = NodePath("../../../..")
+on_signal = "input_released"
+
[node name="IconTextureRect" type="TextureRect" parent="EdgeMarginContainer/LayoutHBox"]
unique_name_in_owner = true
custom_minimum_size = Vector2(40, 40)
@@ -51,28 +119,30 @@ layout_mode = 2
text = "1337 Gb"
horizontal_alignment = 2
+[node name="Control" type="Control" parent="EdgeMarginContainer/LayoutHBox/DriveDataVBox"]
+layout_mode = 2
+size_flags_vertical = 3
+
[node name="FormatButton" parent="EdgeMarginContainer/LayoutHBox/DriveDataVBox" instance=ExtResource("3_84mee")]
unique_name_in_owner = true
visible = false
layout_mode = 2
text = "Format Drive"
-[node name="HBoxContainer" type="HBoxContainer" parent="EdgeMarginContainer/LayoutHBox/DriveDataVBox"]
-layout_mode = 2
-
[node name="DriveFocusGroup" parent="EdgeMarginContainer/LayoutHBox/DriveDataVBox" node_paths=PackedStringArray("focus_neighbor_right") instance=ExtResource("3_c72py")]
unique_name_in_owner = true
-focus_neighbor_right = NodePath("../../PartitionsVBox/PartitionsFocusGroup")
+focus_neighbor_right = NodePath("../../PartitionsHBox/PartitionsFocusGroup")
-[node name="PartitionsVBox" type="VBoxContainer" parent="EdgeMarginContainer/LayoutHBox"]
+[node name="PartitionsHBox" type="HBoxContainer" parent="EdgeMarginContainer/LayoutHBox"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 4
theme_override_constants/separation = 20
-[node name="PartitionsFocusGroup" parent="EdgeMarginContainer/LayoutHBox/PartitionsVBox" node_paths=PackedStringArray("focus_neighbor_left") instance=ExtResource("3_c72py")]
+[node name="PartitionsFocusGroup" parent="EdgeMarginContainer/LayoutHBox/PartitionsHBox" node_paths=PackedStringArray("focus_neighbor_left", "focus_neighbor_right") instance=ExtResource("3_c72py")]
unique_name_in_owner = true
focus_neighbor_left = NodePath("../../DriveDataVBox/DriveFocusGroup")
+focus_neighbor_right = NodePath("../../DriveDataVBox/DriveFocusGroup")
-[node name="PartitionCard" parent="EdgeMarginContainer/LayoutHBox/PartitionsVBox" instance=ExtResource("4_wgas0")]
+[node name="PartitionCard" parent="EdgeMarginContainer/LayoutHBox/PartitionsHBox" instance=ExtResource("4_wgas0")]
layout_mode = 2
diff --git a/core/ui/components/dropdown.gd b/core/ui/components/dropdown.gd
index 5e3920f6..5955cb3a 100644
--- a/core/ui/components/dropdown.gd
+++ b/core/ui/components/dropdown.gd
@@ -41,16 +41,18 @@ signal item_selected(index: int)
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
- focus_entered.connect(_grab_focus)
label.text = title
description_label.text = description
option_button.disabled = disabled
+ if Engine.is_editor_hint():
+ return
option_button.focus_neighbor_bottom = focus_neighbor_bottom
option_button.focus_neighbor_left = focus_neighbor_left
option_button.focus_neighbor_right = focus_neighbor_right
option_button.focus_neighbor_top = focus_neighbor_top
option_button.focus_previous = focus_previous
option_button.focus_next = focus_next
+ focus_entered.connect(_grab_focus)
# Hide labels if nothing is specified
if title == "":
@@ -76,10 +78,12 @@ func _ready() -> void:
option_button.select(focused_item)
option_button.item_selected.emit(focused_item)
option_popup.visible = false
- if event.is_action_pressed("ogui_east") \
- or event.is_action_pressed("ogui_back") \
- or event.is_action_pressed("ogui_east_ov"):
+ get_viewport().set_input_as_handled()
+ if event.is_action_released("ogui_east") \
+ or event.is_action_released("ogui_back") \
+ or event.is_action_released("ogui_east_ov"):
option_popup.visible = false
+ get_viewport().set_input_as_handled()
option_popup.window_input.connect(on_option_button_input)
diff --git a/core/ui/components/expandable_card.gd b/core/ui/components/expandable_card.gd
index a1786c14..cbee0d13 100644
--- a/core/ui/components/expandable_card.gd
+++ b/core/ui/components/expandable_card.gd
@@ -30,7 +30,12 @@ func _ready() -> void:
focus_exited.connect(_on_unfocus)
pressed.connect(_on_pressed)
theme_changed.connect(_on_theme_changed)
- _on_theme_changed()
+
+ # Find the parent theme and update if required
+ var effective_theme := ThemeUtils.get_effective_theme(self)
+ if effective_theme:
+ _on_theme_changed()
+
label.text = title
# Do nothing if running in the editor
diff --git a/core/ui/components/input_icon.gd b/core/ui/components/input_icon.gd
index b4711e1d..099ba1d3 100644
--- a/core/ui/components/input_icon.gd
+++ b/core/ui/components/input_icon.gd
@@ -105,6 +105,9 @@ var internal_children: Array[Node] = []
texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
func _ready():
+ # Don't run in the editor
+ if Engine.is_editor_hint():
+ return
# Add the children
self.add_child(label)
# Only listen for input type changes if no mapping is forced
diff --git a/core/ui/components/partition_card.gd b/core/ui/components/partition_card.gd
index efc3bb9f..81e885ce 100644
--- a/core/ui/components/partition_card.gd
+++ b/core/ui/components/partition_card.gd
@@ -41,7 +41,7 @@ func _srm_init_drive() -> void:
var dialog := get_tree().get_first_node_in_group("dialog") as Dialog
var msg := "INFO: Adding " + device_path + " as a steam path will NOT cause data loss. " + \
"The drive will be made available to Steam for installing games. Do you wish to continue?"
- dialog.open(msg, "Cancel", "Continue")
+ dialog.open(init_button, msg, "Cancel", "Continue")
var cancel := await dialog.choice_selected as bool
if cancel:
return
diff --git a/core/ui/components/partition_card.tscn b/core/ui/components/partition_card.tscn
index 75a199dd..3462c5f6 100644
--- a/core/ui/components/partition_card.tscn
+++ b/core/ui/components/partition_card.tscn
@@ -60,6 +60,10 @@ unique_name_in_owner = true
layout_mode = 2
text = "1000 Gb"
+[node name="Control" type="Control" parent="EdgeMarginContainer/VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+
[node name="HBoxContainer" type="HBoxContainer" parent="EdgeMarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_horizontal = 4
diff --git a/core/ui/components/plugin_store_card.gd b/core/ui/components/plugin_store_card.gd
index 199bdb63..e86346a6 100644
--- a/core/ui/components/plugin_store_card.gd
+++ b/core/ui/components/plugin_store_card.gd
@@ -1,26 +1,18 @@
-extends PanelContainer
+@tool
+extends Container
signal pressed
signal button_up
signal button_down
+signal nonchild_focused
const plugin_icon := preload("res://assets/ui/icons/plugin-solid.svg")
const install_icon := preload("res://assets/ui/icons/download-cloud-2-fill.svg")
const upgrade_icon := preload("res://assets/ui/icons/upgrade.svg")
const delete_icon := preload("res://assets/ui/icons/round-delete-forever.svg")
-@export_category("Animation")
-@export var highlight_speed := 0.1
-
-@export_category("AudioSteamPlayer")
-@export_file("*.ogg") var focus_audio = "res://assets/audio/interface/536764__egomassive__toss.ogg"
-@export_file("*.ogg") var select_audio = "res://assets/audio/interface/96127__bmaczero__contact1.ogg"
-
-var NotificationManager := load("res://core/global/notification_manager.tres") as NotificationManager
-var PluginLoader := load("res://core/global/plugin_loader.tres") as PluginLoader
-var highlight_tween: Tween
-var focus_audio_stream = load(focus_audio)
-var select_audio_stream = load(select_audio)
+var notification_manager := load("res://core/global/notification_manager.tres") as NotificationManager
+var plugin_loader := load("res://core/global/plugin_loader.tres") as PluginLoader
var download_url: String
var project_url: String
var sha256: String
@@ -38,14 +30,24 @@ var logger: Logger
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
- focus_entered.connect(_on_focus)
- focus_exited.connect(_on_unfocus)
+ # Do nothing if running in the editor
+ if Engine.is_editor_hint():
+ return
+
+ var on_focus_exited := func():
+ self._on_unfocus.call_deferred()
+ focus_exited.connect(on_focus_exited)
theme_changed.connect(_on_theme_changed)
- _on_theme_changed()
- update_button.visible = PluginLoader.is_upgradable(plugin_id)
+
+ # Find the parent theme and update if required
+ var effective_theme := ThemeUtils.get_effective_theme(self)
+ if effective_theme:
+ _on_theme_changed()
+
+ update_button.visible = plugin_loader.is_upgradable(plugin_id)
action_button.pressed.connect(_on_install_button)
update_button.pressed.connect(_on_update_button)
- PluginLoader.plugin_upgradable.connect(_on_update_available)
+ plugin_loader.plugin_upgradable.connect(_on_update_available)
_set_installed_state()
@@ -58,7 +60,7 @@ func _on_theme_changed() -> void:
# Updates the store item based on whether it is installed
func _set_installed_state():
- if PluginLoader.is_installed(plugin_id):
+ if plugin_loader.is_installed(plugin_id):
action_button.texture = delete_icon
return
action_button.texture = install_icon
@@ -73,23 +75,23 @@ func _on_install_button() -> void:
var notify := Notification.new("Installing plugin " + plugin_id)
notify.icon = plugin_icon
# Handle uninstall
- if PluginLoader.is_installed(plugin_id):
+ if plugin_loader.is_installed(plugin_id):
notify.text = "Plugin " + plugin_id + " uninstalled"
- if PluginLoader.uninstall_plugin(plugin_id) != OK:
+ if plugin_loader.uninstall_plugin(plugin_id) != OK:
notify.text = "Plugin " + plugin_id + " failed to uninstall"
logger.error("Failed to uninstall plugin: " + plugin_id)
_set_installed_state()
- NotificationManager.show(notify)
+ notification_manager.show(notify)
return
# Handle install
- NotificationManager.show(notify)
- PluginLoader.install_plugin(plugin_id, download_url, sha256)
- await PluginLoader.plugin_installed
+ notification_manager.show(notify)
+ plugin_loader.install_plugin(plugin_id, download_url, sha256)
+ await plugin_loader.plugin_installed
_set_installed_state()
notify = Notification.new("Plugin " + plugin_id + " installed")
notify.icon = plugin_icon
- NotificationManager.show(notify)
+ notification_manager.show(notify)
# Shows in the store item if an update is available
@@ -106,55 +108,38 @@ func _on_update_available(name: String, type: int) -> void:
func _on_update_button() -> void:
var notify := Notification.new("Updating plugin " + plugin_id)
notify.icon = plugin_icon
- NotificationManager.show(notify)
- PluginLoader.install_plugin(plugin_id, download_url, sha256)
- await PluginLoader.plugin_installed
+ notification_manager.show(notify)
+ plugin_loader.install_plugin(plugin_id, download_url, sha256)
+ await plugin_loader.plugin_installed
notify = Notification.new("Plugin " + plugin_id + " updated")
notify.icon = plugin_icon
- NotificationManager.show(notify)
- PluginLoader.set_plugin_upgraded(plugin_id)
+ notification_manager.show(notify)
+ plugin_loader.set_plugin_upgraded(plugin_id)
update_button.visible = false
-func _on_focus() -> void:
- _highlight()
-
-
func _on_unfocus() -> void:
- # If a child focus group is focused, don't do anything. That means that
- if not focus_group:
- logger.warn("No focus group defined!")
+ # Emit a signal if a non-child node grabs focus
+ var focus_owner := get_viewport().gui_get_focus_owner()
+ if not self.is_ancestor_of(focus_owner):
+ nonchild_focused.emit()
return
- # a child node is focused and we want the card to remain "selected"
- if focus_group.is_in_focus_stack():
- return
-
- _unhighlight()
+ # If a child has focus, listen for focus changes until a non-child has focus
+ get_viewport().gui_focus_changed.connect(_on_focus_change)
-func _highlight() -> void:
- if highlight_tween:
- highlight_tween.kill()
- highlight_tween = get_tree().create_tween()
- highlight_tween.tween_property(highlight, "visible", true, 0)
- highlight_tween.tween_property(highlight, "modulate", Color(1, 1, 1, 1), 0)
- _play_sound(focus_audio_stream)
-
-
-func _unhighlight() -> void:
- if highlight_tween:
- highlight_tween.kill()
- highlight_tween = get_tree().create_tween()
- highlight_tween.tween_property(highlight, "modulate", Color(1, 1, 1, 1), 0)
- highlight_tween.tween_property(highlight, "modulate", Color(1, 1, 1, 0), highlight_speed)
- highlight_tween.tween_property(highlight, "visible", false, 0)
-
-
-func _play_sound(stream: AudioStream) -> void:
- var audio_player: AudioStreamPlayer = $AudioStreamPlayer
- audio_player.stream = stream
- audio_player.play()
+func _on_focus_change(focused: Control) -> void:
+ # Don't do anything if the focused node is a child
+ if self.is_ancestor_of(focused):
+ return
+
+ # If a non-child has focus, emit a signal to indicate that this node and none
+ # of its children have focus.
+ nonchild_focused.emit()
+ var viewport := get_viewport()
+ if viewport.gui_focus_changed.is_connected(_on_focus_change):
+ viewport.gui_focus_changed.disconnect(_on_focus_change)
func _gui_input(event: InputEvent) -> void:
diff --git a/core/ui/components/plugin_store_card.tscn b/core/ui/components/plugin_store_card.tscn
index c07b55ad..a0b3f879 100644
--- a/core/ui/components/plugin_store_card.tscn
+++ b/core/ui/components/plugin_store_card.tscn
@@ -1,37 +1,59 @@
-[gd_scene load_steps=12 format=3 uid="uid://cc7a35n2pqmmf"]
+[gd_scene load_steps=15 format=3 uid="uid://cc7a35n2pqmmf"]
[ext_resource type="Texture2D" uid="uid://djy4rejy21s6g" path="res://icon.svg" id="1_8yjid"]
[ext_resource type="Script" path="res://core/ui/components/plugin_store_card.gd" id="1_mpwli"]
+[ext_resource type="PackedScene" uid="uid://c5sfkhrfbao71" path="res://core/systems/effects/play_audio_effect.tscn" id="2_bs6e3"]
[ext_resource type="PackedScene" uid="uid://dithv38oqgy58" path="res://core/ui/components/section_label.tscn" id="2_cg18p"]
[ext_resource type="PackedScene" uid="uid://uljtdvmuol3l" path="res://core/systems/input/focus_group_setter.tscn" id="2_wtj1w"]
[ext_resource type="PackedScene" uid="uid://cr83fmlociwko" path="res://core/ui/components/card_icon_button.tscn" id="3_3jc55"]
+[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="3_73lox"]
[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="4_6yau4"]
[ext_resource type="Texture2D" uid="uid://54ncgpi6yy3p" path="res://assets/ui/icons/download-cloud-2-fill.svg" id="4_b4p24"]
[ext_resource type="Texture2D" uid="uid://dt1r8cyx48k2p" path="res://assets/ui/icons/upgrade.svg" id="6_dsefr"]
-[ext_resource type="Resource" uid="uid://dgi16frh3mgj8" path="res://core/ui/card_ui/settings/settings_menu_focus.tres" id="6_fo8lr"]
+[ext_resource type="Script" path="res://core/systems/input/input_watcher.gd" id="7_wgty6"]
+[ext_resource type="Script" path="res://core/systems/input/focus_setter.gd" id="8_kpm75"]
-[sub_resource type="Gradient" id="Gradient_2e33l"]
+[sub_resource type="Gradient" id="Gradient_fhi2i"]
colors = PackedColorArray(0.741176, 0.576471, 0.976471, 1, 1, 0.47451, 0.776471, 1)
[sub_resource type="GradientTexture2D" id="GradientTexture2D_thoiv"]
-gradient = SubResource("Gradient_2e33l")
+gradient = SubResource("Gradient_fhi2i")
fill = 1
fill_to = Vector2(1, 2)
-[node name="PluginStoreCard" type="PanelContainer"]
-clip_children = 2
-custom_minimum_size = Vector2(340, 200)
-offset_right = 253.0
-offset_bottom = 143.0
+[node name="PluginStoreCard" type="MarginContainer"]
+custom_minimum_size = Vector2(360, 200)
+offset_right = 360.0
+offset_bottom = 200.0
focus_mode = 2
theme_type_variation = &"PluginStoreCard"
script = ExtResource("1_mpwli")
+[node name="PlayFocusAudioEffect" parent="." instance=ExtResource("2_bs6e3")]
+on_signal = "focus_entered"
+
+[node name="PlaySelectedAudioEffect" parent="." instance=ExtResource("2_bs6e3")]
+audio = "res://assets/audio/interface/select_002.ogg"
+on_signal = "pressed"
+
+[node name="HighlightFadeEffect" parent="." node_paths=PackedStringArray("target") instance=ExtResource("3_73lox")]
+target = NodePath("../PanelContainer/HighlightTexture")
+on_signal = "focus_entered"
+fade_out_signal = "nonchild_focused"
+on_signal = "focus_entered"
+
[node name="FocusGroupSetter" parent="." node_paths=PackedStringArray("target") instance=ExtResource("2_wtj1w")]
target = NodePath("../MarginContainer/HBoxContainer/FocusGroup")
-on_signal = "pressed"
+on_signal = "button_up"
-[node name="HighlightTexture" type="TextureRect" parent="."]
+[node name="PanelContainer" type="PanelContainer" parent="."]
+clip_children = 2
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+theme_type_variation = &"PluginStoreCard"
+
+[node name="HighlightTexture" type="TextureRect" parent="PanelContainer"]
unique_name_in_owner = true
visible = false
layout_mode = 2
@@ -100,10 +122,19 @@ size_flags_horizontal = 8
size_flags_vertical = 8
alignment = 2
+[node name="InputWatcher" type="Node" parent="MarginContainer/HBoxContainer"]
+script = ExtResource("7_wgty6")
+stop_propagation = true
+action = "ogui_east"
+
+[node name="FocusSetter" type="Node" parent="MarginContainer/HBoxContainer/InputWatcher" node_paths=PackedStringArray("target")]
+script = ExtResource("8_kpm75")
+target = NodePath("../../../..")
+on_signal = "input_released"
+
[node name="FocusGroup" parent="MarginContainer/HBoxContainer" node_paths=PackedStringArray("current_focus") instance=ExtResource("4_6yau4")]
unique_name_in_owner = true
current_focus = NodePath("../ActionButton")
-focus_stack = ExtResource("6_fo8lr")
[node name="UpgradeButton" parent="MarginContainer/HBoxContainer" instance=ExtResource("3_3jc55")]
unique_name_in_owner = true
@@ -121,6 +152,3 @@ layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 8
texture = ExtResource("4_b4p24")
-
-[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."]
-unique_name_in_owner = true
diff --git a/core/ui/components/search_bar.gd b/core/ui/components/search_bar.gd
index 36a2c940..5b1d0c93 100644
--- a/core/ui/components/search_bar.gd
+++ b/core/ui/components/search_bar.gd
@@ -3,7 +3,7 @@ class_name SearchBar
signal search_submitted(text: String)
-var state_machine := load("res://assets/state/state_machines/global_state_machine.tres") as StateMachine
+var state_machine := load("res://assets/state/state_machines/menu_state_machine.tres") as StateMachine
var library_state := load("res://assets/state/states/library.tres")
var keyboard_context := KeyboardContext.new(KeyboardContext.TYPE.GODOT, self)
diff --git a/core/ui/components/slider.gd b/core/ui/components/slider.gd
index 5666369e..310b7e2a 100644
--- a/core/ui/components/slider.gd
+++ b/core/ui/components/slider.gd
@@ -83,7 +83,7 @@ func _ready() -> void:
slider.focus_neighbor_top = focus_neighbor_top
slider.focus_previous = focus_previous
slider.focus_next = focus_next
-
+
# Wire up all slider signals
var on_drag_ended := func(changed: bool):
drag_ended.emit(changed)
@@ -98,7 +98,11 @@ func _ready() -> void:
# Set color based on theme
theme_changed.connect(_on_theme_changed)
- _on_theme_changed()
+
+ # Find the parent theme and update if required
+ var effective_theme := ThemeUtils.get_effective_theme(self)
+ if effective_theme:
+ _on_theme_changed()
func _on_theme_changed() -> void:
diff --git a/core/ui/components/status.gd b/core/ui/components/status.gd
index 80c1b0f5..eb689b72 100644
--- a/core/ui/components/status.gd
+++ b/core/ui/components/status.gd
@@ -54,17 +54,24 @@ var color: String = "green":
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
- focus_entered.connect(_on_focus.bind(true))
- focus_exited.connect(_on_focus.bind(false))
- theme_changed.connect(_on_theme_changed)
- _on_theme_changed()
-
label.text = title
description_label.text = description
description_label.visible = description != ""
texture_rect.texture = status_texture_map[status]
texture_rect.modulate = get_theme_color(color, "Status")
+ if Engine.is_editor_hint():
+ return
+
+ focus_entered.connect(_on_focus.bind(true))
+ focus_exited.connect(_on_focus.bind(false))
+ theme_changed.connect(_on_theme_changed)
+
+ # Find the parent theme and update if required
+ var effective_theme := ThemeUtils.get_effective_theme(self)
+ if effective_theme:
+ _on_theme_changed()
+
func _on_theme_changed() -> void:
# Get the style from the set theme so it can be set on the panel container
diff --git a/core/ui/components/tabs_header.gd b/core/ui/components/tabs_header.gd
index c0339267..de2648f8 100644
--- a/core/ui/components/tabs_header.gd
+++ b/core/ui/components/tabs_header.gd
@@ -29,6 +29,10 @@ func _ready() -> void:
tab.text = tab_text
container.add_child(tab)
+ # Don't run in the editor
+ if Engine.is_editor_hint():
+ return
+
# Set the currently selected tab
_on_tab_changed(tabs_state.current_tab)
diff --git a/core/ui/components/text.gd b/core/ui/components/text.gd
index 5bbfc4f7..7ec9aca2 100644
--- a/core/ui/components/text.gd
+++ b/core/ui/components/text.gd
@@ -39,7 +39,11 @@ func _ready() -> void:
focus_entered.connect(_on_focus.bind(true))
focus_exited.connect(_on_focus.bind(false))
theme_changed.connect(_on_theme_changed)
- _on_theme_changed()
+
+ # Find the parent theme and update if required
+ var effective_theme := ThemeUtils.get_effective_theme(self)
+ if effective_theme:
+ _on_theme_changed()
func _on_theme_changed() -> void:
diff --git a/core/ui/components/toggle.gd b/core/ui/components/toggle.gd
index 8c553864..0024da3a 100644
--- a/core/ui/components/toggle.gd
+++ b/core/ui/components/toggle.gd
@@ -63,7 +63,11 @@ func _ready() -> void:
focus_entered.connect(_on_focus.bind(true))
focus_exited.connect(_on_focus.bind(false))
theme_changed.connect(_on_theme_changed)
- _on_theme_changed()
+
+ # Find the parent theme and update if required
+ var effective_theme := ThemeUtils.get_effective_theme(self)
+ if effective_theme:
+ _on_theme_changed()
func _on_theme_changed() -> void: