Skip to content

Commit

Permalink
Save color palette as resources to reuse later
Browse files Browse the repository at this point in the history
  • Loading branch information
nongvantinh committed May 18, 2024
1 parent bd2300d commit 168b4f7
Show file tree
Hide file tree
Showing 11 changed files with 367 additions and 8 deletions.
16 changes: 16 additions & 0 deletions doc/classes/ColorPalette.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ColorPalette" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
A resource class for managing a palette of colors.
</brief_description>
<description>
The ColorPalette class is designed to store and manage a collection of colors. This class is useful in scenarios where a predefined set of colors is required, such as for creating themes, designing user interfaces, or managing game assets.
</description>
<tutorials>
</tutorials>
<members>
<member name="colors" type="PackedColorArray" setter="set_colors" getter="get_colors" default="PackedColorArray()">
A PackedColorArray containing the colors in the palette. This array holds the color data that can be accessed and manipulated through the set_colors and get_colors methods. By default, it initializes as an empty array.
</member>
</members>
</class>
15 changes: 15 additions & 0 deletions editor/editor_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2636,6 +2636,11 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
quick_open->popup_dialog("Script", true);
quick_open->set_title(TTR("Quick Open Script..."));

} break;
case FILE_QUICK_OPEN_PALETTE: {
quick_open->popup_dialog("ColorPalette", true);
quick_open->set_title(TTR("Quick Open Color Palette..."));

} break;
case FILE_OPEN_PREV: {
if (previous_scenes.is_empty()) {
Expand Down Expand Up @@ -3384,6 +3389,10 @@ void EditorNode::_update_file_menu_closed() {
file_menu->set_item_disabled(file_menu->get_item_index(FILE_OPEN_PREV), false);
}

void EditorNode::_palette_quick_open_dialog() {
_menu_option_confirm(FILE_QUICK_OPEN_PALETTE, false);
}

VBoxContainer *EditorNode::get_main_screen_control() {
return main_screen_vbox;
}
Expand Down Expand Up @@ -3884,6 +3893,10 @@ void EditorNode::setup_color_picker(ColorPicker *p_picker) {

p_picker->set_color_mode((ColorPicker::ColorModeType)default_color_mode);
p_picker->set_picker_shape((ColorPicker::PickerShapeType)picker_shape);

p_picker->set_quick_open_callback(callable_mp(this, &EditorNode::_palette_quick_open_dialog));
p_picker->set_palette_saved_callback(callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::update_file));
palette_file_selected_callback = callable_mp(p_picker, &ColorPicker::_palette_file_selected);
}

bool EditorNode::is_scene_open(const String &p_path) {
Expand Down Expand Up @@ -4400,6 +4413,8 @@ void EditorNode::_quick_opened() {
const String &res_path = files[i];
if (open_scene_dialog || ClassDB::is_parent_class(ResourceLoader::get_resource_type(res_path), "PackedScene")) {
open_request(res_path);
} else if (ClassDB::is_parent_class(ResourceLoader::get_resource_type(res_path), "ColorPalette")) {
palette_file_selected_callback.call_deferred(files[i]);
} else {
load_resource(res_path);
}
Expand Down
3 changes: 3 additions & 0 deletions editor/editor_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ class EditorNode : public Node {
FILE_QUICK_OPEN,
FILE_QUICK_OPEN_SCENE,
FILE_QUICK_OPEN_SCRIPT,
FILE_QUICK_OPEN_PALETTE,
FILE_OPEN_PREV,
FILE_CLOSE,
FILE_CLOSE_OTHERS,
Expand Down Expand Up @@ -423,6 +424,7 @@ class EditorNode : public Node {
Timer *editor_layout_save_delay_timer = nullptr;
Timer *scan_changes_timer = nullptr;
Button *distraction_free = nullptr;
Callable palette_file_selected_callback;

EditorBottomPanel *bottom_panel = nullptr;

Expand Down Expand Up @@ -539,6 +541,7 @@ class EditorNode : public Node {
void _export_as_menu_option(int p_idx);
void _update_file_menu_opened();
void _update_file_menu_closed();
void _palette_quick_open_dialog();

void _remove_plugin_from_enabled(const String &p_name);
void _plugin_over_edit(EditorPlugin *p_plugin, Object *p_object);
Expand Down
214 changes: 206 additions & 8 deletions scene/gui/color_picker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "scene/gui/color_mode.h"
#include "scene/gui/file_dialog.h"
#include "scene/gui/margin_container.h"
#include "scene/resources/color_palette.h"
#include "scene/resources/image_texture.h"
#include "scene/resources/style_box_flat.h"
#include "scene/resources/style_box_texture.h"
#include "scene/scene_string_names.h"
#include "scene/theme/theme_db.h"
#include "servers/display_server.h"
#include "thirdparty/misc/ok_color.h"
Expand Down Expand Up @@ -75,6 +78,7 @@ void ColorPicker::_notification(int p_what) {
_update_drop_down_arrow(btn_preset->is_pressed(), btn_preset);
_update_drop_down_arrow(btn_recent_preset->is_pressed(), btn_recent_preset);
btn_add_preset->set_icon(theme_cache.add_preset);
menu_btn->set_icon(get_theme_icon(SNAME("menu_highlight"), SNAME("TabContainer")));

btn_pick->set_custom_minimum_size(Size2(28 * theme_cache.base_scale, 0));
btn_shape->set_custom_minimum_size(Size2(28 * theme_cache.base_scale, 0));
Expand Down Expand Up @@ -476,6 +480,15 @@ void ColorPicker::set_editor_settings(Object *p_editor_settings) {
_update_presets();
_update_recent_presets();
}

void ColorPicker::set_quick_open_callback(const Callable &p_file_selected) {
quick_open_callback = p_file_selected;
}

void ColorPicker::set_palette_saved_callback(const Callable &p_palette_saved) {
palette_saved_callback = p_palette_saved;
}

#endif

HSlider *ColorPicker::get_slider(int p_idx) {
Expand Down Expand Up @@ -656,16 +669,32 @@ void ColorPicker::_update_presets() {

#ifdef TOOLS_ENABLED
if (editor_settings) {
// Rebuild swatch color buttons, keeping the add-preset button in the first position.
for (int i = 1; i < preset_container->get_child_count(); i++) {
preset_container->get_child(i)->queue_free();
}
for (const Color &preset : preset_cache) {
_add_preset_button(preset_size, preset);
palette_name->set_text(editor_settings->call(SNAME("get_project_metadata"), "color_picker", "palette_name", String()));
bool palette_edited = editor_settings->call(SNAME("get_project_metadata"), "color_picker", "palette_edited", false);
if (!palette_name->get_text().is_empty()) {
if (btn_preset->is_pressed() && preset_cache.size()) {
palette_name->show();
}

if (palette_edited) {
palette_name->set_text(vformat("%s*", palette_name->get_text().replace("*", "")));
palette_name->set_tooltip_text(ETR("The changes to this palette have not been saved to a file."));
}
}
_notification(NOTIFICATION_VISIBILITY_CHANGED);
}
#endif

// Rebuild swatch color buttons, keeping the add-preset button in the first position.
for (int i = 1; i < preset_container->get_child_count(); i++) {
preset_container->get_child(i)->queue_free();
}

presets = preset_cache;
for (const Color &preset : preset_cache) {
_add_preset_button(preset_size, preset);
}

_notification(NOTIFICATION_VISIBILITY_CHANGED);
}

void ColorPicker::_update_recent_presets() {
Expand Down Expand Up @@ -774,13 +803,88 @@ void ColorPicker::_add_recent_preset_button(int p_size, const Color &p_color) {
btn_preset_new->connect("toggled", callable_mp(this, &ColorPicker::_recent_preset_pressed).bind(btn_preset_new));
}

void ColorPicker::_load_palette() {
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("ColorPalette", &extensions);

file_dialog->set_title(RTR("Load Color Palette"));
file_dialog->clear_filters();
for (const String &K : extensions) {
file_dialog->add_filter("*." + K);
}

file_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE);
file_dialog->set_current_file("");
file_dialog->popup_centered_ratio();
}

void ColorPicker::_save_palette() {
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("ColorPalette", &extensions);

file_dialog->set_title(RTR("Save Color Palette"));
file_dialog->clear_filters();
for (const String &K : extensions) {
file_dialog->add_filter("*." + K);
}

file_dialog->set_file_mode(FileDialog::FILE_MODE_SAVE_FILE);
file_dialog->set_current_file("new_palette.tres");
file_dialog->popup_centered_ratio();
}

void ColorPicker::_palette_file_selected(const String &p_path) {
switch (file_dialog->get_file_mode()) {
case FileDialog::FileMode::FILE_MODE_OPEN_FILE: {
Ref<ColorPalette> palette = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE);
ERR_FAIL_COND_MSG(palette.is_null(), vformat("Cannot open color palette file for reading at: %s", p_path));
preset_cache.clear();
presets.clear();

PackedColorArray saved_presets = palette->get_colors();
for (const Color &saved_preset : saved_presets) {
preset_cache.push_back(saved_preset);
presets.push_back(saved_preset);
}
} break;
case FileDialog::FileMode::FILE_MODE_SAVE_FILE: {
ColorPalette *palette = memnew(ColorPalette);
palette->set_colors(get_presets());
Error error = ResourceSaver::save(palette, p_path);
ERR_FAIL_COND_MSG(error != Error::OK, vformat("Cannot open color palette file for writing at: %s", p_path));
if (palette_saved_callback.is_valid()) {
palette_saved_callback.call_deferred(p_path);
}
} break;
default:
break;
}

palette_name->set_text(p_path.get_file().get_basename());
palette_name->set_tooltip_text("");
btn_preset->set_pressed(true);

#ifdef TOOLS_ENABLED
if (editor_settings) {
editor_settings->call(SNAME("set_project_metadata"), "color_picker", "palette_name", palette_name->get_text());
editor_settings->call(SNAME("set_project_metadata"), "color_picker", "palette_edited", false);
}
#endif
_update_presets();
}

void ColorPicker::_show_hide_preset(const bool &p_is_btn_pressed, Button *p_btn_preset, Container *p_preset_container) {
if (p_is_btn_pressed) {
p_preset_container->show();
} else {
p_preset_container->hide();
}
_update_drop_down_arrow(p_is_btn_pressed, p_btn_preset);

palette_name->hide();
if (btn_preset->is_pressed() && !palette_name->get_text().is_empty()) {
palette_name->show();
}
}

void ColorPicker::_update_drop_down_arrow(const bool &p_is_btn_pressed, Button *p_btn_preset) {
Expand Down Expand Up @@ -857,10 +961,14 @@ void ColorPicker::add_preset(const Color &p_color) {
_add_preset_button(_get_preset_size(), p_color);
}

palette_name->set_text(vformat("%s*", palette_name->get_text().replace("*", "")));
palette_name->set_tooltip_text(ETR("The changes to this palette have not been saved to a file."));

#ifdef TOOLS_ENABLED
if (editor_settings) {
PackedColorArray arr_to_save = get_presets();
editor_settings->call(SNAME("set_project_metadata"), "color_picker", "presets", arr_to_save);
editor_settings->call(SNAME("set_project_metadata"), "color_picker", "palette_edited", true);
}
#endif
}
Expand Down Expand Up @@ -901,12 +1009,24 @@ void ColorPicker::erase_preset(const Color &p_color) {
}
}

palette_name->set_text(vformat("%s*", palette_name->get_text().replace("*", "")));
palette_name->set_tooltip_text(ETR("The changes to this palette have not been saved to a file."));

#ifdef TOOLS_ENABLED
if (editor_settings) {
PackedColorArray arr_to_save = get_presets();
editor_settings->call(SNAME("set_project_metadata"), "color_picker", "presets", arr_to_save);
editor_settings->call(SNAME("set_project_metadata"), "color_picker", "palette_edited", true);
if (preset_cache.is_empty()) {
palette_name->hide();
editor_settings->call(SNAME("set_project_metadata"), "color_picker", "palette_name", "");
}
}
#endif
if (preset_cache.is_empty()) {
palette_name->set_text("");
palette_name->hide();
}
}
}

Expand Down Expand Up @@ -1531,6 +1651,56 @@ void ColorPicker::_pick_finished() {
picker_window->hide();
}

void ColorPicker::_update_menu_items() {
options_menu->clear();
options_menu->reset_size();

if (preset_cache.size()) {
options_menu->add_icon_item(get_theme_icon(SNAME("save"), SNAME("FileDialog")), RTR("Save"), MENU_SAVE);
options_menu->set_item_tooltip(-1, ETR("Save the current color palette to reuse later."));
}
options_menu->add_icon_item(get_theme_icon(SNAME("load"), SNAME("FileDialog")), RTR("Load"), MENU_LOAD);
options_menu->set_item_tooltip(-1, ETR("Load existing color palette."));

if (Engine::get_singleton()->is_editor_hint()) {
options_menu->add_icon_item(get_theme_icon(SNAME("load"), SNAME("FileDialog")), RTR("Quick Load"), MENU_QUICKLOAD);
options_menu->set_item_tooltip(-1, ETR("Load existing color palette."));
}
}

void ColorPicker::_update_menu() {
_update_menu_items();
Rect2 gt = menu_btn->get_screen_rect();
menu_btn->reset_size();
int min_size = menu_btn->get_minimum_size().width;
Vector2 popup_pos = gt.get_end() - Vector2(min_size, 0);
options_menu->set_position(popup_pos);
options_menu->popup();
}

void ColorPicker::_options_menu_cbk(int p_which) {
switch (p_which) {
case MENU_SAVE:
_save_palette();
break;
case MENU_LOAD:
_load_palette();
break;

#ifdef TOOLS_ENABLED
case MENU_QUICKLOAD:
if (quick_open_callback.is_valid()) {
file_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE);
quick_open_callback.call_deferred();
}
break;
#endif // TOOLS_ENABLED

default:
break;
}
}

void ColorPicker::_pick_button_pressed_legacy() {
if (!is_inside_tree()) {
return;
Expand Down Expand Up @@ -1811,6 +1981,12 @@ void ColorPicker::_bind_methods() {
}

ColorPicker::ColorPicker() {
file_dialog = memnew(FileDialog);
add_child(file_dialog, false, INTERNAL_MODE_FRONT);
file_dialog->connect("file_selected", callable_mp(this, &ColorPicker::_palette_file_selected));
file_dialog->set_current_dir(Engine::get_singleton()->is_editor_hint() ? "res://" : "user://");
file_dialog->set_access(FileDialog::ACCESS_FILESYSTEM);

internal_margin = memnew(MarginContainer);
add_child(internal_margin, false, INTERNAL_MODE_FRONT);

Expand Down Expand Up @@ -1993,14 +2169,36 @@ ColorPicker::ColorPicker() {

preset_group.instantiate();

HBoxContainer *palette_box = memnew(HBoxContainer);
palette_box->set_h_size_flags(SIZE_EXPAND_FILL);
real_vbox->add_child(palette_box);

btn_preset = memnew(Button);
btn_preset->set_text("Swatches");
btn_preset->set_flat(true);
btn_preset->set_toggle_mode(true);
btn_preset->set_focus_mode(FOCUS_NONE);
btn_preset->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
btn_preset->connect("toggled", callable_mp(this, &ColorPicker::_show_hide_preset).bind(btn_preset, preset_container));
real_vbox->add_child(btn_preset);
palette_box->add_child(btn_preset);

HBoxContainer *padding_box = memnew(HBoxContainer);
padding_box->set_h_size_flags(SIZE_EXPAND_FILL);
palette_box->add_child(padding_box);

menu_btn = memnew(Button);
menu_btn->set_flat(true);
menu_btn->set_tooltip_text(ETR("Show all options available."));
menu_btn->set_focus_mode(FOCUS_NONE);
menu_btn->connect(SceneStringName(pressed), callable_mp(this, &ColorPicker::_update_menu));
palette_box->add_child(menu_btn);
options_menu = memnew(PopupMenu);
add_child(options_menu);
options_menu->connect("id_pressed", callable_mp(this, &ColorPicker::_options_menu_cbk));

palette_name = memnew(Label);
palette_name->hide();
real_vbox->add_child(palette_name);

real_vbox->add_child(preset_container);

Expand Down

0 comments on commit 168b4f7

Please sign in to comment.