10var default_size := Vector2i(340, 500)
13func configure(layout, settings = null, ui_flow = null, options:Dictionary = {}) -> void:
14 layout_manager = layout
15 settings_manager = settings
16 ui_flow_manager = ui_flow
17 default_size = _coerce_size(options.get(
"size", default_size), default_size)
21func show_popup(parent:Node, stats_manager, filters:Dictionary = {}, options:Dictionary = {}) -> PopupPanel:
22 var popup = PopupPanel.new()
23 popup.name = str(options.get(
"name",
"AchievementsOverlay"))
24 var popup_size = _coerce_size(options.get(
"size", _popup_size(parent, options)), default_size)
25 popup.size = popup_size
26 popup.min_size = _coerce_size(options.get(
"minimum", Vector2i(280, 220)), Vector2i(280, 220))
27 var margin :=
int(options.get(
"margin", 14))
28 var content_options := options.duplicate(true)
29 content_options[
"popup_owner"] = popup
30 var content = _build_content(stats_manager, filters, content_options)
31 content.set_anchors_preset(Control.PRESET_FULL_RECT)
32 content.offset_left = margin
33 content.offset_top = margin
34 content.offset_right = -margin
35 content.offset_bottom = -margin
36 popup.add_child(content)
38 parent.add_child(popup)
39 if layout_manager != null:
40 layout_manager.apply_theme(popup, str(_settings_value(
"theme",
"dark")))
41 if settings_manager != null
and settings_manager.has_method(
"apply_text_size"):
42 settings_manager.apply_text_size(popup)
43 _fit_content(popup, popup_size, margin)
44 if bool(options.get(
"auto_popup", true))
and popup.is_inside_tree():
45 popup_centered(popup, _parent_viewport_size(parent), {
47 "minimum": popup.min_size,
48 "content_margin": margin
50 call_deferred(
"_focus_popup", popup)
57func render_page(parent:Node, stats_manager, filters:Dictionary = {}, options:Dictionary = {}) -> VBoxContainer:
58 var content = _build_content(stats_manager, filters, options)
59 content.name = str(options.get(
"name",
"AchievementsPage"))
61 parent.add_child(content)
62 if layout_manager != null
and parent != null:
63 layout_manager.apply_theme(parent, str(_settings_value(
"theme",
"dark")))
64 if settings_manager != null
and settings_manager.has_method(
"apply_text_size")
and parent != null:
65 settings_manager.apply_text_size(parent)
66 call_deferred(
"_focus_popup", content)
71func update_popup(popup:PopupPanel, stats_manager, filters:Dictionary = {}, options:Dictionary = {}) -> void:
72 if popup == null
or not is_instance_valid(popup):
74 var margin :=
int(options.get(
"margin", 14))
75 for child
in popup.get_children():
76 popup.remove_child(child)
78 var content_options := options.duplicate(true)
79 content_options[
"popup_owner"] = popup
80 var content = _build_content(stats_manager, filters, content_options)
81 content.set_anchors_preset(Control.PRESET_FULL_RECT)
82 content.offset_left = margin
83 content.offset_top = margin
84 content.offset_right = -margin
85 content.offset_bottom = -margin
86 popup.add_child(content)
87 if layout_manager != null:
88 layout_manager.apply_theme(popup, str(_settings_value(
"theme",
"dark")))
89 if settings_manager != null
and settings_manager.has_method(
"apply_text_size"):
90 settings_manager.apply_text_size(popup)
91 _fit_content(popup, _coerce_size(options.get(
"size", popup.size), popup.size), margin)
92 call_deferred(
"_focus_popup", popup)
97func popup_centered(popup:PopupPanel, viewport_size:Vector2, options:Dictionary = {}) -> void:
98 if popup == null
or not is_instance_valid(popup):
100 var popup_size:Vector2i
101 if layout_manager != null
and layout_manager.has_method(
"fit_popup_to_viewport"):
102 popup_size = layout_manager.fit_popup_to_viewport(popup, viewport_size, options)
104 popup_size = _fallback_fit_popup(popup, viewport_size, options)
105 popup.size = popup_size
106 _fit_content(popup, popup_size,
int(options.get(
"content_margin", options.get(
"margin", 14))))
107 if popup.is_inside_tree():
108 popup.popup_centered(popup_size)
111func _focus_popup(popup:Node) -> void:
112 if is_instance_valid(popup)
and popup.is_inside_tree()
and ui_flow_manager != null
and ui_flow_manager.has_method(
"focus_first"):
113 ui_flow_manager.focus_first(popup)
116func _build_content(stats_manager, filters:Dictionary, options:Dictionary) -> VBoxContainer:
117 var content = VBoxContainer.new()
118 content.name =
"Content"
119 content.size_flags_horizontal = Control.SIZE_EXPAND_FILL
120 content.size_flags_vertical = Control.SIZE_EXPAND_FILL
121 content.add_theme_constant_override(
"separation",
int(options.get(
"separation", 8)))
123 var title = Label.new()
125 title.text = str(options.get(
"title",
"Achievements"))
126 title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
127 title.add_theme_font_size_override(
"font_size",
int(options.get(
"title_size", 20)))
128 content.add_child(title)
130 if options.has(
"filter_options")
and layout_manager != null:
131 var filter_options:Dictionary = options.get(
"filter_options", {})
132 var filter_callback:Callable = options.get(
"filter_callback",
Callable())
133 var filter_bar = layout_manager.create_achievement_filter_bar(filter_options, filters, filter_callback)
134 filter_bar.name =
"Filters"
135 content.add_child(filter_bar)
137 if layout_manager != null
and stats_manager != null:
138 var collection_options:Dictionary = options.get(
"collection_options", {})
139 var collection = layout_manager.create_achievement_collection(stats_manager.get_achievement_collection(filters), collection_options)
140 collection.name =
"Collection"
141 collection.custom_minimum_size = Vector2(0, 0)
142 collection.size_flags_horizontal = Control.SIZE_EXPAND_FILL
143 collection.size_flags_vertical = Control.SIZE_EXPAND_FILL
144 content.add_child(collection)
146 var back = Button.new()
148 back.text = str(options.get(
"back_label",
"Back"))
149 back.custom_minimum_size = Vector2(0,
int(options.get(
"button_height", 44)))
150 back.size_flags_horizontal = Control.SIZE_EXPAND_FILL
151 var back_callback:Callable = options.get(
"back_callback",
Callable())
152 back.pressed.connect(func():
153 if options.has(
"popup_owner")
and options[
"popup_owner"]
is PopupPanel:
154 options[
"popup_owner"].hide()
155 if back_callback.is_valid():
158 content.add_child(back)
162func _popup_size(parent:Node, options:Dictionary) -> Vector2i:
163 var requested = _coerce_size(options.get(
"size", default_size), default_size)
164 var viewport_size := _parent_viewport_size(parent)
165 if viewport_size != Vector2.ZERO:
166 var available_width:int = max(260,
int(viewport_size.x) -
int(options.get(
"viewport_margin_x", 24)))
167 var available_height:int = max(260,
int(viewport_size.y) -
int(options.get(
"viewport_margin_y", 24)))
169 min(requested.x, available_width),
170 min(requested.y, available_height)
175func _parent_viewport_size(parent:Node) -> Vector2:
176 if parent
is Control
and parent.is_inside_tree():
177 return parent.get_viewport_rect().size
178 if parent != null
and parent.is_inside_tree():
179 var viewport := parent.get_viewport()
181 return viewport.get_visible_rect().size
185func _fallback_fit_popup(popup:PopupPanel, viewport_size:Vector2, options:Dictionary) -> Vector2i:
186 var requested = _coerce_size(options.get(
"size", popup.size), default_size)
187 var minimum = _coerce_size(options.get(
"minimum", popup.min_size), Vector2i(260, 180))
188 var margin :=
int(options.get(
"margin", 24))
189 var available = Vector2i(max(1,
int(viewport_size.x) - margin), max(1,
int(viewport_size.y) - margin))
190 var effective_min = Vector2i(min(minimum.x, available.x), min(minimum.y, available.y))
192 clamp(requested.x, effective_min.x, available.x),
193 clamp(requested.y, effective_min.y, available.y)
197func _coerce_size(value, fallback:Vector2i) -> Vector2i:
198 if value
is Vector2i:
201 return Vector2i(
int(value.x),
int(value.y))
202 if value
is Dictionary:
203 return Vector2i(
int(value.get(
"x", fallback.x)),
int(value.get(
"y", fallback.y)))
207func _fit_content(popup:PopupPanel, popup_size:Vector2i, margin:int) -> void:
208 if popup == null
or not is_instance_valid(popup):
210 var content = popup.get_node_or_null(
"Content")
211 if content
is Control:
212 content.set_anchors_preset(Control.PRESET_FULL_RECT)
213 content.offset_left = margin
214 content.offset_top = margin
215 content.offset_right = -margin
216 content.offset_bottom = -margin
217 var collection = popup.get_node_or_null(
"Content/Collection")
218 if collection
is Control:
219 collection.custom_minimum_size = Vector2(0, 0)
220 collection.size_flags_vertical = Control.SIZE_EXPAND_FILL
223func _settings_value(key:String, default_value):
224 if settings_manager != null
and settings_manager.has_method(
"get_settings"):
225 return settings_manager.get_settings().get(key, default_value)