Skip to main content

PlatformServices

Trait PlatformServices 

Source
pub trait PlatformServices {
Show 17 methods // Required methods fn clipboard(&self) -> &dyn Clipboard; fn show_file_open_dialog(&self, opts: FileDialogOptions) -> Option<PathBuf>; fn show_file_save_dialog(&self, opts: FileDialogOptions) -> Option<PathBuf>; fn show_folder_open_dialog( &self, opts: FileDialogOptions, ) -> Option<PathBuf>; fn show_message_dialog( &self, opts: MessageDialogOptions, ) -> Option<MessageDialogChoice>; fn send_notification(&self, n: Notification); fn open_url(&self, url: &str); fn platform_name(&self) -> &'static str; // Provided methods fn open_url_result(&self, url: &str) -> ServiceResult<()> { ... } fn reveal_in_file_manager(&self, path: &Path) -> ServiceResult<()> { ... } fn open_path(&self, path: &Path) -> ServiceResult<()> { ... } fn move_to_trash(&self, path: &Path) -> ServiceResult<()> { ... } fn beep(&self) -> ServiceResult<()> { ... } fn system_theme(&self) -> ServiceResult<SystemTheme> { ... } fn secret_store(&self) -> &dyn SecretStore { ... } fn displays(&self) -> ServiceResult<Vec<Display>> { ... } fn cursor_screen_point(&self) -> ServiceResult<Point> { ... }
}
Expand description

Platform services the backend exposes to apps: clipboard, file dialogs, message/alert dialogs, notifications, URL opening, and the OS credential store (Self::secret_store).

Required Methods§

Source

fn clipboard(&self) -> &dyn Clipboard

Source

fn show_file_open_dialog(&self, opts: FileDialogOptions) -> Option<PathBuf>

Show a native file-open dialog (blocking). Returns None if the user cancelled. TUI backends have no native dialog to show and unconditionally return None (no stderr hint is written); apps should provide an in-TUI picker instead.

Source

fn show_file_save_dialog(&self, opts: FileDialogOptions) -> Option<PathBuf>

Show a native file-save dialog.

Source

fn show_folder_open_dialog(&self, opts: FileDialogOptions) -> Option<PathBuf>

Show a native directory-select dialog (blocking). Returns None if the user cancelled.

Reuses FileDialogOptions rather than a narrower type: only FileDialogOptions::title and FileDialogOptions::initial_dir apply to a directory chooser. FileDialogOptions::filters and FileDialogOptions::initial_filename are meaningless for a directory (no extension, no “file name” field to seed) and every implementation of this method ignores them — callers should just leave them at their Default rather than populating them.

TUI backends have no native dialog to show and unconditionally return None (no stderr hint is written), same as Self::show_file_open_dialog — apps should provide an in-canvas picker instead. Callers distinguish “no native chooser at all” from “the user cancelled” via BackendCaps::folder_dialogs, the same way they already do for BackendCaps::file_dialogs.

Source

fn show_message_dialog( &self, opts: MessageDialogOptions, ) -> Option<MessageDialogChoice>

Show a native message/alert dialog (blocking). Returns the id of the button the user chose, or None if the dialog was dismissed without choosing one (Escape, close box) or this backend has no native alert facility at all — mirrors Self::show_file_open_dialog’s None shape exactly. Callers separate the two via BackendCaps::native_dialogs, the same way they already do for BackendCaps::file_dialogs.

This is a parallel path alongside the in-canvas Dialog primitive (Backend::draw_dialog), not a replacement for it: crate::primitives::dialog::native_dialog_options decides, per-dialog, whether a given Dialog can go native at all — callers should consult that before calling this method, and fall back to draw_dialog when it returns None. TUI backends have no native dialog to show and unconditionally return None (no stderr hint is written); the in-canvas Dialog primitive stays the TUI path.

Source

fn send_notification(&self, n: Notification)

Dispatch a system notification — see Notification’s doc for icon/actions/silent/tag.

A click on the notification (its body, or one of n’s declared actions) should push UiEvent::NotificationActivated where the backend has a native channel for it — today only GTK does (see gtk::services’s module doc); macOS’s unbundled osascript fallback and Win’s Shell_NotifyIconW balloon fallback have no click-through of their own to report through, and honestly do not emit the event rather than faking it (each backend’s send_notification doc explains why, and what a real implementation there would need).

Source

fn open_url(&self, url: &str)

Open a URL in the platform’s default browser.

Source

fn platform_name(&self) -> &'static str

Platform identifier — matches the BackendNative.backend field. One of "tui", "gtk", "win-gui", "macos".

Provided Methods§

Source

fn open_url_result(&self, url: &str) -> ServiceResult<()>

