Add the ability to open a window on a given screen

Antonio Scandurra created

This is done by supplying the screen in the `WindowOptions` struct.
Note that it's optional, and we will let the operating system choose
which screen to show the window on when `screen` is not provided, as
we did before this change.

Change summary

crates/gpui/src/platform.rs              | 11 +++++-
crates/gpui/src/platform/mac.rs          |  1 
crates/gpui/src/platform/mac/platform.rs | 20 +++++------
crates/gpui/src/platform/mac/screen.rs   | 44 ++++++++++++++++++++++++++
crates/gpui/src/platform/mac/window.rs   | 10 ++++-
crates/gpui/src/platform/test.rs         |  4 +-
6 files changed, 73 insertions(+), 17 deletions(-)

Detailed changes

crates/gpui/src/platform.rs 🔗

@@ -25,7 +25,7 @@ use postage::oneshot;
 use serde::Deserialize;
 use std::{
     any::Any,
-    fmt::{self, Display},
+    fmt::{self, Debug, Display},
     ops::Range,
     path::{Path, PathBuf},
     rc::Rc,
@@ -44,7 +44,7 @@ pub trait Platform: Send + Sync {
     fn unhide_other_apps(&self);
     fn quit(&self);
 
-    fn screen_size(&self) -> Vector2F;
+    fn screens(&self) -> Vec<Rc<dyn Screen>>;
 
     fn open_window(
         &self,
@@ -115,6 +115,11 @@ pub trait InputHandler {
     fn rect_for_range(&self, range_utf16: Range<usize>) -> Option<RectF>;
 }
 
+pub trait Screen: Debug {
+    fn as_any(&self) -> &dyn Any;
+    fn size(&self) -> Vector2F;
+}
+
 pub trait Window {
     fn as_any_mut(&mut self) -> &mut dyn Any;
     fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>);
@@ -149,6 +154,7 @@ pub struct WindowOptions<'a> {
     pub center: bool,
     pub kind: WindowKind,
     pub is_movable: bool,
+    pub screen: Option<Rc<dyn Screen>>,
 }
 
 #[derive(Debug)]
@@ -292,6 +298,7 @@ impl<'a> Default for WindowOptions<'a> {
             center: false,
             kind: WindowKind::Normal,
             is_movable: true,
+            screen: None,
         }
     }
 }

crates/gpui/src/platform/mac/platform.rs 🔗

@@ -1,10 +1,9 @@
 use super::{
-    event::key_to_native, status_item::StatusItem, BoolExt as _, Dispatcher, FontSystem, Window,
+    event::key_to_native, screen::Screen, status_item::StatusItem, BoolExt as _, Dispatcher,
+    FontSystem, Window,
 };
 use crate::{
-    executor,
-    geometry::vector::{vec2f, Vector2F},
-    keymap,
+    executor, keymap,
     platform::{self, CursorStyle},
     Action, AppVersion, ClipboardItem, Event, Menu, MenuItem,
 };
@@ -14,7 +13,7 @@ use cocoa::{
     appkit::{
         NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
         NSEventModifierFlags, NSMenu, NSMenuItem, NSModalResponse, NSOpenPanel, NSPasteboard,
-        NSPasteboardTypeString, NSSavePanel, NSScreen, NSWindow,
+        NSPasteboardTypeString, NSSavePanel, NSWindow,
     },
     base::{id, nil, selector, YES},
     foundation::{
@@ -488,12 +487,11 @@ impl platform::Platform for MacPlatform {
         }
     }
 
-    fn screen_size(&self) -> Vector2F {
-        unsafe {
-            let screen = NSScreen::mainScreen(nil);
-            let frame = NSScreen::frame(screen);
-            vec2f(frame.size.width as f32, frame.size.height as f32)
-        }
+    fn screens(&self) -> Vec<Rc<dyn platform::Screen>> {
+        Screen::all()
+            .into_iter()
+            .map(|screen| Rc::new(screen) as Rc<_>)
+            .collect()
     }
 
     fn open_window(

crates/gpui/src/platform/mac/screen.rs 🔗

@@ -0,0 +1,44 @@
+use std::any::Any;
+
+use crate::{
+    geometry::vector::{vec2f, Vector2F},
+    platform,
+};
+use cocoa::{
+    appkit::NSScreen,
+    base::{id, nil},
+    foundation::NSArray,
+};
+
+#[derive(Debug)]
+pub struct Screen {
+    pub(crate) native_screen: id,
+}
+
+impl Screen {
+    pub fn all() -> Vec<Self> {
+        let mut screens = Vec::new();
+        unsafe {
+            let native_screens = NSScreen::screens(nil);
+            for ix in 0..native_screens.count() {
+                screens.push(Screen {
+                    native_screen: native_screens.objectAtIndex(ix),
+                });
+            }
+        }
+        screens
+    }
+}
+
+impl platform::Screen for Screen {
+    fn as_any(&self) -> &dyn Any {
+        self
+    }
+
+    fn size(&self) -> Vector2F {
+        unsafe {
+            let frame = self.native_screen.frame();
+            vec2f(frame.size.width as f32, frame.size.height as f32)
+        }
+    }
+}

crates/gpui/src/platform/mac/window.rs 🔗

@@ -8,7 +8,7 @@ use crate::{
     mac::platform::NSViewLayerContentsRedrawDuringViewResize,
     platform::{
         self,
-        mac::{geometry::RectFExt, renderer::Renderer},
+        mac::{geometry::RectFExt, renderer::Renderer, screen::Screen},
         Event, WindowBounds,
     },
     InputHandler, KeyDownEvent, ModifiersChangedEvent, MouseButton, MouseButtonEvent,
@@ -377,11 +377,17 @@ impl Window {
                     msg_send![PANEL_CLASS, alloc]
                 }
             };
-            let native_window = native_window.initWithContentRect_styleMask_backing_defer_(
+            let native_window = native_window.initWithContentRect_styleMask_backing_defer_screen_(
                 RectF::new(Default::default(), vec2f(1024., 768.)).to_ns_rect(),
                 style_mask,
                 NSBackingStoreBuffered,
                 NO,
+                options
+                    .screen
+                    .and_then(|screen| {
+                        Some(screen.as_any().downcast_ref::<Screen>()?.native_screen)
+                    })
+                    .unwrap_or(nil),
             );
             assert!(!native_window.is_null());
 

crates/gpui/src/platform/test.rs 🔗

@@ -131,8 +131,8 @@ impl super::Platform for Platform {
 
     fn quit(&self) {}
 
-    fn screen_size(&self) -> Vector2F {
-        vec2f(1024., 768.)
+    fn screens(&self) -> Vec<Rc<dyn crate::Screen>> {
+        Default::default()
     }
 
     fn open_window(