1#![allow(missing_docs)]
2
3use gpui::Hsla;
4use serde::Deserialize;
5
6use crate::{
7 PlayerColorContent, amber, blue, jade, lime, orange, pink, purple, red, try_parse_color,
8};
9
10#[derive(Debug, Clone, Copy, Deserialize, Default, PartialEq)]
11pub struct PlayerColor {
12 pub cursor: Hsla,
13 pub background: Hsla,
14 pub selection: Hsla,
15}
16
17/// A collection of colors that are used to color players in the editor.
18///
19/// The first color is always the local player's color, usually a blue.
20///
21/// The rest of the default colors crisscross back and forth on the
22/// color wheel so that the colors are as distinct as possible.
23#[derive(Clone, Debug, Deserialize, PartialEq)]
24pub struct PlayerColors(pub Vec<PlayerColor>);
25
26impl Default for PlayerColors {
27 /// Don't use this!
28 /// We have to have a default to be `[refineable::Refinable]`.
29 /// TODO "Find a way to not need this for Refinable"
30 fn default() -> Self {
31 Self::dark()
32 }
33}
34
35impl PlayerColors {
36 pub fn dark() -> Self {
37 Self(vec![
38 PlayerColor {
39 cursor: blue().dark().step_9(),
40 background: blue().dark().step_5(),
41 selection: blue().dark().step_3(),
42 },
43 PlayerColor {
44 cursor: orange().dark().step_9(),
45 background: orange().dark().step_5(),
46 selection: orange().dark().step_3(),
47 },
48 PlayerColor {
49 cursor: pink().dark().step_9(),
50 background: pink().dark().step_5(),
51 selection: pink().dark().step_3(),
52 },
53 PlayerColor {
54 cursor: lime().dark().step_9(),
55 background: lime().dark().step_5(),
56 selection: lime().dark().step_3(),
57 },
58 PlayerColor {
59 cursor: purple().dark().step_9(),
60 background: purple().dark().step_5(),
61 selection: purple().dark().step_3(),
62 },
63 PlayerColor {
64 cursor: amber().dark().step_9(),
65 background: amber().dark().step_5(),
66 selection: amber().dark().step_3(),
67 },
68 PlayerColor {
69 cursor: jade().dark().step_9(),
70 background: jade().dark().step_5(),
71 selection: jade().dark().step_3(),
72 },
73 PlayerColor {
74 cursor: red().dark().step_9(),
75 background: red().dark().step_5(),
76 selection: red().dark().step_3(),
77 },
78 ])
79 }
80
81 pub fn light() -> Self {
82 Self(vec![
83 PlayerColor {
84 cursor: blue().light().step_9(),
85 background: blue().light().step_4(),
86 selection: blue().light().step_3(),
87 },
88 PlayerColor {
89 cursor: orange().light().step_9(),
90 background: orange().light().step_4(),
91 selection: orange().light().step_3(),
92 },
93 PlayerColor {
94 cursor: pink().light().step_9(),
95 background: pink().light().step_4(),
96 selection: pink().light().step_3(),
97 },
98 PlayerColor {
99 cursor: lime().light().step_9(),
100 background: lime().light().step_4(),
101 selection: lime().light().step_3(),
102 },
103 PlayerColor {
104 cursor: purple().light().step_9(),
105 background: purple().light().step_4(),
106 selection: purple().light().step_3(),
107 },
108 PlayerColor {
109 cursor: amber().light().step_9(),
110 background: amber().light().step_4(),
111 selection: amber().light().step_3(),
112 },
113 PlayerColor {
114 cursor: jade().light().step_9(),
115 background: jade().light().step_4(),
116 selection: jade().light().step_3(),
117 },
118 PlayerColor {
119 cursor: red().light().step_9(),
120 background: red().light().step_4(),
121 selection: red().light().step_3(),
122 },
123 ])
124 }
125}
126
127impl PlayerColors {
128 pub fn local(&self) -> PlayerColor {
129 *self.0.first().unwrap()
130 }
131
132 pub fn agent(&self) -> PlayerColor {
133 *self.0.last().unwrap()
134 }
135
136 pub fn absent(&self) -> PlayerColor {
137 *self.0.last().unwrap()
138 }
139
140 pub fn read_only(&self) -> PlayerColor {
141 let local = self.local();
142 PlayerColor {
143 cursor: local.cursor.grayscale(),
144 background: local.background.grayscale(),
145 selection: local.selection.grayscale(),
146 }
147 }
148
149 pub fn color_for_participant(&self, participant_index: u32) -> PlayerColor {
150 let len = self.0.len() - 1;
151 self.0[(participant_index as usize % len) + 1]
152 }
153
154 /// Merges the given player colors into this [`PlayerColors`] instance.
155 pub fn merge(&mut self, user_player_colors: &[PlayerColorContent]) {
156 if user_player_colors.is_empty() {
157 return;
158 }
159
160 for (idx, player) in user_player_colors.iter().enumerate() {
161 let cursor = player
162 .cursor
163 .as_ref()
164 .and_then(|color| try_parse_color(color).ok());
165 let background = player
166 .background
167 .as_ref()
168 .and_then(|color| try_parse_color(color).ok());
169 let selection = player
170 .selection
171 .as_ref()
172 .and_then(|color| try_parse_color(color).ok());
173
174 if let Some(player_color) = self.0.get_mut(idx) {
175 *player_color = PlayerColor {
176 cursor: cursor.unwrap_or(player_color.cursor),
177 background: background.unwrap_or(player_color.background),
178 selection: selection.unwrap_or(player_color.selection),
179 };
180 } else {
181 self.0.push(PlayerColor {
182 cursor: cursor.unwrap_or_default(),
183 background: background.unwrap_or_default(),
184 selection: selection.unwrap_or_default(),
185 });
186 }
187 }
188 }
189}