graphic design tool
git clone https://git.lucas.co/cce-designer.git
fix: a press on the spreadsheet's column header sorts the column, not the camera
The toolkit's Spreadsheet has sorted on a header click since cce-ui@a693b97,
but in this app the click never reached it: `cursor_in_viewport` counted the
whole centre column as scene, floating spreadsheet and playbar included, and
the orbit arm at the top of the left-press path returns before any widget is
asked. Clicking a header hovered it, tinted it, and did nothing.
The non-overlay branch now subtracts the floating panes, as the overlay
branch always did; the right-click menu and the pinch zoom gate on the same
test and stop claiming the spreadsheet with it. The wheel's pane-focus
routing asked the spreadsheet's rect as a refinement of the old answer, so
it now asks it first.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
src/app.rs | 26 ++++++++++++++++++++------
src/main.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 67 insertions(+), 6 deletions(-)
diff --git a/src/app.rs b/src/app.rs
index 63e69b0..ba1dbcf 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1807,8 +1807,16 @@ impl State {
&& self.cursor_y >= HEADER_H
&& self.cursor_y < self.height - STATUS_H;
}
+ // Minus the floating panes here too. The spreadsheet and the playbar
+ // sit INSIDE the centre column, over the full-bleed scene, and this
+ // test used to count them as viewport — so a left press on a column
+ // header armed the camera orbit and returned before any widget was
+ // asked, and the spreadsheet's own header-click sort never fired.
+ // Same for the right-click menu and the pinch zoom, which gate on
+ // this test as well.
let node_area_y = self.positions[CONTENT_IDX].1;
- self.cursor_x >= self.content_right_x()
+ !self.over_floating_pane()
+ && self.cursor_x >= self.content_right_x()
&& self.cursor_x < self.splitter_layout.splitter2_x
&& self.cursor_y >= node_area_y
&& self.cursor_y < self.height - STATUS_H
@@ -5924,12 +5932,18 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Detail) -> (Vec<String>, Vec<V
{
if in_network_pane {
new_pane = Some(LEFT_MENUBAR_IDX);
+ } else if self.show_spreadsheet && {
+ let (sx, sy, sw, sh) = self.positions[SPREADSHEET_IDX];
+ sw > 0.0 && sh > 0.0
+ && self.cursor_x >= sx && self.cursor_x < sx + sw
+ && self.cursor_y >= sy && self.cursor_y < sy + sh
+ } {
+ // Asked before the viewport: `cursor_in_viewport` no
+ // longer counts the spreadsheet as scene, so this can
+ // no longer be a refinement of that answer.
+ new_pane = Some(SPREADSHEET_MENUBAR_IDX);
} else if in_viewport {
- if self.show_spreadsheet && self.cursor_y >= self.positions[SPREADSHEET_IDX].1 {
- new_pane = Some(SPREADSHEET_MENUBAR_IDX);
- } else {
- new_pane = Some(RIGHT_MENUBAR_IDX);
- }
+ new_pane = Some(RIGHT_MENUBAR_IDX);
} else if self.cursor_x > self.splitter_layout.splitter2_x + SPLITTER_W && self.cursor_y >= node_area_y && self.cursor_y < self.height - STATUS_H {
new_pane = Some(PARAM_MENUBAR_IDX);
}
diff --git a/src/main.rs b/src/main.rs
index 9f8b24f..d201c12 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -422,6 +422,53 @@ mod tests {
/// Dock swap: dragging a plate's dot to another region swaps occupants,
/// and the dock-owned dimensions stay put — the network lands in the
/// bottom strip's rect, the spreadsheet in the left column's.
+ /// A left press on the spreadsheet's column header reaches the widget,
+ /// which sorts the column — it does not orbit the camera.
+ ///
+ /// `cursor_in_viewport` used to be the whole centre column, spreadsheet
+ /// included, and the orbit arm at the top of the left-press path returns
+ /// before any widget is asked. So the toolkit's header-click sort
+ /// (tested in cce-ui) never fired in this app: clicking a header
+ /// hovered it, tinted it, and did nothing.
+ #[test]
+ fn spreadsheet_header_press_reaches_the_widget_not_the_camera() {
+ use crate::slots::{SPREADSHEET_IDX, SPREADSHEET_MENUBAR_IDX, VIEWPORT_IDX};
+ use crate::window::{LocalPosition, WindowEvent};
+ use cce_ui::widget::{ElementState, MouseButton};
+ let mut state = State::new(false);
+ state.resize(1600.0, 900.0, 1.0);
+ state.show_spreadsheet = true;
+ state.rebuild_positions();
+ state.apply_layout();
+ state.spreadsheet_mut().set_spreadsheet_data(
+ vec!["Point".into(), "Pos.x".into()],
+ vec![vec!["0".into(), "9".into()], vec!["1".into(), "10".into()]],
+ );
+
+ // The middle of the second column's header cell.
+ let (sx, sy, sw, sh) = state.positions[SPREADSHEET_IDX];
+ assert!(sw > 0.0 && sh > 0.0, "the spreadsheet is laid out");
+ let (hx, hy) = (sx + sw * 0.75, sy + 12.0);
+ state.handle_event(&WindowEvent::CursorMoved { position: LocalPosition { x: hx as f64, y: hy as f64 } });
+ assert!(!state.cursor_in_viewport(), "a floating pane is not the scene");
+
+ state.handle_event(&WindowEvent::MouseInput { state: ElementState::Pressed, button: MouseButton::Left });
+ assert!(state.orbit_drag.is_none(), "the press must not arm the camera orbit");
+ assert_eq!(state.focused_widget, Some(SPREADSHEET_IDX), "the press reached the spreadsheet");
+ assert_eq!(state.focused_pane, SPREADSHEET_MENUBAR_IDX);
+ state.handle_event(&WindowEvent::MouseInput { state: ElementState::Released, button: MouseButton::Left });
+
+ // And the scene beside it is still the scene: a press in the
+ // viewport's own rect, clear of every floating pane, orbits.
+ let (vx, vy, vw, vh) = state.positions[VIEWPORT_IDX];
+ let (cx, cy) = (vx + vw * 0.5, vy + vh * 0.3);
+ assert!(!state.over_floating_pane_at(cx, cy), "pick a point clear of the panes");
+ state.handle_event(&WindowEvent::CursorMoved { position: LocalPosition { x: cx as f64, y: cy as f64 } });
+ assert!(state.cursor_in_viewport());
+ state.handle_event(&WindowEvent::MouseInput { state: ElementState::Pressed, button: MouseButton::Left });
+ assert_eq!(state.orbit_drag, Some((cx, cy)), "a press on the scene still orbits");
+ }
+
#[test]
fn test_dock_swap_repositions_plates() {
use crate::app::Dock;