From 9f150baeccc6a67ed2fe00885283791e52341b01 Mon Sep 17 00:00:00 2001 From: hz Date: Thu, 30 Jul 2026 14:50:12 -0500 Subject: [PATCH] Simplify exam simulations and strip the slot machine down to just the machine Simulations no longer color-code correct/incorrect choices, retry a failed per-question autosave, or show the RZ coin/ELO badge - users move on immediately after submitting a timed block, same as a real exam. The slot machine panel now shows only the cabinet/reels/lever and a status line (title, banner, controls row, rewards list, and footer removed), the reels fill the cabinet window instead of floating in a black border, the coin-burst animation no longer starts oversized, and the toolbar button is UAT-only via Brand.is_uat_environment(). Co-Authored-By: Claude Sonnet 5 --- scripts/autoload/exam_simulation_service.gd | 4 + scripts/ui/question_player.gd | 13 +- scripts/ui/rz_slot_machine_screen.gd | 381 +------------------- 3 files changed, 31 insertions(+), 367 deletions(-) diff --git a/scripts/autoload/exam_simulation_service.gd b/scripts/autoload/exam_simulation_service.gd index c3dc7f5..741693b 100644 --- a/scripts/autoload/exam_simulation_service.gd +++ b/scripts/autoload/exam_simulation_service.gd @@ -23,6 +23,7 @@ var is_loading := false var pending_action := "" var request_queue: Array = [] var request_access_token := "" +var last_failed_action := "" func _ready() -> void: @@ -141,6 +142,7 @@ func _send_absolute(action: String, method: int, url: String, payload: Dictionar if err != OK: is_loading = false pending_action = "" + last_failed_action = action request_failed.emit("The simulation request could not be started.") @@ -166,6 +168,7 @@ func _on_request_completed(result: int, response_code: int, _headers: PackedStri var message := "Simulation request failed with status %d." % response_code if not server_message.is_empty(): message = "%s %s" % [message, server_message] + last_failed_action = action request_failed.emit(message) return if response_code == 204: @@ -178,6 +181,7 @@ func _on_request_completed(result: int, response_code: int, _headers: PackedStri return var parsed: Variant = JSON.parse_string(body.get_string_from_utf8()) if typeof(parsed) != TYPE_DICTIONARY: + last_failed_action = action request_failed.emit("The simulation API returned an invalid response.") return if action == "readiness": diff --git a/scripts/ui/question_player.gd b/scripts/ui/question_player.gd index b8aa4a6..1d6825e 100644 --- a/scripts/ui/question_player.gd +++ b/scripts/ui/question_player.gd @@ -1193,6 +1193,11 @@ func _on_simulation_request_failed(message: String) -> void: if BlockSessionService.session_kind != "simulation": return simulation_submit_pending = false + if ExamSimulationService.last_failed_action != "submit": + # A per-question autosave failing shouldn't interrupt a timed exam with a + # blocking "Retry" - the next answer/navigation naturally retries it, and + # the final submit sends the complete, authoritative state regardless. + return next_button.disabled = false next_button.text = "Retry" next_button.tooltip_text = message @@ -2389,7 +2394,9 @@ func _refresh_toolbar_state() -> void: if calculator_button != null: calculator_button.visible = true if slot_machine_button != null: - slot_machine_button.visible = true + slot_machine_button.visible = Brand.is_uat_environment() + if credits_badge != null: + credits_badge.visible = BlockSessionService.session_kind != "simulation" _apply_question_theme() @@ -3404,7 +3411,9 @@ func _style_choice_button_for_state(index: int, choice_id: String) -> void: var dark: bool = true if theme_service == null else theme_service.is_dark_mode() var state := "default" var correct_id := str(BlockSessionService.get_current_question().get("correct_choice_id", "")) - var reviewing := pending_correct_advance or not current_answer.is_empty() + # Exam simulations don't reveal right/wrong - you move on after submitting, + # same as a real exam - so only "selected"/"eliminated" styling applies here. + var reviewing := BlockSessionService.session_kind != "simulation" and (pending_correct_advance or not current_answer.is_empty()) if reviewing and choice_id == correct_id: state = "correct" elif locked_wrong_choice_ids.has(choice_id) or (reviewing and choice_id != "" and choice_id != correct_id): diff --git a/scripts/ui/rz_slot_machine_screen.gd b/scripts/ui/rz_slot_machine_screen.gd index ea99543..fbcd8b8 100644 --- a/scripts/ui/rz_slot_machine_screen.gd +++ b/scripts/ui/rz_slot_machine_screen.gd @@ -1,8 +1,6 @@ extends PanelContainer const Brand := preload("res://scripts/ui/medmind_style.gd") -const STAR_ICON := preload("res://assets/icons/lucide/star.svg") -const CLOCK_ICON := preload("res://assets/icons/lucide/clock-3.svg") const CABINET_FRAME := preload("res://assets/slot_machine/cabinet_frame.png") const LEVER_TEXTURE := preload("res://assets/slot_machine/lever.png") const SLOT_COIN := preload("res://assets/slot_machine/slot_coin.png") @@ -11,6 +9,7 @@ const SLOT_POTION := preload("res://assets/slot_machine/slot_potion.png") const SLOT_SHIELD := preload("res://assets/slot_machine/slot_shield.png") const SLOT_COIN_STACK := preload("res://assets/slot_machine/slot_coin_stack.png") const SLOT_GIFT := preload("res://assets/slot_machine/slot_gift.png") +const CLOCK_ICON := preload("res://assets/icons/lucide/clock-3.svg") const REEL_SYMBOLS := [SLOT_COIN, SLOT_GEM, SLOT_POTION, SLOT_SHIELD, SLOT_COIN_STACK, SLOT_GIFT] # fraction-of-image rect of the cabinet's empty reel window, measured against cabinet_frame.png const WINDOW_RECT := Rect2(0.203, 0.246, 0.591, 0.604) @@ -24,7 +23,8 @@ const REWARD_ICONS := { "good_luck": {"texture": SLOT_COIN, "modulate": false, "tint": true}, } const MACHINE_SIZE := 380.0 -const CELL := 44.0 +const CELL := 71.0 +const REEL_SEPARATION := 4 var main: Control @@ -32,24 +32,10 @@ var drag_active := false var drag_offset := Vector2.ZERO var has_been_positioned := false -var subtitle_label: Label var machine_root: Control var lever_button: TextureButton var reel_states: Array = [] -var win_up_to_card: PanelContainer -var win_up_to_label: Label -var cost_card: PanelContainer -var cost_value_label: Label -var auto_card: PanelContainer -var spin_button: Button -var auto_spin_toggle: CheckButton var status_label: Label -var rewards_list: VBoxContainer -var footer_card: PanelContainer -var footer_icon_panel: PanelContainer -var footer_icon: TextureRect -var footer_heading: Label -var footer_description: Label var _spinning := false @@ -59,7 +45,7 @@ func _ready() -> void: visible = false top_level = true mouse_filter = Control.MOUSE_FILTER_STOP - custom_minimum_size = Vector2(460, 680) + custom_minimum_size = Vector2(440, 470) size = custom_minimum_size _build_ui() @@ -69,10 +55,8 @@ func _ready() -> void: _on_viewport_size_changed() CreditsService.credits_changed.connect(_refresh_spin_button) - CreditsService.spin_config_loaded.connect(_refresh_rewards) UIThemeService.theme_changed.connect(func(_dark: bool): refresh_theme_controls()) refresh_theme_controls() - _refresh_rewards() _refresh_spin_button() @@ -86,7 +70,6 @@ func open_panel() -> void: _clamp_to_viewport() CreditsService.refresh() CreditsService.fetch_spin_config() - _refresh_rewards() _refresh_spin_button() @@ -115,34 +98,13 @@ func _build_ui() -> void: margin.add_child(content) _build_title_bar(content) - - var scroll := ScrollContainer.new() - scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL - scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED - content.add_child(scroll) - - var scroll_content := VBoxContainer.new() - scroll_content.add_theme_constant_override("separation", 14) - scroll_content.size_flags_horizontal = Control.SIZE_EXPAND_FILL - scroll.add_child(scroll_content) - - subtitle_label = Label.new() - subtitle_label.text = "Test your luck and win exciting rewards!" - subtitle_label.add_theme_font_override("font", Brand.font) - subtitle_label.add_theme_font_size_override("font_size", 13) - subtitle_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - scroll_content.add_child(subtitle_label) - - _build_machine(scroll_content) - _build_controls(scroll_content) - _build_rewards_section(scroll_content) - _build_footer(scroll_content) + _build_machine(content) status_label = Label.new() status_label.add_theme_font_override("font", Brand.font) status_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART status_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - scroll_content.add_child(status_label) + content.add_child(status_label) func _build_title_bar(content: VBoxContainer) -> void: @@ -153,13 +115,10 @@ func _build_title_bar(content: VBoxContainer) -> void: title_bar.gui_input.connect(_on_title_bar_gui_input) content.add_child(title_bar) - var title := Label.new() - title.text = "RZ Slot Machine" - title.size_flags_horizontal = Control.SIZE_EXPAND_FILL - title.mouse_filter = Control.MOUSE_FILTER_IGNORE - title.add_theme_font_override("font", Brand.font) - title.add_theme_font_size_override("font_size", 18) - title_bar.add_child(title) + var spacer := Control.new() + spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL + spacer.mouse_filter = Control.MOUSE_FILTER_IGNORE + title_bar.add_child(spacer) var close := Button.new() close.name = "SlotMachineClose" @@ -200,7 +159,7 @@ func _build_machine(parent: Control) -> void: window.add_child(reel_center) var reels_grid := HBoxContainer.new() - reels_grid.add_theme_constant_override("separation", 10) + reels_grid.add_theme_constant_override("separation", REEL_SEPARATION) reel_center.add_child(reels_grid) reel_states.clear() @@ -251,282 +210,15 @@ func _build_machine(parent: Control) -> void: machine_root.add_child(lever_button) -func _build_controls(parent: Control) -> void: - var dark := UIThemeService.is_dark_mode() - - win_up_to_card = PanelContainer.new() - win_up_to_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false)) - parent.add_child(win_up_to_card) - var win_margin := MarginContainer.new() - win_margin.add_theme_constant_override("margin_left", 14) - win_margin.add_theme_constant_override("margin_right", 14) - win_margin.add_theme_constant_override("margin_top", 10) - win_margin.add_theme_constant_override("margin_bottom", 10) - win_up_to_card.add_child(win_margin) - win_up_to_label = Label.new() - win_up_to_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - win_up_to_label.add_theme_font_override("font", Brand.font) - win_up_to_label.add_theme_font_size_override("font_size", 14) - win_up_to_label.add_theme_color_override("font_color", Brand.text_color(dark)) - win_margin.add_child(win_up_to_label) - - var controls_row := HBoxContainer.new() - controls_row.add_theme_constant_override("separation", 10) - parent.add_child(controls_row) - - cost_card = PanelContainer.new() - cost_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false)) - controls_row.add_child(cost_card) - var cost_margin := MarginContainer.new() - cost_margin.add_theme_constant_override("margin_left", 10) - cost_margin.add_theme_constant_override("margin_right", 10) - cost_margin.add_theme_constant_override("margin_top", 6) - cost_margin.add_theme_constant_override("margin_bottom", 6) - cost_card.add_child(cost_margin) - var cost_box := VBoxContainer.new() - cost_box.add_theme_constant_override("separation", 0) - cost_margin.add_child(cost_box) - var cost_caption := Label.new() - cost_caption.text = "Cost per spin" - cost_caption.add_theme_font_override("font", Brand.font) - cost_caption.add_theme_font_size_override("font_size", 10) - cost_caption.add_theme_color_override("font_color", Brand.muted_color(dark)) - cost_box.add_child(cost_caption) - cost_value_label = Label.new() - cost_value_label.add_theme_font_override("font", Brand.font) - cost_value_label.add_theme_font_size_override("font_size", 14) - cost_value_label.add_theme_color_override("font_color", Brand.text_color(dark)) - cost_box.add_child(cost_value_label) - - spin_button = Button.new() - spin_button.text = "Spin" - spin_button.custom_minimum_size = Vector2(110, 52) - spin_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL - spin_button.size_flags_vertical = Control.SIZE_SHRINK_CENTER - spin_button.add_theme_font_override("font", Brand.font) - spin_button.add_theme_font_size_override("font_size", 16) - _style_spin_button() - spin_button.pressed.connect(_on_spin_pressed) - controls_row.add_child(spin_button) - - auto_card = PanelContainer.new() - auto_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false)) - controls_row.add_child(auto_card) - var auto_margin := MarginContainer.new() - auto_margin.add_theme_constant_override("margin_left", 10) - auto_margin.add_theme_constant_override("margin_right", 10) - auto_margin.add_theme_constant_override("margin_top", 6) - auto_margin.add_theme_constant_override("margin_bottom", 6) - auto_card.add_child(auto_margin) - var auto_box := VBoxContainer.new() - auto_box.add_theme_constant_override("separation", 0) - auto_margin.add_child(auto_box) - auto_spin_toggle = CheckButton.new() - auto_spin_toggle.text = "Auto Spin" - auto_spin_toggle.add_theme_font_override("font", Brand.font) - auto_box.add_child(auto_spin_toggle) - var auto_caption := Label.new() - auto_caption.text = "10 spins" - auto_caption.add_theme_font_override("font", Brand.font) - auto_caption.add_theme_font_size_override("font_size", 10) - auto_caption.add_theme_color_override("font_color", Brand.muted_color(dark)) - auto_box.add_child(auto_caption) - - var info_label := Label.new() - info_label.text = "All rewards are instant and sent to your account." - info_label.add_theme_font_override("font", Brand.font) - info_label.add_theme_font_size_override("font_size", 10) - info_label.add_theme_color_override("font_color", Brand.muted_color(dark)) - info_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - parent.add_child(info_label) - - -func _build_rewards_section(parent: Control) -> void: - var dark := UIThemeService.is_dark_mode() - var panel := PanelContainer.new() - panel.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false)) - parent.add_child(panel) - - var margin := MarginContainer.new() - margin.add_theme_constant_override("margin_left", 14) - margin.add_theme_constant_override("margin_right", 14) - margin.add_theme_constant_override("margin_top", 12) - margin.add_theme_constant_override("margin_bottom", 12) - panel.add_child(margin) - - var column := VBoxContainer.new() - column.add_theme_constant_override("separation", 10) - margin.add_child(column) - - var heading := Label.new() - heading.text = "Possible Rewards" - heading.add_theme_font_override("font", Brand.font) - heading.add_theme_font_size_override("font_size", 15) - heading.add_theme_color_override("font_color", Brand.text_color(dark)) - column.add_child(heading) - - rewards_list = VBoxContainer.new() - rewards_list.add_theme_constant_override("separation", 8) - column.add_child(rewards_list) - - -func _build_footer(parent: Control) -> void: - var dark := UIThemeService.is_dark_mode() - footer_card = PanelContainer.new() - footer_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false)) - parent.add_child(footer_card) - - var margin := MarginContainer.new() - margin.add_theme_constant_override("margin_left", 14) - margin.add_theme_constant_override("margin_right", 14) - margin.add_theme_constant_override("margin_top", 10) - margin.add_theme_constant_override("margin_bottom", 10) - footer_card.add_child(margin) - - var row := HBoxContainer.new() - row.add_theme_constant_override("separation", 12) - margin.add_child(row) - - footer_icon_panel = PanelContainer.new() - footer_icon_panel.custom_minimum_size = Vector2(32, 32) - footer_icon_panel.size_flags_vertical = Control.SIZE_SHRINK_CENTER - footer_icon_panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, Brand.ROYAL_BLUE, 16)) - footer_icon = TextureRect.new() - footer_icon.texture = STAR_ICON - footer_icon.custom_minimum_size = Vector2(16, 16) - footer_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE - footer_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED - footer_icon.modulate = Brand.ROYAL_BLUE - footer_icon_panel.add_child(footer_icon) - row.add_child(footer_icon_panel) - - var copy := VBoxContainer.new() - copy.size_flags_horizontal = Control.SIZE_EXPAND_FILL - copy.size_flags_vertical = Control.SIZE_SHRINK_CENTER - copy.add_theme_constant_override("separation", 2) - footer_heading = Label.new() - footer_heading.text = "How it works" - footer_heading.add_theme_font_override("font", Brand.font) - footer_heading.add_theme_font_size_override("font_size", 14) - footer_heading.add_theme_color_override("font_color", Brand.text_color(dark)) - copy.add_child(footer_heading) - footer_description = Label.new() - footer_description.text = "Spend RZ Coins to spin the slot machine. Win coins, boosts, tokens, and more!" - footer_description.add_theme_font_override("font", Brand.font) - footer_description.add_theme_font_size_override("font_size", 11) - footer_description.add_theme_color_override("font_color", Brand.muted_color(dark)) - footer_description.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART - copy.add_child(footer_description) - row.add_child(copy) - - func refresh_theme_controls() -> void: var dark := UIThemeService.is_dark_mode() add_theme_stylebox_override("panel", Brand.content_card_box(dark, true, Brand.ROYAL_BLUE)) - subtitle_label.add_theme_color_override("font_color", Brand.muted_color(dark)) - win_up_to_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false)) - win_up_to_label.add_theme_color_override("font_color", Brand.text_color(dark)) - cost_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false)) - cost_value_label.add_theme_color_override("font_color", Brand.text_color(dark)) - auto_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false)) - footer_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false)) - footer_icon_panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, Brand.ROYAL_BLUE, 16)) - footer_heading.add_theme_color_override("font_color", Brand.text_color(dark)) - footer_description.add_theme_color_override("font_color", Brand.muted_color(dark)) - _refresh_rewards() - - -func _reward_color(key: String) -> Color: - match key: - "jackpot": return Brand.AMBER - "epic": return Brand.PURPLE - "great": return Brand.ROYAL_BLUE - "exam_token": return Brand.ROYAL_BLUE - "qbank_reset": return Brand.SUCCESS - "nice": return Brand.SKY_BLUE - "good_luck": return Brand.SLATE - _: return Brand.ROYAL_BLUE - - -func _refresh_rewards() -> void: - if cost_value_label == null: - return - var dark := UIThemeService.is_dark_mode() - cost_value_label.text = "%d RZ Coins" % CreditsService.spin_cost - - var max_coins := 0 - for reward_variant in CreditsService.spin_rewards: - if typeof(reward_variant) != TYPE_DICTIONARY: - continue - max_coins = maxi(max_coins, int((reward_variant as Dictionary).get("coinAmount", 0))) - win_up_to_label.text = "Win up to %s RZ Coins!" % _format_thousands(max_coins) if max_coins > 0 else "" - - for child in rewards_list.get_children(): - child.queue_free() - for reward_variant in CreditsService.spin_rewards: - if typeof(reward_variant) != TYPE_DICTIONARY: - continue - var reward: Dictionary = reward_variant - rewards_list.add_child(_build_reward_row(dark, reward)) - - -func _build_reward_row(dark: bool, reward: Dictionary) -> HBoxContainer: - var key := str(reward.get("key", "")) - var accent := _reward_color(key) - var row := HBoxContainer.new() - row.add_theme_constant_override("separation", 10) - - var icon_panel := PanelContainer.new() - icon_panel.custom_minimum_size = Vector2(34, 34) - icon_panel.size_flags_vertical = Control.SIZE_SHRINK_CENTER - icon_panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, accent, 17)) - var icon := TextureRect.new() - var spec: Dictionary = REWARD_ICONS.get(key, {"texture": SLOT_COIN, "modulate": false}) - icon.texture = spec.get("texture") - icon.custom_minimum_size = Vector2(20, 20) - icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE - icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED - if spec.get("modulate", false): - icon.modulate = accent - elif spec.get("tint", false): - icon.modulate = Color("94a3b8") - icon_panel.add_child(icon) - row.add_child(icon_panel) - - var copy := VBoxContainer.new() - copy.size_flags_horizontal = Control.SIZE_EXPAND_FILL - copy.size_flags_vertical = Control.SIZE_SHRINK_CENTER - copy.add_theme_constant_override("separation", 0) - var label := Label.new() - label.text = str(reward.get("label", "")) - label.add_theme_font_override("font", Brand.font) - label.add_theme_font_size_override("font_size", 12) - label.add_theme_color_override("font_color", Brand.text_color(dark)) - copy.add_child(label) - var description := Label.new() - description.text = str(reward.get("description", "")) - description.add_theme_font_override("font", Brand.font) - description.add_theme_font_size_override("font_size", 10) - description.add_theme_color_override("font_color", Brand.muted_color(dark)) - copy.add_child(description) - row.add_child(copy) - - var percent_label := Label.new() - percent_label.text = "%s%%" % _format_percent(float(reward.get("probability", 0.0)) * 100.0) - percent_label.add_theme_font_override("font", Brand.font) - percent_label.add_theme_font_size_override("font_size", 12) - percent_label.add_theme_color_override("font_color", accent) - row.add_child(percent_label) - - return row func _refresh_spin_button() -> void: - if spin_button == null: + if lever_button == null: return - var disabled := _spinning or CreditsService.credits < CreditsService.spin_cost - spin_button.disabled = disabled - lever_button.disabled = disabled + lever_button.disabled = _spinning or CreditsService.credits < CreditsService.spin_cost func _on_lever_pulled() -> void: @@ -545,13 +237,7 @@ func _on_spin_pressed() -> void: return _spinning = true _refresh_spin_button() - var spin_count := 10 if auto_spin_toggle.button_pressed else 1 - for _i in range(spin_count): - if CreditsService.credits < CreditsService.spin_cost: - break - var should_continue := await _do_one_spin() - if not should_continue: - break + await _do_one_spin() _spinning = false _refresh_spin_button() @@ -679,6 +365,8 @@ func _play_coin_burst(coins_won: int) -> void: for index in range(coin_count): var coin := TextureRect.new() coin.texture = SLOT_COIN + coin.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + coin.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED coin.custom_minimum_size = Vector2(16, 16) coin.size = Vector2(16, 16) coin.top_level = true @@ -700,25 +388,6 @@ func _play_coin_burst(coins_won: int) -> void: credits_badge.bump()) -func _style_spin_button() -> void: - var normal := StyleBoxFlat.new() - normal.bg_color = Brand.ROYAL_BLUE - normal.set_corner_radius_all(8) - var hover := StyleBoxFlat.new() - hover.bg_color = Brand.SKY_BLUE - hover.set_corner_radius_all(8) - var disabled := StyleBoxFlat.new() - disabled.bg_color = Color(Brand.ROYAL_BLUE, 0.35) - disabled.set_corner_radius_all(8) - spin_button.add_theme_stylebox_override("normal", normal) - spin_button.add_theme_stylebox_override("hover", hover) - spin_button.add_theme_stylebox_override("pressed", normal) - spin_button.add_theme_stylebox_override("disabled", disabled) - spin_button.add_theme_color_override("font_color", Brand.DARK_TEXT) - spin_button.add_theme_color_override("font_hover_color", Brand.DARK_TEXT) - spin_button.add_theme_color_override("font_disabled_color", Color(Brand.DARK_TEXT, 0.7)) - - func _on_title_bar_gui_input(event: InputEvent) -> void: var touch_event := event as InputEventScreenTouch if touch_event != null: @@ -757,21 +426,3 @@ func _clamp_to_viewport() -> void: var viewport_size := get_viewport_rect().size position.x = clampf(position.x, 12.0, maxf(12.0, viewport_size.x - size.x - 12.0)) position.y = clampf(position.y, 12.0, maxf(12.0, viewport_size.y - size.y - 12.0)) - - -func _format_thousands(value: int) -> String: - var digits := str(absi(value)) - var grouped := "" - var count := 0 - for index in range(digits.length() - 1, -1, -1): - grouped = digits[index] + grouped - count += 1 - if count % 3 == 0 and index != 0: - grouped = "," + grouped - return ("-" if value < 0 else "") + grouped - - -func _format_percent(value: float) -> String: - if absf(value - roundf(value)) < 0.001: - return str(int(roundf(value))) - return "%.1f" % value