1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 08:37:45 +00:00

PaintBrush: Port all the existing toolbox tools to the Layer world :^)

Many tools are not working perfectly right yet, but we'll fix them!
This commit is contained in:
Andreas Kling 2020-05-12 23:44:46 +02:00
parent 7dd8f1b921
commit 83d24dcb1d
25 changed files with 302 additions and 199 deletions

View file

@ -25,12 +25,16 @@
*/
#include "BucketTool.h"
#include "ImageEditor.h"
#include "Layer.h"
#include "PaintableWidget.h"
#include <AK/Queue.h>
#include <AK/SinglyLinkedList.h>
#include <LibGUI/Painter.h>
#include <LibGfx/Bitmap.h>
#include <stdio.h>
#include <LibGfx/Rect.h>
namespace PaintBrush {
BucketTool::BucketTool()
{
@ -74,15 +78,17 @@ static void flood_fill(Gfx::Bitmap& bitmap, const Gfx::Point& start_position, Co
}
}
void BucketTool::on_mousedown(GUI::MouseEvent& event)
void BucketTool::on_mousedown(Layer& layer, GUI::MouseEvent& event)
{
if (!m_widget->rect().contains(event.position()))
if (!layer.rect().contains(event.position()))
return;
GUI::Painter painter(m_widget->bitmap());
auto target_color = m_widget->bitmap().get_pixel(event.x(), event.y());
GUI::Painter painter(layer.bitmap());
auto target_color = layer.bitmap().get_pixel(event.x(), event.y());
flood_fill(m_widget->bitmap(), event.position(), target_color, m_widget->color_for(event));
flood_fill(layer.bitmap(), event.position(), target_color, PaintableWidget::the().color_for(event));
m_editor->update();
}
m_widget->update();
}

View file

@ -28,13 +28,17 @@
#include "Tool.h"
namespace PaintBrush {
class BucketTool final : public Tool {
public:
BucketTool();
virtual ~BucketTool() override;
virtual void on_mousedown(GUI::MouseEvent&) override;
virtual void on_mousedown(Layer&, GUI::MouseEvent&) override;
private:
virtual const char* class_name() const override { return "BucketTool"; }
};
}

View file

@ -25,6 +25,8 @@
*/
#include "EllipseTool.h"
#include "ImageEditor.h"
#include "Layer.h"
#include "PaintableWidget.h"
#include <LibGUI/Action.h>
#include <LibGUI/Menu.h>
@ -32,6 +34,8 @@
#include <LibGfx/Rect.h>
#include <LibM/math.h>
namespace PaintBrush {
EllipseTool::EllipseTool()
{
}
@ -45,14 +49,14 @@ 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_widget->color_for(m_drawing_button), m_thickness);
painter.draw_ellipse_intersecting(ellipse_intersecting_rect, PaintableWidget::the().color_for(m_drawing_button), m_thickness);
break;
default:
ASSERT_NOT_REACHED();
}
}
void EllipseTool::on_mousedown(GUI::MouseEvent& event)
void EllipseTool::on_mousedown(Layer&, GUI::MouseEvent& event)
{
if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right)
return;
@ -63,29 +67,29 @@ void EllipseTool::on_mousedown(GUI::MouseEvent& event)
m_drawing_button = event.button();
m_ellipse_start_position = event.position();
m_ellipse_end_position = event.position();
m_widget->update();
m_editor->update();
}
void EllipseTool::on_mouseup(GUI::MouseEvent& event)
void EllipseTool::on_mouseup(Layer& layer, GUI::MouseEvent& event)
{
if (event.button() == m_drawing_button) {
GUI::Painter painter(m_widget->bitmap());
GUI::Painter painter(layer.bitmap());
draw_using(painter);
m_drawing_button = GUI::MouseButton::None;
m_widget->update();
m_editor->update();
}
}
void EllipseTool::on_mousemove(GUI::MouseEvent& event)
void EllipseTool::on_mousemove(Layer& layer, GUI::MouseEvent& event)
{
if (m_drawing_button == GUI::MouseButton::None)
return;
if (!m_widget->rect().contains(event.position()))
if (!layer.rect().contains(event.position()))
return;
m_ellipse_end_position = event.position();
m_widget->update();
m_editor->update();
}
void EllipseTool::on_second_paint(GUI::PaintEvent& event)
@ -93,7 +97,7 @@ void EllipseTool::on_second_paint(GUI::PaintEvent& event)
if (m_drawing_button == GUI::MouseButton::None)
return;
GUI::Painter painter(*m_widget);
GUI::Painter painter(*m_editor);
painter.add_clip_rect(event.rect());
draw_using(painter);
}
@ -102,7 +106,7 @@ 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_widget->update();
m_editor->update();
event.accept();
}
}
@ -131,3 +135,5 @@ void EllipseTool::on_contextmenu(GUI::ContextMenuEvent& event)
}
m_context_menu->popup(event.screen_position());
}
}

