229 lines
8.0 KiB
GDScript
229 lines
8.0 KiB
GDScript
@tool
|
|
class_name PieMenu
|
|
extends Control
|
|
|
|
## Emitted when the "3D Cursor to Origin" command is invoked through the [PieMenu]
|
|
signal cursor_to_origin_pressed
|
|
## Emitted when the "3D Cursor to Selected Object(s)" command is invoked through the [PieMenu]
|
|
signal cursor_to_selected_objects_pressed
|
|
## Emitted when the "Selected Object to 3D Cursor" command is invoked through the [PieMenu]
|
|
signal selected_object_to_cursor_pressed
|
|
## Emitted when the "Remove 3D Cursor from Scene" command is invoked through the [PieMenu]
|
|
signal remove_cursor_from_scene_pressed
|
|
## Emitted when the "Toggle 3D Cursor" command is invoked through the [PieMenu]
|
|
signal toggle_cursor_pressed
|
|
|
|
## The dimmed color for the selection indicator that is used if no button is hovered
|
|
const _dimmed_selection_indicator_color: Color = Color("8c8c8cFF")
|
|
|
|
## The value at which the buttons start to animate/slide if the menu is shown
|
|
var slide_start: int = 0
|
|
## The position the buttons animate/slide to
|
|
var slide_end: int = 100
|
|
## The radius of the menu. The buttons are aligned around an invisible circle
|
|
## and this is the corresponding radius.
|
|
var menu_radius: int = slide_start
|
|
## The buttons that are "loaded"
|
|
var buttons: Array[Button] = []
|
|
|
|
var _hovered_button: Button
|
|
var _show_menu_echo: bool = false
|
|
|
|
@onready var selection_indicator: Sprite2D = $SelectionIndicator
|
|
@onready var toggle_3d_cursor: Button = $Toggle3DCursor
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
#var viewport_height: int = get_viewport().size.y
|
|
#var viewport_width: int = get_viewport().size.x
|
|
|
|
# If the menu is shown animate the buttons
|
|
if visible:
|
|
menu_radius = lerp(menu_radius, slide_end, 20 * delta)
|
|
|
|
# Reset the button positions when the menu is hidden
|
|
if not visible:
|
|
menu_radius = slide_start
|
|
_show_menu_echo = true
|
|
|
|
_align_buttons()
|
|
|
|
# Load all children from the pie menu
|
|
var children = get_children()
|
|
# Get all the children that are buttons if there are no new button return
|
|
if children.filter(_is_button) == buttons:
|
|
return
|
|
|
|
# If there are new buttons repopulate the buttons list and display them
|
|
buttons.clear()
|
|
for button: Button in children.filter(_is_button):
|
|
buttons.append(button)
|
|
|
|
_align_buttons()
|
|
|
|
|
|
func _input(event: InputEvent) -> void:
|
|
if not is_visible_in_tree():
|
|
return
|
|
|
|
# If the [PieMenu] is opened the selection indicator will rotate according
|
|
# to the mouse position. If the user hovers over a button the indicator
|
|
# will change its color to white and a more dimmed color otherwise
|
|
if event is InputEventMouseMotion:
|
|
# Calculate the angle of the mouse position to the x axis
|
|
var rot: float = rad_to_deg(get_local_mouse_position().angle_to(Vector2.RIGHT)) - 45
|
|
# Apply the rotation to the indicator
|
|
selection_indicator.rotation_degrees = -rot
|
|
|
|
# If the key used to open the [PieMenu] is held down while selecting hovering
|
|
# over a button the user can invoke the buttons action by releasing the
|
|
# key (s). This does not work if the key was released prior to hovering
|
|
# over a button. This functionality is similar to the one in Blender
|
|
if not event is InputEventKey:
|
|
return
|
|
|
|
if not event.keycode == KEY_S:
|
|
return
|
|
|
|
if event.is_released() and _hovered_button == null:
|
|
_show_menu_echo = false
|
|
return
|
|
|
|
if _show_menu_echo and event.is_released():
|
|
_hovered_button.pressed.emit()
|
|
|
|
|
|
## This method should be used in conjuncton with a [Array.filter] method.
|
|
## It checks whether a node inherits from Button
|
|
func _is_button(child: Node) -> bool:
|
|
return child is Button
|
|
|
|
|
|
## This method aligns the available buttons in a circular menu by using
|
|
## some [sin] and [cos] magic
|
|
func _align_buttons() -> void:
|
|
var button_count: int = len(buttons)
|
|
for i in range(button_count):
|
|
var button: Button = buttons[i]
|
|
var theta: float = (i / float(button_count)) * TAU
|
|
var x: float = (menu_radius * cos(theta))
|
|
var y: float = (menu_radius * sin(theta)) - button.size.y / 2.0
|
|
x = x - button.size.x if x < 0 else x
|
|
button.position = Vector2(x, y)
|
|
|
|
|
|
## Connected to the corresponding UI button this method acts as a repeater
|
|
## by emitting the corresponding signal classes can listen to via a [PieMenu]
|
|
## instance
|
|
func _on_3d_cursor_to_origin_pressed() -> void:
|
|
hide()
|
|
cursor_to_origin_pressed.emit()
|
|
|
|
## Executes when the "3D Cursor to Origin" button is hovered
|
|
func _on_3d_cursor_to_origin_mouse_entered() -> void:
|
|
_hovered_button = $"3DCursorToOrigin"
|
|
_on_mouse_entered_button()
|
|
|
|
## Executes when the "3D Cursor to Origin" button is no longer hovered
|
|
func _on_3d_cursor_to_origin_mouse_exited() -> void:
|
|
_hovered_button = null
|
|
_on_mouse_exited_button()
|
|
|
|
|
|
## Connected to the corresponding UI button this method acts as a repeater
|
|
## by emitting the corresponding signal classes can listen to via a [PieMenu]
|
|
## instance
|
|
func _on_3d_cursor_to_selected_objects_pressed() -> void:
|
|
hide()
|
|
cursor_to_selected_objects_pressed.emit()
|
|
|
|
## Executes when the "3D Cursor to Selected Object(s)" button is hovered
|
|
func _on_3d_cursor_to_selected_objects_mouse_entered() -> void:
|
|
_hovered_button = $"3DCursorToSelectedObjects"
|
|
_on_mouse_entered_button()
|
|
|
|
## Executes when the "3D Cursor to Selected Object(s)" button is no longer hovered
|
|
func _on_3d_cursor_to_selected_objects_mouse_exited() -> void:
|
|
_hovered_button = null
|
|
_on_mouse_exited_button()
|
|
|
|
|
|
## Connected to the corresponding UI button this method acts as a repeater
|
|
## by emitting the corresponding signal classes can listen to via a [PieMenu]
|
|
## instance
|
|
func _on_selected_object_to_3d_cursor_pressed() -> void:
|
|
hide()
|
|
selected_object_to_cursor_pressed.emit()
|
|
|
|
## Executes when the "Selected Object to 3D Cursor" button is hovered
|
|
func _on_selected_object_to_3d_cursor_mouse_entered() -> void:
|
|
_hovered_button = $SelectedObjectTo3DCursor
|
|
_on_mouse_entered_button()
|
|
|
|
## Executes when the "Selected Object to 3D Cursor" button is no longer hovered
|
|
func _on_selected_object_to_3d_cursor_mouse_exited() -> void:
|
|
_hovered_button = null
|
|
_on_mouse_exited_button()
|
|
|
|
|
|
## Connected to the corresponding UI button this method acts as a repeater
|
|
## by emitting the corresponding signal classes can listen to via a [PieMenu]
|
|
## instance
|
|
func _on_remove_3d_cursor_from_scene_pressed() -> void:
|
|
hide()
|
|
remove_cursor_from_scene_pressed.emit()
|
|
|
|
## Executes when the "Remove 3D Cursor" button is hovered
|
|
func _on_remove_3d_cursor_from_scene_mouse_entered() -> void:
|
|
_hovered_button = $Remove3DCursorFromScene
|
|
_on_mouse_entered_button()
|
|
|
|
## Executes when the "Remove 3D Cursor" button is no longer hovered
|
|
func _on_remove_3d_cursor_from_scene_mouse_exited() -> void:
|
|
_hovered_button = null
|
|
_on_mouse_exited_button()
|
|
|
|
|
|
## Connected to the corresponding UI button this method acts as a repeater
|
|
## by emitting the corresponding signal classes can listen to via a [PieMenu]
|
|
## instance
|
|
func _on_toggle_3d_cursor_pressed() -> void:
|
|
hide()
|
|
toggle_cursor_pressed.emit()
|
|
|
|
## Executes when the "Disable/Enable 3D Cursor" button is hovered
|
|
func _on_toggle_3d_cursor_mouse_entered() -> void:
|
|
_hovered_button = toggle_3d_cursor
|
|
_on_mouse_entered_button()
|
|
|
|
## Executes when the "Disable/Enable 3D Cursor" button is hovered
|
|
func _on_toggle_3d_cursor_mouse_exited() -> void:
|
|
_hovered_button = null
|
|
_on_mouse_exited_button()
|
|
|
|
|
|
## This method is executed by every button of the [PieMenu]. It brightens
|
|
## the color of the selection indicator when a button is hovered.
|
|
func _on_mouse_entered_button() -> void:
|
|
selection_indicator.modulate = Color.WHITE
|
|
|
|
## This method is executed by every button of the [PieMenu]. It dims the color
|
|
## of the selection indicator when a button is no longer hovered
|
|
func _on_mouse_exited_button() -> void:
|
|
selection_indicator.modulate = _dimmed_selection_indicator_color
|
|
|
|
|
|
## This method is a little helper that is used to prevent some quirky behaviour
|
|
## with the consumption of events. It checks whether the user clicked on a
|
|
## button rather than the space around it
|
|
func hit_any_button() -> bool:
|
|
var mouse_position: Vector2 = get_global_mouse_position()
|
|
for button in buttons:
|
|
if button.get_global_rect().has_point(mouse_position):
|
|
return true
|
|
return false
|
|
|
|
|
|
func change_toggle_label(visible: bool) -> void:
|
|
toggle_3d_cursor.text = ("Disable" if visible else "Enable") + " 3D Cursor"
|