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:
223
src/input/touch/mod.rs
Normal file
223
src/input/touch/mod.rs
Normal file
@@ -0,0 +1,223 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>
|
||||
|
||||
//! Wayland touch → ltk dispatch.
|
||||
//!
|
||||
//! `wl_touch` down / up / motion map directly onto the three
|
||||
//! lifecycle methods of [`GestureState`](super::gesture::GestureState),
|
||||
//! same as pointer press / release / motion. Touch has no hover and
|
||||
//! no scroll-axis event, so this file is shorter than `pointer.rs`;
|
||||
//! the two handlers share `apply_move_outcome` /
|
||||
//! `apply_release_events` in `dispatch.rs`.
|
||||
//!
|
||||
//! The only touch-specific state is `AppData::touch_focus`, a
|
||||
//! `HashMap<touch_id, SurfaceFocus>` so a finger that landed on an
|
||||
//! overlay continues to route to that overlay even if it drifts over
|
||||
//! the main surface. Multi-finger tracking is not yet modelled — the
|
||||
//! gesture machine is single-gesture; a second finger arriving while
|
||||
//! the first is pressed overwrites the same slot. Good enough for
|
||||
//! sliders, swipes and taps; a proper multi-touch rewrite is a
|
||||
//! separate refactor.
|
||||
|
||||
use smithay_client_toolkit::seat::touch::TouchHandler;
|
||||
use smithay_client_toolkit::reexports::client::
|
||||
{
|
||||
protocol::{ wl_surface::WlSurface, wl_touch::WlTouch },
|
||||
Connection, QueueHandle,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::event_loop::{ AppData, SurfaceFocus, SurfaceState };
|
||||
use crate::tree::find_handlers;
|
||||
|
||||
impl<A: App> TouchHandler for AppData<A>
|
||||
{
|
||||
fn down(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
_touch: &WlTouch,
|
||||
serial: u32,
|
||||
_time: u32,
|
||||
surface: WlSurface,
|
||||
id: i32,
|
||||
position: ( f64, f64 ),
|
||||
)
|
||||
{
|
||||
self.last_input_serial = serial;
|
||||
let focus = self.focus_for_surface( &surface ).unwrap_or( SurfaceFocus::Main );
|
||||
self.touch_focus.insert( id, focus );
|
||||
let pos = self.surface( focus ).to_physical( position.0, position.1 );
|
||||
self.pointer_pos = pos;
|
||||
if matches!( focus, SurfaceFocus::Main ) && !self.overlays.is_empty()
|
||||
{
|
||||
self.dismiss_main_outside_popups( pos );
|
||||
}
|
||||
|
||||
// Built-in context menu intercepts the touch before the
|
||||
// regular gesture machine — same logic as the pointer path.
|
||||
if self.surface( focus ).context_menu.is_some()
|
||||
{
|
||||
if self.handle_context_menu_press( focus, pos )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let outcome =
|
||||
{
|
||||
let ss = self.surface_mut( focus );
|
||||
let result = ss.gesture.on_press( pos, &ss.widget_rects, &ss.scroll_rects );
|
||||
ss.needs_redraw = true;
|
||||
result
|
||||
};
|
||||
self.set_focus( focus, outcome.hit_idx, qh );
|
||||
if let Some( msg ) = outcome.initial_slider_msg
|
||||
{
|
||||
self.pending_msgs.push( msg );
|
||||
}
|
||||
// Press-and-hold repeat — same wiring as the pointer path.
|
||||
if let Some( idx ) = outcome.hit_idx
|
||||
{
|
||||
let immediate = {
|
||||
let handlers = find_handlers( &self.surface( focus ).widget_rects, idx );
|
||||
if matches!( handlers, Some( crate::widget::WidgetHandlers::Button { repeating: true, .. } ) )
|
||||
{
|
||||
handlers.and_then( |h| h.press_msg() )
|
||||
} else { None }
|
||||
};
|
||||
if let Some( msg ) = immediate
|
||||
{
|
||||
self.pending_msgs.push( msg );
|
||||
self.start_button_repeat( focus, idx );
|
||||
}
|
||||
}
|
||||
// Click-to-position the text cursor for touch presses too,
|
||||
// and double-tap selects the word under the press.
|
||||
if let Some( idx ) = outcome.hit_idx
|
||||
{
|
||||
let is_text = find_handlers( &self.surface( focus ).widget_rects, idx )
|
||||
.map( |h| h.is_text_input() ).unwrap_or( false );
|
||||
if is_text
|
||||
{
|
||||
// Eye icon hit on a password field short-circuits
|
||||
// the text-edit dispatch — fire the toggle msg and
|
||||
// skip cursor placement.
|
||||
if self.handle_password_toggle_press( focus, idx, pos )
|
||||
{
|
||||
let _ = self.note_press_for_double_click( pos );
|
||||
}
|
||||
else
|
||||
{
|
||||
let is_double = self.note_press_for_double_click( pos );
|
||||
if is_double
|
||||
{
|
||||
self.handle_text_select_word( focus, idx, pos );
|
||||
} else {
|
||||
self.handle_text_pointer_down( focus, idx, pos );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let _ = self.note_press_for_double_click( pos );
|
||||
}
|
||||
} else {
|
||||
let _ = self.note_press_for_double_click( pos );
|
||||
}
|
||||
}
|
||||
|
||||
fn up(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_touch: &WlTouch,
|
||||
_serial: u32,
|
||||
_time: u32,
|
||||
id: i32,
|
||||
)
|
||||
{
|
||||
let focus = self.touch_focus.remove( &id ).unwrap_or( SurfaceFocus::Main );
|
||||
// Touch-up does not carry a position in wl_touch — the last
|
||||
// motion's position is the release point.
|
||||
let pos = self.pointer_pos;
|
||||
let global_drag = self.has_active_long_press_drag();
|
||||
let swipe = self.swipe_config( focus );
|
||||
let events_out =
|
||||
{
|
||||
let ss = self.surface_mut( focus );
|
||||
ss.needs_redraw = true;
|
||||
ss.gesture.on_release( pos, &ss.widget_rects, &swipe, global_drag )
|
||||
};
|
||||
self.apply_release_events( focus, events_out );
|
||||
self.stop_button_repeat();
|
||||
}
|
||||
|
||||
fn motion(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_touch: &WlTouch,
|
||||
_time: u32,
|
||||
id: i32,
|
||||
position: ( f64, f64 ),
|
||||
)
|
||||
{
|
||||
let focus = *self.touch_focus.get( &id ).unwrap_or( &SurfaceFocus::Main );
|
||||
let pp = self.surface( focus ).to_physical( position.0, position.1 );
|
||||
self.pointer_pos = pp;
|
||||
|
||||
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 (touch path).
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
fn shape(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_touch: &WlTouch,
|
||||
_id: i32,
|
||||
_major: f64,
|
||||
_minor: f64,
|
||||
) {}
|
||||
|
||||
fn orientation(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_touch: &WlTouch,
|
||||
_id: i32,
|
||||
_orientation: f64,
|
||||
) {}
|
||||
|
||||
fn cancel( &mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _touch: &WlTouch )
|
||||
{
|
||||
self.touch_focus.clear();
|
||||
// The compositor is stealing every active touch — drop all
|
||||
// in-flight gesture state across every surface.
|
||||
let clear = |ss: &mut SurfaceState<A::Message>| { ss.gesture.on_cancel(); };
|
||||
clear( &mut self.main );
|
||||
for ss in self.overlays.values_mut()
|
||||
{
|
||||
clear( ss );
|
||||
}
|
||||
self.stop_button_repeat();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user