platform.rs

   1mod app_menu;
   2mod keyboard;
   3mod keystroke;
   4
   5#[cfg(any(target_os = "linux", target_os = "freebsd"))]
   6mod linux;
   7
   8#[cfg(target_os = "macos")]
   9mod mac;
  10
  11#[cfg(any(
  12    all(
  13        any(target_os = "linux", target_os = "freebsd"),
  14        any(feature = "x11", feature = "wayland")
  15    ),
  16    all(target_os = "macos", feature = "macos-blade")
  17))]
  18mod blade;
  19
  20#[cfg(any(test, feature = "test-support"))]
  21mod test;
  22
  23#[cfg(target_os = "windows")]
  24mod windows;
  25
  26#[cfg(all(
  27    feature = "screen-capture",
  28    any(
  29        target_os = "windows",
  30        all(
  31            any(target_os = "linux", target_os = "freebsd"),
  32            any(feature = "wayland", feature = "x11"),
  33        )
  34    )
  35))]
  36pub(crate) mod scap_screen_capture;
  37
  38use crate::{
  39    Action, AnyWindowHandle, App, AsyncWindowContext, BackgroundExecutor, Bounds,
  40    DEFAULT_WINDOW_SIZE, DevicePixels, DispatchEventResult, Font, FontId, FontMetrics, FontRun,
  41    ForegroundExecutor, GlyphId, GpuSpecs, ImageSource, Keymap, LineLayout, Pixels, PlatformInput,
  42    Point, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, ScaledPixels, Scene,
  43    ShapedGlyph, ShapedRun, SharedString, Size, SvgRenderer, SvgSize, Task, TaskLabel, Window,
  44    WindowControlArea, hash, point, px, size,
  45};
  46use anyhow::Result;
  47use async_task::Runnable;
  48use futures::channel::oneshot;
  49use image::codecs::gif::GifDecoder;
  50use image::{AnimationDecoder as _, Frame};
  51use parking::Unparker;
  52use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
  53use schemars::JsonSchema;
  54use seahash::SeaHasher;
  55use serde::{Deserialize, Serialize};
  56use smallvec::SmallVec;
  57use std::borrow::Cow;
  58use std::hash::{Hash, Hasher};
  59use std::io::Cursor;
  60use std::ops;
  61use std::time::{Duration, Instant};
  62use std::{
  63    fmt::{self, Debug},
  64    ops::Range,
  65    path::{Path, PathBuf},
  66    rc::Rc,
  67    sync::Arc,
  68};
  69use strum::EnumIter;
  70use uuid::Uuid;
  71
  72pub use app_menu::*;
  73pub use keyboard::*;
  74pub use keystroke::*;
  75
  76#[cfg(any(target_os = "linux", target_os = "freebsd"))]
  77pub(crate) use linux::*;
  78#[cfg(target_os = "macos")]
  79pub(crate) use mac::*;
  80pub use semantic_version::SemanticVersion;
  81#[cfg(any(test, feature = "test-support"))]
  82pub(crate) use test::*;
  83#[cfg(target_os = "windows")]
  84pub(crate) use windows::*;
  85
  86#[cfg(any(test, feature = "test-support"))]
  87pub use test::{TestDispatcher, TestScreenCaptureSource, TestScreenCaptureStream};
  88
  89/// Returns a background executor for the current platform.
  90pub fn background_executor() -> BackgroundExecutor {
  91    current_platform(true).background_executor()
  92}
  93
  94#[cfg(target_os = "macos")]
  95pub(crate) fn current_platform(headless: bool) -> Rc<dyn Platform> {
  96    Rc::new(MacPlatform::new(headless))
  97}
  98
  99#[cfg(any(target_os = "linux", target_os = "freebsd"))]
 100pub(crate) fn current_platform(headless: bool) -> Rc<dyn Platform> {
 101    #[cfg(feature = "x11")]
 102    use anyhow::Context as _;
 103
 104    if headless {
 105        return Rc::new(HeadlessClient::new());
 106    }
 107
 108    match guess_compositor() {
 109        #[cfg(feature = "wayland")]
 110        "Wayland" => Rc::new(WaylandClient::new()),
 111
 112        #[cfg(feature = "x11")]
 113        "X11" => Rc::new(
 114            X11Client::new()
 115                .context("Failed to initialize X11 client.")
 116                .unwrap(),
 117        ),
 118
 119        "Headless" => Rc::new(HeadlessClient::new()),
 120        _ => unreachable!(),
 121    }
 122}
 123
 124/// Return which compositor we're guessing we'll use.
 125/// Does not attempt to connect to the given compositor
 126#[cfg(any(target_os = "linux", target_os = "freebsd"))]
 127#[inline]
 128pub fn guess_compositor() -> &'static str {
 129    if std::env::var_os("ZED_HEADLESS").is_some() {
 130        return "Headless";
 131    }
 132
 133    #[cfg(feature = "wayland")]
 134    let wayland_display = std::env::var_os("WAYLAND_DISPLAY");
 135    #[cfg(not(feature = "wayland"))]
 136    let wayland_display: Option<std::ffi::OsString> = None;
 137
 138    #[cfg(feature = "x11")]
 139    let x11_display = std::env::var_os("DISPLAY");
 140    #[cfg(not(feature = "x11"))]
 141    let x11_display: Option<std::ffi::OsString> = None;
 142
 143    let use_wayland = wayland_display.is_some_and(|display| !display.is_empty());
 144    let use_x11 = x11_display.is_some_and(|display| !display.is_empty());
 145
 146    if use_wayland {
 147        "Wayland"
 148    } else if use_x11 {
 149        "X11"
 150    } else {
 151        "Headless"
 152    }
 153}
 154
 155#[cfg(target_os = "windows")]
 156pub(crate) fn current_platform(_headless: bool) -> Rc<dyn Platform> {
 157    Rc::new(
 158        WindowsPlatform::new()
 159            .inspect_err(|err| show_error("Failed to launch", err.to_string()))
 160            .unwrap(),
 161    )
 162}
 163
 164pub(crate) trait Platform: 'static {
 165    fn background_executor(&self) -> BackgroundExecutor;
 166    fn foreground_executor(&self) -> ForegroundExecutor;
 167    fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
 168
 169    fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>);
 170    fn quit(&self);
 171    fn restart(&self, binary_path: Option<PathBuf>);
 172    fn activate(&self, ignoring_other_apps: bool);
 173    fn hide(&self);
 174    fn hide_other_apps(&self);
 175    fn unhide_other_apps(&self);
 176
 177    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>>;
 178    fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>>;
 179    fn active_window(&self) -> Option<AnyWindowHandle>;
 180    fn window_stack(&self) -> Option<Vec<AnyWindowHandle>> {
 181        None
 182    }
 183
 184    #[cfg(feature = "screen-capture")]
 185    fn is_screen_capture_supported(&self) -> bool;
 186    #[cfg(not(feature = "screen-capture"))]
 187    fn is_screen_capture_supported(&self) -> bool {
 188        false
 189    }
 190    #[cfg(feature = "screen-capture")]
 191    fn screen_capture_sources(&self)
 192    -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>>;
 193    #[cfg(not(feature = "screen-capture"))]
 194    fn screen_capture_sources(
 195        &self,
 196    ) -> oneshot::Receiver<anyhow::Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
 197        let (sources_tx, sources_rx) = oneshot::channel();
 198        sources_tx
 199            .send(Err(anyhow::anyhow!(
 200                "gpui was compiled without the screen-capture feature"
 201            )))
 202            .ok();
 203        sources_rx
 204    }
 205
 206    fn open_window(
 207        &self,
 208        handle: AnyWindowHandle,
 209        options: WindowParams,
 210    ) -> anyhow::Result<Box<dyn PlatformWindow>>;
 211
 212    /// Returns the appearance of the application's windows.
 213    fn window_appearance(&self) -> WindowAppearance;
 214
 215    fn open_url(&self, url: &str);
 216    fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>);
 217    fn register_url_scheme(&self, url: &str) -> Task<Result<()>>;
 218
 219    fn prompt_for_paths(
 220        &self,
 221        options: PathPromptOptions,
 222    ) -> oneshot::Receiver<Result<Option<Vec<PathBuf>>>>;
 223    fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver<Result<Option<PathBuf>>>;
 224    fn can_select_mixed_files_and_dirs(&self) -> bool;
 225    fn reveal_path(&self, path: &Path);
 226    fn open_with_system(&self, path: &Path);
 227
 228    fn on_quit(&self, callback: Box<dyn FnMut()>);
 229    fn on_reopen(&self, callback: Box<dyn FnMut()>);
 230    fn on_keyboard_layout_change(&self, callback: Box<dyn FnMut()>);
 231
 232    fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap);
 233    fn get_menus(&self) -> Option<Vec<OwnedMenu>> {
 234        None
 235    }
 236
 237    fn set_dock_menu(&self, menu: Vec<MenuItem>, keymap: &Keymap);
 238    fn perform_dock_menu_action(&self, _action: usize) {}
 239    fn add_recent_document(&self, _path: &Path) {}
 240    fn update_jump_list(
 241        &self,
 242        _menus: Vec<MenuItem>,
 243        _entries: Vec<SmallVec<[PathBuf; 2]>>,
 244    ) -> Vec<SmallVec<[PathBuf; 2]>> {
 245        Vec::new()
 246    }
 247    fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>);
 248    fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>);
 249    fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>);
 250    fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout>;
 251
 252    fn compositor_name(&self) -> &'static str {
 253        ""
 254    }
 255    fn app_path(&self) -> Result<PathBuf>;
 256    fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf>;
 257
 258    fn set_cursor_style(&self, style: CursorStyle);
 259    fn should_auto_hide_scrollbars(&self) -> bool;
 260
 261    #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 262    fn write_to_primary(&self, item: ClipboardItem);
 263    fn write_to_clipboard(&self, item: ClipboardItem);
 264    #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 265    fn read_from_primary(&self) -> Option<ClipboardItem>;
 266    fn read_from_clipboard(&self) -> Option<ClipboardItem>;
 267
 268    fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task<Result<()>>;
 269    fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>>;
 270    fn delete_credentials(&self, url: &str) -> Task<Result<()>>;
 271}
 272
 273/// A handle to a platform's display, e.g. a monitor or laptop screen.
 274pub trait PlatformDisplay: Send + Sync + Debug {
 275    /// Get the ID for this display
 276    fn id(&self) -> DisplayId;
 277
 278    /// Returns a stable identifier for this display that can be persisted and used
 279    /// across system restarts.
 280    fn uuid(&self) -> Result<Uuid>;
 281
 282    /// Get the bounds for this display
 283    fn bounds(&self) -> Bounds<Pixels>;
 284
 285    /// Get the default bounds for this display to place a window
 286    fn default_bounds(&self) -> Bounds<Pixels> {
 287        let center = self.bounds().center();
 288        let offset = DEFAULT_WINDOW_SIZE / 2.0;
 289        let origin = point(center.x - offset.width, center.y - offset.height);
 290        Bounds::new(origin, DEFAULT_WINDOW_SIZE)
 291    }
 292}
 293
 294/// Metadata for a given [ScreenCaptureSource]
 295#[derive(Clone)]
 296pub struct SourceMetadata {
 297    /// Opaque identifier of this screen.
 298    pub id: u64,
 299    /// Human-readable label for this source.
 300    pub label: Option<SharedString>,
 301    /// Whether this source is the main display.
 302    pub is_main: Option<bool>,
 303    /// Video resolution of this source.
 304    pub resolution: Size<DevicePixels>,
 305}
 306
 307/// A source of on-screen video content that can be captured.
 308pub trait ScreenCaptureSource {
 309    /// Returns metadata for this source.
 310    fn metadata(&self) -> Result<SourceMetadata>;
 311
 312    /// Start capture video from this source, invoking the given callback
 313    /// with each frame.
 314    fn stream(
 315        &self,
 316        foreground_executor: &ForegroundExecutor,
 317        frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
 318    ) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>>;
 319}
 320
 321/// A video stream captured from a screen.
 322pub trait ScreenCaptureStream {
 323    /// Returns metadata for this source.
 324    fn metadata(&self) -> Result<SourceMetadata>;
 325}
 326
 327/// A frame of video captured from a screen.
 328pub struct ScreenCaptureFrame(pub PlatformScreenCaptureFrame);
 329
 330/// An opaque identifier for a hardware display
 331#[derive(PartialEq, Eq, Hash, Copy, Clone)]
 332pub struct DisplayId(pub(crate) u32);
 333
 334impl From<DisplayId> for u32 {
 335    fn from(id: DisplayId) -> Self {
 336        id.0
 337    }
 338}
 339
 340impl Debug for DisplayId {
 341    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 342        write!(f, "DisplayId({})", self.0)
 343    }
 344}
 345
 346unsafe impl Send for DisplayId {}
 347
 348/// Which part of the window to resize
 349#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 350pub enum ResizeEdge {
 351    /// The top edge
 352    Top,
 353    /// The top right corner
 354    TopRight,
 355    /// The right edge
 356    Right,
 357    /// The bottom right corner
 358    BottomRight,
 359    /// The bottom edge
 360    Bottom,
 361    /// The bottom left corner
 362    BottomLeft,
 363    /// The left edge
 364    Left,
 365    /// The top left corner
 366    TopLeft,
 367}
 368
 369/// A type to describe the appearance of a window
 370#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
 371pub enum WindowDecorations {
 372    #[default]
 373    /// Server side decorations
 374    Server,
 375    /// Client side decorations
 376    Client,
 377}
 378
 379/// A type to describe how this window is currently configured
 380#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
 381pub enum Decorations {
 382    /// The window is configured to use server side decorations
 383    #[default]
 384    Server,
 385    /// The window is configured to use client side decorations
 386    Client {
 387        /// The edge tiling state
 388        tiling: Tiling,
 389    },
 390}
 391
 392/// What window controls this platform supports
 393#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 394pub struct WindowControls {
 395    /// Whether this platform supports fullscreen
 396    pub fullscreen: bool,
 397    /// Whether this platform supports maximize
 398    pub maximize: bool,
 399    /// Whether this platform supports minimize
 400    pub minimize: bool,
 401    /// Whether this platform supports a window menu
 402    pub window_menu: bool,
 403}
 404
 405impl Default for WindowControls {
 406    fn default() -> Self {
 407        // Assume that we can do anything, unless told otherwise
 408        Self {
 409            fullscreen: true,
 410            maximize: true,
 411            minimize: true,
 412            window_menu: true,
 413        }
 414    }
 415}
 416
 417/// A type to describe which sides of the window are currently tiled in some way
 418#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
 419pub struct Tiling {
 420    /// Whether the top edge is tiled
 421    pub top: bool,
 422    /// Whether the left edge is tiled
 423    pub left: bool,
 424    /// Whether the right edge is tiled
 425    pub right: bool,
 426    /// Whether the bottom edge is tiled
 427    pub bottom: bool,
 428}
 429
 430impl Tiling {
 431    /// Initializes a [`Tiling`] type with all sides tiled
 432    pub fn tiled() -> Self {
 433        Self {
 434            top: true,
 435            left: true,
 436            right: true,
 437            bottom: true,
 438        }
 439    }
 440
 441    /// Whether any edge is tiled
 442    pub fn is_tiled(&self) -> bool {
 443        self.top || self.left || self.right || self.bottom
 444    }
 445}
 446
 447#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
 448pub(crate) struct RequestFrameOptions {
 449    pub(crate) require_presentation: bool,
 450}
 451
 452pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
 453    fn bounds(&self) -> Bounds<Pixels>;
 454    fn is_maximized(&self) -> bool;
 455    fn window_bounds(&self) -> WindowBounds;
 456    fn content_size(&self) -> Size<Pixels>;
 457    fn resize(&mut self, size: Size<Pixels>);
 458    fn scale_factor(&self) -> f32;
 459    fn appearance(&self) -> WindowAppearance;
 460    fn display(&self) -> Option<Rc<dyn PlatformDisplay>>;
 461    fn mouse_position(&self) -> Point<Pixels>;
 462    fn modifiers(&self) -> Modifiers;
 463    fn capslock(&self) -> Capslock;
 464    fn set_input_handler(&mut self, input_handler: PlatformInputHandler);
 465    fn take_input_handler(&mut self) -> Option<PlatformInputHandler>;
 466    fn prompt(
 467        &self,
 468        level: PromptLevel,
 469        msg: &str,
 470        detail: Option<&str>,
 471        answers: &[PromptButton],
 472    ) -> Option<oneshot::Receiver<usize>>;
 473    fn activate(&self);
 474    fn is_active(&self) -> bool;
 475    fn is_hovered(&self) -> bool;
 476    fn set_title(&mut self, title: &str);
 477    fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance);
 478    fn minimize(&self);
 479    fn zoom(&self);
 480    fn toggle_fullscreen(&self);
 481    fn is_fullscreen(&self) -> bool;
 482    fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions)>);
 483    fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> DispatchEventResult>);
 484    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>);
 485    fn on_hover_status_change(&self, callback: Box<dyn FnMut(bool)>);
 486    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>);
 487    fn on_moved(&self, callback: Box<dyn FnMut()>);
 488    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>);
 489    fn on_hit_test_window_control(&self, callback: Box<dyn FnMut() -> Option<WindowControlArea>>);
 490    fn on_close(&self, callback: Box<dyn FnOnce()>);
 491    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>);
 492    fn draw(&self, scene: &Scene);
 493    fn completed_frame(&self) {}
 494    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
 495
 496    // macOS specific methods
 497    fn set_edited(&mut self, _edited: bool) {}
 498    fn show_character_palette(&self) {}
 499    fn titlebar_double_click(&self) {}
 500
 501    #[cfg(target_os = "windows")]
 502    fn get_raw_handle(&self) -> windows::HWND;
 503
 504    // Linux specific methods
 505    fn inner_window_bounds(&self) -> WindowBounds {
 506        self.window_bounds()
 507    }
 508    fn request_decorations(&self, _decorations: WindowDecorations) {}
 509    fn show_window_menu(&self, _position: Point<Pixels>) {}
 510    fn start_window_move(&self) {}
 511    fn start_window_resize(&self, _edge: ResizeEdge) {}
 512    fn window_decorations(&self) -> Decorations {
 513        Decorations::Server
 514    }
 515    fn set_app_id(&mut self, _app_id: &str) {}
 516    fn map_window(&mut self) -> anyhow::Result<()> {
 517        Ok(())
 518    }
 519    fn window_controls(&self) -> WindowControls {
 520        WindowControls::default()
 521    }
 522    fn set_client_inset(&self, _inset: Pixels) {}
 523    fn gpu_specs(&self) -> Option<GpuSpecs>;
 524
 525    fn update_ime_position(&self, _bounds: Bounds<ScaledPixels>);
 526
 527    #[cfg(any(test, feature = "test-support"))]
 528    fn as_test(&mut self) -> Option<&mut TestWindow> {
 529        None
 530    }
 531}
 532
 533/// This type is public so that our test macro can generate and use it, but it should not
 534/// be considered part of our public API.
 535#[doc(hidden)]
 536pub trait PlatformDispatcher: Send + Sync {
 537    fn is_main_thread(&self) -> bool;
 538    fn dispatch(&self, runnable: Runnable, label: Option<TaskLabel>);
 539    fn dispatch_on_main_thread(&self, runnable: Runnable);
 540    fn dispatch_after(&self, duration: Duration, runnable: Runnable);
 541    fn park(&self, timeout: Option<Duration>) -> bool;
 542    fn unparker(&self) -> Unparker;
 543    fn now(&self) -> Instant {
 544        Instant::now()
 545    }
 546
 547    #[cfg(any(test, feature = "test-support"))]
 548    fn as_test(&self) -> Option<&TestDispatcher> {
 549        None
 550    }
 551}
 552
 553pub(crate) trait PlatformTextSystem: Send + Sync {
 554    fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()>;
 555    fn all_font_names(&self) -> Vec<String>;
 556    fn font_id(&self, descriptor: &Font) -> Result<FontId>;
 557    fn font_metrics(&self, font_id: FontId) -> FontMetrics;
 558    fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>>;
 559    fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>>;
 560    fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId>;
 561    fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>>;
 562    fn rasterize_glyph(
 563        &self,
 564        params: &RenderGlyphParams,
 565        raster_bounds: Bounds<DevicePixels>,
 566    ) -> Result<(Size<DevicePixels>, Vec<u8>)>;
 567    fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout;
 568}
 569
 570pub(crate) struct NoopTextSystem;
 571
 572impl NoopTextSystem {
 573    #[allow(dead_code)]
 574    pub fn new() -> Self {
 575        Self
 576    }
 577}
 578
 579impl PlatformTextSystem for NoopTextSystem {
 580    fn add_fonts(&self, _fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {
 581        Ok(())
 582    }
 583
 584    fn all_font_names(&self) -> Vec<String> {
 585        Vec::new()
 586    }
 587
 588    fn font_id(&self, _descriptor: &Font) -> Result<FontId> {
 589        return Ok(FontId(1));
 590    }
 591
 592    fn font_metrics(&self, _font_id: FontId) -> FontMetrics {
 593        FontMetrics {
 594            units_per_em: 1000,
 595            ascent: 1025.0,
 596            descent: -275.0,
 597            line_gap: 0.0,
 598            underline_position: -95.0,
 599            underline_thickness: 60.0,
 600            cap_height: 698.0,
 601            x_height: 516.0,
 602            bounding_box: Bounds {
 603                origin: Point {
 604                    x: -260.0,
 605                    y: -245.0,
 606                },
 607                size: Size {
 608                    width: 1501.0,
 609                    height: 1364.0,
 610                },
 611            },
 612        }
 613    }
 614
 615    fn typographic_bounds(&self, _font_id: FontId, _glyph_id: GlyphId) -> Result<Bounds<f32>> {
 616        Ok(Bounds {
 617            origin: Point { x: 54.0, y: 0.0 },
 618            size: size(392.0, 528.0),
 619        })
 620    }
 621
 622    fn advance(&self, _font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>> {
 623        Ok(size(600.0 * glyph_id.0 as f32, 0.0))
 624    }
 625
 626    fn glyph_for_char(&self, _font_id: FontId, ch: char) -> Option<GlyphId> {
 627        Some(GlyphId(ch.len_utf16() as u32))
 628    }
 629
 630    fn glyph_raster_bounds(&self, _params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
 631        Ok(Default::default())
 632    }
 633
 634    fn rasterize_glyph(
 635        &self,
 636        _params: &RenderGlyphParams,
 637        raster_bounds: Bounds<DevicePixels>,
 638    ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
 639        Ok((raster_bounds.size, Vec::new()))
 640    }
 641
 642    fn layout_line(&self, text: &str, font_size: Pixels, _runs: &[FontRun]) -> LineLayout {
 643        let mut position = px(0.);
 644        let metrics = self.font_metrics(FontId(0));
 645        let em_width = font_size
 646            * self
 647                .advance(FontId(0), self.glyph_for_char(FontId(0), 'm').unwrap())
 648                .unwrap()
 649                .width
 650            / metrics.units_per_em as f32;
 651        let mut glyphs = Vec::new();
 652        for (ix, c) in text.char_indices() {
 653            if let Some(glyph) = self.glyph_for_char(FontId(0), c) {
 654                glyphs.push(ShapedGlyph {
 655                    id: glyph,
 656                    position: point(position, px(0.)),
 657                    index: ix,
 658                    is_emoji: glyph.0 == 2,
 659                });
 660                if glyph.0 == 2 {
 661                    position += em_width * 2.0;
 662                } else {
 663                    position += em_width;
 664                }
 665            } else {
 666                position += em_width
 667            }
 668        }
 669        let mut runs = Vec::default();
 670        if glyphs.len() > 0 {
 671            runs.push(ShapedRun {
 672                font_id: FontId(0),
 673                glyphs,
 674            });
 675        } else {
 676            position = px(0.);
 677        }
 678
 679        LineLayout {
 680            font_size,
 681            width: position,
 682            ascent: font_size * (metrics.ascent / metrics.units_per_em as f32),
 683            descent: font_size * (metrics.descent / metrics.units_per_em as f32),
 684            runs,
 685            len: text.len(),
 686        }
 687    }
 688}
 689
 690#[derive(PartialEq, Eq, Hash, Clone)]
 691pub(crate) enum AtlasKey {
 692    Glyph(RenderGlyphParams),
 693    Svg(RenderSvgParams),
 694    Image(RenderImageParams),
 695}
 696
 697impl AtlasKey {
 698    #[cfg_attr(
 699        all(
 700            any(target_os = "linux", target_os = "freebsd"),
 701            not(any(feature = "x11", feature = "wayland"))
 702        ),
 703        allow(dead_code)
 704    )]
 705    pub(crate) fn texture_kind(&self) -> AtlasTextureKind {
 706        match self {
 707            AtlasKey::Glyph(params) => {
 708                if params.is_emoji {
 709                    AtlasTextureKind::Polychrome
 710                } else {
 711                    AtlasTextureKind::Monochrome
 712                }
 713            }
 714            AtlasKey::Svg(_) => AtlasTextureKind::Monochrome,
 715            AtlasKey::Image(_) => AtlasTextureKind::Polychrome,
 716        }
 717    }
 718}
 719
 720impl From<RenderGlyphParams> for AtlasKey {
 721    fn from(params: RenderGlyphParams) -> Self {
 722        Self::Glyph(params)
 723    }
 724}
 725
 726impl From<RenderSvgParams> for AtlasKey {
 727    fn from(params: RenderSvgParams) -> Self {
 728        Self::Svg(params)
 729    }
 730}
 731
 732impl From<RenderImageParams> for AtlasKey {
 733    fn from(params: RenderImageParams) -> Self {
 734        Self::Image(params)
 735    }
 736}
 737
 738pub(crate) trait PlatformAtlas: Send + Sync {
 739    fn get_or_insert_with<'a>(
 740        &self,
 741        key: &AtlasKey,
 742        build: &mut dyn FnMut() -> Result<Option<(Size<DevicePixels>, Cow<'a, [u8]>)>>,
 743    ) -> Result<Option<AtlasTile>>;
 744    fn remove(&self, key: &AtlasKey);
 745}
 746
 747struct AtlasTextureList<T> {
 748    textures: Vec<Option<T>>,
 749    free_list: Vec<usize>,
 750}
 751
 752impl<T> Default for AtlasTextureList<T> {
 753    fn default() -> Self {
 754        Self {
 755            textures: Vec::default(),
 756            free_list: Vec::default(),
 757        }
 758    }
 759}
 760
 761impl<T> ops::Index<usize> for AtlasTextureList<T> {
 762    type Output = Option<T>;
 763
 764    fn index(&self, index: usize) -> &Self::Output {
 765        &self.textures[index]
 766    }
 767}
 768
 769impl<T> AtlasTextureList<T> {
 770    #[allow(unused)]
 771    fn drain(&mut self) -> std::vec::Drain<'_, Option<T>> {
 772        self.free_list.clear();
 773        self.textures.drain(..)
 774    }
 775
 776    #[allow(dead_code)]
 777    fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut T> {
 778        self.textures.iter_mut().flatten()
 779    }
 780}
 781
 782#[derive(Clone, Debug, PartialEq, Eq)]
 783#[repr(C)]
 784pub(crate) struct AtlasTile {
 785    pub(crate) texture_id: AtlasTextureId,
 786    pub(crate) tile_id: TileId,
 787    pub(crate) padding: u32,
 788    pub(crate) bounds: Bounds<DevicePixels>,
 789}
 790
 791#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 792#[repr(C)]
 793pub(crate) struct AtlasTextureId {
 794    // We use u32 instead of usize for Metal Shader Language compatibility
 795    pub(crate) index: u32,
 796    pub(crate) kind: AtlasTextureKind,
 797}
 798
 799#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 800#[repr(C)]
 801#[cfg_attr(
 802    all(
 803        any(target_os = "linux", target_os = "freebsd"),
 804        not(any(feature = "x11", feature = "wayland"))
 805    ),
 806    allow(dead_code)
 807)]
 808pub(crate) enum AtlasTextureKind {
 809    Monochrome = 0,
 810    Polychrome = 1,
 811}
 812
 813#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
 814#[repr(C)]
 815pub(crate) struct TileId(pub(crate) u32);
 816
 817impl From<etagere::AllocId> for TileId {
 818    fn from(id: etagere::AllocId) -> Self {
 819        Self(id.serialize())
 820    }
 821}
 822
 823impl From<TileId> for etagere::AllocId {
 824    fn from(id: TileId) -> Self {
 825        Self::deserialize(id.0)
 826    }
 827}
 828
 829pub(crate) struct PlatformInputHandler {
 830    cx: AsyncWindowContext,
 831    handler: Box<dyn InputHandler>,
 832}
 833
 834#[cfg_attr(
 835    all(
 836        any(target_os = "linux", target_os = "freebsd"),
 837        not(any(feature = "x11", feature = "wayland"))
 838    ),
 839    allow(dead_code)
 840)]
 841impl PlatformInputHandler {
 842    pub fn new(cx: AsyncWindowContext, handler: Box<dyn InputHandler>) -> Self {
 843        Self { cx, handler }
 844    }
 845
 846    fn selected_text_range(&mut self, ignore_disabled_input: bool) -> Option<UTF16Selection> {
 847        self.cx
 848            .update(|window, cx| {
 849                self.handler
 850                    .selected_text_range(ignore_disabled_input, window, cx)
 851            })
 852            .ok()
 853            .flatten()
 854    }
 855
 856    #[cfg_attr(target_os = "windows", allow(dead_code))]
 857    fn marked_text_range(&mut self) -> Option<Range<usize>> {
 858        self.cx
 859            .update(|window, cx| self.handler.marked_text_range(window, cx))
 860            .ok()
 861            .flatten()
 862    }
 863
 864    #[cfg_attr(
 865        any(target_os = "linux", target_os = "freebsd", target_os = "windows"),
 866        allow(dead_code)
 867    )]
 868    fn text_for_range(
 869        &mut self,
 870        range_utf16: Range<usize>,
 871        adjusted: &mut Option<Range<usize>>,
 872    ) -> Option<String> {
 873        self.cx
 874            .update(|window, cx| {
 875                self.handler
 876                    .text_for_range(range_utf16, adjusted, window, cx)
 877            })
 878            .ok()
 879            .flatten()
 880    }
 881
 882    fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str) {
 883        self.cx
 884            .update(|window, cx| {
 885                self.handler
 886                    .replace_text_in_range(replacement_range, text, window, cx);
 887            })
 888            .ok();
 889    }
 890
 891    pub fn replace_and_mark_text_in_range(
 892        &mut self,
 893        range_utf16: Option<Range<usize>>,
 894        new_text: &str,
 895        new_selected_range: Option<Range<usize>>,
 896    ) {
 897        self.cx
 898            .update(|window, cx| {
 899                self.handler.replace_and_mark_text_in_range(
 900                    range_utf16,
 901                    new_text,
 902                    new_selected_range,
 903                    window,
 904                    cx,
 905                )
 906            })
 907            .ok();
 908    }
 909
 910    #[cfg_attr(target_os = "windows", allow(dead_code))]
 911    fn unmark_text(&mut self) {
 912        self.cx
 913            .update(|window, cx| self.handler.unmark_text(window, cx))
 914            .ok();
 915    }
 916
 917    fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>> {
 918        self.cx
 919            .update(|window, cx| self.handler.bounds_for_range(range_utf16, window, cx))
 920            .ok()
 921            .flatten()
 922    }
 923
 924    #[allow(dead_code)]
 925    fn apple_press_and_hold_enabled(&mut self) -> bool {
 926        self.handler.apple_press_and_hold_enabled()
 927    }
 928
 929    pub(crate) fn dispatch_input(&mut self, input: &str, window: &mut Window, cx: &mut App) {
 930        self.handler.replace_text_in_range(None, input, window, cx);
 931    }
 932
 933    pub fn selected_bounds(&mut self, window: &mut Window, cx: &mut App) -> Option<Bounds<Pixels>> {
 934        let selection = self.handler.selected_text_range(true, window, cx)?;
 935        self.handler.bounds_for_range(
 936            if selection.reversed {
 937                selection.range.start..selection.range.start
 938            } else {
 939                selection.range.end..selection.range.end
 940            },
 941            window,
 942            cx,
 943        )
 944    }
 945
 946    #[allow(unused)]
 947    pub fn character_index_for_point(&mut self, point: Point<Pixels>) -> Option<usize> {
 948        self.cx
 949            .update(|window, cx| self.handler.character_index_for_point(point, window, cx))
 950            .ok()
 951            .flatten()
 952    }
 953}
 954
 955/// A struct representing a selection in a text buffer, in UTF16 characters.
 956/// This is different from a range because the head may be before the tail.
 957#[derive(Debug)]
 958pub struct UTF16Selection {
 959    /// The range of text in the document this selection corresponds to
 960    /// in UTF16 characters.
 961    pub range: Range<usize>,
 962    /// Whether the head of this selection is at the start (true), or end (false)
 963    /// of the range
 964    pub reversed: bool,
 965}
 966
 967/// Zed's interface for handling text input from the platform's IME system
 968/// This is currently a 1:1 exposure of the NSTextInputClient API:
 969///
 970/// <https://developer.apple.com/documentation/appkit/nstextinputclient>
 971pub trait InputHandler: 'static {
 972    /// Get the range of the user's currently selected text, if any
 973    /// Corresponds to [selectedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438242-selectedrange)
 974    ///
 975    /// Return value is in terms of UTF-16 characters, from 0 to the length of the document
 976    fn selected_text_range(
 977        &mut self,
 978        ignore_disabled_input: bool,
 979        window: &mut Window,
 980        cx: &mut App,
 981    ) -> Option<UTF16Selection>;
 982
 983    /// Get the range of the currently marked text, if any
 984    /// Corresponds to [markedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438250-markedrange)
 985    ///
 986    /// Return value is in terms of UTF-16 characters, from 0 to the length of the document
 987    fn marked_text_range(&mut self, window: &mut Window, cx: &mut App) -> Option<Range<usize>>;
 988
 989    /// Get the text for the given document range in UTF-16 characters
 990    /// Corresponds to [attributedSubstring(forProposedRange: actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438238-attributedsubstring)
 991    ///
 992    /// range_utf16 is in terms of UTF-16 characters
 993    fn text_for_range(
 994        &mut self,
 995        range_utf16: Range<usize>,
 996        adjusted_range: &mut Option<Range<usize>>,
 997        window: &mut Window,
 998        cx: &mut App,
 999    ) -> Option<String>;
