char_table.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
---
char_table.rs (9417B)
---
1 use std::sync::Arc;
2
3 use eframe::{
4 egui::{self, RichText, Sense},
5 epaint::{Color32, FontId, Pos2, Rect, Vec2},
6 };
7 use egui::{load::SizedTexture, mutex::Mutex, Context, Image, TextureHandle};
8 use i18n_embed_fl::fl;
9 use icy_engine::{AttributedChar, BitFont, Buffer, TextAttribute};
10
11 use crate::{create_image, AnsiEditor, Document, Message, ToolWindow};
12
13 pub struct CharTableToolWindow {
14 font: BitFont,
15 hover_char: Option<char>,
16 hover_char_image: TextureHandle,
17 char_table: TextureHandle,
18 buffer_width: usize,
19 }
20
21 impl CharTableToolWindow {
22 pub fn new(ctx: &Context, buffer_width: usize) -> Self {
23 let font = BitFont::default();
24 let char_table = create_font_image(ctx, &font, buffer_width, TextAttribute::default(), TextAttribute::default(), 0);
25 let hover_char_image = create_hover_image(ctx, &font, ' ', 14);
26 Self {
27 font,
28 char_table,
29 hover_char: None,
30 hover_char_image,
31 buffer_width,
32 }
33 }
34
35 pub fn get_font(&self) -> &BitFont {
36 &self.font
37 }
38
39 pub fn set_font(&mut self, ctx: &Context, font: BitFont) {
40 self.font = font;
41 self.char_table = create_font_image(ctx, &self.font, self.buffer_width, TextAttribute::default(), TextAttribute::default(), 0);
42 self.hover_char = None;
43 }
44
45 pub fn show_plain_char_table(&mut self, ui: &mut egui::Ui) -> Option<char> {
46 let mut something_hovered = false;
47 let mut result = None;
48 egui::ScrollArea::vertical().id_source("char_table_scroll_area").show(ui, |ui| {
49 ui.add_space(4.0);
50 ui.horizontal(|ui| {
51 let scale = 2.0;
52 let sized_texture: SizedTexture = (&self.char_table).into();
53 let width = sized_texture.size.x * scale;
54 let height = sized_texture.size.y * scale;
55 ui.add_space((ui.available_width() - width) / 2.0);
56
57 let (id, rect) = ui.allocate_space([width, height].into());
58 let response = ui.interact(rect, id, Sense::click());
59 let r = Rect::from_min_size(Pos2::new(rect.left(), rect.top()), Vec2::new(width, height));
60 let image = Image::from_texture(sized_texture);
61
62 image.paint_at(ui, r);
63
64 let fw = scale * self.font.size.width as f32;
65 let fh = scale * self.font.size.height as f32;
66 if response.clicked() {
67 result = self.hover_char;
68 }
69 if response.hovered() {
70 if let Some(pos) = response.hover_pos() {
71 something_hovered = true;
72 let pos = pos - response.rect.min;
73 let ch = (pos.x / fw) as usize + self.buffer_width * (pos.y / fh) as usize;
74 let ch = unsafe { char::from_u32_unchecked(ch as u32) };
75 let hover_char = Some(ch);
76 if self.hover_char != hover_char {
77 self.hover_char = hover_char;
78 self.hover_char_image = create_hover_image(ui.ctx(), &self.font, ch, 14);
79 }
80
81 let x = (ch as usize) % self.buffer_width;
82 let y = (ch as usize) / self.buffer_width;
83
84 let rect = Rect::from_min_size(rect.min + Vec2::new(x as f32 * fw, y as f32 * fh), Vec2::new(fw, fh));
85 let sized_texture: SizedTexture = (&self.hover_char_image).into();
86 let image = Image::from_texture(sized_texture);
87 image.paint_at(ui, rect.expand(2.0));
88 }
89 }
90 });
91 });
92 ui.horizontal(|ui| {
93 ui.add_space(4.0);
94 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "font-view-font_label")).small());
95 ui.label(RichText::new(self.font.name.to_string()).small().color(Color32::WHITE));
96 });
97
98 if let Some(ch) = self.hover_char {
99 ui.horizontal(|ui| {
100 ui.add_space(4.0);
101 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "font-view-char_label")).small());
102 ui.label(RichText::new(format!("{0}/0x{0:02X}", ch as u32)).small().color(Color32::WHITE));
103 });
104 } else {
105 ui.horizontal(|ui| {
106 ui.label(" ");
107 });
108 ui.horizontal(|ui| {
109 ui.label(" ");
110 });
111 }
112 if !something_hovered {
113 self.hover_char = None;
114 }
115 result
116 }
117
118 pub fn show_char_table(&mut self, ui: &mut egui::Ui, editor: &AnsiEditor) -> Option<Message> {
119 let mut result = None;
120
121 let font_page = editor.buffer_view.lock().get_caret().get_font_page();
122 let font_count = editor.buffer_view.lock().get_buffer().font_count();
123 if let Some(cur_font) = editor.buffer_view.lock().get_buffer().get_font(font_page) {
124 if cur_font.name != self.font.name {
125 self.font = cur_font.clone();
126 self.char_table = create_font_image(ui.ctx(), &self.font, self.buffer_width, TextAttribute::default(), TextAttribute::default(), 0);
127 self.hover_char = None;
128 }
129 }
130
131 if font_count > 1 {
132 ui.add_space(8.0);
133
134 ui.horizontal(|ui| {
135 ui.add_space(12.0);
136
137 ui.label(fl!(crate::LANGUAGE_LOADER, "font-view-font_page_label"));
138 if ui.selectable_label(false, RichText::new("◀").font(FontId::proportional(14.))).clicked() {
139 let mut prev = font_page;
140 let mut last = 0;
141 for (page, _) in editor.buffer_view.lock().get_buffer().font_iter() {
142 last = last.max(*page);
143 if *page < font_page {
144 if prev == font_page {
145 prev = *page;
146 } else {
147 prev = prev.max(*page);
148 }
149 }
150 }
151 if prev == font_page {
152 result = Some(Message::SetFontPage(last));
153 } else {
154 result = Some(Message::SetFontPage(prev));
155 }
156 }
157 ui.label(RichText::new(font_page.to_string()));
158
159 if ui.selectable_label(false, RichText::new("▶").font(FontId::proportional(14.))).clicked() {
160 let mut next = font_page;
161 let mut first = usize::MAX;
162 for (page, _) in editor.buffer_view.lock().get_buffer().font_iter() {
163 first = first.min(*page);
164 if *page > font_page {
165 if next == font_page {
166 next = *page;
167 } else {
168 next = next.min(*page);
169 }
170 }
171 }
172 if next == font_page {
173 result = Some(Message::SetFontPage(first));
174 } else {
175 result = Some(Message::SetFontPage(next));
176 }
177 }
178 });
179 }
180
181 let response = self.show_plain_char_table(ui);
182
183 if let Some(ch) = response {
184 result = Some(Message::CharTable(ch));
185 }
186
187 result
188 }
189 }
190
191 pub fn create_font_image(
192 ctx: &Context,
193 font: &BitFont,
194 buffer_width: usize,
195 attribute: TextAttribute,
196 selected_attribute: TextAttribute,
197 selected: usize,
198 ) -> TextureHandle {
199 let mut buffer = Buffer::new((buffer_width, (font.length as usize) / buffer_width));
200 buffer.set_font(0, font.clone());
201 for ch in 0..font.length as usize {
202 buffer.layers[0].set_char(
203 (ch % buffer_width, ch / buffer_width),
204 AttributedChar::new(
205 unsafe { char::from_u32_unchecked(ch as u32) },
206 if ch == selected { selected_attribute } else { attribute },
207 ),
208 );
209 }
210 create_image(ctx, &buffer)
211 }
212
213 pub fn create_hover_image(ctx: &Context, font: &BitFont, ch: char, color: u32) -> TextureHandle {
214 let mut buffer = Buffer::new((1, 1));
215 buffer.set_font(0, font.clone());
216 let mut attr = TextAttribute::default();
217 attr.set_foreground(color);
218
219 buffer.layers[0].set_char((0, 0), AttributedChar::new(unsafe { char::from_u32_unchecked(ch as u32) }, attr));
220 create_image(ctx, &buffer)
221 }
222
223 impl ToolWindow for CharTableToolWindow {
224 fn get_title(&self) -> String {
225 fl!(crate::LANGUAGE_LOADER, "char_table_tool_title")
226 }
227
228 fn show_ui(&mut self, ui: &mut egui::Ui, active_document: Option<Arc<Mutex<Box<dyn Document>>>>) -> Option<Message> {
229 if let Some(doc) = active_document {
230 if let Some(editor) = doc.lock().get_ansi_editor() {
231 return self.show_char_table(ui, editor);
232 }
233 }
234 ui.vertical_centered(|ui| {
235 ui.add_space(8.0);
236 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "no_document_selected")).small());
237 });
238 None
239 }
240 }