players.rs

  1use gpui::Hsla;
  2
  3#[derive(Debug, Clone, Copy)]
  4pub struct PlayerColor {
  5    pub cursor: Hsla,
  6    pub background: Hsla,
  7    pub selection: Hsla,
  8}
  9
 10/// A collection of colors that are used to color players in the editor.
 11///
 12/// The first color is always the local player's color, usually a blue.
 13///
 14/// The rest of the default colors crisscross back and forth on the
 15/// color wheel so that the colors are as distinct as possible.
 16#[derive(Clone)]
 17pub struct PlayerColors(pub Vec<PlayerColor>);
 18
 19impl Default for PlayerColors {
 20    /// Don't use this!
 21    /// We have to have a default to be `[refineable::Refinable]`.
 22    /// todo!("Find a way to not need this for Refinable")
 23    fn default() -> Self {
 24        Self::dark()
 25    }
 26}
 27
 28impl PlayerColors {
 29    pub fn dark() -> Self {
 30        Self(vec![
 31            PlayerColor {
 32                cursor: blue().dark().step_9(),
 33                background: blue().dark().step_5(),
 34                selection: blue().dark().step_3(),
 35            },
 36            PlayerColor {
 37                cursor: orange().dark().step_9(),
 38                background: orange().dark().step_5(),
 39                selection: orange().dark().step_3(),
 40            },
 41            PlayerColor {
 42                cursor: pink().dark().step_9(),
 43                background: pink().dark().step_5(),
 44                selection: pink().dark().step_3(),
 45            },
 46            PlayerColor {
 47                cursor: lime().dark().step_9(),
 48                background: lime().dark().step_5(),
 49                selection: lime().dark().step_3(),
 50            },
 51            PlayerColor {
 52                cursor: purple().dark().step_9(),
 53                background: purple().dark().step_5(),
 54                selection: purple().dark().step_3(),
 55            },
 56            PlayerColor {
 57                cursor: amber().dark().step_9(),
 58                background: amber().dark().step_5(),
 59                selection: amber().dark().step_3(),
 60            },
 61            PlayerColor {
 62                cursor: jade().dark().step_9(),
 63                background: jade().dark().step_5(),
 64                selection: jade().dark().step_3(),
 65            },
 66            PlayerColor {
 67                cursor: red().dark().step_9(),
 68                background: red().dark().step_5(),
 69                selection: red().dark().step_3(),
 70            },
 71        ])
 72    }
 73
 74    pub fn light() -> Self {
 75        Self(vec![
 76            PlayerColor {
 77                cursor: blue().light().step_9(),
 78                background: blue().light().step_4(),
 79                selection: blue().light().step_3(),
 80            },
 81            PlayerColor {
 82                cursor: orange().light().step_9(),
 83                background: orange().light().step_4(),
 84                selection: orange().light().step_3(),
 85            },
 86            PlayerColor {
 87                cursor: pink().light().step_9(),
 88                background: pink().light().step_4(),
 89                selection: pink().light().step_3(),
 90            },
 91            PlayerColor {
 92                cursor: lime().light().step_9(),
 93                background: lime().light().step_4(),
 94                selection: lime().light().step_3(),
 95            },
 96            PlayerColor {
 97                cursor: purple().light().step_9(),
 98                background: purple().light().step_4(),
 99                selection: purple().light().step_3(),
100            },
101            PlayerColor {
102                cursor: amber().light().step_9(),
103                background: amber().light().step_4(),
104                selection: amber().light().step_3(),
105            },
106            PlayerColor {
107                cursor: jade().light().step_9(),
108                background: jade().light().step_4(),
109                selection: jade().light().step_3(),
110            },
111            PlayerColor {
112                cursor: red().light().step_9(),
113                background: red().light().step_4(),
114                selection: red().light().step_3(),
115            },
116        ])
117    }
118}
119
120impl PlayerColors {
121    pub fn local(&self) -> PlayerColor {
122        // todo!("use a valid color");
123        *self.0.first().unwrap()
124    }
125
126    pub fn absent(&self) -> PlayerColor {
127        // todo!("use a valid color");
128        *self.0.last().unwrap()
129    }
130
131    pub fn color_for_participant(&self, participant_index: u32) -> PlayerColor {
132        let len = self.0.len() - 1;
133        self.0[(participant_index as usize % len) + 1]
134    }
135}
136
137#[cfg(feature = "stories")]
138pub use stories::*;
139
140use crate::{amber, blue, jade, lime, orange, pink, purple, red};
141
142#[cfg(feature = "stories")]
143mod stories {
144    use super::*;
145    use crate::{ActiveTheme, Story};
146    use gpui::{div, img, px, Div, ParentComponent, Render, Styled, ViewContext};
147
148    pub struct PlayerStory;
149
150    impl Render for PlayerStory {
151        type Element = Div<Self>;
152
153        fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
154            Story::container(cx).child(
155                div()
156                    .flex()
157                    .flex_col()
158                    .gap_4()
159                    .child(Story::title_for::<_, PlayerColors>(cx))
160                    .child(Story::label(cx, "Player Colors"))
161                    .child(
162                        div()
163                            .flex()
164                            .flex_col()
165                            .gap_1()
166                            .child(
167                                div().flex().gap_1().children(
168                                    cx.theme().players().0.clone().iter_mut().map(|player| {
169                                        div().w_8().h_8().rounded_md().bg(player.cursor)
170                                    }),
171                                ),
172                            )
173                            .child(div().flex().gap_1().children(
174                                cx.theme().players().0.clone().iter_mut().map(|player| {
175                                    div().w_8().h_8().rounded_md().bg(player.background)
176                                }),
177                            ))
178                            .child(div().flex().gap_1().children(
179                                cx.theme().players().0.clone().iter_mut().map(|player| {
180                                    div().w_8().h_8().rounded_md().bg(player.selection)
181                                }),
182                            )),
183                    )
184                    .child(Story::label(cx, "Avatar Rings"))
185                    .child(div().flex().gap_1().children(
186                        cx.theme().players().0.clone().iter_mut().map(|player| {
187                            div()
188                                .my_1()
189                                .rounded_full()
190                                .border_2()
191                                .border_color(player.cursor)
192                                .child(
193                                    img()
194                                        .rounded_full()
195                                        .uri("https://avatars.githubusercontent.com/u/1714999?v=4")
196                                        .size_6()
197                                        .bg(gpui::red()),
198                                )
199                        }),
200                    ))
201                    .child(Story::label(cx, "Player Backgrounds"))
202                    .child(div().flex().gap_1().children(
203                        cx.theme().players().0.clone().iter_mut().map(|player| {
204                            div()
205                                .my_1()
206                                .rounded_xl()
207                                .flex()
208                                .items_center()
209                                .h_8()
210                                .py_0p5()
211                                .px_1p5()
212                                .bg(player.background)
213                                .child(
214                                div().relative().neg_mx_1().rounded_full().z_index(3)
215                                    .border_2()
216                                    .border_color(player.background)
217                                    .size(px(28.))
218                                    .child(
219                                    img()
220                                        .rounded_full()
221                                        .uri("https://avatars.githubusercontent.com/u/1714999?v=4")
222                                        .size(px(24.))
223                                        .bg(gpui::red()),
224                                ),
225                            ).child(
226                            div().relative().neg_mx_1().rounded_full().z_index(2)
227                                .border_2()
228                                .border_color(player.background)
229                                .size(px(28.))
230                                .child(
231                                img()
232                                    .rounded_full()
233                                    .uri("https://avatars.githubusercontent.com/u/1714999?v=4")
234                                    .size(px(24.))
235                                    .bg(gpui::red()),
236                            ),
237                        ).child(
238                        div().relative().neg_mx_1().rounded_full().z_index(1)
239                            .border_2()
240                            .border_color(player.background)
241                            .size(px(28.))
242                            .child(
243                            img()
244                                .rounded_full()
245                                .uri("https://avatars.githubusercontent.com/u/1714999?v=4")
246                                .size(px(24.))
247                                .bg(gpui::red()),
248                        ),
249                    )
250                        }),
251                    ))
252                    .child(Story::label(cx, "Player Selections"))
253                    .child(div().flex().flex_col().gap_px().children(
254                        cx.theme().players().0.clone().iter_mut().map(|player| {
255                            div()
256                                .flex()
257                                .child(
258                                    div()
259                                        .flex()
260                                        .flex_none()
261                                        .rounded_sm()
262                                        .px_0p5()
263                                        .text_color(cx.theme().colors().text)
264                                        .bg(player.selection)
265                                        .child("The brown fox jumped over the lazy dog."),
266                                )
267                                .child(div().flex_1())
268                        }),
269                    )),
270            )
271        }
272    }
273}