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 }