mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-12-23 13:35:56 +00:00
refactor: popup lifecycle and add more docs
This commit is contained in:
parent
95fb71dfb8
commit
fefb5a4ef3
10 changed files with 329 additions and 306 deletions
|
|
@ -12,6 +12,21 @@ use slint_interpreter::ComponentInstance;
|
||||||
use std::cell::{Cell, OnceCell, RefCell};
|
use std::cell::{Cell, OnceCell, RefCell};
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
|
|
||||||
|
/// Represents the rendering lifecycle state of a popup window
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum PopupRenderState {
|
||||||
|
/// Awaiting Wayland configure event before rendering can begin
|
||||||
|
Unconfigured,
|
||||||
|
/// Wayland is recalculating geometry; rendering is paused
|
||||||
|
Repositioning,
|
||||||
|
/// Ready to render, no pending changes
|
||||||
|
ReadyClean,
|
||||||
|
/// Ready to render, frame is dirty and needs redraw
|
||||||
|
ReadyDirty,
|
||||||
|
/// Needs an additional layout pass after the next render
|
||||||
|
NeedsRelayout,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct PopupWindow {
|
pub struct PopupWindow {
|
||||||
window: Window,
|
window: Window,
|
||||||
renderer: FemtoVGRenderer,
|
renderer: FemtoVGRenderer,
|
||||||
|
|
@ -20,9 +35,7 @@ pub struct PopupWindow {
|
||||||
scale_factor: Cell<f32>,
|
scale_factor: Cell<f32>,
|
||||||
popup_handle: Cell<Option<PopupHandle>>,
|
popup_handle: Cell<Option<PopupHandle>>,
|
||||||
on_close: OnceCell<OnCloseCallback>,
|
on_close: OnceCell<OnCloseCallback>,
|
||||||
configured: Cell<bool>,
|
popup_render_state: Cell<PopupRenderState>,
|
||||||
repositioning: Cell<bool>,
|
|
||||||
needs_relayout: Cell<bool>,
|
|
||||||
component_instance: RefCell<Option<ComponentInstance>>,
|
component_instance: RefCell<Option<ComponentInstance>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,9 +52,7 @@ impl PopupWindow {
|
||||||
scale_factor: Cell::new(1.),
|
scale_factor: Cell::new(1.),
|
||||||
popup_handle: Cell::new(None),
|
popup_handle: Cell::new(None),
|
||||||
on_close: OnceCell::new(),
|
on_close: OnceCell::new(),
|
||||||
configured: Cell::new(false),
|
popup_render_state: Cell::new(PopupRenderState::Unconfigured),
|
||||||
repositioning: Cell::new(false),
|
|
||||||
needs_relayout: Cell::new(false),
|
|
||||||
component_instance: RefCell::new(None),
|
component_instance: RefCell::new(None),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -96,11 +107,26 @@ impl PopupWindow {
|
||||||
|
|
||||||
pub fn mark_configured(&self) {
|
pub fn mark_configured(&self) {
|
||||||
info!("Popup window marked as configured");
|
info!("Popup window marked as configured");
|
||||||
self.configured.set(true);
|
|
||||||
|
if matches!(
|
||||||
|
self.popup_render_state.get(),
|
||||||
|
PopupRenderState::Unconfigured
|
||||||
|
) {
|
||||||
|
info!("Transitioning from Unconfigured to ReadyDirty state");
|
||||||
|
self.popup_render_state.set(PopupRenderState::ReadyDirty);
|
||||||
|
} else {
|
||||||
|
info!(
|
||||||
|
"Preserving current render state to avoid overwriting: {:?}",
|
||||||
|
self.popup_render_state.get()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_configured(&self) -> bool {
|
pub fn is_configured(&self) -> bool {
|
||||||
self.configured.get()
|
!matches!(
|
||||||
|
self.popup_render_state.get(),
|
||||||
|
PopupRenderState::Unconfigured
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_component_instance(&self, instance: ComponentInstance) {
|
pub fn set_component_instance(&self, instance: ComponentInstance) {
|
||||||
|
|
@ -110,6 +136,9 @@ impl PopupWindow {
|
||||||
info!("Component instance already set for popup window - replacing");
|
info!("Component instance already set for popup window - replacing");
|
||||||
}
|
}
|
||||||
*comp = Some(instance);
|
*comp = Some(instance);
|
||||||
|
|
||||||
|
self.window()
|
||||||
|
.dispatch_event(WindowEvent::WindowActiveChanged(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_resize(&self, width: f32, height: f32) {
|
pub fn request_resize(&self, width: f32, height: f32) {
|
||||||
|
|
@ -119,26 +148,33 @@ impl PopupWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn begin_repositioning(&self) {
|
pub fn begin_repositioning(&self) {
|
||||||
self.repositioning.set(true);
|
self.popup_render_state.set(PopupRenderState::Repositioning);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn end_repositioning(&self) {
|
pub fn end_repositioning(&self) {
|
||||||
self.repositioning.set(false);
|
self.popup_render_state.set(PopupRenderState::NeedsRelayout);
|
||||||
self.needs_relayout.set(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderableWindow for PopupWindow {
|
impl RenderableWindow for PopupWindow {
|
||||||
fn render_frame_if_dirty(&self) -> Result<()> {
|
fn render_frame_if_dirty(&self) -> Result<()> {
|
||||||
if !self.configured.get() {
|
match self.popup_render_state.get() {
|
||||||
|
PopupRenderState::Unconfigured => {
|
||||||
info!("Popup not yet configured, skipping render");
|
info!("Popup not yet configured, skipping render");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
PopupRenderState::Repositioning => {
|
||||||
if self.repositioning.get() {
|
|
||||||
info!("Popup repositioning in progress, skipping render");
|
info!("Popup repositioning in progress, skipping render");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
PopupRenderState::ReadyClean => {
|
||||||
|
// Nothing to render
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
PopupRenderState::ReadyDirty | PopupRenderState::NeedsRelayout => {
|
||||||
|
// Proceed with rendering
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
self.render_state.replace(RenderState::Clean),
|
self.render_state.replace(RenderState::Clean),
|
||||||
|
|
@ -156,10 +192,15 @@ impl RenderableWindow for PopupWindow {
|
||||||
})?;
|
})?;
|
||||||
info!("Popup frame rendered successfully");
|
info!("Popup frame rendered successfully");
|
||||||
|
|
||||||
if self.needs_relayout.get() {
|
if matches!(
|
||||||
|
self.popup_render_state.get(),
|
||||||
|
PopupRenderState::NeedsRelayout
|
||||||
|
) {
|
||||||
info!("Popup needs relayout, requesting additional render");
|
info!("Popup needs relayout, requesting additional render");
|
||||||
self.needs_relayout.set(false);
|
self.popup_render_state.set(PopupRenderState::ReadyDirty);
|
||||||
RenderableWindow::request_redraw(self);
|
RenderableWindow::request_redraw(self);
|
||||||
|
} else {
|
||||||
|
self.popup_render_state.set(PopupRenderState::ReadyClean);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -203,6 +244,9 @@ impl WindowAdapter for PopupWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_redraw(&self) {
|
fn request_redraw(&self) {
|
||||||
|
if matches!(self.popup_render_state.get(), PopupRenderState::ReadyClean) {
|
||||||
|
self.popup_render_state.set(PopupRenderState::ReadyDirty);
|
||||||
|
}
|
||||||
RenderableWindow::request_redraw(self);
|
RenderableWindow::request_redraw(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -142,15 +142,29 @@ impl EventContext {
|
||||||
self.active_surface = ActiveWindow::None;
|
self.active_surface = ActiveWindow::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn is_popup_active(&self) -> bool {
|
||||||
|
matches!(self.active_surface, ActiveWindow::Popup(_))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dispatch_to_active_window(&self, event: WindowEvent) {
|
pub fn dispatch_to_active_window(&self, event: WindowEvent) {
|
||||||
match self.active_surface {
|
match self.active_surface {
|
||||||
ActiveWindow::Main => {
|
ActiveWindow::Main => {
|
||||||
self.main_window.window().dispatch_event(event);
|
self.main_window.window().dispatch_event(event);
|
||||||
}
|
}
|
||||||
ActiveWindow::Popup(handle) => {
|
ActiveWindow::Popup(handle) => {
|
||||||
|
let is_pointer_event = matches!(
|
||||||
|
event,
|
||||||
|
WindowEvent::PointerMoved { .. }
|
||||||
|
| WindowEvent::PointerPressed { .. }
|
||||||
|
| WindowEvent::PointerReleased { .. }
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(popup_manager) = &self.popup_manager {
|
if let Some(popup_manager) = &self.popup_manager {
|
||||||
if let Some(popup_surface) = popup_manager.get_popup_window(handle.key()) {
|
if let Some(popup_surface) = popup_manager.get_popup_window(handle.key()) {
|
||||||
popup_surface.dispatch_event(event);
|
popup_surface.dispatch_event(event);
|
||||||
|
if is_pointer_event {
|
||||||
|
popup_surface.request_redraw();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,8 @@ struct ActivePopup {
|
||||||
impl Drop for ActivePopup {
|
impl Drop for ActivePopup {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
info!("ActivePopup being dropped - cleaning up resources");
|
info!("ActivePopup being dropped - cleaning up resources");
|
||||||
|
self.window.cleanup_resources();
|
||||||
|
self.surface.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -424,10 +426,9 @@ impl PopupManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destroy_popup(&self, id: PopupId) {
|
fn destroy_popup(&self, id: PopupId) {
|
||||||
if let Some(popup) = self.state.borrow_mut().popups.remove(&id) {
|
if let Some(_popup) = self.state.borrow_mut().popups.remove(&id) {
|
||||||
info!("Destroying popup with id {:?}", id);
|
info!("Destroying popup with id {:?}", id);
|
||||||
popup.window.cleanup_resources();
|
// cleanup happens automatically via ActivePopup::drop()
|
||||||
popup.surface.destroy();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -264,6 +264,10 @@ impl SurfaceState {
|
||||||
self.event_context.borrow_mut().clear_entered_surface();
|
self.event_context.borrow_mut().clear_entered_surface();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_popup_active(&self) -> bool {
|
||||||
|
self.event_context.borrow().is_popup_active()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dispatch_to_active_window(&self, event: WindowEvent) {
|
pub fn dispatch_to_active_window(&self, event: WindowEvent) {
|
||||||
self.event_context.borrow().dispatch_to_active_window(event);
|
self.event_context.borrow().dispatch_to_active_window(event);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -451,7 +451,7 @@ impl Runtime {
|
||||||
|
|
||||||
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) {
|
||||||
log::error!("Failed to show popup: {}", e);
|
log::error!("Failed to show popup: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -582,11 +582,6 @@ impl Runtime {
|
||||||
&self.compilation_result
|
&self.compilation_result
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn popup(&self, component_name: impl Into<String>) -> PopupBuilder<'_> {
|
|
||||||
PopupBuilder::new(self, component_name.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on<F, R>(&self, surface_name: &str, callback_name: &str, handler: F) -> Result<()>
|
pub fn on<F, R>(&self, surface_name: &str, callback_name: &str, handler: F) -> Result<()>
|
||||||
where
|
where
|
||||||
F: Fn(ShellControl) -> R + 'static,
|
F: Fn(ShellControl) -> R + 'static,
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,26 @@
|
||||||
use crate::Result;
|
|
||||||
use crate::shell::Shell;
|
|
||||||
use layer_shika_adapters::platform::slint_interpreter::Value;
|
|
||||||
use layer_shika_domain::prelude::AnchorStrategy;
|
|
||||||
use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode;
|
use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode;
|
||||||
use layer_shika_domain::value_objects::popup_request::{PopupPlacement, PopupRequest, PopupSize};
|
use layer_shika_domain::value_objects::popup_request::{PopupPlacement, PopupRequest, PopupSize};
|
||||||
|
|
||||||
/// Builder for configuring and displaying popup windows
|
/// Builder for configuring popup windows
|
||||||
///
|
///
|
||||||
/// Useful for context menus, tooltips, dropdowns, and other transient UI.
|
/// This is a convenience wrapper around `PopupRequest::builder()` that provides
|
||||||
pub struct PopupBuilder<'a> {
|
/// a fluent API for configuring popups. Once built, pass the resulting `PopupRequest`
|
||||||
shell: &'a Shell,
|
/// to `ShellControl::show_popup()` from within a callback.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// shell.on("Main", "open_menu", |control| {
|
||||||
|
/// let request = PopupBuilder::new("MenuPopup")
|
||||||
|
/// .relative_to_cursor()
|
||||||
|
/// .anchor_top_left()
|
||||||
|
/// .grab(true)
|
||||||
|
/// .close_on("menu_closed")
|
||||||
|
/// .build();
|
||||||
|
///
|
||||||
|
/// control.show_popup(&request)?;
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
|
pub struct PopupBuilder {
|
||||||
component: String,
|
component: String,
|
||||||
reference: PopupPlacement,
|
reference: PopupPlacement,
|
||||||
anchor: PopupPositioningMode,
|
anchor: PopupPositioningMode,
|
||||||
|
|
@ -19,11 +30,12 @@ pub struct PopupBuilder<'a> {
|
||||||
resize_callback: Option<String>,
|
resize_callback: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PopupBuilder<'a> {
|
impl PopupBuilder {
|
||||||
pub(crate) fn new(shell: &'a Shell, component: String) -> Self {
|
/// Creates a new popup builder for the specified component
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(component: impl Into<String>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
shell,
|
component: component.into(),
|
||||||
component,
|
|
||||||
reference: PopupPlacement::AtCursor,
|
reference: PopupPlacement::AtCursor,
|
||||||
anchor: PopupPositioningMode::TopLeft,
|
anchor: PopupPositioningMode::TopLeft,
|
||||||
size: PopupSize::Content,
|
size: PopupSize::Content,
|
||||||
|
|
@ -168,181 +180,11 @@ impl<'a> PopupBuilder<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Binds the popup to show when the specified Slint callback is triggered
|
/// Builds the popup request
|
||||||
pub fn bind(self, trigger_callback: &str) -> Result<()> {
|
///
|
||||||
let request = self.build_request();
|
/// After building, pass the request to `ShellControl::show_popup()` to display the popup.
|
||||||
let control = self.shell.control();
|
#[must_use]
|
||||||
|
pub fn build(self) -> PopupRequest {
|
||||||
self.shell.with_all_surfaces(|_name, instance| {
|
|
||||||
let request_clone = request.clone();
|
|
||||||
let control_clone = control.clone();
|
|
||||||
|
|
||||||
if let Err(e) = instance.set_callback(trigger_callback, move |_args| {
|
|
||||||
if let Err(e) = control_clone.show_popup(&request_clone) {
|
|
||||||
log::error!("Failed to show popup: {}", e);
|
|
||||||
}
|
|
||||||
Value::Void
|
|
||||||
}) {
|
|
||||||
log::error!(
|
|
||||||
"Failed to bind popup callback '{}': {}",
|
|
||||||
trigger_callback,
|
|
||||||
e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Binds the popup to toggle visibility when the specified callback is triggered
|
|
||||||
pub fn toggle(self, trigger_callback: &str) -> Result<()> {
|
|
||||||
let request = self.build_request();
|
|
||||||
let control = self.shell.control();
|
|
||||||
let component_name = request.component.clone();
|
|
||||||
|
|
||||||
self.shell.with_all_surfaces(|_name, instance| {
|
|
||||||
let request_clone = request.clone();
|
|
||||||
let control_clone = control.clone();
|
|
||||||
let component_clone = component_name.clone();
|
|
||||||
|
|
||||||
if let Err(e) = instance.set_callback(trigger_callback, move |_args| {
|
|
||||||
log::debug!("Toggle callback for component: {}", component_clone);
|
|
||||||
if let Err(e) = control_clone.show_popup(&request_clone) {
|
|
||||||
log::error!("Failed to toggle popup: {}", e);
|
|
||||||
}
|
|
||||||
Value::Void
|
|
||||||
}) {
|
|
||||||
log::error!(
|
|
||||||
"Failed to bind toggle popup callback '{}': {}",
|
|
||||||
trigger_callback,
|
|
||||||
e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
pub fn bind_anchored(self, trigger_callback: &str, strategy: AnchorStrategy) -> Result<()> {
|
|
||||||
let component_name = self.component.clone();
|
|
||||||
let grab = self.grab;
|
|
||||||
let close_callback = self.close_callback.clone();
|
|
||||||
let resize_callback = self.resize_callback.clone();
|
|
||||||
let control = self.shell.control();
|
|
||||||
|
|
||||||
self.shell.with_all_surfaces(|_name, instance| {
|
|
||||||
let component_clone = component_name.clone();
|
|
||||||
let control_clone = control.clone();
|
|
||||||
let close_cb = close_callback.clone();
|
|
||||||
let resize_cb = resize_callback.clone();
|
|
||||||
|
|
||||||
if let Err(e) = instance.set_callback(trigger_callback, move |args| {
|
|
||||||
if args.len() < 4 {
|
|
||||||
log::error!(
|
|
||||||
"bind_anchored callback expects 4 arguments (x, y, width, height), got {}",
|
|
||||||
args.len()
|
|
||||||
);
|
|
||||||
return Value::Void;
|
|
||||||
}
|
|
||||||
|
|
||||||
let anchor_x = args
|
|
||||||
.first()
|
|
||||||
.and_then(|v| v.clone().try_into().ok())
|
|
||||||
.unwrap_or(0.0);
|
|
||||||
let anchor_y = args
|
|
||||||
.get(1)
|
|
||||||
.and_then(|v| v.clone().try_into().ok())
|
|
||||||
.unwrap_or(0.0);
|
|
||||||
let anchor_w = args
|
|
||||||
.get(2)
|
|
||||||
.and_then(|v| v.clone().try_into().ok())
|
|
||||||
.unwrap_or(0.0);
|
|
||||||
let anchor_h = args
|
|
||||||
.get(3)
|
|
||||||
.and_then(|v| v.clone().try_into().ok())
|
|
||||||
.unwrap_or(0.0);
|
|
||||||
|
|
||||||
log::debug!(
|
|
||||||
"Anchored popup triggered for '{}' at rect: ({}, {}, {}, {})",
|
|
||||||
component_clone,
|
|
||||||
anchor_x,
|
|
||||||
anchor_y,
|
|
||||||
anchor_w,
|
|
||||||
anchor_h
|
|
||||||
);
|
|
||||||
|
|
||||||
let (reference_x, reference_y, mode) = match strategy {
|
|
||||||
AnchorStrategy::CenterBottom => {
|
|
||||||
let center_x = anchor_x + anchor_w / 2.0;
|
|
||||||
let bottom_y = anchor_y + anchor_h;
|
|
||||||
(center_x, bottom_y, PopupPositioningMode::TopCenter)
|
|
||||||
}
|
|
||||||
AnchorStrategy::CenterTop => {
|
|
||||||
let center_x = anchor_x + anchor_w / 2.0;
|
|
||||||
(center_x, anchor_y, PopupPositioningMode::BottomCenter)
|
|
||||||
}
|
|
||||||
AnchorStrategy::RightBottom => {
|
|
||||||
let right_x = anchor_x + anchor_w;
|
|
||||||
let bottom_y = anchor_y + anchor_h;
|
|
||||||
(right_x, bottom_y, PopupPositioningMode::TopRight)
|
|
||||||
}
|
|
||||||
AnchorStrategy::LeftTop => {
|
|
||||||
(anchor_x, anchor_y, PopupPositioningMode::BottomLeft)
|
|
||||||
}
|
|
||||||
AnchorStrategy::RightTop => {
|
|
||||||
let right_x = anchor_x + anchor_w;
|
|
||||||
(right_x, anchor_y, PopupPositioningMode::BottomRight)
|
|
||||||
}
|
|
||||||
AnchorStrategy::LeftBottom => {
|
|
||||||
let bottom_y = anchor_y + anchor_h;
|
|
||||||
(anchor_x, bottom_y, PopupPositioningMode::TopLeft)
|
|
||||||
}
|
|
||||||
AnchorStrategy::Cursor => (anchor_x, anchor_y, PopupPositioningMode::TopLeft),
|
|
||||||
};
|
|
||||||
|
|
||||||
log::debug!(
|
|
||||||
"Resolved anchored popup reference for '{}' -> ({}, {}), mode: {:?}",
|
|
||||||
component_clone,
|
|
||||||
reference_x,
|
|
||||||
reference_y,
|
|
||||||
mode
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut builder = PopupRequest::builder(component_clone.clone())
|
|
||||||
.placement(PopupPlacement::at_position(reference_x, reference_y))
|
|
||||||
.size(PopupSize::Content)
|
|
||||||
.grab(grab)
|
|
||||||
.mode(mode);
|
|
||||||
|
|
||||||
if let Some(ref close_cb) = close_cb {
|
|
||||||
builder = builder.close_on(close_cb.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ref resize_cb) = resize_cb {
|
|
||||||
builder = builder.resize_on(resize_cb.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let request = builder.build();
|
|
||||||
|
|
||||||
if let Err(e) = control_clone.show_popup(&request) {
|
|
||||||
log::error!("Failed to show anchored popup: {}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::Void
|
|
||||||
}) {
|
|
||||||
log::error!(
|
|
||||||
"Failed to bind anchored popup callback '{}': {}",
|
|
||||||
trigger_callback,
|
|
||||||
e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_request(&self) -> PopupRequest {
|
|
||||||
let mut builder = PopupRequest::builder(self.component.clone())
|
let mut builder = PopupRequest::builder(self.component.clone())
|
||||||
.placement(self.reference)
|
.placement(self.reference)
|
||||||
.size(self.size)
|
.size(self.size)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::event_loop::{EventLoopHandle, FromAppState};
|
use crate::event_loop::{EventLoopHandle, FromAppState};
|
||||||
use crate::layer_surface::LayerSurfaceHandle;
|
use crate::layer_surface::LayerSurfaceHandle;
|
||||||
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};
|
||||||
|
|
@ -580,11 +579,11 @@ impl Shell {
|
||||||
fn handle_popup_command(
|
fn handle_popup_command(
|
||||||
command: PopupCommand,
|
command: PopupCommand,
|
||||||
ctx: &mut EventDispatchContext<'_>,
|
ctx: &mut EventDispatchContext<'_>,
|
||||||
control: &ShellControl,
|
_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) {
|
||||||
log::error!("Failed to show popup: {}", e);
|
log::error!("Failed to show popup: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -963,12 +962,6 @@ impl Shell {
|
||||||
&self.compilation_result
|
&self.compilation_result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a popup builder for showing a popup window
|
|
||||||
#[must_use]
|
|
||||||
pub fn popup(&self, component_name: impl Into<String>) -> PopupBuilder<'_> {
|
|
||||||
PopupBuilder::new(self, component_name.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the registry of all connected outputs
|
/// Returns the registry of all connected outputs
|
||||||
pub fn output_registry(&self) -> OutputRegistry {
|
pub fn output_registry(&self) -> OutputRegistry {
|
||||||
let system = self.inner.borrow();
|
let system = self.inner.borrow();
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,59 @@ impl CallbackContext {
|
||||||
self.control
|
self.control
|
||||||
.surface_by_name_and_output(&self.surface_name, self.output_handle())
|
.surface_by_name_and_output(&self.surface_name, self.output_handle())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shows a popup from a popup request
|
||||||
|
///
|
||||||
|
/// Convenience method that forwards to the underlying `ShellControl`.
|
||||||
|
/// See [`ShellControl::show_popup`] for full documentation.
|
||||||
|
pub fn show_popup(&self, request: &PopupRequest) -> Result<()> {
|
||||||
|
self.control.show_popup(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shows a popup at the current cursor position
|
||||||
|
///
|
||||||
|
/// Convenience method that forwards to the underlying `ShellControl`.
|
||||||
|
/// See [`ShellControl::show_popup_at_cursor`] for full documentation.
|
||||||
|
pub fn show_popup_at_cursor(&self, component: impl Into<String>) -> Result<()> {
|
||||||
|
self.control.show_popup_at_cursor(component)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shows a popup centered on screen
|
||||||
|
///
|
||||||
|
/// Convenience method that forwards to the underlying `ShellControl`.
|
||||||
|
/// See [`ShellControl::show_popup_centered`] for full documentation.
|
||||||
|
pub fn show_popup_centered(&self, component: impl Into<String>) -> Result<()> {
|
||||||
|
self.control.show_popup_centered(component)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shows a popup at the specified absolute position
|
||||||
|
///
|
||||||
|
/// Convenience method that forwards to the underlying `ShellControl`.
|
||||||
|
/// See [`ShellControl::show_popup_at_position`] for full documentation.
|
||||||
|
pub fn show_popup_at_position(
|
||||||
|
&self,
|
||||||
|
component: impl Into<String>,
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.control.show_popup_at_position(component, x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Closes a specific popup by its handle
|
||||||
|
///
|
||||||
|
/// Convenience method that forwards to the underlying `ShellControl`.
|
||||||
|
/// See [`ShellControl::close_popup`] for full documentation.
|
||||||
|
pub fn close_popup(&self, handle: PopupHandle) -> Result<()> {
|
||||||
|
self.control.close_popup(handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resizes a popup to the specified dimensions
|
||||||
|
///
|
||||||
|
/// Convenience method that forwards to the underlying `ShellControl`.
|
||||||
|
/// See [`ShellControl::resize_popup`] for full documentation.
|
||||||
|
pub fn resize_popup(&self, handle: PopupHandle, width: f32, height: f32) -> Result<()> {
|
||||||
|
self.control.resize_popup(handle, width, height)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle for runtime control of shell operations
|
/// Handle for runtime control of shell operations
|
||||||
|
|
@ -172,6 +225,41 @@ impl ShellControl {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shows a popup from a popup request
|
/// Shows a popup from a popup request
|
||||||
|
///
|
||||||
|
/// This is the primary API for showing popups from Slint callbacks. Popups are
|
||||||
|
/// transient windows that appear above the main surface, commonly used for menus,
|
||||||
|
/// tooltips, dropdowns, and other temporary UI elements.
|
||||||
|
///
|
||||||
|
/// # Content-Based Sizing
|
||||||
|
///
|
||||||
|
/// When using `PopupSize::Content`, you must configure a resize callback via
|
||||||
|
/// `resize_on()` to enable automatic resizing. The popup component should use a
|
||||||
|
/// `Timer` with `interval: 1ms` to invoke the resize callback after initialization,
|
||||||
|
/// ensuring the component is initialized before callback invocation. This allows the
|
||||||
|
/// popup to reposition itself to fit the content. See the `popup-demo` example.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// shell.on("Main", "open_menu", |control| {
|
||||||
|
/// let request = PopupRequest::builder("MenuPopup")
|
||||||
|
/// .placement(PopupPlacement::at_cursor())
|
||||||
|
/// .grab(true)
|
||||||
|
/// .close_on("menu_closed")
|
||||||
|
/// .build();
|
||||||
|
///
|
||||||
|
/// control.show_popup(&request)?;
|
||||||
|
/// Value::Void
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # See Also
|
||||||
|
///
|
||||||
|
/// - [`show_popup_at_cursor`](Self::show_popup_at_cursor) - Convenience method for cursor-positioned popups
|
||||||
|
/// - [`show_popup_centered`](Self::show_popup_centered) - Convenience method for centered popups
|
||||||
|
/// - [`show_popup_at_position`](Self::show_popup_at_position) - Convenience method for absolute positioning
|
||||||
|
/// - [`PopupRequest`] - Full popup configuration options
|
||||||
|
/// - [`PopupBuilder`] - Fluent API for building popup requests
|
||||||
pub fn show_popup(&self, request: &PopupRequest) -> Result<()> {
|
pub fn show_popup(&self, request: &PopupRequest) -> Result<()> {
|
||||||
self.sender
|
self.sender
|
||||||
.send(ShellCommand::Popup(PopupCommand::Show(request.clone())))
|
.send(ShellCommand::Popup(PopupCommand::Show(request.clone())))
|
||||||
|
|
@ -183,6 +271,19 @@ impl ShellControl {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shows a popup at the current cursor position
|
/// Shows a popup at the current cursor position
|
||||||
|
///
|
||||||
|
/// Convenience method for showing a popup at the cursor with default settings.
|
||||||
|
/// For more control over popup positioning, sizing, and behavior, use
|
||||||
|
/// [`show_popup`](Self::show_popup) with a [`PopupRequest`].
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// shell.on("Main", "context_menu", |control| {
|
||||||
|
/// control.show_popup_at_cursor("ContextMenu")?;
|
||||||
|
/// Value::Void
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
pub fn show_popup_at_cursor(&self, component: impl Into<String>) -> Result<()> {
|
pub fn show_popup_at_cursor(&self, component: impl Into<String>) -> Result<()> {
|
||||||
let request = PopupRequest::builder(component.into())
|
let request = PopupRequest::builder(component.into())
|
||||||
.placement(PopupPlacement::AtCursor)
|
.placement(PopupPlacement::AtCursor)
|
||||||
|
|
@ -191,6 +292,18 @@ impl ShellControl {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shows a popup centered on screen
|
/// Shows a popup centered on screen
|
||||||
|
///
|
||||||
|
/// Convenience method for showing a centered popup. Useful for dialogs
|
||||||
|
/// and modal content that should appear in the middle of the screen.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// shell.on("Main", "show_dialog", |control| {
|
||||||
|
/// control.show_popup_centered("ConfirmDialog")?;
|
||||||
|
/// Value::Void
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
pub fn show_popup_centered(&self, component: impl Into<String>) -> Result<()> {
|
pub fn show_popup_centered(&self, component: impl Into<String>) -> Result<()> {
|
||||||
let request = PopupRequest::builder(component.into())
|
let request = PopupRequest::builder(component.into())
|
||||||
.placement(PopupPlacement::AtCursor)
|
.placement(PopupPlacement::AtCursor)
|
||||||
|
|
@ -199,7 +312,19 @@ impl ShellControl {
|
||||||
self.show_popup(&request)
|
self.show_popup(&request)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shows a popup at the specified position
|
/// Shows a popup at the specified absolute position
|
||||||
|
///
|
||||||
|
/// Convenience method for showing a popup at an exact screen coordinate.
|
||||||
|
/// The position is in logical pixels relative to the surface origin.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// shell.on("Main", "show_tooltip", |control| {
|
||||||
|
/// control.show_popup_at_position("Tooltip", 100.0, 50.0)?;
|
||||||
|
/// Value::Void
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
pub fn show_popup_at_position(
|
pub fn show_popup_at_position(
|
||||||
&self,
|
&self,
|
||||||
component: impl Into<String>,
|
component: impl Into<String>,
|
||||||
|
|
@ -212,7 +337,23 @@ impl ShellControl {
|
||||||
self.show_popup(&request)
|
self.show_popup(&request)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Closes a popup by its handle
|
/// Closes a specific popup by its handle
|
||||||
|
///
|
||||||
|
/// Use this when you need to close a specific popup that you opened previously.
|
||||||
|
/// The handle is returned by [`show_popup`](Self::show_popup) and related methods.
|
||||||
|
///
|
||||||
|
/// For closing popups from within the popup itself, consider using the
|
||||||
|
/// `close_on` callback configuration in [`PopupRequest`] instead.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// // Store handle when showing popup
|
||||||
|
/// let handle = context.show_popup(&request)?;
|
||||||
|
///
|
||||||
|
/// // Later, close it
|
||||||
|
/// control.close_popup(handle)?;
|
||||||
|
/// ```
|
||||||
pub fn close_popup(&self, handle: PopupHandle) -> Result<()> {
|
pub fn close_popup(&self, handle: PopupHandle) -> Result<()> {
|
||||||
self.sender
|
self.sender
|
||||||
.send(ShellCommand::Popup(PopupCommand::Close(handle)))
|
.send(ShellCommand::Popup(PopupCommand::Close(handle)))
|
||||||
|
|
@ -224,6 +365,22 @@ impl ShellControl {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resizes a popup to the specified dimensions
|
/// Resizes a popup to the specified dimensions
|
||||||
|
///
|
||||||
|
/// Dynamically changes the size of an active popup. This is typically used
|
||||||
|
/// in response to content changes or user interaction.
|
||||||
|
///
|
||||||
|
/// For automatic content-based sizing, use `PopupSize::Content` with the
|
||||||
|
/// `resize_on` callback configuration in [`PopupRequest`] instead.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// shell.on("Main", "expand_menu", |control| {
|
||||||
|
/// // Assuming we have the popup handle stored somewhere
|
||||||
|
/// control.resize_popup(menu_handle, 400.0, 600.0)?;
|
||||||
|
/// Value::Void
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
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(ShellCommand::Popup(PopupCommand::Resize {
|
.send(ShellCommand::Popup(PopupCommand::Resize {
|
||||||
|
|
@ -722,11 +879,11 @@ impl EventDispatchContext<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shows a popup from a popup request
|
/// Shows a popup from a popup request
|
||||||
pub fn show_popup(
|
///
|
||||||
&mut self,
|
/// Resize callbacks (if configured via `resize_on()`) will operate directly
|
||||||
req: &PopupRequest,
|
/// on the popup manager for immediate updates.
|
||||||
resize_control: Option<ShellControl>,
|
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
|
||||||
) -> Result<PopupHandle> {
|
pub fn show_popup(&mut self, req: &PopupRequest) -> Result<PopupHandle> {
|
||||||
log::info!("show_popup called for component '{}'", req.component);
|
log::info!("show_popup called for component '{}'", req.component);
|
||||||
|
|
||||||
let compilation_result = self.compilation_result().ok_or_else(|| {
|
let compilation_result = self.compilation_result().ok_or_else(|| {
|
||||||
|
|
@ -779,32 +936,67 @@ impl EventDispatchContext<'_> {
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
// For content-based sizing, we need to query the component's preferred size first
|
||||||
let initial_dimensions = match req.size {
|
let initial_dimensions = match req.size {
|
||||||
PopupSize::Fixed { w, h } => {
|
PopupSize::Fixed { w, h } => {
|
||||||
log::debug!("Using fixed popup size: {}x{}", w, h);
|
log::debug!("Using fixed popup size: {}x{}", w, h);
|
||||||
(w, h)
|
(w, h)
|
||||||
}
|
}
|
||||||
PopupSize::Content => {
|
PopupSize::Content => {
|
||||||
log::debug!("Using content-based sizing - will measure after instance creation");
|
log::debug!("Using content-based sizing - starting at 2×2");
|
||||||
|
// Start with minimal size. Consumer app should register a callback to
|
||||||
|
// call resize_popup() with the desired dimensions.
|
||||||
(2.0, 2.0)
|
(2.0, 2.0)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let resolved_placement = match req.placement {
|
||||||
|
PopupPlacement::AtCursor => {
|
||||||
|
let cursor_pos = active_surface.current_pointer_position();
|
||||||
|
log::debug!(
|
||||||
|
"Resolving AtCursor placement to actual cursor position: ({}, {})",
|
||||||
|
cursor_pos.x,
|
||||||
|
cursor_pos.y
|
||||||
|
);
|
||||||
|
PopupPlacement::AtPosition {
|
||||||
|
x: cursor_pos.x,
|
||||||
|
y: cursor_pos.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
other => other,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (ref_x, ref_y) = resolved_placement.position();
|
||||||
|
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Creating popup for '{}' with dimensions {}x{} at position ({}, {}), mode: {:?}",
|
"Creating popup for '{}' with dimensions {}x{} at position ({}, {}), mode: {:?}",
|
||||||
req.component,
|
req.component,
|
||||||
initial_dimensions.0,
|
initial_dimensions.0,
|
||||||
initial_dimensions.1,
|
initial_dimensions.1,
|
||||||
req.placement.position().0,
|
ref_x,
|
||||||
req.placement.position().1,
|
ref_y,
|
||||||
req.mode
|
req.mode
|
||||||
);
|
);
|
||||||
|
|
||||||
let popup_handle =
|
// Create a new request with resolved placement
|
||||||
popup_manager.request_popup(req.clone(), initial_dimensions.0, initial_dimensions.1);
|
let resolved_request = PopupRequest {
|
||||||
|
component: req.component.clone(),
|
||||||
|
placement: resolved_placement,
|
||||||
|
size: req.size,
|
||||||
|
mode: req.mode,
|
||||||
|
grab: req.grab,
|
||||||
|
close_callback: req.close_callback.clone(),
|
||||||
|
resize_callback: req.resize_callback.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let popup_handle = popup_manager.request_popup(
|
||||||
|
resolved_request,
|
||||||
|
initial_dimensions.0,
|
||||||
|
initial_dimensions.1,
|
||||||
|
);
|
||||||
|
|
||||||
let (instance, popup_key_cell) =
|
let (instance, popup_key_cell) =
|
||||||
Self::create_popup_instance(&definition, &popup_manager, resize_control, req)?;
|
Self::create_popup_instance(&definition, &popup_manager, req)?;
|
||||||
|
|
||||||
popup_key_cell.set(popup_handle.key());
|
popup_key_cell.set(popup_handle.key());
|
||||||
|
|
||||||
|
|
@ -894,7 +1086,6 @@ impl EventDispatchContext<'_> {
|
||||||
fn create_popup_instance(
|
fn create_popup_instance(
|
||||||
definition: &ComponentDefinition,
|
definition: &ComponentDefinition,
|
||||||
popup_manager: &Rc<PopupManager>,
|
popup_manager: &Rc<PopupManager>,
|
||||||
resize_control: Option<ShellControl>,
|
|
||||||
req: &PopupRequest,
|
req: &PopupRequest,
|
||||||
) -> Result<(ComponentInstance, Rc<Cell<usize>>)> {
|
) -> Result<(ComponentInstance, Rc<Cell<usize>>)> {
|
||||||
let instance = definition.create().map_err(|e| {
|
let instance = definition.create().map_err(|e| {
|
||||||
|
|
@ -905,13 +1096,7 @@ impl EventDispatchContext<'_> {
|
||||||
|
|
||||||
let popup_key_cell = Rc::new(Cell::new(0));
|
let popup_key_cell = Rc::new(Cell::new(0));
|
||||||
|
|
||||||
Self::register_popup_callbacks(
|
Self::register_popup_callbacks(&instance, popup_manager, &popup_key_cell, req)?;
|
||||||
&instance,
|
|
||||||
popup_manager,
|
|
||||||
resize_control,
|
|
||||||
&popup_key_cell,
|
|
||||||
req,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
instance.show().map_err(|e| {
|
instance.show().map_err(|e| {
|
||||||
Error::Domain(DomainError::Configuration {
|
Error::Domain(DomainError::Configuration {
|
||||||
|
|
@ -925,7 +1110,6 @@ impl EventDispatchContext<'_> {
|
||||||
fn register_popup_callbacks(
|
fn register_popup_callbacks(
|
||||||
instance: &ComponentInstance,
|
instance: &ComponentInstance,
|
||||||
popup_manager: &Rc<PopupManager>,
|
popup_manager: &Rc<PopupManager>,
|
||||||
resize_control: Option<ShellControl>,
|
|
||||||
popup_key_cell: &Rc<Cell<usize>>,
|
popup_key_cell: &Rc<Cell<usize>>,
|
||||||
req: &PopupRequest,
|
req: &PopupRequest,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
|
@ -934,10 +1118,9 @@ impl EventDispatchContext<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(resize_callback_name) = &req.resize_callback {
|
if let Some(resize_callback_name) = &req.resize_callback {
|
||||||
Self::register_resize_callback(
|
Self::register_resize_direct(
|
||||||
instance,
|
instance,
|
||||||
popup_manager,
|
popup_manager,
|
||||||
resize_control,
|
|
||||||
popup_key_cell,
|
popup_key_cell,
|
||||||
resize_callback_name,
|
resize_callback_name,
|
||||||
)?;
|
)?;
|
||||||
|
|
@ -966,59 +1149,6 @@ impl EventDispatchContext<'_> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_resize_callback(
|
|
||||||
instance: &ComponentInstance,
|
|
||||||
popup_manager: &Rc<PopupManager>,
|
|
||||||
resize_control: Option<ShellControl>,
|
|
||||||
popup_key_cell: &Rc<Cell<usize>>,
|
|
||||||
callback_name: &str,
|
|
||||||
) -> Result<()> {
|
|
||||||
if let Some(control) = resize_control {
|
|
||||||
Self::register_resize_with_control(instance, popup_key_cell, &control, callback_name)
|
|
||||||
} else {
|
|
||||||
Self::register_resize_direct(instance, popup_manager, popup_key_cell, callback_name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_resize_with_control(
|
|
||||||
instance: &ComponentInstance,
|
|
||||||
popup_key_cell: &Rc<Cell<usize>>,
|
|
||||||
control: &ShellControl,
|
|
||||||
callback_name: &str,
|
|
||||||
) -> Result<()> {
|
|
||||||
let key_cell = Rc::clone(popup_key_cell);
|
|
||||||
let control = control.clone();
|
|
||||||
instance
|
|
||||||
.set_callback(callback_name, move |args| {
|
|
||||||
let dimensions = extract_dimensions_from_callback(args);
|
|
||||||
let popup_key = key_cell.get();
|
|
||||||
|
|
||||||
log::info!(
|
|
||||||
"Resize callback invoked: {}x{} for key {}",
|
|
||||||
dimensions.width,
|
|
||||||
dimensions.height,
|
|
||||||
popup_key
|
|
||||||
);
|
|
||||||
|
|
||||||
if control
|
|
||||||
.resize_popup(
|
|
||||||
PopupHandle::from_raw(popup_key),
|
|
||||||
dimensions.width,
|
|
||||||
dimensions.height,
|
|
||||||
)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
log::error!("Failed to resize popup through control");
|
|
||||||
}
|
|
||||||
Value::Void
|
|
||||||
})
|
|
||||||
.map_err(|e| {
|
|
||||||
Error::Domain(DomainError::Configuration {
|
|
||||||
message: format!("Failed to set '{}' callback: {}", callback_name, e),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_resize_direct(
|
fn register_resize_direct(
|
||||||
instance: &ComponentInstance,
|
instance: &ComponentInstance,
|
||||||
popup_manager: &Rc<PopupManager>,
|
popup_manager: &Rc<PopupManager>,
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ pub use shell::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use window::{
|
pub use window::{
|
||||||
AnchorEdges, AnchorStrategy, KeyboardInteractivity, Layer, PopupHandle, PopupPlacement,
|
AnchorEdges, AnchorStrategy, KeyboardInteractivity, Layer, PopupBuilder, PopupHandle, PopupPlacement,
|
||||||
PopupPositioningMode, PopupRequest, PopupSize,
|
PopupPositioningMode, PopupRequest, PopupSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
pub use layer_shika_composition::{
|
pub use layer_shika_composition::{
|
||||||
AnchorEdges, AnchorStrategy, KeyboardInteractivity, Layer, PopupHandle, PopupPlacement,
|
AnchorEdges, AnchorStrategy, KeyboardInteractivity, Layer, PopupBuilder, PopupHandle,
|
||||||
PopupPositioningMode, PopupRequest, PopupSize,
|
PopupPlacement, PopupPositioningMode, PopupRequest, PopupSize,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue