Files
leonwww/flumi/addons/godot-flexbox/toolbar.gd
2025-08-02 14:18:14 +03:00

414 lines
15 KiB
GDScript

class_name EditorToolbar
extends HBoxContainer
const Icon = preload("icon.svg")
enum DirectionPreset {
Column,ColumnReverse,Row,RowReverse,Reverse
}
enum WrapPreset {
NoWrap,Wrap,WrapReverse
}
enum AlignContentPreset {
Auto,FlexStart,Center,FlexEnd,Stretch,Baseline,SpaceBetween,SpaceAround
}
enum JustifyPreset {
FlexStart,Center,FlexEnd,SpaceBetween,SpaceAround,SpaceEvenly
}
enum AlignItemsPreset {
Auto,FlexStart,Center,FlexEnd,Stretch,Baseline,SpaceBetween,SpaceAround
}
var plugin:EditorPlugin
var presets_button: EditorPopupButton
var container_h_picker
var container_v_picker
var presets = {}
var _current_node:FlexContainer
var _selection:EditorSelection
var undo_redo:EditorUndoRedoManager
var flex_picker:FlexPresetPicker
var EDSCALE = 1
func _init(p_editor_scale):
EDSCALE = p_editor_scale
presets_button = EditorPopupButton.new(EDSCALE)
presets_button.set_tooltip_text("Presets for the direction values of a Flexbox node.")
add_child(presets_button)
#
var presets_label = Label.new()
presets_label.text = "Flexbox Presets"
presets_button.get_popup_hbox().add_child(presets_label)
#set default direction
presets["flex_direction"] = 2
func _ready():
flex_picker = FlexPresetPicker.new(EDSCALE)
flex_picker.set_h_size_flags(SIZE_SHRINK_CENTER);
presets_button.get_popup_hbox().add_child(flex_picker)
flex_picker.flexbox_preset_selected.connect(_flexbox_preset_selected)
_selection = plugin.get_editor_interface().get_selection()
_selection.selection_changed.connect(_selection_changed)
_selection_changed()
func _exit_tree():
_selection.selection_changed.disconnect(_selection_changed)
func _selection_changed():
var nodes = _selection.get_selected_nodes()
if nodes.size()<=0:
_current_node = null
return
var node = nodes[0]
if node is FlexContainer:
_current_node =node
set_visible(true)
flex_picker.map_state(_current_node.state)
else:
_current_node = null
set_visible(false)
func _flexbox_preset_selected(p_category, p_preset, p_state):
var old_preset = presets.get(p_category, null)
#flex direction reverse
if p_category == "reverse":
p_category = "flex_direction"
old_preset = presets.get(p_category, null)
if p_state:
var is_row = old_preset == DirectionPreset.Row or old_preset == DirectionPreset.RowReverse
p_preset = DirectionPreset.RowReverse if is_row else DirectionPreset.ColumnReverse
else:#reverse
p_preset = DirectionPreset.Row if old_preset == DirectionPreset.RowReverse else DirectionPreset.Column
presets[p_category] = p_preset
else:
presets[p_category] = p_preset if p_state else -1
undo_redo.create_action(p_category)
undo_redo.add_do_method(_current_node, "edit_set_state", presets)
undo_redo.add_undo_method(_current_node, "edit_set_state", _current_node.edit_get_state())
undo_redo.commit_action()
func _notification(what):
match what:
NOTIFICATION_ENTER_TREE, NOTIFICATION_THEME_CHANGED:
pass
class EditorPopupButton extends Button:
var arrow_icon:Texture2D
var popup_panel:PopupPanel
var popup_vbox:VBoxContainer
func _init(EDSCALE):
flat = true
toggle_mode = true
focus_mode = Control.FOCUS_NONE
texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
var image = Icon.get_image()
image.resize(image.get_width(), image.get_width(),Image.INTERPOLATE_NEAREST)
var resize_icon = ImageTexture.new().create_from_image(image)
icon = resize_icon
popup_panel = PopupPanel.new()
popup_panel.theme_type_variation = "ControlEditorPopupPanel"
add_child(popup_panel)
popup_panel.about_to_popup.connect(_popup_visibility_changed.bind(true))
popup_panel.popup_hide.connect(_popup_visibility_changed.bind(false))
popup_vbox = VBoxContainer.new()
popup_panel.add_child(popup_vbox)
func _toggled(p_pressed):
if !p_pressed:
return
var size = get_size() * get_viewport().get_canvas_transform().get_scale()
popup_panel.size = Vector2(size.x, 0)
var gp = get_screen_position()
gp.y+= size.y
popup_panel.position = gp
popup_panel.popup()
func _popup_visibility_changed(p_visible):
button_pressed = p_visible
func _notification(what):
match what:
NOTIFICATION_ENTER_TREE, NOTIFICATION_THEME_CHANGED:
#arrow_icon = get_theme_icon("select_arrow", "Tree");
pass
NOTIFICATION_DRAW:
pass
#if is_instance_valid(arrow_icon):
#var arrow_pos = Vector2(26,0)
#arrow_pos.y = get_size().y / 2 - arrow_icon.get_height() / 2;
#draw_texture(arrow_icon, arrow_pos);
NOTIFICATION_VISIBILITY_CHANGED:
if is_visible_in_tree():
popup_panel.hide()
func get_popup_hbox():
return popup_vbox
class Space extends Control:
var ms = Vector2(3, 0)
func _init(x_space):
ms.x = x_space
func _get_minimum_size():
return ms
class EditorPresetPicker extends MarginContainer:
var grid_separation = 0
var preset_buttons = {}
var EDSCALE = 1
var BASE_SIZE = Vector2(32,32)
func _init(scale):
EDSCALE = scale
func _add_button(p_category, p_preset, b):
if preset_buttons.get(p_category) == null:
preset_buttons[p_category] = {}
preset_buttons[p_category][p_preset] = b
func _add_row_button(p_row, p_category, p_preset, p_name):
var b = Button.new()
b.auto_translate = false
b.toggle_mode = true
b.set_custom_minimum_size(BASE_SIZE * EDSCALE)
b.set_size(BASE_SIZE * EDSCALE)
b.set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER)
b.set_tooltip_text(p_name)
b.set_flat(true)
b.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
b.expand_icon = true
p_row.add_child(b)
b.pressed.connect(_preset_button_pressed.bind(p_category, p_preset))
_add_button(p_category, p_preset, b)
func _add_text_button(p_row, p_category, p_preset, p_name):
var b = Button.new()
b.toggle_mode = true
b.set("theme_override_font_sizes/font_size",12)
b.set_custom_minimum_size(BASE_SIZE * EDSCALE)
b.set_size(BASE_SIZE * EDSCALE)
b.set_text(p_name)
p_row.add_child(b)
b.pressed.connect(_preset_button_pressed.bind(p_category, p_preset))
_add_button(p_category, p_preset, b)
func _add_separator(p_box, p_separator):
p_separator.add_theme_constant_override("separation", grid_separation)
p_separator.set_custom_minimum_size(Vector2(1, 1))
p_box.add_child(p_separator)
func _preset_button_pressed(p_category, p_preset):
print("catgory: ", p_category, "preset: ", p_preset)
class FlexPresetPicker extends EditorPresetPicker:
signal flexbox_preset_selected
var state:Dictionary
const Category = {
Direction = "flex_direction",
DirectionReverse = "reverse",
Wrap = "flex_wrap",
Align = "align_items",
Justify = "justify_content",
AlignContent = "align_content",
}
const LABEL_WIDTH = 70
func _init(scale):
EDSCALE = scale
var main_vb = VBoxContainer.new()
main_vb.add_theme_constant_override("separation", grid_separation)
add_child(main_vb)
#direction
var dir_row = HBoxContainer.new()
dir_row.set_alignment(HBoxContainer.ALIGNMENT_BEGIN)
dir_row.add_theme_constant_override("separation", grid_separation)
main_vb.add_child(dir_row)
var direction_label = Label.new()
direction_label.set_text("Direction")
direction_label.custom_minimum_size.x = LABEL_WIDTH
dir_row.add_child(direction_label)
_add_row_button(dir_row, Category.Direction, DirectionPreset.Row, " Row ")
_add_row_button(dir_row, Category.Direction, DirectionPreset.Column, " Column ")
_add_separator(dir_row, VSeparator.new());
_add_row_button(dir_row, Category.DirectionReverse, DirectionPreset.Reverse, "Reverse");
#wrap
var wrap_row = HBoxContainer.new()
wrap_row.set_alignment(HBoxContainer.ALIGNMENT_BEGIN)
wrap_row.add_theme_constant_override("separation", grid_separation)
main_vb.add_child(wrap_row)
var wrap_label = Label.new()
wrap_label.set_text("Wrap")
wrap_label.custom_minimum_size.x = LABEL_WIDTH
wrap_row.add_child(wrap_label)
_add_row_button(wrap_row, Category.Wrap, WrapPreset.NoWrap, "NoWrap")
_add_row_button(wrap_row, Category.Wrap, WrapPreset.Wrap, "Wrap")
#align
var align_row = HBoxContainer.new()
align_row.set_alignment(HBoxContainer.ALIGNMENT_BEGIN)
align_row.add_theme_constant_override("separation", grid_separation)
main_vb.add_child(align_row)
var align_label = Label.new()
align_label.custom_minimum_size.x = LABEL_WIDTH
align_label.set_text("Align")
align_row.add_child(align_label)
_add_row_button(align_row, Category.Align, AlignItemsPreset.FlexStart, "Start");
_add_row_button(align_row, Category.Align, AlignItemsPreset.Center, "Center");
_add_row_button(align_row, Category.Align, AlignItemsPreset.FlexEnd, "End");
_add_row_button(align_row, Category.Align, AlignItemsPreset.Stretch, "Stretch");
_add_row_button(align_row, Category.Align, AlignItemsPreset.Baseline, "Baseline");
#justify
var justify_row = HBoxContainer.new()
justify_row.set_alignment(HBoxContainer.ALIGNMENT_BEGIN)
justify_row.add_theme_constant_override("separation", grid_separation)
main_vb.add_child(justify_row)
var justify_label = Label.new()
justify_label.custom_minimum_size.x = LABEL_WIDTH
justify_label.set_text("Justify")
justify_row.add_child(justify_label)
_add_row_button(justify_row, Category.Justify, JustifyPreset.FlexStart, "Start");
_add_row_button(justify_row, Category.Justify, JustifyPreset.Center, "Center");
_add_row_button(justify_row, Category.Justify, JustifyPreset.FlexEnd, "End");
_add_row_button(justify_row, Category.Justify, JustifyPreset.SpaceBetween, "SpaceBetween");
_add_row_button(justify_row, Category.Justify, JustifyPreset.SpaceAround, "SpaceAround");
_add_row_button(justify_row, Category.Justify, JustifyPreset.SpaceEvenly, "SpaceEvenly");
#
_add_separator(main_vb, HSeparator.new());
var content_row = HBoxContainer.new()
content_row.set_alignment(HBoxContainer.ALIGNMENT_BEGIN)
content_row.add_theme_constant_override("separation", grid_separation)
main_vb.add_child(content_row)
var content_label = Label.new()
content_label.custom_minimum_size.x = LABEL_WIDTH
content_label.set_text("Content")
content_row.add_child(content_label)
_add_row_button(content_row, Category.AlignContent, AlignContentPreset.FlexStart, "Start");
_add_row_button(content_row, Category.AlignContent, AlignContentPreset.Center, "Center");
_add_row_button(content_row, Category.AlignContent, AlignContentPreset.FlexEnd, "End");
_add_row_button(content_row, Category.AlignContent, AlignContentPreset.SpaceBetween, "SpaceBetween");
_add_row_button(content_row, Category.AlignContent, AlignContentPreset.SpaceAround, "SpaceAround");
_add_row_button(content_row, Category.AlignContent, AlignContentPreset.Stretch, "Stretch");
func map_state(p_state):
state = p_state
#reset all
for cat in preset_buttons:
var cat_btns = preset_buttons[cat]
for btn in cat_btns.values():
btn.button_pressed = false
#
for category in p_state:
var value = p_state[category]
if category == Category.Direction:
var is_reverse = value == DirectionPreset.RowReverse or value == DirectionPreset.ColumnReverse
if value == DirectionPreset.Row or value == DirectionPreset.RowReverse:
preset_buttons[Category.Direction][DirectionPreset.Row].button_pressed = true
else:
preset_buttons[Category.Direction][DirectionPreset.Column].button_pressed = true
preset_buttons[Category.DirectionReverse][DirectionPreset.Reverse].button_pressed = is_reverse
else:
preset_buttons[category][value].button_pressed = true
update_icons()
func is_reverse():
var direction = state[Category.Direction]
return direction == DirectionPreset.ColumnReverse or direction == DirectionPreset.RowReverse
func is_row():
var direction = state[Category.Direction]
return direction == DirectionPreset.RowReverse or direction == DirectionPreset.Row
func get_icon(icon):
return IconAssets.get_icon(icon)
func _preset_button_pressed(p_category, p_preset):
var update_icon = false
var select_btn = preset_buttons[p_category][p_preset]
for b in preset_buttons[p_category].values():
if b == select_btn:
continue
b.button_pressed = false
#
if p_category == "flex_direction":
var row_selected = preset_buttons[Category.Direction][DirectionPreset.Row].button_pressed
var column_selected = preset_buttons[Category.Direction][DirectionPreset.Column].button_pressed
if !row_selected && !column_selected:
select_btn.button_pressed = true
update_icon = true
elif p_category == "reverse":
update_icon = true
flexbox_preset_selected.emit(p_category, p_preset, select_btn.button_pressed)
if update_icon:
update_icons()
func _notification(what):
match what:
NOTIFICATION_ENTER_TREE,NOTIFICATION_THEME_CHANGED:
if state.is_empty(): return
update_icons()
func update_icons():
await get_tree().process_frame
var direction = "Row" if is_row() else "Column"
var reverse = is_reverse()
preset_buttons[Category.Direction][DirectionPreset.Row].icon = get_icon("DisplayFlexRow")
preset_buttons[Category.Direction][DirectionPreset.Column].icon = get_icon("DisplayFlexColumn")
preset_buttons[Category.DirectionReverse][DirectionPreset.Reverse].icon = get_icon("ArrowReverseIcon")
#
preset_buttons[Category.Wrap][WrapPreset.NoWrap].icon = get_icon("FlexWrapNoWrap%sIcon" % direction)
preset_buttons[Category.Wrap][WrapPreset.Wrap].icon = get_icon("FlexWrapWrap%sIcon" % direction)
#
preset_buttons[Category.Align][AlignItemsPreset.FlexStart].icon = get_icon("AlignItemsStart%sIcon" % direction)
preset_buttons[Category.Align][AlignItemsPreset.Center].icon = get_icon("AlignItemsCenter%sIcon" % direction)
preset_buttons[Category.Align][AlignItemsPreset.FlexEnd].icon = get_icon("AlignItemsEnd%sIcon" % direction)
preset_buttons[Category.Align][AlignItemsPreset.Stretch].icon = get_icon("AlignItemsStretch%sIcon" % direction)
preset_buttons[Category.Align][AlignItemsPreset.Baseline].icon = get_icon("AlignItemsBaseline%sIcon" % direction)
#
var justify_content_start_icon = get_icon("JustifyContentStart%sIcon" % direction)
var justify_content_end_icon = get_icon("JustifyContentEnd%sIcon" % direction)
preset_buttons[Category.Justify][JustifyPreset.Center].icon = get_icon("JustifyContentCenter%sIcon" % direction)
preset_buttons[Category.Justify][JustifyPreset.FlexStart].icon = justify_content_end_icon if reverse else justify_content_start_icon
preset_buttons[Category.Justify][JustifyPreset.FlexEnd].icon =justify_content_start_icon if reverse else justify_content_end_icon
preset_buttons[Category.Justify][JustifyPreset.SpaceBetween].icon = get_icon("JustifyContentSpaceBetween%sIcon" % direction)
preset_buttons[Category.Justify][JustifyPreset.SpaceAround].icon = get_icon("JustifyContentSpaceAround%sIcon" % direction)
preset_buttons[Category.Justify][JustifyPreset.SpaceEvenly].icon = get_icon("JustifyContentSpaceEvenly%sIcon" % direction)
#
preset_buttons[Category.AlignContent][AlignContentPreset.FlexStart].icon = get_icon("AlignContentStart%sIcon" % direction)
preset_buttons[Category.AlignContent][AlignContentPreset.Center].icon = get_icon("AlignContentCenter%sIcon" % direction)
preset_buttons[Category.AlignContent][AlignContentPreset.FlexEnd].icon = get_icon("AlignContentEnd%sIcon" % direction)
preset_buttons[Category.AlignContent][AlignContentPreset.SpaceAround].icon = get_icon("AlignContentAround%sIcon" % direction)
preset_buttons[Category.AlignContent][AlignContentPreset.SpaceBetween].icon = get_icon("AlignContentBetween%sIcon" % direction)
preset_buttons[Category.AlignContent][AlignContentPreset.Stretch].icon = get_icon("AlignContentStretch%sIcon" % direction)