feat: add surface command for shell control

This commit is contained in:
drendog 2025-12-07 21:01:24 +01:00
parent 0aa0aeae0b
commit c2064c6012
Signed by: dwenya
GPG key ID: 8DD77074645332D0
6 changed files with 480 additions and 36 deletions

View file

@ -376,6 +376,16 @@ impl AppState {
.map(|(_, v)| v) .map(|(_, v)| v)
} }
pub fn surfaces_by_name_mut(
&mut self,
surface_name: &str,
) -> impl Iterator<Item = &mut PerOutputSurface> {
self.surfaces
.iter_mut()
.filter(move |(k, _)| k.surface_name == surface_name)
.map(|(_, v)| v)
}
pub fn get_output_by_handle(&self, handle: OutputHandle) -> Option<&PerOutputSurface> { pub fn get_output_by_handle(&self, handle: OutputHandle) -> Option<&PerOutputSurface> {
self.get_first_surface_for_output(handle) self.get_first_surface_for_output(handle)
} }

View file

@ -123,6 +123,42 @@ impl SurfaceState {
self.rendering.update_size(width, height, scale_factor); self.rendering.update_size(width, height, scale_factor);
} }
#[allow(clippy::cast_precision_loss)]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
pub fn update_size_with_compositor_logic(
&mut self,
requested_width: u32,
requested_height: u32,
) {
let scale_factor = self.event_context.borrow().scale_factor();
let output_width = self.output_size().width;
let target_width = if requested_width == 0 || (requested_width == 1 && output_width > 1) {
if scale_factor > 1.0 {
(output_width as f32 / scale_factor).round() as u32
} else {
output_width
}
} else {
requested_width
};
let target_height = if requested_height > 0 {
requested_height
} else {
let h = self.height();
if scale_factor > 1.0 {
(h as f32 / scale_factor).round() as u32
} else {
h
}
};
self.rendering
.update_size(target_width, target_height, scale_factor);
}
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
pub fn set_current_pointer_position(&mut self, physical_x: f64, physical_y: f64) { pub fn set_current_pointer_position(&mut self, physical_x: f64, physical_y: f64) {
self.event_context self.event_context

View file

@ -1,6 +1,7 @@
use layer_shika_adapters::SurfaceState; use layer_shika_adapters::SurfaceState;
use layer_shika_adapters::platform::slint_interpreter::ComponentInstance; use layer_shika_adapters::platform::slint_interpreter::ComponentInstance;
use layer_shika_adapters::platform::wayland::{Anchor, WaylandKeyboardInteractivity, WaylandLayer}; use layer_shika_adapters::platform::wayland::{Anchor, WaylandKeyboardInteractivity, WaylandLayer};
use layer_shika_domain::value_objects::anchor::AnchorEdges;
use layer_shika_domain::value_objects::keyboard_interactivity::KeyboardInteractivity; use layer_shika_domain::value_objects::keyboard_interactivity::KeyboardInteractivity;
use layer_shika_domain::value_objects::layer::Layer; use layer_shika_domain::value_objects::layer::Layer;
use layer_shika_domain::value_objects::margins::Margins; use layer_shika_domain::value_objects::margins::Margins;
@ -18,6 +19,30 @@ impl<'a> LayerSurfaceHandle<'a> {
self.window_state.layer_surface().set_anchor(anchor); self.window_state.layer_surface().set_anchor(anchor);
} }
pub fn set_anchor_edges(&self, anchor: AnchorEdges) {
let wayland_anchor = Self::convert_anchor(anchor);
self.window_state.layer_surface().set_anchor(wayland_anchor);
}
fn convert_anchor(anchor: AnchorEdges) -> Anchor {
let mut result = Anchor::empty();
if anchor.has_top() {
result = result.union(Anchor::Top);
}
if anchor.has_bottom() {
result = result.union(Anchor::Bottom);
}
if anchor.has_left() {
result = result.union(Anchor::Left);
}
if anchor.has_right() {
result = result.union(Anchor::Right);
}
result
}
pub fn set_size(&self, width: u32, height: u32) { pub fn set_size(&self, width: u32, height: u32) {
self.window_state.layer_surface().set_size(width, height); self.window_state.layer_surface().set_size(width, height);
} }

View file

@ -33,7 +33,7 @@ pub use layer_shika_domain::value_objects::popup_request::{
pub use layer_surface::{LayerSurfaceHandle, ShellSurfaceConfigHandler}; pub use layer_surface::{LayerSurfaceHandle, ShellSurfaceConfigHandler};
pub use popup_builder::PopupBuilder; pub use popup_builder::PopupBuilder;
pub use shell_runtime::{DEFAULT_SURFACE_NAME, ShellRuntime}; pub use shell_runtime::{DEFAULT_SURFACE_NAME, ShellRuntime};
pub use system::{EventDispatchContext, ShellControl}; pub use system::{EventDispatchContext, ShellControl, SurfaceControlHandle};
pub use value_conversion::IntoValue; pub use value_conversion::IntoValue;
pub use shell::{ pub use shell::{
@ -70,8 +70,9 @@ pub mod prelude {
OutputPolicy, OutputRegistry, PopupBuilder, PopupHandle, PopupPlacement, OutputPolicy, OutputRegistry, PopupBuilder, PopupHandle, PopupPlacement,
PopupPositioningMode, PopupRequest, PopupSize, PopupWindow, Result, Shell, ShellBuilder, PopupPositioningMode, PopupRequest, PopupSize, PopupWindow, Result, Shell, ShellBuilder,
ShellConfig, ShellControl, ShellEventContext, ShellEventLoop, ShellRuntime, ShellConfig, ShellControl, ShellEventContext, ShellEventLoop, ShellRuntime,
ShellSurfaceConfigHandler, SurfaceComponentConfig, SurfaceConfigBuilder, SurfaceDefinition, ShellSurfaceConfigHandler, SurfaceComponentConfig, SurfaceConfigBuilder,
SurfaceEntry, SurfaceHandle, SurfaceMetadata, SurfaceRegistry, SurfaceControlHandle, SurfaceDefinition, SurfaceEntry, SurfaceHandle, SurfaceMetadata,
SurfaceRegistry,
}; };
pub use crate::calloop::{Generic, Interest, Mode, PostAction, RegistrationToken, Timer}; pub use crate::calloop::{Generic, Interest, Mode, PostAction, RegistrationToken, Timer};

View file

@ -4,7 +4,9 @@ use crate::popup_builder::PopupBuilder;
use crate::shell_config::{CompiledUiSource, ShellConfig}; use crate::shell_config::{CompiledUiSource, ShellConfig};
use crate::shell_runtime::ShellRuntime; use crate::shell_runtime::ShellRuntime;
use crate::surface_registry::{SurfaceDefinition, SurfaceEntry, SurfaceRegistry}; use crate::surface_registry::{SurfaceDefinition, SurfaceEntry, SurfaceRegistry};
use crate::system::{PopupCommand, ShellControl}; use crate::system::{
EventDispatchContext, PopupCommand, ShellCommand, ShellControl, SurfaceCommand,
};
use crate::value_conversion::IntoValue; use crate::value_conversion::IntoValue;
use crate::{Error, Result}; use crate::{Error, Result};
use layer_shika_adapters::errors::EventLoopError; use layer_shika_adapters::errors::EventLoopError;
@ -220,7 +222,7 @@ pub struct Shell {
inner: Rc<RefCell<dyn WaylandSystemOps>>, inner: Rc<RefCell<dyn WaylandSystemOps>>,
registry: SurfaceRegistry, registry: SurfaceRegistry,
compilation_result: Rc<CompilationResult>, compilation_result: Rc<CompilationResult>,
popup_command_sender: channel::Sender<PopupCommand>, command_sender: channel::Sender<ShellCommand>,
output_connected_handlers: Rc<RefCell<Vec<OutputConnectedHandler>>>, output_connected_handlers: Rc<RefCell<Vec<OutputConnectedHandler>>>,
output_disconnected_handlers: Rc<RefCell<Vec<OutputDisconnectedHandler>>>, output_disconnected_handlers: Rc<RefCell<Vec<OutputDisconnectedHandler>>>,
} }
@ -403,12 +405,12 @@ impl Shell {
inner: Rc::clone(&inner_rc), inner: Rc::clone(&inner_rc),
registry, registry,
compilation_result, compilation_result,
popup_command_sender: sender, command_sender: sender,
output_connected_handlers: Rc::new(RefCell::new(Vec::new())), output_connected_handlers: Rc::new(RefCell::new(Vec::new())),
output_disconnected_handlers: Rc::new(RefCell::new(Vec::new())), output_disconnected_handlers: Rc::new(RefCell::new(Vec::new())),
}; };
shell.setup_popup_command_handler(receiver)?; shell.setup_command_handler(receiver)?;
log::info!("Shell created (single-window mode)"); log::info!("Shell created (single-window mode)");
@ -463,12 +465,12 @@ impl Shell {
inner: Rc::clone(&inner_rc), inner: Rc::clone(&inner_rc),
registry, registry,
compilation_result, compilation_result,
popup_command_sender: sender, command_sender: sender,
output_connected_handlers: Rc::new(RefCell::new(Vec::new())), output_connected_handlers: Rc::new(RefCell::new(Vec::new())),
output_disconnected_handlers: Rc::new(RefCell::new(Vec::new())), output_disconnected_handlers: Rc::new(RefCell::new(Vec::new())),
}; };
shell.setup_popup_command_handler(receiver)?; shell.setup_command_handler(receiver)?;
log::info!( log::info!(
"Shell created (multi-surface mode) with surfaces: {:?}", "Shell created (multi-surface mode) with surfaces: {:?}",
@ -478,7 +480,7 @@ impl Shell {
Ok(shell) Ok(shell)
} }
fn setup_popup_command_handler(&self, receiver: channel::Channel<PopupCommand>) -> Result<()> { fn setup_command_handler(&self, receiver: channel::Channel<ShellCommand>) -> Result<()> {
let loop_handle = self.inner.borrow().event_loop_handle(); let loop_handle = self.inner.borrow().event_loop_handle();
let control = self.control(); let control = self.control();
@ -488,23 +490,15 @@ impl Shell {
let mut ctx = crate::system::EventDispatchContext::from_app_state(app_state); let mut ctx = crate::system::EventDispatchContext::from_app_state(app_state);
match command { match command {
PopupCommand::Show(request) => { ShellCommand::Popup(popup_cmd) => {
if let Err(e) = ctx.show_popup(&request, Some(control.clone())) { Self::handle_popup_command(popup_cmd, &mut ctx, &control);
log::error!("Failed to show popup: {}", e);
}
} }
PopupCommand::Close(handle) => { ShellCommand::Surface(surface_cmd) => {
if let Err(e) = ctx.close_popup(handle) { Self::handle_surface_command(surface_cmd, &mut ctx);
log::error!("Failed to close popup: {}", e);
}
} }
PopupCommand::Resize { ShellCommand::Render => {
handle, if let Err(e) = ctx.render_frame_if_dirty() {
width, log::error!("Failed to render frame: {}", e);
height,
} => {
if let Err(e) = ctx.resize_popup(handle, width, height) {
log::error!("Failed to resize popup: {}", e);
} }
} }
} }
@ -513,7 +507,7 @@ impl Shell {
.map_err(|e| { .map_err(|e| {
Error::Adapter( Error::Adapter(
EventLoopError::InsertSource { EventLoopError::InsertSource {
message: format!("Failed to setup popup command handler: {e:?}"), message: format!("Failed to setup command handler: {e:?}"),
} }
.into(), .into(),
) )
@ -522,9 +516,134 @@ impl Shell {
Ok(()) Ok(())
} }
fn handle_popup_command(
command: PopupCommand,
ctx: &mut EventDispatchContext<'_>,
control: &ShellControl,
) {
match command {
PopupCommand::Show(request) => {
if let Err(e) = ctx.show_popup(&request, Some(control.clone())) {
log::error!("Failed to show popup: {}", e);
}
}
PopupCommand::Close(handle) => {
if let Err(e) = ctx.close_popup(handle) {
log::error!("Failed to close popup: {}", e);
}
}
PopupCommand::Resize {
handle,
width,
height,
} => {
if let Err(e) = ctx.resize_popup(handle, width, height) {
log::error!("Failed to resize popup: {}", e);
}
}
}
}
fn handle_surface_command(command: SurfaceCommand, ctx: &mut EventDispatchContext<'_>) {
match command {
SurfaceCommand::Resize {
name,
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);
}
}
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);
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);
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);
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);
handle.set_layer(layer);
handle.commit();
}
}
SurfaceCommand::SetKeyboardInteractivity { name, mode } => {
log::debug!(
"Surface command: SetKeyboardInteractivity '{}' to {:?}",
name,
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 } => {
log::debug!(
"Surface command: SetOutputPolicy '{}' to {:?}",
name,
policy
);
log::warn!(
"SetOutputPolicy is not yet implemented - requires runtime surface spawning"
);
}
SurfaceCommand::SetScaleFactor { name, factor } => {
log::debug!("Surface command: SetScaleFactor '{}' to {:?}", name, 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(&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();
}
}
}
if let Err(e) = ctx.render_frame_if_dirty() {
log::error!("Failed to render frame after surface command: {}", e);
}
}
#[must_use] #[must_use]
pub fn control(&self) -> ShellControl { pub fn control(&self) -> ShellControl {
ShellControl::new(self.popup_command_sender.clone()) ShellControl::new(self.command_sender.clone())
} }
pub fn surface_names(&self) -> Vec<&str> { pub fn surface_names(&self) -> Vec<&str> {

View file

@ -1,4 +1,5 @@
use crate::event_loop::FromAppState; use crate::event_loop::FromAppState;
use crate::layer_surface::LayerSurfaceHandle;
use crate::{Error, Result}; use crate::{Error, Result};
use layer_shika_adapters::platform::calloop::channel; use layer_shika_adapters::platform::calloop::channel;
use layer_shika_adapters::platform::slint::ComponentHandle; use layer_shika_adapters::platform::slint::ComponentHandle;
@ -6,8 +7,12 @@ use layer_shika_adapters::platform::slint_interpreter::{
CompilationResult, ComponentDefinition, ComponentInstance, Value, CompilationResult, ComponentDefinition, ComponentInstance, Value,
}; };
use layer_shika_adapters::{AppState, PopupManager, SurfaceState}; use layer_shika_adapters::{AppState, PopupManager, SurfaceState};
use layer_shika_domain::config::SurfaceConfig;
use layer_shika_domain::entities::output_registry::OutputRegistry; use layer_shika_domain::entities::output_registry::OutputRegistry;
use layer_shika_domain::errors::DomainError; use layer_shika_domain::errors::DomainError;
use layer_shika_domain::prelude::{
AnchorEdges, KeyboardInteractivity, Layer, Margins, OutputPolicy, ScaleFactor,
};
use layer_shika_domain::value_objects::dimensions::PopupDimensions; use layer_shika_domain::value_objects::dimensions::PopupDimensions;
use layer_shika_domain::value_objects::output_handle::OutputHandle; use layer_shika_domain::value_objects::output_handle::OutputHandle;
use layer_shika_domain::value_objects::output_info::OutputInfo; use layer_shika_domain::value_objects::output_info::OutputInfo;
@ -28,19 +33,65 @@ pub enum PopupCommand {
}, },
} }
pub enum SurfaceCommand {
Resize {
name: String,
width: u32,
height: u32,
},
SetAnchor {
name: String,
anchor: AnchorEdges,
},
SetExclusiveZone {
name: String,
zone: i32,
},
SetMargins {
name: String,
margins: Margins,
},
SetLayer {
name: String,
layer: Layer,
},
SetOutputPolicy {
name: String,
policy: OutputPolicy,
},
SetScaleFactor {
name: String,
factor: ScaleFactor,
},
SetKeyboardInteractivity {
name: String,
mode: KeyboardInteractivity,
},
ApplyConfig {
name: String,
config: SurfaceConfig,
},
}
pub enum ShellCommand {
Popup(PopupCommand),
Surface(SurfaceCommand),
Render,
}
#[derive(Clone)] #[derive(Clone)]
pub struct ShellControl { pub struct ShellControl {
sender: channel::Sender<PopupCommand>, sender: channel::Sender<ShellCommand>,
} }
impl ShellControl { impl ShellControl {
pub fn new(sender: channel::Sender<PopupCommand>) -> Self { pub fn new(sender: channel::Sender<ShellCommand>) -> Self {
Self { sender } Self { sender }
} }
pub fn show_popup(&self, request: &PopupRequest) -> Result<()> { pub fn show_popup(&self, request: &PopupRequest) -> Result<()> {
self.sender self.sender
.send(PopupCommand::Show(request.clone())) .send(ShellCommand::Popup(PopupCommand::Show(request.clone())))
.map_err(|_| { .map_err(|_| {
Error::Domain(DomainError::Configuration { Error::Domain(DomainError::Configuration {
message: "Failed to send popup show command: channel closed".to_string(), message: "Failed to send popup show command: channel closed".to_string(),
@ -76,26 +127,186 @@ impl ShellControl {
} }
pub fn close_popup(&self, handle: PopupHandle) -> Result<()> { pub fn close_popup(&self, handle: PopupHandle) -> Result<()> {
self.sender.send(PopupCommand::Close(handle)).map_err(|_| { self.sender
Error::Domain(DomainError::Configuration { .send(ShellCommand::Popup(PopupCommand::Close(handle)))
message: "Failed to send popup close command: channel closed".to_string(), .map_err(|_| {
Error::Domain(DomainError::Configuration {
message: "Failed to send popup close command: channel closed".to_string(),
})
}) })
})
} }
pub fn resize_popup(&self, handle: PopupHandle, width: f32, height: f32) -> Result<()> { pub fn resize_popup(&self, handle: PopupHandle, width: f32, height: f32) -> Result<()> {
self.sender self.sender
.send(PopupCommand::Resize { .send(ShellCommand::Popup(PopupCommand::Resize {
handle, handle,
width, width,
height, height,
}) }))
.map_err(|_| { .map_err(|_| {
Error::Domain(DomainError::Configuration { Error::Domain(DomainError::Configuration {
message: "Failed to send popup resize command: channel closed".to_string(), message: "Failed to send popup resize command: channel closed".to_string(),
}) })
}) })
} }
pub fn request_redraw(&self) -> Result<()> {
self.sender.send(ShellCommand::Render).map_err(|_| {
Error::Domain(DomainError::Configuration {
message: "Failed to send redraw command: channel closed".to_string(),
})
})
}
pub fn surface(&self, name: impl Into<String>) -> SurfaceControlHandle {
SurfaceControlHandle {
name: name.into(),
sender: self.sender.clone(),
}
}
}
pub struct SurfaceControlHandle {
name: String,
sender: channel::Sender<ShellCommand>,
}
impl SurfaceControlHandle {
pub fn resize(&self, width: u32, height: u32) -> Result<()> {
self.sender
.send(ShellCommand::Surface(SurfaceCommand::Resize {
name: self.name.clone(),
width,
height,
}))
.map_err(|_| {
Error::Domain(DomainError::Configuration {
message: "Failed to send surface resize command: channel closed".to_string(),
})
})
}
pub fn set_width(&self, width: u32) -> Result<()> {
self.resize(width, 0)
}
pub fn set_height(&self, height: u32) -> Result<()> {
self.resize(0, height)
}
pub fn set_anchor(&self, anchor: AnchorEdges) -> Result<()> {
self.sender
.send(ShellCommand::Surface(SurfaceCommand::SetAnchor {
name: self.name.clone(),
anchor,
}))
.map_err(|_| {
Error::Domain(DomainError::Configuration {
message: "Failed to send surface set_anchor command: channel closed"
.to_string(),
})
})
}
pub fn set_exclusive_zone(&self, zone: i32) -> Result<()> {
self.sender
.send(ShellCommand::Surface(SurfaceCommand::SetExclusiveZone {
name: self.name.clone(),
zone,
}))
.map_err(|_| {
Error::Domain(DomainError::Configuration {
message: "Failed to send surface set_exclusive_zone command: channel closed"
.to_string(),
})
})
}
pub fn set_margins(&self, margins: impl Into<Margins>) -> Result<()> {
self.sender
.send(ShellCommand::Surface(SurfaceCommand::SetMargins {
name: self.name.clone(),
margins: margins.into(),
}))
.map_err(|_| {
Error::Domain(DomainError::Configuration {
message: "Failed to send surface set_margins command: channel closed"
.to_string(),
})
})
}
pub fn set_layer(&self, layer: Layer) -> Result<()> {
self.sender
.send(ShellCommand::Surface(SurfaceCommand::SetLayer {
name: self.name.clone(),
layer,
}))
.map_err(|_| {
Error::Domain(DomainError::Configuration {
message: "Failed to send surface set_layer command: channel closed".to_string(),
})
})
}
pub fn set_output_policy(&self, policy: OutputPolicy) -> Result<()> {
self.sender
.send(ShellCommand::Surface(SurfaceCommand::SetOutputPolicy {
name: self.name.clone(),
policy,
}))
.map_err(|_| {
Error::Domain(DomainError::Configuration {
message: "Failed to send surface set_output_policy command: channel closed"
.to_string(),
})
})
}
pub fn set_scale_factor(&self, factor: ScaleFactor) -> Result<()> {
self.sender
.send(ShellCommand::Surface(SurfaceCommand::SetScaleFactor {
name: self.name.clone(),
factor,
}))
.map_err(|_| {
Error::Domain(DomainError::Configuration {
message: "Failed to send surface set_scale_factor command: channel closed"
.to_string(),
})
})
}
pub fn set_keyboard_interactivity(&self, mode: KeyboardInteractivity) -> Result<()> {
self.sender
.send(ShellCommand::Surface(
SurfaceCommand::SetKeyboardInteractivity {
name: self.name.clone(),
mode,
},
))
.map_err(|_| {
Error::Domain(DomainError::Configuration {
message:
"Failed to send surface set_keyboard_interactivity command: channel closed"
.to_string(),
})
})
}
pub fn apply_config(&self, config: SurfaceConfig) -> Result<()> {
self.sender
.send(ShellCommand::Surface(SurfaceCommand::ApplyConfig {
name: self.name.clone(),
config,
}))
.map_err(|_| {
Error::Domain(DomainError::Configuration {
message: "Failed to send surface apply_config command: channel closed"
.to_string(),
})
})
}
} }
pub struct EventDispatchContext<'a> { pub struct EventDispatchContext<'a> {
@ -121,6 +332,17 @@ fn extract_dimensions_from_callback(args: &[Value]) -> PopupDimensions {
} }
impl EventDispatchContext<'_> { 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 surfaces_by_name_mut(
&mut self,
name: &str,
) -> impl Iterator<Item = &mut SurfaceState> {
self.app_state.surfaces_by_name_mut(name)
}
pub fn with_surface<F, R>(&self, name: &str, f: F) -> Result<R> pub fn with_surface<F, R>(&self, name: &str, f: F) -> Result<R>
where where
F: FnOnce(&ComponentInstance) -> R, F: FnOnce(&ComponentInstance) -> R,
@ -572,4 +794,35 @@ impl EventDispatchContext<'_> {
}) })
}) })
} }
pub fn configure_surface<F>(&mut self, name: &str, f: F) -> Result<()>
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 handle = LayerSurfaceHandle::from_window_state(surface);
let component = surface.component_instance();
f(component, handle);
Ok(())
}
pub fn configure_all_surfaces<F>(&mut self, mut f: F)
where
F: FnMut(&ComponentInstance, LayerSurfaceHandle<'_>),
{
for surface in self.app_state.all_outputs() {
let handle = LayerSurfaceHandle::from_window_state(surface);
let component = surface.component_instance();
f(component, handle);
}
}
} }