gate icon and undiscoverable mode

This commit is contained in:
Nordup 2025-06-29 01:26:39 +07:00
parent fb6cf98f2b
commit 414ad4eeda
18 changed files with 108 additions and 52 deletions

View file

@ -34,7 +34,7 @@ visible = false
[node name="BookmarkSaver" type="Node" parent="."]
script = ExtResource("5_ev0ch")
save_dir = "user://resources"
image_save_dir = "user://images"
icon_save_dir = "user://icons"
bookmarks = ExtResource("6_rupvx")
[node name="Api" type="Node" parent="."]

View file

@ -31,7 +31,7 @@ corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[node name="Bookmark" type="Control" node_paths=PackedStringArray("image", "title", "button", "button_special")]
[node name="Bookmark" type="Control" node_paths=PackedStringArray("icon", "title", "button", "button_special")]
clip_children = 1
custom_minimum_size = Vector2(180, 100)
layout_mode = 3
@ -40,7 +40,7 @@ offset_right = 180.0
offset_bottom = 100.0
script = ExtResource("1_bpkqj")
gate_events = ExtResource("2_7i5yr")
image = NodePath("Mask/Image")
icon = NodePath("Mask/Icon")
title = NodePath("Title")
button = NodePath("Button")
button_special = NodePath("ButtonSpecial")
@ -86,7 +86,7 @@ grow_horizontal = 2
mouse_filter = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_od0ga")
[node name="Image" type="TextureRect" parent="Mask"]
[node name="Icon" type="TextureRect" parent="Mask"]
custom_minimum_size = Vector2(87, 87)
layout_mode = 1
anchors_preset = 15

View file

@ -28,7 +28,7 @@ font = ExtResource("4_tevcf")
font_size = 15
font_color = Color(0.431373, 0.435294, 0.494118, 1)
[node name="Result" type="Button" node_paths=PackedStringArray("url", "title", "description", "image")]
[node name="Result" type="Button" node_paths=PackedStringArray("url", "title", "description", "icon")]
custom_minimum_size = Vector2(850, 100)
offset_bottom = 100.0
mouse_default_cursor_shape = 2
@ -36,12 +36,14 @@ theme_override_styles/focus = ExtResource("1_bqxb5")
theme_override_styles/hover = ExtResource("1_bqxb5")
theme_override_styles/pressed = ExtResource("1_bqxb5")
theme_override_styles/normal = ExtResource("2_agglm")
icon = Object(TextureRect,"_import_path":NodePath(""),"unique_name_in_owner":false,"process_mode":0,"process_priority":0,"process_physics_priority":0,"process_thread_group":0,"physics_interpolation_mode":2,"auto_translate_mode":0,"editor_description":"","visible":true,"modulate":Color(1, 1, 1, 1),"self_modulate":Color(1, 1, 1, 1),"show_behind_parent":false,"top_level":false,"clip_children":0,"light_mask":1,"visibility_layer":1,"z_index":0,"z_as_relative":true,"y_sort_enabled":false,"texture_filter":0,"texture_repeat":0,"material":null,"use_parent_material":false,"clip_contents":false,"custom_minimum_size":Vector2(87, 87),"layout_direction":0,"layout_mode":1,"anchors_preset":15,"anchor_left":0.0,"anchor_top":0.0,"anchor_right":1.0,"anchor_bottom":1.0,"offset_left":0.0,"offset_top":0.0,"offset_right":0.0,"offset_bottom":0.0,"grow_horizontal":2,"grow_vertical":2,"rotation":0.0,"scale":Vector2(1, 1),"pivot_offset":Vector2(0, 0),"size_flags_horizontal":4,"size_flags_vertical":4,"size_flags_stretch_ratio":1.0,"localize_numeral_system":true,"tooltip_text":"","focus_neighbor_left":NodePath(""),"focus_neighbor_top":NodePath(""),"focus_neighbor_right":NodePath(""),"focus_neighbor_bottom":NodePath(""),"focus_next":NodePath(""),"focus_previous":NodePath(""),"focus_mode":0,"mouse_filter":1,"mouse_force_pass_scroll_events":true,"mouse_default_cursor_shape":0,"shortcut_context":null,"theme":null,"theme_type_variation":&"","texture":ExtResource("5_a6pb2"),"expand_mode":1,"stretch_mode":5,"flip_h":false,"flip_v":false,"script":null)
script = ExtResource("1_b6nfm")
gate_events = ExtResource("2_wgd6i")
url = NodePath("VBoxContainer/Url")
title = NodePath("VBoxContainer/Title")
description = NodePath("VBoxContainer/Description")
image = NodePath("Mask/Image")
icon = NodePath("Mask/Icon")
[node name="Mask" type="Panel" parent="."]
clip_children = 1
@ -56,7 +58,7 @@ size_flags_vertical = 4
mouse_filter = 1
theme_override_styles/panel = SubResource("StyleBoxFlat_6os7t")
[node name="Image" type="TextureRect" parent="Mask"]
[node name="Icon" type="TextureRect" parent="Mask"]
custom_minimum_size = Vector2(87, 87)
layout_mode = 1
anchors_preset = 15

View file

@ -9,10 +9,15 @@ func _ready() -> void:
func send_discover_gate(c_url: String, c_gate: ConfigGate) -> void:
if not c_gate.discoverable:
Debug.logclr("Gate is not discoverable", Color.DIM_GRAY)
return
var body = {}
body.url = c_url
body.title = c_gate.title
body.description = c_gate.description
body.icon = c_gate.icon_url
body.image = c_gate.image_url
body.resource_pack = c_gate.resource_pack_url
body.libraries = c_gate.libraries

View file

@ -38,7 +38,7 @@ func featured_gates_request() -> void:
func star_gate(gate_d: Dictionary) -> void:
var image_path = await FileDownloader.download(gate_d["image"])
var gate = Gate.create(gate_d["url"], gate_d["title"], gate_d["description"], image_path, "", "")
var icon_path = await FileDownloader.download(gate_d["icon"])
var gate = Gate.create(gate_d["url"], gate_d["title"], gate_d["description"], icon_path, "", "", "")
bookmarks.star(gate, true)

View file

@ -1,7 +1,7 @@
extends Node
@export_dir var save_dir: String
@export_dir var image_save_dir: String
@export_dir var icon_save_dir: String
@export var bookmarks: Bookmarks
@onready var path = save_dir + "/" + bookmarks.resource_path.get_file()
@ -11,7 +11,7 @@ func _ready() -> void:
load_bookmarks()
bookmarks.ready()
bookmarks.save_image.connect(save_image)
bookmarks.save_icon.connect(save_icon)
bookmarks.on_star.connect(func(_gate, _featured): save_bookmarks())
bookmarks.on_unstar.connect(func(_gate): save_bookmarks())
bookmarks.on_update.connect(func(_gate): save_bookmarks())
@ -32,29 +32,29 @@ func save_bookmarks() -> void:
ResourceSaver.save(bookmarks, path)
func save_image(gate: Gate) -> void:
if not FileAccess.file_exists(gate.image): return
if not DirAccess.dir_exists_absolute(image_save_dir):
DirAccess.make_dir_recursive_absolute(image_save_dir)
func save_icon(gate: Gate) -> void:
if not FileAccess.file_exists(gate.icon): return
if not DirAccess.dir_exists_absolute(icon_save_dir):
DirAccess.make_dir_recursive_absolute(icon_save_dir)
var new_path = image_save_dir + "/" + gate.image.get_file()
if new_path == gate.image: return
DirAccess.copy_absolute(gate.image, new_path)
gate.image = new_path
var new_path = icon_save_dir + "/" + gate.icon.get_file()
if new_path == gate.icon: return
DirAccess.copy_absolute(gate.icon, new_path)
gate.icon = new_path
func clear_image_folder() -> void:
if not DirAccess.dir_exists_absolute(image_save_dir): return
func clear_icon_folder() -> void:
if not DirAccess.dir_exists_absolute(icon_save_dir): return
var used_images: Array[String] = []
var used_icons: Array[String] = []
for gate in bookmarks.gates.values():
used_images.append(gate.image.get_file())
used_icons.append(gate.icon.get_file())
for file in DirAccess.get_files_at(image_save_dir):
if not file in used_images:
DirAccess.remove_absolute(image_save_dir + "/" + file)
for file in DirAccess.get_files_at(icon_save_dir):
if not file in used_icons:
DirAccess.remove_absolute(icon_save_dir + "/" + file)
func _exit_tree() -> void:
save_bookmarks()
clear_image_folder()
clear_icon_folder()

View file

@ -21,8 +21,8 @@ func get_string(section: String, key: String) -> String:
return value
func get_value(section: String, key: String) -> Variant:
var value: Variant
func get_value(section: String, key: String, default: Variant = null) -> Variant:
var value: Variant = default
if config.has_section_key(section, key):
value = config.get_value(section, key)
# Debug.logr(key + "=" + str(value))

View file

