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}