1use gpui::{App, BoxShadow, Hsla, Pixels};
2use gpui_base::{ColorTokens, TextStyleToken, Theme};
3
4use crate::layout::with_fields;
5
6const DEFAULT_PALETTE: [u32; 10] = [
11 0x5b8ff9, 0x61ddaa, 0x65789b, 0xf6bd16, 0x7262fd, 0x78d3f8, 0x9661bc, 0xf6903d, 0x008685,
12 0xf08bb4,
13];
14
15#[derive(Clone, Debug)]
18#[non_exhaustive]
19pub struct GraphColors {
20 pub background: Hsla,
21 pub surface: Hsla,
23 pub foreground: Hsla,
24 pub muted: Hsla,
25 pub border: Hsla,
26 pub edge: Hsla,
27 pub edge_active: Hsla,
29 pub selection: Hsla,
31 pub focus_ring: Hsla,
33 pub ungrouped: Hsla,
35 pub on_fill_light: Hsla,
37 pub on_fill_dark: Hsla,
38}
39
40impl GraphColors {
41 pub fn from_theme(cx: &App) -> Self {
42 let t = Theme::global(cx).tokens.colors;
43 Self {
44 background: t.background,
45 surface: t.surface,
46 foreground: t.foreground,
47 muted: t.muted_foreground,
48 border: t.border,
49 edge: t.muted_foreground.opacity(0.45),
50 edge_active: t.foreground.opacity(0.8),
51 selection: t.primary,
52 focus_ring: t.ring,
53 ungrouped: t.muted_foreground.opacity(0.6),
54 on_fill_light: ColorTokens::light().background,
55 on_fill_dark: ColorTokens::light().foreground,
56 }
57 }
58
59 pub fn on_fill(&self, fill: Hsla) -> Hsla {
61 if fill.l > 0.62 {
62 self.on_fill_dark
63 } else {
64 self.on_fill_light
65 }
66 }
67}
68
69with_fields!(GraphColors {
70 background: Hsla => with_background,
71 surface: Hsla => with_surface,
72 foreground: Hsla => with_foreground,
73 muted: Hsla => with_muted,
74 border: Hsla => with_border,
75 edge: Hsla => with_edge,
76 edge_active: Hsla => with_edge_active,
77 selection: Hsla => with_selection,
78 focus_ring: Hsla => with_focus_ring,
79 ungrouped: Hsla => with_ungrouped,
80});
81
82#[derive(Clone, Debug)]
84pub(crate) struct GraphMetrics {
85 pub label: TextStyleToken,
87 pub caption: TextStyleToken,
89 pub radius_sm: Pixels,
90 pub radius_md: Pixels,
91 pub space_xs: Pixels,
92 pub space_sm: Pixels,
93 pub space_md: Pixels,
94 pub shadow: Vec<BoxShadow>,
95}
96
97impl GraphMetrics {
98 pub fn from_theme(cx: &App) -> Self {
99 let t = Theme::global(cx).tokens;
100 Self {
101 label: t.typography.sm,
102 caption: t.typography.xs,
103 radius_sm: t.radius.sm,
104 radius_md: t.radius.md,
105 space_xs: t.spacing.xs,
106 space_sm: t.spacing.sm,
107 space_md: t.spacing.md,
108 shadow: t.shadow.md,
109 }
110 }
111}
112
113#[derive(Clone, Debug)]
115pub struct Palette(pub Vec<Hsla>);
116
117impl Palette {
118 pub fn get(&self, ix: usize) -> Hsla {
120 match self.0.len() {
121 0 => ColorTokens::default().muted_foreground,
122 n => self.0[ix % n],
123 }
124 }
125}
126
127impl Default for Palette {
128 fn default() -> Self {
129 Self(
130 DEFAULT_PALETTE
131 .iter()
132 .map(|&c| gpui::rgb(c).into())
133 .collect(),
134 )
135 }
136}
137
138#[derive(Clone, Copy, Debug, PartialEq)]
140pub enum NodeSizing {
141 Fixed(f32),
142 Degree {
144 min: f32,
145 max: f32,
146 },
147}
148
149impl Default for NodeSizing {
150 fn default() -> Self {
151 Self::Degree { min: 7., max: 28. }
152 }
153}
154
155impl NodeSizing {
156 pub fn radius(&self, degree: usize) -> f32 {
157 match *self {
158 Self::Fixed(r) => r,
159 Self::Degree { min, max } => (min + (degree as f32).sqrt() * 2.6).min(max),
160 }
161 }
162}
163
164#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
166pub enum LabelMode {
167 Focus,
169 #[default]
171 Auto,
172 Always,
173 Never,
174}
175
176#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
178pub enum ColorBy {
179 #[default]
181 Category,
182 Group,
184}