mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-12-12 16:35:56 +00:00
feat: add surface command for shell control
This commit is contained in:
parent
0aa0aeae0b
commit
c2064c6012
6 changed files with 480 additions and 36 deletions
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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};
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
@ -487,6 +489,38 @@ impl Shell {
|
||||||
if let channel::Event::Msg(command) = event {
|
if let channel::Event::Msg(command) = event {
|
||||||
let mut ctx = crate::system::EventDispatchContext::from_app_state(app_state);
|
let mut ctx = crate::system::EventDispatchContext::from_app_state(app_state);
|
||||||
|
|
||||||
|
match command {
|
||||||
|
ShellCommand::Popup(popup_cmd) => {
|
||||||
|
Self::handle_popup_command(popup_cmd, &mut ctx, &control);
|
||||||
|
}
|
||||||
|
ShellCommand::Surface(surface_cmd) => {
|
||||||
|
Self::handle_surface_command(surface_cmd, &mut ctx);
|
||||||
|
}
|
||||||
|
ShellCommand::Render => {
|
||||||
|
if let Err(e) = ctx.render_frame_if_dirty() {
|
||||||
|
log::error!("Failed to render frame: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err(|e| {
|
||||||
|
Error::Adapter(
|
||||||
|
EventLoopError::InsertSource {
|
||||||
|
message: format!("Failed to setup command handler: {e:?}"),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_popup_command(
|
||||||
|
command: PopupCommand,
|
||||||
|
ctx: &mut EventDispatchContext<'_>,
|
||||||
|
control: &ShellControl,
|
||||||
|
) {
|
||||||
match command {
|
match command {
|
||||||
PopupCommand::Show(request) => {
|
PopupCommand::Show(request) => {
|
||||||
if let Err(e) = ctx.show_popup(&request, Some(control.clone())) {
|
if let Err(e) = ctx.show_popup(&request, Some(control.clone())) {
|
||||||
|
|
@ -509,22 +543,107 @@ impl Shell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.map_err(|e| {
|
|
||||||
Error::Adapter(
|
|
||||||
EventLoopError::InsertSource {
|
|
||||||
message: format!("Failed to setup popup command handler: {e:?}"),
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
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> {
|
||||||
|
|
|
||||||
|
|
@ -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,7 +127,9 @@ 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
|
||||||
|
.send(ShellCommand::Popup(PopupCommand::Close(handle)))
|
||||||
|
.map_err(|_| {
|
||||||
Error::Domain(DomainError::Configuration {
|
Error::Domain(DomainError::Configuration {
|
||||||
message: "Failed to send popup close command: channel closed".to_string(),
|
message: "Failed to send popup close command: channel closed".to_string(),
|
||||||
})
|
})
|
||||||
|
|
@ -85,17 +138,175 @@ impl ShellControl {
|
||||||
|
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue