Остання активність 1786002716

ZZ's Avatar ZZ ревизій цього gist 1786002716. До ревизії

1 file changed, 143 insertions, 146 deletions

clipboard_ui.gd

@@ -3,157 +3,154 @@ class_name ClipboardUIHelper
3 3
4 4 # Builds the Network tab content into a provided container page
5 5 static func build_network_ui(page_container: Node, engine: Node) -> void:
6 - ## 1. CLEANUP PREVIOUS RENDERS: Safely look for and wipe old structural layout node
7 - var old_wrapper = page_container.get_node_or_null("NetworkMarginWrapper")
8 - if old_wrapper:
9 - old_wrapper.queue_free()
10 - page_container.remove_child(old_wrapper)
11 -
12 - ## 2. MAIN PAGE LAYOUT: The root wrapper for this specific tab page
13 - var margin_wrapper = MarginContainer.new()
14 - margin_wrapper.name = "NetworkMarginWrapper"
15 - margin_wrapper.add_theme_constant_override("margin_left", 50)
16 - margin_wrapper.add_theme_constant_override("margin_top", 40)
17 - page_container.add_child(margin_wrapper)
18 -
19 - var main_stack = VBoxContainer.new()
20 - main_stack.add_theme_constant_override("separation", 15)
21 - margin_wrapper.add_child(main_stack)
22 -
23 - # ----------------------------------------------------
24 - # ROW 1: Connection Actions (Start Server / Connect Client)
25 - # ----------------------------------------------------
26 - var row_actions = HBoxContainer.new()
27 - row_actions.add_theme_constant_override("separation", 15)
28 - main_stack.add_child(row_actions)
29 -
30 - var btn_server = Button.new()
31 - btn_server.text = "Start Server"
32 - btn_server.pressed.connect(func(): _on_start_server_pressed(engine))
33 - row_actions.add_child(btn_server)
34 -
35 - var btn_client = Button.new()
36 - btn_client.text = "Connect Client"
37 - btn_client.pressed.connect(func(): _on_start_client_pressed(engine))
38 - row_actions.add_child(btn_client)
39 -
40 - # ----------------------------------------------------
41 - # ROW 2: Address Info (IP Input & Port Input)
42 - # ----------------------------------------------------
43 - var row_address = HBoxContainer.new()
44 - row_address.add_theme_constant_override("separation", 15)
45 - main_stack.add_child(row_address)
46 -
47 - var ip_input = LineEdit.new()
48 - ip_input.text = engine.server_ip
49 - ip_input.placeholder_text = "Target Server IP"
50 - ip_input.custom_minimum_size = Vector2(180, 35)
51 - row_address.add_child(ip_input)
52 -
53 - var port_input = LineEdit.new()
54 - port_input.text = str(engine.port_number)
55 - port_input.placeholder_text = "Port"
56 - port_input.custom_minimum_size = Vector2(100, 35)
57 - row_address.add_child(port_input)
58 -
59 - # ----------------------------------------------------
60 - # ROW 3: Security & Utility (Passphrase, Generate, Copy)
61 - # ----------------------------------------------------
62 - var row_security = HBoxContainer.new()
63 - row_security.add_theme_constant_override("separation", 15)
64 - main_stack.add_child(row_security)
65 -
66 - var key_input = LineEdit.new()
67 - key_input.text = engine.secret_key
68 - key_input.secret = true
69 - key_input.placeholder_text = "Shared Secret Key Passphrase"
70 - key_input.custom_minimum_size = Vector2(300, 35)
71 - row_security.add_child(key_input)
72 -
73 - var btn_generate = Button.new()
74 - btn_generate.text = "New Key"
75 - btn_generate.custom_minimum_size = Vector2(0, 35)
76 - btn_generate.pressed.connect(func(): _on_generate_key(engine, key_input))
77 - row_security.add_child(btn_generate)
78 -
79 - var btn_copy = Button.new()
80 - btn_copy.text = "Copy Key"
81 - btn_copy.custom_minimum_size = Vector2(0, 35)
82 - btn_copy.pressed.connect(func(): engine.copy_key_to_clipboard())
83 - row_security.add_child(btn_copy)
84 -
85 - # ----------------------------------------------------
86 - # ROW 4: Manual Sync Input
87 - # ----------------------------------------------------
88 - var row_input = HBoxContainer.new()
89 - row_address.add_theme_constant_override("separation", 15)
90 - main_stack.add_child(row_input)
91 -
92 - var line_edit = LineEdit.new()
93 - line_edit.custom_minimum_size = Vector2(400, 40)
94 - line_edit.placeholder_text = "Type and press Enter to securely sync..."
95 - # Wire the UI input directly to the engine's RPC method using encryption
96 - line_edit.text_submitted.connect(func(new_text): engine.receive_secure_clipboard(engine.encrypt_string(new_text)))
97 - row_input.add_child(line_edit)
98 -
99 - # ----------------------------------------------------
100 - # ROW 5: Status Panel (Replaces draw_rect entirely)
101 - # ----------------------------------------------------
102 - var row_status = HBoxContainer.new()
103 - row_status.add_theme_constant_override("separation", 15)
104 - main_stack.add_child(row_status)
105 -
106 - var status_card = PanelContainer.new()
107 - status_card.custom_minimum_size = Vector2(400, 80)
108 - status_card.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
109 -
110 - var card_padding = MarginContainer.new()
111 - card_padding.add_theme_constant_override("margin_left", 30)
112 - card_padding.add_theme_constant_override("margin_right", 30)
113 - card_padding.add_theme_constant_override("margin_top", 15)
114 - card_padding.add_theme_constant_override("margin_bottom", 15)
115 -
116 - status_card.add_child(card_padding)
117 -
118 - var status_label = RichTextLabel.new()
119 - status_label.text = "Loading..."
120 - status_label.selection_enabled = true
121 - status_label.add_theme_font_size_override("font_size", 18)
122 - status_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
123 - card_padding.add_child(status_label)
124 -
125 - # StyleBoxFlat to mimic your old draw_rect background
126 - var custom_style = StyleBoxFlat.new()
127 - custom_style.bg_color = Color(0.13, 0.13, 0.13, 1.0)
128 - custom_style.set_corner_radius_all(4)
129 - status_card.add_theme_stylebox_override("panel", custom_style)
130 -
131 - main_stack.add_child(status_card)
132 -
133 - # Listen for engine status updates and wire them to the label
134 - engine.status_changed.connect(func(new_status_text): _update_status_label(status_label, new_status_text))
135 -
136 - # -- Static Helper Functions for UI Wiring --
6 + ## FIX: Skip building if UI already exists.
7 + ## This preserves state, prevents signal duplication, and stops the "Loading..." reset.
8 + if page_container.has_node("NetworkMarginWrapper"):
9 + return
10 +
11 + # ----------------------------------------------------
12 + # MAIN PAGE LAYOUT: The root wrapper for this specific tab page
13 + # ----------------------------------------------------
14 + var margin_wrapper = MarginContainer.new()
15 + margin_wrapper.name = "NetworkMarginWrapper"
16 + margin_wrapper.add_theme_constant_override("margin_left", 50)
17 + margin_wrapper.add_theme_constant_override("margin_top", 40)
18 + page_container.add_child(margin_wrapper)
19 +
20 + var main_stack = VBoxContainer.new()
21 + main_stack.add_theme_constant_override("separation", 15)
22 + margin_wrapper.add_child(main_stack)
23 +
24 + # ----------------------------------------------------
25 + # ROW 1: Connection Actions
26 + # ----------------------------------------------------
27 + var row_actions = HBoxContainer.new()
28 + row_actions.add_theme_constant_override("separation", 15)
29 + main_stack.add_child(row_actions)
30 +
31 + var btn_server = Button.new()
32 + btn_server.text = "Start Server"
33 + btn_server.pressed.connect(func(): _on_start_server_pressed(engine))
34 + row_actions.add_child(btn_server)
35 +
36 + var btn_client = Button.new()
37 + btn_client.text = "Connect Client"
38 + btn_client.pressed.connect(func(): _on_start_client_pressed(engine))
39 + row_actions.add_child(btn_client)
40 +
41 + # ----------------------------------------------------
42 + # ROW 2: Address Info
43 + # ----------------------------------------------------
44 + var row_address = HBoxContainer.new()
45 + row_address.add_theme_constant_override("separation", 15)
46 + main_stack.add_child(row_address)
47 +
48 + var ip_input = LineEdit.new()
49 + ip_input.text = str(engine.server_ip)
50 + ip_input.placeholder_text = "Target Server IP"
51 + ip_input.custom_minimum_size = Vector2(180, 35)
52 + row_address.add_child(ip_input)
53 +
54 + var port_input = LineEdit.new()
55 + port_input.text = str(engine.port_number)
56 + port_input.placeholder_text = "Port"
57 + port_input.custom_minimum_size = Vector2(100, 35)
58 + row_address.add_child(port_input)
59 +
60 + # ----------------------------------------------------
61 + # ROW 3: Security & Utility
62 + # ----------------------------------------------------
63 + var row_security = HBoxContainer.new()
64 + row_security.add_theme_constant_override("separation", 15)
65 + main_stack.add_child(row_security)
66 +
67 + var key_input = LineEdit.new()
68 + key_input.text = engine.secret_key
69 + key_input.secret = true
70 + key_input.placeholder_text = "Shared Secret Key Passphrase"
71 + key_input.custom_minimum_size = Vector2(300, 35)
72 + row_security.add_child(key_input)
73 +
74 + var btn_generate = Button.new()
75 + btn_generate.text = "New Key"
76 + btn_generate.custom_minimum_size = Vector2(0, 35)
77 + btn_generate.pressed.connect(func(): _on_generate_key(engine, key_input))
78 + row_security.add_child(btn_generate)
79 +
80 + var btn_copy = Button.new()
81 + btn_copy.text = "Copy Key"
82 + btn_copy.custom_minimum_size = Vector2(0, 35)
83 + btn_copy.pressed.connect(func(): engine.copy_key_to_clipboard())
84 + row_security.add_child(btn_copy)
85 +
86 + # ----------------------------------------------------
87 + # ROW 4: Manual Sync Input
88 + # ----------------------------------------------------
89 + var row_input = HBoxContainer.new()
90 + row_address.add_theme_constant_override("separation", 15)
91 + main_stack.add_child(row_input)
92 +
93 + var line_edit = LineEdit.new()
94 + line_edit.custom_minimum_size = Vector2(400, 40)
95 + line_edit.placeholder_text = "Type and press Enter to securely sync..."
96 + line_edit.text_submitted.connect(func(new_text): engine.receive_secure_clipboard(engine.encrypt_string(new_text)))
97 + row_input.add_child(line_edit)
98 +
99 + # ----------------------------------------------------
100 + # ROW 5: Status Panel
101 + # ----------------------------------------------------
102 + var row_status = HBoxContainer.new()
103 + row_status.add_theme_constant_override("separation", 15)
104 + main_stack.add_child(row_status)
105 +
106 + var status_card = PanelContainer.new()
107 + status_card.custom_minimum_size = Vector2(400, 80)
108 + status_card.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
109 +
110 + var card_padding = MarginContainer.new()
111 + card_padding.add_theme_constant_override("margin_left", 30)
112 + card_padding.add_theme_constant_override("margin_right", 30)
113 + card_padding.add_theme_constant_override("margin_top", 15)
114 + card_padding.add_theme_constant_override("margin_bottom", 15)
115 +
116 + status_card.add_child(card_padding)
117 +
118 + var status_label = RichTextLabel.new()
119 + status_label.text = "Initializing..." # Temporary until first signal fires
120 + status_label.selection_enabled = true
121 + status_label.add_theme_font_size_override("font_size", 18)
122 + status_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
123 + card_padding.add_child(status_label)
124 +
125 + var custom_style = StyleBoxFlat.new()
126 + custom_style.bg_color = Color(0.13, 0.13, 0.13, 1.0)
127 + custom_style.set_corner_radius_all(4)
128 + status_card.add_theme_stylebox_override("panel", custom_style)
129 +
130 + main_stack.add_child(status_card)
131 +
132 + # 🔗 Connect signal exactly ONCE when UI is built
133 + engine.status_changed.connect(func(new_status_text): _update_status_label(status_label, new_status_text))
134 +
135 + # -- Static Helper Functions --
137 136
138 137 static func _on_start_server_pressed(engine: Node) -> void:
139 - # Reads current properties. In a real app you might pass inputs as arguments instead.
140 - engine.start_server(int(engine.port_number))
138 + engine.start_server(int(engine.port_number))
141 139
142 140 static func _on_start_client_pressed(engine: Node) -> void:
143 - engine.start_client(str(engine.server_ip), int(engine.port_number))
141 + engine.start_client(str(engine.server_ip), int(engine.port_number))
144 142
145 143 static func _on_generate_key(engine: Node, key_input: LineEdit) -> void:
146 - var new_key = engine.generate_random_key(16)
147 - engine.secret_key = new_key
148 - key_input.text = new_key
144 + var new_key = engine.generate_random_key(16)
145 + engine.secret_key = new_key
146 + key_input.text = new_key
149 147
150 148 static func _update_status_label(label: RichTextLabel, text: String) -> void:
151 - label.text = text
152 - # Calculate dynamic color state based on connection status
153 - var target_color: Color = Color.GREEN
154 - if "Disconnected" in text or "Offline" in text:
155 - target_color = Color.YELLOW
156 - elif "Reconnecting" in text:
157 - target_color = Color.ORANGE
158 -
159 - label.add_theme_color_override("default_color", target_color)
149 + label.text = text
150 + var target_color: Color = Color.GREEN
151 + if "Disconnected" in text or "Offline" in text:
152 + target_color = Color.YELLOW
153 + elif "Reconnecting" in text:
154 + target_color = Color.ORANGE
155 +
156 + label.add_theme_color_override("default_color", target_color)