View file

@ -30,14 +30,16 @@
#include <LibGfx/Point.h>
#include <LibGUI/ActionGroup.h>
namespace PaintBrush {
class EllipseTool final : public Tool {
public:
EllipseTool();
virtual ~EllipseTool() override;
virtual void on_mousedown(GUI::MouseEvent&) override;
virtual void on_mousemove(GUI::MouseEvent&) override;
virtual void on_mouseup(GUI::MouseEvent&) override;
virtual void on_mousedown(Layer&, GUI::MouseEvent&) override;
virtual void on_mousemove(Layer&, GUI::MouseEvent&) override;
virtual void on_mouseup(Layer&, GUI::MouseEvent&) override;
virtual void on_contextmenu(GUI::ContextMenuEvent&) override;
virtual void on_second_paint(GUI::PaintEvent&) override;
virtual void on_keydown(GUI::KeyEvent&) override;
@ -59,3 +61,5 @@ private:
GUI::ActionGroup m_thickness_actions;
Mode m_mode { Mode::Outline };
};
}

View file

@ -25,12 +25,16 @@
*/
#include "EraseTool.h"
#include "ImageEditor.h"
#include "Layer.h"
#include "PaintableWidget.h"
#include <LibGUI/Action.h>
#include <LibGUI/Menu.h>
#include <LibGUI/Painter.h>
#include <LibGfx/Bitmap.h>
namespace PaintBrush {
EraseTool::EraseTool()
{
}
@ -49,26 +53,26 @@ Gfx::Rect EraseTool::build_rect(const Gfx::Point& pos, const Gfx::Rect& widget_r
return Gfx::Rect(ex - eraser_radius, ey - eraser_radius, eraser_size, eraser_size).intersected(widget_rect);
}
void EraseTool::on_mousedown(GUI::MouseEvent& event)
void EraseTool::on_mousedown(Layer& layer, GUI::MouseEvent& event)
{
if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right)
return;
Gfx::Rect r = build_rect(event.position(), m_widget->bitmap().rect());
GUI::Painter painter(m_widget->bitmap());
Gfx::Rect r = build_rect(event.position(), layer.rect());
GUI::Painter painter(layer.bitmap());
painter.clear_rect(r, get_color());
m_widget->update();
m_editor->update();
}
void EraseTool::on_mousemove(GUI::MouseEvent& event)
void EraseTool::on_mousemove(Layer& layer, GUI::MouseEvent& event)
{
if (!m_widget->rect().contains(event.position()))
if (!m_editor->rect().contains(event.position()))
return;
if (event.buttons() & GUI::MouseButton::Left || event.buttons() & GUI::MouseButton::Right) {
Gfx::Rect r = build_rect(event.position(), m_widget->bitmap().rect());
GUI::Painter painter(m_widget->bitmap());
Gfx::Rect r = build_rect(event.position(), layer.rect());
GUI::Painter painter(layer.bitmap());
painter.clear_rect(r, get_color());
m_widget->update();
m_editor->update();
}
}
@ -106,6 +110,8 @@ void EraseTool::on_contextmenu(GUI::ContextMenuEvent& event)
Color EraseTool::get_color() const
{
if (m_use_secondary_color)
return m_widget->secondary_color();
return PaintableWidget::the().secondary_color();
return Color(255, 255, 255, 0);
}
}

View file

