1use std::fmt::{self, Display, Formatter};
2
3use gpui::{App, BoxShadow, Hsla, hsla, point, px};
4use theme::{ActiveTheme, Appearance};
5
6/// Today, elevation is primarily used to add shadows to elements, and set the correct background for elements like buttons.
7///
8/// Elevation can be thought of as the physical closeness of an element to the
9/// user. Elements with lower elevations are physically further away on the
10/// z-axis and appear to be underneath elements with higher elevations.
11///
12/// In the future, a more complete approach to elevation may be added.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum ElevationIndex {
15 /// On the layer of the app background. This is under panels, panes, and
16 /// other surfaces.
17 Background,
18 /// The primary surface – Contains panels, panes, containers, etc.
19 Surface,
20 /// The same elevation as the primary surface, but used for the editable areas, like buffers
21 EditorSurface,
22 /// A surface that is elevated above the primary surface. but below washes, models, and dragged elements.
23 ElevatedSurface,
24 /// A surface above the [ElevationIndex::ElevatedSurface] that is used for dialogs, alerts, modals, etc.
25 ModalSurface,
26}
27
28impl Display for ElevationIndex {
29 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
30 match self {
31 ElevationIndex::Background => write!(f, "Background"),
32 ElevationIndex::Surface => write!(f, "Surface"),
33 ElevationIndex::EditorSurface => write!(f, "Editor Surface"),
34 ElevationIndex::ElevatedSurface => write!(f, "Elevated Surface"),
35 ElevationIndex::ModalSurface => write!(f, "Modal Surface"),
36 }
37 }
38}
39
40impl ElevationIndex {
41 /// Returns an appropriate shadow for the given elevation index.
42 pub fn shadow(self, cx: &App) -> Vec<BoxShadow> {
43 let is_light = cx.theme().appearance() == Appearance::Light;
44
45 match self {
46 ElevationIndex::Surface => vec![],
47 ElevationIndex::EditorSurface => vec![],
48
49 ElevationIndex::ElevatedSurface => vec![
50 BoxShadow {
51 color: hsla(0., 0., 0., 0.12),
52 offset: point(px(0.), px(2.)),
53 blur_radius: px(3.),
54 spread_radius: px(0.),
55 },
56 BoxShadow {
57 color: hsla(0., 0., 0., if is_light { 0.03 } else { 0.06 }),
58 offset: point(px(1.), px(1.)),
59 blur_radius: px(0.),
60 spread_radius: px(0.),
61 },
62 ],
63
64 ElevationIndex::ModalSurface => vec![
65 BoxShadow {
66 color: hsla(0., 0., 0., if is_light { 0.06 } else { 0.12 }),
67 offset: point(px(0.), px(2.)),
68 blur_radius: px(3.),
69 spread_radius: px(0.),
70 },
71 BoxShadow {
72 color: hsla(0., 0., 0., if is_light { 0.06 } else { 0.08 }),
73 offset: point(px(0.), px(3.)),
74 blur_radius: px(6.),
75 spread_radius: px(0.),
76 },
77 BoxShadow {
78 color: hsla(0., 0., 0., 0.04),
79 offset: point(px(0.), px(6.)),
80 blur_radius: px(12.),
81 spread_radius: px(0.),
82 },
83 BoxShadow {
84 color: hsla(0., 0., 0., if is_light { 0.04 } else { 0.12 }),
85 offset: point(px(1.), px(1.)),
86 blur_radius: px(0.),
87 spread_radius: px(0.),
88 },
89 ],
90
91 _ => vec![],
92 }
93 }
94
95 /// Returns the background color for the given elevation index.
96 pub fn bg(&self, cx: &mut App) -> Hsla {
97 match self {
98 ElevationIndex::Background => cx.theme().colors().background,
99 ElevationIndex::Surface => cx.theme().colors().surface_background,
100 ElevationIndex::EditorSurface => cx.theme().colors().editor_background,
101 ElevationIndex::ElevatedSurface => cx.theme().colors().elevated_surface_background,
102 ElevationIndex::ModalSurface => cx.theme().colors().elevated_surface_background,
103 }
104 }
105
106 /// Returns a color that is appropriate a filled element on this elevation
107 pub fn on_elevation_bg(&self, cx: &App) -> Hsla {
108 match self {
109 ElevationIndex::Background => cx.theme().colors().surface_background,
110 ElevationIndex::Surface => cx.theme().colors().background,
111 ElevationIndex::EditorSurface => cx.theme().colors().surface_background,
112 ElevationIndex::ElevatedSurface => cx.theme().colors().background,
113 ElevationIndex::ModalSurface => cx.theme().colors().background,
114 }
115 }
116
117 /// Attempts to return a darker background color than the current elevation index's background.
118 ///
119 /// If the current background color is already dark, it will return a lighter color instead.
120 pub fn darker_bg(&self, cx: &App) -> Hsla {
121 match self {
122 ElevationIndex::Background => cx.theme().colors().surface_background,
123 ElevationIndex::Surface => cx.theme().colors().editor_background,
124 ElevationIndex::EditorSurface => cx.theme().colors().surface_background,
125 ElevationIndex::ElevatedSurface => cx.theme().colors().editor_background,
126 ElevationIndex::ModalSurface => cx.theme().colors().editor_background,
127 }
128 }
129}