Detailed changes
@@ -265,7 +265,7 @@ impl TestServer {
workspace_store,
languages: Arc::new(language_registry),
fs: fs.clone(),
- build_window_options: |_, _, _| Default::default(),
+ build_window_options: |_, _| Default::default(),
node_runtime: FakeNodeRuntime::new(),
});
@@ -6,7 +6,6 @@ use gpui::{
actions, canvas, div, point, px, Action, AnyElement, AppContext, Element, Hsla,
InteractiveElement, IntoElement, Model, ParentElement, Path, Render,
StatefulInteractiveElement, Styled, Subscription, View, ViewContext, VisualContext, WeakView,
- WindowBounds,
};
use project::{Project, RepositoryEntry};
use recent_projects::RecentProjects;
@@ -65,7 +64,7 @@ impl Render for CollabTitlebarItem {
.w_full()
.h(titlebar_height(cx))
.map(|this| {
- if matches!(cx.window_bounds(), WindowBounds::Fullscreen) {
+ if cx.is_full_screen() {
this.pl_2()
} else {
// Use pixels here instead of a rem-based size because the macOS traffic
@@ -13,8 +13,8 @@ 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, WindowBounds,
- WindowContext, WindowKind, WindowOptions,
+ actions, point, AppContext, GlobalPixels, Pixels, PlatformDisplay, Size, Task, WindowContext,
+ WindowKind, WindowOptions,
};
use panel_settings::MessageEditorSettings;
pub use panel_settings::{
@@ -111,14 +111,15 @@ fn notification_window_options(
),
size: window_size.into(),
};
+
WindowOptions {
- bounds: WindowBounds::Fixed(bounds),
+ bounds: Some(bounds),
titlebar: None,
- center: false,
focus: false,
show: true,
kind: WindowKind::PopUp,
is_movable: false,
display_id: Some(screen.id()),
+ fullscreen: false,
}
}
@@ -9,7 +9,7 @@ use crate::{
};
use futures::StreamExt;
-use gpui::{div, TestAppContext, VisualTestContext, WindowBounds, WindowOptions};
+use gpui::{div, TestAppContext, VisualTestContext, WindowOptions};
use indoc::indoc;
use language::{
language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
@@ -6873,7 +6873,7 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
let follower = cx.update(|cx| {
cx.open_window(
WindowOptions {
- bounds: WindowBounds::Fixed(Bounds::from_corners(
+ bounds: Some(Bounds::from_corners(
gpui::Point::new(0_f64.into(), 0_f64.into()),
gpui::Point::new(10_f64.into(), 80_f64.into()),
)),
@@ -2664,6 +2664,7 @@ pub mod tests {
cx.executor().run_until_parked();
let editor =
cx.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx));
+
let editor_edited = Arc::new(AtomicBool::new(false));
let fake_server = fake_servers.next().await.unwrap();
let closure_editor_edited = Arc::clone(&editor_edited);
@@ -23,18 +23,17 @@ impl Render for HelloWorld {
fn main() {
App::new().run(|cx: &mut AppContext| {
- let options = WindowOptions {
- bounds: WindowBounds::Fixed(Bounds {
- size: size(px(600.0), px(600.0)).into(),
- origin: Default::default(),
- }),
- center: true,
- ..Default::default()
- };
- cx.open_window(options, |cx| {
- cx.new_view(|_cx| HelloWorld {
- text: "World".into(),
- })
- });
+ let bounds = Bounds::centered(size(px(600.0), px(600.0)), cx);
+ cx.open_window(
+ WindowOptions {
+ bounds: Some(bounds),
+ ..Default::default()
+ },
+ |cx| {
+ cx.new_view(|_cx| HelloWorld {
+ text: "World".into(),
+ })
+ },
+ );
});
}
@@ -520,6 +520,11 @@ impl AppContext {
self.platform.displays()
}
+ /// Returns the primary display that will be used for new windows.
+ pub fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
+ self.platform.primary_display()
+ }
+
/// Returns the appearance of the application's windows.
pub fn window_appearance(&self) -> WindowAppearance {
self.platform.window_appearance()
@@ -171,13 +171,29 @@ impl TestAppContext {
V: 'static + Render,
{
let mut cx = self.app.borrow_mut();
- cx.open_window(WindowOptions::default(), |cx| cx.new_view(build_window))
+
+ // Some tests rely on the window size matching the bounds of the test display
+ let bounds = Bounds::maximized(&mut cx);
+ cx.open_window(
+ WindowOptions {
+ bounds: Some(bounds),
+ ..Default::default()
+ },
+ |cx| cx.new_view(build_window),
+ )
}
/// Adds a new window with no content.
pub fn add_empty_window(&mut self) -> &mut VisualTestContext {
let mut cx = self.app.borrow_mut();
- let window = cx.open_window(WindowOptions::default(), |cx| cx.new_view(|_| Empty));
+ let bounds = Bounds::maximized(&mut cx);
+ let window = cx.open_window(
+ WindowOptions {
+ bounds: Some(bounds),
+ ..Default::default()
+ },
+ |cx| cx.new_view(|_| Empty),
+ );
drop(cx);
let cx = VisualTestContext::from_window(*window.deref(), self).as_mut();
cx.run_until_parked();
@@ -193,7 +209,14 @@ impl TestAppContext {
V: 'static + Render,
{
let mut cx = self.app.borrow_mut();
- let window = cx.open_window(WindowOptions::default(), |cx| cx.new_view(build_window));
+ let bounds = Bounds::maximized(&mut cx);
+ let window = cx.open_window(
+ WindowOptions {
+ bounds: Some(bounds),
+ ..Default::default()
+ },
+ |cx| cx.new_view(build_window),
+ );
drop(cx);
let view = window.root_view(self).unwrap();
let cx = VisualTestContext::from_window(*window.deref(), self).as_mut();
@@ -9,9 +9,12 @@ use serde_derive::{Deserialize, Serialize};
use std::{
cmp::{self, PartialOrd},
fmt,
+ hash::Hash,
ops::{Add, Div, Mul, MulAssign, Sub},
};
+use crate::AppContext;
+
/// An axis along which a measurement can be made.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Axis {
@@ -84,7 +87,7 @@ pub struct Point<T: Default + Clone + Debug> {
/// assert_eq!(p.x, 10);
/// assert_eq!(p.y, 20);
/// ```
-pub fn point<T: Clone + Debug + Default>(x: T, y: T) -> Point<T> {
+pub const fn point<T: Clone + Debug + Default>(x: T, y: T) -> Point<T> {
Point { x, y }
}
@@ -354,6 +357,15 @@ pub struct Size<T: Clone + Default + Debug> {
pub height: T,
}
+impl From<Size<GlobalPixels>> for Size<Pixels> {
+ fn from(size: Size<GlobalPixels>) -> Self {
+ Size {
+ width: Pixels(size.width.0),
+ height: Pixels(size.height.0),
+ }
+ }
+}
+
/// Constructs a new `Size<T>` with the provided width and height.
///
/// # Arguments
@@ -369,7 +381,7 @@ pub struct Size<T: Clone + Default + Debug> {
/// assert_eq!(my_size.width, 10);
/// assert_eq!(my_size.height, 20);
/// ```
-pub fn size<T>(width: T, height: T) -> Size<T>
+pub const fn size<T>(width: T, height: T) -> Size<T>
where
T: Clone + Default + Debug,
{
@@ -662,6 +674,35 @@ pub struct Bounds<T: Clone + Default + Debug> {
pub size: Size<T>,
}
+impl Bounds<GlobalPixels> {
+ /// Generate a centered bounds for the primary display
+ pub fn centered(size: impl Into<Size<GlobalPixels>>, cx: &mut AppContext) -> Self {
+ let size = size.into();
+ cx.primary_display()
+ .map(|display| {
+ let center = display.bounds().center();
+ Bounds {
+ origin: point(center.x - size.width / 2.0, center.y - size.height / 2.0),
+ size,
+ }
+ })
+ .unwrap_or_else(|| Bounds {
+ origin: point(GlobalPixels(0.0), GlobalPixels(0.0)),
+ size,
+ })
+ }
+
+ /// Generate maximized bounds for the primary display
+ pub fn maximized(cx: &mut AppContext) -> Self {
+ cx.primary_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)),
+ })
+ }
+}
+
impl<T> Bounds<T>
where
T: Clone + Debug + Sub<Output = T> + Default,
@@ -1165,6 +1206,29 @@ where
size: self.size.map(f),
}
}
+
+ /// Applies a function to the origin of the bounds, producing a new `Bounds` with the new origin
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use zed::{Bounds, Point, Size};
+ /// let bounds = Bounds {
+ /// origin: Point { x: 10.0, y: 10.0 },
+ /// size: Size { width: 10.0, height: 20.0 },
+ /// };
+ /// let new_bounds = bounds.map_origin(|value| value * 1.5);
+ ///
+ /// assert_eq!(new_bounds, Bounds {
+ /// origin: Point { x: 15.0, y: 15.0 },
+ /// size: Size { width: 10.0, height: 20.0 },
+ /// });
+ pub fn map_origin(self, f: impl Fn(Point<T>) -> Point<T>) -> Bounds<T> {
+ Bounds {
+ origin: f(self.origin),
+ size: self.size,
+ }
+ }
}
/// Checks if the bounds represent an empty area.
@@ -436,6 +436,7 @@ impl PlatformInput {
#[cfg(test)]
mod test {
+
use crate::{
self as gpui, div, Element, FocusHandle, InteractiveElement, IntoElement, KeyBinding,
Keystroke, ParentElement, Render, TestAppContext, VisualContext,
@@ -88,12 +88,13 @@ pub(crate) trait Platform: 'static {
fn unhide_other_apps(&self);
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>>;
+ fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>>;
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>>;
fn active_window(&self) -> Option<AnyWindowHandle>;
fn open_window(
&self,
handle: AnyWindowHandle,
- options: WindowOptions,
+ options: WindowParams,
) -> Box<dyn PlatformWindow>;
/// Returns the appearance of the application's windows.
@@ -166,7 +167,7 @@ impl Debug for DisplayId {
unsafe impl Send for DisplayId {}
pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
- fn bounds(&self) -> WindowBounds;
+ fn bounds(&self) -> Bounds<GlobalPixels>;
fn content_size(&self) -> Size<Pixels>;
fn scale_factor(&self) -> f32;
fn titlebar_height(&self) -> Pixels;
@@ -191,6 +192,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
fn minimize(&self);
fn zoom(&self);
fn toggle_full_screen(&self);
+ fn is_full_screen(&self) -> bool;
fn on_request_frame(&self, callback: Box<dyn FnMut()>);
fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> bool>);
fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>);
@@ -501,21 +503,21 @@ pub trait InputHandler: 'static {
/// The variables that can be configured when creating a new window
#[derive(Debug)]
pub struct WindowOptions {
- /// The initial bounds of the window
- pub bounds: WindowBounds,
+ /// None -> inherit, Some(bounds) -> set bounds
+ pub bounds: Option<Bounds<GlobalPixels>>,
/// The titlebar configuration of the window
pub titlebar: Option<TitlebarOptions>,
- /// Whether the window should be centered on the screen
- pub center: bool,
-
/// Whether the window should be focused when created
pub focus: bool,
/// Whether the window should be shown when created
pub show: bool,
+ /// Whether the window should be fullscreen when created
+ pub fullscreen: bool,
+
/// The kind of window to create
pub kind: WindowKind,
@@ -526,21 +528,44 @@ pub struct WindowOptions {
pub display_id: Option<DisplayId>,
}
+/// The variables that can be configured when creating a new window
+#[derive(Debug)]
+pub(crate) struct WindowParams {
+ ///
+ pub bounds: Bounds<GlobalPixels>,
+
+ /// The titlebar configuration of the window
+ pub titlebar: Option<TitlebarOptions>,
+
+ /// The kind of window to create
+ pub kind: WindowKind,
+
+ /// Whether the window should be movable by the user
+ pub is_movable: bool,
+
+ pub focus: bool,
+
+ pub show: bool,
+
+ /// The display to create the window on
+ pub display_id: Option<DisplayId>,
+}
+
impl Default for WindowOptions {
fn default() -> Self {
Self {
- bounds: WindowBounds::default(),
+ bounds: None,
titlebar: Some(TitlebarOptions {
title: Default::default(),
appears_transparent: Default::default(),
traffic_light_position: Default::default(),
}),
- center: false,
focus: true,
show: true,
kind: WindowKind::Normal,
is_movable: true,
display_id: None,
+ fullscreen: false,
}
}
}
@@ -569,19 +594,9 @@ pub enum WindowKind {
PopUp,
}
-/// Which bounds algorithm to use for the initial size a window
-#[derive(Copy, Clone, Debug, PartialEq, Default)]
-pub enum WindowBounds {
- /// The window should be full screen, on macOS this corresponds to the full screen feature
- Fullscreen,
-
- /// Make the window as large as the current display's size.
- #[default]
- Maximized,
-
- /// Set the window to the given size in pixels
- Fixed(Bounds<GlobalPixels>),
-}
+/// Platform level interface
+/// bounds: Bounds<GlobalPixels>
+/// full_screen: bool
/// The appearance of the window, as defined by the operating system.
///
@@ -4,15 +4,16 @@ use std::rc::Rc;
use copypasta::ClipboardProvider;
use crate::platform::PlatformWindow;
-use crate::{AnyWindowHandle, CursorStyle, DisplayId, PlatformDisplay, WindowOptions};
+use crate::{AnyWindowHandle, CursorStyle, DisplayId, PlatformDisplay, WindowParams};
pub trait Client {
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>>;
+ fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>>;
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>>;
fn open_window(
&self,
handle: AnyWindowHandle,
- options: WindowOptions,
+ options: WindowParams,
) -> Box<dyn PlatformWindow>;
fn set_cursor_style(&self, style: CursorStyle);
fn get_clipboard(&self) -> Rc<RefCell<dyn ClipboardProvider>>;
@@ -25,7 +25,7 @@ use crate::{
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
ForegroundExecutor, Keymap, LinuxDispatcher, LinuxTextSystem, Menu, PathPromptOptions,
Platform, PlatformDisplay, PlatformInput, PlatformTextSystem, PlatformWindow, Result,
- SemanticVersion, Task, WindowOptions,
+ SemanticVersion, Task, WindowOptions, WindowParams,
};
use super::x11::X11Client;
@@ -156,6 +156,10 @@ impl Platform for LinuxPlatform {
// todo(linux)
fn unhide_other_apps(&self) {}
+ fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
+ self.client.primary_display()
+ }
+
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
self.client.displays()
}
@@ -172,7 +176,7 @@ impl Platform for LinuxPlatform {
fn open_window(
&self,
handle: AnyWindowHandle,
- options: WindowOptions,
+ options: WindowParams,
) -> Box<dyn PlatformWindow> {
self.client.open_window(handle, options)
}
@@ -39,11 +39,12 @@ use crate::platform::linux::client::Client;
use crate::platform::linux::wayland::cursor::Cursor;
use crate::platform::linux::wayland::window::{WaylandDecorationState, WaylandWindow};
use crate::platform::{LinuxPlatformInner, PlatformWindow};
+use crate::WindowParams;
use crate::{
platform::linux::wayland::window::WaylandWindowState, AnyWindowHandle, CursorStyle, DisplayId,
KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
MouseDownEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay,
- PlatformInput, Point, ScrollDelta, ScrollWheelEvent, TouchPhase, WindowOptions,
+ PlatformInput, Point, ScrollDelta, ScrollWheelEvent, TouchPhase,
};
/// Used to convert evdev scancode to xkb scancode
@@ -207,10 +208,14 @@ impl Client for WaylandClient {
unimplemented!()
}
+ fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
+ None
+ }
+
fn open_window(
&self,
handle: AnyWindowHandle,
- options: WindowOptions,
+ options: WindowParams,
) -> Box<dyn PlatformWindow> {
let mut state = self.state.client_state_inner.borrow_mut();
@@ -22,8 +22,8 @@ use crate::platform::linux::wayland::display::WaylandDisplay;
use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow};
use crate::scene::Scene;
use crate::{
- px, size, Bounds, Modifiers, Pixels, PlatformDisplay, PlatformInput, Point, PromptLevel, Size,
- WindowAppearance, WindowBounds, WindowOptions,
+ px, size, Bounds, GlobalPixels, Modifiers, Pixels, PlatformDisplay, PlatformInput, Point,
+ PromptLevel, Size, WindowAppearance, WindowParams,
};
#[derive(Default)]
@@ -125,24 +125,9 @@ impl WaylandWindowState {
wl_surf: Arc<wl_surface::WlSurface>,
viewport: Option<wp_viewport::WpViewport>,
toplevel: Arc<xdg_toplevel::XdgToplevel>,
- options: WindowOptions,
+ options: WindowParams,
) -> Self {
- if options.bounds == WindowBounds::Maximized {
- toplevel.set_maximized();
- } else if options.bounds == WindowBounds::Fullscreen {
- toplevel.set_fullscreen(None);
- }
-
- let bounds: Bounds<u32> = match options.bounds {
- WindowBounds::Fullscreen | WindowBounds::Maximized => Bounds {
- origin: Point::default(),
- size: Size {
- width: 500,
- height: 500,
- }, // todo(implement)
- },
- WindowBounds::Fixed(bounds) => bounds.map(|p| p.0 as u32),
- };
+ let bounds = options.bounds.map(|p| p.0 as u32);
Self {
surface: Arc::clone(&wl_surf),
@@ -290,8 +275,8 @@ impl HasDisplayHandle for WaylandWindow {
impl PlatformWindow for WaylandWindow {
// todo(linux)
- fn bounds(&self) -> WindowBounds {
- WindowBounds::Maximized
+ fn bounds(&self) -> Bounds<GlobalPixels> {
+ unimplemented!()
}
fn content_size(&self) -> Size<Pixels> {
@@ -331,9 +316,8 @@ impl PlatformWindow for WaylandWindow {
crate::Modifiers::default()
}
- // todo(linux)
fn as_any_mut(&mut self) -> &mut dyn Any {
- unimplemented!()
+ self
}
fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
@@ -379,13 +363,17 @@ impl PlatformWindow for WaylandWindow {
}
fn toggle_full_screen(&self) {
- if !self.0.inner.borrow_mut().fullscreen {
+ if !self.0.inner.borrow().fullscreen {
self.0.toplevel.set_fullscreen(None);
} else {
self.0.toplevel.unset_fullscreen();
}
}
+ fn is_full_screen(&self) -> bool {
+ self.0.inner.borrow_mut().fullscreen
+ }
+
fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
self.0.callbacks.borrow_mut().request_frame = Some(callback);
}
@@ -13,7 +13,7 @@ use crate::platform::linux::client::Client;
use crate::platform::{LinuxPlatformInner, PlatformWindow};
use crate::{
AnyWindowHandle, Bounds, CursorStyle, DisplayId, PlatformDisplay, PlatformInput, Point,
- ScrollDelta, Size, TouchPhase, WindowOptions,
+ ScrollDelta, Size, TouchPhase,
};
use super::{X11Display, X11Window, X11WindowState, XcbAtoms};
@@ -284,26 +284,35 @@ impl Client for X11Client {
setup
.roots()
.enumerate()
- .map(|(root_id, _)| {
- Rc::new(X11Display::new(&self.xcb_connection, root_id as i32))
- as Rc<dyn PlatformDisplay>
+ .filter_map(|(root_id, _)| {
+ Some(
+ Rc::new(X11Display::new(&self.xcb_connection, root_id as i32)?)
+ as Rc<dyn PlatformDisplay>,
+ )
})
.collect()
}
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
- Some(Rc::new(X11Display::new(&self.xcb_connection, id.0 as i32)))
+ Some(Rc::new(X11Display::new(&self.xcb_connection, id.0 as i32)?))
+ }
+
+ fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
+ Some(Rc::new(
+ X11Display::new(&self.xcb_connection, self.x_root_index)
+ .expect("There should always be a root index"),
+ ))
}
fn open_window(
&self,
_handle: AnyWindowHandle,
- options: WindowOptions,
+ params: crate::WindowParams,
) -> Box<dyn PlatformWindow> {
let x_window = self.xcb_connection.generate_id();
let window_ptr = Rc::new(X11WindowState::new(
- options,
+ params,
&self.xcb_connection,
self.x_root_index,
x_window,
@@ -11,9 +11,9 @@ pub(crate) struct X11Display {
}
impl X11Display {
- pub(crate) fn new(xc: &xcb::Connection, x_screen_index: i32) -> Self {
- let screen = xc.get_setup().roots().nth(x_screen_index as usize).unwrap();
- Self {
+ pub(crate) fn new(xc: &xcb::Connection, x_screen_index: i32) -> Option<Self> {
+ let screen = xc.get_setup().roots().nth(x_screen_index as usize)?;
+ Some(Self {
x_screen_index,
bounds: Bounds {
origin: Default::default(),
@@ -23,7 +23,7 @@ impl X11Display {
},
},
uuid: Uuid::from_bytes([0; 16]),
- }
+ })
}
}
@@ -4,7 +4,7 @@
use crate::{
platform::blade::BladeRenderer, size, Bounds, GlobalPixels, Modifiers, Pixels, PlatformAtlas,
PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PromptLevel,
- Scene, Size, WindowAppearance, WindowBounds, WindowOptions,
+ Scene, Size, WindowAppearance, WindowOptions, WindowParams,
};
use blade_graphics as gpu;
use parking_lot::Mutex;
@@ -138,13 +138,13 @@ impl rwh::HasDisplayHandle for X11Window {
impl X11WindowState {
pub fn new(
- options: WindowOptions,
+ params: WindowParams,
xcb_connection: &Rc<xcb::Connection>,
x_main_screen_index: i32,
x_window: x::Window,
atoms: &XcbAtoms,
) -> Self {
- let x_screen_index = options
+ let x_screen_index = params
.display_id
.map_or(x_main_screen_index, |did| did.0 as i32);
let screen = xcb_connection
@@ -175,32 +175,21 @@ impl X11WindowState {
),
];
- let bounds = match options.bounds {
- WindowBounds::Fullscreen | WindowBounds::Maximized => Bounds {
- origin: Point::default(),
- size: Size {
- width: screen.width_in_pixels() as i32,
- height: screen.height_in_pixels() as i32,
- },
- },
- WindowBounds::Fixed(bounds) => bounds.map(|p| p.0 as i32),
- };
-
xcb_connection.send_request(&x::CreateWindow {
depth: x::COPY_FROM_PARENT as u8,
wid: x_window,
parent: screen.root(),
- x: bounds.origin.x as i16,
- y: bounds.origin.y as i16,
- width: bounds.size.width as u16,
- height: bounds.size.height as u16,
+ x: params.bounds.origin.x.0 as i16,
+ y: params.bounds.origin.y.0 as i16,
+ width: params.bounds.size.width.0 as u16,
+ height: params.bounds.size.height.0 as u16,
border_width: 0,
class: x::WindowClass::InputOutput,
visual: screen.root_visual(),
value_list: &xcb_values,
});
- if let Some(titlebar) = options.titlebar {
+ if let Some(titlebar) = params.titlebar {
if let Some(title) = titlebar.title {
xcb_connection.send_request(&x::ChangeProperty {
mode: x::PropMode::Replace,
@@ -250,12 +239,12 @@ impl X11WindowState {
Self {
xcb_connection: xcb_connection.clone(),
- display: Rc::new(X11Display::new(xcb_connection, x_screen_index)),
+ display: Rc::new(X11Display::new(xcb_connection, x_screen_index).unwrap()),
raw,
x_window,
callbacks: RefCell::new(Callbacks::default()),
inner: RefCell::new(LinuxWindowInner {
- bounds,
+ bounds: params.bounds.map(|v| v.0 as i32),
scale_factor: 1.0,
renderer: BladeRenderer::new(gpu, gpu_extent),
input_handler: None,
@@ -339,14 +328,12 @@ impl X11WindowState {
}
impl PlatformWindow for X11Window {
- fn bounds(&self) -> WindowBounds {
- WindowBounds::Fixed(
- self.0
- .inner
- .borrow_mut()
- .bounds
- .map(|v| GlobalPixels(v as f32)),
- )
+ fn bounds(&self) -> Bounds<GlobalPixels> {
+ self.0
+ .inner
+ .borrow_mut()
+ .bounds
+ .map(|v| GlobalPixels(v as f32))
}
fn content_size(&self) -> Size<Pixels> {
@@ -454,6 +441,11 @@ impl PlatformWindow for X11Window {
unimplemented!()
}
+ // todo(linux)
+ fn is_full_screen(&self) -> bool {
+ unimplemented!()
+ }
+
fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
self.0.callbacks.borrow_mut().request_frame = Some(callback);
}
@@ -3,7 +3,7 @@ use crate::{
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
ForegroundExecutor, Keymap, MacDispatcher, MacDisplay, MacTextSystem, MacWindow, Menu,
MenuItem, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, PlatformTextSystem,
- PlatformWindow, Result, SemanticVersion, Task, WindowAppearance, WindowOptions,
+ PlatformWindow, Result, SemanticVersion, Task, WindowAppearance, WindowParams,
};
use anyhow::{anyhow, bail};
use block::ConcreteBlock;
@@ -477,6 +477,10 @@ impl Platform for MacPlatform {
}
}
+ fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
+ Some(Rc::new(MacDisplay::primary()))
+ }
+
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
MacDisplay::all()
.map(|screen| Rc::new(screen) as Rc<_>)
@@ -494,7 +498,7 @@ impl Platform for MacPlatform {
fn open_window(
&self,
handle: AnyWindowHandle,
- options: WindowOptions,
+ options: WindowParams,
) -> Box<dyn PlatformWindow> {
// Clippy thinks that this evaluates to `()`, for some reason.
#[allow(clippy::unit_arg, clippy::clone_on_copy)]
@@ -4,8 +4,7 @@ use crate::{
Bounds, DisplayLink, ExternalPaths, FileDropEvent, ForegroundExecutor, GlobalPixels,
KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent,
MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
- PlatformWindow, Point, PromptLevel, Size, Timer, WindowAppearance, WindowBounds, WindowKind,
- WindowOptions,
+ PlatformWindow, Point, PromptLevel, Size, Timer, WindowAppearance, WindowKind, WindowParams,
};
use block::ConcreteBlock;
use cocoa::{
@@ -419,23 +418,7 @@ impl MacWindowState {
}
}
- fn bounds(&self) -> WindowBounds {
- unsafe {
- if self.is_fullscreen() {
- return WindowBounds::Fullscreen;
- }
-
- let frame = self.frame();
- let screen_size = self.native_window.screen().visibleFrame().into();
- if frame.size == screen_size {
- WindowBounds::Maximized
- } else {
- WindowBounds::Fixed(frame)
- }
- }
- }
-
- fn frame(&self) -> Bounds<GlobalPixels> {
+ fn bounds(&self) -> Bounds<GlobalPixels> {
let frame = unsafe { NSWindow::frame(self.native_window) };
global_bounds_from_ns_rect(frame)
}
@@ -483,7 +466,15 @@ pub(crate) struct MacWindow(Arc<Mutex<MacWindowState>>);
impl MacWindow {
pub fn open(
handle: AnyWindowHandle,
- options: WindowOptions,
+ WindowParams {
+ bounds,
+ titlebar,
+ kind,
+ is_movable,
+ display_id,
+ focus,
+ show,
+ }: WindowParams,
executor: ForegroundExecutor,
renderer_context: renderer::Context,
) -> Self {
@@ -491,7 +482,7 @@ impl MacWindow {
let pool = NSAutoreleasePool::new(nil);
let mut style_mask;
- if let Some(titlebar) = options.titlebar.as_ref() {
+ if let Some(titlebar) = titlebar.as_ref() {
style_mask = NSWindowStyleMask::NSClosableWindowMask
| NSWindowStyleMask::NSMiniaturizableWindowMask
| NSWindowStyleMask::NSResizableWindowMask
@@ -505,7 +496,7 @@ impl MacWindow {
| NSWindowStyleMask::NSFullSizeContentViewWindowMask;
}
- let native_window: id = match options.kind {
+ let native_window: id = match kind {
WindowKind::Normal => msg_send![WINDOW_CLASS, alloc],
WindowKind::PopUp => {
style_mask |= NSWindowStyleMaskNonactivatingPanel;
@@ -513,8 +504,7 @@ impl MacWindow {
}
};
- let display = options
- .display_id
+ let display = display_id
.and_then(MacDisplay::find_by_id)
.unwrap_or_else(MacDisplay::primary);
@@ -530,23 +520,13 @@ impl MacWindow {
}
}
- let window_rect = match options.bounds {
- WindowBounds::Fullscreen => {
- // Set a temporary size as we will asynchronously resize the window
- NSRect::new(NSPoint::new(0., 0.), NSSize::new(1024., 768.))
- }
- WindowBounds::Maximized => {
- let display_bounds = display.bounds();
+ 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)
}
- WindowBounds::Fixed(bounds) => {
- 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 native_window = native_window.initWithContentRect_styleMask_backing_defer_screen_(
@@ -568,17 +548,8 @@ impl MacWindow {
assert!(!native_view.is_null());
let window_size = {
- let bounds = match options.bounds {
- WindowBounds::Fullscreen | WindowBounds::Maximized => {
- native_window.screen().visibleFrame()
- }
- WindowBounds::Fixed(bounds) => global_bounds_to_ns_rect(bounds),
- };
let scale = get_scale_factor(native_window);
- size(
- bounds.size.width as f32 * scale,
- bounds.size.height as f32 * scale,
- )
+ size(bounds.size.width.0 * scale, bounds.size.height.0 * scale)
};
let window = Self(Arc::new(Mutex::new(MacWindowState {
@@ -594,7 +565,7 @@ impl MacWindow {
native_view as *mut _,
window_size,
),
- kind: options.kind,
+ kind,
request_frame_callback: None,
event_callback: None,
activate_callback: None,
@@ -608,8 +579,7 @@ impl MacWindow {
last_key_equivalent: None,
synthetic_drag_counter: 0,
last_fresh_keydown: None,
- traffic_light_position: options
- .titlebar
+ traffic_light_position: titlebar
.as_ref()
.and_then(|titlebar| titlebar.traffic_light_position),
previous_modifiers_changed_event: None,
@@ -628,20 +598,16 @@ impl MacWindow {
Arc::into_raw(window.0.clone()) as *const c_void,
);
- if let Some(title) = options
- .titlebar
+ if let Some(title) = titlebar
.as_ref()
.and_then(|t| t.title.as_ref().map(AsRef::as_ref))
{
native_window.setTitle_(NSString::alloc(nil).init_str(title));
}
- native_window.setMovable_(options.is_movable as BOOL);
+ native_window.setMovable_(is_movable as BOOL);
- if options
- .titlebar
- .map_or(true, |titlebar| titlebar.appears_transparent)
- {
+ if titlebar.map_or(true, |titlebar| titlebar.appears_transparent) {
native_window.setTitlebarAppearsTransparent_(YES);
native_window.setTitleVisibility_(NSWindowTitleVisibility::NSWindowTitleHidden);
}
@@ -663,11 +629,7 @@ impl MacWindow {
native_window.setContentView_(native_view.autorelease());
native_window.makeFirstResponder_(native_view);
- if options.center {
- native_window.center();
- }
-
- match options.kind {
+ match kind {
WindowKind::Normal => {
native_window.setLevel_(NSNormalWindowLevel);
native_window.setAcceptsMouseMovedEvents_(YES);
@@ -698,18 +660,13 @@ impl MacWindow {
);
}
}
- if options.focus {
+
+ if focus {
native_window.makeKeyAndOrderFront_(nil);
- } else if options.show {
+ } else if show {
native_window.orderFront_(nil);
}
- if options.bounds == WindowBounds::Fullscreen {
- // We need to toggle full screen asynchronously as doing so may
- // call back into the platform handlers.
- window.toggle_full_screen();
- }
-
window.0.lock().move_traffic_light();
pool.drain();
@@ -754,7 +711,7 @@ impl Drop for MacWindow {
}
impl PlatformWindow for MacWindow {
- fn bounds(&self) -> WindowBounds {
+ fn bounds(&self) -> Bounds<GlobalPixels> {
self.0.as_ref().lock().bounds()
}
@@ -995,6 +952,17 @@ impl PlatformWindow for MacWindow {
.detach();
}
+ fn is_full_screen(&self) -> bool {
+ let this = self.0.lock();
+ let window = this.native_window;
+
+ unsafe {
+ window
+ .styleMask()
+ .contains(NSWindowStyleMask::NSFullScreenWindowMask)
+ }
+ }
+
fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
self.0.as_ref().lock().request_frame_callback = Some(callback);
}
@@ -1,7 +1,7 @@
use crate::{
AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor,
Keymap, Platform, PlatformDisplay, PlatformTextSystem, Task, TestDisplay, TestWindow,
- WindowAppearance, WindowOptions,
+ WindowAppearance, WindowParams,
};
use anyhow::{anyhow, Result};
use collections::VecDeque;
@@ -161,6 +161,10 @@ impl Platform for TestPlatform {
vec![self.active_display.clone()]
}
+ fn primary_display(&self) -> Option<std::rc::Rc<dyn crate::PlatformDisplay>> {
+ Some(self.active_display.clone())
+ }
+
fn display(&self, id: DisplayId) -> Option<std::rc::Rc<dyn crate::PlatformDisplay>> {
self.displays().iter().find(|d| d.id() == id).cloned()
}
@@ -175,11 +179,11 @@ impl Platform for TestPlatform {
fn open_window(
&self,
handle: AnyWindowHandle,
- options: WindowOptions,
+ params: WindowParams,
) -> Box<dyn crate::PlatformWindow> {
let window = TestWindow::new(
- options,
handle,
+ params,
self.weak.clone(),
self.active_display.clone(),
);
@@ -1,7 +1,7 @@
use crate::{
- px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, Pixels, PlatformAtlas,
- PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, Size,
- TestPlatform, TileId, WindowAppearance, WindowBounds, WindowOptions,
+ AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, GlobalPixels, Pixels,
+ PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
+ Size, TestPlatform, TileId, WindowAppearance, WindowParams,
};
use collections::HashMap;
use parking_lot::Mutex;
@@ -12,7 +12,7 @@ use std::{
};
pub(crate) struct TestWindowState {
- pub(crate) bounds: WindowBounds,
+ pub(crate) bounds: Bounds<GlobalPixels>,
pub(crate) handle: AnyWindowHandle,
display: Rc<dyn PlatformDisplay>,
pub(crate) title: Option<String>,
@@ -25,6 +25,7 @@ pub(crate) struct TestWindowState {
resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
moved_callback: Option<Box<dyn FnMut()>>,
input_handler: Option<PlatformInputHandler>,
+ is_fullscreen: bool,
}
#[derive(Clone)]
@@ -48,13 +49,13 @@ impl HasDisplayHandle for TestWindow {
impl TestWindow {
pub fn new(
- options: WindowOptions,
handle: AnyWindowHandle,
+ params: WindowParams,
platform: Weak<TestPlatform>,
display: Rc<dyn PlatformDisplay>,
) -> Self {
Self(Arc::new(Mutex::new(TestWindowState {
- bounds: options.bounds,
+ bounds: params.bounds,
display,
platform,
handle,
@@ -67,6 +68,7 @@ impl TestWindow {
resize_callback: None,
moved_callback: None,
input_handler: None,
+ is_fullscreen: false,
})))
}
@@ -76,17 +78,7 @@ impl TestWindow {
let Some(mut callback) = lock.resize_callback.take() else {
return;
};
- match &mut lock.bounds {
- WindowBounds::Fullscreen | WindowBounds::Maximized => {
- lock.bounds = WindowBounds::Fixed(Bounds {
- origin: Point::default(),
- size: size.map(|pixels| f64::from(pixels).into()),
- });
- }
- WindowBounds::Fixed(bounds) => {
- bounds.size = size.map(|pixels| f64::from(pixels).into());
- }
- }
+ lock.bounds.size = size.map(|pixels| f64::from(pixels).into());
drop(lock);
callback(size, scale_factor);
self.0.lock().resize_callback = Some(callback);
@@ -115,16 +107,12 @@ impl TestWindow {
}
impl PlatformWindow for TestWindow {
- fn bounds(&self) -> WindowBounds {
+ fn bounds(&self) -> Bounds<GlobalPixels> {
self.0.lock().bounds
}
fn content_size(&self) -> Size<Pixels> {
- let bounds = match self.bounds() {
- WindowBounds::Fixed(bounds) => bounds,
- WindowBounds::Maximized | WindowBounds::Fullscreen => self.display().bounds(),
- };
- bounds.size.map(|p| px(p.0))
+ self.bounds().size.into()
}
fn scale_factor(&self) -> f32 {
@@ -210,7 +198,12 @@ impl PlatformWindow for TestWindow {
}
fn toggle_full_screen(&self) {
- unimplemented!()
+ let mut lock = self.0.lock();
+ lock.is_fullscreen = !lock.is_fullscreen;
+ }
+
+ fn is_full_screen(&self) -> bool {
+ self.0.lock().is_fullscreen
}
fn on_request_frame(&self, _callback: Box<dyn FnMut()>) {}
@@ -53,8 +53,8 @@ use windows::{
use crate::{
try_get_window_inner, Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle,
ForegroundExecutor, Keymap, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput,
- PlatformTextSystem, PlatformWindow, Task, WindowAppearance, WindowOptions, WindowsDispatcher,
- WindowsDisplay, WindowsTextSystem, WindowsWindow,
+ PlatformTextSystem, PlatformWindow, Task, WindowAppearance, WindowOptions, WindowParams,
+ WindowsDispatcher, WindowsDisplay, WindowsTextSystem, WindowsWindow,
};
pub(crate) struct WindowsPlatform {
@@ -327,15 +327,20 @@ impl Platform for WindowsPlatform {
Some(Rc::new(WindowsDisplay::new()))
}
+ // todo(windows)
+ fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
+ Some(Rc::new(WindowsDisplay::new()))
+ }
+
// todo(windows)
fn active_window(&self) -> Option<AnyWindowHandle> {
- unimplemented!()
+ None
}
fn open_window(
&self,
handle: AnyWindowHandle,
- options: WindowOptions,
+ options: WindowParams,
) -> Box<dyn PlatformWindow> {
Box::new(WindowsWindow::new(self.inner.clone(), handle, options))
}
@@ -48,7 +48,7 @@ use windows::{
WindowsAndMessaging::{
CreateWindowExW, DefWindowProcW, GetWindowLongPtrW, LoadCursorW, PostQuitMessage,
RegisterClassW, SetWindowLongPtrW, SetWindowTextW, ShowWindow, CREATESTRUCTW,
- CW_USEDEFAULT, GWLP_USERDATA, HMENU, IDC_ARROW, SW_MAXIMIZE, SW_SHOW, WHEEL_DELTA,
+ GWLP_USERDATA, HMENU, IDC_ARROW, SW_MAXIMIZE, SW_SHOW, WHEEL_DELTA,
WINDOW_EX_STYLE, WINDOW_LONG_PTR_INDEX, WM_CHAR, WM_CLOSE, WM_DESTROY, WM_KEYDOWN,
WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP,
WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_MOVE, WM_NCCREATE, WM_NCDESTROY,
@@ -65,7 +65,7 @@ use crate::{
KeyUpEvent, Keystroke, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
NavigationDirection, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
PlatformInputHandler, PlatformWindow, Point, PromptLevel, Scene, ScrollDelta, Size, TouchPhase,
- WindowAppearance, WindowBounds, WindowOptions, WindowsDisplay, WindowsPlatformInner,
+ WindowAppearance, WindowParams, WindowsDisplay, WindowsPlatformInner,
};
#[derive(PartialEq)]
@@ -614,7 +614,7 @@ impl WindowsWindow {
pub(crate) fn new(
platform_inner: Rc<WindowsPlatformInner>,
handle: AnyWindowHandle,
- options: WindowOptions,
+ options: WindowParams,
) -> Self {
let dwexstyle = WINDOW_EX_STYLE::default();
let classname = register_wnd_class();
@@ -627,20 +627,10 @@ impl WindowsWindow {
.unwrap_or(""),
);
let dwstyle = WS_OVERLAPPEDWINDOW & !WS_VISIBLE;
- let mut x = CW_USEDEFAULT;
- let mut y = CW_USEDEFAULT;
- let mut nwidth = CW_USEDEFAULT;
- let mut nheight = CW_USEDEFAULT;
- match options.bounds {
- WindowBounds::Fullscreen => {}
- WindowBounds::Maximized => {}
- WindowBounds::Fixed(bounds) => {
- x = bounds.origin.x.0 as i32;
- y = bounds.origin.y.0 as i32;
- nwidth = bounds.size.width.0 as i32;
- nheight = bounds.size.height.0 as i32;
- }
- };
+ let x = options.bounds.origin.x.0 as i32;
+ let y = options.bounds.origin.y.0 as i32;
+ let nwidth = options.bounds.size.width.0 as i32;
+ let nheight = options.bounds.size.height.0 as i32;
let hwndparent = HWND::default();
let hmenu = HMENU::default();
let hinstance = HINSTANCE::default();
@@ -684,11 +674,7 @@ impl WindowsWindow {
.window_handle_values
.borrow_mut()
.insert(wnd.inner.hwnd.0);
- match options.bounds {
- WindowBounds::Fullscreen => wnd.toggle_full_screen(),
- WindowBounds::Maximized => wnd.maximize(),
- WindowBounds::Fixed(_) => {}
- }
+
unsafe { ShowWindow(wnd.inner.hwnd, SW_SHOW) };
wnd
}
@@ -728,11 +714,11 @@ impl Drop for WindowsWindow {
}
impl PlatformWindow for WindowsWindow {
- fn bounds(&self) -> WindowBounds {
- WindowBounds::Fixed(Bounds {
+ fn bounds(&self) -> Bounds<GlobalPixels> {
+ Bounds {
origin: self.inner.origin.get(),
size: self.inner.size.get(),
- })
+ }
}
// todo(windows)
@@ -887,6 +873,11 @@ impl PlatformWindow for WindowsWindow {
// todo(windows)
fn toggle_full_screen(&self) {}
+ // todo(windows)
+ fn is_full_screen(&self) -> bool {
+ false
+ }
+
// todo(windows)
fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
self.inner.callbacks.borrow_mut().request_frame = Some(callback);
@@ -1,13 +1,13 @@
use crate::{
- px, 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,
- Hsla, KeyBinding, KeyDownEvent, KeyMatch, KeymatchResult, Keystroke, KeystrokeEvent, Model,
- ModelContext, Modifiers, MouseButton, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas,
- PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel, Render, ScaledPixels,
- SharedString, Size, SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle,
- TextStyleRefinement, View, VisualContext, WeakView, WindowAppearance, WindowBounds,
- WindowOptions, WindowTextSystem,
+ 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,
+ 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};
use collections::FxHashSet;
@@ -253,7 +253,6 @@ pub struct Window {
mouse_hit_test: HitTest,
modifiers: Modifiers,
scale_factor: f32,
- bounds: WindowBounds,
bounds_observers: SubscriberSet<(), AnyObserver>,
appearance: WindowAppearance,
appearance_observers: SubscriberSet<(), AnyObserver>,
@@ -315,20 +314,69 @@ pub(crate) struct ElementStateBox {
pub(crate) type_name: &'static str,
}
+fn default_bounds(cx: &mut AppContext) -> Bounds<GlobalPixels> {
+ const DEFAULT_WINDOW_SIZE: Size<GlobalPixels> = size(GlobalPixels(1024.0), GlobalPixels(700.0));
+ const DEFAULT_WINDOW_OFFSET: Point<GlobalPixels> = point(GlobalPixels(0.0), GlobalPixels(35.0));
+
+ 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()
+ .map(|display| {
+ let center = display.bounds().center();
+ let offset = DEFAULT_WINDOW_SIZE / 2.0;
+ 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,
+ )
+ })
+ })
+}
+
+// Fixed, Maximized, Fullscreen, and 'Inherent / default'
+// Platform part, you don't, you only need Fixed, Maximized, Fullscreen
+
impl Window {
pub(crate) fn new(
handle: AnyWindowHandle,
options: WindowOptions,
cx: &mut AppContext,
) -> Self {
- let platform_window = cx.platform.open_window(handle, options);
+ let WindowOptions {
+ bounds,
+ titlebar,
+ focus,
+ show,
+ kind,
+ is_movable,
+ display_id,
+ fullscreen,
+ } = options;
+
+ let bounds = bounds.unwrap_or_else(|| default_bounds(cx));
+ let platform_window = cx.platform.open_window(
+ handle,
+ WindowParams {
+ bounds,
+ titlebar,
+ kind,
+ is_movable,
+ focus,
+ show,
+ display_id,
+ },
+ );
let display_id = platform_window.display().id();
let sprite_atlas = platform_window.sprite_atlas();
let mouse_position = platform_window.mouse_position();
let modifiers = platform_window.modifiers();
let content_size = platform_window.content_size();
let scale_factor = platform_window.scale_factor();
- let bounds = platform_window.bounds();
let appearance = platform_window.appearance();
let text_system = Arc::new(WindowTextSystem::new(cx.text_system().clone()));
let dirty = Rc::new(Cell::new(true));
@@ -337,6 +385,10 @@ impl Window {
let next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>> = Default::default();
let last_input_timestamp = Rc::new(Cell::new(Instant::now()));
+ if fullscreen {
+ platform_window.toggle_full_screen();
+ }
+
platform_window.on_close(Box::new({
let mut cx = cx.to_async();
move || {
@@ -457,7 +509,6 @@ impl Window {
mouse_hit_test: HitTest::default(),
modifiers,
scale_factor,
- bounds,
bounds_observers: SubscriberSet::new(),
appearance,
appearance_observers: SubscriberSet::new(),
@@ -740,7 +791,6 @@ impl<'a> WindowContext<'a> {
fn window_bounds_changed(&mut self) {
self.window.scale_factor = self.window.platform_window.scale_factor();
self.window.viewport_size = self.window.platform_window.content_size();
- self.window.bounds = self.window.platform_window.bounds();
self.window.display_id = self.window.platform_window.display().id();
self.refresh();
@@ -751,8 +801,13 @@ 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) -> WindowBounds {
- self.window.bounds
+ pub fn window_bounds(&self) -> Bounds<GlobalPixels> {
+ self.window.platform_window.bounds()
+ }
+
+ /// Retusn whether or not the window is currently fullscreen
+ pub fn is_full_screen(&self) -> bool {
+ self.window.platform_window.is_full_screen()
}
fn appearance_changed(&mut self) {
@@ -7,8 +7,7 @@ mod story_selector;
use clap::Parser;
use dialoguer::FuzzySelect;
use gpui::{
- div, px, size, AnyView, AppContext, Bounds, Render, ViewContext, VisualContext, WindowBounds,
- WindowOptions,
+ div, px, size, AnyView, AppContext, Bounds, Render, ViewContext, VisualContext, WindowOptions,
};
use log::LevelFilter;
use settings::{default_settings, KeymapFile, Settings, SettingsStore};
@@ -85,12 +84,11 @@ fn main() {
load_storybook_keymap(cx);
cx.set_menus(app_menus());
+ let size = size(px(1500.), px(780.));
+ let bounds = Bounds::centered(size, cx);
let _window = cx.open_window(
WindowOptions {
- bounds: WindowBounds::Fixed(Bounds {
- origin: Default::default(),
- size: size(px(1500.), px(780.)).into(),
- }),
+ bounds: Some(bounds),
..Default::default()
},
move |cx| {
@@ -4,7 +4,7 @@ use std::path::Path;
use anyhow::{anyhow, bail, Context, Result};
use db::{define_connection, query, sqlez::connection::Connection, sqlez_macros::sql};
-use gpui::{point, size, Axis, Bounds, WindowBounds};
+use gpui::{point, size, Axis, Bounds};
use sqlez::{
bindable::{Bind, Column, StaticColumnCount},
@@ -59,7 +59,7 @@ impl sqlez::bindable::Column for SerializedAxis {
}
#[derive(Clone, Debug, PartialEq)]
-pub(crate) struct SerializedWindowsBounds(pub(crate) WindowBounds);
+pub(crate) struct SerializedWindowsBounds(pub(crate) Bounds<gpui::GlobalPixels>);
impl StaticColumnCount for SerializedWindowsBounds {
fn column_count() -> usize {
@@ -69,30 +69,15 @@ impl StaticColumnCount for SerializedWindowsBounds {
impl Bind for SerializedWindowsBounds {
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
- let (region, next_index) = match self.0 {
- WindowBounds::Fullscreen => {
- let next_index = statement.bind(&"Fullscreen", start_index)?;
- (None, next_index)
- }
- WindowBounds::Maximized => {
- let next_index = statement.bind(&"Maximized", start_index)?;
- (None, next_index)
- }
- WindowBounds::Fixed(region) => {
- let next_index = statement.bind(&"Fixed", start_index)?;
- (Some(region), next_index)
- }
- };
+ let next_index = statement.bind(&"Fixed", start_index)?;
statement.bind(
- ®ion.map(|region| {
- (
- SerializedGlobalPixels(region.origin.x),
- SerializedGlobalPixels(region.origin.y),
- SerializedGlobalPixels(region.size.width),
- SerializedGlobalPixels(region.size.height),
- )
- }),
+ &(
+ SerializedGlobalPixels(self.0.origin.x),
+ SerializedGlobalPixels(self.0.origin.y),
+ SerializedGlobalPixels(self.0.size.width),
+ SerializedGlobalPixels(self.0.size.height),
+ ),
next_index,
)
}
@@ -102,18 +87,16 @@ impl Column for SerializedWindowsBounds {
fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
let (window_state, next_index) = String::column(statement, start_index)?;
let bounds = match window_state.as_str() {
- "Fullscreen" => SerializedWindowsBounds(WindowBounds::Fullscreen),
- "Maximized" => SerializedWindowsBounds(WindowBounds::Maximized),
"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;
- SerializedWindowsBounds(WindowBounds::Fixed(Bounds {
+ SerializedWindowsBounds(Bounds {
origin: point(x.into(), y.into()),
size: size(width.into(), height.into()),
- }))
+ })
}
_ => bail!("Window State did not have a valid string"),
};
@@ -6,7 +6,7 @@ use db::sqlez::{
bindable::{Bind, Column, StaticColumnCount},
statement::Statement,
};
-use gpui::{AsyncWindowContext, Model, Task, View, WeakView, WindowBounds};
+use gpui::{AsyncWindowContext, Bounds, GlobalPixels, 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<WindowBounds>,
+ pub(crate) bounds: Option<Bounds<GlobalPixels>>,
pub(crate) display: Option<Uuid>,
pub(crate) docks: DockStructure,
}
@@ -32,7 +32,7 @@ use gpui::{
FocusableView, Global, GlobalPixels, InteractiveElement, IntoElement, KeyContext, Keystroke,
LayoutId, ManagedView, Model, ModelContext, ParentElement, PathPromptOptions, Pixels, Point,
PromptLevel, Render, SharedString, Size, Styled, Subscription, Task, View, ViewContext,
- VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
+ VisualContext, WeakView, WindowContext, WindowHandle, WindowOptions,
};
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
use itertools::Itertools;
@@ -362,8 +362,7 @@ pub struct AppState {
pub user_store: Model<UserStore>,
pub workspace_store: Model<WorkspaceStore>,
pub fs: Arc<dyn fs::Fs>,
- pub build_window_options:
- fn(Option<WindowBounds>, Option<Uuid>, &mut AppContext) -> WindowOptions,
+ pub build_window_options: fn(Option<Uuid>, &mut AppContext) -> WindowOptions,
pub node_runtime: Arc<dyn NodeRuntime>,
}
@@ -424,7 +423,7 @@ impl AppState {
user_store,
workspace_store,
node_runtime: FakeNodeRuntime::new(),
- build_window_options: |_, _, _| Default::default(),
+ build_window_options: |_, _| Default::default(),
})
}
}
@@ -690,18 +689,16 @@ impl Workspace {
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 bounds = cx.window_bounds();
- if let WindowBounds::Fixed(window_bounds) = &mut bounds {
- let display_bounds = display.bounds();
- window_bounds.origin.x -= display_bounds.origin.x;
- window_bounds.origin.y -= display_bounds.origin.y;
- }
+ 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;
if let Some(display_uuid) = display.uuid().log_err() {
cx.background_executor()
.spawn(DB.set_window_bounds(
workspace_id,
- SerializedWindowsBounds(bounds),
+ SerializedWindowsBounds(window_bounds),
display_uuid,
))
.detach_and_log_err(cx);
@@ -847,19 +844,16 @@ impl Workspace {
// Stored bounds are relative to the containing display.
// So convert back to global coordinates if that screen still exists
- if let WindowBounds::Fixed(mut window_bounds) = bounds {
- let screen = cx
- .update(|cx| {
- cx.displays().into_iter().find(|display| {
- display.uuid().ok() == Some(serialized_display)
- })
+ let screen = cx
+ .update(|cx| {
+ cx.displays().into_iter().find(|display| {
+ display.uuid().ok() == Some(serialized_display)
})
- .ok()??;
- let screen_bounds = screen.bounds();
- window_bounds.origin.x += screen_bounds.origin.x;
- window_bounds.origin.y += screen_bounds.origin.y;
- bounds = WindowBounds::Fixed(window_bounds);
- }
+ })
+ .ok()??;
+ let screen_bounds = screen.bounds();
+ bounds.origin.x += screen_bounds.origin.x;
+ bounds.origin.y += screen_bounds.origin.y;
Some((bounds, serialized_display))
})
@@ -867,9 +861,8 @@ impl Workspace {
};
// Use the serialized workspace to construct the new window
- let options =
- cx.update(|cx| (app_state.build_window_options)(bounds, display, cx))?;
-
+ let mut options = cx.update(|cx| (app_state.build_window_options)(display, cx))?;
+ options.bounds = bounds;
cx.open_window(options, {
let app_state = app_state.clone();
let project_handle = project_handle.clone();
@@ -3610,7 +3603,7 @@ impl Workspace {
client,
user_store,
fs: project.read(cx).fs().clone(),
- build_window_options: |_, _, _| Default::default(),
+ build_window_options: |_, _| Default::default(),
node_runtime: FakeNodeRuntime::new(),
});
let workspace = Self::new(0, project, app_state, cx);
@@ -3663,17 +3656,15 @@ impl Workspace {
}
}
-fn window_bounds_env_override(cx: &AsyncAppContext) -> Option<WindowBounds> {
+fn window_bounds_env_override(cx: &AsyncAppContext) -> Option<Bounds<GlobalPixels>> {
let display_origin = cx
.update(|cx| Some(cx.displays().first()?.bounds().origin))
.ok()??;
ZED_WINDOW_POSITION
.zip(*ZED_WINDOW_SIZE)
- .map(|(position, size)| {
- WindowBounds::Fixed(Bounds {
- origin: display_origin + position,
- size,
- })
+ .map(|(position, size)| Bounds {
+ origin: display_origin + position,
+ size,
})
}
@@ -4553,7 +4544,8 @@ pub fn join_hosted_project(
let window_bounds_override = window_bounds_env_override(&cx);
cx.update(|cx| {
- let options = (app_state.build_window_options)(window_bounds_override, None, cx);
+ let mut options = (app_state.build_window_options)(None, cx);
+ options.bounds = window_bounds_override;
cx.open_window(options, |cx| {
cx.new_view(|cx| Workspace::new(0, project, app_state.clone(), cx))
})
@@ -4611,7 +4603,8 @@ pub fn join_in_room_project(
let window_bounds_override = window_bounds_env_override(&cx);
cx.update(|cx| {
- let options = (app_state.build_window_options)(window_bounds_override, None, cx);
+ let mut options = (app_state.build_window_options)(None, cx);
+ options.bounds = window_bounds_override;
cx.open_window(options, |cx| {
cx.new_view(|cx| Workspace::new(0, project, app_state.clone(), cx))
})
@@ -10,7 +10,7 @@ use collections::VecDeque;
use editor::{scroll::Autoscroll, Editor, MultiBuffer};
use gpui::{
actions, point, px, AppContext, AsyncAppContext, Context, FocusableView, PromptLevel,
- TitlebarOptions, View, ViewContext, VisualContext, WindowBounds, WindowKind, WindowOptions,
+ TitlebarOptions, View, ViewContext, VisualContext, WindowKind, WindowOptions,
};
pub use only_instance::*;
pub use open_listener::*;
@@ -79,12 +79,7 @@ pub fn init(cx: &mut AppContext) {
cx.on_action(quit);
}
-pub fn build_window_options(
- bounds: Option<WindowBounds>,
- display_uuid: Option<Uuid>,
- cx: &mut AppContext,
-) -> WindowOptions {
- let bounds = bounds.unwrap_or(WindowBounds::Maximized);
+pub fn build_window_options(display_uuid: Option<Uuid>, cx: &mut AppContext) -> WindowOptions {
let display = display_uuid.and_then(|uuid| {
cx.displays()
.into_iter()
@@ -92,18 +87,18 @@ pub fn build_window_options(
});
WindowOptions {
- bounds,
titlebar: Some(TitlebarOptions {
title: None,
appears_transparent: true,
traffic_light_position: Some(point(px(9.5), px(9.5))),
}),
- center: false,
+ bounds: None,
focus: false,
show: false,
kind: WindowKind::Normal,
is_movable: true,
display_id: display.map(|display| display.id()),
+ fullscreen: false,
}
}