@ -29,18 +29,21 @@
#include "Tool.h"
#include <LibGUI/ActionGroup.h>
#include <LibGfx/Point.h>
#include <LibGfx/Forward.h>
namespace PaintBrush {
class EraseTool final : public Tool {
public:
EraseTool();
virtual ~EraseTool() override;
virtual void on_mousedown(GUI::MouseEvent&) override;
virtual void on_mousemove(GUI::MouseEvent&) override;
virtual void on_mousedown(Layer&, GUI::MouseEvent&) override;
virtual void on_mousemove(Layer&, GUI::MouseEvent&) override;
virtual void on_contextmenu(GUI::ContextMenuEvent&) override;
private:
Color get_color() const;
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;
@ -49,3 +52,5 @@ private:
int m_thickness { 1 };
GUI::ActionGroup m_thickness_actions;
};
}

View file

@ -28,6 +28,7 @@
#include "Image.h"
#include "Layer.h"
#include "LayerModel.h"
#include "Tool.h"
#include <LibGUI/Painter.h>
#include <LibGfx/Palette.h>
@ -61,6 +62,43 @@ void ImageEditor::paint_event(GUI::PaintEvent& 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());
dbg() << "adjusted: " << position_in_active_layer_coordinates;
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_layer || !m_active_tool)
return;
auto layer_event = event_adjusted_for_layer(event, *m_active_layer);
m_active_tool->on_mousedown(*m_active_layer, layer_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);
}
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);
}
void ImageEditor::set_active_layer(Layer* layer)
{
if (m_active_layer == layer)
@ -69,4 +107,18 @@ void ImageEditor::set_active_layer(Layer* layer)
update();
}
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);
}
}

View file

@ -32,6 +32,7 @@ namespace PaintBrush {
class Image;
class Layer;
class Tool;
class ImageEditor final : public GUI::Frame {
C_OBJECT(ImageEditor);
@ -45,13 +46,21 @@ public:
Layer* active_layer() { return m_active_layer; }
void set_active_layer(Layer*);
Tool* active_tool() { return m_active_tool; }
void set_active_tool(Tool*);
private:
ImageEditor();
virtual void 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;
RefPtr<Image> m_image;
RefPtr<Layer> m_active_layer;
Tool* m_active_tool { nullptr };
};
}

View file

@ -25,12 +25,16 @@
*/
#include "LineTool.h"
#include "ImageEditor.h"
#include "Layer.h"
#include "PaintableWidget.h"
#include <LibGUI/Action.h>
#include <LibGUI/Menu.h>
#include <LibGUI/Painter.h>
#include <LibM/math.h>
namespace PaintBrush {
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.;
@ -52,7 +56,7 @@ LineTool::~LineTool()
{
}
void LineTool::on_mousedown(GUI::MouseEvent& event)
void LineTool::on_mousedown(Layer&, GUI::MouseEvent& event)
{
if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right)
return;
@ -63,25 +67,25 @@ void LineTool::on_mousedown(GUI::MouseEvent& event)
m_drawing_button = event.button();
m_line_start_position = event.position();
m_line_end_position = event.position();
m_widget->update();
m_editor->update();
}
void LineTool::on_mouseup(GUI::MouseEvent& event)
void LineTool::on_mouseup(Layer& layer, GUI::MouseEvent& event)
{
if (event.button() == m_drawing_button) {
GUI::Painter painter(m_widget->bitmap());
painter.draw_line(m_line_start_position, m_line_end_position, m_widget->color_for(m_drawing_button), m_thickness);
GUI::Painter painter(layer.bitmap());
painter.draw_line(m_line_start_position, m_line_end_position, PaintableWidget::the().color_for(m_drawing_button), m_thickness);
m_drawing_button = GUI::MouseButton::None;
m_widget->update();
m_editor->update();
}
}
void LineTool::on_mousemove(GUI::MouseEvent& event)
void LineTool::on_mousemove(Layer&, GUI::MouseEvent& event)
{
if (m_drawing_button == GUI::MouseButton::None)
return;
if (!m_widget->rect().contains(event.position()))
if (!m_editor->rect().contains(event.position()))
return;
if (!m_constrain_angle) {
@ -90,7 +94,7 @@ void LineTool::on_mousemove(GUI::MouseEvent& event)
const float ANGLE_STEP = M_PI / 8.0f;
m_line_end_position = constrain_line_angle(m_line_start_position, event.position(), ANGLE_STEP);
}
m_widget->update();
m_editor->update();
}
void LineTool::on_second_paint(GUI::PaintEvent& event)
@ -98,22 +102,26 @@ void LineTool::on_second_paint(GUI::PaintEvent& event)
if (m_drawing_button == GUI::MouseButton::None)
return;
(void)event;
#if 0
GUI::Painter painter(*m_widget);
painter.add_clip_rect(event.rect());
painter.draw_line(m_line_start_position, m_line_end_position, m_widget->color_for(m_drawing_button), m_thickness);
painter.draw_line(m_line_start_position, m_line_end_position, m_editor->color_for(m_drawing_button), m_thickness);
#endif
}
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_widget->update();
m_editor->update();
event.accept();
}
if (event.key() == Key_Shift) {
m_constrain_angle = true;
m_widget->update();
m_editor->update();
event.accept();
}
}
@ -122,7 +130,7 @@ void LineTool::on_keyup(GUI::KeyEvent& event)
{
if (event.key() == Key_Shift) {
m_constrain_angle = false;
m_widget->update();
m_editor->update();
event.accept();
}
}
@ -147,3 +155,5 @@ void LineTool::on_contextmenu(GUI::ContextMenuEvent& event)
}
m_context_menu->popup(event.screen_position());
}
}

