Skip to content

Releases: matrix-research-inc/egui-elegance

v0.17.4

Choose a tag to compare

@stephenberry stephenberry released this 29 Oct 20:06

Adds per-option disabling to Select, and fixes theming after an OS dark/light switch and the docs.rs build.

Select

  • Select::option_enabled greys out options that can't be picked, leaving them in the list. Clicking a disabled option leaves the value unchanged and the list open.
    ui.add(
        Select::new("rate", &mut rate)
            .options([(44_100, "44.1 kHz"), (48_000, "48 kHz"), (192_000, "192 kHz")])
            .option_enabled(|&r| r <= max_rate),
    );
  • Select options now have accessible names and expose selected/disabled state.

Fixes

  • Theme::install now themes both of egui's styles, so stock widgets keep the theme after the OS switches between dark and light mode.
  • docs.rs builds, failing since 0.17.2. docs.rs now documents the serde API too.

Released on crates.io: https://crates.io/crates/egui-elegance/0.17.4

PRs: #39, #40

Full changelog: https://github.com/matrix-research-inc/egui-elegance/blob/main/CHANGELOG.md

v0.17.3

Choose a tag to compare

@stephenberry stephenberry released this 29 Oct 15:35

Adds media-control icons to the bundled glyphs.

Glyphs

  • glyphs::PLAY, PAUSE and STOP for media and run controls, from Lucide play, pause and square. Use them instead of base-font ▶/■ so they match the other icons.
    ui.add(Button::icon_text(glyphs::PLAY, "Run"));

Released on crates.io: https://crates.io/crates/egui-elegance/0.17.3

PRs: #37, #38

Full changelog: https://github.com/matrix-research-inc/egui-elegance/blob/main/CHANGELOG.md

v0.17.2

Choose a tag to compare

@stephenberry stephenberry released this 26 Oct 17:36

Adds lenient loading for Dock layouts.

Dock

  • DockLayout::deserialize_lenient loads a saved layout while dropping tabs that no longer deserialize, such as a tab variant the app has since removed, instead of failing the whole layout. No placeholder tab type is needed. Errors in the layout's own structure still fail.
    #[derive(Deserialize)]
    struct State {
        #[serde(deserialize_with = "DockLayout::deserialize_lenient")]
        dock: DockLayout<Tab>,
    }
    It needs a self-describing format such as JSON or RON. The default Deserialize is unchanged.

Released on crates.io: https://crates.io/crates/egui-elegance/0.17.2

PRs: #36

Full changelog: https://github.com/matrix-research-inc/egui-elegance/blob/main/CHANGELOG.md

v0.17.1

Choose a tag to compare

@stephenberry stephenberry released this 26 Oct 16:54

Adds Dock, tabbed split panes.

Dock

  • Drag tabs to reorder them, move them between panes, or drop them on a pane edge to split it. Drag gutters to resize.
  • Tabs are your own type, such as an enum carrying data, held in a DockLayout<T> that serializes with the serde feature. A DockViewer supplies each tab's label, content and optional context menu.
    #[derive(Clone, PartialEq, Eq, Hash)]
    enum Tab { Files, Editor(PathBuf), Console }
    
    impl DockViewer for Viewer {
        type Tab = Tab;
        fn tab(&mut self, tab: &Tab) -> DockTab { /* label, icon, closable, ... */ }
        fn ui(&mut self, ui: &mut Ui, tab: &Tab) { /* content */ }
    }
    
    Dock::new("dock").show(ui, &mut layout, &mut viewer);
  • Crowded tab strips shrink tabs down to min_tab_width, then scroll, fading at ends with hidden tabs, and offer a menu of every tab. Truncated labels show in full on hover.
  • Scrolling content: DockTab::scroll(true) wraps a tab's content in a scroll area.
  • Lenient loading: DockLayout::retain and filter_map drop or convert tabs, so a saved layout that names tabs the app no longer has can load with those tabs dropped instead of failing.

Released on crates.io: https://crates.io/crates/egui-elegance/0.17.1

