platform.rs

  1// todo(linux): remove
  2#![cfg_attr(target_os = "linux", allow(dead_code))]
  3// todo(windows): remove
  4#![cfg_attr(windows, allow(dead_code))]
  5
  6mod app_menu;
  7mod keystroke;
  8
  9#[cfg(not(target_os = "macos"))]
 10mod cosmic_text;
 11
 12#[cfg(target_os = "linux")]
 13mod linux;
 14
 15#[cfg(target_os = "macos")]
 16mod mac;
 17
 18#[cfg(any(target_os = "linux", target_os = "windows", feature = "macos-blade"))]
 19mod blade;
 20
 21#[cfg(any(test, feature = "test-support"))]
 22mod test;
 23
 24#[cfg(target_os = "windows")]
 25mod windows;
 26
 27use crate::{
 28    Action, AnyWindowHandle, AsyncWindowContext, BackgroundExecutor, Bounds, DevicePixels,
 29    DispatchEventResult, Font, FontId, FontMetrics, FontRun, ForegroundExecutor, GlyphId, Keymap,
 30    LineLayout, Pixels, PlatformInput, Point, RenderGlyphParams, RenderImageParams,
 31    RenderSvgParams, Scene, SharedString, Size, Task, TaskLabel, WindowContext,
 32};
 33use anyhow::Result;
 34use async_task::Runnable;
 35use futures::channel::oneshot;
 36use parking::Unparker;
 37use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
 38use seahash::SeaHasher;
 39use serde::{Deserialize, Serialize};
 40use std::borrow::Cow;
 41use std::hash::{Hash, Hasher};
 42use std::time::Duration;
 43use std::{
 44    fmt::{self, Debug},
 45    ops::Range,
 46    path::{Path, PathBuf},
 47    rc::Rc,
 48    sync::Arc,
 49};
 50use uuid::Uuid;
 51
 52pub use app_menu::*;
 53pub use keystroke::*;
 54
 55#[cfg(not(target_os = "macos"))]
 56pub(crate) use cosmic_text::*;
 57#[cfg(target_os = "linux")]
 58pub(crate) use linux::*;
 59#[cfg(target_os = "macos")]
 60pub(crate) use mac::*;
 61pub use semantic_version::SemanticVersion;
 62#[cfg(any(test, feature = "test-support"))]
 63pub(crate) use test::*;
 64use time::UtcOffset;
 65#[cfg(target_os = "windows")]
 66pub(crate) use windows::*;
 67
 68#[cfg(target_os = "macos")]
 69pub(crate) fn current_platform() -> Rc<dyn Platform> {
 70    Rc::new(MacPlatform::new())
 71}
 72#[cfg(target_os = "linux")]
 73pub(crate) fn current_platform() -> Rc<dyn Platform> {
 74    let wayland_display = std::env::var_os("WAYLAND_DISPLAY");
 75    let x11_display = std::env::var_os("DISPLAY");
 76
 77    let use_wayland = wayland_display.is_some_and(|display| !display.is_empty());
 78    let use_x11 = x11_display.is_some_and(|display| !display.is_empty());
 79
 80    if use_wayland {
 81        Rc::new(WaylandClient::new())
 82    } else if use_x11 {
 83        Rc::new(X11Client::new())
 84    } else {
 85        Rc::new(HeadlessClient::new())
 86    }
 87}
 88// todo("windows")
 89#[cfg(target_os = "windows")]
 90pub(crate) fn current_platform() -> Rc<dyn Platform> {
 91    Rc::new(WindowsPlatform::new())
 92}
 93
 94pub(crate) trait Platform: 'static {
 95    fn background_executor(&self) -> BackgroundExecutor;
 96    fn foreground_executor(&self) -> ForegroundExecutor;
 97    fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
 98
 99    fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>);
