diff --git a/Applications/PixelPaint/CMakeLists.txt b/Applications/PixelPaint/CMakeLists.txt index b5bd9c4283..52c55e48b0 100644 --- a/Applications/PixelPaint/CMakeLists.txt +++ b/Applications/PixelPaint/CMakeLists.txt @@ -7,6 +7,7 @@ set(SOURCES ImageEditor.cpp Layer.cpp LayerListWidget.cpp + LayerPropertiesWidget.cpp LineTool.cpp main.cpp MoveTool.cpp diff --git a/Applications/PixelPaint/Image.cpp b/Applications/PixelPaint/Image.cpp index d56a0fc7f7..9e945fa836 100644 --- a/Applications/PixelPaint/Image.cpp +++ b/Applications/PixelPaint/Image.cpp @@ -54,9 +54,11 @@ void Image::paint_into(GUI::Painter& painter, const Gfx::IntRect& dest_rect) Gfx::PainterStateSaver saver(painter); painter.add_clip_rect(dest_rect); for (auto& layer : m_layers) { + if (!layer.is_visible()) + continue; auto target = dest_rect.translated(layer.location().x() * scale, layer.location().y() * scale); target.set_size(layer.size().width() * scale, layer.size().height() * scale); - painter.draw_scaled_bitmap(target, layer.bitmap(), layer.rect()); + painter.draw_scaled_bitmap(target, layer.bitmap(), layer.rect(), (float)layer.opacity_percent() / 100.0f); } } @@ -174,6 +176,15 @@ void Image::layer_did_modify_bitmap(Badge, const Layer& layer) did_change(); } +void Image::layer_did_modify_properties(Badge, const Layer& layer) +{ + auto layer_index = index_of(layer); + for (auto* client : m_clients) + client->image_did_modify_layer(layer_index); + + did_change(); +} + void Image::did_change() { for (auto* client : m_clients) diff --git a/Applications/PixelPaint/Image.h b/Applications/PixelPaint/Image.h index 55bc109479..0ab8fac301 100644 --- a/Applications/PixelPaint/Image.h +++ b/Applications/PixelPaint/Image.h @@ -75,6 +75,7 @@ public: void remove_client(ImageClient&); void layer_did_modify_bitmap(Badge, const Layer&); + void layer_did_modify_properties(Badge, const Layer&); size_t index_of(const Layer&) const; diff --git a/Applications/PixelPaint/Layer.cpp b/Applications/PixelPaint/Layer.cpp index 437fa74e3f..6b91daf31e 100644 --- a/Applications/PixelPaint/Layer.cpp +++ b/Applications/PixelPaint/Layer.cpp @@ -30,7 +30,7 @@ namespace PixelPaint { -RefPtr Layer::create_with_size(const Gfx::IntSize& size, const String& name) +RefPtr Layer::create_with_size(Image& image, const Gfx::IntSize& size, const String& name) { if (size.is_empty()) return nullptr; @@ -38,11 +38,12 @@ RefPtr Layer::create_with_size(const Gfx::IntSize& size, const String& na if (size.width() > 16384 || size.height() > 16384) return nullptr; - return adopt(*new Layer(size, name)); + return adopt(*new Layer(image, size, name)); } -Layer::Layer(const Gfx::IntSize& size, const String& name) - : m_name(name) +Layer::Layer(Image& image, const Gfx::IntSize& size, const String& name) + : m_image(image) + , m_name(name) { m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, size); } @@ -52,4 +53,20 @@ void Layer::did_modify_bitmap(Image& image) image.layer_did_modify_bitmap({}, *this); } +void Layer::set_visible(bool visible) +{ + if (m_visible == visible) + return; + m_visible = visible; + m_image.layer_did_modify_properties({}, *this); +} + +void Layer::set_opacity_percent(int opacity_percent) +{ + if (m_opacity_percent == opacity_percent) + return; + m_opacity_percent = opacity_percent; + m_image.layer_did_modify_properties({}, *this); +} + } diff --git a/Applications/PixelPaint/Layer.h b/Applications/PixelPaint/Layer.h index 1c0f43daaf..e8ea62d887 100644 --- a/Applications/PixelPaint/Layer.h +++ b/Applications/PixelPaint/Layer.h @@ -29,20 +29,24 @@ #include #include #include +#include #include namespace PixelPaint { class Image; -class Layer : public RefCounted { +class Layer + : public RefCounted + , public Weakable { + AK_MAKE_NONCOPYABLE(Layer); AK_MAKE_NONMOVABLE(Layer); public: - static RefPtr create_with_size(const Gfx::IntSize&, const String& name); + static RefPtr create_with_size(Image&, const Gfx::IntSize&, const String& name); - ~Layer() {} + ~Layer() { } const Gfx::IntPoint& location() const { return m_location; } void set_location(const Gfx::IntPoint& location) { m_location = location; } @@ -62,14 +66,25 @@ public: void set_selected(bool selected) { m_selected = selected; } bool is_selected() const { return m_selected; } + bool is_visible() const { return m_visible; } + void set_visible(bool visible); + + int opacity_percent() const { return m_opacity_percent; } + void set_opacity_percent(int); + private: - explicit Layer(const Gfx::IntSize&, const String& name); + explicit Layer(Image&, const Gfx::IntSize&, const String& name); + + Image& m_image; String m_name; Gfx::IntPoint m_location; RefPtr m_bitmap; bool m_selected { false }; + bool m_visible { true }; + + int m_opacity_percent { 100 }; }; } diff --git a/Applications/PixelPaint/LayerPropertiesWidget.cpp b/Applications/PixelPaint/LayerPropertiesWidget.cpp new file mode 100644 index 0000000000..9f677e886c --- /dev/null +++ b/Applications/PixelPaint/LayerPropertiesWidget.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "LayerPropertiesWidget.h" +#include "Layer.h" +#include +#include +#include +#include +#include + +namespace PixelPaint { + +LayerPropertiesWidget::LayerPropertiesWidget() +{ + set_layout(); + auto& label = add("Layer properties"); + label.set_font(Gfx::Font::default_bold_font()); + + m_opacity_slider = add(); + m_opacity_slider->set_range(0, 100); + m_opacity_slider->on_value_changed = [this](int value) { + if (m_layer) + m_layer->set_opacity_percent(value); + }; + + m_visibility_checkbox = add("Visible"); + m_visibility_checkbox->on_checked = [this](bool checked) { + if (m_layer) + m_layer->set_visible(checked); + }; +} + +LayerPropertiesWidget::~LayerPropertiesWidget() +{ +} + +void LayerPropertiesWidget::set_layer(Layer* layer) +{ + if (m_layer == layer) + return; + + if (layer) { + m_layer = layer->make_weak_ptr(); + m_opacity_slider->set_value(layer->opacity_percent()); + m_visibility_checkbox->set_checked(layer->is_visible()); + set_enabled(true); + } else { + m_layer = nullptr; + set_enabled(false); + } +} + +} diff --git a/Applications/PixelPaint/LayerPropertiesWidget.h b/Applications/PixelPaint/LayerPropertiesWidget.h new file mode 100644 index 0000000000..b4b59c2696 --- /dev/null +++ b/Applications/PixelPaint/LayerPropertiesWidget.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace PixelPaint { + +class Layer; + +class LayerPropertiesWidget final : public GUI::Widget { + C_OBJECT(LayerPropertiesWidget); + +public: + virtual ~LayerPropertiesWidget() override; + + void set_layer(Layer*); + +private: + LayerPropertiesWidget(); + + RefPtr m_visibility_checkbox; + RefPtr m_opacity_slider; + + WeakPtr m_layer; +}; + +} diff --git a/Applications/PixelPaint/main.cpp b/Applications/PixelPaint/main.cpp index 3538d5c509..d5b39f35f8 100644 --- a/Applications/PixelPaint/main.cpp +++ b/Applications/PixelPaint/main.cpp @@ -32,6 +32,7 @@ #include "PaletteWidget.h" #include "Tool.h" #include "ToolboxWidget.h" +#include "LayerPropertiesWidget.h" #include #include #include @@ -94,6 +95,8 @@ int main(int argc, char** argv) auto& layer_list_widget = right_panel.add(); + auto& layer_properties_widget = right_panel.add(); + window->show(); auto menubar = GUI::MenuBar::construct(); @@ -131,7 +134,7 @@ int main(int argc, char** argv) "Create new layer...", { Mod_Ctrl | Mod_Shift, Key_N }, [&](auto&) { auto dialog = PixelPaint::CreateNewLayerDialog::construct(image_editor.image()->size(), window); if (dialog->exec() == GUI::Dialog::ExecOK) { - auto layer = PixelPaint::Layer::create_with_size(dialog->layer_size(), dialog->layer_name()); + auto layer = PixelPaint::Layer::create_with_size(*image_editor.image(), dialog->layer_size(), dialog->layer_name()); if (!layer) { GUI::MessageBox::show_error(window, String::format("Unable to create layer with size %s", dialog->size().to_string().characters())); return; @@ -200,20 +203,21 @@ int main(int argc, char** argv) image_editor.on_active_layer_change = [&](auto* layer) { layer_list_widget.set_selected_layer(layer); + layer_properties_widget.set_layer(layer); }; auto image = PixelPaint::Image::create_with_size({ 640, 480 }); - auto bg_layer = PixelPaint::Layer::create_with_size({ 640, 480 }, "Background"); + auto bg_layer = PixelPaint::Layer::create_with_size(*image, { 640, 480 }, "Background"); image->add_layer(*bg_layer); bg_layer->bitmap().fill(Color::White); - auto fg_layer1 = PixelPaint::Layer::create_with_size({ 200, 200 }, "FG Layer 1"); + auto fg_layer1 = PixelPaint::Layer::create_with_size(*image, { 200, 200 }, "FG Layer 1"); fg_layer1->set_location({ 50, 50 }); image->add_layer(*fg_layer1); fg_layer1->bitmap().fill(Color::Yellow); - auto fg_layer2 = PixelPaint::Layer::create_with_size({ 100, 100 }, "FG Layer 2"); + auto fg_layer2 = PixelPaint::Layer::create_with_size(*image, { 100, 100 }, "FG Layer 2"); fg_layer2->set_location({ 300, 300 }); image->add_layer(*fg_layer2); fg_layer2->bitmap().fill(Color::Blue);