10var default_size := Vector2i(340, 500)
11var global_achievement_path :=
""
14func configure(layout, settings = null, ui_flow = null, options:Dictionary = {}) -> void:
15 layout_manager = layout
16 settings_manager = settings
17 ui_flow_manager = ui_flow
18 default_size = _coerce_size(options.get(
"size", default_size), default_size)
19 global_achievement_path = str(options.get(
"global_achievement_path", global_achievement_path))
23func show_popup(parent:Node, stats_manager, filters:Dictionary = {}, options:Dictionary = {}) -> PopupPanel:
24 var popup = PopupPanel.new()
25 popup.name = str(options.get(
"name",
"AchievementsOverlay"))
26 var popup_size = _coerce_size(options.get(
"size", _popup_size(parent, options)), default_size)
27 popup.size = popup_size
28 popup.min_size = _coerce_size(options.get(
"minimum", Vector2i(280, 220)), Vector2i(280, 220))
29 var margin :=
int(options.get(
"margin", 14))
30 var content_options := options.duplicate(true)
31 content_options[
"popup_owner"] = popup
32 var content = _build_content(stats_manager, filters, content_options)
33 content.set_anchors_preset(Control.PRESET_FULL_RECT)
34 content.offset_left = margin
35 content.offset_top = margin
36 content.offset_right = -margin
37 content.offset_bottom = -margin
38 popup.add_child(content)
40 parent.add_child(popup)
41 if layout_manager != null:
42 layout_manager.apply_theme(popup, str(_settings_value(
"theme",
"dark")))
43 if settings_manager != null
and settings_manager.has_method(
"apply_text_size"):
44 settings_manager.apply_text_size(popup)
45 _fit_content(popup, popup_size, margin)
46 if bool(options.get(
"auto_popup", true))
and popup.is_inside_tree():
47 popup_centered(popup, _parent_viewport_size(parent), {
49 "minimum": popup.min_size,
50 "content_margin": margin
52 call_deferred(
"_focus_popup", popup)
59func render_page(parent:Node, stats_manager, filters:Dictionary = {}, options:Dictionary = {}) -> VBoxContainer:
60 var content = _build_content(stats_manager, filters, options)
61 content.name = str(options.get(
"name",
"AchievementsPage"))
62 _fit_page_content(parent, content, options)
64 parent.add_child(content)
65 if layout_manager != null
and parent != null:
66 layout_manager.apply_theme(parent, str(_settings_value(
"theme",
"dark")))
67 if settings_manager != null
and settings_manager.has_method(
"apply_text_size")
and parent != null:
68 settings_manager.apply_text_size(parent)
69 call_deferred(
"_focus_popup", content)
74func update_popup(popup:PopupPanel, stats_manager, filters:Dictionary = {}, options:Dictionary = {}) -> void:
75 if popup == null
or not is_instance_valid(popup):
77 var margin :=
int(options.get(
"margin", 14))
78 for child
in popup.get_children():
79 popup.remove_child(child)
81 var content_options := options.duplicate(true)
82 content_options[
"popup_owner"] = popup
83 var content = _build_content(stats_manager, filters, content_options)
84 content.set_anchors_preset(Control.PRESET_FULL_RECT)
85 content.offset_left = margin
86 content.offset_top = margin
87 content.offset_right = -margin
88 content.offset_bottom = -margin
89 popup.add_child(content)
90 if layout_manager != null:
91 layout_manager.apply_theme(popup, str(_settings_value(
"theme",
"dark")))
92 if settings_manager != null
and settings_manager.has_method(
"apply_text_size"):
93 settings_manager.apply_text_size(popup)
94 _fit_content(popup, _coerce_size(options.get(
"size", popup.size), popup.size), margin)
95 call_deferred(
"_focus_popup", popup)
100func popup_centered(popup:PopupPanel, viewport_size:Vector2, options:Dictionary = {}) -> void:
101 if popup == null
or not is_instance_valid(popup):
103 var popup_size:Vector2i
104 if layout_manager != null
and layout_manager.has_method(
"popup_centered_standard"):
105 layout_manager.popup_centered_standard(popup, viewport_size, options)
106 popup_size = popup.size
108 popup_size = _fallback_fit_popup(popup, viewport_size, options)
109 popup.size = popup_size
110 _fit_content(popup, popup_size,
int(options.get(
"content_margin", options.get(
"margin", 14))))
111 if (layout_manager == null
or not layout_manager.has_method(
"popup_centered_standard"))
and popup.is_inside_tree():
112 popup.popup_centered(popup_size)
115func _focus_popup(popup:Node) -> void:
116 if is_instance_valid(popup)
and popup.is_inside_tree()
and ui_flow_manager != null
and ui_flow_manager.has_method(
"focus_first"):
117 ui_flow_manager.focus_first(popup)
120func _build_content(stats_manager, filters:Dictionary, options:Dictionary) -> VBoxContainer:
121 _refresh_global_achievement_state(stats_manager)
122 var content = VBoxContainer.new()
123 content.name =
"Content"
124 content.size_flags_horizontal = Control.SIZE_EXPAND_FILL
125 content.size_flags_vertical = Control.SIZE_EXPAND_FILL
126 content.add_theme_constant_override(
"separation",
int(options.get(
"separation", 8)))
128 var title = Label.new()
130 title.text = str(options.get(
"title",
"Achievements"))
131 title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
132 title.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
133 title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
134 title.add_theme_font_size_override(
"font_size",
int(options.get(
"title_size", 20)))
135 content.add_child(title)
137 if options.has(
"filter_options")
and layout_manager != null:
138 var filter_options:Dictionary = options.get(
"filter_options", {})
139 var filter_callback:Callable = options.get(
"filter_callback",
Callable())
140 var filter_bar = layout_manager.create_achievement_filter_bar(filter_options, filters, filter_callback)
141 filter_bar.name =
"Filters"
142 content.add_child(filter_bar)
144 if layout_manager != null
and stats_manager != null:
145 var collection_options:Dictionary = options.get(
"collection_options", {})
146 var collection = layout_manager.create_achievement_collection(stats_manager.get_achievement_collection(filters), collection_options)
147 collection.name =
"Collection"
148 var collection_min_height := float(options.get(
"collection_min_height", collection_options.get(
"min_height", 220.0)))
149 collection.custom_minimum_size = Vector2(0, maxf(120.0, collection_min_height))
150 collection.size_flags_horizontal = Control.SIZE_EXPAND_FILL
151 collection.size_flags_vertical = Control.SIZE_EXPAND_FILL
152 content.add_child(collection)
154 var back = Button.new()
156 back.text = str(options.get(
"back_label",
"Back"))
157 back.custom_minimum_size = Vector2(0,
int(options.get(
"button_height", 44)))
158 back.size_flags_horizontal = Control.SIZE_EXPAND_FILL
159 var back_callback:Callable = options.get(
"back_callback",
Callable())
160 back.pressed.connect(func():
161 if options.has(
"popup_owner")
and options[
"popup_owner"]
is PopupPanel:
162 options[
"popup_owner"].hide()
163 if back_callback.is_valid():
166 content.add_child(back)
170func _refresh_global_achievement_state(stats_manager) -> void:
171 if stats_manager != null
and stats_manager.has_method(
"load_global_achievement_state"):
172 if global_achievement_path !=
"":
173 stats_manager.load_global_achievement_state(global_achievement_path)
175 stats_manager.load_global_achievement_state()
178func _fit_page_content(parent:Node, content:Control, options:Dictionary) -> void:
181 var viewport_size := _parent_viewport_size(parent)
182 var width := float(options.get(
"page_width", 0.0))
183 var height := float(options.get(
"page_height", 0.0))
184 if viewport_size != Vector2.ZERO:
185 var margin_x := float(options.get(
"page_margin_x", 48.0))
186 var margin_y := float(options.get(
"page_margin_y", 220.0))
187 width = maxf(width, viewport_size.x - margin_x)
188 height = maxf(height, viewport_size.y - margin_y)
189 content.custom_minimum_size = Vector2(maxf(280.0, width), maxf(320.0, height))
190 content.size_flags_horizontal = Control.SIZE_EXPAND_FILL
191 content.size_flags_vertical = Control.SIZE_EXPAND_FILL
192 var collection = content.get_node_or_null(
"Collection")
193 if collection
is Control:
194 var reserved_height := float(options.get(
"reserved_height", 156.0))
195 var collection_min := float(options.get(
"collection_min_height", 0.0))
196 if viewport_size != Vector2.ZERO:
197 collection_min = maxf(collection_min, viewport_size.y - float(options.get(
"page_margin_y", 220.0)) - reserved_height)
198 collection.custom_minimum_size.y = maxf(180.0, collection_min)
201func _popup_size(parent:Node, options:Dictionary) -> Vector2i:
202 var requested = _coerce_size(options.get(
"size", default_size), default_size)
203 var viewport_size := _parent_viewport_size(parent)
204 if viewport_size != Vector2.ZERO:
205 var available_width:int = max(260,
int(viewport_size.x) -
int(options.get(
"viewport_margin_x", 24)))
206 var available_height:int = max(260,
int(viewport_size.y) -
int(options.get(
"viewport_margin_y", 24)))
208 min(requested.x, available_width),
209 min(requested.y, available_height)
214func _parent_viewport_size(parent:Node) -> Vector2:
215 if parent
is Control
and parent.is_inside_tree():
216 return parent.get_viewport_rect().size
217 if parent != null
and parent.is_inside_tree():
218 var viewport := parent.get_viewport()
220 return viewport.get_visible_rect().size
224func _fallback_fit_popup(popup:PopupPanel, viewport_size:Vector2, options:Dictionary) -> Vector2i:
225 var requested = _coerce_size(options.get(
"size", popup.size), default_size)
226 var minimum = _coerce_size(options.get(
"minimum", popup.min_size), Vector2i(260, 180))
227 var margin :=
int(options.get(
"margin", 24))
228 var available = Vector2i(max(1,
int(viewport_size.x) - margin), max(1,
int(viewport_size.y) - margin))
229 var effective_min = Vector2i(min(minimum.x, available.x), min(minimum.y, available.y))
231 clamp(requested.x, effective_min.x, available.x),
232 clamp(requested.y, effective_min.y, available.y)
236func _coerce_size(value, fallback:Vector2i) -> Vector2i:
237 if value
is Vector2i:
240 return Vector2i(
int(value.x),
int(value.y))
241 if value
is Dictionary:
242 return Vector2i(
int(value.get(
"x", fallback.x)),
int(value.get(
"y", fallback.y)))
246func _fit_content(popup:PopupPanel, popup_size:Vector2i, margin:int) -> void:
247 if popup == null
or not is_instance_valid(popup):
249 var content = popup.get_node_or_null(
"Content")
250 if content
is Control:
251 content.set_anchors_preset(Control.PRESET_FULL_RECT)
252 content.offset_left = margin
253 content.offset_top = margin
254 content.offset_right = -margin
255 content.offset_bottom = -margin
256 var collection = popup.get_node_or_null(
"Content/Collection")
257 if collection
is Control:
258 collection.custom_minimum_size = Vector2(0, 0)
259 collection.size_flags_vertical = Control.SIZE_EXPAND_FILL
262func _settings_value(key:String, default_value):
263 if settings_manager != null
and settings_manager.has_method(
"get_settings"):
264 return settings_manager.get_settings().get(key, default_value)