refactor: split every monolithic module into focused submodules
Each source file that had grown beyond a single concern is replaced by an identically-named directory containing focused submodules. `src/event_loop/mod.rs` (878 lines) becomes a directory with clipboard, context_menu, cursor_shape, drag, focus, handlers, invalidation, overlays_reconcile, repeat, run, surface, text_editing, and tooltip. Every widget, input handler, and theme component follows the same split. Public interfaces are unchanged — only the internal file layout moves. image bumped from 0.25.2 to 0.25.9.
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>
|
||||
|
||||
use smithay_client_toolkit::seat::pointer::PointerEvent;
|
||||
use smithay_client_toolkit::reexports::client::
|
||||
{
|
||||
protocol::wl_pointer::WlPointer,
|
||||
Connection, QueueHandle,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::event_loop::{ AppData, SurfaceFocus };
|
||||
use crate::tree::{ find_handlers, find_widget_at };
|
||||
|
||||
use super::helpers::hover_affects_paint;
|
||||
|
||||
impl<A: App> AppData<A>
|
||||
{
|
||||
pub( super ) fn on_pointer_motion(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_pointer: &WlPointer,
|
||||
event: &PointerEvent,
|
||||
)
|
||||
{
|
||||
let focus = self.focus_for_surface( &event.surface )
|
||||
.unwrap_or( SurfaceFocus::Main );
|
||||
let pp = self.surface( focus ).to_physical( event.position.0, event.position.1 );
|
||||
self.pointer_pos = pp;
|
||||
self.app.on_pointer_move( pp.x, pp.y );
|
||||
|
||||
// Hover tracking — pointer-only (touch has no hover).
|
||||
// Runs before the gesture motion so the cache-dirty
|
||||
// below picks up any hover-dependent redraw request.
|
||||
let new_hover = find_widget_at( &self.surface( focus ).widget_rects, pp );
|
||||
let old_hover = self.surface( focus ).hovered_idx;
|
||||
if new_hover != old_hover
|
||||
{
|
||||
let redraw = hover_affects_paint( &self.surface( focus ).widget_rects, old_hover )
|
||||
|| hover_affects_paint( &self.surface( focus ).widget_rects, new_hover );
|
||||
{
|
||||
let ss = self.surface_mut( focus );
|
||||
ss.hovered_idx = new_hover;
|
||||
if redraw { ss.needs_redraw = true; }
|
||||
}
|
||||
match new_hover
|
||||
{
|
||||
Some( idx ) => self.arm_tooltip( focus, idx ),
|
||||
None => self.cancel_tooltip(),
|
||||
}
|
||||
}
|
||||
|
||||
// Mouse drag-promotion: a left-button press whose hit
|
||||
// widget carries `on_drag_start` should arm a drag as
|
||||
// soon as the cursor moves past the threshold, without
|
||||
// waiting for the touch hold timer. Touch keeps its
|
||||
// hold-then-drag path because scroll / swipe gestures
|
||||
// need the in-between motion budget; mouse has a
|
||||
// dedicated right-click for the menu so left-button
|
||||
// can be drag-only.
|
||||
//
|
||||
// Threshold of 24 px (logical) sits comfortably above
|
||||
// the gesture machine's 6 px long-press cancel
|
||||
// tolerance and above crustace's 16 px drag-commit
|
||||
// threshold, so by the time we promote the next
|
||||
// motion sample is already past the app's commit
|
||||
// distance and drag mode latches without flashing
|
||||
// any half-state.
|
||||
//
|
||||
// Promotion is synchronous (`self.app.update(...)`
|
||||
// directly) so the app's drag state is armed BEFORE
|
||||
// the `on_drag_move` call below runs — otherwise the
|
||||
// seed coords land on a `dragging_item = None`
|
||||
// shell and get lost. We pay the cost of bypassing
|
||||
// `invalidate_after` for this one msg, but the next
|
||||
// frame will repaint everything anyway because the
|
||||
// drag is in flight.
|
||||
let promote = {
|
||||
let ss = self.surface( focus );
|
||||
match ( ss.gesture.long_press_origin, ss.gesture.drag_start_msg.is_some() )
|
||||
{
|
||||
( Some( o ), true ) => ( pp.x - o.x ).hypot( pp.y - o.y ) > 24.0,
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
if promote
|
||||
{
|
||||
let ( ds_msg, origin ) = {
|
||||
let ss = self.surface_mut( focus );
|
||||
let m = ss.gesture.drag_start_msg.take().expect( "promote checked is_some" );
|
||||
let o = ss.gesture.long_press_origin.expect( "promote checked Some(origin)" );
|
||||
ss.gesture.long_press_start = None;
|
||||
ss.gesture.long_press_origin = None;
|
||||
ss.gesture.long_press_msg = None;
|
||||
ss.gesture.long_press_text_idx = None;
|
||||
ss.gesture.long_press_fired = true;
|
||||
ss.request_redraw();
|
||||
( m, o )
|
||||
};
|
||||
self.app.update( ds_msg );
|
||||
let ( ox, oy ) = self.surface_offset_for( focus );
|
||||
self.app.on_drag_move( origin.x + ox, origin.y + oy );
|
||||
self.dirty_caches();
|
||||
self.stop_button_repeat();
|
||||
}
|
||||
|
||||
// `global_drag` must be sampled AFTER the promotion
|
||||
// above — promotion flips `long_press_fired` and we
|
||||
// want the gesture machine to take the drag branch
|
||||
// for the same motion event that triggered the
|
||||
// promotion.
|
||||
let global_drag = self.has_active_long_press_drag();
|
||||
let swipe = self.swipe_config( focus );
|
||||
let outcome =
|
||||
{
|
||||
let ss = self.surface_mut( focus );
|
||||
ss.gesture.on_move( pp, &ss.widget_rects, &mut ss.scroll_offsets, &swipe, global_drag )
|
||||
};
|
||||
self.apply_move_outcome( focus, outcome );
|
||||
|
||||
// Drag-to-select inside a TextEdit. Runs after the
|
||||
// gesture machine so the gesture's "did we leave
|
||||
// the press's hit rect?" reasoning still applies
|
||||
// to the press itself; for text fields the answer
|
||||
// is "fine, keep selecting" because we only widen
|
||||
// the selection while the pointer is still inside
|
||||
// the same widget rect.
|
||||
let pressed_text = self.surface( focus ).gesture.pressed_idx
|
||||
.and_then( |idx|
|
||||
{
|
||||
let is_text = find_handlers( &self.surface( focus ).widget_rects, idx )
|
||||
.map( |h| h.is_text_input() ).unwrap_or( false );
|
||||
if is_text { Some( idx ) } else { None }
|
||||
} );
|
||||
if let Some( idx ) = pressed_text
|
||||
{
|
||||
self.handle_text_pointer_drag( focus, idx, pp );
|
||||
}
|
||||
// Cursor shape: hover may have changed → push the
|
||||
// new shape to the compositor (no-op when
|
||||
// unchanged).
|
||||
self.dispatch_cursor_shape( focus );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user