diff options
author | Felix Rauch <felix.rauch@avenga.com> | 2021-06-21 16:14:27 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-06-21 22:32:58 +0200 |
commit | 8d91dbf6c07d030d72acb060eceda1ee05ec2d5d (patch) | |
tree | 2e0f5089de2bd9c711e338d5eec1a4f88874ffc5 | |
parent | 5ac94944836f79b1564b2255eaa3441bfca6ba83 (diff) | |
download | serenity-8d91dbf6c07d030d72acb060eceda1ee05ec2d5d.zip |
PixelPaint: Add loading and saving of color palettes
Color palettes can now be stored in and read from files. The default
palette will be read from `/res/color-palettes/default.palette`
instead of being hard-coded in PaletteWidget.
The file format is one color per line, in any format that can be
understood by `Gfx::Color::from_string`.
-rw-r--r-- | Base/res/color-palettes/default.palette | 28 | ||||
-rw-r--r-- | Base/res/color-palettes/greyscale.palette | 16 | ||||
-rw-r--r-- | Userland/Applications/PixelPaint/PaletteWidget.cpp | 174 | ||||
-rw-r--r-- | Userland/Applications/PixelPaint/PaletteWidget.h | 12 | ||||
-rw-r--r-- | Userland/Applications/PixelPaint/main.cpp | 26 |
5 files changed, 214 insertions, 42 deletions
diff --git a/Base/res/color-palettes/default.palette b/Base/res/color-palettes/default.palette new file mode 100644 index 0000000000..21598d9763 --- /dev/null +++ b/Base/res/color-palettes/default.palette @@ -0,0 +1,28 @@ +#000000 +#808080 +#800000 +#808000 +#008000 +#008080 +#000080 +#800080 +#808040 +#004040 +#0080ff +#004080 +#8000ff +#804000 +#ffffff +#c0c0c0 +#ff0000 +#ffff00 +#00ff00 +#00ffff +#0000ff +#ff00ff +#ffff80 +#00ff80 +#80ffff +#8080ff +#ff0080 +#ff8040 diff --git a/Base/res/color-palettes/greyscale.palette b/Base/res/color-palettes/greyscale.palette new file mode 100644 index 0000000000..13730ad3b3 --- /dev/null +++ b/Base/res/color-palettes/greyscale.palette @@ -0,0 +1,16 @@ +#000000 +#111111 +#222222 +#333333 +#444444 +#555555 +#666666 +#777777 +#888888 +#999999 +#aaaaaa +#bbbbbb +#cccccc +#dddddd +#eeeeee +#ffffff diff --git a/Userland/Applications/PixelPaint/PaletteWidget.cpp b/Userland/Applications/PixelPaint/PaletteWidget.cpp index 794a0c2298..3ecfa084c4 100644 --- a/Userland/Applications/PixelPaint/PaletteWidget.cpp +++ b/Userland/Applications/PixelPaint/PaletteWidget.cpp @@ -1,13 +1,18 @@ /* * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2021, Felix Rauch <noreply@felixrau.ch> * * SPDX-License-Identifier: BSD-2-Clause */ #include "PaletteWidget.h" #include "ImageEditor.h" +#include <AK/Result.h> +#include <AK/Vector.h> +#include <LibCore/File.h> #include <LibGUI/BoxLayout.h> #include <LibGUI/ColorPicker.h> +#include <LibGUI/MessageBox.h> #include <LibGfx/Palette.h> REGISTER_WIDGET(PixelPaint, PaletteWidget); @@ -29,6 +34,8 @@ public: { } + virtual Color color() { return m_color; } + virtual void mousedown_event(GUI::MouseEvent& event) override { if (event.modifiers() & KeyModifier::Mod_Ctrl && event.button() == GUI::MouseButton::Left) { @@ -73,56 +80,30 @@ PaletteWidget::PaletteWidget() m_primary_color_widget->set_relative_rect(rect); m_primary_color_widget->set_fill_with_background_color(true); - auto& color_container = add<GUI::Widget>(); - color_container.set_relative_rect(m_secondary_color_widget->relative_rect().right() + 2, 2, 500, 32); - color_container.set_layout<GUI::VerticalBoxLayout>(); - color_container.layout()->set_spacing(1); + m_color_container = add<GUI::Widget>(); + m_color_container->set_relative_rect(m_secondary_color_widget->relative_rect().right() + 2, 2, 500, 32); + m_color_container->set_layout<GUI::VerticalBoxLayout>(); + m_color_container->layout()->set_spacing(1); - auto& top_color_container = color_container.add<GUI::Widget>(); + auto& top_color_container = m_color_container->add<GUI::Widget>(); + top_color_container.set_name("top_color_container"); top_color_container.set_layout<GUI::HorizontalBoxLayout>(); top_color_container.layout()->set_spacing(1); - auto& bottom_color_container = color_container.add<GUI::Widget>(); + auto& bottom_color_container = m_color_container->add<GUI::Widget>(); + bottom_color_container.set_name("bottom_color_container"); bottom_color_container.set_layout<GUI::HorizontalBoxLayout>(); bottom_color_container.layout()->set_spacing(1); - auto add_color_widget = [&](GUI::Widget& container, Color color) { - auto& color_widget = container.add<ColorWidget>(color, *this); - color_widget.set_fill_with_background_color(true); - auto pal = color_widget.palette(); - pal.set_color(ColorRole::Background, color); - color_widget.set_palette(pal); - }; + auto result = load_palette_file("/res/color-palettes/default.palette"); + if (result.is_error()) { + GUI::MessageBox::show_error(window(), String::formatted("Loading default palette failed: {}", result.error())); + display_color_list(fallback_colors()); - add_color_widget(top_color_container, Color::from_rgb(0x000000)); - add_color_widget(top_color_container, Color::from_rgb(0x808080)); - add_color_widget(top_color_container, Color::from_rgb(0x800000)); - add_color_widget(top_color_container, Color::from_rgb(0x808000)); - add_color_widget(top_color_container, Color::from_rgb(0x008000)); - add_color_widget(top_color_container, Color::from_rgb(0x008080)); - add_color_widget(top_color_container, Color::from_rgb(0x000080)); - add_color_widget(top_color_container, Color::from_rgb(0x800080)); - add_color_widget(top_color_container, Color::from_rgb(0x808040)); - add_color_widget(top_color_container, Color::from_rgb(0x004040)); - add_color_widget(top_color_container, Color::from_rgb(0x0080ff)); - add_color_widget(top_color_container, Color::from_rgb(0x004080)); - add_color_widget(top_color_container, Color::from_rgb(0x8000ff)); - add_color_widget(top_color_container, Color::from_rgb(0x804000)); - - add_color_widget(bottom_color_container, Color::from_rgb(0xffffff)); - add_color_widget(bottom_color_container, Color::from_rgb(0xc0c0c0)); - add_color_widget(bottom_color_container, Color::from_rgb(0xff0000)); - add_color_widget(bottom_color_container, Color::from_rgb(0xffff00)); - add_color_widget(bottom_color_container, Color::from_rgb(0x00ff00)); - add_color_widget(bottom_color_container, Color::from_rgb(0x00ffff)); - add_color_widget(bottom_color_container, Color::from_rgb(0x0000ff)); - add_color_widget(bottom_color_container, Color::from_rgb(0xff00ff)); - add_color_widget(bottom_color_container, Color::from_rgb(0xffff80)); - add_color_widget(bottom_color_container, Color::from_rgb(0x00ff80)); - add_color_widget(bottom_color_container, Color::from_rgb(0x80ffff)); - add_color_widget(bottom_color_container, Color::from_rgb(0x8080ff)); - add_color_widget(bottom_color_container, Color::from_rgb(0xff0080)); - add_color_widget(bottom_color_container, Color::from_rgb(0xff8040)); + return; + } + + display_color_list(result.value()); } void PaletteWidget::set_image_editor(ImageEditor& editor) @@ -162,4 +143,113 @@ void PaletteWidget::set_secondary_color(Color color) m_secondary_color_widget->update(); } +void PaletteWidget::display_color_list(Vector<Color> const& colors) +{ + int colors_to_add = colors.size(); + if (colors_to_add == 0) { + dbgln("Empty color list given. Using fallback colors."); + display_color_list(fallback_colors()); + return; + } + + auto& top_color_container = *m_color_container->find_descendant_of_type_named<GUI::Widget>("top_color_container"); + top_color_container.remove_all_children(); + + auto& bottom_color_container = *m_color_container->find_descendant_of_type_named<GUI::Widget>("bottom_color_container"); + bottom_color_container.remove_all_children(); + + auto add_color_widget = [&](GUI::Widget& container, Color color) { + auto& color_widget = container.add<ColorWidget>(color, *this); + color_widget.set_fill_with_background_color(true); + auto pal = color_widget.palette(); + pal.set_color(ColorRole::Background, color); + color_widget.set_palette(pal); + }; + + int colors_per_row = ceil(colors_to_add / 2); + int number_of_added_colors = 0; + for (auto& color : colors) { + if (number_of_added_colors < colors_per_row) + add_color_widget(top_color_container, color); + else + add_color_widget(bottom_color_container, color); + + ++number_of_added_colors; + } +} + +Vector<Color> PaletteWidget::colors() +{ + Vector<Color> colors; + + for (auto& color_container : m_color_container->child_widgets()) { + color_container.for_each_child_of_type<ColorWidget>([&](auto& color_widget) { + colors.append(color_widget.color()); + return IterationDecision::Continue; + }); + } + + return colors; +} + +Result<Vector<Color>, String> PaletteWidget::load_palette_file(String const& file_path) +{ + auto file_or_error = Core::File::open(file_path, Core::OpenMode::ReadOnly); + if (file_or_error.is_error()) + return file_or_error.error(); + + auto& file = *file_or_error.value(); + + Vector<Color> palette; + + while (file.can_read_line()) { + auto line = file.read_line(); + if (line.is_whitespace()) + continue; + + auto color = Color::from_string(line); + if (!color.has_value()) { + dbgln("Could not parse \"{}\" as a color", line); + continue; + } + + palette.append(color.value()); + } + + file.close(); + + if (palette.is_empty()) + return String { "The palette file did not contain any usable colors"sv }; + + return palette; +} + +Result<void, String> PaletteWidget::save_palette_file(Vector<Color> palette, String const& file_path) +{ + auto file_or_error = Core::File::open(file_path, Core::OpenMode::WriteOnly); + if (file_or_error.is_error()) + return file_or_error.error(); + + auto& file = *file_or_error.value(); + + for (auto& color : palette) { + file.write(color.to_string_without_alpha()); + file.write("\n"); + } + + file.close(); + + return {}; +} + +Vector<Color> PaletteWidget::fallback_colors() +{ + Vector<Color> fallback_colors; + + fallback_colors.append(Color::from_rgb(0x000000)); + fallback_colors.append(Color::from_rgb(0xffffff)); + + return fallback_colors; +} + } diff --git a/Userland/Applications/PixelPaint/PaletteWidget.h b/Userland/Applications/PixelPaint/PaletteWidget.h index 2a52627577..022909e54e 100644 --- a/Userland/Applications/PixelPaint/PaletteWidget.h +++ b/Userland/Applications/PixelPaint/PaletteWidget.h @@ -1,11 +1,14 @@ /* * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2021, Felix Rauch <noreply@felixrau.ch> * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once +#include <AK/Result.h> +#include <AK/Vector.h> #include <LibGUI/Frame.h> namespace PixelPaint { @@ -21,6 +24,14 @@ public: void set_primary_color(Color); void set_secondary_color(Color); + void display_color_list(Vector<Color> const&); + + Vector<Color> colors(); + + static Result<Vector<Color>, String> load_palette_file(String const&); + static Result<void, String> save_palette_file(Vector<Color>, String const&); + static Vector<Color> fallback_colors(); + void set_image_editor(ImageEditor&); private: @@ -29,6 +40,7 @@ private: ImageEditor* m_editor { nullptr }; RefPtr<GUI::Frame> m_primary_color_widget; RefPtr<GUI::Frame> m_secondary_color_widget; + RefPtr<GUI::Widget> m_color_container; }; } diff --git a/Userland/Applications/PixelPaint/main.cpp b/Userland/Applications/PixelPaint/main.cpp index 6aa591ad2d..76d413fa42 100644 --- a/Userland/Applications/PixelPaint/main.cpp +++ b/Userland/Applications/PixelPaint/main.cpp @@ -265,6 +265,32 @@ int main(int argc, char** argv) } }, window)); + edit_menu.add_action(GUI::Action::create( + "&Load Color Palette", [&](auto&) { + auto open_path = GUI::FilePicker::get_open_filepath(window, "Load Color Palette"); + if (!open_path.has_value()) + return; + + auto result = PixelPaint::PaletteWidget::load_palette_file(open_path.value()); + if (result.is_error()) { + GUI::MessageBox::show_error(window, String::formatted("Loading color palette failed: {}", result.error())); + return; + } + + palette_widget.display_color_list(result.value()); + }, + window)); + edit_menu.add_action(GUI::Action::create( + "Sa&ve Color Palette", [&](auto&) { + auto save_path = GUI::FilePicker::get_save_filepath(window, "untitled", "palette"); + if (!save_path.has_value()) + return; + + auto result = PixelPaint::PaletteWidget::save_palette_file(palette_widget.colors(), save_path.value()); + if (result.is_error()) + GUI::MessageBox::show_error(window, String::formatted("Writing color palette failed: {}", result.error())); + }, + window)); auto& view_menu = menubar->add_menu("&View"); |