layer_view.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
---
layer_view.rs (14577B)
---
1 use std::sync::Arc;
2
3 use eframe::{
4 egui::{self, CentralPanel, RichText, Sense, TextStyle, TopBottomPanel},
5 emath::Align2,
6 epaint::{Color32, Rect, Rounding, Vec2},
7 };
8 use egui::{mutex::Mutex, Image};
9 use i18n_embed_fl::fl;
10 use icy_engine_egui::BufferView;
11
12 use crate::{AnsiEditor, Document, Message, ToolWindow, INVISIBLE_SVG, VISIBLE_SVG};
13
14 pub struct LayerToolWindow {
15 gl: Arc<glow::Context>,
16 view_cache_id: usize,
17 stack_len: usize,
18 view_cache: Vec<Arc<eframe::epaint::mutex::Mutex<BufferView>>>,
19 }
20
21 impl LayerToolWindow {
22 pub(crate) fn new(gl: Arc<glow::Context>) -> Self {
23 Self {
24 gl,
25 view_cache: Vec::new(),
26 view_cache_id: usize::MAX,
27 stack_len: usize::MAX,
28 }
29 }
30
31 pub fn get_buffer_view(&mut self, i: usize) -> Arc<eframe::epaint::mutex::Mutex<BufferView>> {
32 while self.view_cache.len() <= i {
33 let mut buffer_view = BufferView::new(&self.gl);
34 buffer_view.interactive = false;
35 buffer_view.get_buffer_mut().is_terminal_buffer = false;
36 buffer_view.get_caret_mut().set_is_visible(false);
37 self.view_cache.push(Arc::new(eframe::epaint::mutex::Mutex::new(buffer_view)));
38 }
39
40 self.view_cache[i].clone()
41 }
42
43 fn show_layer_view(&mut self, ui: &mut egui::Ui, editor: &AnsiEditor) -> Option<Message> {
44 let row_height = 48.0;
45 let mut result = None;
46
47 let max = editor.buffer_view.lock().get_buffer().layers.len();
48 let Ok(cur_layer) = editor.get_cur_layer_index() else {
49 log::error!("Invalid layer index");
50 return result;
51 };
52
53 let paste_mode = editor.buffer_view.lock().get_buffer().layers.iter().position(|layer| layer.role.is_paste());
54
55 TopBottomPanel::bottom("layer_bottom").show_inside(ui, |ui| {
56 ui.horizontal(|ui| {
57 ui.add_space(4.0);
58 ui.spacing_mut().item_spacing = eframe::epaint::Vec2::new(0.0, 0.0);
59
60 if paste_mode.is_some() {
61 let r = medium_hover_button(ui, &crate::ADD_LAYER_SVG).on_hover_ui(|ui| {
62 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "add_layer_tooltip")).small());
63 });
64
65 if r.clicked() {
66 result = Some(Message::AddFloatingLayer);
67 }
68
69 if let Some(layer) = editor.buffer_view.lock().get_edit_state().get_cur_layer() {
70 let role = layer.role;
71 if matches!(role, icy_engine::Role::PastePreview) {
72 let r = medium_hover_button(ui, &crate::ANCHOR_SVG).on_hover_ui(|ui| {
73 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "anchor_layer_tooltip")).small());
74 });
75
76 if r.clicked() && cur_layer < max {
77 result = Some(Message::AnchorLayer);
78 }
79 }
80 }
81
82 let r = medium_hover_button(ui, &crate::DELETE_SVG).on_hover_ui(|ui| {
83 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "delete_layer_tooltip")).small());
84 });
85
86 if r.clicked() && cur_layer < max {
87 result = Some(Message::RemoveFloatingLayer);
88 }
89 } else {
90 let r = medium_hover_button(ui, &crate::ADD_LAYER_SVG).on_hover_ui(|ui| {
91 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "add_layer_tooltip")).small());
92 });
93
94 if r.clicked() {
95 result = Some(Message::AddNewLayer(cur_layer));
96 }
97
98 let r = medium_hover_button(ui, &crate::MOVE_UP_SVG).on_hover_ui(|ui| {
99 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "move_layer_up_tooltip")).small());
100 });
101
102 if r.clicked() {
103 result = Some(Message::RaiseLayer(cur_layer));
104 }
105
106 let r = medium_hover_button(ui, &crate::MOVE_DOWN_SVG).on_hover_ui(|ui| {
107 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "move_layer_down_tooltip")).small());
108 });
109
110 if r.clicked() {
111 result = Some(Message::LowerLayer(cur_layer));
112 }
113
114 let r = medium_hover_button(ui, &crate::DELETE_SVG).on_hover_ui(|ui| {
115 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "delete_layer_tooltip")).small());
116 });
117
118 if r.clicked() && cur_layer < max {
119 result = Some(Message::RemoveLayer(cur_layer));
120 }
121 }
122 });
123 });
124
125 CentralPanel::default().show_inside(ui, |ui| {
126 let redraw_layer_views = self.view_cache_id != editor.buffer_view.lock().id || editor.undo_stack_len() != self.stack_len;
127 if redraw_layer_views {
128 self.view_cache_id = editor.buffer_view.lock().id;
129 self.stack_len = editor.undo_stack_len();
130 }
131
132 egui::ScrollArea::vertical().id_source("layer_view_scroll_area").show(ui, |ui| {
133 for i in (0..max).rev() {
134 ui.horizontal(|ui| {
135 ui.add_space(4.0);
136 let dims = editor.buffer_view.lock().get_buffer().get_font_dimensions();
137 let size = dims.height as f32 * 25.0;
138 let scale = row_height / size;
139
140 ui.allocate_ui(Vec2::new(scale * dims.width as f32 * 80.0, row_height), |ui| {
141 let opt = icy_engine_egui::TerminalOptions {
142 filter: glow::LINEAR as i32,
143 stick_to_bottom: false,
144 scale: Some(Vec2::new(scale, scale)),
145 use_terminal_height: false,
146 hide_scrollbars: true,
147 id: Some(ui.id().with(i)),
148 clip_rect: Some(ui.clip_rect()),
149 ..Default::default()
150 };
151 let view = self.get_buffer_view(i);
152 if redraw_layer_views {
153 view.lock().get_buffer_mut().layers.clear();
154 let width = editor.buffer_view.lock().get_width();
155 view.lock().get_buffer_mut().set_width(width);
156 let lock = &editor.buffer_view.lock();
157 if let Some(layer) = lock.get_buffer().layers.get(i) {
158 let mut l = layer.clone();
159 l.set_is_visible(true);
160 view.lock().get_buffer_mut().set_font_table(lock.get_buffer().get_font_table());
161 view.lock().get_buffer_mut().palette = lock.get_buffer().palette.clone();
162 view.lock().get_buffer_mut().layers.push(l);
163 view.lock().get_edit_state_mut().set_is_buffer_dirty();
164 }
165 }
166
167 let (_, _) = icy_engine_egui::show_terminal_area(ui, view, opt);
168 });
169
170 let (is_visible, title, color) = {
171 let lock = editor.buffer_view.lock();
172 let layer = &lock.get_buffer().layers[i];
173 (layer.get_is_visible(), layer.get_title().to_string(), layer.properties.color.clone())
174 };
175 let width = ui.available_width();
176
177 let (id, back_rect) = ui.allocate_space(Vec2::new(width, row_height));
178 let mut response = ui.interact(back_rect, id, Sense::click());
179
180 let back_painter = ui.painter_at(back_rect);
181
182 if response.hovered() {
183 back_painter.rect_filled(back_rect, Rounding::ZERO, ui.style().visuals.widgets.active.bg_fill);
184 } else if i == cur_layer {
185 back_painter.rect_filled(back_rect, Rounding::ZERO, ui.style().visuals.extreme_bg_color);
186 }
187
188 let stroke_rect = Rect::from_min_size(back_rect.min + Vec2::new(0.0, (row_height - 22.0) / 2.0), Vec2::new(22.0, 22.0));
189 let visible_icon_response = ui.interact(stroke_rect, id.with("visible"), Sense::click());
190
191 let painter = ui.painter_at(stroke_rect);
192
193 if let Some(color) = color {
194 let (r, g, b) = color.into();
195 painter.rect_filled(stroke_rect, Rounding::ZERO, Color32::from_rgb(r, g, b));
196 }
197
198 let image: Image<'static> = if is_visible { VISIBLE_SVG.clone() } else { INVISIBLE_SVG.clone() };
199
200 let tint = if i == cur_layer {
201 ui.visuals().widgets.active.fg_stroke.color
202 } else {
203 ui.visuals().widgets.inactive.fg_stroke.color
204 };
205 let image = image.tint(tint);
206 image.paint_at(ui, stroke_rect);
207
208 let color = if i == cur_layer {
209 ui.style().visuals.strong_text_color()
210 } else {
211 ui.style().visuals.text_color()
212 };
213 let font_id = TextStyle::Button.resolve(ui.style());
214
215 back_painter.text(stroke_rect.right_center() + Vec2::new(4., 0.), Align2::LEFT_CENTER, title, font_id, color);
216
217 if visible_icon_response.clicked() {
218 result = Some(Message::ToggleLayerVisibility(i));
219 }
220
221 if paste_mode.is_none() {
222 let response_opt = response.context_menu(|ui| {
223 ui.set_width(250.);
224 if ui.button(fl!(crate::LANGUAGE_LOADER, "layer_tool_menu_layer_properties")).clicked() {
225 result = Some(Message::EditLayer(i));
226 ui.close_menu();
227 }
228 if ui.button(fl!(crate::LANGUAGE_LOADER, "layer_tool_menu_resize_layer")).clicked() {
229 result = Some(Message::ResizeLayer(i));
230 ui.close_menu();
231 }
232 ui.separator();
233 if ui.button(fl!(crate::LANGUAGE_LOADER, "layer_tool_menu_new_layer")).clicked() {
234 result = Some(Message::AddNewLayer(i));
235 ui.close_menu();
236 }
237 if ui.button(fl!(crate::LANGUAGE_LOADER, "layer_tool_menu_duplicate_layer")).clicked() {
238 result = Some(Message::DuplicateLayer(i));
239 ui.close_menu();
240 }
241 if ui.button(fl!(crate::LANGUAGE_LOADER, "layer_tool_menu_merge_layer")).clicked() {
242 result = Some(Message::MergeLayerDown(i));
243 ui.close_menu();
244 }
245 if ui.button(fl!(crate::LANGUAGE_LOADER, "layer_tool_menu_delete_layer")).clicked() {
246 result = Some(Message::RemoveLayer(i));
247 ui.close_menu();
248 }
249 ui.separator();
250
251 if ui.button(fl!(crate::LANGUAGE_LOADER, "layer_tool_menu_clear_layer")).clicked() {
252 result = Some(Message::ClearLayer(i));
253 ui.close_menu();
254 }
255 });
256 if let Some(response_opt) = response_opt {
257 response = response_opt.response;
258 }
259 }
260
261 if paste_mode.is_none() && response.clicked() {
262 result = Some(Message::SelectLayer(i));
263 }
264
265 if paste_mode.is_none() && response.double_clicked() {
266 result = Some(Message::EditLayer(i));
267 }
268 });
269 }
270 });
271 });
272 result
273 }
274 }
275
276 impl ToolWindow for LayerToolWindow {
277 fn get_title(&self) -> String {
278 fl!(crate::LANGUAGE_LOADER, "layer_tool_title")
279 }
280
281 fn show_ui(&mut self, ui: &mut egui::Ui, active_document: Option<Arc<Mutex<Box<dyn Document>>>>) -> Option<Message> {
282 if let Some(doc) = active_document {
283 if let Some(editor) = doc.lock().get_ansi_editor() {
284 return self.show_layer_view(ui, editor);
285 }
286 }
287 ui.vertical_centered(|ui| {
288 ui.add_space(8.0);
289 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "no_document_selected")).small());
290 });
291 None
292 }
293 }
294
295 pub fn medium_hover_button(ui: &mut egui::Ui, image: &Image<'_>) -> egui::Response {
296 let size_points = egui::Vec2::splat(28.0);
297
298 let (id, rect) = ui.allocate_space(size_points);
299 let response = ui.interact(rect, id, Sense::click());
300
301 let tint = if response.hovered() {
302 ui.painter().rect_filled(rect, Rounding::same(4.0), ui.style().visuals.extreme_bg_color);
303
304 ui.visuals().widgets.active.fg_stroke.color
305 } else {
306 ui.visuals().widgets.inactive.fg_stroke.color
307 };
308
309 let image = image.clone().tint(tint);
310 image.paint_at(ui, rect.shrink(4.0));
311
312 response
313 }