Window Creation and Control

Windows are created by WindowOption, returning the current window's WindowId after successful creation.

use flor::windows::WindowOption;
use flor_lys::label::label;

let window_id = WindowOption {
    title: "Hello Flor".to_string(),
    width: 800,
    height: 600,
    ..WindowOption::default()
}
.open(move |_window_id| {
    label("hello flor ~")
})?;

WindowOption handles window's initial configuration:

pub struct WindowOption {
    pub title: String,
    pub width: u32,
    pub height: u32,
    pub rem_px: f32,
    pub wait_v_sync: bool,
    pub show_fps: bool,
    pub continuous_rendering: bool,
    pub background_color: Color,
    pub tooltip_delay: Duration,
    pub borderless: bool,
    pub corner_radius: Option<f32>,
    pub parent_window: Option<WindowId>,
    pub position: Option<(i32, i32)>,
    #[cfg(feature = "resize-layout-coalescing")]
    pub resize_layout_policy: ResizeLayoutPolicy,
}

Default values:

FieldDefaultDescription
title"Window"Window title.
width800Initial window width.
height600Initial window height.
rem_px16.0Pixel value corresponding to 1rem in current window.
wait_v_synctrueWhether rendering backend waits for vertical sync.
show_fpsfalseWhether to show FPS.
continuous_renderingfalseWhether to continuously request redraw.
background_colorColor::rgb(255, 255, 255)Window background color.
tooltip_delayDuration::from_millis(500)Tooltip response delay.
borderlessfalseWhether to create a borderless window. Current Windows implementation uses WS_POPUP.
corner_radiusNoneOptional window corner radius. Current Windows implementation clips via window region and updates on size changes.
parent_windowNoneOptional parent window. Suitable for popups, child windows and other scenarios that need to bind to an existing window.
positionNoneOptional initial screen coordinates; when not set, normal windows use the platform default position, borderless or child window scenarios center on the work area.
resize_layout_policyResizeLayoutPolicy::ImmediateOnly exists when resize-layout-coalescing feature is enabled; controls the layout refresh strategy during resize.

continuous_rendering only controls whether event loop continuously triggers redraw, doesn't change Flor's interface model. Regular GUI applications keep default value; animation, real-time preview, game loop etc. scenarios needing per-frame refresh set to true.

resize_layout_policy is only compiled into WindowOption when the resize-layout-coalescing feature is enabled. It currently has two values:

ValueDescription
ResizeLayoutPolicy::ImmediateDefault policy. Refreshes layout immediately when window resize messages arrive; behavior is most direct, suitable for normal windows.
ResizeLayoutPolicy::CoalescedCoalesces layout refresh requests during resize, avoiding high-frequency resize messages from repeatedly triggering full layout when the user drags window edges. Suitable for complex pages, long lists, or windows where layout cost is significantly high during resize.

If you don't experience noticeable lag during resize, keep the default Immediate. For complete feature description, see Resize Layout Policy.

open's View Function

open signature is:

pub fn open<F, V>(self, view_fn: F) -> Result<WindowId, Error>
where
    F: Fn(WindowId) -> V + Send + Sync + 'static,
    V: IntoViewIter,

view_fn is the window root view's build function. After Flor creates platform window, renderer and window entry, it passes current window's WindowId to it, and converts return value to window root view tree.

use flor::platform::WindowId;
use flor::view::View;
use flor_lys::label::label;

fn build_view(_window_id: WindowId) -> impl View {
    label("hello flor ~")
}

WindowOption::default().open(build_view)?;

If you don't need to use window ID, write it as _window_id.

How to Use WindowId in open Parameter

It is not recommended to directly call window control methods using the window_id in open(move |window_id| { ... }) during the root view build phase. At this time the window is completing initialization, Flor is still mounting the root view, registering the renderer, initializing focus and refreshing layout; directly calling set_size, set_window_mode, request_redraw, destroy etc. in this closure easily makes the initialization order unexpected, producing extra redraw, layout state inconsistency or platform layer behavior issues.

This WindowId is more suitable for being captured into view events, used to control current window in subsequent events:

use flor::platform::base::WindowApi;
use flor::view::builder::EventBuilder;
use flor_lys::button::button;

WindowOption::default().open(move |window_id| {
    button("Close Window").on_click(move || {
        let _ = window_id.destroy();
    })
})?;

If just setting initial title, size, background color, refresh mode, should prioritize writing in WindowOption fields, not changing in open's closure.

Borderless Windows and Initial Position

borderless, corner_radius, parent_window and position are passed once to the underlying WindowCreateOptions when creating the platform window. These parameters should be written in WindowOption, not patched in the open closure.

use flor::types::Color;
use flor::windows::WindowOption;
use flor_lys::label::label;

let popup = WindowOption {
    title: "Popup".to_string(),
    width: 320,
    height: 180,
    borderless: true,
    corner_radius: Some(12.0),
    position: Some((100, 100)),
    background_color: Color::TRANSPARENT,
    ..WindowOption::default()
}
.open(|_| label("Borderless window"))?;

If parent_window is set but position is not, the current Windows implementation will first try to place the window within the parent window's monitor work area, centered relative to the parent window when possible.

WindowId

WindowId is platform window's handle wrapper. In current Windows platform implementation it's:

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct WindowId(pub isize);

It implements WindowApi and WindowOperations. Need to import trait when calling these methods:

use flor::platform::base::{WindowApi, WindowOperations};
use flor::platform::WindowId;

Creation and Lifecycle

APIPurposeUsage Suggestion
WindowOption::open(view_fn)Create Flor window and mount root view.Application side prioritize using this entry.
WindowApi::create_window(title, width, height)Only create platform window.Framework internal use; regular applications shouldn't bypass WindowOption::open.
WindowApi::create_window_with_options(title, width, height, options)Create platform window with WindowCreateOptions.Framework internal use for handling WindowOption's borderless, corner radius, parent window and initial position.
update_window()Synchronously trigger platform window update.Initialization flow internally calls; application side usually doesn't manually call.
destroy()Destroy window.Suitable to call in button click, menu command etc. events.

Display and Window Mode

APIPurpose
show()Show window.
hide()Hide window.
set_window_mode(mode)Set window mode.
get_window_mode()Read current window mode.

WindowMode includes Normal, Minimized, Maximized, Fullscreen. In current Windows implementation, Fullscreen is temporarily treated as maximized.

Position and Size

APIPurpose
get_left() / get_top()Read window top-left screen coordinates.
set_left(left) / set_top(top)Set window horizontal or vertical position separately.
set_position((x, y))Set window position simultaneously.
get_width() / get_height()Read entire window width/height.
set_width(width) / set_height(height)Set window width or height separately.
set_size((width, height))Set window size simultaneously.
get_client_size()Read client area size.
get_client_rect()Read client area rectangle on screen.
get_window_rect()Read entire window rectangle on screen.

Position uses i32, allowing negative coordinates in multi-monitor environments; size uses u32.

DPI, IME and Mouse

APIPurpose
get_scale_factor()Read DPI scaling factor; current Windows implementation returns dpi_x / 96.0.
get_dpi()Read window DPI.
set_ime_window_location(rect)Set IME candidate window position.
set_ime_open_state(is_open)Open or close IME state.
set_ime_allowed(allow)Allow or disable IME.
set_cursor(cursor)Set current cursor.
drag_window()Trigger system window drag.
capture_mouse()Capture mouse.
release_mouse()Release mouse capture.

Redraw

APIPurpose
request_redraw()Asynchronously request window redraw.

Regular view state changes are automatically requested for redraw by Flor. Only when you directly change external state through window or platform capabilities that framework can't detect, you need to manually call request_redraw().

Deferred Window Commands

WindowRequestApi adds a set of request_* methods to WindowId, placing window operations into Flor's event loop queue for execution rather than executing them immediately at the call site. For the complete method reference, return values and callback timing, see WindowRequestApi.

Its main reason for existing is to avoid window operations directly entering platform APIs on the wrong call stack, causing re-entry, deadlocks or infinite loops. For example: during window message processing, reactive updates, cross-thread callbacks or while the event loop is refreshing layout / drawing, directly calling set_size, show, destroy, drag_window may re-trigger window messages or wake-up logic, causing nested event loop execution. request_* defers these operations to a unified flush point in Flor's event loop — first finishing the current dispatch, then executing window commands in FIFO order.

If your requirement comes from a non-event-loop thread, also see Cross-thread Window Commands: this feature actively wakes the event loop after commands are enqueued, preventing requests from sitting in the queue while the event loop is waiting.

use flor::windows::WindowRequestApi;

window_id.request_set_size((1024, 768));
window_id.request_show().result(|result| {
    if let Err(err) = result {
        eprintln!("show failed: {err}");
    }
});

The key point here is using request_* to express "execute safely by the event loop later". If you need to check which window operations support deferred execution, or need to handle result(...) callbacks, refer directly to WindowRequestApi.

Anchored Windows

flor::windows exposes anchored window helper functions for making a popup continuously follow a given ViewId within a parent window:

APIPurpose
register_anchored_window(parent_window_id, anchor_view_id, window_id, size, initial_position)Register the relationship between a popup and its anchor view.
unregister_anchored_window(window_id)Remove anchoring for a popup without destroying the window.
anchored_window_position(parent_window_id, anchor_view_id, size)Calculate the popup's top-left position based on the anchor view's current window coordinates.

After layout refresh, Flor updates the registered anchored windows under the current parent window. The default placement strategy prefers displaying below the anchor, falls back to above when space is insufficient, and clamps the horizontal position within the parent window's client area as much as possible. If the anchor no longer belongs to the parent window, its position cannot be read, or it completely leaves the client area, the registered popup will be requested to be destroyed.