Skip to main content

floem/views/
overlay.rs

1//! Declarative overlay view that renders content above all other views.
2//!
3//! This module provides a declarative overlay that automatically manages
4//! the overlay lifecycle, without requiring manual `add_overlay`/`remove_overlay` calls.
5
6use floem_reactive::Scope;
7
8use crate::{
9    style::Style,
10    view::{ParentView, View, ViewId},
11};
12
13/// A declarative overlay that renders content above all other views.
14///
15/// The overlay content remains in the view tree as a child of this view,
16/// maintaining proper parent-child lifetime semantics. Floem reparents overlay
17/// nodes in the box tree to the window root so they participate in normal
18/// z-index ordering above regular content.
19///
20/// Visibility can be controlled through styles on the content itself.
21///
22/// ## Example
23/// ```rust
24/// use floem::prelude::*;
25/// use floem::views::{Label, Overlay, Decorators};
26///
27/// let show_dialog = RwSignal::new(false);
28///
29/// Stack::vertical((
30///     Button::new("Show Dialog").action(move || show_dialog.set(true)),
31///     Overlay::new_dyn(move || {
32///         let visible = show_dialog.get();
33///         Stack::vertical((
34///             Label::derived(|| "This is a dialog!".to_string()),
35///             Button::new("Close").action(move || show_dialog.set(false)),
36///         ))
37///         .style(move |s| {
38///             s.apply_if(!visible, |s| s.hide())
39///                 .background(Color::WHITE)
40///                 .padding(20)
41///                 .border_radius(8)
42///         })
43///     }),
44/// ));
45/// ```
46///
47/// ## Notes
48/// - The overlay is positioned absolutely at the window level
49/// - Overlay uses a default `z-index: 1` so it sorts above non-overlay content
50/// - You can style the overlay content using the view returned by `Overlay::new(...)`
51/// - The overlay is automatically removed when this view is cleaned up
52pub struct Overlay {
53    id: ViewId,
54    scope: Scope,
55}
56
57impl Overlay {
58    /// Creates a new overlay.
59    ///
60    ///
61    /// # Example
62    /// ```rust
63    /// use floem::prelude::*;
64    /// use floem::views::{Overlay, Label};
65    ///
66    /// Overlay::new("Static overlay content");
67    /// ```
68    pub fn new(child: impl crate::IntoView + 'static) -> Self {
69        let id = ViewId::new();
70        id.add_child(child.into_any());
71        Self::with_id(id)
72    }
73
74    /// Creates a new overlay with no child.
75    ///
76    ///
77    /// # Example
78    /// ```rust
79    /// use floem::prelude::*;
80    /// use floem::views::Overlay;
81    ///
82    /// Overlay::base();
83    /// ```
84    pub fn base() -> Self {
85        let id = ViewId::new();
86        Self::with_id(id)
87    }
88
89    /// Creates a new overlay whose child will dynamically update in response to signal changes.
90    ///
91    /// # Example
92    /// ```rust
93    /// use floem::prelude::*;
94    /// use floem::views::{Label, Overlay};
95    ///
96    /// let message = RwSignal::new("Loading...".to_string());
97    ///
98    /// Overlay::new_dyn(move || Label::new(message.get()));
99    /// ```
100    pub fn new_dyn<CF, V>(child_fn: CF) -> Self
101    where
102        CF: Fn() -> V + 'static,
103        V: crate::IntoView + 'static,
104    {
105        Self::with_id(ViewId::new()).derived_child(child_fn)
106    }
107
108    /// Creates a new empty overlay with a specific ViewId.
109    ///
110    /// This is useful when you need to control the ViewId for the overlay.
111    ///
112    /// # Arguments
113    /// * `id` - The ViewId to use for this overlay
114    pub fn with_id(id: ViewId) -> Self {
115        let scope = Scope::current().create_child();
116        id.register_overlay();
117        Overlay { id, scope }
118    }
119}
120
121impl Default for Overlay {
122    fn default() -> Self {
123        Self::new(())
124    }
125}
126
127impl View for Overlay {
128    fn id(&self) -> ViewId {
129        self.id
130    }
131
132    fn view_style(&self) -> Option<Style> {
133        Some(Style::new().z_index(1))
134    }
135
136    fn debug_name(&self) -> std::borrow::Cow<'static, str> {
137        "Overlay".into()
138    }
139}
140
141impl ParentView for Overlay {
142    fn scope(&self) -> Option<Scope> {
143        Some(self.scope)
144    }
145}
146
147/// A trait that adds an `overlay` method to any type that implements `IntoView`.
148pub trait OverlayExt {
149    /// Wrap the view in an overlay.
150    fn overlay(self) -> Overlay;
151}
152
153impl<T: crate::IntoView + 'static> OverlayExt for T {
154    fn overlay(self) -> Overlay {
155        Overlay::new(self)
156    }
157}