Add tests for scenery surface caching and preview behavior
- Introduced `test_scenery_brick_preview.gd` to validate focused placement and CPU draw-cost for large brick walls. - Added `test_scenery_surface_cache.gd` to assess texture budget, visual equivalence, and cache invalidation. - Created `test_scenery_surface_preview.gd` to evaluate adaptive preview selection and placement behavior. - Implemented various helper functions to measure drawing performance and validate expected behaviors during tests. - Added unique identifiers for new test files to ensure proper tracking and management.
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
class_name StageDepthChoicePreview
|
||||
extends Control
|
||||
## Small fixed-size diagram showing how scenery depth changes draw order.
|
||||
|
||||
const DEPTH_FAR := &"far"
|
||||
const DEPTH_NEAR := &"near"
|
||||
const DEPTH_FLOOR := &"floor"
|
||||
|
||||
var depth_id: StringName = DEPTH_NEAR
|
||||
var artwork_texture: Texture2D
|
||||
|
||||
|
||||
func configure(depth: StringName, texture: Texture2D, preview_size: Vector2) -> void:
|
||||
depth_id = depth if depth in [DEPTH_FAR, DEPTH_NEAR, DEPTH_FLOOR] else DEPTH_NEAR
|
||||
artwork_texture = texture
|
||||
custom_minimum_size = preview_size
|
||||
size_flags_horizontal = Control.SIZE_SHRINK_CENTER
|
||||
size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
clip_contents = true
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func set_artwork_texture(texture: Texture2D) -> void:
|
||||
artwork_texture = texture
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
var bounds := Rect2(Vector2.ZERO, size)
|
||||
draw_rect(bounds, Color("#B9D9D9"), true)
|
||||
_draw_hills()
|
||||
if depth_id != DEPTH_FAR:
|
||||
_draw_near_scenery()
|
||||
if depth_id == DEPTH_FLOOR:
|
||||
_draw_ground_tiles()
|
||||
_draw_artwork()
|
||||
if depth_id == DEPTH_FAR:
|
||||
_draw_near_scenery()
|
||||
if depth_id != DEPTH_FLOOR:
|
||||
_draw_ground_tiles()
|
||||
draw_rect(bounds, Color("#566273"), false, 1.0)
|
||||
|
||||
|
||||
func _draw_hills() -> void:
|
||||
var points := PackedVector2Array([
|
||||
Vector2(0.0, size.y * 0.68),
|
||||
Vector2(0.0, size.y * 0.52),
|
||||
Vector2(size.x * 0.18, size.y * 0.39),
|
||||
Vector2(size.x * 0.38, size.y * 0.54),
|
||||
Vector2(size.x * 0.62, size.y * 0.36),
|
||||
Vector2(size.x * 0.84, size.y * 0.49),
|
||||
Vector2(size.x, size.y * 0.41),
|
||||
Vector2(size.x, size.y * 0.68),
|
||||
])
|
||||
draw_colored_polygon(points, Color("#79AFA4"))
|
||||
|
||||
|
||||
func _draw_near_scenery() -> void:
|
||||
var canopy := Color("#277447")
|
||||
var trunk := Color("#84502B")
|
||||
var canopy_center := Vector2(size.x * 0.49, size.y * 0.56)
|
||||
var radius := maxf(4.0, size.y * 0.25)
|
||||
draw_rect(Rect2(Vector2(size.x * 0.49 - 2.0, size.y * 0.46), Vector2(4.0, size.y * 0.23)), trunk, true)
|
||||
draw_circle(canopy_center, radius, canopy)
|
||||
draw_circle(canopy_center + Vector2(-radius * 0.52, radius * 0.08), radius * 0.62, Color("#48A84E"))
|
||||
draw_circle(canopy_center + Vector2(radius * 0.48, radius * 0.02), radius * 0.55, Color("#36934A"))
|
||||
|
||||
|
||||
func _draw_ground_tiles() -> void:
|
||||
var top: float = size.y * 0.64
|
||||
var tile_height: float = size.y - top
|
||||
var tile_count: int = 5
|
||||
var tile_width: float = size.x / float(tile_count)
|
||||
for index: int in tile_count:
|
||||
var tile := Rect2(Vector2(float(index) * tile_width, top), Vector2(tile_width + 0.5, tile_height))
|
||||
draw_rect(tile, Color("#87512C"), true)
|
||||
draw_rect(tile, Color("#A96C3B"), false, 1.0)
|
||||
draw_line(Vector2(float(index) * tile_width + 2.0, top + tile_height * 0.58), Vector2(float(index + 1) * tile_width - 2.0, top + tile_height * 0.58), Color("#C9894B"), 1.0)
|
||||
draw_rect(Rect2(Vector2(0.0, top - 3.0), Vector2(size.x, 6.0)), Color("#62D34D"), true)
|
||||
draw_line(Vector2(0.0, top - 3.0), Vector2(size.x, top - 3.0), Color("#D5F27B"), 1.0)
|
||||
|
||||
|
||||
func _draw_artwork() -> void:
|
||||
if artwork_texture == null:
|
||||
var center := Vector2(size.x * 0.49, size.y * 0.59)
|
||||
draw_circle(center, maxf(3.0, minf(size.x, size.y) * 0.20), Color("#F4F1E8"))
|
||||
draw_circle(center, maxf(3.0, minf(size.x, size.y) * 0.20), Color("#58616D"), false, 1.0)
|
||||
return
|
||||
var texture_size := Vector2(artwork_texture.get_size())
|
||||
if texture_size.x <= 0.0 or texture_size.y <= 0.0:
|
||||
return
|
||||
var available := Vector2(size.x * 0.84, size.y * 0.84)
|
||||
var scale_factor: float = minf(available.x / texture_size.x, available.y / texture_size.y)
|
||||
var target_size := texture_size * scale_factor
|
||||
var target := Rect2(Vector2((size.x - target_size.x) * 0.5, size.y * 0.59 - target_size.y * 0.5), target_size)
|
||||
draw_texture_rect(artwork_texture, target, false)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cc5hn0jge7758
|
||||
@@ -116,6 +116,52 @@ func style_action_button(button: Button, prominent: bool = false) -> void:
|
||||
_style_action_button(button, prominent)
|
||||
|
||||
|
||||
func style_primary_action_button(button: Button) -> void:
|
||||
if button == null:
|
||||
return
|
||||
var primary := _color("stage_panels.action.primary_color", Color("#087DBA"))
|
||||
var hover := _color("stage_panels.action.primary_hover_color", Color("#1596D3"))
|
||||
var pressed := _color("stage_panels.action.primary_pressed_color", Color("#066B9F"))
|
||||
var accent := _color("mode_colors.edit", Color("#22C6FF"))
|
||||
button.add_theme_stylebox_override("normal", _make_style(primary, accent.lightened(0.15), 2, 10, 6))
|
||||
button.add_theme_stylebox_override("hover", _make_style(hover, accent, 2, 10, 6))
|
||||
button.add_theme_stylebox_override("pressed", _make_style(pressed, accent, 2, 10, 6))
|
||||
button.add_theme_stylebox_override("hover_pressed", _make_style(pressed.lightened(0.05), accent, 2, 10, 6))
|
||||
button.add_theme_stylebox_override("disabled", _make_style(_color("stage_panels.choice.disabled_surface", Color("#252B38")), _color("global.border_color", Color("#FFFFFF33")), 1, 10, 6))
|
||||
button.add_theme_stylebox_override("focus", _make_style(Color(0, 0, 0, 0), accent, 2, 10, 6))
|
||||
button.add_theme_font_size_override("font_size", _integer("stage_panels.type.body_size", 20, 14, 40))
|
||||
button.add_theme_color_override("font_color", Color.WHITE)
|
||||
button.add_theme_color_override("font_hover_color", Color.WHITE)
|
||||
button.add_theme_color_override("font_pressed_color", Color.WHITE)
|
||||
if _stage_theme != null:
|
||||
var ui_font := _stage_theme.font()
|
||||
if ui_font != null:
|
||||
button.add_theme_font_override("font", ui_font)
|
||||
|
||||
|
||||
func style_destructive_button(button: Button) -> void:
|
||||
if button == null:
|
||||
return
|
||||
var danger := _color("stage_panels.action.destructive_color", Color("#FF626B"))
|
||||
var neutral := _color("stage_panels.action.surface_color", Color("#171B24"))
|
||||
var hover := _color("stage_panels.action.destructive_hover_surface", Color("#3B252C"))
|
||||
var pressed := _color("stage_panels.action.destructive_pressed_surface", Color("#4A252D"))
|
||||
var border := _color("global.border_color", Color("#FFFFFF33"))
|
||||
var accent := _color("mode_colors.edit", Color("#22C6FF"))
|
||||
button.add_theme_stylebox_override("normal", _make_style(neutral, danger, 2, 10, 6))
|
||||
button.add_theme_stylebox_override("hover", _make_style(hover, danger, 2, 10, 6))
|
||||
button.add_theme_stylebox_override("pressed", _make_style(pressed, danger, 2, 10, 6))
|
||||
button.add_theme_stylebox_override("hover_pressed", _make_style(pressed.lightened(0.06), danger, 2, 10, 6))
|
||||
button.add_theme_stylebox_override("disabled", _make_style(_color("stage_panels.choice.disabled_surface", Color("#252B38")), border, 1, 10, 6))
|
||||
button.add_theme_stylebox_override("focus", _make_style(Color(0, 0, 0, 0), accent, 2, 10, 6))
|
||||
button.add_theme_font_size_override("font_size", _integer("stage_panels.type.body_size", 20, 14, 40))
|
||||
button.add_theme_color_override("font_color", _color("stage_panels.type.text_color", Color("#F5F7FA")))
|
||||
if _stage_theme != null:
|
||||
var ui_font := _stage_theme.font()
|
||||
if ui_font != null:
|
||||
button.add_theme_font_override("font", ui_font)
|
||||
|
||||
|
||||
func _build_shell() -> void:
|
||||
if _body != null:
|
||||
return
|
||||
@@ -145,11 +191,12 @@ func _build_shell() -> void:
|
||||
_title.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
_header.add_child(_title)
|
||||
_close_button = Button.new()
|
||||
_close_button.text = "✕ Close"
|
||||
_close_button.text = "Close"
|
||||
_close_button.custom_minimum_size = Vector2(
|
||||
float(_integer("stage_panels.action.close_width", 88, 72, 180)),
|
||||
float(_integer("stage_panels.action.min_target_size", 48, 44, 96))
|
||||
)
|
||||
_close_button.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
|
||||
_close_button.focus_mode = Control.FOCUS_ALL
|
||||
_close_button.pressed.connect(func() -> void: close_requested.emit())
|
||||
_header.add_child(_close_button)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class_name StagePanelChoiceGroup
|
||||
extends VBoxContainer
|
||||
extends BoxContainer
|
||||
## Exclusive, full-width choices with optional decorative cues.
|
||||
|
||||
signal choice_selected(id: StringName)
|
||||
@@ -8,20 +8,43 @@ var _stage_theme: SandboxTheme = null
|
||||
var _group := ButtonGroup.new()
|
||||
var _buttons: Dictionary = {}
|
||||
var _selected_id: StringName = &""
|
||||
var _vertical_layout: bool = true
|
||||
var _minimum_height_override: float = -1.0
|
||||
var _font_size_override: int = -1
|
||||
var _corner_radius_override: int = -1
|
||||
var _border_width_override: int = -1
|
||||
var _selected_surface_override: Color = Color.TRANSPARENT
|
||||
var _has_selected_surface_override: bool = false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
vertical = _vertical_layout
|
||||
_group.allow_unpress = true
|
||||
add_theme_constant_override("separation", _integer("stage_panels.choice.row_gap", 6, 0, 32))
|
||||
|
||||
|
||||
func add_choice(id: StringName, caption: String, cue: Control = null) -> Button:
|
||||
func configure_layout(vertical_layout: bool, minimum_height: float = -1.0, font_size: int = -1, corner_radius: int = -1, border_width: int = -1, selected_surface: Color = Color.TRANSPARENT) -> void:
|
||||
_vertical_layout = vertical_layout
|
||||
vertical = vertical_layout
|
||||
_minimum_height_override = minimum_height
|
||||
_font_size_override = font_size
|
||||
_corner_radius_override = corner_radius
|
||||
_border_width_override = border_width
|
||||
_selected_surface_override = selected_surface
|
||||
_has_selected_surface_override = selected_surface.a > 0.0
|
||||
for entry: Dictionary in _buttons.values():
|
||||
_apply_choice_theme(entry["button"] as Button, entry["label"] as Label, entry.get("detail") as Label)
|
||||
|
||||
|
||||
func add_choice(id: StringName, caption: String, cue: Control = null, detail: String = "") -> Button:
|
||||
var button := Button.new()
|
||||
button.name = "Choice_%s" % String(id)
|
||||
button.toggle_mode = true
|
||||
button.button_group = _group
|
||||
button.custom_minimum_size.y = float(_integer("stage_panels.choice.minimum_height", 56, 44, 112))
|
||||
button.custom_minimum_size.y = _minimum_height_override if _minimum_height_override > 0.0 else float(_integer("stage_panels.choice.minimum_height", 56, 44, 112))
|
||||
button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
if not _vertical_layout:
|
||||
button.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
button.focus_mode = Control.FOCUS_ALL
|
||||
button.text = ""
|
||||
var margin := MarginContainer.new()
|
||||
@@ -42,16 +65,31 @@ func add_choice(id: StringName, caption: String, cue: Control = null) -> Button:
|
||||
var label := Label.new()
|
||||
label.text = caption
|
||||
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT if not detail.is_empty() else HORIZONTAL_ALIGNMENT_CENTER
|
||||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
row.add_child(label)
|
||||
var detail_label: Label
|
||||
if detail.is_empty():
|
||||
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
row.add_child(label)
|
||||
else:
|
||||
var text_stack := VBoxContainer.new()
|
||||
text_stack.add_theme_constant_override("separation", 0)
|
||||
text_stack.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
text_stack.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
text_stack.add_child(label)
|
||||
detail_label = Label.new()
|
||||
detail_label.text = detail
|
||||
detail_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
detail_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
detail_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
text_stack.add_child(detail_label)
|
||||
row.add_child(text_stack)
|
||||
button.pressed.connect(_on_choice_pressed.bind(id, button))
|
||||
add_child(button)
|
||||
_buttons[id] = {"button": button, "label": label}
|
||||
_buttons[id] = {"button": button, "label": label, "detail": detail_label}
|
||||
if _stage_theme != null:
|
||||
_apply_choice_theme(button, label)
|
||||
_apply_choice_theme(button, label, detail_label)
|
||||
return button
|
||||
|
||||
|
||||
@@ -75,7 +113,7 @@ func apply_theme(stage_theme: SandboxTheme) -> void:
|
||||
_stage_theme = stage_theme
|
||||
add_theme_constant_override("separation", _integer("stage_panels.choice.row_gap", 6, 0, 32))
|
||||
for entry: Dictionary in _buttons.values():
|
||||
_apply_choice_theme(entry["button"] as Button, entry["label"] as Label)
|
||||
_apply_choice_theme(entry["button"] as Button, entry["label"] as Label, entry.get("detail") as Label)
|
||||
|
||||
|
||||
func _on_choice_pressed(id: StringName, button: Button) -> void:
|
||||
@@ -87,26 +125,40 @@ func _on_choice_pressed(id: StringName, button: Button) -> void:
|
||||
choice_selected.emit(id)
|
||||
|
||||
|
||||
func _apply_choice_theme(button: Button, label: Label) -> void:
|
||||
func _apply_choice_theme(button: Button, label: Label, detail: Label = null) -> void:
|
||||
if _stage_theme == null:
|
||||
return
|
||||
var font := _stage_theme.font()
|
||||
if font != null:
|
||||
label.add_theme_font_override("font", font)
|
||||
label.add_theme_font_size_override("font_size", _integer("stage_panels.type.body_size", 20, 14, 40))
|
||||
if not _vertical_layout:
|
||||
var strong_font := FontVariation.new()
|
||||
strong_font.base_font = font
|
||||
strong_font.variation_embolden = 0.25
|
||||
label.add_theme_font_override("font", strong_font)
|
||||
else:
|
||||
label.add_theme_font_override("font", font)
|
||||
var font_size: int = _font_size_override if _font_size_override > 0 else _integer("stage_panels.type.body_size", 20, 14, 40)
|
||||
label.add_theme_font_size_override("font_size", font_size)
|
||||
label.add_theme_color_override("font_color", _color("stage_panels.type.text_color", Color("#F5F7FA")))
|
||||
if detail != null:
|
||||
if font != null:
|
||||
detail.add_theme_font_override("font", font)
|
||||
detail.add_theme_font_size_override("font_size", maxi(12, font_size - 4))
|
||||
detail.add_theme_color_override("font_color", _color("stage_panels.type.muted_text_color", Color("#AAB2C0")))
|
||||
var neutral := _color("stage_panels.choice.neutral_surface", Color("#171B24"))
|
||||
var hover := _color("stage_panels.choice.hover_surface", Color("#303A4B"))
|
||||
var selected := _color("stage_panels.choice.selected_surface", Color("#12394A"))
|
||||
var selected := _selected_surface_override if _has_selected_surface_override else _color("stage_panels.choice.selected_surface", Color("#12394A"))
|
||||
var disabled := _color("stage_panels.choice.disabled_surface", Color("#252B38"))
|
||||
var border := _color("stage_panels.choice.border_color", Color("#FFFFFF33"))
|
||||
var accent := _color("mode_colors.edit", Color("#22C6FF"))
|
||||
button.add_theme_stylebox_override("normal", _make_style(neutral, border, 1))
|
||||
button.add_theme_stylebox_override("hover", _make_style(hover, border, 1))
|
||||
button.add_theme_stylebox_override("pressed", _make_style(selected, accent, 2))
|
||||
button.add_theme_stylebox_override("hover_pressed", _make_style(selected.lightened(0.06), accent, 2))
|
||||
button.add_theme_stylebox_override("disabled", _make_style(disabled, border, 1))
|
||||
button.add_theme_stylebox_override("focus", _make_style(Color(0, 0, 0, 0), accent, 2))
|
||||
var border_width: int = _border_width_override if _border_width_override >= 0 else 1
|
||||
var selected_border_width: int = border_width + 1 if _border_width_override >= 0 else 2
|
||||
button.add_theme_stylebox_override("normal", _make_style(neutral, border, border_width))
|
||||
button.add_theme_stylebox_override("hover", _make_style(hover, border, border_width))
|
||||
button.add_theme_stylebox_override("pressed", _make_style(selected, accent, selected_border_width))
|
||||
button.add_theme_stylebox_override("hover_pressed", _make_style(selected.lightened(0.06), accent, selected_border_width))
|
||||
button.add_theme_stylebox_override("disabled", _make_style(disabled, border, border_width))
|
||||
button.add_theme_stylebox_override("focus", _make_style(Color(0, 0, 0, 0), accent, selected_border_width))
|
||||
|
||||
|
||||
func _make_style(bg: Color, border: Color, width: int) -> StyleBoxFlat:
|
||||
@@ -114,7 +166,8 @@ func _make_style(bg: Color, border: Color, width: int) -> StyleBoxFlat:
|
||||
style.bg_color = bg
|
||||
style.border_color = border
|
||||
style.set_border_width_all(width)
|
||||
style.set_corner_radius_all(_integer("stage_panels.shell.corner_radius", 10, 0, 32))
|
||||
var radius: int = _corner_radius_override if _corner_radius_override >= 0 else _integer("stage_panels.shell.corner_radius", 10, 0, 32)
|
||||
style.set_corner_radius_all(radius)
|
||||
return style
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ var _current_value: Label
|
||||
var _recent_empty_label: Label
|
||||
var _recent_swatches: ColorSwatchSelector
|
||||
var _preset_swatches: ColorSwatchSelector
|
||||
var _more_button: Button
|
||||
var _compact_mode: bool = false
|
||||
|
||||
|
||||
@@ -54,60 +53,107 @@ func set_recent_colors(colors: Array[Color]) -> void:
|
||||
|
||||
|
||||
func focus_custom_button() -> void:
|
||||
if _more_button != null:
|
||||
_more_button.grab_focus()
|
||||
if _current_chip != null:
|
||||
_current_chip.grab_focus()
|
||||
|
||||
|
||||
func _build() -> void:
|
||||
_built = true
|
||||
_apply_compact_layout()
|
||||
var current_heading: Label = _make_heading("Color")
|
||||
add_child(current_heading)
|
||||
var current_parent: Control = self
|
||||
var recent_parent: Control = self
|
||||
var presets_parent: Control = self
|
||||
if _compact_mode:
|
||||
var row := HBoxContainer.new()
|
||||
row.add_theme_constant_override("separation", _integer("popups.asset_selector.color_group_gap", 8, 4, 32))
|
||||
row.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
add_child(row)
|
||||
current_parent = _make_compact_section(132.0)
|
||||
row.add_child(current_parent)
|
||||
row.add_child(_make_compact_separator())
|
||||
recent_parent = _make_compact_section(92.0)
|
||||
row.add_child(recent_parent)
|
||||
row.add_child(_make_compact_separator())
|
||||
presets_parent = _make_compact_section(180.0)
|
||||
presets_parent.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
row.add_child(presets_parent)
|
||||
_build_current_color_section(current_parent)
|
||||
_build_recent_color_section(recent_parent)
|
||||
_build_palette_section(presets_parent)
|
||||
if _stage_theme != null:
|
||||
var font: Font = _stage_theme.font()
|
||||
if font != null:
|
||||
if _compact_mode:
|
||||
var strong_font := FontVariation.new()
|
||||
strong_font.base_font = font
|
||||
strong_font.variation_embolden = 0.2
|
||||
_current_value.add_theme_font_override("font", strong_font)
|
||||
else:
|
||||
_current_value.add_theme_font_override("font", font)
|
||||
_current_value.add_theme_font_size_override("font_size", _integer("stage_panels.type.body_size", 20, 14, 40))
|
||||
|
||||
|
||||
func _build_current_color_section(parent: Control) -> void:
|
||||
parent.add_child(_make_heading("Color"))
|
||||
var current_row := HBoxContainer.new()
|
||||
current_row.add_theme_constant_override("separation", _compact_gap())
|
||||
add_child(current_row)
|
||||
parent.add_child(current_row)
|
||||
_current_chip = Button.new()
|
||||
var swatch_size: float = float(_integer("stage_surface_colors.swatch_size", 48, 44, 96))
|
||||
if _compact_mode:
|
||||
swatch_size = minf(swatch_size, 44.0)
|
||||
swatch_size = maxf(swatch_size, 52.0)
|
||||
_current_chip.custom_minimum_size = Vector2(swatch_size, swatch_size)
|
||||
_current_chip.focus_mode = Control.FOCUS_NONE
|
||||
_current_chip.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_current_chip.focus_mode = Control.FOCUS_ALL
|
||||
_current_chip.tooltip_text = "Choose a custom color"
|
||||
_current_chip.pressed.connect(func() -> void: custom_color_requested.emit())
|
||||
current_row.add_child(_current_chip)
|
||||
_current_value = Label.new()
|
||||
_current_value.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_current_value.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
current_row.add_child(_current_value)
|
||||
add_child(_make_heading("Recently used"))
|
||||
|
||||
|
||||
func _build_recent_color_section(parent: Control) -> void:
|
||||
parent.add_child(_make_heading("Recently used"))
|
||||
_recent_empty_label = Label.new()
|
||||
_recent_empty_label.text = "Colors you use will appear here"
|
||||
_recent_empty_label.text = "No colors yet" if _compact_mode else "Colors you use will appear here"
|
||||
_recent_empty_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
_recent_empty_label.add_theme_color_override("font_color", _color("global.muted_text_color", Color("#AAB2C0")))
|
||||
add_child(_recent_empty_label)
|
||||
if _compact_mode:
|
||||
_recent_empty_label.add_theme_font_size_override("font_size", _integer("stage_panels.type.helper_size", 16, 12, 32))
|
||||
parent.add_child(_recent_empty_label)
|
||||
_recent_swatches = COLOR_SWATCH_SELECTOR.new() as ColorSwatchSelector
|
||||
_recent_swatches.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
if _compact_mode:
|
||||
_recent_swatches.set_swatch_layout_override(_integer("popups.asset_selector.compact_swatch_size", 42, 28, 52), 8)
|
||||
_recent_swatches.color_selected.connect(_on_swatch_selected)
|
||||
add_child(_recent_swatches)
|
||||
add_child(_make_heading("Colors"))
|
||||
parent.add_child(_recent_swatches)
|
||||
|
||||
|
||||
func _build_palette_section(parent: Control) -> void:
|
||||
parent.add_child(_make_heading("Colors"))
|
||||
_preset_swatches = COLOR_SWATCH_SELECTOR.new() as ColorSwatchSelector
|
||||
_preset_swatches.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
if _compact_mode:
|
||||
_preset_swatches.set_swatch_layout_override(_integer("popups.asset_selector.compact_swatch_size", 42, 28, 52), 8)
|
||||
_preset_swatches.color_selected.connect(_on_swatch_selected)
|
||||
add_child(_preset_swatches)
|
||||
_more_button = Button.new()
|
||||
_more_button.text = "More colors…"
|
||||
var action_height: float = float(_integer("stage_panels.action.min_target_size", 48, 44, 96))
|
||||
_more_button.custom_minimum_size.y = minf(action_height, 44.0) if _compact_mode else action_height
|
||||
_more_button.focus_mode = Control.FOCUS_ALL
|
||||
_more_button.pressed.connect(func() -> void: custom_color_requested.emit())
|
||||
add_child(_more_button)
|
||||
if _stage_theme != null:
|
||||
var font: Font = _stage_theme.font()
|
||||
if font != null:
|
||||
_current_value.add_theme_font_override("font", font)
|
||||
_more_button.add_theme_font_override("font", font)
|
||||
_current_value.add_theme_font_size_override("font_size", _integer("stage_panels.type.body_size", 20, 14, 40))
|
||||
_more_button.add_theme_font_size_override("font_size", _integer("stage_panels.type.body_size", 20, 14, 40))
|
||||
_more_button.add_theme_color_override("font_color", _color("global.text_color", Color("#F5F7FA")))
|
||||
_more_button.add_theme_stylebox_override("normal", _stage_theme.stylebox("stage_panels.action"))
|
||||
_more_button.add_theme_stylebox_override("hover", _stage_theme.stylebox("stage_panels.action").duplicate())
|
||||
parent.add_child(_preset_swatches)
|
||||
|
||||
|
||||
func _make_compact_section(minimum_width: float) -> VBoxContainer:
|
||||
var section := VBoxContainer.new()
|
||||
section.custom_minimum_size.x = minimum_width
|
||||
section.add_theme_constant_override("separation", 4)
|
||||
return section
|
||||
|
||||
|
||||
func _make_compact_separator() -> ColorRect:
|
||||
var separator := ColorRect.new()
|
||||
separator.color = _color("popups.asset_selector.separator_color", Color("#AAB2C0A0"))
|
||||
separator.custom_minimum_size = Vector2(2.0, 64.0)
|
||||
separator.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
separator.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
return separator
|
||||
|
||||
|
||||
func _refresh_visuals() -> void:
|
||||
@@ -124,11 +170,16 @@ func _refresh_current_color() -> void:
|
||||
_current_value.text = "#%s" % _selected.to_html(false).to_upper()
|
||||
var chip_style := StyleBoxFlat.new()
|
||||
chip_style.bg_color = _selected
|
||||
chip_style.border_color = _color("stage_panels.choice.border_color", Color("#FFFFFFAA"))
|
||||
chip_style.set_border_width_all(_integer("stage_surface_colors.swatch_border_width", 2, 0, 8))
|
||||
chip_style.border_color = _color("mode_colors.edit", Color("#22C6FF"))
|
||||
chip_style.set_border_width_all(_integer("stage_surface_colors.swatch_border_width", 3, 1, 8))
|
||||
chip_style.set_corner_radius_all(_integer("stage_surface_colors.swatch_corner_radius", 8, 0, 24))
|
||||
_current_chip.add_theme_stylebox_override("normal", chip_style)
|
||||
_current_chip.add_theme_stylebox_override("hover", chip_style.duplicate())
|
||||
_current_chip.add_theme_stylebox_override("pressed", chip_style.duplicate())
|
||||
var focus_style: StyleBoxFlat = chip_style.duplicate() as StyleBoxFlat
|
||||
focus_style.border_color = Color.WHITE
|
||||
focus_style.set_border_width_all(5)
|
||||
_current_chip.add_theme_stylebox_override("focus", focus_style)
|
||||
|
||||
|
||||
func _on_swatch_selected(color: Color) -> void:
|
||||
@@ -143,18 +194,23 @@ func _make_heading(text_value: String) -> Label:
|
||||
var heading := Label.new()
|
||||
heading.text = text_value
|
||||
var heading_size: int = _integer("stage_panels.type.section_size", 20, 12, 40)
|
||||
if _compact_mode:
|
||||
heading_size = mini(heading_size, 18)
|
||||
heading.add_theme_font_size_override("font_size", heading_size)
|
||||
heading.add_theme_color_override("font_color", _color("stage_panels.type.text_color", Color("#F5F7FA")))
|
||||
if _stage_theme != null and _stage_theme.font() != null:
|
||||
heading.add_theme_font_override("font", _stage_theme.font())
|
||||
var font: Font = _stage_theme.font()
|
||||
if _compact_mode:
|
||||
var strong_font := FontVariation.new()
|
||||
strong_font.base_font = font
|
||||
strong_font.variation_embolden = 0.2
|
||||
heading.add_theme_font_override("font", strong_font)
|
||||
else:
|
||||
heading.add_theme_font_override("font", font)
|
||||
return heading
|
||||
|
||||
|
||||
func _apply_compact_layout() -> void:
|
||||
var section_gap: int = _integer("stage_surface_colors.section_gap", 8, 0, 28)
|
||||
add_theme_constant_override("separation", mini(section_gap, 4) if _compact_mode else section_gap)
|
||||
add_theme_constant_override("separation", section_gap)
|
||||
|
||||
|
||||
func _compact_gap() -> int:
|
||||
|
||||
@@ -1,22 +1,37 @@
|
||||
class_name StageSurfaceColorDialog
|
||||
extends Window
|
||||
## Bounded custom-color dialog with a live Stage surface preview.
|
||||
extends PopupPanel
|
||||
## Rounded, reusable Stage color popup with the shared surface picker view.
|
||||
|
||||
signal preview_color_changed(color: Color)
|
||||
signal color_confirmed(color: Color)
|
||||
signal cancelled
|
||||
|
||||
const SURFACE_PREVIEW := preload("res://scripts/stage/scenery/stage_surface_preview.gd")
|
||||
const SURFACE_COLOR_PICKER_VIEW := preload("res://scripts/shared/stage_ui/stage_surface_color_picker_view.gd")
|
||||
const COMPACT_LAYOUT_MAX_HEIGHT := 640
|
||||
|
||||
var _surface_kind: StringName = &"wall"
|
||||
var _finish_id: StringName = &"finish_wall_paint"
|
||||
var _initial_color: Color = Color.WHITE
|
||||
var _stage_theme: SandboxTheme
|
||||
var _color_picker: ColorPicker
|
||||
var _surface_preview: StageSurfacePreview
|
||||
var _margin: MarginContainer
|
||||
var _layout: VBoxContainer
|
||||
var _heading: Label
|
||||
var _view_scroll: ScrollContainer
|
||||
var _picker_view: StageSurfaceColorPickerView
|
||||
var _actions: HBoxContainer
|
||||
var _cancel_button: Button
|
||||
var _use_button: Button
|
||||
var _built: bool = false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
exclusive = true
|
||||
_build()
|
||||
close_requested.connect(_cancel)
|
||||
get_tree().root.size_changed.connect(_on_root_size_changed)
|
||||
_apply_visual_style()
|
||||
|
||||
|
||||
func configure(surface_kind: StringName, finish: StringName, color: Color, stage_theme: SandboxTheme) -> void:
|
||||
_surface_kind = surface_kind
|
||||
_finish_id = finish
|
||||
@@ -24,26 +39,20 @@ func configure(surface_kind: StringName, finish: StringName, color: Color, stage
|
||||
_stage_theme = stage_theme
|
||||
if not _built:
|
||||
_build()
|
||||
_color_picker.color = color
|
||||
_update_preview(color)
|
||||
title = "Choose a color"
|
||||
exclusive = true
|
||||
transient = true
|
||||
theme = stage_theme.control_theme() if stage_theme != null else null
|
||||
if _picker_view != null:
|
||||
_picker_view.configure(_surface_kind, _finish_id, color, _stage_theme)
|
||||
theme = _stage_theme.control_theme() if _stage_theme != null else null
|
||||
_apply_visual_style()
|
||||
|
||||
|
||||
func open_dialog() -> void:
|
||||
var max_size: Vector2i = _usable_window_size()
|
||||
var preferred_size := Vector2i(560, 640)
|
||||
var raw_size: Variant = _stage_theme.value("popups.dialog.surface_color_size", [560, 640]) if _stage_theme != null else [560, 640]
|
||||
if raw_size is Array and (raw_size as Array).size() == 2:
|
||||
preferred_size = Vector2i(int((raw_size as Array)[0]), int((raw_size as Array)[1]))
|
||||
var dialog_size := Vector2i(mini(preferred_size.x, max_size.x), mini(preferred_size.y, max_size.y))
|
||||
dialog_size.x = maxi(1, dialog_size.x)
|
||||
dialog_size.y = maxi(1, dialog_size.y)
|
||||
size = dialog_size
|
||||
popup_centered(dialog_size)
|
||||
_color_picker.grab_focus()
|
||||
if not _built or _picker_view == null:
|
||||
return
|
||||
var popup_size: Vector2i = _bounded_dialog_size()
|
||||
_apply_responsive_layout(popup_size)
|
||||
size = popup_size
|
||||
popup_centered(popup_size)
|
||||
call_deferred("_focus_picker")
|
||||
|
||||
|
||||
func _unhandled_key_input(event: InputEvent) -> void:
|
||||
@@ -53,98 +62,168 @@ func _unhandled_key_input(event: InputEvent) -> void:
|
||||
|
||||
|
||||
func _build() -> void:
|
||||
if _built:
|
||||
return
|
||||
_built = true
|
||||
var margin := MarginContainer.new()
|
||||
margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
_margin = MarginContainer.new()
|
||||
_margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
add_child(_margin)
|
||||
_layout = VBoxContainer.new()
|
||||
_margin.add_child(_layout)
|
||||
_heading = Label.new()
|
||||
_heading.name = "ChooseColorHeading"
|
||||
_heading.text = "Choose a color"
|
||||
_heading.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_heading.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
_heading.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_layout.add_child(_heading)
|
||||
_view_scroll = ScrollContainer.new()
|
||||
_view_scroll.name = "PickerScroll"
|
||||
_view_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
_view_scroll.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_view_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
_layout.add_child(_view_scroll)
|
||||
_picker_view = SURFACE_COLOR_PICKER_VIEW.new() as StageSurfaceColorPickerView
|
||||
_picker_view.name = "SurfaceColorPickerView"
|
||||
_picker_view.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_picker_view.preview_color_changed.connect(_on_preview_color_changed)
|
||||
_view_scroll.add_child(_picker_view)
|
||||
_actions = HBoxContainer.new()
|
||||
_actions.name = "Actions"
|
||||
_actions.alignment = BoxContainer.ALIGNMENT_END
|
||||
_actions.add_theme_constant_override("separation", 8)
|
||||
_layout.add_child(_actions)
|
||||
_cancel_button = Button.new()
|
||||
_cancel_button.name = "CancelButton"
|
||||
_cancel_button.text = "Cancel"
|
||||
_cancel_button.focus_mode = Control.FOCUS_ALL
|
||||
_cancel_button.pressed.connect(_cancel)
|
||||
_actions.add_child(_cancel_button)
|
||||
_use_button = Button.new()
|
||||
_use_button.name = "UseColorButton"
|
||||
_use_button.text = "Use color"
|
||||
_use_button.focus_mode = Control.FOCUS_ALL
|
||||
_use_button.pressed.connect(_confirm_color)
|
||||
_actions.add_child(_use_button)
|
||||
|
||||
|
||||
func _apply_visual_style() -> void:
|
||||
if _stage_theme == null or not _built:
|
||||
return
|
||||
theme = _stage_theme.control_theme()
|
||||
var radius: int = _integer("popups.asset_selector.corner_radius", 16, 8, 32)
|
||||
var shell: StyleBoxFlat = _stage_theme.stylebox("popups.asset_selector")
|
||||
shell.set_corner_radius_all(radius)
|
||||
shell.content_margin_left = 16.0
|
||||
shell.content_margin_right = 16.0
|
||||
shell.content_margin_top = 14.0
|
||||
shell.content_margin_bottom = 14.0
|
||||
add_theme_stylebox_override("panel", shell)
|
||||
var text_color: Color = _color("stage_panels.type.text_color", Color("#F5F7FA"))
|
||||
var font: Font = _stage_theme.font()
|
||||
if font != null:
|
||||
_heading.add_theme_font_override("font", font)
|
||||
_cancel_button.add_theme_font_override("font", font)
|
||||
var strong_font := FontVariation.new()
|
||||
strong_font.base_font = font
|
||||
strong_font.variation_embolden = 0.55
|
||||
_use_button.add_theme_font_override("font", strong_font)
|
||||
_heading.add_theme_font_size_override("font_size", _integer("popups.asset_selector.setup_title_size", 30, 22, 48))
|
||||
_heading.add_theme_color_override("font_color", text_color)
|
||||
_apply_action_button(_cancel_button, false)
|
||||
_apply_action_button(_use_button, true)
|
||||
|
||||
|
||||
func _apply_action_button(button: Button, primary: bool) -> void:
|
||||
var accent: Color = _color("mode_colors.edit", Color("#22C6FF"))
|
||||
var text_color: Color = _color("stage_panels.type.text_color", Color("#F5F7FA"))
|
||||
var neutral: Color = _color("stage_panels.choice.neutral_surface", Color("#171B24"))
|
||||
var hover: Color = _color("stage_panels.choice.hover_surface", Color("#303A4B"))
|
||||
var selected: Color = _color("stage_panels.choice.selected_surface", Color("#12394A"))
|
||||
var border: Color = _color("stage_panels.choice.border_color", Color("#FFFFFF33"))
|
||||
var blue: Color = _color("popups.asset_selector.action_color", Color("#087DBA"))
|
||||
var radius: int = _integer("popups.asset_selector.corner_radius", 16, 8, 32)
|
||||
var border_width: int = _integer("popups.asset_selector.button_border_width", 2, 1, 5)
|
||||
var normal_color: Color = blue if primary else neutral
|
||||
var hover_color: Color = blue.lightened(0.12) if primary else hover
|
||||
var pressed_color: Color = blue.darkened(0.12) if primary else selected
|
||||
var edge: Color = accent.lightened(0.15) if primary else border
|
||||
button.add_theme_stylebox_override("normal", _make_button_style(normal_color, edge, border_width, radius))
|
||||
button.add_theme_stylebox_override("hover", _make_button_style(hover_color, edge, border_width + 1, radius))
|
||||
button.add_theme_stylebox_override("pressed", _make_button_style(pressed_color, accent, border_width + 1, radius))
|
||||
button.add_theme_stylebox_override("hover_pressed", _make_button_style(pressed_color.lightened(0.05), accent, border_width + 1, radius))
|
||||
button.add_theme_stylebox_override("focus", _make_button_style(Color(0, 0, 0, 0), accent, border_width + 1, radius))
|
||||
button.add_theme_color_override("font_color", Color.WHITE if primary else text_color)
|
||||
button.add_theme_color_override("font_hover_color", Color.WHITE if primary else text_color)
|
||||
button.add_theme_color_override("font_pressed_color", Color.WHITE if primary else text_color)
|
||||
button.add_theme_font_size_override("font_size", _integer("popups.asset_selector.setup_button_font_size", 20, 14, 40))
|
||||
button.custom_minimum_size.y = float(_integer("popups.dialog.action_min_target_size", 48, 44, 96))
|
||||
|
||||
|
||||
func _apply_responsive_layout(popup_size: Vector2i) -> void:
|
||||
if not _built:
|
||||
return
|
||||
var compact: bool = popup_size.y < COMPACT_LAYOUT_MAX_HEIGHT
|
||||
var margin_size: int = 8 if compact else 12
|
||||
for side: String in ["left", "right", "top", "bottom"]:
|
||||
margin.add_theme_constant_override("margin_" + side, 12)
|
||||
add_child(margin)
|
||||
var layout := VBoxContainer.new()
|
||||
layout.add_theme_constant_override("separation", 8)
|
||||
margin.add_child(layout)
|
||||
var preview_heading := Label.new()
|
||||
preview_heading.text = "Preview"
|
||||
layout.add_child(preview_heading)
|
||||
_surface_preview = SURFACE_PREVIEW.new() as StageSurfacePreview
|
||||
var preview_size := Vector2(420.0, 140.0)
|
||||
var raw_preview: Variant = _stage_theme.value("popups.dialog.surface_color_preview_size", [420, 140]) if _stage_theme != null else [420, 140]
|
||||
if raw_preview is Array and (raw_preview as Array).size() == 2:
|
||||
preview_size = Vector2(float((raw_preview as Array)[0]), float((raw_preview as Array)[1]))
|
||||
preview_size.x = minf(preview_size.x, maxf(1.0, float(_usable_window_size().x - 56)))
|
||||
_surface_preview.custom_minimum_size = preview_size
|
||||
_surface_preview.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_surface_preview.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
layout.add_child(_surface_preview)
|
||||
var picker_scroll := ScrollContainer.new()
|
||||
picker_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
picker_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
picker_scroll.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
layout.add_child(picker_scroll)
|
||||
_color_picker = ColorPicker.new()
|
||||
_color_picker.name = "ColorPickerControl"
|
||||
var picker_min_size := Vector2(320.0, 340.0)
|
||||
var raw_picker_size: Variant = _stage_theme.value("popups.dialog.surface_color_picker_min_size", [320, 340]) if _stage_theme != null else [320, 340]
|
||||
if raw_picker_size is Array and (raw_picker_size as Array).size() == 2:
|
||||
picker_min_size = Vector2(float((raw_picker_size as Array)[0]), float((raw_picker_size as Array)[1]))
|
||||
picker_min_size.x = minf(picker_min_size.x, maxf(1.0, float(_usable_window_size().x - 56)))
|
||||
_color_picker.custom_minimum_size = picker_min_size
|
||||
_color_picker.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_color_picker.focus_mode = Control.FOCUS_ALL
|
||||
_color_picker.color_changed.connect(_on_picker_color_changed)
|
||||
picker_scroll.add_child(_color_picker)
|
||||
var actions := HBoxContainer.new()
|
||||
actions.alignment = BoxContainer.ALIGNMENT_END
|
||||
actions.add_theme_constant_override("separation", 8)
|
||||
layout.add_child(actions)
|
||||
var use_button := Button.new()
|
||||
use_button.text = "Use color"
|
||||
var action_width: float = maxf(44.0, minf(120.0, (float(_usable_window_size().x) - 40.0) * 0.5))
|
||||
use_button.custom_minimum_size = Vector2(action_width, float(_integer("popups.dialog.action_min_target_size", 48, 44, 96)))
|
||||
use_button.focus_mode = Control.FOCUS_ALL
|
||||
use_button.pressed.connect(_confirm_color)
|
||||
actions.add_child(use_button)
|
||||
var cancel_button := Button.new()
|
||||
cancel_button.text = "✕ Cancel"
|
||||
cancel_button.custom_minimum_size = Vector2(action_width, float(_integer("popups.dialog.action_min_target_size", 48, 44, 96)))
|
||||
cancel_button.focus_mode = Control.FOCUS_ALL
|
||||
cancel_button.pressed.connect(_cancel)
|
||||
actions.add_child(cancel_button)
|
||||
close_requested.connect(_cancel)
|
||||
if _stage_theme != null:
|
||||
var font: Font = _stage_theme.font()
|
||||
if font != null:
|
||||
preview_heading.add_theme_font_override("font", font)
|
||||
use_button.add_theme_font_override("font", font)
|
||||
cancel_button.add_theme_font_override("font", font)
|
||||
preview_heading.add_theme_font_size_override("font_size", _stage_theme.integer("stage_panels.type.section_size", 20, 12, 40))
|
||||
use_button.add_theme_stylebox_override("normal", _stage_theme.stylebox("stage_panels.action"))
|
||||
cancel_button.add_theme_stylebox_override("normal", _stage_theme.stylebox("stage_panels.action"))
|
||||
use_button.add_theme_color_override("font_color", _stage_theme.color("global.text_color", Color("#F5F7FA")))
|
||||
cancel_button.add_theme_color_override("font_color", _stage_theme.color("global.text_color", Color("#F5F7FA")))
|
||||
_margin.add_theme_constant_override("margin_" + side, margin_size)
|
||||
_layout.add_theme_constant_override("separation", 4 if compact else 8)
|
||||
_heading.add_theme_font_size_override("font_size", _integer("popups.asset_selector.setup_title_size", 30, 22, 48))
|
||||
var content_width: float = maxf(1.0, float(popup_size.x - margin_size * 2))
|
||||
_picker_view.set_available_size(Vector2(content_width, float(popup_size.y)))
|
||||
_apply_action_layout(content_width)
|
||||
|
||||
|
||||
func _on_picker_color_changed(color: Color) -> void:
|
||||
_update_preview(color)
|
||||
func _apply_action_layout(content_width: float) -> void:
|
||||
if _actions == null or _cancel_button == null or _use_button == null:
|
||||
return
|
||||
var stack_buttons: bool = content_width < 344.0
|
||||
_actions.vertical = stack_buttons
|
||||
_actions.alignment = BoxContainer.ALIGNMENT_END
|
||||
var action_height: float = float(_integer("popups.dialog.action_min_target_size", 48, 44, 96))
|
||||
var button_width: float = content_width if stack_buttons else maxf(112.0, (content_width - 8.0) * 0.5)
|
||||
_cancel_button.custom_minimum_size = Vector2(button_width, action_height)
|
||||
_use_button.custom_minimum_size = Vector2(button_width, action_height)
|
||||
|
||||
|
||||
func _on_preview_color_changed(color: Color) -> void:
|
||||
preview_color_changed.emit(color)
|
||||
|
||||
|
||||
func _update_preview(color: Color) -> void:
|
||||
if _surface_preview != null:
|
||||
_surface_preview.configure(_surface_kind, _finish_id, color)
|
||||
|
||||
|
||||
func _confirm_color() -> void:
|
||||
var confirmed_color: Color = _color_picker.color
|
||||
if _picker_view == null:
|
||||
return
|
||||
var confirmed_color: Color = _picker_view.get_color()
|
||||
hide()
|
||||
color_confirmed.emit(confirmed_color)
|
||||
|
||||
|
||||
func _cancel() -> void:
|
||||
_update_preview(_initial_color)
|
||||
if _picker_view != null:
|
||||
_picker_view.set_color_no_signal(_initial_color)
|
||||
hide()
|
||||
cancelled.emit()
|
||||
|
||||
|
||||
func _focus_picker() -> void:
|
||||
if visible and _picker_view != null:
|
||||
_picker_view.focus_picker()
|
||||
|
||||
|
||||
func _on_root_size_changed() -> void:
|
||||
if visible:
|
||||
open_dialog()
|
||||
|
||||
|
||||
func _bounded_dialog_size() -> Vector2i:
|
||||
var max_size: Vector2i = _usable_window_size()
|
||||
var preferred_size := Vector2i(560, 640)
|
||||
var raw_size: Variant = _stage_theme.value("popups.dialog.surface_color_size", [560, 640]) if _stage_theme != null else [560, 640]
|
||||
if raw_size is Array and (raw_size as Array).size() == 2:
|
||||
preferred_size = Vector2i(int((raw_size as Array)[0]), int((raw_size as Array)[1]))
|
||||
return Vector2i(maxi(1, mini(preferred_size.x, max_size.x)), maxi(1, mini(preferred_size.y, max_size.y)))
|
||||
|
||||
|
||||
func _usable_window_size() -> Vector2i:
|
||||
var available: Vector2i = Vector2i(get_tree().root.size)
|
||||
var usable: Rect2i = DisplayServer.screen_get_usable_rect(DisplayServer.window_get_current_screen())
|
||||
@@ -156,5 +235,22 @@ func _usable_window_size() -> Vector2i:
|
||||
return available
|
||||
|
||||
|
||||
func _make_button_style(background: Color, border: Color, border_width: int, radius: int) -> StyleBoxFlat:
|
||||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = background
|
||||
style.border_color = border
|
||||
style.set_border_width_all(border_width)
|
||||
style.set_corner_radius_all(radius)
|
||||
style.content_margin_left = 12.0
|
||||
style.content_margin_right = 12.0
|
||||
style.content_margin_top = 8.0
|
||||
style.content_margin_bottom = 8.0
|
||||
return style
|
||||
|
||||
|
||||
func _integer(path: String, fallback: int, minimum: int, maximum: int) -> int:
|
||||
return fallback if _stage_theme == null else _stage_theme.integer(path, fallback, minimum, maximum)
|
||||
|
||||
|
||||
func _color(path: String, fallback: Color) -> Color:
|
||||
return fallback if _stage_theme == null else _stage_theme.color(path, fallback)
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
class_name StageSurfaceColorPickerView
|
||||
extends VBoxContainer
|
||||
## Inline color picker content for the scenery chooser.
|
||||
|
||||
const SURFACE_PREVIEW := preload("res://scripts/stage/scenery/stage_surface_preview.gd")
|
||||
const COMPACT_LAYOUT_MAX_HEIGHT := 640.0
|
||||
const COMPACT_PREVIEW_HEIGHT := 64.0
|
||||
const COMPACT_PICKER_MIN_HEIGHT := 260.0
|
||||
const COMPACT_SV_HEIGHT := 144
|
||||
const REGULAR_SV_HEIGHT := 224
|
||||
|
||||
signal preview_color_changed(color: Color)
|
||||
|
||||
var _surface_kind: StringName = &"wall"
|
||||
var _finish_id: StringName = &"finish_wall_paint"
|
||||
var _stage_theme: SandboxTheme
|
||||
var _color_picker: ColorPicker
|
||||
var _surface_preview: StageSurfacePreview
|
||||
var _preview_heading: Label
|
||||
var _preferred_preview_size: Vector2 = Vector2(420.0, 140.0)
|
||||
var _preferred_preview_height: float = 140.0
|
||||
var _preferred_picker_min_size: Vector2 = Vector2(320.0, 340.0)
|
||||
var _built: bool = false
|
||||
var _suppress_color_signal: bool = false
|
||||
|
||||
|
||||
func configure(surface_kind: StringName, finish: StringName, color: Color, stage_theme: SandboxTheme) -> void:
|
||||
_surface_kind = surface_kind
|
||||
_finish_id = finish
|
||||
_stage_theme = stage_theme
|
||||
if not _built:
|
||||
_build()
|
||||
set_color_no_signal(color)
|
||||
theme = stage_theme.control_theme() if stage_theme != null else null
|
||||
_apply_preview_heading_theme()
|
||||
|
||||
|
||||
func get_color() -> Color:
|
||||
return _color_picker.color if _color_picker != null else Color.WHITE
|
||||
|
||||
|
||||
func set_color_no_signal(color: Color) -> void:
|
||||
if _color_picker == null:
|
||||
return
|
||||
_suppress_color_signal = true
|
||||
_color_picker.color = color
|
||||
_suppress_color_signal = false
|
||||
_update_preview(color)
|
||||
|
||||
|
||||
func set_available_size(available_size: Vector2) -> void:
|
||||
if not _built:
|
||||
return
|
||||
var compact: bool = available_size.y < COMPACT_LAYOUT_MAX_HEIGHT
|
||||
var preview_size: Vector2 = _surface_preview.custom_minimum_size
|
||||
preview_size.x = minf(_preferred_preview_size.x, maxf(1.0, available_size.x - 56.0))
|
||||
preview_size.y = minf(_preferred_preview_height, COMPACT_PREVIEW_HEIGHT) if compact else _preferred_preview_height
|
||||
_surface_preview.custom_minimum_size = preview_size
|
||||
var picker_size: Vector2 = _color_picker.custom_minimum_size
|
||||
picker_size.x = minf(_preferred_picker_min_size.x, maxf(1.0, available_size.x - 56.0))
|
||||
picker_size.y = minf(_preferred_picker_min_size.y, COMPACT_PICKER_MIN_HEIGHT) if compact else _preferred_picker_min_size.y
|
||||
_color_picker.custom_minimum_size = picker_size
|
||||
_color_picker.add_theme_constant_override("sv_height", COMPACT_SV_HEIGHT if compact else REGULAR_SV_HEIGHT)
|
||||
|
||||
|
||||
func focus_picker() -> void:
|
||||
if _color_picker != null:
|
||||
_color_picker.grab_focus()
|
||||
|
||||
|
||||
func _build() -> void:
|
||||
_built = true
|
||||
add_theme_constant_override("separation", 8)
|
||||
_surface_preview = SURFACE_PREVIEW.new() as StageSurfacePreview
|
||||
_preview_heading = Label.new()
|
||||
_preview_heading.name = "PreviewHeading"
|
||||
_preview_heading.text = "Preview"
|
||||
_preview_heading.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add_child(_preview_heading)
|
||||
var preview_size := _preferred_preview_size
|
||||
var raw_preview: Variant = _stage_theme.value("popups.dialog.surface_color_preview_size", [420, 140]) if _stage_theme != null else [420, 140]
|
||||
if raw_preview is Array and (raw_preview as Array).size() == 2:
|
||||
preview_size = Vector2(float((raw_preview as Array)[0]), float((raw_preview as Array)[1]))
|
||||
_preferred_preview_size = preview_size
|
||||
_preferred_preview_height = preview_size.y
|
||||
_surface_preview.custom_minimum_size = preview_size
|
||||
_surface_preview.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_surface_preview.set_rounded_frame(
|
||||
float(_stage_theme.integer("popups.asset_selector.surface_corner_radius", 14, 4, 32)) if _stage_theme != null else 14.0,
|
||||
_stage_theme.color("popups.style_defaults.bg_color", Color("#202633FA")) if _stage_theme != null else Color("#202633FA")
|
||||
)
|
||||
add_child(_surface_preview)
|
||||
_color_picker = ColorPicker.new()
|
||||
_color_picker.name = "ColorPickerControl"
|
||||
var picker_size := _preferred_picker_min_size
|
||||
var raw_picker_size: Variant = _stage_theme.value("popups.dialog.surface_color_picker_min_size", [320, 340]) if _stage_theme != null else [320, 340]
|
||||
if raw_picker_size is Array and (raw_picker_size as Array).size() == 2:
|
||||
picker_size = Vector2(float((raw_picker_size as Array)[0]), float((raw_picker_size as Array)[1]))
|
||||
_preferred_picker_min_size = picker_size
|
||||
_color_picker.custom_minimum_size = picker_size
|
||||
_color_picker.presets_visible = false
|
||||
_color_picker.sampler_visible = false
|
||||
_color_picker.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_color_picker.focus_mode = Control.FOCUS_ALL
|
||||
_color_picker.color_changed.connect(_on_picker_color_changed)
|
||||
add_child(_color_picker)
|
||||
_apply_preview_heading_theme()
|
||||
|
||||
|
||||
func _apply_preview_heading_theme() -> void:
|
||||
if _preview_heading == null:
|
||||
return
|
||||
if _stage_theme == null:
|
||||
return
|
||||
_preview_heading.add_theme_font_size_override("font_size", _stage_theme.integer("stage_panels.type.section_size", 20, 12, 40))
|
||||
_preview_heading.add_theme_color_override("font_color", _stage_theme.color("stage_panels.type.text_color", Color("#F5F7FA")))
|
||||
var font: Font = _stage_theme.font()
|
||||
if font != null:
|
||||
_preview_heading.add_theme_font_override("font", font)
|
||||
|
||||
|
||||
func _on_picker_color_changed(color: Color) -> void:
|
||||
_update_preview(color)
|
||||
if not _suppress_color_signal:
|
||||
preview_color_changed.emit(color)
|
||||
|
||||
|
||||
func _update_preview(color: Color) -> void:
|
||||
if _surface_preview != null:
|
||||
_surface_preview.configure(_surface_kind, _finish_id, color)
|
||||
@@ -0,0 +1 @@
|
||||
uid://dbvatqk4a287y
|
||||
+623
-125
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,15 @@ var _group: ButtonGroup = ButtonGroup.new()
|
||||
var _buttons: Array[Button] = []
|
||||
var _stage_theme: SandboxTheme
|
||||
var _colors: Array[Color] = []
|
||||
var _swatch_size_override: float = -1.0
|
||||
var _swatch_gap_override: int = -1
|
||||
|
||||
|
||||
func set_swatch_layout_override(size: float, gap: int) -> void:
|
||||
_swatch_size_override = size
|
||||
_swatch_gap_override = gap
|
||||
if is_inside_tree() and not _colors.is_empty():
|
||||
configure(_colors, _selected, _stage_theme)
|
||||
|
||||
|
||||
func configure(colors: Array[Color], selected: Color, theme: Variant = null) -> void:
|
||||
@@ -25,8 +34,9 @@ func configure(colors: Array[Color], selected: Color, theme: Variant = null) ->
|
||||
for child: Node in get_children():
|
||||
remove_child(child)
|
||||
child.queue_free()
|
||||
add_theme_constant_override("h_separation", _integer("stage_surface_colors.swatch_gap", 8))
|
||||
add_theme_constant_override("v_separation", _integer("stage_surface_colors.swatch_gap", 8))
|
||||
var gap: int = _swatch_gap_override if _swatch_gap_override >= 0 else _integer("stage_surface_colors.swatch_gap", 8)
|
||||
add_theme_constant_override("h_separation", gap)
|
||||
add_theme_constant_override("v_separation", gap)
|
||||
var seen: Dictionary = {}
|
||||
for color: Color in _colors:
|
||||
var color_key: String = color.to_html(true).to_upper()
|
||||
@@ -34,7 +44,7 @@ func configure(colors: Array[Color], selected: Color, theme: Variant = null) ->
|
||||
continue
|
||||
seen[color_key] = true
|
||||
var button := Button.new()
|
||||
var swatch_size: float = float(_integer("stage_surface_colors.swatch_size", 48))
|
||||
var swatch_size: float = _swatch_size_override if _swatch_size_override > 0.0 else float(_integer("stage_surface_colors.swatch_size", 48))
|
||||
button.custom_minimum_size = Vector2(swatch_size, swatch_size)
|
||||
button.toggle_mode = true
|
||||
button.button_group = _group
|
||||
|
||||
Reference in New Issue
Block a user