pokemon-base/addons/gd-blender-3d-shortcuts/scenes/pie_menu/PieMenu.gd
2025-01-26 20:23:56 +01:00

159 lines
4 KiB
GDScript

@tool
extends Control
signal item_selected(index)
signal item_focused(index)
signal item_cancelled()
const button_margin = 6
@export var items := [] : set = set_items
@export var selected_index = -1 : set = set_selected_index
@export var radius = 100.0 : set = set_radius
var buttons = []
var pie_menus = []
var focused_index = -1
var theme_source_node = self : set = set_theme_source_node
var grow_with_max_button_width = false
func _ready():
set_items(items)
set_selected_index(selected_index)
set_radius(radius)
hide()
connect("visibility_changed", _on_visiblity_changed)
func _input(event):
if visible:
if event is InputEventKey:
if event.pressed:
match event.keycode:
KEY_ESCAPE:
cancel()
if event is InputEventMouseMotion:
focus_item()
get_viewport().set_input_as_handled()
if event is InputEventMouseButton:
if event.pressed:
match event.button_index:
MOUSE_BUTTON_LEFT:
select_item(focused_index)
get_viewport().set_input_as_handled()
MOUSE_BUTTON_RIGHT:
cancel()
get_viewport().set_input_as_handled()
func _on_visiblity_changed():
if not visible:
if selected_index != focused_index: # Cancellation
focused_index = selected_index
func cancel():
hide()
get_viewport().set_input_as_handled()
emit_signal("item_cancelled")
func select_item(index):
set_button_style(selected_index, "normal", "normal")
selected_index = index
focused_index = selected_index
hide()
emit_signal("item_selected", selected_index)
func focus_item():
queue_redraw()
var pos = get_global_mouse_position()
var count = max(buttons.size(), 1)
var angle_offset = 2 * PI / count
var angle = pos.angle_to_point(global_position) + PI / 2 # -90 deg initial offset
if angle < 0:
angle += 2 * PI
var index = (angle / angle_offset)
var decimal = index - floor(index)
index = floor(index)
if decimal >= 0.5:
index += 1
if index > buttons.size()-1:
index = 0
set_button_style(focused_index, "normal", "normal")
focused_index = index
set_button_style(focused_index, "normal", "hover")
set_button_style(selected_index, "normal", "focus")
emit_signal("item_focused", focused_index)
func popup(pos):
global_position = pos
show()
func populate_menu():
clear_menu()
buttons = []
for i in items.size():
var item = items[i]
var is_array = item is Array
var name = item if not is_array else item[0]
var value = null if not is_array else item[1]
var button = Button.new()
button.grow_horizontal = Control.GROW_DIRECTION_BOTH
button.text = name
if value != null:
button.set_meta("value", value)
buttons.append(button)
set_button_style(i, "hover", "hover")
set_button_style(i, "pressed", "pressed")
set_button_style(i, "focus", "focus")
set_button_style(i, "disabled", "disabled")
set_button_style(i, "normal", "normal")
add_child(button)
align()
set_button_style(selected_index, "normal", "focus")
func align():
var final_radius = radius
if grow_with_max_button_width:
var max_button_width = 0.0
for button in buttons:
max_button_width = max(max_button_width, button.size.x)
final_radius = max(radius, max_button_width)
var count = max(buttons.size(), 1)
var angle_offset = 2 * PI / count
var angle = PI / 2 # 90 deg initial offset
for button in buttons:
button.position = Vector2(final_radius, 0.0).rotated(angle) - (button.size / 2.0)
angle += angle_offset
func clear_menu():
for button in buttons:
button.queue_free()
func set_button_style(index, name, source):
if index < 0 or index > buttons.size() - 1:
return
buttons[index].set("theme_override_styles/%s" % name, get_theme_stylebox(source, "Button"))
func set_items(v):
items = v
if is_inside_tree():
populate_menu()
func set_selected_index(v):
set_button_style(selected_index, "normal", "normal")
selected_index = v
set_button_style(selected_index, "normal", "focus")
func set_radius(v):
radius = v
align()
func set_theme_source_node(v):
theme_source_node = v
for pie_menu in pie_menus:
if pie_menu:
pie_menu.theme_source_node = theme_source_node