palette_editor.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
---
palette_editor.rs (8837B)
---
1 use crate::{Message, SWAP_SVG};
2 use eframe::egui::{self, Sense};
3 use eframe::epaint::{Color32, Pos2, Rect, Rounding, Stroke, Vec2};
4 use icy_engine::{Palette, TextAttribute};
5 use std::cmp::min;
6
7 pub fn palette_switcher(_ctx: &egui::Context, ui: &mut egui::Ui, caret_attr: &TextAttribute, palette: &Palette) -> Option<Message> {
8 let mut result = None;
9
10 let height = 62.0;
11 let (id, rect) = ui.allocate_space(Vec2::new(height, height));
12 let response = ui.interact(rect, id, Sense::click());
13 let painter = ui.painter_at(rect);
14
15 let rect_height = height * 0.618;
16
17 painter.rect_filled(
18 Rect::from_min_size(
19 Pos2::new(height - rect_height, height - rect_height) + rect.left_top().to_vec2(),
20 Vec2::new(rect_height, rect_height),
21 ),
22 Rounding::ZERO,
23 Color32::BLACK,
24 );
25
26 painter.rect_filled(
27 Rect::from_min_size(
28 Pos2::new(height - rect_height + 1., height - rect_height + 1.) + rect.left_top().to_vec2(),
29 Vec2::new(rect_height - 2., rect_height - 2.),
30 ),
31 Rounding::ZERO,
32 Color32::WHITE,
33 );
34
35 let (r, g, b) = palette.get_rgb(caret_attr.get_background());
36 painter.rect_filled(
37 Rect::from_min_size(
38 Pos2::new(height - rect_height + 2., height - rect_height + 2.) + rect.left_top().to_vec2(),
39 Vec2::new(rect_height - 4., rect_height - 4.),
40 ),
41 Rounding::ZERO,
42 Color32::from_rgb(r, g, b),
43 );
44
45 painter.rect_filled(
46 Rect::from_min_size(Pos2::new(0., 0.) + rect.left_top().to_vec2(), Vec2::new(rect_height, rect_height)),
47 Rounding::ZERO,
48 Color32::BLACK,
49 );
50
51 painter.rect_filled(
52 Rect::from_min_size(Pos2::new(1., 1.) + rect.left_top().to_vec2(), Vec2::new(rect_height - 2., rect_height - 2.)),
53 Rounding::ZERO,
54 Color32::WHITE,
55 );
56
57 let (r, g, b) = palette.get_rgb(caret_attr.get_foreground());
58 painter.rect_filled(
59 Rect::from_min_size(Pos2::new(2., 2.) + rect.left_top().to_vec2(), Vec2::new(rect_height - 4., rect_height - 4.)),
60 Rounding::ZERO,
61 Color32::from_rgb(r, g, b),
62 );
63
64 let s_rect_height = height * 0.382;
65 let rh = s_rect_height / 1.8;
66 let (r, g, b) = palette.get_rgb(7);
67
68 let overlap = 2.0;
69
70 painter.rect_filled(
71 Rect::from_min_size(Pos2::new(rh - overlap, height - rh - overlap) + rect.left_top().to_vec2(), Vec2::new(rh, rh)),
72 Rounding::ZERO,
73 Color32::from_rgb(r ^ 0xFF, g ^ 0xFF, b ^ 0xFF),
74 );
75
76 painter.rect_filled(
77 Rect::from_min_size(
78 Pos2::new(rh - overlap + 1., height - rh - overlap + 1.) + rect.left_top().to_vec2(),
79 Vec2::new(rh - 2., rh - 2.),
80 ),
81 Rounding::ZERO,
82 Color32::from_rgb(r, g, b),
83 );
84
85 let (r, g, b) = palette.get_rgb(0);
86 painter.rect_filled(
87 Rect::from_min_size(
88 Pos2::new(overlap, height - 2. * rh + 2. + overlap) + rect.left_top().to_vec2(),
89 Vec2::new(rh, rh),
90 ),
91 Rounding::ZERO,
92 Color32::from_rgb(r ^ 0xFF, g ^ 0xFF, b ^ 0xFF),
93 );
94
95 painter.rect_filled(
96 Rect::from_min_size(
97 Pos2::new(1. + overlap, height - 2. * rh + 3. + overlap) + rect.left_top().to_vec2(),
98 Vec2::new(rh - 2., rh - 2.),
99 ),
100 Rounding::ZERO,
101 Color32::from_rgb(r, g, b),
102 );
103 let mut tex_id = SWAP_SVG.clone();
104 tex_id = tex_id.tint(Color32::WHITE);
105 tex_id.paint_at(
106 ui,
107 Rect::from_min_size(
108 Pos2::new(rect_height + 1., 0.) + rect.left_top().to_vec2(),
109 Vec2::new(s_rect_height, s_rect_height),
110 ),
111 );
112
113 if let Some(hp) = response.hover_pos() {
114 let pos = hp.to_vec2() - rect.left_top().to_vec2();
115
116 if response.clicked() {
117 if pos.x > rect_height && pos.y < rect_height {
118 result = Some(Message::ToggleColor);
119 }
120
121 if pos.x < rect_height && pos.y > rect_height {
122 result = Some(Message::SwitchToDefaultColor);
123 }
124 }
125 }
126 result
127 }
128
129 pub fn palette_editor_16(
130 ui: &mut egui::Ui,
131 caret_attr: &TextAttribute,
132 palette: &Palette,
133 ice_mode: icy_engine::IceMode,
134 font_mode: icy_engine::FontMode,
135 ) -> Option<Message> {
136 let mut result = None;
137
138 ui.horizontal(|ui| {
139 ui.add_space(4.0);
140 let right_border = 4.0;
141 let items_per_row = if palette.len() < 64 { 8 } else { 16 };
142
143 let upper_limit = (palette.len() as f32 / items_per_row as f32).ceil() as usize * items_per_row;
144
145 let height = (ui.available_width() - right_border) / items_per_row as f32;
146
147 let (id, stroke_rect) = ui.allocate_space(Vec2::new(
148 ui.available_width() - right_border,
149 height * upper_limit as f32 / items_per_row as f32,
150 ));
151
152 let mut response = ui.interact(stroke_rect, id, Sense::click());
153 let painter = ui.painter_at(stroke_rect);
154
155 for i in 0..upper_limit {
156 let (r, g, b) = palette.get_rgb(i as u32);
157 painter.rect_filled(
158 Rect::from_min_size(
159 Pos2::new(
160 stroke_rect.left() + (i % items_per_row) as f32 * height,
161 stroke_rect.top() + (i / items_per_row) as f32 * height,
162 ),
163 Vec2::new(height, height),
164 ),
165 Rounding::ZERO,
166 Color32::from_rgb(r, g, b),
167 );
168 }
169
170 let marker_len = height / 3.;
171 // paint fg marker
172 let stroke = Stroke::new(1., Color32::WHITE);
173 let origin = Pos2::new(
174 stroke_rect.left() + (caret_attr.get_foreground() % items_per_row as u32) as f32 * height,
175 stroke_rect.top() + (caret_attr.get_foreground() / items_per_row as u32) as f32 * height,
176 );
177 painter.line_segment([origin, origin + Vec2::new(marker_len, 0.)], stroke);
178 painter.line_segment([origin, origin + Vec2::new(0., marker_len)], stroke);
179 for i in 0..marker_len as usize {
180 painter.line_segment([origin + Vec2::new(i as f32, 0.), origin + Vec2::new(0., i as f32)], stroke);
181 }
182 let stroke = Stroke::new(1., Color32::GRAY);
183 painter.line_segment([origin, origin + Vec2::new(marker_len, 0.)], stroke);
184 painter.line_segment([origin, origin + Vec2::new(0., marker_len)], stroke);
185 painter.line_segment([origin + Vec2::new(marker_len, 0.), origin + Vec2::new(0., marker_len)], stroke);
186
187 // paint bg marker
188 let stroke = Stroke::new(1., Color32::WHITE);
189 let origin = Pos2::new(
190 stroke_rect.left() + (1 + caret_attr.get_background() % items_per_row as u32) as f32 * height,
191 stroke_rect.top() + (1 + caret_attr.get_background() / items_per_row as u32) as f32 * height,
192 );
193 painter.line_segment([origin, origin - Vec2::new(marker_len, 0.)], stroke);
194 painter.line_segment([origin, origin - Vec2::new(0., marker_len)], stroke);
195 for i in 0..marker_len as usize {
196 painter.line_segment([origin - Vec2::new(i as f32, 0.), origin - Vec2::new(0., i as f32)], stroke);
197 }
198 let stroke = Stroke::new(1., Color32::GRAY);
199 painter.line_segment([origin, origin - Vec2::new(marker_len, 0.)], stroke);
200 painter.line_segment([origin, origin - Vec2::new(0., marker_len)], stroke);
201 painter.line_segment([origin - Vec2::new(marker_len, 0.), origin - Vec2::new(0., marker_len)], stroke);
202
203 if let Some(hp) = response.hover_pos() {
204 let pos = (hp.to_vec2() - stroke_rect.left_top().to_vec2()) / Vec2::new(height, height);
205 let color = min(palette.len() as u32 - 1, pos.x as u32 + pos.y as u32 * items_per_row as u32);
206
207 if response.hovered() {
208 response = response.on_hover_ui(|ui| {
209 let col = palette.get_color(color);
210 let (r, g, b) = col.get_rgb();
211 if let Some(title) = &col.name {
212 ui.label(title);
213 }
214 ui.label(format!("#{:02X}{:02X}{:02X}", r, g, b));
215 });
216 }
217
218 if response.clicked() {
219 if color < 8 || font_mode.has_high_fg_colors() || palette.len() > 16 {
220 result = Some(Message::SetForeground(color));
221 }
222 response.mark_changed();
223 }
224 if response.secondary_clicked() {
225 if color < 8 || ice_mode.has_high_bg_colors() || palette.len() > 16 {
226 result = Some(Message::SetBackground(color));
227 }
228 response.mark_changed();
229 }
230 }
231 });
232 result
233 }