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