// SPDX-License-Identifier: LGPL-2.1-only // Copyright (C) 2026 Liberux Labs, S. L. use crate::app::App; use crate::event_loop::{ AppData, SurfaceFocus }; use crate::input::gesture::{ MoveOutcome, ReleaseEvent }; impl AppData { /// Apply the outcome of a motion event. Non-blocking side-effects /// only: push messages, call app callbacks, set redraw flags. pub( crate ) fn apply_move_outcome ( &mut self, focus: SurfaceFocus, outcome: MoveOutcome, ) { match outcome { MoveOutcome::Idle => {} MoveOutcome::Drag { pos } => { let ( ox, oy ) = self.surface_offset_for( focus ); self.app.on_drag_move( pos.x + ox, pos.y + oy ); self.dirty_caches(); self.surface_mut( focus ).request_redraw(); self.main.request_redraw(); for ss in self.overlays.values_mut() { ss.request_redraw(); } } MoveOutcome::Slider { msg } => { if let Some( m ) = msg { self.pending_msgs.push( m ); } self.surface_mut( focus ).request_redraw(); } MoveOutcome::Scroll => { self.surface_mut( focus ).request_redraw(); } MoveOutcome::Swipe { up, down, horizontal } => { if let Some( v ) = up { self.app.on_swipe_progress( v ); } if let Some( v ) = down { self.app.on_swipe_down_progress( v ); } if let Some( v ) = horizontal { self.app.on_swipe_horizontal_progress( v ); } // Swipe-progress callbacks mutate app state outside // `update`, so the cached view tree is stale — unless the // motion only slides a subsurface over a static main // surface, in which case the per-frame subsurface reconcile // carries the move and a main re-raster is wasted work. if !self.app.subsurface_motion_only() { self.dirty_caches(); self.surface_mut( focus ).request_redraw(); for ss in self.overlays.values_mut() { ss.request_redraw(); } } } } } /// Apply the ordered list of events from a release. A single /// release can emit more than one event (e.g. horizontal /// fall-through followed by a vertical commit or fall-through), so /// we walk the list and run each event's side-effects in order. pub( crate ) fn apply_release_events ( &mut self, focus: SurfaceFocus, events: Vec>, ) { for event in events { self.apply_release_event( focus, event ); } } fn apply_release_event ( &mut self, focus: SurfaceFocus, event: ReleaseEvent, ) { match event { ReleaseEvent::Drop { pos } => { let ( ox, oy ) = self.surface_offset_for( focus ); if let Some( msg ) = self.app.on_drop( pos.x + ox, pos.y + oy ) { self.pending_msgs.push( msg ); } self.clear_long_press_drag(); self.dirty_caches(); self.main.request_redraw(); self.main.frame_pending = false; for ss in self.overlays.values_mut() { ss.request_redraw(); } } ReleaseEvent::SwipeLeft => { if let Some( msg ) = self.app.on_swipe_left() { self.pending_msgs.push( msg ); } // When the app handles the release internally (settle // animation, state-only mutation) it returns no // Message, so the update-driven redraw never fires. // Kick the main surface here so `is_animating()` has a // frame to latch onto. self.dirty_caches(); self.main.request_redraw(); self.main.frame_pending = false; } ReleaseEvent::SwipeRight => { if let Some( msg ) = self.app.on_swipe_right() { self.pending_msgs.push( msg ); } self.dirty_caches(); self.main.request_redraw(); self.main.frame_pending = false; } ReleaseEvent::SwipeUp => { if let Some( msg ) = self.app.on_swipe_up() { self.pending_msgs.push( msg ); } self.dirty_caches(); self.main.request_redraw(); self.main.frame_pending = false; for ss in self.overlays.values_mut() { ss.request_redraw(); ss.frame_pending = false; } } ReleaseEvent::SwipeDown => { if let Some( msg ) = self.app.on_swipe_down() { self.pending_msgs.push( msg ); } self.dirty_caches(); self.main.request_redraw(); self.main.frame_pending = false; for ss in self.overlays.values_mut() { ss.request_redraw(); ss.frame_pending = false; } } ReleaseEvent::HorizontalFellThrough => { // Below threshold: pulse horizontal 0 so a // vertical-dominant gesture that drifted laterally // does not leave the app stuck on a stale horizontal // progress. self.app.on_swipe_horizontal_progress( 0.0 ); self.dirty_caches(); self.main.request_redraw(); self.main.frame_pending = false; } ReleaseEvent::VerticalFellThrough => { self.app.on_swipe_progress( 0.0 ); self.app.on_swipe_down_progress( 0.0 ); self.dirty_caches(); } ReleaseEvent::PushMsg( msg ) => { self.pending_msgs.push( msg ); } ReleaseEvent::EmptyRelease => { match focus { SurfaceFocus::Main => { if let Some( msg ) = self.app.on_tap() { self.pending_msgs.push( msg ); } } SurfaceFocus::Overlay( id ) => { if let Some( msg ) = self.overlay_dismiss_msg( id ) { self.pending_msgs.push( msg ); } } } } } } }