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