1000
1001    /// Replace the text in the given document range with the given text
1002    /// Corresponds to [insertText(_:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438258-inserttext)
1003    ///
1004    /// replacement_range is in terms of UTF-16 characters
1005    fn replace_text_in_range(
1006        &mut self,
1007        replacement_range: Option<Range<usize>>,
1008        text: &str,
1009        window: &mut Window,
1010        cx: &mut App,
1011    );
1012
1013    /// Replace the text in the given document range with the given text,
1014    /// and mark the given text as part of an IME 'composing' state
1015    /// Corresponds to [setMarkedText(_:selectedRange:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438246-setmarkedtext)
1016    ///
1017    /// range_utf16 is in terms of UTF-16 characters
1018    /// new_selected_range is in terms of UTF-16 characters
1019    fn replace_and_mark_text_in_range(
1020        &mut self,
1021        range_utf16: Option<Range<usize>>,
1022        new_text: &str,
1023        new_selected_range: Option<Range<usize>>,
1024        window: &mut Window,
1025        cx: &mut App,
1026    );
1027
1028    /// Remove the IME 'composing' state from the document
1029    /// Corresponds to [unmarkText()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438239-unmarktext)
1030    fn unmark_text(&mut self, window: &mut Window, cx: &mut App);
1031
1032    /// Get the bounds of the given document range in screen coordinates
1033    /// Corresponds to [firstRect(forCharacterRange:actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438240-firstrect)
1034    ///
1035    /// This is used for positioning the IME candidate window
1036    fn bounds_for_range(
1037        &mut self,
1038        range_utf16: Range<usize>,
1039        window: &mut Window,
1040        cx: &mut App,
1041    ) -> Option<Bounds<Pixels>>;
1042
1043    /// Get the character offset for the given point in terms of UTF16 characters
1044    ///
1045    /// Corresponds to [characterIndexForPoint:](https://developer.apple.com/documentation/appkit/nstextinputclient/characterindex(for:))
1046    fn character_index_for_point(
1047        &mut self,
1048        point: Point<Pixels>,
1049        window: &mut Window,
1050        cx: &mut App,
1051    ) -> Option<usize>;
1052
1053    /// Allows a given input context to opt into getting raw key repeats instead of
1054    /// sending these to the platform.
1055    /// TODO: Ideally we should be able to set ApplePressAndHoldEnabled in NSUserDefaults
1056    /// (which is how iTerm does it) but it doesn't seem to work for me.
1057    #[allow(dead_code)]
1058    fn apple_press_and_hold_enabled(&mut self) -> bool {
1059        true
1060    }
1061}
1062
1063/// The variables that can be configured when creating a new window
1064#[derive(Debug)]
1065pub struct WindowOptions {
1066    /// Specifies the state and bounds of the window in screen coordinates.
1067    /// - `None`: Inherit the bounds.
1068    /// - `Some(WindowBounds)`: Open a window with corresponding state and its restore size.
1069    pub window_bounds: Option<WindowBounds>,
1070
1071    /// The titlebar configuration of the window
1072    pub titlebar: Option<TitlebarOptions>,
1073
1074    /// Whether the window should be focused when created
1075    pub focus: bool,
1076
1077    /// Whether the window should be shown when created
1078    pub show: bool,
1079
1080    /// The kind of window to create
1081    pub kind: WindowKind,
1082
1083    /// Whether the window should be movable by the user
1084    pub is_movable: bool,
1085
1086    /// The display to create the window on, if this is None,
1087    /// the window will be created on the main display
1088    pub display_id: Option<DisplayId>,
1089
1090    /// The appearance of the window background.
1091    pub window_background: WindowBackgroundAppearance,
1092
1093    /// Application identifier of the window. Can by used by desktop environments to group applications together.
1094    pub app_id: Option<String>,
1095
1096    /// Window minimum size
1097    pub window_min_size: Option<Size<Pixels>>,
1098
1099    /// Whether to use client or server side decorations. Wayland only
1100    /// Note that this may be ignored.
1101    pub window_decorations: Option<WindowDecorations>,
1102}
1103
1104/// The variables that can be configured when creating a new window
1105#[derive(Debug)]
1106#[cfg_attr(
1107    all(
1108        any(target_os = "linux", target_os = "freebsd"),
1109        not(any(feature = "x11", feature = "wayland"))
1110    ),
1111    allow(dead_code)
1112)]
1113pub(crate) struct WindowParams {
1114    pub bounds: Bounds<Pixels>,
1115
1116    /// The titlebar configuration of the window
1117    #[cfg_attr(feature = "wayland", allow(dead_code))]
1118    pub titlebar: Option<TitlebarOptions>,
1119
1120    /// The kind of window to create
1121    #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
1122    pub kind: WindowKind,
1123
1124    /// Whether the window should be movable by the user
1125    #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
1126    pub is_movable: bool,
1127
1128    #[cfg_attr(
1129        any(target_os = "linux", target_os = "freebsd", target_os = "windows"),
1130        allow(dead_code)
1131    )]
1132    pub focus: bool,
1133
1134    #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
1135    pub show: bool,
1136
1137    #[cfg_attr(feature = "wayland", allow(dead_code))]
1138    pub display_id: Option<DisplayId>,
1139
1140    pub window_min_size: Option<Size<Pixels>>,
1141}
1142
1143/// Represents the status of how a window should be opened.
1144#[derive(Debug, Copy, Clone, PartialEq)]
1145pub enum WindowBounds {
1146    /// Indicates that the window should open in a windowed state with the given bounds.
1147    Windowed(Bounds<Pixels>),
1148    /// Indicates that the window should open in a maximized state.
1149    /// The bounds provided here represent the restore size of the window.
1150    Maximized(Bounds<Pixels>),
1151    /// Indicates that the window should open in fullscreen mode.
1152    /// The bounds provided here represent the restore size of the window.
1153    Fullscreen(Bounds<Pixels>),
1154}
1155
1156impl Default for WindowBounds {
1157    fn default() -> Self {
1158        WindowBounds::Windowed(Bounds::default())
1159    }
1160}
1161
1162impl WindowBounds {
1163    /// Retrieve the inner bounds
1164    pub fn get_bounds(&self) -> Bounds<Pixels> {
1165        match self {
1166            WindowBounds::Windowed(bounds) => *bounds,
1167            WindowBounds::Maximized(bounds) => *bounds,
1168            WindowBounds::Fullscreen(bounds) => *bounds,
1169        }
1170    }
1171}
1172
1173impl Default for WindowOptions {
1174    fn default() -> Self {
1175        Self {
1176            window_bounds: None,
1177            titlebar: Some(TitlebarOptions {
1178                title: Default::default(),
1179                appears_transparent: Default::default(),
1180                traffic_light_position: Default::default(),
1181            }),
1182            focus: true,
1183            show: true,
1184            kind: WindowKind::Normal,
1185            is_movable: true,
1186            display_id: None,
1187            window_background: WindowBackgroundAppearance::default(),
1188            app_id: None,
1189            window_min_size: None,
1190            window_decorations: None,
1191        }
1192    }
1193}
1194
1195/// The options that can be configured for a window's titlebar
1196#[derive(Debug, Default)]
1197pub struct TitlebarOptions {
1198    /// The initial title of the window
1199    pub title: Option<SharedString>,
1200
1201    /// Should the default system titlebar be hidden to allow for a custom-drawn titlebar? (macOS and Windows only)
1202    /// Refer to [`WindowOptions::window_decorations`] on Linux
1203    pub appears_transparent: bool,
1204
1205    /// The position of the macOS traffic light buttons
1206    pub traffic_light_position: Option<Point<Pixels>>,
1207}
1208
1209/// The kind of window to create
1210#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1211pub enum WindowKind {
1212    /// A normal application window
1213    Normal,
1214
1215    /// A window that appears above all other windows, usually used for alerts or popups
1216    /// use sparingly!
1217    PopUp,
1218}
1219
1220/// The appearance of the window, as defined by the operating system.
1221///
1222/// On macOS, this corresponds to named [`NSAppearance`](https://developer.apple.com/documentation/appkit/nsappearance)
1223/// values.
1224#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1225pub enum WindowAppearance {
1226    /// A light appearance.
1227    ///
1228    /// On macOS, this corresponds to the `aqua` appearance.
1229    Light,
1230
1231    /// A light appearance with vibrant colors.
1232    ///
1233    /// On macOS, this corresponds to the `NSAppearanceNameVibrantLight` appearance.
1234    VibrantLight,
1235
1236    /// A dark appearance.
1237    ///
1238    /// On macOS, this corresponds to the `darkAqua` appearance.
1239    Dark,
1240
1241    /// A dark appearance with vibrant colors.
1242    ///
1243    /// On macOS, this corresponds to the `NSAppearanceNameVibrantDark` appearance.
1244    VibrantDark,
1245}
1246
1247impl Default for WindowAppearance {
1248    fn default() -> Self {
1249        Self::Light
1250    }
1251}
1252
1253/// The appearance of the background of the window itself, when there is
1254/// no content or the content is transparent.
1255#[derive(Copy, Clone, Debug, Default, PartialEq)]
1256pub enum WindowBackgroundAppearance {
1257    /// Opaque.
1258    ///
1259    /// This lets the window manager know that content behind this
1260    /// window does not need to be drawn.
1261    ///
1262    /// Actual color depends on the system and themes should define a fully
1263    /// opaque background color instead.
1264    #[default]
1265    Opaque,
1266    /// Plain alpha transparency.
1267    Transparent,
1268    /// Transparency, but the contents behind the window are blurred.
1269    ///
1270    /// Not always supported.
1271    Blurred,
1272}
1273
1274/// The options that can be configured for a file dialog prompt
1275#[derive(Copy, Clone, Debug)]
1276pub struct PathPromptOptions {
1277    /// Should the prompt allow files to be selected?
1278    pub files: bool,
1279    /// Should the prompt allow directories to be selected?
1280    pub directories: bool,
1281    /// Should the prompt allow multiple files to be selected?
1282    pub multiple: bool,
1283}
1284
1285/// What kind of prompt styling to show
1286#[derive(Copy, Clone, Debug, PartialEq)]
1287pub enum PromptLevel {
1288    /// A prompt that is shown when the user should be notified of something
1289    Info,
1290
1291    /// A prompt that is shown when the user needs to be warned of a potential problem
1292    Warning,
1293
1294    /// A prompt that is shown when a critical problem has occurred
1295    Critical,
1296}
1297
1298/// Prompt Button
1299#[derive(Clone, Debug, PartialEq)]
1300pub enum PromptButton {
1301    /// Ok button
1302    Ok(SharedString),
1303    /// Cancel button
1304    Cancel(SharedString),
1305    /// Other button
1306    Other(SharedString),
1307}
1308
1309impl PromptButton {
1310    /// Create a button with label
1311    pub fn new(label: impl Into<SharedString>) -> Self {
1312        PromptButton::Other(label.into())
1313    }
1314
1315    /// Create an Ok button
1316    pub fn ok(label: impl Into<SharedString>) -> Self {
1317        PromptButton::Ok(label.into())
1318    }
1319
1320    /// Create a Cancel button
1321    pub fn cancel(label: impl Into<SharedString>) -> Self {
1322        PromptButton::Cancel(label.into())
1323    }
1324
1325    #[allow(dead_code)]
1326    pub(crate) fn is_cancel(&self) -> bool {
1327        matches!(self, PromptButton::Cancel(_))
1328    }
1329
1330    /// Returns the label of the button
1331    pub fn label(&self) -> &SharedString {
1332        match self {
1333            PromptButton::Ok(label) => label,
1334            PromptButton::Cancel(label) => label,
1335            PromptButton::Other(label) => label,
1336        }
1337    }
1338}
1339
1340impl From<&str> for PromptButton {
1341    fn from(value: &str) -> Self {
1342        match value.to_lowercase().as_str() {
1343            "ok" => PromptButton::Ok("Ok".into()),
1344            "cancel" => PromptButton::Cancel("Cancel".into()),
1345            _ => PromptButton::Other(SharedString::from(value.to_owned())),
1346        }
1347    }
1348}
1349
1350/// The style of the cursor (pointer)
1351#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
1352pub enum CursorStyle {
1353    /// The default cursor
1354    Arrow,
1355
1356    /// A text input cursor
1357    /// corresponds to the CSS cursor value `text`
1358    IBeam,
1359
1360    /// A crosshair cursor
1361    /// corresponds to the CSS cursor value `crosshair`
1362    Crosshair,
1363
1364    /// A closed hand cursor
1365    /// corresponds to the CSS cursor value `grabbing`
1366    ClosedHand,
1367
1368    /// An open hand cursor
1369    /// corresponds to the CSS cursor value `grab`
1370    OpenHand,
1371
1372    /// A pointing hand cursor
1373    /// corresponds to the CSS cursor value `pointer`
1374    PointingHand,
1375
1376    /// A resize left cursor
1377    /// corresponds to the CSS cursor value `w-resize`
1378    ResizeLeft,
1379
1380    /// A resize right cursor
1381    /// corresponds to the CSS cursor value `e-resize`
1382    ResizeRight,
1383
1384    /// A resize cursor to the left and right
1385    /// corresponds to the CSS cursor value `ew-resize`
1386    ResizeLeftRight,
1387
1388    /// A resize up cursor
1389    /// corresponds to the CSS cursor value `n-resize`
1390    ResizeUp,
1391
1392    /// A resize down cursor
1393    /// corresponds to the CSS cursor value `s-resize`
1394    ResizeDown,
1395
1396    /// A resize cursor directing up and down
1397    /// corresponds to the CSS cursor value `ns-resize`
1398    ResizeUpDown,
1399
1400    /// A resize cursor directing up-left and down-right
1401    /// corresponds to the CSS cursor value `nesw-resize`
1402    ResizeUpLeftDownRight,
1403
1404    /// A resize cursor directing up-right and down-left
1405    /// corresponds to the CSS cursor value `nwse-resize`
1406    ResizeUpRightDownLeft,
1407
1408    /// A cursor indicating that the item/column can be resized horizontally.
1409    /// corresponds to the CSS cursor value `col-resize`
1410    ResizeColumn,
1411
1412    /// A cursor indicating that the item/row can be resized vertically.
1413    /// corresponds to the CSS cursor value `row-resize`
1414    ResizeRow,
1415
1416    /// A text input cursor for vertical layout
1417    /// corresponds to the CSS cursor value `vertical-text`
1418    IBeamCursorForVerticalLayout,
1419
1420    /// A cursor indicating that the operation is not allowed
1421    /// corresponds to the CSS cursor value `not-allowed`
1422    OperationNotAllowed,
1423
1424    /// A cursor indicating that the operation will result in a link
1425    /// corresponds to the CSS cursor value `alias`
1426    DragLink,
1427
1428    /// A cursor indicating that the operation will result in a copy
1429    /// corresponds to the CSS cursor value `copy`
1430    DragCopy,
1431
1432    /// A cursor indicating that the operation will result in a context menu
1433    /// corresponds to the CSS cursor value `context-menu`
1434    ContextualMenu,
1435
1436    /// Hide the cursor
1437    None,
1438}
1439
1440impl Default for CursorStyle {
1441    fn default() -> Self {
1442        Self::Arrow
1443    }
1444}
1445
1446/// A clipboard item that should be copied to the clipboard
1447#[derive(Clone, Debug, Eq, PartialEq)]
1448pub struct ClipboardItem {
1449    entries: Vec<ClipboardEntry>,
1450}
1451
1452/// Either a ClipboardString or a ClipboardImage
1453#[derive(Clone, Debug, Eq, PartialEq)]
1454pub enum ClipboardEntry {
1455    /// A string entry
1456    String(ClipboardString),
1457    /// An image entry
1458    Image(Image),
1459}
1460
1461impl ClipboardItem {
1462    /// Create a new ClipboardItem::String with no associated metadata
1463    pub fn new_string(text: String) -> Self {
1464        Self {
1465            entries: vec![ClipboardEntry::String(ClipboardString::new(text))],
1466        }
1467    }
1468
1469    /// Create a new ClipboardItem::String with the given text and associated metadata
1470    pub fn new_string_with_metadata(text: String, metadata: String) -> Self {
1471        Self {
1472            entries: vec![ClipboardEntry::String(ClipboardString {
1473                text,
1474                metadata: Some(metadata),
1475            })],
1476        }
1477    }
1478
1479    /// Create a new ClipboardItem::String with the given text and associated metadata
1480    pub fn new_string_with_json_metadata<T: Serialize>(text: String, metadata: T) -> Self {
1481        Self {
1482            entries: vec![ClipboardEntry::String(
1483                ClipboardString::new(text).with_json_metadata(metadata),
1484            )],
1485        }
1486    }
1487
1488    /// Create a new ClipboardItem::Image with the given image with no associated metadata
1489    pub fn new_image(image: &Image) -> Self {
1490        Self {
1491            entries: vec![ClipboardEntry::Image(image.clone())],
1492        }
1493    }
1494
1495    /// Concatenates together all the ClipboardString entries in the item.
1496    /// Returns None if there were no ClipboardString entries.
1497    pub fn text(&self) -> Option<String> {
1498        let mut answer = String::new();
1499        let mut any_entries = false;
1500
1501        for entry in self.entries.iter() {
1502            if let ClipboardEntry::String(ClipboardString { text, metadata: _ }) = entry {
1503                answer.push_str(&text);
1504                any_entries = true;
1505            }
1506        }
1507
1508        if any_entries { Some(answer) } else { None }
1509    }
1510
1511    /// If this item is one ClipboardEntry::String, returns its metadata.
1512    #[cfg_attr(not(target_os = "windows"), allow(dead_code))]
1513    pub fn metadata(&self) -> Option<&String> {
1514        match self.entries().first() {
1515            Some(ClipboardEntry::String(clipboard_string)) if self.entries.len() == 1 => {
1516                clipboard_string.metadata.as_ref()
1517            }
1518            _ => None,
1519        }
1520    }
1521
1522    /// Get the item's entries
1523    pub fn entries(&self) -> &[ClipboardEntry] {
1524        &self.entries
1525    }
1526
1527    /// Get owned versions of the item's entries
1528    pub fn into_entries(self) -> impl Iterator<Item = ClipboardEntry> {
1529        self.entries.into_iter()
1530    }
1531}
1532
1533impl From<ClipboardString> for ClipboardEntry {
1534    fn from(value: ClipboardString) -> Self {
1535        Self::String(value)
1536    }
1537}
1538
1539impl From<String> for ClipboardEntry {
1540    fn from(value: String) -> Self {
1541        Self::from(ClipboardString::from(value))
1542    }
1543}
1544
1545impl From<Image> for ClipboardEntry {
1546    fn from(value: Image) -> Self {
1547        Self::Image(value)
1548    }
1549}
1550
1551impl From<ClipboardEntry> for ClipboardItem {
1552    fn from(value: ClipboardEntry) -> Self {
1553        Self {
1554            entries: vec![value],
1555        }
1556    }
1557}
1558
1559impl From<String> for ClipboardItem {
1560    fn from(value: String) -> Self {
1561        Self::from(ClipboardEntry::from(value))
1562    }
1563}
1564
1565impl From<Image> for ClipboardItem {
1566    fn from(value: Image) -> Self {
1567        Self::from(ClipboardEntry::from(value))
1568    }
1569}
1570
1571/// One of the editor's supported image formats (e.g. PNG, JPEG) - used when dealing with images in the clipboard
1572#[derive(Clone, Copy, Debug, Eq, PartialEq, EnumIter, Hash)]
1573pub enum ImageFormat {
1574    // Sorted from most to least likely to be pasted into an editor,
1575    // which matters when we iterate through them trying to see if
1576    // clipboard content matches them.
1577    /// .png
1578    Png,
1579    /// .jpeg or .jpg
1580    Jpeg,
1581    /// .webp
1582    Webp,
1583    /// .gif
1584    Gif,
1585    /// .svg
1586    Svg,
1587    /// .bmp
1588    Bmp,
1589    /// .tif or .tiff
1590    Tiff,
1591}
1592
1593impl ImageFormat {
1594    /// Returns the mime type for the ImageFormat
1595    pub const fn mime_type(self) -> &'static str {
1596        match self {
1597            ImageFormat::Png => "image/png",
1598            ImageFormat::Jpeg => "image/jpeg",
1599            ImageFormat::Webp => "image/webp",
1600            ImageFormat::Gif => "image/gif",
1601            ImageFormat::Svg => "image/svg+xml",
1602            ImageFormat::Bmp => "image/bmp",
1603            ImageFormat::Tiff => "image/tiff",
1604        }
1605    }
1606
1607    /// Returns the ImageFormat for the given mime type
1608    pub fn from_mime_type(mime_type: &str) -> Option<Self> {
1609        match mime_type {
1610            "image/png" => Some(Self::Png),
1611            "image/jpeg" | "image/jpg" => Some(Self::Jpeg),
1612            "image/webp" => Some(Self::Webp),
1613            "image/gif" => Some(Self::Gif),
1614            "image/svg+xml" => Some(Self::Svg),
1615            "image/bmp" => Some(Self::Bmp),
1616            "image/tiff" | "image/tif" => Some(Self::Tiff),
1617            _ => None,
1618        }
1619    }
1620}
1621
1622/// An image, with a format and certain bytes
1623#[derive(Clone, Debug, PartialEq, Eq)]
1624pub struct Image {
1625    /// The image format the bytes represent (e.g. PNG)
1626    pub format: ImageFormat,
1627    /// The raw image bytes
1628    pub bytes: Vec<u8>,
1629    /// The unique ID for the image
1630    id: u64,
1631}
1632
1633impl Hash for Image {
1634    fn hash<H: Hasher>(&self, state: &mut H) {
1635        state.write_u64(self.id);
1636    }
1637}
1638
1639impl Image {
1640    /// An empty image containing no data
1641    pub fn empty() -> Self {
1642        Self::from_bytes(ImageFormat::Png, Vec::new())
1643    }
1644
1645    /// Create an image from a format and bytes
1646    pub fn from_bytes(format: ImageFormat, bytes: Vec<u8>) -> Self {
1647        Self {
1648            id: hash(&bytes),
1649            format,
1650            bytes,
1651        }
1652    }
1653
1654    /// Get this image's ID
1655    pub fn id(&self) -> u64 {
1656        self.id
1657    }
1658
1659    /// Use the GPUI `use_asset` API to make this image renderable
1660    pub fn use_render_image(
1661        self: Arc<Self>,
1662        window: &mut Window,
1663        cx: &mut App,
1664    ) -> Option<Arc<RenderImage>> {
1665        ImageSource::Image(self)
1666            .use_data(None, window, cx)
1667            .and_then(|result| result.ok())
1668    }
1669
1670    /// Use the GPUI `get_asset` API to make this image renderable
1671    pub fn get_render_image(
1672        self: Arc<Self>,
1673        window: &mut Window,
1674        cx: &mut App,
1675    ) -> Option<Arc<RenderImage>> {
1676        ImageSource::Image(self)
1677            .get_data(None, window, cx)
1678            .and_then(|result| result.ok())
1679    }
1680
1681    /// Use the GPUI `remove_asset` API to drop this image, if possible.
1682    pub fn remove_asset(self: Arc<Self>, cx: &mut App) {
1683        ImageSource::Image(self).remove_asset(cx);
1684    }
1685
1686    /// Convert the clipboard image to an `ImageData` object.
1687    pub fn to_image_data(&self, svg_renderer: SvgRenderer) -> Result<Arc<RenderImage>> {
1688        fn frames_for_image(
1689            bytes: &[u8],
1690            format: image::ImageFormat,
1691        ) -> Result<SmallVec<[Frame; 1]>> {
1692            let mut data = image::load_from_memory_with_format(bytes, format)?.into_rgba8();
1693
1694            // Convert from RGBA to BGRA.
1695            for pixel in data.chunks_exact_mut(4) {
1696                pixel.swap(0, 2);
1697            }
1698
1699            Ok(SmallVec::from_elem(Frame::new(data), 1))
1700        }
1701
1702        let frames = match self.format {
1703            ImageFormat::Gif => {
1704                let decoder = GifDecoder::new(Cursor::new(&self.bytes))?;
1705                let mut frames = SmallVec::new();
1706
1707                for frame in decoder.into_frames() {
1708                    let mut frame = frame?;
1709                    // Convert from RGBA to BGRA.
1710                    for pixel in frame.buffer_mut().chunks_exact_mut(4) {
1711                        pixel.swap(0, 2);
1712                    }
1713                    frames.push(frame);
1714                }
1715
1716                frames
1717            }
1718            ImageFormat::Png => frames_for_image(&self.bytes, image::ImageFormat::Png)?,
1719            ImageFormat::Jpeg => frames_for_image(&self.bytes, image::ImageFormat::Jpeg)?,
1720            ImageFormat::Webp => frames_for_image(&self.bytes, image::ImageFormat::WebP)?,
1721            ImageFormat::Bmp => frames_for_image(&self.bytes, image::ImageFormat::Bmp)?,
1722            ImageFormat::Tiff => frames_for_image(&self.bytes, image::ImageFormat::Tiff)?,
1723            ImageFormat::Svg => {
1724                let pixmap = svg_renderer.render_pixmap(&self.bytes, SvgSize::ScaleFactor(1.0))?;
1725
1726                let buffer =
1727                    image::ImageBuffer::from_raw(pixmap.width(), pixmap.height(), pixmap.take())
1728                        .unwrap();
1729
1730                SmallVec::from_elem(Frame::new(buffer), 1)
1731            }
1732        };
1733
1734        Ok(Arc::new(RenderImage::new(frames)))
1735    }
1736
1737    /// Get the format of the clipboard image
1738    pub fn format(&self) -> ImageFormat {
1739        self.format
1740    }
1741
1742    /// Get the raw bytes of the clipboard image
1743    pub fn bytes(&self) -> &[u8] {
1744        self.bytes.as_slice()
1745    }
1746}
1747
1748/// A clipboard item that should be copied to the clipboard
1749#[derive(Clone, Debug, Eq, PartialEq)]
1750pub struct ClipboardString {
1751    pub(crate) text: String,
1752    pub(crate) metadata: Option<String>,
1753}
1754
1755impl ClipboardString {
1756    /// Create a new clipboard string with the given text
1757    pub fn new(text: String) -> Self {
1758        Self {
1759            text,
1760            metadata: None,
1761        }
1762    }
1763
1764    /// Return a new clipboard item with the metadata replaced by the given metadata,
1765    /// after serializing it as JSON.
1766    pub fn with_json_metadata<T: Serialize>(mut self, metadata: T) -> Self {
1767        self.metadata = Some(serde_json::to_string(&metadata).unwrap());
1768        self
1769    }
1770
1771    /// Get the text of the clipboard string
1772    pub fn text(&self) -> &String {
1773        &self.text
1774    }
1775
1776    /// Get the owned text of the clipboard string
1777    pub fn into_text(self) -> String {
1778        self.text
1779    }
1780
1781    /// Get the metadata of the clipboard string, formatted as JSON
1782    pub fn metadata_json<T>(&self) -> Option<T>
1783    where
1784        T: for<'a> Deserialize<'a>,
1785    {
1786        self.metadata
1787            .as_ref()
1788            .and_then(|m| serde_json::from_str(m).ok())
1789    }
1790
1791    #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
1792    pub(crate) fn text_hash(text: &str) -> u64 {
1793        let mut hasher = SeaHasher::new();
1794        text.hash(&mut hasher);
1795        hasher.finish()
1796    }
1797}
1798
1799impl From<String> for ClipboardString {
1800    fn from(value: String) -> Self {
1801        Self {
1802            text: value,
1803            metadata: None,
1804        }
1805    }
1806}