1use alloc::string::String;
14
15use crate::consts::*;
16use crate::keys::*;
17use crate::theme::color_mix;
18use crate::{Sp, StyleData, StyleShell, Ui, DOCK_MK_BOTTOM, DOCK_MK_LEFT, DOCK_MK_RIGHT, DOCK_MK_TOP, DOCK_TAB_HOLD, DTB_ICONS};
19
20pub struct Stock;
22
23impl StyleData for Stock {}
24impl StyleShell for Stock {}
25
26fn fill(ui: &mut Ui, x: i32, y: i32, w: i32, h: i32, r: i32, argb: u32) {
29 if argb & 0xFF00_0000 != 0 && w > 0 && h > 0 {
30 ui.cmd_rrect(x, y, w, h, r, 0, argb, 0, 0);
31 }
32}
33
34fn frame(ui: &mut Ui, x: i32, y: i32, w: i32, h: i32, r: i32, t: i32, argb: u32) {
35 if argb & 0xFF00_0000 != 0 && w > 0 && h > 0 {
36 ui.cmd_rrect(x, y, w, h, r, t, 0, argb, argb);
37 }
38}
39
40pub(crate) fn baseline(ui: &mut Ui, ts: u32, y: i32, h: i32) -> i32 {
42 let line = ui.ts_line(ts);
43 let b = ui.ts_base(ts);
44 y + ((h - line) >> 1) + b
45}
46
47pub(crate) fn text_at(ui: &mut Ui, ts: u32, x: i32, r: [i32; 4], s: &str, argb: u32, maxw: i32) {
49 let y = baseline(ui, ts, r[1], r[3]);
50 let (font, size) = ui.text.get_font();
51 let b = s.as_bytes();
52 let n = b.iter().position(|&c| c == 0).unwrap_or(b.len());
53 if maxw > 0 && ui.text.width_n(b, n) > maxw {
54 let (e, _) = ui.ellipsize(&b[..n], maxw);
55 ui.cmd_text(x, y, e.as_bytes(), font, size, argb, maxw);
56 return;
57 }
58 ui.cmd_text(x, y, &b[..n], font, size, argb, maxw);
59}
60
61pub(crate) fn fit(ui: &mut Ui, s: &str, maxw: i32) -> (String, i32) {
63 let w = ui.text.width(s);
64 if w <= maxw {
65 return (String::from(s), w);
66 }
67 ui.ellipsize(s.as_bytes(), maxw)
68}
69
70pub(crate) fn icon_in(ui: &mut Ui, cp: u32, r: [i32; 4], px: i32, argb: u32) {
72 ui.draw_color(argb);
73 ui.draw_icon(cp, r[0] + ((r[2] - px) >> 1), r[1] + ((r[3] - px) >> 1), px << 8);
74}
75
76pub fn card_opaque(ui: &Ui) -> u32 {
79 let card = ui.color(T_CARD);
80 let a = (card >> 24) as i32;
81 if a >= 255 {
82 return card;
83 }
84 color_mix(ui.color(T_BG) | 0xFF00_0000, card | 0xFF00_0000, a)
85}
86
87pub fn kind_colors(ui: &Ui, kind: u32) -> (u32, u32, u32) {
89 let dark = ui.theme_dark();
90 let card = card_opaque(ui);
91 let solid = match kind {
92 BADGE_INFO => ui.color(T_INFO),
93 BADGE_OK => ui.color(T_OK),
94 BADGE_WARN => ui.color(T_WARN),
95 BADGE_CRIT => ui.color(T_CRIT),
96 BADGE_ACCENT => ui.color(T_ACCENT),
97 _ => ui.color(T_TEXT3),
98 };
99 let bg = match kind {
100 BADGE_INFO => ui.color(T_INFO_BG),
101 BADGE_OK => ui.color(T_OK_BG),
102 BADGE_WARN => ui.color(T_WARN_BG),
103 BADGE_CRIT => ui.color(T_CRIT_BG),
104 BADGE_ACCENT => color_mix(card, ui.color(T_ACCENT), if dark { 72 } else { 40 }),
105 _ => color_mix(card, ui.color(T_TEXT), if dark { 36 } else { 22 }),
106 };
107 let text = if kind == BADGE_NEUTRAL {
109 ui.color(T_TEXT2)
110 } else if dark {
111 color_mix(solid, 0xFFFF_FFFF, 48)
112 } else {
113 color_mix(solid, 0xFF00_0000, 72)
114 };
115 (text, bg | 0xFF00_0000, solid)
116}
117
118pub(crate) fn grid_header_bg(ui: &mut Ui, sp: &Sp) {
121 let h = ui.hair();
122 let c = ui.color(T_DIVIDER);
123 ui.draw_color(c);
124 ui.draw_rect(sp.x, sp.y + sp.h - h, sp.w, h);
125}
126
127pub(crate) fn grid_header(ui: &mut Ui, sp: &Sp) {
128 let r = sp.rect();
129 if sp.ev & (EV_HOVER | EV_HELD) != 0 {
130 let c = ui.color(if sp.ev & EV_HELD != 0 { T_SUBTLE_PRESSED } else { T_SUBTLE_HOVER });
131 fill(ui, r[0], r[1], r[2], r[3] - ui.hair(), 0, c);
132 }
133 let inset = sp.s[0];
134 let chev = ui.dp(12);
135 let sorted = sp.flags & (GH_SORT_ASC | GH_SORT_DESC) != 0;
136 let avail = r[2] - inset * 2 - if sorted { chev + ui.dp(4) } else { 0 };
137 ui.ts_font(TS_CAPTION);
139 let (label, tw) = fit(ui, sp.label.unwrap_or(""), avail.max(1));
140 let tx = if sp.flags & GH_RIGHT != 0 { r[0] + r[2] - inset - tw.min(avail) - if sorted { chev + ui.dp(4) } else { 0 } } else { r[0] + inset };
141 let c = ui.color(if sorted { T_TEXT } else { T_TEXT2 });
142 text_at(ui, TS_CAPTION, tx, r, &label, c, avail.max(1));
143 if sorted {
144 let x = tx + tw.min(avail) + ui.dp(4);
145 let cp = if sp.flags & GH_SORT_DESC != 0 { IC_CHEV_DOWN_SMALL } else { IC_CHEV_UP_SMALL };
146 icon_in(ui, cp, [x, r[1], chev, r[3]], chev, c);
147 }
148 if sp.flags & GH_LAST == 0 {
149 let h = ui.hair();
150 let m = r[3] / 4;
151 let c = ui.color(T_DIVIDER);
152 ui.draw_color(c);
153 ui.draw_rect(r[0] + r[2] - h, r[1] + m, h, r[3] - m * 2);
154 }
155}
156
157pub(crate) fn grid_row(ui: &mut Ui, sp: &Sp) {
158 let r = sp.rect();
159 let card = card_opaque(ui);
160 let sel = sp.value != 0;
161 let bg = if sel {
162 let a = ui.color(T_ACCENT);
163 color_mix(card, a, if ui.theme_dark() { 64 } else { 36 })
164 } else if sp.ev & EV_HELD != 0 {
165 ui.color(T_SUBTLE_PRESSED)
166 } else if sp.ev & EV_HOVER != 0 {
167 ui.color(T_SUBTLE_HOVER)
168 } else if sp.flags & GR_STRIPE != 0 {
169 color_mix(card, ui.color(T_TEXT), 6)
170 } else {
171 0
172 };
173 fill(ui, r[0], r[1], r[2], r[3], 0, bg);
174 if sel {
175 let c = ui.color(T_ACCENT);
176 let (pw, ph) = (ui.dp(3), ui.dp(16).min(r[3] - ui.dp(8)));
177 fill(ui, r[0], r[1] + ((r[3] - ph) >> 1), pw, ph, pw >> 1, c);
178 }
179 let h = ui.hair();
180 let c = ui.color(T_DIVIDER);
181 ui.draw_color(c);
182 ui.draw_rect(r[0], r[1] + r[3] - h, r[2], h);
183 if sp.flags & GR_FOCUS != 0 {
184 let c = ui.color(T_FOCUS_OUT);
185 frame(ui, r[0] + h, r[1] + h, r[2] - h * 2, r[3] - h * 2, ui.dp(2), h, c);
186 }
187}
188
189pub(crate) fn tree_twisty(ui: &mut Ui, sp: &Sp) {
190 let r = sp.rect();
191 if sp.ev & EV_HOVER != 0 {
192 let c = ui.color(T_SUBTLE_HOVER);
193 fill(ui, r[0], r[1], r[2], r[3], ui.dp(4), c);
194 }
195 let cp = if sp.value != 0 { IC_CHEV_DOWN_SMALL } else { IC_CHEV_RIGHT_SMALL };
196 let c = ui.color(T_TEXT2);
197 let px = ui.dp(12);
198 icon_in(ui, cp, r, px, c);
199}
200
201pub(crate) fn badge_size(ui: &mut Ui, sp: &mut Sp) -> (i32, i32) {
202 ui.ts_font(TS_CAPTION);
203 let tw = ui.text.width(sp.label.unwrap_or(""));
204 sp.s[0] = tw;
205 let dot = if sp.icon != 0 { ui.dp(14) } else { 0 };
206 (tw + ui.dp(16) + dot, ui.dp_text(20))
207}
208
209pub(crate) fn badge(ui: &mut Ui, sp: &Sp) {
210 let r = sp.rect();
211 let (text, bg, solid) = kind_colors(ui, sp.flags & 7);
212 let h = r[3].min(ui.dp_text(20));
213 let y = r[1] + ((r[3] - h) >> 1);
214 fill(ui, r[0], y, r[2], h, h >> 1, bg);
215 let mut x = r[0] + ui.dp(8);
216 if sp.icon != 0 {
217 let px = ui.dp(12);
218 icon_in(ui, sp.icon, [x, y, px, h], px, solid);
219 x += ui.dp(14);
220 }
221 text_at(ui, TS_CAPTION, x, [r[0], y, r[2], h], sp.label.unwrap_or(""), text, r[0] + r[2] - x);
222}
223
224pub(crate) fn status_dot(ui: &mut Ui, sp: &Sp) {
225 let r = sp.rect();
226 let (_, bg, solid) = kind_colors(ui, sp.flags & 7);
227 let d = r[2].min(r[3]);
228 let cx = (r[0] << 8) + (r[2] << 7);
229 let cy = (r[1] << 8) + (r[3] << 7);
230 ui.draw_color(bg);
231 ui.draw_circle(cx, cy, d << 7, 0);
232 ui.draw_color(solid);
233 ui.draw_circle(cx, cy, (d << 7) * 5 / 8, 0);
234}
235
236pub(crate) fn meter(ui: &mut Ui, sp: &Sp) {
237 let r = sp.rect();
238 let th = r[3].min(ui.dp(8));
239 let y = r[1] + ((r[3] - th) >> 1);
240 let c = ui.color(T_TRACK);
241 fill(ui, r[0], y, r[2], th, th >> 1, c);
242 if sp.value2 <= 0 || sp.value <= 0 {
243 return;
244 }
245 let w = ((sp.value.min(sp.value2) as i64 * r[2] as i64) / sp.value2 as i64) as i32;
246 let (_, _, solid) = kind_colors(ui, sp.flags & 7);
247 fill(ui, r[0], y, w.max(th), th, th >> 1, solid);
248}
249
250pub(crate) fn chart_palette(ui: &Ui) -> [u32; 8] {
251 let a = ui.color(T_ACCENT);
252 let t = ui.color(T_TEXT);
253 [
254 a,
255 ui.color(T_INFO),
256 ui.color(T_OK),
257 ui.color(T_WARN),
258 ui.color(T_CRIT),
259 color_mix(a, t, 112),
260 ui.color(T_TEXT3),
261 color_mix(ui.color(T_INFO), ui.color(T_OK), 128),
262 ]
263}
264
265pub(crate) fn nav_pane(ui: &mut Ui, sp: &Sp) {
268 let r = sp.rect();
269 let c = ui.color(T_LAYER);
270 fill(ui, r[0], r[1], r[2], r[3], 0, c);
271 let h = ui.hair();
272 let c = ui.color(T_DIVIDER);
273 ui.draw_color(c);
274 ui.draw_rect(r[0] + r[2] - h, r[1], h, r[3]);
275}
276
277pub(crate) fn nav_item_size(ui: &mut Ui, sp: &mut Sp) -> (i32, i32) {
278 if sp.flags & NAV_COMPACT != 0 {
279 return (ui.dp(40), ui.dp(36));
280 }
281 ui.ts_font(TS_BODY);
282 let tw = ui.text.width(sp.label.unwrap_or(""));
283 let bw = match sp.label2 {
284 Some(b) if !b.is_empty() => {
285 ui.ts_font(TS_CAPTION);
286 ui.text.width(b) + ui.dp(16)
287 }
288 _ => 0,
289 };
290 sp.s[0] = tw;
291 sp.s[1] = bw;
292 (ui.dp(16 + 16 + 12 + 12) + tw + bw, ui.dp(36))
293}
294
295pub(crate) fn nav_item(ui: &mut Ui, sp: &Sp) {
296 let r = sp.rect();
297 let m = ui.dp(4);
298 let (x, y, w, h) = (r[0] + m, r[1] + ui.dp(2), r[2] - m * 2, r[3] - ui.dp(4));
299 let sel = sp.value != 0;
300 let bg = if sp.ev & EV_HELD != 0 {
301 ui.color(T_SUBTLE_PRESSED)
302 } else if sel || sp.ev & EV_HOVER != 0 {
303 ui.color(T_SUBTLE_HOVER)
304 } else {
305 0
306 };
307 fill(ui, x, y, w, h, ui.dp(4), bg);
308 if sel {
309 let c = ui.color(T_ACCENT);
310 let (pw, ph) = (ui.dp(3), ui.dp(16));
311 fill(ui, x, y + ((h - ph) >> 1), pw, ph, pw >> 1, c);
312 }
313 let disabled = sp.state == 3;
314 let fg = ui.color(if disabled { T_TEXT_DISABLED } else { T_TEXT });
315 let px = ui.dp(16);
316 let compact = sp.flags & NAV_COMPACT != 0;
317 let ix = if compact { x + ((w - px) >> 1) } else { x + ui.dp(12) };
318 if sp.icon != 0 {
319 icon_in(ui, sp.icon, [ix, y, px, h], px, if sel { ui.color(T_ACCENT_TEXT) } else { fg });
320 }
321 let badge = sp.label2.filter(|b| !b.is_empty());
323 if compact {
324 if badge.is_some() {
325 let c = ui.color(T_ACCENT);
326 ui.draw_color(c);
327 ui.draw_circle((ix + px) << 8, (y + ui.dp(8)) << 8, ui.dp_fx(3 * 256), 0);
328 }
329 return;
330 }
331 let tx = ix + px + ui.dp(12);
332 let mut end = x + w - ui.dp(8);
333 if let Some(b) = badge {
334 ui.ts_font(TS_CAPTION);
335 let bh = ui.dp_text(18).min(h);
337 let bw = (ui.text.width(b) + ui.dp(12)).max(bh);
338 end -= bw;
339 let a = ui.color(T_ACCENT);
340 fill(ui, end, y + ((h - bh) >> 1), bw, bh, bh >> 1, a);
341 let c = ui.color(T_ON_ACCENT);
342 text_at(ui, TS_CAPTION, end + ui.dp(6), [end, y, bw, h], b, c, 0);
343 end -= ui.dp(6);
344 }
345 let ts = if sel { TS_BODY_STRONG } else { TS_BODY };
346 text_at(ui, ts, tx, [x, y, w, h], sp.label.unwrap_or(""), fg, (end - tx).max(1));
347}
348
349pub(crate) fn nav_section_size(ui: &mut Ui, sp: &mut Sp) -> (i32, i32) {
350 if sp.flags & NAV_COMPACT != 0 {
351 return (ui.dp(40), ui.dp(13));
352 }
353 (ui.dp(40), ui.dp(30))
354}
355
356pub(crate) fn nav_section(ui: &mut Ui, sp: &Sp) {
357 let r = sp.rect();
358 if sp.flags & NAV_COMPACT != 0 {
359 let h = ui.hair();
360 let c = ui.color(T_DIVIDER);
361 ui.draw_color(c);
362 ui.draw_rect(r[0] + ui.dp(10), r[1] + ((r[3] - h) >> 1), r[2] - ui.dp(20), h);
363 return;
364 }
365 let c = ui.color(T_TEXT3);
366 let y = r[1] + ui.dp(8);
367 text_at(ui, TS_CAPTION, r[0] + ui.dp(16), [r[0], y, r[2], r[3] - ui.dp(8)], sp.label.unwrap_or(""), c, r[2] - ui.dp(24));
368}
369
370pub(crate) fn command_bar(_ui: &mut Ui, _sp: &Sp) {}
371
372pub(crate) fn split_grip(ui: &mut Ui, sp: &Sp) {
373 let r = sp.rect();
374 let active = sp.ev & (EV_HOVER | EV_HELD) != 0;
375 let t = if active { ui.dp(2) } else { ui.hair() };
376 let c = ui.color(if active { T_ACCENT } else { T_DIVIDER });
377 ui.draw_color(c);
378 if sp.flags & 1 != 0 {
379 ui.draw_rect(r[0], r[1] + ((r[3] - t) >> 1), r[2], t);
380 } else {
381 ui.draw_rect(r[0] + ((r[2] - t) >> 1), r[1], t, r[3]);
382 }
383}
384
385pub(crate) fn breadcrumb_sep(ui: &mut Ui, sp: &Sp) {
386 let px = ui.dp(12);
387 let c = ui.color(T_TEXT3);
388 icon_in(ui, IC_CHEV_RIGHT_SMALL, sp.rect(), px, c);
389}
390
391pub(crate) fn segment(ui: &mut Ui, sp: &Sp) {
392 let r = sp.rect();
393 let rad = ui.dp(4);
394 if sp.value2 == 1 {
395 let c = ui.color(T_SUBTLE_HOVER);
397 let c = if c & 0xFF00_0000 == 0 { ui.color(T_CTRL_DISABLED) } else { c };
398 fill(ui, r[0], r[1], r[2], r[3], rad + ui.dp(2), c);
399 return;
400 }
401 let m = ui.dp(2);
402 let (x, y, w, h) = (r[0] + m, r[1] + m, r[2] - m * 2, r[3] - m * 2);
403 if sp.value != 0 {
404 let f = ui.color(T_CTRL);
407 let f = if ui.theme_dark() { f } else { f | 0xFF00_0000 };
408 let s = ui.color(T_CTRL_STROKE);
409 ui.cmd_rrect(x, y, w, h, rad, ui.hair(), f, s, ui.color(T_CTRL_STROKE2));
410 } else if sp.ev & EV_HOVER != 0 {
411 let c = ui.color(T_SUBTLE_HOVER);
412 fill(ui, x, y, w, h, rad, c);
413 }
414 let ts = if sp.value != 0 { TS_BODY_STRONG } else { TS_BODY };
415 ui.ts_font(ts);
416 let (label, tw) = fit(ui, sp.label.unwrap_or(""), (w - ui.dp(8)).max(1));
418 let c = ui.color(if sp.state == 3 { T_TEXT_DISABLED } else if sp.value != 0 { T_TEXT } else { T_TEXT2 });
419 text_at(ui, ts, x + ((w - tw) >> 1).max(0), r, &label, c, w);
420}
421
422pub(crate) fn wizard_step(ui: &mut Ui, sp: &Sp) {
423 let r = sp.rect();
424 let d = ui.dp(24);
425 let cx = r[0] + ui.dp(12) + (d >> 1);
426 let cy = r[1] + (r[3] >> 1);
427 if sp.flags & 1 != 0 {
429 let t = ui.dp(2);
430 let c = ui.color(if sp.value2 == WS_DONE { T_ACCENT } else { T_DIVIDER });
431 ui.draw_color(c);
432 ui.draw_rect(cx - (t >> 1), cy + (d >> 1) + ui.dp(2), t, r[1] + r[3] - cy - (d >> 1) + (r[3] >> 1) - ui.dp(4));
433 }
434 let hover = sp.ev & EV_HOVER != 0;
435 if hover && sp.value2 != WS_CURRENT {
436 let c = ui.color(T_SUBTLE_HOVER);
437 fill(ui, r[0] + ui.dp(4), r[1] + ui.dp(2), r[2] - ui.dp(8), r[3] - ui.dp(4), ui.dp(4), c);
438 }
439 let (ring, inner, num) = match sp.value2 {
440 WS_CURRENT => (ui.color(T_ACCENT), ui.color(T_ACCENT), ui.color(T_ON_ACCENT)),
441 WS_DONE => (ui.color(T_ACCENT), color_mix(card_opaque(ui), ui.color(T_ACCENT), 40), ui.color(T_ACCENT_TEXT)),
442 WS_ERROR => (ui.color(T_CRIT), ui.color(T_CRIT_BG), ui.color(T_CRIT)),
443 _ => (ui.color(T_STRONG_STROKE), card_opaque(ui), ui.color(T_TEXT2)),
444 };
445 ui.draw_color(ring);
446 ui.draw_circle(cx << 8, cy << 8, d << 7, 0);
447 ui.draw_color(inner);
448 ui.draw_circle(cx << 8, cy << 8, (d << 7) - (ui.hair() << 8) - 128, 0);
449 let cr = [cx - (d >> 1), cy - (d >> 1), d, d];
450 if sp.value2 == WS_DONE {
451 let px = ui.dp(12);
452 icon_in(ui, IC_CHECK_MARK, cr, px, num);
453 } else if sp.value2 == WS_ERROR {
454 let px = ui.dp(12);
455 icon_in(ui, IC_CANCEL, cr, px, num);
456 } else {
457 let s = alloc::format!("{}", sp.value);
458 ui.ts_font(TS_CAPTION);
459 let tw = ui.text.width(&s);
460 text_at(ui, TS_CAPTION, cx - (tw >> 1), cr, &s, num, 0);
461 }
462 let ts = if sp.value2 == WS_CURRENT { TS_BODY_STRONG } else { TS_BODY };
463 let c = ui.color(match sp.value2 {
464 WS_CURRENT => T_TEXT,
465 WS_TODO => T_TEXT2,
466 _ => T_TEXT,
467 });
468 let tx = cx + (d >> 1) + ui.dp(12);
469 text_at(ui, ts, tx, r, sp.label.unwrap_or(""), c, r[0] + r[2] - tx - ui.dp(8));
470}
471
472pub(crate) fn toast(ui: &mut Ui, sp: &mut Sp) {
473 let r = sp.rect();
474 let rad = ui.dp(8);
475 ui.draw_color(ui.color(T_SHADOW));
476 ui.cmd_shadow(r[0], r[1], r[2], r[3], rad, ui.dp(12));
477 let f = ui.color(T_FLYOUT) | 0xFF00_0000;
478 let s = ui.color(T_FLYOUT_STROKE);
479 ui.cmd_rrect(r[0], r[1], r[2], r[3], rad, ui.hair(), f, s, s);
480 let kind = match sp.flags & 3 {
481 IB_SUCCESS => BADGE_OK,
482 IB_WARNING => BADGE_WARN,
483 IB_ERROR => BADGE_CRIT,
484 _ => BADGE_INFO,
485 };
486 let (_, _, solid) = kind_colors(ui, kind);
487 fill(ui, r[0] + ui.dp(6), r[1] + ui.dp(10), ui.dp(3), r[3] - ui.dp(20), ui.dp(1), solid);
489 let icon = match sp.flags & 3 {
490 IB_SUCCESS => IC_CHECK_CIRCLE_FILL,
491 IB_WARNING => IC_WARNING,
492 IB_ERROR => IC_ERROR_FILL,
493 _ => IC_INFO,
494 };
495 let px = ui.dp(16);
496 icon_in(ui, icon, [r[0] + ui.dp(18), r[1] + ui.dp(14), px, px], px, solid);
497 let tx = r[0] + ui.dp(44);
498 let cs = ui.dp(28);
499 let cx = r[0] + r[2] - cs - ui.dp(8);
500 let maxw = cx - tx - ui.dp(4);
501 let t = ui.color(T_TEXT);
502 text_at(ui, TS_BODY_STRONG, tx, [r[0], r[1] + ui.dp(10), r[2], ui.dp(24)], sp.label.unwrap_or(""), t, maxw);
503 if let Some(m) = sp.label2 {
504 let c = ui.color(T_TEXT2);
505 text_at(ui, TS_CAPTION, tx, [r[0], r[1] + ui.dp(32), r[2], ui.dp(20)], m, c, maxw);
506 }
507 sp.s[0] = cx;
508 sp.s[1] = cs;
509}
510
511pub(crate) fn spinner(ui: &mut Ui, sp: &Sp) {
512 let r = sp.rect();
513 let d = r[2].min(r[3]);
514 let cx = ((r[0] << 8) + (r[2] << 7)) as f32;
515 let cy = ((r[1] << 8) + (r[3] << 7)) as f32;
516 let t = (d / 8).max(ui.dp(2));
517 let rad = ((d - t) << 7) as f32;
518 let phase = (sp.value & 1023) as f32 / 1024.0;
520 let a0 = phase * core::f32::consts::TAU;
521 let track = ui.color(T_TRACK);
522 let accent = ui.color(T_ACCENT);
523 let n = 32;
524 for k in 0..n {
525 let b0 = k as f32 / n as f32 * core::f32::consts::TAU;
526 let b1 = (k + 1) as f32 / n as f32 * core::f32::consts::TAU;
527 let (s0, c0) = sincos(b0);
528 let (s1, c1) = sincos(b1);
529 ui.draw_color(track);
530 ui.draw_line(rt::fx::cvt(cx + c0 * rad), rt::fx::cvt(cy + s0 * rad), rt::fx::cvt(cx + c1 * rad), rt::fx::cvt(cy + s1 * rad), t << 8);
531 }
532 let span = 0.30 * core::f32::consts::TAU;
533 let m = 12;
534 for k in 0..m {
535 let b0 = a0 + span * k as f32 / m as f32;
536 let b1 = a0 + span * (k + 1) as f32 / m as f32;
537 let (s0, c0) = sincos(b0);
538 let (s1, c1) = sincos(b1);
539 ui.draw_color(accent);
540 ui.draw_line(rt::fx::cvt(cx + c0 * rad), rt::fx::cvt(cy + s0 * rad), rt::fx::cvt(cx + c1 * rad), rt::fx::cvt(cy + s1 * rad), t << 8);
541 }
542}
543
544pub(crate) fn progress_indeterminate(ui: &mut Ui, sp: &Sp) {
545 let r = sp.rect();
546 let th = r[3].min(ui.dp(4));
547 let y = r[1] + ((r[3] - th) >> 1);
548 let c = ui.color(T_TRACK);
549 fill(ui, r[0], y, r[2], th, th >> 1, c);
550 let seg = r[2] * 3 / 10;
551 let pos = ((sp.value & 1023) as i64 * (r[2] + seg) as i64 / 1024) as i32 - seg;
552 let x0 = (r[0] + pos).max(r[0]);
553 let x1 = (r[0] + pos + seg).min(r[0] + r[2]);
554 if x1 > x0 {
555 let c = ui.color(T_ACCENT);
556 fill(ui, x0, y, x1 - x0, th, th >> 1, c);
557 }
558}
559
560pub(crate) fn calendar_day(ui: &mut Ui, sp: &Sp) {
561 let r = sp.rect();
562 let d = r[2].min(r[3]) - ui.dp(4);
563 let cx = r[0] + (r[2] >> 1);
564 let cy = r[1] + (r[3] >> 1);
565 let sel = sp.value != 0;
566 if sel {
567 let c = ui.color(T_ACCENT);
568 ui.draw_color(c);
569 ui.draw_circle(cx << 8, cy << 8, d << 7, 0);
570 } else if sp.ev & EV_HOVER != 0 {
571 let c = ui.color(T_SUBTLE_HOVER);
572 ui.draw_color(c);
573 ui.draw_circle(cx << 8, cy << 8, d << 7, 0);
574 }
575 if sp.value2 != 0 && !sel {
576 let c = ui.color(T_ACCENT);
577 ui.draw_color(c);
578 ui.draw_circle(cx << 8, cy << 8, (d << 7) - 128, ui.hair() << 8);
579 }
580 let label = sp.label.unwrap_or("");
581 ui.ts_font(TS_BODY);
582 let tw = ui.text.width(label);
583 let c = ui.color(if sel {
584 T_ON_ACCENT
585 } else if sp.state == 3 {
586 T_TEXT_DISABLED
587 } else if sp.flags & 1 != 0 {
588 T_TEXT3
589 } else {
590 T_TEXT
591 });
592 text_at(ui, TS_BODY, cx - (tw >> 1), r, label, c, 0);
593}
594
595pub(crate) fn dock_group(ui: &mut Ui, sp: &Sp) {
598 let r = sp.rect();
599 let c = ui.color(T_LAYER);
600 fill(ui, r[0], r[1], r[2], r[3], 0, c);
601 let h = ui.hair();
602 let c = ui.color(T_DIVIDER);
603 frame(ui, r[0], r[1], r[2], r[3], 0, h, c);
604}
605
606pub(crate) fn dock_header(ui: &mut Ui, sp: &Sp) {
607 let r = sp.rect();
608 let h = ui.hair();
609 let c = ui.color(T_DIVIDER);
610 ui.draw_color(c);
611 ui.draw_rect(r[0], r[1] + r[3] - h, r[2], h);
612}
613
614pub(crate) fn dock_tab_size(ui: &mut Ui, sp: &mut Sp) -> (i32, i32) {
615 ui.ts_font(TS_BODY);
616 let tw = ui.text.width(sp.label.unwrap_or(""));
617 let icon = if sp.icon != 0 { 16 + 6 } else { 0 };
618 (ui.dp(10 + 10 + icon) + tw, ui.dp(32))
619}
620
621pub(crate) fn dock_tab(ui: &mut Ui, sp: &Sp) {
622 let r = sp.rect();
623 let m = ui.dp(2);
624 let (x, y, w, h) = (r[0] + m, r[1] + ui.dp(4), r[2] - m * 2, r[3] - ui.dp(8));
625 let hold = sp.flags & DOCK_TAB_HOLD != 0;
626 let bg = if hold || sp.ev & EV_HELD != 0 {
627 ui.color(T_SUBTLE_PRESSED)
628 } else if sp.ev & EV_HOVER != 0 {
629 ui.color(T_SUBTLE_HOVER)
630 } else {
631 0
632 };
633 fill(ui, x, y, w, h, ui.dp(4), bg);
634 if hold {
635 let a = ui.color(T_ACCENT);
637 frame(ui, x, y, w, h, ui.dp(4), ui.hair(), a);
638 }
639 let sel = sp.value != 0;
640 let fg = ui.color(if sel { T_TEXT } else { T_TEXT2 });
641 let mut tx = r[0] + ui.dp(10);
642 if sp.icon != 0 {
643 let px = ui.dp(16);
644 icon_in(ui, sp.icon, [tx, r[1], px, r[3]], px, fg);
645 tx += px + ui.dp(6);
646 }
647 let avail = (r[0] + r[2] - ui.dp(8) - tx).max(1);
648 let label = sp.label.unwrap_or("");
649 ui.ts_font(TS_BODY);
650 let s = if ui.text.width(label) > avail { ui.ellipsize(label.as_bytes(), avail).0 } else { alloc::string::String::from(label) };
651 if sp.icon == 0 || s != "…" {
653 text_at(ui, TS_BODY, tx, r, &s, fg, avail);
654 }
655 if sel {
656 let t = ui.dp(2);
658 let c = ui.color(if sp.value2 != 0 { T_ACCENT } else { T_TEXT3 });
659 let i = ui.dp(8);
660 fill(ui, r[0] + i, r[1] + r[3] - t, r[2] - i * 2, t, t >> 1, c);
661 }
662}
663
664pub(crate) fn dock_rail(ui: &mut Ui, sp: &Sp) {
665 let r = sp.rect();
666 let c = ui.color(T_LAYER);
667 fill(ui, r[0], r[1], r[2], r[3], 0, c);
668 let h = ui.hair();
669 let c = ui.color(T_DIVIDER);
670 ui.draw_color(c);
671 match sp.flags {
672 0 => ui.draw_rect(r[0] + r[2] - h, r[1], h, r[3]),
673 1 => ui.draw_rect(r[0], r[1], h, r[3]),
674 _ => ui.draw_rect(r[0], r[1], r[2], h),
675 }
676}
677
678pub(crate) fn dock_marker(ui: &mut Ui, sp: &Sp) {
679 let r = sp.rect();
680 let hover = sp.ev & EV_HOVER != 0;
681 let rad = ui.dp(6);
682 let a = ui.color(T_ACCENT);
683 ui.draw_color(ui.color(T_SHADOW));
684 ui.cmd_shadow(r[0], r[1], r[2], r[3], rad, ui.dp(8));
685 let bg = card_opaque(ui) | 0xFF00_0000;
686 let stroke = if hover { a } else { ui.color(T_CTRL_STROKE) };
687 let t = if hover { ui.dp(2) } else { ui.hair() };
688 ui.cmd_rrect(r[0], r[1], r[2], r[3], rad, t, bg, stroke, stroke);
689 let i = ui.dp(8);
691 let f = [r[0] + i, r[1] + i, r[2] - i * 2, r[3] - i * 2];
692 let c = ui.color(T_TEXT2);
693 frame(ui, f[0], f[1], f[2], f[3], ui.dp(2), ui.hair(), c);
694 let (pw, ph) = (f[2] * 2 / 5, f[3] * 2 / 5);
695 let p = match sp.value {
696 DOCK_MK_LEFT => [f[0], f[1], pw, f[3]],
697 DOCK_MK_RIGHT => [f[0] + f[2] - pw, f[1], pw, f[3]],
698 DOCK_MK_TOP => [f[0], f[1], f[2], ph],
699 DOCK_MK_BOTTOM => [f[0], f[1] + f[3] - ph, f[2], ph],
700 _ => {
701 let m = ui.dp(3);
702 [f[0] + m, f[1] + m, f[2] - m * 2, f[3] - m * 2]
703 }
704 };
705 let fc = if hover { a } else { (a & 0x00FF_FFFF) | 0xA000_0000 };
706 fill(ui, p[0], p[1], p[2], p[3], ui.dp(1), fc);
707}
708
709pub(crate) fn dock_preview(ui: &mut Ui, sp: &Sp) {
710 let r = sp.rect();
711 let c = ui.color(T_ACCENT);
712 let tint = (c & 0x00FF_FFFF) | if sp.value != 0 { 0x1800_0000 } else { 0x3000_0000 };
713 let (t, rad) = (ui.dp(2), ui.dp(4));
714 ui.cmd_rrect(r[0], r[1], r[2], r[3], rad, t, tint, c, c);
715}
716
717fn bg_opaque(ui: &Ui) -> u32 {
721 ui.color(T_BG) | 0xFF00_0000
722}
723
724fn over_bg(ui: &Ui, tok: u32) -> u32 {
726 let c = ui.color(tok);
727 color_mix(bg_opaque(ui), c | 0xFF00_0000, (c >> 24) as i32)
728}
729
730const DESK_R: i32 = 6;
732
733pub(crate) fn desk_bg(ui: &mut Ui, sp: &Sp) {
734 let r = sp.rect();
735 let c = if ui.high_contrast() { bg_opaque(ui) } else { color_mix(bg_opaque(ui), ui.color(T_TEXT) | 0xFF00_0000, if ui.theme_dark() { 14 } else { 22 }) };
737 fill(ui, r[0], r[1], r[2], r[3], 0, c);
738}
739
740pub(crate) fn desk_window(ui: &mut Ui, sp: &Sp) {
741 let r = sp.rect();
742 let (act, max, hc) = (sp.value != 0, sp.flags & 1 != 0, ui.high_contrast());
743 let rad = if max { 0 } else { ui.dp(DESK_R) };
744 if !max && !hc {
745 ui.draw_color(ui.color(T_SHADOW));
746 ui.cmd_shadow(r[0], r[1] + ui.dp(2), r[2], r[3], rad, ui.dp(if act { 12 } else { 6 }));
747 }
748 let bg = bg_opaque(ui);
749 let stroke = if hc {
750 ui.color(if act { T_ACCENT } else { T_TEXT })
751 } else if act {
752 color_mix(ui.color(T_CTRL_STROKE) | 0xFF00_0000, ui.color(T_ACCENT) | 0xFF00_0000, 140)
753 } else {
754 over_bg(ui, T_CTRL_STROKE)
755 };
756 let t = if max { 0 } else if hc { ui.dp(2) } else { ui.hair() };
757 ui.cmd_rrect(r[0], r[1], r[2], r[3], rad, t, bg, stroke, stroke);
758}
759
760pub(crate) fn desk_titlebar(ui: &mut Ui, sp: &Sp) {
761 let r = sp.rect();
762 let (act, hc) = (sp.value != 0, ui.high_contrast());
763 let fillc = if hc && act {
766 ui.color(T_ACCENT) | 0xFF00_0000
767 } else if act {
768 color_mix(bg_opaque(ui), ui.color(T_ACCENT) | 0xFF00_0000, if ui.theme_dark() { 40 } else { 26 })
769 } else {
770 0
771 };
772 if fillc != 0 && r[2] > 0 && r[3] > 0 {
773 let rad = (ui.dp(DESK_R) - ui.hair()).max(0).min(r[3] / 2);
775 ui.cmd_rrect(r[0], r[1], r[2], r[3], rad, 0, fillc, 0, 0);
776 ui.draw_color(fillc);
777 ui.draw_rect(r[0], r[1] + rad, r[2], r[3] - rad);
778 }
779 let h = ui.hair();
780 let line = over_bg(ui, T_DIVIDER);
781 ui.draw_color(line);
782 ui.draw_rect(r[0], r[1] + r[3] - h, r[2], h);
783 let fg = ui.color(if hc && act {
784 T_ON_ACCENT
785 } else if act {
786 T_TEXT
787 } else {
788 T_TEXT3
789 });
790 let (mut x, mut w) = (sp.s[0], sp.s[1]);
791 let px = ui.dp(16);
792 if sp.icon != 0 && w > px {
793 icon_in(ui, sp.icon, [x, r[1], px, r[3]], px, fg);
794 x += px + ui.dp(8);
795 w -= px + ui.dp(8);
796 }
797 let ts = {
798 let st = ui.style;
799 let v = st.metric(SM_TB_TITLE_TS) as u32;
800 if v != 0 { v } else { TS_BODY }
801 };
802 let title = sp.label.unwrap_or("");
803 if w > 0 && !title.is_empty() {
804 text_at(ui, ts, x, r, title, fg, w);
805 }
806}
807
808pub(crate) fn desk_snap(ui: &mut Ui, sp: &Sp) {
809 let m = ui.dp(4);
810 let mut q = *sp;
811 q.place([sp.x + m, sp.y + m, sp.w - m * 2, sp.h - m * 2]);
812 q.value = 0;
813 dock_preview(ui, &q);
814}
815
816pub(crate) fn taskbar(ui: &mut Ui, sp: &Sp) {
817 let r = sp.rect();
818 let c = over_bg(ui, T_LAYER);
819 fill(ui, r[0], r[1], r[2], r[3], 0, c);
820 let h = ui.hair();
821 let c = over_bg(ui, T_LAYER_STROKE);
822 ui.draw_color(c);
823 ui.draw_rect(r[0], r[1], r[2], h);
824}
825
826pub(crate) fn taskbar_button_size(ui: &mut Ui, sp: &mut Sp) -> (i32, i32) {
827 if sp.flags & DTB_ICONS != 0 {
828 return (ui.dp(40), ui.dp(32));
829 }
830 ui.ts_font(TS_BODY);
831 let tw = ui.text.width(sp.label.unwrap_or(""));
832 let icon = if sp.icon != 0 { 16 + 8 } else { 0 };
833 (ui.dp(10 + icon + 12) + tw, ui.dp(32))
834}
835
836pub(crate) fn taskbar_button(ui: &mut Ui, sp: &Sp) {
837 let r = sp.rect();
838 let (on, min, hc) = (sp.value != 0, sp.value2 != 0, ui.high_contrast());
839 let rad = ui.dp(4);
840 let bg = if hc && on {
841 ui.color(T_ACCENT)
842 } else if sp.ev & EV_HELD != 0 {
843 ui.color(T_SUBTLE_PRESSED)
844 } else if sp.ev & EV_HOVER != 0 || on {
845 ui.color(T_SUBTLE_HOVER)
846 } else {
847 0
848 };
849 fill(ui, r[0], r[1], r[2], r[3], rad, bg);
850 if hc && !on {
851 frame(ui, r[0], r[1], r[2], r[3], rad, ui.hair(), ui.color(T_TEXT));
852 }
853 let fg = ui.color(if hc && on {
854 T_ON_ACCENT
855 } else if min {
856 T_TEXT3
857 } else {
858 T_TEXT
859 });
860 let px = ui.dp(16);
861 let icons = sp.flags & DTB_ICONS != 0;
862 let mut tx = r[0] + ui.dp(10);
863 if sp.icon != 0 {
864 if icons {
865 icon_in(ui, sp.icon, r, px, fg);
866 } else {
867 icon_in(ui, sp.icon, [tx, r[1], px, r[3]], px, fg);
868 tx += px + ui.dp(8);
869 }
870 }
871 if !icons {
872 let avail = (r[0] + r[2] - ui.dp(8) - tx).max(1);
873 let label = sp.label.unwrap_or("");
874 ui.ts_font(TS_BODY);
875 let s = if ui.text.width(label) > avail { ui.ellipsize(label.as_bytes(), avail).0 } else { String::from(label) };
876 if sp.icon == 0 || s != "…" {
877 text_at(ui, TS_BODY, tx, r, &s, fg, avail);
878 }
879 }
880 let t = ui.dp(3);
883 let (bw, c) = if on { (ui.dp(16), ui.color(if hc { T_ON_ACCENT } else { T_ACCENT })) } else { (ui.dp(6), ui.color(T_TEXT3)) };
884 fill(ui, r[0] + ((r[2] - bw) >> 1), r[1] + r[3] - t - ui.dp(1), bw, t, t >> 1, c);
885}
886
887pub(crate) fn sincos(a: f32) -> (f32, f32) {
889 let (s, c) = gfx::xform::sincos(a as f64);
890 (s as f32, c as f32)
891}