diff --git a/Main.tscn b/Main.tscn index 46c1d23..3acbb51 100644 --- a/Main.tscn +++ b/Main.tscn @@ -1174,8 +1174,8 @@ resizable = true mode = 0 access = 2 filters = PoolStringArray( "*.pxo ; Pixelorama Project" ) -current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama" -current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/" +current_dir = "" +current_path = "" [node name="SaveSprite" type="FileDialog" parent="."] anchor_left = 0.5 @@ -1190,10 +1190,12 @@ window_title = "Save Sprite as .pxo" resizable = true access = 2 filters = PoolStringArray( "*.pxo ; Pixelorama Project" ) -current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama" -current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/" +current_dir = "" +current_path = "" [node name="ImportSprites" parent="." instance=ExtResource( 29 )] +current_dir = "" +current_path = "" [node name="ExportDialog" parent="." instance=ExtResource( 39 )] @@ -1242,8 +1244,8 @@ dialog_text = "This is an error message!" [node name="PaletteImportFileDialog" parent="." instance=ExtResource( 37 )] filters = PoolStringArray( "*.json ; JavaScript Object Notation", "*.gpl ; Gimp Palette Library", "*.png; Portable Network Graphics" ) -current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama" -current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/" +current_dir = "" +current_path = "" [node name="LeftCursor" type="Sprite" parent="."] visible = false diff --git a/Scripts/Global.gd b/Scripts/Global.gd index c223c3a..b6a661e 100644 --- a/Scripts/Global.gd +++ b/Scripts/Global.gd @@ -7,6 +7,9 @@ enum Brush_Types {PIXEL, CIRCLE, FILLED_CIRCLE, FILE, RANDOM_FILE, CUSTOM} var root_directory := "." var window_title := "" setget title_changed # Why doesn't Godot have get_window_title()? var config_cache := ConfigFile.new() +var XDGDataPaths = preload("res://Scripts/XDGDataPaths.gd") +var directory_module : Node + # warning-ignore:unused_class_variable var loaded_locales : Array var undo_redo : UndoRedo @@ -263,12 +266,16 @@ func _ready() -> void: root_directory = OS.get_executable_path().get_base_dir() # Load settings from the config file config_cache.load("user://cache.ini") + + # The fact that root_dir is set earlier than this is important + # XDGDataDirs depends on it nyaa + directory_module = XDGDataPaths.new() undo_redo = UndoRedo.new() transparent_background = ImageTexture.new() transparent_background.create_from_image(preload("res://Assets/Graphics/Canvas Backgrounds/Transparent Background Dark.png"), 0) image_clipboard = Image.new() - + var root = get_tree().get_root() control = find_node_by_name(root, "Control") top_menu_container = find_node_by_name(control, "TopMenuContainer") diff --git a/Scripts/Import.gd b/Scripts/Import.gd index f37ea48..f60bbb3 100644 --- a/Scripts/Import.gd +++ b/Scripts/Import.gd @@ -1,17 +1,224 @@ extends Node -func import_brushes(path : String) -> void: - var brushes_dir := Directory.new() - brushes_dir.open(Global.root_directory) - if !brushes_dir.dir_exists(path): - brushes_dir.make_dir(path) +# Get hold of the brushes, including random brushes (subdirectories and % files +# in them, non % files get loaded independently.) nyaaa +# Returns a list of [ +# [non random single png files in the root subdir], +# { +# map of subdirectories to lists of files for +# the randomised brush - if a directory contains no +# randomised files then it is not included in this. +# }, +# { +# map of subdirectories to lists of files inside of them +# that are not for randomised brushes. +# } +# ] +# The separation of nonrandomised and randomised files +# in subdirectories allows different XDG_DATA_DIR overriding +# for each nyaa. +# +# Returns null if the directory gave an error opening. +# +func get_brush_files_from_directory(directory: String): # -> Array + var base_png_files := [] # list of files in the base directory + var subdirectories := [] # list of subdirectories to process. + + var randomised_subdir_files_map : Dictionary = {} + var nonrandomised_subdir_files_map : Dictionary = {} + + var main_directory : Directory = Directory.new() + var err := main_directory.open(directory) + if err != OK: + return null + + + # Build first the list of base png files and all subdirectories to + # scan later (skip navigational . and ..) + main_directory.list_dir_begin(true) + var fname : String = main_directory.get_next() + while fname != "": + if main_directory.current_is_dir(): + subdirectories.append(fname) + else: # Filter for pngs + if fname.get_extension().to_lower() == "png": + print_debug("Found brush at '%s'" % [fname]) + base_png_files.append(fname) + + # go to next + fname = main_directory.get_next() + main_directory.list_dir_end() + + # Now we iterate over subdirectories! + for subdirectory in subdirectories: + var the_directory : Directory = Directory.new() + + # Holds names of files that make this + # a component of a randomised brush ^.^ + var randomised_files := [] + + # Non-randomise-indicated image files + var non_randomised_files := [] + + the_directory.open(directory.plus_file(subdirectory)) + the_directory.list_dir_begin(true) + var curr_file := the_directory.get_next() + + while curr_file != "": + # only do stuff if we are actually dealing with a file + # and png one at that nya + if !the_directory.current_is_dir() and curr_file.get_extension().to_lower() == "png": + # if we are a random element, add + if "%" in curr_file: + randomised_files.append(curr_file) + else: + non_randomised_files.append(curr_file) + curr_file = the_directory.get_next() + + the_directory.list_dir_end() + + # Add these to the maps nyaa + if len(randomised_files) > 0: + randomised_subdir_files_map[subdirectory] = randomised_files + if len(non_randomised_files) > 0: + nonrandomised_subdir_files_map[subdirectory] = non_randomised_files + # We are done generating the maps! + return [base_png_files, randomised_subdir_files_map, nonrandomised_subdir_files_map] + + +# Add a randomised brush from the given list of files as a +# source. +# The tooltip name is what shows up on the tooltip +# and is probably in this case the name of the containing +# randomised directory. +func add_randomised_brush(fpaths : Array, tooltip_name : String) -> void: + # Attempt to load the images from the file paths. + var loaded_images : Array = [] + for filen in fpaths: + var image := Image.new() + var err := image.load(filen) + if err == OK: + image.convert(Image.FORMAT_RGBA8) + loaded_images.append(image) + print_debug("Loaded image from '%s'" % [filen]) + + # If any images were successfully loaded, then + # we create the randomised brush button, copied + # from find_brushes. + + if len(loaded_images) > 0: # actually have images + # to use. + # take initial image... + var first_image : Image = loaded_images.pop_front() + + # The index which this random brush will be at + var next_random_brush_index := Global.file_brush_container.get_child_count() + + Global.custom_brushes.append(first_image) + Global.create_brush_button(first_image, Global.Brush_Types.RANDOM_FILE, tooltip_name) + # # Process the rest + for remaining_image in loaded_images: + var brush_button = Global.file_brush_container.get_child(next_random_brush_index) + brush_button.random_brushes.append(remaining_image) + +# Add a plain brush from the given path to the list of brushes. +# Taken, again, from find_brushes +func add_plain_brush(path: String, tooltip_name: String) -> void: + var image := Image.new() + var err := image.load(path) + if err != OK: + return + # do the standard conversion thing... + image.convert(Image.FORMAT_RGBA8) + Global.custom_brushes.append(image) + Global.create_brush_button(image, Global.Brush_Types.FILE, tooltip_name) + + +# Import brushes, in priority order, from the paths in question in priority order +# i.e. with an override system +# We use a very particular override system here where, for randomised brushes +# the directories containing them get overridden, but for nonrandomised files +# (including in directories containing randomised components too), the override +# is on a file-by-file basis nyaaaa ^.^ +func import_brushes(priority_ordered_search_path: Array) -> void: + # Maps for files in the base directory (name : true) + var processed_basedir_paths : Dictionary = {} + var randomised_brush_subdirectories : Dictionary = {} + # Map from a subdirectory to a map similar to processed_basedir_files + # i.e. once a filename has been dealt with, set it to true. + var processed_subdir_paths : Dictionary = {} + + # Sets of results of get_brush_files_from_directory + var all_available_paths : Array = [] + for directory in priority_ordered_search_path: + all_available_paths.append(get_brush_files_from_directory(directory)) + + # Now to process. Note these are in order of the + # priority, as intended nyaa :) + for i in range(len(all_available_paths)): + var available_brush_file_information = all_available_paths[i] + var current_main_directory: String = priority_ordered_search_path[i] + if available_brush_file_information != null: + # The brush files in the main directory + var main_directory_file_paths : Array = available_brush_file_information[0] + # The subdirectory/list-of-randomised-brush-files + # map for this directory + var randomised_brush_subdirectory_map : Dictionary = available_brush_file_information[1] + # Map for subdirectories to non-randomised-brush files nyaa + var nonrandomised_brush_subdirectory_map : Dictionary = available_brush_file_information[2] + + # Iterate over components and do stuff with them! nyaa + # first for the main directory path... + for subfile in main_directory_file_paths: + if not (subfile in processed_basedir_paths): + add_plain_brush( + current_main_directory.plus_file(subfile), + subfile.get_basename() + ) + processed_basedir_paths[subfile] = true + + # Iterate over the randomised brush files nyaa + for randomised_subdir in randomised_brush_subdirectory_map: + if not (randomised_subdir in randomised_brush_subdirectories): + var full_paths := [] + # glue the proper path onto the single file names in the + # random brush directory data system, so they can be + # opened nya + for non_extended_path in randomised_brush_subdirectory_map[randomised_subdir]: + full_paths.append(current_main_directory.plus_file( + randomised_subdir + ).plus_file( + non_extended_path + )) + # Now load! + add_randomised_brush(full_paths, randomised_subdir) + # and mark that we are done in the overall map ^.^ + randomised_brush_subdirectories[randomised_subdir] = true + # Now to iterate over the nonrandom brush files inside directories + for nonrandomised_subdir in nonrandomised_brush_subdirectory_map: + # initialise the set-map for this one if not already present :) + if not (nonrandomised_subdir in processed_subdir_paths): + processed_subdir_paths[nonrandomised_subdir] = {} + # Get the paths within this subdirectory to check if they are + # processed or not and if not, then process them. + var relpaths_of_contained_nonrandom_brushes : Array = nonrandomised_brush_subdirectory_map[nonrandomised_subdir] + for relative_path in relpaths_of_contained_nonrandom_brushes: + if not (relative_path in processed_subdir_paths[nonrandomised_subdir]): + # We are not yet processed + var full_path : String = current_main_directory.plus_file( + nonrandomised_subdir + ).plus_file( + relative_path + ) + # Add the path with the tooltip including the directory + add_plain_brush(full_path, nonrandomised_subdir.plus_file( + relative_path + ).get_basename()) + # Mark this as a processed relpath + processed_subdir_paths[nonrandomised_subdir][relative_path] = true + - var subdirectories := find_brushes(brushes_dir, path) - for subdir_str in subdirectories: - var subdir := Directory.new() - find_brushes(subdir, "%s/%s" % [path, subdir_str]) - Global.brushes_from_files = Global.custom_brushes.size() func find_brushes(brushes_dir : Directory, path : String) -> Array: var subdirectories := [] diff --git a/Scripts/Main.gd b/Scripts/Main.gd index 8a357b3..2d03940 100644 --- a/Scripts/Main.gd +++ b/Scripts/Main.gd @@ -168,7 +168,7 @@ func _ready() -> void: Global.layers_container.get_child(0).label.text = Global.layers[0][0] Global.layers_container.get_child(0).line_edit.text = Global.layers[0][0] - Import.import_brushes("Brushes") + Import.import_brushes(Global.directory_module.get_brushes_search_path_in_order()) Global.left_color_picker.get_picker().presets_visible = false Global.right_color_picker.get_picker().presets_visible = false diff --git a/Scripts/Palette/EditPalettePopup.gd b/Scripts/Palette/EditPalettePopup.gd index cfa7df3..7e495bc 100644 --- a/Scripts/Palette/EditPalettePopup.gd +++ b/Scripts/Palette/EditPalettePopup.gd @@ -106,12 +106,31 @@ func re_index_swatches() -> void: child.index = index index += 1 +# Rename a palette, copying to user directory if necessary. +func rename_palette_file_with_priority_dirs(old_fname: String, new_fname: String) -> void: + var user_write_directory: String = Global.directory_module.get_palette_write_path() + var usrwrite_dir := Directory.new() + usrwrite_dir.open(user_write_directory) + if usrwrite_dir.file_exists(old_fname): + usrwrite_dir.rename(old_fname, new_fname) + else: + # Scan through the main system directories + var priority_dirs : Array = Global.directory_module.get_palette_search_path_in_order() + var best_clone_location = Global.palette_container.get_best_palette_file_location( + priority_dirs, + old_fname + ) + if best_clone_location != null: + usrwrite_dir.copy(best_clone_location, new_fname) + + func _on_EditPaletteSaveButton_pressed() -> void: if palette_name_edit.text != current_palette: Global.palettes.erase(current_palette) - var dir := Directory.new() - dir.open(Global.root_directory) - dir.rename("Palettes".plus_file(current_palette + ".json"), "Palettes".plus_file(palette_name_edit.text + ".json")) + rename_palette_file_with_priority_dirs( + current_palette + ".json", + palette_name_edit.text + ".json" + ) current_palette = palette_name_edit.text working_palette.name = current_palette diff --git a/Scripts/Palette/PaletteContainer.gd b/Scripts/Palette/PaletteContainer.gd index b8dca1d..ef920bf 100644 --- a/Scripts/Palette/PaletteContainer.gd +++ b/Scripts/Palette/PaletteContainer.gd @@ -2,6 +2,7 @@ extends GridContainer const palette_button = preload("res://Prefabs/PaletteButton.tscn") + var palettes_path := "Palettes" var current_palette = "Default" var from_palette : Palette @@ -154,30 +155,38 @@ func on_color_select(index : int) -> void: Global.update_right_custom_brush() func _load_palettes() -> void: - palettes_path = Global.root_directory.plus_file("Palettes") - var dir := Directory.new() - dir.open(Global.root_directory) - if not dir.dir_exists(palettes_path): - dir.make_dir(palettes_path) - - var palette_files : Array = get_palette_files(palettes_path) - - for file_name in palette_files: - var palette : Palette = Palette.new().load_from_file(palettes_path.plus_file(file_name)) - if palette: - Global.palettes[palette.name] = palette - Global.palette_option_button.add_item(palette.name) - var index: int = Global.palette_option_button.get_item_count() - 1 - Global.palette_option_button.set_item_metadata(index, palette.name) - if palette.name == "Default": - Global.palette_option_button.select(index) + Global.directory_module.ensure_xdg_user_dirs_exist() + var search_locations = Global.directory_module.get_palette_search_path_in_order() + var priority_ordered_files := get_palette_priority_file_map(search_locations) + + # Iterate backwards, so any palettes defined in default files + # get overwritten by those of the same name in user files + search_locations.invert() + priority_ordered_files.invert() + for i in range(len(search_locations)): + var base_directory : String = search_locations[i] + var palette_files : Array = priority_ordered_files[i] + for file_name in palette_files: + var palette : Palette = Palette.new().load_from_file(base_directory.plus_file(file_name)) + if palette: + Global.palettes[palette.name] = palette + Global.palette_option_button.add_item(palette.name) + var index: int = Global.palette_option_button.get_item_count() - 1 + Global.palette_option_button.set_item_metadata(index, palette.name) + if palette.name == "Default": + Global.palette_option_button.select(index) if not "Default" in Global.palettes && Global.palettes.size() > 0: - Global.control._on_PaletteOptionButton_item_selected(0) + Global.palette_container._on_PaletteOptionButton_item_selected(0) -func get_palette_files(path : String) -> Array: +# Get the palette files in a single directory. +# if it does not exist, return [] +func get_palette_files(path : String ) -> Array: var dir := Directory.new() var results = [] + + if not dir.dir_exists(path): + return [] dir.open(path) dir.list_dir_begin() @@ -186,15 +195,59 @@ func get_palette_files(path : String) -> Array: var file_name = dir.get_next() if file_name == "": break - elif not file_name.begins_with(".") && file_name.to_lower().ends_with("json"): + elif (not file_name.begins_with(".")) && file_name.to_lower().ends_with("json") && not dir.current_is_dir(): results.append(file_name) dir.list_dir_end() return results +# This returns an array of arrays, with priorities. +# In particular, it takes an array of paths to look for +# arrays in, in order of file and palette override priority +# such that the files in the first directory override the +# second, third, etc. ^.^ +# It returns an array of arrays, where each output array +# corresponds to the given input array at the same index, and +# contains the (relative to the given directory) palette files +# to load, excluding all ones already existing in higher-priority +# directories. nya +# in particular, this also means you can run backwards on the result +# so that palettes with the given palette name in the higher priority +# directories override those set in lower priority directories :) +func get_palette_priority_file_map(looking_paths: Array) -> Array: + var final_list := [] + # Holds pattern files already found + var working_file_set : Dictionary = {} + for search_directory in looking_paths: + var to_add_files := [] + var files = get_palette_files(search_directory) + # files to check + for maybe_to_add in files: + if not maybe_to_add in working_file_set: + to_add_files.append(maybe_to_add) + working_file_set[maybe_to_add] = true + + final_list.append(to_add_files) + return final_list + +# Locate the highest priority palette by the given relative filename +# If none is found in the directories, then do nothing and return +# null +func get_best_palette_file_location(looking_paths: Array, fname: String): # -> String: + var priority_fmap : Array = get_palette_priority_file_map(looking_paths) + for i in range(len(looking_paths)): + var base_path : String = looking_paths[i] + var the_files : Array = priority_fmap[i] + if the_files.has(fname): + return base_path.plus_file(fname) + + return null + func save_palette(palette_name : String, filename : String) -> void: + Global.directory_module.ensure_xdg_user_dirs_exist() var palette = Global.palettes[palette_name] - palette.save_to_file(palettes_path.plus_file(filename)) + var palettes_write_path: String = Global.directory_module.get_palette_write_path() + palette.save_to_file(palettes_write_path.plus_file(filename)) func _on_NewPaletteDialog_popup_hide() -> void: diff --git a/Scripts/XDGDataPaths.gd b/Scripts/XDGDataPaths.gd new file mode 100644 index 0000000..87d62df --- /dev/null +++ b/Scripts/XDGDataPaths.gd @@ -0,0 +1,135 @@ +extends Node + +# These are *with* the config subdirectory name +var xdg_data_home : String +var xdg_data_dirs : Array + +# These are *without* the config subdirectory name +var raw_xdg_data_home : String +var raw_xdg_data_dirs : Array + +# Default location for xdg_data_home relative to $HOME +const default_xdg_data_home_rel := ".local/share" +const default_xdg_data_dirs := ["/usr/local/share", "/usr/share"] + +const config_subdir_name := "pixelorama" + +const palettes_data_subdirectory := "Palettes" +const brushes_data_subdirectory := "Brushes" + +# Declare member variables here. Examples: +# var a = 2 +# var b = "text" + + +# Get if we should use XDG standard or not nyaaaa +func use_xdg_standard() -> bool: + # see: https://docs.godotengine.org/en/latest/getting_started/workflow/export/feature_tags.html + # return OS.has_feature("Linux") or OS.has_feature("BSD") + # Previous was unreliable and buggy >.< nyaa + return OS.get_name() == "X11" + +# Called when the node enters the scene tree for the first time. +func _ready(): + pass + +func _init(): + if use_xdg_standard(): + print("Detected system where we should use XDG basedir standard (currently Linux or BSD)") + var home := OS.get_environment("HOME") + raw_xdg_data_home = home.plus_file( + default_xdg_data_home_rel + ) + xdg_data_home = raw_xdg_data_home.plus_file( + config_subdir_name + ) + + # Create defaults + xdg_data_dirs = [] + raw_xdg_data_dirs = default_xdg_data_dirs + for default_loc in raw_xdg_data_dirs: + xdg_data_dirs.append( + default_loc.plus_file(config_subdir_name) + ) + + # Now check the XDG environment variables and if + # present, replace the defaults with them! + # See: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + # Checks the xdg data home var + if OS.has_environment("XDG_DATA_HOME"): + raw_xdg_data_home = OS.get_environment("XDG_DATA_HOME") + xdg_data_home = raw_xdg_data_home.plus_file(config_subdir_name) + # Checks the list of files var, and processes them. + if OS.has_environment("XDG_DATA_DIRS"): + var raw_env_var := OS.get_environment("XDG_DATA_DIRS") + # includes empties. + var unappended_subdirs := raw_env_var.split(":", true) + raw_xdg_data_dirs = unappended_subdirs + xdg_data_dirs = [] + for unapp_subdir in raw_xdg_data_dirs: + xdg_data_dirs.append(unapp_subdir.plus_file(config_subdir_name)) + + else: + raw_xdg_data_home = Global.root_directory + xdg_data_home = raw_xdg_data_home.plus_file(config_subdir_name) + raw_xdg_data_dirs = [] + xdg_data_dirs = [] + + + +func append_file_to_all(basepaths: Array, subpath: String) -> Array: + var res := [] + for _path in basepaths: + res.append(_path.plus_file(subpath)) + return res + + +# Get search paths in order of priority +func get_search_paths_in_order() -> Array: + return [xdg_data_home] + xdg_data_dirs + +# Gets the paths, in order of search priority, for palettes. +func get_palette_search_path_in_order() -> Array: + var base_paths := get_search_paths_in_order() + return append_file_to_all(base_paths, palettes_data_subdirectory) + +# Gets the paths, in order of search priority, for brushes. +func get_brushes_search_path_in_order() -> Array: + var base_paths := get_search_paths_in_order() + return append_file_to_all(base_paths, brushes_data_subdirectory) + + +# Get the path that we are ok to be writing palettes to: +func get_palette_write_path() -> String: + return xdg_data_home.plus_file(palettes_data_subdirectory) + +# Get the path that we are ok to be writing brushes to: +func get_brushes_write_path() -> String: + return xdg_data_home.plus_file(brushes_data_subdirectory) + +# Ensure the user xdg directories exist: +func ensure_xdg_user_dirs_exist() -> void: + var base_dir := Directory.new() + base_dir.open(raw_xdg_data_home) + # Ensure the main config directory exists. + if not base_dir.dir_exists(xdg_data_home): + base_dir.make_dir(xdg_data_home) + + var actual_data_dir := Directory.new() + actual_data_dir.open(xdg_data_home) + var palette_writing_dir := get_palette_write_path() + var brushes_writing_dir := get_brushes_write_path() + # Create the palette and brush dirs + if not actual_data_dir.dir_exists(palette_writing_dir): + print("Making directory %s" % [palette_writing_dir]) + actual_data_dir.make_dir(palette_writing_dir) + if not actual_data_dir.dir_exists(brushes_writing_dir): + print("Making directory %s" % [brushes_writing_dir]) + actual_data_dir.make_dir(brushes_writing_dir) + + + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +#func _process(delta): +# pass diff --git a/Brushes/.gdignore b/pixelorama/Brushes/.gdignore similarity index 100% rename from Brushes/.gdignore rename to pixelorama/Brushes/.gdignore diff --git a/Brushes/Godot.png b/pixelorama/Brushes/Godot.png similarity index 100% rename from Brushes/Godot.png rename to pixelorama/Brushes/Godot.png diff --git a/Brushes/Grass/%Grass1.png b/pixelorama/Brushes/Grass/%Grass1.png similarity index 100% rename from Brushes/Grass/%Grass1.png rename to pixelorama/Brushes/Grass/%Grass1.png diff --git a/Brushes/Grass/%Grass2.png b/pixelorama/Brushes/Grass/%Grass2.png similarity index 100% rename from Brushes/Grass/%Grass2.png rename to pixelorama/Brushes/Grass/%Grass2.png diff --git a/Brushes/Grass/%Grass3.png b/pixelorama/Brushes/Grass/%Grass3.png similarity index 100% rename from Brushes/Grass/%Grass3.png rename to pixelorama/Brushes/Grass/%Grass3.png diff --git a/Brushes/Grass/%Grass4.png b/pixelorama/Brushes/Grass/%Grass4.png similarity index 100% rename from Brushes/Grass/%Grass4.png rename to pixelorama/Brushes/Grass/%Grass4.png diff --git a/Brushes/Grass/%Grass5.png b/pixelorama/Brushes/Grass/%Grass5.png similarity index 100% rename from Brushes/Grass/%Grass5.png rename to pixelorama/Brushes/Grass/%Grass5.png diff --git a/Brushes/Pixelorama.png b/pixelorama/Brushes/Pixelorama.png similarity index 100% rename from Brushes/Pixelorama.png rename to pixelorama/Brushes/Pixelorama.png diff --git a/Brushes/Snow.png b/pixelorama/Brushes/Snow.png similarity index 100% rename from Brushes/Snow.png rename to pixelorama/Brushes/Snow.png diff --git a/Brushes/Star/%star1.png b/pixelorama/Brushes/Star/%star1.png similarity index 100% rename from Brushes/Star/%star1.png rename to pixelorama/Brushes/Star/%star1.png diff --git a/Brushes/Star/%star2.png b/pixelorama/Brushes/Star/%star2.png similarity index 100% rename from Brushes/Star/%star2.png rename to pixelorama/Brushes/Star/%star2.png diff --git a/Brushes/Star/%star3.png b/pixelorama/Brushes/Star/%star3.png similarity index 100% rename from Brushes/Star/%star3.png rename to pixelorama/Brushes/Star/%star3.png diff --git a/Brushes/Star/%star4.png b/pixelorama/Brushes/Star/%star4.png similarity index 100% rename from Brushes/Star/%star4.png rename to pixelorama/Brushes/Star/%star4.png diff --git a/Brushes/Star/%star5.png b/pixelorama/Brushes/Star/%star5.png similarity index 100% rename from Brushes/Star/%star5.png rename to pixelorama/Brushes/Star/%star5.png diff --git a/Brushes/Star/%star6.png b/pixelorama/Brushes/Star/%star6.png similarity index 100% rename from Brushes/Star/%star6.png rename to pixelorama/Brushes/Star/%star6.png diff --git a/Brushes/circle_blended.png b/pixelorama/Brushes/circle_blended.png similarity index 100% rename from Brushes/circle_blended.png rename to pixelorama/Brushes/circle_blended.png diff --git a/Brushes/hline_3x3.png b/pixelorama/Brushes/hline_3x3.png similarity index 100% rename from Brushes/hline_3x3.png rename to pixelorama/Brushes/hline_3x3.png diff --git a/Brushes/vline_3x3.png b/pixelorama/Brushes/vline_3x3.png similarity index 100% rename from Brushes/vline_3x3.png rename to pixelorama/Brushes/vline_3x3.png diff --git a/Palettes/.gdignore b/pixelorama/Palettes/.gdignore similarity index 100% rename from Palettes/.gdignore rename to pixelorama/Palettes/.gdignore diff --git a/Palettes/Complementary.json b/pixelorama/Palettes/Complementary.json similarity index 100% rename from Palettes/Complementary.json rename to pixelorama/Palettes/Complementary.json diff --git a/Palettes/Default.json b/pixelorama/Palettes/Default.json similarity index 100% rename from Palettes/Default.json rename to pixelorama/Palettes/Default.json diff --git a/Palettes/Monochromatic.json b/pixelorama/Palettes/Monochromatic.json similarity index 100% rename from Palettes/Monochromatic.json rename to pixelorama/Palettes/Monochromatic.json diff --git a/Palettes/Shades.json b/pixelorama/Palettes/Shades.json similarity index 100% rename from Palettes/Shades.json rename to pixelorama/Palettes/Shades.json diff --git a/Palettes/Triad.json b/pixelorama/Palettes/Triad.json similarity index 100% rename from Palettes/Triad.json rename to pixelorama/Palettes/Triad.json diff --git a/Palettes/bubblegum16.json b/pixelorama/Palettes/bubblegum16.json similarity index 100% rename from Palettes/bubblegum16.json rename to pixelorama/Palettes/bubblegum16.json