Improve art tools and UI docking

This commit is contained in:
Alexander Sellite 2026-08-02 09:20:44 -04:00
parent 10e0442437
commit 984f7c641c
13 changed files with 626 additions and 82 deletions

View file

@ -61,6 +61,7 @@ var _pending_send_body: String = ""
var _prior_movement: bool = true
var _prior_camera: bool = true
var _output_scale: float = 1.0
var _dock_right: bool = false
func _ready() -> void:
@ -85,6 +86,7 @@ func setup(
_player = player
_fishing_spot = fishing_spot
_settings = settings
set_dock_right(_settings.current_settings.chat_dock_right)
_service.message_received.connect(_on_message)
_service.local_message_confirmed.connect(_on_local_message_confirmed)
_service.history_replaced.connect(_on_history)
@ -333,10 +335,10 @@ func _build_ui() -> void:
func _chat_panel_style() -> StyleBoxFlat:
var style := _borderless_style(Color(0.025, 0.13, 0.19, 0.94))
style.corner_radius_top_left = 0
style.corner_radius_bottom_left = 0
style.corner_radius_top_right = 10
style.corner_radius_bottom_right = 10
style.corner_radius_top_left = 10 if _dock_right else 0
style.corner_radius_bottom_left = 10 if _dock_right else 0
style.corner_radius_top_right = 0 if _dock_right else 10
style.corner_radius_bottom_right = 0 if _dock_right else 10
style.content_margin_left = 12
style.content_margin_right = 12
style.content_margin_top = 10
@ -568,10 +570,26 @@ func set_output_scale(output_scale: float) -> void:
_output_scale = maxf(output_scale, 0.001)
func set_dock_right(should_dock_right: bool) -> void:
_dock_right = should_dock_right
if not is_node_ready() or _panel == null:
return
_panel.add_theme_stylebox_override("panel", _chat_panel_style())
_refresh_handle_labels(_presentation_state)
_layout_presentation(false)
func is_docked_right() -> bool:
return _dock_right
func _refresh_handle_labels(state: PresentationState) -> void:
var collapsed := state == PresentationState.COLLAPSED
var height_state := _visible_state_before_collapse if collapsed else state
_collapse_button.text = ">" if collapsed else "<"
if _dock_right:
_collapse_button.text = "<" if collapsed else ">"
else:
_collapse_button.text = ">" if collapsed else "<"
_collapse_button.tooltip_text = (
"Show chat" if collapsed else "Hide chat"
)
@ -687,6 +705,9 @@ func _set_status(text: String) -> void:
func _layout_presentation(animate: bool) -> void:
if _panel == null:
return
var viewport_width := size.x
if viewport_width <= 0.0:
viewport_width = 1280.0
var viewport_height := size.y
if viewport_height <= 0.0:
viewport_height = 720.0
@ -704,32 +725,40 @@ func _layout_presentation(animate: bool) -> void:
if layout_state == PresentationState.EXPANDED:
height = maxf(MIN_HISTORY_HEIGHT, viewport_height - 2.0 * bottom_margin)
height = minf(height, maxf(MIN_HISTORY_HEIGHT, bottom - MIN_TOP_MARGIN))
var target_position := Vector2(
-(PANEL_WIDTH - COLLAPSED_REVEAL_WIDTH)
if _presentation_state == PresentationState.COLLAPSED
else 0.0,
bottom - height,
)
var collapsed := _presentation_state == PresentationState.COLLAPSED
var panel_x: float
if _dock_right:
panel_x = (
viewport_width - COLLAPSED_REVEAL_WIDTH
if collapsed
else viewport_width - PANEL_WIDTH
)
else:
panel_x = -(PANEL_WIDTH - COLLAPSED_REVEAL_WIDTH) if collapsed else 0.0
var target_position := Vector2(panel_x, bottom - height)
var target_size := Vector2(PANEL_WIDTH, height)
var handle_stack_height := HANDLE_SIZE.y * 2.0 + HANDLE_GAP
var handle_stack_top := (
bottom - COMPACT_HEIGHT * 0.5 - handle_stack_height * 0.5
)
var handle_x: float
if _dock_right:
handle_x = panel_x - HANDLE_SIZE.x
else:
handle_x = COLLAPSED_REVEAL_WIDTH if collapsed else PANEL_WIDTH
var collapse_position := Vector2(
COLLAPSED_REVEAL_WIDTH
if _presentation_state == PresentationState.COLLAPSED
else PANEL_WIDTH,
handle_x,
handle_stack_top + HANDLE_SIZE.y + HANDLE_GAP,
)
var height_position := Vector2(
COLLAPSED_REVEAL_WIDTH
if _presentation_state == PresentationState.COLLAPSED
else PANEL_WIDTH,
handle_stack_top,
)
var unread_position := collapse_position + Vector2(6.0, -18.0)
var height_position := Vector2(handle_x, handle_stack_top)
var unread_offset_x := -18.0 if _dock_right else 6.0
var unread_position := collapse_position + Vector2(unread_offset_x, -18.0)
var hint_position := Vector2(
HINT_EDGE_MARGIN,
(
viewport_width - _hint.size.x - HINT_EDGE_MARGIN
if _dock_right
else HINT_EDGE_MARGIN
),
viewport_height - _hint.size.y - HINT_EDGE_MARGIN,
)
if _height_tween != null and _height_tween.is_valid():

View file

@ -219,6 +219,10 @@ func setup(
_shop_interaction = shop_interaction
_surface_drawing = surface_drawing
_surface_drawing_toolbar.setup(_surface_drawing, art_unlocks)
set_edge_docks(
settings_manager.current_settings.chat_dock_right,
settings_manager.current_settings.paint_dock_right,
)
func _input(event: InputEvent) -> void:
@ -409,6 +413,11 @@ func set_effective_ui_pixel_size(pixel_size: int) -> void:
_pause_settings_panel.set_effective_ui_pixel_size(pixel_size)
func set_edge_docks(chat_dock_right: bool, paint_dock_right: bool) -> void:
_chat_ui.set_dock_right(chat_dock_right)
_surface_drawing_toolbar.set_dock_right(paint_dock_right)
func focus_open_settings_back_button() -> void:
if _title_settings_panel.visible:
_title_settings_panel.focus_back_button()

View file

@ -46,7 +46,7 @@ mouse_filter = 2
unique_name_in_owner = true
mouse_filter = 2
[node name="SurfaceDrawingToolbar" parent="UIRoot/CanonicalStage" instance=ExtResource("11_drawing_toolbar")]
[node name="SurfaceDrawingToolbar" parent="UIRoot" instance=ExtResource("11_drawing_toolbar")]
unique_name_in_owner = true
[node name="GameplayTransientHUD" type="Control" parent="UIRoot/CanonicalStage"]

View file

@ -41,6 +41,8 @@ enum PresentationMode {
@onready var _world_value: BubbleButton = %WorldValue
@onready var _ui_value: BubbleButton = %UIValue
@onready var _chat_dock: BubbleButton = %ChatDock
@onready var _paint_dock: BubbleButton = %PaintDock
@onready var _mouse_value: BubbleButton = %MouseValue
@onready var _controller_value: BubbleButton = %ControllerValue
@onready var _invert_y_toggle: BubbleButton = %InvertYToggle
@ -133,6 +135,8 @@ func _ready() -> void:
_ui_options[index].pressed.connect(
_set_ui_pixelation.bind(index + 1)
)
_chat_dock.pressed.connect(_toggle_chat_dock)
_paint_dock.pressed.connect(_toggle_paint_dock)
%MouseDecrease.pressed.connect(_adjust_mouse_sensitivity.bind(-1))
%MouseIncrease.pressed.connect(_adjust_mouse_sensitivity.bind(1))
_mouse_value.pressed.connect(_adjust_mouse_sensitivity.bind(1))
@ -642,14 +646,12 @@ func _apply_settings() -> void:
if _settings_manager == null:
_feedback.text = "settings are unavailable."
return
var edited := PlayerSettings.new()
var edited: PlayerSettings = _settings_manager.current_settings.copy()
edited.auto_click_enabled = _auto_click_enabled
edited.auto_click_interval = _auto_click_interval_value
edited.mouse_camera_sensitivity = _mouse_sensitivity
edited.controller_camera_sensitivity = _controller_sensitivity
edited.invert_camera_y = _invert_camera_y
edited.world_pixel_size = _settings_manager.current_settings.world_pixel_size
edited.ui_pixel_size = _settings_manager.current_settings.ui_pixel_size
if _settings_manager.apply_settings(edited):
if (
_presentation_mode == PresentationMode.TITLE_EMBEDDED
@ -686,6 +688,10 @@ func _refresh_value_labels() -> void:
"ui\npixelation\n"
+ _pixel_size_label(settings.ui_pixel_size)
)
_chat_dock.text = "chat dock\n" + ("right" if settings.chat_dock_right else "left")
_paint_dock.text = (
"paint dock\n" + ("right" if settings.paint_dock_right else "left")
)
_mouse_value.text = "mouse\nsensitivity\n%.4f" % _mouse_sensitivity
_controller_value.text = (
"controller\nsensitivity\n%.1f" % _controller_sensitivity
@ -757,6 +763,28 @@ func _set_ui_pixelation(pixel_size: int) -> void:
_refresh_value_labels()
func _toggle_chat_dock() -> void:
if _settings_manager == null:
return
var edited: PlayerSettings = _settings_manager.current_settings.copy()
edited.chat_dock_right = not edited.chat_dock_right
if not _settings_manager.apply_settings(edited):
_feedback.text = "failed to save chat dock setting."
return
_refresh_value_labels()
func _toggle_paint_dock() -> void:
if _settings_manager == null:
return
var edited: PlayerSettings = _settings_manager.current_settings.copy()
edited.paint_dock_right = not edited.paint_dock_right
if not _settings_manager.apply_settings(edited):
_feedback.text = "failed to save paint dock setting."
return
_refresh_value_labels()
func _adjust_mouse_sensitivity(direction: int) -> void:
_mouse_sensitivity = clampf(
_mouse_sensitivity + float(direction) * 0.0005,

View file

@ -157,8 +157,8 @@ grow_horizontal = 2
grow_vertical = 2
script = ExtResource("3_page")
page_id = &"display"
bubble_paths = Array[NodePath]([NodePath("BubbleCluster/WorldValue"), NodePath("BubbleCluster/WorldLegible"), NodePath("BubbleCluster/WorldCute"), NodePath("BubbleCluster/WorldRetro"), NodePath("BubbleCluster/WorldHardcore"), NodePath("BubbleCluster/WorldWtf"), NodePath("BubbleCluster/UIValue"), NodePath("BubbleCluster/UILegible"), NodePath("BubbleCluster/UICute"), NodePath("BubbleCluster/UIRetro"), NodePath("BubbleCluster/UIHardcore"), NodePath("BubbleCluster/UIWtf"), NodePath("BubbleCluster/DisplayBackButton")])
focus_paths = Array[NodePath]([NodePath("BubbleCluster/WorldLegible"), NodePath("BubbleCluster/WorldCute"), NodePath("BubbleCluster/WorldRetro"), NodePath("BubbleCluster/WorldHardcore"), NodePath("BubbleCluster/WorldWtf"), NodePath("BubbleCluster/UILegible"), NodePath("BubbleCluster/UICute"), NodePath("BubbleCluster/UIRetro"), NodePath("BubbleCluster/UIHardcore"), NodePath("BubbleCluster/UIWtf"), NodePath("BubbleCluster/DisplayBackButton")])
bubble_paths = Array[NodePath]([NodePath("BubbleCluster/WorldValue"), NodePath("BubbleCluster/WorldLegible"), NodePath("BubbleCluster/WorldCute"), NodePath("BubbleCluster/WorldRetro"), NodePath("BubbleCluster/WorldHardcore"), NodePath("BubbleCluster/WorldWtf"), NodePath("BubbleCluster/UIValue"), NodePath("BubbleCluster/UILegible"), NodePath("BubbleCluster/UICute"), NodePath("BubbleCluster/UIRetro"), NodePath("BubbleCluster/UIHardcore"), NodePath("BubbleCluster/UIWtf"), NodePath("BubbleCluster/ChatDock"), NodePath("BubbleCluster/PaintDock"), NodePath("BubbleCluster/DisplayBackButton")])
focus_paths = Array[NodePath]([NodePath("BubbleCluster/WorldLegible"), NodePath("BubbleCluster/WorldCute"), NodePath("BubbleCluster/WorldRetro"), NodePath("BubbleCluster/WorldHardcore"), NodePath("BubbleCluster/WorldWtf"), NodePath("BubbleCluster/UILegible"), NodePath("BubbleCluster/UICute"), NodePath("BubbleCluster/UIRetro"), NodePath("BubbleCluster/UIHardcore"), NodePath("BubbleCluster/UIWtf"), NodePath("BubbleCluster/ChatDock"), NodePath("BubbleCluster/PaintDock"), NodePath("BubbleCluster/DisplayBackButton")])
initial_focus_path = NodePath("BubbleCluster/WorldRetro")
back_focus_path = NodePath("BubbleCluster/DisplayBackButton")
compact_maximum_layout_size = Vector2(544, 400)
@ -178,12 +178,12 @@ custom_minimum_size = Vector2(0, 0)
mouse_filter = 2
focus_mode = 0
text = "world\npixelation\nretro"
neutral_size = Vector2(160, 150)
neutral_size = Vector2(140, 130)
desktop_anchor = Vector2(160, 240)
compact_anchor = Vector2(175, 210)
compact_minimum_size = Vector2(132, 126)
compact_minimum_size = Vector2(118, 110)
minimum_font_size = 15
maximum_font_size = 25
maximum_font_size = 22
motion_phase = 0.2
[node name="WorldLegible" parent="DisplayPage/BubbleCluster" instance=ExtResource("4_bubble")]
@ -258,12 +258,12 @@ custom_minimum_size = Vector2(0, 0)
mouse_filter = 2
focus_mode = 0
text = "ui\npixelation\nretro"
neutral_size = Vector2(160, 150)
neutral_size = Vector2(140, 130)
desktop_anchor = Vector2(560, 240)
compact_anchor = Vector2(545, 210)
compact_minimum_size = Vector2(132, 126)
compact_minimum_size = Vector2(118, 110)
minimum_font_size = 15
maximum_font_size = 25
maximum_font_size = 22
motion_phase = 3.55
[node name="UILegible" parent="DisplayPage/BubbleCluster" instance=ExtResource("4_bubble")]
@ -332,6 +332,30 @@ minimum_font_size = 12
maximum_font_size = 13
motion_phase = 0.55
[node name="ChatDock" parent="DisplayPage/BubbleCluster" instance=ExtResource("4_bubble")]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 0)
text = "chat dock\nleft"
neutral_size = Vector2(96, 96)
desktop_anchor = Vector2(190, 435)
compact_anchor = Vector2(160, 378)
compact_minimum_size = Vector2(82, 82)
minimum_font_size = 12
maximum_font_size = 16
motion_phase = 1.1
[node name="PaintDock" parent="DisplayPage/BubbleCluster" instance=ExtResource("4_bubble")]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 0)
text = "paint dock\nright"
neutral_size = Vector2(96, 96)
desktop_anchor = Vector2(530, 435)
compact_anchor = Vector2(448, 378)
compact_minimum_size = Vector2(82, 82)
minimum_font_size = 12
maximum_font_size = 16
motion_phase = 1.65
[node name="DisplayBackButton" parent="DisplayPage/BubbleCluster" instance=ExtResource("4_bubble")]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 0)
@ -342,7 +366,7 @@ compact_anchor = Vector2(315, 390)
compact_minimum_size = Vector2(82, 78)
minimum_font_size = 13
maximum_font_size = 16
motion_phase = 1.1
motion_phase = 2.2
[node name="ControlsPage" type="Control" parent="."]
unique_name_in_owner = true

View file

@ -1,48 +1,94 @@
class_name SurfaceDrawingToolbar
extends PanelContainer
extends Control
const UtilityPageStyleType = preload("res://ui/utility_page_style.gd")
const TOOLBAR_WIDTH: float = 580.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
@onready var _close_button: Button = %CloseButton
var _service: NetworkSurfaceDrawingService
var _unlocks: PlayerArtUnlocks
var _color_buttons: Dictionary[StringName, Button] = {}
var _dock_right: bool = true
func _ready() -> void:
UtilityPageStyleType.apply_page(self)
var panel: StyleBoxFlat = UtilityPageStyleType.panel_style()
panel.content_margin_left = 10.0
panel.content_margin_top = 10.0
panel.content_margin_right = 10.0
panel.content_margin_bottom = 10.0
add_theme_stylebox_override("panel", panel)
_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_mode_button_round()
_make_button_round(_mode_button, 24)
_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)
)
_close_button.pressed.connect(_close_toolbar)
_brush_option.item_selected.connect(_select_brush)
_grid_option.item_selected.connect(_select_grid)
_build_options()
_apply_dock_side()
hide()
func _make_mode_button_round() -> void:
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,
_close_button,
]
func _make_button_round(button: Button, radius: int) -> void:
for style_name: StringName in [
&"normal", &"hover", &"pressed", &"focus", &"disabled",
]:
var existing: StyleBox = _mode_button.get_theme_stylebox(style_name)
var existing: StyleBox = button.get_theme_stylebox(style_name)
var style := existing.duplicate() as StyleBoxFlat
if style == null:
continue
style.set_corner_radius_all(24)
_mode_button.add_theme_stylebox_override(style_name, style)
style.set_corner_radius_all(radius)
button.add_theme_stylebox_override(style_name, style)
func setup(
@ -62,13 +108,42 @@ func setup(
_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
_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:
return get_global_rect().has_point((event as InputEventMouse).position)
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
@ -114,7 +189,11 @@ func _refresh_unlocks() -> void:
_apply_color_button_style(
button,
color_id,
_service != null and _service.get_color_id() == color_id,
(
_service != null
and not _service.is_eraser_mode()
and _service.get_color_id() == color_id
),
)
@ -161,6 +240,26 @@ func _select_color(color_id: StringName) -> void:
_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 _close_toolbar() -> void:
if _service != null:
_service.deactivate()
func _on_unlocks_changed(_unlock_mask: int) -> void:
_refresh_unlocks()
@ -185,9 +284,26 @@ func _on_service_state_changed(
)
_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:

View file

@ -3,55 +3,97 @@
[ext_resource type="Script" path="res://ui/surface_drawing_toolbar.gd" id="1_script"]
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
[node name="SurfaceDrawingToolbar" type="PanelContainer"]
[node name="SurfaceDrawingToolbar" type="Control"]
visible = false
z_index = 85
layout_mode = 0
offset_left = 18.0
offset_top = 18.0
offset_right = 284.0
offset_bottom = 340.0
mouse_filter = 0
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -580.0
offset_bottom = 373.0
grow_horizontal = 0
mouse_filter = 2
theme = ExtResource("2_theme")
script = ExtResource("1_script")
[node name="Layout" type="HBoxContainer" parent="."]
layout_mode = 2
theme_override_constants/separation = 10
[node name="ColorList" type="VBoxContainer" parent="Layout"]
[node name="TopPanel" type="PanelContainer" parent="."]
unique_name_in_owner = true
layout_mode = 0
offset_right = 580.0
offset_bottom = 60.0
[node name="Top" type="HBoxContainer" parent="TopPanel"]
layout_mode = 2
theme_override_constants/separation = 5
[node name="Tools" type="VBoxContainer" parent="Layout"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 8
[node name="Top" type="HBoxContainer" parent="Layout/Tools"]
layout_mode = 2
theme_override_constants/separation = 6
[node name="ModeButton" type="Button" parent="Layout/Tools/Top"]
[node name="ModeButton" type="Button" parent="TopPanel/Top"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "switch between grid and marker mode"
text = "▦"
[node name="BrushOption" type="OptionButton" parent="Layout/Tools/Top"]
[node name="BrushOption" type="OptionButton" parent="TopPanel/Top"]
unique_name_in_owner = true
custom_minimum_size = Vector2(80, 48)
layout_mode = 2
tooltip_text = "brush size"
[node name="GridOption" type="OptionButton" parent="Layout/Tools/Top"]
[node name="GridOption" type="OptionButton" parent="TopPanel/Top"]
unique_name_in_owner = true
custom_minimum_size = Vector2(92, 48)
layout_mode = 2
tooltip_text = "grid size"
[node name="Help" type="Label" parent="Layout/Tools"]
[node name="EraserButton" type="Button" parent="TopPanel/Top"]
unique_name_in_owner = true
layout_mode = 2
text = "click place/draw\nshift click hide/erase\nctrl z undo\nshift scroll zoom"
theme_override_colors/font_color = Color(0.623529, 0.811765, 0.823529, 1)
theme_override_font_sizes/font_size = 14
tooltip_text = "toggle eraser"
toggle_mode = true
text = "⌫"
[node name="UndoButton" type="Button" parent="TopPanel/Top"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "undo last stroke"
text = "↶"
[node name="HideGuideButton" type="Button" parent="TopPanel/Top"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "hide grid on next click"
toggle_mode = true
text = "◌"
[node name="RestoreGuideButton" type="Button" parent="TopPanel/Top"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "restore hidden grid on next click"
toggle_mode = true
text = "▤"
[node name="FinalizeGuideButton" type="Button" parent="TopPanel/Top"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "finish grid on next click"
toggle_mode = true
text = "✓"
[node name="CloseButton" type="Button" parent="TopPanel/Top"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "close art tools"
text = "×"
[node name="ColorPanel" type="PanelContainer" parent="."]
unique_name_in_owner = true
layout_mode = 0
offset_left = 534.0
offset_top = 54.0
offset_right = 580.0
offset_bottom = 373.0
[node name="ColorList" type="VBoxContainer" parent="ColorPanel"]
unique_name_in_owner = true
layout_mode = 2
theme_override_constants/separation = 5