pipette_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
       ---
       pipette_imp.rs (6513B)
       ---
            1 use eframe::{
            2     emath::Align2,
            3     epaint::{Color32, FontId, Rounding, Vec2},
            4 };
            5 use egui::{Image, TextureHandle, Widget};
            6 use i18n_embed_fl::fl;
            7 use icy_engine::{AttributedChar, Buffer, TextAttribute};
            8 
            9 use crate::{create_image, AnsiEditor, Message};
           10 
           11 use super::{Position, Tool};
           12 
           13 pub static mut CUR_CHAR: Option<AttributedChar> = None;
           14 
           15 #[derive(Default)]
           16 pub struct PipetteTool {
           17     ch: Option<char>,
           18     char_image: Option<TextureHandle>,
           19     cur_pos: Option<Position>,
           20     take_fg: bool,
           21     take_bg: bool,
           22 }
           23 
           24 impl Tool for PipetteTool {
           25     fn get_icon(&self) -> &egui::Image<'static> {
           26         &super::icons::DROPPER_SVG
           27     }
           28 
           29     fn tool_name(&self) -> String {
           30         fl!(crate::LANGUAGE_LOADER, "tool-pipette_name")
           31     }
           32 
           33     fn tooltip(&self) -> String {
           34         fl!(crate::LANGUAGE_LOADER, "tool-pipette_tooltip")
           35     }
           36 
           37     fn use_caret(&self, _editor: &AnsiEditor) -> bool {
           38         false
           39     }
           40 
           41     fn use_selection(&self) -> bool {
           42         false
           43     }
           44 
           45     fn show_ui(&mut self, ctx: &egui::Context, ui: &mut egui::Ui, editor_opt: Option<&mut AnsiEditor>) -> Option<Message> {
           46         let Some(editor) = editor_opt else {
           47             return None;
           48         };
           49 
           50         if let Some(ch) = unsafe { CUR_CHAR } {
           51             ui.vertical_centered(|ui| {
           52                 ui.label(fl!(crate::LANGUAGE_LOADER, "pipette_tool_char_code", code = (ch.ch as u32)));
           53 
           54                 if self.ch.is_none() || !self.ch.unwrap().eq(&ch.ch) {
           55                     self.ch = Some(ch.ch);
           56 
           57                     let mut buf = Buffer::new((1, 1));
           58                     buf.clear_font_table();
           59                     if let Some(font) = editor.buffer_view.lock().get_buffer().get_font(ch.get_font_page()) {
           60                         buf.set_font(0, font.clone());
           61                     } else {
           62                         log::error!("Pipette tool: font page {} not found", ch.get_font_page());
           63                     }
           64                     buf.layers[0].set_char((0, 0), AttributedChar::new(ch.ch, TextAttribute::default()));
           65                     self.char_image = Some(create_image(ctx, &buf));
           66                 }
           67 
           68                 if let Some(image) = &self.char_image {
           69                     let image = Image::from_texture(image).fit_to_original_size(2.0);
           70                     image.ui(ui);
           71                 }
           72 
           73                 self.take_fg = !ui.input(|i| i.modifiers.ctrl) || ui.input(|i| i.modifiers.shift);
           74                 if self.take_fg {
           75                     ui.label(fl!(crate::LANGUAGE_LOADER, "pipette_tool_foreground", fg = ch.attribute.get_foreground()));
           76                     paint_color(ui, &editor.buffer_view.lock().get_buffer().palette.get_color(ch.attribute.get_foreground()));
           77                 }
           78 
           79                 self.take_bg = !ui.input(|i| i.modifiers.shift) || ui.input(|i| i.modifiers.ctrl);
           80 
           81                 if self.take_bg {
           82                     ui.label(fl!(crate::LANGUAGE_LOADER, "pipette_tool_background", bg = ch.attribute.get_background()));
           83                     paint_color(ui, &editor.buffer_view.lock().get_buffer().palette.get_color(ch.attribute.get_background()));
           84                 }
           85             });
           86 
           87             ui.add_space(4.0);
           88             ui.horizontal(|ui| {
           89                 ui.add_space(8.0);
           90                 ui.label(fl!(crate::LANGUAGE_LOADER, "pipette_tool_keys"));
           91             });
           92         }
           93         None
           94     }
           95 
           96     fn handle_hover(&mut self, _ui: &egui::Ui, response: egui::Response, editor: &mut AnsiEditor, cur: Position, cur_abs: Position) -> egui::Response {
           97         unsafe {
           98             CUR_CHAR = Some(editor.get_char(cur_abs));
           99         }
          100         if self.cur_pos != Some(cur) {
          101             self.cur_pos = Some(cur);
          102             let lock = &mut editor.buffer_view.lock();
          103             let get_tool_overlay_mask_mut = lock.get_edit_state_mut().get_tool_overlay_mask_mut();
          104             get_tool_overlay_mask_mut.clear();
          105             get_tool_overlay_mask_mut.set_is_selected(cur_abs, true);
          106             lock.get_edit_state_mut().set_is_buffer_dirty();
          107         }
          108 
          109         response.on_hover_cursor(egui::CursorIcon::Crosshair)
          110     }
          111 
          112     fn get_toolbar_location_text(&self, _editor: &AnsiEditor) -> String {
          113         if let Some(pos) = self.cur_pos {
          114             fl!(crate::LANGUAGE_LOADER, "toolbar-position", line = (pos.y + 1), column = (pos.x + 1))
          115         } else {
          116             String::new()
          117         }
          118     }
          119 
          120     fn handle_no_hover(&mut self, editor: &mut AnsiEditor) {
          121         unsafe {
          122             CUR_CHAR = None;
          123         }
          124         self.cur_pos = None;
          125 
          126         let lock: &mut eframe::epaint::mutex::MutexGuard<'_, icy_engine_egui::BufferView> = &mut editor.buffer_view.lock();
          127         let get_edit_state_mut = lock.get_edit_state_mut();
          128         if !get_edit_state_mut.get_tool_overlay_mask_mut().is_empty() {
          129             get_edit_state_mut.get_tool_overlay_mask_mut().clear();
          130             get_edit_state_mut.set_is_buffer_dirty();
          131         }
          132     }
          133 
          134     fn handle_click(&mut self, editor: &mut AnsiEditor, button: i32, _pos: Position, _pos_abs: Position, _response: &egui::Response) -> Option<Message> {
          135         if button == 1 {
          136             unsafe {
          137                 if let Some(ch) = CUR_CHAR {
          138                     let mut attr = editor.buffer_view.lock().get_caret_mut().get_attribute();
          139                     if self.take_fg {
          140                         attr.set_foreground(ch.attribute.get_foreground());
          141                     }
          142 
          143                     if self.take_bg {
          144                         attr.set_background(ch.attribute.get_background());
          145                     }
          146                     editor.set_caret_attribute(attr);
          147                 }
          148             }
          149             return Some(Message::SelectPreviousTool);
          150         }
          151         None
          152     }
          153 }
          154 
          155 fn paint_color(ui: &mut egui::Ui, color: &icy_engine::Color) {
          156     let (_, stroke_rect) = ui.allocate_space(Vec2::new(100.0, 32.0));
          157 
          158     let painter = ui.painter_at(stroke_rect);
          159 
          160     let (r, g, b) = color.get_rgb();
          161     painter.rect_filled(stroke_rect, Rounding::ZERO, Color32::BLACK);
          162     painter.rect_filled(stroke_rect.shrink(1.0), Rounding::ZERO, Color32::WHITE);
          163     let color = Color32::from_rgb(r, g, b);
          164     painter.rect_filled(stroke_rect.shrink(2.0), Rounding::ZERO, color);
          165 
          166     let text_color = if (r as f32 * 0.299 + g as f32 * 0.587 + b as f32 * 0.114) > 186.0 {
          167         Color32::BLACK
          168     } else {
          169         Color32::WHITE
          170     };
          171 
          172     let text = format!("#{r:02x}{g:02x}{b:02x}");
          173     let font_id: eframe::epaint::FontId = FontId::monospace(16.0);
          174     painter.text(stroke_rect.center(), Align2::CENTER_CENTER, text, font_id, text_color);
          175 }