layer_shika_composition/lock_selection.rs
1// TODO: Maybe refactor to reuse the layer shell selector
2
3use crate::{
4 Error,
5 selector::{Selector, SurfaceInfo},
6 slint_interpreter::{ComponentInstance, Value},
7};
8use layer_shika_domain::errors::DomainError;
9
10/// A selection of session lock surfaces matching a selector
11///
12/// Provides methods to interact with all matching lock surfaces at once.
13/// Created via `Shell::select_lock()`.
14pub struct LockSelection<'a> {
15 shell: &'a crate::Shell,
16 selector: Selector,
17}
18
19impl<'a> LockSelection<'a> {
20 pub(crate) fn new(shell: &'a crate::Shell, selector: Selector) -> Self {
21 Self { shell, selector }
22 }
23
24 /// Registers a callback handler for all matching lock surfaces
25 ///
26 /// Handler receives a `CallbackContext` with surface identity and shell control.
27 /// Callbacks are stored and applied when the lock is activated, and automatically
28 /// applied to new surfaces when outputs are hotplugged during an active lock.
29 pub fn on_callback<F, R>(&self, callback_name: &str, handler: F) -> &Self
30 where
31 F: Fn(crate::CallbackContext) -> R + Clone + 'static,
32 R: crate::IntoValue,
33 {
34 self.shell
35 .on_lock_internal(&self.selector, callback_name, handler);
36 self
37 }
38
39 /// Registers a callback handler that receives arguments for all matching lock surfaces
40 pub fn on_callback_with_args<F, R>(&self, callback_name: &str, handler: F) -> &Self
41 where
42 F: Fn(&[Value], crate::CallbackContext) -> R + Clone + 'static,
43 R: crate::IntoValue,
44 {
45 self.shell
46 .on_lock_with_args_internal(&self.selector, callback_name, handler);
47 self
48 }
49
50 /// Executes a function with each matching lock component instance
51 ///
52 /// Returns immediately if no lock surfaces are active. During activation,
53 /// this iterates over all lock component instances matching the selector.
54 pub fn with_component<F>(&self, mut f: F)
55 where
56 F: FnMut(&ComponentInstance),
57 {
58 self.shell
59 .with_selected_lock(&self.selector, |_, component| {
60 f(component);
61 });
62 }
63
64 /// Sets a property value on all matching lock surfaces
65 ///
66 /// If the lock is inactive, the property operation is stored and will be applied
67 /// when the lock is activated. If the lock is active, the property is set immediately
68 /// on all matching component instances.
69 pub fn set_property(&self, name: &str, value: &Value) -> Result<(), Error> {
70 self.shell
71 .register_lock_property_internal(&self.selector, name, value.clone());
72 Ok(())
73 }
74
75 /// Gets property values from all matching lock surfaces
76 ///
77 /// Returns an empty vector if the lock is inactive.
78 pub fn get_property(&self, name: &str) -> Result<Vec<Value>, Error> {
79 let mut values = Vec::new();
80 let mut result = Ok(());
81 self.shell
82 .with_selected_lock(&self.selector, |_, component| {
83 match component.get_property(name) {
84 Ok(value) => values.push(value),
85 Err(e) => {
86 log::error!("Failed to get property '{}' from lock surface: {}", name, e);
87 result = Err(Error::Domain(DomainError::Configuration {
88 message: format!("Failed to get property '{}': {}", name, e),
89 }));
90 }
91 }
92 });
93 result.map(|()| values)
94 }
95
96 /// Returns the number of lock surfaces matching the selector
97 ///
98 /// Returns 0 if the lock is inactive.
99 pub fn count(&self) -> usize {
100 self.shell.count_selected_lock(&self.selector)
101 }
102
103 /// Checks if no lock surfaces match the selector
104 ///
105 /// Returns true if the lock is inactive or no surfaces match the selector.
106 pub fn is_empty(&self) -> bool {
107 self.count() == 0
108 }
109
110 /// Returns information about all matching lock surfaces
111 ///
112 /// Returns an empty vector if the lock is inactive.
113 pub fn info(&self) -> Vec<SurfaceInfo> {
114 self.shell.get_selected_lock_info(&self.selector)
115 }
116}