khora_editor/chrome/
spine.rs1use std::sync::{Arc, Mutex};
23
24use khora_sdk::editor_ui::*;
25use khora_tool_ui::widgets::paint::{icon_centered, selection_bar};
26
27pub const SPINE_WIDTH: f32 = 48.0;
29const BTN_SIZE: f32 = 34.0;
30
31const MODES: &[(EditorMode, Icon, &str)] = &[
33 (EditorMode::Scene, Icon::Cube, "Scene"),
34 (EditorMode::ControlPlane, Icon::Cpu, "Control Plane ยท DCC"),
35];
36
37pub struct SpinePanel {
39 state: Arc<Mutex<EditorState>>,
40 theme: UiTheme,
41}
42
43impl SpinePanel {
44 pub fn new(state: Arc<Mutex<EditorState>>, theme: UiTheme) -> Self {
46 Self { state, theme }
47 }
48
49 fn current_mode(&self) -> EditorMode {
50 self.state
51 .lock()
52 .ok()
53 .map(|s| s.active_mode)
54 .unwrap_or_default()
55 }
56
57 fn set_mode(&self, mode: EditorMode) {
58 if let Ok(mut s) = self.state.lock() {
59 s.active_mode = mode;
60 }
61 }
62}
63
64impl EditorPanel for SpinePanel {
65 fn id(&self) -> &str {
66 "khora.editor.spine"
67 }
68
69 fn title(&self) -> &str {
70 "Spine"
71 }
72
73 fn preferred_size(&self) -> Option<f32> {
74 Some(SPINE_WIDTH)
75 }
76
77 fn ui(&mut self, ui: &mut dyn UiBuilder) {
78 let theme = &self.theme;
79 let [px, py, pw, ph] = ui.panel_rect();
80
81 ui.paint_rect_filled([px, py], [pw, ph], theme.surface, 0.0);
82 ui.paint_line([px + pw, py], [px + pw, py + ph], theme.border, 1.0);
83
84 let cx = px + pw * 0.5;
87 khora_tool_ui::widgets::diamond(ui, [cx, py + 22.0], 18.0, theme.primary);
88
89 let current = self.current_mode();
91 let mut y = py + 48.0;
92
93 for (mode, icon, tooltip) in MODES {
94 let bx = px + (pw - BTN_SIZE) * 0.5;
95 let rect = [bx, y, BTN_SIZE, BTN_SIZE];
96 let active = *mode == current;
97
98 let hit = ui.interact_rect(&format!("spine-{tooltip}"), rect);
99
100 if active || hit.hovered {
101 ui.paint_rect_filled(
102 [rect[0], rect[1]],
103 [rect[2], rect[3]],
104 theme.surface_interactive,
105 theme.radius_md,
106 );
107 }
108 if active {
109 selection_bar(ui, [px + 1.0, y, 2.0, BTN_SIZE], theme.accent_c);
111 }
112
113 let color = if active {
114 theme.accent_c
115 } else if hit.hovered {
116 theme.text
117 } else {
118 theme.text_muted
119 };
120 icon_centered(ui, rect, *icon, 17.0, color);
121
122 if hit.hovered {
123 ui.tooltip_for_last(tooltip);
124 }
125 if hit.clicked {
126 self.set_mode(*mode);
127 }
128
129 y += BTN_SIZE + 4.0;
130 }
131 }
132}