Running apps

AppConfig

The options a window starts with, built with chained methods from AppConfig::new.

FNcrownui::AppConfig
main.rs
use crownui::prelude::*;
 
fn main() -> Result<(), crownui::RunError> {
    let config = AppConfig::new("Notes")
        .app_id("org.example.notes")
        .size(960, 640)
        .min_size(480, 360)
        .color_scheme(ColorScheme::Dark)
        .clear_color(rgb(0x0f172a));
    crownui::run(component(|_cx| text("Hello")), config)
}
Signature
pub struct AppConfig {
    pub title: String,
    pub app_id: Option<String>,
    pub size: LogicalSize,
    pub min_size: Option<LogicalSize>,
    pub material: Option<Material>,
    pub clear_color: Color,
    pub color_scheme: ColorScheme,
    pub fonts: Vec<(Vec<u8>, Option<String>)>,
    pub mcp: bool,
    pub accessibility: bool,
    pub layer: Option<LayerOptions>,
    pub corner_shape: CornerShape,
    pub decorations: Decorations,
    pub frame_pacing: FramePacing,
}
 
impl AppConfig {
    pub fn new(title: impl Into<String>) -> Self
}

AppConfig says how one window starts. You pass it to crownui::run, or pair it with a view in crownui::run_windows. Start from AppConfig::new(title) and chain the methods you need. Every method takes self and returns Self, so the order of calls does not matter, with one exception: material also sets the clear color, so call clear_color after it if you want a different one.

AppConfig is in crownui::prelude and at the crate root as crownui::AppConfig. It implements Default, Clone and Debug. All fields are public, so you can also write AppConfig { size: .., ..AppConfig::default() }.

Defaults

FieldDefault
title"CrownUI"
app_idNone
size800 by 600 logical pixels
min_sizeNone
materialNone
clear_colorWHITE
color_schemeColorScheme::Light
fontsempty
mcpfalse
accessibilitytrue
layerNone, a normal window
corner_shapeCornerShape::Round
decorationsDecorations::Client
frame_pacingFramePacing::Display

Constructor

newfn new(title: impl Into<String>) -> Selfrequired

A config with the given title and every other field at its default. The title is the window title and also the app name that MCP agents and screen readers see.

Builder methods

sizefn size(self, width: u32, height: u32) -> Self

The initial content size in logical pixels. For a layer surface it is the fallback size until the compositor reports the output size.

min_sizefn min_size(self, width: u32, height: u32) -> Self

The smallest content size the user can resize the window to. Set it when your layout clips below some size.

app_idfn app_id(self, app_id: impl Into<String>) -> Self

The Wayland application id. The compositor and the launcher use it to match the window to its desktop entry and icon. When MCP is on, the app asks the platform for agent access under this id, or under the title when no id is set.

materialfn material(self, material: Material) -> Self

Puts a compositor-rendered material (blur, tint, rim and shadow) behind the whole window and sets the clear color to TRANSPARENT so the material shows through. Material::thin() and Material::thick() are the two presets.

clear_colorfn clear_color(self, color: Color) -> Self

The color the window is cleared to before your views draw. Use TRANSPARENT for a material or for a layer surface that should only show what it draws.

color_schemefn color_scheme(self, scheme: ColorScheme) -> Self

The initial light or dark scheme. .dark(|style| ..) variants apply when it is ColorScheme::Dark. Read the live value with cx.color_scheme().

fontfn font(self, bytes: Vec<u8>, alias: Option<&str>) -> Self

Registers a font before the first layout. The alias is the family name you pass to .font_family(..). Call it once per font file.

mcpfn mcp(self, enabled: bool) -> Self

Starts the in-app MCP server so agents can read the semantic tree and act on it. It needs the mcp feature, which is on by default.

accessibilityfn accessibility(self, enabled: bool) -> Self

Turns the AT-SPI screen reader bridge on or off. It is on by default. Panels and overlays that only decorate the screen often turn it off.

layerfn layer(self, layer: LayerOptions) -> Self

Runs the window as a desktop-shell layer surface (panel, dock, overlay or wallpaper) instead of an application window. See LayerOptions.

corner_shapefn corner_shape(self, shape: CornerShape) -> Self

The curve every rounded corner in the window follows, kit widgets included. CornerShape::Squircle matches the corners the CrownOS compositor draws.

decorationsfn decorations(self, decorations: Decorations) -> Self

Who draws the window frame. Decorations::Client, the default, means the app draws its own title bar with the kit's window_controls() and resizes from its edges. Decorations::Server asks the compositor to draw the title bar and borders.

frame_pacingfn frame_pacing(self, pacing: FramePacing) -> Self

When the render thread draws frames. See FramePacing below.

Examples

A glass window

AppConfig::new("Player")
    .size(420, 220)
    .material(Material::thin())
    .corner_shape(CornerShape::Squircle)

The compositor draws the blur behind the window. On a compositor without the CrownOS material protocol, nothing is drawn behind it and the transparent window shows what is under it. Check cx.platform_capabilities() and its materials field to pick an opaque fallback.

A bundled font

use crownui::kit;
 
AppConfig::new("Settings").font(kit::inter_font().to_vec(), Some(kit::INTER_FAMILY))

Server-drawn decorations

use crownui::windowing::Decorations;
 
AppConfig::new("Terminal").decorations(Decorations::Server)

Decorations is not in the prelude. The published crownui crate exposes it as crownui::windowing::Decorations.

A panel

AppConfig::new("Bar")
    .app_id("crownbar")
    .clear_color(TRANSPARENT)
    .accessibility(false)
    .layer(
        LayerOptions::new("crownbar", Anchor::TOP | Anchor::LEFT | Anchor::RIGHT)
            .height(LayerExtent::Fixed(36))
            .exclusive_zone(36),
    )

FramePacing

Signature
pub enum FramePacing {
    Display,
    Unpaced,
}

FramePacing is in the prelude and at crownui::FramePacing.

FramePacing::Displayvariant

The default. Each frame waits for the platform's frame callback, so the app draws at most one frame per display refresh.

FramePacing::Unpacedvariant

The render thread draws the next frame as soon as it submits the previous one. Frames the display cannot show are drawn and dropped. Use it to measure throughput, never in a shipped app.

AppConfig::new("Bench").frame_pacing(FramePacing::Unpaced)

Why the config is fixed at start

Most fields describe how the window is created on the Wayland side: its app id, its layer role, its decorations and its material. CrownUI reads the config once, when it opens the window. Things that change while the app runs have their own calls on Cx instead, for example cx.set_layer_height, cx.set_keyboard_interactivity and cx.color_scheme(). The cost is that you cannot turn a normal window into a panel at runtime. Open a second window with run_windows for that.