Floating Panels
Display floating web content above a window layout for toasts, dialogs, and dropdowns.
On this page
You can display web content that floats above a window's layout without shifting the panels beneath it. This works well for toasts, heads-up displays, in-window dialogs, command palettes, and dropdown menus.
Floating panels clip to the window frame. For menus and dropdown lists that need to extend outside the window, use Popups and Dialogs instead.
Adding a Floating Panel
Call Foreground::AddPanel() on Window::foreground() to create a floating panel with a new View:
#include <AppCore/Window.h>
#include <Ultralight/DOM.h>
using namespace ultralight;
class MyApp {
public:
void ShowHUD() {
///
/// Float a small HUD 24px in from the window's top-left corner.
///
hud_ = window_->foreground()->AddPanel({
.width = "200px", .height = "120px", .placement = Anchor::At(24, 24) });
hud_->view()->LoadHTML("<h1>Ultralight rocks!</h1>");
}
void OpenDropdown(dom::Element button);
private:
RefPtr<Window> window_;
RefPtr<Panel> toolbar_;
RefPtr<Panel> hud_;
RefPtr<Panel> dropdown_;
LayoutCallback on_dismiss_;
};
A floating panel is an ordinary Panel without a parent container— everything in Laying Out Panels applies.
Setting Size and Visibility
Set width and height in ForegroundPanelOptions using logical pixels or a percentage of the window. Unset dimensions default to 100%— passing empty options creates a panel that covers the entire window.
Set hidden = true to create the panel hidden. Call Panel::Show() once the page finishes loading to display it.
To configure the new View yourself, pass a ViewConfig as the second argument to Foreground::AddPanel().
Adopting an Existing View
Call Foreground::AdoptPanel() to float a View you already created. You can pass ForegroundPanelOptions as the optional second argument to set the panel's size and placement.
Positioning with Anchors
The placement option takes an Anchor that determines where the floating panel sits inside the window:
| Form | Placement |
|---|---|
Anchor::At(x, y) |
Places the top-left corner at an absolute window position. |
Anchor::WindowCorner(corner) |
Snaps to a window corner and follows the window during resize. |
Anchor::WindowCenter() |
Centers the panel in the window and follows window resizes. |
Anchor::Fill() |
Places the panel at the window origin, filling the window when combined with default sizes. |
Anchor::Below(target, rect) |
Hangs the panel's top edge under a rectangle inside another panel. |
You can call Offset(dx, dy) on any anchor form to nudge the final position— for example, Anchor::WindowCorner(AnchorCorner::BottomRight).Offset(-16, -16) moves the panel inward from the bottom-right corner.
📘 Moving or Resizing Panels
A floating panel's placement and size are fixed at creation. To move or resize a panel, keep a reference to its View with
Panel::view()before callingForeground::Remove()—Panel::view()returns null once the panel is removed. Pass that View toForeground::AdoptPanel()with the new placement to keep the page loaded.
Anchoring Below an Element
To anchor a floating panel to a DOM element in another panel, pass the element's bounding rectangle to Anchor::Below():
///
/// Open a dropdown under a button on the toolbar's page.
///
void MyApp::OpenDropdown(dom::Element button) {
dom::DOMRect box = button.getBoundingClientRect();
///
/// Hang the panel's top edge under the button with left edges aligned,
/// flipping above the button when there is no room below.
///
dropdown_ = window_->foreground()->AddPanel({
.width = "240px", .height = "180px",
.placement = Anchor::Below(toolbar_,
Rect::FromXYWH(box.x, box.y, box.width,
box.height))
.Align(AnchorAlign::Start)
.Fit(AnchorFit::Flip),
.dismiss = Dismiss::Auto,
.focus = FocusPolicy::Grab });
dropdown_->view()->LoadURL("file:///dropdown.html");
}
The anchor rectangle uses the target panel's local logical pixels, which match the page's CSS pixels— so a bounding box from Element Geometry and Scrolling passes straight through. The library re-derives this placement every frame as the target panel moves.
You can refine the placement with Align() (AnchorAlign::Start, Center, or End) and call Fit(AnchorFit::Flip) to flip the panel above the element when there isn't enough room below.
Dismissing Automatically
Floating panels can close automatically when someone interacts with the window, behaving like native menus and dropdowns.
Enabling Auto-Dismiss
Floating panels use Dismiss::Manual by default— they stay visible until you hide them.
Pass dismiss = Dismiss::Auto in ForegroundPanelOptions to enable automatic dismissal. The library hides the panel when:
- A mouse press occurs outside the panel.
- The user presses Esc.
- The window moves, resizes, minimizes, maximizes, or deactivates.
- The user presses the title bar or window frame edges.
Scrolling content inside the window does not dismiss the panel.
Dismissal Behavior
Dismissing a floating panel hides it rather than destroying it— calling Panel::Show() brings it back.
The press that dismisses the panel is swallowed, so the page beneath never receives it.
Reacting to Dismissals
Call Panel::OnDismiss() to respond when the library dismisses the panel:
///
/// Fires only when the library dismissed the dropdown.
///
on_dismiss_ = dropdown_->OnDismiss([] {
// The panel is already hidden; update application state here.
});
This callback fires only for automatic dismissals— calling Panel::Hide() yourself does not trigger it.
Keep the returned LayoutCallback alive for as long as the callback should fire. Destroying the object unregisters the callback.
Managing Keyboard Focus
The focus option controls how a floating panel receives keyboard focus:
| Policy | Behavior |
|---|---|
FocusPolicy::Auto |
Focuses on click (the default). Showing the panel never takes keyboard focus. |
FocusPolicy::Grab |
Takes keyboard focus each time the panel is shown, even from inside a click handler. Suited for dropdowns, palettes, and in-window dialogs. |
FocusPolicy::None |
Never takes keyboard focus. Suited for toasts and heads-up displays. |
To focus a panel, call Panel::Focus() rather than View::Focus(), which ensures the window routes keyboard input properly. Calling Panel::Focus() on a hidden panel or a panel configured with FocusPolicy::None is ignored with a warning.
Hiding a focused floating panel releases keyboard focus— no panel holds focus until the user clicks one or you call Panel::Focus().
Stacking and Removing Panels
Floating panels stack in the order they're added, with the latest panel on top. Call Panel::BringToFront() to move a panel to the top of the floating layer— it receives mouse clicks first and paints last, on top of the others.
To remove a panel from the floating layer, call Foreground::Remove(). Dropping the panel handle does not remove it from the layer. If you hold a RefPtr to the panel's View, the View survives removal and can be adopted by another panel.