Skip to content

Commit

Permalink
Save color palette as resources to reuse later
Browse files Browse the repository at this point in the history
The lack of a palette library slows down development time because swatches contain many colors that may not match the mood an artist is trying to achieve. This can hinder their workflow as they search for the right color within a large set of mostly irrelevant options.
  • Loading branch information
nongvantinh committed May 15, 2024
1 parent 557f63d commit f9b68fc
Show file tree
Hide file tree
Showing 8 changed files with 348 additions and 8 deletions.
220 changes: 212 additions & 8 deletions scene/gui/color_picker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,14 @@
#include "core/math/color.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_quick_open.h"
#endif
#include "scene/gui/color_mode.h"
#include "scene/gui/file_dialog.h"
#include "scene/gui/margin_container.h"
#include "scene/resources/image_texture.h"
#include "scene/resources/palette.h"
#include "scene/resources/style_box_flat.h"
#include "scene/resources/style_box_texture.h"
#include "scene/theme/theme_db.h"
Expand Down Expand Up @@ -75,6 +80,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 @@ -656,16 +662,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 +796,89 @@ 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("Palette", &extensions);

file_dialog->set_title(RTR("Load 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("Palette", &extensions);

file_dialog->set_title(RTR("Save 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.res");
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<Palette> 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: {
Palette *palette = memnew(Palette);
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));
#ifdef TOOLS_ENABLED
EditorFileSystem::get_singleton()->update_file(p_path);
#endif // TOOLS_ENABLED

} 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 +955,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 +1003,23 @@ 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->hide();
}
}
}

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

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

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."));

#ifdef TOOLS_ENABLED
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."));
#endif
}

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) {
quick_open = memnew(EditorQuickOpen);
add_child(quick_open);
quick_open->connect("quick_open", callable_mp(this, &ColorPicker::_file_quick_selected));
}

quick_open->popup_dialog("Palette");
quick_open->set_title(RTR("Palette"));

break;
#endif // TOOLS_ENABLED

default:
break;
}
}

#ifdef TOOLS_ENABLED

void ColorPicker::_file_quick_selected() {
file_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE);
_palette_file_selected(quick_open->get_selected());
}
#endif // TOOLS_ENABLED

void ColorPicker::_pick_button_pressed_legacy() {
if (!is_inside_tree()) {
return;
Expand Down Expand Up @@ -1811,6 +1986,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 +2174,37 @@ 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("pressed", callable_mp(this, &ColorPicker::_update_menu));
palette_box->add_child(menu_btn);
options_menu = memnew(PopupMenu);
//options_menu->add_theme_constant_override("icon_max_width", get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)));
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
27 changes: 27 additions & 0 deletions scene/gui/color_picker.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ class ColorModeRGB;
class ColorModeHSV;
class ColorModeRAW;
class ColorModeOKHSL;
class FileDialog;
#ifdef TOOLS_ENABLED
class EditorQuickOpen;
#endif // TOOLS_ENABLED

class ColorPresetButton : public BaseButton {
GDCLASS(ColorPresetButton, BaseButton);
Expand Down Expand Up @@ -110,6 +114,12 @@ class ColorPicker : public VBoxContainer {
static const int SLIDER_COUNT = 4;

private:
enum MenuOption {
MENU_SAVE,
MENU_LOAD,
MENU_QUICKLOAD,
};

static Ref<Shader> wheel_shader;
static Ref<Shader> circle_shader;
static Ref<Shader> circle_ok_color_shader;
Expand All @@ -134,6 +144,12 @@ class ColorPicker : public VBoxContainer {
Label *picker_preview_label = nullptr;
Ref<StyleBoxFlat> picker_preview_style_box;
Color picker_color;
FileDialog *file_dialog = nullptr;
Button *menu_btn = nullptr;
PopupMenu *options_menu = nullptr;
#ifdef TOOLS_ENABLED
EditorQuickOpen *quick_open = nullptr;
#endif // TOOLS_ENABLED

MarginContainer *internal_margin = nullptr;
Control *uv_edit = nullptr;
Expand All @@ -149,6 +165,7 @@ class ColorPicker : public VBoxContainer {
HBoxContainer *recent_preset_hbc = nullptr;
Button *btn_add_preset = nullptr;
Button *btn_pick = nullptr;
Label *palette_name = nullptr;
Button *btn_preset = nullptr;
Button *btn_recent_preset = nullptr;
PopupMenu *shape_popup = nullptr;
Expand Down Expand Up @@ -279,13 +296,23 @@ class ColorPicker : public VBoxContainer {
void _html_focus_exit();
void _pick_button_pressed();
void _pick_finished();
void _update_menu_items();
void _update_menu();
void _options_menu_cbk(int p_which);
#ifdef TOOLS_ENABLED
void _file_quick_selected();
#endif // TOOLS_ENABLED

// Legacy color picking.
void _pick_button_pressed_legacy();
void _picker_texture_input(const Ref<InputEvent> &p_event);

inline int _get_preset_size();
void _add_preset_button(int p_size, const Color &p_color);
void _add_recent_preset_button(int p_size, const Color &p_color);
void _save_palette();
void _load_palette();
void _palette_file_selected(const String &p_path);

void _show_hide_preset(const bool &p_is_btn_pressed, Button *p_btn_preset, Container *p_preset_container);
void _update_drop_down_arrow(const bool &p_is_btn_pressed, Button *p_btn_preset);
Expand Down
2 changes: 2 additions & 0 deletions scene/register_scene_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
#include "scene/resources/multimesh.h"
#include "scene/resources/navigation_mesh.h"
#include "scene/resources/packed_scene.h"
#include "scene/resources/palette.h"
#include "scene/resources/particle_process_material.h"
#include "scene/resources/physics_material.h"
#include "scene/resources/placeholder_textures.h"
Expand Down Expand Up @@ -961,6 +962,7 @@ void register_scene_types() {
GDREGISTER_CLASS(FontFile);
GDREGISTER_CLASS(FontVariation);
GDREGISTER_CLASS(SystemFont);
GDREGISTER_CLASS(Palette);

GDREGISTER_CLASS(Curve);

Expand Down

0 comments on commit f9b68fc

Please sign in to comment.