diff --git a/addons/flexible_layout/flexible_layout.gd b/addons/flexible_layout/flexible_layout.gd index 257ff497e..45a7a959f 100644 --- a/addons/flexible_layout/flexible_layout.gd +++ b/addons/flexible_layout/flexible_layout.gd @@ -5,18 +5,21 @@ class FlexNode: extends RefCounted var type : String var parent : WeakRef - var flexible_layout : Control + var flexible_layout : FlexLayout var rect : Rect2 = Rect2(0, 0, 1000, 1000) - func _init(p : FlexNode = null, fl : Control = null): - type = "FlexNode" + func _init(p : FlexNode = null, fl : FlexLayout = null, t = "FlexNode"): + assert(fl != null) + type = t parent = weakref(p) flexible_layout = fl func _notification(what): if what == NOTIFICATION_PREDELETE: print("removing %s (%s)!" % [ self, type ]) - print("Done") + return + if parent.get_ref() != null: + parent.get_ref().replace(self, null) func serialize() -> Dictionary: var rv : Dictionary = { type=type, w=rect.size.x, h=rect.size.y, children=[] } @@ -45,29 +48,23 @@ class FlexNode: class PanelInfo: - var flex_node : FlexNode var flex_panel : Control - func _init(fn : FlexNode, fp : Control): - flex_node = fn + func _init(fp : Control): flex_panel = fp - flex_node.flexible_layout.start_drag() + flex_panel.get_meta("flex_layout").start_drag() func _notification(what): if what == NOTIFICATION_PREDELETE: - print("Goodbye!") - flex_node.flexible_layout.end_drag() + flex_panel.get_meta("flex_layout").end_drag() class FlexTop: extends FlexNode - var control : Control var child : FlexNode = null - func _init(fl : Control, c : Control = null): - type = "FlexTop" - control = c if c else fl - flexible_layout = fl + func _init(fl : FlexLayout): + super._init(null, fl, "FlexTop") func deserialize(data : Dictionary): super.deserialize(data) @@ -89,6 +86,7 @@ class FlexTop: new_fn.parent = weakref(self) else: child = null + flexible_layout.delete_subwindow() return true func layout(r : Rect2): @@ -105,9 +103,8 @@ class FlexSplit: const DRAGGER_SCENE = preload("res://addons/flexible_layout/flexible_dragger.tscn") - func _init(p : FlexNode, fl : Control): - super._init(p, fl) - type = "FlexSplit" + func _init(p : FlexNode, fl : FlexLayout): + super._init(p, fl, "FlexSplit") func _notification(what): if what == NOTIFICATION_PREDELETE: @@ -149,7 +146,7 @@ class FlexSplit: func replace(fn : FlexNode, new_fn : FlexNode) -> bool: var index : int = children.find(fn) if index == -1: - print("Error replacing FlexNode") + print("Error replacing FlexNode in FlexSplit") return false children.remove_at(index) if new_fn: @@ -173,7 +170,7 @@ class FlexSplit: for i in children_count-1-draggers_count: var dragger = DRAGGER_SCENE.instantiate() draggers.append(dragger) - flexible_layout.add_child(dragger) + flexible_layout.control.add_child(dragger) elif draggers_count > children_count-1: while draggers.size() > children_count-1: draggers.pop_back().queue_free() @@ -228,15 +225,15 @@ class FlexSplit: class FlexTab: extends FlexNode var tabs : Control + var adding : bool = false const TAB_SCENE = preload("res://addons/flexible_layout/flexible_tabs.tscn") - func _init(p : FlexNode, fl : Control): - super._init(p, fl) - type = "FlexTab" - assert(flexible_layout != null) + func _init(p : FlexNode, fl : FlexLayout): + assert(p != null) + super._init(p, fl, "FlexTab") tabs = TAB_SCENE.instantiate() - flexible_layout.add_child(tabs) + flexible_layout.control.add_child(tabs) tabs.set_flex_tab(self) func _notification(what): @@ -255,16 +252,29 @@ class FlexTab: func deserialize(data : Dictionary): super.deserialize(data) for c in data.tabs: - add(flexible_layout.panels[c]) + add(flexible_layout.main_control.panels[c]) func add(fp : Control): + adding = true + if fp.has_meta("flex_node"): + var current_owner = fp.get_meta("flex_node") + if current_owner != null: + current_owner.remove(fp) + fp.set_meta("flex_node", null) + assert(not fp.has_meta("flex_node")) tabs.add(fp) - if fp.get_parent() != flexible_layout: - flexible_layout.add_child(fp) + if fp.get_parent() != flexible_layout.control: + if fp.get_parent() != null: + fp.get_parent().remove_child(fp) + flexible_layout.control.add_child(fp) + fp.set_meta("flex_node", self) + adding = false func remove(fp : Control): + assert(fp.get_meta("flex_node") == self) tabs.erase(fp) - if tabs.get_controls().is_empty(): + fp.set_meta("flex_node", null) + if not adding and tabs.get_controls().is_empty(): tabs.queue_free() parent.get_ref().replace(self, null) @@ -286,19 +296,20 @@ class FlexMain: extends FlexNode var child : Control = null - func _init(p : FlexNode = null, fl : Control = null): + func _init(p : FlexNode = null, fl : FlexLayout = null): super._init(p, fl) type = "FlexMain" func deserialize(data : Dictionary): super.deserialize(data) - add(flexible_layout.panels["Main"]) + add(flexible_layout.main_control.panels["Main"]) func add(fp : Control): #assert(child == null) child = fp - if fp.get_parent() != flexible_layout: - flexible_layout.add_child(fp) + fp.set_meta("flex_node", self) + if fp.get_parent() != flexible_layout.control: + flexible_layout.control.add_child(fp) func layout(r : Rect2): #print("Layout FlexNode - "+str(r)) @@ -306,14 +317,209 @@ class FlexMain: child.position = rect.position child.size = rect.size +class FlexLayout: + var main_control : Control + var control : Control + var top : FlexTop = null + + func _init(m, c): + main_control = m + control = c + + func serialize() -> Dictionary: + return top.serialize() + + func deserialize(parent : FlexNode, data : Dictionary) -> FlexNode: + var fn : FlexNode + match data.type: + "FlexTop": + fn = FlexTop.new(self) + "FlexMain": + fn = FlexMain.new(parent, self) + "FlexSplit": + fn = FlexSplit.new(parent, self) + "FlexTab": + fn = FlexTab.new(parent, self) + _: + print(data.type) + assert(false) + fn.deserialize(data) + return fn + + func get_flexnode_at(p : Vector2) -> FlexNode: + return top.get_flexnode_at(p) + + func get_flexmain(p : FlexNode = top) -> FlexMain: + for c in p.get_children(): + if c is FlexMain: + return c + for c in p.get_children(): + var rv : FlexMain = get_flexmain(c) + if rv != null: + return rv + var main : FlexMain = FlexMain.new(top, top.flexible_layout) + if top.child == null: + top.set_child(main) + else: + if not top.child is FlexSplit: + var flex_split : FlexSplit = FlexSplit.new(top, top.flexible_layout) + flex_split.rect = top.child.rect + flex_split.insert(top.child) + top.set_child(flex_split) + top.child.insert(main) + return main + + func get_default_flextab(p : FlexNode = top) -> FlexTab: + for c in p.get_children(): + if c is FlexTab: + return c + for c in p.get_children(): + var rv : FlexTab = get_default_flextab(c) + if rv != null: + return rv + var tab : FlexTab = FlexTab.new(top, top.flexible_layout) + if top.child == null: + top.set_child(tab) + else: + if not top.child is FlexSplit: + var flex_split : FlexSplit = FlexSplit.new(top, top.flexible_layout) + flex_split.rect = top.child.rect + flex_split.insert(top.child) + top.set_child(flex_split) + top.child.insert(tab) + return tab + + func layout(layout = null): + var default_flextab : FlexTab = null + if layout != null: + top = deserialize(null, layout) + elif main_control == control: + if top == null: + top = FlexTop.new(self) + for panel_name in main_control.panels.keys(): + var panel : Control = main_control.panels[panel_name] + if panel.get_meta("flex_node") != null: + continue + if panel_name == "Main": + get_flexmain().add(panel) + else: + if default_flextab == null: + default_flextab = get_default_flextab() + default_flextab.add(panel) + if top: + top.layout(control.get_rect()) + main_control.layout_changed.emit() + + func move_panel(panel, reference_panel : FlexNode, destination): + var parent_panel : FlexNode = reference_panel.parent.get_ref() as FlexNode + var vertical : bool + var offset : int + var tab : FlexTab = null + match destination: + 0: + if not reference_panel is FlexTab: + return + tab = reference_panel + 1: + vertical = true + offset = 0 + 2: + vertical = false + offset = 0 + 3: + vertical = false + offset = 1 + 4: + vertical = true + offset = 1 + _: + return + if tab == null: + var split : FlexSplit + if parent_panel is FlexSplit and parent_panel.vertical == vertical: + split = parent_panel as FlexSplit + else: + split = FlexSplit.new(parent_panel, reference_panel.flexible_layout) + split.vertical = vertical + parent_panel.replace(reference_panel, split) + split.insert(reference_panel, 0) + tab = FlexTab.new(parent_panel, reference_panel.flexible_layout) + var ref_index : int = split.find(reference_panel) + split.insert(tab, ref_index+offset, ref_index) + tab.add(panel.flex_panel) + layout() + + func undock(panel : Control): + main_control.subwindows.append(FlexWindow.new(main_control, panel)) + layout() + + func delete_subwindow(): + if control == main_control: + return + var subwindow : FlexWindow = control.get_window() + main_control.subwindows.erase(subwindow) + subwindow.queue_free() class FlexWindow: extends Window + + var panel : Control + var flex_layout : FlexLayout + var overlay : Control + + func _init(main_control : Control, first_panel : Control = null): + if first_panel: + position = Vector2i(first_panel.get_global_rect().position)+first_panel.get_window().position + size = first_panel.size + panel = Control.new() + add_child(panel) + panel.position = Vector2i(0, 0) + panel.size = size + flex_layout = FlexLayout.new(main_control, panel) + main_control.get_viewport().add_child(self) + if first_panel: + flex_layout.layout({children=[{type="FlexTab",tabs=[first_panel.name],current=0,h=768,w=288,children=[]}],type="FlexTop",h=768,w=1024}) + resize() + + func _ready(): + self.size_changed.connect(self.resize) + + func resize(): + panel.position = Vector2i(0, 0) + panel.size = size + flex_layout.layout() + + func serialize() -> Dictionary: + var data : Dictionary = {} + data.x = position.x + data.y = position.y + data.w = size.x + data.h = size.y + data.layout = flex_layout.serialize() + return data + + func layout(data : Dictionary): + position = Vector2i(data.x, data.y) + size = Vector2i(data.w, data.h) + flex_layout.layout(data.layout) + resize() + + func start_drag(): + overlay = OVERLAY_SCENE.instantiate() + overlay.position = Vector2(0, 0) + overlay.size = panel.size + overlay.flex_layout = flex_layout + panel.add_child(overlay) + + func end_drag(): + overlay.queue_free() + overlay = null - -var top : FlexTop = null -var unassigned : Array[Control] = [] var panels : Dictionary = {} +var flex_layout : FlexLayout +var subwindows : Array[Window] + +var no_auto_layout : bool = true var overlay : Control @@ -324,153 +530,54 @@ const OVERLAY_SCENE = preload("res://addons/flexible_layout/flexible_overlay.tsc signal layout_changed +func _init(): + flex_layout = FlexLayout.new(self, self) + func _ready(): var children = get_children() for c in children: if c.visible and ! c.has_meta("flexlayout"): add(c.name, c) -func serialize() -> Dictionary: - return top.serialize() - -func deserialize(parent : FlexNode, data : Dictionary) -> FlexNode: - var fn : FlexNode - match data.type: - "FlexTop": - fn = FlexTop.new(self) - "FlexMain": - fn = FlexMain.new(parent, self) - "FlexSplit": - fn = FlexSplit.new(parent, self) - "FlexTab": - fn = FlexTab.new(parent, self) - _: - print(data.type) - assert(false) - fn.deserialize(data) - return fn - -func get_flexmain(p : FlexNode = top) -> FlexMain: - for c in p.get_children(): - if c is FlexMain: - return c - for c in p.get_children(): - var rv : FlexMain = get_flexmain(c) - if rv != null: - return rv - var main : FlexMain = FlexMain.new(top, top.flexible_layout) - if top.child == null: - top.set_child(main) - else: - if not top.child is FlexSplit: - var flex_split : FlexSplit = FlexSplit.new(top, top.flexible_layout) - flex_split.rect = top.child.rect - flex_split.insert(top.child) - top.set_child(flex_split) - top.child.insert(main) - return main - -func get_default_flextab(p : FlexNode = top) -> FlexTab: - for c in p.get_children(): - if c is FlexTab: - return c - for c in p.get_children(): - var rv : FlexTab = get_default_flextab(c) - if rv != null: - return rv - var tab : FlexTab = FlexTab.new(top, top.flexible_layout) - if top.child == null: - top.set_child(tab) - else: - if not top.child is FlexSplit: - var flex_split : FlexSplit = FlexSplit.new(top, top.flexible_layout) - flex_split.rect = top.child.rect - flex_split.insert(top.child) - top.set_child(flex_split) - top.child.insert(tab) - return tab - -func layout(layout = null): - var default_flextab : FlexTab = null - if layout != null: - top = FlexTop.new(self) - print("Deleted?") - top = deserialize(null, layout) - unassigned = [] - else: - if top == null: - top = FlexTop.new(self) - for c in unassigned: - if c.name == "Main": - get_flexmain().add(c) - else: - if default_flextab == null: - default_flextab = get_default_flextab() - default_flextab.add(c) - unassigned = [] - top.layout(get_rect()) - print("done") - func add(n : String, c : Control): c.name = n - unassigned.push_back(c) + c.set_meta("flex_layout", self) panels[n] = c -func _on_resized(): - layout() - layout_changed.emit() +func layout(layout = null): + if layout and layout.has("main"): + for w in layout.windows: + var subwindow = FlexWindow.new(self) + subwindows.append(subwindow) + subwindow.layout(w) + flex_layout.layout(layout.main) + else: + flex_layout.layout(layout) + no_auto_layout = false + +func serialize() -> Dictionary: + var data : Dictionary = {} + data.main = flex_layout.serialize() + data.windows = [] + for w in subwindows: + data.windows.append(w.serialize()) + return data func start_drag(): overlay = OVERLAY_SCENE.instantiate() overlay.position = Vector2(0, 0) overlay.size = size + overlay.flex_layout = flex_layout add_child(overlay) + for w in subwindows: + w.start_drag() func end_drag(): overlay.queue_free() overlay = null + for w in subwindows: + w.end_drag() -func get_flexnode_at(p : Vector2) -> FlexNode: - return top.get_flexnode_at(p) - -func move_panel(panel, reference_panel : FlexNode, destination): - var parent_panel : FlexNode = reference_panel.parent.get_ref() as FlexNode - var vertical : bool - var offset : int - var tab : FlexTab = null - match destination: - 0: - if not reference_panel is FlexTab: - return - tab = reference_panel - 1: - vertical = true - offset = 0 - 2: - vertical = false - offset = 0 - 3: - vertical = false - offset = 1 - 4: - vertical = true - offset = 1 - _: - return - if tab == null: - var split : FlexSplit - if parent_panel is FlexSplit and parent_panel.vertical == vertical: - split = parent_panel as FlexSplit - else: - split = FlexSplit.new(parent_panel, reference_panel.flexible_layout) - split.vertical = vertical - parent_panel.replace(reference_panel, split) - split.insert(reference_panel, 0) - tab = FlexTab.new(parent_panel, reference_panel.flexible_layout) - var ref_index : int = split.find(reference_panel) - split.insert(tab, ref_index+offset, ref_index) - panel.flex_node.remove(panel.flex_panel) - tab.add(panel.flex_panel) - layout() - layout_changed.emit() - +func _on_resized(): + if not no_auto_layout: + layout() diff --git a/addons/flexible_layout/flexible_overlay.gd b/addons/flexible_layout/flexible_overlay.gd index e0172da3c..493d5d606 100644 --- a/addons/flexible_layout/flexible_overlay.gd +++ b/addons/flexible_layout/flexible_overlay.gd @@ -1,6 +1,8 @@ extends ColorRect +var flex_layout + var arrow_icon = preload("res://addons/flexible_layout/arrow.svg") var tab_icon = preload("res://addons/flexible_layout/tab.svg") @@ -12,19 +14,21 @@ func find_position_from_target(at_position, target): return POSITIONS[pos_x+3*pos_y] func _drop_data(at_position, data): - var target = get_parent().get_flexnode_at(at_position) + at_position /= get_window().content_scale_factor + var target = flex_layout.get_flexnode_at(at_position) if target: var destination = find_position_from_target(at_position, target) if destination != -1: - get_parent().move_panel(data, target, destination) + flex_layout.move_panel(data, target, destination) func _can_drop_data(at_position, data): - var target = get_parent().get_flexnode_at(at_position) + at_position /= get_window().content_scale_factor + var target = flex_layout.get_flexnode_at(at_position) if target: var rect : Rect2 = target.rect match find_position_from_target(at_position, target): 0: - if data.flex_node == target: + if data.flex_panel.get_meta("flex_node") == target: $Arrow.visible = false return false $Arrow.visible = true diff --git a/addons/flexible_layout/flexible_tab.gd b/addons/flexible_layout/flexible_tab.gd index 1581c26c3..c9ecf3b00 100644 --- a/addons/flexible_layout/flexible_tab.gd +++ b/addons/flexible_layout/flexible_tab.gd @@ -14,9 +14,13 @@ func init(fp : Control): func _draw(): var is_current : bool = (get_index() == get_parent().get_parent().current) draw_style_box(get_theme_stylebox("tab_selected" if is_current else "tab_unselected", "TabBar"), Rect2(Vector2(), size)) - #$Container/Undock.visible = is_current + $Container/Undock.visible = is_current $Container/Close.visible = is_current +func _on_undock_pressed(): + var flex_tab = get_parent().get_parent().get_flex_tab() + flex_tab.flexible_layout.undock(flex_panel) + func _on_close_pressed(): var flex_tab = get_parent().get_parent().get_flex_tab() flex_tab.remove(flex_panel) @@ -27,6 +31,6 @@ func _gui_input(event): get_parent().get_parent().set_current(get_index()) func _get_drag_data(_position): - var flex_tab = get_parent().get_parent().get_flex_tab() - var flexible_layout = flex_tab.flexible_layout - return flexible_layout.PanelInfo.new(flex_tab, flex_panel) + return preload("res://addons/flexible_layout/flexible_layout.gd").PanelInfo.new(flex_panel) + + diff --git a/addons/flexible_layout/flexible_tab.tscn b/addons/flexible_layout/flexible_tab.tscn index 3d7c82c94..b889ef507 100644 --- a/addons/flexible_layout/flexible_tab.tscn +++ b/addons/flexible_layout/flexible_tab.tscn @@ -1,9 +1,7 @@ [gd_scene load_steps=4 format=3 uid="uid://e06xegp2tp3f"] [ext_resource type="Script" path="res://addons/flexible_layout/flexible_tab.gd" id="1"] - -[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_b47q6"] -size = Vector2(16, 16) +[ext_resource type="Texture2D" uid="uid://c5br5opw6gmyp" path="res://addons/flexible_layout/undock.svg" id="2_ln5tf"] [sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_bok24"] size = Vector2(16, 16) @@ -36,7 +34,7 @@ visible = false layout_mode = 2 size_flags_horizontal = 4 size_flags_vertical = 4 -texture_normal = SubResource("PlaceholderTexture2D_b47q6") +texture_normal = ExtResource("2_ln5tf") [node name="Close" type="TextureButton" parent="Container"] visible = false @@ -45,4 +43,5 @@ size_flags_horizontal = 4 size_flags_vertical = 4 texture_normal = SubResource("PlaceholderTexture2D_bok24") +[connection signal="pressed" from="Container/Undock" to="." method="_on_undock_pressed"] [connection signal="pressed" from="Container/Close" to="." method="_on_close_pressed"] diff --git a/addons/flexible_layout/test.gd b/addons/flexible_layout/test.gd index 265919f85..842938012 100644 --- a/addons/flexible_layout/test.gd +++ b/addons/flexible_layout/test.gd @@ -3,10 +3,11 @@ extends Control func _ready(): get_window().borderless = false get_window().size = Vector2i(1024, 768) + #await get_tree().process_frame var file : FileAccess = FileAccess.open("res://addons/flexible_layout/layout.json", FileAccess.READ) if file: - var layout : Dictionary = JSON.parse_string(file.get_as_text()) - $FlexibleLayout.layout(layout) + var layout = JSON.parse_string(file.get_as_text()) + $FlexibleLayout.layout(layout as Dictionary if layout else null) func _on_tree_exiting(): var file : FileAccess = FileAccess.open("res://addons/flexible_layout/layout.json", FileAccess.WRITE) diff --git a/addons/flexible_layout/undock.svg b/addons/flexible_layout/undock.svg new file mode 100644 index 000000000..803ca95ad --- /dev/null +++ b/addons/flexible_layout/undock.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + diff --git a/addons/flexible_layout/undock.svg.import b/addons/flexible_layout/undock.svg.import new file mode 100644 index 000000000..5694204ea --- /dev/null +++ b/addons/flexible_layout/undock.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c5br5opw6gmyp" +path="res://.godot/imported/undock.svg-0e462ba8ce32a7b4dbda8774c1ab8185.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/flexible_layout/undock.svg" +dest_files=["res://.godot/imported/undock.svg-0e462ba8ce32a7b4dbda8774c1ab8185.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/material_maker/main_window.gd b/material_maker/main_window.gd index e3f9bfaad..49211f0ad 100644 --- a/material_maker/main_window.gd +++ b/material_maker/main_window.gd @@ -118,7 +118,7 @@ func _ready() -> void: get_window().borderless = false get_window().transparent = false get_window().move_to_foreground() - #get_window().gui_embed_subwindows = false + get_window().gui_embed_subwindows = false get_window().close_requested.connect(self.on_close_requested) @@ -161,7 +161,6 @@ func _ready() -> void: if f.ends_with(".ptex"): print(f) dir.copy("res://material_maker/examples/"+f, "/examples/"+f) - print("Done") # Set a minimum window size to prevent UI elements from collapsing on each other. get_window().min_size = Vector2(1024, 600)