mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 00:57:45 +00:00
PixelPaint: Relate cursor to brush tool size
This patch changes the cursor for the brush tool to a circle of dynamic size to indicate the region where the tool will apply color changes.
This commit is contained in:
parent
e9ca641d45
commit
e520b9c3a3
6 changed files with 87 additions and 6 deletions
|
@ -17,6 +17,7 @@
|
||||||
#include "ResizeImageDialog.h"
|
#include "ResizeImageDialog.h"
|
||||||
#include <Applications/PixelPaint/PixelPaintWindowGML.h>
|
#include <Applications/PixelPaint/PixelPaintWindowGML.h>
|
||||||
#include <LibConfig/Client.h>
|
#include <LibConfig/Client.h>
|
||||||
|
#include <LibCore/Debounce.h>
|
||||||
#include <LibCore/File.h>
|
#include <LibCore/File.h>
|
||||||
#include <LibCore/MimeData.h>
|
#include <LibCore/MimeData.h>
|
||||||
#include <LibFileSystemAccessClient/Client.h>
|
#include <LibFileSystemAccessClient/Client.h>
|
||||||
|
@ -1100,9 +1101,11 @@ ImageEditor& MainWidget::create_new_editor(NonnullRefPtr<Image> image)
|
||||||
m_show_rulers_action->set_checked(show_rulers);
|
m_show_rulers_action->set_checked(show_rulers);
|
||||||
};
|
};
|
||||||
|
|
||||||
image_editor.on_scale_change = [this](float scale) {
|
image_editor.on_scale_change = Core::debounce([this](float scale) {
|
||||||
m_zoom_combobox->set_text(String::formatted("{}%", roundf(scale * 100)));
|
m_zoom_combobox->set_text(String::formatted("{}%", roundf(scale * 100)));
|
||||||
};
|
current_image_editor()->update_tool_cursor();
|
||||||
|
},
|
||||||
|
100);
|
||||||
|
|
||||||
if (image->layer_count())
|
if (image->layer_count())
|
||||||
image_editor.set_active_layer(&image->layer(0));
|
image_editor.set_active_layer(&image->layer(0));
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Ben Jilks <benjyjilks@gmail.com>
|
* Copyright (c) 2020, Ben Jilks <benjyjilks@gmail.com>
|
||||||
* Copyright (c) 2021, Mustafa Quraish <mustafa@serenityos.org>
|
* Copyright (c) 2021, Mustafa Quraish <mustafa@serenityos.org>
|
||||||
|
* Copyright (c) 2022, Torsten Engelmann <engelTorsten@gmx.de>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -13,11 +14,20 @@
|
||||||
#include <LibGUI/Label.h>
|
#include <LibGUI/Label.h>
|
||||||
#include <LibGUI/Painter.h>
|
#include <LibGUI/Painter.h>
|
||||||
#include <LibGUI/ValueSlider.h>
|
#include <LibGUI/ValueSlider.h>
|
||||||
|
#include <LibGfx/AntiAliasingPainter.h>
|
||||||
#include <LibGfx/Color.h>
|
#include <LibGfx/Color.h>
|
||||||
#include <LibGfx/Rect.h>
|
#include <LibGfx/Rect.h>
|
||||||
|
|
||||||
namespace PixelPaint {
|
namespace PixelPaint {
|
||||||
|
|
||||||
|
void BrushTool::set_size(int size)
|
||||||
|
{
|
||||||
|
if (size == m_size)
|
||||||
|
return;
|
||||||
|
m_size = size;
|
||||||
|
refresh_editor_cursor();
|
||||||
|
}
|
||||||
|
|
||||||
void BrushTool::on_mousedown(Layer* layer, MouseEvent& event)
|
void BrushTool::on_mousedown(Layer* layer, MouseEvent& event)
|
||||||
{
|
{
|
||||||
if (!layer)
|
if (!layer)
|
||||||
|
@ -145,9 +155,12 @@ GUI::Widget* BrushTool::get_properties_widget()
|
||||||
auto& size_slider = size_container.add<GUI::ValueSlider>(Orientation::Horizontal, "px");
|
auto& size_slider = size_container.add<GUI::ValueSlider>(Orientation::Horizontal, "px");
|
||||||
size_slider.set_range(1, 100);
|
size_slider.set_range(1, 100);
|
||||||
size_slider.set_value(m_size);
|
size_slider.set_value(m_size);
|
||||||
|
size_slider.set_override_cursor(cursor());
|
||||||
|
|
||||||
size_slider.on_change = [&](int value) {
|
size_slider.on_change = [&](int value) {
|
||||||
set_size(value);
|
set_size(value);
|
||||||
|
// Update cursor to provide an instant preview for the selected size.
|
||||||
|
size_slider.set_override_cursor(cursor());
|
||||||
};
|
};
|
||||||
set_primary_slider(&size_slider);
|
set_primary_slider(&size_slider);
|
||||||
|
|
||||||
|
@ -172,4 +185,30 @@ GUI::Widget* BrushTool::get_properties_widget()
|
||||||
return m_properties_widget.ptr();
|
return m_properties_widget.ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NonnullRefPtr<Gfx::Bitmap> BrushTool::build_cursor()
|
||||||
|
{
|
||||||
|
m_scale_last_created_cursor = m_editor ? m_editor->scale() : 1;
|
||||||
|
auto scaled_size = size() * m_scale_last_created_cursor;
|
||||||
|
auto containing_box_size = 2 * scaled_size;
|
||||||
|
NonnullRefPtr<Gfx::Bitmap> new_cursor = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, Gfx::IntSize(containing_box_size, containing_box_size)).release_value_but_fixme_should_propagate_errors();
|
||||||
|
|
||||||
|
Gfx::Painter painter { new_cursor };
|
||||||
|
Gfx::AntiAliasingPainter aa_painter { painter };
|
||||||
|
|
||||||
|
painter.draw_line({ scaled_size - 5, scaled_size }, { scaled_size + 5, scaled_size }, Color::LightGray, 3);
|
||||||
|
painter.draw_line({ scaled_size, scaled_size - 5 }, { scaled_size, scaled_size + 5 }, Color::LightGray, 3);
|
||||||
|
painter.draw_line({ scaled_size - 5, scaled_size }, { scaled_size + 5, scaled_size }, Color::MidGray, 1);
|
||||||
|
painter.draw_line({ scaled_size, scaled_size - 5 }, { scaled_size, scaled_size + 5 }, Color::MidGray, 1);
|
||||||
|
aa_painter.draw_ellipse(Gfx::IntRect(0, 0, containing_box_size, containing_box_size), Color::LightGray, 1);
|
||||||
|
|
||||||
|
return new_cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrushTool::refresh_editor_cursor()
|
||||||
|
{
|
||||||
|
m_cursor = build_cursor();
|
||||||
|
if (m_editor)
|
||||||
|
m_editor->update_tool_cursor();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
* Copyright (c) 2020, Ben Jilks <benjyjilks@gmail.com>
|
* Copyright (c) 2020, Ben Jilks <benjyjilks@gmail.com>
|
||||||
* Copyright (c) 2021, Mustafa Quraish <mustafa@serenityos.org>
|
* Copyright (c) 2021, Mustafa Quraish <mustafa@serenityos.org>
|
||||||
* Copyright (c) 2022, the SerenityOS developers.
|
* Copyright (c) 2022, the SerenityOS developers.
|
||||||
|
* Copyright (c) 2022, Torsten Engelmann <engelTorsten@gmx.de>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "../ImageEditor.h"
|
||||||
#include "Tool.h"
|
#include "Tool.h"
|
||||||
|
|
||||||
namespace PixelPaint {
|
namespace PixelPaint {
|
||||||
|
@ -21,9 +23,14 @@ public:
|
||||||
virtual void on_mousemove(Layer*, MouseEvent&) override;
|
virtual void on_mousemove(Layer*, MouseEvent&) override;
|
||||||
virtual void on_mouseup(Layer*, MouseEvent&) override;
|
virtual void on_mouseup(Layer*, MouseEvent&) override;
|
||||||
virtual GUI::Widget* get_properties_widget() override;
|
virtual GUI::Widget* get_properties_widget() override;
|
||||||
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override
|
||||||
|
{
|
||||||
|
if (m_editor && m_editor->scale() != m_scale_last_created_cursor)
|
||||||
|
refresh_editor_cursor();
|
||||||
|
return m_cursor;
|
||||||
|
}
|
||||||
|
|
||||||
void set_size(int size) { m_size = size; }
|
void set_size(int size);
|
||||||
int size() const { return m_size; }
|
int size() const { return m_size; }
|
||||||
|
|
||||||
void set_hardness(int hardness) { m_hardness = hardness; }
|
void set_hardness(int hardness) { m_hardness = hardness; }
|
||||||
|
@ -41,6 +48,9 @@ protected:
|
||||||
virtual Color color_for(GUI::MouseEvent const& event);
|
virtual Color color_for(GUI::MouseEvent const& event);
|
||||||
virtual void draw_point(Gfx::Bitmap& bitmap, Gfx::Color const& color, Gfx::IntPoint const& point);
|
virtual void draw_point(Gfx::Bitmap& bitmap, Gfx::Color const& color, Gfx::IntPoint const& point);
|
||||||
virtual void draw_line(Gfx::Bitmap& bitmap, Gfx::Color const& color, Gfx::IntPoint const& start, Gfx::IntPoint const& end);
|
virtual void draw_line(Gfx::Bitmap& bitmap, Gfx::Color const& color, Gfx::IntPoint const& start, Gfx::IntPoint const& end);
|
||||||
|
virtual NonnullRefPtr<Gfx::Bitmap> build_cursor();
|
||||||
|
void refresh_editor_cursor();
|
||||||
|
float m_scale_last_created_cursor = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RefPtr<GUI::Widget> m_properties_widget;
|
RefPtr<GUI::Widget> m_properties_widget;
|
||||||
|
@ -49,6 +59,7 @@ private:
|
||||||
bool m_was_drawing { false };
|
bool m_was_drawing { false };
|
||||||
bool m_has_clicked { false };
|
bool m_has_clicked { false };
|
||||||
Gfx::IntPoint m_last_position;
|
Gfx::IntPoint m_last_position;
|
||||||
|
NonnullRefPtr<Gfx::Bitmap> m_cursor = build_cursor();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,7 @@ GUI::Widget* EraseTool::get_properties_widget()
|
||||||
|
|
||||||
size_slider.on_change = [&](int value) {
|
size_slider.on_change = [&](int value) {
|
||||||
set_size(value);
|
set_size(value);
|
||||||
|
size_slider.set_override_cursor(cursor());
|
||||||
};
|
};
|
||||||
set_primary_slider(&size_slider);
|
set_primary_slider(&size_slider);
|
||||||
|
|
||||||
|
@ -119,10 +120,14 @@ GUI::Widget* EraseTool::get_properties_widget()
|
||||||
pencil_mode_radio.on_checked = [&](bool) {
|
pencil_mode_radio.on_checked = [&](bool) {
|
||||||
m_draw_mode = DrawMode::Pencil;
|
m_draw_mode = DrawMode::Pencil;
|
||||||
hardness_slider.set_enabled(false);
|
hardness_slider.set_enabled(false);
|
||||||
|
refresh_editor_cursor();
|
||||||
|
size_slider.set_override_cursor(cursor());
|
||||||
};
|
};
|
||||||
brush_mode_radio.on_checked = [&](bool) {
|
brush_mode_radio.on_checked = [&](bool) {
|
||||||
m_draw_mode = DrawMode::Brush;
|
m_draw_mode = DrawMode::Brush;
|
||||||
hardness_slider.set_enabled(true);
|
hardness_slider.set_enabled(true);
|
||||||
|
refresh_editor_cursor();
|
||||||
|
size_slider.set_override_cursor(cursor());
|
||||||
};
|
};
|
||||||
|
|
||||||
pencil_mode_radio.set_checked(true);
|
pencil_mode_radio.set_checked(true);
|
||||||
|
@ -131,4 +136,26 @@ GUI::Widget* EraseTool::get_properties_widget()
|
||||||
return m_properties_widget.ptr();
|
return m_properties_widget.ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NonnullRefPtr<Gfx::Bitmap> EraseTool::build_cursor()
|
||||||
|
{
|
||||||
|
if (m_draw_mode == DrawMode::Brush)
|
||||||
|
return BrushTool::build_cursor();
|
||||||
|
|
||||||
|
m_scale_last_created_cursor = m_editor ? m_editor->scale() : 1;
|
||||||
|
int scaled_size = size() * m_scale_last_created_cursor;
|
||||||
|
|
||||||
|
NonnullRefPtr<Gfx::Bitmap> new_cursor = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, Gfx::IntSize(scaled_size, scaled_size)).release_value_but_fixme_should_propagate_errors();
|
||||||
|
|
||||||
|
Gfx::IntRect rect { 0, 0, scaled_size, scaled_size };
|
||||||
|
Gfx::Painter painter { new_cursor };
|
||||||
|
|
||||||
|
painter.draw_rect(rect, Color::LightGray);
|
||||||
|
painter.draw_line({ scaled_size / 2 - 5, scaled_size / 2 }, { scaled_size / 2 + 5, scaled_size / 2 }, Color::LightGray, 3);
|
||||||
|
painter.draw_line({ scaled_size / 2, scaled_size / 2 - 5 }, { scaled_size / 2, scaled_size / 2 + 5 }, Color::LightGray, 3);
|
||||||
|
painter.draw_line({ scaled_size / 2 - 5, scaled_size / 2 }, { scaled_size / 2 + 5, scaled_size / 2 }, Color::MidGray, 1);
|
||||||
|
painter.draw_line({ scaled_size / 2, scaled_size / 2 - 5 }, { scaled_size / 2, scaled_size / 2 + 5 }, Color::MidGray, 1);
|
||||||
|
|
||||||
|
return new_cursor;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
virtual Color color_for(GUI::MouseEvent const& event) override;
|
virtual Color color_for(GUI::MouseEvent const& event) override;
|
||||||
virtual void draw_point(Gfx::Bitmap& bitmap, Gfx::Color const& color, Gfx::IntPoint const& point) override;
|
virtual void draw_point(Gfx::Bitmap& bitmap, Gfx::Color const& color, Gfx::IntPoint const& point) override;
|
||||||
|
virtual NonnullRefPtr<Gfx::Bitmap> build_cursor() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual StringView tool_name() const override { return "Erase Tool"sv; }
|
virtual StringView tool_name() const override { return "Erase Tool"sv; }
|
||||||
|
@ -35,7 +36,7 @@ private:
|
||||||
Pencil,
|
Pencil,
|
||||||
Brush,
|
Brush,
|
||||||
};
|
};
|
||||||
DrawMode m_draw_mode { DrawMode::Brush };
|
DrawMode m_draw_mode { DrawMode::Pencil };
|
||||||
bool m_use_secondary_color { false };
|
bool m_use_secondary_color { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ class PenTool final : public BrushTool {
|
||||||
public:
|
public:
|
||||||
PenTool();
|
PenTool();
|
||||||
virtual ~PenTool() override = default;
|
virtual ~PenTool() override = default;
|
||||||
|
virtual Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> cursor() override { return Gfx::StandardCursor::Crosshair; }
|
||||||
virtual GUI::Widget* get_properties_widget() override;
|
virtual GUI::Widget* get_properties_widget() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue