1use crate::consts::*;
18use crate::{DrawRef, Ui};
19
20const PAD: u32 = 10;
21const GAP: i32 = 8;
22
23#[derive(Clone, Default)]
25pub struct CardDesc<'a> {
26 pub title: &'a str,
27 pub icon: u32,
29 pub text: &'a str,
31 pub one_line: bool,
33 pub badge: Option<(u32, &'a str)>,
35 pub aside: &'a str,
37 pub picture_h: i32,
39 pub draw: Option<DrawRef>,
41 pub draw_ver: u32,
42}
43
44impl Ui {
45 #[cfg_attr(jfgui_dev, track_caller)]
48 pub fn action_card(&mut self, id: u32, name: &str, d: &CardDesc) -> u32 {
49 let _src = self.src_scope();
50 let flags = self.take_flags();
51 let disabled = flags & ST_DISABLED != 0;
52 if self.wp.lay.next_w == 0 {
53 self.next_size(200, 0);
54 }
55 self.box_begin(0, BOX_V, 0);
56 let r = self.box_rect();
57 self.wp.ctx.last_rect = r;
58 let ev = self.behave(id, HB_FOCUS | CUR_HAND << HB_CURSOR_SHIFT | if disabled { HB_DISABLED } else { 0 });
59 let state = self.wp.ctx.last_state;
60 let hot = !disabled && ev & EV_HOVER != 0;
61 let held = hot && ev & EV_HELD != 0;
62 let checked = flags & ST_CHECKED != 0;
63 let subtle = flags & ST_SUBTLE != 0;
64 let radius = self.dp(if subtle { 4 } else { 6 });
66 let line = self.dp(1);
67 let (fill, stroke) = if subtle {
68 let f = self.color(if held { T_SUBTLE_PRESSED } else if hot || checked { T_SUBTLE_HOVER } else { T_SUBTLE });
69 (f, f)
70 } else {
71 let f = self.color(if held { T_CTRL_PRESSED } else if hot || checked { T_CTRL_HOVER } else { T_CARD });
72 (f, self.color(if hot || checked { T_ACCENT } else { T_CARD_STROKE }))
73 };
74 if r[2] > 0 && r[3] > 0 {
75 self.cmd_rrect(r[0], r[1], r[2], r[3], radius, line, fill, stroke, stroke);
76 }
77 if d.picture_h > 0 {
79 let area = self.place(r[2].max(1), self.dp(d.picture_h));
80 if area[2] > 2 * line && area[3] > line {
81 let (x, y, w, h) = (area[0] + line, area[1] + line, area[2] - 2 * line, area[3] - line);
82 let top = self.color(T_LAYER);
83 self.cmd_rrect(x, y, w, h, radius, 0, top, top, top);
84 if let Some(dr) = &d.draw {
85 self.cmd_custom(x, y, w, h, Some(dr.clone()), 0, d.draw_ver);
86 } else if d.icon != 0 {
87 let s = self.dp(32).min(h);
88 let c = self.color(if disabled { T_TEXT_DISABLED } else { T_TEXT3 });
89 self.draw_color(c);
90 self.draw_icon(d.icon, x + (w - s) / 2, y + (h - s) / 2, s << 8);
91 }
92 }
93 }
94 let dis = if disabled { TX_DISABLED } else { 0 };
96 let how = if d.one_line { TX_ELLIPSIS } else { TX_WRAP };
97 self.next_size(-1, 0);
98 self.box_begin(0, BOX_H | pad(PAD), GAP);
99 if d.icon != 0 && d.picture_h == 0 {
100 let s = self.dp(20);
101 let at = self.place(s, s);
102 let c = self.color(if disabled { T_TEXT_DISABLED } else { T_TEXT2 });
103 self.draw_color(c);
104 self.draw_icon(d.icon, at[0], at[1], s << 8);
105 }
106 self.next_size(-1, 0);
107 self.box_begin(0, BOX_V, 2);
108 self.next_size(-1, 0);
109 self.box_begin(0, BOX_H, GAP);
110 self.next_size(-1, 0);
111 self.text(TS_BODY_STRONG | how | dis, d.title);
112 if let Some((kind, label)) = d.badge {
113 self.badge(kind, label);
114 }
115 self.box_end();
116 if !d.text.is_empty() {
117 self.next_size(-1, 0);
118 self.text(TS_CAPTION | TX_SECONDARY | how | dis, d.text);
119 }
120 self.box_end();
121 if !d.aside.is_empty() {
122 self.text(TS_CAPTION | TX_SECONDARY | dis, d.aside);
123 }
124 self.box_end();
125 self.box_end();
126 self.wp.ctx.last_rect = r;
128 self.wp.ctx.last_state = state | if checked { SS_CHECKED as u32 } else { 0 };
129 self.focus_ring_draw(ev);
130 self.sem(K_BUTTON, id, Some(name), 0);
131 ev
132 }
133}