100    fn quit(&self);
101    fn restart(&self, binary_path: Option<PathBuf>);
102    fn activate(&self, ignoring_other_apps: bool);
103    fn hide(&self);
104    fn hide_other_apps(&self);
105    fn unhide_other_apps(&self);
106
107    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>>;
108    fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>>;
109    fn active_window(&self) -> Option<AnyWindowHandle>;
110    fn open_window(
111        &self,
112        handle: AnyWindowHandle,
113        options: WindowParams,
114    ) -> Box<dyn PlatformWindow>;
115
116    /// Returns the appearance of the application's windows.
117    fn window_appearance(&self) -> WindowAppearance;
118
119    fn open_url(&self, url: &str);
120    fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>);
121    fn register_url_scheme(&self, url: &str) -> Task<Result<()>>;
122
123    fn prompt_for_paths(
124        &self,
125        options: PathPromptOptions,
126    ) -> oneshot::Receiver<Option<Vec<PathBuf>>>;
127    fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver<Option<PathBuf>>;
128    fn reveal_path(&self, path: &Path);
129
130    fn on_quit(&self, callback: Box<dyn FnMut()>);
131    fn on_reopen(&self, callback: Box<dyn FnMut()>);
132
133    fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap);
134    fn add_recent_document(&self, _path: &Path) {}
135    fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>);
136    fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>);
137    fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>);
138
139    fn os_name(&self) -> &'static str;
140    fn os_version(&self) -> Result<SemanticVersion>;
141    fn app_version(&self) -> Result<SemanticVersion>;
142    fn app_path(&self) -> Result<PathBuf>;
143    fn local_timezone(&self) -> UtcOffset;
144    fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf>;
145
146    fn set_cursor_style(&self, style: CursorStyle);
147    fn should_auto_hide_scrollbars(&self) -> bool;
148
149    fn write_to_primary(&self, item: ClipboardItem);
150    fn write_to_clipboard(&self, item: ClipboardItem);
151    fn read_from_primary(&self) -> Option<ClipboardItem>;
152    fn read_from_clipboard(&self) -> Option<ClipboardItem>;
153
154    fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task<Result<()>>;
155    fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>>;
156    fn delete_credentials(&self, url: &str) -> Task<Result<()>>;
157}
158
159/// A handle to a platform's display, e.g. a monitor or laptop screen.
160pub trait PlatformDisplay: Send + Sync + Debug {
161    /// Get the ID for this display
162    fn id(&self) -> DisplayId;
163
164    /// Returns a stable identifier for this display that can be persisted and used
165    /// across system restarts.
166    fn uuid(&self) -> Result<Uuid>;
167
168    /// Get the bounds for this display
169    fn bounds(&self) -> Bounds<DevicePixels>;
170}
171
172/// An opaque identifier for a hardware display
173#[derive(PartialEq, Eq, Hash, Copy, Clone)]
174pub struct DisplayId(pub(crate) u32);
175
176impl Debug for DisplayId {
177    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178        write!(f, "DisplayId({})", self.0)
179    }
180}
181
182unsafe impl Send for DisplayId {}
183
184pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
185    fn bounds(&self) -> Bounds<DevicePixels>;
186    fn is_maximized(&self) -> bool;
187    fn window_bounds(&self) -> WindowBounds;
188    fn content_size(&self) -> Size<Pixels>;
189    fn scale_factor(&self) -> f32;
190    fn appearance(&self) -> WindowAppearance;
191    fn display(&self) -> Rc<dyn PlatformDisplay>;
192    fn mouse_position(&self) -> Point<Pixels>;
193    fn modifiers(&self) -> Modifiers;
194    fn set_input_handler(&mut self, input_handler: PlatformInputHandler);
195    fn take_input_handler(&mut self) -> Option<PlatformInputHandler>;
196    fn prompt(
197        &self,
198        level: PromptLevel,
199        msg: &str,
200        detail: Option<&str>,
201        answers: &[&str],
202    ) -> Option<oneshot::Receiver<usize>>;
203    fn activate(&self);
204    fn is_active(&self) -> bool;
205    fn set_title(&mut self, title: &str);
206    fn set_app_id(&mut self, app_id: &str);
207    fn set_background_appearance(&mut self, background_appearance: WindowBackgroundAppearance);
208    fn set_edited(&mut self, edited: bool);
209    fn show_character_palette(&self);
210    fn minimize(&self);
211    fn zoom(&self);
212    fn toggle_fullscreen(&self);
213    fn is_fullscreen(&self) -> bool;
214    fn on_request_frame(&self, callback: Box<dyn FnMut()>);
215    fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> DispatchEventResult>);
216    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>);
217    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>);
218    fn on_moved(&self, callback: Box<dyn FnMut()>);
219    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>);
220    fn on_close(&self, callback: Box<dyn FnOnce()>);
221    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>);
222    fn draw(&self, scene: &Scene);
223    fn completed_frame(&self) {}
224    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
225
226    #[cfg(target_os = "windows")]
227    fn get_raw_handle(&self) -> windows::HWND;
228
229    fn show_window_menu(&self, position: Point<Pixels>);
230    fn start_system_move(&self);
231    fn should_render_window_controls(&self) -> bool;
232
233    #[cfg(any(test, feature = "test-support"))]
234    fn as_test(&mut self) -> Option<&mut TestWindow> {
235        None
236    }
237}
238
239/// This type is public so that our test macro can generate and use it, but it should not
240/// be considered part of our public API.
241#[doc(hidden)]
242pub trait PlatformDispatcher: Send + Sync {
243    fn is_main_thread(&self) -> bool;
244    fn dispatch(&self, runnable: Runnable, label: Option<TaskLabel>);
245    fn dispatch_on_main_thread(&self, runnable: Runnable);
246    fn dispatch_after(&self, duration: Duration, runnable: Runnable);
247    fn park(&self, timeout: Option<Duration>) -> bool;
248    fn unparker(&self) -> Unparker;
249
250    #[cfg(any(test, feature = "test-support"))]
251    fn as_test(&self) -> Option<&TestDispatcher> {
252        None
253    }
254}
255
256pub(crate) trait PlatformTextSystem: Send + Sync {
257    fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()>;
258    fn all_font_names(&self) -> Vec<String>;
259    fn all_font_families(&self) -> Vec<String>;
260    fn font_id(&self, descriptor: &Font) -> Result<FontId>;
261    fn font_metrics(&self, font_id: FontId) -> FontMetrics;
262    fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>>;
263    fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>>;
264    fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId>;
265    fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>>;
266    fn rasterize_glyph(
267        &self,
268        params: &RenderGlyphParams,
269        raster_bounds: Bounds<DevicePixels>,
270    ) -> Result<(Size<DevicePixels>, Vec<u8>)>;
271    fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout;
272}
273
274/// Basic metadata about the current application and operating system.
275#[derive(Clone, Debug)]
276pub struct AppMetadata {
277    /// The name of the current operating system
278    pub os_name: &'static str,
279
280    /// The operating system's version
281    pub os_version: Option<SemanticVersion>,
282
283    /// The current version of the application
284    pub app_version: Option<SemanticVersion>,
285}
286
287#[derive(PartialEq, Eq, Hash, Clone)]
288pub(crate) enum AtlasKey {
289    Glyph(RenderGlyphParams),
290    Svg(RenderSvgParams),
291    Image(RenderImageParams),
292}
293
294impl AtlasKey {
295    pub(crate) fn texture_kind(&self) -> AtlasTextureKind {
296        match self {
297            AtlasKey::Glyph(params) => {
298                if params.is_emoji {
299                    AtlasTextureKind::Polychrome
300                } else {
301                    AtlasTextureKind::Monochrome
302                }
303            }
304            AtlasKey::Svg(_) => AtlasTextureKind::Monochrome,
305            AtlasKey::Image(_) => AtlasTextureKind::Polychrome,
306        }
307    }
308}
309
310impl From<RenderGlyphParams> for AtlasKey {
311    fn from(params: RenderGlyphParams) -> Self {
312        Self::Glyph(params)
313    }
314}
315
316impl From<RenderSvgParams> for AtlasKey {
317    fn from(params: RenderSvgParams) -> Self {
318        Self::Svg(params)
319    }
320}
321
322impl From<RenderImageParams> for AtlasKey {
323    fn from(params: RenderImageParams) -> Self {
324        Self::Image(params)
325    }
326}
327
328pub(crate) trait PlatformAtlas: Send + Sync {
329    fn get_or_insert_with<'a>(
330        &self,
331        key: &AtlasKey,
332        build: &mut dyn FnMut() -> Result<(Size<DevicePixels>, Cow<'a, [u8]>)>,
333    ) -> Result<AtlasTile>;
334}
335
336#[derive(Clone, Debug, PartialEq, Eq)]
337#[repr(C)]
338pub(crate) struct AtlasTile {
339    pub(crate) texture_id: AtlasTextureId,
340    pub(crate) tile_id: TileId,
341    pub(crate) padding: u32,
342    pub(crate) bounds: Bounds<DevicePixels>,
343}
344
345#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
346#[repr(C)]
347pub(crate) struct AtlasTextureId {
348    // We use u32 instead of usize for Metal Shader Language compatibility
349    pub(crate) index: u32,
350    pub(crate) kind: AtlasTextureKind,
351}
352
353#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
354#[repr(C)]
355pub(crate) enum AtlasTextureKind {
356    Monochrome = 0,
357    Polychrome = 1,
358    Path = 2,
359}
360
361#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
362#[repr(C)]
363pub(crate) struct TileId(pub(crate) u32);
364
365impl From<etagere::AllocId> for TileId {
366    fn from(id: etagere::AllocId) -> Self {
367        Self(id.serialize())
368    }
369}
370
371impl From<TileId> for etagere::AllocId {
372    fn from(id: TileId) -> Self {
373        Self::deserialize(id.0)
374    }
375}
376
377pub(crate) struct PlatformInputHandler {
378    cx: AsyncWindowContext,
379    handler: Box<dyn InputHandler>,
380}
381
382impl PlatformInputHandler {
383    pub fn new(cx: AsyncWindowContext, handler: Box<dyn InputHandler>) -> Self {
384        Self { cx, handler }
385    }
386
387    fn selected_text_range(&mut self) -> Option<Range<usize>> {
388        self.cx
389            .update(|cx| self.handler.selected_text_range(cx))
390            .ok()
391            .flatten()
392    }
393
394    fn marked_text_range(&mut self) -> Option<Range<usize>> {
395        self.cx
396            .update(|cx| self.handler.marked_text_range(cx))
397            .ok()
398            .flatten()
399    }
400
401    fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String> {
402        self.cx
403            .update(|cx| self.handler.text_for_range(range_utf16, cx))
404            .ok()
405            .flatten()
406    }
407
408    fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str) {
409        self.cx
410            .update(|cx| {
411                self.handler
412                    .replace_text_in_range(replacement_range, text, cx);
413            })
414            .ok();
415    }
416
417    fn replace_and_mark_text_in_range(
418        &mut self,
419        range_utf16: Option<Range<usize>>,
420        new_text: &str,
421        new_selected_range: Option<Range<usize>>,
422    ) {
423        self.cx
424            .update(|cx| {
425                self.handler.replace_and_mark_text_in_range(
426                    range_utf16,
427                    new_text,
428                    new_selected_range,
429                    cx,
430                )
431            })
432            .ok();
433    }
434
435    fn unmark_text(&mut self) {
436        self.cx.update(|cx| self.handler.unmark_text(cx)).ok();
437    }
438
439    fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>> {
440        self.cx
441            .update(|cx| self.handler.bounds_for_range(range_utf16, cx))
442            .ok()
443            .flatten()
444    }
445
446    pub(crate) fn dispatch_input(&mut self, input: &str, cx: &mut WindowContext) {
447        self.handler.replace_text_in_range(None, input, cx);
448    }
449}
450
451/// Zed's interface for handling text input from the platform's IME system
452/// This is currently a 1:1 exposure of the NSTextInputClient API:
453///
454/// <https://developer.apple.com/documentation/appkit/nstextinputclient>
455pub trait InputHandler: 'static {
456    /// Get the range of the user's currently selected text, if any
457    /// Corresponds to [selectedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438242-selectedrange)
458    ///
459    /// Return value is in terms of UTF-16 characters, from 0 to the length of the document
460    fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>>;
461
462    /// Get the range of the currently marked text, if any
463    /// Corresponds to [markedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438250-markedrange)
464    ///
465    /// Return value is in terms of UTF-16 characters, from 0 to the length of the document
466    fn marked_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>>;
467
468    /// Get the text for the given document range in UTF-16 characters
469    /// Corresponds to [attributedSubstring(forProposedRange: actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438238-attributedsubstring)
470    ///
471    /// range_utf16 is in terms of UTF-16 characters
472    fn text_for_range(
473        &mut self,
474        range_utf16: Range<usize>,
475        cx: &mut WindowContext,
476    ) -> Option<String>;
477
478    /// Replace the text in the given document range with the given text
479    /// Corresponds to [insertText(_:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438258-inserttext)
480    ///
481    /// replacement_range is in terms of UTF-16 characters
482    fn replace_text_in_range(
483        &mut self,
484        replacement_range: Option<Range<usize>>,
485        text: &str,
486        cx: &mut WindowContext,
487    );
488
489    /// Replace the text in the given document range with the given text,
490    /// and mark the given text as part of of an IME 'composing' state
491    /// Corresponds to [setMarkedText(_:selectedRange:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438246-setmarkedtext)
492    ///
493    /// range_utf16 is in terms of UTF-16 characters
494    /// new_selected_range is in terms of UTF-16 characters
495    fn replace_and_mark_text_in_range(
496        &mut self,
497        range_utf16: Option<Range<usize>>,
498        new_text: &str,
499        new_selected_range: Option<Range<usize>>,
500        cx: &mut WindowContext,
501    );
502
503    /// Remove the IME 'composing' state from the document
504    /// Corresponds to [unmarkText()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438239-unmarktext)
505    fn unmark_text(&mut self, cx: &mut WindowContext);
506
507    /// Get the bounds of the given document range in screen coordinates
508    /// Corresponds to [firstRect(forCharacterRange:actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438240-firstrect)
509    ///
510    /// This is used for positioning the IME candidate window
511    fn bounds_for_range(
512        &mut self,
513        range_utf16: Range<usize>,
514        cx: &mut WindowContext,
515    ) -> Option<Bounds<Pixels>>;
516}
517
518/// The variables that can be configured when creating a new window
519#[derive(Debug)]
520pub struct WindowOptions {
521    /// Specifies the state and bounds of the window in screen coordinates.
522    /// - `None`: Inherit the bounds.
523    /// - `Some(WindowBounds)`: Open a window with corresponding state and its restore size.
524    pub window_bounds: Option<WindowBounds>,
525
526    /// The titlebar configuration of the window
527    pub titlebar: Option<TitlebarOptions>,
528
529    /// Whether the window should be focused when created
530    pub focus: bool,
531
532    /// Whether the window should be shown when created
533    pub show: bool,
534
535    /// The kind of window to create
536    pub kind: WindowKind,
537
538    /// Whether the window should be movable by the user
539    pub is_movable: bool,
540
541    /// The display to create the window on, if this is None,
542    /// the window will be created on the main display
543    pub display_id: Option<DisplayId>,
544
545    /// The appearance of the window background.
546    pub window_background: WindowBackgroundAppearance,
547
548    /// Application identifier of the window. Can by used by desktop environments to group applications together.
549    pub app_id: Option<String>,
550}
551
552/// The variables that can be configured when creating a new window
553#[derive(Debug)]
554pub(crate) struct WindowParams {
555    pub bounds: Bounds<DevicePixels>,
556
557    /// The titlebar configuration of the window
558    pub titlebar: Option<TitlebarOptions>,
559
560    /// The kind of window to create
561    pub kind: WindowKind,
562
563    /// Whether the window should be movable by the user
564    pub is_movable: bool,
565
566    pub focus: bool,
567
568    pub show: bool,
569
570    pub display_id: Option<DisplayId>,
571
572    pub window_background: WindowBackgroundAppearance,
573}
574
575/// Represents the status of how a window should be opened.
576#[derive(Debug, Copy, Clone, PartialEq)]
577pub enum WindowBounds {
578    /// Indicates that the window should open in a windowed state with the given bounds.
579    Windowed(Bounds<DevicePixels>),
580    /// Indicates that the window should open in a maximized state.
581    /// The bounds provided here represent the restore size of the window.
582    Maximized(Bounds<DevicePixels>),
583    /// Indicates that the window should open in fullscreen mode.
584    /// The bounds provided here represent the restore size of the window.
585    Fullscreen(Bounds<DevicePixels>),
586}
587
588impl Default for WindowBounds {
589    fn default() -> Self {
590        WindowBounds::Windowed(Bounds::default())
591    }
592}
593
594impl WindowBounds {
595    /// Retrieve the inner bounds
596    pub fn get_bounds(&self) -> Bounds<DevicePixels> {
597        match self {
598            WindowBounds::Windowed(bounds) => *bounds,
599            WindowBounds::Maximized(bounds) => *bounds,
600            WindowBounds::Fullscreen(bounds) => *bounds,
601        }
602    }
603}
604
605impl Default for WindowOptions {
606    fn default() -> Self {
607        Self {
608            window_bounds: None,
609            titlebar: Some(TitlebarOptions {
610                title: Default::default(),
611                appears_transparent: Default::default(),
612                traffic_light_position: Default::default(),
613            }),
614            focus: true,
615            show: true,
616            kind: WindowKind::Normal,
617            is_movable: true,
618            display_id: None,
619            window_background: WindowBackgroundAppearance::default(),
620            app_id: None,
621        }
622    }
623}
624
625/// The options that can be configured for a window's titlebar
626#[derive(Debug, Default)]
627pub struct TitlebarOptions {
628    /// The initial title of the window
629    pub title: Option<SharedString>,
630
631    /// Whether the titlebar should appear transparent
632    pub appears_transparent: bool,
633
634    /// The position of the macOS traffic light buttons
635    pub traffic_light_position: Option<Point<Pixels>>,
636}
637
638/// The kind of window to create
639#[derive(Copy, Clone, Debug, PartialEq, Eq)]
640pub enum WindowKind {
641    /// A normal application window
642    Normal,
643
644    /// A window that appears above all other windows, usually used for alerts or popups
645    /// use sparingly!
646    PopUp,
647}
648
649/// The appearance of the window, as defined by the operating system.
650///
651/// On macOS, this corresponds to named [`NSAppearance`](https://developer.apple.com/documentation/appkit/nsappearance)
652/// values.
653#[derive(Copy, Clone, Debug)]
654pub enum WindowAppearance {
655    /// A light appearance.
656    ///
657    /// On macOS, this corresponds to the `aqua` appearance.
658    Light,
659
660    /// A light appearance with vibrant colors.
661    ///
662    /// On macOS, this corresponds to the `NSAppearanceNameVibrantLight` appearance.
663    VibrantLight,
664
665    /// A dark appearance.
666    ///
667    /// On macOS, this corresponds to the `darkAqua` appearance.
668    Dark,
669
670    /// A dark appearance with vibrant colors.
671    ///
672    /// On macOS, this corresponds to the `NSAppearanceNameVibrantDark` appearance.
673    VibrantDark,
674}
675
676impl Default for WindowAppearance {
677    fn default() -> Self {
678        Self::Light
679    }
680}
681
682/// The appearance of the background of the window itself, when there is
683/// no content or the content is transparent.
684#[derive(Copy, Clone, Debug, Default, PartialEq)]
685pub enum WindowBackgroundAppearance {
686    /// Opaque.
687    ///
688    /// This lets the window manager know that content behind this
689    /// window does not need to be drawn.
690    ///
691    /// Actual color depends on the system and themes should define a fully
692    /// opaque background color instead.
693    #[default]
694    Opaque,
695    /// Plain alpha transparency.
696    Transparent,
697    /// Transparency, but the contents behind the window are blurred.
698    ///
699    /// Not always supported.
700    Blurred,
701}
702
703/// The options that can be configured for a file dialog prompt
704#[derive(Copy, Clone, Debug)]
705pub struct PathPromptOptions {
706    /// Should the prompt allow files to be selected?
707    pub files: bool,
708    /// Should the prompt allow directories to be selected?
709    pub directories: bool,
710    /// Should the prompt allow multiple files to be selected?
711    pub multiple: bool,
712}
713
714/// What kind of prompt styling to show
715#[derive(Copy, Clone, Debug, PartialEq)]
716pub enum PromptLevel {
717    /// A prompt that is shown when the user should be notified of something
718    Info,
719
720    /// A prompt that is shown when the user needs to be warned of a potential problem
721    Warning,
722
723    /// A prompt that is shown when a critical problem has occurred
724    Critical,
725}
726
727/// The style of the cursor (pointer)
728#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
729pub enum CursorStyle {
730    /// The default cursor
731    Arrow,
732
733    /// A text input cursor
734    /// corresponds to the CSS cursor value `text`
735    IBeam,
736
737    /// A crosshair cursor
738    /// corresponds to the CSS cursor value `crosshair`
739    Crosshair,
740
741    /// A closed hand cursor
742    /// corresponds to the CSS cursor value `grabbing`
743    ClosedHand,
744
745    /// An open hand cursor
746    /// corresponds to the CSS cursor value `grab`
747    OpenHand,
748
749    /// A pointing hand cursor
750    /// corresponds to the CSS cursor value `pointer`
751    PointingHand,
752
753    /// A resize left cursor
754    /// corresponds to the CSS cursor value `w-resize`
755    ResizeLeft,
756
757    /// A resize right cursor
758    /// corresponds to the CSS cursor value `e-resize`
759    ResizeRight,
760
761    /// A resize cursor to the left and right
762    /// corresponds to the CSS cursor value `ew-resize`
763    ResizeLeftRight,
764
765    /// A resize up cursor
766    /// corresponds to the CSS cursor value `n-resize`
767    ResizeUp,
768
769    /// A resize down cursor
770    /// corresponds to the CSS cursor value `s-resize`
771    ResizeDown,
772
773    /// A resize cursor directing up and down
774    /// corresponds to the CSS cursor value `ns-resize`
775    ResizeUpDown,
776
777    /// A cursor indicating that the item/column can be resized horizontally.
778    /// corresponds to the CSS curosr value `col-resize`
779    ResizeColumn,
780
781    /// A cursor indicating that the item/row can be resized vertically.
782    /// corresponds to the CSS curosr value `row-resize`
783    ResizeRow,
784
785    /// A cursor indicating that something will disappear if moved here
786    /// Does not correspond to a CSS cursor value
787    DisappearingItem,
788
789    /// A text input cursor for vertical layout
790    /// corresponds to the CSS cursor value `vertical-text`
791    IBeamCursorForVerticalLayout,
792
793    /// A cursor indicating that the operation is not allowed
794    /// corresponds to the CSS cursor value `not-allowed`
795    OperationNotAllowed,
796
797    /// A cursor indicating that the operation will result in a link
798    /// corresponds to the CSS cursor value `alias`
799    DragLink,
800
801    /// A cursor indicating that the operation will result in a copy
802    /// corresponds to the CSS cursor value `copy`
803    DragCopy,
804
805    /// A cursor indicating that the operation will result in a context menu
806    /// corresponds to the CSS cursor value `context-menu`
807    ContextualMenu,
808}
809
810impl Default for CursorStyle {
811    fn default() -> Self {
812        Self::Arrow
813    }
814}
815
816/// A clipboard item that should be copied to the clipboard
817#[derive(Clone, Debug, Eq, PartialEq)]
818pub struct ClipboardItem {
819    pub(crate) text: String,
820    pub(crate) metadata: Option<String>,
821}
822
823impl ClipboardItem {
824    /// Create a new clipboard item with the given text
825    pub fn new(text: String) -> Self {
826        Self {
827            text,
828            metadata: None,
829        }
830    }
831
832    /// Create a new clipboard item with the given text and metadata
833    pub fn with_metadata<T: Serialize>(mut self, metadata: T) -> Self {
834        self.metadata = Some(serde_json::to_string(&metadata).unwrap());
835        self
836    }
837
838    /// Get the text of the clipboard item
839    pub fn text(&self) -> &String {
840        &self.text
841    }
842
843    /// Get the metadata of the clipboard item
844    pub fn metadata<T>(&self) -> Option<T>
845    where
846        T: for<'a> Deserialize<'a>,
847    {
848        self.metadata
849            .as_ref()
850            .and_then(|m| serde_json::from_str(m).ok())
851    }
852
853    pub(crate) fn text_hash(text: &str) -> u64 {
854        let mut hasher = SeaHasher::new();
855        text.hash(&mut hasher);
856        hasher.finish()
857    }
858}