mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-12-12 12:15:56 +00:00
refactor: surface identifier refactor
This commit is contained in:
parent
2c484ef392
commit
71089fc7af
16 changed files with 765 additions and 456 deletions
|
|
@ -1,5 +1,6 @@
|
|||
use layer_shika_domain::config::SurfaceConfig as DomainSurfaceConfig;
|
||||
use layer_shika_domain::value_objects::anchor::AnchorEdges;
|
||||
use layer_shika_domain::value_objects::handle::SurfaceHandle;
|
||||
use layer_shika_domain::value_objects::keyboard_interactivity::KeyboardInteractivity as DomainKeyboardInteractivity;
|
||||
use layer_shika_domain::value_objects::layer::Layer;
|
||||
use layer_shika_domain::value_objects::margins::Margins;
|
||||
|
|
@ -23,6 +24,7 @@ pub(crate) struct LayerSurfaceConfig {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct WaylandSurfaceConfig {
|
||||
pub surface_handle: SurfaceHandle,
|
||||
pub surface_name: String,
|
||||
pub height: u32,
|
||||
pub width: u32,
|
||||
|
|
@ -41,12 +43,14 @@ pub struct WaylandSurfaceConfig {
|
|||
impl WaylandSurfaceConfig {
|
||||
#[must_use]
|
||||
pub fn from_domain_config(
|
||||
surface_handle: SurfaceHandle,
|
||||
surface_name: impl Into<String>,
|
||||
component_definition: ComponentDefinition,
|
||||
compilation_result: Option<Rc<CompilationResult>>,
|
||||
domain_config: DomainSurfaceConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
surface_handle,
|
||||
surface_name: surface_name.into(),
|
||||
height: domain_config.dimensions.height(),
|
||||
width: domain_config.dimensions.width(),
|
||||
|
|
|
|||
|
|
@ -146,6 +146,7 @@ impl OutputManager {
|
|||
|
||||
app_state.add_output(
|
||||
output_id,
|
||||
self.config.surface_handle,
|
||||
&self.config.surface_name,
|
||||
main_surface_id,
|
||||
surface,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use crate::wayland::{
|
|||
surface_state::SurfaceState,
|
||||
},
|
||||
};
|
||||
use layer_shika_domain::value_objects::handle::SurfaceHandle;
|
||||
use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
|
||||
use crate::{
|
||||
errors::{EventLoopError, LayerShikaError, RenderingError, Result},
|
||||
|
|
@ -51,6 +52,7 @@ struct OutputSetup {
|
|||
main_surface_id: ObjectId,
|
||||
window: Rc<FemtoVGWindow>,
|
||||
builder: SurfaceStateBuilder,
|
||||
surface_handle: SurfaceHandle,
|
||||
shell_surface_name: String,
|
||||
}
|
||||
|
||||
|
|
@ -199,6 +201,7 @@ impl WaylandShellSystem {
|
|||
main_surface_id,
|
||||
window,
|
||||
builder,
|
||||
surface_handle: config.surface_handle,
|
||||
shell_surface_name: config.surface_name.clone(),
|
||||
});
|
||||
}
|
||||
|
|
@ -254,6 +257,7 @@ impl WaylandShellSystem {
|
|||
|
||||
app_state.add_shell_surface(
|
||||
&setup.output_id,
|
||||
setup.surface_handle,
|
||||
&setup.shell_surface_name,
|
||||
setup.main_surface_id,
|
||||
per_output_surface,
|
||||
|
|
@ -479,6 +483,7 @@ impl WaylandShellSystem {
|
|||
main_surface_id,
|
||||
window,
|
||||
builder,
|
||||
surface_handle: shell_config.config.surface_handle,
|
||||
shell_surface_name: shell_config.name.clone(),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use super::surface_state::SurfaceState;
|
|||
use crate::wayland::managed_proxies::ManagedWlPointer;
|
||||
use crate::wayland::outputs::{OutputManager, OutputMapping};
|
||||
use layer_shika_domain::entities::output_registry::OutputRegistry;
|
||||
use layer_shika_domain::value_objects::handle::SurfaceHandle;
|
||||
use layer_shika_domain::value_objects::output_handle::OutputHandle;
|
||||
use layer_shika_domain::value_objects::output_info::OutputInfo;
|
||||
use std::cell::RefCell;
|
||||
|
|
@ -16,14 +17,14 @@ pub type PerOutputSurface = SurfaceState;
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ShellSurfaceKey {
|
||||
pub output_handle: OutputHandle,
|
||||
pub surface_name: String,
|
||||
pub surface_handle: SurfaceHandle,
|
||||
}
|
||||
|
||||
impl ShellSurfaceKey {
|
||||
pub fn new(output_handle: OutputHandle, surface_name: impl Into<String>) -> Self {
|
||||
pub const fn new(output_handle: OutputHandle, surface_handle: SurfaceHandle) -> Self {
|
||||
Self {
|
||||
output_handle,
|
||||
surface_name: surface_name.into(),
|
||||
surface_handle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +34,7 @@ pub struct AppState {
|
|||
output_mapping: OutputMapping,
|
||||
surfaces: HashMap<ShellSurfaceKey, PerOutputSurface>,
|
||||
surface_to_key: HashMap<ObjectId, ShellSurfaceKey>,
|
||||
surface_handle_to_name: HashMap<SurfaceHandle, String>,
|
||||
_pointer: ManagedWlPointer,
|
||||
shared_pointer_serial: Rc<SharedPointerSerial>,
|
||||
output_manager: Option<Rc<RefCell<OutputManager>>>,
|
||||
|
|
@ -47,6 +49,7 @@ impl AppState {
|
|||
output_mapping: OutputMapping::new(),
|
||||
surfaces: HashMap::new(),
|
||||
surface_to_key: HashMap::new(),
|
||||
surface_handle_to_name: HashMap::new(),
|
||||
_pointer: pointer,
|
||||
shared_pointer_serial: shared_serial,
|
||||
output_manager: None,
|
||||
|
|
@ -78,6 +81,7 @@ impl AppState {
|
|||
pub fn add_shell_surface(
|
||||
&mut self,
|
||||
output_id: &ObjectId,
|
||||
surface_handle: SurfaceHandle,
|
||||
surface_name: &str,
|
||||
main_surface_id: ObjectId,
|
||||
surface_state: PerOutputSurface,
|
||||
|
|
@ -91,19 +95,28 @@ impl AppState {
|
|||
h
|
||||
});
|
||||
|
||||
let key = ShellSurfaceKey::new(handle, surface_name);
|
||||
let key = ShellSurfaceKey::new(handle, surface_handle);
|
||||
self.surface_to_key.insert(main_surface_id, key.clone());
|
||||
self.surfaces.insert(key, surface_state);
|
||||
self.surface_handle_to_name
|
||||
.insert(surface_handle, surface_name.to_string());
|
||||
}
|
||||
|
||||
pub fn add_output(
|
||||
&mut self,
|
||||
output_id: &ObjectId,
|
||||
surface_handle: SurfaceHandle,
|
||||
surface_name: &str,
|
||||
main_surface_id: ObjectId,
|
||||
surface_state: PerOutputSurface,
|
||||
) {
|
||||
self.add_shell_surface(output_id, surface_name, main_surface_id, surface_state);
|
||||
self.add_shell_surface(
|
||||
output_id,
|
||||
surface_handle,
|
||||
surface_name,
|
||||
main_surface_id,
|
||||
surface_state,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn remove_output(&mut self, handle: OutputHandle) -> Vec<PerOutputSurface> {
|
||||
|
|
@ -139,21 +152,21 @@ impl AppState {
|
|||
self.surfaces.get_mut(key)
|
||||
}
|
||||
|
||||
pub fn get_surface_by_name(
|
||||
pub fn get_surface_by_instance(
|
||||
&self,
|
||||
surface_handle: SurfaceHandle,
|
||||
output_handle: OutputHandle,
|
||||
shell_window_name: &str,
|
||||
) -> Option<&PerOutputSurface> {
|
||||
let key = ShellSurfaceKey::new(output_handle, shell_window_name);
|
||||
let key = ShellSurfaceKey::new(output_handle, surface_handle);
|
||||
self.surfaces.get(&key)
|
||||
}
|
||||
|
||||
pub fn get_surface_by_name_mut(
|
||||
pub fn get_surface_by_instance_mut(
|
||||
&mut self,
|
||||
surface_handle: SurfaceHandle,
|
||||
output_handle: OutputHandle,
|
||||
shell_window_name: &str,
|
||||
) -> Option<&mut PerOutputSurface> {
|
||||
let key = ShellSurfaceKey::new(output_handle, shell_window_name);
|
||||
let key = ShellSurfaceKey::new(output_handle, surface_handle);
|
||||
self.surfaces.get_mut(&key)
|
||||
}
|
||||
|
||||
|
|
@ -213,6 +226,12 @@ impl AppState {
|
|||
.find(|surface| surface.layer_surface().as_ref().id() == *layer_surface_id)
|
||||
}
|
||||
|
||||
pub fn get_surface_name(&self, surface_handle: SurfaceHandle) -> Option<&str> {
|
||||
self.surface_handle_to_name
|
||||
.get(&surface_handle)
|
||||
.map(String::as_str)
|
||||
}
|
||||
|
||||
pub fn get_key_by_surface(&self, surface_id: &ObjectId) -> Option<&ShellSurfaceKey> {
|
||||
self.surface_to_key.get(surface_id)
|
||||
}
|
||||
|
|
@ -280,11 +299,17 @@ impl AppState {
|
|||
pub fn surfaces_for_output(
|
||||
&self,
|
||||
handle: OutputHandle,
|
||||
) -> impl Iterator<Item = (&str, &PerOutputSurface)> {
|
||||
) -> impl Iterator<Item = (&str, &PerOutputSurface)> + '_ {
|
||||
self.surfaces
|
||||
.iter()
|
||||
.filter(move |(k, _)| k.output_handle == handle)
|
||||
.map(|(k, v)| (k.surface_name.as_str(), v))
|
||||
.map(|(k, v)| {
|
||||
let name = self
|
||||
.surface_handle_to_name
|
||||
.get(&k.surface_handle)
|
||||
.map_or("unknown", String::as_str);
|
||||
(name, v)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn surfaces_with_keys(
|
||||
|
|
@ -360,30 +385,101 @@ impl AppState {
|
|||
|
||||
pub fn shell_surface_names(&self) -> Vec<&str> {
|
||||
let mut names: Vec<_> = self
|
||||
.surfaces
|
||||
.keys()
|
||||
.map(|k| k.surface_name.as_str())
|
||||
.surface_handle_to_name
|
||||
.values()
|
||||
.map(String::as_str)
|
||||
.collect();
|
||||
names.sort_unstable();
|
||||
names.dedup();
|
||||
names
|
||||
}
|
||||
|
||||
pub fn surfaces_by_name(&self, surface_name: &str) -> impl Iterator<Item = &PerOutputSurface> {
|
||||
pub fn surfaces_by_name(&self, surface_name: &str) -> Vec<&PerOutputSurface> {
|
||||
let matching_handles: Vec<SurfaceHandle> = self
|
||||
.surface_handle_to_name
|
||||
.iter()
|
||||
.filter(|(_, n)| n.as_str() == surface_name)
|
||||
.map(|(h, _)| *h)
|
||||
.collect();
|
||||
|
||||
self.surfaces
|
||||
.iter()
|
||||
.filter(move |(k, _)| k.surface_name == surface_name)
|
||||
.filter(|(k, _)| matching_handles.contains(&k.surface_handle))
|
||||
.map(|(_, v)| v)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn surfaces_by_name_mut(
|
||||
&mut self,
|
||||
surface_name: &str,
|
||||
) -> impl Iterator<Item = &mut PerOutputSurface> {
|
||||
pub fn surfaces_by_name_mut(&mut self, surface_name: &str) -> Vec<&mut PerOutputSurface> {
|
||||
let matching_handles: Vec<SurfaceHandle> = self
|
||||
.surface_handle_to_name
|
||||
.iter()
|
||||
.filter(|(_, n)| n.as_str() == surface_name)
|
||||
.map(|(h, _)| *h)
|
||||
.collect();
|
||||
|
||||
self.surfaces
|
||||
.iter_mut()
|
||||
.filter(move |(k, _)| k.surface_name == surface_name)
|
||||
.filter(|(k, _)| matching_handles.contains(&k.surface_handle))
|
||||
.map(|(_, v)| v)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn surfaces_by_handle(&self, handle: SurfaceHandle) -> Vec<&PerOutputSurface> {
|
||||
self.surfaces
|
||||
.iter()
|
||||
.filter(|(k, _)| k.surface_handle == handle)
|
||||
.map(|(_, v)| v)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn surfaces_by_handle_mut(&mut self, handle: SurfaceHandle) -> Vec<&mut PerOutputSurface> {
|
||||
self.surfaces
|
||||
.iter_mut()
|
||||
.filter(|(k, _)| k.surface_handle == handle)
|
||||
.map(|(_, v)| v)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn surfaces_by_name_and_output(
|
||||
&self,
|
||||
name: &str,
|
||||
output: OutputHandle,
|
||||
) -> Vec<&PerOutputSurface> {
|
||||
let matching_handles: Vec<SurfaceHandle> = self
|
||||
.surface_handle_to_name
|
||||
.iter()
|
||||
.filter(|(_, n)| n.as_str() == name)
|
||||
.map(|(h, _)| *h)
|
||||
.collect();
|
||||
|
||||
self.surfaces
|
||||
.iter()
|
||||
.filter(|(k, _)| {
|
||||
k.output_handle == output && matching_handles.contains(&k.surface_handle)
|
||||
})
|
||||
.map(|(_, v)| v)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn surfaces_by_name_and_output_mut(
|
||||
&mut self,
|
||||
name: &str,
|
||||
output: OutputHandle,
|
||||
) -> Vec<&mut PerOutputSurface> {
|
||||
let matching_handles: Vec<SurfaceHandle> = self
|
||||
.surface_handle_to_name
|
||||
.iter()
|
||||
.filter(|(_, n)| n.as_str() == name)
|
||||
.map(|(h, _)| *h)
|
||||
.collect();
|
||||
|
||||
self.surfaces
|
||||
.iter_mut()
|
||||
.filter(|(k, _)| {
|
||||
k.output_handle == output && matching_handles.contains(&k.surface_handle)
|
||||
})
|
||||
.map(|(_, v)| v)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_output_by_handle(&self, handle: OutputHandle) -> Option<&PerOutputSurface> {
|
||||
|
|
@ -420,10 +516,17 @@ impl AppState {
|
|||
}
|
||||
|
||||
pub fn remove_surfaces_by_name(&mut self, surface_name: &str) -> Vec<PerOutputSurface> {
|
||||
let matching_handles: Vec<SurfaceHandle> = self
|
||||
.surface_handle_to_name
|
||||
.iter()
|
||||
.filter(|(_, n)| n.as_str() == surface_name)
|
||||
.map(|(h, _)| *h)
|
||||
.collect();
|
||||
|
||||
let keys_to_remove: Vec<_> = self
|
||||
.surfaces
|
||||
.keys()
|
||||
.filter(|k| k.surface_name == surface_name)
|
||||
.filter(|k| matching_handles.contains(&k.surface_handle))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
|
|
@ -435,7 +538,7 @@ impl AppState {
|
|||
}
|
||||
|
||||
self.surface_to_key
|
||||
.retain(|_, k| k.surface_name != surface_name);
|
||||
.retain(|_, k| !matching_handles.contains(&k.surface_handle));
|
||||
|
||||
removed
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,13 +32,15 @@ pub use layer_shika_domain::value_objects::popup_positioning_mode::PopupPosition
|
|||
pub use layer_shika_domain::value_objects::popup_request::{
|
||||
PopupHandle, PopupPlacement, PopupRequest, PopupSize,
|
||||
};
|
||||
pub use layer_shika_domain::value_objects::surface_instance_id::SurfaceInstanceId;
|
||||
pub use layer_surface::{LayerSurfaceHandle, ShellSurfaceConfigHandler};
|
||||
pub use popup_builder::PopupBuilder;
|
||||
pub use selection::Selection;
|
||||
pub use selector::{Output, Selector, Surface, SurfaceInfo};
|
||||
pub use shell_runtime::{DEFAULT_SURFACE_NAME, ShellRuntime};
|
||||
pub use system::{
|
||||
EventDispatchContext, RuntimeSurfaceConfigBuilder, ShellControl, SurfaceControlHandle,
|
||||
CallbackContext, EventDispatchContext, RuntimeSurfaceConfigBuilder, ShellControl,
|
||||
SurfaceControlHandle, SurfaceTarget,
|
||||
};
|
||||
pub use value_conversion::IntoValue;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ impl<'a> Selection<'a> {
|
|||
|
||||
pub fn on_callback<F, R>(&mut self, callback_name: &str, handler: F) -> &mut Self
|
||||
where
|
||||
F: Fn(crate::ShellControl) -> R + Clone + 'static,
|
||||
F: Fn(crate::CallbackContext) -> R + Clone + 'static,
|
||||
R: crate::IntoValue,
|
||||
{
|
||||
self.shell
|
||||
|
|
@ -32,7 +32,7 @@ impl<'a> Selection<'a> {
|
|||
|
||||
pub fn on_callback_with_args<F, R>(&mut self, callback_name: &str, handler: F) -> &mut Self
|
||||
where
|
||||
F: Fn(&[Value], crate::ShellControl) -> R + Clone + 'static,
|
||||
F: Fn(&[Value], crate::CallbackContext) -> R + Clone + 'static,
|
||||
R: crate::IntoValue,
|
||||
{
|
||||
self.shell
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use crate::shell_config::{CompiledUiSource, ShellConfig};
|
|||
use crate::shell_runtime::ShellRuntime;
|
||||
use crate::surface_registry::{SurfaceDefinition, SurfaceEntry, SurfaceRegistry};
|
||||
use crate::system::{
|
||||
EventDispatchContext, PopupCommand, ShellCommand, ShellControl, SurfaceCommand,
|
||||
CallbackContext, EventDispatchContext, PopupCommand, ShellCommand, ShellControl,
|
||||
SurfaceCommand, SurfaceTarget,
|
||||
};
|
||||
use crate::value_conversion::IntoValue;
|
||||
use crate::{Error, Result};
|
||||
|
|
@ -26,6 +27,7 @@ use layer_shika_domain::prelude::{
|
|||
use layer_shika_domain::value_objects::handle::SurfaceHandle;
|
||||
use layer_shika_domain::value_objects::output_handle::OutputHandle;
|
||||
use layer_shika_domain::value_objects::output_info::OutputInfo;
|
||||
use layer_shika_domain::value_objects::surface_instance_id::SurfaceInstanceId;
|
||||
use spin_on::spin_on;
|
||||
use std::cell::RefCell;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
@ -379,7 +381,7 @@ impl Shell {
|
|||
})?;
|
||||
Self::new_single_window(compilation_result, definition)
|
||||
} else {
|
||||
Self::new_multi_window(compilation_result, definitions)
|
||||
Self::new_multi_window(compilation_result, &definitions)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -398,7 +400,9 @@ impl Shell {
|
|||
})
|
||||
})?;
|
||||
|
||||
let handle = SurfaceHandle::new();
|
||||
let wayland_config = WaylandSurfaceConfig::from_domain_config(
|
||||
handle,
|
||||
&definition.component,
|
||||
component_definition,
|
||||
Some(Rc::clone(&compilation_result)),
|
||||
|
|
@ -411,7 +415,6 @@ impl Shell {
|
|||
let (sender, receiver) = channel::channel();
|
||||
|
||||
let mut registry = SurfaceRegistry::new();
|
||||
let handle = SurfaceHandle::new();
|
||||
let entry = SurfaceEntry::new(handle, definition.component.clone(), definition);
|
||||
registry.insert(entry)?;
|
||||
|
||||
|
|
@ -433,9 +436,9 @@ impl Shell {
|
|||
|
||||
fn new_multi_window(
|
||||
compilation_result: Rc<CompilationResult>,
|
||||
definitions: Vec<SurfaceDefinition>,
|
||||
definitions: &[SurfaceDefinition],
|
||||
) -> Result<Self> {
|
||||
let shell_configs: Vec<ShellSurfaceConfig> = definitions
|
||||
let shell_configs_with_handles: Vec<(SurfaceHandle, ShellSurfaceConfig)> = definitions
|
||||
.iter()
|
||||
.map(|def| {
|
||||
let component_definition = compilation_result
|
||||
|
|
@ -449,29 +452,39 @@ impl Shell {
|
|||
})
|
||||
})?;
|
||||
|
||||
let handle = SurfaceHandle::new();
|
||||
let wayland_config = WaylandSurfaceConfig::from_domain_config(
|
||||
handle,
|
||||
&def.component,
|
||||
component_definition,
|
||||
Some(Rc::clone(&compilation_result)),
|
||||
def.config.clone(),
|
||||
);
|
||||
|
||||
Ok(ShellSurfaceConfig {
|
||||
name: def.component.clone(),
|
||||
config: wayland_config,
|
||||
})
|
||||
Ok((
|
||||
handle,
|
||||
ShellSurfaceConfig {
|
||||
name: def.component.clone(),
|
||||
config: wayland_config,
|
||||
},
|
||||
))
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
let shell_configs: Vec<ShellSurfaceConfig> = shell_configs_with_handles
|
||||
.iter()
|
||||
.map(|(_, cfg)| cfg.clone())
|
||||
.collect();
|
||||
|
||||
let inner = layer_shika_adapters::WaylandShellSystem::new_multi(&shell_configs)?;
|
||||
let inner_rc: Rc<RefCell<dyn WaylandSystemOps>> = Rc::new(RefCell::new(inner));
|
||||
|
||||
let (sender, receiver) = channel::channel();
|
||||
|
||||
let mut registry = SurfaceRegistry::new();
|
||||
for definition in definitions {
|
||||
let handle = SurfaceHandle::new();
|
||||
let entry = SurfaceEntry::new(handle, definition.component.clone(), definition);
|
||||
for ((handle, _), definition) in shell_configs_with_handles.iter().zip(definitions.iter()) {
|
||||
let entry =
|
||||
SurfaceEntry::new(*handle, definition.component.clone(), definition.clone());
|
||||
registry.insert(entry)?;
|
||||
}
|
||||
|
||||
|
|
@ -558,100 +571,152 @@ impl Shell {
|
|||
}
|
||||
}
|
||||
|
||||
fn resolve_surface_target<'a>(
|
||||
ctx: &'a mut EventDispatchContext<'_>,
|
||||
target: &SurfaceTarget,
|
||||
) -> Vec<&'a mut SurfaceState> {
|
||||
match target {
|
||||
SurfaceTarget::ByInstance(id) => {
|
||||
if let Some(surface) = ctx.surface_by_instance_mut(id.surface(), id.output()) {
|
||||
vec![surface]
|
||||
} else {
|
||||
log::warn!(
|
||||
"Surface instance not found: handle {:?} on output {:?}",
|
||||
id.surface(),
|
||||
id.output()
|
||||
);
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
SurfaceTarget::ByHandle(handle) => ctx.surfaces_by_handle_mut(*handle),
|
||||
SurfaceTarget::ByName(name) => ctx.surfaces_by_name_mut(name),
|
||||
SurfaceTarget::ByNameAndOutput { name, output } => {
|
||||
ctx.surfaces_by_name_and_output_mut(name, *output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_surface_resize(
|
||||
ctx: &mut EventDispatchContext<'_>,
|
||||
target: &SurfaceTarget,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) {
|
||||
log::debug!(
|
||||
"Surface command: Resize {:?} to {}x{}",
|
||||
target,
|
||||
width,
|
||||
height
|
||||
);
|
||||
for surface in Self::resolve_surface_target(ctx, target) {
|
||||
let handle = LayerSurfaceHandle::from_window_state(surface);
|
||||
handle.set_size(width, height);
|
||||
handle.commit();
|
||||
surface.update_size_with_compositor_logic(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_surface_config_change<F>(
|
||||
ctx: &mut EventDispatchContext<'_>,
|
||||
target: &SurfaceTarget,
|
||||
operation: &str,
|
||||
apply: F,
|
||||
) where
|
||||
F: Fn(&LayerSurfaceHandle<'_>),
|
||||
{
|
||||
log::debug!("Surface command: {} {:?}", operation, target);
|
||||
for surface in Self::resolve_surface_target(ctx, target) {
|
||||
let handle = LayerSurfaceHandle::from_window_state(surface);
|
||||
apply(&handle);
|
||||
handle.commit();
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_full_config(
|
||||
ctx: &mut EventDispatchContext<'_>,
|
||||
target: &SurfaceTarget,
|
||||
config: &SurfaceConfig,
|
||||
) {
|
||||
log::debug!("Surface command: ApplyConfig {:?}", target);
|
||||
for surface in Self::resolve_surface_target(ctx, target) {
|
||||
let handle = LayerSurfaceHandle::from_window_state(surface);
|
||||
|
||||
handle.set_size(config.dimensions.width(), config.dimensions.height());
|
||||
handle.set_anchor_edges(config.anchor);
|
||||
handle.set_exclusive_zone(config.exclusive_zone);
|
||||
handle.set_margins(config.margin);
|
||||
handle.set_layer(config.layer);
|
||||
handle.set_keyboard_interactivity(config.keyboard_interactivity);
|
||||
handle.commit();
|
||||
|
||||
surface.update_size_with_compositor_logic(
|
||||
config.dimensions.width(),
|
||||
config.dimensions.height(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_surface_command(command: SurfaceCommand, ctx: &mut EventDispatchContext<'_>) {
|
||||
match command {
|
||||
SurfaceCommand::Resize {
|
||||
name,
|
||||
target,
|
||||
width,
|
||||
height,
|
||||
} => {
|
||||
log::debug!("Surface command: Resize '{}' to {}x{}", name, width, height);
|
||||
for surface in ctx.surfaces_by_name_mut(&name) {
|
||||
let handle = LayerSurfaceHandle::from_window_state(surface);
|
||||
handle.set_size(width, height);
|
||||
handle.commit();
|
||||
|
||||
surface.update_size_with_compositor_logic(width, height);
|
||||
}
|
||||
Self::apply_surface_resize(ctx, &target, width, height);
|
||||
}
|
||||
SurfaceCommand::SetAnchor { name, anchor } => {
|
||||
log::debug!("Surface command: SetAnchor '{}' to {:?}", name, anchor);
|
||||
for surface in ctx.surfaces_by_name(&name) {
|
||||
let handle = LayerSurfaceHandle::from_window_state(surface);
|
||||
SurfaceCommand::SetAnchor { target, anchor } => {
|
||||
Self::apply_surface_config_change(ctx, &target, "SetAnchor", |handle| {
|
||||
handle.set_anchor_edges(anchor);
|
||||
handle.commit();
|
||||
}
|
||||
});
|
||||
}
|
||||
SurfaceCommand::SetExclusiveZone { name, zone } => {
|
||||
log::debug!("Surface command: SetExclusiveZone '{}' to {}", name, zone);
|
||||
for surface in ctx.surfaces_by_name(&name) {
|
||||
let handle = LayerSurfaceHandle::from_window_state(surface);
|
||||
SurfaceCommand::SetExclusiveZone { target, zone } => {
|
||||
Self::apply_surface_config_change(ctx, &target, "SetExclusiveZone", |handle| {
|
||||
handle.set_exclusive_zone(zone);
|
||||
handle.commit();
|
||||
}
|
||||
});
|
||||
}
|
||||
SurfaceCommand::SetMargins { name, margins } => {
|
||||
log::debug!("Surface command: SetMargins '{}' to {:?}", name, margins);
|
||||
for surface in ctx.surfaces_by_name(&name) {
|
||||
let handle = LayerSurfaceHandle::from_window_state(surface);
|
||||
SurfaceCommand::SetMargins { target, margins } => {
|
||||
Self::apply_surface_config_change(ctx, &target, "SetMargins", |handle| {
|
||||
handle.set_margins(margins);
|
||||
handle.commit();
|
||||
}
|
||||
});
|
||||
}
|
||||
SurfaceCommand::SetLayer { name, layer } => {
|
||||
log::debug!("Surface command: SetLayer '{}' to {:?}", name, layer);
|
||||
for surface in ctx.surfaces_by_name(&name) {
|
||||
let handle = LayerSurfaceHandle::from_window_state(surface);
|
||||
SurfaceCommand::SetLayer { target, layer } => {
|
||||
Self::apply_surface_config_change(ctx, &target, "SetLayer", |handle| {
|
||||
handle.set_layer(layer);
|
||||
handle.commit();
|
||||
}
|
||||
});
|
||||
}
|
||||
SurfaceCommand::SetKeyboardInteractivity { name, mode } => {
|
||||
log::debug!(
|
||||
"Surface command: SetKeyboardInteractivity '{}' to {:?}",
|
||||
name,
|
||||
mode
|
||||
SurfaceCommand::SetKeyboardInteractivity { target, mode } => {
|
||||
Self::apply_surface_config_change(
|
||||
ctx,
|
||||
&target,
|
||||
"SetKeyboardInteractivity",
|
||||
|handle| {
|
||||
handle.set_keyboard_interactivity(mode);
|
||||
},
|
||||
);
|
||||
for surface in ctx.surfaces_by_name(&name) {
|
||||
let handle = LayerSurfaceHandle::from_window_state(surface);
|
||||
handle.set_keyboard_interactivity(mode);
|
||||
handle.commit();
|
||||
}
|
||||
}
|
||||
SurfaceCommand::SetOutputPolicy { name, policy } => {
|
||||
SurfaceCommand::SetOutputPolicy { target, policy } => {
|
||||
log::debug!(
|
||||
"Surface command: SetOutputPolicy '{}' to {:?}",
|
||||
name,
|
||||
"Surface command: SetOutputPolicy {:?} to {:?}",
|
||||
target,
|
||||
policy
|
||||
);
|
||||
log::warn!(
|
||||
"SetOutputPolicy is not yet implemented - requires runtime surface spawning"
|
||||
);
|
||||
}
|
||||
SurfaceCommand::SetScaleFactor { name, factor } => {
|
||||
log::debug!("Surface command: SetScaleFactor '{}' to {:?}", name, factor);
|
||||
SurfaceCommand::SetScaleFactor { target, factor } => {
|
||||
log::debug!(
|
||||
"Surface command: SetScaleFactor {:?} to {:?}",
|
||||
target,
|
||||
factor
|
||||
);
|
||||
log::warn!(
|
||||
"SetScaleFactor is not yet implemented - requires runtime surface property updates"
|
||||
);
|
||||
}
|
||||
SurfaceCommand::ApplyConfig { name, config } => {
|
||||
log::debug!("Surface command: ApplyConfig '{}'", name);
|
||||
for surface in ctx.surfaces_by_name_mut(&name) {
|
||||
let handle = LayerSurfaceHandle::from_window_state(surface);
|
||||
|
||||
handle.set_size(config.dimensions.width(), config.dimensions.height());
|
||||
handle.set_anchor_edges(config.anchor);
|
||||
handle.set_exclusive_zone(config.exclusive_zone);
|
||||
handle.set_margins(config.margin);
|
||||
handle.set_layer(config.layer);
|
||||
handle.set_keyboard_interactivity(config.keyboard_interactivity);
|
||||
handle.commit();
|
||||
|
||||
surface.update_size_with_compositor_logic(
|
||||
config.dimensions.width(),
|
||||
config.dimensions.height(),
|
||||
);
|
||||
}
|
||||
SurfaceCommand::ApplyConfig { target, config } => {
|
||||
Self::apply_full_config(ctx, &target, &config);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -699,7 +764,9 @@ impl Shell {
|
|||
})
|
||||
})?;
|
||||
|
||||
let handle = SurfaceHandle::new();
|
||||
let wayland_config = WaylandSurfaceConfig::from_domain_config(
|
||||
handle,
|
||||
&definition.component,
|
||||
component_definition,
|
||||
Some(Rc::clone(&self.compilation_result)),
|
||||
|
|
@ -789,7 +856,7 @@ impl Shell {
|
|||
system
|
||||
.app_state()
|
||||
.surfaces_by_name(name)
|
||||
.next()
|
||||
.first()
|
||||
.map(|surface| f(surface.component_instance()))
|
||||
.ok_or_else(|| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
|
|
@ -877,7 +944,7 @@ impl Shell {
|
|||
callback_name: &str,
|
||||
handler: F,
|
||||
) where
|
||||
F: Fn(ShellControl) -> R + Clone + 'static,
|
||||
F: Fn(CallbackContext) -> R + Clone + 'static,
|
||||
R: IntoValue,
|
||||
{
|
||||
let control = self.control();
|
||||
|
|
@ -886,26 +953,44 @@ impl Shell {
|
|||
let (primary, active) = self.get_output_handles();
|
||||
|
||||
for (key, surface) in system.app_state().surfaces_with_keys() {
|
||||
let surface_handle = key.surface_handle;
|
||||
let output_handle = key.output_handle;
|
||||
|
||||
let surface_name = self.registry.by_handle(surface_handle).map_or_else(
|
||||
|| format!("Unknown-{}", surface_handle.id()),
|
||||
|entry| entry.name.clone(),
|
||||
);
|
||||
|
||||
let surface_info = crate::SurfaceInfo {
|
||||
name: key.surface_name.clone(),
|
||||
output: key.output_handle,
|
||||
name: surface_name.clone(),
|
||||
output: output_handle,
|
||||
};
|
||||
|
||||
let output_info = system.app_state().get_output_info(key.output_handle);
|
||||
let output_info = system.app_state().get_output_info(output_handle);
|
||||
|
||||
if selector.matches(&surface_info, output_info, primary, active) {
|
||||
let instance_id = SurfaceInstanceId::new(surface_handle, output_handle);
|
||||
|
||||
let handler_rc = Rc::clone(&handler);
|
||||
let control_clone = control.clone();
|
||||
if let Err(e) = surface
|
||||
.component_instance()
|
||||
.set_callback(callback_name, move |_args| {
|
||||
handler_rc(control_clone.clone()).into_value()
|
||||
})
|
||||
let surface_name_clone = surface_name.clone();
|
||||
|
||||
if let Err(e) =
|
||||
surface
|
||||
.component_instance()
|
||||
.set_callback(callback_name, move |_args| {
|
||||
let ctx = CallbackContext::new(
|
||||
instance_id,
|
||||
surface_name_clone.clone(),
|
||||
control_clone.clone(),
|
||||
);
|
||||
handler_rc(ctx).into_value()
|
||||
})
|
||||
{
|
||||
log::error!(
|
||||
"Failed to register callback '{}' on surface '{}': {}",
|
||||
callback_name,
|
||||
key.surface_name,
|
||||
surface_name,
|
||||
e
|
||||
);
|
||||
}
|
||||
|
|
@ -919,7 +1004,7 @@ impl Shell {
|
|||
callback_name: &str,
|
||||
handler: F,
|
||||
) where
|
||||
F: Fn(&[Value], ShellControl) -> R + Clone + 'static,
|
||||
F: Fn(&[Value], CallbackContext) -> R + Clone + 'static,
|
||||
R: IntoValue,
|
||||
{
|
||||
let control = self.control();
|
||||
|
|
@ -928,26 +1013,44 @@ impl Shell {
|
|||
let (primary, active) = self.get_output_handles();
|
||||
|
||||
for (key, surface) in system.app_state().surfaces_with_keys() {
|
||||
let surface_handle = key.surface_handle;
|
||||
let output_handle = key.output_handle;
|
||||
|
||||
let surface_name = self.registry.by_handle(surface_handle).map_or_else(
|
||||
|| format!("Unknown-{}", surface_handle.id()),
|
||||
|entry| entry.name.clone(),
|
||||
);
|
||||
|
||||
let surface_info = crate::SurfaceInfo {
|
||||
name: key.surface_name.clone(),
|
||||
output: key.output_handle,
|
||||
name: surface_name.clone(),
|
||||
output: output_handle,
|
||||
};
|
||||
|
||||
let output_info = system.app_state().get_output_info(key.output_handle);
|
||||
let output_info = system.app_state().get_output_info(output_handle);
|
||||
|
||||
if selector.matches(&surface_info, output_info, primary, active) {
|
||||
let instance_id = SurfaceInstanceId::new(surface_handle, output_handle);
|
||||
|
||||
let handler_rc = Rc::clone(&handler);
|
||||
let control_clone = control.clone();
|
||||
if let Err(e) = surface
|
||||
.component_instance()
|
||||
.set_callback(callback_name, move |args| {
|
||||
handler_rc(args, control_clone.clone()).into_value()
|
||||
})
|
||||
let surface_name_clone = surface_name.clone();
|
||||
|
||||
if let Err(e) =
|
||||
surface
|
||||
.component_instance()
|
||||
.set_callback(callback_name, move |args| {
|
||||
let ctx = CallbackContext::new(
|
||||
instance_id,
|
||||
surface_name_clone.clone(),
|
||||
control_clone.clone(),
|
||||
);
|
||||
handler_rc(args, ctx).into_value()
|
||||
})
|
||||
{
|
||||
log::error!(
|
||||
"Failed to register callback '{}' on surface '{}': {}",
|
||||
callback_name,
|
||||
key.surface_name,
|
||||
surface_name,
|
||||
e
|
||||
);
|
||||
}
|
||||
|
|
@ -963,15 +1066,19 @@ impl Shell {
|
|||
let (primary, active) = self.get_output_handles();
|
||||
|
||||
for (key, surface) in system.app_state().surfaces_with_keys() {
|
||||
let surface_name = system
|
||||
.app_state()
|
||||
.get_surface_name(key.surface_handle)
|
||||
.unwrap_or("unknown");
|
||||
let surface_info = crate::SurfaceInfo {
|
||||
name: key.surface_name.clone(),
|
||||
name: surface_name.to_string(),
|
||||
output: key.output_handle,
|
||||
};
|
||||
|
||||
let output_info = system.app_state().get_output_info(key.output_handle);
|
||||
|
||||
if selector.matches(&surface_info, output_info, primary, active) {
|
||||
f(&key.surface_name, surface.component_instance());
|
||||
f(surface_name, surface.component_instance());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -984,8 +1091,12 @@ impl Shell {
|
|||
let (primary, active) = self.get_output_handles();
|
||||
|
||||
for (key, surface) in system.app_state().surfaces_with_keys() {
|
||||
let surface_name = system
|
||||
.app_state()
|
||||
.get_surface_name(key.surface_handle)
|
||||
.unwrap_or("unknown");
|
||||
let surface_info = crate::SurfaceInfo {
|
||||
name: key.surface_name.clone(),
|
||||
name: surface_name.to_string(),
|
||||
output: key.output_handle,
|
||||
};
|
||||
|
||||
|
|
@ -1006,8 +1117,12 @@ impl Shell {
|
|||
.app_state()
|
||||
.surfaces_with_keys()
|
||||
.filter(|(key, _)| {
|
||||
let surface_name = system
|
||||
.app_state()
|
||||
.get_surface_name(key.surface_handle)
|
||||
.unwrap_or("unknown");
|
||||
let surface_info = crate::SurfaceInfo {
|
||||
name: key.surface_name.clone(),
|
||||
name: surface_name.to_string(),
|
||||
output: key.output_handle,
|
||||
};
|
||||
|
||||
|
|
@ -1026,8 +1141,12 @@ impl Shell {
|
|||
.app_state()
|
||||
.surfaces_with_keys()
|
||||
.filter_map(|(key, _)| {
|
||||
let surface_name = system
|
||||
.app_state()
|
||||
.get_surface_name(key.surface_handle)
|
||||
.unwrap_or("unknown");
|
||||
let surface_info = crate::SurfaceInfo {
|
||||
name: key.surface_name.clone(),
|
||||
name: surface_name.to_string(),
|
||||
output: key.output_handle,
|
||||
};
|
||||
|
||||
|
|
@ -1100,8 +1219,8 @@ impl ShellEventContext<'_> {
|
|||
pub fn get_surface_component(&self, name: &str) -> Option<&ComponentInstance> {
|
||||
self.app_state
|
||||
.surfaces_by_name(name)
|
||||
.next()
|
||||
.map(SurfaceState::component_instance)
|
||||
.first()
|
||||
.map(|s| s.component_instance())
|
||||
}
|
||||
|
||||
pub fn all_surface_components(&self) -> impl Iterator<Item = &ComponentInstance> {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use crate::{Error, Result};
|
||||
use crate::Result;
|
||||
use layer_shika_adapters::platform::slint_interpreter::ComponentInstance;
|
||||
use layer_shika_domain::config::SurfaceConfig;
|
||||
use layer_shika_domain::errors::DomainError;
|
||||
use layer_shika_domain::value_objects::handle::SurfaceHandle;
|
||||
use layer_shika_domain::value_objects::output_handle::OutputHandle;
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -63,7 +62,7 @@ impl SurfaceEntry {
|
|||
|
||||
pub struct SurfaceRegistry {
|
||||
entries: HashMap<SurfaceHandle, SurfaceEntry>,
|
||||
by_name: HashMap<String, SurfaceHandle>,
|
||||
by_name: HashMap<String, Vec<SurfaceHandle>>,
|
||||
by_component: HashMap<String, Vec<SurfaceHandle>>,
|
||||
next_spawn_order: usize,
|
||||
}
|
||||
|
|
@ -79,12 +78,6 @@ impl SurfaceRegistry {
|
|||
}
|
||||
|
||||
pub fn insert(&mut self, mut entry: SurfaceEntry) -> Result<()> {
|
||||
if self.by_name.contains_key(&entry.name) {
|
||||
return Err(Error::Domain(DomainError::Configuration {
|
||||
message: format!("Surface with name '{}' already exists", entry.name),
|
||||
}));
|
||||
}
|
||||
|
||||
entry.metadata.spawn_order = self.next_spawn_order;
|
||||
self.next_spawn_order += 1;
|
||||
|
||||
|
|
@ -92,7 +85,7 @@ impl SurfaceRegistry {
|
|||
let name = entry.name.clone();
|
||||
let component = entry.component.clone();
|
||||
|
||||
self.by_name.insert(name, handle);
|
||||
self.by_name.entry(name).or_default().push(handle);
|
||||
|
||||
self.by_component.entry(component).or_default().push(handle);
|
||||
|
||||
|
|
@ -104,7 +97,12 @@ impl SurfaceRegistry {
|
|||
pub fn remove(&mut self, handle: SurfaceHandle) -> Option<SurfaceEntry> {
|
||||
let entry = self.entries.remove(&handle)?;
|
||||
|
||||
self.by_name.remove(&entry.name);
|
||||
if let Some(handles) = self.by_name.get_mut(&entry.name) {
|
||||
handles.retain(|&h| h != handle);
|
||||
if handles.is_empty() {
|
||||
self.by_name.remove(&entry.name);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(handles) = self.by_component.get_mut(&entry.component) {
|
||||
handles.retain(|&h| h != handle);
|
||||
|
|
@ -120,20 +118,44 @@ impl SurfaceRegistry {
|
|||
self.entries.get(&handle)
|
||||
}
|
||||
|
||||
pub fn by_handle(&self, handle: SurfaceHandle) -> Option<&SurfaceEntry> {
|
||||
self.entries.get(&handle)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, handle: SurfaceHandle) -> Option<&mut SurfaceEntry> {
|
||||
self.entries.get_mut(&handle)
|
||||
}
|
||||
|
||||
pub fn by_name(&self, name: &str) -> Option<&SurfaceEntry> {
|
||||
self.by_name.get(name).and_then(|h| self.entries.get(h))
|
||||
pub fn by_handle_mut(&mut self, handle: SurfaceHandle) -> Option<&mut SurfaceEntry> {
|
||||
self.entries.get_mut(&handle)
|
||||
}
|
||||
|
||||
pub fn by_name_mut(&mut self, name: &str) -> Option<&mut SurfaceEntry> {
|
||||
self.by_name.get(name).and_then(|h| self.entries.get_mut(h))
|
||||
pub fn by_name(&self, name: &str) -> Vec<&SurfaceEntry> {
|
||||
self.by_name
|
||||
.get(name)
|
||||
.map(|handles| handles.iter().filter_map(|h| self.entries.get(h)).collect())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn by_name_mut(&mut self, name: &str) -> Vec<&mut SurfaceEntry> {
|
||||
let handles: Vec<SurfaceHandle> = self.by_name.get(name).cloned().unwrap_or_default();
|
||||
|
||||
let entries_ptr = std::ptr::addr_of_mut!(self.entries);
|
||||
|
||||
handles
|
||||
.iter()
|
||||
.filter_map(|h| unsafe { (*entries_ptr).get_mut(h) })
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn handle_by_name(&self, name: &str) -> Option<SurfaceHandle> {
|
||||
self.by_name.get(name).copied()
|
||||
self.by_name
|
||||
.get(name)
|
||||
.and_then(|handles| handles.first().copied())
|
||||
}
|
||||
|
||||
pub fn handles_by_name(&self, name: &str) -> Vec<SurfaceHandle> {
|
||||
self.by_name.get(name).cloned().unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn name_by_handle(&self, handle: SurfaceHandle) -> Option<&str> {
|
||||
|
|
|
|||
|
|
@ -14,12 +14,14 @@ use layer_shika_domain::prelude::{
|
|||
AnchorEdges, KeyboardInteractivity, Layer, Margins, OutputPolicy, ScaleFactor,
|
||||
};
|
||||
use layer_shika_domain::value_objects::dimensions::{PopupDimensions, SurfaceDimension};
|
||||
use layer_shika_domain::value_objects::handle::SurfaceHandle;
|
||||
use layer_shika_domain::value_objects::output_handle::OutputHandle;
|
||||
use layer_shika_domain::value_objects::output_info::OutputInfo;
|
||||
use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode;
|
||||
use layer_shika_domain::value_objects::popup_request::{
|
||||
PopupHandle, PopupPlacement, PopupRequest, PopupSize,
|
||||
};
|
||||
use layer_shika_domain::value_objects::surface_instance_id::SurfaceInstanceId;
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
|
||||
|
|
@ -33,42 +35,50 @@ pub enum PopupCommand {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SurfaceTarget {
|
||||
ByInstance(SurfaceInstanceId),
|
||||
ByHandle(SurfaceHandle),
|
||||
ByName(String),
|
||||
ByNameAndOutput { name: String, output: OutputHandle },
|
||||
}
|
||||
|
||||
pub enum SurfaceCommand {
|
||||
Resize {
|
||||
name: String,
|
||||
target: SurfaceTarget,
|
||||
width: u32,
|
||||
height: u32,
|
||||
},
|
||||
SetAnchor {
|
||||
name: String,
|
||||
target: SurfaceTarget,
|
||||
anchor: AnchorEdges,
|
||||
},
|
||||
SetExclusiveZone {
|
||||
name: String,
|
||||
target: SurfaceTarget,
|
||||
zone: i32,
|
||||
},
|
||||
SetMargins {
|
||||
name: String,
|
||||
target: SurfaceTarget,
|
||||
margins: Margins,
|
||||
},
|
||||
SetLayer {
|
||||
name: String,
|
||||
target: SurfaceTarget,
|
||||
layer: Layer,
|
||||
},
|
||||
SetOutputPolicy {
|
||||
name: String,
|
||||
target: SurfaceTarget,
|
||||
policy: OutputPolicy,
|
||||
},
|
||||
SetScaleFactor {
|
||||
name: String,
|
||||
target: SurfaceTarget,
|
||||
factor: ScaleFactor,
|
||||
},
|
||||
SetKeyboardInteractivity {
|
||||
name: String,
|
||||
target: SurfaceTarget,
|
||||
mode: KeyboardInteractivity,
|
||||
},
|
||||
ApplyConfig {
|
||||
name: String,
|
||||
target: SurfaceTarget,
|
||||
config: SurfaceConfig,
|
||||
},
|
||||
}
|
||||
|
|
@ -79,6 +89,63 @@ pub enum ShellCommand {
|
|||
Render,
|
||||
}
|
||||
|
||||
pub struct CallbackContext {
|
||||
instance_id: SurfaceInstanceId,
|
||||
surface_name: String,
|
||||
control: ShellControl,
|
||||
}
|
||||
|
||||
impl CallbackContext {
|
||||
pub fn new(
|
||||
instance_id: SurfaceInstanceId,
|
||||
surface_name: String,
|
||||
control: ShellControl,
|
||||
) -> Self {
|
||||
Self {
|
||||
instance_id,
|
||||
surface_name,
|
||||
control,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn instance_id(&self) -> &SurfaceInstanceId {
|
||||
&self.instance_id
|
||||
}
|
||||
|
||||
pub const fn surface_handle(&self) -> SurfaceHandle {
|
||||
self.instance_id.surface()
|
||||
}
|
||||
|
||||
pub const fn output_handle(&self) -> OutputHandle {
|
||||
self.instance_id.output()
|
||||
}
|
||||
|
||||
pub fn surface_name(&self) -> &str {
|
||||
&self.surface_name
|
||||
}
|
||||
|
||||
pub const fn control(&self) -> &ShellControl {
|
||||
&self.control
|
||||
}
|
||||
|
||||
pub fn this_instance(&self) -> SurfaceControlHandle {
|
||||
self.control.surface_instance(&self.instance_id)
|
||||
}
|
||||
|
||||
pub fn all_surface_instances(&self) -> SurfaceControlHandle {
|
||||
self.control.surface_by_handle(self.surface_handle())
|
||||
}
|
||||
|
||||
pub fn all_named(&self) -> SurfaceControlHandle {
|
||||
self.control.surface_by_name(&self.surface_name)
|
||||
}
|
||||
|
||||
pub fn all_named_on_this_output(&self) -> SurfaceControlHandle {
|
||||
self.control
|
||||
.surface_by_name_and_output(&self.surface_name, self.output_handle())
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle for runtime control of shell operations
|
||||
///
|
||||
/// Provides methods to manipulate surfaces, show popups, and request redraws.
|
||||
|
|
@ -162,12 +229,44 @@ impl ShellControl {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn surface(&self, name: impl Into<String>) -> SurfaceControlHandle {
|
||||
pub fn surface_instance(&self, id: &SurfaceInstanceId) -> SurfaceControlHandle {
|
||||
SurfaceControlHandle {
|
||||
name: name.into(),
|
||||
target: SurfaceTarget::ByInstance(*id),
|
||||
sender: self.sender.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn surface_by_handle(&self, handle: SurfaceHandle) -> SurfaceControlHandle {
|
||||
SurfaceControlHandle {
|
||||
target: SurfaceTarget::ByHandle(handle),
|
||||
sender: self.sender.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn surface_by_name(&self, name: impl Into<String>) -> SurfaceControlHandle {
|
||||
SurfaceControlHandle {
|
||||
target: SurfaceTarget::ByName(name.into()),
|
||||
sender: self.sender.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn surface_by_name_and_output(
|
||||
&self,
|
||||
name: impl Into<String>,
|
||||
output: OutputHandle,
|
||||
) -> SurfaceControlHandle {
|
||||
SurfaceControlHandle {
|
||||
target: SurfaceTarget::ByNameAndOutput {
|
||||
name: name.into(),
|
||||
output,
|
||||
},
|
||||
sender: self.sender.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn surface(&self, name: impl Into<String>) -> SurfaceControlHandle {
|
||||
self.surface_by_name(name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle for runtime control of a specific surface
|
||||
|
|
@ -175,7 +274,7 @@ impl ShellControl {
|
|||
/// Allows modifying surface properties like size, anchor, layer, and margins at runtime.
|
||||
/// Obtained via `ShellControl::surface()`.
|
||||
pub struct SurfaceControlHandle {
|
||||
name: String,
|
||||
target: SurfaceTarget,
|
||||
sender: channel::Sender<ShellCommand>,
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +282,7 @@ impl SurfaceControlHandle {
|
|||
pub fn resize(&self, width: u32, height: u32) -> Result<()> {
|
||||
self.sender
|
||||
.send(ShellCommand::Surface(SurfaceCommand::Resize {
|
||||
name: self.name.clone(),
|
||||
target: self.target.clone(),
|
||||
width,
|
||||
height,
|
||||
}))
|
||||
|
|
@ -205,7 +304,7 @@ impl SurfaceControlHandle {
|
|||
pub fn set_anchor(&self, anchor: AnchorEdges) -> Result<()> {
|
||||
self.sender
|
||||
.send(ShellCommand::Surface(SurfaceCommand::SetAnchor {
|
||||
name: self.name.clone(),
|
||||
target: self.target.clone(),
|
||||
anchor,
|
||||
}))
|
||||
.map_err(|_| {
|
||||
|
|
@ -219,7 +318,7 @@ impl SurfaceControlHandle {
|
|||
pub fn set_exclusive_zone(&self, zone: i32) -> Result<()> {
|
||||
self.sender
|
||||
.send(ShellCommand::Surface(SurfaceCommand::SetExclusiveZone {
|
||||
name: self.name.clone(),
|
||||
target: self.target.clone(),
|
||||
zone,
|
||||
}))
|
||||
.map_err(|_| {
|
||||
|
|
@ -233,7 +332,7 @@ impl SurfaceControlHandle {
|
|||
pub fn set_margins(&self, margins: impl Into<Margins>) -> Result<()> {
|
||||
self.sender
|
||||
.send(ShellCommand::Surface(SurfaceCommand::SetMargins {
|
||||
name: self.name.clone(),
|
||||
target: self.target.clone(),
|
||||
margins: margins.into(),
|
||||
}))
|
||||
.map_err(|_| {
|
||||
|
|
@ -247,7 +346,7 @@ impl SurfaceControlHandle {
|
|||
pub fn set_layer(&self, layer: Layer) -> Result<()> {
|
||||
self.sender
|
||||
.send(ShellCommand::Surface(SurfaceCommand::SetLayer {
|
||||
name: self.name.clone(),
|
||||
target: self.target.clone(),
|
||||
layer,
|
||||
}))
|
||||
.map_err(|_| {
|
||||
|
|
@ -260,7 +359,7 @@ impl SurfaceControlHandle {
|
|||
pub fn set_output_policy(&self, policy: OutputPolicy) -> Result<()> {
|
||||
self.sender
|
||||
.send(ShellCommand::Surface(SurfaceCommand::SetOutputPolicy {
|
||||
name: self.name.clone(),
|
||||
target: self.target.clone(),
|
||||
policy,
|
||||
}))
|
||||
.map_err(|_| {
|
||||
|
|
@ -274,7 +373,7 @@ impl SurfaceControlHandle {
|
|||
pub fn set_scale_factor(&self, factor: ScaleFactor) -> Result<()> {
|
||||
self.sender
|
||||
.send(ShellCommand::Surface(SurfaceCommand::SetScaleFactor {
|
||||
name: self.name.clone(),
|
||||
target: self.target.clone(),
|
||||
factor,
|
||||
}))
|
||||
.map_err(|_| {
|
||||
|
|
@ -289,7 +388,7 @@ impl SurfaceControlHandle {
|
|||
self.sender
|
||||
.send(ShellCommand::Surface(
|
||||
SurfaceCommand::SetKeyboardInteractivity {
|
||||
name: self.name.clone(),
|
||||
target: self.target.clone(),
|
||||
mode,
|
||||
},
|
||||
))
|
||||
|
|
@ -305,7 +404,7 @@ impl SurfaceControlHandle {
|
|||
pub fn apply_config(&self, config: SurfaceConfig) -> Result<()> {
|
||||
self.sender
|
||||
.send(ShellCommand::Surface(SurfaceCommand::ApplyConfig {
|
||||
name: self.name.clone(),
|
||||
target: self.target.clone(),
|
||||
config,
|
||||
}))
|
||||
.map_err(|_| {
|
||||
|
|
@ -428,15 +527,32 @@ fn extract_dimensions_from_callback(args: &[Value]) -> PopupDimensions {
|
|||
}
|
||||
|
||||
impl EventDispatchContext<'_> {
|
||||
pub(crate) fn surfaces_by_name(&self, name: &str) -> impl Iterator<Item = &SurfaceState> {
|
||||
self.app_state.surfaces_by_name(name)
|
||||
pub(crate) fn surface_by_instance_mut(
|
||||
&mut self,
|
||||
surface_handle: SurfaceHandle,
|
||||
output_handle: OutputHandle,
|
||||
) -> Option<&mut SurfaceState> {
|
||||
self.app_state
|
||||
.get_surface_by_instance_mut(surface_handle, output_handle)
|
||||
}
|
||||
|
||||
pub(crate) fn surfaces_by_name_mut(
|
||||
pub(crate) fn surfaces_by_handle_mut(
|
||||
&mut self,
|
||||
handle: SurfaceHandle,
|
||||
) -> Vec<&mut SurfaceState> {
|
||||
self.app_state.surfaces_by_handle_mut(handle)
|
||||
}
|
||||
|
||||
pub(crate) fn surfaces_by_name_mut(&mut self, name: &str) -> Vec<&mut SurfaceState> {
|
||||
self.app_state.surfaces_by_name_mut(name)
|
||||
}
|
||||
|
||||
pub(crate) fn surfaces_by_name_and_output_mut(
|
||||
&mut self,
|
||||
name: &str,
|
||||
) -> impl Iterator<Item = &mut SurfaceState> {
|
||||
self.app_state.surfaces_by_name_mut(name)
|
||||
output: OutputHandle,
|
||||
) -> Vec<&mut SurfaceState> {
|
||||
self.app_state.surfaces_by_name_and_output_mut(name, output)
|
||||
}
|
||||
|
||||
pub fn with_surface<F, R>(&self, name: &str, f: F) -> Result<R>
|
||||
|
|
@ -466,8 +582,8 @@ impl EventDispatchContext<'_> {
|
|||
fn get_surface_component(&self, name: &str) -> Option<&ComponentInstance> {
|
||||
self.app_state
|
||||
.surfaces_by_name(name)
|
||||
.next()
|
||||
.map(SurfaceState::component_instance)
|
||||
.first()
|
||||
.map(|s| s.component_instance())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
|
@ -895,15 +1011,12 @@ impl EventDispatchContext<'_> {
|
|||
where
|
||||
F: FnOnce(&ComponentInstance, LayerSurfaceHandle<'_>),
|
||||
{
|
||||
let surface = self
|
||||
.app_state
|
||||
.surfaces_by_name(name)
|
||||
.next()
|
||||
.ok_or_else(|| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!("Surface '{}' not found", name),
|
||||
})
|
||||
})?;
|
||||
let surfaces = self.app_state.surfaces_by_name(name);
|
||||
let surface = surfaces.first().ok_or_else(|| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!("Surface '{}' not found", name),
|
||||
})
|
||||
})?;
|
||||
|
||||
let handle = LayerSurfaceHandle::from_window_state(surface);
|
||||
let component = surface.component_instance();
|
||||
|
|
|
|||
|
|
@ -11,4 +11,5 @@ pub mod output_policy;
|
|||
pub mod popup_config;
|
||||
pub mod popup_positioning_mode;
|
||||
pub mod popup_request;
|
||||
pub mod surface_instance_id;
|
||||
pub mod ui_source;
|
||||
|
|
|
|||
24
crates/domain/src/value_objects/surface_instance_id.rs
Normal file
24
crates/domain/src/value_objects/surface_instance_id.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
use crate::value_objects::handle::{Handle, Surface};
|
||||
use crate::value_objects::output_handle::OutputHandle;
|
||||
|
||||
pub type SurfaceHandle = Handle<Surface>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct SurfaceInstanceId {
|
||||
surface: SurfaceHandle,
|
||||
output: OutputHandle,
|
||||
}
|
||||
|
||||
impl SurfaceInstanceId {
|
||||
pub const fn new(surface: SurfaceHandle, output: OutputHandle) -> Self {
|
||||
Self { surface, output }
|
||||
}
|
||||
|
||||
pub const fn surface(self) -> SurfaceHandle {
|
||||
self.surface
|
||||
}
|
||||
|
||||
pub const fn output(self) -> OutputHandle {
|
||||
self.output
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,20 @@
|
|||
# Runtime Surface Config Example
|
||||
|
||||
This example demonstrates layer-shika's runtime surface configuration capabilities using the Surface Control API.
|
||||
This example demonstrates layer-shika's runtime surface configuration capabilities using the instance-based Surface Control API.
|
||||
|
||||
## Features Demonstrated
|
||||
|
||||
1. **Dynamic Sizing**: Toggle between compact (32px) and expanded (64px) bar heights
|
||||
2. **Anchor Position Control**: Switch between top and bottom screen edges at runtime
|
||||
3. **Layer Management**: Cycle through Background, Bottom, Top, and Overlay layers
|
||||
4. **Channel-based UI Updates**: Use event loop channels to update UI state from callbacks
|
||||
5. **Surface Control API**: Manipulate surfaces via callback handlers
|
||||
4. **Instance-based Targeting**: Use `CallbackContext` to control the specific surface that invoked the callback
|
||||
5. **Slint State Management**: State lives in Slint component properties, Rust callbacks apply Wayland configuration
|
||||
|
||||
## Controls
|
||||
|
||||
- **Expand/Collapse Button**: Toggle between 32px and 64px bar heights
|
||||
- **Switch Anchor**: Toggle between top and bottom screen positions
|
||||
- **Switch Layer**: Cycle through Background → Bottom → Top → Overlay layers
|
||||
- **Switch Layer**: Cycle through Top → Overlay → Bottom → Background layers
|
||||
|
||||
## Running the Example
|
||||
|
||||
|
|
@ -24,50 +24,61 @@ cargo run -p runtime-surface-config
|
|||
|
||||
## Implementation Highlights
|
||||
|
||||
### Control from Slint Callbacks
|
||||
### Slint-managed State
|
||||
|
||||
```rust
|
||||
shell.on("Bar", "toggle-size", move |control| {
|
||||
control.surface("Bar")
|
||||
.configure()
|
||||
.size(0, new_size)
|
||||
.exclusive_zone(new_size as i32)
|
||||
.margins(0, 0, 0, 0)
|
||||
.apply()?;
|
||||
The Slint component owns all state via properties:
|
||||
|
||||
Value::Struct(Struct::from_iter([("expanded".into(), is_expanded.into())]))
|
||||
})?;
|
||||
```
|
||||
```slint
|
||||
export component Bar inherits Window {
|
||||
in-out property <bool> is-expanded: false;
|
||||
in-out property <string> current-anchor: "Top";
|
||||
in-out property <string> current-layer: "Top";
|
||||
|
||||
### Channel-based UI Updates
|
||||
callback toggle-size(bool);
|
||||
callback switch-anchor(string);
|
||||
callback switch-layer(string);
|
||||
|
||||
```rust
|
||||
let (_token, sender) = handle.add_channel(|message: UiUpdate, app_state| {
|
||||
for surface in app_state.all_outputs() {
|
||||
let component = surface.component_instance();
|
||||
match &message {
|
||||
UiUpdate::IsExpanded(is_expanded) => {
|
||||
component.set_property("is-expanded", (*is_expanded).into())?;
|
||||
}
|
||||
// ... other updates
|
||||
Button {
|
||||
text: is-expanded ? "Collapse" : "Expand";
|
||||
clicked => {
|
||||
root.is-expanded = !root.is-expanded;
|
||||
toggle-size(root.is-expanded);
|
||||
}
|
||||
}
|
||||
})?;
|
||||
}
|
||||
```
|
||||
|
||||
### Instance-based Control from Callbacks
|
||||
|
||||
Callbacks receive `CallbackContext` and use `this_instance()` to target only the specific surface:
|
||||
|
||||
```rust
|
||||
shell.select(Surface::named("Bar"))
|
||||
.on_callback_with_args("toggle-size", move |args, control| {
|
||||
let is_expanded = args.first()
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or(false);
|
||||
|
||||
let new_size = if is_expanded { 64 } else { 32 };
|
||||
|
||||
control.this_instance()
|
||||
.configure()
|
||||
.size(0, new_size)
|
||||
.exclusive_zone(new_size.try_into().unwrap_or(32))
|
||||
.apply()?;
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
## API Patterns
|
||||
|
||||
This example showcases the Surface Control API pattern:
|
||||
- Send configuration commands via `.configure()` builder or individual methods
|
||||
- Commands execute asynchronously in the event loop
|
||||
- Multiple surfaces with the same name can be controlled independently
|
||||
|
||||
**SurfaceControlHandle** (channel-based):
|
||||
**State Management**:
|
||||
|
||||
- Accessible via `control.surface(name)` in callback handlers
|
||||
- Safe to call from Slint callbacks
|
||||
- Commands execute asynchronously in event loop
|
||||
|
||||
**Event Loop Channels**:
|
||||
|
||||
- Use `add_channel` to create message handlers
|
||||
- Send messages from callbacks to update UI state
|
||||
- Process messages in event loop context
|
||||
- Access all surfaces via `app_state.all_outputs()`
|
||||
- Slint component properties hold all UI state
|
||||
- Slint logic computes next states (e.g., toggling, cycling)
|
||||
- Rust callbacks receive new values and apply Wayland configuration
|
||||
- No Rust-side state synchronization needed
|
||||
|
|
|
|||
|
|
@ -1,201 +1,115 @@
|
|||
use layer_shika::calloop::channel::Sender;
|
||||
use layer_shika::prelude::*;
|
||||
use layer_shika::slint::SharedString;
|
||||
use layer_shika::slint_interpreter::{Struct, Value};
|
||||
use std::cell::RefCell;
|
||||
use layer_shika::slint_interpreter::Value;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum UiUpdate {
|
||||
IsExpanded(bool),
|
||||
CurrentAnchor(String),
|
||||
CurrentLayer(String),
|
||||
}
|
||||
enum AnchorPosition {
|
||||
Top,
|
||||
Bottom,
|
||||
ToggleSize,
|
||||
SwitchAnchor,
|
||||
SwitchLayer,
|
||||
}
|
||||
|
||||
struct BarState {
|
||||
is_expanded: bool,
|
||||
current_anchor: AnchorPosition,
|
||||
current_layer: Layer,
|
||||
}
|
||||
|
||||
impl BarState {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
is_expanded: false,
|
||||
current_anchor: AnchorPosition::Top,
|
||||
current_layer: Layer::Top,
|
||||
}
|
||||
}
|
||||
|
||||
fn anchor_name(&self) -> &'static str {
|
||||
match self.current_anchor {
|
||||
AnchorPosition::Top => "Top",
|
||||
AnchorPosition::Bottom => "Bottom",
|
||||
}
|
||||
}
|
||||
|
||||
fn next_anchor(&mut self) {
|
||||
self.current_anchor = match self.current_anchor {
|
||||
AnchorPosition::Top => AnchorPosition::Bottom,
|
||||
AnchorPosition::Bottom => AnchorPosition::Top,
|
||||
};
|
||||
}
|
||||
|
||||
fn get_anchor_edges(&self) -> AnchorEdges {
|
||||
match self.current_anchor {
|
||||
AnchorPosition::Top => AnchorEdges::top_bar(),
|
||||
AnchorPosition::Bottom => AnchorEdges::bottom_bar(),
|
||||
}
|
||||
}
|
||||
|
||||
fn layer_name(&self) -> &'static str {
|
||||
match self.current_layer {
|
||||
Layer::Background => "Background",
|
||||
Layer::Bottom => "Bottom",
|
||||
Layer::Top => "Top",
|
||||
Layer::Overlay => "Overlay",
|
||||
}
|
||||
}
|
||||
|
||||
fn next_layer(&mut self) -> Layer {
|
||||
self.current_layer = match self.current_layer {
|
||||
Layer::Background => Layer::Bottom,
|
||||
Layer::Bottom => Layer::Top,
|
||||
Layer::Top => Layer::Overlay,
|
||||
Layer::Overlay => Layer::Background,
|
||||
};
|
||||
self.current_layer
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_toggle_size_callback(
|
||||
sender: &Rc<Sender<UiUpdate>>,
|
||||
shell: &Shell,
|
||||
state: &Rc<RefCell<BarState>>,
|
||||
) {
|
||||
let state_clone = Rc::clone(state);
|
||||
fn setup_toggle_size_callback(sender: &Rc<Sender<UiUpdate>>, shell: &Shell) {
|
||||
let sender_clone = Rc::clone(sender);
|
||||
shell
|
||||
.select(Surface::named("Bar"))
|
||||
.on_callback("toggle-size", move |control| {
|
||||
let is_expanded = {
|
||||
let mut st = state_clone.borrow_mut();
|
||||
st.is_expanded = !st.is_expanded;
|
||||
shell.select(Surface::named("Bar")).on_callback_with_args(
|
||||
"toggle-size",
|
||||
move |args, control| {
|
||||
let is_expanded = args
|
||||
.first()
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or(false);
|
||||
|
||||
let new_size = if st.is_expanded { 64 } else { 32 };
|
||||
let new_size = if is_expanded { 64 } else { 32 };
|
||||
let (width, height) = (0, new_size);
|
||||
|
||||
let (width, height) = match st.current_anchor {
|
||||
AnchorPosition::Top | AnchorPosition::Bottom => {
|
||||
log::info!("Resizing horizontal bar to {}px", new_size);
|
||||
(0, new_size)
|
||||
}
|
||||
};
|
||||
log::info!(
|
||||
"Toggling bar size to {}px (expanded: {})",
|
||||
new_size,
|
||||
is_expanded
|
||||
);
|
||||
|
||||
if let Err(e) = control
|
||||
.surface("Bar")
|
||||
.configure()
|
||||
.size(width, height)
|
||||
.exclusive_zone(new_size.try_into().unwrap_or(32))
|
||||
.margins((0, 0, 0, 0))
|
||||
.apply()
|
||||
{
|
||||
log::error!("Failed to apply configuration: {}", e);
|
||||
}
|
||||
|
||||
log::info!(
|
||||
"Updated bar state: size={}, is_expanded={}",
|
||||
new_size,
|
||||
st.is_expanded
|
||||
);
|
||||
|
||||
st.is_expanded
|
||||
};
|
||||
|
||||
if let Err(e) = sender_clone.send(UiUpdate::IsExpanded(is_expanded)) {
|
||||
log::error!("Failed to send UI update: {}", e);
|
||||
if let Err(e) = control
|
||||
.this_instance()
|
||||
.configure()
|
||||
.size(width, height)
|
||||
.exclusive_zone(new_size.try_into().unwrap_or(32))
|
||||
.apply()
|
||||
{
|
||||
log::error!("Failed to apply configuration: {}", e);
|
||||
}
|
||||
|
||||
Value::Struct(Struct::from_iter([("expanded".into(), is_expanded.into())]))
|
||||
});
|
||||
}
|
||||
|
||||
fn setup_anchor_switch_callback(
|
||||
sender: &Rc<Sender<UiUpdate>>,
|
||||
shell: &Shell,
|
||||
state: &Rc<RefCell<BarState>>,
|
||||
) {
|
||||
let state_clone = Rc::clone(state);
|
||||
let sender_clone = Rc::clone(sender);
|
||||
shell
|
||||
.select(Surface::named("Bar"))
|
||||
.on_callback("switch-anchor", move |control| {
|
||||
let anchor_name = {
|
||||
let mut st = state_clone.borrow_mut();
|
||||
st.next_anchor();
|
||||
|
||||
log::info!("Switching to anchor: {}", st.anchor_name());
|
||||
|
||||
let bar = control.surface("Bar");
|
||||
if let Err(e) = bar.set_anchor(st.get_anchor_edges()) {
|
||||
log::error!("Failed to apply config: {}", e);
|
||||
}
|
||||
|
||||
st.anchor_name()
|
||||
};
|
||||
|
||||
if let Err(e) = sender_clone.send(UiUpdate::CurrentAnchor(anchor_name.to_string())) {
|
||||
if let Err(e) = sender_clone.send(UiUpdate::ToggleSize) {
|
||||
log::error!("Failed to send UI update: {}", e);
|
||||
}
|
||||
|
||||
log::info!("Switched to {} anchor", anchor_name);
|
||||
|
||||
Value::Struct(Struct::from_iter([(
|
||||
"anchor".into(),
|
||||
SharedString::from(anchor_name).into(),
|
||||
)]))
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn setup_layer_switch_callback(
|
||||
sender: &Rc<Sender<UiUpdate>>,
|
||||
shell: &Shell,
|
||||
state: &Rc<RefCell<BarState>>,
|
||||
) {
|
||||
let state_clone = Rc::clone(state);
|
||||
fn setup_anchor_switch_callback(sender: &Rc<Sender<UiUpdate>>, shell: &Shell) {
|
||||
let sender_clone = Rc::clone(sender);
|
||||
shell
|
||||
.select(Surface::named("Bar"))
|
||||
.on_callback("switch-layer", move |control| {
|
||||
let layer_name = {
|
||||
let mut st = state_clone.borrow_mut();
|
||||
let new_layer = st.next_layer();
|
||||
shell.select(Surface::named("Bar")).on_callback_with_args(
|
||||
"switch-anchor",
|
||||
move |args, control| {
|
||||
let new_anchor = args
|
||||
.first()
|
||||
.and_then(|v| match v {
|
||||
Value::String(s) => Some(s.as_str()),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or("Top");
|
||||
|
||||
log::info!("Switching to layer: {:?}", new_layer);
|
||||
|
||||
let bar = control.surface("Bar");
|
||||
if let Err(e) = bar.set_layer(new_layer) {
|
||||
log::error!("Failed to set layer: {}", e);
|
||||
}
|
||||
|
||||
st.layer_name()
|
||||
let anchor_edges = match new_anchor {
|
||||
"Bottom" => AnchorEdges::bottom_bar(),
|
||||
_ => AnchorEdges::top_bar(),
|
||||
};
|
||||
|
||||
if let Err(e) = sender_clone.send(UiUpdate::CurrentLayer(layer_name.to_string())) {
|
||||
log::error!("Failed to send UI update: {}", e);
|
||||
log::info!("Switching anchor to: {}", new_anchor);
|
||||
|
||||
if let Err(e) = control.this_instance().set_anchor(anchor_edges) {
|
||||
log::error!("Failed to apply anchor config: {}", e);
|
||||
}
|
||||
|
||||
log::info!("Switched to {} layer", layer_name);
|
||||
if let Err(e) = sender_clone.send(UiUpdate::SwitchAnchor) {
|
||||
log::error!("Failed to send UI update: {}", e);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Value::Struct(Struct::from_iter([(
|
||||
"layer".into(),
|
||||
SharedString::from(layer_name).into(),
|
||||
)]))
|
||||
});
|
||||
fn setup_layer_switch_callback(sender: &Rc<Sender<UiUpdate>>, shell: &Shell) {
|
||||
let sender_clone = Rc::clone(sender);
|
||||
shell.select(Surface::named("Bar")).on_callback_with_args(
|
||||
"switch-layer",
|
||||
move |args, control| {
|
||||
let new_layer_str = args
|
||||
.first()
|
||||
.and_then(|v| match v {
|
||||
Value::String(s) => Some(s.as_str()),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or("Top");
|
||||
|
||||
let new_layer = match new_layer_str {
|
||||
"Background" => Layer::Background,
|
||||
"Bottom" => Layer::Bottom,
|
||||
"Overlay" => Layer::Overlay,
|
||||
_ => Layer::Top,
|
||||
};
|
||||
|
||||
log::info!("Switching layer to: {:?}", new_layer);
|
||||
|
||||
if let Err(e) = control.this_instance().set_layer(new_layer) {
|
||||
log::error!("Failed to set layer: {}", e);
|
||||
}
|
||||
|
||||
if let Err(e) = sender_clone.send(UiUpdate::SwitchLayer) {
|
||||
log::error!("Failed to send UI update: {}", e);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
|
|
@ -208,8 +122,6 @@ fn main() -> Result<()> {
|
|||
|
||||
let ui_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("ui/bar.slint");
|
||||
|
||||
let state = Rc::new(RefCell::new(BarState::new()));
|
||||
|
||||
let mut shell = Shell::from_file(ui_path)
|
||||
.surface("Bar")
|
||||
.height(32)
|
||||
|
|
@ -218,65 +130,34 @@ fn main() -> Result<()> {
|
|||
.namespace("runtime-control-example")
|
||||
.build()?;
|
||||
|
||||
shell.select(Surface::all()).with_component(|component| {
|
||||
log::info!("Initializing properties for Bar surface");
|
||||
let state_ref = state.borrow();
|
||||
shell
|
||||
.select(Surface::named("Bar"))
|
||||
.with_component(|component| {
|
||||
log::info!("Initializing properties for Bar surface");
|
||||
|
||||
let set_property = |name: &str, value: Value| {
|
||||
if let Err(e) = component.set_property(name, value) {
|
||||
log::error!("Failed to set initial {}: {}", name, e);
|
||||
}
|
||||
};
|
||||
let set_property = |name: &str, value: Value| {
|
||||
if let Err(e) = component.set_property(name, value) {
|
||||
log::error!("Failed to set initial {}: {}", name, e);
|
||||
}
|
||||
};
|
||||
|
||||
set_property("is-expanded", state_ref.is_expanded.into());
|
||||
set_property(
|
||||
"current-anchor",
|
||||
SharedString::from(state_ref.anchor_name()).into(),
|
||||
);
|
||||
set_property(
|
||||
"current-layer",
|
||||
SharedString::from(state_ref.layer_name()).into(),
|
||||
);
|
||||
set_property("is-expanded", false.into());
|
||||
set_property("current-anchor", SharedString::from("Top").into());
|
||||
set_property("current-layer", SharedString::from("Top").into());
|
||||
|
||||
log::info!("Initialized properties for Bar surface");
|
||||
});
|
||||
log::info!("Initialized properties for Bar surface");
|
||||
});
|
||||
|
||||
let handle = shell.event_loop_handle();
|
||||
let (_token, sender) = handle.add_channel(|message: UiUpdate, app_state| {
|
||||
let (_token, sender) = handle.add_channel(|message: UiUpdate, _app_state| {
|
||||
log::info!("Received UI update: {:?}", message);
|
||||
|
||||
for surface in app_state.all_outputs() {
|
||||
let component = surface.component_instance();
|
||||
|
||||
match &message {
|
||||
UiUpdate::IsExpanded(is_expanded) => {
|
||||
if let Err(e) = component.set_property("is-expanded", (*is_expanded).into()) {
|
||||
log::error!("Failed to set is-expanded: {}", e);
|
||||
}
|
||||
}
|
||||
UiUpdate::CurrentAnchor(anchor) => {
|
||||
if let Err(e) = component
|
||||
.set_property("current-anchor", SharedString::from(anchor.as_str()).into())
|
||||
{
|
||||
log::error!("Failed to set current-anchor: {}", e);
|
||||
}
|
||||
}
|
||||
UiUpdate::CurrentLayer(layer) => {
|
||||
if let Err(e) = component
|
||||
.set_property("current-layer", SharedString::from(layer.as_str()).into())
|
||||
{
|
||||
log::error!("Failed to set current-layer: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})?;
|
||||
|
||||
let sender_rc = Rc::new(sender);
|
||||
|
||||
setup_toggle_size_callback(&sender_rc, &shell, &state);
|
||||
setup_anchor_switch_callback(&sender_rc, &shell, &state);
|
||||
setup_layer_switch_callback(&sender_rc, &shell, &state);
|
||||
setup_toggle_size_callback(&sender_rc, &shell);
|
||||
setup_anchor_switch_callback(&sender_rc, &shell);
|
||||
setup_layer_switch_callback(&sender_rc, &shell);
|
||||
shell.run()?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -6,9 +6,29 @@ export component Bar inherits Window {
|
|||
in-out property <string> current-anchor: "Top";
|
||||
in-out property <string> current-layer: "Top";
|
||||
|
||||
callback toggle-size() -> {expanded: bool};
|
||||
callback switch-anchor() -> {anchor: string};
|
||||
callback switch-layer() -> {layer: string};
|
||||
callback toggle-size(bool);
|
||||
callback switch-anchor(string);
|
||||
callback switch-layer(string);
|
||||
|
||||
function next-anchor(current: string) -> string {
|
||||
if (current == "Top") {
|
||||
return "Bottom";
|
||||
} else {
|
||||
return "Top";
|
||||
}
|
||||
}
|
||||
|
||||
function next-layer(current: string) -> string {
|
||||
if (current == "Top") {
|
||||
return "Overlay";
|
||||
} else if (current == "Overlay") {
|
||||
return "Bottom";
|
||||
} else if (current == "Bottom") {
|
||||
return "Background";
|
||||
} else {
|
||||
return "Top";
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalLayout {
|
||||
spacing: 12px;
|
||||
|
|
@ -37,21 +57,24 @@ export component Bar inherits Window {
|
|||
Button {
|
||||
text: is-expanded ? "Collapse" : "Expand";
|
||||
clicked => {
|
||||
root.is-expanded = toggle-size().expanded;
|
||||
root.is-expanded = !root.is-expanded;
|
||||
toggle-size(root.is-expanded);
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Switch Anchor";
|
||||
clicked => {
|
||||
root.current-anchor = switch-anchor().anchor;
|
||||
root.current-anchor = next-anchor(root.current-anchor);
|
||||
switch-anchor(root.current-anchor);
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Switch Layer";
|
||||
clicked => {
|
||||
root.current-layer = switch-layer().layer;
|
||||
root.current-layer = next-layer(root.current-layer);
|
||||
switch-layer(root.current-layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ pub mod shell;
|
|||
pub mod slint_integration;
|
||||
pub mod window;
|
||||
|
||||
pub use layer_shika_composition::{Error, Handle, Result, SurfaceHandle};
|
||||
pub use layer_shika_composition::{CallbackContext, Error, Handle, Result, SurfaceHandle, SurfaceInstanceId, SurfaceTarget};
|
||||
|
||||
pub use shell::{
|
||||
CompiledUiSource, DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, LayerSurfaceHandle, Output,
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ pub use crate::event::{EventDispatchContext, EventLoopHandle, ShellEventLoop};
|
|||
|
||||
pub use crate::slint_integration::{PopupWindow, slint, slint_interpreter};
|
||||
|
||||
pub use crate::{Error, Handle, Result, SurfaceHandle};
|
||||
pub use crate::{CallbackContext, Error, Handle, Result, SurfaceHandle, SurfaceInstanceId, SurfaceTarget};
|
||||
|
||||
pub use layer_shika_composition::prelude::{
|
||||
Anchor, LogicalSize, Margins, PhysicalSize, ScaleFactor, SurfaceConfig, SurfaceDimension,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue