Part 13 of 14 27 Jan 2026 By Raj Patil 25 min read

Part 13: Window Configuration - Customizing Your Application

Part 13 of the Freya Rust GUI series. Learn to configure windows with custom titles, sizes, icons, decorations, multiple windows, custom fonts, and more.

Intermediate #rust #freya #gui #window #configuration #fonts #multi-window #tutorial
Building Native GUIs with Rust & Freya 13 / 14

Window Configuration

Freya provides flexible window configuration options through WindowConfig and LaunchConfig. In this tutorial, we’ll explore how to customize your application’s windows.

[!NOTE] Window vs Launch Configuration

  • WindowConfig configures individual windows (title, size, decorations)
  • LaunchConfig configures the entire application (fonts, plugins, multiple windows)

Basic Window

Create a simple window with default settings:

fn main() {
    launch(WindowConfig::new_app(App));
}

#[derive(PartialEq)]
struct App;

impl Component for App {
    fn render(&self) -> impl IntoElement {
        rect().child(label().text("Hello, World!"))
    }
}

WindowConfig

Configure individual window properties:

WindowConfig::new_app(MyApp)
    .with_title("My Application")
    .with_size(1024.0, 768.0)
    .with_background(Color::WHITE)
    .with_decorations(true)
    .with_resizable(true)

Window Size

WindowConfig::new_app(App)
    .with_size(800.0, 600.0)           // Initial size
    .with_min_size(400.0, 300.0)       // Minimum size
    .with_max_size(1920.0, 1080.0)     // Maximum size

[!TIP] Always Set Minimum Size Setting a minimum size prevents your UI from breaking when users resize the window too small.

Window Appearance

WindowConfig::new_app(App)
    .with_title("My App")                    // Window title
    .with_decorations(true)                  // Title bar and borders
    .with_transparency(false)                // Transparent background
    .with_background(Color::_rgb(240, 240, 240))  // Background color

Window Icon

let icon = LaunchConfig::window_icon(include_bytes!("../icon.png"));

WindowConfig::new_app(App)
    .with_icon(icon)

Resizable Windows

WindowConfig::new_app(App)
    .with_resizable(true)   // Allow user to resize (default)
    .with_resizable(false)  // Fixed size window

Close Handling

Control what happens when the user tries to close the window:

WindowConfig::new_app(App)
    .with_on_close(|ctx, window_id| {
        if has_unsaved_changes() {
            show_save_dialog();
            CloseDecision::KeepOpen  // Don't close yet
        } else {
            CloseDecision::Close     // Allow close
        }
    })

Advanced Window Attributes

Access low-level winit window attributes for platform-specific options:

WindowConfig::new_app(App)
    .with_window_attributes(|attrs, event_loop| {
        attrs
            .with_visible(false)       // Start hidden
            .with_always_on_top(true)  // Stay on top
    })

Window Handle Hook

Access the window handle after creation:

WindowConfig::new_app(App)
    .with_window_handle(|window| {
        // Access winit Window directly
        window.set_ime_allowed(true);
    })

LaunchConfig

Configure the overall application launch.

Multiple Windows

Create applications with multiple windows:

fn main() {
    launch(
        LaunchConfig::new()
            .with_window(WindowConfig::new_app(MainWindow))
            .with_window(
                WindowConfig::new_app(SettingsWindow)
                    .with_title("Settings")
                    .with_size(400.0, 500.0)
            )
    );
}

Custom Fonts

Embedded Fonts

Embed fonts directly in your binary:

launch(
    LaunchConfig::new()
        .with_window(WindowConfig::new_app(App))
        .with_font("Inter", include_bytes!("../fonts/Inter-Regular.ttf"))
        .with_font("Inter-Bold", include_bytes!("../fonts/Inter-Bold.ttf"))
        .with_font("Inter-Light", include_bytes!("../fonts/Inter-Light.ttf"))
);

Default Font

Set the default font for all text:

launch(
    LaunchConfig::new()
        .with_window(WindowConfig::new_app(App))
        .with_default_font("Inter")
);

Fallback Fonts

Specify fallback fonts for missing characters:

launch(
    LaunchConfig::new()
        .with_window(WindowConfig::new_app(App))
        .with_fallback_font("Arial")
        .with_fallback_font("sans-serif")
);

Using embedded fonts:

label()
    .text("Hello")
    .font_family("Inter")  // Uses Inter font

Window State at Runtime

Access window information while your app is running.

Window Size

fn component() -> impl IntoElement {
    let size = use_window_size();

    rect()
        .child(label().text(format!("Window: {}x{}", size.width, size.height)))
}

Responsive Design

fn responsive_layout() -> impl IntoElement {
    let size = use_window_size();

    let is_mobile = size.width < 768.0;

    rect()
        .direction(if is_mobile {
            Direction::Vertical
        } else {
            Direction::Horizontal
        })
        .child(sidebar(if !is_mobile))
        .child(main_content())
}

Focus Events

rect()
    .on_window_focus(|_| {
        println!("Window focused");
    })
    .on_window_blur(|_| {
        println!("Window lost focus");
    })

Complete Configuration Example

fn main() {
    let icon = LaunchConfig::window_icon(include_bytes!("../assets/icon.png"));

    launch(
        LaunchConfig::new()
            .with_window(
                WindowConfig::new_app(App)
                    .with_title("My Application")
                    .with_size(1024.0, 768.0)
                    .with_min_size(640.0, 480.0)
                    .with_max_size(1920.0, 1200.0)
                    .with_icon(icon)
                    .with_background(Color::from_rgb(248, 250, 252))
                    .with_decorations(true)
                    .with_resizable(true)
                    .with_on_close(|ctx, id| {
                        println!("Window close requested");
                        CloseDecision::Close
                    })
            )
            .with_font("Inter", include_bytes!("../assets/fonts/Inter-Regular.ttf"))
            .with_font("Inter-Medium", include_bytes!("../assets/fonts/Inter-Medium.ttf"))
            .with_font("Inter-Bold", include_bytes!("../assets/fonts/Inter-Bold.ttf"))
            .with_default_font("Inter")
            .with_fallback_font("Arial")
    );
}

Platform Considerations

Windows

macOS

Linux


Best Practices

1. Set Minimum Sizes

Prevent UI from breaking at small sizes:

.with_min_size(640.0, 480.0)

2. Handle Close Events

Prompt for unsaved changes:

.with_on_close(|ctx, id| {
    if has_unsaved_changes() {
        show_save_dialog();
        CloseDecision::KeepOpen
    } else {
        CloseDecision::Close
    }
})

3. Use Appropriate Backgrounds

Match your app’s theme:

// Light theme
.with_background(Color::from_rgb(248, 250, 252))

// Dark theme
.with_background(Color::from_rgb(17, 24, 39))

4. Embed Required Fonts

Ensure consistent appearance across platforms:

.with_font("Inter", include_bytes!("../fonts/Inter-Regular.ttf"))

5. Test on Multiple Platforms

Window behavior varies by OS - test on your target platforms.


Summary

In this tutorial, you learned:


Previous: Part 12: Routing ←

Next: Part 14: Internationalization →

In the next tutorial, we’ll explore internationalization - making your app speak multiple languages with Fluent.