diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 6692e8bf38..515cd4d506 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -13,7 +13,7 @@ use call::{report_call_event_for_room, ActiveCall}; pub use collab_panel::CollabPanel; pub use collab_titlebar_item::CollabTitlebarItem; use gpui::{ - actions, point, AppContext, GlobalPixels, Pixels, PlatformDisplay, Size, Task, WindowContext, + actions, point, AppContext, DevicePixels, Pixels, PlatformDisplay, Size, Task, WindowContext, WindowKind, WindowOptions, }; use panel_settings::MessageEditorSettings; @@ -97,13 +97,13 @@ fn notification_window_options( screen: Rc, window_size: Size, ) -> WindowOptions { - let notification_margin_width = GlobalPixels::from(16.); - let notification_margin_height = GlobalPixels::from(-0.) - GlobalPixels::from(48.); + let notification_margin_width = DevicePixels::from(16); + let notification_margin_height = DevicePixels::from(-0) - DevicePixels::from(48); let screen_bounds = screen.bounds(); - let size: Size = window_size.into(); + let size: Size = window_size.into(); - let bounds = gpui::Bounds:: { + let bounds = gpui::Bounds:: { origin: screen_bounds.upper_right() - point( size.width + notification_margin_width, diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index f3f476d25f..197accd055 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -7304,8 +7304,8 @@ async fn test_following(cx: &mut gpui::TestAppContext) { cx.open_window( WindowOptions { bounds: Some(Bounds::from_corners( - gpui::Point::new(0_f64.into(), 0_f64.into()), - gpui::Point::new(10_f64.into(), 80_f64.into()), + gpui::Point::new(0.into(), 0.into()), + gpui::Point::new(10.into(), 80.into()), )), ..Default::default() }, diff --git a/crates/gpui/examples/animation.rs b/crates/gpui/examples/animation.rs index b4e0f4c0a3..b0e2c07df0 100644 --- a/crates/gpui/examples/animation.rs +++ b/crates/gpui/examples/animation.rs @@ -63,7 +63,7 @@ fn main() { .with_assets(Assets {}) .run(|cx: &mut AppContext| { let options = WindowOptions { - bounds: Some(Bounds::centered(size(px(300.), px(300.)), cx)), + bounds: Some(Bounds::centered(None, size(px(300.), px(300.)), cx)), ..Default::default() }; cx.open_window(options, |cx| { diff --git a/crates/gpui/examples/hello_world.rs b/crates/gpui/examples/hello_world.rs index 202dd2f691..e377aff745 100644 --- a/crates/gpui/examples/hello_world.rs +++ b/crates/gpui/examples/hello_world.rs @@ -23,7 +23,7 @@ impl Render for HelloWorld { fn main() { App::new().run(|cx: &mut AppContext| { - let bounds = Bounds::centered(size(px(600.0), px(600.0)), cx); + let bounds = Bounds::centered(None, size(px(600.0), px(600.0)), cx); cx.open_window( WindowOptions { bounds: Some(bounds), diff --git a/crates/gpui/examples/window_positioning.rs b/crates/gpui/examples/window_positioning.rs new file mode 100644 index 0000000000..4c3300c693 --- /dev/null +++ b/crates/gpui/examples/window_positioning.rs @@ -0,0 +1,66 @@ +use gpui::*; + +struct WindowContent { + text: SharedString, +} + +impl Render for WindowContent { + fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement { + div() + .flex() + .bg(rgb(0x1e2025)) + .size_full() + .justify_center() + .items_center() + .text_xl() + .text_color(rgb(0xffffff)) + .child(self.text.clone()) + } +} + +fn main() { + App::new().run(|cx: &mut AppContext| { + // Create several new windows, positioned in the top right corner of each screen + + for screen in cx.displays() { + let options = { + let popup_margin_width = DevicePixels::from(16); + let popup_margin_height = DevicePixels::from(-0) - DevicePixels::from(48); + + let window_size = Size { + width: px(400.), + height: px(72.), + }; + + let screen_bounds = screen.bounds(); + let size: Size = window_size.into(); + + let bounds = gpui::Bounds:: { + origin: screen_bounds.upper_right() + - point(size.width + popup_margin_width, popup_margin_height), + size: window_size.into(), + }; + + WindowOptions { + // Set the bounds of the window in screen coordinates + bounds: Some(bounds), + // Specify the display_id to ensure the window is created on the correct screen + display_id: Some(screen.id()), + + titlebar: None, + focus: false, + show: true, + kind: WindowKind::PopUp, + is_movable: false, + fullscreen: false, + } + }; + + cx.open_window(options, |cx| { + cx.new_view(|_| WindowContent { + text: format!("{:?}", screen.id()).into(), + }) + }); + } + }); +} diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index ef4292791d..c03770b5c0 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -30,11 +30,11 @@ use util::{ use crate::{ current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any, AnyView, AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, - DispatchPhase, Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap, Keystroke, - LayoutId, Menu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, PromptBuilder, - PromptHandle, PromptLevel, Render, RenderablePromptHandle, SharedString, SubscriberSet, - Subscription, SvgRenderer, Task, TextSystem, View, ViewContext, Window, WindowAppearance, - WindowContext, WindowHandle, WindowId, + DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap, + Keystroke, LayoutId, Menu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, + PromptBuilder, PromptHandle, PromptLevel, Render, RenderablePromptHandle, SharedString, + SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, View, ViewContext, Window, + WindowAppearance, WindowContext, WindowHandle, WindowId, }; mod async_context; @@ -525,6 +525,14 @@ impl AppContext { self.platform.primary_display() } + /// Returns the display with the given ID, if one exists. + pub fn find_display(&self, id: DisplayId) -> Option> { + self.displays() + .iter() + .find(|display| display.id() == id) + .cloned() + } + /// Returns the appearance of the application's windows. pub fn window_appearance(&self) -> WindowAppearance { self.platform.window_appearance() diff --git a/crates/gpui/src/app/test_context.rs b/crates/gpui/src/app/test_context.rs index 0ca6f038a4..59bd023d77 100644 --- a/crates/gpui/src/app/test_context.rs +++ b/crates/gpui/src/app/test_context.rs @@ -173,7 +173,7 @@ impl TestAppContext { let mut cx = self.app.borrow_mut(); // Some tests rely on the window size matching the bounds of the test display - let bounds = Bounds::maximized(&mut cx); + let bounds = Bounds::maximized(None, &mut cx); cx.open_window( WindowOptions { bounds: Some(bounds), @@ -186,7 +186,7 @@ impl TestAppContext { /// Adds a new window with no content. pub fn add_empty_window(&mut self) -> &mut VisualTestContext { let mut cx = self.app.borrow_mut(); - let bounds = Bounds::maximized(&mut cx); + let bounds = Bounds::maximized(None, &mut cx); let window = cx.open_window( WindowOptions { bounds: Some(bounds), @@ -209,7 +209,7 @@ impl TestAppContext { V: 'static + Render, { let mut cx = self.app.borrow_mut(); - let bounds = Bounds::maximized(&mut cx); + let bounds = Bounds::maximized(None, &mut cx); let window = cx.open_window( WindowOptions { bounds: Some(bounds), diff --git a/crates/gpui/src/geometry.rs b/crates/gpui/src/geometry.rs index 41d330cdf1..7fa50032d8 100644 --- a/crates/gpui/src/geometry.rs +++ b/crates/gpui/src/geometry.rs @@ -13,7 +13,7 @@ use std::{ ops::{Add, Div, Mul, MulAssign, Sub}, }; -use crate::AppContext; +use crate::{AppContext, DisplayId}; /// An axis along which a measurement can be made. #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -363,11 +363,11 @@ pub struct Size { pub height: T, } -impl From> for Size { - fn from(size: Size) -> Self { +impl From> for Size { + fn from(size: Size) -> Self { Size { - width: Pixels(size.width.0), - height: Pixels(size.height.0), + width: Pixels(size.width.0 as f32), + height: Pixels(size.height.0 as f32), } } } @@ -604,11 +604,11 @@ impl From> for Size { } } -impl From> for Size { +impl From> for Size { fn from(size: Size) -> Self { Size { - width: GlobalPixels(size.width.0), - height: GlobalPixels(size.height.0), + width: DevicePixels(size.width.0 as i32), + height: DevicePixels(size.height.0 as i32), } } } @@ -693,31 +693,43 @@ pub struct Bounds { pub size: Size, } -impl Bounds { - /// Generate a centered bounds for the primary display - pub fn centered(size: impl Into>, cx: &mut AppContext) -> Self { +impl Bounds { + /// Generate a centered bounds for the given display or primary display if none is provided + pub fn centered( + display_id: Option, + size: impl Into>, + cx: &mut AppContext, + ) -> Self { + let display = display_id + .and_then(|id| cx.find_display(id)) + .or_else(|| cx.primary_display()); + let size = size.into(); - cx.primary_display() + display .map(|display| { let center = display.bounds().center(); Bounds { - origin: point(center.x - size.width / 2.0, center.y - size.height / 2.0), + origin: point(center.x - size.width / 2, center.y - size.height / 2), size, } }) .unwrap_or_else(|| Bounds { - origin: point(GlobalPixels(0.0), GlobalPixels(0.0)), + origin: point(DevicePixels(0), DevicePixels(0)), size, }) } - /// Generate maximized bounds for the primary display - pub fn maximized(cx: &mut AppContext) -> Self { - cx.primary_display() + /// Generate maximized bounds for the given display or primary display if none is provided + pub fn maximized(display_id: Option, cx: &mut AppContext) -> Self { + let display = display_id + .and_then(|id| cx.find_display(id)) + .or_else(|| cx.primary_display()); + + display .map(|display| display.bounds()) .unwrap_or_else(|| Bounds { - origin: point(GlobalPixels(0.0), GlobalPixels(0.0)), - size: size(GlobalPixels(1024.0), GlobalPixels(768.0)), + origin: point(DevicePixels(0), DevicePixels(0)), + size: size(DevicePixels(1024), DevicePixels(768)), }) } } @@ -2455,34 +2467,6 @@ impl From for f64 { } } -/// Represents pixels in a global coordinate space, which can span across multiple displays. -/// -/// `GlobalPixels` is used when dealing with a coordinate system that is not limited to a single -/// display's boundaries. This type is particularly useful in multi-monitor setups where -/// positioning and measurements need to be consistent and relative to a "global" origin point -/// rather than being relative to any individual display. -#[derive(Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd)] -#[repr(transparent)] -pub struct GlobalPixels(pub(crate) f32); - -impl Debug for GlobalPixels { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} px (global coordinate space)", self.0) - } -} - -impl From for f64 { - fn from(global_pixels: GlobalPixels) -> Self { - global_pixels.0 as f64 - } -} - -impl From for GlobalPixels { - fn from(global_pixels: f64) -> Self { - GlobalPixels(global_pixels as f32) - } -} - /// Represents a length in rems, a unit based on the font-size of the window, which can be assigned with [`WindowContext::set_rem_size`][set_rem_size]. /// /// Rems are used for defining lengths that are scalable and consistent across different UI elements. @@ -2834,12 +2818,6 @@ impl Half for Rems { } } -impl Half for GlobalPixels { - fn half(&self) -> Self { - Self(self.0 / 2.) - } -} - /// Provides a trait for types that can negate their values. pub trait Negate { /// Returns the negation of the given value @@ -2882,12 +2860,6 @@ impl Negate for Rems { } } -impl Negate for GlobalPixels { - fn negate(self) -> Self { - Self(-self.0) - } -} - /// A trait for checking if a value is zero. /// /// This trait provides a method to determine if a value is considered to be zero. diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 744dd2fe92..34a13f673a 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -23,9 +23,9 @@ mod windows; use crate::{ Action, AnyWindowHandle, AsyncWindowContext, BackgroundExecutor, Bounds, DevicePixels, - DispatchEventResult, Font, FontId, FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, - GlyphId, Keymap, LineLayout, Pixels, PlatformInput, Point, RenderGlyphParams, - RenderImageParams, RenderSvgParams, Scene, SharedString, Size, Task, TaskLabel, WindowContext, + DispatchEventResult, Font, FontId, FontMetrics, FontRun, ForegroundExecutor, GlyphId, Keymap, + LineLayout, Pixels, PlatformInput, Point, RenderGlyphParams, RenderImageParams, + RenderSvgParams, Scene, SharedString, Size, Task, TaskLabel, WindowContext, }; use anyhow::Result; use async_task::Runnable; @@ -152,7 +152,7 @@ pub trait PlatformDisplay: Send + Sync + Debug { fn uuid(&self) -> Result; /// Get the bounds for this display - fn bounds(&self) -> Bounds; + fn bounds(&self) -> Bounds; } /// An opaque identifier for a hardware display @@ -168,7 +168,7 @@ impl Debug for DisplayId { unsafe impl Send for DisplayId {} pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle { - fn bounds(&self) -> Bounds; + fn bounds(&self) -> Bounds; fn is_maximized(&self) -> bool; fn is_minimized(&self) -> bool; fn content_size(&self) -> Size; @@ -508,8 +508,9 @@ pub trait InputHandler: 'static { /// The variables that can be configured when creating a new window #[derive(Debug)] pub struct WindowOptions { + /// The bounds of the window in screen coordinates. /// None -> inherit, Some(bounds) -> set bounds - pub bounds: Option>, + pub bounds: Option>, /// The titlebar configuration of the window pub titlebar: Option, @@ -529,7 +530,8 @@ pub struct WindowOptions { /// Whether the window should be movable by the user pub is_movable: bool, - /// The display to create the window on + /// The display to create the window on, if this is None, + /// the window will be created on the main display pub display_id: Option, } @@ -537,7 +539,7 @@ pub struct WindowOptions { #[derive(Debug)] pub(crate) struct WindowParams { /// - pub bounds: Bounds, + pub bounds: Bounds, /// The titlebar configuration of the window pub titlebar: Option, @@ -552,7 +554,6 @@ pub(crate) struct WindowParams { pub show: bool, - /// The display to create the window on pub display_id: Option, } @@ -599,10 +600,6 @@ pub enum WindowKind { PopUp, } -/// Platform level interface -/// bounds: Bounds -/// fullscreen: bool - /// The appearance of the window, as defined by the operating system. /// /// On macOS, this corresponds to named [`NSAppearance`](https://developer.apple.com/documentation/appkit/nsappearance) diff --git a/crates/gpui/src/platform/linux/wayland/display.rs b/crates/gpui/src/platform/linux/wayland/display.rs index 0b76a4b7dc..2e61770d02 100644 --- a/crates/gpui/src/platform/linux/wayland/display.rs +++ b/crates/gpui/src/platform/linux/wayland/display.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; use uuid::Uuid; -use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Size}; +use crate::{Bounds, DevicePixels, DisplayId, PlatformDisplay, Size}; #[derive(Debug)] pub(crate) struct WaylandDisplay {} @@ -19,12 +19,12 @@ impl PlatformDisplay for WaylandDisplay { } // todo(linux) - fn bounds(&self) -> Bounds { + fn bounds(&self) -> Bounds { Bounds { origin: Default::default(), size: Size { - width: GlobalPixels(1000f32), - height: GlobalPixels(500f32), + width: DevicePixels(1000), + height: DevicePixels(500), }, } // return some fake data so it doesn't panic } diff --git a/crates/gpui/src/platform/linux/wayland/window.rs b/crates/gpui/src/platform/linux/wayland/window.rs index 1fe2659704..cce210d4ca 100644 --- a/crates/gpui/src/platform/linux/wayland/window.rs +++ b/crates/gpui/src/platform/linux/wayland/window.rs @@ -22,7 +22,7 @@ use crate::platform::linux::wayland::display::WaylandDisplay; use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow}; use crate::scene::Scene; use crate::{ - px, size, Bounds, GlobalPixels, Modifiers, Pixels, PlatformDisplay, PlatformInput, Point, + px, size, Bounds, DevicePixels, Modifiers, Pixels, PlatformDisplay, PlatformInput, Point, PromptLevel, Size, WindowAppearance, WindowParams, }; @@ -274,7 +274,7 @@ impl HasDisplayHandle for WaylandWindow { impl PlatformWindow for WaylandWindow { // todo(linux) - fn bounds(&self) -> Bounds { + fn bounds(&self) -> Bounds { unimplemented!() } diff --git a/crates/gpui/src/platform/linux/x11/display.rs b/crates/gpui/src/platform/linux/x11/display.rs index b96fdb319b..f68b2194c6 100644 --- a/crates/gpui/src/platform/linux/x11/display.rs +++ b/crates/gpui/src/platform/linux/x11/display.rs @@ -2,12 +2,12 @@ use anyhow::Result; use uuid::Uuid; use x11rb::{connection::Connection as _, xcb_ffi::XCBConnection}; -use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Size}; +use crate::{Bounds, DevicePixels, DisplayId, PlatformDisplay, Size}; #[derive(Debug)] pub(crate) struct X11Display { x_screen_index: usize, - bounds: Bounds, + bounds: Bounds, uuid: Uuid, } @@ -19,8 +19,8 @@ impl X11Display { bounds: Bounds { origin: Default::default(), size: Size { - width: GlobalPixels(screen.width_in_pixels as f32), - height: GlobalPixels(screen.height_in_pixels as f32), + width: DevicePixels(screen.width_in_pixels as i32), + height: DevicePixels(screen.height_in_pixels as i32), }, }, uuid: Uuid::from_bytes([0; 16]), @@ -37,7 +37,7 @@ impl PlatformDisplay for X11Display { Ok(self.uuid) } - fn bounds(&self) -> Bounds { + fn bounds(&self) -> Bounds { self.bounds } } diff --git a/crates/gpui/src/platform/linux/x11/window.rs b/crates/gpui/src/platform/linux/x11/window.rs index a11b361195..84681d41d8 100644 --- a/crates/gpui/src/platform/linux/x11/window.rs +++ b/crates/gpui/src/platform/linux/x11/window.rs @@ -2,7 +2,7 @@ #![allow(unused)] use crate::{ - platform::blade::BladeRenderer, size, Bounds, GlobalPixels, Modifiers, Pixels, PlatformAtlas, + platform::blade::BladeRenderer, size, Bounds, DevicePixels, Modifiers, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PromptLevel, Scene, Size, WindowAppearance, WindowOptions, WindowParams, }; @@ -245,7 +245,7 @@ impl X11WindowState { x_window, callbacks: RefCell::new(Callbacks::default()), inner: RefCell::new(LinuxWindowInner { - bounds: params.bounds.map(|v| v.0 as i32), + bounds: params.bounds.map(|v| v.0), scale_factor: 1.0, renderer: BladeRenderer::new(gpu, gpu_extent), input_handler: None, @@ -325,12 +325,8 @@ impl X11WindowState { } impl PlatformWindow for X11Window { - fn bounds(&self) -> Bounds { - self.0 - .inner - .borrow_mut() - .bounds - .map(|v| GlobalPixels(v as f32)) + fn bounds(&self) -> Bounds { + self.0.inner.borrow_mut().bounds.map(|v| v.into()) } // todo(linux) diff --git a/crates/gpui/src/platform/mac.rs b/crates/gpui/src/platform/mac.rs index 15d10225ba..62cca0d3a8 100644 --- a/crates/gpui/src/platform/mac.rs +++ b/crates/gpui/src/platform/mac.rs @@ -22,7 +22,7 @@ mod text_system; mod window; mod window_appearance; -use crate::{px, size, GlobalPixels, Pixels, Size}; +use crate::{px, size, DevicePixels, Pixels, Size}; use cocoa::{ base::{id, nil}, foundation::{NSAutoreleasePool, NSNotFound, NSRect, NSSize, NSString, NSUInteger}, @@ -122,9 +122,9 @@ impl From for Size { } } -impl From for Size { +impl From for Size { fn from(rect: NSRect) -> Self { let NSSize { width, height } = rect.size; - size(width.into(), height.into()) + size(DevicePixels(width as i32), DevicePixels(height as i32)) } } diff --git a/crates/gpui/src/platform/mac/display.rs b/crates/gpui/src/platform/mac/display.rs index d5eb089300..ce3e5ef85c 100644 --- a/crates/gpui/src/platform/mac/display.rs +++ b/crates/gpui/src/platform/mac/display.rs @@ -1,9 +1,9 @@ -use crate::{point, size, Bounds, DisplayId, GlobalPixels, PlatformDisplay}; +use crate::{point, size, Bounds, DevicePixels, DisplayId, PlatformDisplay}; use anyhow::Result; use cocoa::{ appkit::NSScreen, base::{id, nil}, - foundation::{NSDictionary, NSPoint, NSRect, NSSize, NSString}, + foundation::{NSDictionary, NSString}, }; use core_foundation::uuid::{CFUUIDGetUUIDBytes, CFUUIDRef}; use core_graphics::display::{CGDirectDisplayID, CGDisplayBounds, CGGetActiveDisplayList}; @@ -69,49 +69,6 @@ extern "C" { fn CGDisplayCreateUUIDFromDisplayID(display: CGDirectDisplayID) -> CFUUIDRef; } -/// Convert the given rectangle from Cocoa's coordinate space to GPUI's coordinate space. -/// -/// Cocoa's coordinate space has its origin at the bottom left of the primary screen, -/// with the Y axis pointing upwards. -/// -/// Conversely, in GPUI's coordinate system, the origin is placed at the top left of the primary -/// screen, with the Y axis pointing downwards (matching CoreGraphics) -pub(crate) fn global_bounds_from_ns_rect(rect: NSRect) -> Bounds { - let primary_screen_size = unsafe { CGDisplayBounds(MacDisplay::primary().id().0) }.size; - - Bounds { - origin: point( - GlobalPixels(rect.origin.x as f32), - GlobalPixels( - primary_screen_size.height as f32 - rect.origin.y as f32 - rect.size.height as f32, - ), - ), - size: size( - GlobalPixels(rect.size.width as f32), - GlobalPixels(rect.size.height as f32), - ), - } -} - -/// Convert the given rectangle from GPUI's coordinate system to Cocoa's native coordinate space. -/// -/// Cocoa's coordinate space has its origin at the bottom left of the primary screen, -/// with the Y axis pointing upwards. -/// -/// Conversely, in GPUI's coordinate system, the origin is placed at the top left of the primary -/// screen, with the Y axis pointing downwards (matching CoreGraphics) -pub(crate) fn global_bounds_to_ns_rect(bounds: Bounds) -> NSRect { - let primary_screen_height = MacDisplay::primary().bounds().size.height; - - NSRect::new( - NSPoint::new( - bounds.origin.x.into(), - (primary_screen_height - bounds.origin.y - bounds.size.height).into(), - ), - NSSize::new(bounds.size.width.into(), bounds.size.height.into()), - ) -} - impl PlatformDisplay for MacDisplay { fn id(&self) -> DisplayId { DisplayId(self.0) @@ -145,20 +102,17 @@ impl PlatformDisplay for MacDisplay { ])) } - fn bounds(&self) -> Bounds { + fn bounds(&self) -> Bounds { unsafe { // CGDisplayBounds is in "global display" coordinates, where 0 is // the top left of the primary display. let bounds = CGDisplayBounds(self.0); Bounds { - origin: point( - GlobalPixels(bounds.origin.x as f32), - GlobalPixels(bounds.origin.y as f32), - ), + origin: point(DevicePixels(0), DevicePixels(0)), size: size( - GlobalPixels(bounds.size.width as f32), - GlobalPixels(bounds.size.height as f32), + DevicePixels(bounds.size.width as i32), + DevicePixels(bounds.size.height as i32), ), } } diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 41e550c587..e321b9b16c 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -1,10 +1,10 @@ -use super::{global_bounds_from_ns_rect, ns_string, renderer, MacDisplay, NSRange}; +use super::{ns_string, renderer, MacDisplay, NSRange}; use crate::{ - global_bounds_to_ns_rect, platform::PlatformInputHandler, point, px, size, AnyWindowHandle, - Bounds, DisplayLink, ExternalPaths, FileDropEvent, ForegroundExecutor, GlobalPixels, - KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, - MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, - PlatformWindow, Point, PromptLevel, Size, Timer, WindowAppearance, WindowKind, WindowParams, + platform::PlatformInputHandler, point, px, size, AnyWindowHandle, Bounds, DevicePixels, + DisplayLink, ExternalPaths, FileDropEvent, ForegroundExecutor, KeyDownEvent, Keystroke, + Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, + Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel, + Size, Timer, WindowAppearance, WindowKind, WindowParams, }; use block::ConcreteBlock; use cocoa::{ @@ -441,9 +441,28 @@ impl MacWindowState { } } - fn bounds(&self) -> Bounds { - let frame = unsafe { NSWindow::frame(self.native_window) }; - global_bounds_from_ns_rect(frame) + fn bounds(&self) -> Bounds { + let mut window_frame = unsafe { NSWindow::frame(self.native_window) }; + let screen_frame = unsafe { + let screen = NSWindow::screen(self.native_window); + NSScreen::frame(screen) + }; + + // Flip the y coordinate to be top-left origin + window_frame.origin.y = + screen_frame.size.height - window_frame.origin.y - window_frame.size.height; + + let bounds = Bounds::new( + point( + ((window_frame.origin.x - screen_frame.origin.x) as i32).into(), + ((window_frame.origin.y - screen_frame.origin.y) as i32).into(), + ), + size( + (window_frame.size.width as i32).into(), + (window_frame.size.height as i32).into(), + ), + ); + bounds } fn content_size(&self) -> Size { @@ -494,9 +513,9 @@ impl MacWindow { titlebar, kind, is_movable, - display_id, focus, show, + display_id, }: WindowParams, executor: ForegroundExecutor, renderer_context: renderer::Context, @@ -529,28 +548,37 @@ impl MacWindow { let display = display_id .and_then(MacDisplay::find_by_id) - .unwrap_or_else(MacDisplay::primary); + .unwrap_or_else(|| MacDisplay::primary()); let mut target_screen = nil; + let mut screen_frame = None; + let screens = NSScreen::screens(nil); let count: u64 = cocoa::foundation::NSArray::count(screens); for i in 0..count { let screen = cocoa::foundation::NSArray::objectAtIndex(screens, i); + let frame = NSScreen::visibleFrame(screen); let display_id = display_id_for_screen(screen); - if display_id == display.id().0 { + if display_id == display.0 { + screen_frame = Some(frame); target_screen = screen; - break; } } - let window_rect = { - let display_bounds = display.bounds(); - if bounds.intersects(&display_bounds) { - global_bounds_to_ns_rect(bounds) - } else { - global_bounds_to_ns_rect(display_bounds) - } - }; + let screen_frame = screen_frame.unwrap_or_else(|| { + let screen = NSScreen::mainScreen(nil); + target_screen = screen; + NSScreen::visibleFrame(screen) + }); + + let window_rect = NSRect::new( + NSPoint::new( + screen_frame.origin.x + bounds.origin.x.0 as f64, + screen_frame.origin.y + + (display.bounds().size.height - bounds.origin.y).0 as f64, + ), + NSSize::new(bounds.size.width.0 as f64, bounds.size.height.0 as f64), + ); let native_window = native_window.initWithContentRect_styleMask_backing_defer_screen_( window_rect, @@ -572,7 +600,10 @@ impl MacWindow { let window_size = { let scale = get_scale_factor(native_window); - size(bounds.size.width.0 * scale, bounds.size.height.0 * scale) + size( + bounds.size.width.0 as f32 * scale, + bounds.size.height.0 as f32 * scale, + ) }; let window = Self(Arc::new(Mutex::new(MacWindowState { @@ -692,6 +723,11 @@ impl MacWindow { native_window.orderFront_(nil); } + // Set the initial position of the window to the specified origin. + // Although we already specified the position using `initWithContentRect_styleMask_backing_defer_screen_`, + // the window position might be incorrect if the main screen (the screen that contains the window that has focus) + // is different from the primary screen. + NSWindow::setFrameTopLeftPoint_(native_window, window_rect.origin); window.0.lock().move_traffic_light(); pool.drain(); @@ -737,7 +773,7 @@ impl Drop for MacWindow { } impl PlatformWindow for MacWindow { - fn bounds(&self) -> Bounds { + fn bounds(&self) -> Bounds { self.0.as_ref().lock().bounds() } diff --git a/crates/gpui/src/platform/test/display.rs b/crates/gpui/src/platform/test/display.rs index 4887316070..4e2e9dbae6 100644 --- a/crates/gpui/src/platform/test/display.rs +++ b/crates/gpui/src/platform/test/display.rs @@ -1,12 +1,12 @@ use anyhow::{Ok, Result}; -use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Point}; +use crate::{Bounds, DevicePixels, DisplayId, PlatformDisplay, Point}; #[derive(Debug)] pub(crate) struct TestDisplay { id: DisplayId, uuid: uuid::Uuid, - bounds: Bounds, + bounds: Bounds, } impl TestDisplay { @@ -16,7 +16,7 @@ impl TestDisplay { uuid: uuid::Uuid::new_v4(), bounds: Bounds::from_corners( Point::default(), - Point::new(GlobalPixels(1920.), GlobalPixels(1080.)), + Point::new(DevicePixels(1920), DevicePixels(1080)), ), } } @@ -31,7 +31,7 @@ impl PlatformDisplay for TestDisplay { Ok(self.uuid) } - fn bounds(&self) -> crate::Bounds { + fn bounds(&self) -> crate::Bounds { self.bounds } } diff --git a/crates/gpui/src/platform/test/window.rs b/crates/gpui/src/platform/test/window.rs index 60bf3ec683..db85feba88 100644 --- a/crates/gpui/src/platform/test/window.rs +++ b/crates/gpui/src/platform/test/window.rs @@ -1,7 +1,8 @@ use crate::{ - AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DispatchEventResult, - GlobalPixels, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, - PlatformWindow, Point, Size, TestPlatform, TileId, WindowAppearance, WindowParams, + AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DevicePixels, + DispatchEventResult, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, + PlatformInputHandler, PlatformWindow, Point, Size, TestPlatform, TileId, WindowAppearance, + WindowParams, }; use collections::HashMap; use parking_lot::Mutex; @@ -12,7 +13,7 @@ use std::{ }; pub(crate) struct TestWindowState { - pub(crate) bounds: Bounds, + pub(crate) bounds: Bounds, pub(crate) handle: AnyWindowHandle, display: Rc, pub(crate) title: Option, @@ -78,7 +79,7 @@ impl TestWindow { let Some(mut callback) = lock.resize_callback.take() else { return; }; - lock.bounds.size = size.map(|pixels| f64::from(pixels).into()); + lock.bounds.size = size.map(|pixels| (pixels.0 as i32).into()); drop(lock); callback(size, scale_factor); self.0.lock().resize_callback = Some(callback); @@ -107,7 +108,7 @@ impl TestWindow { } impl PlatformWindow for TestWindow { - fn bounds(&self) -> Bounds { + fn bounds(&self) -> Bounds { self.0.lock().bounds } diff --git a/crates/gpui/src/platform/windows/display.rs b/crates/gpui/src/platform/windows/display.rs index a3784cf83c..c721d30587 100644 --- a/crates/gpui/src/platform/windows/display.rs +++ b/crates/gpui/src/platform/windows/display.rs @@ -7,13 +7,13 @@ use windows::{ Win32::{Foundation::*, Graphics::Gdi::*}, }; -use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Point, Size}; +use crate::{Bounds, DevicePixels, DisplayId, PlatformDisplay, Point, Size}; #[derive(Debug)] pub(crate) struct WindowsDisplay { pub handle: HMONITOR, pub display_id: DisplayId, - bounds: Bounds, + bounds: Bounds, uuid: Uuid, } @@ -33,12 +33,12 @@ impl WindowsDisplay { display_id, bounds: Bounds { origin: Point { - x: GlobalPixels(size.left as f32), - y: GlobalPixels(size.top as f32), + x: DevicePixels(size.left as i32), + y: DevicePixels(size.top as i32), }, size: Size { - width: GlobalPixels((size.right - size.left) as f32), - height: GlobalPixels((size.bottom - size.top) as f32), + width: DevicePixels((size.right - size.left) as i32), + height: DevicePixels((size.bottom - size.top) as i32), }, }, uuid, @@ -59,12 +59,12 @@ impl WindowsDisplay { display_id: DisplayId(display_id as _), bounds: Bounds { origin: Point { - x: GlobalPixels(size.left as f32), - y: GlobalPixels(size.top as f32), + x: DevicePixels(size.left as i32), + y: DevicePixels(size.top as i32), }, size: Size { - width: GlobalPixels((size.right - size.left) as f32), - height: GlobalPixels((size.bottom - size.top) as f32), + width: DevicePixels((size.right - size.left) as i32), + height: DevicePixels((size.bottom - size.top) as i32), }, }, uuid, @@ -81,12 +81,12 @@ impl WindowsDisplay { display_id, bounds: Bounds { origin: Point { - x: GlobalPixels(size.left as f32), - y: GlobalPixels(size.top as f32), + x: DevicePixels(size.left as i32), + y: DevicePixels(size.top as i32), }, size: Size { - width: GlobalPixels((size.right - size.left) as f32), - height: GlobalPixels((size.bottom - size.top) as f32), + width: DevicePixels((size.right - size.left) as i32), + height: DevicePixels((size.bottom - size.top) as i32), }, }, uuid, @@ -148,7 +148,7 @@ impl PlatformDisplay for WindowsDisplay { Ok(self.uuid) } - fn bounds(&self) -> Bounds { + fn bounds(&self) -> Bounds { self.bounds } } diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 430d8ab0ee..c46dcb87eb 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -42,8 +42,8 @@ use crate::*; pub(crate) struct WindowsWindowInner { hwnd: HWND, - origin: Cell>, - physical_size: Cell>, + origin: Cell>, + physical_size: Cell>, scale_factor: Cell, input_handler: Cell>, renderer: RefCell, @@ -68,12 +68,12 @@ impl WindowsWindowInner { ) -> Self { let monitor_dpi = unsafe { GetDpiForWindow(hwnd) } as f32; let origin = Cell::new(Point { - x: GlobalPixels(cs.x as f32), - y: GlobalPixels(cs.y as f32), + x: DevicePixels(cs.x as i32), + y: DevicePixels(cs.y as i32), }); let physical_size = Cell::new(Size { - width: GlobalPixels(cs.cx as f32), - height: GlobalPixels(cs.cy as f32), + width: DevicePixels(cs.cx as i32), + height: DevicePixels(cs.cy as i32), }); let scale_factor = Cell::new(monitor_dpi / USER_DEFAULT_SCREEN_DPI as f32); let input_handler = Cell::new(None); @@ -299,15 +299,15 @@ impl WindowsWindowInner { } fn handle_move_msg(&self, lparam: LPARAM) -> Option { - let x = lparam.signed_loword() as f32; - let y = lparam.signed_hiword() as f32; + let x = lparam.signed_loword() as i32; + let y = lparam.signed_hiword() as i32; self.origin.set(Point { - x: GlobalPixels(x), - y: GlobalPixels(y), + x: DevicePixels(x), + y: DevicePixels(y), }); let size = self.physical_size.get(); - let center_x = x + size.width.0 / 2.0; - let center_y = y + size.height.0 / 2.0; + let center_x = x + size.width.0 / 2; + let center_y = y + size.height.0 / 2; let monitor_bounds = self.display.borrow().bounds(); if center_x < monitor_bounds.left().0 || center_x > monitor_bounds.right().0 @@ -329,12 +329,12 @@ impl WindowsWindowInner { } fn handle_size_msg(&self, lparam: LPARAM) -> Option { - let width = lparam.loword().max(1) as f32; - let height = lparam.hiword().max(1) as f32; + let width = lparam.loword().max(1) as i32; + let height = lparam.hiword().max(1) as i32; let scale_factor = self.scale_factor.get(); let new_physical_size = Size { - width: GlobalPixels(width), - height: GlobalPixels(height), + width: DevicePixels(width), + height: DevicePixels(height), }; self.physical_size.set(new_physical_size); self.renderer.borrow_mut().update_drawable_size(Size { @@ -648,7 +648,7 @@ impl WindowsWindowInner { if let Some(callback) = callbacks.input.as_mut() { let x = lparam.signed_loword() as f32; let y = lparam.signed_hiword() as f32; - let physical_point = point(GlobalPixels(x), GlobalPixels(y)); + let physical_point = point(DevicePixels(x as i32), DevicePixels(y as i32)); let click_count = self.click_state.borrow_mut().update(button, physical_point); let scale_factor = self.scale_factor.get(); let event = MouseDownEvent { @@ -924,8 +924,8 @@ impl WindowsWindowInner { let height = size_rect.bottom - size_rect.top; self.physical_size.set(Size { - width: GlobalPixels(width as f32), - height: GlobalPixels(height as f32), + width: DevicePixels(width as i32), + height: DevicePixels(height as i32), }); if self.hide_title_bar { @@ -1077,8 +1077,8 @@ impl WindowsWindowInner { }; unsafe { ScreenToClient(self.hwnd, &mut cursor_point) }; let physical_point = point( - GlobalPixels(cursor_point.x as f32), - GlobalPixels(cursor_point.y as f32), + DevicePixels(cursor_point.x as i32), + DevicePixels(cursor_point.y as i32), ); let click_count = self.click_state.borrow_mut().update(button, physical_point); let scale_factor = self.scale_factor.get(); @@ -1305,7 +1305,7 @@ impl Drop for WindowsWindow { } impl PlatformWindow for WindowsWindow { - fn bounds(&self) -> Bounds { + fn bounds(&self) -> Bounds { Bounds { origin: self.inner.origin.get(), size: self.inner.physical_size.get(), @@ -1674,7 +1674,7 @@ impl IDropTarget_Impl for WindowsDragDropHandler { struct ClickState { button: MouseButton, last_click: Instant, - last_position: Point, + last_position: Point, current_count: usize, } @@ -1689,7 +1689,7 @@ impl ClickState { } /// update self and return the needed click count - pub fn update(&mut self, button: MouseButton, new_position: Point) -> usize { + pub fn update(&mut self, button: MouseButton, new_position: Point) -> usize { if self.button == button && self.is_double_click(new_position) { self.current_count += 1; } else { @@ -1703,7 +1703,7 @@ impl ClickState { } #[inline] - fn is_double_click(&self, new_position: Point) -> bool { + fn is_double_click(&self, new_position: Point) -> bool { let diff = self.last_position - new_position; self.last_click.elapsed() < DOUBLE_CLICK_INTERVAL @@ -1839,10 +1839,10 @@ fn oemkey_vkcode_to_string(code: u16) -> Option { } #[inline] -fn logical_size(physical_size: Size, scale_factor: f32) -> Size { +fn logical_size(physical_size: Size, scale_factor: f32) -> Size { Size { - width: px(physical_size.width.0 / scale_factor), - height: px(physical_size.height.0 / scale_factor), + width: px(physical_size.width.0 as f32 / scale_factor), + height: px(physical_size.height.0 as f32 / scale_factor), } } @@ -1867,51 +1867,36 @@ const DRAGDROP_GET_FILES_COUNT: u32 = 0xFFFFFFFF; // https://learn.microsoft.com/en-us/windows/win32/controls/ttm-setdelaytime?redirectedfrom=MSDN const DOUBLE_CLICK_INTERVAL: Duration = Duration::from_millis(500); // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics -const DOUBLE_CLICK_SPATIAL_TOLERANCE: f32 = 4.0; +const DOUBLE_CLICK_SPATIAL_TOLERANCE: i32 = 4; #[cfg(test)] mod tests { use super::ClickState; - use crate::{point, GlobalPixels, MouseButton}; + use crate::{point, DevicePixels, MouseButton}; use std::time::Duration; #[test] fn test_double_click_interval() { let mut state = ClickState::new(); assert_eq!( - state.update( - MouseButton::Left, - point(GlobalPixels(0.0), GlobalPixels(0.0)) - ), + state.update(MouseButton::Left, point(DevicePixels(0), DevicePixels(0))), 1 ); assert_eq!( - state.update( - MouseButton::Right, - point(GlobalPixels(0.0), GlobalPixels(0.0)) - ), + state.update(MouseButton::Right, point(DevicePixels(0), DevicePixels(0))), 1 ); assert_eq!( - state.update( - MouseButton::Left, - point(GlobalPixels(0.0), GlobalPixels(0.0)) - ), + state.update(MouseButton::Left, point(DevicePixels(0), DevicePixels(0))), 1 ); assert_eq!( - state.update( - MouseButton::Left, - point(GlobalPixels(0.0), GlobalPixels(0.0)) - ), + state.update(MouseButton::Left, point(DevicePixels(0), DevicePixels(0))), 2 ); state.last_click -= Duration::from_millis(700); assert_eq!( - state.update( - MouseButton::Left, - point(GlobalPixels(0.0), GlobalPixels(0.0)) - ), + state.update(MouseButton::Left, point(DevicePixels(0), DevicePixels(0))), 1 ); } @@ -1920,31 +1905,19 @@ mod tests { fn test_double_click_spatial_tolerance() { let mut state = ClickState::new(); assert_eq!( - state.update( - MouseButton::Left, - point(GlobalPixels(-3.0), GlobalPixels(0.0)) - ), + state.update(MouseButton::Left, point(DevicePixels(-3), DevicePixels(0))), 1 ); assert_eq!( - state.update( - MouseButton::Left, - point(GlobalPixels(0.0), GlobalPixels(3.0)) - ), + state.update(MouseButton::Left, point(DevicePixels(0), DevicePixels(3))), 2 ); assert_eq!( - state.update( - MouseButton::Right, - point(GlobalPixels(3.0), GlobalPixels(2.0)) - ), + state.update(MouseButton::Right, point(DevicePixels(3), DevicePixels(2))), 1 ); assert_eq!( - state.update( - MouseButton::Right, - point(GlobalPixels(10.0), GlobalPixels(0.0)) - ), + state.update(MouseButton::Right, point(DevicePixels(10), DevicePixels(0))), 1 ); } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 5baea12dc0..a30ef0e40f 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -1,13 +1,13 @@ use crate::{ point, px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, - AsyncWindowContext, Bounds, Context, Corners, CursorStyle, DispatchActionListener, - DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, - FileDropEvent, Flatten, Global, GlobalElementId, GlobalPixels, Hsla, KeyBinding, KeyDownEvent, - KeyMatch, KeymatchResult, Keystroke, KeystrokeEvent, Model, ModelContext, Modifiers, - ModifiersChangedEvent, MouseButton, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, - PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel, Render, ScaledPixels, - SharedString, Size, SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle, - TextStyleRefinement, View, VisualContext, WeakView, WindowAppearance, WindowOptions, + AsyncWindowContext, Bounds, Context, Corners, CursorStyle, DevicePixels, + DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, + EntityId, EventEmitter, FileDropEvent, Flatten, Global, GlobalElementId, Hsla, KeyBinding, + KeyDownEvent, KeyMatch, KeymatchResult, Keystroke, KeystrokeEvent, Model, ModelContext, + Modifiers, ModifiersChangedEvent, MouseButton, MouseMoveEvent, MouseUpEvent, Pixels, + PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel, Render, + ScaledPixels, SharedString, Size, SubscriberSet, Subscription, TaffyLayoutEngine, Task, + TextStyle, TextStyleRefinement, View, VisualContext, WeakView, WindowAppearance, WindowOptions, WindowParams, WindowTextSystem, }; use anyhow::{anyhow, Context as _, Result}; @@ -358,26 +358,27 @@ pub(crate) struct ElementStateBox { pub(crate) type_name: &'static str, } -fn default_bounds(cx: &mut AppContext) -> Bounds { - const DEFAULT_WINDOW_SIZE: Size = size(GlobalPixels(1024.0), GlobalPixels(700.0)); - const DEFAULT_WINDOW_OFFSET: Point = point(GlobalPixels(0.0), GlobalPixels(35.0)); +fn default_bounds(display_id: Option, cx: &mut AppContext) -> Bounds { + const DEFAULT_WINDOW_SIZE: Size = size(DevicePixels(1024), DevicePixels(700)); + const DEFAULT_WINDOW_OFFSET: Point = point(DevicePixels(0), DevicePixels(35)); cx.active_window() .and_then(|w| w.update(cx, |_, cx| cx.window_bounds()).ok()) .map(|bounds| bounds.map_origin(|origin| origin + DEFAULT_WINDOW_OFFSET)) .unwrap_or_else(|| { - cx.primary_display() + let display = display_id + .map(|id| cx.find_display(id)) + .unwrap_or_else(|| cx.primary_display()); + + display .map(|display| { let center = display.bounds().center(); - let offset = DEFAULT_WINDOW_SIZE / 2.0; + let offset = DEFAULT_WINDOW_SIZE / 2; let origin = point(center.x - offset.width, center.y - offset.height); Bounds::new(origin, DEFAULT_WINDOW_SIZE) }) .unwrap_or_else(|| { - Bounds::new( - point(GlobalPixels(0.0), GlobalPixels(0.0)), - DEFAULT_WINDOW_SIZE, - ) + Bounds::new(point(DevicePixels(0), DevicePixels(0)), DEFAULT_WINDOW_SIZE) }) }) } @@ -399,7 +400,7 @@ impl Window { fullscreen, } = options; - let bounds = bounds.unwrap_or_else(|| default_bounds(cx)); + let bounds = bounds.unwrap_or_else(|| default_bounds(display_id, cx)); let platform_window = cx.platform.open_window( handle, WindowParams { @@ -867,7 +868,7 @@ impl<'a> WindowContext<'a> { } /// Returns the bounds of the current window in the global coordinate space, which could span across multiple displays. - pub fn window_bounds(&self) -> Bounds { + pub fn window_bounds(&self) -> Bounds { self.window.platform_window.bounds() } diff --git a/crates/storybook/src/storybook.rs b/crates/storybook/src/storybook.rs index 3e9d89bfe8..77b0a38c3d 100644 --- a/crates/storybook/src/storybook.rs +++ b/crates/storybook/src/storybook.rs @@ -85,7 +85,7 @@ fn main() { cx.set_menus(app_menus()); let size = size(px(1500.), px(780.)); - let bounds = Bounds::centered(size, cx); + let bounds = Bounds::centered(None, size, cx); let _window = cx.open_window( WindowOptions { bounds: Some(bounds), diff --git a/crates/workspace/src/persistence.rs b/crates/workspace/src/persistence.rs index a49e7cb936..7cf764a5b4 100644 --- a/crates/workspace/src/persistence.rs +++ b/crates/workspace/src/persistence.rs @@ -59,7 +59,7 @@ impl sqlez::bindable::Column for SerializedAxis { } #[derive(Clone, Debug, PartialEq)] -pub(crate) struct SerializedWindowsBounds(pub(crate) Bounds); +pub(crate) struct SerializedWindowsBounds(pub(crate) Bounds); impl StaticColumnCount for SerializedWindowsBounds { fn column_count() -> usize { @@ -73,10 +73,10 @@ impl Bind for SerializedWindowsBounds { statement.bind( &( - SerializedGlobalPixels(self.0.origin.x), - SerializedGlobalPixels(self.0.origin.y), - SerializedGlobalPixels(self.0.size.width), - SerializedGlobalPixels(self.0.size.height), + SerializedDevicePixels(self.0.origin.x), + SerializedDevicePixels(self.0.origin.y), + SerializedDevicePixels(self.0.size.width), + SerializedDevicePixels(self.0.size.height), ), next_index, ) @@ -89,10 +89,10 @@ impl Column for SerializedWindowsBounds { let bounds = match window_state.as_str() { "Fixed" => { let ((x, y, width, height), _) = Column::column(statement, next_index)?; - let x: f64 = x; - let y: f64 = y; - let width: f64 = width; - let height: f64 = height; + let x: i32 = x; + let y: i32 = y; + let width: i32 = width; + let height: i32 = height; SerializedWindowsBounds(Bounds { origin: point(x.into(), y.into()), size: size(width.into(), height.into()), @@ -106,17 +106,16 @@ impl Column for SerializedWindowsBounds { } #[derive(Clone, Debug, PartialEq)] -struct SerializedGlobalPixels(gpui::GlobalPixels); -impl sqlez::bindable::StaticColumnCount for SerializedGlobalPixels {} +struct SerializedDevicePixels(gpui::DevicePixels); +impl sqlez::bindable::StaticColumnCount for SerializedDevicePixels {} -impl sqlez::bindable::Bind for SerializedGlobalPixels { +impl sqlez::bindable::Bind for SerializedDevicePixels { fn bind( &self, statement: &sqlez::statement::Statement, start_index: i32, ) -> anyhow::Result { - let this: f64 = self.0.into(); - let this: f32 = this as _; + let this: i32 = self.0.into(); this.bind(statement, start_index) } } diff --git a/crates/workspace/src/persistence/model.rs b/crates/workspace/src/persistence/model.rs index 6e5ce246c9..ac8fa76377 100644 --- a/crates/workspace/src/persistence/model.rs +++ b/crates/workspace/src/persistence/model.rs @@ -6,7 +6,7 @@ use db::sqlez::{ bindable::{Bind, Column, StaticColumnCount}, statement::Statement, }; -use gpui::{AsyncWindowContext, Bounds, GlobalPixels, Model, Task, View, WeakView}; +use gpui::{AsyncWindowContext, Bounds, DevicePixels, Model, Task, View, WeakView}; use project::Project; use std::{ path::{Path, PathBuf}, @@ -69,7 +69,7 @@ pub(crate) struct SerializedWorkspace { pub(crate) id: WorkspaceId, pub(crate) location: WorkspaceLocation, pub(crate) center_group: SerializedPaneGroup, - pub(crate) bounds: Option>, + pub(crate) bounds: Option>, pub(crate) fullscreen: bool, pub(crate) display: Option, pub(crate) docks: DockStructure, diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 89e7f379f4..937e9e2fa2 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -27,8 +27,8 @@ use futures::{ }; use gpui::{ actions, canvas, impl_actions, point, size, Action, AnyElement, AnyView, AnyWeakView, - AppContext, AsyncAppContext, AsyncWindowContext, Bounds, DragMoveEvent, Entity as _, EntityId, - EventEmitter, FocusHandle, FocusableView, Global, GlobalPixels, KeyContext, Keystroke, + AppContext, AsyncAppContext, AsyncWindowContext, Bounds, DevicePixels, DragMoveEvent, + Entity as _, EntityId, EventEmitter, FocusHandle, FocusableView, Global, KeyContext, Keystroke, LayoutId, ManagedView, Model, ModelContext, PathPromptOptions, Point, PromptLevel, Render, Size, Subscription, Task, View, WeakView, WindowHandle, WindowOptions, }; @@ -89,11 +89,11 @@ use crate::persistence::{ }; lazy_static! { - static ref ZED_WINDOW_SIZE: Option> = env::var("ZED_WINDOW_SIZE") + static ref ZED_WINDOW_SIZE: Option> = env::var("ZED_WINDOW_SIZE") .ok() .as_deref() .and_then(parse_pixel_size_env_var); - static ref ZED_WINDOW_POSITION: Option> = env::var("ZED_WINDOW_POSITION") + static ref ZED_WINDOW_POSITION: Option> = env::var("ZED_WINDOW_POSITION") .ok() .as_deref() .and_then(parse_pixel_position_env_var); @@ -745,11 +745,7 @@ impl Workspace { cx.observe_window_activation(Self::on_window_activation_changed), cx.observe_window_bounds(move |_, cx| { if let Some(display) = cx.display() { - // Transform fixed bounds to be stored in terms of the containing display - let mut window_bounds = cx.window_bounds(); - let display_bounds = display.bounds(); - window_bounds.origin.x -= display_bounds.origin.x; - window_bounds.origin.y -= display_bounds.origin.y; + let window_bounds = cx.window_bounds(); let fullscreen = cx.is_fullscreen(); if let Some(display_uuid) = display.uuid().log_err() { @@ -902,7 +898,7 @@ impl Workspace { })?; window } else { - let window_bounds_override = window_bounds_env_override(&cx); + let window_bounds_override = window_bounds_env_override(); let (bounds, display, fullscreen) = if let Some(bounds) = window_bounds_override { (Some(bounds), None, false) @@ -917,24 +913,7 @@ impl Workspace { Some((display?, bounds?.0, fullscreen.unwrap_or(false))) }); - if let Some((serialized_display, mut bounds, fullscreen)) = restorable_bounds { - // Stored bounds are relative to the containing display. - // So convert back to global coordinates if that screen still exists - let screen_bounds = cx - .update(|cx| { - cx.displays() - .into_iter() - .find(|display| display.uuid().ok() == Some(serialized_display)) - }) - .ok() - .flatten() - .map(|screen| screen.bounds()); - - if let Some(screen_bounds) = screen_bounds { - bounds.origin.x += screen_bounds.origin.x; - bounds.origin.y += screen_bounds.origin.y; - } - + if let Some((serialized_display, bounds, fullscreen)) = restorable_bounds { (Some(bounds), Some(serialized_display), fullscreen) } else { (None, None, false) @@ -3756,14 +3735,11 @@ impl Workspace { } } -fn window_bounds_env_override(cx: &AsyncAppContext) -> Option> { - let display_origin = cx - .update(|cx| Some(cx.displays().first()?.bounds().origin)) - .ok()??; +fn window_bounds_env_override() -> Option> { ZED_WINDOW_POSITION .zip(*ZED_WINDOW_SIZE) .map(|(position, size)| Bounds { - origin: display_origin + position, + origin: position, size, }) } @@ -4662,7 +4638,7 @@ pub fn join_hosted_project( ) .await?; - let window_bounds_override = window_bounds_env_override(&cx); + let window_bounds_override = window_bounds_env_override(); cx.update(|cx| { let mut options = (app_state.build_window_options)(None, cx); options.bounds = window_bounds_override; @@ -4723,7 +4699,7 @@ pub fn join_in_room_project( })? .await?; - let window_bounds_override = window_bounds_env_override(&cx); + let window_bounds_override = window_bounds_env_override(); cx.update(|cx| { let mut options = (app_state.build_window_options)(None, cx); options.bounds = window_bounds_override; @@ -4817,18 +4793,18 @@ pub fn restart(_: &Restart, cx: &mut AppContext) { .detach_and_log_err(cx); } -fn parse_pixel_position_env_var(value: &str) -> Option> { +fn parse_pixel_position_env_var(value: &str) -> Option> { let mut parts = value.split(','); let x: usize = parts.next()?.parse().ok()?; let y: usize = parts.next()?.parse().ok()?; - Some(point((x as f64).into(), (y as f64).into())) + Some(point((x as i32).into(), (y as i32).into())) } -fn parse_pixel_size_env_var(value: &str) -> Option> { +fn parse_pixel_size_env_var(value: &str) -> Option> { let mut parts = value.split(','); let width: usize = parts.next()?.parse().ok()?; let height: usize = parts.next()?.parse().ok()?; - Some(size((width as f64).into(), (height as f64).into())) + Some(size((width as i32).into(), (height as i32).into())) } struct DisconnectedOverlay;