mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-11-04 07:24:24 +00:00
feat: add popup abstraction
This commit is contained in:
parent
bc46570971
commit
aa5052b566
3 changed files with 180 additions and 3 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1857,6 +1857,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"layer-shika-adapters",
|
||||
"layer-shika-domain",
|
||||
"log",
|
||||
"spin_on",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -16,5 +16,6 @@ workspace = true
|
|||
[dependencies]
|
||||
layer-shika-adapters.workspace = true
|
||||
layer-shika-domain.workspace = true
|
||||
log.workspace = true
|
||||
spin_on.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
|
|
|||
|
|
@ -4,14 +4,18 @@ use layer_shika_adapters::platform::calloop::{
|
|||
EventSource, Generic, Interest, Mode, PostAction, RegistrationToken, TimeoutAction, Timer,
|
||||
channel,
|
||||
};
|
||||
use layer_shika_adapters::platform::slint::ComponentHandle;
|
||||
use layer_shika_adapters::platform::slint_interpreter::{
|
||||
CompilationResult, ComponentDefinition, ComponentInstance,
|
||||
CompilationResult, ComponentDefinition, ComponentInstance, Value,
|
||||
};
|
||||
use layer_shika_adapters::wayland::{
|
||||
config::WaylandWindowConfig, shell_adapter::WaylandWindowingSystem,
|
||||
surfaces::surface_state::WindowState,
|
||||
};
|
||||
use layer_shika_adapters::{clear_popup_config, close_current_popup, set_popup_config};
|
||||
use layer_shika_domain::config::WindowConfig;
|
||||
use layer_shika_domain::errors::DomainError;
|
||||
use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode;
|
||||
use std::cell::{Ref, RefCell};
|
||||
use std::os::unix::io::AsFd;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
|
@ -124,10 +128,97 @@ impl RuntimeState<'_> {
|
|||
pub fn compilation_result(&self) -> Option<Rc<CompilationResult>> {
|
||||
self.window_state.compilation_result()
|
||||
}
|
||||
|
||||
pub fn show_popup_component(
|
||||
&self,
|
||||
component_name: &str,
|
||||
position: Option<(f32, f32)>,
|
||||
size: Option<(f32, f32)>,
|
||||
positioning_mode: PopupPositioningMode,
|
||||
) -> Result<()> {
|
||||
let compilation_result = self.compilation_result().ok_or_else(|| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: "No compilation result available for popup creation".to_string(),
|
||||
})
|
||||
})?;
|
||||
|
||||
let definition = compilation_result
|
||||
.component(component_name)
|
||||
.ok_or_else(|| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!(
|
||||
"{} component not found in compilation result",
|
||||
component_name
|
||||
),
|
||||
})
|
||||
})?;
|
||||
|
||||
close_current_popup();
|
||||
|
||||
let temp_instance = definition.create().map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!("Failed to create temporary popup instance: {}", e),
|
||||
})
|
||||
})?;
|
||||
temp_instance.hide().map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!("Failed to hide temporary popup instance: {}", e),
|
||||
})
|
||||
})?;
|
||||
|
||||
let width: f32 = temp_instance
|
||||
.get_property("popup-width")
|
||||
.ok()
|
||||
.and_then(|v| v.try_into().ok())
|
||||
.or(size.map(|(w, _)| w))
|
||||
.unwrap_or(300.0);
|
||||
|
||||
let height: f32 = temp_instance
|
||||
.get_property("popup-height")
|
||||
.ok()
|
||||
.and_then(|v| v.try_into().ok())
|
||||
.or(size.map(|(_, h)| h))
|
||||
.unwrap_or(400.0);
|
||||
|
||||
drop(temp_instance);
|
||||
close_current_popup();
|
||||
|
||||
if let Some((reference_x, reference_y)) = position {
|
||||
set_popup_config(reference_x, reference_y, width, height, positioning_mode);
|
||||
} else {
|
||||
clear_popup_config();
|
||||
}
|
||||
|
||||
let instance = definition.create().map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!("Failed to create popup instance: {}", e),
|
||||
})
|
||||
})?;
|
||||
|
||||
instance
|
||||
.set_callback("closed", move |_| {
|
||||
close_current_popup();
|
||||
Value::Void
|
||||
})
|
||||
.map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!("Failed to set popup closed callback: {}", e),
|
||||
})
|
||||
})?;
|
||||
|
||||
instance.show().map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!("Failed to show popup instance: {}", e),
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WindowingSystem {
|
||||
inner: Rc<RefCell<WaylandWindowingSystem>>,
|
||||
popup_positioning_mode: Rc<RefCell<PopupPositioningMode>>,
|
||||
}
|
||||
|
||||
impl WindowingSystem {
|
||||
|
|
@ -143,9 +234,93 @@ impl WindowingSystem {
|
|||
);
|
||||
let inner = WaylandWindowingSystem::new(wayland_config)?;
|
||||
|
||||
Ok(Self {
|
||||
let system = Self {
|
||||
inner: Rc::new(RefCell::new(inner)),
|
||||
popup_positioning_mode: Rc::new(RefCell::new(PopupPositioningMode::Center)),
|
||||
};
|
||||
|
||||
system.register_popup_callbacks()?;
|
||||
|
||||
Ok(system)
|
||||
}
|
||||
|
||||
fn register_popup_callbacks(&self) -> Result<()> {
|
||||
let component_instance = self.component_instance();
|
||||
|
||||
let popup_mode_clone = Rc::clone(&self.popup_positioning_mode);
|
||||
component_instance
|
||||
.set_callback("set_popup_positioning_mode", move |args| {
|
||||
let center_x: bool = args
|
||||
.first()
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or(false);
|
||||
let center_y: bool = args
|
||||
.get(1)
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or(false);
|
||||
|
||||
let mode = PopupPositioningMode::from_flags(center_x, center_y);
|
||||
*popup_mode_clone.borrow_mut() = mode;
|
||||
Value::Void
|
||||
})
|
||||
.map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!(
|
||||
"Failed to register set_popup_positioning_mode callback: {}",
|
||||
e
|
||||
),
|
||||
})
|
||||
})?;
|
||||
|
||||
let event_loop_handle = self.event_loop_handle();
|
||||
let popup_mode_for_channel = Rc::clone(&self.popup_positioning_mode);
|
||||
|
||||
let (_token, sender) = event_loop_handle.add_channel(
|
||||
move |(component_name, x, y): (String, f32, f32), state| {
|
||||
let mode = *popup_mode_for_channel.borrow();
|
||||
if let Err(e) =
|
||||
state.show_popup_component(&component_name, Some((x, y)), None, mode)
|
||||
{
|
||||
log::error!("Failed to show popup: {}", e);
|
||||
}
|
||||
},
|
||||
)?;
|
||||
|
||||
component_instance
|
||||
.set_callback("show_popup", move |args| {
|
||||
use layer_shika_adapters::platform::slint::SharedString;
|
||||
|
||||
let component_name: SharedString = args
|
||||
.first()
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or_else(|| SharedString::from(""));
|
||||
|
||||
if component_name.is_empty() {
|
||||
log::error!("show_popup called without component name");
|
||||
return Value::Void;
|
||||
}
|
||||
|
||||
let x: f32 = args
|
||||
.get(1)
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or(0.0);
|
||||
let y: f32 = args
|
||||
.get(2)
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or(0.0);
|
||||
|
||||
if sender.send((component_name.to_string(), x, y)).is_err() {
|
||||
log::error!("Failed to send popup request through channel");
|
||||
}
|
||||
Value::Void
|
||||
})
|
||||
.map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!("Failed to register show_popup callback: {}", e),
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue