From 6a9ef5f17737ab632cfe7e75d3db60cc134deb8d Mon Sep 17 00:00:00 2001 From: William Edwards Date: Thu, 26 Dec 2024 19:00:17 -0800 Subject: [PATCH] fix(Search Bar): add keyboard support for search bar - Update search bar to use LineEdit instead of TextEdit - Add ability to move text cursor with OSK - Switch to library state upon keyboard input --- core/global/keyboard_instance.gd | 14 +++++++- core/ui/card_ui/navigation/search_bar_menu.gd | 6 ++-- .../card_ui/navigation/search_bar_menu.tscn | 19 +++++++---- core/ui/common/osk/on_screen_keyboard.gd | 32 +++++++++++++------ core/ui/components/search_bar.gd | 9 ++++-- core/ui/components/search_bar.tscn | 10 ------ 6 files changed, 57 insertions(+), 33 deletions(-) delete mode 100644 core/ui/components/search_bar.tscn diff --git a/core/global/keyboard_instance.gd b/core/global/keyboard_instance.gd index d0f9caa9..42d7981e 100644 --- a/core/global/keyboard_instance.gd +++ b/core/global/keyboard_instance.gd @@ -19,6 +19,14 @@ var context: KeyboardContext # keyboard inputs should go, and how to handle submits. func open(ctx: KeyboardContext) -> void: set_context(ctx) + if context and context.target: + # Show the caret + if context.target is LineEdit: + var line_edit := context.target as LineEdit + line_edit.caret_force_displayed = true + # Some nodes (like LineEdit), will grab focus when text is manipulated. + # Don't allow the node to grab focus + context.target.focus_mode = Control.FOCUS_NONE keyboard_opened.emit() @@ -27,6 +35,11 @@ func close() -> void: # If the target is a control node, return focus back to it when the # keyboard closes if context and context.target: + # Hide the caret + if context.target is LineEdit: + var line_edit := context.target as LineEdit + line_edit.caret_force_displayed = false + context.target.focus_mode = Control.FOCUS_ALL context.target.grab_focus.call_deferred() set_context(null) keyboard_closed.emit() @@ -48,7 +61,6 @@ func set_context(ctx: KeyboardContext) -> void: text_edit.set_caret_line(lines-1) var current_line := text_edit.get_line(lines-1) text_edit.set_caret_column(current_line.length()) - #text_edit.clear() # Update our internal keyboard context if context == ctx: diff --git a/core/ui/card_ui/navigation/search_bar_menu.gd b/core/ui/card_ui/navigation/search_bar_menu.gd index c6712389..3498281f 100644 --- a/core/ui/card_ui/navigation/search_bar_menu.gd +++ b/core/ui/card_ui/navigation/search_bar_menu.gd @@ -7,9 +7,9 @@ var default_size := Vector2(custom_minimum_size.x, custom_minimum_size.y) @export var animate_time := 0.2 -@onready var tabs_container := $%LibraryTabsContainer -@onready var search_bar := $%SearchBar -@onready var search_button := $%SearchButton +@onready var tabs_container := $%LibraryTabsContainer as TabsHeader +@onready var search_bar := $%SearchBar as SearchBar +@onready var search_button := $%SearchButton as CardIconButton # Called when the node enters the scene tree for the first time. diff --git a/core/ui/card_ui/navigation/search_bar_menu.tscn b/core/ui/card_ui/navigation/search_bar_menu.tscn index 433e6f4d..6366a6bf 100644 --- a/core/ui/card_ui/navigation/search_bar_menu.tscn +++ b/core/ui/card_ui/navigation/search_bar_menu.tscn @@ -1,16 +1,17 @@ -[gd_scene load_steps=12 format=3 uid="uid://d4bmkauhrlhq0"] +[gd_scene load_steps=13 format=3 uid="uid://d4bmkauhrlhq0"] [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="Script" path="res://core/systems/state/state.gd" id="3_truj2"] [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"] [ext_resource type="Resource" uid="uid://cjda3nse6s3n1" path="res://core/ui/card_ui/library/library_tabs_state.tres" id="9_dlgkq"] [ext_resource type="Script" path="res://core/ui/components/input_icon.gd" id="9_qnoau"] +[ext_resource type="Script" path="res://core/ui/components/search_bar.gd" id="9_smv7o"] [node name="SearchBarMenu" type="PanelContainer"] z_index = 19 @@ -21,7 +22,7 @@ theme_type_variation = &"SearchBar" script = ExtResource("1_518qs") [node name="StatesWatcher" parent="." instance=ExtResource("2_nb2d7")] -states = Array[Resource("res://core/systems/state/state.gd")]([ExtResource("2_kr4pi"), ExtResource("3_sj5or")]) +states = Array[ExtResource("3_truj2")]([ExtResource("2_kr4pi"), ExtResource("3_sj5or")]) [node name="FadeEffect" parent="StatesWatcher" node_paths=PackedStringArray("target") instance=ExtResource("4_1043g")] target = NodePath("../..") @@ -47,13 +48,17 @@ texture = ExtResource("5_pq07x") layout_mode = 2 theme_override_constants/separation = 1 -[node name="SearchBar" parent="MarginContainer/HBoxContainer/HBoxContainer" groups=["global_search_bar"] instance=ExtResource("2_gqstr")] +[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/HBoxContainer/HBoxContainer"] +layout_mode = 2 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_bottom = 5 + +[node name="SearchBar" type="LineEdit" parent="MarginContainer/HBoxContainer/HBoxContainer/MarginContainer" groups=["global_search_bar", "search_bar"]] unique_name_in_owner = true -custom_minimum_size = Vector2(250, 36) +custom_minimum_size = Vector2(200, 0) layout_mode = 2 -theme_override_colors/background_color = Color(0, 0, 0, 0) -theme_override_font_sizes/font_size = 15 placeholder_text = "What should you play?" +script = ExtResource("9_smv7o") [node name="InputIcon" type="HBoxContainer" parent="MarginContainer/HBoxContainer/HBoxContainer"] layout_mode = 2 diff --git a/core/ui/common/osk/on_screen_keyboard.gd b/core/ui/common/osk/on_screen_keyboard.gd index 80ece694..5a95e1f3 100644 --- a/core/ui/common/osk/on_screen_keyboard.gd +++ b/core/ui/common/osk/on_screen_keyboard.gd @@ -179,7 +179,7 @@ func open() -> void: break # Try to scroll any scroll containers to the target node - var target = instance.context.target + var target := instance.context.target if target: # TODO: Find a better way to scroll to the focused element after grow # effect finishes @@ -211,18 +211,20 @@ func open() -> void: # Closes the OSK func close() -> void: + # Remove the OSK state popup_state_machine.remove_state(osk_state) # If the keyboard is configured to send input to the game, set gamescope accordinly var xwayland := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_OGUI) - var pid := OS.get_process_id() - var ogui_windows := xwayland.get_windows_for_pid(pid) - if not ogui_windows.is_empty(): - var overlay_window_id := ogui_windows[0] - if state_machine.current_state() == in_game_state: - xwayland.set_input_focus(overlay_window_id, 0) - else: - xwayland.set_input_focus(overlay_window_id, 1) + if xwayland: + var pid := OS.get_process_id() + var ogui_windows := xwayland.get_windows_for_pid(pid) + if not ogui_windows.is_empty(): + var overlay_window_id := ogui_windows[0] + if state_machine.current_state() == in_game_state: + xwayland.set_input_focus(overlay_window_id, 0) + else: + xwayland.set_input_focus(overlay_window_id, 1) closed.emit() @@ -393,6 +395,7 @@ func _handle_native(key: KeyboardKeyConfig) -> void: if target is LineEdit: var line_edit := instance.context.target as LineEdit line_edit.insert_text_at_caret(character) + line_edit.text_changed.emit(line_edit.text) # insert_text doesn't fire this signal return logger.warn("Keyboard target is not a supported type. Can't send key input.") @@ -404,6 +407,16 @@ func _handle_native_action(key: KeyboardKeyConfig) -> void: return var target = instance.context.target match key.input.keycode: + KEY_LEFT: + if target != null and target is LineEdit: + var line_edit := target as LineEdit + if line_edit.caret_column > 0: + line_edit.caret_column -= 1 + KEY_RIGHT: + if target != null and target is LineEdit: + var line_edit := target as LineEdit + if line_edit.caret_column < line_edit.text.length(): + line_edit.caret_column += 1 KEY_SHIFT: if _mode_shift > MODE_SHIFT.OFF: set_mode_shift(MODE_SHIFT.OFF) @@ -423,6 +436,7 @@ func _handle_native_action(key: KeyboardKeyConfig) -> void: if target != null and target is LineEdit: var line_edit := target as LineEdit line_edit.delete_char_at_caret() + # Upon delete, the LineEdit grabs focus return KEY_ENTER: instance.context.submitted.emit() diff --git a/core/ui/components/search_bar.gd b/core/ui/components/search_bar.gd index 5b1d0c93..39cce8c9 100644 --- a/core/ui/components/search_bar.gd +++ b/core/ui/components/search_bar.gd @@ -1,10 +1,10 @@ -extends TextEdit +extends LineEdit class_name SearchBar signal search_submitted(text: String) 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 library_state := load("res://assets/state/states/library.tres") as State var keyboard_context := KeyboardContext.new(KeyboardContext.TYPE.GODOT, self) @export var keyboard: KeyboardInstance = preload("res://core/global/keyboard_instance.tres") @@ -16,7 +16,10 @@ func _ready() -> void: text_changed.connect(_on_text_changed) -func _on_text_changed() -> void: +func _on_text_changed(new_text: String) -> void: + if state_machine.current_state() != library_state: + state_machine.push_state(library_state) + grab_focus.call_deferred() search_submitted.emit(text) diff --git a/core/ui/components/search_bar.tscn b/core/ui/components/search_bar.tscn deleted file mode 100644 index dc7e91b0..00000000 --- a/core/ui/components/search_bar.tscn +++ /dev/null @@ -1,10 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://bx0fnuxd8mm51"] - -[ext_resource type="Script" path="res://core/ui/components/search_bar.gd" id="1_1ui87"] - -[node name="SearchBar" type="TextEdit" groups=["search_bar"]] -custom_minimum_size = Vector2(0, 35) -size_flags_horizontal = 3 -size_flags_vertical = 4 -placeholder_text = "Search" -script = ExtResource("1_1ui87")