Initial commit

This commit is contained in:
2021-08-07 21:15:07 +02:00
commit efe9556579
91 changed files with 5387 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
extends Button
class_name UIB_ActionButton
var action : UIB_Action setget _set_action
#############
# overrides #
#############
func _ready():
if !action:
_set_action(GDBUtility.find_node_by_type(self, UIB_Action))
if !action:
var action_reference : UIB_ActionReference = GDBUtility.find_node_by_type(self, UIB_ActionReference)
if action_reference:
_set_action(action_reference.get_action())
func _pressed():
if action:
action._apply()
#################
# private stuff #
#################
func __connect():
if !action:
return
action.connect("disabled_changed", self, "_on_action_disabled_changed")
action.connect("toggled_changed", self, "_on_action_toggled_changed")
action.connect("text_changed", self, "_on_action_text_changed")
action.connect("icon_changed", self, "_on_action_icon_changed")
func __disconnect():
if !action:
return
action.disconnect("disabled_changed", self, "_on_action_disabled_changed")
action.disconnect("toggled_changed", self, "_on_action_toggled_changed")
action.disconnect("text_changed", self, "_on_action_text_changed")
action.disconnect("icon_changed", self, "_on_action_icon_changed")
############
# handlers #
############
func _on_action_disabled_changed():
disabled = action._is_disabled()
func _on_action_toggled_changed():
toggle_mode = action._is_toggled()
pressed = action._is_toggled()
func _on_action_text_changed():
if !icon:
text = action._get_text()
hint_tooltip = action._get_text()
func _on_action_icon_changed():
icon = action._get_icon()
_on_action_text_changed() # update hint/text
###########
# setters #
###########
func _set_action(value : UIB_Action):
if value != action:
__disconnect()
action = value
if action:
icon = action._get_icon()
if !icon:
text = action._get_text()
hint_tooltip = action._get_text()
disabled = action._is_disabled()
toggle_mode = action._is_toggled()
pressed = action._is_toggled()
__connect()
else:
icon = null
text = ""
hint_tooltip = ""
disabled = true
toggle_mode = false
pressed = false

View File

@@ -0,0 +1,57 @@
extends MenuButton
class_name UIB_ActionMenuButton
onready var __popup := get_popup()
#############
# overrides #
#############
func _ready():
for child in get_children():
if child is UIB_Action:
add_action(child)
elif child is UIB_Seperator:
add_seperator(child)
elif child.has_method("get_action"):
add_action(child.get_action())
connect("about_to_show", self, "_on_about_to_show")
__popup.connect("index_pressed", self, "_on_popup_index_pressed")
################
# public stuff #
################
func add_action(action : UIB_Action):
if !action:
return
var idx = __popup.get_item_count()
__popup.add_item(action._get_text())
__popup.set_item_metadata(idx, action)
func add_seperator(seperator : UIB_Seperator):
__popup.add_separator(seperator.text)
############
# handlers #
############
func _on_about_to_show():
for i in range(__popup.get_item_count()):
var action := __popup.get_item_metadata(i) as UIB_Action
if !action:
continue
action._update()
__popup.set_item_text(i, action._get_text())
__popup.set_item_icon(i, action._get_icon())
__popup.set_item_shortcut(i, action._get_shortcut())
__popup.set_item_disabled(i, action._is_disabled())
__popup.set_item_as_checkable(i, action._is_toggleable())
__popup.set_item_checked(i, action._is_toggled())
func _on_popup_index_pressed(index : int):
var action := __popup.get_item_metadata(index) as UIB_Action
if !action:
return
action._apply()

View File

@@ -0,0 +1,50 @@
extends PopupMenu
class_name UIB_ActionPopupMenu
#############
# overrides #
#############
func _ready():
for child in get_children():
if child is UIB_Action:
add_action(child)
elif child.has_method("get_action"):
add_action(child.get_action())
connect("about_to_show", self, "_on_about_to_show")
connect("index_pressed", self, "_on_popup_index_pressed")
################
# public stuff #
################
func add_action(action : UIB_Action):
if !action:
return
var idx = get_item_count()
add_item(action._get_text())
set_item_metadata(idx, action)
############
# handlers #
############
func _on_about_to_show():
for i in range(get_item_count()):
var action := get_item_metadata(i) as UIB_Action
if !action:
continue
action._update()
set_item_text(i, action._get_text())
set_item_icon(i, action._get_icon())
set_item_shortcut(i, action._get_shortcut())
set_item_disabled(i, action._is_disabled())
set_item_as_checkable(i, action._is_toggleable())
set_item_checked(i, action._is_toggled())
func _on_popup_index_pressed(index : int):
var action := get_item_metadata(index) as UIB_Action
if !action:
return
action._apply()

View File

@@ -0,0 +1,29 @@
extends BoxContainer
class_name UIB_ActionToolbar
#############
# overrides #
#############
func _ready():
for child in get_children():
if child is UIB_Action:
add_action(child)
elif child.has_method("get_action"):
add_action(child.get_action())
################
# public stuff #
################
func add_action(action : UIB_Action):
var btn = ToolButton.new()
btn.set_script(preload("res://addons/de.mewin.gduibasics/scripts/types/controls/action_button.gd"))
btn.action = action
call_deferred("add_child", btn)
func remove_action(action : UIB_Action):
for child in get_children():
if child == action || child.get("action") == action \
|| (child is UIB_ActionReference && child.get_action() == action):
remove_child(child)
child.queue_free()

View File

