aetna_core/widgets/
command.rs1use std::panic::Location;
8
9use crate::cursor::Cursor;
10use crate::metrics::MetricsRole;
11use crate::style::StyleProfile;
12use crate::tokens;
13use crate::tree::*;
14use crate::widgets::text::{mono, text};
15use crate::{IntoIconSource, icon};
16
17#[track_caller]
18pub fn command_group<I, E>(children: I) -> El
19where
20 I: IntoIterator<Item = E>,
21 E: Into<El>,
22{
23 El::new(Kind::Custom("command_group"))
24 .at_loc(Location::caller())
25 .children(children)
26 .axis(Axis::Column)
27 .align(Align::Stretch)
28 .width(Size::Fill(1.0))
29 .height(Size::Hug)
30 .default_gap(0.0)
31}
32
33#[track_caller]
34pub fn command_item<I, E>(children: I) -> El
35where
36 I: IntoIterator<Item = E>,
37 E: Into<El>,
38{
39 El::new(Kind::Custom("command_item"))
40 .at_loc(Location::caller())
41 .style_profile(StyleProfile::Solid)
42 .metrics_role(MetricsRole::MenuItem)
43 .surface_role(SurfaceRole::Raised)
44 .focusable()
45 .cursor(Cursor::Pointer)
46 .children(children)
47 .axis(Axis::Row)
48 .align(Align::Center)
49 .justify(Justify::Start)
50 .fill(tokens::CARD)
51 .default_radius(tokens::RADIUS_SM)
52 .default_padding(Sides::xy(tokens::SPACE_2, 0.0))
53 .default_gap(tokens::SPACE_2)
54 .width(Size::Fill(1.0))
55 .default_height(Size::Fixed(tokens::CONTROL_HEIGHT))
56}
57
58#[track_caller]
59pub fn command_icon(source: impl IntoIconSource) -> El {
60 El::new(Kind::Custom("command_icon"))
61 .at_loc(Location::caller())
62 .style_profile(StyleProfile::Surface)
63 .child(
64 icon(source)
65 .icon_size(tokens::ICON_XS)
66 .color(tokens::FOREGROUND),
67 )
68 .align(Align::Center)
69 .justify(Justify::Center)
70 .fill(tokens::MUTED)
71 .stroke(tokens::BORDER)
72 .default_radius(tokens::RADIUS_SM)
73 .width(Size::Fixed(24.0))
74 .height(Size::Fixed(24.0))
75}
76
77#[track_caller]
78pub fn command_label(label: impl Into<String>) -> El {
79 text(label)
80 .at_loc(Location::caller())
81 .label()
82 .font_weight(FontWeight::Regular)
83 .ellipsis()
84 .width(Size::Fill(1.0))
85}
86
87#[track_caller]
88pub fn command_shortcut(shortcut: impl Into<String>) -> El {
89 mono(shortcut)
90 .at_loc(Location::caller())
91 .caption()
92 .color(tokens::MUTED_FOREGROUND)
93 .width(Size::Hug)
94}
95
96#[track_caller]
97pub fn command_row(
98 source: impl IntoIconSource,
99 label: impl Into<String>,
100 shortcut: impl Into<String>,
101) -> El {
102 command_item([
103 command_icon(source),
104 command_label(label),
105 command_shortcut(shortcut),
106 ])
107 .at_loc(Location::caller())
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn command_item_uses_menu_density_and_centered_row() {
116 let item = command_item([command_label("New branch"), command_shortcut("Ctrl+B")]);
117
118 assert_eq!(item.kind, Kind::Custom("command_item"));
119 assert_eq!(item.metrics_role, Some(MetricsRole::MenuItem));
120 assert_eq!(item.axis, Axis::Row);
121 assert_eq!(item.align, Align::Center);
122 assert_eq!(item.gap, tokens::SPACE_2);
123 assert_eq!(item.width, Size::Fill(1.0));
124 assert!(item.focusable);
125 }
126
127 #[test]
128 fn command_row_builds_icon_label_and_shortcut() {
129 let row = command_row("git-branch", "New branch", "Ctrl+B");
130
131 assert_eq!(row.children.len(), 3);
132 assert_eq!(row.children[0].kind, Kind::Custom("command_icon"));
133 assert_eq!(row.children[0].width, Size::Fixed(24.0));
134 assert_eq!(row.children[1].text.as_deref(), Some("New branch"));
135 assert_eq!(row.children[1].width, Size::Fill(1.0));
136 assert_eq!(row.children[2].text.as_deref(), Some("Ctrl+B"));
137 assert_eq!(row.children[2].text_role, TextRole::Caption);
138 }
139}