141 lines
4.9 KiB
GDScript
141 lines
4.9 KiB
GDScript
extends Control
|
||
|
||
signal overview_changed(active: bool)
|
||
|
||
const FOCUS_SCOPES = preload("res://scripts/selectable_container.gd")
|
||
|
||
@export var grid_size := Vector2i(32, 18):
|
||
set(value):
|
||
grid_size = Vector2i(maxi(1, value.x), maxi(1, value.y))
|
||
if is_node_ready():
|
||
_update_grid()
|
||
@export var monitor_boundary := 0
|
||
@export_range(0.5, 0.95) var overview_scale := 0.82
|
||
@export var transition_seconds := 0.22
|
||
@export var hot_corner_enabled := true
|
||
@export var corner_size := 20.0
|
||
@export var corner_dwell := 0.18
|
||
|
||
var overview_active := false
|
||
var _pointer_in_window := false
|
||
var _corner_hover := false
|
||
var _corner_armed := true
|
||
var _dwell := 0.0
|
||
var _tween: Tween
|
||
|
||
@onready var dashboard: Control = $Dashboard
|
||
@onready var grid: Control = $Dashboard/Grid
|
||
@onready var canvas: Control = $Dashboard/Canvas
|
||
|
||
func _ready() -> void:
|
||
grid.gui_input.connect(canvas.handle_grid_input)
|
||
$Toolbar/Add.pressed.connect(func() -> void: $Library.visible = not $Library.visible)
|
||
$Library.element_requested.connect(_add_element)
|
||
canvas.layout_saved.connect(func() -> void: $Toolbar/Hint.text = "Esc to return")
|
||
canvas.load_failed.connect(func() -> void: $Toolbar/Hint.text = "Could not load saved layout")
|
||
$Toolbar/Reset.pressed.connect(canvas.reset_layout)
|
||
canvas.save_failed.connect(func() -> void: $Toolbar/Hint.text = "Could not save layout")
|
||
$Toolbar/Return.pressed.connect(func() -> void: set_overview(false))
|
||
resized.connect(_resize)
|
||
get_window().mouse_entered.connect(func() -> void: _pointer_in_window = true)
|
||
get_window().mouse_exited.connect(_leave_corner)
|
||
theme_changed.connect(queue_redraw)
|
||
_update_grid()
|
||
canvas.load_layout()
|
||
_resize()
|
||
|
||
func _input(event: InputEvent) -> void:
|
||
if FOCUS_SCOPES.dispatch_focus_shortcut(event, get_tree()):
|
||
get_viewport().set_input_as_handled()
|
||
return
|
||
if event is InputEventMouseMotion and hot_corner_enabled:
|
||
_pointer_in_window = true
|
||
_update_corner(get_window().get_final_transform() * event.position)
|
||
elif overview_active and event is InputEventKey and event.pressed and event.keycode == KEY_ESCAPE:
|
||
if $Library.visible:
|
||
$Library.hide()
|
||
else:
|
||
set_overview(false)
|
||
get_viewport().set_input_as_handled()
|
||
elif overview_active and event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
|
||
var pointer: Vector2 = get_global_transform_with_canvas().affine_inverse() * event.position
|
||
var in_library: bool = $Library.visible and $Library.get_global_rect().has_point(event.position)
|
||
if not in_library and not preview_rect().has_point(pointer) and not $Toolbar.get_global_rect().has_point(event.position):
|
||
set_overview(false)
|
||
get_viewport().set_input_as_handled()
|
||
|
||
func _process(delta: float) -> void:
|
||
var window := get_window()
|
||
# Letterbox regions may not send canvas mouse-motion events.
|
||
if hot_corner_enabled and window.visible and (_pointer_in_window or window.has_focus()):
|
||
_update_corner(window.get_final_transform() * window.get_mouse_position())
|
||
else:
|
||
_leave_corner()
|
||
if hot_corner_enabled and _corner_hover and _corner_armed and not overview_active:
|
||
_dwell += delta
|
||
if _dwell >= corner_dwell:
|
||
_corner_armed = false
|
||
set_overview(true)
|
||
|
||
func _update_corner(window_position: Vector2) -> void:
|
||
_corner_hover = Rect2(Vector2.ZERO, Vector2.ONE * corner_size).has_point(window_position)
|
||
if not _corner_hover:
|
||
_corner_armed = true
|
||
_dwell = 0
|
||
|
||
func _leave_corner() -> void:
|
||
_pointer_in_window = false
|
||
_corner_hover = false
|
||
_dwell = 0
|
||
|
||
func set_overview(active: bool) -> void:
|
||
if overview_active == active:
|
||
return
|
||
if not active:
|
||
$Library.hide()
|
||
canvas.set_edit_mode(active)
|
||
overview_active = active
|
||
_corner_armed = false
|
||
_dwell = 0
|
||
if is_instance_valid(_tween):
|
||
_tween.kill()
|
||
grid.visible = active
|
||
$Toolbar.visible = active
|
||
queue_redraw()
|
||
var target := Vector2.ONE * overview_scale if active else Vector2.ONE
|
||
if transition_seconds <= 0:
|
||
dashboard.scale = target
|
||
else:
|
||
_tween = create_tween().set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT)
|
||
_tween.tween_property(dashboard, "scale", target, transition_seconds)
|
||
overview_changed.emit(active)
|
||
|
||
func _add_element(element_type: String) -> void:
|
||
if canvas.add_element(element_type) == null:
|
||
$Library.show_no_space()
|
||
else:
|
||
$Library.hide()
|
||
|
||
func preview_rect() -> Rect2:
|
||
return Rect2((size - size * dashboard.scale) / 2, size * dashboard.scale)
|
||
|
||
func _resize() -> void:
|
||
_dwell = 0
|
||
if is_instance_valid(_tween):
|
||
_tween.kill()
|
||
dashboard.pivot_offset = size / 2
|
||
dashboard.scale = Vector2.ONE * overview_scale if overview_active else Vector2.ONE
|
||
queue_redraw()
|
||
|
||
func _update_grid() -> void:
|
||
canvas.grid_size = grid_size
|
||
canvas.arrange_items()
|
||
grid.grid_size = grid_size
|
||
grid.monitor_boundary = monitor_boundary
|
||
grid.queue_redraw()
|
||
$Toolbar/Title.text = "LAYOUT OVERVIEW %d × %d" % [grid_size.x, grid_size.y]
|
||
|
||
func _draw() -> void:
|
||
if overview_active:
|
||
draw_rect(Rect2(Vector2.ZERO, size), get_theme_color("instrument_empty", "SurfaceSide"))
|