Skip to content

Repository files navigation

guicons logo

guicons

A manifest-driven icon system for native Rust GUI applications: describe your icon set once in TOML (local files, URLs, iconify.design ids, font glyphs, mixed freely), get a typed Rust registry, a Slint component, and a compile-time-checked icon! macro that hands your GUI framework its native image type directly.

Why guicons?

Most native Rust GUI apps hardcode include_bytes! per icon by hand - no manifest, no shared registry, no compile-time check that a referenced icon actually exists in the set. guicons gives you that single source of truth, with codegen and a macro on top so referencing an icon is a typed, checked operation instead of a bare file path.

Features

  • One manifest, icons.gui.toml: family/variant/size axes, [link] includes = [...] to split across files, [providers.<name>] schemas (10+ built-in providers, with per-field .override).
  • Any source, mixed freely: local file, URL, iconify.design id (auto-fetched and cached offline), or a font glyph.
  • Typed build-time codegen (guicons-build): a Rust registry with per-family/size builder methods, and a matching Slint Icon component.
  • guicons::icon!: resolves a selector against your manifest at compile time straight into your active GUI framework's native type (slint::Image, an iced Handle).
  • Slint integration out of the box, with a runnable example in crates/guicons/examples/.
  • CLI (guicons-cli): icons fetch/update populates the offline iconify cache; icons add <iconify-id|file> adds an icon to your manifest in one command.
  • LSP (guicons-lsp): completion, hover, goto-definition, find usages, rename, and diagnostics, with navigation between icons.gui.toml and your Rust code in both directions.
  • RustRover/IntelliJ plugin: a sidebar tool window showing your manifest's structure, with icon previews and a built-in iconify.design browser to search, preview, and insert a reference at the caret in one click.

Usage

# icons.gui.toml
[defaults]
root = "assets"

[providers.phosphor]
variants = ["thin", "light", "bold", "fill", "duotone"]

[link]
includes = ["icons/toolbar.gui.toml"]

[settings]
variants.filled = { file = "settings-filled.svg" }
variants.regular = { file = "settings-regular.svg" }

[docker]
file = "docker.svg"

[home]
iconify = "mdi:home"

[spinner]
glyph = "MyIconFont:U+E001"
# icons/toolbar.gui.toml - split out via [link], same [defaults] root
[toolbar.16]
variants.filled = { file = "toolbar-16-filled.svg" }

[toolbar.24]
variants.filled = { file = "toolbar-24-filled.svg" }
variants.regular = { file = "toolbar-24-regular.svg" }
// build.rs
fn main() {
    guicons_build::IconBuild::auto()
        .emit(guicons_build::Emit::Rust)
        .emit(guicons_build::Emit::Slint)
        .build();
}
// lib.rs or main.rs
guicons::include_icons!();

Most of the time you just want to hand an icon straight to your GUI framework - guicons::icon! resolves a selector, checked against your manifest at compile time, directly to whichever framework's native type matches your enabled feature (slint or iced; plain IconData if neither is enabled):

// with the `slint` feature enabled, this is already a `slint::Image`
my_component.set_icon(guicons::icon!(settings.filled));

Use guicons::icon_data! instead if you always want the plain IconData, regardless of which GUI feature is enabled.

Use guicons normally at runtime, and add guicons-build as a build-dependencies entry when generating code from build.rs.

Dynamic/runtime lookups

For the less common case where the family/variant is only known at runtime (not a literal you can pass to icon!), or you need a runtime-swappable IconKey (theming, hot-reload via IconResolver), use the generated registry functions or guicons::icon_key! directly:

let key = icons::key_from_dynamic_family_variant("settings", None, Some("filled"));
let key = guicons::icon_key!(settings.filled); // icons::keys::SETTINGS_FILLED

GUI framework integration

Slint (slint feature)

The generated icons.slint (from Emit::Slint above) exports an Icon component that switches on a name property, plus one typed component per icon (settings-filledSettingsFilledIcon) - import either from your own .slint files:

import { Icon, SettingsFilledIcon } from "icons.slint"; // resolved via an include path pointing at OUT_DIR

// Dynamic - runtime string match against `name`
Icon {
    name: "settings-filled";
}

// Typed - one exported component per icon, checked at compile time
SettingsFilledIcon {}

For icon data you don't already have as icon! output - e.g. resolved at runtime through an IconResolver - guicons::slint converts IconData/ IconSource to a Slint Image, or to a (font_family, char) pair for glyph sources. See examples/slint_icon.rs for a complete, runnable example (cargo run -p guicons --example slint_icon --features slint).

iced (iced feature)

guicons::iced gives you iced::widget::svg::Handle/image::Handle directly from IconData/IconSource, plus the same glyph pair for font-based icons. See examples/iced_icon.rs for a complete, runnable example (cargo run -p guicons --example iced_icon --features iced).

About

Manifest-driven icon system for native Rust GUI apps

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages