Initial Commit

This commit is contained in:
Mario Steele 2026-02-23 18:38:03 -06:00
commit 48a5e71e00
1136 changed files with 64347 additions and 0 deletions

View file

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="Container.svg"
inkscape:export-filename="..\GODOT\ToShare\favoritedock\addons\script-ide\split_gui\icon\MultiSpliter.svg"
inkscape:export-xdpi="877.71429"
inkscape:export-ydpi="877.71429"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050">
<inkscape:page
x="0"
y="0"
width="16"
height="16"
id="page2"
margin="0"
bleed="0" />
</sodipodi:namedview>
<path
fill="#8eef97"
d="M 1,1 V 5 H 5 V 1 Z m 9,0 v 4 h 5 V 1 Z m -9,9 v 5 h 4 v -5 z m 9,0 v 5 h 5 V 10 Z M 8,15 7,9 1,8 H 15 L 9,7 8,1 Z"
id="path1"
sodipodi:nodetypes="ccccccccccccccccccccccccccc"
inkscape:export-filename="..\GODOT\ToShare\favoritedock\addons\script-ide\split_gui\GODOT\ToShare\favoritedock\addons\script-ide\split_gui\icon\MultiSpliter.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c042di3o7rqml"
path="res://.godot/imported/MultiSpliter.svg-3fac225927d0b135f9e22aa6666d3a55.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/script_splitter/core/ui/multi_split_container/icon/MultiSpliter.svg"
dest_files=["res://.godot/imported/MultiSpliter.svg-3fac225927d0b135f9e22aa6666d3a55.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=8.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="ControlItemButton.svg"
inkscape:export-filename="ToShare\favoritedock\addons\script-ide\split_gui\icon\MultiSpliterButton.svg"
inkscape:export-xdpi="768"
inkscape:export-ydpi="768"
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:zoom="15.999999"
inkscape:cx="4.4375002"
inkscape:cy="8.5937504"
inkscape:window-width="1287"
inkscape:window-height="745"
inkscape:window-x="65"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg1">
<inkscape:page
x="0"
y="0"
width="16"
height="16"
id="page2"
margin="0"
bleed="0" />
</sodipodi:namedview>
<rect
style="fill:#bfbfbf;fill-opacity:1;stroke:#757575;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;paint-order:stroke markers fill"
id="rect1"
width="12"
height="15"
x="2"
y="0.45580584"
ry="2" />
<circle
style="fill:#ffffff;fill-opacity:1;stroke:#bbbbbb;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="path1"
cy="2.8863502"
cx="5.3863502"
r="1.8863502" />
<circle
style="fill:#ffffff;fill-opacity:1;stroke:#bbbbbb;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="path1-1"
cy="7.965826"
cx="5.3863502"
r="1.8863502" />
<circle
style="fill:#ffffff;fill-opacity:1;stroke:#bbbbbb;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="path1-1-7"
cy="7.965826"
cx="10.465826"
r="1.8863502" />
<circle
style="fill:#ffffff;fill-opacity:1;stroke:#bbbbbb;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="path1-1-7-2"
cy="13.0453"
cx="5.3863502"
r="1.8863502" />
<circle
style="fill:#ffffff;fill-opacity:1;stroke:#bbbbbb;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="path1-1-7-2-7"
cy="13.0453"
cx="10.465826"
r="1.8863502" />
<circle
style="fill:#ffffff;fill-opacity:1;stroke:#bbbbbb;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="path1-5"
cy="2.8863502"
cx="10.465826"
r="1.8863502" />
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d1oshw4o6jues"
path="res://.godot/imported/MultiSpliterButton.svg-2d0a07173fe1e75a33d019b041262a7d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/script_splitter/core/ui/multi_split_container/icon/MultiSpliterButton.svg"
dest_files=["res://.godot/imported/MultiSpliterButton.svg-2d0a07173fe1e75a33d019b041262a7d.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=8.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg1"
sodipodi:docname="Control.svg"
inkscape:export-filename="..\GODOT\ToShare\favoritedock\addons\script-ide\split_gui\icon\MultiSplitItem.png"
inkscape:export-xdpi="768"
inkscape:export-ydpi="768"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050">
<inkscape:page
x="0"
y="0"
width="16"
height="16"
id="page2"
margin="0"
bleed="0" />
</sodipodi:namedview>
<circle
cx="8"
cy="8"
fill="none"
stroke="#8eef97"
stroke-width="2"
id="circle1"
r="5" />
<circle
style="fill:none;fill-opacity:1;stroke:#8eef97;stroke-width:0.933;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
id="path2"
cx="7.9897118"
cy="7.9897118"
r="2.1256859" />
<path
style="fill:#8eef97;fill-opacity:1;stroke:none;stroke-width:0.933;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
d="M 1,5 5,1 H 1 M 1,5 5,1 H 1"
id="path7"
sodipodi:nodetypes="ccc" />
<path
style="fill:#8eef97;fill-opacity:1;stroke:none;stroke-width:0.933;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
d="M 15,5 V 1 h -5"
id="path7-8"
sodipodi:nodetypes="ccc" />
<path
style="fill:#8eef97;fill-opacity:1;stroke:none;stroke-width:0.933;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
d="m 10,15 h 5 m -5,0 h 5 v -5"
id="path7-8-5"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#8eef97;fill-opacity:1;stroke:none;stroke-width:0.933;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
d="m 1,10 4,5 H 1 m 0,0 H 5 1"
id="path7-8-5-1"
sodipodi:nodetypes="cccccc" />
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dvg5cjdqtdpd7"
path="res://.godot/imported/MultiSpliterItem.svg-1c00cf564bbcca09a4b17a958816995f.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/script_splitter/core/ui/multi_split_container/icon/MultiSpliterItem.svg"
dest_files=["res://.godot/imported/MultiSpliterItem.svg-1c00cf564bbcca09a4b17a958816995f.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=8.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,998 @@
@tool
@icon("icon/MultiSpliter.svg")
extends Container
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# https://github.com/CodeNameTwister/Multi-Split-Container
#
# Multi-Split-Container addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
const SplitContainerItem : Script = preload("split_container_item.gd")
const SplitButton : Texture = preload("icon/MultiSpliterButton.svg")
@export_category("Multi-Split Settings")
## Max columns by rows, after added childs this is eparated by row group by columns size!
## [br][br]
## if this value is 0, will not create rows spliters.
@export_range(0.0, 1000.0, 1.0) var max_columns : int = 0:
set(e):
max_columns = maxi(0, e)
if Engine.is_editor_hint():
for x : int in range(separators_line_offsets.size()):
separators_line_offsets[x] = 0.0
for x : LineSep in _separators:
x.queue_free()
_separators.clear()
_first = true
update()
@export_group("Line Separator", "separator_line")
## Line separator size.
@export var separator_line_size : float = 4.0:
set(e):
separator_line_size = max(e, 0.0)
update()
## Separator line color.
@export var separator_line_color : Color = Color.MAGENTA:
set(e):
separator_line_color = e
if separator_line_color == Color.MAGENTA: # That color reminds me of texture not found errors.
var root = EditorInterface.get_base_control()
separator_line_color = root.get_theme_color("base_color", "Editor")
update()
## Separator line visibility.
@export var separator_line_visible : bool = true:
set(e):
separator_line_visible = e
for l : LineSep in _separators:
l.visible = separator_line_visible
@export_subgroup("Behaviour", "behaviour_")
## Enable function for auto expand lines container on inside focus.
@export var behaviour_expand_on_focus : bool = true
## Enable function for auto expand lines container on double click in the line.
@export var behaviour_expand_on_double_click : bool = true:
set(e):
behaviour_expand_on_double_click = e
for l : LineSep in _separators:
l.double_click_handler = behaviour_expand_on_double_click
## Enable movement by touching line.
@export var behaviour_can_move_by_line : bool = true:
set(e):
behaviour_can_move_by_line = e
for l : LineSep in _separators:
l.draggable = behaviour_can_move_by_line
## This allow expand you current focused container if you shrunk it.
@export var behaviour_can_expand_focus_same_container : bool = false
## Enable smooth when expand container.
@export var behaviour_expand_smoothed : bool = true:
set(e):
behaviour_expand_smoothed = e
if !e:
if _tween and _tween.is_running():
_tween.kill()
_tween = null
## Time speed duration for reset expand container.
@export_range(0.01, 1000.0, 0.01) var behaviour_expand_smoothed_time : float = 0.24:
set(e):
behaviour_expand_smoothed_time = maxf(0.01, e)
if _tween and _tween.is_running():
_tween.kill()
_tween = null
## Custom initial offset for separator lines. (TODO: Still Working here!)
@export var separators_line_offsets : Array[float] :
set(e):
separators_line_offsets = e
if Engine.is_editor_hint():
if separators_line_offsets.size() != _separators.size():
separators_line_offsets.resize(_separators.size())
update()
@export_subgroup("Drag Button", "drag_button")
## Set if drag button always be visible (Useful for test button size)
@export var drag_button_always_visible : bool = false:
set(e):
drag_button_always_visible = e
var min_visible_drag_button : float = 0.0
if drag_button_always_visible:
min_visible_drag_button = 0.4
for l : LineSep in _separators:
if l.button:
l.button.modulate.a = 0.0
l.button.min_no_focus_transparense = min_visible_drag_button
## Min size for drag button visible on split lines.
@export_range(1.0, 200.0, 0.1) var drag_button_size : float = 24.0:
set(e):
drag_button_size = e
update()
## Modulate color for the drag button.
@export var drag_button_modulate : Color = Color.MAGENTA:
set(e):
drag_button_modulate = e
if drag_button_modulate == Color.MAGENTA:
if Engine.is_editor_hint():
var root : Control = EditorInterface.get_base_control()
drag_button_modulate = root.get_theme_color("base_color", "Editor").lightened(0.5)
update()
## Change default drag button icon.
@export var drag_button_icon : Texture = null:
set(e):
drag_button_icon = e
update()
var _separators : Array[LineSep] = []
var _last_container_focus : Node = null
var _frame : int = 1
var _first : bool = true
var _tween : Tween = null
func get_separators() -> Array[LineSep]:
return _separators
## Get line begin offset limit.
func get_line_seperator_left_offset_limit(index : int) -> float:
if index < _separators.size():
var line_sep : LineSep = _separators[index]
if !line_sep.is_vertical:
if index < 1:
return -_separators[index].initial_position.x
var next : LineSep = _separators[index - 1]
return (next.initial_position.x + (next.size.x/2.0)) - _separators[index].initial_position.x
else:
if index < 1:
return -_separators[index].initial_position.y
var next : LineSep = _separators[index - 1]
return (next.initial_position.y + (next.size.y/2.0)) - _separators[index].initial_position.y
push_warning("[PLUGIN] Not valid index for line separator!")
return 0.0
## Get line end offset limit.
func get_line_seperator_right_offset_limit(index : int) -> float:
if index < _separators.size():
var line_sep : LineSep = _separators[index]
if !line_sep.is_vertical:
if index + 1 == _separators.size():
return (size.x/2.0) -_separators[index].initial_position.x
var current : LineSep = _separators[index]
return (_separators[index + 1].initial_position.x - current.initial_position.x + (current.size.x/2.0))
else:
if index + 1 == _separators.size():
return size.x -_separators[index].initial_position.y
var current : LineSep = _separators[index]
return (_separators[index + 1].initial_position.y - current.initial_position.y + (current.size.y/2.0))
push_warning("[PLUGIN] Not valid index for line separator!")
return 0.0
# This is function is util when you want expand or constraint manualy offset.
## Update offset of the line
func update_line_separator_offset(index : int, offset : float) -> void:
var line_sep : LineSep = _separators[index]
line_sep.offset = offset
line_sep.force_update()
## Get total line count.
func get_line_separator_count() -> int:
return _separators.size()
## Get Line reference by index, see get_line_separator_count()
func get_line_separator(index : int) -> LineSep:
return _separators[index]
## Get if line separator is vertical.
func is_vertical_line_separator(index : int) -> bool:
if index < _separators.size():
return _separators[index].is_vertical
push_warning("[PLUGIN] Not valid index for line separator!")
return false
## Expand splited container by index container.
func expand_splited_container(node : Node) -> void:
var same : bool = _last_container_focus == node
if same and !behaviour_can_expand_focus_same_container:
return
_last_container_focus = node
if !behaviour_expand_on_focus:
return
if _tween and _tween.is_running():
if same:
return
_tween.kill()
_tween = null
var top_lines : Array[LineSep] = []
var bottom_lines : Array[LineSep] = []
var update_required : bool = false
for line : LineSep in _separators:
if node in line.top_items:
update_required = update_required or line.offset < 0.0
top_lines.append(line)
elif node in line.bottom_items:
update_required = update_required or line.offset > 0.0
bottom_lines.append(line)
if update_required:
if behaviour_expand_smoothed:
_tween = get_tree().create_tween()
_tween.tween_method(_reset_expanded_lines.bind(top_lines, bottom_lines), 0.0, 1.0, behaviour_expand_smoothed_time)
else:
_reset_expanded_lines(1.0, top_lines, bottom_lines)
func _reset_expanded_lines(_lerp : float, top_lines : Array[LineSep], bottom_lines : Array[LineSep]) -> void:
for iline : int in range(top_lines.size() - 1, -1, -1):
var line : LineSep = top_lines[iline]
if is_instance_valid(line):
if line.offset < 0.0:
line.offset = lerp(line.offset, 0.0, _lerp)
else:
top_lines.remove_at(iline)
for iline : int in range(bottom_lines.size() - 1, -1, -1):
var line : LineSep = bottom_lines[iline]
if is_instance_valid(line):
if line.offset > 0.0:
line.offset = lerp(line.offset, 0.0, _lerp)
else:
bottom_lines.remove_at(iline)
for line : LineSep in top_lines:
line.force_update()
for line : LineSep in bottom_lines:
line.force_update()
## Get initial position of a separator line.
func get_line_separator_initial_position(index : int) -> Vector2:
if index < _separators.size():
return _separators[index].initial_position
push_warning("[PLUGIN] Not valid index for line separator!")
return Vector2.ZERO
class DragButton extends Button:
var _frm : float = 0.0
var _line_sep : LineSep = null
var _is_pressed : bool = false
var is_hover : bool = false
var _hover : Array[bool] = [false, false]
var min_no_focus_transparense : float = 0.0:
set(e):
min_no_focus_transparense = e
modulate.a = maxf(modulate.a, min_no_focus_transparense)
static var DEFAULT_STYLE : StyleBox = null
func set_drag_icon(new_icon : Texture) -> void:
if icon != new_icon:
if new_icon == null:
icon = SplitButton
return
icon = new_icon
func update_gui() -> void:
if !_line_sep:
return
if _line_sep.is_vertical:
_line_sep.mouse_default_cursor_shape = Control.CURSOR_VSPLIT
mouse_default_cursor_shape = Control.CURSOR_VSPLIT
else:
_line_sep.mouse_default_cursor_shape = Control.CURSOR_HSPLIT
mouse_default_cursor_shape = Control.CURSOR_HSPLIT
func set_line(line_sep : LineSep) -> void:
if _line_sep:
if _line_sep.mouse_entered.is_connected(_on_enter):
_line_sep.mouse_entered.disconnect(_on_enter)
if _line_sep.mouse_exited.is_connected(_on_exit):
_line_sep.mouse_exited.disconnect(_on_exit)
if _line_sep.gui_input.is_connected(_on_input):
_line_sep.gui_input.disconnect(_on_input)
_line_sep = line_sep
if _line_sep:
if !_line_sep.mouse_entered.is_connected(_on_enter):
_line_sep.mouse_entered.connect(_on_enter.bind(1))
if !_line_sep.mouse_exited.is_connected(_on_exit):
_line_sep.mouse_exited.connect(_on_exit.bind(1))
if !_line_sep.gui_input.is_connected(_on_input):
_line_sep.gui_input.connect(_on_input)
func _init(line_sep : LineSep = null) -> void:
modulate.a = 0.0
set_line(line_sep)
button_down.connect(_on_press)
button_up.connect(_out_press)
mouse_entered.connect(_on_enter.bind(0))
mouse_exited.connect(_on_exit.bind(0))
gui_input.connect(_custom_input)
icon = SplitButton
icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER
expand_icon = true
if null != icon:
flat = true
if DEFAULT_STYLE == null:
DEFAULT_STYLE = StyleBoxEmpty.new()
focus_mode = Control.FOCUS_CLICK
set(&"theme_override_styles/focus", DEFAULT_STYLE)
set(&"theme_override_styles/disabled_mirrored", DEFAULT_STYLE)
set(&"theme_override_styles/disabled", DEFAULT_STYLE)
set(&"theme_override_styles/hover_pressed_mirrored", DEFAULT_STYLE)
set(&"theme_override_styles/hover_pressed", DEFAULT_STYLE)
set(&"theme_override_styles/hover_mirrored", DEFAULT_STYLE)
set(&"theme_override_styles/hover", DEFAULT_STYLE)
set(&"theme_override_styles/pressed_mirrored", DEFAULT_STYLE)
set(&"theme_override_styles/pressed", DEFAULT_STYLE)
set(&"theme_override_styles/normal_mirrored", DEFAULT_STYLE)
set(&"theme_override_styles/normal", DEFAULT_STYLE)
z_as_relative = true
z_index = 20
update_gui()
func _custom_input(e : InputEvent) -> void:
if e is InputEventMouseButton:
if e.pressed and e.double_click:
get_tree().call_group(&"ScriptSplitter", &"swap", get_parent())
func _on_input(e : InputEvent) -> void:
if e is InputEventMouseButton:
if e.pressed and e.double_click:
if _line_sep and _line_sep.double_click_handler:
_line_sep.offset = 0.0
_line_sep.offset_updated.emit()
elif e.pressed and _line_sep.draggable and e.button_index == 1:
button_down.emit()
elif !e.pressed and _line_sep.draggable and e.button_index == 1:
button_up.emit()
func set_line_sep_reference(ref : LineSep) -> void:
_line_sep = ref
func _ready() -> void:
set_process(false)
func _on_enter(x : int = 0) -> void:
_hover[x] = true
_frm = 0.0
modulate.a = 1.0
is_hover = true
set_process(true)
func _on_exit(x : int = 0) -> void:
_hover[x] = false
for h : bool in _hover:
if h != false:
return
_frm = 0.0
modulate.a = 1.0
is_hover = false
set_process(true)
func _on_press() -> void:
_is_pressed = true
_frm = 0.0
modulate.a = 1.0
set_process(true)
func _out_press() -> void:
_is_pressed = false
set_process(true)
func _process(delta : float) -> void:
if !has_focus() and !is_hover:
_frm += delta * 0.4
if _frm >= 1.0:
_frm = 1.0
set_process(false)
modulate.a = lerp(modulate.a, min_no_focus_transparense, _frm)
if _is_pressed:
var mpos : Vector2 = _line_sep.get_parent().get_local_mouse_position()
if mpos != get_rect().get_center():
_line_sep.update_offset_by_position(mpos)
class UndoredoSplit extends RefCounted:
var object : SplitContainerItem = null
var c_objects : Array[Node] = []
class LineSep extends ColorRect:
signal offset_updated()
var top_lines : Array[LineSep] = []
var bottom_lines : Array[LineSep] = []
var top_items : Array[Control] = []
var bottom_items : Array[Control] = []
var is_vertical : bool = false:
set(e):
is_vertical = e
if button:
button.update_gui()
var row : int = 0
var initial_position : Vector2 = Vector2.ZERO
var offset : float = 0.0
var min_size_offset : float = 0.0
var prev_line : LineSep = null
var next_line : LineSep = null
var button : DragButton = null
var double_click_handler : bool = true
var draggable : bool = true
func set_next_line(next : LineSep = null) -> void:
next_line = next
next.prev_line = self
func clear() -> void:
top_items.clear()
bottom_items.clear()
top_lines.clear()
bottom_lines.clear()
func reset() -> void:
position = initial_position
update_items()
func update_items() -> void:
if is_vertical:
for item : Control in top_items:
item.size.y = position.y - item.position.y
if !prev_line:
item.position.y = 0.0
for item : Control in bottom_items:
item.position.y = position.y + size.y
if next_line:
item.size.y = next_line.position.y - item.position.y
else:
item.size.y = get_parent().size.y - item.position.y
else:
for item : Control in top_items:
item.size.x = position.x - item.position.x + (size.x / 2.0) - 2.0
if !prev_line:
item.position.x = 0.0
for item : Control in bottom_items:
var diff : float = position.x + (size.x / 2.0) + 2.0
item.position.x = diff
if next_line:
item.size.x = next_line.position.x - item.position.x
else:
item.size.x = get_parent().size.x - item.position.x
func force_update() -> void:
update_offset_by_position(initial_position + Vector2(offset * int(!is_vertical), offset * int(is_vertical)))
func get_current_position() -> Vector2:
return initial_position + Vector2(offset * int(!is_vertical), offset * int(is_vertical))
func update_offset_by_position(vpos : Vector2) -> void:
if is_vertical:
min_size_offset = 0.0
for x : Control in top_items:
min_size_offset = maxf(min_size_offset, x.get_minimum_size().y)
if prev_line:
prev_line.min_size_offset = 0.0
for x : Control in prev_line.bottom_items:
prev_line.min_size_offset = maxf(prev_line.min_size_offset, x.get_minimum_size().y)
offset = vpos.y - initial_position.y
offset = minf(offset, get_parent().size.y - (initial_position.y + size.y + min_size_offset))
offset = maxf(offset, -(initial_position.y - min_size_offset))
if next_line:
var val : float = next_line.position.y - (initial_position.y + size.y + min_size_offset)
if offset > val:
offset = val
else:
var val : float = get_parent().size.y - (initial_position.y + (size.y / 2.0) + min_size_offset)
if offset > val:
offset = val
if prev_line:
var val : float = -(initial_position.y - (prev_line.position.y + prev_line.size.y + prev_line.min_size_offset))
if offset < val:
offset = val
else:
var top_size_offset : float = 0.0
for x : Control in top_items:
top_size_offset = maxf(top_size_offset, x.get_minimum_size().y)
offset = maxf(offset, top_size_offset-initial_position.y)
position.y = initial_position.y + offset
for line : LineSep in top_lines:
line.size.y = position.y - line.position.y
for line : LineSep in bottom_lines:
line.position.y = position.y + size.y
if next_line:
line.size.y = next_line.position.y - line.position.y
else:
line.size.y = get_parent().size.y - line.position.y
else:
min_size_offset = 0.0
for x : Control in bottom_items:
min_size_offset = maxf(min_size_offset, x.get_minimum_size().x)
if prev_line:
prev_line.min_size_offset = 0.0
for x : Control in prev_line.bottom_items:
prev_line.min_size_offset = maxf(prev_line.min_size_offset, x.get_minimum_size().x)
offset = vpos.x - initial_position.x
offset = minf(offset, get_parent().size.x - (initial_position.x + size.x + min_size_offset))
offset = maxf(offset, -initial_position.x)
if next_line:
var val : float = next_line.position.x - (initial_position.x + size.x + min_size_offset)
if offset > val:
offset = val
else:
var val : float = get_parent().size.x - (initial_position.x + (size.x/2.0) + min_size_offset)
if offset > val:
offset = val
if prev_line:
var val : float = -(initial_position.x - (prev_line.position.x + prev_line.size.x + prev_line.min_size_offset))
if offset < val:
offset = val
else:
var top_size_offset : float = 0.0
for x : Control in top_items:
top_size_offset = maxf(top_size_offset, x.get_minimum_size().x)
offset = maxf(offset, top_size_offset-initial_position.x)
position.x = initial_position.x + offset
update_items()
func _draw() -> void:
update()
func update() -> void:
button.rotation_degrees = 90.0 * int(is_vertical)
button.pivot_offset = button.size / 2.0
button.position = size / 2.0 - button.pivot_offset
func _init() -> void:
color = Color.RED
func _ready() -> void:
name = "SplitLine"
if button == null:
button = DragButton.new(self)
add_child(button, false, Node.INTERNAL_MODE_BACK)
func _test() -> void:
queue_redraw()
func _init() -> void:
child_entered_tree.connect(_on_enter)
child_exiting_tree.connect(_on_exiting)
func update() -> void:
set_process(true)
func _create_separator() -> Control:
var line_sep : LineSep = LineSep.new()
line_sep.offset_updated.connect(update)
return line_sep
func _undoredo_undo(ur : UndoredoSplit) -> void:
if !is_instance_valid(ur):
return
var split : SplitContainerItem = ur.object
if is_instance_valid(split):
if split.get_parent() == self:
ur.c_objects = split.get_children()
for x : Node in ur.c_objects:
split.remove_child(x)
if x is Control:
x.visible = false
add_child(x)
if is_instance_valid(split) and split.get_parent() == self:
remove_child(split)
func _update() -> void:
var items : Array[Control] = []
for x : Node in get_children():
if is_instance_valid(x) and x is Control:
if x.visible and !x.is_queued_for_deletion():
if x is SplitContainerItem:
if x.get_child_count() > 0:
var _is_visible : bool = false
for y : Node in x.get_children():
if y is Control and y.visible:
_is_visible = true
break
if !_is_visible:
continue
else:
x.queue_free()
continue
elif x is DragButton or x is LineSep:
x.queue_free()
continue
else:
var container : SplitContainerItem = SplitContainerItem.new()
add_child(container, true)
x.reparent(container)
x = container
x.size_flags_horizontal = Control.SIZE_FILL
x.size_flags_vertical = Control.SIZE_FILL
x.clip_contents = true
x.custom_minimum_size = Vector2.ZERO
items.append(x)
var totals : int = items.size()
var rows : int = 0
if max_columns > 0:
var _totals : int = totals
rows = 0
while _totals > max_columns:
_totals -= max_columns
rows += 1
totals -= rows
if totals < 1:
for x : int in range(0, _separators.size(), 1):
_separators[x].queue_free()
_separators[x] = null
_separators.clear()
for x : Control in items:
x.position = Vector2.ZERO
x.size = get_rect().size
return
else:
if separator_line_size <= 0.0:
for x : int in range(0, _separators.size(), 1):
_separators[x].queue_free()
_separators[x] = null
_separators.clear()
else:
var sep : int = totals - 1 + rows
for x : int in range(sep, _separators.size(), 1):
_separators[x].queue_free()
_separators[x] = null
_separators.resize(sep)
for x : int in range(0, _separators.size(), 1):
if _separators[x] == null:
_separators[x] = _create_separator()
rows += 1
if max_columns > 1:
if totals > max_columns:
totals = max_columns
var rect_size : Vector2 = get_rect().size
var start_position : Vector2 = Vector2.ZERO
var size_split : Vector2 = (rect_size / Vector2(totals, rows))
var size_sep : Vector2 = Vector2.ONE * separator_line_size
if totals > 1:
size_sep = (size_sep / (totals - 1))
var item_size : Vector2 = Vector2(size_split.x, size_split.y)
var line_size : Vector2 = Vector2(separator_line_size, item_size.y)
var total_items : int = items.size()
var vpos : Vector2 = Vector2.ZERO
var current_row : int = 0
var item_index : int = 0
var last_vline : LineSep = null
var last_hline : LineSep = null
for x : Control in items:
x.position = Vector2.ZERO
x.size = x.get_minimum_size()
for z : int in range(_separators.size()):
var x : LineSep = _separators[z]
x.clear()
start_position.x += 1
if 0 < max_columns and start_position.x + 1 > max_columns:
total_items -= max_columns
start_position.x = 0.0
start_position.y += 1.0
current_row += 1
if total_items <= max_columns and total_items > 0:
size_split = (rect_size / Vector2(total_items, rows))
if total_items == 1:
size_sep = Vector2.ONE * separator_line_size
else:
size_sep = ((Vector2.ONE * separator_line_size) / (total_items - 1))
item_size = Vector2(size_split.x, size_split.y)
line_size = Vector2(separator_line_size, rect_size.y - x.position.y)
vpos = Vector2(0.0, start_position.y) * item_size
x.is_vertical = true
if x.get_parent() == null:
add_child(x, false, Node.INTERNAL_MODE_BACK)
x.row = current_row
if items.size() > 0:
var it : int = mini(item_index, items.size() - 1)
var min_size : float = 0.0
var _has : bool = false
for y : int in range(z - 1, -1, -1):
if it > -1:
var item : Control = items[it]
x.top_items.append(item)
min_size = maxf(item.get_minimum_size().y, min_size)
it -= 1
var ln : LineSep = _separators[y]
if ln.is_vertical:
_has = true
break
x.top_lines.append(ln)
if !_has:
for _it : int in range(it, -1, -1):
var item : Control = items[it]
x.top_items.append(item)
if item_index + 1 < items.size():
it = item_index + 1
_has = false
for y : int in range(z + 1, _separators.size(), 1):
if it < items.size():
var item : Control = items[it]
x.bottom_items.append(item)
it += 1
var ln : LineSep = _separators[y]
if ln.is_vertical:
_has = true
break
x.bottom_lines.append(ln)
if !_has:
for _it : int in range(it, items.size(), 1):
var item : Control = items[_it]
x.bottom_items.append(item)
var vline_size : Vector2 = Vector2(rect_size.x, separator_line_size)
x.initial_position = vpos
x.initial_position.y -= (vline_size.y) / 2.0
x.position = x.initial_position
x.button.size = Vector2(drag_button_size, drag_button_size)
x.set(&"size", vline_size)
x.update()
if last_vline:
last_vline.set_next_line(x)
last_vline = x
last_hline = null
item_index += 1
continue
vpos = start_position * item_size
if x.get_parent() == null:
add_child(x, false, Node.INTERNAL_MODE_BACK)
if item_index < items.size():
var item : Control = items[item_index]
x.top_items.append(item)
item_index += 1
if item_index < items.size():
if z + 1 < _separators.size():
if !_separators[z].is_vertical:
x.bottom_items.append(items[item_index])
else:
x.bottom_items.append(items[item_index])
x.initial_position = vpos
x.initial_position.x -= (line_size.x) / 2.0
x.button.size = Vector2(drag_button_size, drag_button_size)
x.row = current_row
x.position = x.initial_position
x.set(&"size", line_size)
x.update()
if last_hline:
last_hline.set_next_line(x)
last_hline = x
for x : Control in items:
x.size = size
var min_visible_drag_button : float = 0.0
if drag_button_always_visible:
min_visible_drag_button = 0.4
if _first:
for l : LineSep in _separators:
l.visible = separator_line_visible
l.color = separator_line_color
l.double_click_handler = behaviour_expand_on_double_click
l.button.self_modulate = drag_button_modulate
l.button.min_no_focus_transparense = min_visible_drag_button
l.button.set_drag_icon(drag_button_icon)
l.draggable = behaviour_can_move_by_line
l.reset()
else:
if separators_line_offsets.size() > 0:
for l : int in range(0, _separators.size(), 1):
if l < separators_line_offsets.size():
_separators[l].offset = separators_line_offsets[l]
continue
break
for l : LineSep in _separators:
l.visible = separator_line_visible
l.color = separator_line_color
l.double_click_handler = behaviour_expand_on_double_click
l.button.self_modulate = drag_button_modulate
l.button.min_no_focus_transparense = min_visible_drag_button
l.button.set_drag_icon(drag_button_icon)
l.draggable = behaviour_can_move_by_line
l.force_update()
if !Engine.is_editor_hint():
separators_line_offsets.clear()
else:
for l : int in range(0, _separators.size(), 1):
if l < separators_line_offsets.size():
separators_line_offsets[l] = _separators[l].offset
continue
break
func _on_enter(n : Node) -> void:
n.is_inside_tree()
if n is SplitContainerItem or (n is Control and !Engine.is_editor_hint()):
if !n.visibility_changed.is_connected(_on_visible):
n.visibility_changed.connect(_on_visible)
if is_node_ready():
for x : int in range(separators_line_offsets.size()):
separators_line_offsets[x] = 0.0
update()
func _on_visible() -> void:
update()
func _on_exiting(n : Node) -> void:
if n is SplitContainerItem or (n is Control and !Engine.is_editor_hint()):
if is_node_ready():
for x : int in range(separators_line_offsets.size()):
separators_line_offsets[x] = 0.0
for x : LineSep in _separators:
x.offset = 0.0
if n.visibility_changed.is_connected(_on_visible):
n.visibility_changed.disconnect(_on_visible)
update()
func _process(__ : float) -> void:
if is_node_ready():
if _frame > 0:
_frame -= 1
return
_update()
if _first:
_first = false
else:
set_process(false)
func _on_exiting_tree() -> void:
var vp : Viewport = get_viewport()
if vp and vp.size_changed.is_connected(update):
vp.size_changed.disconnect(update)
var parent : Node = get_parent()
if parent is Control:
if parent.item_rect_changed.is_connected(update):
parent.item_rect_changed.disconnect(update)
func _enter_tree() -> void:
var vp : Viewport = get_viewport()
if vp and !vp.size_changed.is_connected(update):
vp.size_changed.connect(update)
var parent : Node = get_parent()
if parent is Control:
if !parent.item_rect_changed.is_connected(update):
parent.item_rect_changed.connect(update)
if !tree_exiting.is_connected(_on_exiting_tree):
tree_exiting.connect(_on_exiting_tree)
func _on_draw() -> void:
update()
func _ready() -> void:
separator_line_color = separator_line_color
drag_button_modulate = drag_button_modulate
size_flags_horizontal = Control.SIZE_EXPAND_FILL
size_flags_vertical =Control.SIZE_EXPAND_FILL
set_deferred(&"anchor_left", 0.0)
set_deferred(&"anchor_top", 0.0)
set_deferred(&"anchor_bottom", 1.0)
set_deferred(&"anchor_right", 1.0)
if Engine.is_editor_hint():
draw.connect(_on_draw)
if _first:
update()

View file

@ -0,0 +1 @@
uid://sffqwro3flkc

View file

@ -0,0 +1,69 @@
@tool
@icon("icon/MultiSpliterItem.svg")
extends Control
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# https://github.com/CodeNameTwister/Multi-Split-Container
#
# Multi-Split-Container addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
var focus_handler : bool = false
## Expand if tight by spliter
func show_splited_container() -> void:
var parent : Node = get_parent()
if parent.has_method(&"expand_splited_container"):
parent.call(&"expand_splited_container", self)
func _ready() -> void:
set_process(false)
size_flags_horizontal = Control.SIZE_FILL
size_flags_vertical = Control.SIZE_FILL
set_deferred(&"anchor_left", 0.0)
set_deferred(&"anchor_top", 0.0)
set_deferred(&"anchor_bottom", 1.0)
set_deferred(&"anchor_right", 1.0)
func _init() -> void:
name = "SplitContainerItem"
child_exiting_tree.connect(_on_child_exiting_tree)
child_entered_tree.connect(_on_child_entered_tree)
func _on_visible() -> void:
var _visible : bool = false
for x : Node in get_children():
if x is Control:
if x.visible:
_visible = true
break
visible = _visible
func _on_child_entered_tree(n : Node) -> void:
if n is Control:
n.size = size
n.set_anchor(SIDE_LEFT, 0.0)
n.set_anchor(SIDE_RIGHT, 1.0)
n.set_anchor(SIDE_TOP, 0.0)
n.set_anchor(SIDE_BOTTOM, 1.0)
if !n.visibility_changed.is_connected(_on_visible):
n.visibility_changed.connect(_on_visible)
func _disconnect(n : Node) -> void:
if n is Control:
if n.visibility_changed.is_connected(_on_visible):
n.visibility_changed.disconnect(_on_visible)
for x : Node in n.get_children():
_disconnect(x)
func _on_child_exiting_tree(n : Node) -> void:
_disconnect(n)
func _enter_tree() -> void:
var c : Node = get_parent()
if c is Control:
size = c.size

View file

@ -0,0 +1 @@
uid://rh6vq7m1qfkr

View file

@ -0,0 +1,24 @@
@tool
extends Label
func _ready() -> void:
add_to_group(&"SP_TAB_BUTTON")
func _gui_input(event: InputEvent) -> void:
if event is InputEventMouseButton:
if event.is_pressed() and event.button_index == MOUSE_BUTTON_LEFT:
var btn : Node = get_parent().get_child(0)
if btn is Button:
btn.pressed.emit()
func _get_drag_data(__ : Vector2) -> Variant:
return owner.button_main._get_drag_data(__)
func _drop_data(_at_position: Vector2, data: Variant) -> void:
owner.button_main._drop_data(_at_position, data)
func _can_drop_data(at_position: Vector2, data: Variant) -> bool:
return owner.button_main._can_drop_data(at_position, data)
func get_selected_color() -> Color:
return owner.get_selected_color()

View file

@ -0,0 +1 @@
uid://bkj4lec06udg5

View file

@ -0,0 +1,384 @@
@tool
extends PanelContainer
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
const TAB = preload("./../../../../core/ui/multi_split_container/taby/tab.tscn")
const TIME_WAIT : float = 0.35
const MAX_COLLAPSED : int = 6
@export var container : Control = null
var _dlt : float = 0.0
var _try : int = 0
var buttons : Array[Control] = []
var hbox : Array[HBoxContainer] = []
var pins : PackedStringArray = []
var _enable_update : bool = true
var _reference : TabBar = null
var _select_color : Color = Color.CADET_BLUE:
set = set_select_color
var _updating : bool = false
var style : StyleBox = null
var _behaviour_collapsed : int = MAX_COLLAPSED:
set(e):
_behaviour_collapsed = mini(maxi(0, e), MAX_COLLAPSED)
func _enter_tree() -> void:
modulate.a = 0.0
z_index = 10
get_tree().create_tween().tween_property(self, "modulate:a", 1.0, 0.3)
_setup()
func _exit_tree() -> void:
var settings : EditorSettings = EditorInterface.get_editor_settings()
if settings.settings_changed.is_connected(_on_change):
settings.settings_changed.disconnect(_on_change)
func _on_change() -> void:
var dt : Array = [
"plugin/script_splitter/behaviour/refresh_warnings_on_saveplugin/script_splitter/editor/list/selected_color"
]
var settings : EditorSettings = EditorInterface.get_editor_settings()
var changes : PackedStringArray = settings.get_changed_settings()
for c in changes:
if c in dt:
_setup()
break
func _setup() -> void:
var settings : EditorSettings = EditorInterface.get_editor_settings()
if !settings.settings_changed.is_connected(_on_change):
settings.settings_changed.connect(_on_change)
for x : Array in [
["_select_color", "plugin/script_splitter/behaviour/refresh_warnings_on_saveplugin/script_splitter/editor/list/selected_color"]
]:
if settings.has_setting(x[1]):
set(x[0], settings.get_setting(x[1]))
else:
settings.set_setting(x[1], get(x[0]))
func _notification(what: int) -> void:
if what == NOTIFICATION_PREDELETE:
for x : Variant in hbox:
if is_instance_valid(x):
if x.get_parent() == null:
x.queue_free()
for x : Variant in buttons:
if is_instance_valid(x):
if x.get_parent() == null:
x.queue_free()
func _on_pressed(btn : Button) -> void:
if is_instance_valid(_reference):
for x : int in _reference.tab_count:
if _reference.get_tab_tooltip(x) == btn.tooltip_text:
_reference.current_tab = x
#_reference.tab_clicked.emit(x)
_reference.tab_clicked.emit(x)
func _on_gui_pressed(input : InputEvent, btn : Button) -> void:
if input.is_pressed():
if is_instance_valid(_reference):
for x : int in _reference.tab_count:
if _reference.get_tab_tooltip(x) == btn.tooltip_text:
if input is InputEventMouseButton:
if input.button_index == MOUSE_BUTTON_RIGHT:
_reference.tab_selected.emit(x)
_reference.tab_rmb_clicked.emit(x)
return
elif input.button_index == MOUSE_BUTTON_MIDDLE:
_reference.tab_close_pressed.emit(x)
return
func remove_tab(tooltip : String) -> void:
for x : Control in buttons:
if x.get_src() == tooltip:
x.queue_free()
return
func rename_tab(_tab_name : String, tooltip : String, new_tab_name : String, new_tooltip : String) -> void:
for x : Button in buttons:
if x.get_src() == tooltip:
x.set_src(new_tooltip)
x.set_text(new_tab_name)
return
func set_select_color(color : Color) -> void:
_select_color = color.lightened(0.4)
func set_ref(tab : TabBar) -> void:
_reference = tab
update()
func set_enable(e : bool) -> void:
_enable_update = e
visible = e
if e:
_updating = false
update()
return
for x : Variant in hbox:
if is_instance_valid(x):
x.queue_free()
for x : Variant in buttons:
if is_instance_valid(x):
x.queue_free()
buttons.clear()
hbox.clear()
func _on_pin(btn : Object) -> void:
if btn:
if btn.has_method(&"get_src"):
var value : Variant = btn.call(&"get_src")
if value is String:
if value.is_empty():
return
var x : int = pins.find(value)
if x > -1:
pins.remove_at(x)
else:
pins.append(value)
if pins.size() > 30:
var exist : Dictionary[String, bool] = {}
for b : Button in buttons:
exist[b.tooltip_text] = true
for y : int in range(pins.size() - 1, -1, -1):
if !exist.has(pins[y]):
pins.remove_at(y)
_on_rect_change()
update()
func update(fllbck : bool = true) -> void:
if !_enable_update:
return
if _updating:
return
_updating = true
var tab : TabBar = _reference
if !is_instance_valid(tab):
set_deferred(&"_updating", false)
return
for x : int in range(buttons.size() -1, -1, -1):
var _container : Variant = buttons[x]
if is_instance_valid(_container):
continue
buttons.remove_at(x)
while buttons.size() < tab.tab_count:
var btn : Control = TAB.instantiate()
var control : Control = btn.get_button()
var cls : Button = btn.get_button_close()
if style:
btn.set(&"theme_override_styles/panel", style)
if !control.gui_input.is_connected(_on_gui_pressed):
control.gui_input.connect(_on_gui_pressed.bind(control))
if !control.pressed.is_connected(_on_pressed):
control.pressed.connect(_on_pressed.bind(control))
if !cls.pressed.is_connected(_on_close):
cls.pressed.connect(_on_close.bind(control))
if !btn.on_pin.is_connected(_on_pin):
btn.on_pin.connect(_on_pin)
buttons.append(btn)
while buttons.size() > tab.tab_count:
var btn : Variant = buttons.pop_back()
if is_instance_valid(btn):
if btn is Node:
btn.queue_free()
else:
btn.free()
if pins.size() > 0:
var indx : int = 0
var control : Node = tab.get_parent_control()
if control:
for x : int in range(control.get_child_count()):
if x > -1 and tab.tab_count > x:
if pins.has(tab.get_tab_tooltip(x)):
if x != indx:
if x < control.get_child_count():
control.move_child(control.get_child(x), indx)
indx += 1
var alpha_pin : Color = Color.WHITE
var errors : bool = false
alpha_pin.a = 0.25
for x : int in range(tab.tab_count):
var _container : Control = buttons[x]
var btn : Button = _container.get_button()
var pin : Button = _container.get_button_pin()
_container.visible = true
btn.tooltip_text = tab.get_tab_tooltip(x)
_container.set_text(tab.get_tab_title(x))
btn.icon = tab.get_tab_icon(x)
#if fllbck and (btn.tooltip_text.is_empty() or btn.text.begins_with("@VSplitContainer") or btn.text.begins_with("@VBoxContainer")):
#if btn.text.begins_with("@VSplitContainer") or btn.text.begins_with("@VBoxContainer"):
#btn.text = "File"
#errors = true
if pin:
if pins.has(btn.tooltip_text):
_container.is_pinned = true
pin.set(&"theme_override_colors/icon_normal_color",_select_color)
elif _container.is_pinned:
_container.is_pinned = false
pin.set(&"theme_override_colors/icon_normal_color", alpha_pin)
btn.set(&"theme_override_colors/icon_normal_color", Color.GRAY)
_container.color_rect.visible = false
_container.modulate.a = 0.85
if tab.current_tab > -1 and tab.current_tab < buttons.size():
var _container : Control = buttons[tab.current_tab]
var btn : Button = _container.get_button()
btn.set(&"theme_override_colors/icon_normal_color", _select_color)
_container.modulate.a = 1.0
var c : ColorRect = _container.color_rect
c.visible = true
c.color = _select_color
if _behaviour_collapsed < MAX_COLLAPSED:
var iminor : int = tab.current_tab - _behaviour_collapsed
var isup : int = tab.current_tab + _behaviour_collapsed
for x : int in range(tab.tab_count):
if x < iminor or x > isup:
var _btn : Control = buttons[x]
_btn.visible = false
_on_rect_change()
if fllbck and errors:
Engine.get_main_loop().create_timer(3.0).timeout.connect(update.bind(false))
set_deferred(&"_updating", false)
func _on_close(btn : Button) -> void:
if is_instance_valid(_reference):
for x : int in range(0, _reference.tab_count, 1):
if _reference.get_tab_tooltip(x) == btn.tooltip_text:
_reference.tab_close_pressed.emit(x)
break
func _on_gui(event : InputEvent) -> void:
if event is InputEventMouseButton:
if event.pressed:
if event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
if _behaviour_collapsed < MAX_COLLAPSED:
_behaviour_collapsed += 1
update()
get_viewport().set_input_as_handled()
elif event.button_index == MOUSE_BUTTON_WHEEL_UP:
if _behaviour_collapsed > 0:
_behaviour_collapsed -= 1
update()
get_viewport().set_input_as_handled()
func _ready() -> void:
size_flags_horizontal = Control.SIZE_EXPAND_FILL
size_flags_vertical = Control.SIZE_FILL
item_rect_changed.connect(_on_rect_change)
if !gui_input.is_connected(_on_gui):
gui_input.connect(_on_gui)
var bd : Control = EditorInterface.get_base_control()
if bd:
style = bd.get_theme_stylebox("panel", "")
if is_instance_valid(style):
style = style.duplicate()
if style is StyleBoxFlat:
style.border_width_top = 0.0
style.border_width_left = 0.0
style.border_width_right = 0.0
style.border_width_bottom = 0.0
style.expand_margin_left = 2.0
style.content_margin_bottom = 0.0
style.content_margin_top = 0.0
style.content_margin_left = 0.0
style.content_margin_right = 0.0
func _on_rect_change() -> void:
if !_enable_update:
return
_dlt = TIME_WAIT - 0.005
_try = 0
set_physics_process(true)
func get_reference() -> TabBar:
return _reference
func _physics_process(delta: float) -> void:
_dlt += delta
if _dlt < TIME_WAIT:
return
_dlt = 0.0
var rsize : Vector2 = get_parent().get_parent().size
if rsize.x > 10.0:
for x : Node in container.get_children():
container.remove_child(x)
for x : Control in buttons:
var p : Node = x.get_parent()
if p:
p.remove_child(x)
var current : HBoxContainer = null
var index : int = 0
var min_size : float = 0.0
var btn_size : float = 0.0
for x : Control in buttons:
var bsize : float = x.get_rect().size.x
if current == null or (bsize > 0.0 and rsize.x < current.get_minimum_size().x + bsize + 12):
if hbox.size() > index:
current = hbox[index]
else:
current = HBoxContainer.new()
current.set(&"theme_override_constants/separation", 4)
hbox.append(current)
index += 1
container.add_child(current)
current.add_child(x)
btn_size = maxf(btn_size, x.size.y)
if current:
var indx : int = current.get_index() + 1
min_size = indx * (btn_size) #+ 12.5
if custom_minimum_size.y != min_size:
_try = 0
set_physics_process(true)
custom_minimum_size.y = min_size
return
_try += 1
if _try % 5 == 0:
set_physics_process(false)

View file

@ -0,0 +1 @@
uid://cnubduf2plac4

View file

@ -0,0 +1,16 @@
[gd_scene load_steps=2 format=3 uid="uid://c86tj28gq8d6t"]
[ext_resource type="Script" uid="uid://cnubduf2plac4" path="res://addons/script_splitter/core/ui/multi_split_container/taby/container.gd" id="1_fdccq"]
[node name="Container" type="PanelContainer" node_paths=PackedStringArray("container")]
z_index = 10
anchors_preset = 10
anchor_right = 1.0
grow_horizontal = 2
size_flags_horizontal = 3
script = ExtResource("1_fdccq")
container = NodePath("Container")
[node name="Container" type="VBoxContainer" parent="."]
layout_mode = 2
theme_override_constants/separation = 1

View file

@ -0,0 +1,107 @@
@tool
extends Control
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
signal on_pin(button : Object)
@export var color_rect : ColorRect
@export var button_main : Button
@export var button_close : Button
@export var button_pin : Button
@export var changes : Label
var is_pinned : bool = false
func _ready() -> void:
add_to_group(&"SP_TAB_BUTTON")
mouse_entered.connect(_on_enter)
mouse_exited.connect(_on_exit)
var c : Color = Color.WHITE
c.a = 0.25
button_close.set(&"theme_override_colors/icon_normal_color", c)
if !is_pinned:
button_pin.set(&"theme_override_colors/icon_normal_color", c)
_on_exit()
var settings : EditorSettings = EditorInterface.get_editor_settings()
if settings:
if !settings.settings_changed.is_connected(_on_change):
settings.settings_changed.connect(_on_change)
if settings.has_setting("plugin/script_splitter/editor/tabs/close_button_visible"):
button_close.visible = settings.get_setting("plugin/script_splitter/editor/tabs/close_button_visible")
else:
settings.set_setting("plugin/script_splitter/editor/tabs/close_button_visible", true)
if settings.has_setting("plugin/script_splitter/editor/tabs/pin_button_visible"):
button_pin.visible = settings.get_setting("plugin/script_splitter/editor/tabs/pin_button_visible")
else:
settings.set_setting("plugin/script_splitter/editor/tabs/pin_button_visible", true)
func _on_change() -> void:
var settings : EditorSettings = EditorInterface.get_editor_settings()
if settings:
var st : PackedStringArray = settings.get_changed_settings()
if "plugin/script_splitter/editor/tabs/close_button_visible" in st:
button_close.visible = settings.get_setting("plugin/script_splitter/editor/tabs/close_button_visible")
if "plugin/script_splitter/editor/tabs/pin_button_visible" in st:
button_pin.visible = settings.get_setting("plugin/script_splitter/editor/tabs/pin_button_visible")
func _on_enter() -> void:
add_to_group(&"__SPLITER_BUTTON_TAB__")
func _on_exit() -> void:
remove_from_group(&"__SPLITER_BUTTON_TAB__")
func get_reference() -> TabBar:
return get_parent().get_parent().get_parent().get_reference()
func get_button_pin() -> Button:
return button_pin
func _on_pin_pressed() -> void:
on_pin.emit(self)
func set_close_visible(e : bool) -> void:
button_close.visible = e
func set_src(src : String) -> void:
button_main.tooltip_text = src
func get_src() -> String:
return button_main.tooltip_text
func set_text(txt : String) -> void:
if txt.ends_with("(*)"):
button_main.text = txt.trim_suffix("(*)")
changes.modulate.a = 1.0
return
button_main.text = txt
changes.modulate.a = 0.0
func get_button() -> Button:
return button_main
func get_button_close() -> Button:
return button_close
func _get_drag_data(__ : Vector2) -> Variant:
return button_main._get_drag_data(__)
func _drop_data(_at_position: Vector2, data: Variant) -> void:
button_main._drop_data(_at_position, data)
func _can_drop_data(at_position: Vector2, data: Variant) -> bool:
return button_main._can_drop_data(at_position, data)
func get_selected_color() -> Color:
return color_rect.color

View file

@ -0,0 +1 @@
uid://c3hg84uuly33f

View file

@ -0,0 +1,60 @@
@tool
extends VSeparator
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
var _delta : float = 0.0
var _ref : Control = null
func _ready() -> void:
visible = false
z_index = RenderingServer.CANVAS_ITEM_Z_MAX - 1
z_as_relative = false
mouse_filter = Control.MOUSE_FILTER_IGNORE
set_process(_ref != null)
func update(ref : Control) -> void:
_ref = ref
_delta = 0.0
visible = _ref != null
set_process(visible)
func delete() -> void:
_delta = 10.0
_ref = null
queue_free()
func _process(delta: float) -> void:
_delta += delta
if _delta < 0.5:
return
if is_instance_valid(_ref) and is_inside_tree():
if _ref.get_global_rect().has_point(get_global_mouse_position()):
return
if !is_queued_for_deletion():
queue_free()
func _get_drag_data(__ : Vector2) -> Variant:
if !_ref:
return null
return _ref.owner.button_main._get_drag_data(__)
func _drop_data(_at_position: Vector2, data: Variant) -> void:
if _ref:
_ref.owner.button_main._drop_data(_at_position, data)
func _can_drop_data(at_position: Vector2, data: Variant) -> bool:
if !_ref:
return false
return _ref.owner.button_main._can_drop_data(at_position, data)
func get_selected_color() -> Color:
if !_ref:
return Color.GRAY
return _ref.owner.get_selected_color()

View file

@ -0,0 +1 @@
uid://d05p8nj7f18s0

View file

@ -0,0 +1,19 @@
[gd_scene load_steps=3 format=3 uid="uid://dwe7jhlq2p40h"]
[ext_resource type="Script" uid="uid://d05p8nj7f18s0" path="res://addons/script_splitter/core/ui/multi_split_container/taby/separator.gd" id="1_byjdv"]
[sub_resource type="StyleBoxLine" id="StyleBoxLine_ksadu"]
color = Color(0.62352943, 0.77176476, 0.7764706, 1)
grow_begin = 4.0
grow_end = 4.0
thickness = 24
[node name="HSeparator" type="VSeparator"]
visible = false
z_index = 4095
z_as_relative = false
offset_right = 4.0
offset_bottom = 4.0
mouse_filter = 2
theme_override_styles/separator = SubResource("StyleBoxLine_ksadu")
script = ExtResource("1_byjdv")

View file

@ -0,0 +1,226 @@
@tool
extends Button
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
const SEPARATOR = preload("./../../../../core/ui/multi_split_container/taby/separator.tscn")
const DRAG_TIME : float = 0.15
static var line : VSeparator = null
var _delta : float = 0.0
var _last_control : Control = null
var is_drag : bool = false:
set(e):
is_drag = e
if is_drag:
on_drag()
Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
else:
out_drag()
if Input.mouse_mode != Input.MOUSE_MODE_VISIBLE:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
var _fms : float = 0.0
func _ready() -> void:
auto_translate_mode = Node.AUTO_TRANSLATE_MODE_DISABLED
set_process(false)
add_to_group(&"SP_TAB_BUTTON")
setup()
func on_drag() -> void:
var tab : TabBar = owner.get_reference()
if tab:
for x : Node in tab.get_tree().get_nodes_in_group(&"ScriptSplitter"):
if x.has_method(&"dragged"):
x.call(&"dragged", tab, true)
func out_drag() -> void:
var tab : TabBar = owner.get_reference()
if tab:
for x : Node in tab.get_tree().get_nodes_in_group(&"ScriptSplitter"):
if x.has_method(&"dragged"):
x.call(&"dragged", tab, false)
func _get_drag_data(__ : Vector2) -> Variant:
if !button_pressed:
pressed.emit()
var c : Control = duplicate(0)
c.mouse_filter = Control.MOUSE_FILTER_IGNORE
c.z_index = RenderingServer.CANVAS_ITEM_Z_MAX - 2
set_drag_preview(c)
return self
func _drop_data(_at_position: Vector2, data: Variant) -> void:
if is_instance_valid(line):
line.delete()
if data is Node:
if data == self:
return
elif data.is_in_group(&"SP_TAB_BUTTON"):
if is_instance_valid(line):
line.update(self)
var node : Node = owner
if node:
var idx : int = node.get_index()
if idx >= 0:
var _node : Node = data.owner
var lft : bool = false
if owner.get_global_mouse_position().x <= owner.get_global_rect().get_center().x:
lft = true
var root : Node = _node
for __ : int in range(3):
root = root.get_parent()
if !is_instance_valid(root):
out_drag()
return
for x : Node in get_tree().get_nodes_in_group(&"__SCRIPT_SPLITTER__"):
if x.has_method(&"get_builder"):
var o : Object = x.call(&"get_builder")
if o.has_method(&"swap_by_src"):
o.call(&"swap_by_src", data.tooltip_text, tooltip_text, lft)
break
if root:
if root.has_method(&"update"):
root.call(&"update")
out_drag()
func _can_drop_data(_at_position: Vector2, data: Variant) -> bool:
if data is Node:
if data == self:
return false
elif data.is_in_group(&"SP_TAB_BUTTON"):
_last_control = data
_delta = 0.0
if !is_instance_valid(line):
line = SEPARATOR.instantiate()
var root : Node = Engine.get_main_loop().root
if root:
root.add_child(line)
if line:
var rct : Rect2 = owner.get_global_rect()
line.update(self)
if owner.get_global_mouse_position().x <= owner.get_global_rect().get_center().x:
line.global_position = rct.position
else:
line.global_position = Vector2(rct.end.x, rct.position.y) - Vector2(line.size.x * 1.5, 0.0)
var style : StyleBoxLine = line.get(&"theme_override_styles/separator")
style.set(&"thickness",size.y)
style.set(&"color",data.get_selected_color())
return true
return false
func reset() -> void:
if is_drag:
set_process(false)
is_drag = false
if is_inside_tree():
var parent : Node = self
for __ : int in range(10):
parent = parent.get_parent()
if parent.has_signal(&"out_dragging"):
break
if !is_instance_valid(parent):
return
if parent.has_signal(&"out_dragging"):
for x : Node in parent.get_children():
if x is TabContainer:
parent.emit_signal(&"out_dragging",x.get_tab_bar())
return
func _enter_tree() -> void:
if !is_in_group(&"__SPLITER_TAB__"):
add_to_group(&"__SPLITER_TAB__")
if is_node_ready():
return
owner.modulate.a = 0.0
get_tree().create_tween().tween_property(owner, "modulate:a", 1.0, 0.5)
func _exit_tree() -> void:
if is_in_group(&"__SPLITER_TAB__"):
remove_from_group(&"__SPLITER_TAB__")
func _process(delta: float) -> void:
_fms += delta
if _fms > DRAG_TIME:
if is_drag:
if !Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
set_process(false)
is_drag = false
var parent : Node = self
for __ : int in range(10):
parent = parent.get_parent()
if parent.has_signal(&"out_dragging"):
break
if !is_instance_valid(parent):
return
if parent.has_signal(&"out_dragging"):
for x : Node in parent.get_children():
if x is TabContainer:
parent.emit_signal(&"out_dragging",x.get_tab_bar())
return
else:
if !Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
set_process(false)
return
is_drag = true
var parent : Node = self
for __ : int in range(10):
parent = parent.get_parent()
if parent.has_signal(&"on_dragging"):
break
if !is_instance_valid(parent):
return
if parent.has_signal(&"on_dragging"):
for x : Node in parent.get_children():
if x is TabContainer:
parent.emit_signal(&"on_dragging",x.get_tab_bar())
return
func setup() -> void:
if !gui_input.is_connected(_on_input):
gui_input.connect(_on_input)
if !is_in_group(&"__SPLITER_TAB__"):
add_to_group(&"__SPLITER_TAB__")
func _on_input(e : InputEvent) -> void:
if e is InputEventMouseButton:
if e.button_index == MOUSE_BUTTON_LEFT:
is_drag = false
if e.pressed:
_fms = 0.0
set_process(true)
else:
set_process(false)
if _fms >= DRAG_TIME:
var parent : Node = self
for __ : int in range(10):
parent = parent.get_parent()
if parent.has_signal(&"out_dragging"):
break
if !is_instance_valid(parent):
return
if parent.has_signal(&"out_dragging"):
for x : Node in parent.get_children():
if x is TabContainer:
parent.emit_signal(&"out_dragging",x.get_tab_bar())
return
#elif e.button_index == MOUSE_BUTTON_RIGHT:
#pressed.emit()
func get_selected_color() -> Color:
return owner.get_selected_color()

View file

@ -0,0 +1 @@
uid://cf447gbwtnwsl

View file

@ -0,0 +1,81 @@
[gd_scene load_steps=8 format=3 uid="uid://bei2ybryufpnd"]
[ext_resource type="Script" uid="uid://c3hg84uuly33f" path="res://addons/script_splitter/core/ui/multi_split_container/taby/container_button.gd" id="1_c6ml5"]
[ext_resource type="Script" uid="uid://cf447gbwtnwsl" path="res://addons/script_splitter/core/ui/multi_split_container/taby/tab.gd" id="2_gel5x"]
[ext_resource type="Script" uid="uid://bkj4lec06udg5" path="res://addons/script_splitter/core/ui/multi_split_container/taby/changes.gd" id="3_f5jk5"]
[ext_resource type="Texture2D" uid="uid://7rel5pr2g7d2" path="res://addons/script_splitter/assets/pin.svg" id="3_y1lda"]
[ext_resource type="Texture2D" uid="uid://4juherhkw8hp" path="res://addons/script_splitter/assets/Close.svg" id="4_b84sh"]
[ext_resource type="Script" uid="uid://bi84jgnpiilbi" path="res://addons/script_splitter/core/ui/multi_split_container/taby/tab_dd.gd" id="5_4jcjw"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hvct0"]
[node name="PanelContainer" type="PanelContainer" node_paths=PackedStringArray("color_rect", "button_main", "button_close", "button_pin", "changes")]
offset_right = 49.0
offset_bottom = 33.0
theme_override_styles/panel = SubResource("StyleBoxFlat_hvct0")
script = ExtResource("1_c6ml5")
color_rect = NodePath("VBoxContainer/Selected")
button_main = NodePath("VBoxContainer/ButtonContainer/PanelContainer")
button_close = NodePath("VBoxContainer/ButtonContainer/Close")
button_pin = NodePath("VBoxContainer/ButtonContainer/Pin")
changes = NodePath("VBoxContainer/ButtonContainer/Changes")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 2
theme_override_constants/separation = 0
[node name="Selected" type="ColorRect" parent="VBoxContainer"]
custom_minimum_size = Vector2(0, 2)
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 0
mouse_filter = 1
color = Color(0.4904182, 0.5339965, 0.61462164, 1)
[node name="ButtonContainer" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
size_flags_horizontal = 0
theme_override_constants/separation = 0
[node name="PanelContainer" type="Button" parent="VBoxContainer/ButtonContainer"]
auto_translate_mode = 2
layout_mode = 2
focus_mode = 0
theme_override_colors/font_focus_color = Color(1, 1, 1, 1)
theme_override_colors/font_pressed_color = Color(1, 1, 1, 1)
theme_override_colors/icon_normal_color = Color(1, 1, 1, 1)
button_mask = 3
shortcut_feedback = false
shortcut_in_tooltip = false
flat = true
script = ExtResource("2_gel5x")
[node name="Changes" type="Label" parent="VBoxContainer/ButtonContainer"]
layout_mode = 2
mouse_filter = 1
text = "*"
script = ExtResource("3_f5jk5")
[node name="Pin" type="Button" parent="VBoxContainer/ButtonContainer"]
layout_mode = 2
focus_mode = 0
theme_override_colors/icon_normal_color = Color(1, 1, 1, 0.25)
shortcut_feedback = false
shortcut_in_tooltip = false
icon = ExtResource("3_y1lda")
flat = true
icon_alignment = 1
script = ExtResource("5_4jcjw")
[node name="Close" type="Button" parent="VBoxContainer/ButtonContainer"]
layout_mode = 2
focus_mode = 0
theme_override_colors/font_color = Color(1, 1, 1, 0.25)
theme_override_colors/icon_normal_color = Color(1, 1, 1, 0.25)
shortcut_feedback = false
shortcut_in_tooltip = false
icon = ExtResource("4_b84sh")
flat = true
script = ExtResource("5_4jcjw")
[connection signal="pressed" from="VBoxContainer/ButtonContainer/Pin" to="." method="_on_pin_pressed"]

View file

@ -0,0 +1,17 @@
@tool
extends Control
func _ready() -> void:
add_to_group(&"SP_TAB_BUTTON")
func _get_drag_data(__ : Vector2) -> Variant:
return owner.button_main._get_drag_data(__)
func _drop_data(_at_position: Vector2, data: Variant) -> void:
owner.button_main._drop_data(_at_position, data)
func _can_drop_data(at_position: Vector2, data: Variant) -> bool:
return owner.button_main._can_drop_data(at_position, data)
func get_selected_color() -> Color:
return owner.get_selected_color()

View file

@ -0,0 +1 @@
uid://bi84jgnpiilbi

View file

@ -0,0 +1,41 @@
@tool
extends CodeEdit
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4f
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
const UPDATE_TIME : float = 0.25
var _dlt : float = 0.0
var _text : String = ""
var _dlt_update : float = UPDATE_TIME
func set_text_reference(txt : String) -> void:
if _text == txt:
return
_text = txt
_dlt = 0.0
_dlt_update = UPDATE_TIME + (txt.length() * 0.00001)
set_process(true)
func _init() -> void:
if is_node_ready():
_ready()
func _ready() -> void:
set_process(false)
func _process(delta: float) -> void:
_dlt += delta
if _dlt > _dlt_update:
set_process(false)
var sv : float = scroll_vertical
var sh : int = scroll_horizontal
text = _text
scroll_vertical = sv
scroll_horizontal = sh

View file

@ -0,0 +1 @@
uid://bec3qea1dh8xe

View file

@ -0,0 +1,110 @@
@tool
extends TabContainer
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
const Dottab = preload("./../../../core/ui/splitter/taby/dottab.gd")
const CLOSE = preload("./../../../assets/Close.svg")
const GLOBALS : PackedStringArray = ["_GlobalScope", "_GDScript"]
#
signal focus(o : TabContainer, index : int)
signal remove(o : TabContainer, index : int)
var _new_tab_settings : bool = false
var _tab_queue : int = -1
var _last_selected : int = -1
func _enter_tree() -> void:
add_to_group(&"__SC_SPLITTER__")
func _exit_tree() -> void:
if is_in_group(&"__SC_SPLITTER__"):
remove_from_group(&"__SC_SPLITTER__")
func _ready() -> void:
size_flags_horizontal = Control.SIZE_EXPAND_FILL
size_flags_vertical = Control.SIZE_EXPAND_FILL
auto_translate_mode = Node.AUTO_TRANSLATE_MODE_DISABLED
var tb : TabBar = get_tab_bar()
if tb:
tb.auto_translate_mode = auto_translate_mode
drag_to_rearrange_enabled = true
#CONNECT
var tab : TabBar = get_tab_bar()
tab.set_script(Dottab)
tab.tab_selected.connect(_on_selected)
tab.active_tab_rearranged.connect(_on_rearranged)
tab.tab_close_display_policy = TabBar.CLOSE_BUTTON_SHOW_ACTIVE_ONLY
tab.tab_close_pressed.connect(_on_remove)
tab.select_with_rmb = true
tab.on_start_drag.connect(on_drag)
tab.on_stop_drag.connect(out_drag)
func _on_rearranged(t : int) -> void:
if _last_selected == t or t < 0:
return
for x : int in [_last_selected, t]:
if x < 0 or x >= get_tab_count():
return
var sc : SceneTree = Engine.get_main_loop()
if sc:
for x : Node in sc.get_nodes_in_group(&"__SCRIPT_SPLITTER__"):
x.call(&"move_item_container", self, _last_selected, t)
func _set_tab() -> void:
if current_tab != _tab_queue and _tab_queue > -1 and _tab_queue < get_tab_count():
current_tab = _tab_queue
_new_tab_settings = false
func set_tab(index : int) -> void:
if index > -1 and index < get_tab_count():
_tab_queue = index
if _new_tab_settings:
return
_new_tab_settings = true
_set_tab.call_deferred()
func on_drag(tab : TabBar) -> void:
for x : Node in tab.get_tree().get_nodes_in_group(&"ScriptSplitter"):
if x.has_method(&"dragged"):
x.call(&"dragged", tab, true)
func out_drag(tab : TabBar) -> void:
for x : Node in tab.get_tree().get_nodes_in_group(&"ScriptSplitter"):
if x.has_method(&"dragged"):
x.call(&"dragged", tab, false)
func _on_remove(index : int) -> void:
remove.emit(self, index)
func _on_selected(value : int) -> void:
_last_selected = value
focus.emit(self, value)
func get_root() -> Node:
return self
func set_item_tooltip(idx : int, txt : String) -> void:
if idx > -1 and get_tab_count() > idx:
set_tab_tooltip(idx, txt)
func set_item_text(idx : int, txt : String) -> void:
if idx > -1 and get_tab_count() > idx:
set_tab_title(idx, txt)

View file

@ -0,0 +1 @@
uid://b45owls32pkxk

View file

@ -0,0 +1,122 @@
@tool
extends ScrollContainer
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
const PIN = preload("./../../../../assets/pin.svg")
const FILL_EXPAND = preload("./../../../../assets/fill_expand.svg")
const SPLIT_CPLUS_TOOL = preload("./../../../../assets/split_cplus_tool.svg")
const SPLIT_MINUS_TOOL = preload("./../../../../assets/split_minus_tool.svg")
const SPLIT_PLUS_TOOL = preload("./../../../../assets/split_plus_tool.svg")
const SPLIT_RMINUS_TOOL = preload("./../../../../assets/split_rminus_tool.svg")
const SPLIT_RPLUS_TOOL = preload("./../../../../assets/split_rplus_tool.svg")
const SPLIT_CMINUS_TOOL = preload("./../../../../assets/split_cminus_tool.svg")
const ATOP = preload("./../../../../assets/atop.png")
const PAD : float = 12.0
#CFG
var enable_expand : bool = true
var enable_horizontal_split : bool = true
var enable_vertical_split : bool = true
var enable_pop_script : bool = true
var enable_sub_split : bool = true
var _root : VBoxContainer = null
var _min_size : float = 0.0
@warning_ignore("unused_private_class_variable")
var _pin_root : Control = null
func _ready() -> void:
if _root == null:
_root = VBoxContainer.new()
_root.alignment = BoxContainer.ALIGNMENT_BEGIN
_root.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_root.size_flags_vertical = Control.SIZE_EXPAND_FILL
add_child(_root)
clear()
_setup()
custom_minimum_size.x = _min_size + PAD
horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
vertical_scroll_mode = ScrollContainer.SCROLL_MODE_SHOW_NEVER
func get_root() -> Node:
return _root
func _enter_tree() -> void:
add_to_group(&"__script_splitter__IO__")
func _exit_tree() -> void:
remove_from_group(&"__script_splitter__IO__")
# Traduction?
func _tr(st : String) -> String:
# ...
return st.capitalize()
func clear() -> void:
if _root:
if is_inside_tree():
for x : Node in _root.get_children():
x.queue_free()
else:
for x : Node in _root.get_children():
x.free()
func setup() -> void:
clear()
_setup()
func _setup() -> void:
if !_root:
return
if enable_expand:
make_function(&"EXPAND", FILL_EXPAND, _tr("Expand/Unexpand current tab container"))
if enable_horizontal_split:
make_function(&"SPLIT_COLUMN", SPLIT_CPLUS_TOOL, _tr("Split to new column"))
make_function(&"MERGE_COLUMN", SPLIT_CMINUS_TOOL, _tr("Merge current column"))
if enable_vertical_split:
make_function(&"SPLIT_ROW", SPLIT_RPLUS_TOOL, _tr("Split to new row"))
make_function(&"MERGE_ROW", SPLIT_RMINUS_TOOL, _tr("Merge current row"))
if enable_sub_split:
make_function(&"SPLIT_SUB", SPLIT_PLUS_TOOL, _tr("Sub Split current editor"))
make_function(&"MERGE_SPLIT_SUB", SPLIT_MINUS_TOOL, _tr("Merge sub split of current editor"))
if enable_pop_script:
make_function(&"MAKE_FLOATING", ATOP, _tr("Make separate window"))
func enable(id : StringName, e : bool) -> void:
for x : Node in _root.get_children():
if x.name == id:
x.set(&"disabled", !e)
func get_button(id : String) -> Button:
if _root.has_node(id):
var node : Node = _root.get_node(id)
if node is Button:
return node
return null
func make_function(id : StringName, icon : Texture2D = null, txt : String = "") -> void:
var btn : Button = Button.new()
btn.name = id
btn.pressed.connect(_call.bind(id))
btn.icon = icon
btn.alignment = HORIZONTAL_ALIGNMENT_CENTER
btn.tooltip_text = txt
btn.flat = is_instance_valid(icon)
_min_size = maxf(icon.get_size().x, _min_size)
_root.add_child(btn)
func _call(id : StringName) -> void:
for x : Node in get_tree().get_nodes_in_group(&"__SCRIPT_SPLITTER__"):
if x.has_method(&"_io_call"):
x.call(&"_io_call", id)

View file

@ -0,0 +1 @@
uid://b817wwia7hrqd

View file

@ -0,0 +1,307 @@
@tool
extends MarginContainer
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
const SplitterRoot = preload("./../../../core/ui/splitter/splitter_root.gd")
const HandlerContainer = preload("./../../../core/base/container.gd")
const BaseContainerItem = preload("splitter_item.gd")
const SplitterEditorContainer = preload("./../../../core/ui/splitter/splitter_editor_container.gd")
const Overlay = preload("./../../../core/ui/splitter/taby/overlay.gd")
const CODE_NAME_TWISTER = preload("./../../../assets/github_CodeNameTwister.svg")
var _handler_container : HandlerContainer = null
var _base_container : TabContainer = null
var _root : Container = null
var _root_container : SplitterRoot = null
var _last_editor_container : SplitterEditorContainer.Editor = null
var _overlay : Overlay = null
var swap_by_button : bool = true
func get_root() -> SplitterRoot:
return _root_container
func get_base_editors() -> Array[Node]:
if is_instance_valid(_base_container):
return _base_container.get_children()
return []
func initialize(container : TabContainer, handler_container : HandlerContainer) -> void:
_setup()
_handler_container = handler_container
_base_container = container
_root = self
var credits : TextureRect = TextureRect.new()
add_child(credits)
credits.texture = CODE_NAME_TWISTER
credits.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
credits.size_flags_vertical = Control.SIZE_SHRINK_CENTER
credits.expand_mode = TextureRect.EXPAND_KEEP_SIZE
credits.stretch_mode = TextureRect.STRETCH_KEEP_CENTERED
credits.modulate.a = 0.25
if is_instance_valid(_base_container):
var root : Node = _base_container.get_parent()
root.add_child(_root)
root.move_child(_root, mini(_base_container.get_index(), 0))
#_root.add_child(_root_container)
_root.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_root.size_flags_vertical = Control.SIZE_EXPAND_FILL
var vspl : HBoxContainer = HBoxContainer.new()
_root.add_child(vspl)
vspl.size_flags_horizontal = Control.SIZE_EXPAND_FILL
vspl.size_flags_vertical = Control.SIZE_EXPAND_FILL
var base : SplitterRoot = create_base_container(vspl, false)
base.max_columns = 1
_root_container = base
var io : Node = _handler_container.get_io_bar()
if io.get_parent() == null:
vspl.add_child(io)
else:
io = HandlerContainer.IoBar.new()
io.enable_vertical_split = false
vspl.add_child(io)
initialize_editor_contianer()
_overlay = Overlay.new()
add_child(_overlay)
func _on_change() -> void:
var dt : Array = ["plugin/script_splitter/editor/behaviour/swap_by_double_click_separator_button"]
var settings : EditorSettings = EditorInterface.get_editor_settings()
var changes : PackedStringArray = settings.get_changed_settings()
for c in changes:
if c in dt:
_setup()
break
func _setup() -> void:
var settings : EditorSettings = EditorInterface.get_editor_settings()
if !settings.settings_changed.is_connected(_on_change):
settings.settings_changed.connect(_on_change)
for x : Array in [
["swap_by_button", "plugin/script_splitter/editor/behaviour/swap_by_double_click_separator_button"]
]:
if settings.has_setting(x[1]):
set(x[0], settings.get_setting(x[1]))
else:
settings.set_setting(x[1], get(x[0]))
func initialize_editor_contianer() -> void:
if _root_container.get_child_count() > 0:
for x : Node in _root_container.get_children():
x.queue_free()
_last_editor_container = create_new_editor_container(_root_container, true)
func swap(value : Variant) -> void:
if !swap_by_button:
return
if !is_instance_valid(value):
return
elif !is_instance_valid(_root_container) or _root_container.get_child_count() == 0:
return
elif !value is SplitterRoot.LineSep:
return
var caller : SplitterRoot.LineSep = value
var _main : SplitterRoot = caller.get_parent()
if !is_instance_valid(_main):
return
var separators : Array = _main.get_separators()
if separators.size() == 0:
return
var index : int = 0
var linesep : Object = null
for x : Object in separators:
if x == caller:
linesep =x
break
index += 1
if linesep:
if linesep.is_vertical:
var atotal : int = 1
var btotal : int = 1
var nodes : Array[Node] = []
for x : int in range(index + 1, separators.size(), 1):
var clinesep : Object = separators[x]
if clinesep.is_vertical:
break
atotal += 1
for x : int in range(index - 1, -1, -1):
var clinesep : Object = separators[x]
if clinesep.is_vertical:
break
btotal += 1
var cindex : int = index
while atotal > 0:
cindex += 1
atotal -= 1
if cindex < _main.get_child_count():
nodes.append(_main.get_child(cindex))
continue
break
for x : Node in nodes:
cindex = btotal
while cindex > 0:
cindex -= 1
var idx : int = x.get_index() - 1
if _main.get_child_count() > idx:
_main.move_child(x, idx)
else:
index += 1
if _main.get_child_count() > index:
var child : Node = _main.get_child(index - 1)
_main.move_child(child, index)
func _enter_tree() -> void:
add_to_group(&"ScriptSplitter")
func _exit_tree() -> void:
remove_from_group(&"ScriptSplitter")
var settings : EditorSettings = EditorInterface.get_editor_settings()
if settings.settings_changed.is_connected(_on_change):
settings.settings_changed.disconnect(_on_change)
func dragged(tab : TabBar, is_drag : bool) -> void:
if is_instance_valid(_overlay):
if is_drag:
_overlay.start(tab)
else:
if _overlay.stop(tab):
var container : Node = _overlay.get_container()
var from : Container = tab.get_parent()
if is_instance_valid(container) and is_instance_valid(from):
if from != container:
_handler_container.swap_tab.emit(from, tab.current_tab, container)
else:
var type : StringName = _overlay.get_type_split()
if !type.is_empty():
_handler_container.same_swap_tab.emit(from, tab.current_tab, type)
func create_new_column() -> SplitterEditorContainer.Editor:
var item : BaseContainerItem = get_base_container_item(_last_editor_container)
var root : Container = get_base_container(_last_editor_container)
var index : int = item.get_index()
var custom_position : bool = index >= 0 and index < item.get_parent().get_child_count() - 1
_last_editor_container = create_editor_container(create_base_container_item(root))
if custom_position:
root.move_child(get_base_container_item(_last_editor_container), index + 1)
return _last_editor_container
func create_new_row() -> SplitterEditorContainer.Editor:
var root : Container = get_base_container(_last_editor_container)
var index : int = root.get_index()
var custom_position : bool = index >= 0 and index < root.get_parent().get_child_count() - 1
_last_editor_container = create_new_editor_container(_root_container)# create_editor_container(create_base_container_item(create_base_container(_root_container)))
if custom_position:
_root_container.move_child(get_base_container(_last_editor_container).get_parent(), index + 1)
return _last_editor_container
func set_current_editor(container : Node) -> bool:
if container is SplitterEditorContainer.Editor:
_last_editor_container = container
return true
return false
func get_base_container(editor : SplitterEditorContainer.Editor) -> Container:
return editor.get_node("./../../../")
func get_base_container_item(editor : SplitterEditorContainer.Editor) -> BaseContainerItem:
return editor.get_node("./../../")
func create_base_container(c_root : Node, _add_to_group : bool = true) -> Container:
var b_root : Container = SplitterRoot.new()
b_root.max_columns = 0
c_root.add_child(b_root)
b_root.size_flags_horizontal = Control.SIZE_EXPAND_FILL
b_root.size_flags_vertical = Control.SIZE_EXPAND_FILL
if _add_to_group:
b_root.add_to_group(&"__SP_BR__")
return b_root
func create_base_container_item(c_root : Container) -> BaseContainerItem:
var b_item : BaseContainerItem = BaseContainerItem.new()
c_root.add_child(b_item)
b_item.size_flags_horizontal = Control.SIZE_EXPAND_FILL
b_item.size_flags_vertical = Control.SIZE_EXPAND_FILL
return b_item
func create_editor_container(c_root : BaseContainerItem) -> SplitterEditorContainer.Editor:
var b_editor : SplitterEditorContainer = SplitterEditorContainer.new()
c_root.add_child(b_editor)
b_editor.get_editor()
var editor : SplitterEditorContainer.Editor = b_editor.get_editor()
editor.focus.connect(_handler_container.on_focus)
editor.remove.connect(_handler_container.on_remove)
editor.get_tab_bar().tab_rmb_clicked.connect(_on_rmb_clicked.bind(editor))
return editor
func _on_rmb_clicked(index : int, tab : Variant) -> void:
if tab is SplitterEditorContainer.Editor:
_handler_container.rmb_click.emit(index, tab)
func create_new_editor_container(c_root : Node, _add_to_group : bool = true) -> SplitterEditorContainer.Editor:
return create_editor_container(create_base_container_item(create_base_container(c_root, _add_to_group)))
func get_current_editor() -> SplitterEditorContainer.Editor:
return _last_editor_container
func reset() -> void:
_root.queue_free()
if is_instance_valid(_base_container):
_base_container.visible = true
var settings : EditorSettings = EditorInterface.get_editor_settings()
if settings.settings_changed.is_connected(_on_change):
settings.settings_changed.disconnect(_on_change)
func notify_creation() -> void:
if is_instance_valid(_base_container) and _base_container.visible:
_base_container.visible = false

View file

@ -0,0 +1 @@
uid://dyo7c2g4uwn0g

View file

@ -0,0 +1,82 @@
@tool
extends VBoxContainer
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
const Editor = preload("./../../../core/ui/splitter/editor_container.gd")
const CONTAINER = preload("./../../../core/ui/multi_split_container/taby/container.tscn")
var _editor : Editor = null
var _tab_old_behaviour : bool = false:
set = _on_behaviour_changed
var tab : Node = null
func _on_behaviour_changed(e) -> void:
_tab_old_behaviour = e
if is_instance_valid(tab):
tab.set_enable(!_tab_old_behaviour)
_editor.tabs_visible = _tab_old_behaviour
func _on_change() -> void:
var dt : Array = ["plugin/script_splitter/editor/tabs/use_old_behaviour"]
var settings : EditorSettings = EditorInterface.get_editor_settings()
var changes : PackedStringArray = settings.get_changed_settings()
for c in changes:
if c in dt:
_setup()
break
func _setup() -> void:
var settings : EditorSettings = EditorInterface.get_editor_settings()
if !settings.settings_changed.is_connected(_on_change):
settings.settings_changed.connect(_on_change)
for x : Array in [
["_tab_old_behaviour", "plugin/script_splitter/editor/tabs/use_old_behaviour"]
]:
if settings.has_setting(x[1]):
set(x[0], settings.get_setting(x[1]))
else:
settings.set_setting(x[1], get(x[0]))
func _ready() -> void:
_editor = Editor.new()
var iscale : int = -8
set(&"theme_override_constants/separation", iscale)
tab = CONTAINER.instantiate()
tab.set_ref(_editor.get_tab_bar())
tab.set_enable(!_tab_old_behaviour)
_editor.tabs_visible = _tab_old_behaviour
add_child(tab)
add_child(_editor)
size_flags_horizontal = Control.SIZE_EXPAND_FILL
size_flags_vertical = Control.SIZE_EXPAND_FILL
func _enter_tree() -> void:
add_to_group(&"__SP_EC__")
_setup()
func _exit_tree() -> void:
remove_from_group(&"__SP_EC__")
var settings : EditorSettings = EditorInterface.get_editor_settings()
if settings.settings_changed.is_connected(_on_change):
settings.settings_changed.disconnect(_on_change)
func get_editor() -> Editor:
return _editor
func update() -> void:
if !is_instance_valid(tab):
return
tab.update()

View file

@ -0,0 +1 @@
uid://dlqgddtcsov1m

View file

@ -0,0 +1,67 @@
@tool
extends "./../../../core/ui/multi_split_container/split_container_item.gd"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
var _fms : float = 0.0
func _ready() -> void:
super()
modulate = Color.DARK_GRAY
set_physics_process(modulate != Color.WHITE)
func _physics_process(delta: float) -> void:
_fms += delta * 0.24
if _fms >= 1.0:
_fms = 1.0
set_physics_process(false)
modulate = lerp(modulate, Color.WHITE, _fms)
func _enter_tree() -> void:
super()
add_to_group(&"__SP_IC__")
var parent : Node = get_parent()
if parent.has_method(&"expand_splited_container"):
_on_child(self)
func _exit_tree() -> void:
add_to_group(&"__SP_IC__")
func _on_child(n : Node) -> void:
if n is Control:
var parent : Node = get_parent()
if !n.child_entered_tree.is_connected(_on_child):
n.child_entered_tree.connect(_on_child)
if !n.child_exiting_tree.is_connected(_out_child):
n.child_exiting_tree.connect(_out_child)
if n.focus_mode != Control.FOCUS_NONE:
if !n.focus_entered.is_connected(parent.expand_splited_container):
n.focus_entered.connect(parent.expand_splited_container.bind(self))
for x : Node in n.get_children():
_on_child(x)
func _out_child(n : Node) -> void:
if n is Control:
var parent : Node = get_parent()
if n.child_entered_tree.is_connected(_on_child):
n.child_entered_tree.disconnect(_on_child)
if n.child_exiting_tree.is_connected(_out_child):
n.child_exiting_tree.disconnect(_out_child)
if n.focus_mode != Control.FOCUS_NONE:
if n.focus_entered.is_connected(parent.expand_splited_container):
n.focus_entered.disconnect(parent.expand_splited_container)
for x : Node in n.get_children():
_out_child(x)

View file

@ -0,0 +1 @@
uid://b2kiv55ed0aj

View file

@ -0,0 +1,106 @@
@tool
extends ItemList
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
signal move_item_by_index(from : int, to : int)
var _ss: Callable
var _delta : int = 0
var _list : ItemList = null
var _dragged_item_index: int = -1
func _ready() -> void:
set_process(false)
set_physics_process(false)
func update() -> void:
_delta = 0
set_physics_process(true)
func set_list(item : ItemList) -> void:
_list = item
func set_reference(scall : Callable) -> void:
_ss = scall
func changes(list : ItemList) -> bool:
if list.item_count != item_count:
return true
for x : int in list.item_count:
if is_selected(x) != is_selected(x) or \
get_item_text(x) != list.get_item_text(x) or\
get_item_icon(x) != list.get_item_icon(x) or \
get_item_icon_modulate(x) != list.get_item_icon_modulate(x) or \
get_item_tooltip(x) != list.get_item_tooltip(x):
return true
return false
func _physics_process(__ : float) -> void:
_delta += 1
if _delta < 10:
return
set_physics_process(false)
if !_ss.is_valid():
return
if !changes(_list):
return
_ss.call()
func _get_drag_data(at_position: Vector2) -> Variant:
var item_index : int = get_item_at_position(at_position)
if item_index != -1:
_dragged_item_index = item_index
var drag_preview : HBoxContainer = HBoxContainer.new()
var icon : TextureRect = TextureRect.new()
var label : Label = Label.new()
drag_preview.set(&"theme_override_constants/separation", 0)
icon.texture = get_item_icon(0)
icon.modulate = get_item_icon_modulate(0)
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
label.text = get_item_text(item_index)
drag_preview.add_child(icon)
drag_preview.add_child(label)
set_drag_preview(drag_preview)
var tp : String = get_item_tooltip(item_index)
for x : Node in Engine.get_main_loop().get_nodes_in_group(&"SP_TAB_BUTTON"):
if x is Control:
if tp == x.tooltip_text and x.has_method(&"_on_input"):
var ip : InputEventMouseButton = InputEventMouseButton.new()
ip.button_index = MOUSE_BUTTON_LEFT
ip.pressed = true
x.call(&"_on_input", ip)
return item_index
return null
func _can_drop_data(at_position: Vector2, data: Variant) -> bool:
if typeof(data) == TYPE_INT and data > -1 and data == _dragged_item_index:
var drop_index : int = get_item_at_position(at_position)
return drop_index != -1 and drop_index != data
return false
func _drop_data(at_position: Vector2, data: Variant) -> void:
if typeof(data) != TYPE_INT or data < 0 or data != _dragged_item_index:
return
var from_index : int = data as int
var to_index : int = get_item_at_position(at_position)
if from_index != -1 and to_index != -1:
move_item_by_index.emit(from_index, to_index)
_dragged_item_index = -1

View file

@ -0,0 +1 @@
uid://c12gyjf2qmasp

View file

@ -0,0 +1,108 @@
@tool
extends "./../../../core/ui/multi_split_container/multi_split_container.gd"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
const EXPAND = preload("./../../../assets/expand.svg")
var __setup : bool = false
var _delta : float = 0.0
func _init() -> void:
super()
drag_button_icon = EXPAND
drag_button_size = 24.0
behaviour_expand_on_focus = true
behaviour_can_expand_focus_same_container = false
behaviour_expand_smoothed = true
drag_button_always_visible = false
drag_button_modulate = Color.WHITE
behaviour_expand_on_double_click = true
behaviour_can_move_by_line = true
_setup()
func _ready() -> void:
super()
modulate.a = 0.0
set_physics_process(true)
func _physics_process(delta : float) -> void:
_delta += delta * 2.0
if _delta >= 1.0:
_delta = 1.0
set_physics_process(false)
modulate.a = _delta
func _on_change() -> void:
var dt : Array = ["plugin/script_splitter/editor/behaviour/expand_on_focus"
,"plugin/script_splitter/editor/behaviour/can_expand_on_same_focus"
,"plugin/script_splitter/editor/behaviour/smooth_expand"
,"plugin/script_splitter/editor/behaviour/smooth_expand_time"
,"plugin/script_splitter/line/size"
,"plugin/script_splitter/line/color"
,"plugin/script_splitter/line/draggable"
,"plugin/script_splitter/line/expand_by_double_click"
,"plugin/script_splitter/line/button/size"
,"plugin/script_splitter/line/button/modulate"
,"plugin/script_splitter/line/button/always_visible"
]
var settings : EditorSettings = EditorInterface.get_editor_settings()
var changes : PackedStringArray = settings.get_changed_settings()
for c in changes:
if c in dt:
_setup()
update()
break
func _setup() -> void:
var settings : EditorSettings = EditorInterface.get_editor_settings()
if !settings.settings_changed.is_connected(_on_change):
settings.settings_changed.connect(_on_change)
for x : Array in [
["behaviour_expand_on_focus", "plugin/script_splitter/editor/behaviour/expand_on_focus"]
,["behaviour_can_expand_focus_same_container", "plugin/script_splitter/editor/behaviour/can_expand_on_same_focus"]
,["behaviour_expand_smoothed", "plugin/script_splitter/editor/behaviour/smooth_expand"]
,["drag_button_size", "plugin/script_splitter/editor/behaviour/smooth_expand_time"]
,["separator_line_size", "plugin/script_splitter/line/size"]
,["separator_line_color", "plugin/script_splitter/line/color"]
,["behaviour_can_move_by_line", "plugin/script_splitter/line/draggable"]
,["behaviour_expand_on_double_click", "plugin/script_splitter/line/expand_by_double_click"]
,["drag_button_size", "plugin/script_splitter/line/button/size"]
,["drag_button_modulate", "plugin/script_splitter/line/button/modulate"]
,["drag_button_always_visible", "plugin/script_splitter/line/button/always_visible"]
]:
if settings.has_setting(x[1]):
set(x[0], settings.get_setting(x[1]))
else:
settings.set_setting(x[1], get(x[0]))
func _enter_tree() -> void:
add_to_group(&"__ST_CS__")
super()
if __setup:
return
__setup = true
_setup()
func _exit_tree() -> void:
remove_from_group(&"__ST_CS__")
var settings : EditorSettings = EditorInterface.get_editor_settings()
if settings.settings_changed.is_connected(_on_change):
settings.settings_changed.disconnect(_on_change)

View file

@ -0,0 +1 @@
uid://cl2dnbvuovoro

View file

@ -0,0 +1,78 @@
@tool
extends TabBar
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
signal on_start_drag(t : TabBar)
signal on_stop_drag(t : TabBar)
var is_drag : bool = false:
set(e):
is_drag = e
if is_drag:
Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
else:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
var _fms : float = 0.0
func reset() -> void:
if is_drag:
set_process(false)
is_drag = false
if is_inside_tree():
on_stop_drag.emit(null)
func _init() -> void:
if is_node_ready():
_ready()
func _ready() -> void:
set_process(false)
setup()
select_with_rmb = true
func _enter_tree() -> void:
if !is_in_group(&"__SPLITER_TAB__"):
add_to_group(&"__SPLITER_TAB__")
func _exit_tree() -> void:
if is_in_group(&"__SPLITER_TAB__"):
remove_from_group(&"__SPLITER_TAB__")
func _process(delta: float) -> void:
_fms += delta
if _fms > 0.24:
if is_drag:
if !Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
set_process(false)
is_drag = false
on_stop_drag.emit(self)
else:
on_start_drag.emit(self)
is_drag = true
func setup() -> void:
if !gui_input.is_connected(_on_input):
gui_input.connect(_on_input)
if !is_in_group(&"__SPLITER_TAB__"):
add_to_group(&"__SPLITER_TAB__")
func _on_input(e : InputEvent) -> void:
if e is InputEventMouseButton:
if e.button_index == MOUSE_BUTTON_LEFT:
is_drag = false
if e.pressed:
_fms = 0.0
set_process(true)
else:
set_process(false)
if _fms >= 0.24:
on_stop_drag.emit(self)

View file

@ -0,0 +1 @@
uid://bbwrffwhmepvw

View file

@ -0,0 +1,208 @@
@tool
extends ColorRect
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
const FILE_IN = preload("./../../../../assets/file_in.png")
const SPLIT_SELECTION = preload("./../../../../core/ui/splitter/taby/split_selection/SplitSelection.tscn")
const NORMAL : float = 0.4
const FILL : float = 0.65
var _dt : float = 0.0
var _fc : float = 0.0
var _ec : float = 1.0
var _ref : TabBar = null
var _container : Control = null
var _target : Control = null
var _split_selection : Control = null
var _type_split : StringName = &""
static var _busy : bool = false
func get_type_split() -> StringName:
return _type_split
func _init() -> void:
visible = false
mouse_filter = Control.MOUSE_FILTER_IGNORE
z_as_relative = false
z_index = RenderingServer.CANVAS_ITEM_Z_MAX - 1
func start(ref : TabBar) -> void:
_fc = NORMAL
_ec = FILL
_dt = 0.0
_ref = ref
modulate.a = _fc
_target = null
if is_instance_valid(ref):
_container = ref.get_parent()
else:
_container = null
_update()
set_process(true)
func _reset() -> void:
for x : Node in Engine.get_main_loop().get_nodes_in_group(&"__SCRIPT_SPLITTER__"):
if get_parent() != x:
reparent(x)
break
static func _free() -> void:
var sc : SceneTree = Engine.get_main_loop()
if sc:
for __ : int in range(0, 5, 1):
await sc.process_frame
if !is_instance_valid(sc):
return
_busy = false
func stop(tab : TabBar = null) -> bool:
set_process(false)
var out : bool = false
if !_busy and mouse_over(_target):
set_physics_process(true)
_busy = true
_type_split = &""
_free.call_deferred()
if is_instance_valid(tab) and tab == _ref:
var container : Node = _ref.get_parent()
if is_instance_valid(_container) and _container == container:
out = get_global_rect().has_point(get_global_mouse_position())
for b : Node in _split_selection.get_buttons():
if b is Control:
if !b.visible:
continue
if b.get_global_rect().has_point(get_global_mouse_position()):
_type_split = b.name
break
visible = false
_container = null
_target = null
return out
func get_container(ignore_self : bool = true) -> Node:
for x : Node in get_tree().get_nodes_in_group(&"__SC_SPLITTER__"):
if ignore_self and x == _container:
continue
var root : Node = x.get_parent()
if root is Control:
var rect : Rect2 = root.get_global_rect()
if rect.has_point(get_global_mouse_position()):
return x
return null
func _ready() -> void:
color = Color.DARK_GREEN
set_process(false)
set_physics_process(false)
visible = false
set_anchors_preset(Control.PRESET_FULL_RECT)
var cnt : Control = SPLIT_SELECTION.instantiate()
add_child(cnt)
cnt.size_flags_horizontal = Control.SIZE_EXPAND_FILL
cnt.size_flags_vertical = Control.SIZE_EXPAND_FILL
cnt.set_anchors_preset(Control.PRESET_FULL_RECT)
_split_selection = cnt
func mouse_over(control: Control) -> bool:
if null == control or !control.is_visible_in_tree():
return false
var control_window : Window = control.get_window()
if control_window.get_window_id() != get_window().get_window_id():
return false
var mp : Vector2i = DisplayServer.mouse_get_position()
var mouse_window_id = DisplayServer.get_window_at_screen_position(mp)
if mouse_window_id != control_window.get_window_id():
return false
return control.get_global_rect().has_point(control.get_global_mouse_position())#mp)
func _update() -> void:
if is_instance_valid(_container):
var sc : SceneTree = Engine.get_main_loop()
if sc:
for x : Node in sc.get_nodes_in_group(&"__SC_SPLITTER__"):
if x is Control and mouse_over(x):
var same : bool = x == _container
if same and (!(x is TabContainer) or x.get_child_count() < 2):
continue
if !visible:
modulate.a = 0.0
_fc = NORMAL
_ec = FILL
_dt = 0.0
visible = true
size = x.size
global_position = x.global_position
for y : Control in _split_selection.get_buttons():
y.visible = same
if _split_selection.file_texture:
_split_selection.file_texture.modulate.a = float(!same)
_target = x
return
_fc = NORMAL
_ec = FILL
_dt = 0.0
modulate.a = _fc
_target = null
visible = false
func _process(delta: float) -> void:
_update()
if !visible:
return
_dt += delta * 2.0
if _dt >= 1.0:
modulate.a = _ec
if _ec == FILL:
_ec = NORMAL
_fc = FILL
else:
_ec = FILL
_fc = NORMAL
_dt = 0.0
return
modulate.a = lerpf(_fc, _ec, _dt)
func resize() -> void:
if !is_inside_tree():
await tree_entered
position = Vector2.ZERO
reset_size()
size_flags_horizontal = Control.SIZE_EXPAND_FILL
size_flags_vertical = Control.SIZE_EXPAND_FILL
#set_anchors_preset(Control.PRESET_FULL_RECT)

View file

@ -0,0 +1 @@
uid://lo116b5bry0t

View file

@ -0,0 +1,106 @@
[gd_scene load_steps=10 format=3 uid="uid://8468n431hc6u"]
[ext_resource type="Texture2D" uid="uid://cffqinddvan8k" path="res://addons/script_splitter/assets/sep_up.svg" id="1_jygbp"]
[ext_resource type="Script" uid="uid://bcf6213bhffkm" path="res://addons/script_splitter/core/ui/splitter/taby/split_selection/split_selection.gd" id="1_tifrm"]
[ext_resource type="Script" uid="uid://b2jjpfip8pcsf" path="res://addons/script_splitter/core/ui/splitter/taby/split_selection/btn.gd" id="2_tifrm"]
[ext_resource type="Texture2D" uid="uid://wp32fwv4flno" path="res://addons/script_splitter/assets/sep_left.svg" id="2_ttdhj"]
[ext_resource type="Texture2D" uid="uid://cxds5tr6aq5v3" path="res://addons/script_splitter/assets/file_in.png" id="2_w45p2"]
[ext_resource type="Texture2D" uid="uid://d1ee2fk3y8n83" path="res://addons/script_splitter/assets/sep_right.svg" id="3_tifrm"]
[ext_resource type="Texture2D" uid="uid://dt830c42xmul5" path="res://addons/script_splitter/assets/sep_bottom.svg" id="4_jevbt"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_w45p2"]
bg_color = Color(0.09320182, 0.57214785, 0.68215865, 0.5)
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 8
border_color = Color(0.047066845, 0.37056562, 0.44495192, 0.5019608)
border_blend = true
corner_radius_top_left = 16
corner_radius_top_right = 16
corner_radius_bottom_right = 16
corner_radius_bottom_left = 16
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2cbhf"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
bg_color = Color(0, 0.91349006, 0.47941613, 0.5019608)
border_color = Color(4.813075e-07, 0.57995814, 0.19868338, 0.5019608)
corner_radius_top_left = 8
corner_radius_top_right = 8
corner_radius_bottom_right = 8
corner_radius_bottom_left = 8
[node name="SplitSelection" type="AspectRatioContainer" node_paths=PackedStringArray("file_texture")]
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -108.0
offset_top = -108.0
offset_right = 108.0
offset_bottom = 108.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_tifrm")
file_texture = NodePath("VB/HB/TextureRect")
[node name="VB" type="VBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 4
size_flags_vertical = 4
theme_override_constants/separation = 12
[node name="TOP" type="Button" parent="VB"]
custom_minimum_size = Vector2(64, 64)
layout_mode = 2
focus_mode = 0
theme_override_styles/normal = SubResource("StyleBoxFlat_w45p2")
theme_override_styles/hover = SubResource("StyleBoxFlat_2cbhf")
icon = ExtResource("1_jygbp")
icon_alignment = 1
script = ExtResource("2_tifrm")
[node name="HB" type="HBoxContainer" parent="VB"]
layout_mode = 2
theme_override_constants/separation = 12
[node name="LEFT" type="Button" parent="VB/HB"]
custom_minimum_size = Vector2(64, 64)
layout_mode = 2
focus_mode = 0
theme_override_styles/normal = SubResource("StyleBoxFlat_w45p2")
theme_override_styles/hover = SubResource("StyleBoxFlat_2cbhf")
icon = ExtResource("2_ttdhj")
icon_alignment = 1
script = ExtResource("2_tifrm")
[node name="TextureRect" type="TextureRect" parent="VB/HB"]
custom_minimum_size = Vector2(64, 64)
layout_mode = 2
texture = ExtResource("2_w45p2")
expand_mode = 3
stretch_mode = 5
[node name="RIGHT" type="Button" parent="VB/HB"]
custom_minimum_size = Vector2(64, 64)
layout_mode = 2
focus_mode = 0
theme_override_styles/normal = SubResource("StyleBoxFlat_w45p2")
theme_override_styles/hover = SubResource("StyleBoxFlat_2cbhf")
icon = ExtResource("3_tifrm")
icon_alignment = 1
script = ExtResource("2_tifrm")
[node name="BOTTOM" type="Button" parent="VB"]
custom_minimum_size = Vector2(64, 64)
layout_mode = 2
focus_mode = 0
theme_override_styles/normal = SubResource("StyleBoxFlat_w45p2")
theme_override_styles/hover = SubResource("StyleBoxFlat_2cbhf")
icon = ExtResource("4_jevbt")
icon_alignment = 1
script = ExtResource("2_tifrm")

View file

@ -0,0 +1,12 @@
@tool
extends Button
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
func _ready() -> void:
owner.add_button(self)

View file

@ -0,0 +1 @@
uid://b2jjpfip8pcsf

View file

@ -0,0 +1,13 @@
[gd_resource type="StyleBoxFlat" format=3 uid="uid://cv7c0vox6s5ug"]
[resource]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
bg_color = Color(0, 0, 0, 0.6)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
corner_detail = 5

View file

@ -0,0 +1,17 @@
@tool
extends Control
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
@export var file_texture : TextureRect = null
var _btns : Array[Button] = []
func get_buttons() -> Array[Button]:
return _btns
func add_button(b : Button) -> void:
_btns.append(b)

View file

@ -0,0 +1 @@
uid://bcf6213bhffkm

View file

@ -0,0 +1,13 @@
@tool
extends Button
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
func _pressed() -> void:
if owner and owner.has_method(name):
owner.call(name)

View file

@ -0,0 +1 @@
uid://d64ag8slx57b

View file

@ -0,0 +1,108 @@
@tool
extends Panel
# from: https://github.com/cherriesandmochi/gdmaim
@export var code_edit : CodeEdit
var _search_results : Array[Vector2i]
var _cur_search_result : int = 0
func is_search_focused() -> bool:
return $VBoxContainer/Search.has_focus()
func open() -> void:
var node : Node = (get_viewport().gui_get_focus_owner())
if node is CodeEdit:
code_edit = node
if code_edit == null:
return
if code_edit.get_selected_text():
$VBoxContainer/Search.text = code_edit.get_selected_text()
_update_search()
show()
$VBoxContainer/Search.grab_focus()
func close() -> void:
hide()
if code_edit:
code_edit.grab_focus()
code_edit.set_search_text("")
code_edit.queue_redraw()
func get_search_text() -> String:
return $VBoxContainer/Search.text
func update_search() -> void:
var txt : String = get_search_text()
if txt.length() == 0:
return
_update_search(txt)
func _update_search(_new_text : String = "") -> void:
var node : Node = (get_viewport().gui_get_focus_owner())
if node is CodeEdit:
code_edit = node
if code_edit == null:
close()
return
var updated_text : String = $VBoxContainer/Search.text
var flags : int = (
TextEdit.SEARCH_MATCH_CASE * int($VBoxContainer/MatchCase.button_pressed) +
TextEdit.SEARCH_WHOLE_WORDS * int($VBoxContainer/WholeWords.button_pressed))
code_edit.set_search_text(updated_text)
code_edit.set_search_flags(flags)
code_edit.queue_redraw()
_search_results.clear()
_cur_search_result = 0
var result : Vector2i = code_edit.search(updated_text, flags, 0, 0)
while result.x != -1:
_search_results.append(result)
if result.y + 1 == code_edit.get_line_count():
break
result = code_edit.search(updated_text, flags, result.y + 1, 0)
if _search_results.has(result):
break
_update_matches()
func _update_matches() -> void:
$VBoxContainer/Matches.modulate = Color.WHITE
if !$VBoxContainer/Search.text:
$VBoxContainer/Matches.text = ""
elif !_search_results:
$VBoxContainer/Matches.text = "No match"
$VBoxContainer/Matches.modulate = Color.SALMON
else:
_cur_search_result = posmod(_cur_search_result, _search_results.size())
$VBoxContainer/Matches.text = str(_cur_search_result + 1) + " of " + str(_search_results.size()) + " matches"
code_edit.set_caret_line(_search_results[_cur_search_result].y)
code_edit.set_caret_column(_search_results[_cur_search_result].x)
func _on_search_text_submitted(_new_text : String) -> void:
_cur_search_result += 1
_update_matches()
func _on_previous_pressed() -> void:
_cur_search_result -= 1
_update_matches()
func _on_next_pressed() -> void:
_cur_search_result += 1
_update_matches()

View file

@ -0,0 +1 @@
uid://bvqs6iskv4kx2

View file

@ -0,0 +1,74 @@
[gd_scene load_steps=7 format=3 uid="uid://bomtra3fmv57j"]
[ext_resource type="Script" uid="uid://bvqs6iskv4kx2" path="res://addons/script_splitter/core/ui/window/code_search.gd" id="1_dym63"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_58b8g"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_m7op6"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_jjfj5"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_7k12u"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_dylah"]
[node name="CodeSearch" type="Panel"]
custom_minimum_size = Vector2(0, 30)
script = ExtResource("1_dym63")
[node name="VBoxContainer" type="HBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="Search" type="LineEdit" parent="VBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="Matches" type="Label" parent="VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 13
[node name="Previous" type="Button" parent="VBoxContainer"]
layout_mode = 2
theme_override_styles/focus = SubResource("StyleBoxEmpty_58b8g")
text = "<"
flat = true
[node name="Next" type="Button" parent="VBoxContainer"]
layout_mode = 2
theme_override_styles/focus = SubResource("StyleBoxEmpty_m7op6")
text = ">"
flat = true
[node name="MatchCase" type="CheckBox" parent="VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 13
theme_override_styles/focus = SubResource("StyleBoxEmpty_jjfj5")
text = "Match Case"
flat = true
[node name="WholeWords" type="CheckBox" parent="VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 13
theme_override_styles/focus = SubResource("StyleBoxEmpty_7k12u")
text = "Whole Words"
flat = true
[node name="Close" type="Button" parent="VBoxContainer"]
layout_mode = 2
size_flags_horizontal = 8
theme_override_styles/focus = SubResource("StyleBoxEmpty_dylah")
text = "X"
flat = true
[connection signal="text_changed" from="VBoxContainer/Search" to="." method="_update_search"]
[connection signal="text_submitted" from="VBoxContainer/Search" to="." method="_on_search_text_submitted"]
[connection signal="pressed" from="VBoxContainer/Previous" to="." method="_on_previous_pressed"]
[connection signal="pressed" from="VBoxContainer/Next" to="." method="_on_next_pressed"]
[connection signal="pressed" from="VBoxContainer/MatchCase" to="." method="_update_search"]
[connection signal="pressed" from="VBoxContainer/WholeWords" to="." method="_update_search"]
[connection signal="pressed" from="VBoxContainer/Close" to="." method="close"]

View file

@ -0,0 +1,134 @@
@tool
extends Window
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Script Splitter
# https://github.com/CodeNameTwister/Script-Splitter
#
# Script Splitter addon for godot 4
# author: "Twister"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
const GRANT_KEY_CODES : PackedInt64Array = [
KEY_S, KEY_SPACE, KEY_K, KEY_G, KEY_SLASH
]
@export var _root : Node = null
@export var _search : Control = null
func get_root() -> Node:
return _root
func _ready() -> void:
_search.visible = false
set_physics_process(false)
var _size : Vector2 = Engine.get_main_loop().root.size
_size = _size * 0.75
_size.x = maxf(_size.x, 512.0)
_size.y = maxf(_size.y, 512.0)
size = _size
show()
move_to_center()
$PanelContainer/VBoxContainer/HBoxContainer/always_top.button_pressed = always_on_top
func _notification(what: int) -> void:
if what == NOTIFICATION_PREDELETE:
_on_close()
func _enter_tree() -> void:
if !close_requested.is_connected(_on_close):
close_requested.connect(_on_close)
if !focus_entered.is_connected(_on_focus):
focus_entered.connect(_on_focus)
if !focus_exited.is_connected(_out_focus):
focus_exited.connect(_out_focus)
if !tree_exiting.is_connected(_on_close):
tree_exiting.connect(_on_close)
func _out_focus() -> void:
always_on_top = $PanelContainer/VBoxContainer/HBoxContainer/always_top.button_pressed
func setup() -> void:
if _root:
var x : Node = _root.get_child(1).get_child(0)
x.child_exiting_tree.connect(update)
func _on_focus(__ : Variant = null) -> void:
always_on_top = false
_search.code_edit = null
_focus(_root)
_search.visible = _search.visible and null != _search.code_edit
func _focus(n : Node, focus : bool = false) -> void:
if n:
if focus and n is Control:
var c : Control = n
if c.focus_mode != Control.FOCUS_NONE:
c.grab_focus.call_deferred()
if c is CodeEdit:
_search.code_edit = c
if _search.visible:
_search.update_search()
for x : Node in n.get_children():
if x is TabContainer:
if !x.tab_changed.is_connected(_on_focus):
x.tab_changed.connect(_on_focus)
_focus(x.get_current_tab_control(), true)
break
_focus(x, focus)
func _on_close() -> void:
for x : Node in Engine.get_main_loop().get_nodes_in_group(&"__SCRIPT_SPLITTER__"):
x.call(&"remove_from_control", self)
if !is_queued_for_deletion():
queue_free()
func _resizez(n : Node) -> void:
if n is Control:
if n.size > _root.size:
n.set_deferred(&"size", _root.size)
for x : Node in n.get_children():
_resizez(x)
func update(__ : Variant = null) -> void:
if _root.get_child_count() == 0:
queue_free()
return
call_deferred(&"set_physics_process", true)
func _physics_process(__: float) -> void:
set_physics_process(false)
if !_root or _root.get_child_count() == 0 or _root.get_child(1).get_child(0).get_child_count() == 0:
queue_free()
return
_resizez(_root)
func _input(event: InputEvent) -> void:
if event is InputEventKey:
if event.ctrl_pressed and event.shift_pressed == false:
if event.keycode == KEY_F:
_search.open()
get_viewport().set_input_as_handled()
return
elif event.keycode in GRANT_KEY_CODES:
var vp : Viewport = (Engine.get_main_loop().root)
vp.push_input(event)
get_viewport().set_input_as_handled()
elif event.keycode == KEY_ESCAPE:
if _search.visible and (_search.has_focus() or _search.is_search_focused()):
_search.close()
get_viewport().set_input_as_handled()
func center() -> void:
move_to_center()
func always_top() -> void:
pass

View file

@ -0,0 +1 @@
uid://b18w1ib1rv47t

View file

@ -0,0 +1,63 @@
[gd_scene load_steps=8 format=3 uid="uid://c1ou1s1ynw4nq"]
[ext_resource type="Script" uid="uid://b18w1ib1rv47t" path="res://addons/script_splitter/core/ui/window/editor.gd" id="1_7ydjp"]
[ext_resource type="Script" uid="uid://dyo7c2g4uwn0g" path="res://addons/script_splitter/core/ui/splitter/splitter_container.gd" id="2_l3fsh"]
[ext_resource type="Texture2D" uid="uid://cejhhnje48450" path="res://addons/script_splitter/assets/fill_expand.svg" id="2_y3itc"]
[ext_resource type="PackedScene" uid="uid://bomtra3fmv57j" path="res://addons/script_splitter/core/ui/window/code_search.tscn" id="3_j866e"]
[ext_resource type="Texture2D" uid="uid://r6u1jtnbr4eg" path="res://addons/script_splitter/assets/atop.png" id="3_qfcg7"]
[ext_resource type="Script" uid="uid://d64ag8slx57b" path="res://addons/script_splitter/core/ui/window/button.gd" id="4_4imua"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_j866e"]
bg_color = Color(0.21176471, 0.23921569, 0.2901961, 1)
[node name="Editor" type="Window" node_paths=PackedStringArray("_root", "_search")]
auto_translate_mode = 2
oversampling_override = 1.0
title = "Script Splitter: Pop Script"
initial_position = 4
size = Vector2i(968, 558)
script = ExtResource("1_7ydjp")
_root = NodePath("PanelContainer/VBoxContainer/MarginContainer")
_search = NodePath("PanelContainer/VBoxContainer/CodeSearch")
[node name="PanelContainer" type="PanelContainer" parent="."]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_j866e")
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer"]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer"]
layout_mode = 2
[node name="center" type="Button" parent="PanelContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
text = "Center"
icon = ExtResource("2_y3itc")
flat = true
script = ExtResource("4_4imua")
[node name="always_top" type="Button" parent="PanelContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
toggle_mode = true
text = "Always Top"
icon = ExtResource("3_qfcg7")
flat = true
script = ExtResource("4_4imua")
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
theme_override_constants/margin_left = 4
theme_override_constants/margin_top = 4
theme_override_constants/margin_right = 4
theme_override_constants/margin_bottom = 4
script = ExtResource("2_l3fsh")
[node name="CodeSearch" parent="PanelContainer/VBoxContainer" instance=ExtResource("3_j866e")]
visible = false
layout_mode = 2