ltk/input/keyboard/
back.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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

//! The back request: the shell's back button or gesture (`XF86Back`) and
//! the mouse's back button.
//!
//! Back peels one layer per request, top-most first — see [`App::on_back`]
//! for the order as the app sees it. `Escape` shares only the first step
//! ([`AppData::dismiss_transient`]): it cancels transient UI, never
//! navigates and never leaves the app.

use crate::app::{ App, BackAtRoot, BackResponse };
use crate::event_loop::{ AppData, SurfaceFocus };
use crate::platform::Platform;
use crate::Keysym;

impl<A: App, P: Platform> AppData<A, P>
{
	/// Close the top-most piece of transient UI the runtime owns. Order:
	/// open xdg-popup overlays, the built-in context menu, an active text
	/// selection, then the top-most laid-out pressable carrying an
	/// `on_escape` message (typically a `dialog`'s cancel). Returns whether
	/// there was one.
	pub( crate ) fn dismiss_transient( &mut self, focus: SurfaceFocus ) -> bool
	{
		if !self.overlays.is_empty() && self.app.overlays().iter().any( | s | s.anchor_widget_id.is_some() )
		{
			self.dismiss_all_popups();
			return true;
		}
		if self.surface( focus ).context_menu.is_some()
		{
			self.hide_context_menu( focus );
			return true;
		}
		if self.collapse_selection_if_any( focus ) { return true; }
		let escape_msg = self.surface( focus ).frame.widget_rects.iter()
			.rev()
			.find_map( |w| w.handlers.escape_msg() );
		if let Some( msg ) = escape_msg
		{
			self.pending_msgs.push( msg );
			return true;
		}
		false
	}

	pub( crate ) fn handle_back( &mut self, focus: SurfaceFocus )
	{
		if self.dismiss_transient( focus ) { return; }
		if self.focused_text_input( focus )
		{
			self.set_focus( focus, None );
			return;
		}
		self.back_into_app();
	}

	fn back_into_app( &mut self )
	{
		match self.app.on_back()
		{
			BackResponse::Message( msg ) => self.pending_msgs.push( msg ),
			BackResponse::Consumed       => {}
			BackResponse::Unhandled      =>
			{
				// Apps that predate `on_back` bind the key themselves.
				let legacy = self.app.on_key_with_modifiers( Keysym::XF86_Back, self.ctrl_pressed, self.shift_pressed );
				match legacy
				{
					Some( msg ) => self.pending_msgs.push( msg ),
					None        => self.back_at_root(),
				}
			}
		}
	}

	fn back_at_root( &mut self )
	{
		if !self.platform.back_leaves( &self.main.platform ) { return; }
		match self.app.back_at_root()
		{
			BackAtRoot::Minimize => self.platform.minimize_window( &self.main.platform ),
			BackAtRoot::Close    =>
			{
				self.request_close();
			}
			BackAtRoot::Ignore   => {}
		}
	}
}