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:
2026-05-15 23:46:56 +02:00
parent 3d237039c6
commit 4aa3480b64
155 changed files with 13832 additions and 13035 deletions

View 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
View 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
View 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;