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 { self.on_fill_dark } else { self.on_fill_light }
62 }
63}
64
65with_fields!(GraphColors {
66 background: Hsla => with_background,
67 surface: Hsla => with_surface,
68 foreground: Hsla => with_foreground,
69 muted: Hsla => with_muted,
70 border: Hsla => with_border,
71 edge: Hsla => with_edge,
72 edge_active: Hsla => with_edge_active,
73 selection: Hsla => with_selection,
74 focus_ring: Hsla => with_focus_ring,
75 ungrouped: Hsla => with_ungrouped,
76});
77
78#[derive(Clone, Debug)]
80pub(crate) struct GraphMetrics {
81 pub label: TextStyleToken,
83 pub caption: TextStyleToken,
85 pub radius_sm: Pixels,
86 pub radius_md: Pixels,
87 pub space_xs: Pixels,
88 pub space_sm: Pixels,
89 pub space_md: Pixels,
90 pub shadow: Vec<BoxShadow>,
91}
92
93impl GraphMetrics {
94 pub fn from_theme(cx: &App) -> Self {
95 let t = Theme::global(cx).tokens;
96 Self {
97 label: t.typography.sm,
98 caption: t.typography.xs,
99 radius_sm: t.radius.sm,
100 radius_md: t.radius.md,
101 space_xs: t.spacing.xs,
102 space_sm: t.spacing.sm,
103 space_md: t.spacing.md,
104 shadow: t.shadow.md,
105 }
106 }
107}
108
109#[derive(Clone, Debug)]
111pub struct Palette(pub Vec<Hsla>);
112
113impl Palette {
114 pub fn get(&self, ix: usize) -> Hsla {
116 match self.0.len() {
117 0 => ColorTokens::default().muted_foreground,
118 n => self.0[ix % n],
119 }
120 }
121}
122
123impl Default for Palette {
124 fn default() -> Self {
125 Self(DEFAULT_PALETTE.iter().map(|&c| gpui::rgb(c).into()).collect())
126 }
127}
128
129#[derive(Clone, Copy, Debug, PartialEq)]
131pub enum NodeSizing {
132 Fixed(f32),
133 Degree { min: f32, max: f32 },
135}
136
137impl Default for NodeSizing {
138 fn default() -> Self {
139 Self::Degree { min: 7., max: 28. }
140 }
141}
142
143impl NodeSizing {
144 pub fn radius(&self, degree: usize) -> f32 {
145 match *self {
146 Self::Fixed(r) => r,
147 Self::Degree { min, max } => (min + (degree as f32).sqrt() * 2.6).min(max),
148 }
149 }
150}
151
152#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
154pub enum LabelMode {
155 Focus,
157 #[default]
159 Auto,
160 Always,
161 Never,
162}
163
164#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
166pub enum ColorBy {
167 #[default]
169 Category,
170 Group,
172}