155 lines
6.3 KiB
GDScript
155 lines
6.3 KiB
GDScript
# ui_base.gd
|
||
class_name UIPage
|
||
extends Control
|
||
|
||
# --- Signals ---
|
||
signal navigate_requested(target_page_node: Control)
|
||
signal workflow_end_requested(is_confirm_and_trigger_action: bool)
|
||
|
||
# --- Exported Variables for Inspector ---
|
||
## The page to navigate to when a "next" or "confirm" action is triggered.
|
||
@export var next_ui_node_path: NodePath
|
||
## The page to navigate to when a "back" or "cancel" action is triggered.
|
||
@export var back_ui_node_path: NodePath
|
||
## For sub-pages, this is their direct parent page (e.g., A is parent of A_1).
|
||
## Used if back_ui_node_path is not set for a sub-page, to return to its parent.
|
||
@export var parent_page_node_path: NodePath
|
||
## If true (default), this page will be hidden when go_next() is called and
|
||
## a next_ui_node_path is successfully navigated to.
|
||
## If false, this page will remain visible.
|
||
@export var hide_on_next: bool = true
|
||
|
||
# --- Internal Variables ---
|
||
var main_controller # Will hold reference to Main.gd
|
||
|
||
func _ready():
|
||
# Attempt to find the Main controller. Assumes Main is the owner or a known path.
|
||
if owner and owner.has_method("get_shared_data"): # Simple check if owner is Main
|
||
main_controller = owner
|
||
|
||
# --- Godot Input Handling ---
|
||
func _input(event):
|
||
# Only process input if the page is currently visible and the feature is enabled
|
||
if not visible:
|
||
return
|
||
|
||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_RIGHT and event.is_pressed():
|
||
print("UIPage '", name, "' detected right-click. Attempting to go back.")
|
||
go_back()
|
||
get_viewport().set_input_as_handled() # Consume the event so other nodes don't process it
|
||
|
||
# --- Public Methods ---
|
||
func show_page():
|
||
# 仅当页面之前是不可见状态时,才设置 visible = true。
|
||
# 或者更简单地,总是设置 visible = true,因为重复设置 true 没有副作用。
|
||
if not visible:
|
||
visible = true
|
||
|
||
func hide_page():
|
||
# Only call _on_page_deactivated if the page was previously visible.
|
||
if visible:
|
||
_on_page_deactivated() # Call the overridable method before hiding
|
||
visible = false
|
||
# else:
|
||
# print("UIPage " + name + " already hidden. Not re-deactivating.")
|
||
|
||
func _on_page_activated():
|
||
# Base implementation can be empty or log
|
||
print("UIPage activated: " + name)
|
||
pass
|
||
|
||
func _on_page_deactivated():
|
||
# Base implementation can be empty or log
|
||
print("UIPage deactivated: " + name)
|
||
pass
|
||
|
||
# --- Navigation Methods (to be called by buttons on this page) ---
|
||
func go_next():
|
||
if not next_ui_node_path.is_empty():
|
||
var target_node = get_node_or_null(next_ui_node_path)
|
||
if target_node and target_node is Control: # Ensure target_node is a Control, ideally a UIPage
|
||
emit_signal("navigate_requested", target_node)
|
||
else:
|
||
printerr("Next UI node not found or not a Control: ", next_ui_node_path, " from page ", name)
|
||
emit_signal("workflow_end_requested", true) # Or some error state
|
||
else:
|
||
# next_ui_node is null, meaning workflow ends with confirmation
|
||
emit_signal("workflow_end_requested", true)
|
||
|
||
func go_back():
|
||
var target_path_to_try = back_ui_node_path
|
||
if target_path_to_try.is_empty() and not parent_page_node_path.is_empty():
|
||
target_path_to_try = parent_page_node_path
|
||
|
||
if not target_path_to_try.is_empty():
|
||
var target_node = get_node_or_null(target_path_to_try)
|
||
if target_node and target_node is Control: # Ensure target_node is a Control, ideally a UIPage
|
||
emit_signal("navigate_requested", target_node)
|
||
else:
|
||
printerr("Back/Parent UI node not found or not a Control: ", target_path_to_try, " from page ", name)
|
||
emit_signal("workflow_end_requested", false) # Or some error state
|
||
else:
|
||
emit_signal("workflow_end_requested", false)
|
||
|
||
# --- Data Access Methods (Examples) ---
|
||
func get_main_data(key: String, default = null):
|
||
if main_controller:
|
||
return main_controller.get_shared_data(key, default)
|
||
else: # <--- 修改点: 将警告移入 else 分支
|
||
push_warning("Cannot get_main_data, Main controller not found from page '", name, "'")
|
||
return default
|
||
|
||
func set_main_data(key: String, value):
|
||
if main_controller:
|
||
main_controller.set_shared_data(key, value)
|
||
else: # <--- 修改点: 将警告移入 else 分支
|
||
push_warning("Cannot set_main_data, Main controller not found from page '", name, "'. Data not set.")
|
||
|
||
# --- Helper function to check if potential_ancestor is an ancestor of node_to_check ---
|
||
func _is_ancestor_of(potential_ancestor: Node, node_to_check: Node) -> bool:
|
||
if not potential_ancestor or not node_to_check:
|
||
return false
|
||
# Ensure both nodes are in the scene tree to safely use get_parent()
|
||
if not node_to_check.is_inside_tree() or not potential_ancestor.is_inside_tree():
|
||
return false
|
||
|
||
var current_parent = node_to_check.get_parent()
|
||
while current_parent:
|
||
if current_parent == potential_ancestor:
|
||
return true
|
||
current_parent = current_parent.get_parent() # Move up the tree
|
||
return false
|
||
|
||
# --- Helper for navigating to a specific child page (MODIFIED) ---
|
||
func go_to_child_page(child_page_node: Control):
|
||
# Check if a child_page_node was provided
|
||
if not child_page_node:
|
||
printerr("Error in '", name, ".go_to_child_page': Target child_page_node is null.")
|
||
return
|
||
|
||
# This check prevents navigating to an ancestor using this function.
|
||
# If child_page_node is an ancestor of self, it's considered an invalid target for "go_to_child_page".
|
||
# Original problematic line: if child_page_node and child_page_node.is_a_parent_of(self):
|
||
if _is_ancestor_of(child_page_node, self):
|
||
printerr("Error in '", name, ".go_to_child_page': Navigation target '", child_page_node.name,
|
||
"' cannot be an ancestor of the current page '", self.name,
|
||
"'. 'go_to_child_page' is intended for pages that are descendants or unrelated in the scene tree hierarchy.")
|
||
return
|
||
|
||
# Optional: If 'go_to_child_page' strictly means a *direct child* in the scene tree,
|
||
# you might want to add a check like this:
|
||
# if child_page_node.get_parent() != self:
|
||
# printerr("Error in '", name, ".go_to_child_page': Target '", child_page_node.name,
|
||
# "' is not a direct child of this page '", self.name, "'.")
|
||
# return
|
||
|
||
# Ensure child_page_node is a Control (already type-hinted, but good for safety)
|
||
if child_page_node is Control:
|
||
emit_signal("navigate_requested", child_page_node)
|
||
#self._on_page_deactivated()
|
||
else:
|
||
# This case might occur if something other than a Control node is passed,
|
||
# despite the type hint.
|
||
printerr("Error in '", name, ".go_to_child_page': Invalid target '", child_page_node,
|
||
"'. It is not a Control node.")
|