Restyle the RZ Coin Shop screen as a card grid
Replaces the plain title/list layout with a header, a coin-balance summary card, and a product card grid (icon, description, price, solid Buy button) matching the app's existing card/badge visual language. Only the two real products render - no fabricated categories or bundles, since the backend doesn't have that data yet.
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="#ffffff"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
|
||||||
|
<path d="M3 3v5h5" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 292 B |
@@ -0,0 +1,43 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cwt1kxtnhfgqo"
|
||||||
|
path="res://.godot/imported/rotate-ccw.svg-72097dc869bb37466442f5645fc6d506.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/icons/lucide/rotate-ccw.svg"
|
||||||
|
dest_files=["res://.godot/imported/rotate-ccw.svg-72097dc869bb37466442f5645fc6d506.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
||||||
+1
-1
@@ -432,7 +432,7 @@ func _populate_navigation(container: VBoxContainer, include_brand: bool) -> void
|
|||||||
_add_nav_button(container, "flashcards", "Flashcards", "clipboard-plus", true, Brand.is_uat_environment())
|
_add_nav_button(container, "flashcards", "Flashcards", "clipboard-plus", true, Brand.is_uat_environment())
|
||||||
_add_nav_button(container, "bookmarks", "Bookmarks", "bookmark", true)
|
_add_nav_button(container, "bookmarks", "Bookmarks", "bookmark", true)
|
||||||
_add_nav_button(container, "notes", "Notes", "notebook-pen", true)
|
_add_nav_button(container, "notes", "Notes", "notebook-pen", true)
|
||||||
_add_nav_button(container, "rz_store", "Rz Coin Store", "star", true)
|
_add_nav_button(container, "rz_store", "RZ Coin Shop", "star", true)
|
||||||
|
|
||||||
var spacer := Control.new()
|
var spacer := Control.new()
|
||||||
spacer.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
spacer.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
|
|||||||
+338
-50
@@ -1,13 +1,27 @@
|
|||||||
extends VBoxContainer
|
extends VBoxContainer
|
||||||
|
|
||||||
const Brand := preload("res://scripts/ui/medmind_style.gd")
|
const Brand := preload("res://scripts/ui/medmind_style.gd")
|
||||||
|
const ICON_BY_SKU := {
|
||||||
|
"exam_token_1": "clock-3",
|
||||||
|
"qbank_reset_1": "rotate-ccw",
|
||||||
|
}
|
||||||
|
|
||||||
var main: Control
|
var main: Control
|
||||||
var balance_label: Label
|
var subtitle_label: Label
|
||||||
|
var summary_card: PanelContainer
|
||||||
|
var summary_icon_panel: PanelContainer
|
||||||
|
var summary_icon: TextureRect
|
||||||
|
var balance_value_label: Label
|
||||||
|
var coin_caption_label: Label
|
||||||
var inventory_label: Label
|
var inventory_label: Label
|
||||||
var products_box: VBoxContainer
|
var products_grid: HFlowContainer
|
||||||
var status_label: Label
|
var status_label: Label
|
||||||
var confirm_dialog: ConfirmationDialog
|
var confirm_dialog: ConfirmationDialog
|
||||||
|
var footer_card: PanelContainer
|
||||||
|
var footer_icon_panel: PanelContainer
|
||||||
|
var footer_icon: TextureRect
|
||||||
|
var footer_heading: Label
|
||||||
|
var footer_description: Label
|
||||||
var pending_sku := ""
|
var pending_sku := ""
|
||||||
|
|
||||||
|
|
||||||
@@ -15,32 +29,36 @@ func _ready() -> void:
|
|||||||
size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
size_flags_vertical = Control.SIZE_EXPAND_FILL
|
size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
add_theme_constant_override("separation", 18)
|
add_theme_constant_override("separation", 18)
|
||||||
var title := Label.new()
|
|
||||||
title.text = "Rz Coin Store"
|
_build_header()
|
||||||
title.add_theme_font_override("font", Brand.font)
|
_build_summary_card()
|
||||||
title.add_theme_font_size_override("font_size", 30)
|
|
||||||
add_child(title)
|
var section_title := Label.new()
|
||||||
var intro := Label.new()
|
section_title.text = "Featured"
|
||||||
intro.text = "Spend coins earned from correct, on-time first answers. Purchases use Rz Coins only."
|
section_title.add_theme_font_override("font", Brand.font)
|
||||||
intro.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
section_title.add_theme_font_size_override("font_size", 18)
|
||||||
add_child(intro)
|
add_child(section_title)
|
||||||
balance_label = Label.new()
|
|
||||||
balance_label.add_theme_font_size_override("font_size", 20)
|
products_grid = HFlowContainer.new()
|
||||||
add_child(balance_label)
|
products_grid.add_theme_constant_override("h_separation", 14)
|
||||||
inventory_label = Label.new()
|
products_grid.add_theme_constant_override("v_separation", 14)
|
||||||
add_child(inventory_label)
|
add_child(products_grid)
|
||||||
products_box = VBoxContainer.new()
|
|
||||||
products_box.add_theme_constant_override("separation", 12)
|
|
||||||
add_child(products_box)
|
|
||||||
status_label = Label.new()
|
status_label = Label.new()
|
||||||
|
status_label.add_theme_font_override("font", Brand.font)
|
||||||
status_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
status_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||||
add_child(status_label)
|
add_child(status_label)
|
||||||
|
|
||||||
confirm_dialog = ConfirmationDialog.new()
|
confirm_dialog = ConfirmationDialog.new()
|
||||||
confirm_dialog.confirmed.connect(_confirm_purchase)
|
confirm_dialog.confirmed.connect(_confirm_purchase)
|
||||||
add_child(confirm_dialog)
|
add_child(confirm_dialog)
|
||||||
|
|
||||||
|
_build_footer()
|
||||||
|
|
||||||
CreditsService.credits_changed.connect(_refresh)
|
CreditsService.credits_changed.connect(_refresh)
|
||||||
CreditsService.credits_failed.connect(_show_error)
|
CreditsService.credits_failed.connect(_show_error)
|
||||||
CreditsService.store_operation_completed.connect(_show_message)
|
CreditsService.store_operation_completed.connect(_show_message)
|
||||||
|
UIThemeService.theme_changed.connect(func(_dark: bool): _refresh_theme_controls(); _refresh())
|
||||||
_refresh()
|
_refresh()
|
||||||
|
|
||||||
|
|
||||||
@@ -49,45 +67,301 @@ func on_show(_payload: Variant = null) -> void:
|
|||||||
_refresh()
|
_refresh()
|
||||||
|
|
||||||
|
|
||||||
|
func _build_header() -> void:
|
||||||
|
var title := Label.new()
|
||||||
|
title.text = "RZ Coin Shop"
|
||||||
|
title.add_theme_font_override("font", Brand.font)
|
||||||
|
title.add_theme_font_size_override("font_size", 30)
|
||||||
|
add_child(title)
|
||||||
|
subtitle_label = Label.new()
|
||||||
|
subtitle_label.text = "Spend your RZ Coins on premium content and boosts."
|
||||||
|
subtitle_label.add_theme_font_override("font", Brand.font)
|
||||||
|
subtitle_label.add_theme_font_size_override("font_size", 14)
|
||||||
|
subtitle_label.add_theme_color_override("font_color", Brand.muted_color(UIThemeService.is_dark_mode()))
|
||||||
|
add_child(subtitle_label)
|
||||||
|
|
||||||
|
|
||||||
|
func _build_summary_card() -> void:
|
||||||
|
var dark := UIThemeService.is_dark_mode()
|
||||||
|
summary_card = PanelContainer.new()
|
||||||
|
summary_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, true, Brand.AMBER))
|
||||||
|
summary_card.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||||
|
add_child(summary_card)
|
||||||
|
|
||||||
|
var margin := MarginContainer.new()
|
||||||
|
margin.add_theme_constant_override("margin_left", 16)
|
||||||
|
margin.add_theme_constant_override("margin_right", 16)
|
||||||
|
margin.add_theme_constant_override("margin_top", 12)
|
||||||
|
margin.add_theme_constant_override("margin_bottom", 12)
|
||||||
|
summary_card.add_child(margin)
|
||||||
|
|
||||||
|
var row := HBoxContainer.new()
|
||||||
|
row.add_theme_constant_override("separation", 14)
|
||||||
|
margin.add_child(row)
|
||||||
|
|
||||||
|
summary_icon_panel = PanelContainer.new()
|
||||||
|
summary_icon_panel.custom_minimum_size = Vector2(40, 40)
|
||||||
|
summary_icon_panel.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||||
|
summary_icon_panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, Brand.AMBER, 20))
|
||||||
|
summary_icon = TextureRect.new()
|
||||||
|
summary_icon.texture = load("res://assets/icons/lucide/star.svg")
|
||||||
|
summary_icon.custom_minimum_size = Vector2(22, 22)
|
||||||
|
summary_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
|
summary_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||||
|
summary_icon.modulate = Brand.AMBER
|
||||||
|
summary_icon_panel.add_child(summary_icon)
|
||||||
|
row.add_child(summary_icon_panel)
|
||||||
|
|
||||||
|
var copy := VBoxContainer.new()
|
||||||
|
copy.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||||
|
copy.add_theme_constant_override("separation", 0)
|
||||||
|
balance_value_label = Label.new()
|
||||||
|
balance_value_label.add_theme_font_override("font", Brand.font)
|
||||||
|
balance_value_label.add_theme_font_size_override("font_size", 22)
|
||||||
|
balance_value_label.add_theme_color_override("font_color", Brand.text_color(dark))
|
||||||
|
copy.add_child(balance_value_label)
|
||||||
|
coin_caption_label = Label.new()
|
||||||
|
coin_caption_label.text = "RZ Coins"
|
||||||
|
coin_caption_label.add_theme_font_override("font", Brand.font)
|
||||||
|
coin_caption_label.add_theme_font_size_override("font_size", 12)
|
||||||
|
coin_caption_label.add_theme_color_override("font_color", Brand.muted_color(dark))
|
||||||
|
copy.add_child(coin_caption_label)
|
||||||
|
inventory_label = Label.new()
|
||||||
|
inventory_label.add_theme_font_override("font", Brand.font)
|
||||||
|
inventory_label.add_theme_font_size_override("font_size", 12)
|
||||||
|
inventory_label.add_theme_color_override("font_color", Brand.muted_color(dark))
|
||||||
|
copy.add_child(inventory_label)
|
||||||
|
row.add_child(copy)
|
||||||
|
|
||||||
|
|
||||||
|
func _build_footer() -> void:
|
||||||
|
var dark := UIThemeService.is_dark_mode()
|
||||||
|
footer_card = PanelContainer.new()
|
||||||
|
footer_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, false))
|
||||||
|
add_child(footer_card)
|
||||||
|
|
||||||
|
var margin := MarginContainer.new()
|
||||||
|
margin.add_theme_constant_override("margin_left", 16)
|
||||||
|
margin.add_theme_constant_override("margin_right", 16)
|
||||||
|
margin.add_theme_constant_override("margin_top", 12)
|
||||||
|
margin.add_theme_constant_override("margin_bottom", 12)
|
||||||
|
footer_card.add_child(margin)
|
||||||
|
|
||||||
|
var row := HBoxContainer.new()
|
||||||
|
row.add_theme_constant_override("separation", 14)
|
||||||
|
margin.add_child(row)
|
||||||
|
|
||||||
|
footer_icon_panel = PanelContainer.new()
|
||||||
|
footer_icon_panel.custom_minimum_size = Vector2(36, 36)
|
||||||
|
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, 18))
|
||||||
|
footer_icon = TextureRect.new()
|
||||||
|
footer_icon.texture = load("res://assets/icons/lucide/activity.svg")
|
||||||
|
footer_icon.custom_minimum_size = Vector2(18, 18)
|
||||||
|
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 to earn RZ Coins"
|
||||||
|
footer_heading.add_theme_font_override("font", Brand.font)
|
||||||
|
footer_heading.add_theme_font_size_override("font_size", 15)
|
||||||
|
footer_heading.add_theme_color_override("font_color", Brand.text_color(dark))
|
||||||
|
copy.add_child(footer_heading)
|
||||||
|
footer_description = Label.new()
|
||||||
|
footer_description.text = "Answer questions correctly on the first try, within the time limit."
|
||||||
|
footer_description.add_theme_font_override("font", Brand.font)
|
||||||
|
footer_description.add_theme_font_size_override("font_size", 12)
|
||||||
|
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()
|
||||||
|
subtitle_label.add_theme_color_override("font_color", Brand.muted_color(dark))
|
||||||
|
summary_card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, true, Brand.AMBER))
|
||||||
|
summary_icon_panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, Brand.AMBER, 20))
|
||||||
|
balance_value_label.add_theme_color_override("font_color", Brand.text_color(dark))
|
||||||
|
coin_caption_label.add_theme_color_override("font_color", Brand.muted_color(dark))
|
||||||
|
inventory_label.add_theme_color_override("font_color", Brand.muted_color(dark))
|
||||||
|
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, 18))
|
||||||
|
footer_heading.add_theme_color_override("font_color", Brand.text_color(dark))
|
||||||
|
footer_description.add_theme_color_override("font_color", Brand.muted_color(dark))
|
||||||
|
|
||||||
|
|
||||||
func _refresh() -> void:
|
func _refresh() -> void:
|
||||||
balance_label.text = "%d Rz Coins" % CreditsService.credits
|
var dark := UIThemeService.is_dark_mode()
|
||||||
inventory_label.text = "Inventory: %d exam token(s) · %d QBank reset token(s)" % [
|
balance_value_label.text = _format_thousands(CreditsService.credits)
|
||||||
|
inventory_label.text = "%d exam token(s) · %d QBank reset token(s)" % [
|
||||||
CreditsService.exam_tokens, CreditsService.qbank_reset_tokens]
|
CreditsService.exam_tokens, CreditsService.qbank_reset_tokens]
|
||||||
for child in products_box.get_children():
|
for child in products_grid.get_children():
|
||||||
child.queue_free()
|
child.queue_free()
|
||||||
for product_variant in CreditsService.products:
|
for product_variant in CreditsService.products:
|
||||||
if typeof(product_variant) != TYPE_DICTIONARY:
|
if typeof(product_variant) != TYPE_DICTIONARY:
|
||||||
continue
|
continue
|
||||||
var product: Dictionary = product_variant
|
var product: Dictionary = product_variant
|
||||||
var card := PanelContainer.new()
|
products_grid.add_child(_build_product_card(dark, product))
|
||||||
card.add_theme_stylebox_override("panel", Brand.content_card_box(UIThemeService.is_dark_mode(), true, Brand.ROYAL_BLUE))
|
|
||||||
var row := HBoxContainer.new()
|
|
||||||
row.add_theme_constant_override("separation", 16)
|
|
||||||
card.add_child(row)
|
|
||||||
var copy := VBoxContainer.new()
|
|
||||||
copy.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
||||||
var name_label := Label.new()
|
|
||||||
name_label.text = str(product.get("name", "Store item"))
|
|
||||||
name_label.add_theme_font_size_override("font_size", 19)
|
|
||||||
copy.add_child(name_label)
|
|
||||||
var description := Label.new()
|
|
||||||
description.text = str(product.get("description", ""))
|
|
||||||
description.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
||||||
copy.add_child(description)
|
|
||||||
row.add_child(copy)
|
|
||||||
var buy := Button.new()
|
|
||||||
var price := int(product.get("coinPrice", 0))
|
|
||||||
buy.text = "Buy · %d coins" % price
|
|
||||||
buy.disabled = CreditsService.credits < price
|
|
||||||
buy.pressed.connect(_ask_purchase.bind(str(product.get("sku", "")), str(product.get("name", "item")), price))
|
|
||||||
row.add_child(buy)
|
|
||||||
products_box.add_child(card)
|
|
||||||
if CreditsService.qbank_reset_tokens > 0:
|
if CreditsService.qbank_reset_tokens > 0:
|
||||||
var reset := Button.new()
|
products_grid.add_child(_build_reset_card(dark))
|
||||||
reset.text = "Use QBank Reset Token"
|
|
||||||
reset.tooltip_text = "Question eligibility resets; ELO, history, bookmarks, notes, reviews, and earning limits are preserved."
|
|
||||||
reset.pressed.connect(_ask_reset)
|
func _build_product_card(dark: bool, product: Dictionary) -> PanelContainer:
|
||||||
products_box.add_child(reset)
|
var sku := str(product.get("sku", ""))
|
||||||
|
var price := int(product.get("coinPrice", 0))
|
||||||
|
var card := _new_card(dark)
|
||||||
|
var content: VBoxContainer = card.get_child(0).get_child(0)
|
||||||
|
|
||||||
|
content.add_child(_card_icon(dark, ICON_BY_SKU.get(sku, "star"), Brand.ROYAL_BLUE))
|
||||||
|
|
||||||
|
var name_label := Label.new()
|
||||||
|
name_label.text = str(product.get("name", "Store item"))
|
||||||
|
name_label.add_theme_font_override("font", Brand.font)
|
||||||
|
name_label.add_theme_font_size_override("font_size", 16)
|
||||||
|
name_label.add_theme_color_override("font_color", Brand.text_color(dark))
|
||||||
|
content.add_child(name_label)
|
||||||
|
|
||||||
|
var description := Label.new()
|
||||||
|
description.text = str(product.get("description", ""))
|
||||||
|
description.add_theme_font_override("font", Brand.font)
|
||||||
|
description.add_theme_font_size_override("font_size", 12)
|
||||||
|
description.add_theme_color_override("font_color", Brand.muted_color(dark))
|
||||||
|
description.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||||
|
description.custom_minimum_size = Vector2(190, 0)
|
||||||
|
content.add_child(description)
|
||||||
|
|
||||||
|
var footer_row := HBoxContainer.new()
|
||||||
|
footer_row.add_theme_constant_override("separation", 8)
|
||||||
|
content.add_child(footer_row)
|
||||||
|
footer_row.add_child(_price_tag(dark, price))
|
||||||
|
var spacer := Control.new()
|
||||||
|
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
footer_row.add_child(spacer)
|
||||||
|
var buy := Button.new()
|
||||||
|
buy.text = "Buy"
|
||||||
|
buy.custom_minimum_size = Vector2(64, 34)
|
||||||
|
buy.add_theme_font_override("font", Brand.font)
|
||||||
|
_style_buy_button(buy)
|
||||||
|
buy.disabled = CreditsService.credits < price
|
||||||
|
buy.pressed.connect(_ask_purchase.bind(sku, str(product.get("name", "item")), price))
|
||||||
|
footer_row.add_child(buy)
|
||||||
|
|
||||||
|
return card
|
||||||
|
|
||||||
|
|
||||||
|
func _build_reset_card(dark: bool) -> PanelContainer:
|
||||||
|
var card := _new_card(dark)
|
||||||
|
var content: VBoxContainer = card.get_child(0).get_child(0)
|
||||||
|
|
||||||
|
content.add_child(_card_icon(dark, "rotate-ccw", Brand.SUCCESS))
|
||||||
|
|
||||||
|
var name_label := Label.new()
|
||||||
|
name_label.text = "Use QBank Reset Token"
|
||||||
|
name_label.add_theme_font_override("font", Brand.font)
|
||||||
|
name_label.add_theme_font_size_override("font_size", 16)
|
||||||
|
name_label.add_theme_color_override("font_color", Brand.text_color(dark))
|
||||||
|
content.add_child(name_label)
|
||||||
|
|
||||||
|
var description := Label.new()
|
||||||
|
description.text = "Question eligibility resets; ELO, history, bookmarks, notes, reviews, and earning limits are preserved."
|
||||||
|
description.add_theme_font_override("font", Brand.font)
|
||||||
|
description.add_theme_font_size_override("font_size", 12)
|
||||||
|
description.add_theme_color_override("font_color", Brand.muted_color(dark))
|
||||||
|
description.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||||
|
description.custom_minimum_size = Vector2(190, 0)
|
||||||
|
content.add_child(description)
|
||||||
|
|
||||||
|
var footer_row := HBoxContainer.new()
|
||||||
|
content.add_child(footer_row)
|
||||||
|
var spacer := Control.new()
|
||||||
|
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
footer_row.add_child(spacer)
|
||||||
|
var use_button := Button.new()
|
||||||
|
use_button.text = "Use token"
|
||||||
|
use_button.custom_minimum_size = Vector2(90, 34)
|
||||||
|
use_button.add_theme_font_override("font", Brand.font)
|
||||||
|
_style_buy_button(use_button)
|
||||||
|
use_button.pressed.connect(_ask_reset)
|
||||||
|
footer_row.add_child(use_button)
|
||||||
|
|
||||||
|
return card
|
||||||
|
|
||||||
|
|
||||||
|
func _new_card(dark: bool) -> PanelContainer:
|
||||||
|
var card := PanelContainer.new()
|
||||||
|
card.custom_minimum_size = Vector2(230, 0)
|
||||||
|
card.add_theme_stylebox_override("panel", Brand.content_card_box(dark, true, Brand.ROYAL_BLUE))
|
||||||
|
var margin := MarginContainer.new()
|
||||||
|
margin.add_theme_constant_override("margin_left", 16)
|
||||||
|
margin.add_theme_constant_override("margin_right", 16)
|
||||||
|
margin.add_theme_constant_override("margin_top", 14)
|
||||||
|
margin.add_theme_constant_override("margin_bottom", 14)
|
||||||
|
card.add_child(margin)
|
||||||
|
var content := VBoxContainer.new()
|
||||||
|
content.add_theme_constant_override("separation", 8)
|
||||||
|
margin.add_child(content)
|
||||||
|
return card
|
||||||
|
|
||||||
|
|
||||||
|
func _card_icon(dark: bool, icon_name: String, accent: Color) -> PanelContainer:
|
||||||
|
var icon_panel := PanelContainer.new()
|
||||||
|
icon_panel.custom_minimum_size = Vector2(48, 48)
|
||||||
|
icon_panel.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||||
|
icon_panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, accent, 24))
|
||||||
|
var icon := TextureRect.new()
|
||||||
|
icon.texture = load("res://assets/icons/lucide/%s.svg" % icon_name)
|
||||||
|
icon.custom_minimum_size = Vector2(24, 24)
|
||||||
|
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
|
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||||
|
icon.modulate = accent
|
||||||
|
icon_panel.add_child(icon)
|
||||||
|
return icon_panel
|
||||||
|
|
||||||
|
|
||||||
|
func _price_tag(dark: bool, price: int) -> HBoxContainer:
|
||||||
|
var row := HBoxContainer.new()
|
||||||
|
row.add_theme_constant_override("separation", 4)
|
||||||
|
var icon := TextureRect.new()
|
||||||
|
icon.texture = load("res://assets/icons/lucide/star.svg")
|
||||||
|
icon.custom_minimum_size = Vector2(14, 14)
|
||||||
|
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
|
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||||
|
icon.modulate = Brand.AMBER
|
||||||
|
row.add_child(icon)
|
||||||
|
var label := Label.new()
|
||||||
|
label.text = _format_thousands(price)
|
||||||
|
label.add_theme_font_override("font", Brand.font)
|
||||||
|
label.add_theme_font_size_override("font_size", 14)
|
||||||
|
label.add_theme_color_override("font_color", Brand.text_color(dark))
|
||||||
|
row.add_child(label)
|
||||||
|
return row
|
||||||
|
|
||||||
|
|
||||||
|
func _style_buy_button(button: 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)
|
||||||
|
button.add_theme_stylebox_override("normal", normal)
|
||||||
|
button.add_theme_stylebox_override("hover", hover)
|
||||||
|
button.add_theme_stylebox_override("pressed", normal)
|
||||||
|
button.add_theme_stylebox_override("disabled", disabled)
|
||||||
|
button.add_theme_color_override("font_color", Brand.DARK_TEXT)
|
||||||
|
button.add_theme_color_override("font_hover_color", Brand.DARK_TEXT)
|
||||||
|
button.add_theme_color_override("font_disabled_color", Color(Brand.DARK_TEXT, 0.7))
|
||||||
|
|
||||||
|
|
||||||
func _ask_purchase(sku: String, product_name: String, price: int) -> void:
|
func _ask_purchase(sku: String, product_name: String, price: int) -> void:
|
||||||
@@ -114,8 +388,22 @@ func _ask_reset() -> void:
|
|||||||
|
|
||||||
func _show_error(message: String) -> void:
|
func _show_error(message: String) -> void:
|
||||||
status_label.text = message
|
status_label.text = message
|
||||||
|
status_label.add_theme_color_override("font_color", Brand.MAGENTA)
|
||||||
|
|
||||||
|
|
||||||
func _show_message(message: String) -> void:
|
func _show_message(message: String) -> void:
|
||||||
status_label.text = message
|
status_label.text = message
|
||||||
|
status_label.add_theme_color_override("font_color", Brand.SUCCESS)
|
||||||
_refresh()
|
_refresh()
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user