@@ -0,0 +1,250 @@
tool
extends MarginContainer
class_name UIB_BorderContainer
enum __MouseMode {
NONE,
MOVING,
RESIZE_L,
RESIZE_R,
RESIZE_T,
RESIZE_B,
RESIZE_TL,
RESIZE_TR,
RESIZE_BL,
RESIZE_BR
}
export var resizable := true
export var show_title := false setget _set_show_title
export var title := "" setget _set_title
export var grab_everywhere := false
export(Texture) var title_image = null
export(StyleBox) var style_left setget _set_style_left
export(StyleBox) var style_right setget _set_style_right
export(StyleBox) var style_top setget _set_style_top
export(StyleBox) var style_bottom setget _set_style_bottom
var __mouse_mode = __MouseMode.NONE
var __mouse_down_pos : Vector2
#############
# overrides #
#############
func _gui_input(event):
if event is InputEventMouseButton && event.pressed && event.button_index == BUTTON_LEFT:
if resizable:
__mouse_mode = __resize_mode(event.position)
if __mouse_mode != __MouseMode.NONE:
__mouse_down_pos = _win_get_mouse_position() - _win_get_position()
if __mouse_mode == __MouseMode.RESIZE_B || __mouse_mode == __MouseMode.RESIZE_BL || __mouse_mode == __MouseMode.RESIZE_BR:
__mouse_down_pos.y -= _win_get_size().y
if __mouse_mode == __MouseMode.RESIZE_R || __mouse_mode == __MouseMode.RESIZE_TR || __mouse_mode == __MouseMode.RESIZE_BR:
__mouse_down_pos.x -= _win_get_size().x
set_process(true)
return
if grab_everywhere || event.position.y < __get_margin("top"):
__mouse_mode = __MouseMode.MOVING
__mouse_down_pos = _win_get_mouse_position() - _win_get_position()
set_process(true)
accept_event()
elif event is InputEventMouseMotion && resizable:
match __resize_mode(event.position):
__MouseMode.RESIZE_L, __MouseMode.RESIZE_R:
mouse_default_cursor_shape = CURSOR_HSIZE
__MouseMode.RESIZE_T, __MouseMode.RESIZE_B:
mouse_default_cursor_shape = CURSOR_VSIZE
__MouseMode.RESIZE_TL, __MouseMode.RESIZE_BR:
mouse_default_cursor_shape = CURSOR_FDIAGSIZE
__MouseMode.RESIZE_TR, __MouseMode.RESIZE_BL:
mouse_default_cursor_shape = CURSOR_BDIAGSIZE
_:
mouse_default_cursor_shape = CURSOR_ARROW
func _process(delta):
if !Input.is_mouse_button_pressed(BUTTON_LEFT):
__mouse_mode = __MouseMode.NONE
if __mouse_mode == __MouseMode.NONE:
set_process(false)
return
if __mouse_mode == __MouseMode.MOVING:
_win_set_position(_win_get_mouse_position() - __mouse_down_pos)
return
var win_pos := _win_get_position()
var win_size := _win_get_size()
var win_min_size := _win_get_minimum_size()
var mouse_pos := _win_get_mouse_position()
if __mouse_mode == __MouseMode.RESIZE_L || __mouse_mode == __MouseMode.RESIZE_TL || __mouse_mode == __MouseMode.RESIZE_BL:
var global_right := win_pos.x + win_size.x
var x_pos := mouse_pos.x - __mouse_down_pos.x
x_pos = min(x_pos, win_pos.x + win_size.x - win_min_size.x)
win_pos.x = x_pos
win_size.x = global_right - win_pos.x
elif __mouse_mode == __MouseMode.RESIZE_R || __mouse_mode == __MouseMode.RESIZE_TR || __mouse_mode == __MouseMode.RESIZE_BR:
var x_pos := mouse_pos.x - __mouse_down_pos.x
win_size.x = x_pos - win_pos.x
win_size.x = max(win_size.x, win_min_size.x)
if __mouse_mode == __MouseMode.RESIZE_T || __mouse_mode == __MouseMode.RESIZE_TL || __mouse_mode == __MouseMode.RESIZE_TR:
var global_bottom := win_pos.y + win_size.y
var y_pos := mouse_pos.y - __mouse_down_pos.y
y_pos = min(y_pos, win_pos.y + win_size.y - win_min_size.y)
win_pos.y = y_pos
win_size.y = global_bottom - win_pos.y
elif __mouse_mode == __MouseMode.RESIZE_B || __mouse_mode == __MouseMode.RESIZE_BL || __mouse_mode == __MouseMode.RESIZE_BR:
var y_pos := mouse_pos.y - __mouse_down_pos.y
win_size.y = y_pos - win_pos.y
win_size.y = max(win_size.y, win_min_size.y)
_win_set_position(win_pos)
_win_set_size(win_size)
func _draw():
var marg_left = __get_margin("left")
var marg_right = __get_margin("right")
var marg_top = __get_margin("top")
var marg_bottom = __get_margin("bottom")
if show_title:
var title_height := 0
if title_image:
# title_height = title_image.get_height()
title_height = 0.0
else:
var font := get_font("font", "Label")
title_height = font.get_height() + font.get_descent()
if marg_top < title_height:
marg_top = title_height
set("custom_constants/margin_top", marg_top)
if style_left:
var min_size = style_left.get_minimum_size()
min_size.x = max(min_size.x, marg_left)
draw_style_box(style_left, Rect2(Vector2(), Vector2(min_size.x, rect_size.y)))
if style_right:
var min_size = style_right.get_minimum_size()
min_size.x = max(min_size.x, marg_right)
draw_style_box(style_right, Rect2(Vector2(rect_size.x - min_size.x, 0.0), Vector2(min_size.x, rect_size.y)))
if style_top:
var min_size = style_top.get_minimum_size()
min_size.y = max(min_size.y, marg_top)
draw_style_box(style_top, Rect2(Vector2(), Vector2(rect_size.x, min_size.y)))
if style_bottom:
var min_size = style_bottom.get_minimum_size()
min_size.y = max(min_size.y, marg_bottom)
draw_style_box(style_bottom, Rect2(Vector2(0.0, rect_size.y - min_size.y), Vector2(rect_size.x, min_size.y)))
if show_title:
if title_image:
draw_texture(title_image, Vector2(0.5 * (rect_size.x - title_image.get_width()), 0.0))
else:
var font := get_font("font", "Label")
var string_size := font.get_string_size(title)
draw_string(font, Vector2(0.5 * (rect_size.x - string_size.x), string_size.y), title)
################
# overridables #
################
func _win_get_position() -> Vector2:
return get_parent().rect_global_position
func _win_get_size() -> Vector2:
return get_parent().rect_size
func _win_get_mouse_position() -> Vector2:
return get_viewport().get_mouse_position()
func _win_get_minimum_size() -> Vector2:
return get_parent().rect_min_size
func _win_set_position(pos : Vector2):
get_parent().rect_global_position = pos
func _win_set_size(size : Vector2):
get_parent().rect_size = size
#################
# private stuff #
#################
func __get_margin(what : String) -> int:
var marg = get("custom_constants/margin_%s" % what)
if !marg:
return 0
return marg
func __resize_mode(cursor_pos : Vector2):
var resize_left := false
var resize_right := false
var resize_top := false
var resize_bottom := false
if cursor_pos.y < __get_margin("bottom"):
resize_top = true
elif cursor_pos.y > rect_size.y - __get_margin("bottom"):
resize_bottom = true
if cursor_pos.x < __get_margin("left"):
resize_left = true
elif cursor_pos.x > rect_size.x - __get_margin("right"):
resize_right = true
if resize_top:
if resize_left:
return __MouseMode.RESIZE_TL
elif resize_right:
return __MouseMode.RESIZE_TR
else:
return __MouseMode.RESIZE_T
elif resize_bottom:
if resize_left:
return __MouseMode.RESIZE_BL
elif resize_right:
return __MouseMode.RESIZE_BR
else:
return __MouseMode.RESIZE_B
elif resize_left:
return __MouseMode.RESIZE_L
elif resize_right:
return __MouseMode.RESIZE_R
else:
return __MouseMode.NONE
###########
# setters #
###########
func _set_show_title(val : bool):
if val != show_title:
show_title = val
update()
func _set_title(val : String):
if val != title:
title = val
update()
func _set_style_left(val : StyleBox):
if val != style_left:
style_left = val
update()
func _set_style_right(val : StyleBox):
if val != style_right:
style_right = val
update()
func _set_style_top(val : StyleBox):
if val != style_top:
style_top = val
update()
func _set_style_bottom(val : StyleBox):
if val != style_bottom:
style_bottom = val
update()

