/* * Copyright (c) 2018-2020, Andreas Kling * Copyright (c) 2021, Felix Rauch * Copyright (c) 2021, Mustafa Quraish * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #include "PaletteWidget.h" #include "ImageEditor.h" #include #include #include #include #include #include #include REGISTER_WIDGET(PixelPaint, PaletteWidget); namespace PixelPaint { class ColorWidget : public GUI::Frame { C_OBJECT(ColorWidget); public: virtual ~ColorWidget() override = default; virtual Color color() { return m_color; } virtual void mousedown_event(GUI::MouseEvent& event) override { if (event.modifiers() & KeyModifier::Mod_Ctrl) { auto dialog = GUI::ColorPicker::construct(m_color, window()); if (dialog->exec() == GUI::Dialog::ExecResult::OK) { m_color = dialog->color(); auto pal = palette(); pal.set_color(ColorRole::Background, m_color); set_palette(pal); update(); } } if (event.button() == GUI::MouseButton::Primary) m_palette_widget.set_primary_color(m_color); else if (event.button() == GUI::MouseButton::Secondary) m_palette_widget.set_secondary_color(m_color); } private: explicit ColorWidget(Color color, PaletteWidget& palette_widget) : m_palette_widget(palette_widget) , m_color(color) { set_fixed_width(16); } PaletteWidget& m_palette_widget; Color m_color; }; class SelectedColorWidget : public GUI::Frame { C_OBJECT(SelectedColorWidget); public: virtual ~SelectedColorWidget() override = default; virtual void mousedown_event(GUI::MouseEvent& event) override { if (event.button() != GUI::MouseButton::Primary || !on_color_change) return; auto dialog = GUI::ColorPicker::construct(m_color, window()); if (dialog->exec() == GUI::Dialog::ExecResult::OK) on_color_change(dialog->color()); } void set_background_color(Color color) { auto pal = palette(); pal.set_color(ColorRole::Background, color); set_palette(pal); update(); m_color = color; } Function on_color_change; Color m_color = Color::White; private: SelectedColorWidget() = default; }; PaletteWidget::PaletteWidget() { set_frame_shape(Gfx::FrameShape::Panel); set_frame_shadow(Gfx::FrameShadow::Raised); set_frame_thickness(0); set_fill_with_background_color(true); set_fixed_height(35); m_secondary_color_widget = add(); m_secondary_color_widget->on_color_change = [&](auto color) { set_secondary_color(color); }; m_secondary_color_widget->set_relative_rect({ 0, 2, 60, 33 }); m_secondary_color_widget->set_fill_with_background_color(true); m_primary_color_widget = add(); m_primary_color_widget->on_color_change = [&](auto color) { set_primary_color(color); }; auto rect = Gfx::IntRect(0, 0, 35, 17).centered_within(m_secondary_color_widget->relative_rect()); m_primary_color_widget->set_relative_rect(rect); m_primary_color_widget->set_fill_with_background_color(true); m_color_container = add(); m_color_container->set_relative_rect(m_secondary_color_widget->relative_rect().right() + 2, 2, 500, 33); m_color_container->set_layout(GUI::Margins {}, 1); auto& top_color_container = m_color_container->add(); top_color_container.set_name("top_color_container"); top_color_container.set_layout(GUI::Margins {}, 1); auto& bottom_color_container = m_color_container->add(); bottom_color_container.set_name("bottom_color_container"); bottom_color_container.set_layout(GUI::Margins {}, 1); auto result = load_palette_path("/res/color-palettes/default.palette"); if (result.is_error()) { GUI::MessageBox::show_error(window(), DeprecatedString::formatted("Loading default palette failed: {}", result.error())); display_color_list(fallback_colors()); return; } display_color_list(result.value()); } void PaletteWidget::set_image_editor(ImageEditor* editor) { m_editor = editor; if (!m_editor) return; set_primary_color(editor->primary_color()); set_secondary_color(editor->secondary_color()); } void PaletteWidget::set_primary_color(Color color) { if (m_editor) m_editor->set_primary_color(color); m_primary_color_widget->set_background_color(color); } void PaletteWidget::set_secondary_color(Color color) { if (m_editor) m_editor->set_secondary_color(color); m_secondary_color_widget->set_background_color(color); } void PaletteWidget::display_color_list(Vector 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("top_color_container"); top_color_container.remove_all_children(); auto& bottom_color_container = *m_color_container->find_descendant_of_type_named("bottom_color_container"); bottom_color_container.remove_all_children(); auto add_color_widget = [&](GUI::Widget& container, Color color) { auto& color_widget = container.add(color, *this); color_widget.set_fill_with_background_color(true); color_widget.set_fixed_size(16, 16); 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 PaletteWidget::colors() { Vector colors; for (auto& color_container : m_color_container->child_widgets()) { color_container.for_each_child_of_type([&](auto& color_widget) { colors.append(color_widget.color()); return IterationDecision::Continue; }); } return colors; } ErrorOr> PaletteWidget::load_palette_file(NonnullOwnPtr file) { Vector palette; Array buffer; auto buffered_file = TRY(Core::BufferedFile::create(move(file))); while (TRY(buffered_file->can_read_line())) { auto line = TRY(buffered_file->read_line(buffer)); 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()); } if (palette.is_empty()) return Error::from_string_literal("The palette file did not contain any usable colors"); return palette; } ErrorOr> PaletteWidget::load_palette_path(DeprecatedString const& file_path) { auto file = TRY(Core::File::open(file_path, Core::File::OpenMode::Read)); return load_palette_file(move(file)); } ErrorOr PaletteWidget::save_palette_file(Vector palette, NonnullOwnPtr file) { for (auto& color : palette) { TRY(file->write_entire_buffer(color.to_deprecated_string_without_alpha().bytes())); TRY(file->write_entire_buffer({ "\n", 1 })); } return {}; } Vector PaletteWidget::fallback_colors() { Vector fallback_colors; fallback_colors.append(Color::from_rgb(0x000000)); fallback_colors.append(Color::from_rgb(0xffffff)); return fallback_colors; } }