View file

@ -27,17 +27,19 @@
#pragma once
#include "Tool.h"
#include <LibGfx/Point.h>
#include <LibGUI/ActionGroup.h>
#include <LibGfx/Point.h>
namespace PaintBrush {
class LineTool final : public Tool {
public:
LineTool();
virtual ~LineTool() override;
virtual void on_mousedown(GUI::MouseEvent&) override;
virtual void on_mousemove(GUI::MouseEvent&) override;
virtual void on_mouseup(GUI::MouseEvent&) override;
virtual void on_mousedown(Layer&, GUI::MouseEvent&) override;
virtual void on_mousemove(Layer&, GUI::MouseEvent&) override;
virtual void on_mouseup(Layer&, GUI::MouseEvent&) override;
virtual void on_contextmenu(GUI::ContextMenuEvent&) override;
virtual void on_second_paint(GUI::PaintEvent&) override;
virtual void on_keydown(GUI::KeyEvent&) override;
@ -54,3 +56,5 @@ private:
int m_thickness { 1 };
bool m_constrain_angle { false };
};
}

View file

@ -54,29 +54,6 @@ PaintableWidget::~PaintableWidget()
{
}
void PaintableWidget::paint_event(GUI::PaintEvent& event)
{
GUI::Painter painter(*this);
painter.add_clip_rect(event.rect());
painter.fill_rect_with_checkerboard(m_bitmap->rect(), { 8, 8 }, palette().base().darkened(0.9), palette().base());
painter.blit({ 0, 0 }, *m_bitmap, m_bitmap->rect());
}
void PaintableWidget::set_tool(Tool* tool)
{
if (m_tool)
m_tool->clear();
m_tool = tool;
if (m_tool)
m_tool->setup(*this);
}
Tool* PaintableWidget::tool()
{
return m_tool;
}
Color PaintableWidget::color_for(GUI::MouseButton button) const
{
if (button == GUI::MouseButton::Left)
@ -95,52 +72,6 @@ Color PaintableWidget::color_for(const GUI::MouseEvent& event) const
ASSERT_NOT_REACHED();
}
void PaintableWidget::mousedown_event(GUI::MouseEvent& event)
{
if (event.button() == GUI::MouseButton::Left || event.button() == GUI::MouseButton::Right) {
if (m_tool)
m_tool->on_mousedown(event);
}
GUI::Widget::mousedown_event(event);
}
void PaintableWidget::mouseup_event(GUI::MouseEvent& event)
{
if (event.button() == GUI::MouseButton::Left || event.button() == GUI::MouseButton::Right) {
if (m_tool)
m_tool->on_mouseup(event);
}
GUI::Widget::mouseup_event(event);
}
void PaintableWidget::mousemove_event(GUI::MouseEvent& event)
{
if (m_tool)
m_tool->on_mousemove(event);
GUI::Widget::mousemove_event(event);
}
void PaintableWidget::second_paint_event(GUI::PaintEvent& event)
{
if (m_tool)
m_tool->on_second_paint(event);
GUI::Widget::second_paint_event(event);
}
void PaintableWidget::keydown_event(GUI::KeyEvent& event)
{
if (m_tool)
m_tool->on_keydown(event);
GUI::Widget::keydown_event(event);
}
void PaintableWidget::keyup_event(GUI::KeyEvent& event)
{
if (m_tool)
m_tool->on_keyup(event);
GUI::Widget::keyup_event(event);
}
void PaintableWidget::set_primary_color(Color color)
{
if (m_primary_color == color)

View file

@ -27,7 +27,6 @@
#pragma once
#include <LibGUI/Widget.h>
class Tool;
class PaintableWidget final : public GUI::Widget {
C_OBJECT(PaintableWidget)
@ -42,9 +41,6 @@ public:
void set_primary_color(Color);
void set_secondary_color(Color);
void set_tool(Tool* tool);
Tool* tool();
Color color_for(const GUI::MouseEvent&) const;
Color color_for(GUI::MouseButton) const;
@ -59,19 +55,8 @@ public:
private:
PaintableWidget();
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 mouseup_event(GUI::MouseEvent&) override;
virtual void mousemove_event(GUI::MouseEvent&) override;
virtual void keydown_event(GUI::KeyEvent&) override;
virtual void keyup_event(GUI::KeyEvent&) override;
RefPtr<Gfx::Bitmap> m_bitmap;
Color m_primary_color { Color::Black };
Color m_secondary_color { Color::White };
Tool* m_tool { nullptr };
};

View file

@ -25,11 +25,15 @@
*/
#include "PenTool.h"
#include "ImageEditor.h"
#include "Layer.h"
#include "PaintableWidget.h"
#include <LibGUI/Action.h>
#include <LibGUI/Menu.h>
#include <LibGUI/Painter.h>
namespace PaintBrush {
PenTool::PenTool()
{
}
@ -38,36 +42,36 @@ PenTool::~PenTool()
{
}
void PenTool::on_mousedown(GUI::MouseEvent& event)
void PenTool::on_mousedown(Layer& layer, GUI::MouseEvent& event)
{
if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right)
return;
GUI::Painter painter(m_widget->bitmap());
painter.draw_line(event.position(), event.position(), m_widget->color_for(event), m_thickness);
m_widget->update();
GUI::Painter painter(layer.bitmap());
painter.draw_line(event.position(), event.position(), PaintableWidget::the().color_for(event), m_thickness);
m_editor->update();
m_last_drawing_event_position = event.position();
}
void PenTool::on_mouseup(GUI::MouseEvent& event)
void PenTool::on_mouseup(Layer&, GUI::MouseEvent& event)
{
if (event.button() == GUI::MouseButton::Left || event.button() == GUI::MouseButton::Right)
m_last_drawing_event_position = { -1, -1 };
}
void PenTool::on_mousemove(GUI::MouseEvent& event)
void PenTool::on_mousemove(Layer& layer, GUI::MouseEvent& event)
{
if (!m_widget->rect().contains(event.position()))
if (!layer.rect().contains(event.position()))
return;
if (event.buttons() & GUI::MouseButton::Left || event.buttons() & GUI::MouseButton::Right) {
GUI::Painter painter(m_widget->bitmap());
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_widget->color_for(event), m_thickness);
painter.draw_line(m_last_drawing_event_position, event.position(), PaintableWidget::the().color_for(event), m_thickness);
else
painter.draw_line(event.position(), event.position(), m_widget->color_for(event), m_thickness);
m_widget->update();
painter.draw_line(event.position(), event.position(), PaintableWidget::the().color_for(event), m_thickness);
m_editor->update();
m_last_drawing_event_position = event.position();
}
@ -93,3 +97,5 @@ void PenTool::on_contextmenu(GUI::ContextMenuEvent& event)
}
m_context_menu->popup(event.screen_position());
}
}