@ -3,19 +3,30 @@ class_name ConfigGate
const SECTION = "gate"
const KEY_TITLE = "title"
const KEY_DESCRIPTION = "description"
const KEY_ICON = "icon"
const KEY_IMAGE = "image"
const KEY_RESOURCE_PACK = "resource_pack"
const KEY_DISCOVERABLE = "discoverable"
var title: String
var description: String
var icon_url: String
var image_url: String
var resource_pack_url: String
var discoverable: bool
var libraries: PackedStringArray
func _init(path: String, base_url: String) -> void:
super._init(path)
title = get_string(SECTION, "title")
description = get_string(SECTION, "description")
image_url = Url.join(base_url, get_string(SECTION, "image"))
resource_pack_url = Url.join(base_url, get_string(SECTION, "resource_pack"))
title = get_string(SECTION, KEY_TITLE)
description = get_string(SECTION, KEY_DESCRIPTION)
icon_url = Url.join(base_url, get_string(SECTION, KEY_ICON))
image_url = Url.join(base_url, get_string(SECTION, KEY_IMAGE))
resource_pack_url = Url.join(base_url, get_string(SECTION, KEY_RESOURCE_PACK))
discoverable = get_value(SECTION, KEY_DISCOVERABLE, true)
libraries = get_libraries(base_url)

View file

@ -11,6 +11,10 @@ var load_resources_done: bool
var shared_libs_count: int = -1
var shared_libs_done: int
# Show progress when resource pack is started loading
var resource_pack_url: String
var resource_pack_started_loading: bool
func _ready() -> void:
FileDownloader.progress.connect(on_progress)
@ -26,15 +30,22 @@ func load_gate(config_url: String) -> void:
if c_gate.load_result != OK: return error(GateEvents.GateError.INVALID_CONFIG)
gate_events.gate_config_loaded_emit(config_url, c_gate)
gate = Gate.create(config_url, c_gate.title, c_gate.description, "", "", "")
gate = Gate.create(config_url, c_gate.title, c_gate.description, "", "", "", "")
gate_events.gate_info_loaded_emit(gate)
# Download all in parallel
load_icon(c_gate)
load_image(c_gate)
load_resources(c_gate)
load_shared_libs(c_gate, config_url)
func load_icon(c_gate: ConfigGate) -> void:
gate.icon = await FileDownloader.download(c_gate.icon_url)
gate_events.gate_icon_loaded_emit(gate)
# Finish without icon
func load_image(c_gate: ConfigGate) -> void:
gate.image = await FileDownloader.download(c_gate.image_url)
gate_events.gate_image_loaded_emit(gate)
@ -42,6 +53,7 @@ func load_image(c_gate: ConfigGate) -> void:
func load_resources(c_gate: ConfigGate) -> void:
resource_pack_url = c_gate.resource_pack_url
gate.resource_pack = await FileDownloader.download(c_gate.resource_pack_url)
if gate.resource_pack.is_empty(): return error(GateEvents.GateError.MISSING_PACK)
@ -81,6 +93,12 @@ func error(code: GateEvents.GateError) -> void:
func on_progress(url: String, body_size: int, downloaded_bytes: int) -> void:
if url == resource_pack_url and not resource_pack_started_loading and body_size > 0:
resource_pack_started_loading = true
if not resource_pack_started_loading:
return
gate_events.download_progress_emit(url, body_size, downloaded_bytes)

View file

@ -5,7 +5,7 @@ signal on_ready()
signal on_star(gate: Gate, featured: bool)
signal on_unstar(gate: Gate)
signal on_update(gate: Gate)
signal save_image(gate: Gate)
signal save_icon(gate: Gate)
@export var starred_gates: Array[Gate]
@ -34,7 +34,7 @@ func update(gate: Gate) -> void:
starred_gates.erase(replace)
starred_gates.append(gate)
save_image.emit(gate)
save_icon.emit(gate)
on_update.emit(gate)
@ -44,7 +44,7 @@ func star(gate: Gate, featured: bool = false) -> void:
gates[gate.url] = gate
starred_gates.append(gate)
save_image.emit(gate)
save_icon.emit(gate)
on_star.emit(gate, featured)

View file

