cosmic/widget/segmented_button/
vertical.rs

1// Copyright 2022 System76 <info@system76.com>
2// SPDX-License-Identifier: MPL-2.0
3
4//! Implementation details for the vertical layout of a segmented button.
5
6use 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;
12
13/// A type marker defining the vertical variant of a [`SegmentedButton`].
14pub struct Vertical;
15
16/// Vertical [`SegmentedButton`].
17pub type VerticalSegmentedButton<'a, SelectionMode, Message> =
18    SegmentedButton<'a, Vertical, SelectionMode, Message>;
19
20/// Vertical implementation of the [`SegmentedButton`].
21///
22/// For details on the model, see the [`segmented_button`](super) module for more details.
23pub fn vertical<SelectionMode, Message>(
24    model: &Model<SelectionMode>,
25) -> SegmentedButton<Vertical, SelectionMode, Message>
26where
27    Model<SelectionMode>: Selectable,
28    SelectionMode: Default,
29{
30    SegmentedButton::new(model)
31}
32
33impl<SelectionMode, Message> SegmentedVariant
34    for SegmentedButton<'_, Vertical, SelectionMode, Message>
35where
36    Model<SelectionMode>: Selectable,
37    SelectionMode: Default,
38{
39    const VERTICAL: bool = true;
40
41    fn variant_appearance(
42        theme: &crate::Theme,
43        style: &crate::theme::SegmentedButton,
44    ) -> super::Appearance {
45        theme.vertical(style)
46    }
47
48    #[allow(clippy::cast_precision_loss)]
49    fn variant_bounds<'b>(
50        &'b self,
51        state: &'b LocalState,
52        mut bounds: Rectangle,
53    ) -> Box<dyn Iterator<Item = ItemBounds> + 'b> {
54        let spacing = f32::from(self.spacing);
55
56        Box::new(
57            self.model
58                .order
59                .iter()
60                .copied()
61                .enumerate()
62                .flat_map(move |(nth, key)| {
63                    let mut divider = None;
64                    if self.model.divider_above(key).unwrap_or(false) && nth > 0 {
65                        let mut divider_bounds = bounds;
66                        divider_bounds.height = 1.0;
67                        divider_bounds.x += f32::from(self.button_padding[0]);
68                        divider_bounds.width -= f32::from(self.button_padding[0]);
69                        divider_bounds.width -= f32::from(self.button_padding[2]);
70                        divider = Some(ItemBounds::Divider(divider_bounds, false));
71
72                        bounds.y += divider_bounds.height + spacing;
73                    }
74
75                    let mut layout_bounds = bounds;
76
77                    let layout_size = state.internal_layout[nth].0;
78
79                    layout_bounds.height = layout_size.height;
80
81                    bounds.y += layout_bounds.height + spacing;
82
83                    std::iter::once(ItemBounds::Button(key, layout_bounds)).chain(divider)
84                }),
85        )
86    }
87
88    #[allow(clippy::cast_precision_loss)]
89    #[allow(clippy::cast_possible_truncation)]
90    #[allow(clippy::cast_sign_loss)]
91    fn variant_layout(
92        &self,
93        state: &mut LocalState,
94        renderer: &crate::Renderer,
95        limits: &layout::Limits,
96    ) -> Size {
97        state.internal_layout.clear();
98        state.buttons_visible = self.model.order.len();
99        let limits = limits.width(self.width);
100
101        let (width, item_height) = self.max_button_dimensions(state, renderer);
102
103        for (size, actual) in &mut state.internal_layout {
104            size.width = width;
105            actual.width = item_height;
106        }
107
108        let spacing = f32::from(self.spacing);
109        let mut height = 0.0;
110        for (nth, key) in self.model.order.iter().copied().enumerate() {
111            if nth > 0 {
112                height += spacing;
113                if self.model.divider_above(key).unwrap_or(false) {
114                    height += 1.0 + spacing;
115                }
116            }
117            height += item_height;
118        }
119
120        limits.height(Length::Fixed(height)).resolve(
121            self.width,
122            self.height,
123            Size::new(width, height),
124        )
125    }
126}