cosmic/widget/segmented_button/
horizontal.rs
1use super::model::{Model, Selectable};
7use super::style::StyleSheet;
8use super::widget::{ItemBounds, LocalState, SegmentedButton, SegmentedVariant};
9
10use iced::{Length, Rectangle, Size};
11use iced_core::layout;
12use iced_core::text::Renderer;
13
14pub type HorizontalSegmentedButton<'a, SelectionMode, Message> =
16 SegmentedButton<'a, Horizontal, SelectionMode, Message>;
17
18pub struct Horizontal;
20
21pub fn horizontal<SelectionMode: Default, Message>(
25 model: &Model<SelectionMode>,
26) -> SegmentedButton<Horizontal, SelectionMode, Message>
27where
28 Model<SelectionMode>: Selectable,
29{
30 SegmentedButton::new(model)
31}
32
33impl<SelectionMode, Message> SegmentedVariant
34 for SegmentedButton<'_, Horizontal, SelectionMode, Message>
35where
36 Model<SelectionMode>: Selectable,
37 SelectionMode: Default,
38{
39 fn variant_appearance(
40 theme: &crate::Theme,
41 style: &crate::theme::SegmentedButton,
42 ) -> super::Appearance {
43 theme.horizontal(style)
44 }
45
46 #[allow(clippy::cast_precision_loss)]
47 fn variant_bounds<'b>(
48 &'b self,
49 state: &'b LocalState,
50 mut bounds: Rectangle,
51 ) -> Box<dyn Iterator<Item = ItemBounds> + 'b> {
52 let num = state.buttons_visible;
53 let spacing = f32::from(self.spacing);
54 let mut homogenous_width = 0.0;
55
56 if Length::Shrink != self.width || state.collapsed {
57 let mut width_offset = 0.0;
58 if state.collapsed {
59 bounds.x += f32::from(self.button_height);
60 width_offset = f32::from(self.button_height) * 2.0;
61 }
62
63 homogenous_width = ((num as f32).mul_add(-spacing, bounds.width - width_offset)
64 + spacing)
65 / num as f32;
66 }
67
68 let segmetned_control = matches!(self.style, crate::theme::SegmentedButton::Control);
69
70 Box::new(
71 self.model
72 .order
73 .iter()
74 .copied()
75 .enumerate()
76 .skip(state.buttons_offset)
77 .take(state.buttons_visible)
78 .flat_map(move |(nth, key)| {
79 let mut layout_bounds = bounds;
80
81 let layout_size = &state.internal_layout[nth].0;
82
83 if !state.collapsed && Length::Shrink == self.width {
84 layout_bounds.width = layout_size.width;
85 } else {
86 layout_bounds.width = homogenous_width;
87 }
88
89 bounds.x += layout_bounds.width + spacing;
90
91 let button_bounds = ItemBounds::Button(key, layout_bounds);
92 let mut divider = None;
93
94 if self.dividers && segmetned_control && nth + 1 < num {
95 divider = Some(ItemBounds::Divider(
96 Rectangle {
97 width: 1.0,
98 ..bounds
99 },
100 true,
101 ));
102
103 bounds.x += 1.0;
104 }
105
106 std::iter::once(button_bounds).chain(divider)
107 }),
108 )
109 }
110
111 #[allow(clippy::cast_precision_loss)]
112 #[allow(clippy::cast_possible_truncation)]
113 #[allow(clippy::cast_sign_loss)]
114 fn variant_layout(
115 &self,
116 state: &mut LocalState,
117 renderer: &crate::Renderer,
118 limits: &layout::Limits,
119 ) -> Size {
120 state.internal_layout.clear();
121 let num = self.model.order.len();
122 let spacing = f32::from(self.spacing);
123 let size;
124
125 let mut reduce_button_offset = false;
126 if state.known_length != num {
127 if state.known_length > num {
128 state.buttons_offset -= state.buttons_offset.min(state.known_length - num);
129 } else {
130 reduce_button_offset = true;
131 }
132
133 state.known_length = num;
134 }
135
136 if let Length::Shrink = self.width {
137 let max_height = self.max_button_dimensions(state, renderer).1;
139
140 let max_size = limits.height(Length::Fixed(max_height)).resolve(
142 Length::Fill,
143 max_height,
144 Size::new(f32::MAX, max_height),
145 );
146
147 let mut visible_width = 0.0;
148 state.buttons_visible = 0;
149
150 for (button_size, _actual_size) in &state.internal_layout {
151 visible_width += button_size.width;
152
153 if max_size.width >= visible_width {
154 state.buttons_visible += 1;
155 } else {
156 visible_width = max_size.width - max_height;
157 break;
158 }
159
160 visible_width += spacing;
161 }
162
163 visible_width -= spacing;
164
165 state.collapsed = num > 1 && state.buttons_visible != num;
166
167 size = limits
168 .height(Length::Fixed(max_height))
169 .min_width(visible_width)
170 .min();
171 } else {
172 state.buttons_visible = self.model.items.len();
174
175 let mut width = 0.0f32;
176 let font = renderer.default_font();
177
178 for key in self.model.order.iter().copied() {
179 let (button_width, button_height) = self.button_dimensions(state, font, key);
180
181 state.internal_layout.push((
182 Size::new(button_width, button_height),
183 Size::new(
184 button_width
185 - f32::from(self.button_padding[0])
186 - f32::from(self.button_padding[2]),
187 button_height,
188 ),
189 ));
190
191 width = width.max(button_width);
192 }
193
194 let height = f32::from(self.button_height);
195
196 size = limits.height(Length::Fixed(height)).max();
197
198 let actual_width = size.width as usize;
199 let minimum_width = state.buttons_visible * self.minimum_button_width as usize;
200 state.collapsed = actual_width < minimum_width;
201
202 if state.collapsed {
203 state.buttons_visible =
204 (actual_width / self.minimum_button_width as usize).min(state.buttons_visible);
205 }
206 }
207
208 if !state.collapsed {
209 state.buttons_offset = 0;
210 } else if reduce_button_offset {
211 state.buttons_offset = num - state.buttons_visible;
212 }
213
214 size
215 }
216}