View file

@ -30,14 +30,16 @@
#include <LibGfx/Point.h>
#include <LibGUI/ActionGroup.h>
namespace PaintBrush {
class PenTool final : public Tool {
public:
PenTool();
virtual ~PenTool() override;
virtual void on_mousedown(GUI::MouseEvent&) override;
virtual void on_mousemove(GUI::MouseEvent&) override;
virtual void on_mouseup(GUI::MouseEvent&) override;
virtual void on_mousedown(Layer&, GUI::MouseEvent&) override;
virtual void on_mousemove(Layer&, GUI::MouseEvent&) override;
virtual void on_mouseup(Layer&, GUI::MouseEvent&) override;
virtual void on_contextmenu(GUI::ContextMenuEvent&) override;
private:
@ -48,3 +50,5 @@ private:
int m_thickness { 1 };
GUI::ActionGroup m_thickness_actions;
};
}

View file

@ -25,8 +25,12 @@
*/
#include "PickerTool.h"
#include "Layer.h"
#include "PaintableWidget.h"
#include <LibGfx/Bitmap.h>
namespace PaintBrush {
PickerTool::PickerTool()
{
}
@ -35,14 +39,15 @@ PickerTool::~PickerTool()
{
}
void PickerTool::on_mousedown(GUI::MouseEvent& event)
void PickerTool::on_mousedown(Layer& layer, GUI::MouseEvent& event)
{
ASSERT(m_widget);
if (!m_widget->bitmap().rect().contains(event.position()))
if (!layer.rect().contains(event.position()))
return;
auto color = m_widget->bitmap().get_pixel(event.position());
auto color = layer.bitmap().get_pixel(event.position());
if (event.button() == GUI::MouseButton::Left)
m_widget->set_primary_color(color);
PaintableWidget::the().set_primary_color(color);
else if (event.button() == GUI::MouseButton::Right)
m_widget->set_secondary_color(color);
PaintableWidget::the().set_secondary_color(color);
}
}

