ltk/event_loop/
overlays.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

//! What every backend does as the app's overlays come and go: the size an overlay asks for, and what has to move to the main surface when one goes.

use crate::app::{ App, OverlayId };
use crate::platform::Platform;
use crate::types::Length;

use super::{ AppData, SurfaceFocus, SurfaceState };

impl<A: App, P: Platform> AppData<A, P>
{
	/// `size`, an `OverlaySpec::size`, in physical pixels. It resolves against the main surface's physical viewport, the app's layout space, so an overlay sized with `Length::widget( … )` scales with the display like any widget, and `Length::px( n )` keeps a fixed size.
	pub( crate ) fn overlay_size( &self, size: &( Length, Length ) ) -> ( u32, u32 )
	{
		let viewport = ( self.main.physical_width() as f32, self.main.physical_height() as f32 );
		let density  = crate::types::density();
		let scale    = self.main.scale_factor;
		(
			size.0.resolve_physical( viewport, Length::EM_BASE_DEFAULT, density, scale ).max( 0.0 ).round() as u32,
			size.1.resolve_physical( viewport, Length::EM_BASE_DEFAULT, density, scale ).max( 0.0 ).round() as u32,
		)
	}

	/// Forget overlay `id` and return its state for the backend to release. A drag a long press began on it moves to the main surface, since apps typically hide the overlay because of the long press and the motion and release that follow must still reach the app's drag handlers; every focus that named it names the main surface.
	pub( crate ) fn remove_overlay( &mut self, id: OverlayId ) -> Option<SurfaceState<A::Message, P::Surface>>
	{
		let ss = self.overlays.remove( &id )?;
		if ss.gesture.long_press_fired
		{
			self.main.gesture.long_press_fired  = true;
			self.main.gesture.long_press_origin = ss.gesture.long_press_origin;
			// Keep the primary touch slot with the migrated drag so motion / release still route through the gesture machine.
			if self.main.primary_touch_id.is_none()
			{
				self.main.primary_touch_id = ss.primary_touch_id;
			}
		}
		let gone = SurfaceFocus::Overlay( id );
		if self.pointer_focus == gone { self.pointer_focus = SurfaceFocus::Main; }
		if self.keyboard_focus == gone { self.keyboard_focus = SurfaceFocus::Main; }
		// Rewritten rather than dropped: a touch id with no focus would default to Main without the drag state, turning a release into a stray tap.
		for f in self.touch_focus.values_mut()
		{
			if *f == gone { *f = SurfaceFocus::Main; }
		}
		Some( ss )
	}
}