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}