Repository navigation
Releases: matrix-research-inc/egui-elegance
Release list
v0.17.4
Adds per-option disabling to Select, and fixes theming after an OS dark/light switch and the docs.rs build.
Select
Select::option_enabledgreys 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), );
Selectoptions now have accessible names and expose selected/disabled state.
Fixes
Theme::installnow 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
serdeAPI too.
Released on crates.io: https://crates.io/crates/egui-elegance/0.17.4
Full changelog: https://github.com/matrix-research-inc/egui-elegance/blob/main/CHANGELOG.md
v0.17.3
Adds media-control icons to the bundled glyphs.
Glyphs
glyphs::PLAY,PAUSEandSTOPfor media and run controls, from Lucideplay,pauseandsquare. 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
Full changelog: https://github.com/matrix-research-inc/egui-elegance/blob/main/CHANGELOG.md
v0.17.2
Adds lenient loading for Dock layouts.
Dock
DockLayout::deserialize_lenientloads 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.It needs a self-describing format such as JSON or RON. The default#[derive(Deserialize)] struct State { #[serde(deserialize_with = "DockLayout::deserialize_lenient")] dock: DockLayout<Tab>, }
Deserializeis 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
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 theserdefeature. ADockViewersupplies 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::retainandfilter_mapdrop 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
Full changelog: https://github.com/matrix-research-inc/egui-elegance/blob/main/CHANGELOG.md
v0.17.0
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
Uiscope the salt with thatUi:CollapsingSection,Accordion,BrowserTabs,ColorPicker,MenuBar,SortableList,Pairing, andRangeSlider::id_salt. Popover,Menu, andContextMenuscope it with their trigger.ModalandDrawersalts stay app-wide.
- Widgets shown in a
Popover::popup_idtakes 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'smonochrome_emoji_fontsfeature or eframe'ssystem_fonts, or switch to the bundledglyphs.
Button
Button::icon_text(glyph, label)draws one of the bundledglyphsbefore the label. It is the same height asButton::newandButton::iconat eachButtonSize, so all three line up in a row.ui.add(Button::icon_text(glyphs::DOWNLOAD, "Download").accent(Accent::Blue));
Changed
IdSaltis now an alias of egui's equivalentAsIdSalttrait. Code that uses it as a bound is unaffected.Selectis 1pt taller, because egui'sComboBoxnow sharesButton's layout.
Fixed
flash_success/flash_errortint a non-revealableTextInputand aTextAreaagain under egui 0.37.ColorPicker's hex field now has an accessible name.TagInputandRemovableChipkeep 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
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
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 Lucideglyphs, as tall as a text button of the same size so the two line up in a row.labelis the accessible name and is required.Button::newnow documents that it uses only the string content of its argument; aRichText's size, colour, and style are dropped. Reach forButton::iconinstead of sizing the text.
MenuBar
MenuBarUi::ui()exposes the strip'sUifor 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
Full changelog: https://github.com/matrix-research-inc/egui-elegance/blob/main/CHANGELOG.md
v0.16.0
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
PairItemgained a publicicon_tintfield and is now#[non_exhaustive], as is the newIconTintenum. Struct literals, functional record update (..other), and destructuring without..no longer compile — construct withPairItem::new(...)plus the builder methods:Doing this once now avoids a second break the next time the struct grows.// was: PairItem { id: "web-01".into(), name: "web-01".into(), detail: None, icon: None } // now: PairItem::new("web-01", "web-01")
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 theKnob's scroll wheel — once when the scroll stops rather than once per frame of its smoothed delta. Works onMetricSlider,PercentSlider,Slider,RangeSlider, andKnob. 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. AMetricSliderinstopsmode previously firedchanged()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
ColorPickeris keyboard-operable. Its saturation/value plane, hue strip, and alpha strip were pointer-only:Tablanded 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←/→plusHome/End;Shiftis 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, orKnoband 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.RangeSlideralready 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
Knobmoved 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.TabandEscstill move focus away. Knobno longer resets to itsdefaulton Space, only on0(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 dotBrowserTabalready uses for unsaved work, so a picker reads as unsaved beside a dirtyTextInputin the same form. Pass the last committed value rather than adirtyflag 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::FOLDERandglyphs::FOLDER_OPEN(Lucidefolder/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
ColorPickernow 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
Targets egui 0.36, and fixes overlay layering against foreground windows.
egui 0.36
egui,eframe, andegui_kittestnow target0.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_filesis nowVec<egui::DroppedFileHandle>. Upstream turnedDroppedFileinto a trait, sopathandbytesare methods rather than fields: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.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 }
Overlay layering
- An
egui::WindowatOrder::Foregroundrendered above an openModal's backdrop and stayed clickable, and clicking it raised that window above the modal card. Reported by @greekfetacheese in #13.ModalandDrawernow each render as a single foreground area and claim the modal layer while open, so callers need not know about layer orders. Escdismissed 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
Updates the egui family to 0.35 and centralizes the widget id-salt bound into a single trait.
egui 0.35
egui,eframe, andegui_kittestnow target0.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::newnow requiresHash + Debugid sources, andPanel::show_insidewas renamed toshow.
IdSalt trait
- New public bound
elegance::IdSalt, blanket-implemented over everyHash + Debugtype. Each widget that takes an id salt now spells its parameterimpl 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.