@tool extends Control # TODO: Remember which folders are minimized/expanded for favorites # TODO: when settings are expanded, and plugin dock width is short, wrapping is weird. fix # TODO: Add expand all/minimize all buttons # TODO: Add default texture/tooltip for preview not found # TODO: If a subpalette has no scenes, don't show it # TODO: supress warnings # TODO: Some 2D scenes are not being instantiated for some reason # TODO: Add favorite groupings # TODO: Add ability to rearrange favorites # TODO: Shrink font size on buttons with long names to show more of the name @export_category("Sub Scenes") @export var subpalette_scene:PackedScene @export var scene_drop_scene:PackedScene @export var fav_button_scene:PackedScene var top_level_sub_palette:PalettePluginSubPalette # containers @onready var favorites_bar = %FavoritesBar @onready var favorites_settings = %FavoritesSettings @onready var scroll_container = %ScrollContainer @onready var settings_container = %SettingsContainer # buttons, etc @onready var file_dialog = %FileDialog @onready var choose_directory_button = %ChooseDirectoryButton @onready var save_dir_to_favorites = %SaveDirToFavorites @onready var instantiate_for_preview_button = %UsePreviewCheckButton @onready var icon_scene_scale_slider = %IconSceneScaleSlider @onready var show_scene_label_button = %ShowSceneLabelButton @onready var supported_file_type_label: Label = %SupportedFileTypeLabel @onready var allow_file_types_button: CheckButton = %AllowFileTypesButton # have save data to remember favorite palettes const save_data_dir = "res://addons/scene_palette/save_data/" const save_data_path = save_data_dir + "save_data.tres" const pp = 'ScenePalettePlugin: ' # prepended to any printed messages const allowed_file_types = ['tscn', 'png', 'gltf', 'glb', 'fbx', 'obj'] # signals to scene drops to indicate setting changes signal scene_scale_changed(amt:float) signal show_scene_label_toggled(toggled_on:bool) var _current_dir: set(value): _current_dir = value # remove top level palette for child in scroll_container.get_children(): child.queue_free() # create new top level palette top_level_sub_palette = subpalette_scene.instantiate() top_level_sub_palette.directory = _current_dir scroll_container.add_child(top_level_sub_palette) var palette_title = _current_dir.split('/')[-1] top_level_sub_palette.set_title(palette_title) top_level_sub_palette.expandable = false # all subpalettes can be minimized, but not the top level # default settings for a new directory var toggle_on:bool = false var scene_scale:float = 1 # if we are navigating to a favorite, load saved settings for it if _current_dir_in_favorites(): var save_data:PalettePluginSaveData = _get_save_data() var favorite = save_data.favorites[_current_dir] top_level_sub_palette.set_color(favorite.color) toggle_on = favorite.instantiate_scenes_for_previews scene_scale = favorite.get('scene_preview_scale', scene_scale) show_scene_label_button.button_pressed = favorite.get('show_labels', false) allow_file_types_button.button_pressed = favorite.get('allow_nonscene_files', false) else: icon_scene_scale_slider.value = 1 instantiate_for_preview_button.button_pressed = toggle_on _populate_scenes(top_level_sub_palette, _current_dir) show_scene_label_toggled.emit(show_scene_label_button.button_pressed) await get_tree().create_timer(0.05).timeout # ¯\_(ツ)_/¯ don't work without it icon_scene_scale_slider.value = scene_scale func _reload_palette(): _current_dir = _current_dir ## Recursively create subpalettes for the specified directory func _populate_scenes(sub_palette:PalettePluginSubPalette, dir_path:String): var dir = DirAccess.open(dir_path) if dir: for dir_name in dir.get_directories(): var new_sub_palette:PalettePluginSubPalette = subpalette_scene.instantiate() var path = dir_path + '/' + dir_name sub_palette.add_subpalette(new_sub_palette) new_sub_palette.directory = path new_sub_palette.set_title(dir_name) _populate_scenes(new_sub_palette, path) for file_name in dir.get_files(): var file_extension:String = file_name.split('.')[-1] var all_file_types_allowed:bool = allow_file_types_button.button_pressed if file_extension == 'tscn' or (all_file_types_allowed and file_extension in allowed_file_types): var scene_drop:PalettePluginSceneDrop = scene_drop_scene.instantiate() sub_palette.add_item(scene_drop) scene_drop.instantiate_scene_preview = instantiate_for_preview_button.button_pressed scene_scale_changed.connect(scene_drop.adjust_scale) show_scene_label_toggled.connect(scene_drop.show_file_label) scene_drop.set_scene(dir_path +'/' + file_name) else: print(pp, 'No directory found for ', dir_path) func _ready(): save_dir_to_favorites.hide() settings_container.hide() _populate_favorites_tab_bar() if not FileAccess.file_exists(save_data_path): _create_new_save_data() var types_string:String = 'Supported file types: ' for type in allowed_file_types: types_string += type + ', ' types_string = types_string.left(types_string.length() - 2) # remove last comma supported_file_type_label.text = types_string func _create_new_save_data(): var data = PalettePluginSaveData.new() ResourceSaver.save(data, save_data_path) func _get_save_data() -> PalettePluginSaveData: if not FileAccess.file_exists(save_data_path): # This only happens if a user manually deletes the save_data while the plugin is enabled print(pp, 'favorites data was removed. Creating new save data.') _create_new_save_data() _populate_favorites_tab_bar() return ResourceLoader.load(save_data_path) func _save_data(data:PalettePluginSaveData): ResourceSaver.save(data, save_data_path) func _on_choose_directory_button_pressed(): file_dialog.show() func _on_file_dialog_dir_selected(dir): _current_dir = dir choose_directory_button.text = dir #.split('/')[-1] # set the tooltip in case some of the text is clipped choose_directory_button.tooltip_text = dir if not _current_dir_in_favorites(): save_dir_to_favorites.show() else: save_dir_to_favorites.hide() func _on_use_preview_check_button_toggled(toggled_on): top_level_sub_palette.instantiate_previews(toggled_on) if _current_dir_in_favorites(): var save_data = _get_save_data() save_data.favorites[_current_dir].instantiate_scenes_for_previews = toggled_on _save_data(save_data) ## Load favorites buttons func _populate_favorites_tab_bar(): var data:PalettePluginSaveData = _get_save_data() # clear existing buttons for child in favorites_bar.get_children(): # Don't remove the favorites bar header and settings button: if child is PalettePluginFavoriteButton: child.queue_free() # add new buttons for dir in data.favorites: var btn:PalettePluginFavoriteButton = fav_button_scene.instantiate() btn.favorite_selected.connect(_on_new_favorite_selected) btn.favorite_removed.connect(_on_favorite_removed) btn.favorite_color_changed.connect(_on_favorite_color_changed) favorites_bar.add_child(btn) btn.directory = dir btn.set_color(data.favorites[dir].color) btn.set_settings_visibility(favorites_settings.button_pressed) func _on_new_favorite_selected(dir:String): _on_file_dialog_dir_selected(dir) func _on_favorite_removed(dir:String): var save_data:PalettePluginSaveData = _get_save_data() save_data.favorites.erase(dir) _save_data(save_data) _populate_favorites_tab_bar() # show button to add to favorite again if we removed the current dir if dir == _current_dir: save_dir_to_favorites.show() func _on_favorite_color_changed(dir:String, color:Color): var save_data:PalettePluginSaveData = _get_save_data() save_data.favorites[dir].color = color _save_data(save_data) # show real time updates if we're looking at this directory! if _current_dir == dir: top_level_sub_palette.set_color(color) func _on_save_dir_to_favorites_pressed(): var save_data:PalettePluginSaveData = _get_save_data() if _current_dir_in_favorites(): print(pp, _current_dir, ' is already in favorites') else: save_data.add_favorite(_current_dir, instantiate_for_preview_button.button_pressed) _save_data(save_data) _populate_favorites_tab_bar() save_dir_to_favorites.hide() func _current_dir_in_favorites() -> bool: var save_data:PalettePluginSaveData = _get_save_data() return _current_dir in save_data.favorites ## toggle visibility of configuration options func _on_favorites_settings_toggled(toggled_on): for btn in favorites_bar.get_children(): if btn is PalettePluginFavoriteButton: btn.set_settings_visibility(toggled_on) settings_container.visible = toggled_on func _on_show_scene_label_button_toggled(toggled_on): show_scene_label_toggled.emit(toggled_on) var save_data:PalettePluginSaveData = _get_save_data() if _current_dir in save_data.favorites: save_data.favorites[_current_dir].show_labels = toggled_on _save_data(save_data) func _on_icon_scene_scale_slider_value_changed(value:float): scene_scale_changed.emit(value) var save_data:PalettePluginSaveData = _get_save_data() if _current_dir in save_data.favorites: save_data.favorites[_current_dir].scene_preview_scale = value _save_data(save_data) func _on_reset_scale_button_pressed(): icon_scene_scale_slider.value = 1 func _on_allow_file_types_button_toggled(toggled_on: bool) -> void: var save_data:PalettePluginSaveData = _get_save_data() if _current_dir in save_data.favorites: save_data.favorites[_current_dir].allow_nonscene_files = toggled_on _save_data(save_data) _reload_palette()