Freya Documentation

repository·main·Indexed 25 days ago

https://github.com/marc2332/freya

A Rust-based native GUI framework featuring components, animations, and state management. It includes specialized tools such as Freya Router (a Dioxus Router fork), the Torin layout library, the Ragnarok events processor, and experimental Android support via the AndroidPlugin and AndroidExt trait. The framework also provides a comprehensive animation system with hooks like use_animation and types such as AnimNum, AnimColor, and AnimSequential.

Tokens
58K
Snippets
150
Records
301
Agent score
82%

What's inside Freya

  1. Overview of Torin layout library

    main
    Torin 📐 is a pure Rust layout library designed for use with Freya 🟣, a native GUI library for Rust. While optimized for Freya, Torin is decoupled enough to work with other GUI libraries; a demonstration of this interoperability can be found in the demo.rs example within the repository.
  2. Key differences in Freya Router vs Dioxus Router

    main

    Freya Router is a fork of Dioxus Router optimized for the Freya ecosystem. The primary architectural differences are:

    • Multiple Routers Support: Unlike Dioxus Router, which allows only one router per app, Freya Router allows multiple routers in the same component tree by providing the router context to children rather than injecting it at the root.
    • Built-in ActivableRoute: A helper component (moved from freya-components) that informs child components if a specific route is currently active, without requiring those children to depend on freya-router directly.
    • Built-in NativeRouter: Provides Freya-specific integration for mouse-based back and forward navigation (moved from freya-components).
    • MemoryHistory: The router always uses MemoryHistory, removing the need for renderer-agnostic APIs.
    • Reduced Dependencies: The html feature and WASM-splitting features have been removed to avoid pulling in dioxus-html and unnecessary overhead.
  3. Define the App Root with Function Components or the App Trait

    main

    The application root can be a plain function (Function Component) or a struct implementing the App trait.

    • Function Components: Use these for the app root only. This is where top-level hooks like use_init_theme, use_init_radio_station, or use_provide_context should be called.
    • App Trait: Use this to pass data from main into the root component.

    Note: Reusable UI that uses hooks or props MUST be a Component (struct + impl Component). Plain functions are only for the app root and stateless helpers.

    // Function component root
    fn app() -> impl IntoElement {
        rect().child("Hello, World!")
    }
    
    // App trait root
    struct MyApp { number: u8 }
    
    impl App for MyApp {
        fn render(&self) -> impl IntoElement {
            label().text(self.number.to_string())
        }
    }
  4. Perform Conditional and Dynamic Rendering

    main

    Use the following methods to handle conditional elements within the builder chain:

    • .maybe(bool, |el| ...): Applies the callback only when the boolean condition is true.
    • .map(Option<T>, |el, val| ...): Applies the callback when the Option is Some, passing the inner value to the callback.
    • .maybe_child(Option<impl IntoElement>): Appends a child only when the provided Option is Some.

    Best Practice: Prefer one outer .maybe or .map to wrap multiple conditional children rather than repeating the same condition across multiple .maybe_child calls. This keeps the gating logic in one place.

    // Good: Single .maybe wraps all conditional children
    rect()
        .maybe(show, |el| {
            el.child(Title::new("Hi"))
                .child(Content::new().child("Hello"))
                .child(Footer::new())
        })
    
    // Bad: Same predicate repeated per child
    rect()
        .maybe_child(show.then(|| Title::new("Hi")))
        .maybe_child(show.then(|| Content::new().child("Hello")))
        .maybe_child(show.then(|| Footer::new()))
  5. Use ContextMenu for floating menus

    main

    The ContextMenu component enables floating menus.

    Requirement: You must call ContextMenuViewer::new() in an ancestor of the component where you intend to open menus (ideally near the app root) to provide the rendering slot.

    To open a menu from a press event, use ContextMenu::open_from_event(&event, menu_content).

    fn context_menu() -> Menu {
        Menu::new()
            .child(
                SubMenu::new()
                    .child(MenuButton::new().child("Option 1"))
                    .child(MenuButton::new().child("Option 2"))
                    .label("Options"),
            )
            .child(MenuButton::new().child("Close").on_press(move |_| ContextMenu::close()))
    }
    
    // Open from any press event
    Button::new().on_press(move |e: Event<PressEventData>| {
        ContextMenu::open_from_event(&e, context_menu())
    })
  6. Setup experimental Hot Reloading

    main

    Freya 0.4 supports hot reloading via the subsecond crate and the Dioxus dx CLI. This requires the hotreload feature flag.

    1. Enable the feature in your Cargo.toml:
    [features]
    hotreload = ["freya/hotreload"]
    1. Install the Dioxus CLI:
    cargo install dioxus-cli
    1. Run the application:
    dx serve --hot-patch --features hotreload

    Limitations:

    • Hook state is reset and spawned tasks are cancelled on every patch.
    • Only function bodies can be patched; structural changes (new types or changed signatures) require a full restart.
    # In your Cargo.toml
    [features]
    hotreload = ["freya/hotreload"]
    cargo install dioxus-cli
    
    dx serve --hot-patch --features hotreload
  7. Enable the Performance Overlay plugin

    main

    The PerformanceOverlayPlugin renders a live metrics panel. It is automatically registered on debug builds and can be shown/hidden using Ctrl+Shift+P (or Cmd+Shift+P on macOS). To manually register it in your launch configuration:

    launch(
        LaunchConfig::new()
            .with_plugin(PerformanceOverlayPlugin::default())
            .with_window(WindowConfig::new(app))
    )
  8. Implement a System Tray with Menus

    main

    Freya supports system tray icons with menus. You can define a tray icon using TrayIconBuilder and provide a handler function to respond to TrayEvent::Menu events. This allows you to run apps entirely from the tray or use the tray to control window visibility and lifecycle.

    fn main() {
        let tray_icon = || {
            let tray_menu = Menu::new();
            let _ = tray_menu.append(&MenuItem::with_id("open", "Open", true, None));
            let _ = tray_menu.append(&MenuItem::with_id(
                "toggle-visibility", "Toggle Visibility", true, None,
            ));
            let _ = tray_menu.append(&MenuItem::with_id("exit", "Exit", true, None));
            TrayIconBuilder::new()
                .with_menu(Box::new(tray_menu))
                .with_tooltip("Freya Tray")
                .with_icon(LaunchConfig::tray_icon(ICON))
                .build()
                .unwrap()
        };
    
        let tray_handler = |ev, mut ctx: RendererContext| match ev {
            TrayEvent::Menu(MenuEvent { id }) if id == "open" => {
                ctx.launch_window(WindowConfig::new(app).with_size(500., 450.));
            }
            // ... handle the other menu items (toggle visibility, exit, ...)
            _ => {}
        };
    
        launch(LaunchConfig::new().with_tray(tray_icon, tray_handler))
    }
  9. Embed a WebView with freya-webview

    main

    The experimental freya-webview crate allows embedding native web views. To use it, you must register the WebViewPlugin in your LaunchConfig. You can then use the WebView component to render a URL. Web views are identified by WebViewId, allowing for multi-tab setups and explicit disposal via WebViewManager::close(id). This feature requires the webview feature flag.

    fn main() {
        launch(
            LaunchConfig::new()
                .with_plugin(WebViewPlugin::new())
                .with_window(WindowConfig::new(app)),
        )
    }
    
    fn app() -> impl IntoElement {
        WebView::new("https://duckduckgo.com").expanded()
    }