View File

@@ -0,0 +1,95 @@
tool
extends Container
class_name UIB_DynamicGridContainer
var __DEFAULT_CONSTRAINTS = UIB_DynamicGridConstraints.new()
export(int, 1, 100) var columns := 1 setget _set_columns
func _notification(what):
if what == NOTIFICATION_SORT_CHILDREN:
__sort_children()
func __get_constraints(node : Node) -> UIB_DynamicGridConstraints:
for child in node.get_children():
if child is UIB_DynamicGridConstraints:
return child
return __DEFAULT_CONSTRAINTS
func __sort_children():
var column_widths := []
var column_expand := []
for i in range(columns):
column_widths.append(0)
column_expand.append(0.0)
var rows := [[]]
var col := 0
var row_width := 0.0
for child in get_children():
if !child is Control || !child.visible:
continue
var constraints := __get_constraints(child)
if col + constraints.colspan > columns:
col = 0
row_width = 0.0
rows.append([])
rows[rows.size() - 1].append(child)
child.rect_size = child.get_combined_minimum_size()
column_widths[col] = max(column_widths[col], child.rect_size.x)
if child.size_flags_horizontal & Control.SIZE_EXPAND:
column_expand[col] = max(column_expand[col], child.size_flags_stretch_ratio)
row_width += child.rect_size.x
col = col + constraints.colspan
var full_width := 0.0
for col_width in column_widths:
full_width += col_width
var remaining_width := rect_size.x - full_width
if remaining_width > 0.0:
var combined_expand_ratio := 0.0
for expand in column_expand:
combined_expand_ratio += expand
if combined_expand_ratio > 0.0:
for c in range(columns):
column_widths[c] += remaining_width * (column_expand[c] / combined_expand_ratio)
var pos := Vector2()
for row in rows:
var row_height := 0.0
col = 0
for child in row:
var constraints := __get_constraints(child)
child.rect_position = pos
var width := 0.0
for i in range(min(constraints.colspan, columns - col)):
width += column_widths[col + i]
if child.size_flags_horizontal & Control.SIZE_FILL:
child.rect_size.x = width
pos.x += width
row_height = max(row_height, child.rect_size.y)
col = col + constraints.colspan
for child in row:
if child.size_flags_vertical & Control.SIZE_FILL:
child.rect_size.y = row_height
pos.y += row_height
pos.x = 0.0
rect_min_size.y = pos.y
func _set_columns(value : int):
if value != columns:
columns = value
__sort_children()

View File

@@ -0,0 +1,57 @@
extends Control
class_name UIB_EditorInspectorCategory
var label := "" setget _set_label
var icon : Texture setget _set_icon
onready var bg_color := get_color("prop_category", "Editor") setget _set_bg_color
func _ready():
update()
func _draw():
draw_rect(Rect2(Vector2(), get_size()), bg_color)
var font := get_font("font", "Tree")
var hs := get_constant("hseparation", "Tree")
var w := font.get_string_size(label).x
if icon:
w += hs + icon.get_width()
var ofs := (get_size().x - w) / 2
if icon:
draw_texture(icon, Vector2(ofs, (get_size().y - icon.get_height()) / 2).floor())
ofs += hs + icon.get_width()
var color := get_color("font_color", "Tree");
draw_string(font, Vector2(ofs, font.get_ascent() + (get_size().y - font.get_height()) / 2).floor(), label, color, get_size().x)
func _get_minimum_size() -> Vector2:
var ms : Vector2
var font := get_font("font", "Tree");
ms.x = 1
ms.y = font.get_height()
# if (icon.is_valid()) {
# ms.height = MAX(icon->get_height(), ms.height);
# }
ms.y += get_constant("vseparation", "Tree")
return ms
func _set_label(value : String):
if value != label:
label = value
update()
func _set_icon(value : Texture):
if value != icon:
icon = value
update()
func _set_bg_color(value : Color):
if value != bg_color:
bg_color = value
update()

View File

