Prepare v0.6.6-alpha release

This commit is contained in:
Alexander Sellite 2026-08-11 20:02:54 -04:00
parent 96df168851
commit b044d35021
29 changed files with 568 additions and 134 deletions

View file

@ -41,6 +41,8 @@ func _input(event: InputEvent) -> void:
CONTROLLER_MOTION_THRESHOLD
):
_set_controller_active(true)
elif event is InputEventMouseMotion:
_set_controller_active(false)
elif event is InputEventMouseButton:
if (event as InputEventMouseButton).pressed:
_set_controller_active(false)

View file

@ -3,11 +3,28 @@ extends Node
const CONTROLLER_AXIS_THRESHOLD: float = 0.35
const SEMANTIC_MATCH_BONUS: float = 1000000.0
const KEYBOARD_NAVIGATION_ACTIONS: Array[StringName] = [
&"ui_accept",
&"ui_cancel",
&"ui_up",
&"ui_down",
&"ui_left",
&"ui_right",
&"ui_focus_next",
&"ui_focus_prev",
&"ui_page_up",
&"ui_page_down",
&"ui_home",
&"ui_end",
]
var _controller_active: bool = false
var _focus_navigation_active: bool = false
var _last_focus_center: Vector2 = Vector2.ZERO
var _last_focus_key: String = ""
var _scope_chain: Array[WeakRef] = []
var _pending_focus: WeakRef
var _pointer_button_down: bool = false
var _recovery_generation: int = 0
@ -27,23 +44,51 @@ func _input(event: InputEvent) -> void:
if event is InputEventJoypadButton:
var button_event := event as InputEventJoypadButton
if button_event.pressed:
_pointer_button_down = false
_controller_active = true
_focus_navigation_active = true
if button_event.button_index == JOY_BUTTON_LEFT_SHOULDER:
_recovery_generation += 1
_scope_chain.clear()
_pending_focus = null
return
_request_pending_focus()
return
if event is InputEventJoypadMotion:
if absf((event as InputEventJoypadMotion).axis_value) >= (
CONTROLLER_AXIS_THRESHOLD
):
_pointer_button_down = false
_controller_active = true
_focus_navigation_active = true
_request_pending_focus()
return
if event is InputEventMouseMotion:
_pointer_button_down = (
(event as InputEventMouseMotion).button_mask != 0
)
_leave_focus_navigation(not _pointer_button_down)
return
if event is InputEventMouseButton:
if (event as InputEventMouseButton).pressed:
_controller_active = false
var mouse_button := event as InputEventMouseButton
if mouse_button.button_index in [
MOUSE_BUTTON_LEFT,
MOUSE_BUTTON_RIGHT,
MOUSE_BUTTON_MIDDLE,
]:
_pointer_button_down = mouse_button.pressed
_leave_focus_navigation(false)
if not mouse_button.pressed:
_release_current_pointer_focus.call_deferred()
return
if event is InputEventKey and (event as InputEventKey).pressed:
_pointer_button_down = false
_controller_active = false
if _is_keyboard_navigation_event(event):
_focus_navigation_active = true
_request_pending_focus()
else:
_leave_focus_navigation()
func _process(_delta: float) -> void:
@ -58,8 +103,16 @@ func _process(_delta: float) -> void:
func _on_gui_focus_changed(control: Control) -> void:
_recovery_generation += 1
if control != null:
if _controller_active and control is BaseButton:
_remember_focus(control)
if _focus_navigation_active:
_pending_focus = null
if _controller_active and control is BaseButton:
_remember_focus(control)
return
if _keeps_pointer_focus(control):
return
if _is_focusable(control):
_pending_focus = weakref(control)
_release_focus_if_inactive.call_deferred(control)
return
if not _controller_active or _scope_chain.is_empty():
return
@ -92,13 +145,18 @@ func _recover_focus(generation: int) -> void:
scope == null
or not is_instance_valid(scope)
or not scope.is_inside_tree()
or not scope.is_visible_in_tree()
):
continue
if not scope.is_visible_in_tree():
_scope_chain.clear()
return
var replacement := _best_replacement_in(scope)
if replacement != null:
replacement.grab_focus()
return
else:
_scope_chain.clear()
return
_scope_chain.clear()
func _best_replacement_in(scope: Control) -> Control:
@ -124,7 +182,7 @@ func _is_focusable(control: Control) -> bool:
control == null
or not control.is_inside_tree()
or not control.is_visible_in_tree()
or control.focus_mode == Control.FOCUS_NONE
or control.focus_mode not in [Control.FOCUS_CLICK, Control.FOCUS_ALL]
):
return false
var button := control as BaseButton
@ -140,3 +198,80 @@ func _semantic_key(control: Control) -> String:
tooltip = tooltip.trim_suffix(" (show variants)")
tooltip = tooltip.trim_suffix(" (hide variants)")
return "%s|%s|%s" % [control.get_class(), label, tooltip]
func _leave_focus_navigation(release_focus: bool = true) -> void:
_controller_active = false
_focus_navigation_active = false
_recovery_generation += 1
_scope_chain.clear()
var focus_owner: Control = get_viewport().gui_get_focus_owner()
if focus_owner == null or _keeps_pointer_focus(focus_owner):
return
if _is_focusable(focus_owner):
_pending_focus = weakref(focus_owner)
if release_focus:
_release_focus_if_inactive.call_deferred(focus_owner)
func _release_focus_if_inactive(control: Control) -> void:
if (
_focus_navigation_active
or _pointer_button_down
or control == null
or not is_instance_valid(control)
or get_viewport().gui_get_focus_owner() != control
or _keeps_pointer_focus(control)
):
return
get_viewport().gui_release_focus()
func _release_current_pointer_focus() -> void:
var focus_owner: Control = get_viewport().gui_get_focus_owner()
if focus_owner != null:
_release_focus_if_inactive(focus_owner)
func _request_pending_focus() -> void:
if (
not _focus_navigation_active
or _pending_focus == null
or get_viewport().gui_get_focus_owner() != null
):
return
_restore_pending_focus.call_deferred(_recovery_generation)
func _restore_pending_focus(generation: int) -> void:
if (
generation != _recovery_generation
or not _focus_navigation_active
or _pending_focus == null
or get_viewport().gui_get_focus_owner() != null
):
return
var target := _pending_focus.get_ref() as Control
if not _is_focusable(target):
_pending_focus = null
return
_pending_focus = null
target.grab_focus()
func _is_keyboard_navigation_event(event: InputEvent) -> bool:
if event.is_action_pressed(&"open_backpack"):
return false
if (
event.is_action_pressed(&"ui_cancel")
and get_viewport().gui_get_focus_owner() == null
):
return false
for action: StringName in KEYBOARD_NAVIGATION_ACTIONS:
if event.is_action_pressed(action):
return true
return false
func _keeps_pointer_focus(control: Control) -> bool:
return control is LineEdit or control is TextEdit

