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    fn variant_appearance(
40        theme: &crate::Theme,
41        style: &crate::theme::SegmentedButton,
42    ) -> super::Appearance {
43        theme.vertical(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 spacing = f32::from(self.spacing);
53
54        Box::new(
55            self.model
56                .order
57                .iter()
58                .copied()
59                .enumerate()
60                .flat_map(move |(nth, key)| {
61                    let mut divider = None;
62                    if self.model.divider_above(key).unwrap_or(false) && nth > 0 {
63                        let mut divider_bounds = bounds;
64                        divider_bounds.height = 1.0;
65                        divider_bounds.x += f32::from(self.button_padding[0]);
66                        divider_bounds.width -= f32::from(self.button_padding[0]);
67                        divider_bounds.width -= f32::from(self.button_padding[2]);
68                        divider = Some(ItemBounds::Divider(divider_bounds, false));
69
70                        bounds.y += divider_bounds.height + spacing;
71                    }
72
73                    let mut layout_bounds = bounds;
74
75                    let layout_size = state.internal_layout[nth].0;
76
77                    layout_bounds.height = layout_size.height;
78
79                    bounds.y += layout_bounds.height + spacing;
80
81                    std::iter::once(ItemBounds::Button(key, layout_bounds)).chain(divider)
82                }),
83        )
84    }
85
86    #[allow(clippy::cast_precision_loss)]
87    #[allow(clippy::cast_possible_truncation)]
88    #[allow(clippy::cast_sign_loss)]
89    fn variant_layout(
90        &self,
91        state: &mut LocalState,
92        renderer: &crate::Renderer,
93        limits: &layout::Limits,
94    ) -> Size {
95        state.internal_layout.clear();
96        state.buttons_visible = self.model.order.len();
97        let limits = limits.width(self.width);
98
99        let (width, item_height) = self.max_button_dimensions(state, renderer);
100
101        for (size, actual) in &mut state.internal_layout {
102            size.width = width;
103            actual.width = item_height;
104        }
105
106        let spacing = f32::from(self.spacing);
107        let mut height = 0.0;
108        for (nth, key) in self.model.order.iter().copied().enumerate() {
109            if nth > 0 {
110                height += spacing;
111                if self.model.divider_above(key).unwrap_or(false) {
112                    height += 1.0 + spacing;
113                }
114            }
115            height += item_height;
116        }
117
118        limits.height(Length::Fixed(height)).resolve(
119            self.width,
120            self.height,
121            Size::new(width, height),
122        )
123    }
124}