Skip to main content

Crate macos_liquid_glass

Crate macos_liquid_glass 

Source
Expand description

Liquid Glass windows on macOS, that follow the system Icon & widget style.

macOS 26 introduced NSGlassEffectView, the material behind Liquid Glass. Putting a window on it is the easy part. The hard part — and what this crate is actually for — is making that window agree with System Settings ▸ Appearance ▸ Icon & widget style, the setting that restyles desktop widgets, and keeping it in agreement as the user changes it.

That turns out to involve a preference key with nine tokens behind a four-option UI, a notification that no notification centre posts, and a key-value observer that must never read a preference from inside its own callback. Each of those is documented where it is handled, with the measurement that established it. MEASUREMENTS.md in the repository is the long form.

§Features

featuredefaultwhat it brings
glassyesthe glass::GlassSurface wrapper and its availability guard
windowyesa transparent window hosting one glass surface, titled or borderless
icon-styleyesthe Icon & widget style tracker — usable on its own
private-spinotwo undocumented selectors; see below

is_dark and accessibility are behind no feature: the first is the crate’s only light/dark resolver and both halves need it, and the second is an obligation rather than an option.

§Accessibility

A translucent surface has to answer to Reduce Transparency. This crate reports that setting through accessibility and does not act on it for you — NSGlassEffectView has no opacity control, so honouring it means not using the material at all and substituting opaque content, which is the caller’s to build. That module states the reasoning in full and shows the shape to write. Check it before you construct a surface.

private-spi is off because App Store Review Guideline 2.5.1 is “Apps may only use public APIs”, with no respondsToSelector: exemption — a crate reaching private selectors by default would hand every consumer a submission liability they never opted into. Without it icon-style still tracks the style; only icon_style::WidgetStyle::tint stops reporting a colour, because the theme colour has no public source.

§Example

Compiled as a doctest, so it cannot drift from the API the way a README snippet can.

The example needs window, glass and icon-style together, so cfg_attr picks the opening fence — runnable when all three are on, ignore otherwise — which keeps cargo test green under every other feature set, including default-features = false, features = ["icon-style"], the standalone-tracker configuration this crate advertises. Verify a change here with cargo test, not cargo check: check does not compile doctests.

use macos_liquid_glass::glass::{GlassStyle, GlassSurface};
use macos_liquid_glass::window::GlassWindow;
use objc2_foundation::{MainThreadMarker, NSPoint, NSRect, NSSize};

let mtm = MainThreadMarker::new().expect("main thread");
let size = NSSize::new(560.0, 360.0);
let frame = NSRect::new(NSPoint::new(0.0, 0.0), size);

let window = GlassWindow::new(mtm, size, "example");
let glass = GlassSurface::new(mtm, frame, GlassStyle::Clear, 16.0)?;
window.set_content_view(glass.view());

// Track the Icon & widget style for as long as the window lives. The
// window owns the tracker, so there is nothing to keep hold of.
window.follow_icon_style();

window.show();

§Driving your own drawing from the style

GlassWindow::follow_icon_style sets the window’s appearance and nothing else. To colour your own content from the style — a theme tint, a dimming layer, per-token drawing — observe it directly and keep the observer alive for as long as you want changes, because dropping it is what unregisters:

let _observer = StyleObserver::new(mtm, move |style| {
    match style.token() { /* all nine are distinguishable here */ }
    let tint = style.tint();
});

icon-style carries no dependency on window, so a consumer that already has an NSWindow takes the tracker by itself and applies the style however it draws:

macos-liquid-glass = { version = "1.0.0-beta.1", default-features = false, features = ["icon-style"] }

§Threads

Everything here is main-thread-only and none of it is Send or SyncGlassWindow, GlassSurface and StyleObserver all hold objects that are MainThreadOnly, so the compiler will not let one cross a thread boundary. That is enforcement, not convention: you cannot construct any of them without a MainThreadMarker, and you cannot move one to a thread that has no such marker.

The observer’s callback is delivered on the main thread too, so it may touch AppKit freely. It is Fn rather than FnMut because it can re-enter — see icon_style::StyleObserver::new.

accessibility is the exception, and reads NSWorkspace, which is not main-thread-only.

§Platform

macOS only. On every other target this crate is empty rather than absent, so a cross-platform consumer can depend on it unconditionally and gate at the call site. The glass surface additionally requires macOS 26 at runtimeNSGlassEffectView does not exist before it — which is checked rather than assumed.

Re-exports§

pub use objc2;macOS
pub use objc2_app_kit;macOS
pub use objc2_core_foundation;macOS
pub use objc2_foundation;macOS
pub use objc2_io_surface;macOS and drawable
pub use objc2_quartz_core;macOS and drawable

Modules§

accessibilitymacOS
The accessibility display settings a translucent surface has to answer to.
drawablemacOS and drawable
A content view something else paints into: an NSView whose layer shows an IOSurface, with a display link, the input events a responder wants, and the backing-scale bookkeeping that AppKit leaves to the view.
glassmacOS and glass
The Liquid Glass material surface.
icon_stylemacOS and icon-style
Tracking System Settings ▸ Appearance ▸ Icon & widget style.
menumacOS
The application’s main menu: the three menus every macOS app carries, wired to the standard responder-chain actions, so Cmd-Q quits, Cmd-W closes the window, Cmd-M minimises, and Cmd-C/Cmd-V reach the first responder’s copy: and paste: – which DrawableView forwards to its Responder::command. Both are behind the drawable feature, so these are named rather than linked: this module is not gated on it, and an intra-doc link into a module the default features do not build resolves nowhere.
windowmacOS and window
A transparent window suitable for hosting a glass surface.

Functions§

appkit_versionmacOS
The AppKit the process is running against, as NSAppKitVersionNumber – the build number a launch measurement should be recorded beside. Reading it is also the one reference to an AppKit symbol a binary that reaches every class by name through the runtime has, which is what keeps the framework’s load command when the link dead-strips the dylibs nothing references: -needed_framework only works ahead of the -framework the bindings emit, and a build script’s link arguments come after it.
icon_viewmacOS
An NSImageView showing image, scaled to fit — for a titlebar strip or anywhere the caller then positions with setFrame/autoresizing.
image_from_bytesmacOS
Decode encoded image bytes (PNG, JPEG, …) into an NSImage, or None if the data is not a decodable image.
is_darkmacOS
Whether an appearance resolves to Dark.
set_application_iconmacOS
Set the application’s dock and Cmd-Tab icon.