PRs: #32, #34, #35

Full changelog: https://github.com/matrix-research-inc/egui-elegance/blob/main/CHANGELOG.md

v0.17.0

Choose a tag to compare

@stephenberry stephenberry released this 21 Oct 22:14

Moves to egui 0.37, scopes widget id salts to their parent, and adds icon + text buttons.

Breaking

  • egui 0.37. Your app must move to egui 0.37 as well.
  • Scoped id salts. The same salt can now be reused under different parents, e.g. CollapsingSection::new("advanced", …) in two panels no longer shares state. Salts only need to be unique among siblings.
    • Widgets shown in a Ui scope the salt with that Ui: CollapsingSection, Accordion, BrowserTabs, ColorPicker, MenuBar, SortableList, Pairing, and RangeSlider::id_salt.
    • Popover, Menu, and ContextMenu scope it with their trigger.
    • Modal and Drawer salts stay app-wide.
  • Popover::popup_id takes the trigger's id:
    // was:
    Popover::popup_id("filters")
    // now:
    Popover::popup_id(trigger.id, "filters")
  • Emoji need a font. egui 0.37 no longer bundles emoji fonts, so emoji passed to elegance widgets (e.g. .icon("🔔")) render as empty boxes. Enable egui's monochrome_emoji_fonts feature or eframe's system_fonts, or switch to the bundled glyphs.

Button

  • Button::icon_text(glyph, label) draws one of the bundled glyphs before the label. It is the same height as Button::new and Button::icon at each ButtonSize, so all three line up in a row.
    ui.add(Button::icon_text(glyphs::DOWNLOAD, "Download").accent(Accent::Blue));

Changed

  • IdSalt is now an alias of egui's equivalent AsIdSalt trait. Code that uses it as a bound is unaffected.
  • Select is 1pt taller, because egui's ComboBox now shares Button's layout.

Fixed

  • flash_success / flash_error tint a non-revealable TextInput and a TextArea again under egui 0.37.
  • ColorPicker's hex field now has an accessible name.
  • TagInput and RemovableChip keep their placeholders vertically centred under egui 0.37.

Released on crates.io: https://crates.io/crates/egui-elegance/0.17.0

PRs: #29, #30, #31 · Closes #28

Full changelog: https://github.com/matrix-research-inc/egui-elegance/blob/main/CHANGELOG.md

v0.16.2

Choose a tag to compare

@stephenberry stephenberry released this 08 Oct 14:57

Select now sizes to its widest option by default.

Select

  • Without .width(...), every select was a fixed 160pt wide, contradicting its docs. It now fits its longest option label plus the chevron, and its width doesn't change with the selection, so neighbouring widgets don't shift.
  • Pass .width(160.0) to keep the old size.

Released on crates.io: https://crates.io/crates/egui-elegance/0.16.2

PRs: #26

Full changelog: https://github.com/matrix-research-inc/egui-elegance/blob/main/CHANGELOG.md

v0.16.1

Choose a tag to compare

@stephenberry stephenberry released this 30 Sep 15:44

Icon-only buttons, and custom content in MenuBar, including a title bar mode for apps that hide the native one. All changes are additive; default rendering is unchanged.

Button

  • Button::icon(glyph, label): a square icon-only button showing one of the bundled Lucide glyphs, as tall as a text button of the same size so the two line up in a row. label is the accessible name and is required.
  • Button::new now documents that it uses only the string content of its argument; a RichText's size, colour, and style are dropped. Reach for Button::icon instead of sizing the text.

MenuBar

  • MenuBarUi::ui() exposes the strip's Ui for widgets inline with the menu triggers.
  • MenuBarUi::trailing(|ui| …) pins content to the right edge, outside the status slot, e.g. window controls.
  • MenuBar::title_bar(true) lets the strip stand in for a hidden native title bar: dragging its empty area moves the window and double-clicking toggles maximised. Menu triggers and custom widgets keep their own clicks.
