netfishing/ui/surface_drawing_toolbar.gd

352 lines
11 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

class_name SurfaceDrawingToolbar
extends Control
const UtilityPageStyleType = preload("res://ui/utility_page_style.gd")
const MARKER_MODE_ICON: Texture2D = preload(
"res://items/icons/art/art_kit_marker.png"
)
const MARKER_MODE_SHADER: Shader = preload(
"res://ui/art_kit_marker_icon.gdshader"
)
const GRID_MODE_ICON: Texture2D = preload(
"res://items/icons/art/art_kit_grid_light.png"
)
const TOOLBAR_WIDTH: float = 510.0
const TOOLBAR_HEIGHT: float = 373.0
const COLOR_RAIL_WIDTH: float = 46.0
@onready var _mode_button: Button = %ModeButton
@onready var _brush_option: OptionButton = %BrushOption
@onready var _grid_option: OptionButton = %GridOption
@onready var _color_list: VBoxContainer = %ColorList
@onready var _top_panel: PanelContainer = %TopPanel
@onready var _color_panel: PanelContainer = %ColorPanel
@onready var _eraser_button: Button = %EraserButton
@onready var _undo_button: Button = %UndoButton
@onready var _hide_guide_button: Button = %HideGuideButton
@onready var _restore_guide_button: Button = %RestoreGuideButton
@onready var _finalize_guide_button: Button = %FinalizeGuideButton
var _service: NetworkSurfaceDrawingService
var _unlocks: PlayerArtUnlocks
var _color_buttons: Dictionary[StringName, Button] = {}
var _dock_right: bool = true
var _marker_mode_material: ShaderMaterial
func _ready() -> void:
UtilityPageStyleType.apply_page(self)
_apply_toolbar_panel(_top_panel)
_apply_toolbar_panel(_color_panel)
UtilityPageStyleType.apply_ocean_button(_mode_button)
UtilityPageStyleType.apply_ocean_button(_brush_option)
UtilityPageStyleType.apply_ocean_button(_grid_option)
for button: Button in _action_buttons():
UtilityPageStyleType.apply_ocean_button(button)
button.custom_minimum_size = Vector2(48, 44)
_make_button_round(button, 22)
_mode_button.custom_minimum_size = Vector2(48, 48)
_make_button_round(_mode_button, 24)
_enlarge_action_icon(_mode_button)
_marker_mode_material = ShaderMaterial.new()
_marker_mode_material.shader = MARKER_MODE_SHADER
_marker_mode_material.set_shader_parameter(
"marker_color",
SurfaceDrawingPalette.get_color(SurfaceDrawingPalette.DEFAULT_COLOR_ID),
)
_mode_button.material = _marker_mode_material
for button: Button in _action_buttons():
_enlarge_action_icon(button)
_mode_button.pressed.connect(_toggle_mode)
_eraser_button.pressed.connect(_toggle_eraser)
_undo_button.pressed.connect(_undo_last_stroke)
_hide_guide_button.pressed.connect(
_arm_guide_action.bind(NetworkSurfaceDrawingService.GuideAction.HIDE)
)
_restore_guide_button.pressed.connect(
_arm_guide_action.bind(NetworkSurfaceDrawingService.GuideAction.RESTORE)
)
_finalize_guide_button.pressed.connect(
_arm_guide_action.bind(NetworkSurfaceDrawingService.GuideAction.FINALIZE)
)
_brush_option.item_selected.connect(_select_brush)
_grid_option.item_selected.connect(_select_grid)
_build_options()
_apply_dock_side()
hide()
func _apply_toolbar_panel(panel: PanelContainer) -> void:
var style: StyleBoxFlat = UtilityPageStyleType.panel_style()
style.content_margin_left = 6.0
style.content_margin_top = 6.0
style.content_margin_right = 6.0
style.content_margin_bottom = 6.0
style.set_corner_radius_all(10)
panel.add_theme_stylebox_override("panel", style)
func _action_buttons() -> Array[Button]:
return [
_eraser_button,
_undo_button,
_hide_guide_button,
_restore_guide_button,
_finalize_guide_button,
]
func _make_button_round(button: Button, radius: int) -> void:
for style_name: StringName in [
&"normal", &"hover", &"pressed", &"focus", &"disabled",
]:
var existing: StyleBox = button.get_theme_stylebox(style_name)
var style := existing.duplicate() as StyleBoxFlat
if style == null:
continue
style.set_corner_radius_all(radius)
button.add_theme_stylebox_override(style_name, style)
func _enlarge_action_icon(button: Button) -> void:
# The shared ocean button uses generous text padding, leaving only a
# 20-pixel icon area in these compact bubbles. Keep the 48x44 control and
# hitbox intact while giving icon-only actions a full 40x40 presentation.
button.add_theme_constant_override("icon_max_width", 40)
for style_name: StringName in [
&"normal", &"hover", &"pressed", &"focus", &"disabled",
]:
var existing: StyleBox = button.get_theme_stylebox(style_name)
var style := existing.duplicate() as StyleBoxFlat
if style == null:
continue
style.content_margin_left = 4.0
style.content_margin_right = 4.0
style.content_margin_top = 2.0
style.content_margin_bottom = 2.0
button.add_theme_stylebox_override(style_name, style)
func setup(
service: NetworkSurfaceDrawingService,
unlocks: PlayerArtUnlocks,
) -> void:
_service = service
_unlocks = unlocks
if _service != null and not _service.hud_state_changed.is_connected(
_on_service_state_changed
):
_service.hud_state_changed.connect(_on_service_state_changed)
if _unlocks != null and not _unlocks.unlocks_changed.is_connected(
_on_unlocks_changed
):
_unlocks.unlocks_changed.connect(_on_unlocks_changed)
_refresh_unlocks()
func set_dock_right(should_dock_right: bool) -> void:
_dock_right = should_dock_right
if is_node_ready():
_apply_dock_side()
func is_docked_right() -> bool:
return _dock_right
func _apply_dock_side() -> void:
anchor_left = 1.0 if _dock_right else 0.0
anchor_right = anchor_left
offset_left = -TOOLBAR_WIDTH if _dock_right else 0.0
offset_right = 0.0 if _dock_right else TOOLBAR_WIDTH
offset_top = 0.0
offset_bottom = TOOLBAR_HEIGHT
_top_panel.offset_left = 0.0
_top_panel.offset_right = TOOLBAR_WIDTH
_color_panel.offset_left = (
TOOLBAR_WIDTH - COLOR_RAIL_WIDTH if _dock_right else 0.0
)
_color_panel.offset_right = (
TOOLBAR_WIDTH if _dock_right else COLOR_RAIL_WIDTH
)
func owns_pointer_event(event: InputEvent) -> bool:
if not visible:
return false
if _brush_option.get_popup().visible or _grid_option.get_popup().visible:
return true
if event is InputEventMouse:
var pointer_position: Vector2 = (event as InputEventMouse).position
return (
_top_panel.get_global_rect().has_point(pointer_position)
or _color_panel.get_global_rect().has_point(pointer_position)
)
return false
func _build_options() -> void:
_brush_option.clear()
for brush_size: int in PlayerArtUnlocks.BRUSH_SIZES:
_brush_option.add_item("%d×" % brush_size, brush_size)
_grid_option.clear()
for grid_size: int in PlayerArtUnlocks.GRID_SIZES:
_grid_option.add_item("%d×" % grid_size, grid_size)
for child: Node in _color_list.get_children():
child.queue_free()
_color_buttons.clear()
for color_id: StringName in SurfaceDrawingPalette.get_color_ids():
var button := Button.new()
button.custom_minimum_size = Vector2(34, 34)
button.focus_mode = Control.FOCUS_ALL
button.tooltip_text = SurfaceDrawingPalette.get_display_name(color_id)
button.pressed.connect(_select_color.bind(color_id))
_color_list.add_child(button)
_color_buttons[color_id] = button
_apply_color_button_style(button, color_id, false)
func _refresh_unlocks() -> void:
if not is_node_ready() or _unlocks == null:
return
var brush_popup: PopupMenu = _brush_option.get_popup()
for index: int in range(_brush_option.item_count):
var brush_size: int = _brush_option.get_item_id(index)
brush_popup.set_item_disabled(
index, not _unlocks.is_brush_size_unlocked(brush_size)
)
var grid_popup: PopupMenu = _grid_option.get_popup()
for index: int in range(_grid_option.item_count):
var grid_size: int = _grid_option.get_item_id(index)
grid_popup.set_item_disabled(
index, not _unlocks.is_grid_size_unlocked(grid_size)
)
for color_id: StringName in _color_buttons:
var button: Button = _color_buttons[color_id]
button.disabled = not _unlocks.is_color_unlocked(color_id)
_apply_color_button_style(
button,
color_id,
(
_service != null
and not _service.is_eraser_mode()
and _service.get_color_id() == color_id
),
)
func _apply_color_button_style(
button: Button,
color_id: StringName,
selected: bool,
) -> void:
var color: Color = (
SurfaceDrawingPalette.get_color(color_id)
if not button.disabled
else UtilityPageStyleType.OCEAN_DISABLED
)
for style_name: StringName in [&"normal", &"hover", &"pressed", &"focus"]:
var style := StyleBoxFlat.new()
style.bg_color = color.lightened(0.12) if style_name == &"hover" else color
style.set_border_width_all(3 if selected else 0)
style.border_color = UtilityPageStyleType.OCEAN_TEXT_PRIMARY
style.set_corner_radius_all(17)
button.add_theme_stylebox_override(style_name, style)
var disabled_style := StyleBoxFlat.new()
disabled_style.bg_color = UtilityPageStyleType.OCEAN_DISABLED
disabled_style.set_corner_radius_all(17)
button.add_theme_stylebox_override("disabled", disabled_style)
func _toggle_mode() -> void:
if _service != null:
_service.set_placement_mode(not _service.is_placement_mode())
func _select_brush(index: int) -> void:
if _service != null:
_service.set_brush_size(_brush_option.get_item_id(index))
func _select_grid(index: int) -> void:
if _service != null:
_service.set_grid_size(_grid_option.get_item_id(index))
func _select_color(color_id: StringName) -> void:
if _service != null:
_service.set_color_id(color_id)
func _toggle_eraser() -> void:
if _service != null:
_service.set_eraser_mode(not _service.is_eraser_mode())
func _undo_last_stroke() -> void:
if _service != null:
_service.request_undo_last_stroke()
func _arm_guide_action(action: int) -> void:
if _service != null:
_service.arm_guide_action(action)
func _on_unlocks_changed(_unlock_mask: int) -> void:
_refresh_unlocks()
func _on_service_state_changed(
is_active: bool,
mode_name: String,
_color_name: String,
color_value: Color,
brush_size: int,
grid_size: int,
_status: String,
) -> void:
visible = is_active
if not is_active:
return
_mode_button.icon = (
GRID_MODE_ICON if mode_name == "place grid" else MARKER_MODE_ICON
)
_marker_mode_material.set_shader_parameter("marker_color", color_value)
_mode_button.accessibility_name = (
"switch to marker mode"
if mode_name == "place grid"
else "switch to grid mode"
)
_mode_button.tooltip_text = (
"Switch to marker mode"
if mode_name == "place grid"
else "Switch to grid mode"
)
_select_option_by_id(_brush_option, brush_size)
_select_option_by_id(_grid_option, grid_size)
_refresh_action_state()
_refresh_unlocks()
func _refresh_action_state() -> void:
if _service == null:
return
_eraser_button.button_pressed = _service.is_eraser_mode()
var guide_action: int = _service.get_armed_guide_action()
_hide_guide_button.button_pressed = (
guide_action == NetworkSurfaceDrawingService.GuideAction.HIDE
)
_restore_guide_button.button_pressed = (
guide_action == NetworkSurfaceDrawingService.GuideAction.RESTORE
)
_finalize_guide_button.button_pressed = (
guide_action == NetworkSurfaceDrawingService.GuideAction.FINALIZE
)
func _select_option_by_id(option: OptionButton, item_id: int) -> void:
for index: int in range(option.item_count):
if option.get_item_id(index) == item_id:
option.select(index)
return