@@ -0,0 +1,203 @@
tool
extends Tree
class_name UIB_EditorNodeTree
var __tree : Tree
var selected_node : NodePath setget _set_selected_node
var node_type_filter = Node
export var show_properties := false
export var show_methods := false
#############
# overrides #
#############
func _ready():
assert(Engine.editor_hint)
update()
connect("visibility_changed", self, "_on_visiblity_changed")
connect("item_selected", self, "_on_item_selected")
################
# public stuff #
################
func update():
if !__tree:
var scene_tree_editor := GDBUIUtility.find_scene_tree_editor()
if !scene_tree_editor:
return
__tree = __find_tree(scene_tree_editor)
if !__tree:
return
__copy_from(__tree)
if !selected_node.is_empty():
var full_path := __full_path(selected_node)
if !full_path.is_empty():
__set_selected_node(full_path, get_root())
#################
# private stuff #
#################
func __copy_from(tree : Tree):
clear()
columns = tree.columns
__copy_items(tree.get_root())
func __copy_items(item : TreeItem, parent : TreeItem = null):
if !item:
return
var new_item := create_item(parent)
for i in range(columns):
new_item.set_text(i, item.get_text(i))
new_item.set_icon(i, item.get_icon(i))
new_item.set_metadata(i, item.get_metadata(i))
var meta0 = item.get_metadata(0)
if meta0 is NodePath:
if show_properties:
var prop_item := create_item(new_item)
prop_item.set_text(0, tr("Properties"))
__fill_node_path_properties(prop_item, meta0)
prop_item.collapsed = true
prop_item.set_selectable(0, false)
if show_methods:
var method_item := create_item(new_item)
method_item.set_text(0, tr("Methods"))
__fill_methods(method_item, meta0)
method_item.collapsed = true
method_item.set_selectable(0, false)
var node : Node = get_node(meta0)
if !node is node_type_filter:
new_item.set_selectable(0, false)
else:
new_item.set_selectable(0, false)
if !new_item.is_selectable(0):
new_item.set_custom_color(0, Color.darkgray)
__copy_items(item.get_children(), new_item)
__copy_items(item.get_next(), parent)
func __fill_node_path_properties(parent : TreeItem, node_path : NodePath):
assert(node_path.is_absolute())
var node := get_node_or_null(node_path)
if !node:
return
__fill_properties(parent, node.get_property_list(), node_path)
func __fill_properties(parent : TreeItem, properties : Array, parent_path : NodePath):
GDBAlgorithm.remove_if(properties, 'return prop.get("usage", PROPERTY_USAGE_EDITOR) & PROPERTY_USAGE_EDITOR == 0', ["prop"])
GDBAlgorithm.sort_by(properties, "name")
for prop in properties:
var prop_item := create_item(parent)
var node_path := NodePath(str(parent_path) + ":" + prop["name"])
prop_item.set_text(0, "%s : %s" % [prop["name"], GDBUtility.format_type(prop, "any")])
prop_item.set_metadata(0, node_path)
__fill_properties(prop_item, GDBUtility.get_type_property_list(prop), node_path)
prop_item.collapsed = true
func __fill_methods(parent : TreeItem, node_path : NodePath):
assert(node_path.is_absolute())
var node := get_node_or_null(node_path)
if !node:
return
var methods := node.get_method_list()
GDBAlgorithm.sort_by(methods, "name")
for method in methods:
var method_item := create_item(parent)
method_item.set_text(0, GDBUtility.format_method_signature(method))
func __find_tree(root : Node) -> Tree:
if root is Tree:
return root as Tree
for child in root.get_children():
var tree := __find_tree(child)
if tree:
return tree
return null
func __set_selected_node(node : NodePath, root : TreeItem):
if !root:
return false
for i in range(columns):
if root.get_metadata(i) == node:
GDBUIUtility.uncollapse_tree_item(root)
root.select(i)
return true
if __set_selected_node(node, root.get_children()):
return true
else:
return __set_selected_node(node, root.get_next())
func __full_path(rel_path : NodePath) -> NodePath:
assert(!rel_path.is_absolute())
var node := get_tree().edited_scene_root.get_node_or_null(rel_path)
if !node:
# printerr("EditorNodeTree: could not find node: %s" % rel_path)
return NodePath()
var res_path := node.get_path()
if rel_path.get_subname_count() > 0:
res_path = NodePath(str(res_path) + ":" + rel_path.get_concatenated_subnames())
return res_path
func __format_tree_item(tree : Tree, item : TreeItem) -> String:
var parts := PoolStringArray()
for i in range(tree.columns):
parts.append("%s[%s]" % [item.get_text(i), item.get_metadata(i)])
return parts.join("|")
func __dump_tree_item(tree : Tree, item : TreeItem, indent := 0):
if !item:
return
print(" ".repeat(indent), __format_tree_item(tree, item))
__dump_tree_item(tree, item.get_children(), indent + 2)
__dump_tree_item(tree, item.get_next(), indent)
func _set_selected_node(value : NodePath):
if value == selected_node:
return
if value.is_empty():
var selected_item := get_selected()
if selected_item:
selected_item.deselect(0)
return
if value.is_absolute():
printerr("EditorNodeTree: trying to set absolute NodePath, aborting.")
return
var full_path := __full_path(value)
if full_path.is_empty():
return
__set_selected_node(full_path, get_root())
selected_node = value
############
# handlers #
############
func _on_visiblity_changed():
if visible:
update()
func _on_item_selected():
var item := get_selected()
if !item:
selected_node = NodePath()
emit_signal("selected_node_changed")
return
for i in range(columns):
var meta = item.get_metadata(i)
if meta is NodePath:
assert(meta.is_absolute())
var node : Node = get_node(meta)
if !node:
printerr("EditorNodeTree contains invalid node path: %s" % meta)
return
selected_node = NodePath(str(get_tree().edited_scene_root.get_path_to(node)) + ":" + meta.get_concatenated_subnames())
emit_signal("selected_node_changed")
return
printerr("EditorNodeTree: selected node without NodePath?!")
###########
# signals #
###########
signal selected_node_changed()

View File