@ -5,20 +5,27 @@ class_name Gate
set(value): url = Url.fix_gate_url(value)
@export var title: String
@export var description: String
@export_file("*.png", "*.jpg") var icon: String:
get: return icon if not icon.is_empty() else image
set(value): icon = value
@export_file("*.png", "*.jpg") var image: String
var resource_pack: String
# local path where libs downloaded
var shared_libs_dir: String
var shared_libs_dir: String # local path where libs downloaded
static func create(_url: String, _title: String, _description: String,
static func create(_url: String, _title: String, _description: String, _icon: String,
_image: String, _resource_pack: String, _shared_libs_dir: String) -> Gate:
var gate = Gate.new()
gate.url = _url
gate.title = _title
gate.description = _description
gate.icon = _icon
gate.image = _image
gate.resource_pack = _resource_pack
gate.shared_libs_dir = _shared_libs_dir

View file

@ -5,6 +5,7 @@ signal search(query: String)
signal open_gate(url: String)
signal gate_config_loaded(url: String, config: ConfigGate)
signal gate_info_loaded(gate: Gate)
signal gate_icon_loaded(gate: Gate) # might be empty icon
signal gate_image_loaded(gate: Gate) # might be empty image
signal gate_loaded(gate: Gate)
signal gate_entered
@ -51,6 +52,10 @@ func gate_info_loaded_emit(gate: Gate) -> void:
gate_info_loaded.emit(gate)
func gate_icon_loaded_emit(gate: Gate) -> void:
gate_icon_loaded.emit(gate)
func gate_image_loaded_emit(gate: Gate) -> void:
gate_image_loaded.emit(gate)

View file

@ -8,8 +8,8 @@ extends Control
func _ready() -> void:
visible = false
if key.is_empty() or button == null: Debug.logerr("hint has empty vars")
var first = DataSaver.get_value(section, key)
if first == null or not first: show_hint()
var is_shown = DataSaver.get_value(section, key, false)
if not is_shown: show_hint()
func _notification(what: int) -> void:

View file

@ -2,7 +2,7 @@ extends Control
class_name BookmarkUI
@export var gate_events: GateEvents
@export var image: TextureRect
@export var icon: TextureRect
@export var title: Label
@export var button: Button
@export var button_special: Button
@ -23,7 +23,7 @@ func fill(gate: Gate, special: bool = false) -> void:
url = gate.url
title.text = "Unnamed" if gate.title.is_empty() else gate.title
image.texture = FileTools.load_external_tex(gate.image)
icon.texture = FileTools.load_external_tex(gate.icon)
func on_pressed() -> void:

View file

@ -29,8 +29,7 @@ func scale_content() -> void:
func set_initial_screen() -> void:
var last_screen = DataSaver.get_value("settings", "last_screen")
if last_screen == null: last_screen = 0
var last_screen = DataSaver.get_value("settings", "last_screen", 0)
DisplayServer.window_set_current_screen(last_screen)
Debug.logclr("Initial screen: %d" % [last_screen], Debug.SILENT_CLR)

View file

@ -18,6 +18,7 @@ func _ready() -> void:
gate_events.search.connect(func(_query): hide_buttons())
gate_events.exit_gate.connect(hide_buttons)
gate_events.gate_info_loaded.connect(update_info)
gate_events.gate_icon_loaded.connect(update_info)
gate_events.gate_image_loaded.connect(update_info)

View file

@ -1,23 +1,30 @@
extends Control
class_name SearchResult
const KEY_URL = "url"
const KEY_TITLE = "title"
const KEY_DESCRIPTION = "description"
const KEY_ICON = "icon"
const KEY_IMAGE = "image"
@export var gate_events: GateEvents
@export var url: Label
@export var title: Label
@export var description: RichTextLabel
@export var image: TextureRect
@export var icon: TextureRect
func fill(gate: Dictionary) -> void:
if gate == null: return
url.text = gate["url"]
title.text = "Unnamed" if gate["title"].is_empty() else gate["title"]
description.text = gate["description"]
url.text = gate[KEY_URL]
title.text = "Unnamed" if gate[KEY_TITLE].is_empty() else gate[KEY_TITLE]
description.text = gate[KEY_DESCRIPTION]
var image_path = await FileDownloader.download(gate["image"])
image.texture = FileTools.load_external_tex(image_path)
var icon_url = gate[KEY_ICON] if not gate[KEY_ICON].is_empty() else gate[KEY_IMAGE]
var icon_path = await FileDownloader.download(icon_url)
icon.texture = FileTools.load_external_tex(icon_path)
func _on_button_pressed() -> void:

View file

@ -115,6 +115,7 @@ func on_gate_entered() -> void:
func on_gate_error(code: GateEvents.GateError) -> void:
gate_events.download_progress.disconnect(show_progress)
match code:
GateEvents.GateError.NOT_FOUND:
set_progress("Gate not found", ProgressStatus.ERROR)