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 }