Detailed changes
@@ -32,11 +32,12 @@ pub fn init(cx: &mut MutableAppContext) {
});
for screen in cx.platform().screens() {
- let screen_size = screen.size();
+ let screen_bounds = screen.bounds();
let (window_id, _) = cx.add_window(
WindowOptions {
bounds: WindowBounds::Fixed(RectF::new(
- vec2f(screen_size.x() - window_size.x() - PADDING, PADDING),
+ screen_bounds.upper_right()
+ - vec2f(PADDING + window_size.x(), PADDING),
window_size,
)),
titlebar: None,
@@ -31,11 +31,11 @@ pub fn init(cx: &mut MutableAppContext) {
let window_size = vec2f(theme.window_width, theme.window_height);
for screen in cx.platform().screens() {
- let screen_size = screen.size();
+ let screen_bounds = screen.bounds();
let (window_id, _) = cx.add_window(
WindowOptions {
bounds: WindowBounds::Fixed(RectF::new(
- vec2f(screen_size.x() - window_size.x() - PADDING, PADDING),
+ screen_bounds.upper_right() - vec2f(PADDING + window_size.x(), PADDING),
window_size,
)),
titlebar: None,
@@ -123,7 +123,7 @@ pub trait InputHandler {
pub trait Screen: Debug {
fn as_any(&self) -> &dyn Any;
- fn size(&self) -> Vector2F;
+ fn bounds(&self) -> RectF;
fn display_uuid(&self) -> Uuid;
}
@@ -12,12 +12,15 @@ mod sprite_cache;
mod status_item;
mod window;
-use cocoa::base::{BOOL, NO, YES};
+use cocoa::{
+ base::{id, nil, BOOL, NO, YES},
+ foundation::{NSAutoreleasePool, NSNotFound, NSString, NSUInteger},
+};
pub use dispatcher::Dispatcher;
pub use fonts::FontSystem;
use platform::{MacForegroundPlatform, MacPlatform};
pub use renderer::Surface;
-use std::{rc::Rc, sync::Arc};
+use std::{ops::Range, rc::Rc, sync::Arc};
use window::Window;
pub(crate) fn platform() -> Arc<dyn super::Platform> {
@@ -41,3 +44,57 @@ impl BoolExt for bool {
}
}
}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+struct NSRange {
+ pub location: NSUInteger,
+ pub length: NSUInteger,
+}
+
+impl NSRange {
+ fn invalid() -> Self {
+ Self {
+ location: NSNotFound as NSUInteger,
+ length: 0,
+ }
+ }
+
+ fn is_valid(&self) -> bool {
+ self.location != NSNotFound as NSUInteger
+ }
+
+ fn to_range(self) -> Option<Range<usize>> {
+ if self.is_valid() {
+ let start = self.location as usize;
+ let end = start + self.length as usize;
+ Some(start..end)
+ } else {
+ None
+ }
+ }
+}
+
+impl From<Range<usize>> for NSRange {
+ fn from(range: Range<usize>) -> Self {
+ NSRange {
+ location: range.start as NSUInteger,
+ length: range.len() as NSUInteger,
+ }
+ }
+}
+
+unsafe impl objc::Encode for NSRange {
+ fn encode() -> objc::Encoding {
+ let encoding = format!(
+ "{{NSRange={}{}}}",
+ NSUInteger::encode().as_str(),
+ NSUInteger::encode().as_str()
+ );
+ unsafe { objc::Encoding::from_str(&encoding) }
+ }
+}
+
+unsafe fn ns_string(string: &str) -> id {
+ NSString::alloc(nil).init_str(string).autorelease()
+}
@@ -1,27 +1,99 @@
-use cocoa::foundation::{NSPoint, NSRect, NSSize};
-use pathfinder_geometry::{rect::RectF, vector::Vector2F};
+use cocoa::{
+ appkit::NSWindow,
+ base::id,
+ foundation::{NSPoint, NSRect, NSSize},
+};
+use objc::{msg_send, sel, sel_impl};
+use pathfinder_geometry::{
+ rect::RectF,
+ vector::{vec2f, Vector2F},
+};
+
+///! Macos screen have a y axis that goings up from the bottom of the screen and
+///! an origin at the bottom left of the main display.
pub trait Vector2FExt {
- fn to_ns_point(&self) -> NSPoint;
- fn to_ns_size(&self) -> NSSize;
+ /// Converts self to an NSPoint with y axis pointing up.
+ fn to_screen_ns_point(&self, native_window: id) -> NSPoint;
+}
+impl Vector2FExt for Vector2F {
+ fn to_screen_ns_point(&self, native_window: id) -> NSPoint {
+ unsafe {
+ let point = NSPoint::new(self.x() as f64, -self.y() as f64);
+ msg_send![native_window, convertPointToScreen: point]
+ }
+ }
}
pub trait RectFExt {
+ /// Converts self to an NSRect with y axis pointing up.
+ /// The resulting NSRect will have an origin at the bottom left of the rectangle.
+ /// Also takes care of converting from window scaled coordinates to screen coordinates
+ fn to_screen_ns_rect(&self, native_window: id) -> NSRect;
+
+ /// Converts self to an NSRect with y axis point up.
+ /// The resulting NSRect will have an origin at the bottom left of the rectangle.
+ /// Unlike to_screen_ns_rect, coordinates are not converted and are assumed to already be in screen scale
fn to_ns_rect(&self) -> NSRect;
}
+impl RectFExt for RectF {
+ fn to_screen_ns_rect(&self, native_window: id) -> NSRect {
+ unsafe { native_window.convertRectToScreen_(self.to_ns_rect()) }
+ }
-impl Vector2FExt for Vector2F {
- fn to_ns_point(&self) -> NSPoint {
- NSPoint::new(self.x() as f64, self.y() as f64)
+ fn to_ns_rect(&self) -> NSRect {
+ dbg!(&self);
+ NSRect::new(
+ NSPoint::new(
+ dbg!(self.origin_x() as f64),
+ dbg!(-(self.origin_y() - self.height()) as f64),
+ ),
+ NSSize::new(self.width() as f64, self.height() as f64),
+ )
}
+}
- fn to_ns_size(&self) -> NSSize {
- NSSize::new(self.x() as f64, self.y() as f64)
+pub trait NSPointExt {
+ /// Converts self to a Vector2F with y axis pointing down.
+ /// Also takes care of converting from window scaled coordinates to screen coordinates
+ fn to_window_vector2f(&self, native_window: id) -> Vector2F;
+}
+impl NSPointExt for NSPoint {
+ fn to_window_vector2f(&self, native_window: id) -> Vector2F {
+ unsafe {
+ let point: NSPoint = msg_send![native_window, convertPointFromScreen: self];
+ vec2f(point.x as f32, -point.y as f32)
+ }
}
}
-impl RectFExt for RectF {
- fn to_ns_rect(&self) -> NSRect {
- NSRect::new(self.origin().to_ns_point(), self.size().to_ns_size())
+pub trait NSRectExt {
+ /// Converts self to a RectF with y axis pointing down.
+ /// The resulting RectF will have an origin at the top left of the rectangle.
+ /// Also takes care of converting from screen scale coordinates to window coordinates
+ fn to_window_rectf(&self, native_window: id) -> RectF;
+
+ /// Converts self to a RectF with y axis pointing down.
+ /// The resulting RectF will have an origin at the top left of the rectangle.
+ /// Unlike to_screen_ns_rect, coordinates are not converted and are assumed to already be in screen scale
+ fn to_rectf(&self) -> RectF;
+}
+impl NSRectExt for NSRect {
+ fn to_window_rectf(&self, native_window: id) -> RectF {
+ unsafe {
+ dbg!(self.origin.x);
+ let rect: NSRect = native_window.convertRectFromScreen_(*self);
+ rect.to_rectf()
+ }
+ }
+
+ fn to_rectf(&self) -> RectF {
+ RectF::new(
+ vec2f(
+ dbg!(self.origin.x as f32),
+ dbg!(-(self.origin.y - self.size.height) as f32),
+ ),
+ vec2f(self.size.width as f32, self.size.height as f32),
+ )
}
}
@@ -1,21 +1,21 @@
use std::{any::Any, ffi::c_void};
-use crate::{
- geometry::vector::{vec2f, Vector2F},
- platform,
-};
+use crate::platform;
use cocoa::{
appkit::NSScreen,
base::{id, nil},
- foundation::{NSArray, NSDictionary, NSString},
+ foundation::{NSArray, NSDictionary},
};
use core_foundation::{
number::{kCFNumberIntType, CFNumberGetValue, CFNumberRef},
uuid::{CFUUIDGetUUIDBytes, CFUUIDRef},
};
use core_graphics::display::CGDirectDisplayID;
+use pathfinder_geometry::rect::RectF;
use uuid::Uuid;
+use super::{geometry::NSRectExt, ns_string};
+
#[link(name = "ApplicationServices", kind = "framework")]
extern "C" {
pub fn CGDisplayCreateUUIDFromDisplayID(display: CGDirectDisplayID) -> CFUUIDRef;
@@ -58,13 +58,6 @@ impl platform::Screen for Screen {
self
}
- fn size(&self) -> Vector2F {
- unsafe {
- let frame = self.native_screen.frame();
- vec2f(frame.size.width as f32, frame.size.height as f32)
- }
- }
-
fn display_uuid(&self) -> uuid::Uuid {
unsafe {
// Screen ids are not stable. Further, the default device id is also unstable across restarts.
@@ -72,7 +65,7 @@ impl platform::Screen for Screen {
// This approach is similar to that which winit takes
// https://github.com/rust-windowing/winit/blob/402cbd55f932e95dbfb4e8b5e8551c49e56ff9ac/src/platform_impl/macos/monitor.rs#L99
let device_description = self.native_screen.deviceDescription();
- let key = NSString::alloc(nil).init_str("NSScreenNumber");
+ let key = ns_string("NSScreenNumber");
let device_id_obj = device_description.objectForKey_(key);
let mut device_id: u32 = 0;
CFNumberGetValue(
@@ -102,4 +95,11 @@ impl platform::Screen for Screen {
])
}
}
+
+ fn bounds(&self) -> RectF {
+ unsafe {
+ let frame = self.native_screen.frame();
+ frame.to_rectf()
+ }
+ }
}
@@ -17,14 +17,12 @@ use crate::{
use block::ConcreteBlock;
use cocoa::{
appkit::{
- CGFloat, CGPoint, NSApplication, NSBackingStoreBuffered, NSScreen, NSView,
- NSViewHeightSizable, NSViewWidthSizable, NSWindow, NSWindowButton,
- NSWindowCollectionBehavior, NSWindowStyleMask,
+ CGPoint, NSApplication, NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable,
+ NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowCollectionBehavior,
+ NSWindowStyleMask,
},
base::{id, nil},
- foundation::{
- NSAutoreleasePool, NSInteger, NSNotFound, NSPoint, NSRect, NSSize, NSString, NSUInteger,
- },
+ foundation::{NSAutoreleasePool, NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger},
};
use core_graphics::display::CGRect;
use ctor::ctor;
@@ -52,6 +50,11 @@ use std::{
time::Duration,
};
+use super::{
+ geometry::{NSRectExt, Vector2FExt},
+ ns_string, NSRange,
+};
+
const WINDOW_STATE_IVAR: &str = "windowState";
static mut WINDOW_CLASS: *const Class = ptr::null();
@@ -76,56 +79,6 @@ const NSTrackingInVisibleRect: NSUInteger = 0x200;
#[allow(non_upper_case_globals)]
const NSWindowAnimationBehaviorUtilityWindow: NSInteger = 4;
-#[repr(C)]
-#[derive(Copy, Clone, Debug)]
-struct NSRange {
- pub location: NSUInteger,
- pub length: NSUInteger,
-}
-
-impl NSRange {
- fn invalid() -> Self {
- Self {
- location: NSNotFound as NSUInteger,
- length: 0,
- }
- }
-
- fn is_valid(&self) -> bool {
- self.location != NSNotFound as NSUInteger
- }
-
- fn to_range(self) -> Option<Range<usize>> {
- if self.is_valid() {
- let start = self.location as usize;
- let end = start + self.length as usize;
- Some(start..end)
- } else {
- None
- }
- }
-}
-
-impl From<Range<usize>> for NSRange {
- fn from(range: Range<usize>) -> Self {
- NSRange {
- location: range.start as NSUInteger,
- length: range.len() as NSUInteger,
- }
- }
-}
-
-unsafe impl objc::Encode for NSRange {
- fn encode() -> objc::Encoding {
- let encoding = format!(
- "{{NSRange={}{}}}",
- NSUInteger::encode().as_str(),
- NSUInteger::encode().as_str()
- );
- unsafe { objc::Encoding::from_str(&encoding) }
- }
-}
-
#[ctor]
unsafe fn build_classes() {
WINDOW_CLASS = build_window_class("GPUIWindow", class!(NSWindow));
@@ -315,8 +268,6 @@ unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const C
decl.register()
}
-pub struct Window(Rc<RefCell<WindowState>>);
-
///Used to track what the IME does when we send it a keystroke.
///This is only used to handle the case where the IME mysteriously
///swallows certain keys.
@@ -329,6 +280,11 @@ enum ImeState {
None,
}
+struct InsertText {
+ replacement_range: Option<Range<usize>>,
+ text: String,
+}
+
struct WindowState {
id: usize,
native_window: id,
@@ -357,11 +313,112 @@ struct WindowState {
ime_text: Option<String>,
}
-struct InsertText {
- replacement_range: Option<Range<usize>>,
- text: String,
+impl WindowState {
+ fn move_traffic_light(&self) {
+ if let Some(traffic_light_position) = self.traffic_light_position {
+ let titlebar_height = self.titlebar_height();
+
+ unsafe {
+ let close_button: id = msg_send![
+ self.native_window,
+ standardWindowButton: NSWindowButton::NSWindowCloseButton
+ ];
+ let min_button: id = msg_send![
+ self.native_window,
+ standardWindowButton: NSWindowButton::NSWindowMiniaturizeButton
+ ];
+ let zoom_button: id = msg_send![
+ self.native_window,
+ standardWindowButton: NSWindowButton::NSWindowZoomButton
+ ];
+
+ let mut close_button_frame: CGRect = msg_send![close_button, frame];
+ let mut min_button_frame: CGRect = msg_send![min_button, frame];
+ let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];
+ let mut origin = vec2f(
+ traffic_light_position.x(),
+ titlebar_height
+ - traffic_light_position.y()
+ - close_button_frame.size.height as f32,
+ );
+ let button_spacing =
+ (min_button_frame.origin.x - close_button_frame.origin.x) as f32;
+
+ close_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
+ let _: () = msg_send![close_button, setFrame: close_button_frame];
+ origin.set_x(origin.x() + button_spacing);
+
+ min_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
+ let _: () = msg_send![min_button, setFrame: min_button_frame];
+ origin.set_x(origin.x() + button_spacing);
+
+ zoom_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
+ let _: () = msg_send![zoom_button, setFrame: zoom_button_frame];
+ }
+ }
+ }
+
+ fn is_fullscreen(&self) -> bool {
+ unsafe {
+ let style_mask = self.native_window.styleMask();
+ style_mask.contains(NSWindowStyleMask::NSFullScreenWindowMask)
+ }
+ }
+
+ fn bounds(&self) -> WindowBounds {
+ unsafe {
+ if self.is_fullscreen() {
+ return WindowBounds::Fullscreen;
+ }
+
+ let screen_frame = self
+ .native_window
+ .screen()
+ .visibleFrame()
+ .to_window_rectf(self.native_window);
+ let window_frame = self.frame();
+
+ if screen_frame == window_frame {
+ WindowBounds::Maximized
+ } else {
+ WindowBounds::Fixed(window_frame)
+ }
+ }
+ }
+
+ // Returns the window bounds in window coordinates
+ fn frame(&self) -> RectF {
+ unsafe { NSWindow::frame(self.native_window).to_window_rectf(self.native_window) }
+ }
+
+ fn content_size(&self) -> Vector2F {
+ let NSSize { width, height, .. } =
+ unsafe { NSView::frame(self.native_window.contentView()) }.size;
+ vec2f(width as f32, height as f32)
+ }
+
+ fn scale_factor(&self) -> f32 {
+ get_scale_factor(self.native_window)
+ }
+
+ fn titlebar_height(&self) -> f32 {
+ unsafe {
+ let frame = NSWindow::frame(self.native_window);
+ let content_layout_rect: CGRect = msg_send![self.native_window, contentLayoutRect];
+ (frame.size.height - content_layout_rect.size.height) as f32
+ }
+ }
+
+ fn present_scene(&mut self, scene: Scene) {
+ self.scene_to_render = Some(scene);
+ unsafe {
+ let _: () = msg_send![self.native_window.contentView(), setNeedsDisplay: YES];
+ }
+ }
}
+pub struct Window(Rc<RefCell<WindowState>>);
+
impl Window {
pub fn open(
id: usize,
@@ -395,7 +452,7 @@ impl Window {
}
};
let native_window = native_window.initWithContentRect_styleMask_backing_defer_screen_(
- RectF::new(Default::default(), vec2f(1024., 768.)).to_ns_rect(),
+ NSRect::new(NSPoint::new(0., 0.), NSSize::new(1024., 768.)),
style_mask,
NSBackingStoreBuffered,
NO,
@@ -416,20 +473,8 @@ impl Window {
WindowBounds::Maximized => {
native_window.setFrame_display_(screen.visibleFrame(), YES);
}
- WindowBounds::Fixed(top_left_bounds) => {
- let frame = screen.visibleFrame();
- let bottom_left_bounds = RectF::new(
- dbg!(vec2f(
- top_left_bounds.origin_x(),
- frame.size.height as f32
- - top_left_bounds.origin_y()
- - top_left_bounds.height(),
- )),
- dbg!(top_left_bounds.size()),
- )
- .to_ns_rect();
- let screen_rect = native_window.convertRectToScreen_(bottom_left_bounds);
- native_window.setFrame_display_(screen_rect, YES);
+ WindowBounds::Fixed(rect) => {
+ native_window.setFrame_display_(rect.to_screen_ns_rect(native_window), YES);
}
}
@@ -776,21 +821,16 @@ impl platform::Window for Window {
}
fn is_topmost_for_position(&self, position: Vector2F) -> bool {
- let window_bounds = self.bounds();
let self_borrow = self.0.borrow();
let self_id = self_borrow.id;
unsafe {
+ let window_frame = self_borrow.frame();
let app = NSApplication::sharedApplication(nil);
- // Convert back to bottom-left coordinates
- let point = NSPoint::new(
- position.x() as CGFloat,
- (window_bounds.height() - position.y()) as CGFloat,
- );
-
- let screen_point: NSPoint =
- msg_send![self_borrow.native_window, convertPointToScreen: point];
+ // Convert back to screen coordinates
+ let screen_point =
+ (position + window_frame.origin()).to_screen_ns_point(self_borrow.native_window);
let window_number: NSInteger = msg_send![class!(NSWindow), windowNumberAtPoint:screen_point belowWindowWithWindowNumber:0];
let top_most_window: id = msg_send![app, windowWithWindowNumber: window_number];
@@ -807,113 +847,6 @@ impl platform::Window for Window {
}
}
-impl WindowState {
- fn move_traffic_light(&self) {
- if let Some(traffic_light_position) = self.traffic_light_position {
- let titlebar_height = self.titlebar_height();
-
- unsafe {
- let close_button: id = msg_send![
- self.native_window,
- standardWindowButton: NSWindowButton::NSWindowCloseButton
- ];
- let min_button: id = msg_send![
- self.native_window,
- standardWindowButton: NSWindowButton::NSWindowMiniaturizeButton
- ];
- let zoom_button: id = msg_send![
- self.native_window,
- standardWindowButton: NSWindowButton::NSWindowZoomButton
- ];
-
- let mut close_button_frame: CGRect = msg_send![close_button, frame];
- let mut min_button_frame: CGRect = msg_send![min_button, frame];
- let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];
- let mut origin = vec2f(
- traffic_light_position.x(),
- titlebar_height
- - traffic_light_position.y()
- - close_button_frame.size.height as f32,
- );
- let button_spacing =
- (min_button_frame.origin.x - close_button_frame.origin.x) as f32;
-
- close_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
- let _: () = msg_send![close_button, setFrame: close_button_frame];
- origin.set_x(origin.x() + button_spacing);
-
- min_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
- let _: () = msg_send![min_button, setFrame: min_button_frame];
- origin.set_x(origin.x() + button_spacing);
-
- zoom_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
- let _: () = msg_send![zoom_button, setFrame: zoom_button_frame];
- }
- }
- }
-
- fn is_fullscreen(&self) -> bool {
- unsafe {
- let style_mask = self.native_window.styleMask();
- style_mask.contains(NSWindowStyleMask::NSFullScreenWindowMask)
- }
- }
-
- fn bounds(&self) -> WindowBounds {
- unsafe {
- if self.is_fullscreen() {
- return WindowBounds::Fullscreen;
- }
-
- let screen_frame = self.native_window.screen().visibleFrame();
- let window_frame = NSWindow::frame(self.native_window);
- let origin = vec2f(
- window_frame.origin.x as f32,
- (screen_frame.size.height - window_frame.origin.y - window_frame.size.height)
- as f32,
- );
- let size = vec2f(
- window_frame.size.width as f32,
- window_frame.size.height as f32,
- );
-
- if origin.is_zero()
- && size.x() == screen_frame.size.width as f32
- && size.y() == screen_frame.size.height as f32
- {
- WindowBounds::Maximized
- } else {
- WindowBounds::Fixed(RectF::new(origin, size))
- }
- }
- }
-
- fn content_size(&self) -> Vector2F {
- let NSSize { width, height, .. } =
- unsafe { NSView::frame(self.native_window.contentView()) }.size;
- vec2f(width as f32, height as f32)
- }
-
- fn scale_factor(&self) -> f32 {
- get_scale_factor(self.native_window)
- }
-
- fn titlebar_height(&self) -> f32 {
- unsafe {
- let frame = NSWindow::frame(self.native_window);
- let content_layout_rect: CGRect = msg_send![self.native_window, contentLayoutRect];
- (frame.size.height - content_layout_rect.size.height) as f32
- }
- }
-
- fn present_scene(&mut self, scene: Scene) {
- self.scene_to_render = Some(scene);
- unsafe {
- let _: () = msg_send![self.native_window.contentView(), setNeedsDisplay: YES];
- }
- }
-}
-
fn get_scale_factor(native_window: id) -> f32 {
unsafe {
let screen: id = msg_send![native_window, screen];
@@ -1547,10 +1480,6 @@ async fn synthetic_drag(
}
}
-unsafe fn ns_string(string: &str) -> id {
- NSString::alloc(nil).init_str(string).autorelease()
-}
-
fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
where
F: FnOnce(&mut dyn InputHandler) -> R,
@@ -234,8 +234,8 @@ impl super::Screen for Screen {
self
}
- fn size(&self) -> Vector2F {
- Vector2F::new(1920., 1080.)
+ fn bounds(&self) -> RectF {
+ RectF::new(Vector2F::zero(), Vector2F::new(1920., 1080.))
}
fn display_uuid(&self) -> uuid::Uuid {
@@ -20,8 +20,7 @@ use gpui::{
},
impl_actions,
platform::{WindowBounds, WindowOptions},
- AssetSource, AsyncAppContext, ClipboardItem, Platform, PromptLevel, TitlebarOptions,
- ViewContext, WindowKind,
+ AssetSource, AsyncAppContext, Platform, PromptLevel, TitlebarOptions, ViewContext, WindowKind,
};
use language::Rope;
use lazy_static::lazy_static;