Initial commit
This commit is contained in:
174
scenes/scripts/dynamic_tabs.gd
Normal file
174
scenes/scripts/dynamic_tabs.gd
Normal file
@@ -0,0 +1,174 @@
|
||||
extends Control
|
||||
|
||||
enum CloseButtonPolicy {
|
||||
SHOW_NEVER = 0,
|
||||
SHOW_ACTIVE_ONLY = 1,
|
||||
SHOW_ALWAYS = 2
|
||||
}
|
||||
|
||||
enum TabAlign {
|
||||
ALIGN_LEFT = 0,
|
||||
ALIGN_CENTER = 1,
|
||||
ALIGN_RIGHT = 2
|
||||
}
|
||||
|
||||
export var current_tab := 0 setget _set_current_tab, _get_current_tab
|
||||
export var drag_to_rearrange_enabled := false setget _set_drag_to_rearrange_enabled
|
||||
export var drag_out_enabled := false
|
||||
export var show_tabs := true setget _set_show_tabs
|
||||
export(CloseButtonPolicy) var tab_close_display_policy := CloseButtonPolicy.SHOW_NEVER setget _set_tab_close_display_policy
|
||||
export(TabAlign) var tab_align := TabAlign.ALIGN_CENTER setget _set_tab_align
|
||||
|
||||
var __mouse_was_inside := false
|
||||
|
||||
#############
|
||||
# overrides #
|
||||
#############
|
||||
func _ready() -> void:
|
||||
$tabs.connect("tab_changed", self, "_on_tabs_tab_changed")
|
||||
$tabs.connect("tab_close", self, "_on_tabs_tab_close")
|
||||
$tabs.connect("reposition_active_tab_request", self, "_on_tabs_reposition_active_tab_request")
|
||||
$tabs.connect("gui_input", self, "_on_tabs_gui_input")
|
||||
|
||||
set_process(false) # only enabled for dragging
|
||||
|
||||
func _process(delta : float) -> void:
|
||||
if !Input.is_mouse_button_pressed(BUTTON_LEFT):
|
||||
__stop_tab_dragging()
|
||||
return
|
||||
var mouse_pos := get_viewport().get_mouse_position()
|
||||
var mouse_is_inside : bool = $tabs.get_global_rect().has_point(mouse_pos)
|
||||
if mouse_is_inside == __mouse_was_inside:
|
||||
return
|
||||
__mouse_was_inside = mouse_is_inside
|
||||
if mouse_is_inside:
|
||||
emit_signal("tab_dragged_in", _get_current_tab(), mouse_pos)
|
||||
else:
|
||||
emit_signal("tab_dragged_out", _get_current_tab(), mouse_pos)
|
||||
|
||||
################
|
||||
# public stuff #
|
||||
################
|
||||
func add_tab(title : String, control : Control, icon : Texture = null) -> int:
|
||||
$tabs.add_tab(title, icon)
|
||||
$container.add_child(control)
|
||||
|
||||
var tab := control.get_position_in_parent()
|
||||
emit_signal("tab_added", tab)
|
||||
return tab
|
||||
|
||||
func move_tab(from : int, to : int) -> void:
|
||||
$tabs.move_tab(from, to)
|
||||
$container.move_child($container.get_child(from), to)
|
||||
|
||||
func remove_tab(tab : int) -> void:
|
||||
if tab == current_tab && __is_tab_dragging():
|
||||
__stop_tab_dragging()
|
||||
|
||||
$tabs.remove_tab(tab)
|
||||
$container.remove_child($container.get_child(tab))
|
||||
emit_signal("tab_removed", tab)
|
||||
|
||||
func find_tab(control : Control) -> int:
|
||||
for tab in range(get_tab_count()):
|
||||
if get_tab_control(tab) == control:
|
||||
return tab
|
||||
return -1
|
||||
|
||||
func remove_all_tabs() -> void:
|
||||
while $tabs.get_tab_count() > 0:
|
||||
$tabs.remove_tab(0)
|
||||
while $container.get_child_count() > 0:
|
||||
$container.remove_child($container.get_child(0))
|
||||
|
||||
func get_tab_control(tab : int) -> Control:
|
||||
return $container.get_tab_control(tab)
|
||||
|
||||
func get_tab_title(tab : int) -> String:
|
||||
return $tabs.get_tab_title(tab)
|
||||
|
||||
func get_tabs() -> Array:
|
||||
return $container.get_children()
|
||||
|
||||
func get_tab_count() -> int:
|
||||
return $container.get_child_count()
|
||||
|
||||
func get_tab_rect(tab : int) -> Rect2:
|
||||
var rect : Rect2 = $tabs.get_tab_rect(tab)
|
||||
return $tabs.get_transform().xform_inv(rect)
|
||||
|
||||
#################
|
||||
# private stuff #
|
||||
#################
|
||||
func __handle_tabs_gui_input(event : InputEvent) -> void:
|
||||
if drag_out_enabled \
|
||||
&& event is InputEventMouseButton \
|
||||
&& event.pressed \
|
||||
&& event.button_index == BUTTON_LEFT:
|
||||
yield(get_tree(), "idle_frame") # wait for the original control to handle the input
|
||||
|
||||
var tab_rect : Rect2 = $tabs.get_tab_rect(_get_current_tab())
|
||||
if tab_rect.has_point(event.position):
|
||||
__start_tab_dragging()
|
||||
|
||||
func __start_tab_dragging() -> void:
|
||||
__mouse_was_inside = true
|
||||
set_process(true)
|
||||
|
||||
func __stop_tab_dragging() -> void:
|
||||
set_process(false)
|
||||
|
||||
func __is_tab_dragging() -> bool:
|
||||
return is_processing()
|
||||
|
||||
###########
|
||||
# setters #
|
||||
###########
|
||||
func _set_current_tab(tab : int) -> void:
|
||||
current_tab = tab
|
||||
$tabs.current_tab = current_tab
|
||||
|
||||
func _get_current_tab() -> int:
|
||||
return $tabs.current_tab
|
||||
|
||||
func _set_drag_to_rearrange_enabled(enabled : bool) -> void:
|
||||
drag_to_rearrange_enabled = enabled
|
||||
$tabs.drag_to_rearrange_enabled = enabled
|
||||
|
||||
func _set_tab_close_display_policy(policy : int) -> void:
|
||||
tab_close_display_policy = policy
|
||||
$tabs.tab_close_display_policy = policy
|
||||
|
||||
func _set_tab_align(align : int) -> void:
|
||||
tab_align = align
|
||||
$tabs.tab_align = align
|
||||
|
||||
func _set_show_tabs(show : bool) -> void:
|
||||
show_tabs = show
|
||||
$tabs.visible = show
|
||||
|
||||
###################
|
||||
# signal handlers #
|
||||
###################
|
||||
func _on_tabs_tab_changed(tab : int) -> void:
|
||||
current_tab = tab
|
||||
$container.current_tab = tab
|
||||
|
||||
func _on_tabs_tab_close(tab : int) -> void:
|
||||
emit_signal("tab_close", tab)
|
||||
|
||||
func _on_tabs_reposition_active_tab_request(to : int) -> void:
|
||||
$container.move_child($container.get_child(_get_current_tab()), to)
|
||||
current_tab = to
|
||||
|
||||
func _on_tabs_gui_input(event : InputEvent) -> void:
|
||||
__handle_tabs_gui_input(event)
|
||||
|
||||
###########
|
||||
# signals #
|
||||
###########
|
||||
signal tab_added(tab)
|
||||
signal tab_removed(tab)
|
||||
signal tab_close(tab)
|
||||
signal tab_dragged_out(tab, position)
|
||||
signal tab_dragged_in(tab, position)
|
||||
31
scenes/scripts/editor_file_edit.gd
Normal file
31
scenes/scripts/editor_file_edit.gd
Normal file
@@ -0,0 +1,31 @@
|
||||
tool
|
||||
extends Control
|
||||
|
||||
onready var line_edit := $line_edit
|
||||
onready var button := $button
|
||||
onready var file_dialog := $file_dialog
|
||||
|
||||
export(String, FILE) var selected_file setget _set_selected_file
|
||||
var file_filter = ""
|
||||
|
||||
func _ready():
|
||||
line_edit.text = selected_file
|
||||
|
||||
func _set_selected_file(value : String):
|
||||
if value != selected_file:
|
||||
selected_file = value
|
||||
if line_edit:
|
||||
line_edit.text = selected_file
|
||||
|
||||
func _on_button_pressed():
|
||||
file_dialog.clear_filters()
|
||||
file_dialog.add_filter(file_filter)
|
||||
file_dialog.filename = selected_file
|
||||
file_dialog.popup_centered_ratio(0.5)
|
||||
|
||||
func _on_file_dialog_file_selected(path : String):
|
||||
if path != selected_file:
|
||||
_set_selected_file(path)
|
||||
emit_signal("selected_file_changed")
|
||||
|
||||
signal selected_file_changed()
|
||||
31
scenes/scripts/editor_node_edit.gd
Normal file
31
scenes/scripts/editor_node_edit.gd
Normal file
@@ -0,0 +1,31 @@
|
||||
tool
|
||||
extends Control
|
||||
|
||||
onready var line_edit := $line_edit
|
||||
onready var button := $button
|
||||
onready var node_selection_dialog := $node_selection_dialog
|
||||
|
||||
export(NodePath) var selected_node setget _set_selected_node
|
||||
var node_type_filter = Node
|
||||
|
||||
func _ready():
|
||||
line_edit.text = str(selected_node)
|
||||
|
||||
func _set_selected_node(value : NodePath):
|
||||
if value != selected_node:
|
||||
selected_node = value
|
||||
if line_edit:
|
||||
line_edit.text = str(selected_node)
|
||||
|
||||
func _on_button_pressed():
|
||||
node_selection_dialog.get_editor_node_tree().node_type_filter = node_type_filter
|
||||
node_selection_dialog.get_editor_node_tree().selected_node = selected_node
|
||||
node_selection_dialog.popup_centered_ratio(0.5)
|
||||
|
||||
func _on_node_selection_dialog_confirmed():
|
||||
var tree = node_selection_dialog.get_editor_node_tree()
|
||||
if tree.selected_node != selected_node:
|
||||
_set_selected_node(tree.selected_node)
|
||||
emit_signal("selected_node_changed")
|
||||
|
||||
signal selected_node_changed()
|
||||
371
scenes/scripts/flexview.gd
Normal file
371
scenes/scripts/flexview.gd
Normal file
@@ -0,0 +1,371 @@
|
||||
extends Control
|
||||
|
||||
enum SplitLocation {
|
||||
TOP,
|
||||
BOTTOM,
|
||||
LEFT,
|
||||
RIGHT
|
||||
}
|
||||
enum DropPosition {
|
||||
TOP,
|
||||
BOTTOM,
|
||||
LEFT,
|
||||
RIGHT,
|
||||
CENTER
|
||||
}
|
||||
|
||||
const FlexViewSplitter = preload("res://addons/de.mewin.gduibasics/scenes/scripts/flexview_splitter.gd")
|
||||
|
||||
const META_UNIQUE_TYPE = "__flexview_unique_type__"
|
||||
const META_UNIQUE_KEY = "__flexview_unique_key__"
|
||||
const META_VIEW_TITLE = "__flexview_title__"
|
||||
const META_LAST_DROP_TARGET = "__flexview_last_drop_target__"
|
||||
const GROUP_NAME = "__flexview__"
|
||||
const INDICATOR_ANIM_DURATION = 0.1
|
||||
const __HANDLERS_TO_COPY = ["view_added", "view_removed", "view_closing"]
|
||||
|
||||
onready var _rect_left := $drop_indicator/rect_left
|
||||
onready var _rect_right := $drop_indicator/rect_right
|
||||
onready var _rect_top := $drop_indicator/rect_top
|
||||
onready var _rect_bottom := $drop_indicator/rect_bottom
|
||||
onready var _rect_center := $drop_indicator/rect_center
|
||||
onready var _drop_indicator := $drop_indicator/drop_indicator
|
||||
onready var _tween := $drop_indicator/tween
|
||||
|
||||
var current_view := 0 setget _set_current_view, _get_current_view
|
||||
export var show_tabs := true setget _set_show_tabs
|
||||
|
||||
var __drop_position := -1
|
||||
|
||||
#############
|
||||
# overrides #
|
||||
#############
|
||||
func _ready() -> void:
|
||||
set_process(false) # only used for the drop indicator
|
||||
add_to_group(GROUP_NAME)
|
||||
$tabs.show_tabs = show_tabs
|
||||
|
||||
func _process(delta : float) -> void:
|
||||
var mouse_pos := get_viewport().get_mouse_position()
|
||||
var drop_position : int = DropPosition.CENTER
|
||||
|
||||
if get_view_count() > 0: # only allow splitting if there is at least one view
|
||||
var my_rect := get_global_rect()
|
||||
var distances := [
|
||||
(mouse_pos.y - my_rect.position.y) / my_rect.size.y,
|
||||
(my_rect.position.y + my_rect.size.y - mouse_pos.y) / my_rect.size.y,
|
||||
(mouse_pos.x - my_rect.position.x) / my_rect.size.x,
|
||||
(my_rect.position.x + my_rect.size.x - mouse_pos.x) / my_rect.size.x
|
||||
]
|
||||
var idx := GDBMath.min_element(distances)
|
||||
if distances[idx] < 0.25:
|
||||
drop_position = idx
|
||||
|
||||
if __drop_position == drop_position:
|
||||
return
|
||||
__drop_position = drop_position
|
||||
|
||||
var new_rect := __get_drop_rect(drop_position)
|
||||
_tween.stop(_drop_indicator, "rect_size")
|
||||
_tween.stop(_drop_indicator, "rect_position")
|
||||
_tween.interpolate_property(_drop_indicator, "rect_size", _drop_indicator.rect_size, new_rect.size, INDICATOR_ANIM_DURATION)
|
||||
_tween.interpolate_property(_drop_indicator, "rect_position", _drop_indicator.rect_position, new_rect.position, INDICATOR_ANIM_DURATION)
|
||||
_tween.start()
|
||||
|
||||
################
|
||||
# public stuff #
|
||||
################
|
||||
func add_view(title : String, control : Control, make_current := true) -> int:
|
||||
control.set_meta(META_VIEW_TITLE, title)
|
||||
|
||||
var view : int = $tabs.add_tab(get_view_title_from_control(control), control)
|
||||
emit_signal("view_added", view, self)
|
||||
if make_current:
|
||||
_set_current_view(view)
|
||||
|
||||
# support for control-controlled (lel) titles
|
||||
if control.has_signal("flexview_title_changed"):
|
||||
control.connect("flexview_title_changed", self, "_on_flexview_title_changed")
|
||||
if control.has_signal("flexview_close_me"):
|
||||
control.connect("flexview_close_me", self, "_on_flexview_close_me")
|
||||
|
||||
return view
|
||||
|
||||
func remove_view(view : int) -> void:
|
||||
var control := get_view_control(view)
|
||||
if control:
|
||||
GDBUtility.disconnect_all(control, self)
|
||||
|
||||
$tabs.remove_tab(view)
|
||||
emit_signal("view_removed", view, self)
|
||||
|
||||
if get_view_count() == 0 && get_parent() is FlexViewSplitter:
|
||||
self.queue_free()
|
||||
|
||||
func close_view(view : int) -> void:
|
||||
emit_signal("view_closing", view, self)
|
||||
|
||||
func remove_all_views() -> void:
|
||||
for view in range(get_view_count() - 1, -1, -1):
|
||||
remove_view(view)
|
||||
|
||||
func find_view(control : Control) -> int:
|
||||
return $tabs.find_tab(control)
|
||||
|
||||
func close_all_views() -> void:
|
||||
for view in range(get_view_count() - 1, -1, -1):
|
||||
close_view(view)
|
||||
|
||||
func set_view_title(view : int, title : String) -> void:
|
||||
$tabs.set_tab_title(view, title)
|
||||
|
||||
func get_view_control(view : int) -> Control:
|
||||
return $tabs.get_tab_control(view)
|
||||
|
||||
func get_view_title(view : int) -> String:
|
||||
return $tabs.get_tab_title(view)
|
||||
|
||||
func get_views() -> Array:
|
||||
return $tabs.get_tabs()
|
||||
|
||||
func get_view_count() -> int:
|
||||
return $tabs.get_tab_count()
|
||||
|
||||
func split_view(placement : int, title : String, view : Control) -> Control:
|
||||
var parent := get_parent()
|
||||
var self_first : bool = (placement == SplitLocation.BOTTOM || placement == SplitLocation.RIGHT)
|
||||
|
||||
# add splitter
|
||||
var splitter : SplitContainer
|
||||
if placement == SplitLocation.TOP || placement == SplitLocation.BOTTOM:
|
||||
splitter = preload("res://addons/de.mewin.gduibasics/scenes/flexview_vspliter.tscn").instance()
|
||||
else:
|
||||
splitter = preload("res://addons/de.mewin.gduibasics/scenes/flexview_hspliter.tscn").instance()
|
||||
parent.add_child_below_node(self, splitter)
|
||||
parent.remove_child(self)
|
||||
|
||||
GDBUIUtility.copy_size(splitter, self)
|
||||
|
||||
# add self first (if applicable)
|
||||
if self_first:
|
||||
splitter.add_child(self)
|
||||
|
||||
# create new flex container for other half
|
||||
var new_container : Control = load("res://addons/de.mewin.gduibasics/scenes/flexview.tscn").instance()
|
||||
new_container.add_view(title, view)
|
||||
__copy_signal_handlers(new_container)
|
||||
splitter.add_child(new_container)
|
||||
|
||||
# add self second (if applicable)
|
||||
if !self_first:
|
||||
splitter.add_child(self)
|
||||
|
||||
if placement == SplitLocation.TOP || placement == SplitLocation.BOTTOM:
|
||||
splitter.split_offset = 0.5 * splitter.rect_size.y
|
||||
else:
|
||||
splitter.split_offset = 0.5 * splitter.rect_size.x
|
||||
splitter.clamp_split_offset()
|
||||
return new_container
|
||||
|
||||
################
|
||||
# static stuff #
|
||||
################
|
||||
static func get_view_title_from_control(control : Control) -> String:
|
||||
var prop = control.get("flexview_title")
|
||||
if prop is String:
|
||||
return prop
|
||||
|
||||
var title = control.get_meta(META_VIEW_TITLE)
|
||||
if title is String:
|
||||
return title
|
||||
return control.name
|
||||
|
||||
static func get_all_flex_views() -> Array:
|
||||
return GDBUtility.get_scene_tree().get_nodes_in_group(GROUP_NAME)
|
||||
|
||||
static func get_all_views() -> Array:
|
||||
var views := []
|
||||
for flex_view in get_all_flex_views():
|
||||
views.append_array(flex_view.get_views())
|
||||
return views
|
||||
|
||||
static func get_top_flex_view() -> Control:
|
||||
# can't use GDBUtility.find_node_by_type as we cannot use our own type
|
||||
var all_views := get_all_flex_views()
|
||||
var top_view : Control = null
|
||||
for flex_view in all_views:
|
||||
if top_view == null || top_view.is_greater_than(flex_view):
|
||||
top_view = flex_view
|
||||
return top_view
|
||||
|
||||
static func add_view_anywhere(title : String, view : Control, make_current := true) -> Control:
|
||||
var flex_view := _get_last_drop_target()
|
||||
if flex_view == null:
|
||||
flex_view = get_top_flex_view()
|
||||
if flex_view != null:
|
||||
flex_view.add_view(title, view, make_current)
|
||||
return flex_view
|
||||
|
||||
static func add_unique_view_anywhere(title : String, view : Control, type : String, key, make_current := true) -> Control:
|
||||
var flex_view := add_view_anywhere(title, view, make_current)
|
||||
if flex_view == null: # failed
|
||||
return null
|
||||
view.set_meta(META_UNIQUE_TYPE, type)
|
||||
view.set_meta(META_UNIQUE_KEY, key)
|
||||
return flex_view
|
||||
|
||||
static func remove_all_views_anywhere() -> void:
|
||||
for flex_view in get_all_flex_views():
|
||||
flex_view.remove_all_views()
|
||||
|
||||
static func find_unique_view(type : String, key) -> Control:
|
||||
for view_ctrl in get_all_views():
|
||||
if view_ctrl.has_meta(META_UNIQUE_TYPE) && view_ctrl.has_meta(META_UNIQUE_KEY)\
|
||||
&& view_ctrl.get_meta(META_UNIQUE_TYPE) == type && view_ctrl.get_meta(META_UNIQUE_KEY) == key:
|
||||
return view_ctrl
|
||||
return null
|
||||
|
||||
static func find_flex_view(view_control : Control) -> Control:
|
||||
var my_script : Script = load("res://addons/de.mewin.gduibasics/scripts/types/controls/flexview.gd")
|
||||
return GDBUtility.find_parent_with_type(view_control, my_script) as Control
|
||||
|
||||
static func make_view_current(view_control : Control) -> void:
|
||||
var flex_view := find_flex_view(view_control)
|
||||
if flex_view:
|
||||
flex_view.current_view = view_control.get_position_in_parent()
|
||||
|
||||
static func _set_last_drop_target(flex_view : Control) -> void:
|
||||
flex_view.get_tree().set_meta(META_LAST_DROP_TARGET, flex_view.get_path())
|
||||
|
||||
static func _get_last_drop_target() -> Control:
|
||||
var tree := GDBUtility.get_scene_tree()
|
||||
if tree.has_meta(META_LAST_DROP_TARGET):
|
||||
var flex_view_path := tree.get_meta(META_LAST_DROP_TARGET) as NodePath
|
||||
if flex_view_path:
|
||||
var flex_view := tree.root.get_node_or_null(flex_view_path) as Control
|
||||
return flex_view
|
||||
return null
|
||||
|
||||
#################
|
||||
# private stuff #
|
||||
#################
|
||||
func _start_dropping() -> void:
|
||||
__drop_position = -1
|
||||
_drop_indicator.visible = true
|
||||
_tween.stop_all()
|
||||
_tween.interpolate_property(_drop_indicator, "modulate", Color.transparent, Color.white, INDICATOR_ANIM_DURATION)
|
||||
_tween.start()
|
||||
set_process(true)
|
||||
|
||||
func _stop_dropping() -> void:
|
||||
_tween.stop_all()
|
||||
_tween.interpolate_property(_drop_indicator, "modulate", Color.white, Color.transparent, INDICATOR_ANIM_DURATION)
|
||||
_tween.start()
|
||||
set_process(false)
|
||||
|
||||
yield(get_tree().create_timer(INDICATOR_ANIM_DURATION), "timeout")
|
||||
if !is_processing(): # mOybe we are dragging again
|
||||
_drop_indicator.visible = false
|
||||
|
||||
func _drop(view : Control) -> void:
|
||||
_stop_dropping()
|
||||
|
||||
var title := get_view_title_from_control(view)
|
||||
if __drop_position != DropPosition.CENTER:
|
||||
var new_container := split_view(__drop_position, title, view)
|
||||
_set_last_drop_target(new_container)
|
||||
else:
|
||||
add_view(title, view)
|
||||
_set_last_drop_target(self)
|
||||
|
||||
func __make_floating_tab(tab : int) -> void:
|
||||
var window : WindowDialog = load("res://addons/de.mewin.gduibasics/scenes/flexview_window.tscn").instance()
|
||||
get_tree().root.add_child(window)
|
||||
|
||||
# remove tab
|
||||
var tab_ctrl : Control = $tabs.get_tab_control(tab)
|
||||
remove_view(tab)
|
||||
|
||||
# add to new window
|
||||
var title = get_view_title_from_control(tab_ctrl)
|
||||
window.flex_view.add_view(title, tab_ctrl)
|
||||
__copy_signal_handlers(window.flex_view)
|
||||
|
||||
# place window
|
||||
var vp_size := get_viewport().size
|
||||
var mouse_pos := get_viewport().get_mouse_position()
|
||||
var win_size := vp_size * Vector2(0.5, 0.5)
|
||||
var min_size := window.get_combined_minimum_size()
|
||||
var title_height := window.get_constant("title_height")
|
||||
|
||||
window.rect_size = Vector2(max(win_size.x, min_size.x), max(win_size.y, min_size.y))
|
||||
window.rect_position = mouse_pos - Vector2(0.5 * window.rect_size.x, -0.5 * title_height)
|
||||
window.visible = true
|
||||
|
||||
# emulate mouse down for dragging the window
|
||||
var event := InputEventMouseButton.new()
|
||||
event.button_index = BUTTON_LEFT
|
||||
event.pressed = true
|
||||
event.button_mask = BUTTON_MASK_LEFT
|
||||
event.position = mouse_pos
|
||||
event.global_position = mouse_pos
|
||||
Input.parse_input_event(event)
|
||||
|
||||
func __get_drop_rect(drop_position : int) -> Rect2:
|
||||
var full_rect := Rect2(Vector2(), rect_size)
|
||||
match drop_position:
|
||||
DropPosition.TOP:
|
||||
return full_rect.grow_individual(0.0, 0.0, 0.0, -0.5 * full_rect.size.y)
|
||||
DropPosition.BOTTOM:
|
||||
return full_rect.grow_individual(0.0, -0.5 * full_rect.size.y, 0.0, 0.0)
|
||||
DropPosition.LEFT:
|
||||
return full_rect.grow_individual(0.0, 0.0, -0.5 * full_rect.size.x, 0.0)
|
||||
DropPosition.RIGHT:
|
||||
return full_rect.grow_individual(-0.5 * full_rect.size.x, 0.0, 0.0, 0.0)
|
||||
return full_rect
|
||||
|
||||
func __copy_signal_handlers(other_view : Control) -> void:
|
||||
GDBUtility.copy_all_signal_handlers(other_view, self, __HANDLERS_TO_COPY)
|
||||
|
||||
#####################
|
||||
# setters & getters #
|
||||
#####################
|
||||
func _set_current_view(val : int) -> void:
|
||||
$tabs.current_tab = val
|
||||
emit_signal("current_view_changed", self)
|
||||
|
||||
func _get_current_view() -> int:
|
||||
return $tabs.current_tab
|
||||
|
||||
func _set_show_tabs(show : bool) -> void:
|
||||
show_tabs = show
|
||||
if $tabs:
|
||||
$tabs.show_tabs = show
|
||||
|
||||
###################
|
||||
# signal handlers #
|
||||
###################
|
||||
func _on_tabs_tab_dragged_out(tab : int, position : Vector2) -> void:
|
||||
__make_floating_tab(tab)
|
||||
|
||||
func _on_tabs_tab_close(tab : int):
|
||||
close_view(tab)
|
||||
|
||||
func _on_flexview_title_changed(control : Control) -> void:
|
||||
var view := find_view(control)
|
||||
var title = control.get("flexview_title")
|
||||
if view >= 0 && title is String:
|
||||
set_view_title(view, title)
|
||||
|
||||
func _on_flexview_close_me(control : Control) -> void:
|
||||
var view := find_view(control)
|
||||
if view >= 0:
|
||||
remove_view(view)
|
||||
|
||||
###########
|
||||
# signals #
|
||||
###########
|
||||
signal view_added(view, flex_view)
|
||||
signal view_removed(view, flex_view)
|
||||
signal view_closing(view, flex_view)
|
||||
signal current_view_changed(flex_view)
|
||||
38
scenes/scripts/flexview_splitter.gd
Normal file
38
scenes/scripts/flexview_splitter.gd
Normal file
@@ -0,0 +1,38 @@
|
||||
extends SplitContainer
|
||||
|
||||
#############
|
||||
# overrides #
|
||||
#############
|
||||
func add_child(child : Node, legible_unique_name := false) -> void:
|
||||
.add_child(child, legible_unique_name)
|
||||
|
||||
child.connect("tree_exited", self, "_on_child_tree_exited")
|
||||
|
||||
#################
|
||||
# private stuff #
|
||||
#################
|
||||
func __try_collapse() -> void:
|
||||
var count := get_child_count()
|
||||
if count == 0: # nothing left? I can go now
|
||||
self.queue_free()
|
||||
return
|
||||
elif count > 1: # more than one? I have to stay
|
||||
return
|
||||
|
||||
# exactly one? replace me with them
|
||||
var child := get_child(0)
|
||||
remove_child(child)
|
||||
|
||||
get_parent().add_child_below_node(self, child)
|
||||
GDBUIUtility.copy_size(child, self)
|
||||
self.queue_free()
|
||||
|
||||
###################
|
||||
# signal handlers #
|
||||
###################
|
||||
func _on_child_tree_exited() -> void:
|
||||
if !get_tree():
|
||||
return
|
||||
|
||||
yield(get_tree(), "idle_frame") # wait until child is actually gone
|
||||
__try_collapse()
|
||||
141
scenes/scripts/flexview_window.gd
Normal file
141
scenes/scripts/flexview_window.gd
Normal file
@@ -0,0 +1,141 @@
|
||||
extends WindowDialog
|
||||
|
||||
const FlexView = preload("res://addons/de.mewin.gduibasics/scenes/scripts/flexview.gd")
|
||||
|
||||
var flex_view : FlexView setget _set_flex_view, _get_flex_view
|
||||
|
||||
var __drop_target : FlexView = null
|
||||
var __is_dragging := false
|
||||
|
||||
#############
|
||||
# overrides #
|
||||
#############
|
||||
func _ready() -> void:
|
||||
set_process(false) # only used to stop dropping when mouse button is lifted
|
||||
__update_title()
|
||||
|
||||
func _process(delta : float) -> void:
|
||||
if !Input.is_mouse_button_pressed(BUTTON_LEFT):
|
||||
__drop()
|
||||
|
||||
func _input(event : InputEvent) -> void:
|
||||
if event is InputEventKey && event.scancode == KEY_SHIFT && __is_dragging:
|
||||
__update_dragging()
|
||||
|
||||
#################
|
||||
# private stuff #
|
||||
#################
|
||||
func __update_view_count() -> void:
|
||||
var child := __get_flex_child()
|
||||
if !(child is FlexView):
|
||||
return
|
||||
|
||||
var count : int = child.get_view_count()
|
||||
if count == 0:
|
||||
self.queue_free()
|
||||
return
|
||||
else:
|
||||
# child.show_tabs = (count > 1)
|
||||
pass
|
||||
|
||||
func __update_dragging() -> void:
|
||||
var child := __get_flex_child()
|
||||
if !(child is FlexView) || child.get_view_count() > 1:
|
||||
set_process(false)
|
||||
__is_dragging = false
|
||||
return
|
||||
|
||||
__is_dragging = true
|
||||
set_process(true)
|
||||
|
||||
# dragging with shift disables dropping
|
||||
if Input.is_key_pressed(KEY_SHIFT):
|
||||
__set_drop_target(null)
|
||||
return
|
||||
|
||||
var mouse_pos := get_viewport().get_mouse_position()
|
||||
var flex_views := get_tree().get_nodes_in_group(FlexView.GROUP_NAME)
|
||||
var views_under_cursor := []
|
||||
|
||||
for view in flex_views:
|
||||
if !self.is_a_parent_of(view) && view.visible && view.get_global_rect().has_point(mouse_pos):
|
||||
views_under_cursor.append(view)
|
||||
|
||||
if views_under_cursor.empty():
|
||||
__set_drop_target(null)
|
||||
return
|
||||
|
||||
# find first in tree
|
||||
var target : FlexView = views_under_cursor[0]
|
||||
for i in range(1, views_under_cursor.size()):
|
||||
var node : FlexView = views_under_cursor[i]
|
||||
if GDBUtility.control_is_in_front_of(node, target):
|
||||
target = node
|
||||
__set_drop_target(target)
|
||||
|
||||
func __set_drop_target(target : FlexView) -> void:
|
||||
if target == __drop_target:
|
||||
return
|
||||
set_process(is_instance_valid(__drop_target)) # process to check if mouse button is lifted
|
||||
|
||||
if is_instance_valid(__drop_target):
|
||||
__drop_target._stop_dropping()
|
||||
__drop_target = target
|
||||
if is_instance_valid(__drop_target):
|
||||
__drop_target._start_dropping()
|
||||
|
||||
func __drop() -> void:
|
||||
var child := __get_flex_child()
|
||||
set_process(false)
|
||||
__is_dragging = false
|
||||
|
||||
if !is_instance_valid(__drop_target) || !(child is FlexView) || child.get_view_count() != 1:
|
||||
return
|
||||
|
||||
var control : Control = child.get_view_control(0)
|
||||
child.remove_view(0)
|
||||
__drop_target._drop(control)
|
||||
self.queue_free()
|
||||
|
||||
func __update_title() -> void:
|
||||
var child := __get_flex_child()
|
||||
if !(child is FlexView):
|
||||
window_title = ""
|
||||
elif child.get_view_count() > 0:
|
||||
window_title = child.get_view_title(child.current_view)
|
||||
|
||||
func __get_flex_child() -> Control:
|
||||
return get_child(1) as Control # TODO: something better (and safer) than a fixed index
|
||||
|
||||
func __close_requested() -> void:
|
||||
for flex_view in GDBUtility.find_nodes_by_type(self, FlexView):
|
||||
flex_view.close_all_views()
|
||||
|
||||
#####################
|
||||
# setters & getters #
|
||||
#####################
|
||||
func _set_flex_view(val) -> void:
|
||||
assert(0, "Cannot set this.")
|
||||
|
||||
func _get_flex_view() -> FlexView:
|
||||
return $flex_view as FlexView
|
||||
|
||||
###################
|
||||
# signal handlers #
|
||||
###################
|
||||
func _on_flex_view_view_added(view : int, flex_view : FlexView) -> void:
|
||||
__update_view_count()
|
||||
|
||||
func _on_flex_view_view_removed(view : int, flex_view : FlexView) -> void:
|
||||
__update_view_count()
|
||||
|
||||
func _on_flexview_window_item_rect_changed() -> void:
|
||||
__update_dragging()
|
||||
|
||||
func _on_flex_view_current_view_changed(flex_view : FlexView):
|
||||
__update_title()
|
||||
|
||||
func _on_flexview_window_visibility_changed():
|
||||
if !visible:
|
||||
__close_requested()
|
||||
visible = true
|
||||
100
scenes/scripts/line_edit_suggestion_popup.gd
Normal file
100
scenes/scripts/line_edit_suggestion_popup.gd
Normal file
@@ -0,0 +1,100 @@
|
||||
extends Popup
|
||||
|
||||
enum Position {
|
||||
ABOVE,
|
||||
BELOW
|
||||
}
|
||||
|
||||
var line_edit : LineEdit setget _set_line_edit
|
||||
export(Position) var position = Position.BELOW
|
||||
|
||||
#############
|
||||
# overrides #
|
||||
#############
|
||||
func _ready():
|
||||
call_deferred("__setup")
|
||||
|
||||
#################
|
||||
# private stuff #
|
||||
#################
|
||||
func __setup():
|
||||
__connect()
|
||||
|
||||
func __connect():
|
||||
if !line_edit:
|
||||
return
|
||||
|
||||
line_edit.connect("focus_entered", self, "_on_line_edit_focus_entered")
|
||||
line_edit.connect("focus_exited", self, "_on_line_edit_focus_exited")
|
||||
line_edit.connect("item_rect_changed", self, "_on_line_edit_item_rect_changed")
|
||||
line_edit.connect("resized", self, "_on_line_edit_resized")
|
||||
|
||||
func __disconnect():
|
||||
if !line_edit:
|
||||
return
|
||||
|
||||
line_edit.disconnect("focus_entered", self, "_on_line_edit_focus_entered")
|
||||
line_edit.disconnect("focus_exited", self, "_on_line_edit_focus_exited")
|
||||
line_edit.disconnect("item_rect_changed", self, "_on_line_edit_item_rect_changed")
|
||||
line_edit.disconnect("resized", self, "_on_line_edit_resized")
|
||||
|
||||
func __calc_rect() -> Rect2:
|
||||
var position_ = position
|
||||
var pos := line_edit.rect_global_position
|
||||
var vp_pos = get_viewport_rect().position
|
||||
var vp_size = get_viewport_rect().size
|
||||
var size := rect_min_size
|
||||
var le_size := line_edit.rect_size
|
||||
var max_height : float
|
||||
|
||||
if pos.y + size.y > vp_size.y:
|
||||
position_ = Position.ABOVE
|
||||
elif pos.y < vp_pos.y:
|
||||
position_ = Position.BELOW
|
||||
|
||||
if position_ == Position.ABOVE:
|
||||
max_height = pos.y - vp_pos.y
|
||||
else:
|
||||
max_height = (vp_pos.y + vp_size.y) - (pos.y + size.y)
|
||||
|
||||
var res_size = Vector2(le_size.x, 100.0)
|
||||
if position_ == Position.ABOVE:
|
||||
return Rect2(pos - Vector2(0.0, res_size.y), res_size)
|
||||
else:
|
||||
return Rect2(pos + Vector2(0.0, size.y), res_size)
|
||||
|
||||
func __update():
|
||||
var should_be_visible = line_edit.has_focus()
|
||||
if !should_be_visible:
|
||||
hide()
|
||||
else:
|
||||
var rect := __calc_rect()
|
||||
if visible:
|
||||
rect_position = rect.position
|
||||
rect_size = rect.size
|
||||
else:
|
||||
popup(rect)
|
||||
|
||||
############
|
||||
# handlers #
|
||||
############
|
||||
func _on_line_edit_focus_entered():
|
||||
__update()
|
||||
|
||||
func _on_line_edit_focus_exited():
|
||||
hide()
|
||||
|
||||
func _on_line_edit_item_rect_changed():
|
||||
__update()
|
||||
|
||||
func _on_line_edit_resized():
|
||||
__update()
|
||||
|
||||
###########
|
||||
# setters #
|
||||
###########
|
||||
func _set_line_edit(value : LineEdit):
|
||||
if value != line_edit:
|
||||
__disconnect()
|
||||
line_edit = value
|
||||
__connect()
|
||||
47
scenes/scripts/line_input_dialog.gd
Normal file
47
scenes/scripts/line_input_dialog.gd
Normal file
@@ -0,0 +1,47 @@
|
||||
tool
|
||||
extends ConfirmationDialog
|
||||
|
||||
export(bool) var allow_empty := true setget _set_allow_empty
|
||||
|
||||
################
|
||||
# public stuff #
|
||||
################
|
||||
func get_line_edit() -> LineEdit:
|
||||
return $container/line_edit as LineEdit
|
||||
|
||||
#################
|
||||
# private stuff #
|
||||
#################
|
||||
func __update():
|
||||
var valid := true
|
||||
if !allow_empty && !$container/line_edit.text:
|
||||
valid = false
|
||||
get_ok().disabled = !valid
|
||||
|
||||
###########
|
||||
# setters #
|
||||
###########
|
||||
func _set_allow_empty(value : bool):
|
||||
if value != allow_empty:
|
||||
allow_empty = value
|
||||
__update()
|
||||
|
||||
############
|
||||
# handlers #
|
||||
############
|
||||
func _on_line_input_dialog_about_to_show():
|
||||
__update()
|
||||
|
||||
func _on_line_edit_text_changed(new_text : String):
|
||||
__update()
|
||||
|
||||
func _on_line_edit_text_entered(new_text):
|
||||
if !get_ok().disabled:
|
||||
visible = false
|
||||
emit_signal("confirmed")
|
||||
|
||||
func _on_line_input_dialog_visibility_changed():
|
||||
if visible:
|
||||
yield(get_tree(), "idle_frame")
|
||||
$container/line_edit.grab_focus()
|
||||
$container/line_edit.caret_position = $container/line_edit.text.length()
|
||||
5
scenes/scripts/node_selection_dialog.gd
Normal file
5
scenes/scripts/node_selection_dialog.gd
Normal file
@@ -0,0 +1,5 @@
|
||||
tool
|
||||
extends ConfirmationDialog
|
||||
|
||||
func get_editor_node_tree() -> UIB_EditorNodeTree:
|
||||
return $tree as UIB_EditorNodeTree
|
||||
174
scenes/scripts/selector_popup.gd
Normal file
174
scenes/scripts/selector_popup.gd
Normal file
@@ -0,0 +1,174 @@
|
||||
extends PopupPanel
|
||||
|
||||
class_name SelectorPopup
|
||||
|
||||
class _Item:
|
||||
var label : String
|
||||
var icon : Texture
|
||||
var metadata
|
||||
var current_list_index := -1
|
||||
|
||||
func _init(label_ : String, icon_ : Texture, metadata_):
|
||||
self.label = label_
|
||||
self.icon = icon_
|
||||
self.metadata = metadata_
|
||||
|
||||
export var match_case := false
|
||||
|
||||
onready var _ledit_search : LineEdit = GDBUtility.find_node_by_name(self, "ledit_search")
|
||||
onready var _ilist_content : ItemList = GDBUtility.find_node_by_name(self, "ilist_content")
|
||||
|
||||
var _items := []
|
||||
var __needs_update := false
|
||||
var __caret := 0
|
||||
var __submitted := false
|
||||
|
||||
################
|
||||
# overridables #
|
||||
################
|
||||
func _should_show(item : _Item) -> bool:
|
||||
var search_text := _ledit_search.text.strip_edges()
|
||||
if search_text == "":
|
||||
return true
|
||||
var item_text := item.label
|
||||
if !match_case:
|
||||
search_text = search_text.to_lower()
|
||||
item_text = item_text.to_lower()
|
||||
return search_text in item_text
|
||||
|
||||
################
|
||||
# public stuff #
|
||||
################
|
||||
func add_item(label : String, icon : Texture = null, metadata = null) -> void:
|
||||
_items.append(_Item.new(label, icon, metadata))
|
||||
invalidate()
|
||||
|
||||
func clear_items() -> void:
|
||||
_items.clear()
|
||||
invalidate()
|
||||
|
||||
func get_item_label(idx : int) -> String:
|
||||
if idx < 0 || idx >= _items.size():
|
||||
return ""
|
||||
return _items[idx].label
|
||||
|
||||
func get_item_metadata(idx : int):
|
||||
if idx < 0 || idx >= _items.size():
|
||||
return null
|
||||
return _items[idx].metadata
|
||||
|
||||
func invalidate() -> void:
|
||||
# only updates once if multiple calls are done in a single frame
|
||||
__needs_update = true
|
||||
yield(get_tree(), "idle_frame")
|
||||
if __needs_update:
|
||||
__needs_update = false
|
||||
__update()
|
||||
|
||||
#################
|
||||
# private stuff #
|
||||
#################
|
||||
func __update() -> void:
|
||||
var idx_increment := 0
|
||||
var insert_index := 0
|
||||
|
||||
for item in self._items:
|
||||
var should_show := self._should_show(item)
|
||||
var is_visible : bool = (item.current_list_index > -1)
|
||||
|
||||
if is_visible:
|
||||
item.current_list_index += idx_increment
|
||||
|
||||
if should_show && !is_visible:
|
||||
self.__add_item(item, insert_index)
|
||||
item.current_list_index = insert_index
|
||||
idx_increment += 1
|
||||
elif !should_show && is_visible:
|
||||
_ilist_content.remove_item(item.current_list_index)
|
||||
item.current_list_index = -1
|
||||
idx_increment -= 1
|
||||
|
||||
if should_show:
|
||||
insert_index += 1
|
||||
|
||||
if !_ilist_content.is_anything_selected() && _ilist_content.get_item_count() > 0:
|
||||
__select(0)
|
||||
|
||||
func __add_item(item : _Item, insert_index : int) -> void:
|
||||
var new_idx := _ilist_content.get_item_count()
|
||||
_ilist_content.add_item(item.label, item.icon)
|
||||
_ilist_content.set_item_metadata(new_idx, item)
|
||||
if insert_index != new_idx:
|
||||
_ilist_content.move_item(new_idx, insert_index)
|
||||
|
||||
func __select(idx : int) -> void:
|
||||
_ilist_content.select(idx)
|
||||
_ilist_content.ensure_current_is_visible()
|
||||
|
||||
func __select_next() -> void:
|
||||
var selected := _ilist_content.get_selected_items()
|
||||
var idx := 0
|
||||
if !selected.empty():
|
||||
idx = selected[0] + 1
|
||||
if idx >= _ilist_content.get_item_count():
|
||||
return
|
||||
__select(idx)
|
||||
|
||||
func __select_prev() -> void:
|
||||
var selected := _ilist_content.get_selected_items()
|
||||
var idx := 0
|
||||
if !selected.empty():
|
||||
idx = selected[0] - 1
|
||||
if idx < 0:
|
||||
return
|
||||
__select(idx)
|
||||
|
||||
func __item_from_list_index(idx : int) -> _Item:
|
||||
return _ilist_content.get_item_metadata(idx) as _Item
|
||||
|
||||
func __item_index(itm : _Item) -> int:
|
||||
return _items.find(itm)
|
||||
|
||||
###################
|
||||
# signal handlers #
|
||||
###################
|
||||
func _on_selector_popup_about_to_show() -> void:
|
||||
__submitted = false
|
||||
_ledit_search.clear()
|
||||
_ledit_search.grab_focus()
|
||||
_ilist_content.unselect_all()
|
||||
__update()
|
||||
|
||||
func _on_ledit_search_text_changed(new_text : String) -> void:
|
||||
__update()
|
||||
|
||||
func _on_ledit_search_gui_input(event : InputEvent) -> void:
|
||||
if !event.is_pressed():
|
||||
return
|
||||
|
||||
if event.is_action("ui_down"):
|
||||
__select_next()
|
||||
elif event.is_action("ui_up"):
|
||||
__select_prev()
|
||||
else:
|
||||
__caret = _ledit_search.caret_position
|
||||
return
|
||||
_ledit_search.caret_position = __caret
|
||||
|
||||
func _on_ledit_search_text_entered(new_text : String) -> void:
|
||||
var selected := _ilist_content.get_selected_items()
|
||||
if selected.empty():
|
||||
return
|
||||
var idx := __item_index(__item_from_list_index(selected[0]))
|
||||
__submitted = true
|
||||
emit_signal("selected", idx)
|
||||
hide()
|
||||
|
||||
func _on_selector_popup_popup_hide() -> void:
|
||||
if !__submitted:
|
||||
emit_signal("selected", -1)
|
||||
|
||||
###########
|
||||
# signals #
|
||||
###########
|
||||
signal selected(index)
|
||||
77
scenes/scripts/variant_editor.gd
Normal file
77
scenes/scripts/variant_editor.gd
Normal file
@@ -0,0 +1,77 @@
|
||||
tool
|
||||
extends Control
|
||||
|
||||
enum Type {
|
||||
STRING,
|
||||
INT,
|
||||
REAL
|
||||
}
|
||||
|
||||
onready var edt_text := $edt_text
|
||||
onready var spin_number := $spin_number
|
||||
|
||||
export var value = "" setget _set_value
|
||||
export(Type) var type = Type.STRING setget _set_type
|
||||
|
||||
func _ready():
|
||||
__setup_type()
|
||||
|
||||
func __setup_type():
|
||||
match type:
|
||||
Type.STRING:
|
||||
edt_text.visible = true
|
||||
Type.INT, Type.REAL:
|
||||
spin_number.visible = true
|
||||
_set_value(convert(value, __type_id()))
|
||||
__fill_value()
|
||||
emit_signal("value_changed")
|
||||
|
||||
func __fill_value():
|
||||
match type:
|
||||
Type.STRING:
|
||||
edt_text.text = str(value)
|
||||
Type.INT, Type.REAL:
|
||||
spin_number.value = float(value)
|
||||
|
||||
func __type_id() -> int:
|
||||
match type:
|
||||
Type.STRING:
|
||||
return TYPE_STRING
|
||||
Type.INT:
|
||||
return TYPE_INT
|
||||
Type.REAL:
|
||||
return TYPE_REAL
|
||||
_:
|
||||
assert(false)
|
||||
return TYPE_STRING
|
||||
|
||||
func _set_value(val):
|
||||
val = convert(val, __type_id())
|
||||
if typeof(value) != __type_id() || value != val:
|
||||
value = val
|
||||
__fill_value()
|
||||
|
||||
func _set_type(value : int):
|
||||
if value != type:
|
||||
type = value
|
||||
for child in get_children():
|
||||
child.visible = false
|
||||
__setup_type()
|
||||
|
||||
func _on_edt_text_text_changed(new_text : String):
|
||||
assert(type == Type.STRING)
|
||||
if !value is String || value != new_text:
|
||||
value = new_text
|
||||
emit_signal("value_changed")
|
||||
|
||||
func _on_spin_number_value_changed(value_ : float):
|
||||
assert(type == Type.INT || type == Type.REAL)
|
||||
if type == Type.INT:
|
||||
value_ = int(value_)
|
||||
if typeof(value) != __type_id() || value != value_:
|
||||
value = value_
|
||||
emit_signal("value_changed")
|
||||
|
||||
signal value_changed()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user