View file

@ -28,14 +28,18 @@
#include "Tool.h"
namespace PaintBrush {
class PickerTool final : public Tool {
public:
PickerTool();
virtual ~PickerTool() override;
virtual void on_mousedown(GUI::MouseEvent&) override;
virtual void on_mousedown(Layer&, GUI::MouseEvent&) override;
private:
virtual const char* class_name() const override { return "PickerTool"; }
};
}

View file

@ -25,13 +25,17 @@
*/
#include "RectangleTool.h"
#include "ImageEditor.h"
#include "Layer.h"
#include "PaintableWidget.h"
#include <LibGfx/Rect.h>
#include <LibGUI/Action.h>
#include <LibGUI/Menu.h>
#include <LibGUI/Painter.h>
#include <LibGfx/Rect.h>
#include <LibM/math.h>
namespace PaintBrush {
RectangleTool::RectangleTool()
{
}
@ -45,20 +49,20 @@ 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_widget->color_for(m_drawing_button));
painter.fill_rect(rect_to_draw, PaintableWidget::the().color_for(m_drawing_button));
break;
case Mode::Outline:
painter.draw_rect(rect_to_draw, m_widget->color_for(m_drawing_button));
painter.draw_rect(rect_to_draw, PaintableWidget::the().color_for(m_drawing_button));
break;
case Mode::Gradient:
painter.fill_rect_with_gradient(rect_to_draw, m_widget->primary_color(), m_widget->secondary_color());
painter.fill_rect_with_gradient(rect_to_draw, PaintableWidget::the().primary_color(), PaintableWidget::the().secondary_color());
break;
default:
ASSERT_NOT_REACHED();
}
}
void RectangleTool::on_mousedown(GUI::MouseEvent& event)
void RectangleTool::on_mousedown(Layer&, GUI::MouseEvent& event)
{
if (event.button() != GUI::MouseButton::Left && event.button() != GUI::MouseButton::Right)
return;
@ -69,29 +73,29 @@ void RectangleTool::on_mousedown(GUI::MouseEvent& event)
m_drawing_button = event.button();
m_rectangle_start_position = event.position();
m_rectangle_end_position = event.position();
m_widget->update();
m_editor->update();
}
void RectangleTool::on_mouseup(GUI::MouseEvent& event)
void RectangleTool::on_mouseup(Layer& layer, GUI::MouseEvent& event)
{
if (event.button() == m_drawing_button) {
GUI::Painter painter(m_widget->bitmap());
GUI::Painter painter(layer.bitmap());
draw_using(painter);
m_drawing_button = GUI::MouseButton::None;
m_widget->update();
m_editor->update();
}
}
void RectangleTool::on_mousemove(GUI::MouseEvent& event)
void RectangleTool::on_mousemove(Layer&, GUI::MouseEvent& event)
{
if (m_drawing_button == GUI::MouseButton::None)
return;
if (!m_widget->rect().contains(event.position()))
if (!m_editor->rect().contains(event.position()))
return;
m_rectangle_end_position = event.position();
m_widget->update();
m_editor->update();
}
void RectangleTool::on_second_paint(GUI::PaintEvent& event)
@ -99,16 +103,19 @@ void RectangleTool::on_second_paint(GUI::PaintEvent& event)
if (m_drawing_button == GUI::MouseButton::None)
return;
(void)event;
#if 0
GUI::Painter painter(*m_widget);
painter.add_clip_rect(event.rect());
draw_using(painter);
#endif
}
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_widget->update();
m_editor->update();
event.accept();
}
}
@ -129,3 +136,5 @@ void RectangleTool::on_contextmenu(GUI::ContextMenuEvent& event)
}
m_context_menu->popup(event.screen_position());
}
}