MenuBar::new("app_menubar")
    .brand("My App")
    .title_bar(true)
    .show(ui, |bar| {
        bar.menu("File", |ui| { /* … */ });
        bar.trailing(|ui| {
            let close = Button::icon(glyphs::X, "Close window").outline().size(ButtonSize::Small);
            if ui.add(close).clicked() {
                ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
            }
        });
    });

Released on crates.io: https://crates.io/crates/egui-elegance/0.16.1

PRs: #23, #25 · Closes #24

Full changelog: https://github.com/matrix-research-inc/egui-elegance/blob/main/CHANGELOG.md

v0.16.0

Choose a tag to compare

@stephenberry stephenberry released this 10 Aug 20:00

Value widgets gain a commit signal, ColorPicker becomes keyboard-operable, and Select and Pairing can say more about their state. One small breaking change to PairItem.

Breaking

  • PairItem gained a public icon_tint field and is now #[non_exhaustive], as is the new IconTint enum. Struct literals, functional record update (..other), and destructuring without .. no longer compile — construct with PairItem::new(...) plus the builder methods:
    // was:
    PairItem { id: "web-01".into(), name: "web-01".into(), detail: None, icon: None }
    // now:
    PairItem::new("web-01", "web-01")
    Doing this once now avoids a second break the next time the struct grows.

Commit signal

  • New ResponseCommitExt::committed() reports the frame a value adjustment settles: on pointer release after a drag or a click (any mouse button, plus touch long-press), immediately on a keyboard nudge, and — for the Knob's scroll wheel — once when the scroll stops rather than once per frame of its smoothed delta. Works on MetricSlider, PercentSlider, Slider, RangeSlider, and Knob.
  • changed() still fires on every intermediate value, which is what a live preview wants. committed() is for reactions you would not want to repeat dozens of times for one gesture, such as a network write or a disk persist. A MetricSlider in stops mode previously fired changed() once per stop a drag crossed, with no affordance to tell an intermediate value from a settled one.
  • It reports that the interaction settled, not that the value differs from what you last persisted. Releasing a handle without moving it still commits once, so compare against your last-written value if a redundant write is costly.

Keyboard

  • ColorPicker is keyboard-operable. Its saturation/value plane, hue strip, and alpha strip were pointer-only: Tab landed on them, because they were focusable, but they answered no keys, so the arrows navigated straight back off and a keyboard user could not pick a colour at all. The plane now takes ←/→ for saturation and ↑/↓ for value; the two strips take ←/→ plus Home/End; Shift is a 10x nudge throughout. Each surface draws a focus ring and announces itself to screen readers (previously three unlabelled targets).
  • Grabbing a value widget now gives it keyboard focus. Clicking a MetricSlider, PercentSlider, Slider, or Knob and then pressing an arrow key did nothing: the widget never took focus on press, so the key went to focus navigation instead. You can now set a coarse value with the pointer and refine it with the arrow keys, without tabbing to the widget first. RangeSlider already did this for a click on the track, and now does it for a press that lands directly on a thumb too.
  • Fixed: arrow keys on a focused Knob moved the value and handed focus to the neighbouring widget, because the knob never claimed the arrows the way the sliders do. A knob next to another control nudged once and then drove its neighbour; a knob above a slider became unreachable by keyboard entirely. Tab and Esc still move focus away.
  • Knob no longer resets to its default on Space, only on 0 (and the documented Alt+click / double-click). egui synthesises a click from Space on any focused widget, and knobs are now focused by the pointer, so the old binding would have wiped the value of a knob you had just set by hand.

Unsaved state

  • Select::saved(&T) marks a staged select — one whose selection has moved off the last committed value — with the focus-accent dot BrowserTab already uses for unsaved work, so a picker reads as unsaved beside a dirty TextInput in the same form. Pass the last committed value rather than a dirty flag and the select makes the comparison itself. Needs .label(…), since the dot rides the label.
    ui.add(
        Select::strings("region", &mut region, ["eu-west-1", "us-east-1"])
            .label("Region")
            .saved(&saved_region),
    );

