forked from xiangwang25/whale-town-front-v2
Initial WhaleTown V2 frontend
This commit is contained in:
177
scenes/ui/mall/MallItemCard.gd
Normal file
177
scenes/ui/mall/MallItemCard.gd
Normal file
@@ -0,0 +1,177 @@
|
||||
class_name MallItemCard
|
||||
extends Button
|
||||
|
||||
# ============================================================================
|
||||
# MallItemCard.gd - 商城商品卡片
|
||||
# ============================================================================
|
||||
# 展示商品槽位、状态、价格和选中态,由 MallPanel 统一驱动。
|
||||
# ============================================================================
|
||||
|
||||
signal item_selected(item: Dictionary)
|
||||
|
||||
const TEXT_COLOR: Color = Color(0.111, 0.231, 0.380)
|
||||
const MUTED_COLOR: Color = Color(0.486, 0.584, 0.694)
|
||||
const ACCENT_COLOR: Color = Color(0.086, 0.608, 0.922)
|
||||
const LOCKED_COLOR: Color = Color(0.455, 0.510, 0.584)
|
||||
const COIN_TEXTURE: Texture2D = preload("res://assets/ui/mall/branding/branding_whale_coin.png")
|
||||
const CATALOG_SCRIPT: Script = preload("res://Config/MallCatalog.gd")
|
||||
|
||||
var itemData: Dictionary = {}
|
||||
var isSelected: bool = false
|
||||
|
||||
var _background: PanelContainer
|
||||
var _icon: TextureRect
|
||||
var _nameLabel: Label
|
||||
var _statusLabel: Label
|
||||
var _priceLabel: Label
|
||||
var _lockedOverlay: ColorRect
|
||||
|
||||
func _ready() -> void:
|
||||
focus_mode = Control.FOCUS_NONE
|
||||
mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
|
||||
custom_minimum_size = Vector2(218, 288)
|
||||
add_theme_stylebox_override("normal", StyleBoxEmpty.new())
|
||||
add_theme_stylebox_override("hover", StyleBoxEmpty.new())
|
||||
add_theme_stylebox_override("pressed", StyleBoxEmpty.new())
|
||||
add_theme_stylebox_override("focus", StyleBoxEmpty.new())
|
||||
_build_ui()
|
||||
pressed.connect(_on_pressed)
|
||||
|
||||
func setup(data: Dictionary, selected: bool) -> void:
|
||||
itemData = data.duplicate(true)
|
||||
isSelected = selected
|
||||
if not is_inside_tree():
|
||||
return
|
||||
_render()
|
||||
|
||||
func set_selected(selected: bool) -> void:
|
||||
isSelected = selected
|
||||
if is_inside_tree():
|
||||
_render()
|
||||
|
||||
func _build_ui() -> void:
|
||||
_background = PanelContainer.new()
|
||||
_background.name = "background"
|
||||
_background.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
_background.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add_child(_background)
|
||||
|
||||
var margin := MarginContainer.new()
|
||||
margin.name = "contentMargin"
|
||||
margin.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
margin.add_theme_constant_override("margin_left", 14)
|
||||
margin.add_theme_constant_override("margin_top", 14)
|
||||
margin.add_theme_constant_override("margin_right", 14)
|
||||
margin.add_theme_constant_override("margin_bottom", 14)
|
||||
margin.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add_child(margin)
|
||||
|
||||
var content := VBoxContainer.new()
|
||||
content.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
content.add_theme_constant_override("separation", 7)
|
||||
content.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
margin.add_child(content)
|
||||
|
||||
_statusLabel = Label.new()
|
||||
_statusLabel.custom_minimum_size = Vector2(0, 24)
|
||||
_statusLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||
_statusLabel.add_theme_font_size_override("font_size", 15)
|
||||
_statusLabel.add_theme_color_override("font_color", MUTED_COLOR)
|
||||
content.add_child(_statusLabel)
|
||||
|
||||
_icon = TextureRect.new()
|
||||
_icon.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR
|
||||
_icon.custom_minimum_size = Vector2(152, 134)
|
||||
_icon.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL
|
||||
_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
_icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
content.add_child(_icon)
|
||||
|
||||
_nameLabel = Label.new()
|
||||
_nameLabel.custom_minimum_size = Vector2(0, 48)
|
||||
_nameLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
_nameLabel.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
_nameLabel.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
_nameLabel.add_theme_font_size_override("font_size", 19)
|
||||
_nameLabel.add_theme_color_override("font_color", TEXT_COLOR)
|
||||
content.add_child(_nameLabel)
|
||||
|
||||
var priceRow := HBoxContainer.new()
|
||||
priceRow.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
priceRow.add_theme_constant_override("separation", 4)
|
||||
priceRow.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
content.add_child(priceRow)
|
||||
|
||||
var coin := TextureRect.new()
|
||||
coin.texture = COIN_TEXTURE
|
||||
coin.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR
|
||||
coin.custom_minimum_size = Vector2(28, 28)
|
||||
coin.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL
|
||||
coin.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
coin.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
priceRow.add_child(coin)
|
||||
|
||||
_priceLabel = Label.new()
|
||||
_priceLabel.add_theme_font_size_override("font_size", 18)
|
||||
_priceLabel.add_theme_color_override("font_color", MUTED_COLOR)
|
||||
priceRow.add_child(_priceLabel)
|
||||
|
||||
_lockedOverlay = ColorRect.new()
|
||||
_lockedOverlay.name = "lockedOverlay"
|
||||
_lockedOverlay.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
_lockedOverlay.color = Color(0.350, 0.420, 0.510, 0.18)
|
||||
_lockedOverlay.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add_child(_lockedOverlay)
|
||||
|
||||
_render()
|
||||
|
||||
func _render() -> void:
|
||||
_background.add_theme_stylebox_override("panel", _create_card_style(isSelected))
|
||||
var status := str(itemData.get("status", CATALOG_SCRIPT.STATUS_COMING_SOON))
|
||||
var iconPath := str(itemData.get("icon", ""))
|
||||
_icon.texture = _load_texture(iconPath)
|
||||
_nameLabel.text = str(itemData.get("name", "商品槽位"))
|
||||
_statusLabel.text = CATALOG_SCRIPT.get_status_label(status)
|
||||
_priceLabel.text = _format_price(itemData)
|
||||
_lockedOverlay.visible = status == CATALOG_SCRIPT.STATUS_COMING_SOON or status == CATALOG_SCRIPT.STATUS_LOCKED
|
||||
_statusLabel.add_theme_color_override("font_color", ACCENT_COLOR if status == CATALOG_SCRIPT.STATUS_AVAILABLE else LOCKED_COLOR)
|
||||
|
||||
func _create_card_style(selected: bool) -> StyleBoxFlat:
|
||||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = Color(0.982, 0.995, 1.0, 1.0)
|
||||
style.corner_radius_top_left = 18
|
||||
style.corner_radius_top_right = 18
|
||||
style.corner_radius_bottom_left = 18
|
||||
style.corner_radius_bottom_right = 18
|
||||
style.border_width_left = 2 if not selected else 4
|
||||
style.border_width_top = 2 if not selected else 4
|
||||
style.border_width_right = 2 if not selected else 4
|
||||
style.border_width_bottom = 2 if not selected else 4
|
||||
style.border_color = Color(0.658, 0.835, 0.965, 0.86) if not selected else Color(0.086, 0.690, 0.960, 1.0)
|
||||
style.shadow_color = Color(0.086, 0.314, 0.520, 0.15 if selected else 0.08)
|
||||
style.shadow_size = 14 if selected else 8
|
||||
style.shadow_offset = Vector2(0, 4)
|
||||
return style
|
||||
|
||||
func _format_price(data: Dictionary) -> String:
|
||||
var status := str(data.get("status", ""))
|
||||
if status == CATALOG_SCRIPT.STATUS_OWNED:
|
||||
return "已购买"
|
||||
if status == CATALOG_SCRIPT.STATUS_COMING_SOON or status == CATALOG_SCRIPT.STATUS_LOCKED:
|
||||
return "暂未开放"
|
||||
return str(int(data.get("price", 0)))
|
||||
|
||||
func _load_texture(path: String) -> Texture2D:
|
||||
if path.is_empty():
|
||||
return null
|
||||
if FileAccess.file_exists("%s.import" % path):
|
||||
var texture := load(path) as Texture2D
|
||||
if texture != null:
|
||||
return texture
|
||||
var image := Image.load_from_file(ProjectSettings.globalize_path(path))
|
||||
if image == null or image.is_empty():
|
||||
return null
|
||||
return ImageTexture.create_from_image(image)
|
||||
|
||||
func _on_pressed() -> void:
|
||||
item_selected.emit(itemData)
|
||||
1
scenes/ui/mall/MallItemCard.gd.uid
Normal file
1
scenes/ui/mall/MallItemCard.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://buh0lhtug6bw8
|
||||
1211
scenes/ui/mall/MallPanel.gd
Normal file
1211
scenes/ui/mall/MallPanel.gd
Normal file
File diff suppressed because it is too large
Load Diff
1
scenes/ui/mall/MallPanel.gd.uid
Normal file
1
scenes/ui/mall/MallPanel.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bej4ywlrtg1tb
|
||||
13
scenes/ui/mall/MallPanel.tscn
Normal file
13
scenes/ui/mall/MallPanel.tscn
Normal file
@@ -0,0 +1,13 @@
|
||||
[gd_scene load_steps=2 format=4 uid="uid://whaletown_mall_panel"]
|
||||
|
||||
[ext_resource type="Script" path="res://scenes/ui/mall/MallPanel.gd" id="1_mall_panel"]
|
||||
|
||||
[node name="MallPanel" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
script = ExtResource("1_mall_panel")
|
||||
Reference in New Issue
Block a user