From 836bc1e9d0f25802e56bfa800ad4ffc460ed7fd5 Mon Sep 17 00:00:00 2001 From: Hojjat Date: Tue, 22 Sep 2026 13:56:45 -0600 Subject: [PATCH 1/4] fix: UI being unresponsive to mouse events when window is maximized --- winit/src/application/drag_resize.rs | 38 ++++++++++++++++++---------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/winit/src/application/drag_resize.rs b/winit/src/application/drag_resize.rs index 29fa10da..41c280bc 100644 --- a/winit/src/application/drag_resize.rs +++ b/winit/src/application/drag_resize.rs @@ -49,25 +49,26 @@ pub fn event_func( position, .. } => { - let resizable = !window.is_decorated() - && !window.is_maximized() - && window.fullscreen().is_none(); - if resizable { - let location = cursor_resize_direction( + let location = if is_resizable(window) { + cursor_resize_direction( window.surface_size(), *position, border_size, + ) + } else { + None + }; + if location != cursor_prev_resize_direction { + window.set_cursor( + resize_direction_cursor_icon(location).into(), ); - if location != cursor_prev_resize_direction { - window.set_cursor( - resize_direction_cursor_icon(location) - .into(), - ); - cursor_prev_resize_direction = location; - return true; - } + cursor_prev_resize_direction = location; + return true; } } + winit::event::WindowEvent::PointerLeft { .. } => { + cursor_prev_resize_direction = None; + } winit::event::WindowEvent::PointerButton { state: winit::event::ElementState::Pressed, button: @@ -80,7 +81,9 @@ pub fn event_func( primary: true, .. } => { - if let Some(direction) = cursor_prev_resize_direction { + if let Some(direction) = cursor_prev_resize_direction + && is_resizable(window) + { let _res = window.drag_resize_window(direction); return true; } @@ -96,6 +99,13 @@ pub fn event_func( } } +/// Whether the window can currently be resized from its border. +fn is_resizable(window: &dyn winit::window::Window) -> bool { + !window.is_decorated() + && !window.is_maximized() + && window.fullscreen().is_none() +} + /// Get the cursor icon that corresponds to the resize direction. fn resize_direction_cursor_icon( resize_direction: Option, From b2419520fea14dbaf2759823f2c159acb07de89d Mon Sep 17 00:00:00 2001 From: Hojjat Date: Tue, 22 Sep 2026 16:59:25 -0600 Subject: [PATCH 2/4] fix: restore UI cursor after drag-resize border changes it fix cursor --- winit/src/application/drag_resize.rs | 55 +++++++++++++++++++--------- winit/src/lib.rs | 6 ++- winit/src/window.rs | 1 + 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/winit/src/application/drag_resize.rs b/winit/src/application/drag_resize.rs index 41c280bc..04979db7 100644 --- a/winit/src/application/drag_resize.rs +++ b/winit/src/application/drag_resize.rs @@ -1,3 +1,5 @@ +use crate::conversion; +use crate::core::mouse; use cursor_icon::CursorIcon; use winit::window::ResizeDirection; @@ -24,6 +26,9 @@ const DRAG_RESIZE_SUPPORTED: bool = true; const DRAG_RESIZE_SUPPORTED: bool = false; /// If supported by winit, returns a closure that implements cursor resize support. +/// +/// The closure takes the cursor last set by the UI, which it restores when +/// the pointer leaves the resize border. pub fn event_func( window: &dyn winit::window::Window, border_size: f64, @@ -32,6 +37,7 @@ pub fn event_func( dyn FnMut( &dyn winit::window::Window, &winit::event::WindowEvent, + mouse::Interaction, ) -> bool, >, > { @@ -41,7 +47,8 @@ pub fn event_func( Some(Box::new( move |window: &dyn winit::window::Window, - window_event: &winit::event::WindowEvent| + window_event: &winit::event::WindowEvent, + ui_interaction: mouse::Interaction| -> bool { // Keep track of border resize state and set cursor icon when in range match window_event { @@ -59,15 +66,21 @@ pub fn event_func( None }; if location != cursor_prev_resize_direction { - window.set_cursor( - resize_direction_cursor_icon(location).into(), - ); + match location { + Some(direction) => window.set_cursor( + resize_direction_cursor_icon(direction) + .into(), + ), + None => restore_cursor(window, ui_interaction), + } cursor_prev_resize_direction = location; return true; } } winit::event::WindowEvent::PointerLeft { .. } => { - cursor_prev_resize_direction = None; + if cursor_prev_resize_direction.take().is_some() { + restore_cursor(window, ui_interaction); + } } winit::event::WindowEvent::PointerButton { state: winit::event::ElementState::Pressed, @@ -106,22 +119,30 @@ fn is_resizable(window: &dyn winit::window::Window) -> bool { && window.fullscreen().is_none() } +/// Set the cursor back to the one the UI last set. +fn restore_cursor( + window: &dyn winit::window::Window, + ui_interaction: mouse::Interaction, +) { + // `None` means the UI hid the cursor, which a cursor icon doesn't undo. + if let Some(icon) = conversion::mouse_interaction(ui_interaction) { + window.set_cursor(icon.into()); + } +} + /// Get the cursor icon that corresponds to the resize direction. fn resize_direction_cursor_icon( - resize_direction: Option, + resize_direction: ResizeDirection, ) -> CursorIcon { match resize_direction { - Some(resize_direction) => match resize_direction { - ResizeDirection::East => CursorIcon::EResize, - ResizeDirection::North => CursorIcon::NResize, - ResizeDirection::NorthEast => CursorIcon::NeResize, - ResizeDirection::NorthWest => CursorIcon::NwResize, - ResizeDirection::South => CursorIcon::SResize, - ResizeDirection::SouthEast => CursorIcon::SeResize, - ResizeDirection::SouthWest => CursorIcon::SwResize, - ResizeDirection::West => CursorIcon::WResize, - }, - None => CursorIcon::Default, + ResizeDirection::East => CursorIcon::EResize, + ResizeDirection::North => CursorIcon::NResize, + ResizeDirection::NorthEast => CursorIcon::NeResize, + ResizeDirection::NorthWest => CursorIcon::NwResize, + ResizeDirection::South => CursorIcon::SResize, + ResizeDirection::SouthEast => CursorIcon::SeResize, + ResizeDirection::SouthWest => CursorIcon::SwResize, + ResizeDirection::West => CursorIcon::WResize, } } diff --git a/winit/src/lib.rs b/winit/src/lib.rs index db1192fe..dd1ca382 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -1324,7 +1324,11 @@ async fn run_instance

( }; // Initiates a drag resize window state when found. if let Some(func) = window.drag_resize_window_func.as_mut() { - if func(window.raw.as_ref(), &event) { + if func( + window.raw.as_ref(), + &event, + window.mouse_interaction, + ) { continue; } } diff --git a/winit/src/window.rs b/winit/src/window.rs index 18f5f41f..453d59c4 100644 --- a/winit/src/window.rs +++ b/winit/src/window.rs @@ -191,6 +191,7 @@ where dyn FnMut( &dyn winit::window::Window, &winit::event::WindowEvent, + mouse::Interaction, ) -> bool, >, >, From 2157f9d1598366307e8b3b735525e92061326070 Mon Sep 17 00:00:00 2001 From: KarstenKreh <60428712+KarstenKreh@users.noreply.github.com> Date: Thu, 24 Sep 2026 21:27:10 +0200 Subject: [PATCH 3/4] fix: end slider drag when the pointer leaves the window A popup does not deliver the button release if it happens outside the window, so the drag stayed active after the pointer came back. Co-authored-by: Cursor --- widget/src/slider.rs | 23 ++++++++++++++++------- widget/src/vertical_slider.rs | 20 ++++++++++++++++---- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/widget/src/slider.rs b/widget/src/slider.rs index 70cfc9e4..de8ea65c 100644 --- a/widget/src/slider.rs +++ b/widget/src/slider.rs @@ -344,7 +344,7 @@ where _renderer: &Renderer, _clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, - _viewport: &Rectangle, + viewport: &Rectangle, ) { let state = tree.state.downcast_mut::(); @@ -452,6 +452,8 @@ where Event::Mouse(mouse::Event::ButtonReleased( mouse::Button::Left, )) + | Event::Mouse(mouse::Event::CursorLeft) + | Event::Mouse(mouse::Event::CursorEntered) | Event::Touch(touch::Event::FingerLifted { .. }) | Event::Touch(touch::Event::FingerLost { .. }) => { if state.is_dragging { @@ -464,13 +466,20 @@ where Event::Mouse(mouse::Event::CursorMoved { .. }) | Event::Touch(touch::Event::FingerMoved { .. }) => { if state.is_dragging { - let _ = cursor - .land() - .position() - .and_then(locate) - .map(change); + let position = cursor.land().position(); + let outside = position.is_none_or(|position| { + !viewport.contains(position) + }); - shell.capture_event(); + if outside { + if let Some(on_release) = self.on_release.clone() { + shell.publish(on_release); + } + state.is_dragging = false; + } else { + let _ = position.and_then(locate).map(change); + shell.capture_event(); + } } } Event::Mouse(mouse::Event::WheelScrolled { delta }) diff --git a/widget/src/vertical_slider.rs b/widget/src/vertical_slider.rs index a5260374..47a81ce0 100644 --- a/widget/src/vertical_slider.rs +++ b/widget/src/vertical_slider.rs @@ -255,7 +255,7 @@ where _renderer: &Renderer, _clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, - _viewport: &Rectangle, + viewport: &Rectangle, ) { let state = tree.state.downcast_mut::(); let is_dragging = state.is_dragging; @@ -354,6 +354,8 @@ where } } Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) + | Event::Mouse(mouse::Event::CursorLeft) + | Event::Mouse(mouse::Event::CursorEntered) | Event::Touch(touch::Event::FingerLifted { .. }) | Event::Touch(touch::Event::FingerLost { .. }) => { if is_dragging { @@ -366,10 +368,20 @@ where Event::Mouse(mouse::Event::CursorMoved { .. }) | Event::Touch(touch::Event::FingerMoved { .. }) => { if is_dragging { - let _ = - cursor.land().position().and_then(locate).map(change); + let position = cursor.land().position(); + let outside = position.is_none_or(|position| { + !viewport.contains(position) + }); - shell.capture_event(); + if outside { + if let Some(on_release) = self.on_release.clone() { + shell.publish(on_release); + } + state.is_dragging = false; + } else { + let _ = position.and_then(locate).map(change); + shell.capture_event(); + } } } Event::Mouse(mouse::Event::WheelScrolled { delta }) From a31e6c6f837e4c4d0cdb790df50883bee01ea558 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 30 Sep 2026 10:07:03 -0600 Subject: [PATCH 4/4] COSMIC policy update --- .github/PULL_REQUEST_TEMPLATE.md | 11 +++++++++-- .github/workflows/require-checklist.yml | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/require-checklist.yml diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 224bb8f3..63abe526 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,3 +1,10 @@ -The core team is busy and does not have time to mentor nor babysit new contributors. If a member of the core team thinks that reviewing and understanding your work will take more time and effort than writing it from scratch by themselves, your contribution will be dismissed. It is your responsibility to communicate and figure out how to reduce the likelihood of this! +Replace this text with a description of your changes, and why these changes are important. Read and acknowledge the list below. PRs without a completed checkbox will be closed. -Read the contributing guidelines for more details: https://github.com/iced-rs/iced/blob/master/CONTRIBUTING.md +--- + +- I have not included any LLM (also known as AI) generated content in this PR, including code, comments, and descriptions. +- I understand these changes in full and will be able to respond to review comments. +- My change is accurately described in the commit message. +- My contribution is tested and working as described. +- I have read the [Developer Certificate of Origin](https://developercertificate.org/) and certify my contribution under its conditions. +- [ ] I have read and followed all of the above requirements. For more information, see our [contributor guide](https://github.com/pop-os/pop/blob/master/CONTRIBUTING.md). diff --git a/.github/workflows/require-checklist.yml b/.github/workflows/require-checklist.yml new file mode 100644 index 00000000..fd4ca8c6 --- /dev/null +++ b/.github/workflows/require-checklist.yml @@ -0,0 +1,14 @@ +# From https://github.com/mheap/require-checklist-action, license MIT +name: Require Checklist + +on: + pull_request: + types: [opened, edited, synchronize] + +jobs: + job1: + runs-on: ubuntu-latest + steps: + - uses: mheap/require-checklist-action@v2 + with: + requireChecklist: true