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