summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Rauch <felix.rauch@avenga.com>2021-06-21 16:14:27 +0200
committerAndreas Kling <kling@serenityos.org>2021-06-21 22:32:58 +0200
commit8d91dbf6c07d030d72acb060eceda1ee05ec2d5d (patch)
tree2e0f5089de2bd9c711e338d5eec1a4f88874ffc5
parent5ac94944836f79b1564b2255eaa3441bfca6ba83 (diff)
downloadserenity-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.palette28
-rw-r--r--Base/res/color-palettes/greyscale.palette16
-rw-r--r--Userland/Applications/PixelPaint/PaletteWidget.cpp174
-rw-r--r--Userland/Applications/PixelPaint/PaletteWidget.h12
-rw-r--r--Userland/Applications/PixelPaint/main.cpp26
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");