diff options
Diffstat (limited to 'addons/escoria-core/game/esc_inputs_manager.gd')
| -rw-r--r-- | addons/escoria-core/game/esc_inputs_manager.gd | 886 |
1 files changed, 886 insertions, 0 deletions
diff --git a/addons/escoria-core/game/esc_inputs_manager.gd b/addons/escoria-core/game/esc_inputs_manager.gd new file mode 100644 index 0000000..762da5e --- /dev/null +++ b/addons/escoria-core/game/esc_inputs_manager.gd @@ -0,0 +1,886 @@ +## Escoria inputs manager +## +## Catches, handles and distributes input events for the game. +## @MANAGER +extends Resource +class_name ESCInputsManager + + +## Valid input flags[br] +## * INPUT_ALL: All input is allowed[br] +## * INPUT_NONE: No input is allowed at all[br] +## * INPUT_SKIP: Only skipping dialogs is allowed +enum { + INPUT_ALL, + INPUT_NONE, + INPUT_SKIP, +} + + +## Input action for use by InputMap +const ESC_SHOW_DEBUG_PROMPT = "esc_show_debug_prompt" + +## Input action for use by InputMap that represents a "primary action" from an +## input device, such as a left-click on a mouse or the X button on an XBox +## controller +const ESC_UI_PRIMARY_ACTION = "esc_ui_primary_action" + + +## The current input mode +var input_mode = INPUT_ALL + +## A LIFO stack of hovered items +var hover_stack: HoverStack + +## The global id of the topmost item from the hover_stack +var hotspot_focused: String = "" + +## Function reference that can be used to intercept and process input events. +## If set, this function must have the following signature:[br] +##[br] +## (event: InputEvent, is_default_state: bool) -> bool[br] +##[br] +## #### Parameters[br] +##[br] +## - event: The event to process[br] +## - is_default_state: Whether the current state is escoria.GAME_STATE.DEFAULT[br] +##[br] +## **Returns** Whether the function processed the event. +var custom_input_handler = null + +## The currently hovered element. Usually the one on top of the hover stack. +var _hovered_element = null + +## Constructor.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init(): + escoria.event_manager.connect("event_finished", Callable(self, "_on_event_finished")) + hover_stack = HoverStack.new() + hover_stack.connect("hover_stack_changed", Callable(self, "_on_hover_stack_changed")) + +## Called when an event is finished, so that the current hotspot is reset.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |return_code|`int`|The return code of the event.|yes|[br] +## |event_name|`String`|The name of the event.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_event_finished(return_code: int, event_name: String): + if _hovered_element == null: + hotspot_focused = "" + + +## Register core signals (from escoria.gd)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func register_core(): + escoria.game_scene.request_pause_menu.connect(_on_pause_menu_requested) + +## Connect the item signals to the local methods.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`Node`|The inventory item node to register.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func register_inventory_item(item: Node): + item.mouse_left_inventory_item.connect(_on_mouse_left_click_inventory_item) + item.mouse_double_left_inventory_item.connect(_on_mouse_double_left_click_inventory_item) + item.mouse_right_inventory_item.connect(_on_mouse_right_click_inventory_item) + item.inventory_item_focused.connect(_on_mouse_entered_inventory_item) + item.inventory_item_unfocused.connect(_on_mouse_exited_inventory_item) + +## Connect background signals to local methods.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |background|`ESCBackground`|The background to register.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func register_background(background: ESCBackground): + background.left_click_on_bg.connect(_on_left_click_on_bg) + background.right_click_on_bg.connect(_on_right_click_on_bg) + background.double_left_click_on_bg.connect(_on_double_left_click_on_bg) + background.mouse_wheel_up.connect(_on_mousewheel_action.bind(1)) + background.mouse_wheel_down.connect(_on_mousewheel_action.bind(-1)) + background.hovered_bg.connect(_on_hover_bg) + + +## Registers a function that can be used to intercept and process input events. `callback` must have the following signature: (event: InputEvent, is_default_state: bool) -> bool ...where:[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |callback|`Variant`|Function reference satisfying the above contract event The event to process is_default_state Whether the current state is escoria.GAME_STATE.DEFAULT returns whether the function processed the event `callback` is responsible for calling `get_tree().set_input_as_handled()`, if appropriate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func register_custom_input_handler(callback) -> void: + custom_input_handler = callback + + +## If a callback was specified via `register_custom_input_handler()`, forwards the event to the callback and returns its result; otherwise,[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event|`InputEvent`|The event to process|yes|[br] +## |is_default_state|`bool`|Whether the current state is escoria.GAME_STATE.DEFAULT|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns `false`. (`bool`) +func try_custom_input_handler(event: InputEvent, is_default_state: bool) -> bool: + if custom_input_handler: + return custom_input_handler.call(event, is_default_state) + else: + return false + +## Callback called by hover stack content change.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_hover_stack_changed(): + if hover_stack.is_empty(): + unset_hovered_node(_hovered_element) + else: + set_hovered_node(hover_stack.get_top_item()) + + +## Sets the hovered node and calls its mouse_entered() method if it was the top most item in hover_stack.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`ESCItem`|the item that was focused (mouse_entered)|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func set_hovered_node(item: ESCItem) -> bool: + if _hovered_element != item \ + and escoria.action_manager.is_object_actionable(item.global_id) \ + or (item is ESCPlayer and not (item as ESCPlayer).selectable): + _hovered_element = item + _hovered_element.mouse_entered() + return true + # If tested item was already hovered + # or is not actionable (not selectable for ESCPlayer) then do nothing + if _hovered_element == item \ + or not escoria.action_manager.is_object_actionable(item.global_id) \ + or (item is ESCPlayer and not (item as ESCPlayer).selectable): + return true + if not is_instance_valid(_hovered_element) or hover_stack.get_top_item() != item: + _hovered_element = item + _hovered_element.mouse_entered() + return true + # Else, the tested item is currently on top of hover stack, then do nothing + else: + return false + + +## Unsets the hovered node.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`ESCItem`|the item that was unfocused (mouse_exited)|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func unset_hovered_node(item: ESCItem): + if item == null: + return + if _hovered_element == item: + _hovered_element.do_mouse_exited() + _hovered_element = null + hotspot_focused = "" + + +# Background was hovered +func _on_hover_bg() -> void: + escoria.main.current_scene.game.hovered_bg() + +## The background was clicked with the LMB.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Vector2`|Position of the click.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_left_click_on_bg(position: Vector2) -> void: + if input_mode == INPUT_ALL: + hotspot_focused = "" + escoria.logger.info( + self, + "Left click on background at %s." % str(position) + ) + escoria.main.current_scene.game.left_click_on_bg(position) + +## The background was double-clicked with the LMB.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Vector2`|Position of the click.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_double_left_click_on_bg(position: Vector2) -> void: + if input_mode == INPUT_ALL: + hotspot_focused = "" + escoria.logger.info( + self, + "Double left click on background at %s." % str(position) + ) + escoria.main.current_scene.game.left_double_click_on_bg(position) + +## The background was clicked with the RMB.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Vector2`|Position of the click.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_right_click_on_bg(position: Vector2) -> void: + if input_mode == INPUT_ALL and hotspot_focused.is_empty(): + escoria.logger.info( + self, + "Right click on background at %s." % str(position) + ) + escoria.main.current_scene.game.right_click_on_bg(position) + +## An inventory item was clicked with the LMB.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |inventory_item_global_id|`String`|The global id of the clicked inventory item.|yes|[br] +## |event|`InputEvent`|The input event received.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_left_click_inventory_item( + inventory_item_global_id: String, + event: InputEvent +) -> void: + escoria.logger.info( + self, + "Inventory item %s left clicked." % inventory_item_global_id + ) + escoria.main.current_scene.game.left_click_on_inventory_item( + inventory_item_global_id, + event + ) + +## An inventory item was clicked with the RMB.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |inventory_item_global_id|`String`|The global id of the clicked inventory item.|yes|[br] +## |event|`InputEvent`|The input event received.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_right_click_inventory_item( + inventory_item_global_id: String, + event: InputEvent +) -> void: + if input_mode == INPUT_ALL: + escoria.logger.info( + self, + "Inventory item %s right clicked." % inventory_item_global_id + ) + escoria.main.current_scene.game.right_click_on_inventory_item( + inventory_item_global_id, + event + ) + +## An inventory item was double-clicked with the LMB.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |inventory_item_global_id|`String`|The global id of the clicked inventory item.|yes|[br] +## |event|`InputEvent`|The input event received.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_double_left_click_inventory_item( + inventory_item_global_id: String, + event: InputEvent +) -> void: + if input_mode == INPUT_ALL: + escoria.logger.info( + self, + "Inventory item %s double left clicked." % inventory_item_global_id + ) + escoria.main.current_scene.game.left_double_click_on_inventory_item( + inventory_item_global_id, + event + ) + +## The mouse entered an inventory item.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |inventory_item_global_id|`String`|The global id of the inventory item that is hovered.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_entered_inventory_item(inventory_item_global_id: String) -> void: + escoria.logger.info( + self, + "Inventory item %s focused." % inventory_item_global_id + ) + escoria.main.current_scene.game.inventory_item_focused( + inventory_item_global_id + ) + +## The mouse exited an inventory item.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_exited_inventory_item() -> void: + escoria.logger.info( + self, + "Inventory item unfocused." + ) + escoria.main.current_scene.game.inventory_item_unfocused() + +## The mouse entered an Escoria item.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`ESCItem`|The Escoria item hovered.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_entered_item(item: ESCItem) -> void: + if item as ESCPlayer and not (item as ESCPlayer).selectable: + escoria.logger.trace( + self, + "Ignoring mouse entering player %s: Player not selectable." % [item.global_id] + ) + if hover_stack.is_empty(): + hotspot_focused = "" + escoria.main.current_scene.game.element_unfocused() + else: + hotspot_focused = hover_stack.get_top_item().global_id + escoria.main.current_scene.game.element_focused(hotspot_focused) + return + + if not escoria.action_manager.is_object_actionable(item.global_id): + escoria.logger.debug( + self, + "Ignoring mouse entering item %s." % [item.global_id] + ) + return + + escoria.logger.info( + self, + "Item focused: %s" % item.global_id + ) + + hotspot_focused = item.global_id + escoria.main.current_scene.game.element_focused(item.global_id) + +## The mouse exited an Escoria item.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`ESCItem`|The Escoria item hovered.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_exited_item(item: ESCItem) -> void: + var object: ESCObject = escoria.object_manager.get_object(item.global_id) + if object and not object.interactive: + hover_stack.erase_item(item) + escoria.main.current_scene.game.element_unfocused() + return + + if object and not object.interactive: + return + if object and is_instance_valid(object.node) and object.node is ESCPlayer and not (object.node as ESCPlayer).selectable: + hotspot_focused = "" + return + + escoria.logger.info( + self, + "Item unfocused: %s" % hotspot_focused + ) + + if hover_stack.is_empty(): + hotspot_focused = "" + escoria.main.current_scene.game.element_unfocused() + else: + hotspot_focused = hover_stack.get_top_item().global_id + escoria.main.current_scene.game.element_focused(hotspot_focused) + + +## Function called when the item is set to non-interactive, to re-trigger an input on underlying item.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`ESCItem`|The `ESCItem` that was set non-interactive|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func on_item_non_interactive(item: ESCItem) -> void: + var object: ESCObject = escoria.object_manager.get_object(item.global_id) + if object and not object.interactive: + hover_stack.erase_item(item) + escoria.main.current_scene.game.element_unfocused() + + if hover_stack.is_empty(): + return + else: + var new_item = hover_stack.get_top_item() + escoria.action_manager.set_action_input_state(ESCActionManager.ACTION_INPUT_STATE.AWAITING_VERB_OR_ITEM) + new_item.mouse_entered() + + +# An Escoria item was clicked with the LMB +# +# #### Parameters +# +# - item: The Escoria item clicked +# - event: The input event from the click +func _on_mouse_left_clicked_item(item: ESCItem, event: InputEvent) -> void: + if input_mode == INPUT_ALL: + # Manage clicking through ESCPlayer (if ESCPlayer.selectable is false) + if item as ESCPlayer and not (item as ESCPlayer).selectable: + escoria.logger.trace( + self, + "Ignoring left click on player %s: Player not selectable." + % [item.global_id] + ) + + # Get next object in hover stack and forward event to it + if not hover_stack.is_empty(): + var next_item = hover_stack.pop_top_item() + _on_mouse_left_clicked_item(next_item, event) + else: # if no next object, consider this click as background click + hotspot_focused = "" + _on_left_click_on_bg(event.position) + return + + # Clicked object can't be actioned and there is no other object behind + # We consider this click as a background click + if not escoria.action_manager.is_object_actionable(item.global_id) \ + and hover_stack.is_empty(): + hotspot_focused = "" + _on_left_click_on_bg(event.position) + return + + # Finally, execute the action on the ESCItem + hotspot_focused = item.global_id + escoria.main.current_scene.game.left_click_on_item( + item.global_id, + event + ) + +## An Escoria item was double-clicked with the LMB.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`ESCItem`|The Escoria item clicked.|yes|[br] +## |event|`InputEvent`|The input event from the click.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_left_double_clicked_item( + item: ESCItem, + event: InputEvent +) -> void: + if input_mode == INPUT_ALL: + # Manage clicking through ESCPlayer (if ESCPlayer.selectable is false) + if item as ESCPlayer and not (item as ESCPlayer).selectable: + escoria.logger.trace( + self, + "Ignoring double left click on player %s: Player not selectable." + % [item.global_id] + ) + + # Get next object in hover stack and forward event to it + if not hover_stack.is_empty(): + var next_item = hover_stack.pop_top_item() + _on_mouse_left_double_clicked_item(next_item, event) + else: # if no next object, consider this click as background click + hotspot_focused = "" + _on_double_left_click_on_bg(event.position) + return + + # Clicked object can't be actioned and there is no other object behind + # We consider this click as a background click + if not escoria.action_manager.is_object_actionable(item.global_id) \ + and hover_stack.is_empty(): + hotspot_focused = "" + _on_double_left_click_on_bg(event.position) + return + + # Finally, execute the action on the ESCItem + hotspot_focused = item.global_id + escoria.main.current_scene.game.left_double_click_on_item( + item.global_id, + event + ) + +## An Escoria item was clicked with the RMB.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`ESCItem`|The Escoria item clicked.|yes|[br] +## |event|`InputEvent`|The input event from the click.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_right_clicked_item(item: ESCItem, event: InputEvent) -> void: + if input_mode == INPUT_ALL: + if item as ESCPlayer and not (item as ESCPlayer).selectable: + escoria.logger.debug( + self, + "Ignoring right click on player %s: Player not selectable." % [item.global_id] + ) + + if not hover_stack.is_empty(): + var next_item = hover_stack.pop_top_item() + _on_mouse_right_clicked_item(next_item, event) + return + + if not escoria.action_manager.is_object_actionable(item.global_id) \ + and hover_stack.is_empty(): + # Treat this as a background click now + hotspot_focused = "" + _on_right_click_on_bg(event.position) + return + + var actual_item + + # We check if the clicked object is ESCPlayer and not selectable. If so + # we consider we clicked through it. + var object: ESCObject = escoria.object_manager.get_object(item.global_id) + if object.node is ESCPlayer and not (object.node as ESCPlayer).selectable: + actual_item = hover_stack.get_top_item() + else: + actual_item = item + + if actual_item == null: + if event.position: + (escoria.main.current_scene.game as ESCGame).right_click_on_bg(event.position) + else: + escoria.logger.info( + self, + "Clicked item %s with event %s cannot be activated (player not selectable or not interactive).\n" + % [item.global_id, event] + + "No valid item found in the items stack. Action canceled." + ) + else: + escoria.logger.info( + self, + "Item %s right clicked with event %s." % [actual_item.global_id, event] + ) + hotspot_focused = actual_item.global_id + escoria.main.current_scene.game.right_click_on_item( + actual_item.global_id, + event + ) + + +# The mousewheel was turned +# +# #### Parameters +# +# - direction: The direction the wheel was turned. 1 = up, -1 = down +func _on_mousewheel_action(direction: int): + escoria.main.current_scene.game.mousewheel_action(direction) + +## Event when the pause menu was requested.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_pause_menu_requested(): + escoria.main.current_scene.game.pause_game() + + +## Hover Stack implementation. +class HoverStack: + + + ## Emitted when the content of the hover stack has changed[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + signal hover_stack_changed + + ## Emitted when the hover stack was emptied[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + signal hover_stack_emptied + + + ## Array representing the hover stack + var hover_stack: Array = [] + + + ## Add the given item to the stack if not already in it.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |item|`Variant`|the item to add to the hover stack|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func add_item(item): + if item is ESCPlayer and not (item as ESCPlayer).selectable: + return + if not hover_stack.has(item): + hover_stack.push_back(item) + _sort() + hover_stack_changed.emit() + + + ## Add the items contained in given list to the stack if not already in it.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |items|`Array`|the items list (array) to add to the hover stack|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func add_items(items: Array): + for item in items: + if escoria.action_manager.is_object_actionable(item.global_id): + add_item(item) + + + ## Clean the hover stack[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func clean(): + for e in hover_stack: + if e == null or !is_instance_valid(e): + hover_stack.erase(e) + hover_stack_changed.emit() + + + ## Pops the top element of the hover stack and returns it[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func pop_top_item(): + var ret = hover_stack.pop_back() + if is_instance_valid(ret): + hover_stack_changed.emit() + return ret + + + ## The top element of the hover stack[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the top element of the hover stack. (`Variant`) + func get_top_item(): + return hover_stack.back() + + + ## Remove the given item from the stack[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |item|`Variant`|the item to remove from the hover stack|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func erase_item(item): + if hover_stack.has(item): + hover_stack.erase(item) + _sort() + hover_stack_changed.emit() + + + ## Clear the stack of hovered items[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func clear(): + hover_stack = [] + hover_stack_emptied.emit() + + + ## True if the hover stack is empty, else false[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns true if the hover stack is empty, else false. (`bool`) + func is_empty() -> bool: + return hover_stack.is_empty() + + + ## Sort the hover stack by items' z-index.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func _sort(): + hover_stack.sort_custom(Callable(HoverStackSorter, "sort_ascending_z_index")) + + + ## True if the hover stack contains the given item[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |item|`Variant`|the item to search|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns true if the hover stack contains the given item. (`bool`) + func has(item) -> bool: + return hover_stack.has(item) + + + ## The hover stack array[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the hover stack array. (`Array`) + func get_all() -> Array: + return hover_stack + + ## Z Sorter class for hover stack + class HoverStackSorter: + static func sort_ascending_z_index(a, b): + if a.z_index < b.z_index: + return true + return false |