View file

@ -28,15 +28,18 @@
#include "Tool.h"
#include <LibGfx/Point.h>
#include <LibGUI/Forward.h>
namespace PaintBrush {
class RectangleTool final : public Tool {
public:
RectangleTool();
virtual ~RectangleTool() override;
virtual void on_mousedown(GUI::MouseEvent&) override;
virtual void on_mousemove(GUI::MouseEvent&) override;
virtual void on_mouseup(GUI::MouseEvent&) override;
virtual void on_mousedown(Layer&, GUI::MouseEvent&) override;
virtual void on_mousemove(Layer&, GUI::MouseEvent&) override;
virtual void on_mouseup(Layer&, GUI::MouseEvent&) override;
virtual void on_contextmenu(GUI::ContextMenuEvent&) override;
virtual void on_second_paint(GUI::PaintEvent&) override;
virtual void on_keydown(GUI::KeyEvent&) override;
@ -57,3 +60,5 @@ private:
RefPtr<GUI::Menu> m_context_menu;
Mode m_mode { Mode::Outline };
};
}

View file

@ -25,15 +25,19 @@
*/
#include "SprayTool.h"
#include "ImageEditor.h"
#include "Layer.h"
#include "PaintableWidget.h"
#include <AK/Queue.h>
#include <AK/SinglyLinkedList.h>
#include <LibGUI/Painter.h>
#include <LibGUI/Action.h>
#include <LibGUI/Menu.h>
#include <LibGUI/Painter.h>
#include <LibGfx/Bitmap.h>
#include <stdio.h>
#include <LibM/math.h>
#include <stdio.h>
namespace PaintBrush {
SprayTool::SprayTool()
{
@ -55,10 +59,14 @@ static double nrand()
void SprayTool::paint_it()
{
GUI::Painter painter(m_widget->bitmap());
auto& bitmap = m_widget->bitmap();
auto* layer = m_editor->active_layer();
if (!layer)
return;
auto& bitmap = layer->bitmap();
GUI::Painter painter(bitmap);
ASSERT(bitmap.bpp() == 32);
m_widget->update();
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++) {
@ -74,18 +82,18 @@ void SprayTool::paint_it()
}
}
void SprayTool::on_mousedown(GUI::MouseEvent& event)
void SprayTool::on_mousedown(Layer&, GUI::MouseEvent& event)
{
if (!m_widget->rect().contains(event.position()))
if (!m_editor->rect().contains(event.position()))
return;
m_color = m_widget->color_for(event);
m_color = PaintableWidget::the().color_for(event);
m_last_pos = event.position();
m_timer->start();
paint_it();
}
void SprayTool::on_mousemove(GUI::MouseEvent& event)
void SprayTool::on_mousemove(Layer&, GUI::MouseEvent& event)
{
m_last_pos = event.position();
if (m_timer->is_active()) {
@ -94,7 +102,7 @@ void SprayTool::on_mousemove(GUI::MouseEvent& event)
}
}
void SprayTool::on_mouseup(GUI::MouseEvent&)
void SprayTool::on_mouseup(Layer&, GUI::MouseEvent&)
{
m_timer->stop();
}
@ -120,3 +128,4 @@ void SprayTool::on_contextmenu(GUI::ContextMenuEvent& event)
m_context_menu->popup(event.screen_position());
}
}

View file

@ -31,14 +31,16 @@
#include <LibGUI/ActionGroup.h>
#include <LibGUI/Painter.h>
namespace PaintBrush {
class SprayTool final : public Tool {
public:
SprayTool();
virtual ~SprayTool() override;
virtual void on_mousedown(GUI::MouseEvent&) override;
virtual void on_mouseup(GUI::MouseEvent&) override;
virtual void on_mousemove(GUI::MouseEvent&) override;
virtual void on_mousedown(Layer&, GUI::MouseEvent&) override;
virtual void on_mouseup(Layer&, GUI::MouseEvent&) override;
virtual void on_mousemove(Layer&, GUI::MouseEvent&) override;
virtual void on_contextmenu(GUI::ContextMenuEvent&) override;
private:
@ -51,3 +53,5 @@ private:
GUI::ActionGroup m_thickness_actions;
int m_thickness { 1 };
};
}

