diff --git a/Userland/Applications/PixelPaint/CMakeLists.txt b/Userland/Applications/PixelPaint/CMakeLists.txt index 8ce2b6851c..48dfa4d781 100644 --- a/Userland/Applications/PixelPaint/CMakeLists.txt +++ b/Userland/Applications/PixelPaint/CMakeLists.txt @@ -7,12 +7,16 @@ serenity_component( compile_gml(PixelPaintWindow.gml PixelPaintWindowGML.h pixel_paint_window_gml) compile_gml(EditGuideDialog.gml EditGuideDialogGML.h edit_guide_dialog_gml) +compile_gml(FilterGallery.gml FilterGalleryGML.h filter_gallery_gml) set(SOURCES CreateNewImageDialog.cpp CreateNewLayerDialog.cpp EditGuideDialog.cpp EditGuideDialogGML.h + FilterGallery.cpp + FilterGalleryGML.h + FilterModel.cpp Image.cpp ImageEditor.cpp Layer.cpp diff --git a/Userland/Applications/PixelPaint/FilterGallery.cpp b/Userland/Applications/PixelPaint/FilterGallery.cpp new file mode 100644 index 0000000000..38b327567b --- /dev/null +++ b/Userland/Applications/PixelPaint/FilterGallery.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021, Tobias Christiansen + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "FilterGallery.h" +#include "FilterModel.h" +#include +#include +#include +#include + +namespace PixelPaint { + +FilterGallery::FilterGallery(GUI::Window* parent_window) + : GUI::Dialog(parent_window) +{ + set_title("Filter Gallery"); + set_icon(parent_window->icon()); + resize(200, 250); + set_resizable(true); + + auto& main_widget = set_main_widget(); + if (!main_widget.load_from_gml(filter_gallery_gml)) + VERIFY_NOT_REACHED(); + + auto filter_tree = main_widget.find_descendant_of_type_named("tree_view"); + auto apply_button = main_widget.find_descendant_of_type_named("apply_button"); + auto cancel_button = main_widget.find_descendant_of_type_named("cancel_button"); + + VERIFY(filter_tree); + VERIFY(apply_button); + VERIFY(cancel_button); + + auto filter_model = FilterModel::create(); + filter_tree->set_model(filter_model); + filter_tree->expand_tree(); + + apply_button->on_click = [this](auto) { + dbgln("Click!"); + + done(ExecResult::ExecOK); + }; + + cancel_button->on_click = [this](auto) { + done(ExecResult::ExecCancel); + }; +} + +} diff --git a/Userland/Applications/PixelPaint/FilterGallery.gml b/Userland/Applications/PixelPaint/FilterGallery.gml new file mode 100644 index 0000000000..42c2823640 --- /dev/null +++ b/Userland/Applications/PixelPaint/FilterGallery.gml @@ -0,0 +1,40 @@ +@GUI::Frame { + layout: @GUI::VerticalBoxLayout { + } + + fill_with_background_color: true + + @GUI::Widget { + + layout:@GUI::HorizontalBoxLayout { + margins: [4] + } + + @GUI::TreeView { + name: "tree_view" + } + + } + + @GUI::Widget { + max_height: 24 + + layout:@GUI::HorizontalBoxLayout { + margins: [4] + } + + @GUI::Widget {} + + @GUI::Button { + name: "apply_button" + text: "Apply" + max_width: 75 + } + + @GUI::Button { + name: "cancel_button" + text: "Cancel" + max_width: 75 + } + } +} diff --git a/Userland/Applications/PixelPaint/FilterGallery.h b/Userland/Applications/PixelPaint/FilterGallery.h new file mode 100644 index 0000000000..0d3cd65a18 --- /dev/null +++ b/Userland/Applications/PixelPaint/FilterGallery.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2021, Tobias Christiansen + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace PixelPaint { + +class FilterGallery final : public GUI::Dialog { + C_OBJECT(FilterGallery); + +private: + FilterGallery(GUI::Window* parent_window); +}; + +} diff --git a/Userland/Applications/PixelPaint/FilterModel.cpp b/Userland/Applications/PixelPaint/FilterModel.cpp new file mode 100644 index 0000000000..65d7e927d0 --- /dev/null +++ b/Userland/Applications/PixelPaint/FilterModel.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021-2022, Tobias Christiansen + * Copyright (c) 2021, Mustafa Quraish + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "FilterModel.h" +#include + +namespace PixelPaint { +FilterModel::FilterModel() +{ + + auto filter_bitmap = Gfx::Bitmap::try_load_from_file("/res/icons/pixelpaint/filter.png").release_value_but_fixme_should_propagate_errors(); + m_filter_icon = GUI::Icon(filter_bitmap); +} + +GUI::ModelIndex FilterModel::index(int row, int column, const GUI::ModelIndex& parent_index) const +{ + if (!parent_index.is_valid()) { + if (static_cast(row) >= m_filters.size()) + return {}; + return create_index(row, column, &m_filters[row]); + } + auto* parent = static_cast(parent_index.internal_data()); + if (static_cast(row) >= parent->children.size()) + return {}; + auto* child = &parent->children[row]; + return create_index(row, column, child); +} + +GUI::ModelIndex FilterModel::parent_index(const GUI::ModelIndex& index) const +{ + if (!index.is_valid()) + return {}; + + auto* child = static_cast(index.internal_data()); + auto* parent = child->parent; + if (parent == nullptr) + return {}; + + if (parent->parent == nullptr) { + for (size_t row = 0; row < m_filters.size(); row++) + if (m_filters.ptr_at(row).ptr() == parent) + return create_index(row, 0, parent); + VERIFY_NOT_REACHED(); + } + for (size_t row = 0; row < parent->parent->children.size(); row++) { + FilterInfo* child_at_row = parent->parent->children.ptr_at(row).ptr(); + if (child_at_row == parent) + return create_index(row, 0, parent); + } + VERIFY_NOT_REACHED(); +} + +int FilterModel::row_count(const GUI::ModelIndex& index) const +{ + if (!index.is_valid()) + return m_filters.size(); + auto* node = static_cast(index.internal_data()); + return node->children.size(); +} + +GUI::Variant FilterModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const +{ + auto* filter = static_cast(index.internal_data()); + switch (role) { + case GUI::ModelRole::Display: + return filter->text; + case GUI::ModelRole::Icon: + if (filter->type == FilterInfo::Type::Category) + return GUI::FileIconProvider::directory_icon(); + return m_filter_icon; + default: + return {}; + } +} +} diff --git a/Userland/Applications/PixelPaint/FilterModel.h b/Userland/Applications/PixelPaint/FilterModel.h new file mode 100644 index 0000000000..54ec50b9fd --- /dev/null +++ b/Userland/Applications/PixelPaint/FilterModel.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021, Tobias Christiansen + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace PixelPaint { + +class FilterModel final : public GUI::Model { + +public: + class FilterInfo : public RefCounted { + public: + enum class Type { + Category, + Filter, + } type; + + String text; + + NonnullRefPtrVector children; + FilterInfo* parent; + + static NonnullRefPtr create_filter(String const& text, FilterInfo* parent = nullptr) + { + auto filter = adopt_ref(*new FilterInfo(Type::Filter, text, parent)); + filter->ref(); + + if (parent) + parent->children.append(filter); + return filter; + } + + static NonnullRefPtr create_category(String const& text, FilterInfo* parent = nullptr) + { + auto category = adopt_ref(*new FilterInfo(Type::Category, text, parent)); + category->ref(); + if (parent) + parent->children.append(category); + return category; + } + + FilterInfo(Type type, String text, FilterInfo* parent) + : type(type) + , text(move(text)) + , parent(parent) + { + } + }; + + static NonnullRefPtr create() + { + return adopt_ref(*new FilterModel()); + } + + virtual ~FilterModel() override {}; + + virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; + virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return 1; } + virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role) const override; + virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override; + virtual GUI::ModelIndex index(int row, int column = 0, const GUI::ModelIndex& = GUI::ModelIndex()) const override; + +private: + FilterModel(); + + NonnullRefPtrVector m_filters; + GUI::Icon m_filter_icon; +}; +} diff --git a/Userland/Applications/PixelPaint/MainWidget.cpp b/Userland/Applications/PixelPaint/MainWidget.cpp index cbd875b967..570be8eb15 100644 --- a/Userland/Applications/PixelPaint/MainWidget.cpp +++ b/Userland/Applications/PixelPaint/MainWidget.cpp @@ -10,6 +10,7 @@ #include "CreateNewImageDialog.h" #include "CreateNewLayerDialog.h" #include "EditGuideDialog.h" +#include "FilterGallery.h" #include "FilterParams.h" #include #include @@ -587,6 +588,13 @@ void MainWidget::initialize_menubar(GUI::Window& window) })); auto& filter_menu = window.add_menu("&Filter"); + + filter_menu.add_action(GUI::Action::create("Filter &Gallery", [&](auto&) { + auto dialog = PixelPaint::FilterGallery::construct(&window); + if (dialog->exec() != GUI::Dialog::ExecOK) + return; + })); + auto& spatial_filters_menu = filter_menu.add_submenu("&Spatial"); auto& edge_detect_submenu = spatial_filters_menu.add_submenu("&Edge Detect");