Initial commit
This commit is contained in:
82
scripts/types/controls/action_button.gd
Normal file
82
scripts/types/controls/action_button.gd
Normal 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
|
||||
57
scripts/types/controls/action_menu_button.gd
Normal file
57
scripts/types/controls/action_menu_button.gd
Normal 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()
|
||||
50
scripts/types/controls/action_popup_menu.gd
Normal file
50
scripts/types/controls/action_popup_menu.gd
Normal 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()
|
||||
29
scripts/types/controls/action_toolbar.gd
Normal file
29
scripts/types/controls/action_toolbar.gd
Normal 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()
|
||||
250
scripts/types/controls/border_container.gd
Normal file
250
scripts/types/controls/border_container.gd
Normal 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()
|
||||
95
scripts/types/controls/dynamic_grid_container.gd
Normal file
95
scripts/types/controls/dynamic_grid_container.gd
Normal 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()
|
||||
57
scripts/types/controls/editor_inspector_category.gd
Normal file
57
scripts/types/controls/editor_inspector_category.gd
Normal 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()
|
||||
203
scripts/types/controls/editor_node_tree.gd
Normal file
203
scripts/types/controls/editor_node_tree.gd
Normal 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()
|
||||
280
scripts/types/controls/file_list.gd
Normal file
280
scripts/types/controls/file_list.gd
Normal 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)
|
||||
43
scripts/types/controls/flow_container.gd
Normal file
43
scripts/types/controls/flow_container.gd
Normal 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
|
||||
79
scripts/types/controls/folder_edit.gd
Normal file
79
scripts/types/controls/folder_edit.gd
Normal 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()
|
||||
104
scripts/types/controls/folder_sync.gd
Normal file
104
scripts/types/controls/folder_sync.gd
Normal 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
|
||||
16
scripts/types/controls/named_tab_container.gd
Normal file
16
scripts/types/controls/named_tab_container.gd
Normal 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])
|
||||
122
scripts/types/controls/places_list.gd
Normal file
122
scripts/types/controls/places_list.gd
Normal 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()
|
||||
18
scripts/types/controls/recent_places_list.gd
Normal file
18
scripts/types/controls/recent_places_list.gd
Normal 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)
|
||||
58
scripts/types/controls/scroll_parallax_bg.gd
Normal file
58
scripts/types/controls/scroll_parallax_bg.gd
Normal 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()
|
||||
11
scripts/types/controls/setting_check_box.gd
Normal file
11
scripts/types/controls/setting_check_box.gd
Normal 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)
|
||||
45
scripts/types/controls/setting_option_button.gd
Normal file
45
scripts/types/controls/setting_option_button.gd
Normal 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])
|
||||
231
scripts/types/controls/suggestion_popup.gd
Normal file
231
scripts/types/controls/suggestion_popup.gd
Normal 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)
|
||||
41
scripts/types/controls/tabs_item_list.gd
Normal file
41
scripts/types/controls/tabs_item_list.gd
Normal 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
|
||||
77
scripts/types/controls/touch_scroll_container.gd
Normal file
77
scripts/types/controls/touch_scroll_container.gd
Normal 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)
|
||||
28
scripts/types/controls/window_border_container.gd
Normal file
28
scripts/types/controls/window_border_container.gd
Normal 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
|
||||
Reference in New Issue
Block a user