URI: 
       Layer view now shows previews of the layers. - 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
       ---
   DIR commit 1ad81e4f2a7b46b112bb974e2bb6d04b2fc3b331
   DIR parent 1a9ab76349cbafba6e727865534f25b59fba8f5a
  HTML Author: Mike Krüger <mkrueger@posteo.de>
       Date:   Thu,  5 Oct 2023 13:02:04 +0200
       
       Layer view now shows previews of the layers.
       
       Diffstat:
         M src/ui/editor/animation/asciicast_… |       2 +-
         M src/ui/editor/animation/encoding.rs |       3 ++-
         M src/ui/main_window.rs               |       2 +-
         M src/ui/tools/layer_view.rs          |     209 +++++++++++++++++++------------
       
       4 files changed, 132 insertions(+), 84 deletions(-)
       ---
   DIR diff --git a/src/ui/editor/animation/asciicast_encoder.rs b/src/ui/editor/animation/asciicast_encoder.rs
       @@ -46,7 +46,7 @@ impl AnimationEncoder for AsciiCast {
                let mut timestamp = 0.0;
        
                for frame in 0..frame_count {
       -            let mut opt: SaveOptions = SaveOptions::default();
       +            let mut opt: SaveOptions = SaveOptions::new();
                    opt.control_char_handling = icy_engine::ControlCharHandling::FilterOut;
                    opt.longer_terminal_output = true;
                    opt.compress = true;
   DIR diff --git a/src/ui/editor/animation/encoding.rs b/src/ui/editor/animation/encoding.rs
       @@ -23,13 +23,14 @@ pub trait AnimationEncoder {
            }
        }
        pub const ENCODERS: &[&dyn AnimationEncoder] = &[&GifEncoder {}, &Mp4Encoder {}, &AsciiCast {}];
       +type EncodingThread = (Receiver<usize>, JoinHandle<TerminalResult<()>>);
        
        pub fn start_encoding_thread(
            encoder: usize,
            gl: Arc<glow::Context>,
            path: PathBuf,
            animator: Arc<std::sync::Mutex<Animator>>,
       -) -> TerminalResult<Option<(Receiver<usize>, JoinHandle<TerminalResult<()>>)>> {
       +) -> TerminalResult<Option<EncodingThread>> {
            if !animator.lock().unwrap().success() {
                return Err(anyhow::anyhow!("Animation is not finished."));
            }
   DIR diff --git a/src/ui/main_window.rs b/src/ui/main_window.rs
       @@ -116,7 +116,7 @@ impl<'a> MainWindow<'a> {
                let gl = cc.gl.clone().unwrap();
        
                let mut tool_tree = egui_tiles::Tree::<ToolTab>::empty("tool_tree");
       -        let layers = tool_tree.tiles.insert_pane(ToolTab::new(LayerToolWindow::default()));
       +        let layers = tool_tree.tiles.insert_pane(ToolTab::new(LayerToolWindow::new(gl.clone())));
                let channels = tool_tree.tiles.insert_pane(ToolTab::new(ChannelToolWindow::default()));
                let minimap = tool_tree.tiles.insert_pane(ToolTab::new(MinimapToolWindow::new(gl.clone())));
                let char_table = tool_tree.tiles.insert_pane(ToolTab::new(CharTableToolWindow::new(ctx, 16)));
   DIR diff --git a/src/ui/tools/layer_view.rs b/src/ui/tools/layer_view.rs
       @@ -7,114 +7,141 @@ use eframe::{
        };
        use egui::Image;
        use i18n_embed_fl::fl;
       +use icy_engine_egui::BufferView;
        
        use crate::{AnsiEditor, Document, Message, ToolWindow, INVISIBLE_SVG, VISIBLE_SVG};
        
       -#[derive(Default)]
       -pub struct LayerToolWindow {}
       +pub struct LayerToolWindow {
       +    gl: Arc<glow::Context>,
       +    view_cache: Vec<Arc<eframe::epaint::mutex::Mutex<BufferView>>>,
       +}
        
       -impl ToolWindow for LayerToolWindow {
       -    fn get_title(&self) -> String {
       -        fl!(crate::LANGUAGE_LOADER, "layer_tool_title")
       +impl LayerToolWindow {
       +    pub(crate) fn new(gl: Arc<glow::Context>) -> Self {
       +        Self { gl, view_cache: Vec::new() }
            }
        
       -    fn show_ui(&mut self, ui: &mut egui::Ui, active_document: Option<Arc<Mutex<Box<dyn Document>>>>) -> Option<Message> {
       -        if let Some(doc) = active_document {
       -            if let Some(editor) = doc.lock().unwrap().get_ansi_editor() {
       -                return show_layer_view(ui, editor);
       -            }
       +    pub fn get_buffer_view(&mut self, i: usize) -> Arc<eframe::epaint::mutex::Mutex<BufferView>> {
       +        while self.view_cache.len() <= i {
       +            let mut buffer_view = BufferView::new(&self.gl);
       +            buffer_view.interactive = false;
       +            buffer_view.get_buffer_mut().is_terminal_buffer = false;
       +            buffer_view.get_caret_mut().set_is_visible(false);
       +            self.view_cache.push(Arc::new(eframe::epaint::mutex::Mutex::new(buffer_view)));
                }
       -        ui.vertical_centered(|ui| {
       -            ui.add_space(8.0);
       -            ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "no_document_selected")).small());
       -        });
       -        None
       -    }
       -}
        
       -fn show_layer_view(ui: &mut egui::Ui, editor: &AnsiEditor) -> Option<Message> {
       -    let row_height = 24.0;
       -    let mut result = None;
       +        self.view_cache[i].clone()
       +    }
        
       -    let max = editor.buffer_view.lock().get_buffer().layers.len();
       -    let cur_layer = editor.get_cur_layer_index();
       +    fn show_layer_view(&mut self, ui: &mut egui::Ui, editor: &AnsiEditor) -> Option<Message> {
       +        let row_height = 48.0;
       +        let mut result = None;
        
       -    let paste_mode = editor.buffer_view.lock().get_buffer().layers.iter().position(|layer| layer.role.is_paste());
       +        let max = editor.buffer_view.lock().get_buffer().layers.len();
       +        let cur_layer = editor.get_cur_layer_index();
        
       -    TopBottomPanel::bottom("layer_bottom").show_inside(ui, |ui| {
       -        ui.horizontal(|ui| {
       -            ui.add_space(4.0);
       -            ui.spacing_mut().item_spacing = eframe::epaint::Vec2::new(0.0, 0.0);
       +        let paste_mode = editor.buffer_view.lock().get_buffer().layers.iter().position(|layer| layer.role.is_paste());
        
       -            if paste_mode.is_some() {
       -                let r = medium_hover_button(ui, &crate::ADD_LAYER_SVG).on_hover_ui(|ui| {
       -                    ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "add_layer_tooltip")).small());
       -                });
       +        TopBottomPanel::bottom("layer_bottom").show_inside(ui, |ui| {
       +            ui.horizontal(|ui| {
       +                ui.add_space(4.0);
       +                ui.spacing_mut().item_spacing = eframe::epaint::Vec2::new(0.0, 0.0);
        
       -                if r.clicked() {
       -                    result = Some(Message::AddFloatingLayer);
       -                }
       -                let role = editor.buffer_view.lock().get_edit_state().get_cur_layer().unwrap().role;
       -                if matches!(role, icy_engine::Role::PastePreview) {
       -                    let r = medium_hover_button(ui, &crate::ANCHOR_SVG).on_hover_ui(|ui| {
       -                        ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "anchor_layer_tooltip")).small());
       +                if paste_mode.is_some() {
       +                    let r = medium_hover_button(ui, &crate::ADD_LAYER_SVG).on_hover_ui(|ui| {
       +                        ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "add_layer_tooltip")).small());
                            });
        
       -                    if r.clicked() && cur_layer < max {
       -                        result = Some(Message::AnchorLayer);
       +                    if r.clicked() {
       +                        result = Some(Message::AddFloatingLayer);
       +                    }
       +                    let role = editor.buffer_view.lock().get_edit_state().get_cur_layer().unwrap().role;
       +                    if matches!(role, icy_engine::Role::PastePreview) {
       +                        let r = medium_hover_button(ui, &crate::ANCHOR_SVG).on_hover_ui(|ui| {
       +                            ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "anchor_layer_tooltip")).small());
       +                        });
       +
       +                        if r.clicked() && cur_layer < max {
       +                            result = Some(Message::AnchorLayer);
       +                        }
                            }
       -                }
        
       -                let r = medium_hover_button(ui, &crate::DELETE_SVG).on_hover_ui(|ui| {
       -                    ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "delete_layer_tooltip")).small());
       -                });
       +                    let r = medium_hover_button(ui, &crate::DELETE_SVG).on_hover_ui(|ui| {
       +                        ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "delete_layer_tooltip")).small());
       +                    });
        
       -                if r.clicked() && cur_layer < max {
       -                    result = Some(Message::RemoveFloatingLayer);
       -                }
       -            } else {
       -                let r = medium_hover_button(ui, &crate::ADD_LAYER_SVG).on_hover_ui(|ui| {
       -                    ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "add_layer_tooltip")).small());
       -                });
       +                    if r.clicked() && cur_layer < max {
       +                        result = Some(Message::RemoveFloatingLayer);
       +                    }
       +                } else {
       +                    let r = medium_hover_button(ui, &crate::ADD_LAYER_SVG).on_hover_ui(|ui| {
       +                        ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "add_layer_tooltip")).small());
       +                    });
        
       -                if r.clicked() {
       -                    result = Some(Message::AddNewLayer(cur_layer));
       -                }
       +                    if r.clicked() {
       +                        result = Some(Message::AddNewLayer(cur_layer));
       +                    }
        
       -                let r = medium_hover_button(ui, &crate::MOVE_UP_SVG).on_hover_ui(|ui| {
       -                    ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "move_layer_up_tooltip")).small());
       -                });
       +                    let r = medium_hover_button(ui, &crate::MOVE_UP_SVG).on_hover_ui(|ui| {
       +                        ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "move_layer_up_tooltip")).small());
       +                    });
        
       -                if r.clicked() {
       -                    result = Some(Message::RaiseLayer(cur_layer));
       -                }
       +                    if r.clicked() {
       +                        result = Some(Message::RaiseLayer(cur_layer));
       +                    }
        
       -                let r = medium_hover_button(ui, &crate::MOVE_DOWN_SVG).on_hover_ui(|ui| {
       -                    ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "move_layer_down_tooltip")).small());
       -                });
       +                    let r = medium_hover_button(ui, &crate::MOVE_DOWN_SVG).on_hover_ui(|ui| {
       +                        ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "move_layer_down_tooltip")).small());
       +                    });
        
       -                if r.clicked() {
       -                    result = Some(Message::LowerLayer(cur_layer));
       -                }
       +                    if r.clicked() {
       +                        result = Some(Message::LowerLayer(cur_layer));
       +                    }
        
       -                let r = medium_hover_button(ui, &crate::DELETE_SVG).on_hover_ui(|ui| {
       -                    ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "delete_layer_tooltip")).small());
       -                });
       +                    let r = medium_hover_button(ui, &crate::DELETE_SVG).on_hover_ui(|ui| {
       +                        ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "delete_layer_tooltip")).small());
       +                    });
        
       -                if r.clicked() && cur_layer < max {
       -                    result = Some(Message::RemoveLayer(cur_layer));
       +                    if r.clicked() && cur_layer < max {
       +                        result = Some(Message::RemoveLayer(cur_layer));
       +                    }
                        }
       -            }
       +            });
                });
       -    });
        
       -    CentralPanel::default().show_inside(ui, |ui| {
       -        egui::ScrollArea::vertical()
       -            .id_source("layer_view_scroll_area")
       -            .show_rows(ui, row_height, max, |ui, range| {
       -                for i in range.rev() {
       +        CentralPanel::default().show_inside(ui, |ui| {
       +            egui::ScrollArea::vertical().id_source("layer_view_scroll_area").show(ui, |ui| {
       +                for i in (0..max).rev() {
                            ui.horizontal(|ui| {
                                ui.add_space(4.0);
       +                        let dims = editor.buffer_view.lock().get_buffer().get_font_dimensions();
       +                        let size = dims.height as f32 * 25.0;
       +                        let scale = row_height / size;
       +
       +                        ui.allocate_ui(Vec2::new(scale * dims.width as f32 * 80.0, row_height), |ui| {
       +                            let opt = icy_engine_egui::TerminalOptions {
       +                                filter: glow::LINEAR as i32,
       +                                stick_to_bottom: false,
       +                                scale: Some(Vec2::new(scale, scale)),
       +                                use_terminal_height: false,
       +                                hide_scrollbars: true,
       +                                id: Some(ui.id().with(i)),
       +                                clip_rect: Some(ui.clip_rect()),
       +                                ..Default::default()
       +                            };
       +                            let view = self.get_buffer_view(i);
       +                            {
       +                                view.lock().get_buffer_mut().layers.clear();
       +                                if let Some(layer) = editor.buffer_view.lock().get_buffer().layers.get(i) {
       +                                    let mut l = layer.clone();
       +                                    l.is_visible = true;
       +                                    view.lock().get_buffer_mut().layers.push(l);
       +                                    view.lock().get_edit_state_mut().is_buffer_dirty = true;
       +                                }
       +                            }
       +                            let (_, _) = icy_engine_egui::show_terminal_area(ui, view, opt);
       +                        });
       +
                                let (is_visible, title, color) = {
                                    let lock = editor.buffer_view.lock();
                                    let layer = &lock.get_buffer().layers[i];
       @@ -133,7 +160,7 @@ fn show_layer_view(ui: &mut egui::Ui, editor: &AnsiEditor) -> Option<Message> {
                                    back_painter.rect_filled(back_rect, Rounding::ZERO, ui.style().visuals.extreme_bg_color);
                                }
        
       -                        let stroke_rect = Rect::from_min_size(back_rect.min + Vec2::new(0.0, 1.0), Vec2::new(22.0, 22.0));
       +                        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));
                                let visible_icon_response = ui.interact(stroke_rect, id.with("visible"), Sense::click());
        
                                let painter = ui.painter_at(stroke_rect);
       @@ -213,8 +240,28 @@ fn show_layer_view(ui: &mut egui::Ui, editor: &AnsiEditor) -> Option<Message> {
                            });
                        }
                    });
       -    });
       -    result
       +        });
       +        result
       +    }
       +}
       +
       +impl ToolWindow for LayerToolWindow {
       +    fn get_title(&self) -> String {
       +        fl!(crate::LANGUAGE_LOADER, "layer_tool_title")
       +    }
       +
       +    fn show_ui(&mut self, ui: &mut egui::Ui, active_document: Option<Arc<Mutex<Box<dyn Document>>>>) -> Option<Message> {
       +        if let Some(doc) = active_document {
       +            if let Some(editor) = doc.lock().unwrap().get_ansi_editor() {
       +                return self.show_layer_view(ui, editor);
       +            }
       +        }
       +        ui.vertical_centered(|ui| {
       +            ui.add_space(8.0);
       +            ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "no_document_selected")).small());
       +        });
       +        None
       +    }
        }
        
        pub fn medium_hover_button(ui: &mut egui::Ui, image: &Image<'_>) -> egui::Response {