@@ -0,0 +1,280 @@
extends Tree
class_name UIB_FileList
enum ViewMode {
LIST,
DETAILS
}
enum SpecialColumn {
CHECKBOX
}
enum Checkable {
NONE,
FILES,
FOLDERS,
BOTH
}
class DetailsColumn:
func _get_name() -> String:
return ""
func _get_value(entry) -> String:
return ""
func _get_expand() -> bool:
return false
func _get_min_width() -> float:
return 10.0
func _compare(entry0, entry1) -> bool:
return _get_value(entry0) < _get_value(entry1)
class DetailsColumnName:
extends DetailsColumn
func _get_name() -> String:
return tr("Name")
func _get_value(entry) -> String:
return entry._get_name()
func _get_expand() -> bool:
return true
class DetailsColumnType:
extends DetailsColumn
func _get_name() -> String:
return tr("Type")
func _get_value(entry) -> String:
if entry._is_folder():
return tr("Folder")
else:
return tr("File")
func _get_min_width() -> float:
return 100.0
class DetailsColumnSize:
extends DetailsColumn
func _get_name() -> String:
return tr("Size")
func _get_value(entry) -> String:
if entry._is_folder():
return ""
else:
return String.humanize_size(entry._get_size())
func _get_min_width() -> float:
return 100.0
func _compare(entry0, entry1) -> bool:
return entry0._get_size() < entry1._get_size()
class DetailsColumnModifiedTime:
extends DetailsColumn
func _get_name() -> String:
return tr("Modified")
func _get_value(entry) -> String:
if entry._is_folder():
return ""
else:
return GDBFormat.smart_format_unixtime(entry._get_modified_time())
func _get_min_width() -> float:
return 150.0
func _compare(entry0, entry1) -> bool:
return entry0._get_modified_time() < entry1._get_modified_time()
class __ParentFolderEntry:
extends UIB_FileAdapter.FileEntry
var real_entry
func _init(real_entry_):
real_entry = real_entry_
func _get_name() -> String:
return tr("<up>")
func _is_folder() -> bool:
return true
const CHECKBOX_COLUMN_WIDTH = 25
export(ViewMode) var view_mode = ViewMode.DETAILS setget _set_view_mode
export(Checkable) var checkable = Checkable.NONE setget _set_checkable
export var allow_navigation := true
export var show_up_entry := true
var adapter : UIB_FileAdapter setget _set_adapter
var filter : UIB_FileFilter setget _set_filter
var current_folder : UIB_FileAdapter.FileEntry setget _set_current_folder
var details_columns := [
DetailsColumnName.new(),
DetailsColumnType.new(),
DetailsColumnSize.new(),
DetailsColumnModifiedTime.new()
]
var __special_columns := []
var __sort_column := 0
var __sort_reverse := false
func _ready():
hide_root = true
select_mode = Tree.SELECT_ROW
if adapter == null:
adapter = UIB_LocalFileAdapter.new()
if current_folder == null:
current_folder = adapter._get_root()
__update_entries()
connect("item_activated", self, "_on_item_activated")
connect("item_selected", self, "_on_item_selected")
connect("column_title_pressed", self, "_on_column_title_pressed")
#################
# private stuff #
#################
func __compare_entries(entry0, entry1):
if entry0._is_folder() != entry1._is_folder():
return entry0._is_folder()
var val = details_columns[__sort_column]._compare(entry0, entry1)
if __sort_reverse:
return !val
return val
func __update_columns():
__special_columns.clear()
if checkable != Checkable.NONE:
__special_columns.append(SpecialColumn.CHECKBOX)
match view_mode:
_: # DETAILS
columns = __special_columns.size() + details_columns.size()
set_column_titles_visible(true)
for i in range(details_columns.size()):
set_column_title(__special_columns.size() + i, details_columns[i]._get_name())
set_column_expand(i, details_columns[i]._get_expand())
set_column_min_width(i, details_columns[i]._get_min_width())
for i in range(__special_columns.size()):
set_column_title(i, "")
set_column_expand(i, false)
set_column_min_width(i, CHECKBOX_COLUMN_WIDTH)
func __entry_is_checkable(entry):
match checkable:
Checkable.BOTH:
return true
Checkable.FILES:
return !entry._is_folder()
Checkable.FOLDERS:
return entry._is_folder()
_:
return false
func __update_entries():
if !current_folder:
return
clear()
create_item() # root
__update_columns()
if columns < 1:
return
var entries := current_folder._list_files()
entries.sort_custom(self, "__compare_entries")
if allow_navigation && show_up_entry:
var parent_folder = current_folder._get_parent()
if parent_folder:
entries.push_front(__ParentFolderEntry.new(parent_folder))
for entry in entries:
if filter && !entry._is_folder() && !filter._accepts(entry):
continue
var itm = create_item()
for i in range(__special_columns.size()):
match __special_columns[i]:
SpecialColumn.CHECKBOX:
if __entry_is_checkable(entry):
itm.set_cell_mode(i, TreeItem.CELL_MODE_CHECK)
for i in range(details_columns.size()):
itm.set_text(__special_columns.size() + i, details_columns[i]._get_value(entry))
itm.set_icon(__special_columns.size(), adapter._get_icon(entry))
itm.set_metadata(0, entry)
###########
# setters #
###########
func _set_view_mode(value):
if value != view_mode:
view_mode = value
__update_entries()
func _set_adapter(value : UIB_FileAdapter):
if value != adapter:
adapter = value
__update_entries()
func _set_filter(value : UIB_FileFilter):
if value != filter:
filter = value
__update_entries()
func _set_current_folder(value : UIB_FileAdapter.FileEntry):
if value != current_folder:
current_folder = value
emit_signal("current_folder_changed")
__update_entries()
func _set_checkable(value):
if value != checkable:
checkable = value
__update_entries()
############
# handlers #
############
func _on_item_activated():
var selected := get_selected()
if !selected:
return
var entry = selected.get_metadata(0)
if !entry:
return
if allow_navigation && entry._is_folder():
if entry is __ParentFolderEntry:
_set_current_folder(entry.real_entry)
else:
_set_current_folder(entry)
else:
emit_signal("file_activated", entry)
func _on_item_selected():
var selected := get_selected()
if !selected:
return
var entry = selected.get_metadata(0)
if !entry:
return
emit_signal("file_selected", entry)
func _on_column_title_pressed(idx : int):
var sort_idx := idx - __special_columns.size()
if sort_idx >= details_columns.size():
return
if sort_idx == __sort_column:
__sort_reverse = !__sort_reverse
else:
__sort_column = sort_idx
__sort_reverse = false
__update_entries()
###########
# signals #
###########
signal current_folder_changed()
signal file_activated(file_entry)
signal file_selected(file_entry)

View File

@@ -0,0 +1,43 @@
tool
extends Container
const SORT_DELAY = 0.02
class_name UIB_FlowContainer
export var use_minimum_size := false
export var gap := 0.0
#############
# overrides #
#############
func _notification(what):
if what == NOTIFICATION_SORT_CHILDREN:
__sort_children()
#################
# private stuff #
#################
func __sort_children():
var pos := Vector2()
var row_height := 0.0
var vp_rect := get_viewport_rect()
for child in get_children():
if !child.visible || !child is Control:
continue
if use_minimum_size:
child.rect_size = child.get_combined_minimum_size()
if pos.x > 0.0 && pos.x + child.rect_size.x > rect_size.x:
pos.x = 0.0
pos.y += row_height + gap
row_height = 0.0
child.rect_position = pos
row_height = max(row_height, child.rect_size.y)
pos.x += child.rect_size.x + gap
rect_min_size.y = pos.y + row_height

View File

@@ -0,0 +1,79 @@
extends LineEdit
class_name UIB_FolderEdit
var suggestion_popup := UIB_SuggestionPopup.new()
var adapter : UIB_FileAdapter
var current_folder : UIB_FileAdapter.FileEntry setget _set_current_folder
#############
# overrides #
#############
func _ready():
call_deferred("__setup")
connect("text_changed", self, "_on_text_changed")
connect("text_entered", self, "_on_text_entered")
#################
# private stuff #
#################
func __setup():
suggestion_popup.connect("suggestion_accepted", self, "_on_suggestion_accepted")
add_child(suggestion_popup)
func __set_suggestions(files : Array):
var suggestions := []
for file in files:
if file._is_folder():
suggestions.append(file._get_path() + "/")
suggestion_popup.suggestions = suggestions
func __update_suggestions():
var folder = adapter._get_file(text)
if folder && folder._is_folder():
__set_suggestions(folder._list_files())
return
var parent_path = text.get_base_dir()
if parent_path:
folder = adapter._get_file(parent_path)
if folder && folder._is_folder():
__set_suggestions(folder._list_files())
return
suggestion_popup.suggestions = []
func __update_current_folder():
var new_file = adapter._get_file(text)
if new_file:
_set_current_folder(new_file)
caret_position = text.length()
###########
# setters #
###########
func _set_current_folder(value):
if value != current_folder:
current_folder = value
text = current_folder._get_path()
emit_signal("current_folder_changed")
############
# handlers #
############
func _on_text_changed(new_text):
__update_suggestions()
func _on_text_entered(new_text):
__update_current_folder()
func _on_suggestion_accepted(suggestion):
__update_current_folder()
__update_suggestions()
###########
# signals #
###########
signal current_folder_changed()

