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