Pairing

  • Per-item icon tint. PairItem::icon_tone(BadgeTone) tints a node's leading icon with the crate's status vocabulary; PairItem::icon_color(impl Into<Color32>) takes an explicit colour. Untinted icons are unchanged.
  • BadgeTone::foreground(&Palette) is now public — the resolver behind badge labels and icon tones.

Glyphs

  • glyphs::FOLDER and glyphs::FOLDER_OPEN (Lucide folder / folder-open, U+E023–U+E024), for directories, collections, and breadcrumb roots, with the open variant for the expanded or current node. Apps were falling back to the 📁 emoji, which renders in the system emoji font and reads as a different icon set beside elegance's Lucide glyphs. Existing glyph outlines and advances are unchanged.

Fixed

  • ColorPicker now adds to its recents list on a non-primary click or a touch long-press of the SV plane, hue strip, or alpha slider, matching what those controls already did for a primary click or drag. A keyboard adjustment records one entry per run of presses, so holding an arrow key down cannot flood the row.

Full changelog: https://github.com/matrix-research-inc/egui-elegance/blob/main/CHANGELOG.md

v0.15.0

Choose a tag to compare

@stephenberry stephenberry released this 06 Aug 07:33

Targets egui 0.36, and fixes overlay layering against foreground windows.

egui 0.36

  • egui, eframe, and egui_kittest now target 0.36. Because elegance re-exports egui types in its public API, your app must move to egui 0.36 as well.
  • MSRV moves from 1.92 to 1.95, matching egui's.
  • Breaking: FileDropResponse::dropped_files is now Vec<egui::DroppedFileHandle>. Upstream turned DroppedFile into a trait, so path and bytes are methods rather than fields:
    for file in &drop.dropped_files {
        let path = file.path();     // was `file.path`, an `Option<PathBuf>`
        let bytes = file.bytes()?;  // was `file.bytes`; `bytes_async()` on web
    }
    Mirroring egui's own handle type keeps the widget a pass-through — any conversion back to an owned struct would have to read the file eagerly, which the web cannot do synchronously.

Overlay layering

  • An egui::Window at Order::Foreground rendered above an open Modal's backdrop and stayed clickable, and clicking it raised that window above the modal card. Reported by @greekfetacheese in #13. Modal and Drawer now each render as a single foreground area and claim the modal layer while open, so callers need not know about layer orders.
  • Esc dismissed every open overlay at once rather than just the topmost.
  • Focus was not restored when the overlay was drawn before the widget that had held it.

Toast

  • Wrapped title and description text is no longer justified. A toast whose text wrapped so a long token (a path, a URL) landed alone on a non-final row rendered it letter-spaced.

Notes

  • Visual baselines were regenerated. The hover snapshots (button_hovered, switch_hovered_off) now actually show the hovered state — under egui 0.35 the pointer position did not survive the harness's settle frames, so they had silently been recording the resting state. The rest are time-driven widgets captured at a different animation phase. No widget layout, colour, or shape changed.

v0.14.0

Choose a tag to compare

@stephenberry stephenberry released this 13 Jul 17:21

Updates the egui family to 0.35 and centralizes the widget id-salt bound into a single trait.

egui 0.35

  • egui, eframe, and egui_kittest now target 0.35. Because elegance re-exports egui types in its public API, your app must move to egui 0.35 as well — this is the one breaking change in the release.
  • The two upstream breaks are handled internally: Id::new now requires Hash + Debug id sources, and Panel::show_inside was renamed to show.

IdSalt trait

  • New public bound elegance::IdSalt, blanket-implemented over every Hash + Debug type. Each widget that takes an id salt now spells its parameter impl IdSalt, so the requirement lives in exactly one place — the next egui change to what an id source must implement is a one-line edit.
  • Additive and non-breaking: any id-salt argument that compiled against the previous signatures still compiles.

Notes

  • The visual-snapshot baselines were regenerated for 0.35's text rendering. The diffs are anti-aliasing only (a sub-pixel shift also nudged a few fit-to-content canvases by ~1px); widget layout, colours, and shapes are unchanged.