Fallible twin of Self::open_url (D-009 seam-2’s _result-twin pattern — the same shape Clipboard::write_text_result already carries): reports BackendError::Unsupported on a backend with no way to open a URL at all, instead of open_url’s silent no-op. open_url itself keeps its infallible signature unchanged so both existing consumers (which only ever call open_url) keep compiling untouched.

Default: calls open_url and returns Ok(()) — a backend whose open_url is a real implementation (GTK via gio::AppInfo::launch_default_for_uri, Win-GUI via ShellExecuteW, macOS via open) answers exactly like this method doesn’t exist, at zero cost. TuiPlatformServices is the one override: TUI shells out to the platform’s URL opener (xdg-open/open/cmd /c start) with an OSC 8 hyperlink fallback, and only reports Err(BackendError::Unsupported) when neither reaches anything (see tui::services’s module doc, “URL opening”).

Source

fn reveal_in_file_manager(&self, path: &Path) -> ServiceResult<()>

Reveal path in the platform’s file manager with it selected — “Reveal in Finder” / “Show in Explorer” / “Show in Files” (Electron’s shell.showItemInFolder).

Default: Err(BackendError::Unsupported), the same “future backend compiles before it has an opinion” placeholder Self::system_theme’s default doc explains. GTK, macOS, and Win-GUI each override this with a real implementation. TUI also has a real degrade (crate::desktop::reveal_in_file_manager: open -R / Explorer /select, / D-Bus ShowItems, falling back to opening the parent directory) — the desktop session underneath the terminal can do this even though the terminal itself can’t, the same reasoning open_url_result applies to launching a browser (see tui::services’s module doc’s “shell.* parity” section).

Source

fn open_path(&self, path: &Path) -> ServiceResult<()>

Open path with the platform’s default handler for its type — Electron’s shell.openPath. Distinct from Self::open_url: that method takes a URL string and always goes through the scheme’s registered handler (a browser for https://, a mail client for mailto:); this one takes a filesystem path directly.

Default: Err(BackendError::Unsupported), same placeholder reasoning as Self::reveal_in_file_manager. GTK, macOS, and Win-GUI override with a real implementation. TUI also overrides this one — see tui::services::TuiPlatformServices::open_path’s doc for why a terminal can still shell out to xdg-open/open even though it has no browser for Self::open_url to hand a URL to.

Source

fn move_to_trash(&self, path: &Path) -> ServiceResult<()>

Move path to the platform trash/recycle bin — recoverable, unlike deleting it outright — Electron’s shell.trashItem.

Default: Err(BackendError::Unsupported), same placeholder reasoning as Self::reveal_in_file_manager. Every backend in this crate overrides it with the same implementation ([crate::desktop::move_to_trash]): the cross-platform trash crate already wraps NSWorkspace’s Objective-C -trashItemAtURL:resultingItemURL:error:, gio::File::trash’s freedesktop.org trash-spec equivalent, and SHFileOperationW(FOF_ALLOWUNDO) internally, and needs only a filesystem — no live desktop/window-server session — on any of the three. So TUI gets the exact same real implementation as the other three, not a degraded one (see the module doc’s “TUI story” note this issue itself made about this method).

Source

fn beep(&self) -> ServiceResult<()>

Ring the terminal bell / play the system alert sound — Electron’s shell.beep.

Default: Err(BackendError::Unsupported), same placeholder reasoning as Self::reveal_in_file_manager. Every backend overrides it — TUI’s BEL is arguably the most honest of the four: it’s a terminal’s only notification channel, so this is full support there, not a degrade.

Source

fn system_theme(&self) -> ServiceResult<SystemTheme>

Query the OS-level light/dark preference, accent colour, and high-contrast setting.

Returns BackendError::Unsupported rather than a guessed value on a backend/session with no way to answer — unlike Self::show_file_open_dialog’s ambiguous None (which needed a dedicated BackendCaps flag to disambiguate “cancelled” from “no native facility”), a Result already carries that distinction in its own type, so this method gets no BackendCaps field of its own.

Default: always Err(BackendError::Unsupported) — the honest starting point (rule 2’s “new function alongside the old one” isn’t in play here since no downstream consumer implements PlatformServices, per CLAUDE.md’s Downstream consumers table, but a default still means a future fifth backend compiles before it has an opinion). Every backend in this crate overrides it:

  • TUI (TuiPlatformServices, tui::caps) — honest degrade, same detect/probe split as crate::backend::BackendCaps::kitty_keyboard: an environment-only heuristic (COLORFGBG) parsed by tui::caps::detect_system_theme_from. No OS accent colour or high-contrast signal exists in a terminal, so those fields are always None/false there. Returns Unsupported when COLORFGBG is unset or unparseable — a terminal that never sets it genuinely gives no signal, so guessing would be no more honest than refusing.
  • GTK — gtk4::Settings::gtk-application-prefer-dark-theme / gtk-theme-name (no accent-colour API without libadwaita).
  • macOS — NSApp.effectiveAppearance / NSColor::controlAccentColor.
  • Win-GUI — UISettings::GetColorValue (Background/Accent UIColorTypes).

No backend pushes crate::UiEvent::SystemThemeChanged on a live OS theme change yet (same “declare the gap, don’t fake it” posture as crate::UiEvent::WindowStateChanged, which also has zero producers today) — this method is a poll-on-demand query, not a subscription.

Source

fn secret_store(&self) -> &dyn SecretStore

The OS credential store — Keychain (macOS), Secret Service (Linux), Windows Credential Manager — for apps that hold an API token, a password, or an OAuth refresh token and would otherwise have no choice but to invent their own storage or write it to plaintext config.

This one is backend-independent: the returned SecretStore is backed by the cross-platform keyring crate directly, not by a per-backend native implementation, so it needs no override in tui/gtk/macos/win::services — the same default body serves all four. keyring itself needs no window, no display server, and no desktop session (its Linux store talks to the Secret Service over D-Bus, not through any GUI toolkit), so this is full support on TUI, not a degrade. See SecretStore’s own doc for the get/set/delete shape.

Default: a keyring-backed SecretStore when this crate was built with any of the tui/gtk/macos/win features (the same four that already pull in the cross-platform trash crate for Self::move_to_trash — see that dependency’s Cargo.toml comment for why the list is exactly those four); a SecretStore whose every method returns Err(BackendError::Unsupported) when none of them are enabled (a bare cargo check -p quadraui with no features, which this crate supports — see lib.rs’s unconditional pub mod backend;). No in-tree backend overrides this — there is nothing backend-specific left to override.

Source

fn displays(&self) -> ServiceResult<Vec<Display>>

Enumerate every connected display — bounds, usable work area, DPI scale, and which one is primary. Needed to restore saved window bounds after a monitor change, place a new window sensibly, or position a popup near the cursor across displays. See Display’s own doc for the field-by-field contract, including the GTK/Wayland work-area caveat.

Default: Err(BackendError::Unsupported), the same placeholder reasoning Self::reveal_in_file_manager’s doc explains. Every backend in this crate overrides it:

  • macOS — NSScreen::screens(), frame()/visibleFrame() for bounds/work_area, backingScaleFactor() for scale. screens()[0] is always the screen containing the menu bar — used as primary (distinct from NSScreen::mainScreen(), which tracks the key window’s screen, not the platform’s primary one).
  • GTK — gdk::Display::monitors() + Monitor::geometry() / scale_factor(). No work-area API exists in GDK4 at all (removed from GDK3 entirely, not just absent on Wayland) — GTK’s work_area is always a copy of bounds on every desktop. No primary-monitor flag either; index 0 (enumeration order) is used as a best-effort proxy — see crate::gtk::services::GtkPlatformServices::displays’s doc.
  • Win-GUI — EnumDisplayMonitors + GetMonitorInfoW for bounds/work_area/primary (rcMonitor/rcWork/ MONITORINFOF_PRIMARY), GetDpiForMonitor for scale (dpi / USER_DEFAULT_SCREEN_DPI).
  • TUI — a truthful degrade, not Unsupported: one Display whose bounds/work_area are both the terminal’s cell grid (0, 0, width, height, the same cell units Backend::viewport reports), scale: 1.0, primary: true — see crate::tui::services’s module doc.
Source

fn cursor_screen_point(&self) -> ServiceResult<Point>

The mouse cursor’s current position in screen coordinates — the same coordinate system Self::displays’s bounds/work_area use, so a caller can directly test which Display currently contains the cursor (e.g. positioning a popup near the cursor across displays).

Default: Err(BackendError::Unsupported), same placeholder posture as Self::displays. GTK and TUI both keep this default, for real (not placeholder) reasons documented on each override: GDK4 removed the global/root-window pointer-position query entirely (Surface::device_position is surface-relative only, and GDK4 surfaces don’t expose their own screen origin either — a real protocol-level Wayland restriction, not a missing binding); a terminal has no synchronous “where is the mouse right now” query at all — only UiEvent::MouseMoved, delivered when the terminal’s mouse-tracking mode is on. macOS (NSEvent::mouseLocation()) and Win-GUI (GetCursorPos) both override with a real answer.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§

Source§

impl PlatformServices for GtkPlatformServices

Available on crate feature gtk only.
Source§

impl PlatformServices for TuiPlatformServices

Available on crate feature tui only.
Source§

impl PlatformServices for WinPlatformServices

Available on crate feature win only.