From 18dd6d61fbb496a77bded06bc114c2a0e14c4594 Mon Sep 17 00:00:00 2001 From: Patrick Wuttke Date: Wed, 18 Aug 2021 22:03:39 +0200 Subject: [PATCH] Refactored a lot of stuff. Added property lists. Renamed FileAdapter to FileSystemAdapter. And more smaller stuff. --- images/folder_up.svg | 149 +++++++++++++++ images/folder_up.svg.import | 34 ++++ scenes/property_list/header.tscn | 44 +++++ scenes/property_list/property_label.tscn | 21 +++ scenes/property_list/scripts/header.gd | 19 ++ .../property_list/scripts/property_label.gd | 19 ++ scripts/libs/ui_util.gd | 12 ++ scripts/libs/ux.gd | 8 +- .../types/controls/dynamic_grid_container.gd | 45 ++++- scripts/types/controls/entity_tree.gd | 116 ++++++++++++ scripts/types/controls/file_list.gd | 30 +++- .../types/controls/folder_default_button.gd | 81 +++++++++ scripts/types/controls/folder_edit.gd | 23 ++- scripts/types/controls/folder_sync.gd | 4 +- scripts/types/controls/places_list.gd | 41 +++-- scripts/types/controls/property_check_box.gd | 51 ++++++ scripts/types/controls/property_list.gd | 169 ++++++++++++++++++ scripts/types/controls/recent_places_list.gd | 1 + .../types/controls/simple_property_edit.gd | 102 +++++++++++ ...{file_adapter.gd => filesystem_adapter.gd} | 2 +- ...adapter.gd => local_filesystem_adapter.gd} | 11 +- styles/property_list_header.tres | 8 + 22 files changed, 954 insertions(+), 36 deletions(-) create mode 100644 images/folder_up.svg create mode 100644 images/folder_up.svg.import create mode 100644 scenes/property_list/header.tscn create mode 100644 scenes/property_list/property_label.tscn create mode 100644 scenes/property_list/scripts/header.gd create mode 100644 scenes/property_list/scripts/property_label.gd create mode 100644 scripts/types/controls/entity_tree.gd create mode 100644 scripts/types/controls/folder_default_button.gd create mode 100644 scripts/types/controls/property_check_box.gd create mode 100644 scripts/types/controls/property_list.gd create mode 100644 scripts/types/controls/simple_property_edit.gd rename scripts/types/misc/{file_adapter.gd => filesystem_adapter.gd} (96%) rename scripts/types/misc/{local_file_adapter.gd => local_filesystem_adapter.gd} (90%) create mode 100644 styles/property_list_header.tres diff --git a/images/folder_up.svg b/images/folder_up.svg new file mode 100644 index 0000000..ad113a3 --- /dev/null +++ b/images/folder_up.svg @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/images/folder_up.svg.import b/images/folder_up.svg.import new file mode 100644 index 0000000..f9d3816 --- /dev/null +++ b/images/folder_up.svg.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/folder_up.svg-097930bffb5edf2a0b4b16cb480291b4.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/de.mewin.gduibasics/images/folder_up.svg" +dest_files=[ "res://.import/folder_up.svg-097930bffb5edf2a0b4b16cb480291b4.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/scenes/property_list/header.tscn b/scenes/property_list/header.tscn new file mode 100644 index 0000000..2dc8205 --- /dev/null +++ b/scenes/property_list/header.tscn @@ -0,0 +1,44 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://addons/de.mewin.gduibasics/scripts/types/misc/constraints/dynamic_grid_constraints.gd" type="Script" id=2] +[ext_resource path="res://addons/de.mewin.gduibasics/scenes/property_list/scripts/header.gd" type="Script" id=3] + +[sub_resource type="StyleBoxFlat" id=1] +bg_color = Color( 0, 0, 0, 0.627451 ) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[node name="header" type="PanelContainer"] +margin_right = 198.0 +margin_bottom = 22.0 +size_flags_horizontal = 3 +custom_styles/panel = SubResource( 1 ) +script = ExtResource( 3 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="header_info" type="HBoxContainer" parent="."] +margin_right = 198.0 +margin_bottom = 22.0 +alignment = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="texture" type="TextureRect" parent="header_info"] +margin_left = 70.0 +margin_right = 70.0 +margin_bottom = 22.0 + +[node name="label" type="Label" parent="header_info"] +margin_left = 74.0 +margin_right = 128.0 +margin_bottom = 22.0 +text = "Header" + +[node name="constraints" type="Node" parent="."] +script = ExtResource( 2 ) +colspan = 2 diff --git a/scenes/property_list/property_label.tscn b/scenes/property_list/property_label.tscn new file mode 100644 index 0000000..4aad3c4 --- /dev/null +++ b/scenes/property_list/property_label.tscn @@ -0,0 +1,21 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/de.mewin.gduibasics/scenes/property_list/scripts/property_label.gd" type="Script" id=1] + +[node name="property_label" type="HBoxContainer"] +anchor_right = 1.0 +anchor_bottom = 1.0 +size_flags_horizontal = 3 +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="texture" type="TextureRect" parent="."] +margin_bottom = 600.0 + +[node name="label" type="Label" parent="."] +margin_left = 4.0 +margin_top = 289.0 +margin_right = 12.0 +margin_bottom = 311.0 diff --git a/scenes/property_list/scripts/header.gd b/scenes/property_list/scripts/header.gd new file mode 100644 index 0000000..c441ec2 --- /dev/null +++ b/scenes/property_list/scripts/header.gd @@ -0,0 +1,19 @@ +extends Control + +var label := "" setget _set_label, _get_label +var texture : Texture = null setget _set_texture, _get_texture + +##################### +# setters & getters # +##################### +func _set_label(label_ : String) -> void: + $header_info/label.text = label_ + +func _get_label() -> String: + return $header_info/label.text + +func _set_texture(texture_ : Texture) -> void: + $header_info/texture.texture = texture_ + +func _get_texture() -> Texture: + return $header_info/texture.texture diff --git a/scenes/property_list/scripts/property_label.gd b/scenes/property_list/scripts/property_label.gd new file mode 100644 index 0000000..e0d450c --- /dev/null +++ b/scenes/property_list/scripts/property_label.gd @@ -0,0 +1,19 @@ +extends Control + +var label := "" setget _set_label, _get_label +var texture : Texture = null setget _set_texture, _get_texture + +##################### +# setters & getters # +##################### +func _set_label(label_ : String) -> void: + $label.text = label_ + +func _get_label() -> String: + return $label.text + +func _set_texture(texture_ : Texture) -> void: + $texture.texture = texture_ + +func _get_texture() -> Texture: + return $texture.texture diff --git a/scripts/libs/ui_util.gd b/scripts/libs/ui_util.gd index ce2a053..7ba3fa1 100644 --- a/scripts/libs/ui_util.gd +++ b/scripts/libs/ui_util.gd @@ -39,6 +39,18 @@ static func new_item_sorted(tree : Tree, parent : TreeItem, key : String, meta_c new_itm.set_metadata(meta_column, key) return new_itm +static func find_tree_item_with_meta(root : TreeItem, value, column := 0) -> TreeItem: + if root == null: + return null + elif root.get_metadata(column) == value: + return root + else: + var itm := find_tree_item_with_meta(root.get_next(), value, column) + if itm != null: + return itm + itm = find_tree_item_with_meta(root.get_children(), value, column) + return itm + static func copy_size(target : Control, source : Control) -> void: target.rect_min_size = source.rect_min_size target.rect_size = source.rect_size diff --git a/scripts/libs/ux.gd b/scripts/libs/ux.gd index d582777..59522b6 100644 --- a/scripts/libs/ux.gd +++ b/scripts/libs/ux.gd @@ -16,7 +16,7 @@ static func get_recent_places() -> Array: return GDBSettings.get_value(GDBConstants.SETTING_RECENT_PLACES, []) static func add_recent_place(place) -> void: - if place is UIB_FileAdapter.FileEntry: + if place is UIB_FilesystemAdapter.FileEntry: add_recent_place(place._get_path()) return @@ -32,14 +32,14 @@ static func get_favourite_places() -> Array: return GDBSettings.get_value(GDBConstants.SETTING_FAVOURITE_PLACES, []) static func is_favourite_place(place) -> bool: - if place is UIB_FileAdapter.FileEntry: + if place is UIB_FilesystemAdapter.FileEntry: return is_favourite_place(place._get_path()) assert(place is String) return GDBSettings.array_has_value(GDBConstants.SETTING_FAVOURITE_PLACES, place) static func add_favourite_place(place) -> void: - if place is UIB_FileAdapter.FileEntry: + if place is UIB_FilesystemAdapter.FileEntry: add_favourite_place(place._get_path()) return @@ -52,7 +52,7 @@ static func add_favourite_place(place) -> void: __get_state().emit_signal("favourite_places_changed") static func remove_favourite_place(place) -> void: - if place is UIB_FileAdapter.FileEntry: + if place is UIB_FilesystemAdapter.FileEntry: remove_favourite_place(place._get_path()) return diff --git a/scripts/types/controls/dynamic_grid_container.gd b/scripts/types/controls/dynamic_grid_container.gd index 27e8bea..6d53521 100644 --- a/scripts/types/controls/dynamic_grid_container.gd +++ b/scripts/types/controls/dynamic_grid_container.gd @@ -4,19 +4,51 @@ extends Container class_name UIB_DynamicGridContainer var __DEFAULT_CONSTRAINTS = UIB_DynamicGridConstraints.new() +const __META_CONSTRAINTS = "__uib_dynamic_grind_constraints__" export(int, 1, 100) var columns := 1 setget _set_columns +############# +# overrides # +############# func _notification(what): if what == NOTIFICATION_SORT_CHILDREN: __sort_children() -func __get_constraints(node : Node) -> UIB_DynamicGridConstraints: +################ +# overridables # +################ +func _get_constraints(node : Node) -> UIB_DynamicGridConstraints: + var constraints := get_existing_constraints(node) + return constraints if constraints else __DEFAULT_CONSTRAINTS + +################ +# public stuff # +################ +static func get_existing_constraints(node : Node) -> UIB_DynamicGridConstraints: + if node.has_meta(__META_CONSTRAINTS): + return node.get_meta(__META_CONSTRAINTS).object + for child in node.get_children(): if child is UIB_DynamicGridConstraints: return child - return __DEFAULT_CONSTRAINTS + return null +static func get_constraints(node : Node) -> UIB_DynamicGridConstraints: + var constraints := get_existing_constraints(node) + if constraints != null: + return constraints + + if !node.has_meta(__META_CONSTRAINTS): + var wrapper := GDB_ReferenceWrapper.new() + wrapper.object = UIB_DynamicGridConstraints.new() + node.set_meta(__META_CONSTRAINTS, wrapper) + + return node.get_meta(__META_CONSTRAINTS).object + +################# +# private stuff # +################# func __sort_children(): var column_widths := [] var column_expand := [] @@ -32,7 +64,7 @@ func __sort_children(): for child in get_children(): if !child is Control || !child.visible: continue - var constraints := __get_constraints(child) + var constraints := _get_constraints(child) if col + constraints.colspan > columns: col = 0 @@ -67,7 +99,7 @@ func __sort_children(): var row_height := 0.0 col = 0 for child in row: - var constraints := __get_constraints(child) + var constraints := _get_constraints(child) child.rect_position = pos var width := 0.0 @@ -88,7 +120,10 @@ func __sort_children(): pos.y += row_height pos.x = 0.0 rect_min_size.y = pos.y - + +########### +# setters # +########### func _set_columns(value : int): if value != columns: columns = value diff --git a/scripts/types/controls/entity_tree.gd b/scripts/types/controls/entity_tree.gd new file mode 100644 index 0000000..b56911e --- /dev/null +++ b/scripts/types/controls/entity_tree.gd @@ -0,0 +1,116 @@ +extends Tree + +class_name UIB_EntityTree + +export(Array, String) var column_ids := [].duplicate() +export(Array, String) var column_titles := [].duplicate() +export(NodePath) var selected_entity_adapter := "" + +onready var __selected_entity_adapter := get_node_or_null(selected_entity_adapter) as GDB_DataAdapter + +############# +# overrides # +############# +func _ready(): + create_item() # invisible root + hide_root = true + columns = column_ids.size() + select_mode = SELECT_ROW + set_column_titles_visible(true) + + for col in range(min(columns, column_titles.size())): + set_column_title(col, column_titles[col]) + + connect("item_selected", self, "_on_item_selected") + if __selected_entity_adapter != null: + __selected_entity_adapter.connect("data_changed", self, "_on_selected_entity_adapter_data_changed") + +################ +# public stuff # +################ +func add_entity(entity : Object) -> void: + if !GDB_Entity.is_valid_entity(entity): + printerr("UIB_EntityTree: attempted to add invalid entity") + return + + var itm := create_item() + itm.set_metadata(0, entity) + var col := 0 + for id in column_ids: + var prop : Object = entity.get_property_by_id(id) + + if prop == null: + col += 1 + continue + + itm.set_text(col, GDB_Property.get_prop_value_as_string(prop)) + col += 1 + + GDBUtility.try_connect(prop, "value_changed", self, "_on_property_value_changed", [entity, id]) + +func select_entity(entity : Object) -> void: + if entity == null: + return # TODO: unselect? + var itm := __item_from_entity(entity) + if itm == null: + return # TODO: unselect? + itm.select(0) + +func get_selected_entity() -> Object: + var entities := get_selected_entities() + if !entities.empty(): + return entities[0] + return null + +func get_selected_entities() -> Array: # n/a yet + var entities := [] + var itm := get_selected() + + while itm != null: + var entity_ := __entity_from_item(itm) + if entity_ != null: + entities.append(entity_) + itm = get_next_selected(itm) + return entities + +################# +# private stuff # +################# +func __entity_from_item(itm : TreeItem) -> Object: + return itm.get_metadata(0) as Object + +func __item_from_entity(entity : Object) -> TreeItem: + return GDBUIUtility.find_tree_item_with_meta(get_root(), entity) + +func __update_row(entity : Object, column := ""): + if column == "": + for col in column_ids: + if col != "": + __update_row(entity, col) + return + + var itm := __item_from_entity(entity) + var col_idx := column_ids.find(column) + + if itm == null || col_idx < 0: + return + + var prop := entity.get_property_by_id(column) as Object + if prop == null: + return + + itm.set_text(col_idx, GDB_Property.get_prop_value_as_string(prop)) + +############ +# handlers # +############ +func _on_item_selected() -> void: + if __selected_entity_adapter != null: + __selected_entity_adapter.data = get_selected_entity() + +func _on_selected_entity_adapter_data_changed() -> void: + if __selected_entity_adapter.data != get_selected_entity(): + select_entity(__selected_entity_adapter.data) + +func _on_property_value_changed(entity : Object, property_id : String) -> void: + __update_row(entity, property_id) diff --git a/scripts/types/controls/file_list.gd b/scripts/types/controls/file_list.gd index 2247d1a..f496a3f 100644 --- a/scripts/types/controls/file_list.gd +++ b/scripts/types/controls/file_list.gd @@ -46,7 +46,9 @@ class DetailsColumnType: func _get_name() -> String: return tr("Type") func _get_value(entry) -> String: - if entry._is_folder(): + if entry._is_link(): + return tr("Link") + elif entry._is_folder(): return tr("Folder") else: return tr("File") @@ -82,7 +84,7 @@ class DetailsColumnModifiedTime: return entry0._get_modified_time() < entry1._get_modified_time() class __ParentFolderEntry: - extends UIB_FileAdapter.FileEntry + extends UIB_FilesystemAdapter.FileEntry var real_entry @@ -99,11 +101,12 @@ const CHECKBOX_COLUMN_WIDTH = 25 export(ViewMode) var view_mode = ViewMode.DETAILS setget _set_view_mode export(Checkable) var checkable = Checkable.NONE setget _set_checkable +export(NodePath) var folder_adapter := "" export var allow_navigation := true export var show_up_entry := true -var adapter : UIB_FileAdapter setget _set_adapter +var adapter : UIB_FilesystemAdapter setget _set_adapter var filter : UIB_FileFilter setget _set_filter -var current_folder : UIB_FileAdapter.FileEntry setget _set_current_folder +var current_folder : UIB_FilesystemAdapter.FileEntry setget _set_current_folder var details_columns := [ DetailsColumnName.new(), DetailsColumnType.new(), @@ -113,13 +116,14 @@ var details_columns := [ var __special_columns := [] var __sort_column := 0 var __sort_reverse := false +onready var __folder_adapter := get_node_or_null(folder_adapter) as GDB_DataAdapter func _ready(): hide_root = true select_mode = Tree.SELECT_ROW if adapter == null: - adapter = UIB_LocalFileAdapter.new() + adapter = UIB_LocalFilesystemAdapter.new() if current_folder == null: current_folder = adapter._get_root() @@ -129,6 +133,13 @@ func _ready(): connect("item_activated", self, "_on_item_activated") connect("item_selected", self, "_on_item_selected") connect("column_title_pressed", self, "_on_column_title_pressed") + + if __folder_adapter != null: + __folder_adapter.connect("data_changed", self, "_on_folder_adapter_data_changed") + if __folder_adapter.data: + _set_current_folder(__folder_adapter.data) + else: + __folder_adapter.data = current_folder ################# # private stuff # @@ -213,7 +224,7 @@ func _set_view_mode(value): view_mode = value __update_entries() -func _set_adapter(value : UIB_FileAdapter): +func _set_adapter(value : UIB_FilesystemAdapter): if value != adapter: adapter = value __update_entries() @@ -223,11 +234,13 @@ func _set_filter(value : UIB_FileFilter): filter = value __update_entries() -func _set_current_folder(value : UIB_FileAdapter.FileEntry): +func _set_current_folder(value : UIB_FilesystemAdapter.FileEntry): if value != current_folder: current_folder = value emit_signal("current_folder_changed") __update_entries() + if __folder_adapter != null: + __folder_adapter.data = current_folder func _set_checkable(value): if value != checkable: @@ -272,6 +285,9 @@ func _on_column_title_pressed(idx : int): __sort_reverse = false __update_entries() +func _on_folder_adapter_data_changed() -> void: + _set_current_folder(__folder_adapter.data) + ########### # signals # ########### diff --git a/scripts/types/controls/folder_default_button.gd b/scripts/types/controls/folder_default_button.gd new file mode 100644 index 0000000..329c741 --- /dev/null +++ b/scripts/types/controls/folder_default_button.gd @@ -0,0 +1,81 @@ +extends ToolButton + +class_name UIB_FolderDefaultButton + +enum Type { + FOLDER_UP, + FAVOURITE +} + +export(Type) var type := Type.FAVOURITE +export(NodePath) var folder_adapter := "" +export(Texture) var icon_active : Texture = null + +var current_folder : UIB_FilesystemAdapter.FileEntry = null + +onready var __folder_adapter := get_node_or_null(folder_adapter) as GDB_DataAdapter +onready var __icon_inactive = icon + +############# +# overrides # +############# +func _ready() -> void: + if __folder_adapter == null: + printerr("This UIB_FolderDefaultButton is missing a folder_adapter, it will do nothing.") + disabled = true + return + + if __folder_adapter.data: + __set_current_folder(__folder_adapter.data) + + __update_disabled() + + __folder_adapter.connect("data_changed", self, "_on_folder_adapter_data_changed") + GDBUX.connect_static("favourite_places_changed", self, "_on_favourite_places_changed") + +func _pressed() -> void: + match type: + Type.FOLDER_UP: + __set_current_folder(current_folder._get_parent()) + Type.FAVOURITE: + if GDBUX.is_favourite_place(current_folder): + GDBUX.remove_favourite_place(current_folder) + else: + GDBUX.add_favourite_place(current_folder) + +################# +# private stuff # +################# +func __set_current_folder(value : UIB_FilesystemAdapter.FileEntry) -> void: + if value != current_folder: + current_folder = value + __folder_adapter.data = current_folder + __update_disabled() + __update_icon() + +func __update_disabled() -> void: + if current_folder == null: + disabled = true + return + + match type: + Type.FOLDER_UP: + disabled = (current_folder._get_parent() == null) + Type.FAVOURITE: + disabled = false + +func __update_icon(): + if type == Type.FAVOURITE: + if GDBUX.is_favourite_place(current_folder): + icon = icon_active + else: + icon = __icon_inactive + +############ +# handlers # +############ +func _on_folder_adapter_data_changed() -> void: + __set_current_folder(__folder_adapter.data) + +func _on_favourite_places_changed() -> void: + __update_icon() diff --git a/scripts/types/controls/folder_edit.gd b/scripts/types/controls/folder_edit.gd index c471d9e..caf709c 100644 --- a/scripts/types/controls/folder_edit.gd +++ b/scripts/types/controls/folder_edit.gd @@ -2,9 +2,13 @@ extends LineEdit class_name UIB_FolderEdit +export(NodePath) var folder_adapter := "" + var suggestion_popup := UIB_SuggestionPopup.new() -var adapter : UIB_FileAdapter -var current_folder : UIB_FileAdapter.FileEntry setget _set_current_folder +var adapter : UIB_FilesystemAdapter +var current_folder : UIB_FilesystemAdapter.FileEntry setget _set_current_folder + +onready var __folder_adapter := get_node_or_null(folder_adapter) as GDB_DataAdapter ############# # overrides # @@ -13,6 +17,16 @@ func _ready(): call_deferred("__setup") connect("text_changed", self, "_on_text_changed") connect("text_entered", self, "_on_text_entered") + + if adapter == null: + adapter = UIB_LocalFilesystemAdapter.new() + + if __folder_adapter: + __folder_adapter.connect("data_changed", self, "_on_folder_adapter_data_changed") + if __folder_adapter.data: + _set_current_folder(__folder_adapter.data) + else: + __folder_adapter.data = current_folder ################# # private stuff # @@ -59,6 +73,8 @@ func _set_current_folder(value): current_folder = value text = current_folder._get_path() emit_signal("current_folder_changed") + if __folder_adapter != null: + __folder_adapter.data = current_folder ############ # handlers # @@ -73,6 +89,9 @@ func _on_suggestion_accepted(suggestion): __update_current_folder() __update_suggestions() +func _on_folder_adapter_data_changed() -> void: + _set_current_folder(__folder_adapter.data) + ########### # signals # ########### diff --git a/scripts/types/controls/folder_sync.gd b/scripts/types/controls/folder_sync.gd index 40e0d74..7f3d4c9 100644 --- a/scripts/types/controls/folder_sync.gd +++ b/scripts/types/controls/folder_sync.gd @@ -13,9 +13,9 @@ export(DefaultFolder) var default_folder = DefaultFolder.HOME export(NodePath) var folder_up_action export(NodePath) var folder_favourite_action export var custom_default_folder := "" -var adapter : UIB_FileAdapter = UIB_LocalFileAdapter.new() +var adapter : UIB_FilesystemAdapter = UIB_LocalFilesystemAdapter.new() -var current_folder : UIB_FileAdapter.FileEntry setget set_current_folder +var current_folder : UIB_FilesystemAdapter.FileEntry setget set_current_folder var __folder_up_action : UIB_SimpleAction var __folder_favourite_action : UIB_SimpleAction var __nodes := [] diff --git a/scripts/types/controls/places_list.gd b/scripts/types/controls/places_list.gd index 685c8cf..040a987 100644 --- a/scripts/types/controls/places_list.gd +++ b/scripts/types/controls/places_list.gd @@ -18,17 +18,16 @@ class __DefaultItem: icon = icon_ label = label_ -var __DEFAULT_ITEMS := [ - __DefaultItem.new(GDBFsUtil.get_home_folder(), preload("res://addons/de.mewin.gduibasics/images/home32.svg"), tr("Home")) -] - export(Mode) var mode = Mode.DEFAULT setget _set_mode export(Array, String) var places := [] setget _set_places export(Array, Texture) var icons := [] export(Array, String) var names := [] +export(NodePath) var folder_adapter := "" -var adapter : UIB_FileAdapter -var current_folder : UIB_FileAdapter.FileEntry setget _set_current_folder +var adapter : UIB_FilesystemAdapter +var current_folder : UIB_FilesystemAdapter.FileEntry setget _set_current_folder + +onready var __folder_adapter := get_node_or_null(folder_adapter) as GDB_DataAdapter ############# # overrides # @@ -36,6 +35,16 @@ var current_folder : UIB_FileAdapter.FileEntry setget _set_current_folder func _ready(): call_deferred("__update_items") + if adapter == null: + adapter = UIB_LocalFilesystemAdapter.new() + + if __folder_adapter: + __folder_adapter.connect("data_changed", self, "_on_folder_adapter_data_changed") + if __folder_adapter.data: + _set_current_folder(__folder_adapter.data) + else: + __folder_adapter.data = current_folder + connect("item_activated", self, "_on_item_activated") GDBUX.connect_static("favourite_places_changed", self, "_on_favourite_places_changed") @@ -44,8 +53,6 @@ func _ready(): ################# func __update_items(): clear() - if !adapter: - return match mode: Mode.DEFAULT: @@ -54,7 +61,7 @@ func __update_items(): __add_custom_places() func __add_default_places(): - for default_place in __DEFAULT_ITEMS: + for default_place in __get_default_places(): var folder := adapter._get_file(default_place.place) if !folder || !folder._is_folder(): continue @@ -75,7 +82,7 @@ func __add_custom_places(): var icon : Texture = icons[i] if i < icons.size() else null __add_place(folder, icon, label) -func __add_place(folder : UIB_FileAdapter.FileEntry, icon : Texture, label := ""): +func __add_place(folder : UIB_FilesystemAdapter.FileEntry, icon : Texture, label := ""): if !icon: icon = preload("res://addons/de.mewin.gduibasics/images/folder32.svg") if !label: @@ -89,6 +96,14 @@ func __select_current_folder(): if get_item_metadata(i)._get_path() == current_folder._get_path(): select(i) +func __get_default_places() -> Array: + var places := [ + __DefaultItem.new(GDBFsUtil.get_home_folder(), preload("res://addons/de.mewin.gduibasics/images/home32.svg"), tr("Home")) + ] + for special_folder in GDBFsUtil.get_special_folders(): + places.append(__DefaultItem.new(special_folder.path, preload("res://addons/de.mewin.gduibasics/images/folder32.svg"), special_folder.name)) + return places + ########### # setters # ########### @@ -106,6 +121,8 @@ func _set_current_folder(value): if value != current_folder: current_folder = value emit_signal("current_folder_changed") + if __folder_adapter: + __folder_adapter.data = current_folder ############ # handlers # @@ -116,6 +133,10 @@ func _on_item_activated(idx): func _on_favourite_places_changed(): if mode == Mode.DEFAULT: __update_items() + +func _on_folder_adapter_data_changed() -> void: + _set_current_folder(__folder_adapter.data) + ########### # signals # ########### diff --git a/scripts/types/controls/property_check_box.gd b/scripts/types/controls/property_check_box.gd new file mode 100644 index 0000000..8bd8ec1 --- /dev/null +++ b/scripts/types/controls/property_check_box.gd @@ -0,0 +1,51 @@ +extends CheckBox + +class_name UIB_PropertyCheckBox + +var property : Object = null setget _set_property + +############# +# overrides # +############# +func _pressed() -> void: + if property == null: + return + property.set_value(pressed) + +################# +# private stuff # +################# +func __init_property() -> void: + text = __text_from_property() + pressed = property.get_value() if property != null else false + disabled = (property == null) + + if property != null: + GDBUtility.try_connect(property, "value_changed", self, "_on_property_value_changed") + +func __text_from_property() -> String: + if property == null: + return "" + return property.get_name() + +########### +# setters # +########### +func _set_property(property_ : Object) -> void: + if property_ != null && (!GDB_Property.is_valid_property(property_) || property_.get_type() != TYPE_BOOL): + printerr("Attempting to set invalid property in UIB_PropertyCheckBox.") + return + + if property_ != property: + if property: + GDBUtility.disconnect_all(property, self) + property = property_ + __init_property() + +############ +# handlers # +############ +func _on_property_value_changed() -> void: + var value = property.get_value() + if value != pressed: + pressed = value diff --git a/scripts/types/controls/property_list.gd b/scripts/types/controls/property_list.gd new file mode 100644 index 0000000..5f9828d --- /dev/null +++ b/scripts/types/controls/property_list.gd @@ -0,0 +1,169 @@ +extends UIB_DynamicGridContainer + +class_name UIB_PropertyList + +export(NodePath) var entity_adapter := "" + +var entity : Object = null setget _set_entity +var _properties := [].duplicate() +var __controls_per_property := [].duplicate() +onready var __entity_adapter := get_node_or_null(entity_adapter) as GDB_DataAdapter + +############# +# overrides # +############# +func _ready() -> void: + self.columns = 2 + + if __entity_adapter != null: + __entity_adapter.connect("data_changed", self, "_on_entity_adapter_data_changed") + +################ +# overridables # +################ +func _make_controls(property : Object) -> Array: + match property.get_type(): + GDB_Property.TYPE_HEADER: + return _make_header(property) + TYPE_INT, TYPE_REAL, TYPE_STRING: + return _make_controls_simple(property) + TYPE_BOOL: + return _make_controls_bool(property) + _: + return [] + +func _make_header(property : Object) -> Array: + assert(property.get_type() == GDB_Property.TYPE_HEADER) + var header := preload("res://addons/de.mewin.gduibasics/scenes/property_list/header.tscn").instance() + header.label = property.get_name() + header.texture = GDB_Property.get_prop_icon(property) + get_constraints(header).colspan = self.columns + return [header] + +func _make_controls_simple(property : Object) -> Array: + var label := preload("res://addons/de.mewin.gduibasics/scenes/property_list/property_label.tscn").instance() + label.label = property.get_name() + label.texture = GDB_Property.get_prop_icon(property) + + var edit := UIB_SimplePropertyEdit.new() + edit.property = property + edit.size_flags_horizontal |= SIZE_EXPAND + + return [ + label, edit + ] + +func _make_controls_bool(property : Object) -> Array: + var checkbox := UIB_PropertyCheckBox.new() + checkbox.property = property + checkbox.size_flags_horizontal |= SIZE_EXPAND + get_constraints(checkbox).colspan = self.columns + return [ + checkbox + ] + +################ +# public stuff # +################ +func add_property(property : Object, insert_before := -1) -> void: # no type to allow ducktyping + if find_property(property) >= 0: + return + if !GDB_Property.is_valid_property(property): + printerr("Attempting to add invalid property object to property list: ", property) + return + + var control_insert_index := -1 + var insert_index : int + + if insert_before < 0: + insert_index = _properties.size() + else: + insert_index = min(insert_index, _properties.size()) + control_insert_index = __find_control_index_before(insert_index) + _properties.insert(insert_index, property) + + # create controls + var controls := _make_controls(property) + __controls_per_property.insert(insert_index, controls) + + for control in controls: + add_child(control) + + if control_insert_index >= 0: + for control in controls: + move_child(control, control_insert_index) + control_insert_index += 1 + + emit_signal("property_added", insert_index) + +func remove_property(property : Object) -> bool: + var index := find_property(property) + if index < 0: + return false + + # remove actual property + GDBUtility.disconnect_all(property, self) + _properties.remove(index) + + # remove controls + for control in __controls_per_property[index]: + control.queue_free() + __controls_per_property.remove(index) + + emit_signal("property_removed", property) + return true + +func find_property(property : Object) -> int: + return _properties.find(property) + +func clear_properties() -> void: + _properties.clear() + __controls_per_property.clear() + + for child in get_children(): + child.queue_free() + +################# +# private stuff # +################# +func __find_control_index_before(index : int) -> int: + if index >= __controls_per_property.size(): + return -1 + var controls : Array = __controls_per_property[index] + if controls.empty(): + return __find_control_index_before(index + 1) + return controls[0].get_position_in_parent() + +func __init_entity() -> void: + clear_properties() + if entity == null: + return + + for property in entity.get_properties(): + add_property(property) + +########### +# setters # +########### +func _set_entity(entity_ : Object) -> void: + if entity_ != null && !GDB_Entity.is_valid_entity(entity_): + printerr("Attempting to set invalid entity in UIB_PropertyList.") + return + + if entity_ != entity: + entity = entity_ + if __entity_adapter != null: + __entity_adapter.data = entity + __init_entity() + +############ +# handlers # +############ +func _on_entity_adapter_data_changed() -> void: + _set_entity(__entity_adapter.data) + +########### +# signals # +########### +signal property_added(index) +signal property_removed(index) diff --git a/scripts/types/controls/recent_places_list.gd b/scripts/types/controls/recent_places_list.gd index e7a29f9..bc380c6 100644 --- a/scripts/types/controls/recent_places_list.gd +++ b/scripts/types/controls/recent_places_list.gd @@ -6,6 +6,7 @@ class_name UIB_RecentPlacesList # overrides # ############# func _ready(): + mode = Mode.CUSTOM places = GDBSettings.get_value(GDBConstants.SETTING_RECENT_PLACES, []) GDBSettings.connect_static("setting_changed", self, "_on_setting_changed") diff --git a/scripts/types/controls/simple_property_edit.gd b/scripts/types/controls/simple_property_edit.gd new file mode 100644 index 0000000..3537604 --- /dev/null +++ b/scripts/types/controls/simple_property_edit.gd @@ -0,0 +1,102 @@ +extends LineEdit + +class_name UIB_SimplePropertyEdit + +var property : Object = null setget _set_property + +var __caret_pos := 0 +var __last_valid_text := "" + +############# +# overrides # +############# +func _ready() -> void: + connect("text_changed", self, "_on_text_changed") + +func _gui_input(event : InputEvent) -> void: + __caret_pos = caret_position + +################# +# private stuff # +################# +func __init_property() -> void: + text = __text_from_property() + editable = (property != null) + __validate_text() + + if property != null: + GDBUtility.try_connect(property, "value_changed", self, "_on_property_value_changed") + +func __text_from_property() -> String: + if property == null: + return "" + match property.get_type(): + TYPE_INT, TYPE_REAL: + return str(property.get_value()) + TYPE_STRING: + return property.get_value() + _: + printerr("UIB_SimplePropertyEdit: unsupported property type: %s" % GDBUtility.type_name(property.get_type())) + return "" + +func __text_to_property() -> bool: + if property == null: + return false + var value + match property.get_type(): + TYPE_INT: + value = int(text) + TYPE_REAL: + value = float(text) + TYPE_STRING: + value = text + _: + printerr("UIB_SimplePropertyEdit: unsupported property type: %s" % GDBUtility.type_name(property.get_type())) + return false + if !GDB_Property.is_prop_value_valid(property, value): + return false + property.set_value(value) + return true + +func __is_text_valid() -> bool: + if property == null: + return false + match property.get_type(): + TYPE_INT: + return text == "" || text.is_valid_integer() + TYPE_REAL: + return text == "" || text.is_valid_float() + return true + +func __validate_text() -> void: + if !__is_text_valid() || !__text_to_property(): + # var old_caret := caret_position + text = __last_valid_text + caret_position = __caret_pos + else: + __last_valid_text = text + +########### +# setters # +########### +func _set_property(property_ : Object) -> void: + if property_ != null && !GDB_Property.is_valid_property(property_): + printerr("Attempting to set invalid property in UIB_SimplePropertyEdit.") + return + + if property_ != property: + if property: + GDBUtility.disconnect_all(property, self) + property = property_ + __init_property() + +############ +# handlers # +############ +func _on_text_changed(new_text : String) -> void: + __validate_text() + +func _on_property_value_changed() -> void: + var new_text := __text_from_property() + if new_text != text: + text = new_text diff --git a/scripts/types/misc/file_adapter.gd b/scripts/types/misc/filesystem_adapter.gd similarity index 96% rename from scripts/types/misc/file_adapter.gd rename to scripts/types/misc/filesystem_adapter.gd index b7e1fc0..31dfe96 100644 --- a/scripts/types/misc/file_adapter.gd +++ b/scripts/types/misc/filesystem_adapter.gd @@ -1,6 +1,6 @@ extends Reference -class_name UIB_FileAdapter +class_name UIB_FilesystemAdapter class FileEntry: func _is_folder() -> bool: diff --git a/scripts/types/misc/local_file_adapter.gd b/scripts/types/misc/local_filesystem_adapter.gd similarity index 90% rename from scripts/types/misc/local_file_adapter.gd rename to scripts/types/misc/local_filesystem_adapter.gd index 39a4ca5..8ecfd49 100644 --- a/scripts/types/misc/local_file_adapter.gd +++ b/scripts/types/misc/local_filesystem_adapter.gd @@ -1,11 +1,11 @@ -extends UIB_FileAdapter +extends UIB_FilesystemAdapter -class_name UIB_LocalFileAdapter +class_name UIB_LocalFilesystemAdapter var __dir = Directory.new() class LocalFileEntry: - extends UIB_FileAdapter.FileEntry + extends UIB_FilesystemAdapter.FileEntry var path : String var is_link : bool @@ -30,6 +30,7 @@ class LocalFileEntry: if !is_folder: return [] var dir := Directory.new() + var file := File.new() if dir.open(path + "/") != OK: return [] if dir.list_dir_begin(true) != OK: @@ -59,7 +60,7 @@ class LocalFileEntry: modified_time = __calc_modified_time() return modified_time - func _get_parent() -> UIB_FileAdapter.FileEntry: + func _get_parent() -> UIB_FilesystemAdapter.FileEntry: var parent_path := path.get_base_dir() if parent_path && parent_path != path: return LocalFileEntry.new(parent_path, false, true) @@ -97,7 +98,7 @@ func _get_file(path : String) -> FileEntry: else: return null -func _get_root() -> UIB_FileAdapter.FileEntry: +func _get_root() -> UIB_FilesystemAdapter.FileEntry: return _get_file("/") func _get_drives() -> Array: diff --git a/styles/property_list_header.tres b/styles/property_list_header.tres new file mode 100644 index 0000000..548aff8 --- /dev/null +++ b/styles/property_list_header.tres @@ -0,0 +1,8 @@ +[gd_resource type="StyleBoxFlat" format=2] + +[resource] +bg_color = Color( 0, 0, 0, 0.627451 ) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2