View File

@@ -0,0 +1,104 @@
extends Node
class_name UIB_FolderSync
enum DefaultFolder {
HOME,
ROOT,
CUSTOM
}
export(Array, NodePath) var nodes = []
export(DefaultFolder) var default_folder = DefaultFolder.HOME
export(NodePath) var folder_up_action
export(NodePath) var folder_favourite_action
export var custom_default_folder := ""
var adapter : UIB_FileAdapter = UIB_LocalFileAdapter.new()
var current_folder : UIB_FileAdapter.FileEntry setget set_current_folder
var __folder_up_action : UIB_SimpleAction
var __folder_favourite_action : UIB_SimpleAction
var __nodes := []
var __locked := false
#############
# overrides #
#############
func _ready():
__folder_up_action = __get_action(folder_up_action)
__folder_favourite_action = __get_action(folder_favourite_action)
if __folder_up_action:
__folder_up_action.connect("applied", self, "_on_folder_up_action")
if __folder_favourite_action:
__folder_favourite_action.connect("applied", self, "_on_folder_favourite_action")
var folder := "/"
match default_folder:
DefaultFolder.HOME:
folder = GDBFsUtil.get_home_folder()
DefaultFolder.CUSTOM:
folder = custom_default_folder
for path in nodes:
var node := get_node(path)
assert(node)
if !node:
continue
node.adapter = adapter
node.connect("current_folder_changed", self, "_on_node_current_folder_changed", [node])
__nodes.append(node)
var folder_entry = adapter._get_file(folder)
if folder_entry && folder_entry._is_folder():
set_current_folder(folder_entry)
################
# public stuff #
################
func set_current_folder(folder, ignore_node : Node = null):
if __locked:
return
__locked = true
for node in __nodes:
if node != ignore_node:
node.current_folder = folder
__locked = false
current_folder = folder
if __folder_up_action:
__folder_up_action.disabled = !folder || !folder._get_parent()
if __folder_favourite_action:
__folder_favourite_action.toggled = GDBUX.is_favourite_place(current_folder)
func __get_action(node_path : NodePath) -> UIB_SimpleAction:
var action_node = get_node_or_null(node_path)
if action_node is UIB_SimpleAction:
return action_node
elif action_node.has_method("get_action"):
return action_node.get_action() as UIB_SimpleAction
return null
############
# handlers #
############
func _on_node_current_folder_changed(source_node : Node):
set_current_folder(source_node.current_folder, source_node)
func _on_folder_up_action():
var parent = current_folder._get_parent()
if parent:
set_current_folder(parent)
func _on_folder_favourite_action():
if GDBUX.is_favourite_place(current_folder):
GDBUX.remove_favourite_place(current_folder)
__folder_favourite_action.toggled = false
else:
GDBUX.add_favourite_place(current_folder)
__folder_favourite_action.toggled = true

View File

@@ -0,0 +1,16 @@
extends TabContainer
class_name UIB_NamedTabContainer
export(Array, String) var headers := []
export(Array, Texture) var icons := []
func _ready():
for i in range(headers.size()):
if i >= get_child_count():
break
set_tab_title(i, headers[i])
for i in range(icons.size()):
if i >= get_child_count():
break
set_tab_icon(i, icons[i])

View File

@@ -0,0 +1,122 @@
extends ItemList
class_name UIB_PlacesList
enum Mode {
DEFAULT,
# FAVOURITES,
CUSTOM
}
class __DefaultItem:
var place : String
var icon : Texture
var label : String
func _init(place_ : String, icon_ : Texture, label_ : String):
place = place_
icon = icon_
label = label_
var __DEFAULT_ITEMS := [
__DefaultItem.new(GDBFsUtil.get_home_folder(), preload("res://addons/de.mewin.gduibasics/images/home32.svg"), tr("Home"))
]
export(Mode) var mode = Mode.DEFAULT setget _set_mode
export(Array, String) var places := [] setget _set_places
export(Array, Texture) var icons := []
export(Array, String) var names := []
var adapter : UIB_FileAdapter
var current_folder : UIB_FileAdapter.FileEntry setget _set_current_folder
#############
# overrides #
#############
func _ready():
call_deferred("__update_items")
connect("item_activated", self, "_on_item_activated")
GDBUX.connect_static("favourite_places_changed", self, "_on_favourite_places_changed")
#################
# private stuff #
#################
func __update_items():
clear()
if !adapter:
return
match mode:
Mode.DEFAULT:
__add_default_places()
_:
__add_custom_places()
func __add_default_places():
for default_place in __DEFAULT_ITEMS:
var folder := adapter._get_file(default_place.place)
if !folder || !folder._is_folder():
continue
__add_place(folder, default_place.icon, default_place.label)
for favourite in GDBUX.get_favourite_places():
var folder := adapter._get_file(favourite)
if !folder || !folder._is_folder():
continue
__add_place(folder, preload("res://addons/de.mewin.gduibasics/images/favourite32.svg"))
func __add_custom_places():
for i in range(places.size()):
var folder = adapter._get_file(places[i])
if !folder || !folder._is_folder():
continue
var label : String = names[i] if i < names.size() else ""
var icon : Texture = icons[i] if i < icons.size() else null
__add_place(folder, icon, label)
func __add_place(folder : UIB_FileAdapter.FileEntry, icon : Texture, label := ""):
if !icon:
icon = preload("res://addons/de.mewin.gduibasics/images/folder32.svg")
if !label:
label = folder._get_name()
var idx = get_item_count()
add_item(label, icon)
set_item_metadata(idx, folder)
func __select_current_folder():
for i in range(get_item_count()):
if get_item_metadata(i)._get_path() == current_folder._get_path():
select(i)
###########
# setters #
###########
func _set_mode(value):
if value != mode:
mode = value
__update_items()
func _set_places(value):
if value != places:
places = value
__update_items()
func _set_current_folder(value):
if value != current_folder:
current_folder = value
emit_signal("current_folder_changed")
############
# handlers #
############
func _on_item_activated(idx):
_set_current_folder(get_item_metadata(idx))
func _on_favourite_places_changed():
if mode == Mode.DEFAULT:
__update_items()
###########
# signals #
###########
signal current_folder_changed()

View File

