fix(ribbon): scrollable layer dropdown, stop mouse leak to viewport (#227)

The ribbon layer dropdown grew unbounded so a long layer list ran off the
bottom of the screen with no way to scroll. Cap the panel height and make
the list scrollable; short lists still shrink to fit.

While a dropdown was open, the 3D viewport beneath it kept tracking the
cursor over the panel's empty areas, and the OS cursor vanished there. In
iced 0.14 mouse_area/opaque only capture button presses, never CursorMoved,
so a higher stack layer can't swallow motion — gate it state-side instead:

- ignore viewport motion in on_viewport_move while ribbon.open_dropdown is set
- suppress the crosshair overlay's Hidden cursor (and crosshair draw) so the
  normal OS cursor shows over an open dropdown
- route all ribbon dropdown backdrops through a shared dropdown_backdrop()

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-07-01 00:45:23 +03:00
commit 3da81571a1
4 changed files with 46 additions and 37 deletions

View file

@ -189,6 +189,7 @@ pub fn selection_overlay<'a>(
pane_move_rect: Option<iced::Rectangle>,
pane_drop_rect: Option<iced::Rectangle>,
pan_mode: bool,
suppressed: bool,
) -> Element<'a, Message> {
canvas(SelectionCanvas {
selection,
@ -202,6 +203,7 @@ pub fn selection_overlay<'a>(
pane_move_rect,
pane_drop_rect,
pan_mode,
suppressed,
})
.width(Length::Fill)
.height(Length::Fill)
@ -230,6 +232,10 @@ struct SelectionCanvas {
/// Interactive PAN mode: the crosshair is hidden and the cursor becomes a
/// hand so the viewport reads as a draggable surface.
pan_mode: bool,
/// A ribbon dropdown (or similar overlay) is open over the viewport. The
/// crosshair is not drawn and the OS cursor is shown normally so the panel
/// is usable instead of the cursor vanishing over it. (#227)
suppressed: bool,
}
impl SelectionCanvas {
@ -259,6 +265,12 @@ impl canvas::Program<Message> for SelectionCanvas {
bounds: iced::Rectangle,
cursor: mouse::Cursor,
) -> mouse::Interaction {
// A dropdown/overlay is open over the viewport — show the normal OS
// cursor over the whole canvas instead of hiding it for the crosshair,
// so the cursor doesn't vanish while using the panel. (#227)
if self.suppressed {
return mouse::Interaction::default();
}
// PAN mode owns the whole viewport: an open hand when hovering, a
// closed hand while dragging.
if self.pan_mode && cursor.is_over(bounds) {
@ -826,7 +838,7 @@ impl canvas::Program<Message> for SelectionCanvas {
// top of it would double up the visual feedback.
let over_divider = self.divider_under(cursor, bounds);
// PAN mode replaces the crosshair with a hand cursor.
if !over_viewcube && !over_divider && !self.pan_mode {
if !over_viewcube && !over_divider && !self.pan_mode && !self.suppressed {
if let Some(cp) = self.selection.last_move_pos {
let color = Color {
r: 0.85,