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 = [
|
dependencies = [
|
||||||
"layer-shika-adapters",
|
"layer-shika-adapters",
|
||||||
"layer-shika-domain",
|
"layer-shika-domain",
|
||||||
|
"log",
|
||||||
"spin_on",
|
"spin_on",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -16,5 +16,6 @@ workspace = true
|
||||||
[dependencies]
|
[dependencies]
|
||||||
layer-shika-adapters.workspace = true
|
layer-shika-adapters.workspace = true
|
||||||
layer-shika-domain.workspace = true
|
layer-shika-domain.workspace = true
|
||||||
|
log.workspace = true
|
||||||
spin_on.workspace = true
|
spin_on.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,18 @@ use layer_shika_adapters::platform::calloop::{
|
||||||
EventSource, Generic, Interest, Mode, PostAction, RegistrationToken, TimeoutAction, Timer,
|
EventSource, Generic, Interest, Mode, PostAction, RegistrationToken, TimeoutAction, Timer,
|
||||||
channel,
|
channel,
|
||||||
};
|
};
|
||||||
|
use layer_shika_adapters::platform::slint::ComponentHandle;
|
||||||
use layer_shika_adapters::platform::slint_interpreter::{
|
use layer_shika_adapters::platform::slint_interpreter::{
|
||||||
CompilationResult, ComponentDefinition, ComponentInstance,
|
CompilationResult, ComponentDefinition, ComponentInstance, Value,
|
||||||
};
|
};
|
||||||
use layer_shika_adapters::wayland::{
|
use layer_shika_adapters::wayland::{
|
||||||
config::WaylandWindowConfig, shell_adapter::WaylandWindowingSystem,
|
config::WaylandWindowConfig, shell_adapter::WaylandWindowingSystem,
|
||||||
surfaces::surface_state::WindowState,
|
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::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::cell::{Ref, RefCell};
|
||||||
use std::os::unix::io::AsFd;
|
use std::os::unix::io::AsFd;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
|
|
@ -124,10 +128,97 @@ impl RuntimeState<'_> {
|
||||||
pub fn compilation_result(&self) -> Option<Rc<CompilationResult>> {
|
pub fn compilation_result(&self) -> Option<Rc<CompilationResult>> {
|
||||||
self.window_state.compilation_result()
|
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 {
|
pub struct WindowingSystem {
|
||||||
inner: Rc<RefCell<WaylandWindowingSystem>>,
|
inner: Rc<RefCell<WaylandWindowingSystem>>,
|
||||||
|
popup_positioning_mode: Rc<RefCell<PopupPositioningMode>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowingSystem {
|
impl WindowingSystem {
|
||||||
|
|
@ -143,9 +234,93 @@ impl WindowingSystem {
|
||||||
);
|
);
|
||||||
let inner = WaylandWindowingSystem::new(wayland_config)?;
|
let inner = WaylandWindowingSystem::new(wayland_config)?;
|
||||||
|
|
||||||
Ok(Self {
|
let system = Self {
|
||||||
inner: Rc::new(RefCell::new(inner)),
|
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]
|
#[must_use]
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue