graphic design tool
git clone https://git.lucas.co/cce-designer.git
feat: highlight the focused context's pane border; bind cycling via input.kdl
The focused_pane context (network / viewport / parameters / spreadsheet)
had no visual indicator since the per-pane menubars went hidden in the
floating layout. Draw a highlight border around the focused context's
pane on top of the frame's vertices (circle border on the circular
network pane, respecting network_opacity).
Context cycling moves off the hardcoded Ctrl+Tab check onto the
ShortcutManager table as next_context (Ctrl+Tab) / previous_context
(Ctrl+Shift+Tab), rebindable through input.kdl's cce-designer domain.
The chords still dispatch ahead of the widget key paths so they work
from any pane, and execute_action handles the actions so menus/HTTP
can drive them too. Reverse cycling needs cce-ui's ISO_Left_Tab fix
to actually receive Shift+Tab.
Co-Authored-By: Claude Fable 5 <[email protected]>
src/app.rs | 29 +++++++++++++-------
src/main.rs | 9 +++++++
src/render.rs | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
src/shortcut.rs | 2 ++
4 files changed, 112 insertions(+), 11 deletions(-)
diff --git a/src/app.rs b/src/app.rs
index 0031303..1e37914 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -2582,6 +2582,8 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
register("toggle_spreadsheet", "`", Action::ToggleSpreadsheet);
register("toggle_circular_pane", "Ctrl+d", Action::ToggleCircularPane);
register("save_document", "Ctrl+s", Action::Save);
+ register("next_context", "Ctrl+Tab", Action::NextContext);
+ register("previous_context", "Ctrl+Shift+Tab", Action::PrevContext);
}
let mut state = Self {
@@ -3537,6 +3539,17 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
self.save_file_chooser();
}
}
+ Action::NextContext | Action::PrevContext => {
+ self.focused_pane = get_next_visible_pane(
+ self.focused_pane,
+ self.show_network,
+ self.show_viewport,
+ self.show_parameters,
+ self.show_spreadsheet,
+ action == Action::PrevContext,
+ );
+ self.sync_pane_focus();
+ }
}
if settings_changed {
self.save_settings();
@@ -4482,17 +4495,13 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
self.space_pressed = event.state == ElementState::Pressed;
}
+ // Context switching dispatches ahead of the widget key paths
+ // (param pane, node palette) so the chord works from any pane.
if event.state == ElementState::Pressed {
- if event.logical_key == Key::Named(NamedKey::Tab) && self.modifiers.control_key() && !self.modifiers.alt_key() && !self.modifiers.super_key() {
- self.focused_pane = get_next_visible_pane(
- self.focused_pane,
- self.show_network,
- self.show_viewport,
- self.show_parameters,
- self.show_spreadsheet,
- self.modifiers.shift_key(),
- );
- self.sync_pane_focus();
+ if let Some(action @ (Action::NextContext | Action::PrevContext)) =
+ self.shortcut_manager.match_action(&self.modifiers, &event.logical_key)
+ {
+ self.execute_action(action);
return true;
}
}
diff --git a/src/main.rs b/src/main.rs
index e01d2e8..06fa2ad 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -730,6 +730,15 @@ mod tests {
let mods_none = ModifiersState::default();
let key_tick = Key::Character("`".to_string());
assert_eq!(mgr.match_action(&mods_none, &key_tick), Some(Action::ToggleSpreadsheet));
+
+ // Context cycling chords: exact modifier match separates next from previous
+ mgr.register("Ctrl+Tab", Action::NextContext).unwrap();
+ mgr.register("Ctrl+Shift+Tab", Action::PrevContext).unwrap();
+ let key_tab = Key::Named(NamedKey::Tab);
+ let mods_ctrl_shift = ModifiersState { ctrl: true, alt: false, shift: true, logo: false };
+ assert_eq!(mgr.match_action(&mods_ctrl, &key_tab), Some(Action::NextContext));
+ assert_eq!(mgr.match_action(&mods_ctrl_shift, &key_tab), Some(Action::PrevContext));
+ assert_eq!(mgr.match_action(&mods_none, &key_tab), None);
}
}
diff --git a/src/render.rs b/src/render.rs
index 073fce9..7a65405 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -8,7 +8,7 @@ use crate::app::{
make_text_buffer, make_text_buffer_with_font,
CONTENT_IDX, VIEWPORT_IDX, PARAM_IDX, PARAM_PLATE_IDX,
BREADCRUMB_IDX, HEADER_IDX, RIGHT_MENUBAR_IDX,
- SPREADSHEET_MENUBAR_IDX,
+ SPREADSHEET_MENUBAR_IDX, SPREADSHEET_IDX,
MENUBAR_H,
LEFT_MENUBAR_IDX, PARAM_MENUBAR_IDX, NETWORK_PANEL_IDX,
push_circle_vertices, push_circle_border_vertices,
@@ -106,6 +106,87 @@ impl State {
);
}
}
+
+ self.push_context_border(verts, sw, sh, clip_circle_val);
+ }
+
+ /// Highlight border around the focused context's pane. The per-pane
+ /// menubars are hidden in the floating layout, so this border is the
+ /// only visual indicator of `focused_pane`.
+ fn push_context_border(
+ &self,
+ verts: &mut Vec<Vertex>,
+ sw: f32,
+ sh: f32,
+ clip_circle_val: [f32; 3],
+ ) {
+ if self.is_detached_network {
+ return;
+ }
+
+ let thickness = 2.0;
+ let mut color = colors::highlight_primary_color();
+ color[3] = 0.9;
+
+ let (x, y, w, h) = match self.focused_pane {
+ LEFT_MENUBAR_IDX => {
+ if !self.show_network || self.detached_circular_network {
+ return;
+ }
+ if self.circular_network_pane {
+ color[3] *= self.network_opacity;
+ push_circle_border_vertices(
+ self.circular_network_layout.x,
+ self.circular_network_layout.y,
+ self.circular_network_layout.r,
+ 3.0,
+ sw,
+ sh,
+ color,
+ 64,
+ clip_circle_val,
+ verts,
+ );
+ return;
+ }
+ color[3] *= self.network_opacity;
+ self.positions[NETWORK_PANEL_IDX]
+ }
+ RIGHT_MENUBAR_IDX => {
+ if !self.show_viewport {
+ return;
+ }
+ self.positions[VIEWPORT_IDX]
+ }
+ PARAM_MENUBAR_IDX => {
+ if !self.show_parameters {
+ return;
+ }
+ self.positions[PARAM_IDX]
+ }
+ SPREADSHEET_MENUBAR_IDX => {
+ if !self.show_spreadsheet {
+ return;
+ }
+ self.positions[SPREADSHEET_IDX]
+ }
+ _ => return,
+ };
+
+ if w <= 0.0 || h <= 0.0 {
+ return;
+ }
+ let r = cce_ui::layout::plate_corner_radius();
+ let radii = cce_ui::widget::CornerRadii::new(r, r, r, r);
+ push_plate_solid_border_vertices(
+ x, y, w, h,
+ radii,
+ thickness,
+ sw, sh,
+ color,
+ [0.0, 0.0, 0.0],
+ verts,
+ );
}
pub(crate) fn draw_widget_recursive(
diff --git a/src/shortcut.rs b/src/shortcut.rs
index 92970be..4262855 100644
--- a/src/shortcut.rs
+++ b/src/shortcut.rs
@@ -13,6 +13,8 @@ pub enum Action {
ToggleCircularPane,
DetachCircularWindow,
Save,
+ NextContext,
+ PrevContext,
}
#[derive(Debug, Clone, PartialEq, Eq)]