feat: Implement eraser
This commit is contained in:
192
src/paint.rs
192
src/paint.rs
@@ -3,7 +3,7 @@ use crate::mouse_area::mouse_area;
|
||||
use iced::Theme;
|
||||
use iced::padding;
|
||||
use iced::widget::container;
|
||||
use iced::widget::{Column, button, column, image, row, text};
|
||||
use iced::widget::{Column, button, column, image, pick_list, row, text};
|
||||
use iced::{Border, Color, Element, Length, Point, Renderer, Task};
|
||||
use iced_core::color;
|
||||
|
||||
@@ -12,8 +12,41 @@ use crate::mscanvas::MSCanvas;
|
||||
const WIDTH: u32 = 800;
|
||||
const HEIGHT: u32 = 600;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
enum BrushKind {
|
||||
#[default]
|
||||
Circle,
|
||||
Square,
|
||||
Slash,
|
||||
Backslash,
|
||||
}
|
||||
|
||||
impl BrushKind {
|
||||
const ALL: [BrushKind; 4] = [
|
||||
BrushKind::Circle,
|
||||
BrushKind::Square,
|
||||
BrushKind::Slash,
|
||||
BrushKind::Backslash,
|
||||
];
|
||||
}
|
||||
|
||||
impl std::fmt::Display for BrushKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
BrushKind::Circle => "Circle",
|
||||
BrushKind::Square => "Square",
|
||||
BrushKind::Slash => "Slash",
|
||||
BrushKind::Backslash => "Backslash",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Message {
|
||||
enum Message {
|
||||
MousePressed(Point),
|
||||
MouseReleased(Point),
|
||||
MouseMoved(Point),
|
||||
@@ -23,12 +56,14 @@ pub enum Message {
|
||||
|
||||
ClickTool(Tool),
|
||||
|
||||
Increment,
|
||||
Decrement,
|
||||
Increment(ConfigOption),
|
||||
Decrement(ConfigOption),
|
||||
|
||||
BrushSelected(BrushKind),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Tool {
|
||||
enum Tool {
|
||||
FreeFormSelect,
|
||||
Select,
|
||||
Eraser,
|
||||
@@ -97,6 +132,47 @@ impl From<Tool> for usize {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum ConfigOption {
|
||||
EraserWidth,
|
||||
LineWidth,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Config {
|
||||
eraser_width: i32,
|
||||
line_width: i32,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self::DEFAULT.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
const DEFAULT: Config = Config {
|
||||
line_width: 1,
|
||||
eraser_width: 4,
|
||||
};
|
||||
fn incr(&mut self, option: ConfigOption, step: i32) {
|
||||
match option {
|
||||
ConfigOption::EraserWidth => {
|
||||
self.eraser_width += step;
|
||||
if self.eraser_width < Self::DEFAULT.eraser_width {
|
||||
self.eraser_width = Self::DEFAULT.eraser_width;
|
||||
}
|
||||
}
|
||||
ConfigOption::LineWidth => {
|
||||
self.line_width += step;
|
||||
if self.line_width < Self::DEFAULT.line_width {
|
||||
self.line_width = Self::DEFAULT.line_width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PaintApp {
|
||||
tool_states: [bool; Tool::Count as usize],
|
||||
tool_selected: Tool,
|
||||
@@ -115,34 +191,39 @@ struct PaintApp {
|
||||
// 标记像素是否被修改,用于优化图像句柄的生成
|
||||
dirty: bool,
|
||||
|
||||
value: i32,
|
||||
config: Config,
|
||||
|
||||
brush_selected: Option<BrushKind>,
|
||||
}
|
||||
|
||||
impl PaintApp {
|
||||
// region iced application (new view update)
|
||||
|
||||
pub fn new() -> Self {
|
||||
let canvas = MSCanvas::new(WIDTH as i32, HEIGHT as i32);
|
||||
let mut canvas = MSCanvas::new(WIDTH as i32, HEIGHT as i32);
|
||||
let (width, height) = canvas.size();
|
||||
let pixels = canvas.get_pixels();
|
||||
let config = Config::default();
|
||||
canvas.set_line_width(config.line_width);
|
||||
Self {
|
||||
tool_states: [false; Tool::Count as usize],
|
||||
tool_selected: Tool::Count,
|
||||
canvas,
|
||||
is_drawing: false,
|
||||
begin_point: Point::ORIGIN,
|
||||
image_handle: image::Handle::from_rgba(WIDTH, HEIGHT, pixels),
|
||||
image_handle: image::Handle::from_rgba(width as u32, height as u32, pixels),
|
||||
dirty: false,
|
||||
|
||||
value: 1,
|
||||
config,
|
||||
brush_selected: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view(&self) -> Column<'_, Message> {
|
||||
// 创建显示图像的 Widget
|
||||
// 如果 handle 还没准备好,显示一个占位符
|
||||
let (width, height) = self.canvas.size();
|
||||
let image_widget = image(self.image_handle.clone())
|
||||
.width(Length::Fixed(WIDTH as f32))
|
||||
.height(Length::Fixed(HEIGHT as f32));
|
||||
.width(Length::Fixed(width as f32))
|
||||
.height(Length::Fixed(height as f32));
|
||||
|
||||
let canvas_area = mouse_area(image_widget)
|
||||
.on_press(|pos| Message::MousePressed(pos)) // 占位,实际逻辑在 on_drag 或自定义
|
||||
@@ -207,24 +288,39 @@ impl PaintApp {
|
||||
}
|
||||
});
|
||||
|
||||
let debug_area = column![
|
||||
text("brush"),
|
||||
let mut debug_area = column![
|
||||
text("Eraser Width"),
|
||||
row![
|
||||
button("+").on_press(Message::Increment),
|
||||
text(self.value),
|
||||
button("-").on_press(Message::Decrement),
|
||||
]
|
||||
button("+").on_press(Message::Increment(ConfigOption::EraserWidth)),
|
||||
text(self.config.eraser_width).size(20).center(),
|
||||
button("-").on_press(Message::Decrement(ConfigOption::EraserWidth)),
|
||||
],
|
||||
text("Line Width"),
|
||||
row![
|
||||
button("+").on_press(Message::Increment(ConfigOption::LineWidth)),
|
||||
text(self.config.line_width).size(20).center(),
|
||||
button("-").on_press(Message::Decrement(ConfigOption::LineWidth)),
|
||||
],
|
||||
pick_list(
|
||||
&BrushKind::ALL[..],
|
||||
self.brush_selected,
|
||||
Message::BrushSelected,
|
||||
)
|
||||
.placeholder("Brush..."),
|
||||
];
|
||||
debug_area = debug_area.padding(padding::left(5).right(5));
|
||||
|
||||
column![
|
||||
button("CLEAR").on_press(Message::Clear),
|
||||
row![tool_area, canvas_area],
|
||||
debug_area,
|
||||
row![tool_area, canvas_area, debug_area],
|
||||
]
|
||||
}
|
||||
|
||||
pub fn update(&mut self, message: Message) -> Task<Message> {
|
||||
match self.tool_selected {
|
||||
Tool::Eraser => {
|
||||
self.update_with_eraser(message);
|
||||
}
|
||||
Tool::FillWithColor => {
|
||||
self.update_with_fill_with_color(message);
|
||||
}
|
||||
@@ -255,16 +351,17 @@ impl PaintApp {
|
||||
Message::ClickTool(tool) => {
|
||||
self.update_tool_states(tool);
|
||||
}
|
||||
Message::Increment => {
|
||||
self.value = self.value + 1;
|
||||
self.canvas.set_brush_radius(self.value as f32 / 2.0);
|
||||
}
|
||||
Message::Decrement => {
|
||||
self.value = self.value - 1;
|
||||
if self.value < 1 {
|
||||
self.value = 1;
|
||||
Message::Increment(opt) => {
|
||||
self.config.incr(opt, 1);
|
||||
if opt == ConfigOption::LineWidth {
|
||||
self.canvas.set_line_width(self.config.line_width);
|
||||
}
|
||||
}
|
||||
Message::Decrement(opt) => {
|
||||
self.config.incr(opt, 1);
|
||||
if opt == ConfigOption::LineWidth {
|
||||
self.canvas.set_line_width(self.config.line_width);
|
||||
}
|
||||
self.canvas.set_brush_radius(self.value as f32 / 2.0);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -284,6 +381,36 @@ impl PaintApp {
|
||||
|
||||
// region tool update
|
||||
|
||||
pub fn update_with_eraser(&mut self, message: Message) {
|
||||
match message {
|
||||
Message::MousePressed(pos) => {
|
||||
self.is_drawing = true;
|
||||
self.canvas.clear_rect(
|
||||
(pos.x as i32) - self.config.eraser_width / 2,
|
||||
(pos.y as i32) - self.config.eraser_width / 2,
|
||||
self.config.eraser_width,
|
||||
self.config.eraser_width,
|
||||
);
|
||||
self.dirty = true;
|
||||
}
|
||||
Message::MouseReleased(_) => {
|
||||
self.is_drawing = false;
|
||||
}
|
||||
Message::MouseMoved(pos) => {
|
||||
if self.is_drawing {
|
||||
self.canvas.clear_rect(
|
||||
(pos.x as i32) - self.config.eraser_width / 2,
|
||||
(pos.y as i32) - self.config.eraser_width / 2,
|
||||
self.config.eraser_width,
|
||||
self.config.eraser_width,
|
||||
);
|
||||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_with_fill_with_color(&mut self, message: Message) {
|
||||
match message {
|
||||
Message::MousePressed(pos) => {
|
||||
@@ -320,7 +447,7 @@ impl PaintApp {
|
||||
match message {
|
||||
Message::MousePressed(pos) => {
|
||||
self.is_drawing = true;
|
||||
self.canvas.draw_brush_at(pos);
|
||||
self.canvas.brush_circle(pos);
|
||||
self.canvas.save_pixels();
|
||||
self.begin_point = pos;
|
||||
self.dirty = true;
|
||||
@@ -332,7 +459,8 @@ impl PaintApp {
|
||||
Message::MouseMoved(pos) => {
|
||||
if self.is_drawing {
|
||||
self.canvas.restore_pixels();
|
||||
self.canvas.draw_line_thick(self.begin_point, pos);
|
||||
self.canvas
|
||||
.draw_line_with_circle_brush(self.begin_point, pos);
|
||||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user