minimap_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
---
minimap_view.rs (6284B)
---
1 use std::sync::Arc;
2
3 use eframe::{
4 egui::{self, RichText},
5 epaint::{Color32, Rect, Stroke, Vec2},
6 };
7 use egui::mutex::Mutex;
8 use i18n_embed_fl::fl;
9 use icy_engine::TextPane;
10 use icy_engine_egui::BufferView;
11
12 use crate::{AnsiEditor, Document, Message, ToolWindow};
13
14 pub struct MinimapToolWindow {
15 buffer_view: Arc<eframe::epaint::mutex::Mutex<BufferView>>,
16 undo_size: i32,
17 last_id: usize,
18 palette_hash: u32,
19 next_scroll_pos: Option<Vec2>,
20 }
21
22 impl ToolWindow for MinimapToolWindow {
23 fn get_title(&self) -> String {
24 fl!(crate::LANGUAGE_LOADER, "minimap_tool_title")
25 }
26
27 fn show_ui(&mut self, ui: &mut egui::Ui, active_document: Option<Arc<Mutex<Box<dyn Document>>>>) -> Option<Message> {
28 if let Some(doc) = active_document {
29 if let Some(editor) = doc.lock().get_ansi_editor_mut() {
30 return self.show_minimap(ui, editor);
31 }
32 }
33 ui.vertical_centered(|ui| {
34 ui.add_space(8.0);
35 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "no_document_selected")).small());
36 });
37 None
38 }
39 }
40
41 impl MinimapToolWindow {
42 pub fn show_minimap(&mut self, ui: &mut egui::Ui, editor: &mut AnsiEditor) -> Option<Message> {
43 let undo_stack = editor.buffer_view.lock().get_edit_state().undo_stack_len() as i32;
44 let cur_palette_hash = editor.buffer_view.lock().get_buffer_mut().palette.get_checksum();
45 if undo_stack != self.undo_size || self.last_id != editor.id || self.palette_hash != cur_palette_hash {
46 self.undo_size = undo_stack;
47 self.last_id = editor.id;
48 let bv = editor.buffer_view.lock();
49 let buffer = bv.get_buffer();
50 self.buffer_view.lock().get_buffer_mut().set_size(buffer.get_size());
51 self.buffer_view.lock().get_buffer_mut().layers = buffer.layers.clone();
52 self.buffer_view.lock().get_buffer_mut().palette = buffer.palette.clone();
53 self.buffer_view.lock().get_buffer_mut().set_font_table(buffer.get_font_table());
54 self.palette_hash = cur_palette_hash;
55 self.buffer_view.lock().redraw_font();
56 self.buffer_view.lock().redraw_view();
57 }
58
59 self.buffer_view.lock().use_fg = editor.buffer_view.lock().use_fg;
60 self.buffer_view.lock().use_bg = editor.buffer_view.lock().use_bg;
61 let w = (ui.available_width() / self.buffer_view.lock().get_buffer().get_font_dimensions().width as f32).floor();
62
63 let scalex = (w / self.buffer_view.lock().get_width() as f32).min(2.0);
64 let scaley = if self.buffer_view.lock().get_buffer_mut().use_aspect_ratio() {
65 scalex * 1.35
66 } else {
67 scalex
68 };
69
70 let mut opt = icy_engine_egui::TerminalOptions {
71 filter: glow::LINEAR as i32,
72 stick_to_bottom: false,
73 scale: Some(Vec2::new(scalex, scaley)),
74 use_terminal_height: false,
75 hide_scrollbars: true,
76
77 ..Default::default()
78 };
79
80 let next_scroll_pos = self.next_scroll_pos.take();
81
82 if let Some(next_scroll_pos) = next_scroll_pos {
83 opt.scroll_offset_x = Some(next_scroll_pos.x);
84 opt.scroll_offset_y = Some(next_scroll_pos.y);
85 }
86
87 let (response, ours) = icy_engine_egui::show_terminal_area(ui, self.buffer_view.clone(), opt);
88
89 let theirs = editor.buffer_view.lock().calc.clone();
90
91 let their_total_size = Vec2::new(theirs.char_width, theirs.char_height) * theirs.char_size;
92 let their_buffer_size = Vec2::new(theirs.buffer_char_width, theirs.buffer_char_height) * theirs.char_size;
93
94 let our_total_size = Vec2::new(ours.char_width, ours.char_height) * ours.char_size;
95
96 let tmax_y: f32 = theirs.font_height * (theirs.char_height - theirs.buffer_char_height).max(0.0);
97 let tmax_x: f32 = theirs.font_width * (theirs.real_width as f32 - theirs.buffer_char_width).max(0.0);
98
99 let size = our_total_size * their_buffer_size / their_total_size;
100 let tx = theirs.char_scroll_position.x / tmax_x.max(1.0);
101 let ty = theirs.char_scroll_position.y / tmax_y.max(1.0);
102
103 let pos = (our_total_size - size - Vec2::new(1.0, 1.0)) * Vec2::new(tx, ty);
104
105 let pos = pos - ours.char_scroll_position * ours.scale;
106 let min = (ours.buffer_rect.min + pos).floor() + Vec2::new(0.5, 0.5);
107
108 let corr = ours.buffer_rect.min.y + ours.terminal_rect.height() - (size.y + min.y);
109 let view_size = Vec2::new(size.x, size.y + corr.min(0.0)).floor();
110
111 ui.painter().rect_stroke(
112 Rect::from_min_size(min, view_size),
113 0.0,
114 Stroke::new(1.0, Color32::from_rgba_premultiplied(157, 157, 157, 220)),
115 );
116
117 if pos.x < 0.0 || pos.y < 0.0 {
118 self.next_scroll_pos = Some(ours.char_scroll_position + pos / ours.scale);
119 ui.ctx().request_repaint();
120 }
121
122 if pos.x + size.x > ours.terminal_rect.size().x || pos.y + size.y > ours.terminal_rect.size().y {
123 let p = pos + size - ours.terminal_rect.size();
124 self.next_scroll_pos = Some(ours.char_scroll_position + p / ours.scale);
125 ui.ctx().request_repaint();
126 }
127
128 if response.dragged() {
129 if let Some(pos) = response.hover_pos() {
130 let pos = (pos - ours.buffer_rect.min) / ours.scale + ours.char_scroll_position;
131 editor.next_scroll_x_position = Some(pos.x - theirs.buffer_char_width * theirs.font_width / 2.0);
132 editor.next_scroll_y_position = Some(pos.y - theirs.buffer_char_height * theirs.font_height / 2.0);
133 ui.ctx().request_repaint();
134 }
135 }
136
137 None
138 }
139
140 pub(crate) fn new(gl: Arc<glow::Context>) -> Self {
141 let mut buffer_view = BufferView::new(&gl);
142 buffer_view.interactive = false;
143 buffer_view.get_buffer_mut().is_terminal_buffer = false;
144 buffer_view.get_caret_mut().set_is_visible(false);
145 Self {
146 buffer_view: Arc::new(eframe::epaint::mutex::Mutex::new(buffer_view)),
147 last_id: usize::MAX,
148 undo_size: -1,
149 palette_hash: 0,
150 next_scroll_pos: None,
151 }
152 }
153 }