prelude.rs

  1pub use gpui2::{
  2    div, Element, ElementId, IntoAnyElement, ParentElement, SharedString, StatefulInteractive,
  3    StatelessInteractive, Styled, ViewContext, WindowContext,
  4};
  5
  6use crate::settings::user_settings;
  7pub use crate::{theme, ButtonVariant, ElementExt, Theme};
  8
  9use gpui2::{hsla, rems, rgb, Hsla, Rems};
 10use strum::EnumIter;
 11
 12// TODO Remove uses in favor of ThemeColor
 13#[derive(Default)]
 14pub struct SystemColor {
 15    pub transparent: Hsla,
 16    pub mac_os_traffic_light_red: Hsla,
 17    pub mac_os_traffic_light_yellow: Hsla,
 18    pub mac_os_traffic_light_green: Hsla,
 19    pub state_hover_background: Hsla,
 20    pub state_active_background: Hsla,
 21}
 22
 23impl SystemColor {
 24    pub fn new() -> SystemColor {
 25        SystemColor {
 26            transparent: hsla(0.0, 0.0, 0.0, 0.0),
 27            mac_os_traffic_light_red: rgb::<Hsla>(0xEC695E),
 28            mac_os_traffic_light_yellow: rgb::<Hsla>(0xF4BF4F),
 29            mac_os_traffic_light_green: rgb::<Hsla>(0x62C554),
 30            state_hover_background: hsla(0.0, 0.0, 0.0, 0.08),
 31            state_active_background: hsla(0.0, 0.0, 0.0, 0.16),
 32        }
 33    }
 34    pub fn color(&self) -> Hsla {
 35        self.transparent
 36    }
 37}
 38
 39#[derive(Clone, Copy)]
 40pub struct PlayerThemeColors {
 41    pub cursor: Hsla,
 42    pub selection: Hsla,
 43}
 44
 45impl PlayerThemeColors {
 46    pub fn new(cx: &WindowContext, ix: usize) -> Self {
 47        let theme = theme(cx);
 48
 49        if ix < theme.players.len() {
 50            Self {
 51                cursor: theme.players[ix].cursor,
 52                selection: theme.players[ix].selection,
 53            }
 54        } else {
 55            Self {
 56                cursor: rgb::<Hsla>(0xff00ff),
 57                selection: rgb::<Hsla>(0xff00ff),
 58            }
 59        }
 60    }
 61}
 62
 63#[derive(Clone, Copy)]
 64pub struct SyntaxColor {
 65    pub comment: Hsla,
 66    pub string: Hsla,
 67    pub function: Hsla,
 68    pub keyword: Hsla,
 69}
 70
 71impl SyntaxColor {
 72    pub fn new(cx: &WindowContext) -> Self {
 73        let theme = theme(cx);
 74
 75        Self {
 76            comment: theme
 77                .syntax
 78                .get("comment")
 79                .cloned()
 80                .unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
 81            string: theme
 82                .syntax
 83                .get("string")
 84                .cloned()
 85                .unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
 86            function: theme
 87                .syntax
 88                .get("function")
 89                .cloned()
 90                .unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
 91            keyword: theme
 92                .syntax
 93                .get("keyword")
 94                .cloned()
 95                .unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
 96        }
 97    }
 98}
 99
100#[derive(Clone, Copy)]
101pub struct ThemeColor {
102    pub transparent: Hsla,
103    pub mac_os_traffic_light_red: Hsla,
104    pub mac_os_traffic_light_yellow: Hsla,
105    pub mac_os_traffic_light_green: Hsla,
106    pub border: Hsla,
107    pub border_variant: Hsla,
108    pub border_focused: Hsla,
109    pub border_transparent: Hsla,
110    /// The background color of an elevated surface, like a modal, tooltip or toast.
111    pub elevated_surface: Hsla,
112    pub surface: Hsla,
113    /// Window background color
114    pub background: Hsla,
115    /// Default background for elements like filled buttons,
116    /// text fields, checkboxes, radio buttons, etc.
117    /// - TODO: Map to step 3.
118    pub filled_element: Hsla,
119    /// The background color of a hovered element, like a button being hovered
120    /// with a mouse, or hovered on a touch screen.
121    /// - TODO: Map to step 4.
122    pub filled_element_hover: Hsla,
123    /// The background color of an active element, like a button being pressed,
124    /// or tapped on a touch screen.
125    /// - TODO: Map to step 5.
126    pub filled_element_active: Hsla,
127    /// The background color of a selected element, like a selected tab,
128    /// a button toggled on, or a checkbox that is checked.
129    pub filled_element_selected: Hsla,
130    pub filled_element_disabled: Hsla,
131    pub ghost_element: Hsla,
132    /// The background color of a hovered element with no default background,
133    /// like a ghost-style button or an interactable list item.
134    /// - TODO: Map to step 3.
135    pub ghost_element_hover: Hsla,
136    /// - TODO: Map to step 4.
137    pub ghost_element_active: Hsla,
138    pub ghost_element_selected: Hsla,
139    pub ghost_element_disabled: Hsla,
140    pub text: Hsla,
141    pub text_muted: Hsla,
142    pub text_placeholder: Hsla,
143    pub text_disabled: Hsla,
144    pub text_accent: Hsla,
145    pub icon_muted: Hsla,
146    pub syntax: SyntaxColor,
147
148    pub status_bar: Hsla,
149    pub title_bar: Hsla,
150    pub toolbar: Hsla,
151    pub tab_bar: Hsla,
152    pub editor: Hsla,
153    pub editor_subheader: Hsla,
154    pub editor_active_line: Hsla,
155    pub terminal: Hsla,
156    pub image_fallback_background: Hsla,
157
158    pub git_created: Hsla,
159    pub git_modified: Hsla,
160    pub git_deleted: Hsla,
161    pub git_conflict: Hsla,
162    pub git_ignored: Hsla,
163    pub git_renamed: Hsla,
164
165    pub player: [PlayerThemeColors; 8],
166}
167
168impl ThemeColor {
169    pub fn new(cx: &WindowContext) -> Self {
170        let theme = theme(cx);
171        let system_color = SystemColor::new();
172
173        let players = [
174            PlayerThemeColors::new(cx, 0),
175            PlayerThemeColors::new(cx, 1),
176            PlayerThemeColors::new(cx, 2),
177            PlayerThemeColors::new(cx, 3),
178            PlayerThemeColors::new(cx, 4),
179            PlayerThemeColors::new(cx, 5),
180            PlayerThemeColors::new(cx, 6),
181            PlayerThemeColors::new(cx, 7),
182        ];
183
184        Self {
185            transparent: hsla(0.0, 0.0, 0.0, 0.0),
186            mac_os_traffic_light_red: rgb::<Hsla>(0xEC695E),
187            mac_os_traffic_light_yellow: rgb::<Hsla>(0xF4BF4F),
188            mac_os_traffic_light_green: rgb::<Hsla>(0x62C554),
189            border: theme.lowest.base.default.border,
190            border_variant: theme.lowest.variant.default.border,
191            border_focused: theme.lowest.accent.default.border,
192            border_transparent: system_color.transparent,
193            elevated_surface: theme.lowest.base.default.background,
194            surface: theme.middle.base.default.background,
195            background: theme.lowest.base.default.background,
196            filled_element: theme.lowest.base.default.background,
197            filled_element_hover: theme.lowest.base.hovered.background,
198            filled_element_active: theme.lowest.base.active.background,
199            filled_element_selected: theme.lowest.accent.default.background,
200            filled_element_disabled: system_color.transparent,
201            ghost_element: system_color.transparent,
202            ghost_element_hover: theme.lowest.base.default.background,
203            ghost_element_active: theme.lowest.base.hovered.background,
204            ghost_element_selected: theme.lowest.accent.default.background,
205            ghost_element_disabled: system_color.transparent,
206            text: theme.lowest.base.default.foreground,
207            text_muted: theme.lowest.variant.default.foreground,
208            /// TODO: map this to a real value
209            text_placeholder: theme.lowest.negative.default.foreground,
210            text_disabled: theme.lowest.base.disabled.foreground,
211            text_accent: theme.lowest.accent.default.foreground,
212            icon_muted: theme.lowest.variant.default.foreground,
213            syntax: SyntaxColor::new(cx),
214
215            status_bar: theme.lowest.base.default.background,
216            title_bar: theme.lowest.base.default.background,
217            toolbar: theme.highest.base.default.background,
218            tab_bar: theme.middle.base.default.background,
219            editor: theme.highest.base.default.background,
220            editor_subheader: theme.middle.base.default.background,
221            terminal: theme.highest.base.default.background,
222            editor_active_line: theme.highest.on.default.background,
223            image_fallback_background: theme.lowest.base.default.background,
224
225            git_created: theme.lowest.positive.default.foreground,
226            git_modified: theme.lowest.accent.default.foreground,
227            git_deleted: theme.lowest.negative.default.foreground,
228            git_conflict: theme.lowest.warning.default.foreground,
229            git_ignored: theme.lowest.base.disabled.foreground,
230            git_renamed: theme.lowest.warning.default.foreground,
231
232            player: players,
233        }
234    }
235}
236
237#[derive(Default, PartialEq, EnumIter, Clone, Copy)]
238pub enum HighlightColor {
239    #[default]
240    Default,
241    Comment,
242    String,
243    Function,
244    Keyword,
245}
246
247impl HighlightColor {
248    pub fn hsla(&self, theme: &Theme) -> Hsla {
249        let system_color = SystemColor::new();
250
251        match self {
252            Self::Default => theme
253                .syntax
254                .get("primary")
255                .cloned()
256                .unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
257            Self::Comment => theme
258                .syntax
259                .get("comment")
260                .cloned()
261                .unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
262            Self::String => theme
263                .syntax
264                .get("string")
265                .cloned()
266                .unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
267            Self::Function => theme
268                .syntax
269                .get("function")
270                .cloned()
271                .unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
272            Self::Keyword => theme
273                .syntax
274                .get("keyword")
275                .cloned()
276                .unwrap_or_else(|| rgb::<Hsla>(0xff00ff)),
277        }
278    }
279}
280
281pub fn ui_size(cx: &mut WindowContext, size: f32) -> Rems {
282    const UI_SCALE_RATIO: f32 = 0.875;
283
284    let settings = user_settings(cx);
285
286    rems(*settings.ui_scale * UI_SCALE_RATIO * size)
287}
288
289#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
290pub enum FileSystemStatus {
291    #[default]
292    None,
293    Conflict,
294    Deleted,
295}
296
297impl FileSystemStatus {
298    pub fn to_string(&self) -> String {
299        match self {
300            Self::None => "None".to_string(),
301            Self::Conflict => "Conflict".to_string(),
302            Self::Deleted => "Deleted".to_string(),
303        }
304    }
305}
306
307#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
308pub enum GitStatus {
309    #[default]
310    None,
311    Created,
312    Modified,
313    Deleted,
314    Conflict,
315    Renamed,
316}
317
318impl GitStatus {
319    pub fn to_string(&self) -> String {
320        match self {
321            Self::None => "None".to_string(),
322            Self::Created => "Created".to_string(),
323            Self::Modified => "Modified".to_string(),
324            Self::Deleted => "Deleted".to_string(),
325            Self::Conflict => "Conflict".to_string(),
326            Self::Renamed => "Renamed".to_string(),
327        }
328    }
329
330    pub fn hsla(&self, cx: &WindowContext) -> Hsla {
331        let color = ThemeColor::new(cx);
332        let system_color = SystemColor::new();
333
334        match self {
335            Self::None => system_color.transparent,
336            Self::Created => color.git_created,
337            Self::Modified => color.git_modified,
338            Self::Deleted => color.git_deleted,
339            Self::Conflict => color.git_conflict,
340            Self::Renamed => color.git_renamed,
341        }
342    }
343}
344
345#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
346pub enum DiagnosticStatus {
347    #[default]
348    None,
349    Error,
350    Warning,
351    Info,
352}
353
354#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
355pub enum IconSide {
356    #[default]
357    Left,
358    Right,
359}
360
361#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
362pub enum OrderMethod {
363    #[default]
364    Ascending,
365    Descending,
366    MostRecent,
367}
368
369#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
370pub enum Shape {
371    #[default]
372    Circle,
373    RoundedRectangle,
374}
375
376#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
377pub enum DisclosureControlVisibility {
378    #[default]
379    OnHover,
380    Always,
381}
382
383#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
384pub enum DisclosureControlStyle {
385    /// Shows the disclosure control only when hovered where possible.
386    ///
387    /// More compact, but not available everywhere.
388    ChevronOnHover,
389    /// Shows an icon where possible, otherwise shows a chevron.
390    ///
391    /// For example, in a file tree a folder or file icon is shown
392    /// instead of a chevron
393    Icon,
394    /// Always shows a chevron.
395    Chevron,
396    /// Completely hides the disclosure control where possible.
397    None,
398}
399
400#[derive(Debug, PartialEq, Eq, Clone, Copy, EnumIter)]
401pub enum OverflowStyle {
402    Hidden,
403    Wrap,
404}
405
406#[derive(Default, PartialEq, Copy, Clone, EnumIter, strum::Display)]
407pub enum InteractionState {
408    #[default]
409    Enabled,
410    Hovered,
411    Active,
412    Focused,
413    Disabled,
414}
415
416impl InteractionState {
417    pub fn if_enabled(&self, enabled: bool) -> Self {
418        if enabled {
419            *self
420        } else {
421            InteractionState::Disabled
422        }
423    }
424}
425
426#[derive(Default, PartialEq)]
427pub enum SelectedState {
428    #[default]
429    Unselected,
430    PartiallySelected,
431    Selected,
432}
433
434#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
435pub enum Toggleable {
436    Toggleable(ToggleState),
437    #[default]
438    NotToggleable,
439}
440
441impl Toggleable {
442    pub fn is_toggled(&self) -> bool {
443        match self {
444            Self::Toggleable(ToggleState::Toggled) => true,
445            _ => false,
446        }
447    }
448}
449
450impl From<ToggleState> for Toggleable {
451    fn from(state: ToggleState) -> Self {
452        Self::Toggleable(state)
453    }
454}
455
456#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
457pub enum ToggleState {
458    /// The "on" state of a toggleable element.
459    ///
460    /// Example:
461    ///     - A collasable list that is currently expanded
462    ///     - A toggle button that is currently on.
463    Toggled,
464    /// The "off" state of a toggleable element.
465    ///
466    /// Example:
467    ///     - A collasable list that is currently collapsed
468    ///     - A toggle button that is currently off.
469    #[default]
470    NotToggled,
471}
472
473impl From<Toggleable> for ToggleState {
474    fn from(toggleable: Toggleable) -> Self {
475        match toggleable {
476            Toggleable::Toggleable(state) => state,
477            Toggleable::NotToggleable => ToggleState::NotToggled,
478        }
479    }
480}
481
482impl From<bool> for ToggleState {
483    fn from(toggled: bool) -> Self {
484        if toggled {
485            ToggleState::Toggled
486        } else {
487            ToggleState::NotToggled
488        }
489    }
490}