1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
//! Editor theme and styling for egui
//!
//! Provides a modern, flat UI theme based on the Bevy Editor Figma designs.
//! Colors extracted directly from Figma CSS export.
use bevy_egui::egui::{self, Color32, CornerRadius, Stroke, Visuals};
/// Bevy-editor inspired dark theme colors and styling
/// All colors are extracted from the official Bevy Editor Figma CSS export
pub struct EditorTheme;
impl EditorTheme {
// -------------------------------------------------------------------------------════════════
// Background Colors (from Figma CSS)
// -------------------------------------------------------------------------------════════════
/// Main window/tabs background - #1F1F24
pub const BG_WINDOW: Color32 = Color32::from_rgb(31, 31, 36);
/// Panel content background - #2A2A2E
pub const BG_PANEL: Color32 = Color32::from_rgb(42, 42, 46);
/// Widget/button background - #36373B
pub const BG_WIDGET: Color32 = Color32::from_rgb(54, 55, 59);
/// Selected tab/elevated widget background - #46474C
pub const BG_SELECTED_TAB: Color32 = Color32::from_rgb(70, 71, 76);
/// Row hover/highlight background - #404040
pub const BG_ROW: Color32 = Color32::from_rgb(64, 64, 64);
// -------------------------------------------------------------------------------════════════
// Border Colors (from Figma CSS)
// -------------------------------------------------------------------------------════════════
/// Main border - #303030
pub const BORDER_MAIN: Color32 = Color32::from_rgb(48, 48, 48);
/// Widget/panel border - #414142
pub const BORDER_WIDGET: Color32 = Color32::from_rgb(65, 65, 66);
/// Side panel border - #373737
#[allow(dead_code)]
pub const BORDER_PANEL: Color32 = Color32::from_rgb(55, 55, 55);
/// Tree indent line - #4A4A4A
#[allow(dead_code)]
pub const BORDER_INDENT: Color32 = Color32::from_rgb(74, 74, 74);
// -------------------------------------------------------------------------------════════════
// Accent/Selection Colors (from Figma CSS)
// -------------------------------------------------------------------------------════════════
/// Primary accent blue - #206EC9 (tab borders, selection, active buttons)
pub const ACCENT_BLUE: Color32 = Color32::from_rgb(32, 110, 201);
/// Selected tree item background - #273E5D (subtle blue tint)
pub const SELECTION_BG: Color32 = Color32::from_rgb(39, 62, 93);
/// Yellow accent - #FFCA39
#[allow(dead_code)]
pub const ACCENT_YELLOW: Color32 = Color32::from_rgb(255, 202, 57);
// -------------------------------------------------------------------------------════════════
// Text Colors (from Figma CSS)
// -------------------------------------------------------------------------------════════════
/// Brightest text - #ECECEC
pub const TEXT_BRIGHT: Color32 = Color32::from_rgb(236, 236, 236);
/// Primary text - #E6E6E6
pub const TEXT_PRIMARY: Color32 = Color32::from_rgb(230, 230, 230);
/// Standard text - #DDDDDD
#[allow(dead_code)]
pub const TEXT_STANDARD: Color32 = Color32::from_rgb(221, 221, 221);
/// Field labels - #DCDCDC
#[allow(dead_code)]
pub const TEXT_LABEL: Color32 = Color32::from_rgb(220, 220, 220);
/// Filter/input text - #DADADA
#[allow(dead_code)]
pub const TEXT_INPUT: Color32 = Color32::from_rgb(218, 218, 218);
/// Input values - #C2C2C2
#[allow(dead_code)]
pub const TEXT_VALUE: Color32 = Color32::from_rgb(194, 194, 194);
/// Icon strokes - #C4C4C4
pub const TEXT_ICON: Color32 = Color32::from_rgb(196, 196, 196);
/// Inactive/muted text - #A8A8A8
pub const TEXT_MUTED: Color32 = Color32::from_rgb(168, 168, 168);
/// Very muted text - #838385
pub const TEXT_DISABLED: Color32 = Color32::from_rgb(131, 131, 133);
/// Active tab text - #F2F2F2
#[allow(dead_code)]
pub const TEXT_ACTIVE: Color32 = Color32::from_rgb(242, 242, 242);
/// Pure white for selected/active elements - maximum contrast
pub const TEXT_WHITE: Color32 = Color32::WHITE;
// -------------------------------------------------------------------------------════════════
// Semantic Colors (from Figma CSS - axis indicators)
// -------------------------------------------------------------------------------════════════
/// Error/X-axis - #AB4051
pub const ERROR: Color32 = Color32::from_rgb(171, 64, 81);
/// Success/Y-axis - #5D8D0A
#[allow(dead_code)]
pub const SUCCESS: Color32 = Color32::from_rgb(93, 141, 10);
/// Info/Z-axis - #2160A3
#[allow(dead_code)]
pub const INFO: Color32 = Color32::from_rgb(33, 96, 163);
/// Warning color (derived)
pub const WARNING: Color32 = Color32::from_rgb(200, 160, 60);
// -------------------------------------------------------------------------------════════════
// Theme Application
// -------------------------------------------------------------------------------════════════
/// Apply the editor theme to the egui context.
pub fn apply(ctx: &egui::Context) {
let mut style = (*ctx.style()).clone();
let mut visuals = Visuals::dark();
// ───────────────────────────────────────────────────────────────────────
// Window and Panel fills (from Figma)
// ───────────────────────────────────────────────────────────────────────
visuals.window_fill = Self::BG_WINDOW;
visuals.panel_fill = Self::BG_PANEL;
visuals.faint_bg_color = Self::BG_WINDOW;
visuals.extreme_bg_color = Self::BG_WINDOW;
// Subtle shadows
visuals.popup_shadow = egui::Shadow {
offset: [0, 2],
blur: 8,
spread: 0,
color: Color32::from_black_alpha(80),
};
visuals.window_shadow = egui::Shadow {
offset: [0, 4],
blur: 12,
spread: 0,
color: Color32::from_black_alpha(60),
};
// ───────────────────────────────────────────────────────────────────────
// Widget styling - Non-interactive (labels, static elements)
// ───────────────────────────────────────────────────────────────────────
visuals.widgets.noninteractive.bg_fill = Self::BG_WIDGET;
visuals.widgets.noninteractive.weak_bg_fill = Self::BG_PANEL;
visuals.widgets.noninteractive.fg_stroke = Stroke::new(1.0, Self::TEXT_MUTED);
visuals.widgets.noninteractive.bg_stroke = Stroke::new(1.0, Self::BORDER_WIDGET);
visuals.widgets.noninteractive.corner_radius = CornerRadius::same(5);
// ───────────────────────────────────────────────────────────────────────
// Widget styling - Inactive (interactive but not hovered)
// ───────────────────────────────────────────────────────────────────────
visuals.widgets.inactive.bg_fill = Self::BG_WIDGET;
visuals.widgets.inactive.weak_bg_fill = Self::BG_PANEL;
visuals.widgets.inactive.fg_stroke = Stroke::new(1.0, Self::TEXT_PRIMARY);
visuals.widgets.inactive.bg_stroke = Stroke::new(1.0, Self::BORDER_WIDGET);
visuals.widgets.inactive.corner_radius = CornerRadius::same(5);
// ───────────────────────────────────────────────────────────────────────
// Widget styling - Hovered
// ───────────────────────────────────────────────────────────────────────
visuals.widgets.hovered.bg_fill = Self::BG_SELECTED_TAB;
visuals.widgets.hovered.weak_bg_fill = Self::BG_ROW;
visuals.widgets.hovered.fg_stroke = Stroke::new(1.0, Self::TEXT_BRIGHT);
visuals.widgets.hovered.bg_stroke = Stroke::new(1.0, Self::BORDER_WIDGET);
visuals.widgets.hovered.corner_radius = CornerRadius::same(5);
// ───────────────────────────────────────────────────────────────────────
// Widget styling - Active (being clicked/dragged)
// Solid accent blue background per Figma (background: #206EC9)
// Pure white text for maximum readability over blue
// ───────────────────────────────────────────────────────────────────────
visuals.widgets.active.bg_fill = Self::ACCENT_BLUE;
visuals.widgets.active.weak_bg_fill = Self::ACCENT_BLUE;
visuals.widgets.active.fg_stroke = Stroke::new(1.5, Self::TEXT_WHITE);
visuals.widgets.active.bg_stroke = Stroke::new(1.0, Self::BORDER_WIDGET);
visuals.widgets.active.corner_radius = CornerRadius::same(5);
// ───────────────────────────────────────────────────────────────────────
// Widget styling - Open (dropdown open, etc.)
// Pure white text for readability
// ───────────────────────────────────────────────────────────────────────
visuals.widgets.open.bg_fill = Self::BG_SELECTED_TAB;
visuals.widgets.open.weak_bg_fill = Self::BG_ROW;
visuals.widgets.open.fg_stroke = Stroke::new(1.5, Self::TEXT_WHITE);
visuals.widgets.open.bg_stroke = Stroke::new(1.0, Self::ACCENT_BLUE);
visuals.widgets.open.corner_radius = CornerRadius::same(5);
// ───────────────────────────────────────────────────────────────────────
// Selection (text selection, selected buttons, etc.)
// Solid accent blue per Figma (background: #206EC9), white text
// ───────────────────────────────────────────────────────────────────────
visuals.selection.bg_fill = Self::ACCENT_BLUE;
visuals.selection.stroke = Stroke::new(1.0, Self::TEXT_WHITE);
// ───────────────────────────────────────────────────────────────────────
// Window/menu rounding (from Figma: 6-8px for panels, 4-5px for items)
// ───────────────────────────────────────────────────────────────────────
visuals.window_corner_radius = CornerRadius::same(6);
visuals.menu_corner_radius = CornerRadius::same(5);
// ───────────────────────────────────────────────────────────────────────
// Misc colors
// ───────────────────────────────────────────────────────────────────────
visuals.hyperlink_color = Self::ACCENT_BLUE;
visuals.warn_fg_color = Self::WARNING;
visuals.error_fg_color = Self::ERROR;
// Striped backgrounds (for tables, lists)
visuals.striped = true;
// ───────────────────────────────────────────────────────────────────────
// Spacing (tighter to match Figma compact layout)
// ───────────────────────────────────────────────────────────────────────
style.spacing.item_spacing = egui::vec2(4.0, 2.0);
style.spacing.button_padding = egui::vec2(6.0, 3.0);
style.spacing.indent = 16.0;
style.spacing.interact_size = egui::vec2(40.0, 20.0);
style.spacing.slider_width = 100.0;
style.spacing.combo_width = 120.0;
style.spacing.scroll = egui::style::ScrollStyle {
bar_width: 8.0,
handle_min_length: 20.0,
bar_inner_margin: 2.0,
bar_outer_margin: 2.0,
..Default::default()
};
// ───────────────────────────────────────────────────────────────────────
// Apply
// ───────────────────────────────────────────────────────────────────────
style.visuals = visuals;
ctx.set_style(style);
}
// -------------------------------------------------------------------------------════════════
// Helper Methods
// -------------------------------------------------------------------------------════════════
/// Get a frame style for side panels (matches Figma panel styling)
#[allow(dead_code)]
pub fn panel_frame() -> egui::Frame {
egui::Frame {
fill: Self::BG_PANEL,
inner_margin: egui::Margin::same(6),
outer_margin: egui::Margin::ZERO,
stroke: Stroke::new(1.0, Self::BORDER_MAIN),
corner_radius: CornerRadius::same(6),
shadow: egui::Shadow::NONE,
}
}
/// Get a frame style for floating windows
#[allow(dead_code)]
pub fn window_frame() -> egui::Frame {
egui::Frame {
fill: Self::BG_WINDOW,
inner_margin: egui::Margin::same(8),
outer_margin: egui::Margin::same(4),
stroke: Stroke::new(1.0, Self::BORDER_WIDGET),
corner_radius: CornerRadius::same(8),
shadow: egui::Shadow {
offset: [0, 4],
blur: 12,
spread: 0,
color: Color32::from_black_alpha(60),
},
}
}
/// Get a frame style for toolbars (top bars in Figma)
#[allow(dead_code)]
pub fn toolbar_frame() -> egui::Frame {
egui::Frame {
fill: Self::BG_WINDOW,
inner_margin: egui::Margin::symmetric(8, 4),
outer_margin: egui::Margin::ZERO,
stroke: Stroke::new(1.0, Self::BORDER_MAIN),
corner_radius: CornerRadius::ZERO,
shadow: egui::Shadow::NONE,
}
}
/// Get a frame style for collapsible component headers (like Transform in Figma)
#[allow(dead_code)]
pub fn component_header_frame() -> egui::Frame {
egui::Frame {
fill: Self::BG_WIDGET,
inner_margin: egui::Margin::symmetric(8, 5),
outer_margin: egui::Margin::ZERO,
stroke: Stroke::NONE,
corner_radius: CornerRadius {
nw: 5,
ne: 5,
sw: 0,
se: 0,
},
shadow: egui::Shadow::NONE,
}
}
}