Cross-thread Window Creation

cross-thread-window-creation feature allows calling WindowOption::open(...) outside event loop thread to create windows.

[dependencies]
flor = { version = "0.1.0", features = ["direct2d", "cross-thread-window-creation"] }

When to Enable

Judgment rule is simple: if application needs "create window anywhere" capability, must enable cross-thread-window-creation feature.

Typical scenarios include calling WindowOption::open(...) in business thread, background task, async callback or non-event-loop thread. Once this call path exists, don't rely on manually guaranteeing "just create window in correct thread".

When not enabling this feature, WindowOption::open(...) will by default create the window directly in the current thread. This is suitable for all windows created by the event loop thread or main thread simple programs.

But if creating window from non-event-loop thread without enabling this feature, the framework may encounter platform UI thread restrictions, event loop sync issues, message queue waiting issues, and even freeze or deadlock. Therefore, as long as the program has cross-thread window creation needs, you should explicitly enable this feature.

Basic Behavior

After enabling cross-thread-window-creation, WindowOption::open(...) still uses same API.

When call happens outside event loop thread, Flor will post window creation request to event loop thread to process, and let the current thread wait for creation result. This can ensure the window is actually created by the correct UI thread, while letting the caller still get a sync creation result.

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

std::thread::spawn(|| {
    WindowOption::default()
        .open(|_window| label("from worker thread"))
        .expect("create window");
});

Usage Conditions

Cross-thread window creation depends on event loop thread processing creation queue, therefore one of the following conditions must be satisfied:

  1. Event loop is already running;
  2. Creation request is already queued before event loop enters, afterwards will be processed by event loop thread.

Common usage is: main thread initializes Flor, and enters FlorGui.event_loop()?; business thread calls WindowOption::open(...) when needed to request creating new window.

use flor::FlorGui;

FlorGui.init()?;

// You can request creating a window from the business thread
// ...

FlorGui.event_loop()?;

Why This Is a Feature

cross-thread-window-creation will introduce additional cross-thread request queue, sync waiting and related scheduling logic, therefore will bring certain performance and size overhead.

For somewhat larger GUI programs, this part of the overhead is usually negligible. But for very small tool-type programs, especially programs where all windows are only created on the main thread, these additional logic is not necessary, and the extra code on the size and startup path is more worth controlling.

Therefore Flor designs it as optional feature: programs needing cross-thread window creation capability can explicitly enable; small programs not needing this capability can avoid introducing extra overhead.

Platform Design Reason

Many platforms have requirements for UI thread, among which macOS explicitly requires UI-related operations running in main thread. This is also an important reason Flor adopts this design: the framework needs to let application code initiate window creation requests from a business position, while still ensuring the actual window creation happens on the correct UI / event loop thread.

To accommodate such platform restrictions, and to avoid undefined behavior, message loop freezing or deadlock issues caused by directly creating windows on different threads, Flor designs cross-thread window creation as a "request post to event loop thread, with the event loop thread actually creating the window" pattern.

That is, cross-thread-window-creation does not let any thread truly create a window directly; rather, it lets any thread safely request window creation, and the window is ultimately still created by the correct UI / event loop thread.

If all windows in your application are created on the main thread or event loop thread, you don't need to enable this feature.