URI: 
       fill_imp.rs - icy_draw - icy_draw is the successor to mystic draw. fork / mirror
  HTML git clone https://git.drkhsh.at/icy_draw.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       fill_imp.rs (6299B)
       ---
            1 use std::{cell::RefCell, collections::HashSet, rc::Rc};
            2 
            3 use eframe::egui;
            4 use i18n_embed_fl::fl;
            5 use icy_engine::{AttributedChar, Size, TextPane};
            6 
            7 use crate::{
            8     paint::{BrushMode, ColorMode},
            9     AnsiEditor, Message,
           10 };
           11 
           12 use super::{Position, Tool};
           13 
           14 pub struct FillTool {
           15     color_mode: ColorMode,
           16 
           17     char_code: std::rc::Rc<std::cell::RefCell<char>>,
           18     fill_type: BrushMode,
           19     use_exact_matching: bool,
           20 }
           21 
           22 impl FillTool {
           23     pub fn new() -> Self {
           24         let c = Rc::new(RefCell::new('\u{00B0}'));
           25         Self {
           26             color_mode: ColorMode::Both,
           27             char_code: c.clone(),
           28             fill_type: BrushMode::Char(c),
           29             use_exact_matching: false,
           30         }
           31     }
           32 }
           33 #[allow(clippy::struct_excessive_bools)]
           34 struct FillOperation {
           35     fill_type: BrushMode,
           36     color_mode: ColorMode,
           37     use_exact_matching: bool,
           38 
           39     size: Size,
           40     pub offset: Position,
           41     use_selection: bool,
           42     base_char: AttributedChar,
           43     new_char: AttributedChar,
           44     visited: HashSet<Position>,
           45 }
           46 
           47 impl FillOperation {
           48     pub fn new(fill_tool: &FillTool, editor: &AnsiEditor, base_char: AttributedChar, new_ch: AttributedChar) -> Self {
           49         let lock = &editor.buffer_view.lock();
           50         let state = lock.get_edit_state();
           51         let size = state.get_cur_layer().unwrap().get_size();
           52         let use_selection = state.is_something_selected();
           53         let offset = if let Some(layer) = state.get_cur_layer() {
           54             layer.get_offset()
           55         } else {
           56             Position::default()
           57         };
           58 
           59         Self {
           60             size,
           61             color_mode: fill_tool.color_mode,
           62             fill_type: fill_tool.fill_type.clone(),
           63             use_selection,
           64             base_char,
           65             offset,
           66             new_char: new_ch,
           67             use_exact_matching: fill_tool.use_exact_matching,
           68             visited: HashSet::new(),
           69         }
           70     }
           71 
           72     pub fn fill(&mut self, editor: &mut AnsiEditor, pos: Position) {
           73         let mut pos_stack = vec![pos];
           74 
           75         while let Some(pos) = pos_stack.pop() {
           76             if pos.x < 0 || pos.y < 0 || pos.x >= self.size.width || pos.y >= self.size.height || !self.visited.insert(pos) {
           77                 continue;
           78             }
           79 
           80             if !self.use_selection || editor.buffer_view.lock().get_edit_state().get_is_selected(pos + self.offset) {
           81                 let cur_char = editor.buffer_view.lock().get_edit_state().get_cur_layer().unwrap().get_char(pos);
           82 
           83                 let mut repl_ch = cur_char;
           84 
           85                 match &self.fill_type {
           86                     BrushMode::Char(_) => {
           87                         if self.use_exact_matching && cur_char != self.base_char || !self.use_exact_matching && cur_char.ch != self.base_char.ch {
           88                             continue;
           89                         }
           90                         repl_ch.ch = self.new_char.ch;
           91                         repl_ch.set_font_page(self.new_char.get_font_page());
           92                     }
           93                     BrushMode::Colorize => {
           94                         if self.use_exact_matching && cur_char != self.base_char || !self.use_exact_matching && cur_char.attribute != self.base_char.attribute {
           95                             continue;
           96                         }
           97                     }
           98                     _ => {}
           99                 }
          100                 if self.color_mode.use_fore() {
          101                     repl_ch.attribute.set_foreground(self.new_char.attribute.get_foreground());
          102                     repl_ch.attribute.set_is_bold(self.new_char.attribute.is_bold());
          103                 }
          104 
          105                 if self.color_mode.use_back() {
          106                     repl_ch.attribute.set_background(self.new_char.attribute.get_background());
          107                 }
          108 
          109                 repl_ch.set_font_page(editor.buffer_view.lock().get_caret().get_attribute().get_font_page());
          110                 repl_ch.attribute.attr &= !icy_engine::attribute::INVISIBLE;
          111                 editor.set_char(pos, repl_ch);
          112             }
          113 
          114             pos_stack.push(pos + Position::new(-1, 0));
          115             pos_stack.push(pos + Position::new(1, 0));
          116             pos_stack.push(pos + Position::new(0, -1));
          117             pos_stack.push(pos + Position::new(0, 1));
          118         }
          119     }
          120 }
          121 
          122 // Fill with
          123 // Attribute, Fore/Back
          124 // Character
          125 // Both
          126 impl Tool for FillTool {
          127     fn get_icon(&self) -> &egui::Image<'static> {
          128         &super::icons::FILL_SVG
          129     }
          130 
          131     fn tool_name(&self) -> String {
          132         fl!(crate::LANGUAGE_LOADER, "tool-fill_name")
          133     }
          134 
          135     fn tooltip(&self) -> String {
          136         fl!(crate::LANGUAGE_LOADER, "tool-fill_tooltip")
          137     }
          138 
          139     fn use_caret(&self, _editor: &AnsiEditor) -> bool {
          140         false
          141     }
          142     fn show_ui(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui, editor_opt: Option<&mut AnsiEditor>) -> Option<Message> {
          143         self.color_mode.show_ui(ui);
          144 
          145         ui.checkbox(&mut self.use_exact_matching, fl!(crate::LANGUAGE_LOADER, "tool-fill-exact_match_label"));
          146 
          147         self.fill_type.show_ui(ui, editor_opt, self.char_code.clone(), crate::paint::BrushUi::Fill)
          148     }
          149 
          150     fn handle_hover(&mut self, _ui: &egui::Ui, response: egui::Response, _editor: &mut AnsiEditor, _cur: Position, _cur_abs: Position) -> egui::Response {
          151         response.on_hover_cursor(egui::CursorIcon::Crosshair)
          152     }
          153 
          154     fn handle_click(&mut self, editor: &mut AnsiEditor, button: i32, pos: Position, _pos_abs: Position, _response: &egui::Response) -> Option<Message> {
          155         if button == 1 {
          156             let Ok(layer) = editor.get_cur_layer_index() else { return None };
          157             if layer >= editor.buffer_view.lock().get_buffer().layers.len() {
          158                 return None;
          159             }
          160             let attr = editor.buffer_view.lock().get_caret().get_attribute();
          161             let ch = if let Some(layer) = editor.buffer_view.lock().get_edit_state().get_cur_layer() {
          162                 layer.get_char(pos)
          163             } else {
          164                 return None;
          165             };
          166             if self.color_mode.use_fore() || self.color_mode.use_back() || matches!(self.fill_type, BrushMode::Char(_)) {
          167                 let _undo = editor.begin_atomic_undo(fl!(crate::LANGUAGE_LOADER, "undo-bucket-fill"));
          168                 let mut op = FillOperation::new(self, editor, ch, AttributedChar::new(*self.char_code.borrow(), attr));
          169                 op.fill(editor, pos);
          170             }
          171         }
          172         None
          173     }
          174 }