View file

@ -25,6 +25,9 @@
*/
#include "Tool.h"
#include "ImageEditor.h"
namespace PaintBrush {
Tool::Tool()
{
@ -33,3 +36,10 @@ Tool::Tool()
Tool::~Tool()
{
}
void Tool::setup(ImageEditor& editor)
{
m_editor = editor.make_weak_ptr();
}
}

View file

@ -26,7 +26,12 @@
#pragma once
#include "PaintableWidget.h"
#include <LibGUI/Event.h>
namespace PaintBrush {
class ImageEditor;
class Layer;
class Tool {
public:
@ -34,18 +39,20 @@ public:
virtual const char* class_name() const = 0;
virtual void on_mousedown(GUI::MouseEvent&) {}
virtual void on_mousemove(GUI::MouseEvent&) {}
virtual void on_mouseup(GUI::MouseEvent&) {}
virtual void on_mousedown(Layer&, GUI::MouseEvent&) {}
virtual void on_mousemove(Layer&, GUI::MouseEvent&) {}
virtual void on_mouseup(Layer&, GUI::MouseEvent&) {}
virtual void on_contextmenu(GUI::ContextMenuEvent&) {}
virtual void on_second_paint(GUI::PaintEvent&) {}
virtual void on_keydown(GUI::KeyEvent&) {}
virtual void on_keyup(GUI::KeyEvent&) {}
void clear() { m_widget = nullptr; }
void setup(PaintableWidget& widget) { m_widget = widget.make_weak_ptr(); }
void clear() { m_editor = nullptr; }
void setup(ImageEditor&);
protected:
Tool();
WeakPtr<PaintableWidget> m_widget;
WeakPtr<ImageEditor> m_editor;
};
}

View file

@ -37,6 +37,8 @@
#include <LibGUI/BoxLayout.h>
#include <LibGUI/Button.h>
namespace PaintBrush {
class ToolButton final : public GUI::Button {
C_OBJECT(ToolButton)
public:
@ -82,11 +84,11 @@ ToolboxWidget::ToolboxWidget()
button.set_icon(Gfx::Bitmap::load_from_file(String::format("/res/icons/paintbrush/%s.png", icon_name.to_string().characters())));
button.on_checked = [button = &button](auto checked) {
button.on_checked = [this, button = &button](auto checked) {
if (checked)
PaintableWidget::the().set_tool(&button->tool());
on_tool_selection(&button->tool());
else
PaintableWidget::the().set_tool(nullptr);
on_tool_selection(nullptr);
};
};
@ -103,3 +105,5 @@ ToolboxWidget::ToolboxWidget()
ToolboxWidget::~ToolboxWidget()
{
}
}

View file

@ -28,9 +28,19 @@
#include <LibGUI/Frame.h>
namespace PaintBrush {
class Tool;
class ToolboxWidget final : public GUI::Frame {
C_OBJECT(ToolboxWidget)
public:
explicit ToolboxWidget();
virtual ~ToolboxWidget() override;
Function<void(Tool*)> on_tool_selection;
private:
explicit ToolboxWidget();
};
}

View file

@ -67,7 +67,7 @@ int main(int argc, char** argv)
horizontal_container.set_layout<GUI::HorizontalBoxLayout>();
horizontal_container.layout()->set_spacing(0);
horizontal_container.add<ToolboxWidget>();
auto& toolbox = horizontal_container.add<PaintBrush::ToolboxWidget>();
auto& vertical_container = horizontal_container.add<GUI::Widget>();
vertical_container.set_layout<GUI::VerticalBoxLayout>();
@ -76,6 +76,10 @@ int main(int argc, char** argv)
auto& image_editor = vertical_container.add<PaintBrush::ImageEditor>();
image_editor.set_focus(true);
toolbox.on_tool_selection = [&](auto* tool) {
image_editor.set_active_tool(tool);
};
auto& paintable_widget = vertical_container.add<PaintableWidget>();
paintable_widget.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fixed);
paintable_widget.set_preferred_size(0, 0);