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:
101
src/widget/anchored_overlay/mod.rs
Normal file
101
src/widget/anchored_overlay/mod.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>
|
||||
|
||||
//! Position a child element below the on-screen rect of another widget,
|
||||
//! looked up from the *previous* frame's [`LaidOutWidget`] snapshot.
|
||||
//!
|
||||
//! The classic use case is a combo / dropdown popup that should appear
|
||||
//! flush below its trigger. The trigger's rect is not known in `view()`
|
||||
//! time — it's assigned during the layout pass — so the popup widget
|
||||
//! cannot place itself there directly. `AnchoredOverlay` solves this
|
||||
//! with a one-frame-old anchor: the trigger carries a stable
|
||||
//! [`WidgetId`], the popup wraps in `AnchoredOverlay` referencing the
|
||||
//! same id, and at draw time the wrapper looks up the trigger's rect
|
||||
//! from the runtime's persisted `widget_rects` (frame N − 1) and
|
||||
//! overrides the rect that its parent gave it.
|
||||
//!
|
||||
//! When the anchor is not found (first frame after open, missing id,
|
||||
//! widget went off-screen) the wrapper falls back to drawing the child
|
||||
//! in the parent-supplied rect — which, for a typical Stack-overlay
|
||||
//! root in a `view()`, means the full surface, giving a sane modal
|
||||
//! fallback until the next frame fixes the position.
|
||||
|
||||
use crate::types::{ Rect, WidgetId };
|
||||
|
||||
use super::Element;
|
||||
|
||||
#[ cfg( test ) ]
|
||||
mod tests;
|
||||
|
||||
/// A wrapper that re-positions its child relative to an anchor widget
|
||||
/// found in the previous frame's layout snapshot.
|
||||
pub struct AnchoredOverlay<Msg: Clone>
|
||||
{
|
||||
/// The element to draw at the anchored position.
|
||||
pub child: Box<Element<Msg>>,
|
||||
/// Stable identifier of the widget whose rect provides the anchor.
|
||||
pub anchor_id: WidgetId,
|
||||
/// Vertical pixel gap between the bottom of the anchor and the top
|
||||
/// of the child.
|
||||
pub gap: f32,
|
||||
}
|
||||
|
||||
impl<Msg: Clone> AnchoredOverlay<Msg>
|
||||
{
|
||||
/// Wrap `child` so it draws anchored below the widget that carries
|
||||
/// `anchor_id` in its `.id( … )` builder. `gap` is the vertical
|
||||
/// space (logical pixels) between the anchor's bottom edge and the
|
||||
/// child's top edge.
|
||||
pub fn new( child: impl Into<Element<Msg>>, anchor_id: WidgetId, gap: f32 ) -> Self
|
||||
{
|
||||
Self
|
||||
{
|
||||
child: Box::new( child.into() ),
|
||||
anchor_id,
|
||||
gap,
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the draw rect for the child, given the anchor rect (when
|
||||
/// available) and the parent-supplied fallback.
|
||||
///
|
||||
/// Anchor available → place the child flush below the anchor with
|
||||
/// `gap` spacing, preserving the anchor's width.
|
||||
/// Anchor missing → return the parent rect verbatim, so the child
|
||||
/// renders modal-style as a fallback.
|
||||
pub fn resolve_rect( anchor: Option<Rect>, gap: f32, fallback: Rect ) -> Rect
|
||||
{
|
||||
match anchor
|
||||
{
|
||||
Some( a ) => Rect
|
||||
{
|
||||
x: a.x,
|
||||
y: a.y + a.height + gap,
|
||||
width: a.width,
|
||||
height: fallback.height,
|
||||
},
|
||||
None => fallback,
|
||||
}
|
||||
}
|
||||
|
||||
pub( crate ) fn map_msg<U>( self, f: &super::MapFn<Msg, U> ) -> AnchoredOverlay<U>
|
||||
where
|
||||
U: Clone + 'static,
|
||||
Msg: 'static,
|
||||
{
|
||||
AnchoredOverlay
|
||||
{
|
||||
child: Box::new( self.child.map_arc( f ) ),
|
||||
anchor_id: self.anchor_id,
|
||||
gap: self.gap,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Msg: Clone + 'static> From<AnchoredOverlay<Msg>> for Element<Msg>
|
||||
{
|
||||
fn from( a: AnchoredOverlay<Msg> ) -> Self
|
||||
{
|
||||
Element::AnchoredOverlay( a )
|
||||
}
|
||||
}
|
||||
24
src/widget/anchored_overlay/tests.rs
Normal file
24
src/widget/anchored_overlay/tests.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>
|
||||
|
||||
use super::*;
|
||||
|
||||
#[ test ]
|
||||
fn resolve_rect_uses_anchor_when_present()
|
||||
{
|
||||
let anchor = Rect { x: 100.0, y: 50.0, width: 200.0, height: 40.0 };
|
||||
let fallback = Rect { x: 0.0, y: 0.0, width: 800.0, height: 600.0 };
|
||||
let r = AnchoredOverlay::<()>::resolve_rect( Some( anchor ), 8.0, fallback );
|
||||
assert_eq!( r.x, 100.0 );
|
||||
assert_eq!( r.y, 98.0 ); // anchor.y + height + gap
|
||||
assert_eq!( r.width, 200.0 ); // anchor width
|
||||
assert_eq!( r.height, 600.0 ); // fallback height
|
||||
}
|
||||
|
||||
#[ test ]
|
||||
fn resolve_rect_falls_back_when_anchor_missing()
|
||||
{
|
||||
let fallback = Rect { x: 5.0, y: 6.0, width: 7.0, height: 8.0 };
|
||||
let r = AnchoredOverlay::<()>::resolve_rect( None, 8.0, fallback );
|
||||
assert_eq!( r, fallback );
|
||||
}
|
||||
Reference in New Issue
Block a user