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:
89
src/input/repeat/button.rs
Normal file
89
src/input/repeat/button.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>
|
||||
|
||||
use calloop::timer::{ Timer, TimeoutAction };
|
||||
|
||||
use crate::app::App;
|
||||
use crate::event_loop::{ AppData, SurfaceFocus };
|
||||
use crate::event_loop::repeat::ButtonRepeatState;
|
||||
|
||||
impl<A: App> AppData<A>
|
||||
{
|
||||
/// Schedule a button-press repeat timer. The runtime fires the
|
||||
/// `on_press` message *immediately* on press too — this fn only
|
||||
/// arms the held-down repeat path; the first fire happens at
|
||||
/// the call site so a quick tap still registers as a single
|
||||
/// press.
|
||||
///
|
||||
/// Each timer tick re-reads the live `on_press` from the
|
||||
/// current widget tree (via the snapshotted `flat_idx`) rather
|
||||
/// than replaying a captured message. This is what makes
|
||||
/// stepper-style buttons work: a stepper builds an `on_press`
|
||||
/// like `"go to value + 5"` at view-build time, so replaying
|
||||
/// the press-time snapshot after the first fire would re-issue
|
||||
/// the same target and the value would freeze. By reading
|
||||
/// `on_press` afresh each tick we pick up the new target the
|
||||
/// view rebuilt with the updated value.
|
||||
///
|
||||
/// No-op when [`Self::effective_repeat_interval`] reports
|
||||
/// repeat disabled (zero rate, app override of
|
||||
/// `Duration::ZERO`). Self-cancels on the first tick where the
|
||||
/// widget at `idx` no longer exists or no longer carries a
|
||||
/// press message.
|
||||
pub( crate ) fn start_button_repeat( &mut self, focus: SurfaceFocus, idx: usize )
|
||||
{
|
||||
// Cancel any pre-existing repeat first — only one button can
|
||||
// be in repeat mode at a time, and a fresh press should
|
||||
// supersede a previous one.
|
||||
self.stop_button_repeat();
|
||||
// Button repeat ticks at a fixed 120 ms (~8 Hz). The keyboard
|
||||
// repeat interval is ~33 ms (30 Hz), which suits cursor /
|
||||
// character entry but is too aggressive for pointer steppers
|
||||
// — a date / time picker would walk a full minute in under
|
||||
// two seconds. 120 ms is fast enough to ramp through values,
|
||||
// slow enough that the user can still release on the value
|
||||
// they want.
|
||||
if matches!( self.effective_repeat_interval(), None ) { return; }
|
||||
let interval = std::time::Duration::from_millis( 120 );
|
||||
let delay = self.effective_repeat_delay();
|
||||
|
||||
let timer = Timer::from_duration( delay );
|
||||
let token = self.loop_handle.insert_source( timer, move |_, _, data: &mut AppData<A>|
|
||||
{
|
||||
let live_msg = crate::tree::find_handlers(
|
||||
&data.surface( focus ).widget_rects,
|
||||
idx,
|
||||
)
|
||||
.and_then( |h| h.press_msg() );
|
||||
match live_msg
|
||||
{
|
||||
Some( m ) =>
|
||||
{
|
||||
data.pending_msgs.push( m );
|
||||
TimeoutAction::ToDuration( interval )
|
||||
}
|
||||
None =>
|
||||
{
|
||||
// Widget gone (view restructured) or no longer
|
||||
// has an on_press → drop ourselves so the timer
|
||||
// does not fire forever against a stale slot.
|
||||
data.button_repeat = None;
|
||||
TimeoutAction::Drop
|
||||
}
|
||||
}
|
||||
} );
|
||||
if let Ok( token ) = token
|
||||
{
|
||||
self.button_repeat = Some( ButtonRepeatState { token } );
|
||||
}
|
||||
}
|
||||
|
||||
/// Cancel any active button-press repeat timer.
|
||||
pub( crate ) fn stop_button_repeat( &mut self )
|
||||
{
|
||||
if let Some( state ) = self.button_repeat.take()
|
||||
{
|
||||
self.loop_handle.remove( state.token );
|
||||
}
|
||||
}
|
||||
}
|
||||
100
src/input/repeat/key.rs
Normal file
100
src/input/repeat/key.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use smithay_client_toolkit::seat::keyboard::KeyEvent;
|
||||
use calloop::timer::{ Timer, TimeoutAction };
|
||||
|
||||
use crate::app::App;
|
||||
use crate::event_loop::{ AppData, SurfaceFocus };
|
||||
use crate::event_loop::repeat::KeyRepeatState;
|
||||
|
||||
/// Built-in fallback for the initial key-repeat delay when the
|
||||
/// compositor does not advertise one and the app does not override
|
||||
/// [`crate::App::key_repeat_delay`].
|
||||
const DEFAULT_REPEAT_DELAY: Duration = Duration::from_millis( 500 );
|
||||
|
||||
/// Built-in fallback for the inter-repeat interval when the compositor
|
||||
/// does not advertise a rate. ~30 Hz, matching the GNOME / KDE default
|
||||
/// for "fast" without straying into uncomfortably-twitchy territory.
|
||||
const DEFAULT_REPEAT_INTERVAL: Duration = Duration::from_millis( 33 );
|
||||
|
||||
impl<A: App> AppData<A>
|
||||
{
|
||||
/// Compute the effective initial repeat delay — app override wins,
|
||||
/// then compositor info, then a built-in default.
|
||||
pub( crate ) fn effective_repeat_delay( &self ) -> Duration
|
||||
{
|
||||
if let Some( d ) = self.app.key_repeat_delay() { return d; }
|
||||
if self.compositor_repeat_delay > 0
|
||||
{
|
||||
Duration::from_millis( self.compositor_repeat_delay as u64 )
|
||||
} else {
|
||||
DEFAULT_REPEAT_DELAY
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the effective inter-repeat interval. Same precedence as
|
||||
/// [`Self::effective_repeat_delay`]: app override → compositor →
|
||||
/// built-in default. Returns `None` when repeat is disabled by the
|
||||
/// active source (compositor `RepeatInfo::Disable` with no app
|
||||
/// override, or an app override of `Some(Duration::ZERO)`).
|
||||
pub( crate ) fn effective_repeat_interval( &self ) -> Option<Duration>
|
||||
{
|
||||
if let Some( d ) = self.app.key_repeat_interval()
|
||||
{
|
||||
if d.is_zero() { return None; }
|
||||
return Some( d );
|
||||
}
|
||||
if self.compositor_repeat_rate == 0
|
||||
{
|
||||
// Compositor explicitly disabled repeat, app did not
|
||||
// override — fall back to a built-in default rather than
|
||||
// disabling, because most environments where the
|
||||
// compositor reports 0 also fail to send the event in
|
||||
// the first place. The default keeps the feel close to
|
||||
// what people expect from a desktop toolkit.
|
||||
return Some( DEFAULT_REPEAT_INTERVAL );
|
||||
}
|
||||
let ms = ( 1000 / self.compositor_repeat_rate ).max( 1 );
|
||||
Some( Duration::from_millis( ms as u64 ) )
|
||||
}
|
||||
|
||||
/// Schedule a key-repeat timer for the given event. No-op when the
|
||||
/// app's [`crate::App::key_repeats`] gate returns `false` for this
|
||||
/// keysym, when repeat is disabled, or when the calloop timer
|
||||
/// insertion fails (in which case held keys simply do not repeat).
|
||||
pub( crate ) fn start_key_repeat( &mut self, focus: SurfaceFocus, event: KeyEvent )
|
||||
{
|
||||
if !self.app.key_repeats( event.keysym ) { return; }
|
||||
let interval = match self.effective_repeat_interval()
|
||||
{
|
||||
Some( i ) => i,
|
||||
None => return,
|
||||
};
|
||||
let delay = self.effective_repeat_delay();
|
||||
|
||||
let event_for_timer = event.clone();
|
||||
let timer = Timer::from_duration( delay );
|
||||
let token = self.loop_handle.insert_source( timer, move |_, _, data: &mut AppData<A>|
|
||||
{
|
||||
data.dispatch_key( focus, event_for_timer.clone() );
|
||||
TimeoutAction::ToDuration( interval )
|
||||
} );
|
||||
match token
|
||||
{
|
||||
Ok( token ) => { self.key_repeat = Some( KeyRepeatState { event, token } ); }
|
||||
Err( _ ) => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Cancel any active key-repeat timer.
|
||||
pub( crate ) fn stop_key_repeat( &mut self )
|
||||
{
|
||||
if let Some( state ) = self.key_repeat.take()
|
||||
{
|
||||
self.loop_handle.remove( state.token );
|
||||
}
|
||||
}
|
||||
}
|
||||
5
src/input/repeat/mod.rs
Normal file
5
src/input/repeat/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>
|
||||
|
||||
pub( crate ) mod key;
|
||||
pub( crate ) mod button;
|
||||
Reference in New Issue
Block a user