diff --git a/assets/joint-fixer-icon.png b/assets/joint-fixer-icon.png new file mode 100644 index 0000000..d24eed3 Binary files /dev/null and b/assets/joint-fixer-icon.png differ diff --git a/src/tools/joint_fixer.rs b/src/tools/joint_fixer.rs new file mode 100644 index 0000000..6e0e400 --- /dev/null +++ b/src/tools/joint_fixer.rs @@ -0,0 +1,254 @@ +use druid::EventCtx; + +use crate::{ + consts::{ + CHAR_CORNER_BL_L, CHAR_CORNER_BR_L, CHAR_CORNER_TL_L, CHAR_CORNER_TR_L, CHAR_CROSS, + CHAR_HOR_DOWN_L, CHAR_HOR_L, CHAR_HOR_UP_L, CHAR_SPACE, CHAR_VER_L, CHAR_VER_LEFT_L, + CHAR_VER_RIGHT_L, + }, + data::{ + grid_list::GridList, + history::{Version, HISTORY_MANAGER}, + shape_list::ShapeList, + }, +}; + +use super::ToolControl; + +pub struct JointFixerTool { + version: Version, + last_cursor_position: Option, +} + +macro_rules! idx_to_opt_char { + ($var:ident,$input:ident,$g:ident) => { + let $var = if let Some(i) = $input { + match $g.get(i).read_content() { + CHAR_SPACE => None, + c => Some(c), + } + } else { + None + }; + }; +} + +macro_rules! UP_CHARS { + () => { + CHAR_VER_L + | CHAR_CORNER_TR_L + | CHAR_CORNER_TL_L + | CHAR_HOR_DOWN_L + | CHAR_VER_RIGHT_L + | CHAR_VER_LEFT_L + | CHAR_CROSS + }; +} + +macro_rules! DOWN_CHARS { + () => { + CHAR_VER_L + | CHAR_CORNER_BL_L + | CHAR_CORNER_BR_L + | CHAR_HOR_UP_L + | CHAR_VER_RIGHT_L + | CHAR_VER_LEFT_L + | CHAR_CROSS + }; +} + +macro_rules! LEFT_CHARS { + () => { + CHAR_HOR_L + | CHAR_CORNER_TL_L + | CHAR_CORNER_BL_L + | CHAR_HOR_UP_L + | CHAR_HOR_DOWN_L + | CHAR_VER_RIGHT_L + | CHAR_CROSS + }; +} + +macro_rules! RIGHT_CHARS { + () => { + CHAR_HOR_L + | CHAR_CORNER_TR_L + | CHAR_CORNER_BR_L + | CHAR_HOR_UP_L + | CHAR_HOR_DOWN_L + | CHAR_VER_LEFT_L + | CHAR_CROSS + }; +} + +impl JointFixerTool { + pub fn new() -> Self { + Self { + version: Version::new(), + last_cursor_position: None, + } + } + + fn calculate_content( + &self, + grid_list: &mut GridList, + u: Option, + d: Option, + l: Option, + r: Option, + ) -> Option { + idx_to_opt_char!(uc, u, grid_list); + idx_to_opt_char!(dc, d, grid_list); + idx_to_opt_char!(lc, l, grid_list); + idx_to_opt_char!(rc, r, grid_list); + + match (uc, dc, lc, rc) { + (None, None, None, None) => None, + // Vertical + // (Some(UP_CHARS!()), None, None, None) => Some(CHAR_VER_L), + // (None, Some(DOWN_CHARS!()), None, None) => Some(CHAR_VER_L), + (Some(UP_CHARS!()), Some(DOWN_CHARS!()), l, r) + if !matches!((l, r), (Some(LEFT_CHARS!()), _) | (_, Some(RIGHT_CHARS!()))) => + { + Some(CHAR_VER_L) + } + // Horizontal + // (None, None, Some(LEFT_CHARS!()), None) => Some(CHAR_HOR_L), + // (None, None, None, Some(RIGHT_CHARS!())) => Some(CHAR_HOR_L), + (u, d, Some(LEFT_CHARS!()), Some(RIGHT_CHARS!())) + if !matches!((u, d), (Some(UP_CHARS!()), _) | (_, Some(DOWN_CHARS!()))) => + { + Some(CHAR_HOR_L) + } + // Two joint + (Some(UP_CHARS!()), d, Some(LEFT_CHARS!()), r) + if !matches!((d, r), (Some(DOWN_CHARS!()), _) | (_, Some(RIGHT_CHARS!()))) => + { + Some(CHAR_CORNER_BR_L) + } + (Some(UP_CHARS!()), d, l, Some(RIGHT_CHARS!())) + if !matches!((d, l), (Some(DOWN_CHARS!()), _) | (_, Some(LEFT_CHARS!()))) => + { + Some(CHAR_CORNER_BL_L) + } + (u, Some(DOWN_CHARS!()), Some(LEFT_CHARS!()), r) + if !matches!((u, r), (Some(UP_CHARS!()), _) | (_, Some(RIGHT_CHARS!()))) => + { + Some(CHAR_CORNER_TR_L) + } + (u, Some(DOWN_CHARS!()), l, Some(RIGHT_CHARS!())) + if !matches!((u, l), (Some(UP_CHARS!()), _) | (_, Some(LEFT_CHARS!()))) => + { + Some(CHAR_CORNER_TL_L) + } + // Three joint + (Some(UP_CHARS!()), Some(DOWN_CHARS!()), Some(LEFT_CHARS!()), r) + if !matches!(r, Some(RIGHT_CHARS!())) => + { + Some(CHAR_VER_LEFT_L) + } + (Some(UP_CHARS!()), Some(DOWN_CHARS!()), l, Some(RIGHT_CHARS!())) + if !matches!(l, Some(LEFT_CHARS!())) => + { + Some(CHAR_VER_RIGHT_L) + } + (Some(UP_CHARS!()), d, Some(LEFT_CHARS!()), Some(RIGHT_CHARS!())) + if !matches!(d, Some(DOWN_CHARS!())) => + { + Some(CHAR_HOR_UP_L) + } + (u, Some(DOWN_CHARS!()), Some(LEFT_CHARS!()), Some(RIGHT_CHARS!())) + if !matches!(u, Some(UP_CHARS!())) => + { + Some(CHAR_HOR_DOWN_L) + } + // Four joint + (Some(UP_CHARS!()), Some(DOWN_CHARS!()), Some(LEFT_CHARS!()), Some(RIGHT_CHARS!())) => { + Some(CHAR_CROSS) + } + _ => None, + } + } +} + +impl ToolControl for JointFixerTool { + fn start( + &mut self, + _ctx: &mut EventCtx, + _event: &druid::MouseEvent, + _shape_list: &mut ShapeList, + _grid_list: &mut GridList, + ) { + self.last_cursor_position = None; + } + + fn draw( + &mut self, + _ctx: &mut EventCtx, + event: &druid::MouseEvent, + _shape_list: &mut ShapeList, + grid_list: &mut GridList, + ) { + let (cell_width, cell_height) = grid_list.cell_size; + let row = (event.pos.y / cell_height) as usize; + let col = (event.pos.x / cell_width) as usize; + let (rows, cols) = grid_list.grid_size; + let i = row * cols + col; + + if let Some(last_cursor_pos) = self.last_cursor_position { + if i == last_cursor_pos { + return; + } + } + + let up_i = match row { + 0 => None, + _ => Some((row - 1) * cols + col), + }; + + let down_i = match row { + r if r >= rows - 1 => None, + _ => Some((row + 1) * cols + col), + }; + + let left_i = match col { + 0 => None, + _ => Some(i - 1), + }; + + let right_i = match col { + c if c >= cols - 1 => None, + _ => Some(i + 1), + }; + + if let Some(content) = self.calculate_content(grid_list, up_i, down_i, left_i, right_i) { + self.last_cursor_position = Some(i); + let cell = grid_list.get(i); + let from_content = cell.read_content(); + self.version.push(i, from_content, content); + cell.set_content(content); + } + } + + fn end( + &mut self, + _ctx: &mut EventCtx, + _event: &druid::MouseEvent, + _shape_list: &mut ShapeList, + _grid_list: &mut GridList, + ) { + unsafe { + HISTORY_MANAGER.save_version(self.version.clone()); + self.version.clear(); + } + } + + fn input( + &mut self, + _ctx: &mut EventCtx, + _event: &druid::KeyEvent, + _shape_list: &mut ShapeList, + _grid_list: &mut GridList, + ) { + } +} diff --git a/src/tools/mod.rs b/src/tools/mod.rs index 0644512..f575bd0 100644 --- a/src/tools/mod.rs +++ b/src/tools/mod.rs @@ -10,9 +10,10 @@ use crate::{ tools::{line::LineTool, text::TextTool}, }; -use self::{eraser::EraserTool, rect::RectTool, select::SelectTool}; +use self::{eraser::EraserTool, joint_fixer::JointFixerTool, rect::RectTool, select::SelectTool}; pub mod eraser; +pub mod joint_fixer; pub mod line; pub mod rect; pub mod select; @@ -25,6 +26,7 @@ pub enum DrawingTools { Text = 2, Eraser = 3, Rect = 4, + JointFixer = 5, } impl Display for DrawingTools { @@ -35,6 +37,7 @@ impl Display for DrawingTools { DrawingTools::Text => "TEXT", DrawingTools::Eraser => "ERASER", DrawingTools::Rect => "RECTANGLE", + DrawingTools::JointFixer => "JOINTFIXER", }; write!(f, "{}", op) } @@ -99,6 +102,7 @@ impl ToolManager { Box::new(TextTool::new()), Box::new(EraserTool::new()), Box::new(RectTool::new()), + Box::new(JointFixerTool::new()), ], current: DrawingTools::Select, } diff --git a/src/widgets/grid.rs b/src/widgets/grid.rs index 17942fc..cae75bc 100644 --- a/src/widgets/grid.rs +++ b/src/widgets/grid.rs @@ -108,6 +108,9 @@ impl Widget for CanvasGrid { Code::Digit4 | Code::KeyE => { win_data.mode = DrawingTools::Eraser; } + Code::Digit5 => { + win_data.mode = DrawingTools::JointFixer; + } Code::Delete | Code::Backspace => { self.grid_list.erase_highlighted(); self.grid_list.clear_all_highlight(); @@ -286,6 +289,7 @@ impl Widget for CanvasGrid { DrawingTools::Rect => ctx.set_cursor(&Cursor::Crosshair), DrawingTools::Text => ctx.set_cursor(&Cursor::IBeam), DrawingTools::Eraser => ctx.set_cursor(&Cursor::Crosshair), + DrawingTools::JointFixer => ctx.set_cursor(&Cursor::Crosshair), } } } diff --git a/src/widgets/toolbar.rs b/src/widgets/toolbar.rs index 46041f4..d9d0b60 100644 --- a/src/widgets/toolbar.rs +++ b/src/widgets/toolbar.rs @@ -23,6 +23,8 @@ impl ToolBarWidget { let rect_icon = ImageBuf::from_data(include_bytes!("../../assets/rect-icon.png")).unwrap(); let eraser_icon = ImageBuf::from_data(include_bytes!("../../assets/eraser-icon.png")).unwrap(); + let joint_fixer_icon = + ImageBuf::from_data(include_bytes!("../../assets/joint-fixer-icon.png")).unwrap(); let save_icon = ImageBuf::from_data(include_bytes!("../../assets/save-icon.png")).unwrap(); let open_icon = ImageBuf::from_data(include_bytes!("../../assets/open-icon.png")).unwrap(); @@ -117,6 +119,24 @@ impl ToolBarWidget { ctx.set_handled(); }), ) + .with_spacer(4.0) + .with_child( + ImageButton::new( + joint_fixer_icon, + Size::new(26.0, 26.0), + DrawingTools::JointFixer.to_string(), + ) + .on_click(|ctx, data: &mut ApplicationState, _env| { + let win_data = data + .windows + .get_mut(&ctx.window_id()) + .expect("Invalid WindowID"); + let tool = DrawingTools::JointFixer; + win_data.mode = tool; + ctx.submit_notification(BUTTON_HIGHLIGHT_COMMAND.with(tool.to_string())); + ctx.set_handled(); + }), + ) .cross_axis_alignment(CrossAxisAlignment::End) .main_axis_alignment(MainAxisAlignment::Start), );