ZZ's Avatar ZZ ревизій цього gist 1786002705. До ревизії

1 file changed, 159 insertions

clipboard_ui.gd(файл створено)

@@ -0,0 +1,159 @@
1 + extends Node
2 + class_name ClipboardUIHelper
3 +
4 + # Builds the Network tab content into a provided container page
5 + static func build_network_ui(page_container: Node, engine: Node) -> void:
6 + ## 1. CLEANUP PREVIOUS RENDERS: Safely look for and wipe old structural layout node
7 + var old_wrapper = page_container.get_node_or_null("NetworkMarginWrapper")
8 + if old_wrapper:
9 + old_wrapper.queue_free()
10 + page_container.remove_child(old_wrapper)
11 +
12 + ## 2. MAIN PAGE LAYOUT: The root wrapper for this specific tab page
13 + var margin_wrapper = MarginContainer.new()
14 + margin_wrapper.name = "NetworkMarginWrapper"
15 + margin_wrapper.add_theme_constant_override("margin_left", 50)
16 + margin_wrapper.add_theme_constant_override("margin_top", 40)
17 + page_container.add_child(margin_wrapper)
18 +
19 + var main_stack = VBoxContainer.new()
20 + main_stack.add_theme_constant_override("separation", 15)
21 + margin_wrapper.add_child(main_stack)
22 +
23 + # ----------------------------------------------------
24 + # ROW 1: Connection Actions (Start Server / Connect Client)
25 + # ----------------------------------------------------
26 + var row_actions = HBoxContainer.new()
27 + row_actions.add_theme_constant_override("separation", 15)
28 + main_stack.add_child(row_actions)
29 +
30 + var btn_server = Button.new()
31 + btn_server.text = "Start Server"
32 + btn_server.pressed.connect(func(): _on_start_server_pressed(engine))
33 + row_actions.add_child(btn_server)
34 +
35 + var btn_client = Button.new()
36 + btn_client.text = "Connect Client"
37 + btn_client.pressed.connect(func(): _on_start_client_pressed(engine))
38 + row_actions.add_child(btn_client)
39 +
40 + # ----------------------------------------------------
41 + # ROW 2: Address Info (IP Input & Port Input)
42 + # ----------------------------------------------------
43 + var row_address = HBoxContainer.new()
44 + row_address.add_theme_constant_override("separation", 15)
45 + main_stack.add_child(row_address)
46 +
47 + var ip_input = LineEdit.new()
48 + ip_input.text = engine.server_ip
49 + ip_input.placeholder_text = "Target Server IP"
50 + ip_input.custom_minimum_size = Vector2(180, 35)
51 + row_address.add_child(ip_input)
52 +
53 + var port_input = LineEdit.new()
54 + port_input.text = str(engine.port_number)
55 + port_input.placeholder_text = "Port"
56 + port_input.custom_minimum_size = Vector2(100, 35)
57 + row_address.add_child(port_input)
58 +
59 + # ----------------------------------------------------
60 + # ROW 3: Security & Utility (Passphrase, Generate, Copy)
61 + # ----------------------------------------------------
62 + var row_security = HBoxContainer.new()
63 + row_security.add_theme_constant_override("separation", 15)
64 + main_stack.add_child(row_security)
65 +
66 + var key_input = LineEdit.new()
67 + key_input.text = engine.secret_key
68 + key_input.secret = true
69 + key_input.placeholder_text = "Shared Secret Key Passphrase"
70 + key_input.custom_minimum_size = Vector2(300, 35)
71 + row_security.add_child(key_input)
72 +
73 + var btn_generate = Button.new()
74 + btn_generate.text = "New Key"
75 + btn_generate.custom_minimum_size = Vector2(0, 35)
76 + btn_generate.pressed.connect(func(): _on_generate_key(engine, key_input))
77 + row_security.add_child(btn_generate)
78 +
79 + var btn_copy = Button.new()
80 + btn_copy.text = "Copy Key"
81 + btn_copy.custom_minimum_size = Vector2(0, 35)
82 + btn_copy.pressed.connect(func(): engine.copy_key_to_clipboard())
83 + row_security.add_child(btn_copy)
84 +
85 + # ----------------------------------------------------
86 + # ROW 4: Manual Sync Input
87 + # ----------------------------------------------------
88 + var row_input = HBoxContainer.new()
89 + row_address.add_theme_constant_override("separation", 15)
90 + main_stack.add_child(row_input)
91 +
92 + var line_edit = LineEdit.new()
93 + line_edit.custom_minimum_size = Vector2(400, 40)
94 + line_edit.placeholder_text = "Type and press Enter to securely sync..."
95 + # Wire the UI input directly to the engine's RPC method using encryption
96 + line_edit.text_submitted.connect(func(new_text): engine.receive_secure_clipboard(engine.encrypt_string(new_text)))
97 + row_input.add_child(line_edit)
98 +
99 + # ----------------------------------------------------
100 + # ROW 5: Status Panel (Replaces draw_rect entirely)
101 + # ----------------------------------------------------
102 + var row_status = HBoxContainer.new()
103 + row_status.add_theme_constant_override("separation", 15)
104 + main_stack.add_child(row_status)
105 +
106 + var status_card = PanelContainer.new()
107 + status_card.custom_minimum_size = Vector2(400, 80)
108 + status_card.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
109 +
110 + var card_padding = MarginContainer.new()
111 + card_padding.add_theme_constant_override("margin_left", 30)
112 + card_padding.add_theme_constant_override("margin_right", 30)
113 + card_padding.add_theme_constant_override("margin_top", 15)
114 + card_padding.add_theme_constant_override("margin_bottom", 15)
115 +
116 + status_card.add_child(card_padding)
117 +
118 + var status_label = RichTextLabel.new()
119 + status_label.text = "Loading..."
120 + status_label.selection_enabled = true
121 + status_label.add_theme_font_size_override("font_size", 18)
122 + status_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
123 + card_padding.add_child(status_label)
124 +
125 + # StyleBoxFlat to mimic your old draw_rect background
126 + var custom_style = StyleBoxFlat.new()
127 + custom_style.bg_color = Color(0.13, 0.13, 0.13, 1.0)
128 + custom_style.set_corner_radius_all(4)
129 + status_card.add_theme_stylebox_override("panel", custom_style)
130 +
131 + main_stack.add_child(status_card)
132 +
133 + # Listen for engine status updates and wire them to the label
134 + engine.status_changed.connect(func(new_status_text): _update_status_label(status_label, new_status_text))
135 +
136 + # -- Static Helper Functions for UI Wiring --
137 +
138 + static func _on_start_server_pressed(engine: Node) -> void:
139 + # Reads current properties. In a real app you might pass inputs as arguments instead.
140 + engine.start_server(int(engine.port_number))
141 +
142 + static func _on_start_client_pressed(engine: Node) -> void:
143 + engine.start_client(str(engine.server_ip), int(engine.port_number))
144 +
145 + static func _on_generate_key(engine: Node, key_input: LineEdit) -> void:
146 + var new_key = engine.generate_random_key(16)
147 + engine.secret_key = new_key
148 + key_input.text = new_key
149 +
150 + static func _update_status_label(label: RichTextLabel, text: String) -> void:
151 + label.text = text
152 + # Calculate dynamic color state based on connection status
153 + var target_color: Color = Color.GREEN
154 + if "Disconnected" in text or "Offline" in text:
155 + target_color = Color.YELLOW
156 + elif "Reconnecting" in text:
157 + target_color = Color.ORANGE
158 +
159 + label.add_theme_color_override("default_color", target_color)
Новіше Пізніше