@@ -0,0 +1,18 @@
extends UIB_PlacesList
class_name UIB_RecentPlacesList
#############
# overrides #
#############
func _ready():
places = GDBSettings.get_value(GDBConstants.SETTING_RECENT_PLACES, [])
GDBSettings.connect_static("setting_changed", self, "_on_setting_changed")
############
# handlers #
############
func _on_setting_changed(setting, value):
if setting == GDBConstants.SETTING_RECENT_PLACES:
_set_places(value)

View File

@@ -0,0 +1,58 @@
extends Control
class_name UIB_ScrollParallaxBackground
export(Array, Texture) var textures := []
export(Array, float) var speeds := []
export(NodePath) var scroll_container
var __scroll_container : ScrollContainer
func _ready():
show_behind_parent = true
rect_clip_content = true
if !scroll_container:
for child in get_children():
if child is ScrollContainer:
scroll_container = get_path_to(child)
break
__scroll_container = get_node_or_null(scroll_container) as ScrollContainer
set_process(false)
if __scroll_container:
# __scroll_container.connect("scroll_started", self, "_on_scroll_started")
# __scroll_container.connect("scroll_ended", self, "_on_scroll_ended")
__scroll_container.get_h_scrollbar().connect("value_changed", self, "_on_scroll_value_changed")
__scroll_container.get_v_scrollbar().connect("value_changed", self, "_on_scroll_value_changed")
func _draw():
if !__scroll_container || __scroll_container.get_child_count() < 1:
return
var first_child := __scroll_container.get_child(0) as Control
if !first_child:
return
for i in range(textures.size()):
var texture : Texture = textures[i]
var speed : float = speeds[i] if i < speeds.size() else 1.0
var rel_scroll := __scroll_container.scroll_vertical / max(first_child.rect_size.y - __scroll_container.rect_size.y, 1.0)
var tex_size := rect_size
var tex_pos := Vector2()
tex_size.y = tex_size.x * (texture.get_height() / texture.get_width())
tex_pos.y = speed * -rel_scroll * (tex_size.y - rect_size.y)
draw_texture_rect(texture, Rect2(tex_pos, tex_size), false)
func _process(delta):
update()
func _on_scroll_started():
set_process(true)
func _on_scroll_ended():
set_process(false)
func _on_scroll_value_changed(value : float):
update()

View File

@@ -0,0 +1,11 @@
extends CheckBox
class_name UIB_SettingCheckBox
export var setting_name = ""
func _ready():
pressed = GDBSettings.get_value(setting_name, false)
func _pressed():
GDBSettings.set_value(setting_name, pressed)

View File

@@ -0,0 +1,45 @@
extends OptionButton
class_name UIB_SettingOptionButton
export var setting_name := ""
export(Array) var values := []
export(Array, String) var labels := []
export(Array, Texture) var icons := []
#############
# overrides #
#############
func _ready():
__setup()
connect("item_selected", self, "_on_item_selected")
#################
# private stuff #
#################
func __setup():
clear()
for i in range(values.size()):
var value : String = values[i]
var label : String = labels[i] if i < labels.size() else str(value)
var icn : Texture = icons[i] if i < icons.size() else null
add_item(label)
set_item_icon(i, icn)
if !setting_name:
return
var current_value = GDBSettings.get_value(setting_name)
var current_idx = values.find(current_value)
if current_idx >= 0:
select(current_idx)
############
# handlers #
############
func _on_item_selected(idx : int):
if setting_name:
GDBSettings.set_value(setting_name, values[idx])

View File

@@ -0,0 +1,231 @@
extends Popup
class_name UIB_SuggestionPopup
export(NodePath) var line_edit_path = ".."
export(int, 1, 20) var max_values = 5
export(Array, String) var suggestions = [] setget _set_suggestions
export(StyleBox) var style_normal = preload("res://addons/de.mewin.gduibasics/styles/suggestion_line_normal.stylebox")
export(StyleBox) var style_active = preload("res://addons/de.mewin.gduibasics/styles/suggestion_line_active.stylebox")
onready var line_edit : LineEdit = get_node(line_edit_path)
var __v_box_container := VBoxContainer.new()
var __panel := Panel.new()
var __labels := []
var __filtered_suggestions := []
var __filter_text := ""
var __active_label := -1
var __scroll := 0
var __up_direction := false
#############
# overrides #
#############
func _ready():
call_deferred("__initial_setup")
func _gui_input(event : InputEvent):
if !event.is_pressed() || !event is InputEventMouseButton:
return
if event.button_index == BUTTON_LEFT:
__accept_suggestion()
accept_event()
elif event.button_index == BUTTON_WHEEL_DOWN:
if !__up_direction:
__focus_down()
else:
__focus_up()
elif event.button_index == BUTTON_WHEEL_UP:
if !__up_direction:
__focus_up()
else:
__focus_down()
#################
# private stuff #
#################
func __initial_setup():
__panel.show_behind_parent = true
add_child(__panel)
add_child(__v_box_container)
for i in range(max_values):
var label := Label.new()
label.size_flags_horizontal |= Control.SIZE_EXPAND
label.mouse_filter = Control.MOUSE_FILTER_PASS
label.connect("mouse_entered", self, "_on_label_mouse_entered", [i])
__labels.append(label)
__v_box_container.add_child(label)
__setup()
func __setup():
if !line_edit:
return
line_edit.focus_neighbour_top = "."
line_edit.focus_neighbour_bottom = "."
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("text_changed", self, "_on_line_edit_text_changed")
line_edit.connect("gui_input", self, "_on_line_edit_gui_input")
func __calc_rect() -> Rect2:
var size := Vector2(line_edit.rect_size.x, __v_box_container.get_combined_minimum_size().y)
var lower_rect := Rect2(line_edit.rect_global_position \
+ Vector2(0.0, line_edit.rect_size.y), size)
if get_viewport_rect().encloses(lower_rect):
if __up_direction:
__up_direction = false
__update_labels(false)
return lower_rect
else:
var upper_rect := Rect2(line_edit.rect_global_position \
- Vector2(0.0, size.y), size)
if !__up_direction:
__up_direction = true
__update_labels(false)
return upper_rect
func __get_label(idx : int):
assert(idx < __labels.size())
if __up_direction:
return __labels[__labels.size() - 1 - idx]
return __labels[idx]
func __score_suggestion(suggestion : String) -> float:
if suggestion.to_lower().begins_with(__filter_text):
return 0.5 + 0.5 * suggestion.similarity(line_edit.text)
else:
return 0.5 * suggestion.similarity(line_edit.text)
func __compare_suggestions(sugg0 : String, sugg1 : String):
var score0 := __score_suggestion(sugg0)
var score1 := __score_suggestion(sugg1)
if score0 != score1:
return score0 > score1
else:
return sugg0 < sugg1
func __filter():
__filter_text = line_edit.text.to_lower() # .left(line_edit.caret_position).to_lower()
__filtered_suggestions = suggestions.duplicate()
__filtered_suggestions.sort_custom(self, "__compare_suggestions")
func __update_labels(update_size := true):
for i in range(__labels.size()):
var label : Label = __get_label(i)
if i >= __filtered_suggestions.size():
label.visible = false
else:
label.visible = true
label.text = __filtered_suggestions[(i + __scroll) % __filtered_suggestions.size()]
if i == __active_label:
label.add_stylebox_override("normal", style_active)
else:
label.add_stylebox_override("normal", style_normal)
if update_size:
var rect := __calc_rect()
rect_position = rect.position
rect_size = rect.size
__panel.rect_size = rect_size
__v_box_container.rect_size = rect_size
func __focus_down():
if __active_label == __labels.size() - 1:
__scroll = min(__scroll + 1, __filtered_suggestions.size() - __labels.size())
if __scroll < 0:
__scroll = 0
else:
__active_label += 1
__update_labels()
line_edit.caret_position = line_edit.text.length()
func __focus_up():
if __active_label < 0:
return
elif __active_label > 0:
__active_label -= 1
elif __scroll > 0:
__scroll -= 1
else:
__active_label = -1
__update_labels()
line_edit.caret_position = line_edit.text.length()
func __accept_suggestion():
if __active_label < 0:
return
var suggestion = __get_label(__active_label).text
line_edit.text = suggestion
line_edit.caret_position = suggestion.length()
__active_label = -1
__scroll = 0
__filter()
__update_labels()
emit_signal("suggestion_accepted", suggestion)
###########
# setters #
###########
func _set_suggestions(value : Array):
suggestions = value
if visible:
__filter()
__update_labels()
############
# handlers #
############
func _on_line_edit_focus_entered():
__filter()
__update_labels()
popup()
func _on_line_edit_focus_exited():
visible = false
func _on_line_edit_text_changed(new_text : String):
if !line_edit || !line_edit.has_focus():
return
__active_label = -1
__scroll = 0
__filter()
__update_labels()
if !visible:
popup()
func _on_line_edit_gui_input(input_event : InputEvent):
if !input_event.is_pressed():
return
if input_event.is_action_pressed("ui_accept"):
__accept_suggestion()
elif input_event.is_action("ui_down"):
if !__up_direction:
__focus_down()
else:
__focus_up()
elif input_event.is_action("ui_up"):
if !__up_direction:
__focus_up()
else:
__focus_down()
func _on_label_mouse_entered(index : int):
if __up_direction:
index = __labels.size() - index - 1
if __active_label != index:
__active_label = index
__update_labels()
###########
# signals #
###########
signal suggestion_accepted(suggestion)

