click_imp.rs - icy_draw - icy_draw is the successor to mystic draw. fork / mirror
 (HTM) git clone https://git.drkhsh.at/icy_draw.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       click_imp.rs (19570B)
       ---
            1 use std::sync::Arc;
            2 
            3 use eframe::egui;
            4 use egui::mutex::Mutex;
            5 use i18n_embed_fl::fl;
            6 use icy_engine::{editor::AtomicUndoGuard, AddType, Rectangle, TextPane};
            7 use icy_engine_egui::TerminalCalc;
            8 
            9 use crate::{model::MKey, AnsiEditor, CharTableToolWindow, Document, Message};
           10 
           11 use super::{Event, MModifiers, Position, Tool};
           12 
           13 #[derive(Default)]
           14 enum SelectionDrag {
           15     #[default]
           16     None,
           17     Move,
           18     Left,
           19     Right,
           20     Top,
           21     Bottom,
           22 
           23     TopLeft,
           24     TopRight,
           25     BottomLeft,
           26     BottomRight,
           27 }
           28 
           29 #[derive(Default)]
           30 pub struct ClickTool {
           31     start_selection: Rectangle,
           32     selection_drag: SelectionDrag,
           33     undo_op: Option<AtomicUndoGuard>,
           34     char_table: Option<CharTableToolWindow>,
           35 }
           36 
           37 pub const VALID_OUTLINE_CHARS: &str = "ABCDEFGHIJKLMNO@&\u{F7} ";
           38 
           39 impl Tool for ClickTool {
           40     fn get_icon(&self) -> &'static egui::Image<'static> {
           41         &super::icons::TEXT_SVG
           42     }
           43 
           44     fn tool_name(&self) -> String {
           45         fl!(crate::LANGUAGE_LOADER, "tool-click_name")
           46     }
           47 
           48     fn tooltip(&self) -> String {
           49         fl!(crate::LANGUAGE_LOADER, "tool-click_tooltip")
           50     }
           51 
           52     fn use_caret(&self, editor: &AnsiEditor) -> bool {
           53         let is_selected = editor.buffer_view.lock().get_edit_state().is_something_selected();
           54         !is_selected
           55     }
           56 
           57     fn show_ui(&mut self, ctx: &egui::Context, ui: &mut egui::Ui, editor_opt: Option<&mut AnsiEditor>) -> Option<Message> {
           58         if self.char_table.is_none() {
           59             self.char_table = Some(CharTableToolWindow::new(ctx, 16));
           60         }
           61         let mut msg = None;
           62         if let Some(editor) = editor_opt {
           63             editor.color_mode.show_ui(ui);
           64 
           65             ui.vertical(|ui| {
           66                 ui.set_height(16.0 * 256.0 * 2.0);
           67                 msg = self.char_table.as_mut().unwrap().show_char_table(ui, editor);
           68             });
           69         }
           70         msg
           71     }
           72 
           73     fn show_doc_ui(&mut self, ctx: &egui::Context, ui: &mut egui::Ui, doc: Arc<Mutex<Box<dyn Document>>>) -> Option<Message> {
           74         if !doc.lock().can_paste_char() {
           75             return None;
           76         }
           77         if self.char_table.is_none() {
           78             self.char_table = Some(CharTableToolWindow::new(ctx, 16));
           79         }
           80         ui.vertical(|ui| {
           81             ui.set_height(16.0 * 256.0 * 2.0);
           82             let ch: Option<char> = self.char_table.as_mut().unwrap().show_plain_char_table(ui);
           83 
           84             if let Some(ch) = ch {
           85                 doc.lock().paste_char(ui, ch);
           86             }
           87         });
           88 
           89         None
           90     }
           91 
           92     fn handle_click(&mut self, editor: &mut AnsiEditor, button: i32, pos: Position, cur_abs: Position, _response: &egui::Response) -> Option<Message> {
           93         if button == 1 && !is_inside_selection(editor, cur_abs) {
           94             editor.set_caret_position(pos);
           95             editor.buffer_view.lock().clear_selection();
           96         }
           97         None
           98     }
           99 
          100     fn handle_drag_begin(&mut self, editor: &mut AnsiEditor, _response: &egui::Response) -> Event {
          101         self.selection_drag = get_selection_drag(editor, editor.drag_pos.start_abs);
          102 
          103         if !matches!(self.selection_drag, SelectionDrag::None) {
          104             if let Some(selection) = editor.buffer_view.lock().get_selection() {
          105                 self.start_selection = selection.as_rectangle();
          106             }
          107         }
          108         self.undo_op = Some(editor.begin_atomic_undo(fl!(crate::LANGUAGE_LOADER, "undo-select")));
          109 
          110         Event::None
          111     }
          112     fn handle_drag(&mut self, _ui: &egui::Ui, response: egui::Response, editor: &mut AnsiEditor, _calc: &TerminalCalc) -> egui::Response {
          113         let mut rect = if let Some(selection) = editor.buffer_view.lock().get_selection() {
          114             selection.as_rectangle()
          115         } else {
          116             Rectangle::from_coords(0, 0, 0, 0)
          117         };
          118 
          119         match self.selection_drag {
          120             SelectionDrag::Move => {
          121                 rect.start = self.start_selection.top_left() - editor.drag_pos.start_abs + editor.drag_pos.cur_abs;
          122                 editor.buffer_view.lock().set_selection(rect);
          123             }
          124             SelectionDrag::Left => {
          125                 self.move_left(editor, &mut rect);
          126                 editor.buffer_view.lock().set_selection(rect);
          127             }
          128             SelectionDrag::Right => {
          129                 self.move_right(editor, &mut rect);
          130                 editor.buffer_view.lock().set_selection(rect);
          131             }
          132             SelectionDrag::Top => {
          133                 self.move_top(editor, &mut rect);
          134                 editor.buffer_view.lock().set_selection(rect);
          135             }
          136             SelectionDrag::Bottom => {
          137                 self.move_bottom(editor, &mut rect);
          138                 editor.buffer_view.lock().set_selection(rect);
          139             }
          140             SelectionDrag::TopLeft => {
          141                 self.move_left(editor, &mut rect);
          142                 self.move_top(editor, &mut rect);
          143                 editor.buffer_view.lock().set_selection(rect);
          144             }
          145             SelectionDrag::TopRight => {
          146                 self.move_right(editor, &mut rect);
          147                 self.move_top(editor, &mut rect);
          148                 editor.buffer_view.lock().set_selection(rect);
          149             }
          150             SelectionDrag::BottomLeft => {
          151                 self.move_left(editor, &mut rect);
          152                 self.move_bottom(editor, &mut rect);
          153                 editor.buffer_view.lock().set_selection(rect);
          154             }
          155             SelectionDrag::BottomRight => {
          156                 self.move_right(editor, &mut rect);
          157                 self.move_bottom(editor, &mut rect);
          158                 editor.buffer_view.lock().set_selection(rect);
          159             }
          160 
          161             SelectionDrag::None => {
          162                 if editor.drag_pos.start == editor.drag_pos.cur {
          163                     editor.buffer_view.lock().clear_selection();
          164                 } else {
          165                     editor.buffer_view.lock().set_selection(Rectangle::from(
          166                         editor.drag_pos.start_abs.x.min(editor.drag_pos.cur_abs.x),
          167                         editor.drag_pos.start_abs.y.min(editor.drag_pos.cur_abs.y),
          168                         (editor.drag_pos.cur_abs.x - editor.drag_pos.start_abs.x).abs(),
          169                         (editor.drag_pos.cur_abs.y - editor.drag_pos.start_abs.y).abs(),
          170                     ));
          171                 }
          172             }
          173         }
          174 
          175         let lock = &mut editor.buffer_view.lock();
          176         if let Some(mut selection) = lock.get_selection() {
          177             if response.ctx.input(|i| i.modifiers.command_only()) {
          178                 selection.add_type = AddType::Subtract;
          179             }
          180             if response.ctx.input(|i| i.modifiers.shift_only()) {
          181                 selection.add_type = AddType::Add;
          182             }
          183             lock.set_selection(selection);
          184         }
          185 
          186         response
          187     }
          188 
          189     fn handle_hover(&mut self, ui: &egui::Ui, response: egui::Response, editor: &mut AnsiEditor, _cur: Position, cur_abs: Position) -> egui::Response {
          190         match get_selection_drag(editor, cur_abs) {
          191             SelectionDrag::None => ui.output_mut(|o| o.cursor_icon = egui::CursorIcon::Text),
          192             SelectionDrag::Move => ui.output_mut(|o| o.cursor_icon = egui::CursorIcon::Move),
          193             SelectionDrag::Left => ui.output_mut(|o| o.cursor_icon = egui::CursorIcon::ResizeWest),
          194             SelectionDrag::Right => ui.output_mut(|o| o.cursor_icon = egui::CursorIcon::ResizeEast),
          195             SelectionDrag::Top => ui.output_mut(|o| o.cursor_icon = egui::CursorIcon::ResizeNorth),
          196             SelectionDrag::Bottom => {
          197                 ui.output_mut(|o| o.cursor_icon = egui::CursorIcon::ResizeSouth);
          198             }
          199             SelectionDrag::TopLeft => {
          200                 ui.output_mut(|o| o.cursor_icon = egui::CursorIcon::ResizeNorthWest);
          201             }
          202             SelectionDrag::TopRight => {
          203                 ui.output_mut(|o| o.cursor_icon = egui::CursorIcon::ResizeNorthEast);
          204             }
          205             SelectionDrag::BottomLeft => {
          206                 ui.output_mut(|o| o.cursor_icon = egui::CursorIcon::ResizeSouthWest);
          207             }
          208             SelectionDrag::BottomRight => {
          209                 ui.output_mut(|o| o.cursor_icon = egui::CursorIcon::ResizeSouthEast);
          210             }
          211         }
          212         response
          213     }
          214 
          215     fn handle_drag_end(&mut self, editor: &mut AnsiEditor) -> Option<Message> {
          216         if !matches!(self.selection_drag, SelectionDrag::None) {
          217             self.selection_drag = SelectionDrag::None;
          218             self.undo_op = None;
          219             return None;
          220         }
          221 
          222         let mut cur = editor.drag_pos.cur;
          223         if editor.drag_pos.start < cur {
          224             cur += Position::new(1, 1);
          225         }
          226 
          227         if editor.drag_pos.start == cur {
          228             editor.buffer_view.lock().clear_selection();
          229         }
          230         self.undo_op = None;
          231 
          232         None
          233     }
          234 
          235     fn handle_key(&mut self, editor: &mut AnsiEditor, key: MKey, modifier: MModifiers) -> Event {
          236         // TODO Keys:
          237         // ctrl+pgup  - upper left corner
          238         // ctrl+pgdn  - lower left corner
          239         let pos = editor.buffer_view.lock().get_caret().get_position();
          240         match key {
          241             MKey::Down => {
          242                 if matches!(modifier, MModifiers::None) {
          243                     editor.set_caret(pos.x, pos.y + 1);
          244                 }
          245             }
          246             MKey::Up => {
          247                 if matches!(modifier, MModifiers::None) {
          248                     editor.set_caret(pos.x, pos.y - 1);
          249                 }
          250             }
          251             MKey::Left => {
          252                 if matches!(modifier, MModifiers::None) {
          253                     editor.set_caret(pos.x - 1, pos.y);
          254                 }
          255             }
          256             MKey::Right => {
          257                 if matches!(modifier, MModifiers::None) {
          258                     editor.set_caret(pos.x + 1, pos.y);
          259                 }
          260             }
          261             MKey::PageDown => {
          262                 let height = editor.buffer_view.lock().calc.terminal_rect.height();
          263                 let char_height = editor.buffer_view.lock().calc.char_size.y;
          264                 let pg_size = (height / char_height) as i32;
          265                 editor.set_caret(pos.x, pos.y + pg_size);
          266             }
          267             MKey::PageUp => {
          268                 let height = editor.buffer_view.lock().calc.terminal_rect.height();
          269                 let char_height = editor.buffer_view.lock().calc.char_size.y;
          270                 let pg_size = (height / char_height) as i32;
          271                 editor.set_caret(pos.x, pos.y - pg_size);
          272             }
          273 
          274             MKey::Escape => {
          275                 editor.buffer_view.lock().clear_selection();
          276             }
          277 
          278             MKey::Tab => {
          279                 let tab_size = 8;
          280                 if let MModifiers::Shift = modifier {
          281                     let tabs = ((pos.x / tab_size) - 1).max(0);
          282                     let next_tab = tabs * tab_size;
          283                     editor.set_caret(next_tab, pos.y);
          284                 } else {
          285                     let tabs = 1 + pos.x / tab_size;
          286                     let next_tab = (editor.buffer_view.lock().get_buffer().get_width() - 1).min(tabs * tab_size);
          287                     editor.set_caret(next_tab, pos.y);
          288                 }
          289             }
          290 
          291             MKey::Return => {
          292                 editor.set_caret(0, pos.y + 1);
          293             }
          294             MKey::Insert => {
          295                 let insert_mode = editor.buffer_view.lock().get_caret().insert_mode;
          296                 editor.buffer_view.lock().get_caret_mut().insert_mode = !insert_mode;
          297             }
          298             MKey::Backspace => {
          299                 editor.backspace();
          300             }
          301 
          302             MKey::Delete => {
          303                 if editor.buffer_view.lock().get_selection().is_none() {
          304                     editor.delete();
          305                 }
          306             }
          307 
          308             MKey::Home => {
          309                 let mut pos = editor.get_caret_position();
          310                 pos.x = 0;
          311 
          312                 if let MModifiers::Control = modifier {
          313                     pos.y = 0;
          314                 }
          315                 editor.set_caret(pos.x, pos.y);
          316             }
          317             MKey::End => {
          318                 let mut pos = editor.get_caret_position();
          319                 pos.x = i32::MAX;
          320                 if let MModifiers::Control = modifier {
          321                     pos.y = i32::MAX;
          322                 }
          323                 editor.set_caret(pos.x, pos.y);
          324             }
          325 
          326             MKey::Character(ch) => {
          327                 let typed_char = unsafe { char::from_u32_unchecked(ch as u32) };
          328                 if editor.outline_font_mode {
          329                     let typed_char = typed_char.to_ascii_uppercase();
          330                     if VALID_OUTLINE_CHARS.contains(typed_char) {
          331                         editor.type_key(typed_char);
          332                     } else if let '1'..='8' = typed_char {
          333                         editor.type_key(VALID_OUTLINE_CHARS.chars().nth(10 + typed_char as usize - b'1' as usize).unwrap());
          334                     }
          335                 } else {
          336                     editor.type_key(typed_char);
          337                 }
          338             }
          339 
          340             MKey::F1 => {
          341                 if matches!(modifier, MModifiers::None) {
          342                     if editor.outline_font_mode {
          343                         editor.type_key(VALID_OUTLINE_CHARS.chars().next().unwrap());
          344                     } else {
          345                         editor.type_char_set_key(0);
          346                     }
          347                 }
          348             }
          349             MKey::F2 => {
          350                 if matches!(modifier, MModifiers::None) {
          351                     if editor.outline_font_mode {
          352                         editor.type_key(VALID_OUTLINE_CHARS.chars().nth(1).unwrap());
          353                     } else {
          354                         editor.type_char_set_key(1);
          355                     }
          356                 }
          357             }
          358             MKey::F3 => {
          359                 if matches!(modifier, MModifiers::None) {
          360                     if editor.outline_font_mode {
          361                         editor.type_key(VALID_OUTLINE_CHARS.chars().nth(2).unwrap());
          362                     } else {
          363                         editor.type_char_set_key(2);
          364                     }
          365                 }
          366             }
          367             MKey::F4 => {
          368                 if matches!(modifier, MModifiers::None) {
          369                     if editor.outline_font_mode {
          370                         editor.type_key(VALID_OUTLINE_CHARS.chars().nth(3).unwrap());
          371                     } else {
          372                         editor.type_char_set_key(3);
          373                     }
          374                 }
          375             }
          376             MKey::F5 => {
          377                 if matches!(modifier, MModifiers::None) {
          378                     if editor.outline_font_mode {
          379                         editor.type_key(VALID_OUTLINE_CHARS.chars().nth(4).unwrap());
          380                     } else {
          381                         editor.type_char_set_key(4);
          382                     }
          383                 }
          384             }
          385             MKey::F6 => {
          386                 if matches!(modifier, MModifiers::None) {
          387                     if editor.outline_font_mode {
          388                         editor.type_key(VALID_OUTLINE_CHARS.chars().nth(5).unwrap());
          389                     } else {
          390                         editor.type_char_set_key(5);
          391                     }
          392                 }
          393             }
          394             MKey::F7 => {
          395                 if matches!(modifier, MModifiers::None) {
          396                     if editor.outline_font_mode {
          397                         editor.type_key(VALID_OUTLINE_CHARS.chars().nth(6).unwrap());
          398                     } else {
          399                         editor.type_char_set_key(6);
          400                     }
          401                 }
          402             }
          403             MKey::F8 => {
          404                 if matches!(modifier, MModifiers::None) {
          405                     if editor.outline_font_mode {
          406                         editor.type_key(VALID_OUTLINE_CHARS.chars().nth(7).unwrap());
          407                     } else {
          408                         editor.type_char_set_key(7);
          409                     }
          410                 }
          411             }
          412             MKey::F9 => {
          413                 if matches!(modifier, MModifiers::None) {
          414                     if editor.outline_font_mode {
          415                         editor.type_key(VALID_OUTLINE_CHARS.chars().nth(8).unwrap());
          416                     } else {
          417                         editor.type_char_set_key(8);
          418                     }
          419                 }
          420             }
          421             MKey::F10 => {
          422                 if matches!(modifier, MModifiers::None) {
          423                     if editor.outline_font_mode {
          424                         editor.type_key(VALID_OUTLINE_CHARS.chars().nth(9).unwrap());
          425                     } else {
          426                         editor.type_char_set_key(9);
          427                     }
          428                 }
          429             }
          430             _ => {}
          431         }
          432         Event::None
          433     }
          434 }
          435 
          436 impl ClickTool {
          437     fn move_left(&mut self, editor: &AnsiEditor, rect: &mut Rectangle) {
          438         let delta = editor.drag_pos.start_abs.x - editor.drag_pos.cur_abs.x;
          439         rect.start.x = self.start_selection.left() - delta;
          440         rect.size.width = self.start_selection.get_width() + delta;
          441 
          442         if rect.size.width < 0 {
          443             rect.size.width = rect.start.x - self.start_selection.right();
          444             rect.start.x = self.start_selection.right();
          445         }
          446     }
          447 
          448     fn move_right(&mut self, editor: &AnsiEditor, rect: &mut Rectangle) {
          449         rect.size.width = self.start_selection.get_width() - editor.drag_pos.start_abs.x + editor.drag_pos.cur_abs.x;
          450         if rect.size.width < 0 {
          451             rect.start.x = self.start_selection.left() + rect.size.width;
          452             rect.size.width = self.start_selection.left() - rect.start.x;
          453         }
          454     }
          455 
          456     fn move_top(&mut self, editor: &AnsiEditor, rect: &mut Rectangle) {
          457         let delta = editor.drag_pos.start_abs.y - editor.drag_pos.cur_abs.y;
          458         rect.start.y = self.start_selection.top() - delta;
          459         rect.size.height = self.start_selection.get_height() + delta;
          460 
          461         if rect.size.height < 0 {
          462             rect.size.height = rect.start.y - self.start_selection.bottom();
          463             rect.start.y = self.start_selection.bottom();
          464         }
          465     }
          466 
          467     fn move_bottom(&mut self, editor: &AnsiEditor, rect: &mut Rectangle) {
          468         rect.size.height = self.start_selection.get_height() - editor.drag_pos.start_abs.y + editor.drag_pos.cur_abs.y;
          469         if rect.size.height < 0 {
          470             rect.start.y = self.start_selection.top() + rect.size.height;
          471             rect.size.height = self.start_selection.top() - rect.start.y;
          472         }
          473     }
          474 }
          475 
          476 fn is_inside_selection(editor: &AnsiEditor, cur_abs: Position) -> bool {
          477     if let Some(selection) = editor.buffer_view.lock().get_selection() {
          478         return selection.is_inside(cur_abs);
          479     }
          480     false
          481 }
          482 
          483 fn get_selection_drag(editor: &AnsiEditor, cur_abs: Position) -> SelectionDrag {
          484     if let Some(selection) = editor.buffer_view.lock().get_selection() {
          485         let rect = selection.as_rectangle();
          486 
          487         if rect.is_inside(cur_abs) {
          488             let left = cur_abs.x - rect.left() < 2;
          489             let top = cur_abs.y - rect.top() < 2;
          490             let right = rect.right() - cur_abs.x < 2;
          491             let bottom = rect.bottom() - cur_abs.y < 2;
          492 
          493             if left && top {
          494                 return SelectionDrag::TopLeft;
          495             }
          496 
          497             if right && top {
          498                 return SelectionDrag::TopRight;
          499             }
          500             if left && bottom {
          501                 return SelectionDrag::BottomLeft;
          502             }
          503 
          504             if right && bottom {
          505                 return SelectionDrag::BottomRight;
          506             }
          507 
          508             if left {
          509                 return SelectionDrag::Left;
          510             }
          511             if right {
          512                 return SelectionDrag::Right;
          513             }
          514 
          515             if top {
          516                 return SelectionDrag::Top;
          517             }
          518             if bottom {
          519                 return SelectionDrag::Bottom;
          520             }
          521 
          522             return SelectionDrag::Move;
          523         }
          524     }
          525     SelectionDrag::None
          526 }