1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0

use slotmap::{SecondaryMap, SparseSecondaryMap};

use super::{Entity, Model, Selectable};
use crate::widget::icon::Icon;
use std::borrow::Cow;

/// A builder for a [`Model`].
#[derive(Default)]
pub struct ModelBuilder<SelectionMode: Default>(Model<SelectionMode>);

/// Constructs a new item for the [`ModelBuilder`].
pub struct BuilderEntity<SelectionMode: Default> {
    model: ModelBuilder<SelectionMode>,
    id: Entity,
}

impl<SelectionMode: Default> ModelBuilder<SelectionMode>
where
    Model<SelectionMode>: Selectable,
{
    /// Inserts a new item and its associated data into the model.
    #[must_use]
    pub fn insert(
        mut self,
        builder: impl Fn(BuilderEntity<SelectionMode>) -> BuilderEntity<SelectionMode>,
    ) -> Self {
        let id = self.0.insert().id();
        builder(BuilderEntity { model: self, id }).model
    }

    /// Consumes the builder and returns the model.
    pub fn build(self) -> Model<SelectionMode> {
        self.0
    }
}

impl<SelectionMode: Default> BuilderEntity<SelectionMode>
where
    Model<SelectionMode>: Selectable,
{
    /// Activates the newly-inserted item.
    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
    pub fn activate(mut self) -> Self {
        self.model.0.activate(self.id);
        self
    }

    /// Defines that the close button should appear
    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
    pub fn closable(mut self) -> Self {
        self.model.0.closable_set(self.id, true);
        self
    }

    /// Associates extra data with an external secondary map.
    ///
    /// The secondary map internally uses a `Vec`, so should only be used for data that
    /// is commonly associated.
    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
    pub fn secondary<Data>(self, map: &mut SecondaryMap<Entity, Data>, data: Data) -> Self {
        map.insert(self.id, data);
        self
    }

    /// Associates extra data with an external sparse secondary map.
    ///
    /// Sparse maps internally use a `HashMap`, for data that is sparsely associated.
    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
    pub fn secondary_sparse<Data>(
        self,
        map: &mut SparseSecondaryMap<Entity, Data>,
        data: Data,
    ) -> Self {
        map.insert(self.id, data);
        self
    }

    /// Assigns extra data to the item.
    ///
    /// There can only be one data component per Rust type.
    ///
    /// ```ignore
    /// enum ViewItem { A }
    ///
    /// segmented_button::Model::builder()
    ///     .insert(|b| b.text("Item A").data(ViewItem::A))
    ///     .build()
    /// ```
    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
    pub fn data<Data: 'static>(mut self, data: Data) -> Self {
        self.model.0.data_set(self.id, data);
        self
    }

    pub fn divider_above(mut self) -> Self {
        self.model.0.divider_above_set(self.id, true);
        self
    }

    /// Defines an icon for the item.
    ///
    /// ```ignore
    /// segmented_button::Model::builder()
    ///     .insert(|b| b.text("Item A").icon("custom-icon"))
    ///     .build()
    /// ```
    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
    pub fn icon(mut self, icon: impl Into<Icon>) -> Self {
        self.model.0.icon_set(self.id, icon.into());
        self
    }

    /// Define the position of the newly-inserted item.
    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
    pub fn position(mut self, position: u16) -> Self {
        self.model.0.position_set(self.id, position);
        self
    }

    /// Swap the position with another item in the model.
    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
    pub fn position_swap(mut self, other: Entity) -> Self {
        self.model.0.position_swap(self.id, other);
        self
    }

    /// Defines the text for the item.
    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
    pub fn text(mut self, text: impl Into<Cow<'static, str>>) -> Self {
        self.model.0.text_set(self.id, text);
        self
    }

    /// Calls a function with the ID
    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
    pub fn with_id(self, func: impl FnOnce(Entity)) -> Self {
        func(self.id);
        self
    }
}