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