mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 03:57:44 +00:00
PixelPaint: Rename from PaintBrush :^)
This commit is contained in:
parent
ff4eaa12b4
commit
58fa9c6e89
41 changed files with 58 additions and 58 deletions
93
Applications/PixelPaint/BucketTool.cpp
Normal file
93
Applications/PixelPaint/BucketTool.cpp
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "BucketTool.h"
|
||||
#include "ImageEditor.h"
|
||||
#include "Layer.h"
|
||||
#include <AK/Queue.h>
|
||||
#include <AK/SinglyLinkedList.h>
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
BucketTool::BucketTool()
|
||||
{
|
||||
}
|
||||
|
||||
BucketTool::~BucketTool()
|
||||
{
|
||||
}
|
||||
|
||||
static void flood_fill(Gfx::Bitmap& bitmap, const Gfx::Point& start_position, Color target_color, Color fill_color)
|
||||
{
|
||||
ASSERT(bitmap.bpp() == 32);
|
||||
|
||||
if (target_color == fill_color)
|
||||
return;
|
||||
|
||||
if (!bitmap.rect().contains(start_position))
|
||||
return;
|
||||
|
||||
Queue<Gfx::Point> queue;
|
||||
queue.enqueue(start_position);
|
||||
while (!queue.is_empty()) {
|
||||
auto position = queue.dequeue();
|
||||
|
||||
if (bitmap.get_pixel<Gfx::BitmapFormat::RGBA32>(position.x(), position.y()) != target_color)
|
||||
continue;
|
||||
|
||||
bitmap.set_pixel<Gfx::BitmapFormat::RGBA32>(position.x(), position.y(), fill_color);
|
||||
|
||||
if (position.x() != 0)
|
||||
queue.enqueue(position.translated(-1, 0));
|
||||
|
||||
if (position.x() != bitmap.width() - 1)
|
||||
queue.enqueue(position.translated(1, 0));
|
||||
|
||||
if (position.y() != 0)
|
||||
queue.enqueue(position.translated(0, -1));
|
||||
|
||||
if (position.y() != bitmap.height() - 1)
|
||||
queue.enqueue(position.translated(0, 1));
|
||||
}
|
||||
}
|
||||
|
||||
void BucketTool::on_mousedown(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (!layer.rect().contains(event.position()))
|
||||
return;
|
||||
|
||||
GUI::Painter painter(layer.bitmap());
|
||||
auto target_color = layer.bitmap().get_pixel(event.x(), event.y());
|
||||
|
||||
flood_fill(layer.bitmap(), event.position(), target_color, m_editor->color_for(event));
|
||||
|
||||
m_editor->update();
|
||||
}
|
||||
|
||||
}
|
44
Applications/PixelPaint/BucketTool.h
Normal file
44
Applications/PixelPaint/BucketTool.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "Tool.h"
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class BucketTool final : public Tool {
|
||||
public:
|
||||
BucketTool();
|
||||
virtual ~BucketTool() override;
|
||||
|
||||
virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override { return "BucketTool"; }
|
||||
};
|
||||
|
||||
}
|
23
Applications/PixelPaint/CMakeLists.txt
Normal file
23
Applications/PixelPaint/CMakeLists.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
set(SOURCES
|
||||
BucketTool.cpp
|
||||
CreateNewLayerDialog.cpp
|
||||
EllipseTool.cpp
|
||||
EraseTool.cpp
|
||||
Image.cpp
|
||||
ImageEditor.cpp
|
||||
Layer.cpp
|
||||
LayerModel.cpp
|
||||
LineTool.cpp
|
||||
main.cpp
|
||||
MoveTool.cpp
|
||||
PaletteWidget.cpp
|
||||
PenTool.cpp
|
||||
PickerTool.cpp
|
||||
RectangleTool.cpp
|
||||
SprayTool.cpp
|
||||
ToolboxWidget.cpp
|
||||
Tool.cpp
|
||||
)
|
||||
|
||||
serenity_bin(PixelPaint)
|
||||
target_link_libraries(PixelPaint LibGUI LibGfx)
|
94
Applications/PixelPaint/CreateNewLayerDialog.cpp
Normal file
94
Applications/PixelPaint/CreateNewLayerDialog.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "CreateNewLayerDialog.h"
|
||||
#include <LibGUI/BoxLayout.h>
|
||||
#include <LibGUI/Button.h>
|
||||
#include <LibGUI/Label.h>
|
||||
#include <LibGUI/SpinBox.h>
|
||||
#include <LibGUI/TextBox.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
CreateNewLayerDialog::CreateNewLayerDialog(const Gfx::Size& suggested_size, GUI::Window* parent_window)
|
||||
: Dialog(parent_window)
|
||||
{
|
||||
set_title("Create new layer");
|
||||
resize(200, 200);
|
||||
|
||||
auto& main_widget = set_main_widget<GUI::Widget>();
|
||||
main_widget.set_fill_with_background_color(true);
|
||||
|
||||
auto& layout = main_widget.set_layout<GUI::VerticalBoxLayout>();
|
||||
layout.set_margins({ 4, 4, 4, 4 });
|
||||
|
||||
auto& name_label = main_widget.add<GUI::Label>("Name:");
|
||||
name_label.set_text_alignment(Gfx::TextAlignment::CenterLeft);
|
||||
|
||||
m_name_textbox = main_widget.add<GUI::TextBox>();
|
||||
m_name_textbox->on_change = [this] {
|
||||
m_layer_name = m_name_textbox->text();
|
||||
};
|
||||
|
||||
auto& width_label = main_widget.add<GUI::Label>("Width:");
|
||||
width_label.set_text_alignment(Gfx::TextAlignment::CenterLeft);
|
||||
|
||||
auto& width_spinbox = main_widget.add<GUI::SpinBox>();
|
||||
|
||||
auto& height_label = main_widget.add<GUI::Label>("Height:");
|
||||
height_label.set_text_alignment(Gfx::TextAlignment::CenterLeft);
|
||||
|
||||
auto& height_spinbox = main_widget.add<GUI::SpinBox>();
|
||||
|
||||
auto& button_container = main_widget.add<GUI::Widget>();
|
||||
button_container.set_layout<GUI::HorizontalBoxLayout>();
|
||||
|
||||
auto& ok_button = button_container.add<GUI::Button>("OK");
|
||||
ok_button.on_click = [this](auto) {
|
||||
done(ExecOK);
|
||||
};
|
||||
|
||||
auto& cancel_button = button_container.add<GUI::Button>("Cancel");
|
||||
cancel_button.on_click = [this](auto) {
|
||||
done(ExecCancel);
|
||||
};
|
||||
|
||||
width_spinbox.on_change = [this](int value) {
|
||||
m_layer_size.set_width(value);
|
||||
};
|
||||
|
||||
height_spinbox.on_change = [this](int value) {
|
||||
m_layer_size.set_height(value);
|
||||
};
|
||||
|
||||
width_spinbox.set_range(0, 16384);
|
||||
height_spinbox.set_range(0, 16384);
|
||||
|
||||
width_spinbox.set_value(suggested_size.width());
|
||||
height_spinbox.set_value(suggested_size.height());
|
||||
}
|
||||
|
||||
}
|
48
Applications/PixelPaint/CreateNewLayerDialog.h
Normal file
48
Applications/PixelPaint/CreateNewLayerDialog.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 <LibGUI/Dialog.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class CreateNewLayerDialog final : public GUI::Dialog {
|
||||
C_OBJECT(CreateNewLayerDialog);
|
||||
public:
|
||||
const Gfx::Size& layer_size() const { return m_layer_size; }
|
||||
const String& layer_name() const { return m_layer_name; }
|
||||
|
||||
private:
|
||||
CreateNewLayerDialog(const Gfx::Size& suggested_size, GUI::Window* parent_window);
|
||||
|
||||
Gfx::Size m_layer_size;
|
||||
String m_layer_name;
|
||||
|
||||
RefPtr<GUI::TextBox> m_name_textbox;
|
||||
};
|
||||
|
||||
}
|
136
Applications/PixelPaint/EllipseTool.cpp
Normal file
136
Applications/PixelPaint/EllipseTool.cpp
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "EllipseTool.h"
|
||||
#include "ImageEditor.h"
|
||||
#include "Layer.h"
|
||||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibM/math.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
EllipseTool::EllipseTool()
|
||||
{
|
||||
}
|
||||
|
||||
EllipseTool::~EllipseTool()
|
||||
{
|
||||
}
|
||||
|
||||
void EllipseTool::draw_using(GUI::Painter& painter)
|
||||
{
|
||||
auto ellipse_intersecting_rect = Gfx::Rect::from_two_points(m_ellipse_start_position, m_ellipse_end_position);
|
||||
switch (m_mode) {
|
||||
case Mode::Outline:
|
||||
painter.draw_ellipse_intersecting(ellipse_intersecting_rect, m_editor->color_for(m_drawing_button), m_thickness);
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
void EllipseTool::on_mousedown(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right)
|
||||
return;
|
||||
|
||||
if (m_drawing_button != GUI::MouseButton::None)
|
||||
return;
|
||||
|
||||
m_drawing_button = event.button();
|
||||
m_ellipse_start_position = event.position();
|
||||
m_ellipse_end_position = event.position();
|
||||
m_editor->update();
|
||||
}
|
||||
|
||||
void EllipseTool::on_mouseup(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (event.button() == m_drawing_button) {
|
||||
GUI::Painter painter(layer.bitmap());
|
||||
draw_using(painter);
|
||||
m_drawing_button = GUI::MouseButton::None;
|
||||
m_editor->update();
|
||||
}
|
||||
}
|
||||
|
||||
void EllipseTool::on_mousemove(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (m_drawing_button == GUI::MouseButton::None)
|
||||
return;
|
||||
|
||||
m_ellipse_end_position = event.position();
|
||||
m_editor->update();
|
||||
}
|
||||
|
||||
void EllipseTool::on_second_paint(const Layer& layer, GUI::PaintEvent& event)
|
||||
{
|
||||
if (m_drawing_button == GUI::MouseButton::None)
|
||||
return;
|
||||
|
||||
GUI::Painter painter(*m_editor);
|
||||
painter.add_clip_rect(event.rect());
|
||||
painter.translate(layer.location());
|
||||
draw_using(painter);
|
||||
}
|
||||
|
||||
void EllipseTool::on_keydown(GUI::KeyEvent& event)
|
||||
{
|
||||
if (event.key() == Key_Escape && m_drawing_button != GUI::MouseButton::None) {
|
||||
m_drawing_button = GUI::MouseButton::None;
|
||||
m_editor->update();
|
||||
event.accept();
|
||||
}
|
||||
}
|
||||
|
||||
void EllipseTool::on_tool_button_contextmenu(GUI::ContextMenuEvent& event)
|
||||
{
|
||||
if (!m_context_menu) {
|
||||
m_context_menu = GUI::Menu::construct();
|
||||
m_context_menu->add_action(GUI::Action::create("Outline", [this](auto&) {
|
||||
m_mode = Mode::Outline;
|
||||
}));
|
||||
m_context_menu->add_separator();
|
||||
m_thickness_actions.set_exclusive(true);
|
||||
auto insert_action = [&](int size, bool checked = false) {
|
||||
auto action = GUI::Action::create_checkable(String::number(size), [this, size](auto&) {
|
||||
m_thickness = size;
|
||||
});
|
||||
action->set_checked(checked);
|
||||
m_thickness_actions.add_action(*action);
|
||||
m_context_menu->add_action(move(action));
|
||||
};
|
||||
insert_action(1, true);
|
||||
insert_action(2);
|
||||
insert_action(3);
|
||||
insert_action(4);
|
||||
}
|
||||
m_context_menu->popup(event.screen_position());
|
||||
}
|
||||
|
||||
}
|
65
Applications/PixelPaint/EllipseTool.h
Normal file
65
Applications/PixelPaint/EllipseTool.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "Tool.h"
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibGUI/ActionGroup.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class EllipseTool final : public Tool {
|
||||
public:
|
||||
EllipseTool();
|
||||
virtual ~EllipseTool() override;
|
||||
|
||||
virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) override;
|
||||
virtual void on_second_paint(const Layer&, GUI::PaintEvent&) override;
|
||||
virtual void on_keydown(GUI::KeyEvent&) override;
|
||||
|
||||
private:
|
||||
enum class Mode {
|
||||
Outline,
|
||||
// FIXME: Add Mode::Fill
|
||||
};
|
||||
|
||||
virtual const char* class_name() const override { return "EllipseTool"; }
|
||||
void draw_using(GUI::Painter& painter);
|
||||
|
||||
GUI::MouseButton m_drawing_button { GUI::MouseButton::None };
|
||||
Gfx::Point m_ellipse_start_position;
|
||||
Gfx::Point m_ellipse_end_position;
|
||||
RefPtr<GUI::Menu> m_context_menu;
|
||||
int m_thickness { 1 };
|
||||
GUI::ActionGroup m_thickness_actions;
|
||||
Mode m_mode { Mode::Outline };
|
||||
};
|
||||
|
||||
}
|
113
Applications/PixelPaint/EraseTool.cpp
Normal file
113
Applications/PixelPaint/EraseTool.cpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "EraseTool.h"
|
||||
#include "ImageEditor.h"
|
||||
#include "Layer.h"
|
||||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
EraseTool::EraseTool()
|
||||
{
|
||||
}
|
||||
|
||||
EraseTool::~EraseTool()
|
||||
{
|
||||
}
|
||||
|
||||
Gfx::Rect EraseTool::build_rect(const Gfx::Point& pos, const Gfx::Rect& widget_rect)
|
||||
{
|
||||
const int base_eraser_size = 10;
|
||||
const int eraser_size = (base_eraser_size * m_thickness);
|
||||
const int eraser_radius = eraser_size / 2;
|
||||
const auto ex = pos.x();
|
||||
const auto ey = pos.y();
|
||||
return Gfx::Rect(ex - eraser_radius, ey - eraser_radius, eraser_size, eraser_size).intersected(widget_rect);
|
||||
}
|
||||
|
||||
void EraseTool::on_mousedown(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right)
|
||||
return;
|
||||
Gfx::Rect r = build_rect(event.position(), layer.rect());
|
||||
GUI::Painter painter(layer.bitmap());
|
||||
painter.clear_rect(r, get_color());
|
||||
m_editor->update();
|
||||
}
|
||||
|
||||
void EraseTool::on_mousemove(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (event.buttons() & GUI::MouseButton::Left || event.buttons() & GUI::MouseButton::Right) {
|
||||
Gfx::Rect r = build_rect(event.position(), layer.rect());
|
||||
GUI::Painter painter(layer.bitmap());
|
||||
painter.clear_rect(r, get_color());
|
||||
m_editor->update();
|
||||
}
|
||||
}
|
||||
|
||||
void EraseTool::on_tool_button_contextmenu(GUI::ContextMenuEvent& event)
|
||||
{
|
||||
if (!m_context_menu) {
|
||||
m_context_menu = GUI::Menu::construct();
|
||||
|
||||
auto eraser_color_toggler = GUI::Action::create_checkable("Use secondary color", [&](auto& action) {
|
||||
m_use_secondary_color = action.is_checked();
|
||||
});
|
||||
eraser_color_toggler->set_checked(m_use_secondary_color);
|
||||
|
||||
m_context_menu->add_action(eraser_color_toggler);
|
||||
m_context_menu->add_separator();
|
||||
|
||||
m_thickness_actions.set_exclusive(true);
|
||||
auto insert_action = [&](int size, bool checked = false) {
|
||||
auto action = GUI::Action::create_checkable(String::number(size), [this, size](auto&) {
|
||||
m_thickness = size;
|
||||
});
|
||||
action->set_checked(checked);
|
||||
m_thickness_actions.add_action(*action);
|
||||
m_context_menu->add_action(move(action));
|
||||
};
|
||||
insert_action(1, true);
|
||||
insert_action(2);
|
||||
insert_action(3);
|
||||
insert_action(4);
|
||||
}
|
||||
|
||||
m_context_menu->popup(event.screen_position());
|
||||
}
|
||||
|
||||
Color EraseTool::get_color() const
|
||||
{
|
||||
if (m_use_secondary_color)
|
||||
return m_editor->secondary_color();
|
||||
return Color(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
}
|
56
Applications/PixelPaint/EraseTool.h
Normal file
56
Applications/PixelPaint/EraseTool.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "Tool.h"
|
||||
#include <LibGUI/ActionGroup.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class EraseTool final : public Tool {
|
||||
public:
|
||||
EraseTool();
|
||||
virtual ~EraseTool() override;
|
||||
|
||||
virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) override;
|
||||
|
||||
private:
|
||||
Gfx::Color get_color() const;
|
||||
virtual const char* class_name() const override { return "EraseTool"; }
|
||||
Gfx::Rect build_rect(const Gfx::Point& pos, const Gfx::Rect& widget_rect);
|
||||
RefPtr<GUI::Menu> m_context_menu;
|
||||
|
||||
bool m_use_secondary_color { false };
|
||||
int m_thickness { 1 };
|
||||
GUI::ActionGroup m_thickness_actions;
|
||||
};
|
||||
|
||||
}
|
134
Applications/PixelPaint/Image.cpp
Normal file
134
Applications/PixelPaint/Image.cpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "Image.h"
|
||||
#include "Layer.h"
|
||||
#include "LayerModel.h"
|
||||
#include <LibGUI/Painter.h>
|
||||
|
||||
//#define PAINT_DEBUG
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
RefPtr<Image> Image::create_with_size(const Gfx::Size& size)
|
||||
{
|
||||
if (size.is_empty())
|
||||
return nullptr;
|
||||
|
||||
if (size.width() > 16384 || size.height() > 16384)
|
||||
return nullptr;
|
||||
|
||||
return adopt(*new Image(size));
|
||||
}
|
||||
|
||||
Image::Image(const Gfx::Size& size)
|
||||
: m_size(size)
|
||||
{
|
||||
}
|
||||
|
||||
void Image::paint_into(GUI::Painter& painter, const Gfx::Rect& dest_rect, const Gfx::Rect& src_rect)
|
||||
{
|
||||
(void) src_rect;
|
||||
Gfx::PainterStateSaver saver(painter);
|
||||
painter.add_clip_rect(dest_rect);
|
||||
for (auto& layer : m_layers) {
|
||||
auto target = dest_rect.translated(layer.location());
|
||||
target.set_size(layer.size());
|
||||
#ifdef IMAGE_DEBUG
|
||||
dbg() << "Composite layer " << layer.name() << " target: " << target << ", layer.rect: " << layer.rect();
|
||||
#endif
|
||||
painter.draw_scaled_bitmap(target, layer.bitmap(), layer.rect());
|
||||
}
|
||||
}
|
||||
|
||||
void Image::add_layer(NonnullRefPtr<Layer> layer)
|
||||
{
|
||||
for (auto& existing_layer : m_layers) {
|
||||
ASSERT(&existing_layer != layer.ptr());
|
||||
}
|
||||
m_layers.append(move(layer));
|
||||
}
|
||||
|
||||
GUI::Model& Image::layer_model()
|
||||
{
|
||||
if (!m_layer_model)
|
||||
m_layer_model = LayerModel::create(*this);
|
||||
return *m_layer_model;
|
||||
}
|
||||
|
||||
size_t Image::index_of(const Layer& layer) const
|
||||
{
|
||||
for (size_t i = 0; i < m_layers.size(); ++i) {
|
||||
if (&m_layers.at(i) == &layer)
|
||||
return i;
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
void Image::move_layer_to_back(Layer& layer)
|
||||
{
|
||||
NonnullRefPtr<Layer> protector(layer);
|
||||
auto index = index_of(layer);
|
||||
m_layers.remove(index);
|
||||
m_layers.prepend(layer);
|
||||
}
|
||||
|
||||
void Image::move_layer_to_front(Layer& layer)
|
||||
{
|
||||
NonnullRefPtr<Layer> protector(layer);
|
||||
auto index = index_of(layer);
|
||||
m_layers.remove(index);
|
||||
m_layers.append(layer);
|
||||
}
|
||||
|
||||
void Image::move_layer_down(Layer& layer)
|
||||
{
|
||||
NonnullRefPtr<Layer> protector(layer);
|
||||
auto index = index_of(layer);
|
||||
if (!index)
|
||||
return;
|
||||
m_layers.remove(index);
|
||||
m_layers.insert(index - 1, layer);
|
||||
}
|
||||
|
||||
void Image::move_layer_up(Layer& layer)
|
||||
{
|
||||
NonnullRefPtr<Layer> protector(layer);
|
||||
auto index = index_of(layer);
|
||||
if (index == m_layers.size() - 1)
|
||||
return;
|
||||
m_layers.remove(index);
|
||||
m_layers.insert(index + 1, layer);
|
||||
}
|
||||
|
||||
void Image::remove_layer(Layer& layer)
|
||||
{
|
||||
NonnullRefPtr<Layer> protector(layer);
|
||||
auto index = index_of(layer);
|
||||
m_layers.remove(index);
|
||||
}
|
||||
|
||||
}
|
74
Applications/PixelPaint/Image.h
Normal file
74
Applications/PixelPaint/Image.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 <AK/NonnullRefPtrVector.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGUI/Forward.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/Size.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class Layer;
|
||||
|
||||
class Image : public RefCounted<Image> {
|
||||
public:
|
||||
static RefPtr<Image> create_with_size(const Gfx::Size&);
|
||||
|
||||
size_t layer_count() const { return m_layers.size(); }
|
||||
const Layer& layer(size_t index) const { return m_layers.at(index); }
|
||||
|
||||
const Gfx::Size& size() const { return m_size; }
|
||||
Gfx::Rect rect() const { return { {}, m_size }; }
|
||||
|
||||
void add_layer(NonnullRefPtr<Layer>);
|
||||
|
||||
void paint_into(GUI::Painter&, const Gfx::Rect& dest_rect, const Gfx::Rect& src_rect);
|
||||
|
||||
GUI::Model& layer_model();
|
||||
|
||||
void move_layer_to_front(Layer&);
|
||||
void move_layer_to_back(Layer&);
|
||||
void move_layer_up(Layer&);
|
||||
void move_layer_down(Layer&);
|
||||
void remove_layer(Layer&);
|
||||
|
||||
private:
|
||||
explicit Image(const Gfx::Size&);
|
||||
|
||||
size_t index_of(const Layer&) const;
|
||||
|
||||
Gfx::Size m_size;
|
||||
NonnullRefPtrVector<Layer> m_layers;
|
||||
RefPtr<GUI::Model> m_layer_model;
|
||||
};
|
||||
|
||||
}
|
229
Applications/PixelPaint/ImageEditor.cpp
Normal file
229
Applications/PixelPaint/ImageEditor.cpp
Normal file
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "ImageEditor.h"
|
||||
#include "Image.h"
|
||||
#include "Layer.h"
|
||||
#include "LayerModel.h"
|
||||
#include "Tool.h"
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
ImageEditor::ImageEditor()
|
||||
{
|
||||
}
|
||||
|
||||
void ImageEditor::set_image(RefPtr<Image> image)
|
||||
{
|
||||
m_image = move(image);
|
||||
update();
|
||||
}
|
||||
|
||||
void ImageEditor::paint_event(GUI::PaintEvent& event)
|
||||
{
|
||||
GUI::Frame::paint_event(event);
|
||||
|
||||
GUI::Painter painter(*this);
|
||||
painter.add_clip_rect(event.rect());
|
||||
painter.add_clip_rect(frame_inner_rect());
|
||||
|
||||
painter.translate(frame_thickness(), frame_thickness());
|
||||
painter.fill_rect_with_checkerboard(rect(), { 8, 8 }, palette().base().darkened(0.9), palette().base());
|
||||
|
||||
if (m_image) {
|
||||
painter.draw_rect(m_image->rect().inflated(2, 2), Color::Black);
|
||||
m_image->paint_into(painter, m_image->rect(), m_image->rect());
|
||||
}
|
||||
|
||||
if (m_active_layer) {
|
||||
painter.draw_rect(m_active_layer->relative_rect().inflated(2, 2), Color::Black);
|
||||
}
|
||||
}
|
||||
|
||||
void ImageEditor::second_paint_event(GUI::PaintEvent& event)
|
||||
{
|
||||
if (m_active_tool && m_active_layer)
|
||||
m_active_tool->on_second_paint(*m_active_layer, event);
|
||||
}
|
||||
|
||||
static GUI::MouseEvent event_adjusted_for_layer(const GUI::MouseEvent& original_event, const Layer& layer)
|
||||
{
|
||||
auto position_in_active_layer_coordinates = original_event.position().translated(-layer.location());
|
||||
return {
|
||||
static_cast<GUI::Event::Type>(original_event.type()),
|
||||
position_in_active_layer_coordinates, original_event.buttons(),
|
||||
original_event.button(),
|
||||
original_event.modifiers(),
|
||||
original_event.wheel_delta()
|
||||
};
|
||||
}
|
||||
|
||||
void ImageEditor::mousedown_event(GUI::MouseEvent& event)
|
||||
{
|
||||
if (!m_active_tool)
|
||||
return;
|
||||
|
||||
if (m_active_tool->is_move_tool()) {
|
||||
if (auto* other_layer = layer_at(event.position())) {
|
||||
set_active_layer(other_layer);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_active_layer)
|
||||
return;
|
||||
|
||||
auto layer_event = event_adjusted_for_layer(event, *m_active_layer);
|
||||
m_active_tool->on_mousedown(*m_active_layer, layer_event, event);
|
||||
}
|
||||
|
||||
void ImageEditor::mousemove_event(GUI::MouseEvent& event)
|
||||
{
|
||||
if (!m_active_layer || !m_active_tool)
|
||||
return;
|
||||
auto layer_event = event_adjusted_for_layer(event, *m_active_layer);
|
||||
m_active_tool->on_mousemove(*m_active_layer, layer_event, event);
|
||||
}
|
||||
|
||||
void ImageEditor::mouseup_event(GUI::MouseEvent& event)
|
||||
{
|
||||
if (!m_active_layer || !m_active_tool)
|
||||
return;
|
||||
auto layer_event = event_adjusted_for_layer(event, *m_active_layer);
|
||||
m_active_tool->on_mouseup(*m_active_layer, layer_event, event);
|
||||
}
|
||||
|
||||
void ImageEditor::context_menu_event(GUI::ContextMenuEvent& event)
|
||||
{
|
||||
if (!m_active_layer || !m_active_tool)
|
||||
return;
|
||||
m_active_tool->on_context_menu(*m_active_layer, event);
|
||||
}
|
||||
|
||||
void ImageEditor::keydown_event(GUI::KeyEvent& event)
|
||||
{
|
||||
if (m_active_tool)
|
||||
m_active_tool->on_keydown(event);
|
||||
}
|
||||
|
||||
void ImageEditor::keyup_event(GUI::KeyEvent& event)
|
||||
{
|
||||
if (m_active_tool)
|
||||
m_active_tool->on_keyup(event);
|
||||
}
|
||||
|
||||
void ImageEditor::set_active_layer(Layer* layer)
|
||||
{
|
||||
if (m_active_layer == layer)
|
||||
return;
|
||||
m_active_layer = layer;
|
||||
|
||||
if (m_active_layer) {
|
||||
size_t index = 0;
|
||||
for (; index < m_image->layer_count(); ++index) {
|
||||
if (&m_image->layer(index) == layer)
|
||||
break;
|
||||
}
|
||||
if (on_active_layer_change)
|
||||
on_active_layer_change(m_image->layer_model().index(index));
|
||||
} else {
|
||||
if (on_active_layer_change)
|
||||
on_active_layer_change({});
|
||||
}
|
||||
|
||||
layers_did_change();
|
||||
}
|
||||
|
||||
void ImageEditor::set_active_tool(Tool* tool)
|
||||
{
|
||||
if (m_active_tool == tool)
|
||||
return;
|
||||
|
||||
if (m_active_tool)
|
||||
m_active_tool->clear();
|
||||
|
||||
m_active_tool = tool;
|
||||
|
||||
if (m_active_tool)
|
||||
m_active_tool->setup(*this);
|
||||
}
|
||||
|
||||
void ImageEditor::layers_did_change()
|
||||
{
|
||||
static_cast<LayerModel&>(m_image->layer_model()).update_without_invalidating_indexes();
|
||||
update();
|
||||
}
|
||||
|
||||
Color ImageEditor::color_for(GUI::MouseButton button) const
|
||||
{
|
||||
if (button == GUI::MouseButton::Left)
|
||||
return m_primary_color;
|
||||
if (button == GUI::MouseButton::Right)
|
||||
return m_secondary_color;
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
Color ImageEditor::color_for(const GUI::MouseEvent& event) const
|
||||
{
|
||||
if (event.buttons() & GUI::MouseButton::Left)
|
||||
return m_primary_color;
|
||||
if (event.buttons() & GUI::MouseButton::Right)
|
||||
return m_secondary_color;
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
void ImageEditor::set_primary_color(Color color)
|
||||
{
|
||||
if (m_primary_color == color)
|
||||
return;
|
||||
m_primary_color = color;
|
||||
if (on_primary_color_change)
|
||||
on_primary_color_change(color);
|
||||
}
|
||||
|
||||
void ImageEditor::set_secondary_color(Color color)
|
||||
{
|
||||
if (m_secondary_color == color)
|
||||
return;
|
||||
m_secondary_color = color;
|
||||
if (on_secondary_color_change)
|
||||
on_secondary_color_change(color);
|
||||
}
|
||||
|
||||
Layer* ImageEditor::layer_at(const Gfx::Point& position)
|
||||
{
|
||||
if (!m_image)
|
||||
return nullptr;
|
||||
for (ssize_t i = m_image->layer_count() - 1; i >= 0; --i) {
|
||||
auto& layer = m_image->layer(i);
|
||||
if (layer.relative_rect().contains(position))
|
||||
return const_cast<Layer*>(&layer);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
93
Applications/PixelPaint/ImageEditor.h
Normal file
93
Applications/PixelPaint/ImageEditor.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 <LibGUI/Frame.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class Image;
|
||||
class Layer;
|
||||
class Tool;
|
||||
|
||||
class ImageEditor final : public GUI::Frame {
|
||||
C_OBJECT(ImageEditor);
|
||||
|
||||
public:
|
||||
const Image* image() const { return m_image; }
|
||||
Image* image() { return m_image; }
|
||||
|
||||
void set_image(RefPtr<Image>);
|
||||
|
||||
Layer* active_layer() { return m_active_layer; }
|
||||
void set_active_layer(Layer*);
|
||||
|
||||
Tool* active_tool() { return m_active_tool; }
|
||||
void set_active_tool(Tool*);
|
||||
|
||||
void layers_did_change();
|
||||
|
||||
Layer* layer_at(const Gfx::Point&);
|
||||
|
||||
Color primary_color() const { return m_primary_color; }
|
||||
void set_primary_color(Color);
|
||||
|
||||
Color secondary_color() const { return m_secondary_color; }
|
||||
void set_secondary_color(Color);
|
||||
|
||||
Color color_for(const GUI::MouseEvent&) const;
|
||||
Color color_for(GUI::MouseButton) const;
|
||||
|
||||
Function<void(Color)> on_primary_color_change;
|
||||
Function<void(Color)> on_secondary_color_change;
|
||||
|
||||
Function<void(const GUI::ModelIndex&)> on_active_layer_change;
|
||||
|
||||
private:
|
||||
ImageEditor();
|
||||
|
||||
virtual bool accepts_focus() const override { return true; }
|
||||
|
||||
virtual void paint_event(GUI::PaintEvent&) override;
|
||||
virtual void second_paint_event(GUI::PaintEvent&) override;
|
||||
virtual void mousedown_event(GUI::MouseEvent&) override;
|
||||
virtual void mousemove_event(GUI::MouseEvent&) override;
|
||||
virtual void mouseup_event(GUI::MouseEvent&) override;
|
||||
virtual void keydown_event(GUI::KeyEvent&) override;
|
||||
virtual void keyup_event(GUI::KeyEvent&) override;
|
||||
virtual void context_menu_event(GUI::ContextMenuEvent&) override;
|
||||
|
||||
RefPtr<Image> m_image;
|
||||
RefPtr<Layer> m_active_layer;
|
||||
|
||||
Tool* m_active_tool { nullptr };
|
||||
|
||||
Color m_primary_color { Color::Black };
|
||||
Color m_secondary_color { Color::White };
|
||||
};
|
||||
|
||||
}
|
49
Applications/PixelPaint/Layer.cpp
Normal file
49
Applications/PixelPaint/Layer.cpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "Layer.h"
|
||||
#include <LibGfx/Bitmap.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
RefPtr<Layer> Layer::create_with_size(const Gfx::Size& size, const String& name)
|
||||
{
|
||||
if (size.is_empty())
|
||||
return nullptr;
|
||||
|
||||
if (size.width() > 16384 || size.height() > 16384)
|
||||
return nullptr;
|
||||
|
||||
return adopt(*new Layer(size, name));
|
||||
}
|
||||
|
||||
Layer::Layer(const Gfx::Size& size, const String& name)
|
||||
: m_name(name)
|
||||
{
|
||||
m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, size);
|
||||
}
|
||||
|
||||
}
|
66
Applications/PixelPaint/Layer.h
Normal file
66
Applications/PixelPaint/Layer.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 <AK/Noncopyable.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class Layer : public RefCounted<Layer> {
|
||||
AK_MAKE_NONCOPYABLE(Layer);
|
||||
AK_MAKE_NONMOVABLE(Layer);
|
||||
|
||||
public:
|
||||
static RefPtr<Layer> create_with_size(const Gfx::Size&, const String& name);
|
||||
|
||||
~Layer() {}
|
||||
|
||||
const Gfx::Point& location() const { return m_location; }
|
||||
void set_location(const Gfx::Point& location) { m_location = location; }
|
||||
|
||||
const Gfx::Bitmap& bitmap() const { return *m_bitmap; }
|
||||
Gfx::Bitmap& bitmap() { return *m_bitmap; }
|
||||
Gfx::Size size() const { return bitmap().size(); }
|
||||
|
||||
Gfx::Rect relative_rect() const { return { location(), size() }; }
|
||||
Gfx::Rect rect() const { return { {}, size() }; }
|
||||
|
||||
const String& name() const { return m_name; }
|
||||
void set_name(const String& name) { m_name = name; }
|
||||
|
||||
private:
|
||||
explicit Layer(const Gfx::Size&, const String& name);
|
||||
|
||||
String m_name;
|
||||
Gfx::Point m_location;
|
||||
RefPtr<Gfx::Bitmap> m_bitmap;
|
||||
};
|
||||
|
||||
}
|
77
Applications/PixelPaint/LayerModel.cpp
Normal file
77
Applications/PixelPaint/LayerModel.cpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "LayerModel.h"
|
||||
#include "Image.h"
|
||||
#include "Layer.h"
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
NonnullRefPtr<LayerModel> LayerModel::create(Image& image)
|
||||
{
|
||||
return adopt(*new LayerModel(image));
|
||||
}
|
||||
|
||||
LayerModel::LayerModel(Image& image)
|
||||
: m_image(image)
|
||||
{
|
||||
}
|
||||
|
||||
int LayerModel::row_count(const GUI::ModelIndex&) const
|
||||
{
|
||||
return m_image.layer_count();
|
||||
}
|
||||
|
||||
String LayerModel::column_name(int column) const
|
||||
{
|
||||
switch (column) {
|
||||
case Column::Name:
|
||||
return "Name";
|
||||
case Column::Size:
|
||||
return "Size";
|
||||
case Column::Location:
|
||||
return "Location";
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
GUI::Variant LayerModel::data(const GUI::ModelIndex& index, Role role) const
|
||||
{
|
||||
auto& layer = m_image.layer(index.row());
|
||||
if (role == Role::Display) {
|
||||
switch (index.column()) {
|
||||
case Column::Name:
|
||||
return layer.name();
|
||||
case Column::Size:
|
||||
return layer.size();
|
||||
case Column::Location:
|
||||
return layer.location();
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
60
Applications/PixelPaint/LayerModel.h
Normal file
60
Applications/PixelPaint/LayerModel.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 <LibGUI/Model.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class Image;
|
||||
|
||||
class LayerModel final : public GUI::Model {
|
||||
public:
|
||||
enum Column {
|
||||
Name,
|
||||
Size,
|
||||
Location,
|
||||
__Count
|
||||
};
|
||||
|
||||
static NonnullRefPtr<LayerModel> create(Image&);
|
||||
|
||||
virtual int row_count(const GUI::ModelIndex&) const override;
|
||||
virtual int column_count(const GUI::ModelIndex&) const override { return Column::__Count; }
|
||||
virtual String column_name(int) const override;
|
||||
virtual GUI::Variant data(const GUI::ModelIndex&, Role = Role::Display) const override;
|
||||
virtual void update() override { did_update(); }
|
||||
|
||||
void update_without_invalidating_indexes() { did_update(0); }
|
||||
|
||||
private:
|
||||
explicit LayerModel(Image&);
|
||||
|
||||
Image& m_image;
|
||||
};
|
||||
|
||||
}
|
152
Applications/PixelPaint/LineTool.cpp
Normal file
152
Applications/PixelPaint/LineTool.cpp
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "LineTool.h"
|
||||
#include "ImageEditor.h"
|
||||
#include "Layer.h"
|
||||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibM/math.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
static Gfx::Point constrain_line_angle(const Gfx::Point& start_pos, const Gfx::Point& end_pos, float angle_increment)
|
||||
{
|
||||
float current_angle = atan2(end_pos.y() - start_pos.y(), end_pos.x() - start_pos.x()) + M_PI * 2.;
|
||||
|
||||
float constrained_angle = ((int)((current_angle + angle_increment / 2.) / angle_increment)) * angle_increment;
|
||||
|
||||
auto diff = end_pos - start_pos;
|
||||
float line_length = sqrt(diff.x() * diff.x() + diff.y() * diff.y());
|
||||
|
||||
return { start_pos.x() + (int)(cos(constrained_angle) * line_length),
|
||||
start_pos.y() + (int)(sin(constrained_angle) * line_length) };
|
||||
}
|
||||
|
||||
LineTool::LineTool()
|
||||
{
|
||||
}
|
||||
|
||||
LineTool::~LineTool()
|
||||
{
|
||||
}
|
||||
|
||||
void LineTool::on_mousedown(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right)
|
||||
return;
|
||||
|
||||
if (m_drawing_button != GUI::MouseButton::None)
|
||||
return;
|
||||
|
||||
m_drawing_button = event.button();
|
||||
m_line_start_position = event.position();
|
||||
m_line_end_position = event.position();
|
||||
m_editor->update();
|
||||
}
|
||||
|
||||
void LineTool::on_mouseup(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (event.button() == m_drawing_button) {
|
||||
GUI::Painter painter(layer.bitmap());
|
||||
painter.draw_line(m_line_start_position, m_line_end_position, m_editor->color_for(m_drawing_button), m_thickness);
|
||||
m_drawing_button = GUI::MouseButton::None;
|
||||
m_editor->update();
|
||||
}
|
||||
}
|
||||
|
||||
void LineTool::on_mousemove(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (m_drawing_button == GUI::MouseButton::None)
|
||||
return;
|
||||
|
||||
if (!m_constrain_angle) {
|
||||
m_line_end_position = event.position();
|
||||
} else {
|
||||
const float ANGLE_STEP = M_PI / 8.0f;
|
||||
m_line_end_position = constrain_line_angle(m_line_start_position, event.position(), ANGLE_STEP);
|
||||
}
|
||||
m_editor->update();
|
||||
}
|
||||
|
||||
void LineTool::on_second_paint(const Layer& layer, GUI::PaintEvent& event)
|
||||
{
|
||||
if (m_drawing_button == GUI::MouseButton::None)
|
||||
return;
|
||||
|
||||
GUI::Painter painter(*m_editor);
|
||||
painter.add_clip_rect(event.rect());
|
||||
painter.translate(layer.location());
|
||||
painter.draw_line(m_line_start_position, m_line_end_position, m_editor->color_for(m_drawing_button), m_thickness);
|
||||
}
|
||||
|
||||
void LineTool::on_keydown(GUI::KeyEvent& event)
|
||||
{
|
||||
if (event.key() == Key_Escape && m_drawing_button != GUI::MouseButton::None) {
|
||||
m_drawing_button = GUI::MouseButton::None;
|
||||
m_editor->update();
|
||||
event.accept();
|
||||
}
|
||||
|
||||
if (event.key() == Key_Shift) {
|
||||
m_constrain_angle = true;
|
||||
m_editor->update();
|
||||
event.accept();
|
||||
}
|
||||
}
|
||||
|
||||
void LineTool::on_keyup(GUI::KeyEvent& event)
|
||||
{
|
||||
if (event.key() == Key_Shift) {
|
||||
m_constrain_angle = false;
|
||||
m_editor->update();
|
||||
event.accept();
|
||||
}
|
||||
}
|
||||
|
||||
void LineTool::on_tool_button_contextmenu(GUI::ContextMenuEvent& event)
|
||||
{
|
||||
if (!m_context_menu) {
|
||||
m_context_menu = GUI::Menu::construct();
|
||||
m_thickness_actions.set_exclusive(true);
|
||||
auto insert_action = [&](int size, bool checked = false) {
|
||||
auto action = GUI::Action::create_checkable(String::number(size), [this, size](auto&) {
|
||||
m_thickness = size;
|
||||
});
|
||||
action->set_checked(checked);
|
||||
m_thickness_actions.add_action(*action);
|
||||
m_context_menu->add_action(move(action));
|
||||
};
|
||||
insert_action(1, true);
|
||||
insert_action(2);
|
||||
insert_action(3);
|
||||
insert_action(4);
|
||||
}
|
||||
m_context_menu->popup(event.screen_position());
|
||||
}
|
||||
|
||||
}
|
60
Applications/PixelPaint/LineTool.h
Normal file
60
Applications/PixelPaint/LineTool.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "Tool.h"
|
||||
#include <LibGUI/ActionGroup.h>
|
||||
#include <LibGfx/Point.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class LineTool final : public Tool {
|
||||
public:
|
||||
LineTool();
|
||||
virtual ~LineTool() override;
|
||||
|
||||
virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) override;
|
||||
virtual void on_second_paint(const Layer&, GUI::PaintEvent&) override;
|
||||
virtual void on_keydown(GUI::KeyEvent&) override;
|
||||
virtual void on_keyup(GUI::KeyEvent&) override;
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override { return "LineTool"; }
|
||||
|
||||
GUI::MouseButton m_drawing_button { GUI::MouseButton::None };
|
||||
Gfx::Point m_line_start_position;
|
||||
Gfx::Point m_line_end_position;
|
||||
RefPtr<GUI::Menu> m_context_menu;
|
||||
GUI::ActionGroup m_thickness_actions;
|
||||
int m_thickness { 1 };
|
||||
bool m_constrain_angle { false };
|
||||
};
|
||||
|
||||
}
|
132
Applications/PixelPaint/MoveTool.cpp
Normal file
132
Applications/PixelPaint/MoveTool.cpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "MoveTool.h"
|
||||
#include "Image.h"
|
||||
#include "ImageEditor.h"
|
||||
#include "Layer.h"
|
||||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/Window.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
MoveTool::MoveTool()
|
||||
{
|
||||
}
|
||||
|
||||
MoveTool::~MoveTool()
|
||||
{
|
||||
}
|
||||
|
||||
void MoveTool::on_mousedown(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent& original_event)
|
||||
{
|
||||
if (event.button() != GUI::MouseButton::Left)
|
||||
return;
|
||||
if (!layer.rect().contains(event.position()))
|
||||
return;
|
||||
m_layer_being_moved = layer;
|
||||
m_event_origin = original_event.position();
|
||||
m_layer_origin = layer.location();
|
||||
m_editor->window()->set_override_cursor(GUI::StandardCursor::Move);
|
||||
}
|
||||
|
||||
void MoveTool::on_mousemove(Layer&, GUI::MouseEvent&, GUI::MouseEvent& original_event)
|
||||
{
|
||||
if (!m_layer_being_moved)
|
||||
return;
|
||||
auto delta = original_event.position() - m_event_origin;
|
||||
m_layer_being_moved->set_location(m_layer_origin.translated(delta));
|
||||
m_editor->layers_did_change();
|
||||
}
|
||||
|
||||
void MoveTool::on_mouseup(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (event.button() != GUI::MouseButton::Left)
|
||||
return;
|
||||
m_layer_being_moved = nullptr;
|
||||
m_editor->window()->set_override_cursor(GUI::StandardCursor::None);
|
||||
}
|
||||
|
||||
void MoveTool::on_keydown(GUI::KeyEvent& event)
|
||||
{
|
||||
if (event.modifiers() != 0)
|
||||
return;
|
||||
|
||||
auto* layer = m_editor->active_layer();
|
||||
if (!layer)
|
||||
return;
|
||||
|
||||
auto new_location = layer->location();
|
||||
|
||||
switch (event.key()) {
|
||||
case Key_Up:
|
||||
new_location.move_by(0, -1);
|
||||
break;
|
||||
case Key_Down:
|
||||
new_location.move_by(0, 1);
|
||||
break;
|
||||
case Key_Left:
|
||||
new_location.move_by(-1, 0);
|
||||
break;
|
||||
case Key_Right:
|
||||
new_location.move_by(1, 0);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
layer->set_location(new_location);
|
||||
m_editor->layers_did_change();
|
||||
}
|
||||
|
||||
void MoveTool::on_context_menu(Layer& layer, GUI::ContextMenuEvent& event)
|
||||
{
|
||||
if (!m_context_menu) {
|
||||
m_context_menu = GUI::Menu::construct();
|
||||
m_context_menu->add_action(GUI::CommonActions::make_move_to_front_action([this](auto&) {
|
||||
m_editor->image()->move_layer_to_front(*m_context_menu_layer);
|
||||
m_editor->layers_did_change();
|
||||
}, m_editor));
|
||||
m_context_menu->add_action(GUI::CommonActions::make_move_to_back_action([this](auto&) {
|
||||
m_editor->image()->move_layer_to_back(*m_context_menu_layer);
|
||||
m_editor->layers_did_change();
|
||||
}, m_editor));
|
||||
m_context_menu->add_separator();
|
||||
m_context_menu->add_action(GUI::Action::create("Delete layer", Gfx::Bitmap::load_from_file("/res/icons/16x16/delete.png"), [this](auto&) {
|
||||
m_editor->image()->remove_layer(*m_context_menu_layer);
|
||||
// FIXME: This should not be done imperatively here. Perhaps a Image::Client interface that ImageEditor can implement?
|
||||
if (m_editor->active_layer() == m_context_menu_layer)
|
||||
m_editor->set_active_layer(nullptr);
|
||||
m_editor->layers_did_change();
|
||||
}, m_editor));
|
||||
}
|
||||
m_context_menu_layer = layer;
|
||||
m_context_menu->popup(event.screen_position());
|
||||
}
|
||||
|
||||
}
|
55
Applications/PixelPaint/MoveTool.h
Normal file
55
Applications/PixelPaint/MoveTool.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "Tool.h"
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class MoveTool final : public Tool {
|
||||
public:
|
||||
MoveTool();
|
||||
virtual ~MoveTool() override;
|
||||
|
||||
virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_keydown(GUI::KeyEvent&) override;
|
||||
virtual void on_context_menu(Layer&, GUI::ContextMenuEvent&) override;
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override { return "MoveTool"; }
|
||||
virtual bool is_move_tool() const override { return true; }
|
||||
|
||||
RefPtr<Layer> m_layer_being_moved;
|
||||
Gfx::Point m_event_origin;
|
||||
Gfx::Point m_layer_origin;
|
||||
RefPtr<GUI::Menu> m_context_menu;
|
||||
RefPtr<Layer> m_context_menu_layer;
|
||||
};
|
||||
|
||||
}
|
179
Applications/PixelPaint/PaletteWidget.cpp
Normal file
179
Applications/PixelPaint/PaletteWidget.cpp
Normal file
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "PaletteWidget.h"
|
||||
#include "ImageEditor.h"
|
||||
#include <LibGUI/BoxLayout.h>
|
||||
#include <LibGUI/ColorPicker.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class ColorWidget : public GUI::Frame {
|
||||
C_OBJECT(ColorWidget);
|
||||
|
||||
public:
|
||||
explicit ColorWidget(Color color, PaletteWidget& palette_widget)
|
||||
: m_palette_widget(palette_widget)
|
||||
, m_color(color)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ColorWidget() override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void mousedown_event(GUI::MouseEvent& event) override
|
||||
{
|
||||
if (event.modifiers() & KeyModifier::Mod_Ctrl && event.button() == GUI::MouseButton::Left) {
|
||||
auto dialog = GUI::ColorPicker::construct(m_color, window());
|
||||
if (dialog->exec() == GUI::Dialog::ExecOK) {
|
||||
m_color = dialog->color();
|
||||
auto pal = palette();
|
||||
pal.set_color(ColorRole::Background, m_color);
|
||||
set_palette(pal);
|
||||
update();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.button() == GUI::MouseButton::Left)
|
||||
m_palette_widget.set_primary_color(m_color);
|
||||
else if (event.button() == GUI::MouseButton::Right)
|
||||
m_palette_widget.set_secondary_color(m_color);
|
||||
}
|
||||
|
||||
private:
|
||||
PaletteWidget& m_palette_widget;
|
||||
Color m_color;
|
||||
};
|
||||
|
||||
PaletteWidget::PaletteWidget(ImageEditor& editor)
|
||||
: m_editor(editor)
|
||||
{
|
||||
set_frame_shape(Gfx::FrameShape::Panel);
|
||||
set_frame_shadow(Gfx::FrameShadow::Raised);
|
||||
set_frame_thickness(0);
|
||||
set_fill_with_background_color(true);
|
||||
|
||||
set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed);
|
||||
set_preferred_size(0, 34);
|
||||
|
||||
m_secondary_color_widget = add<GUI::Frame>();
|
||||
m_secondary_color_widget->set_relative_rect({ 2, 2, 60, 31 });
|
||||
m_secondary_color_widget->set_fill_with_background_color(true);
|
||||
set_secondary_color(m_editor.secondary_color());
|
||||
|
||||
m_primary_color_widget = add<GUI::Frame>();
|
||||
Gfx::Rect rect { 0, 0, 38, 15 };
|
||||
rect.center_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);
|
||||
set_primary_color(m_editor.primary_color());
|
||||
|
||||
m_editor.on_primary_color_change = [this](Color color) {
|
||||
set_primary_color(color);
|
||||
};
|
||||
|
||||
m_editor.on_secondary_color_change = [this](Color color) {
|
||||
set_secondary_color(color);
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
auto& top_color_container = color_container.add<GUI::Widget>();
|
||||
top_color_container.set_layout<GUI::HorizontalBoxLayout>();
|
||||
top_color_container.layout()->set_spacing(1);
|
||||
|
||||
auto& bottom_color_container = color_container.add<GUI::Widget>();
|
||||
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);
|
||||
};
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
PaletteWidget::~PaletteWidget()
|
||||
{
|
||||
}
|
||||
|
||||
void PaletteWidget::set_primary_color(Color color)
|
||||
{
|
||||
m_editor.set_primary_color(color);
|
||||
auto pal = m_primary_color_widget->palette();
|
||||
pal.set_color(ColorRole::Background, color);
|
||||
m_primary_color_widget->set_palette(pal);
|
||||
m_primary_color_widget->update();
|
||||
}
|
||||
|
||||
void PaletteWidget::set_secondary_color(Color color)
|
||||
{
|
||||
m_editor.set_secondary_color(color);
|
||||
auto pal = m_secondary_color_widget->palette();
|
||||
pal.set_color(ColorRole::Background, color);
|
||||
m_secondary_color_widget->set_palette(pal);
|
||||
m_secondary_color_widget->update();
|
||||
}
|
||||
|
||||
}
|
52
Applications/PixelPaint/PaletteWidget.h
Normal file
52
Applications/PixelPaint/PaletteWidget.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 <LibGUI/Frame.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class ImageEditor;
|
||||
|
||||
class PaletteWidget final : public GUI::Frame {
|
||||
C_OBJECT(PaletteWidget);
|
||||
|
||||
public:
|
||||
virtual ~PaletteWidget() override;
|
||||
|
||||
void set_primary_color(Color);
|
||||
void set_secondary_color(Color);
|
||||
|
||||
private:
|
||||
explicit PaletteWidget(ImageEditor&);
|
||||
|
||||
ImageEditor& m_editor;
|
||||
RefPtr<GUI::Frame> m_primary_color_widget;
|
||||
RefPtr<GUI::Frame> m_secondary_color_widget;
|
||||
};
|
||||
|
||||
}
|
97
Applications/PixelPaint/PenTool.cpp
Normal file
97
Applications/PixelPaint/PenTool.cpp
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "PenTool.h"
|
||||
#include "ImageEditor.h"
|
||||
#include "Layer.h"
|
||||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/Painter.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
PenTool::PenTool()
|
||||
{
|
||||
}
|
||||
|
||||
PenTool::~PenTool()
|
||||
{
|
||||
}
|
||||
|
||||
void PenTool::on_mousedown(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right)
|
||||
return;
|
||||
|
||||
GUI::Painter painter(layer.bitmap());
|
||||
painter.draw_line(event.position(), event.position(), m_editor->color_for(event), m_thickness);
|
||||
m_editor->update();
|
||||
m_last_drawing_event_position = event.position();
|
||||
}
|
||||
|
||||
void PenTool::on_mouseup(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (event.button() == GUI::MouseButton::Left || event.button() == GUI::MouseButton::Right)
|
||||
m_last_drawing_event_position = { -1, -1 };
|
||||
}
|
||||
|
||||
void PenTool::on_mousemove(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (!(event.buttons() & GUI::MouseButton::Left || event.buttons() & GUI::MouseButton::Right))
|
||||
return;
|
||||
GUI::Painter painter(layer.bitmap());
|
||||
|
||||
if (m_last_drawing_event_position != Gfx::Point(-1, -1))
|
||||
painter.draw_line(m_last_drawing_event_position, event.position(), m_editor->color_for(event), m_thickness);
|
||||
else
|
||||
painter.draw_line(event.position(), event.position(), m_editor->color_for(event), m_thickness);
|
||||
m_editor->update();
|
||||
|
||||
m_last_drawing_event_position = event.position();
|
||||
}
|
||||
|
||||
void PenTool::on_tool_button_contextmenu(GUI::ContextMenuEvent& event)
|
||||
{
|
||||
if (!m_context_menu) {
|
||||
m_context_menu = GUI::Menu::construct();
|
||||
m_thickness_actions.set_exclusive(true);
|
||||
auto insert_action = [&](int size, bool checked = false) {
|
||||
auto action = GUI::Action::create_checkable(String::number(size), [this, size](auto&) {
|
||||
m_thickness = size;
|
||||
});
|
||||
action->set_checked(checked);
|
||||
m_thickness_actions.add_action(*action);
|
||||
m_context_menu->add_action(move(action));
|
||||
};
|
||||
insert_action(1, true);
|
||||
insert_action(2);
|
||||
insert_action(3);
|
||||
insert_action(4);
|
||||
}
|
||||
m_context_menu->popup(event.screen_position());
|
||||
}
|
||||
|
||||
}
|
54
Applications/PixelPaint/PenTool.h
Normal file
54
Applications/PixelPaint/PenTool.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "Tool.h"
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibGUI/ActionGroup.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class PenTool final : public Tool {
|
||||
public:
|
||||
PenTool();
|
||||
virtual ~PenTool() override;
|
||||
|
||||
virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) override;
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override { return "PenTool"; }
|
||||
|
||||
Gfx::Point m_last_drawing_event_position { -1, -1 };
|
||||
RefPtr<GUI::Menu> m_context_menu;
|
||||
int m_thickness { 1 };
|
||||
GUI::ActionGroup m_thickness_actions;
|
||||
};
|
||||
|
||||
}
|
53
Applications/PixelPaint/PickerTool.cpp
Normal file
53
Applications/PixelPaint/PickerTool.cpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "PickerTool.h"
|
||||
#include "ImageEditor.h"
|
||||
#include "Layer.h"
|
||||
#include <LibGfx/Bitmap.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
PickerTool::PickerTool()
|
||||
{
|
||||
}
|
||||
|
||||
PickerTool::~PickerTool()
|
||||
{
|
||||
}
|
||||
|
||||
void PickerTool::on_mousedown(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (!layer.rect().contains(event.position()))
|
||||
return;
|
||||
auto color = layer.bitmap().get_pixel(event.position());
|
||||
if (event.button() == GUI::MouseButton::Left)
|
||||
m_editor->set_primary_color(color);
|
||||
else if (event.button() == GUI::MouseButton::Right)
|
||||
m_editor->set_secondary_color(color);
|
||||
}
|
||||
|
||||
}
|
45
Applications/PixelPaint/PickerTool.h
Normal file
45
Applications/PixelPaint/PickerTool.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "Tool.h"
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class PickerTool final : public Tool {
|
||||
public:
|
||||
PickerTool();
|
||||
virtual ~PickerTool() override;
|
||||
|
||||
virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override { return "PickerTool"; }
|
||||
|
||||
};
|
||||
|
||||
}
|
134
Applications/PixelPaint/RectangleTool.cpp
Normal file
134
Applications/PixelPaint/RectangleTool.cpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "RectangleTool.h"
|
||||
#include "ImageEditor.h"
|
||||
#include "Layer.h"
|
||||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibM/math.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
RectangleTool::RectangleTool()
|
||||
{
|
||||
}
|
||||
|
||||
RectangleTool::~RectangleTool()
|
||||
{
|
||||
}
|
||||
|
||||
void RectangleTool::draw_using(GUI::Painter& painter)
|
||||
{
|
||||
auto rect_to_draw = Gfx::Rect::from_two_points(m_rectangle_start_position, m_rectangle_end_position);
|
||||
switch (m_mode) {
|
||||
case Mode::Fill:
|
||||
painter.fill_rect(rect_to_draw, m_editor->color_for(m_drawing_button));
|
||||
break;
|
||||
case Mode::Outline:
|
||||
painter.draw_rect(rect_to_draw, m_editor->color_for(m_drawing_button));
|
||||
break;
|
||||
case Mode::Gradient:
|
||||
painter.fill_rect_with_gradient(rect_to_draw, m_editor->primary_color(), m_editor->secondary_color());
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
void RectangleTool::on_mousedown(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right)
|
||||
return;
|
||||
|
||||
if (m_drawing_button != GUI::MouseButton::None)
|
||||
return;
|
||||
|
||||
m_drawing_button = event.button();
|
||||
m_rectangle_start_position = event.position();
|
||||
m_rectangle_end_position = event.position();
|
||||
m_editor->update();
|
||||
}
|
||||
|
||||
void RectangleTool::on_mouseup(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (event.button() == m_drawing_button) {
|
||||
GUI::Painter painter(layer.bitmap());
|
||||
draw_using(painter);
|
||||
m_drawing_button = GUI::MouseButton::None;
|
||||
m_editor->update();
|
||||
}
|
||||
}
|
||||
|
||||
void RectangleTool::on_mousemove(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
if (m_drawing_button == GUI::MouseButton::None)
|
||||
return;
|
||||
|
||||
m_rectangle_end_position = event.position();
|
||||
m_editor->update();
|
||||
}
|
||||
|
||||
void RectangleTool::on_second_paint(const Layer& layer, GUI::PaintEvent& event)
|
||||
{
|
||||
if (m_drawing_button == GUI::MouseButton::None)
|
||||
return;
|
||||
|
||||
GUI::Painter painter(*m_editor);
|
||||
painter.add_clip_rect(event.rect());
|
||||
painter.translate(layer.location());
|
||||
draw_using(painter);
|
||||
}
|
||||
|
||||
void RectangleTool::on_keydown(GUI::KeyEvent& event)
|
||||
{
|
||||
if (event.key() == Key_Escape && m_drawing_button != GUI::MouseButton::None) {
|
||||
m_drawing_button = GUI::MouseButton::None;
|
||||
m_editor->update();
|
||||
event.accept();
|
||||
}
|
||||
}
|
||||
|
||||
void RectangleTool::on_tool_button_contextmenu(GUI::ContextMenuEvent& event)
|
||||
{
|
||||
if (!m_context_menu) {
|
||||
m_context_menu = GUI::Menu::construct();
|
||||
m_context_menu->add_action(GUI::Action::create("Fill", [this](auto&) {
|
||||
m_mode = Mode::Fill;
|
||||
}));
|
||||
m_context_menu->add_action(GUI::Action::create("Outline", [this](auto&) {
|
||||
m_mode = Mode::Outline;
|
||||
}));
|
||||
m_context_menu->add_action(GUI::Action::create("Gradient", [this](auto&) {
|
||||
m_mode = Mode::Gradient;
|
||||
}));
|
||||
}
|
||||
m_context_menu->popup(event.screen_position());
|
||||
}
|
||||
|
||||
}
|
64
Applications/PixelPaint/RectangleTool.h
Normal file
64
Applications/PixelPaint/RectangleTool.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "Tool.h"
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibGUI/Forward.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class RectangleTool final : public Tool {
|
||||
public:
|
||||
RectangleTool();
|
||||
virtual ~RectangleTool() override;
|
||||
|
||||
virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) override;
|
||||
virtual void on_second_paint(const Layer&, GUI::PaintEvent&) override;
|
||||
virtual void on_keydown(GUI::KeyEvent&) override;
|
||||
|
||||
private:
|
||||
enum class Mode {
|
||||
Outline,
|
||||
Fill,
|
||||
Gradient,
|
||||
};
|
||||
|
||||
virtual const char* class_name() const override { return "RectangleTool"; }
|
||||
void draw_using(GUI::Painter&);
|
||||
|
||||
GUI::MouseButton m_drawing_button { GUI::MouseButton::None };
|
||||
Gfx::Point m_rectangle_start_position;
|
||||
Gfx::Point m_rectangle_end_position;
|
||||
RefPtr<GUI::Menu> m_context_menu;
|
||||
Mode m_mode { Mode::Outline };
|
||||
};
|
||||
|
||||
}
|
127
Applications/PixelPaint/SprayTool.cpp
Normal file
127
Applications/PixelPaint/SprayTool.cpp
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "SprayTool.h"
|
||||
#include "ImageEditor.h"
|
||||
#include "Layer.h"
|
||||
#include <AK/Queue.h>
|
||||
#include <AK/SinglyLinkedList.h>
|
||||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibM/math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
SprayTool::SprayTool()
|
||||
{
|
||||
m_timer = Core::Timer::construct();
|
||||
m_timer->on_timeout = [&]() {
|
||||
paint_it();
|
||||
};
|
||||
m_timer->set_interval(200);
|
||||
}
|
||||
|
||||
SprayTool::~SprayTool()
|
||||
{
|
||||
}
|
||||
|
||||
static double nrand()
|
||||
{
|
||||
return double(rand()) / double(RAND_MAX);
|
||||
}
|
||||
|
||||
void SprayTool::paint_it()
|
||||
{
|
||||
auto* layer = m_editor->active_layer();
|
||||
if (!layer)
|
||||
return;
|
||||
|
||||
auto& bitmap = layer->bitmap();
|
||||
GUI::Painter painter(bitmap);
|
||||
ASSERT(bitmap.bpp() == 32);
|
||||
m_editor->update();
|
||||
const double minimal_radius = 10;
|
||||
const double base_radius = minimal_radius * m_thickness;
|
||||
for (int i = 0; i < 100 + (nrand() * 800); i++) {
|
||||
double radius = base_radius * nrand();
|
||||
double angle = 2 * M_PI * nrand();
|
||||
const int xpos = m_last_pos.x() + radius * cos(angle);
|
||||
const int ypos = m_last_pos.y() - radius * sin(angle);
|
||||
if (xpos < 0 || xpos >= bitmap.width())
|
||||
continue;
|
||||
if (ypos < 0 || ypos >= bitmap.height())
|
||||
continue;
|
||||
bitmap.set_pixel<Gfx::BitmapFormat::RGB32>(xpos, ypos, m_color);
|
||||
}
|
||||
}
|
||||
|
||||
void SprayTool::on_mousedown(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
m_color = m_editor->color_for(event);
|
||||
m_last_pos = event.position();
|
||||
m_timer->start();
|
||||
paint_it();
|
||||
}
|
||||
|
||||
void SprayTool::on_mousemove(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&)
|
||||
{
|
||||
m_last_pos = event.position();
|
||||
if (m_timer->is_active()) {
|
||||
paint_it();
|
||||
m_timer->restart(m_timer->interval());
|
||||
}
|
||||
}
|
||||
|
||||
void SprayTool::on_mouseup(Layer&, GUI::MouseEvent&, GUI::MouseEvent&)
|
||||
{
|
||||
m_timer->stop();
|
||||
}
|
||||
|
||||
void SprayTool::on_tool_button_contextmenu(GUI::ContextMenuEvent& event)
|
||||
{
|
||||
if (!m_context_menu) {
|
||||
m_context_menu = GUI::Menu::construct();
|
||||
m_thickness_actions.set_exclusive(true);
|
||||
auto insert_action = [&](int size, bool checked = false) {
|
||||
auto action = GUI::Action::create_checkable(String::number(size), [this, size](auto&) {
|
||||
m_thickness = size;
|
||||
});
|
||||
action->set_checked(checked);
|
||||
m_thickness_actions.add_action(*action);
|
||||
m_context_menu->add_action(move(action));
|
||||
};
|
||||
insert_action(1, true);
|
||||
insert_action(2);
|
||||
insert_action(3);
|
||||
insert_action(4);
|
||||
}
|
||||
m_context_menu->popup(event.screen_position());
|
||||
}
|
||||
|
||||
}
|
57
Applications/PixelPaint/SprayTool.h
Normal file
57
Applications/PixelPaint/SprayTool.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "Tool.h"
|
||||
#include <LibCore/Timer.h>
|
||||
#include <LibGUI/ActionGroup.h>
|
||||
#include <LibGUI/Painter.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class SprayTool final : public Tool {
|
||||
public:
|
||||
SprayTool();
|
||||
virtual ~SprayTool() override;
|
||||
|
||||
virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& original_event) override;
|
||||
virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) override;
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override { return "SprayTool"; }
|
||||
void paint_it();
|
||||
RefPtr<Core::Timer> m_timer;
|
||||
Gfx::Point m_last_pos;
|
||||
Color m_color;
|
||||
RefPtr<GUI::Menu> m_context_menu;
|
||||
GUI::ActionGroup m_thickness_actions;
|
||||
int m_thickness { 1 };
|
||||
};
|
||||
|
||||
}
|
51
Applications/PixelPaint/Tool.cpp
Normal file
51
Applications/PixelPaint/Tool.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "Tool.h"
|
||||
#include "ImageEditor.h"
|
||||
#include <LibGUI/Action.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
Tool::Tool()
|
||||
{
|
||||
}
|
||||
|
||||
Tool::~Tool()
|
||||
{
|
||||
}
|
||||
|
||||
void Tool::setup(ImageEditor& editor)
|
||||
{
|
||||
m_editor = editor.make_weak_ptr();
|
||||
}
|
||||
|
||||
void Tool::set_action(GUI::Action* action)
|
||||
{
|
||||
m_action = action;
|
||||
}
|
||||
|
||||
}
|
66
Applications/PixelPaint/Tool.h
Normal file
66
Applications/PixelPaint/Tool.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 <LibGUI/Event.h>
|
||||
#include <LibGUI/Forward.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class ImageEditor;
|
||||
class Layer;
|
||||
|
||||
class Tool {
|
||||
public:
|
||||
virtual ~Tool();
|
||||
|
||||
virtual const char* class_name() const = 0;
|
||||
|
||||
virtual void on_mousedown(Layer&, GUI::MouseEvent&, GUI::MouseEvent&) {}
|
||||
virtual void on_mousemove(Layer&, GUI::MouseEvent&, GUI::MouseEvent&) {}
|
||||
virtual void on_mouseup(Layer&, GUI::MouseEvent&, GUI::MouseEvent&) {}
|
||||
virtual void on_context_menu(Layer&, GUI::ContextMenuEvent&) {}
|
||||
virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) {}
|
||||
virtual void on_second_paint(const Layer&, GUI::PaintEvent&) {}
|
||||
virtual void on_keydown(GUI::KeyEvent&) {}
|
||||
virtual void on_keyup(GUI::KeyEvent&) {}
|
||||
|
||||
virtual bool is_move_tool() const { return false; }
|
||||
|
||||
void clear() { m_editor = nullptr; }
|
||||
void setup(ImageEditor&);
|
||||
|
||||
GUI::Action* action() { return m_action; }
|
||||
void set_action(GUI::Action*);
|
||||
|
||||
protected:
|
||||
Tool();
|
||||
WeakPtr<ImageEditor> m_editor;
|
||||
RefPtr<GUI::Action> m_action;
|
||||
};
|
||||
|
||||
}
|
129
Applications/PixelPaint/ToolboxWidget.cpp
Normal file
129
Applications/PixelPaint/ToolboxWidget.cpp
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "ToolboxWidget.h"
|
||||
#include "BucketTool.h"
|
||||
#include "EllipseTool.h"
|
||||
#include "EraseTool.h"
|
||||
#include "LineTool.h"
|
||||
#include "MoveTool.h"
|
||||
#include "PenTool.h"
|
||||
#include "PickerTool.h"
|
||||
#include "RectangleTool.h"
|
||||
#include "SprayTool.h"
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/BoxLayout.h>
|
||||
#include <LibGUI/Button.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class ToolButton final : public GUI::Button {
|
||||
C_OBJECT(ToolButton)
|
||||
public:
|
||||
ToolButton(ToolboxWidget& toolbox, const String& name, const GUI::Shortcut& shortcut, OwnPtr<Tool> tool)
|
||||
: m_toolbox(toolbox)
|
||||
, m_tool(move(tool))
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.append(name);
|
||||
builder.append(" (");
|
||||
builder.append(shortcut.to_string());
|
||||
builder.append(")");
|
||||
set_tooltip(builder.to_string());
|
||||
|
||||
m_action = GUI::Action::create_checkable(name, shortcut, [this](auto& action) {
|
||||
if (action.is_checked())
|
||||
m_toolbox.on_tool_selection(m_tool);
|
||||
else
|
||||
m_toolbox.on_tool_selection(nullptr);
|
||||
});
|
||||
|
||||
m_tool->set_action(m_action);
|
||||
set_action(*m_action);
|
||||
m_toolbox.m_action_group.add_action(*m_action);
|
||||
}
|
||||
|
||||
const Tool& tool() const { return *m_tool; }
|
||||
Tool& tool() { return *m_tool; }
|
||||
|
||||
virtual bool is_uncheckable() const override { return false; }
|
||||
|
||||
virtual void context_menu_event(GUI::ContextMenuEvent& event) override
|
||||
{
|
||||
m_action->activate();
|
||||
m_tool->on_tool_button_contextmenu(event);
|
||||
}
|
||||
|
||||
private:
|
||||
ToolboxWidget& m_toolbox;
|
||||
OwnPtr<Tool> m_tool;
|
||||
RefPtr<GUI::Action> m_action;
|
||||
};
|
||||
|
||||
ToolboxWidget::ToolboxWidget()
|
||||
{
|
||||
set_fill_with_background_color(true);
|
||||
|
||||
set_frame_thickness(1);
|
||||
set_frame_shape(Gfx::FrameShape::Panel);
|
||||
set_frame_shadow(Gfx::FrameShadow::Raised);
|
||||
|
||||
set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill);
|
||||
set_preferred_size(48, 0);
|
||||
|
||||
set_layout<GUI::VerticalBoxLayout>();
|
||||
layout()->set_margins({ 4, 4, 4, 4 });
|
||||
|
||||
m_action_group.set_exclusive(true);
|
||||
m_action_group.set_unchecking_allowed(false);
|
||||
|
||||
auto add_tool = [&](const StringView& name, const StringView& icon_name, const GUI::Shortcut& shortcut, NonnullOwnPtr<Tool> tool) -> ToolButton& {
|
||||
m_tools.append(tool.ptr());
|
||||
auto& button = add<ToolButton>(*this, name, shortcut, move(tool));
|
||||
button.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed);
|
||||
button.set_preferred_size(0, 32);
|
||||
button.set_checkable(true);
|
||||
button.set_icon(Gfx::Bitmap::load_from_file(String::format("/res/icons/paintbrush/%s.png", icon_name.to_string().characters())));
|
||||
return button;
|
||||
};
|
||||
|
||||
add_tool("Move", "move", { 0, Key_M }, make<MoveTool>());
|
||||
add_tool("Pen", "pen", { 0, Key_N }, make<PenTool>());
|
||||
add_tool("Bucket Fill", "bucket", { Mod_Shift, Key_B }, make<BucketTool>());
|
||||
add_tool("Spray", "spray", { Mod_Shift, Key_S }, make<SprayTool>());
|
||||
add_tool("Color Picker", "picker", { 0, Key_O }, make<PickerTool>());
|
||||
add_tool("Erase", "eraser", { Mod_Shift, Key_E }, make<EraseTool>());
|
||||
add_tool("Line", "line", { Mod_Ctrl | Mod_Shift, Key_L }, make<LineTool>());
|
||||
add_tool("Rectangle", "rectangle", { Mod_Ctrl | Mod_Shift, Key_R }, make<RectangleTool>());
|
||||
add_tool("Ellipse", "circle", { Mod_Ctrl | Mod_Shift, Key_E }, make<EllipseTool>());
|
||||
}
|
||||
|
||||
ToolboxWidget::~ToolboxWidget()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
58
Applications/PixelPaint/ToolboxWidget.h
Normal file
58
Applications/PixelPaint/ToolboxWidget.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 <LibGUI/ActionGroup.h>
|
||||
#include <LibGUI/Frame.h>
|
||||
|
||||
namespace PixelPaint {
|
||||
|
||||
class Tool;
|
||||
|
||||
class ToolboxWidget final : public GUI::Frame {
|
||||
C_OBJECT(ToolboxWidget)
|
||||
public:
|
||||
virtual ~ToolboxWidget() override;
|
||||
|
||||
Function<void(Tool*)> on_tool_selection;
|
||||
|
||||
template<typename Callback>
|
||||
void for_each_tool(Callback callback)
|
||||
{
|
||||
for (auto& tool : m_tools)
|
||||
callback(*tool);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ToolButton;
|
||||
|
||||
explicit ToolboxWidget();
|
||||
GUI::ActionGroup m_action_group;
|
||||
Vector<Tool*> m_tools;
|
||||
};
|
||||
|
||||
}
|
213
Applications/PixelPaint/main.cpp
Normal file
213
Applications/PixelPaint/main.cpp
Normal file
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 "CreateNewLayerDialog.h"
|
||||
#include "Image.h"
|
||||
#include "ImageEditor.h"
|
||||
#include "Layer.h"
|
||||
#include "PaletteWidget.h"
|
||||
#include "Tool.h"
|
||||
#include "ToolboxWidget.h"
|
||||
#include <LibGUI/AboutDialog.h>
|
||||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/Application.h>
|
||||
#include <LibGUI/BoxLayout.h>
|
||||
#include <LibGUI/FilePicker.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/MenuBar.h>
|
||||
#include <LibGUI/MessageBox.h>
|
||||
#include <LibGUI/Model.h>
|
||||
#include <LibGUI/TableView.h>
|
||||
#include <LibGUI/Window.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (pledge("stdio thread shared_buffer accept rpath unix wpath cpath fattr", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
GUI::Application app(argc, argv);
|
||||
|
||||
if (pledge("stdio thread shared_buffer accept rpath wpath cpath", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto window = GUI::Window::construct();
|
||||
window->set_title("PixelPaint");
|
||||
window->set_rect(40, 100, 950, 570);
|
||||
window->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-paintbrush.png"));
|
||||
|
||||
auto& horizontal_container = window->set_main_widget<GUI::Widget>();
|
||||
horizontal_container.set_layout<GUI::HorizontalBoxLayout>();
|
||||
horizontal_container.layout()->set_spacing(0);
|
||||
|
||||
auto& toolbox = horizontal_container.add<PixelPaint::ToolboxWidget>();
|
||||
|
||||
auto& vertical_container = horizontal_container.add<GUI::Widget>();
|
||||
vertical_container.set_layout<GUI::VerticalBoxLayout>();
|
||||
vertical_container.layout()->set_spacing(0);
|
||||
|
||||
auto& image_editor = vertical_container.add<PixelPaint::ImageEditor>();
|
||||
image_editor.set_focus(true);
|
||||
|
||||
toolbox.on_tool_selection = [&](auto* tool) {
|
||||
image_editor.set_active_tool(tool);
|
||||
};
|
||||
|
||||
vertical_container.add<PixelPaint::PaletteWidget>(image_editor);
|
||||
|
||||
auto& right_panel = horizontal_container.add<GUI::Widget>();
|
||||
right_panel.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill);
|
||||
right_panel.set_preferred_size(230, 0);
|
||||
right_panel.set_layout<GUI::VerticalBoxLayout>();
|
||||
|
||||
auto& layer_table_view = right_panel.add<GUI::TableView>();
|
||||
layer_table_view.set_size_columns_to_fit_content(true);
|
||||
|
||||
window->show();
|
||||
|
||||
auto menubar = GUI::MenuBar::construct();
|
||||
auto& app_menu = menubar->add_menu("PixelPaint");
|
||||
|
||||
app_menu.add_action(GUI::CommonActions::make_open_action([&](auto&) {
|
||||
Optional<String> open_path = GUI::FilePicker::get_open_filepath();
|
||||
|
||||
if (!open_path.has_value())
|
||||
return;
|
||||
|
||||
auto bitmap = Gfx::Bitmap::load_from_file(open_path.value());
|
||||
if (!bitmap) {
|
||||
GUI::MessageBox::show(String::format("Failed to load '%s'", open_path.value().characters()), "Open failed", GUI::MessageBox::Type::Error, GUI::MessageBox::InputType::OK, window);
|
||||
return;
|
||||
}
|
||||
}));
|
||||
app_menu.add_separator();
|
||||
app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) {
|
||||
GUI::Application::the().quit(0);
|
||||
return;
|
||||
}));
|
||||
|
||||
menubar->add_menu("Edit");
|
||||
|
||||
auto& tool_menu = menubar->add_menu("Tool");
|
||||
toolbox.for_each_tool([&](auto& tool) {
|
||||
if (tool.action())
|
||||
tool_menu.add_action(*tool.action());
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
auto& layer_menu = menubar->add_menu("Layer");
|
||||
layer_menu.add_action(GUI::Action::create("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());
|
||||
if (!layer) {
|
||||
GUI::MessageBox::show_error(String::format("Unable to create layer with size %s", dialog->size().to_string().characters()));
|
||||
return;
|
||||
}
|
||||
image_editor.image()->add_layer(layer.release_nonnull());
|
||||
image_editor.layers_did_change();
|
||||
}
|
||||
}, window));
|
||||
|
||||
layer_menu.add_separator();
|
||||
layer_menu.add_action(GUI::Action::create("Select previous layer", { 0, Key_PageUp }, [&](auto&) {
|
||||
layer_table_view.move_selection(1);
|
||||
}, window));
|
||||
layer_menu.add_action(GUI::Action::create("Select next layer", { 0, Key_PageDown }, [&](auto&) {
|
||||
layer_table_view.move_selection(-1);
|
||||
}, window));
|
||||
layer_menu.add_action(GUI::Action::create("Select top layer", { 0, Key_Home }, [&](auto&) {
|
||||
layer_table_view.selection().set(layer_table_view.model()->index(image_editor.image()->layer_count() - 1));
|
||||
}, window));
|
||||
layer_menu.add_action(GUI::Action::create("Select bottom layer", { 0, Key_End }, [&](auto&) {
|
||||
layer_table_view.selection().set(layer_table_view.model()->index(0));
|
||||
}, window));
|
||||
layer_menu.add_separator();
|
||||
layer_menu.add_action(GUI::Action::create("Move active layer up", { Mod_Ctrl, Key_PageUp }, [&](auto&) {
|
||||
auto active_layer = image_editor.active_layer();
|
||||
if(!active_layer)
|
||||
return;
|
||||
image_editor.image()->move_layer_up(*active_layer);
|
||||
layer_table_view.move_selection(1);
|
||||
image_editor.layers_did_change();
|
||||
}, window));
|
||||
layer_menu.add_action(GUI::Action::create("Move active layer down", { Mod_Ctrl, Key_PageDown }, [&](auto&) {
|
||||
auto active_layer = image_editor.active_layer();
|
||||
if(!active_layer)
|
||||
return;
|
||||
image_editor.image()->move_layer_down(*active_layer);
|
||||
layer_table_view.move_selection(-1);
|
||||
image_editor.layers_did_change();
|
||||
}, window));
|
||||
layer_menu.add_separator();
|
||||
layer_menu.add_action(GUI::Action::create("Remove active layer", { Mod_Ctrl , Key_D }, [&](auto&) {
|
||||
auto active_layer = image_editor.active_layer();
|
||||
if(!active_layer)
|
||||
return;
|
||||
image_editor.image()->remove_layer(*active_layer);
|
||||
image_editor.set_active_layer(nullptr);
|
||||
image_editor.layers_did_change();
|
||||
}, window));
|
||||
|
||||
auto& help_menu = menubar->add_menu("Help");
|
||||
help_menu.add_action(GUI::Action::create("About", [&](auto&) {
|
||||
GUI::AboutDialog::show("PixelPaint", Gfx::Bitmap::load_from_file("/res/icons/32x32/app-paintbrush.png"), window);
|
||||
}));
|
||||
|
||||
app.set_menubar(move(menubar));
|
||||
|
||||
image_editor.on_active_layer_change = [&](auto& index) {
|
||||
if (index.is_valid())
|
||||
layer_table_view.selection().set(index);
|
||||
else
|
||||
layer_table_view.selection().clear();
|
||||
};
|
||||
|
||||
auto image = PixelPaint::Image::create_with_size({ 640, 480 });
|
||||
|
||||
auto bg_layer = PixelPaint::Layer::create_with_size({ 640, 480 }, "Background");
|
||||
image->add_layer(*bg_layer);
|
||||
bg_layer->bitmap().fill(Color::White);
|
||||
|
||||
layer_table_view.set_model(image->layer_model());
|
||||
layer_table_view.on_selection_change = [&] {
|
||||
auto index = layer_table_view.selection().first();
|
||||
if (index.is_valid())
|
||||
image_editor.set_active_layer(const_cast<PixelPaint::Layer*>(&image->layer(index.row())));
|
||||
else
|
||||
image_editor.set_active_layer(nullptr);
|
||||
};
|
||||
|
||||
image_editor.set_image(image);
|
||||
image_editor.set_active_layer(bg_layer);
|
||||
|
||||
return app.exec();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue