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§
fn clipboard(&self) -> &dyn Clipboard
Sourcefn show_file_open_dialog(&self, opts: FileDialogOptions) -> Option<PathBuf>
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.
Sourcefn show_file_save_dialog(&self, opts: FileDialogOptions) -> Option<PathBuf>
fn show_file_save_dialog(&self, opts: FileDialogOptions) -> Option<PathBuf>
Show a native file-save dialog.
Sourcefn show_folder_open_dialog(&self, opts: FileDialogOptions) -> Option<PathBuf>
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.
Sourcefn show_message_dialog(
&self,
opts: MessageDialogOptions,
) -> Option<MessageDialogChoice>
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.
Sourcefn send_notification(&self, n: Notification)
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).
Sourcefn platform_name(&self) -> &'static str
fn platform_name(&self) -> &'static str
Platform identifier — matches the BackendNative.backend field.
One of "tui", "gtk", "win-gui", "macos".
Provided Methods§
Sourcefn open_url_result(&self, url: &str) -> ServiceResult<()>
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”).
Sourcefn reveal_in_file_manager(&self, path: &Path) -> ServiceResult<()>
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).
Sourcefn open_path(&self, path: &Path) -> ServiceResult<()>
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.
Sourcefn move_to_trash(&self, path: &Path) -> ServiceResult<()>
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).
Sourcefn beep(&self) -> ServiceResult<()>
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.
Sourcefn system_theme(&self) -> ServiceResult<SystemTheme>
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 ascrate::backend::BackendCaps::kitty_keyboard: an environment-only heuristic (COLORFGBG) parsed bytui::caps::detect_system_theme_from. No OS accent colour or high-contrast signal exists in a terminal, so those fields are alwaysNone/falsethere. ReturnsUnsupportedwhenCOLORFGBGis 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/AccentUIColorTypes).
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.
Sourcefn secret_store(&self) -> &dyn SecretStore
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.
Sourcefn displays(&self) -> ServiceResult<Vec<Display>>
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()forbounds/work_area,backingScaleFactor()forscale.screens()[0]is always the screen containing the menu bar — used asprimary(distinct fromNSScreen::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’swork_areais always a copy ofboundson every desktop. No primary-monitor flag either; index0(enumeration order) is used as a best-effort proxy — seecrate::gtk::services::GtkPlatformServices::displays’s doc. - Win-GUI —
EnumDisplayMonitors+GetMonitorInfoWforbounds/work_area/primary(rcMonitor/rcWork/MONITORINFOF_PRIMARY),GetDpiForMonitorforscale(dpi / USER_DEFAULT_SCREEN_DPI). - TUI — a truthful degrade, not
Unsupported: oneDisplaywhosebounds/work_areaare both the terminal’s cell grid (0, 0, width, height, the same cell unitsBackend::viewportreports),scale: 1.0,primary: true— seecrate::tui::services’s module doc.
Sourcefn cursor_screen_point(&self) -> ServiceResult<Point>
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§
impl PlatformServices for GtkPlatformServices
gtk only.impl PlatformServices for TuiPlatformServices
tui only.impl PlatformServices for WinPlatformServices
win only.