Zmiany w oknach
This commit is contained in:
parent
e843d76c68
commit
a3e470ce9a
@ -8,6 +8,7 @@ const RENDER_LAYERS := preload("res://scripts/ui/render_layers.gd")
|
|||||||
@onready var inventory_button: Button = $HUDRoot/ShipEquipmentPanel/Contents/InventoryButton
|
@onready var inventory_button: Button = $HUDRoot/ShipEquipmentPanel/Contents/InventoryButton
|
||||||
@onready var inventory_panel: Control = $HUDRoot/ShipInventoryPanel
|
@onready var inventory_panel: Control = $HUDRoot/ShipInventoryPanel
|
||||||
|
|
||||||
|
var _window_manager: Node
|
||||||
var _energy_bank_button: Button
|
var _energy_bank_button: Button
|
||||||
var _debug_expanded := false
|
var _debug_expanded := false
|
||||||
var _ability_buttons: Dictionary = {}
|
var _ability_buttons: Dictionary = {}
|
||||||
@ -86,6 +87,7 @@ func _create_window(node_name: String, title: String, dimensions: Vector2) -> Pa
|
|||||||
contents.add_theme_constant_override("separation", 12)
|
contents.add_theme_constant_override("separation", 12)
|
||||||
window.add_child(contents)
|
window.add_child(contents)
|
||||||
var header := HBoxContainer.new()
|
var header := HBoxContainer.new()
|
||||||
|
header.name = "Header"
|
||||||
contents.add_child(header)
|
contents.add_child(header)
|
||||||
var label := Label.new()
|
var label := Label.new()
|
||||||
label.text = title
|
label.text = title
|
||||||
@ -100,6 +102,7 @@ func _create_window(node_name: String, title: String, dimensions: Vector2) -> Pa
|
|||||||
close.pressed.connect(window.hide)
|
close.pressed.connect(window.hide)
|
||||||
header.add_child(close)
|
header.add_child(close)
|
||||||
window.hide()
|
window.hide()
|
||||||
|
_window_manager.call("register_window", window, window, header)
|
||||||
return window
|
return window
|
||||||
|
|
||||||
func _toggle_window(window: PanelContainer) -> void:
|
func _toggle_window(window: PanelContainer) -> void:
|
||||||
@ -125,10 +128,15 @@ func _ready() -> void:
|
|||||||
debug_info.add_theme_font_size_override("font_size", 10)
|
debug_info.add_theme_font_size_override("font_size", 10)
|
||||||
# The display font has unusually tall vertical metrics; keep debug rows compact.
|
# The display font has unusually tall vertical metrics; keep debug rows compact.
|
||||||
debug_info.add_theme_constant_override("line_spacing", mini(0, 13 - ceili(debug_info.get_theme_font("font").get_height(10))))
|
debug_info.add_theme_constant_override("line_spacing", mini(0, 13 - ceili(debug_info.get_theme_font("font").get_height(10))))
|
||||||
|
_window_manager = Node.new()
|
||||||
|
_window_manager.name = "FloatingWindows"
|
||||||
|
_window_manager.set_script(preload("res://scripts/ui/floating_window_manager.gd"))
|
||||||
|
$HUDRoot.add_child(_window_manager)
|
||||||
|
_window_manager.connect("interaction_started", player.get_node("WeaponController").call.bind("block_ui_mouse_input"))
|
||||||
_create_sidebar()
|
_create_sidebar()
|
||||||
_inventory_clip = Control.new()
|
_inventory_clip = Control.new()
|
||||||
_inventory_clip.name = "InventoryDrawer"
|
_inventory_clip.name = "InventoryDrawer"
|
||||||
_inventory_clip.clip_contents = true
|
_inventory_clip.clip_contents = false
|
||||||
_inventory_clip.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
_inventory_clip.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
$HUDRoot.add_child(_inventory_clip)
|
$HUDRoot.add_child(_inventory_clip)
|
||||||
inventory_panel.reparent(_inventory_clip, false)
|
inventory_panel.reparent(_inventory_clip, false)
|
||||||
@ -137,6 +145,7 @@ func _ready() -> void:
|
|||||||
inventory_panel.size = Vector2(560, inventory_panel.get_combined_minimum_size().y)
|
inventory_panel.size = Vector2(560, inventory_panel.get_combined_minimum_size().y)
|
||||||
inventory_panel.scale = Vector2.ONE * EQUIPMENT_UI_SCALE
|
inventory_panel.scale = Vector2.ONE * EQUIPMENT_UI_SCALE
|
||||||
inventory_panel.set("drawer_width", inventory_panel.size.x * EQUIPMENT_UI_SCALE)
|
inventory_panel.set("drawer_width", inventory_panel.size.x * EQUIPMENT_UI_SCALE)
|
||||||
|
_window_manager.call("register_window", _inventory_clip, inventory_panel, inventory_panel.get_node("Contents/Header"))
|
||||||
$HUDRoot.resized.connect(func() -> void: _layout_sidebar.call_deferred())
|
$HUDRoot.resized.connect(func() -> void: _layout_sidebar.call_deferred())
|
||||||
_create_ability_bar()
|
_create_ability_bar()
|
||||||
debug_toggle.pressed.connect(_toggle_debug)
|
debug_toggle.pressed.connect(_toggle_debug)
|
||||||
@ -229,6 +238,7 @@ func _create_sidebar() -> void:
|
|||||||
_cockpit.set("player", player)
|
_cockpit.set("player", player)
|
||||||
_cockpit.set("game", get_parent())
|
_cockpit.set("game", get_parent())
|
||||||
$HUDRoot.add_child(_cockpit)
|
$HUDRoot.add_child(_cockpit)
|
||||||
|
_window_manager.call("register_window", _sidebar, _sidebar, equipment.get_node("Contents/Title"))
|
||||||
_create_navigation()
|
_create_navigation()
|
||||||
_layout_debug()
|
_layout_debug()
|
||||||
|
|
||||||
@ -260,10 +270,10 @@ func _toggle_sidebar() -> void:
|
|||||||
_sidebar.show()
|
_sidebar.show()
|
||||||
else:
|
else:
|
||||||
close_inventory()
|
close_inventory()
|
||||||
_sidebar_tween = create_tween()
|
_sidebar.visible = _sidebar_open
|
||||||
_sidebar_tween.tween_property(_sidebar, "position:x", $HUDRoot.size.x - _sidebar.size.x * EQUIPMENT_UI_SCALE - 8 if _sidebar_open else $HUDRoot.size.x, 0.25).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT if _sidebar_open else Tween.EASE_IN)
|
if _sidebar_open:
|
||||||
if not _sidebar_open:
|
_window_manager.call("focus_window", _sidebar)
|
||||||
_sidebar_tween.tween_callback(_sidebar.hide)
|
_layout_sidebar()
|
||||||
|
|
||||||
func _layout_sidebar() -> void:
|
func _layout_sidebar() -> void:
|
||||||
if not is_instance_valid(_sidebar) or not is_instance_valid(_cockpit):
|
if not is_instance_valid(_sidebar) or not is_instance_valid(_cockpit):
|
||||||
@ -283,7 +293,10 @@ func _layout_sidebar() -> void:
|
|||||||
if is_instance_valid(_captain_skills_ui):
|
if is_instance_valid(_captain_skills_ui):
|
||||||
_captain_skills_ui.call_deferred("_layout")
|
_captain_skills_ui.call_deferred("_layout")
|
||||||
_layout_debug()
|
_layout_debug()
|
||||||
_sidebar.position = Vector2($HUDRoot.size.x - _sidebar.size.x * EQUIPMENT_UI_SCALE - 8 if _sidebar_open else $HUDRoot.size.x, 136)
|
if not _sidebar.has_meta("floating_window_dragged"):
|
||||||
|
_sidebar.position = Vector2($HUDRoot.size.x - _sidebar.size.x * EQUIPMENT_UI_SCALE - 8, 136)
|
||||||
|
else:
|
||||||
|
_window_manager.call("clamp_window", _sidebar)
|
||||||
_sidebar.visible = _sidebar_open
|
_sidebar.visible = _sidebar_open
|
||||||
_inventory_clip.size = inventory_panel.size * EQUIPMENT_UI_SCALE
|
_inventory_clip.size = inventory_panel.size * EQUIPMENT_UI_SCALE
|
||||||
_layout_inventory()
|
_layout_inventory()
|
||||||
@ -291,6 +304,9 @@ func _layout_sidebar() -> void:
|
|||||||
func _layout_inventory() -> void:
|
func _layout_inventory() -> void:
|
||||||
if not is_instance_valid(_inventory_clip) or not is_instance_valid(_sidebar):
|
if not is_instance_valid(_inventory_clip) or not is_instance_valid(_sidebar):
|
||||||
return
|
return
|
||||||
|
if _inventory_clip.has_meta("floating_window_dragged"):
|
||||||
|
_window_manager.call("clamp_window", _inventory_clip)
|
||||||
|
return
|
||||||
var equipment := _sidebar.get_node("Scroll/Contents/ShipEquipmentPanel") as Control
|
var equipment := _sidebar.get_node("Scroll/Contents/ShipEquipmentPanel") as Control
|
||||||
_inventory_clip.position = Vector2(maxf(12.0, _sidebar.position.x - _inventory_clip.size.x - 8), minf(equipment.global_position.y, maxf(8.0, $HUDRoot.size.y - inventory_panel.size.y * EQUIPMENT_UI_SCALE - 8)))
|
_inventory_clip.position = Vector2(maxf(12.0, _sidebar.position.x - _inventory_clip.size.x - 8), minf(equipment.global_position.y, maxf(8.0, $HUDRoot.size.y - inventory_panel.size.y * EQUIPMENT_UI_SCALE - 8)))
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,11 @@ func _ensure_input_action(action_name: StringName, button: MouseButton) -> void:
|
|||||||
if not InputMap.action_has_event(action_name, event):
|
if not InputMap.action_has_event(action_name, event):
|
||||||
InputMap.action_add_event(action_name, event)
|
InputMap.action_add_event(action_name, event)
|
||||||
|
|
||||||
|
func block_ui_mouse_input() -> void:
|
||||||
|
_fire_buttons_down.fill(false)
|
||||||
|
_fire_just_pressed.fill(false)
|
||||||
|
_drag_fire_blocked = true
|
||||||
|
|
||||||
func _unhandled_input(event: InputEvent) -> void:
|
func _unhandled_input(event: InputEvent) -> void:
|
||||||
if not event is InputEventMouseButton:
|
if not event is InputEventMouseButton:
|
||||||
return
|
return
|
||||||
|
|||||||
@ -24,6 +24,7 @@ func _ready() -> void:
|
|||||||
add_child(_screen)
|
add_child(_screen)
|
||||||
_screen.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
_screen.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||||
_screen.hide()
|
_screen.hide()
|
||||||
|
get_parent().get_node("HUDRoot/FloatingWindows").set("modal_overlay", _screen)
|
||||||
var dim := ColorRect.new()
|
var dim := ColorRect.new()
|
||||||
dim.color = Color(0.015, 0.025, 0.045, 0.9)
|
dim.color = Color(0.015, 0.025, 0.045, 0.9)
|
||||||
_screen.add_child(dim)
|
_screen.add_child(dim)
|
||||||
@ -99,6 +100,7 @@ func _build_management() -> void:
|
|||||||
child.queue_free()
|
child.queue_free()
|
||||||
var choosing := not _captain.skill_offers.is_empty()
|
var choosing := not _captain.skill_offers.is_empty()
|
||||||
var heading := HBoxContainer.new()
|
var heading := HBoxContainer.new()
|
||||||
|
heading.name = "Header"
|
||||||
_contents.add_child(heading)
|
_contents.add_child(heading)
|
||||||
var title := _label("AWANS KAPITANA" if choosing else "UMIEJĘTNOŚCI KAPITANA", 25, Color("91dbea"))
|
var title := _label("AWANS KAPITANA" if choosing else "UMIEJĘTNOŚCI KAPITANA", 25, Color("91dbea"))
|
||||||
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
@ -143,6 +145,9 @@ func _build_management() -> void:
|
|||||||
if owned.get_child_count() == 0:
|
if owned.get_child_count() == 0:
|
||||||
_contents.add_child(_label("Nie masz jeszcze umiejętności. To początek Twojej specjalizacji.", 14, Color("91aabd")))
|
_contents.add_child(_label("Nie masz jeszcze umiejętności. To początek Twojej specjalizacji.", 14, Color("91aabd")))
|
||||||
_build_loadout()
|
_build_loadout()
|
||||||
|
var manager := get_parent().get_node("HUDRoot/FloatingWindows")
|
||||||
|
manager.call("register_window", _panel, _panel, heading)
|
||||||
|
manager.call("focus_window", _panel)
|
||||||
_layout.call_deferred()
|
_layout.call_deferred()
|
||||||
|
|
||||||
func _build_loadout() -> void:
|
func _build_loadout() -> void:
|
||||||
@ -314,7 +319,10 @@ func _layout() -> void:
|
|||||||
var ui_scale := minf(1.0, minf((viewport_size.x - 32) / base_size.x, (viewport_size.y - 32) / base_size.y))
|
var ui_scale := minf(1.0, minf((viewport_size.x - 32) / base_size.x, (viewport_size.y - 32) / base_size.y))
|
||||||
_panel.size = base_size
|
_panel.size = base_size
|
||||||
_panel.scale = Vector2.ONE * ui_scale
|
_panel.scale = Vector2.ONE * ui_scale
|
||||||
_panel.position = (viewport_size - base_size * ui_scale) * 0.5
|
if _panel.has_meta("floating_window_dragged"):
|
||||||
|
get_parent().get_node("HUDRoot/FloatingWindows").call("clamp_window", _panel)
|
||||||
|
else:
|
||||||
|
_panel.position = (viewport_size - base_size * ui_scale) * 0.5
|
||||||
var cockpit := get_parent().get("_cockpit") as Control
|
var cockpit := get_parent().get("_cockpit") as Control
|
||||||
if is_instance_valid(cockpit):
|
if is_instance_valid(cockpit):
|
||||||
_bar.position = cockpit.position + Vector2(0, -40)
|
_bar.position = cockpit.position + Vector2(0, -40)
|
||||||
|
|||||||
133
scripts/ui/floating_window_manager.gd
Normal file
133
scripts/ui/floating_window_manager.gd
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
extends Node
|
||||||
|
|
||||||
|
signal interaction_started
|
||||||
|
|
||||||
|
const BASE_Z := 100
|
||||||
|
const Z_STEP := 32
|
||||||
|
var _windows: Array[Dictionary] = []
|
||||||
|
var _active: Control
|
||||||
|
var _dragged: Control
|
||||||
|
var _drag_offset := Vector2.ZERO
|
||||||
|
var modal_overlay: Control
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
process_mode = Node.PROCESS_MODE_ALWAYS
|
||||||
|
get_viewport().size_changed.connect(_clamp_all)
|
||||||
|
|
||||||
|
func register_window(root: Control, panel: Control, header: Control) -> void:
|
||||||
|
for entry in _windows:
|
||||||
|
if entry["root"] == root:
|
||||||
|
entry["header"] = header
|
||||||
|
_prepare_header(header)
|
||||||
|
return
|
||||||
|
panel.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||||
|
_prepare_header(header)
|
||||||
|
var original := panel.get_theme_stylebox("panel").duplicate() as StyleBoxFlat
|
||||||
|
_windows.append({"root": root, "panel": panel, "header": header, "style": original})
|
||||||
|
root.visibility_changed.connect(func() -> void:
|
||||||
|
if root.is_visible_in_tree() and panel.is_visible_in_tree():
|
||||||
|
focus_window.call_deferred(root)
|
||||||
|
elif _dragged == root:
|
||||||
|
_dragged = null
|
||||||
|
)
|
||||||
|
_update_order()
|
||||||
|
|
||||||
|
func _prepare_header(header: Control) -> void:
|
||||||
|
header.custom_minimum_size.y = maxf(header.custom_minimum_size.y, 28.0)
|
||||||
|
header.mouse_default_cursor_shape = Control.CURSOR_DRAG
|
||||||
|
header.tooltip_text = "Przeciągnij, aby przesunąć okno"
|
||||||
|
|
||||||
|
func focus_window(root: Control) -> void:
|
||||||
|
if not is_instance_valid(root) or not root.is_visible_in_tree():
|
||||||
|
return
|
||||||
|
for index in _windows.size():
|
||||||
|
if _windows[index]["root"] == root:
|
||||||
|
var entry := _windows[index]
|
||||||
|
_windows.remove_at(index)
|
||||||
|
_windows.append(entry)
|
||||||
|
root.get_parent().move_child(root, root.get_parent().get_child_count() - 1)
|
||||||
|
_active = root
|
||||||
|
_update_order()
|
||||||
|
return
|
||||||
|
|
||||||
|
func _update_order() -> void:
|
||||||
|
for index in _windows.size():
|
||||||
|
var entry := _windows[index]
|
||||||
|
if not is_instance_valid(entry["root"]):
|
||||||
|
continue
|
||||||
|
var root: Control = entry["root"]
|
||||||
|
root.z_index = BASE_Z + index * Z_STEP
|
||||||
|
var panel: Control = entry["panel"]
|
||||||
|
var style: StyleBoxFlat = entry["style"]
|
||||||
|
if style != null:
|
||||||
|
var current := style.duplicate() as StyleBoxFlat
|
||||||
|
if root == _active:
|
||||||
|
current.border_color = Color("88c9e2")
|
||||||
|
current.shadow_color = Color(0, 0, 0, 0.4)
|
||||||
|
current.shadow_size = 8
|
||||||
|
panel.add_theme_stylebox_override("panel", current)
|
||||||
|
|
||||||
|
func window_at(point: Vector2) -> Control:
|
||||||
|
for index in range(_windows.size() - 1, -1, -1):
|
||||||
|
var entry := _windows[index]
|
||||||
|
var root: Control = entry["root"]
|
||||||
|
var panel: Control = entry["panel"]
|
||||||
|
if not is_instance_valid(root) or not is_instance_valid(panel) or not panel.is_visible_in_tree():
|
||||||
|
continue
|
||||||
|
if is_instance_valid(modal_overlay) and modal_overlay.is_visible_in_tree() and not modal_overlay.is_ancestor_of(root):
|
||||||
|
continue
|
||||||
|
if panel.get_global_rect().has_point(point):
|
||||||
|
return root
|
||||||
|
return null
|
||||||
|
|
||||||
|
func _header_at(root: Control, point: Vector2) -> bool:
|
||||||
|
for entry in _windows:
|
||||||
|
if entry["root"] != root:
|
||||||
|
continue
|
||||||
|
var header: Control = entry["header"]
|
||||||
|
if not is_instance_valid(header) or not header.get_global_rect().has_point(point):
|
||||||
|
return false
|
||||||
|
for button in header.find_children("*", "BaseButton", true, false):
|
||||||
|
if button.is_visible_in_tree() and button.get_global_rect().has_point(point):
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
func _input(event: InputEvent) -> void:
|
||||||
|
if event is InputEventMouseButton:
|
||||||
|
if event.button_index == MOUSE_BUTTON_LEFT and not event.pressed and is_instance_valid(_dragged):
|
||||||
|
_dragged = null
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
return
|
||||||
|
if not event.pressed or get_viewport().gui_is_dragging():
|
||||||
|
return
|
||||||
|
var target := window_at(event.position)
|
||||||
|
if target == null:
|
||||||
|
return
|
||||||
|
focus_window(target)
|
||||||
|
interaction_started.emit()
|
||||||
|
if event.button_index == MOUSE_BUTTON_LEFT and _header_at(target, event.position):
|
||||||
|
_dragged = target
|
||||||
|
_drag_offset = event.position - target.global_position
|
||||||
|
target.set_meta("floating_window_dragged", true)
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
elif event is InputEventMouseMotion and is_instance_valid(_dragged):
|
||||||
|
if not _dragged.is_visible_in_tree():
|
||||||
|
_dragged = null
|
||||||
|
return
|
||||||
|
_dragged.global_position = event.position - _drag_offset
|
||||||
|
clamp_window(_dragged)
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
func clamp_window(root: Control) -> void:
|
||||||
|
if not is_instance_valid(root) or not root.is_inside_tree():
|
||||||
|
return
|
||||||
|
var viewport_size := get_viewport().get_visible_rect().size
|
||||||
|
var dimensions := root.size * root.get_global_transform().get_scale().abs()
|
||||||
|
root.global_position = root.global_position.clamp(Vector2(8, 8), (viewport_size - dimensions - Vector2(8, 8)).max(Vector2(8, 8)))
|
||||||
|
|
||||||
|
func _clamp_all() -> void:
|
||||||
|
for entry in _windows:
|
||||||
|
var root: Control = entry["root"]
|
||||||
|
if is_instance_valid(root) and root.has_meta("floating_window_dragged"):
|
||||||
|
clamp_window(root)
|
||||||
1
scripts/ui/floating_window_manager.gd.uid
Normal file
1
scripts/ui/floating_window_manager.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dg15v55ayqn22
|
||||||
@ -17,6 +17,7 @@ var _missile_button: Button
|
|||||||
var _gamma_buy: Button
|
var _gamma_buy: Button
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
get_parent().get_node("FloatingWindows").call("register_window", self, panel, $Center/Panel/Contents/Title)
|
||||||
_gamma_buy = Button.new()
|
_gamma_buy = Button.new()
|
||||||
_gamma_buy.name = "BuyIrradiatedAmmunition"
|
_gamma_buy.name = "BuyIrradiatedAmmunition"
|
||||||
_gamma_buy.custom_minimum_size.y = 36
|
_gamma_buy.custom_minimum_size.y = 36
|
||||||
@ -69,6 +70,9 @@ func _process(_delta: float) -> void:
|
|||||||
_position_at_planet()
|
_position_at_planet()
|
||||||
|
|
||||||
func _position_at_planet() -> void:
|
func _position_at_planet() -> void:
|
||||||
|
if has_meta("floating_window_dragged"):
|
||||||
|
get_parent().get_node("FloatingWindows").call("clamp_window", self)
|
||||||
|
return
|
||||||
if not is_instance_valid(planet):
|
if not is_instance_valid(planet):
|
||||||
return
|
return
|
||||||
var canvas := get_viewport().get_canvas_transform()
|
var canvas := get_viewport().get_canvas_transform()
|
||||||
|
|||||||
@ -108,15 +108,7 @@ func _add_resource_card(row: HBoxContainer, data: Array) -> void:
|
|||||||
func toggle_drawer() -> void:
|
func toggle_drawer() -> void:
|
||||||
_set_drawer_open(not _drawer_open)
|
_set_drawer_open(not _drawer_open)
|
||||||
|
|
||||||
func close_drawer(immediately: bool = false) -> void:
|
func close_drawer(_immediately: bool = false) -> void:
|
||||||
if immediately:
|
|
||||||
if _slide_tween != null and _slide_tween.is_running():
|
|
||||||
_slide_tween.kill()
|
|
||||||
_drawer_open = false
|
|
||||||
GameState.hud_panel_state["inventory_open"] = false
|
|
||||||
position.x = drawer_width
|
|
||||||
hide()
|
|
||||||
return
|
|
||||||
_set_drawer_open(false)
|
_set_drawer_open(false)
|
||||||
|
|
||||||
func _set_drawer_open(value: bool) -> void:
|
func _set_drawer_open(value: bool) -> void:
|
||||||
@ -124,10 +116,9 @@ func _set_drawer_open(value: bool) -> void:
|
|||||||
_slide_tween.kill()
|
_slide_tween.kill()
|
||||||
_drawer_open = value
|
_drawer_open = value
|
||||||
GameState.hud_panel_state["inventory_open"] = value
|
GameState.hud_panel_state["inventory_open"] = value
|
||||||
if value and not visible:
|
position = Vector2.ZERO
|
||||||
position = Vector2(drawer_width, 0)
|
visible = value
|
||||||
show()
|
if value:
|
||||||
_slide_tween = create_tween()
|
var manager := get_parent().get_parent().get_node_or_null("FloatingWindows")
|
||||||
_slide_tween.tween_property(self, "position:x", 0.0 if value else drawer_width, 0.25).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT if value else Tween.EASE_IN)
|
if manager != null:
|
||||||
if not value:
|
manager.call("focus_window", get_parent())
|
||||||
_slide_tween.tween_callback(hide)
|
|
||||||
|
|||||||
1
tests/levels/asteroid_debris_geometry_test.gd.uid
Normal file
1
tests/levels/asteroid_debris_geometry_test.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://bc7qva7gpdo01
|
||||||
1
tests/levels/exploration_death_restart_test.gd.uid
Normal file
1
tests/levels/exploration_death_restart_test.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://d2cbqokcr60c5
|
||||||
122
tests/ships/floating_windows_test.gd
Normal file
122
tests/ships/floating_windows_test.gd
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
extends Node
|
||||||
|
|
||||||
|
const GAME := preload("res://scenes/game/game.tscn")
|
||||||
|
|
||||||
|
func motion(point: Vector2, relative: Vector2 = Vector2.ZERO) -> void:
|
||||||
|
var event := InputEventMouseMotion.new()
|
||||||
|
event.position = point
|
||||||
|
event.global_position = point
|
||||||
|
event.relative = relative
|
||||||
|
event.button_mask = MOUSE_BUTTON_MASK_LEFT if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) else 0
|
||||||
|
get_viewport().push_input(event, true)
|
||||||
|
|
||||||
|
func mouse(point: Vector2, pressed: bool) -> void:
|
||||||
|
var event := InputEventMouseButton.new()
|
||||||
|
event.position = point
|
||||||
|
event.global_position = point
|
||||||
|
event.button_index = MOUSE_BUTTON_LEFT
|
||||||
|
event.pressed = pressed
|
||||||
|
event.button_mask = MOUSE_BUTTON_MASK_LEFT if pressed else 0
|
||||||
|
get_viewport().push_input(event, true)
|
||||||
|
|
||||||
|
func click(point: Vector2) -> void:
|
||||||
|
motion(point)
|
||||||
|
mouse(point, true)
|
||||||
|
mouse(point, false)
|
||||||
|
|
||||||
|
func drag(header: Control, displacement: Vector2) -> void:
|
||||||
|
var origin := header.get_global_rect().position + Vector2(15, header.get_global_rect().size.y * 0.5)
|
||||||
|
motion(origin)
|
||||||
|
mouse(origin, true)
|
||||||
|
motion(origin + displacement, displacement)
|
||||||
|
mouse(origin + displacement, false)
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
process_mode = Node.PROCESS_MODE_ALWAYS
|
||||||
|
GameState.start_new_run()
|
||||||
|
var game := GAME.instantiate() as Node2D
|
||||||
|
add_child(game)
|
||||||
|
await get_tree().process_frame
|
||||||
|
await get_tree().process_frame
|
||||||
|
game.set_physics_process(false)
|
||||||
|
var ship := game.get_node("PlayerShip") as CharacterBody2D
|
||||||
|
ship.set_physics_process(false)
|
||||||
|
ship.get_node("WeaponController").set_physics_process(false)
|
||||||
|
for enemy in get_tree().get_nodes_in_group("enemies"):
|
||||||
|
enemy.set_physics_process(false)
|
||||||
|
var hud := game.get_node("HUD")
|
||||||
|
var manager := hud.get_node("HUDRoot/FloatingWindows")
|
||||||
|
var craft: PanelContainer = hud.get("_crafting_window")
|
||||||
|
var inventory: Control = hud.get("inventory_panel")
|
||||||
|
var drawer: Control = hud.get("_inventory_clip")
|
||||||
|
var sidebar: Control = hud.get("_sidebar")
|
||||||
|
hud.call("_toggle_inventory")
|
||||||
|
craft.show()
|
||||||
|
await get_tree().process_frame
|
||||||
|
await get_tree().process_frame
|
||||||
|
# Put inventory directly behind crafting. Its salvage button occupies the same pixel.
|
||||||
|
drawer.set_meta("floating_window_dragged", true)
|
||||||
|
var recipe: Button = craft.get_node("Contents/CraftingPanel").get("_recipe_buttons")["parts"]
|
||||||
|
var point := recipe.get_global_rect().get_center()
|
||||||
|
var first_slot := inventory.get_node("Contents/CargoGrid").get_child(0) as Control
|
||||||
|
drawer.global_position += point - first_slot.get_global_rect().get_center()
|
||||||
|
manager.call("focus_window", drawer)
|
||||||
|
manager.call("focus_window", craft)
|
||||||
|
var stock := ship.get_node("ShipInventory") as ShipInventory
|
||||||
|
var item := ItemInstance.new()
|
||||||
|
item.definition = preload("res://resources/items/plasma_cannon.tres")
|
||||||
|
assert(stock.add_item(item))
|
||||||
|
GameState.captain.minerals.merge({"iron": 2, "titanium": 2, "aluminium": 2}, true)
|
||||||
|
GameState.captain.emit_changed()
|
||||||
|
await get_tree().process_frame
|
||||||
|
assert(manager.call("window_at", point) == craft)
|
||||||
|
click(point)
|
||||||
|
assert(GameState.captain.parts == 1.0, "Top crafting button must receive the click")
|
||||||
|
assert(stock.cargo[0] == item, "Underlying inventory must not consume the click")
|
||||||
|
assert(craft.z_index > drawer.z_index)
|
||||||
|
assert(craft.get_index() > drawer.get_index())
|
||||||
|
# Dragging a title moves its window; content clicks do not start a window drag.
|
||||||
|
var before := craft.global_position
|
||||||
|
drag(craft.get_node("Contents/Header"), Vector2(-80, -60))
|
||||||
|
assert(craft.global_position.is_equal_approx(before + Vector2(-80, -60)))
|
||||||
|
await get_tree().process_frame
|
||||||
|
assert(craft.global_position.is_equal_approx(before + Vector2(-80, -60)))
|
||||||
|
# The exposed inventory header can activate and raise the lower window.
|
||||||
|
drawer.global_position = Vector2(70, 80)
|
||||||
|
manager.call("focus_window", craft)
|
||||||
|
var inv_header := inventory.get_node("Contents/Header") as Control
|
||||||
|
click(inv_header.get_global_rect().position + Vector2(10, 8))
|
||||||
|
assert(drawer.z_index > craft.z_index and drawer.get_index() > craft.get_index())
|
||||||
|
before = drawer.global_position
|
||||||
|
drag(inv_header, Vector2(100, 80))
|
||||||
|
assert(drawer.global_position.is_equal_approx(before + Vector2(100, 80)))
|
||||||
|
hud.call("_layout_inventory")
|
||||||
|
assert(drawer.global_position.is_equal_approx(before + Vector2(100, 80)))
|
||||||
|
# Close buttons do not turn into drag handles; reopening preserves the moved position.
|
||||||
|
click(inventory.get_node("Contents/Header/CloseButton").get_global_rect().get_center())
|
||||||
|
assert(not inventory.visible)
|
||||||
|
inventory.call("_set_drawer_open", true)
|
||||||
|
assert(drawer.global_position.is_equal_approx(before + Vector2(100, 80)))
|
||||||
|
# Sidebar dragging survives equipment relayout and viewport clamping.
|
||||||
|
manager.call("focus_window", sidebar)
|
||||||
|
before = sidebar.global_position
|
||||||
|
drag(sidebar.get_node("Scroll/Contents/ShipEquipmentPanel/Contents/Title"), Vector2(-100, -50))
|
||||||
|
hud.call("_layout_sidebar")
|
||||||
|
assert(sidebar.global_position.is_equal_approx(before + Vector2(-100, -50)))
|
||||||
|
# The captain modal remains draggable while paused and blocks windows below it.
|
||||||
|
var skills: CanvasLayer = hud.get("_captain_skills_ui")
|
||||||
|
skills.call("open_management")
|
||||||
|
await get_tree().process_frame
|
||||||
|
await get_tree().process_frame
|
||||||
|
assert(get_tree().paused)
|
||||||
|
var panel: Control = skills.get("_panel")
|
||||||
|
before = panel.global_position
|
||||||
|
drag((skills.get("_contents") as Control).get_node("Header"), Vector2(50, 30))
|
||||||
|
assert(panel.global_position.is_equal_approx(before + Vector2(50, 30)))
|
||||||
|
skills.call("_layout")
|
||||||
|
assert(panel.global_position.is_equal_approx(before + Vector2(50, 30)))
|
||||||
|
skills.call("_close")
|
||||||
|
assert(not get_tree().paused)
|
||||||
|
assert(not ship.get_node("WeaponController").get("_fire_buttons_down")[0])
|
||||||
|
print("FLOATING_WINDOWS_TEST: PASS (real GUI click routing over inventory, matching z/tree order, title drag, focus/raise, close/reopen, relayout persistence, paused captain modal, gameplay input blocked)")
|
||||||
|
get_tree().quit()
|
||||||
1
tests/ships/floating_windows_test.gd.uid
Normal file
1
tests/ships/floating_windows_test.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dus5cwp87y35y
|
||||||
4
tests/ships/floating_windows_test.tscn
Normal file
4
tests/ships/floating_windows_test.tscn
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[gd_scene load_steps=2 format=3]
|
||||||
|
[ext_resource type="Script" path="res://tests/ships/floating_windows_test.gd" id="1"]
|
||||||
|
[node name="FloatingWindowsTest" type="Node"]
|
||||||
|
script = ExtResource("1")
|
||||||
Loading…
x
Reference in New Issue
Block a user