View File

@@ -0,0 +1,41 @@
extends ItemList
class_name UIB_TabsItemList
export(NodePath) var tab_container
#############
# overrides #
#############
func _ready():
call_deferred("__setup")
connect("item_selected", self, "_on_item_selected")
#################
# private stuff #
#################
func __get_tab_container() -> TabContainer:
return get_node_or_null(tab_container) as TabContainer
func __setup():
var tab_container_ := __get_tab_container()
clear()
if !tab_container_:
return
for i in range(tab_container_.get_child_count()):
var title := tab_container_.get_tab_title(i)
var icon := tab_container_.get_tab_icon(i)
add_item(title, icon)
select(tab_container_.current_tab)
############
# handlers #
############
func _on_item_selected(idx : int):
var tab_container_ := __get_tab_container()
if tab_container_:
tab_container_.current_tab = idx

View File

@@ -0,0 +1,77 @@
extends ScrollContainer
class_name UIB_TouchScrollContainer
export(float) var drag_threshold := 20.0
var __dragging := false
var __drag_start := Vector2()
var __drag_start_scroll := Vector2()
var __momentum := Vector2()
func _ready():
set_process(false)
get_h_scrollbar().modulate = Color.transparent
get_v_scrollbar().modulate = Color.transparent
func _input(event):
if event is InputEventMouseMotion && (event.button_mask & BUTTON_MASK_LEFT) && \
(__dragging || get_global_rect().has_point(event.position)):
if !__dragging:
var dragged = event.position - __drag_start
if abs(dragged.x) > drag_threshold \
|| abs(dragged.y) > drag_threshold:
__dragging = true
else:
__momentum = event.relative
if scroll_horizontal_enabled:
scroll_horizontal = __drag_start_scroll.x + __drag_start.x - event.position.x
if scroll_vertical_enabled:
scroll_vertical = __drag_start_scroll.y + __drag_start.y - event.position.y
elif event is InputEventMouseButton && event.button_index == BUTTON_LEFT \
&& get_global_rect().has_point(event.position):
if !event.pressed:
if __dragging:
__dragging = false
set_process(true) # process momentum
else:
__emulate_click(event)
else:
__drag_start = event.position
__drag_start_scroll = Vector2(scroll_horizontal, scroll_vertical)
get_tree().set_input_as_handled()
func _process(delta : float):
__momentum *= 1.0 - 1.5 * delta
if abs(__momentum.x) < 0.01 && abs(__momentum.y) < 0.01:
set_process(false)
return
if scroll_horizontal_enabled:
scroll_horizontal -= __momentum.x * 50.0 * delta
if scroll_vertical_enabled:
scroll_vertical -= __momentum.y * 50.0 * delta
func __emulate_click(orig_event : InputEventMouseButton):
if get_child_count() < 1:
return
var child = get_child(0) as Control
if !child:
return
set_process_input(false)
var down_event : InputEventMouseButton = orig_event.duplicate()
down_event.position = orig_event.global_position
down_event.pressed = true
Input.parse_input_event(down_event)
yield(get_tree(), "idle_frame")
var up_event : InputEventMouseButton = orig_event.duplicate()
up_event.position = orig_event.global_position
up_event.pressed = false
Input.parse_input_event(up_event)
set_process_input(true)

View File

@@ -0,0 +1,28 @@
extends UIB_BorderContainer
class_name UIB_WindowBorderContainer
export var minimum_size := Vector2(100.0, 100.0)
#############
# overrides #
#############
func _win_get_position() -> Vector2:
return OS.window_position
func _win_get_size() -> Vector2:
return OS.window_size
func _win_get_mouse_position() -> Vector2:
return get_viewport().get_mouse_position() + OS.window_position
func _win_get_minimum_size() -> Vector2:
return minimum_size
func _win_set_position(pos : Vector2):
if pos != OS.window_position:
OS.window_position = pos
OS.window_maximized = false
func _win_set_size(size : Vector2):
OS.window_size = size