View file

@ -74,6 +74,8 @@ const SUPPLY_PRICE_HORIZONTAL_PADDING: float = 12.0
@onready var _wallet_label: Label = %WalletLabel
@onready var _shop_panel: PanelContainer = %ShopPanel
@onready var _shop_panel_margin: MarginContainer = $ShopPanel/Margin
@onready var _shop_panel_layout: VBoxContainer = $ShopPanel/Margin/Layout
@onready var _shop_cooler_page: Control = %ShopCoolerPage
@onready var _shop_cooler_mount: Control = %ShopCoolerMount
@onready var _shop_body: HBoxContainer = $ShopPanel/Margin/Layout/Body
@ -367,6 +369,7 @@ func get_shop_cooler_mount() -> Control:
func activate_shop_cooler_page() -> void:
_cooler_page_active = true
_shop_panel.show()
_set_shop_panel_background_pointer_blocking(false)
_shop_body.hide()
_feedback.hide()
_shop_cooler_page.show()
@ -377,6 +380,7 @@ func deactivate_shop_cooler_page() -> void:
_shop_tab_bar.show()
_cooler_page_active = false
_cooler_modal_open = false
_set_shop_panel_background_pointer_blocking(true)
for tab: OrganizerTab in _shop_tabs:
tab.disabled = false
_shop_cooler_page.hide()
@ -385,6 +389,17 @@ func deactivate_shop_cooler_page() -> void:
_feedback.show()
func _set_shop_panel_background_pointer_blocking(blocking: bool) -> void:
var mouse_filter := (
Control.MOUSE_FILTER_STOP
if blocking
else Control.MOUSE_FILTER_IGNORE
)
_shop_panel.mouse_filter = mouse_filter
_shop_panel_margin.mouse_filter = mouse_filter
_shop_panel_layout.mouse_filter = mouse_filter
func set_shop_cooler_modal_open(is_open: bool) -> void:
_cooler_modal_open = is_open
for tab: OrganizerTab in _shop_tabs:

View file

@ -351,7 +351,10 @@ func _open_confirmation(
else BubbleConfirmationPageType.InitialFocus.CONFIRM
)
)
_begin_root_exit(_show_confirmation)
# Confirmation actions reject input while the root page is still leaving.
# Finish that exit before exposing an interactive confirmation page so a
# pointer click can never land in the overlap window and be discarded.
_begin_root_exit(_show_confirmation, false)
func _show_confirmation() -> void:
@ -450,7 +453,10 @@ func _finish_root_entry(generation: int) -> void:
_root_transition_active = false
func _begin_root_exit(completed: Callable) -> void:
func _begin_root_exit(
completed: Callable,
overlap_next_page: bool = true,
) -> void:
if _root_transition_active or not _root_page.visible:
return
_root_transition_generation += 1
@ -458,9 +464,14 @@ func _begin_root_exit(completed: Callable) -> void:
_emit_transition_flurry()
var generation: int = _root_transition_generation
_root_page.transition_out(
_finish_root_exit.bind(generation),
_finish_root_exit.bind(
generation,
Callable() if overlap_next_page else completed,
),
0.0
)
if not overlap_next_page:
return
await get_tree().create_timer(
UIMotion.BUBBLE_TRANSITION_OVERLAP_DELAY
).timeout
@ -471,10 +482,13 @@ func _begin_root_exit(completed: Callable) -> void:
func _finish_root_exit(
generation: int,
completed: Callable,
) -> void:
if generation != _root_transition_generation or not visible:
return
_root_transition_active = false
if completed.is_valid():
completed.call()
func _finish_user_close() -> void:

View file

@ -209,7 +209,7 @@ unique_name_in_owner = true
layout_mode = 2
theme_override_colors/font_color = Color(0.682, 0.733, 0.761, 1)
theme_override_font_sizes/font_size = 22
text = "v0.6.5-alpha"
text = "v0.6.6-alpha"
horizontal_alignment = 1
[node name="Spacer" type="Control" parent="ResponsiveTitleStage/TitlePresentationScaleRoot/Center/MainContent"]