Remember window restore size (#10429)
Now, regardless of how the Zed window is closed, Zed can remember the window's restore size. - [x] Windows implementation - [x] macOS implementation - [x] Linux implementation (partial) - [x] update SQL data base (mark column `fullscreen` as deprecated) The current implementation on Linux is basic, and I'm not sure if it's correct. The variable `fullscreen` in SQL can be removed, but I'm unsure how to do it. edit: mark `fullscreen` as deprecated ### Case 1 When the window is closed as maximized, reopening it will open in the maximized state, and returning from maximized state will restore the position and size it had when it was maximized. https://github.com/zed-industries/zed/assets/14981363/7207752e-878a-4d43-93a7-41ad1fdb3a06 ### Case 2 When the window is closed as fullscreen, reopening it will open in fullscreen mode, and toggling fullscreen will restore the position and size it had when it entered fullscreen (note that the fullscreen application was not recorded in the video, showing a black screen, but it had actually entered fullscreen mode). https://github.com/zed-industries/zed/assets/14981363/ea5aa70d-b296-462a-afb3-4c3372883ea3 ### What's more - As English is not my native language, some variable and struct names may need to be modified to match their actual meaning. - I am not familiar with the APIs related to macOS and Linux, so implementation for these two platforms has not been done for now. - Any suggestions and ideas are welcome. Release Notes: - N/A
This commit is contained in:
parent
6ddcff25e3
commit
63a5f46df4
19 changed files with 319 additions and 219 deletions
|
@ -14,7 +14,7 @@ pub use collab_panel::CollabPanel;
|
||||||
pub use collab_titlebar_item::CollabTitlebarItem;
|
pub use collab_titlebar_item::CollabTitlebarItem;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, point, AppContext, DevicePixels, Pixels, PlatformDisplay, Size, Task,
|
actions, point, AppContext, DevicePixels, Pixels, PlatformDisplay, Size, Task,
|
||||||
WindowBackgroundAppearance, WindowContext, WindowKind, WindowOptions,
|
WindowBackgroundAppearance, WindowBounds, WindowContext, WindowKind, WindowOptions,
|
||||||
};
|
};
|
||||||
use panel_settings::MessageEditorSettings;
|
use panel_settings::MessageEditorSettings;
|
||||||
pub use panel_settings::{
|
pub use panel_settings::{
|
||||||
|
@ -117,14 +117,13 @@ fn notification_window_options(
|
||||||
let app_id = ReleaseChannel::global(cx).app_id();
|
let app_id = ReleaseChannel::global(cx).app_id();
|
||||||
|
|
||||||
WindowOptions {
|
WindowOptions {
|
||||||
bounds: Some(bounds),
|
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||||
titlebar: None,
|
titlebar: None,
|
||||||
focus: false,
|
focus: false,
|
||||||
show: true,
|
show: true,
|
||||||
kind: WindowKind::PopUp,
|
kind: WindowKind::PopUp,
|
||||||
is_movable: false,
|
is_movable: false,
|
||||||
display_id: Some(screen.id()),
|
display_id: Some(screen.id()),
|
||||||
fullscreen: false,
|
|
||||||
window_background: WindowBackgroundAppearance::default(),
|
window_background: WindowBackgroundAppearance::default(),
|
||||||
app_id: Some(app_id.to_owned()),
|
app_id: Some(app_id.to_owned()),
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
JoinLines,
|
JoinLines,
|
||||||
};
|
};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::{div, TestAppContext, VisualTestContext, WindowOptions};
|
use gpui::{div, TestAppContext, VisualTestContext, WindowBounds, WindowOptions};
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use language::{
|
use language::{
|
||||||
language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
|
language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
|
||||||
|
@ -7493,10 +7493,10 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
|
||||||
let follower = cx.update(|cx| {
|
let follower = cx.update(|cx| {
|
||||||
cx.open_window(
|
cx.open_window(
|
||||||
WindowOptions {
|
WindowOptions {
|
||||||
bounds: Some(Bounds::from_corners(
|
window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
|
||||||
gpui::Point::new(0.into(), 0.into()),
|
gpui::Point::new(0.into(), 0.into()),
|
||||||
gpui::Point::new(10.into(), 80.into()),
|
gpui::Point::new(10.into(), 80.into()),
|
||||||
)),
|
))),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
|
|cx| cx.new_view(|cx| build_editor(buffer.clone(), cx)),
|
||||||
|
|
|
@ -63,7 +63,11 @@ fn main() {
|
||||||
.with_assets(Assets {})
|
.with_assets(Assets {})
|
||||||
.run(|cx: &mut AppContext| {
|
.run(|cx: &mut AppContext| {
|
||||||
let options = WindowOptions {
|
let options = WindowOptions {
|
||||||
bounds: Some(Bounds::centered(None, size(px(300.), px(300.)), cx)),
|
window_bounds: Some(WindowBounds::Windowed(Bounds::centered(
|
||||||
|
None,
|
||||||
|
size(px(300.), px(300.)),
|
||||||
|
cx,
|
||||||
|
))),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
cx.open_window(options, |cx| {
|
cx.open_window(options, |cx| {
|
||||||
|
|
|
@ -26,7 +26,7 @@ fn main() {
|
||||||
let bounds = Bounds::centered(None, size(px(600.0), px(600.0)), cx);
|
let bounds = Bounds::centered(None, size(px(600.0), px(600.0)), cx);
|
||||||
cx.open_window(
|
cx.open_window(
|
||||||
WindowOptions {
|
WindowOptions {
|
||||||
bounds: Some(bounds),
|
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|cx| {
|
|cx| {
|
||||||
|
|
|
@ -79,10 +79,10 @@ fn main() {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
|
|
||||||
bounds: Some(Bounds {
|
window_bounds: Some(WindowBounds::Windowed(Bounds {
|
||||||
size: size(px(1100.), px(600.)).into(),
|
size: size(px(1100.), px(600.)).into(),
|
||||||
origin: Point::new(DevicePixels::from(200), DevicePixels::from(200)),
|
origin: Point::new(DevicePixels::from(200), DevicePixels::from(200)),
|
||||||
}),
|
})),
|
||||||
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,7 +43,7 @@ fn main() {
|
||||||
|
|
||||||
WindowOptions {
|
WindowOptions {
|
||||||
// Set the bounds of the window in screen coordinates
|
// Set the bounds of the window in screen coordinates
|
||||||
bounds: Some(bounds),
|
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||||
// Specify the display_id to ensure the window is created on the correct screen
|
// Specify the display_id to ensure the window is created on the correct screen
|
||||||
display_id: Some(screen.id()),
|
display_id: Some(screen.id()),
|
||||||
|
|
||||||
|
@ -53,7 +53,6 @@ fn main() {
|
||||||
show: true,
|
show: true,
|
||||||
kind: WindowKind::PopUp,
|
kind: WindowKind::PopUp,
|
||||||
is_movable: false,
|
is_movable: false,
|
||||||
fullscreen: false,
|
|
||||||
app_id: None,
|
app_id: None,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,8 +4,8 @@ use crate::{
|
||||||
Element, Empty, Entity, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Model,
|
Element, Empty, Entity, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Model,
|
||||||
ModelContext, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent,
|
ModelContext, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent,
|
||||||
MouseUpEvent, Pixels, Platform, Point, Render, Result, Size, Task, TestDispatcher,
|
MouseUpEvent, Pixels, Platform, Point, Render, Result, Size, Task, TestDispatcher,
|
||||||
TestPlatform, TestWindow, TextSystem, View, ViewContext, VisualContext, WindowContext,
|
TestPlatform, TestWindow, TextSystem, View, ViewContext, VisualContext, WindowBounds,
|
||||||
WindowHandle, WindowOptions,
|
WindowContext, WindowHandle, WindowOptions,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, bail};
|
use anyhow::{anyhow, bail};
|
||||||
use futures::{channel::oneshot, Stream, StreamExt};
|
use futures::{channel::oneshot, Stream, StreamExt};
|
||||||
|
@ -188,7 +188,7 @@ impl TestAppContext {
|
||||||
let bounds = Bounds::maximized(None, &mut cx);
|
let bounds = Bounds::maximized(None, &mut cx);
|
||||||
cx.open_window(
|
cx.open_window(
|
||||||
WindowOptions {
|
WindowOptions {
|
||||||
bounds: Some(bounds),
|
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|cx| cx.new_view(build_window),
|
|cx| cx.new_view(build_window),
|
||||||
|
@ -201,7 +201,7 @@ impl TestAppContext {
|
||||||
let bounds = Bounds::maximized(None, &mut cx);
|
let bounds = Bounds::maximized(None, &mut cx);
|
||||||
let window = cx.open_window(
|
let window = cx.open_window(
|
||||||
WindowOptions {
|
WindowOptions {
|
||||||
bounds: Some(bounds),
|
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|cx| cx.new_view(|_| Empty),
|
|cx| cx.new_view(|_| Empty),
|
||||||
|
@ -224,7 +224,7 @@ impl TestAppContext {
|
||||||
let bounds = Bounds::maximized(None, &mut cx);
|
let bounds = Bounds::maximized(None, &mut cx);
|
||||||
let window = cx.open_window(
|
let window = cx.open_window(
|
||||||
WindowOptions {
|
WindowOptions {
|
||||||
bounds: Some(bounds),
|
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|cx| cx.new_view(build_root_view),
|
|cx| cx.new_view(build_root_view),
|
||||||
|
|
|
@ -184,7 +184,7 @@ unsafe impl Send for DisplayId {}
|
||||||
pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
|
pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
|
||||||
fn bounds(&self) -> Bounds<DevicePixels>;
|
fn bounds(&self) -> Bounds<DevicePixels>;
|
||||||
fn is_maximized(&self) -> bool;
|
fn is_maximized(&self) -> bool;
|
||||||
fn is_minimized(&self) -> bool;
|
fn window_bounds(&self) -> WindowBounds;
|
||||||
fn content_size(&self) -> Size<Pixels>;
|
fn content_size(&self) -> Size<Pixels>;
|
||||||
fn scale_factor(&self) -> f32;
|
fn scale_factor(&self) -> f32;
|
||||||
fn appearance(&self) -> WindowAppearance;
|
fn appearance(&self) -> WindowAppearance;
|
||||||
|
@ -515,9 +515,10 @@ pub trait InputHandler: 'static {
|
||||||
/// The variables that can be configured when creating a new window
|
/// The variables that can be configured when creating a new window
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct WindowOptions {
|
pub struct WindowOptions {
|
||||||
/// The bounds of the window in screen coordinates.
|
/// Specifies the state and bounds of the window in screen coordinates.
|
||||||
/// None -> inherit, Some(bounds) -> set bounds
|
/// - `None`: Inherit the bounds.
|
||||||
pub bounds: Option<Bounds<DevicePixels>>,
|
/// - `Some(WindowBounds)`: Open a window with corresponding state and its restore size.
|
||||||
|
pub window_bounds: Option<WindowBounds>,
|
||||||
|
|
||||||
/// The titlebar configuration of the window
|
/// The titlebar configuration of the window
|
||||||
pub titlebar: Option<TitlebarOptions>,
|
pub titlebar: Option<TitlebarOptions>,
|
||||||
|
@ -528,9 +529,6 @@ pub struct WindowOptions {
|
||||||
/// Whether the window should be shown when created
|
/// Whether the window should be shown when created
|
||||||
pub show: bool,
|
pub show: bool,
|
||||||
|
|
||||||
/// Whether the window should be fullscreen when created
|
|
||||||
pub fullscreen: bool,
|
|
||||||
|
|
||||||
/// The kind of window to create
|
/// The kind of window to create
|
||||||
pub kind: WindowKind,
|
pub kind: WindowKind,
|
||||||
|
|
||||||
|
@ -571,10 +569,40 @@ pub(crate) struct WindowParams {
|
||||||
pub window_background: WindowBackgroundAppearance,
|
pub window_background: WindowBackgroundAppearance,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents the status of how a window should be opened.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub enum WindowBounds {
|
||||||
|
/// Indicates that the window should open in a windowed state with the given bounds.
|
||||||
|
Windowed(Bounds<DevicePixels>),
|
||||||
|
/// Indicates that the window should open in a maximized state.
|
||||||
|
/// The bounds provided here represent the restore size of the window.
|
||||||
|
Maximized(Bounds<DevicePixels>),
|
||||||
|
/// Indicates that the window should open in fullscreen mode.
|
||||||
|
/// The bounds provided here represent the restore size of the window.
|
||||||
|
Fullscreen(Bounds<DevicePixels>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for WindowBounds {
|
||||||
|
fn default() -> Self {
|
||||||
|
WindowBounds::Windowed(Bounds::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowBounds {
|
||||||
|
/// Retrieve the inner bounds
|
||||||
|
pub fn get_bounds(&self) -> Bounds<DevicePixels> {
|
||||||
|
match self {
|
||||||
|
WindowBounds::Windowed(bounds) => *bounds,
|
||||||
|
WindowBounds::Maximized(bounds) => *bounds,
|
||||||
|
WindowBounds::Fullscreen(bounds) => *bounds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for WindowOptions {
|
impl Default for WindowOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
bounds: None,
|
window_bounds: None,
|
||||||
titlebar: Some(TitlebarOptions {
|
titlebar: Some(TitlebarOptions {
|
||||||
title: Default::default(),
|
title: Default::default(),
|
||||||
appears_transparent: Default::default(),
|
appears_transparent: Default::default(),
|
||||||
|
@ -585,7 +613,6 @@ impl Default for WindowOptions {
|
||||||
kind: WindowKind::Normal,
|
kind: WindowKind::Normal,
|
||||||
is_movable: true,
|
is_movable: true,
|
||||||
display_id: None,
|
display_id: None,
|
||||||
fullscreen: false,
|
|
||||||
window_background: WindowBackgroundAppearance::default(),
|
window_background: WindowBackgroundAppearance::default(),
|
||||||
app_id: None,
|
app_id: None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ use crate::scene::Scene;
|
||||||
use crate::{
|
use crate::{
|
||||||
px, size, Bounds, DevicePixels, Globals, Modifiers, Pixels, PlatformDisplay, PlatformInput,
|
px, size, Bounds, DevicePixels, Globals, Modifiers, Pixels, PlatformDisplay, PlatformInput,
|
||||||
Point, PromptLevel, Size, WaylandClientState, WaylandClientStatePtr, WindowAppearance,
|
Point, PromptLevel, Size, WaylandClientState, WaylandClientStatePtr, WindowAppearance,
|
||||||
WindowBackgroundAppearance, WindowParams,
|
WindowBackgroundAppearance, WindowBounds, WindowParams,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -79,6 +79,7 @@ pub struct WaylandWindowState {
|
||||||
input_handler: Option<PlatformInputHandler>,
|
input_handler: Option<PlatformInputHandler>,
|
||||||
decoration_state: WaylandDecorationState,
|
decoration_state: WaylandDecorationState,
|
||||||
fullscreen: bool,
|
fullscreen: bool,
|
||||||
|
restore_bounds: Bounds<DevicePixels>,
|
||||||
maximized: bool,
|
maximized: bool,
|
||||||
client: WaylandClientStatePtr,
|
client: WaylandClientStatePtr,
|
||||||
callbacks: Callbacks,
|
callbacks: Callbacks,
|
||||||
|
@ -151,6 +152,7 @@ impl WaylandWindowState {
|
||||||
input_handler: None,
|
input_handler: None,
|
||||||
decoration_state: WaylandDecorationState::Client,
|
decoration_state: WaylandDecorationState::Client,
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
|
restore_bounds: Bounds::default(),
|
||||||
maximized: false,
|
maximized: false,
|
||||||
callbacks: Callbacks::default(),
|
callbacks: Callbacks::default(),
|
||||||
client,
|
client,
|
||||||
|
@ -332,10 +334,15 @@ impl WaylandWindowStatePtr {
|
||||||
let height = NonZeroU32::new(height as u32);
|
let height = NonZeroU32::new(height as u32);
|
||||||
let fullscreen = states.contains(&(xdg_toplevel::State::Fullscreen as u8));
|
let fullscreen = states.contains(&(xdg_toplevel::State::Fullscreen as u8));
|
||||||
let maximized = states.contains(&(xdg_toplevel::State::Maximized as u8));
|
let maximized = states.contains(&(xdg_toplevel::State::Maximized as u8));
|
||||||
self.resize(width, height);
|
|
||||||
self.set_fullscreen(fullscreen);
|
|
||||||
let mut state = self.state.borrow_mut();
|
let mut state = self.state.borrow_mut();
|
||||||
state.maximized = maximized;
|
state.maximized = maximized;
|
||||||
|
state.fullscreen = fullscreen;
|
||||||
|
if fullscreen || maximized {
|
||||||
|
state.restore_bounds = state.bounds.map(|p| DevicePixels(p as i32));
|
||||||
|
}
|
||||||
|
drop(state);
|
||||||
|
self.resize(width, height);
|
||||||
|
self.set_fullscreen(fullscreen);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -545,9 +552,17 @@ impl PlatformWindow for WaylandWindow {
|
||||||
self.borrow().maximized
|
self.borrow().maximized
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_minimized(&self) -> bool {
|
// todo(linux)
|
||||||
// This cannot be determined by the client
|
// check if it is right
|
||||||
false
|
fn window_bounds(&self) -> WindowBounds {
|
||||||
|
let state = self.borrow();
|
||||||
|
if state.fullscreen {
|
||||||
|
WindowBounds::Fullscreen(state.restore_bounds)
|
||||||
|
} else if state.maximized {
|
||||||
|
WindowBounds::Maximized(state.restore_bounds)
|
||||||
|
} else {
|
||||||
|
WindowBounds::Windowed(state.bounds.map(|p| DevicePixels(p as i32)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content_size(&self) -> Size<Pixels> {
|
fn content_size(&self) -> Size<Pixels> {
|
||||||
|
@ -679,7 +694,8 @@ impl PlatformWindow for WaylandWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_fullscreen(&self) {
|
fn toggle_fullscreen(&self) {
|
||||||
let state = self.borrow();
|
let mut state = self.borrow_mut();
|
||||||
|
state.restore_bounds = state.bounds.map(|p| DevicePixels(p as i32));
|
||||||
if !state.fullscreen {
|
if !state.fullscreen {
|
||||||
state.toplevel.set_fullscreen(None);
|
state.toplevel.set_fullscreen(None);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -5,8 +5,8 @@ use crate::{
|
||||||
platform::blade::{BladeRenderer, BladeSurfaceConfig},
|
platform::blade::{BladeRenderer, BladeSurfaceConfig},
|
||||||
size, Bounds, DevicePixels, ForegroundExecutor, Modifiers, Pixels, Platform, PlatformAtlas,
|
size, Bounds, DevicePixels, ForegroundExecutor, Modifiers, Pixels, Platform, PlatformAtlas,
|
||||||
PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PromptLevel,
|
PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PromptLevel,
|
||||||
Scene, Size, WindowAppearance, WindowBackgroundAppearance, WindowOptions, WindowParams,
|
Scene, Size, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowOptions,
|
||||||
X11Client, X11ClientState, X11ClientStatePtr,
|
WindowParams, X11Client, X11ClientState, X11ClientStatePtr,
|
||||||
};
|
};
|
||||||
use blade_graphics as gpu;
|
use blade_graphics as gpu;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -526,8 +526,9 @@ impl PlatformWindow for X11Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux)
|
// todo(linux)
|
||||||
fn is_minimized(&self) -> bool {
|
fn window_bounds(&self) -> WindowBounds {
|
||||||
false
|
let state = self.0.state.borrow();
|
||||||
|
WindowBounds::Windowed(state.bounds.map(|p| DevicePixels(p)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content_size(&self) -> Size<Pixels> {
|
fn content_size(&self) -> Size<Pixels> {
|
||||||
|
|
|
@ -4,7 +4,8 @@ use crate::{
|
||||||
DisplayLink, ExternalPaths, FileDropEvent, ForegroundExecutor, KeyDownEvent, Keystroke,
|
DisplayLink, ExternalPaths, FileDropEvent, ForegroundExecutor, KeyDownEvent, Keystroke,
|
||||||
Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
|
Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
|
||||||
Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel,
|
Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel,
|
||||||
Size, Timer, WindowAppearance, WindowBackgroundAppearance, WindowKind, WindowParams,
|
Size, Timer, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowKind,
|
||||||
|
WindowParams,
|
||||||
};
|
};
|
||||||
use block::ConcreteBlock;
|
use block::ConcreteBlock;
|
||||||
use cocoa::{
|
use cocoa::{
|
||||||
|
@ -259,6 +260,10 @@ unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const C
|
||||||
sel!(windowDidChangeOcclusionState:),
|
sel!(windowDidChangeOcclusionState:),
|
||||||
window_did_change_occlusion_state as extern "C" fn(&Object, Sel, id),
|
window_did_change_occlusion_state as extern "C" fn(&Object, Sel, id),
|
||||||
);
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(windowWillEnterFullScreen:),
|
||||||
|
window_will_enter_fullscreen as extern "C" fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
decl.add_method(
|
decl.add_method(
|
||||||
sel!(windowDidMove:),
|
sel!(windowDidMove:),
|
||||||
window_did_move as extern "C" fn(&Object, Sel, id),
|
window_did_move as extern "C" fn(&Object, Sel, id),
|
||||||
|
@ -302,14 +307,6 @@ unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const C
|
||||||
sel!(concludeDragOperation:),
|
sel!(concludeDragOperation:),
|
||||||
conclude_drag_operation as extern "C" fn(&Object, Sel, id),
|
conclude_drag_operation as extern "C" fn(&Object, Sel, id),
|
||||||
);
|
);
|
||||||
decl.add_method(
|
|
||||||
sel!(windowDidMiniaturize:),
|
|
||||||
window_did_miniaturize as extern "C" fn(&Object, Sel, id),
|
|
||||||
);
|
|
||||||
decl.add_method(
|
|
||||||
sel!(windowDidDeminiaturize:),
|
|
||||||
window_did_deminiaturize as extern "C" fn(&Object, Sel, id),
|
|
||||||
);
|
|
||||||
|
|
||||||
decl.register()
|
decl.register()
|
||||||
}
|
}
|
||||||
|
@ -348,7 +345,7 @@ struct MacWindowState {
|
||||||
external_files_dragged: bool,
|
external_files_dragged: bool,
|
||||||
// Whether the next left-mouse click is also the focusing click.
|
// Whether the next left-mouse click is also the focusing click.
|
||||||
first_mouse: bool,
|
first_mouse: bool,
|
||||||
minimized: bool,
|
fullscreen_restore_bounds: Bounds<DevicePixels>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MacWindowState {
|
impl MacWindowState {
|
||||||
|
@ -435,10 +432,6 @@ impl MacWindowState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_minimized(&self) -> bool {
|
|
||||||
self.minimized
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_fullscreen(&self) -> bool {
|
fn is_fullscreen(&self) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
let style_mask = self.native_window.styleMask();
|
let style_mask = self.native_window.styleMask();
|
||||||
|
@ -494,6 +487,14 @@ impl MacWindowState {
|
||||||
px((frame.size.height - content_layout_rect.size.height) as f32)
|
px((frame.size.height - content_layout_rect.size.height) as f32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn window_bounds(&self) -> WindowBounds {
|
||||||
|
if self.is_fullscreen() {
|
||||||
|
WindowBounds::Fullscreen(self.fullscreen_restore_bounds)
|
||||||
|
} else {
|
||||||
|
WindowBounds::Windowed(self.bounds())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for MacWindowState {}
|
unsafe impl Send for MacWindowState {}
|
||||||
|
@ -639,7 +640,7 @@ impl MacWindow {
|
||||||
previous_keydown_inserted_text: None,
|
previous_keydown_inserted_text: None,
|
||||||
external_files_dragged: false,
|
external_files_dragged: false,
|
||||||
first_mouse: false,
|
first_mouse: false,
|
||||||
minimized: false,
|
fullscreen_restore_bounds: Bounds::default(),
|
||||||
})));
|
})));
|
||||||
|
|
||||||
(*native_window).set_ivar(
|
(*native_window).set_ivar(
|
||||||
|
@ -775,12 +776,12 @@ impl PlatformWindow for MacWindow {
|
||||||
self.0.as_ref().lock().bounds()
|
self.0.as_ref().lock().bounds()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_maximized(&self) -> bool {
|
fn window_bounds(&self) -> WindowBounds {
|
||||||
self.0.as_ref().lock().is_maximized()
|
self.0.as_ref().lock().window_bounds()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_minimized(&self) -> bool {
|
fn is_maximized(&self) -> bool {
|
||||||
self.0.as_ref().lock().is_minimized()
|
self.0.as_ref().lock().is_maximized()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content_size(&self) -> Size<Pixels> {
|
fn content_size(&self) -> Size<Pixels> {
|
||||||
|
@ -1466,6 +1467,12 @@ extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
|
||||||
window_state.as_ref().lock().move_traffic_light();
|
window_state.as_ref().lock().move_traffic_light();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
||||||
|
let window_state = unsafe { get_window_state(this) };
|
||||||
|
let mut lock = window_state.as_ref().lock();
|
||||||
|
lock.fullscreen_restore_bounds = lock.bounds();
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" fn window_did_move(this: &Object, _: Sel, _: id) {
|
extern "C" fn window_did_move(this: &Object, _: Sel, _: id) {
|
||||||
let window_state = unsafe { get_window_state(this) };
|
let window_state = unsafe { get_window_state(this) };
|
||||||
let mut lock = window_state.as_ref().lock();
|
let mut lock = window_state.as_ref().lock();
|
||||||
|
@ -1863,18 +1870,6 @@ extern "C" fn conclude_drag_operation(this: &Object, _: Sel, _: id) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn window_did_miniaturize(this: &Object, _: Sel, _: id) {
|
|
||||||
let window_state = unsafe { get_window_state(this) };
|
|
||||||
|
|
||||||
window_state.lock().minimized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn window_did_deminiaturize(this: &Object, _: Sel, _: id) {
|
|
||||||
let window_state = unsafe { get_window_state(this) };
|
|
||||||
|
|
||||||
window_state.lock().minimized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn synthetic_drag(
|
async fn synthetic_drag(
|
||||||
window_state: Weak<Mutex<MacWindowState>>,
|
window_state: Weak<Mutex<MacWindowState>>,
|
||||||
drag_id: usize,
|
drag_id: usize,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DevicePixels,
|
AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DevicePixels,
|
||||||
DispatchEventResult, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
|
DispatchEventResult, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
|
||||||
PlatformInputHandler, PlatformWindow, Point, Size, TestPlatform, TileId, WindowAppearance,
|
PlatformInputHandler, PlatformWindow, Point, Size, TestPlatform, TileId, WindowAppearance,
|
||||||
WindowBackgroundAppearance, WindowParams,
|
WindowBackgroundAppearance, WindowBounds, WindowParams,
|
||||||
};
|
};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -112,11 +112,11 @@ impl PlatformWindow for TestWindow {
|
||||||
self.0.lock().bounds
|
self.0.lock().bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_maximized(&self) -> bool {
|
fn window_bounds(&self) -> WindowBounds {
|
||||||
false
|
WindowBounds::Windowed(self.bounds())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_minimized(&self) -> bool {
|
fn is_maximized(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ pub(crate) struct WindowsWindow(pub Rc<WindowsWindowStatePtr>);
|
||||||
pub struct WindowsWindowState {
|
pub struct WindowsWindowState {
|
||||||
pub origin: Point<DevicePixels>,
|
pub origin: Point<DevicePixels>,
|
||||||
pub physical_size: Size<DevicePixels>,
|
pub physical_size: Size<DevicePixels>,
|
||||||
|
pub fullscreen_restore_bounds: Bounds<DevicePixels>,
|
||||||
pub scale_factor: f32,
|
pub scale_factor: f32,
|
||||||
|
|
||||||
pub callbacks: Callbacks,
|
pub callbacks: Callbacks,
|
||||||
|
@ -71,6 +72,10 @@ impl WindowsWindowState {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let origin = point(cs.x.into(), cs.y.into());
|
let origin = point(cs.x.into(), cs.y.into());
|
||||||
let physical_size = size(cs.cx.into(), cs.cy.into());
|
let physical_size = size(cs.cx.into(), cs.cy.into());
|
||||||
|
let fullscreen_restore_bounds = Bounds {
|
||||||
|
origin,
|
||||||
|
size: physical_size,
|
||||||
|
};
|
||||||
let scale_factor = {
|
let scale_factor = {
|
||||||
let monitor_dpi = unsafe { GetDpiForWindow(hwnd) } as f32;
|
let monitor_dpi = unsafe { GetDpiForWindow(hwnd) } as f32;
|
||||||
monitor_dpi / USER_DEFAULT_SCREEN_DPI as f32
|
monitor_dpi / USER_DEFAULT_SCREEN_DPI as f32
|
||||||
|
@ -84,6 +89,7 @@ impl WindowsWindowState {
|
||||||
Self {
|
Self {
|
||||||
origin,
|
origin,
|
||||||
physical_size,
|
physical_size,
|
||||||
|
fullscreen_restore_bounds,
|
||||||
scale_factor,
|
scale_factor,
|
||||||
callbacks,
|
callbacks,
|
||||||
input_handler,
|
input_handler,
|
||||||
|
@ -113,6 +119,35 @@ impl WindowsWindowState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn window_bounds(&self) -> WindowBounds {
|
||||||
|
let placement = unsafe {
|
||||||
|
let mut placement = WINDOWPLACEMENT {
|
||||||
|
length: std::mem::size_of::<WINDOWPLACEMENT>() as u32,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
GetWindowPlacement(self.hwnd, &mut placement).log_err();
|
||||||
|
placement
|
||||||
|
};
|
||||||
|
let bounds = Bounds {
|
||||||
|
origin: point(
|
||||||
|
DevicePixels(placement.rcNormalPosition.left),
|
||||||
|
DevicePixels(placement.rcNormalPosition.top),
|
||||||
|
),
|
||||||
|
size: size(
|
||||||
|
DevicePixels(placement.rcNormalPosition.right - placement.rcNormalPosition.left),
|
||||||
|
DevicePixels(placement.rcNormalPosition.bottom - placement.rcNormalPosition.top),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.is_fullscreen() {
|
||||||
|
WindowBounds::Fullscreen(self.fullscreen_restore_bounds)
|
||||||
|
} else if placement.showCmd == SW_SHOWMAXIMIZED.0 as u32 {
|
||||||
|
WindowBounds::Maximized(bounds)
|
||||||
|
} else {
|
||||||
|
WindowBounds::Windowed(bounds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// get the logical size of the app's drawable area.
|
/// get the logical size of the app's drawable area.
|
||||||
///
|
///
|
||||||
/// Currently, GPUI uses logical size of the app to handle mouse interactions (such as
|
/// Currently, GPUI uses logical size of the app to handle mouse interactions (such as
|
||||||
|
@ -176,10 +211,6 @@ impl WindowsWindowStatePtr {
|
||||||
main_receiver: context.main_receiver.clone(),
|
main_receiver: context.main_receiver.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_minimized(&self) -> bool {
|
|
||||||
unsafe { IsIconic(self.hwnd) }.as_bool()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -209,7 +240,7 @@ struct WindowCreateContext {
|
||||||
impl WindowsWindow {
|
impl WindowsWindow {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
handle: AnyWindowHandle,
|
handle: AnyWindowHandle,
|
||||||
options: WindowParams,
|
params: WindowParams,
|
||||||
icon: HICON,
|
icon: HICON,
|
||||||
executor: ForegroundExecutor,
|
executor: ForegroundExecutor,
|
||||||
main_receiver: flume::Receiver<Runnable>,
|
main_receiver: flume::Receiver<Runnable>,
|
||||||
|
@ -217,13 +248,13 @@ impl WindowsWindow {
|
||||||
current_cursor: HCURSOR,
|
current_cursor: HCURSOR,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let classname = register_wnd_class(icon);
|
let classname = register_wnd_class(icon);
|
||||||
let hide_title_bar = options
|
let hide_title_bar = params
|
||||||
.titlebar
|
.titlebar
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|titlebar| titlebar.appears_transparent)
|
.map(|titlebar| titlebar.appears_transparent)
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
let windowname = HSTRING::from(
|
let windowname = HSTRING::from(
|
||||||
options
|
params
|
||||||
.titlebar
|
.titlebar
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|titlebar| titlebar.title.as_ref())
|
.and_then(|titlebar| titlebar.title.as_ref())
|
||||||
|
@ -231,12 +262,6 @@ impl WindowsWindow {
|
||||||
.unwrap_or(""),
|
.unwrap_or(""),
|
||||||
);
|
);
|
||||||
let dwstyle = WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX;
|
let dwstyle = WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX;
|
||||||
let x = options.bounds.origin.x.0;
|
|
||||||
let y = options.bounds.origin.y.0;
|
|
||||||
let nwidth = options.bounds.size.width.0;
|
|
||||||
let nheight = options.bounds.size.height.0;
|
|
||||||
let hwndparent = HWND::default();
|
|
||||||
let hmenu = HMENU::default();
|
|
||||||
let hinstance = get_module_handle();
|
let hinstance = get_module_handle();
|
||||||
let mut context = WindowCreateContext {
|
let mut context = WindowCreateContext {
|
||||||
inner: None,
|
inner: None,
|
||||||
|
@ -245,7 +270,7 @@ impl WindowsWindow {
|
||||||
// todo(windows) move window to target monitor
|
// todo(windows) move window to target monitor
|
||||||
// options.display_id
|
// options.display_id
|
||||||
display: WindowsDisplay::primary_monitor().unwrap(),
|
display: WindowsDisplay::primary_monitor().unwrap(),
|
||||||
transparent: options.window_background != WindowBackgroundAppearance::Opaque,
|
transparent: params.window_background != WindowBackgroundAppearance::Opaque,
|
||||||
executor,
|
executor,
|
||||||
main_receiver,
|
main_receiver,
|
||||||
mouse_wheel_settings,
|
mouse_wheel_settings,
|
||||||
|
@ -258,12 +283,12 @@ impl WindowsWindow {
|
||||||
classname,
|
classname,
|
||||||
&windowname,
|
&windowname,
|
||||||
dwstyle,
|
dwstyle,
|
||||||
x,
|
CW_USEDEFAULT,
|
||||||
y,
|
CW_USEDEFAULT,
|
||||||
nwidth,
|
CW_USEDEFAULT,
|
||||||
nheight,
|
CW_USEDEFAULT,
|
||||||
hwndparent,
|
None,
|
||||||
hmenu,
|
None,
|
||||||
hinstance,
|
hinstance,
|
||||||
lpparam,
|
lpparam,
|
||||||
)
|
)
|
||||||
|
@ -272,6 +297,18 @@ impl WindowsWindow {
|
||||||
register_drag_drop(state_ptr.clone());
|
register_drag_drop(state_ptr.clone());
|
||||||
let wnd = Self(state_ptr);
|
let wnd = Self(state_ptr);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let mut placement = WINDOWPLACEMENT {
|
||||||
|
length: std::mem::size_of::<WINDOWPLACEMENT>() as u32,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
GetWindowPlacement(raw_hwnd, &mut placement).log_err();
|
||||||
|
placement.rcNormalPosition.left = params.bounds.left().0;
|
||||||
|
placement.rcNormalPosition.right = params.bounds.right().0;
|
||||||
|
placement.rcNormalPosition.top = params.bounds.top().0;
|
||||||
|
placement.rcNormalPosition.bottom = params.bounds.bottom().0;
|
||||||
|
SetWindowPlacement(raw_hwnd, &placement).log_err();
|
||||||
|
}
|
||||||
unsafe { ShowWindow(raw_hwnd, SW_SHOW) };
|
unsafe { ShowWindow(raw_hwnd, SW_SHOW) };
|
||||||
|
|
||||||
wnd
|
wnd
|
||||||
|
@ -321,8 +358,8 @@ impl PlatformWindow for WindowsWindow {
|
||||||
self.0.state.borrow().is_maximized()
|
self.0.state.borrow().is_maximized()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_minimized(&self) -> bool {
|
fn window_bounds(&self) -> WindowBounds {
|
||||||
self.0.is_minimized()
|
self.0.state.borrow().window_bounds()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get the logical size of the app's drawable area.
|
/// get the logical size of the app's drawable area.
|
||||||
|
@ -493,6 +530,10 @@ impl PlatformWindow for WindowsWindow {
|
||||||
.executor
|
.executor
|
||||||
.spawn(async move {
|
.spawn(async move {
|
||||||
let mut lock = state_ptr.state.borrow_mut();
|
let mut lock = state_ptr.state.borrow_mut();
|
||||||
|
lock.fullscreen_restore_bounds = Bounds {
|
||||||
|
origin: lock.origin,
|
||||||
|
size: lock.physical_size,
|
||||||
|
};
|
||||||
let StyleAndBounds {
|
let StyleAndBounds {
|
||||||
style,
|
style,
|
||||||
x,
|
x,
|
||||||
|
|
|
@ -12,8 +12,8 @@ use crate::{
|
||||||
RenderSvgParams, ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style,
|
RenderSvgParams, ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style,
|
||||||
SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement,
|
SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement,
|
||||||
TransformationMatrix, Underline, UnderlineStyle, View, VisualContext, WeakView,
|
TransformationMatrix, Underline, UnderlineStyle, View, VisualContext, WeakView,
|
||||||
WindowAppearance, WindowBackgroundAppearance, WindowOptions, WindowParams, WindowTextSystem,
|
WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowOptions, WindowParams,
|
||||||
SUBPIXEL_VARIANTS,
|
WindowTextSystem, SUBPIXEL_VARIANTS,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context as _, Result};
|
use anyhow::{anyhow, Context as _, Result};
|
||||||
use collections::{FxHashMap, FxHashSet};
|
use collections::{FxHashMap, FxHashSet};
|
||||||
|
@ -565,7 +565,7 @@ fn default_bounds(display_id: Option<DisplayId>, cx: &mut AppContext) -> Bounds<
|
||||||
const DEFAULT_WINDOW_OFFSET: Point<DevicePixels> = point(DevicePixels(0), DevicePixels(35));
|
const DEFAULT_WINDOW_OFFSET: Point<DevicePixels> = point(DevicePixels(0), DevicePixels(35));
|
||||||
|
|
||||||
cx.active_window()
|
cx.active_window()
|
||||||
.and_then(|w| w.update(cx, |_, cx| cx.window_bounds()).ok())
|
.and_then(|w| w.update(cx, |_, cx| cx.bounds()).ok())
|
||||||
.map(|bounds| bounds.map_origin(|origin| origin + DEFAULT_WINDOW_OFFSET))
|
.map(|bounds| bounds.map_origin(|origin| origin + DEFAULT_WINDOW_OFFSET))
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
let display = display_id
|
let display = display_id
|
||||||
|
@ -592,19 +592,20 @@ impl Window {
|
||||||
cx: &mut AppContext,
|
cx: &mut AppContext,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let WindowOptions {
|
let WindowOptions {
|
||||||
bounds,
|
window_bounds,
|
||||||
titlebar,
|
titlebar,
|
||||||
focus,
|
focus,
|
||||||
show,
|
show,
|
||||||
kind,
|
kind,
|
||||||
is_movable,
|
is_movable,
|
||||||
display_id,
|
display_id,
|
||||||
fullscreen,
|
|
||||||
window_background,
|
window_background,
|
||||||
app_id,
|
app_id,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
let bounds = bounds.unwrap_or_else(|| default_bounds(display_id, cx));
|
let bounds = window_bounds
|
||||||
|
.map(|bounds| bounds.get_bounds())
|
||||||
|
.unwrap_or_else(|| default_bounds(display_id, cx));
|
||||||
let mut platform_window = cx.platform.open_window(
|
let mut platform_window = cx.platform.open_window(
|
||||||
handle,
|
handle,
|
||||||
WindowParams {
|
WindowParams {
|
||||||
|
@ -632,8 +633,12 @@ impl Window {
|
||||||
let next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>> = Default::default();
|
let next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>> = Default::default();
|
||||||
let last_input_timestamp = Rc::new(Cell::new(Instant::now()));
|
let last_input_timestamp = Rc::new(Cell::new(Instant::now()));
|
||||||
|
|
||||||
if fullscreen {
|
if let Some(ref window_open_state) = window_bounds {
|
||||||
platform_window.toggle_fullscreen();
|
match window_open_state {
|
||||||
|
WindowBounds::Fullscreen(_) => platform_window.toggle_fullscreen(),
|
||||||
|
WindowBounds::Maximized(_) => platform_window.zoom(),
|
||||||
|
WindowBounds::Windowed(_) => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_window.on_close(Box::new({
|
platform_window.on_close(Box::new({
|
||||||
|
@ -691,7 +696,7 @@ impl Window {
|
||||||
let mut cx = cx.to_async();
|
let mut cx = cx.to_async();
|
||||||
move |_, _| {
|
move |_, _| {
|
||||||
handle
|
handle
|
||||||
.update(&mut cx, |_, cx| cx.window_bounds_changed())
|
.update(&mut cx, |_, cx| cx.bounds_changed())
|
||||||
.log_err();
|
.log_err();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -699,7 +704,7 @@ impl Window {
|
||||||
let mut cx = cx.to_async();
|
let mut cx = cx.to_async();
|
||||||
move || {
|
move || {
|
||||||
handle
|
handle
|
||||||
.update(&mut cx, |_, cx| cx.window_bounds_changed())
|
.update(&mut cx, |_, cx| cx.bounds_changed())
|
||||||
.log_err();
|
.log_err();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -943,10 +948,10 @@ impl<'a> WindowContext<'a> {
|
||||||
self.window.platform_window.is_maximized()
|
self.window.platform_window.is_maximized()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the platform window is minimized
|
/// Return the `WindowBounds` to indicate that how a window should be opened
|
||||||
/// On some platforms (namely Windows) the position is incorrect when minimized
|
/// after it has been closed
|
||||||
pub fn is_minimized(&self) -> bool {
|
pub fn window_bounds(&self) -> WindowBounds {
|
||||||
self.window.platform_window.is_minimized()
|
self.window.platform_window.window_bounds()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dispatch the given action on the currently focused element.
|
/// Dispatch the given action on the currently focused element.
|
||||||
|
@ -1075,7 +1080,7 @@ impl<'a> WindowContext<'a> {
|
||||||
.spawn(|app| f(AsyncWindowContext::new(app, self.window.handle)))
|
.spawn(|app| f(AsyncWindowContext::new(app, self.window.handle)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_bounds_changed(&mut self) {
|
fn bounds_changed(&mut self) {
|
||||||
self.window.scale_factor = self.window.platform_window.scale_factor();
|
self.window.scale_factor = self.window.platform_window.scale_factor();
|
||||||
self.window.viewport_size = self.window.platform_window.content_size();
|
self.window.viewport_size = self.window.platform_window.content_size();
|
||||||
self.window.display_id = self.window.platform_window.display().id();
|
self.window.display_id = self.window.platform_window.display().id();
|
||||||
|
@ -1088,7 +1093,7 @@ impl<'a> WindowContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the bounds of the current window in the global coordinate space, which could span across multiple displays.
|
/// Returns the bounds of the current window in the global coordinate space, which could span across multiple displays.
|
||||||
pub fn window_bounds(&self) -> Bounds<DevicePixels> {
|
pub fn bounds(&self) -> Bounds<DevicePixels> {
|
||||||
self.window.platform_window.bounds()
|
self.window.platform_window.bounds()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,8 @@ mod story_selector;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use dialoguer::FuzzySelect;
|
use dialoguer::FuzzySelect;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, px, size, AnyView, AppContext, Bounds, Render, ViewContext, VisualContext, WindowOptions,
|
div, px, size, AnyView, AppContext, Bounds, Render, ViewContext, VisualContext, WindowBounds,
|
||||||
|
WindowOptions,
|
||||||
};
|
};
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use project::Project;
|
use project::Project;
|
||||||
|
@ -85,7 +86,7 @@ fn main() {
|
||||||
let bounds = Bounds::centered(None, size, cx);
|
let bounds = Bounds::centered(None, size, cx);
|
||||||
let _window = cx.open_window(
|
let _window = cx.open_window(
|
||||||
WindowOptions {
|
WindowOptions {
|
||||||
bounds: Some(bounds),
|
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
move |cx| {
|
move |cx| {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::path::Path;
|
||||||
use anyhow::{anyhow, bail, Context, Result};
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
use client::DevServerProjectId;
|
use client::DevServerProjectId;
|
||||||
use db::{define_connection, query, sqlez::connection::Connection, sqlez_macros::sql};
|
use db::{define_connection, query, sqlez::connection::Connection, sqlez_macros::sql};
|
||||||
use gpui::{point, size, Axis, Bounds};
|
use gpui::{point, size, Axis, Bounds, WindowBounds};
|
||||||
|
|
||||||
use sqlez::{
|
use sqlez::{
|
||||||
bindable::{Bind, Column, StaticColumnCount},
|
bindable::{Bind, Column, StaticColumnCount},
|
||||||
|
@ -59,50 +59,99 @@ impl sqlez::bindable::Column for SerializedAxis {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Default)]
|
||||||
pub(crate) struct SerializedWindowsBounds(pub(crate) Bounds<gpui::DevicePixels>);
|
pub(crate) struct SerializedWindowBounds(pub(crate) WindowBounds);
|
||||||
|
|
||||||
impl StaticColumnCount for SerializedWindowsBounds {
|
impl StaticColumnCount for SerializedWindowBounds {
|
||||||
fn column_count() -> usize {
|
fn column_count() -> usize {
|
||||||
5
|
5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bind for SerializedWindowsBounds {
|
impl Bind for SerializedWindowBounds {
|
||||||
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
|
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
|
||||||
let next_index = statement.bind(&"Fixed", start_index)?;
|
match self.0 {
|
||||||
|
WindowBounds::Windowed(bounds) => {
|
||||||
|
let next_index = statement.bind(&"Windowed", start_index)?;
|
||||||
statement.bind(
|
statement.bind(
|
||||||
&(
|
&(
|
||||||
SerializedDevicePixels(self.0.origin.x),
|
SerializedDevicePixels(bounds.origin.x),
|
||||||
SerializedDevicePixels(self.0.origin.y),
|
SerializedDevicePixels(bounds.origin.y),
|
||||||
SerializedDevicePixels(self.0.size.width),
|
SerializedDevicePixels(bounds.size.width),
|
||||||
SerializedDevicePixels(self.0.size.height),
|
SerializedDevicePixels(bounds.size.height),
|
||||||
|
),
|
||||||
|
next_index,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
WindowBounds::Maximized(bounds) => {
|
||||||
|
let next_index = statement.bind(&"Maximized", start_index)?;
|
||||||
|
statement.bind(
|
||||||
|
&(
|
||||||
|
SerializedDevicePixels(bounds.origin.x),
|
||||||
|
SerializedDevicePixels(bounds.origin.y),
|
||||||
|
SerializedDevicePixels(bounds.size.width),
|
||||||
|
SerializedDevicePixels(bounds.size.height),
|
||||||
|
),
|
||||||
|
next_index,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
WindowBounds::Fullscreen(bounds) => {
|
||||||
|
let next_index = statement.bind(&"FullScreen", start_index)?;
|
||||||
|
statement.bind(
|
||||||
|
&(
|
||||||
|
SerializedDevicePixels(bounds.origin.x),
|
||||||
|
SerializedDevicePixels(bounds.origin.y),
|
||||||
|
SerializedDevicePixels(bounds.size.width),
|
||||||
|
SerializedDevicePixels(bounds.size.height),
|
||||||
),
|
),
|
||||||
next_index,
|
next_index,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Column for SerializedWindowsBounds {
|
impl Column for SerializedWindowBounds {
|
||||||
fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
|
fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
|
||||||
let (window_state, next_index) = String::column(statement, start_index)?;
|
let (window_state, next_index) = String::column(statement, start_index)?;
|
||||||
let bounds = match window_state.as_str() {
|
let status = match window_state.as_str() {
|
||||||
"Fixed" => {
|
"Windowed" | "Fixed" => {
|
||||||
let ((x, y, width, height), _) = Column::column(statement, next_index)?;
|
let ((x, y, width, height), _) = Column::column(statement, next_index)?;
|
||||||
let x: i32 = x;
|
let x: i32 = x;
|
||||||
let y: i32 = y;
|
let y: i32 = y;
|
||||||
let width: i32 = width;
|
let width: i32 = width;
|
||||||
let height: i32 = height;
|
let height: i32 = height;
|
||||||
SerializedWindowsBounds(Bounds {
|
SerializedWindowBounds(WindowBounds::Windowed(Bounds {
|
||||||
origin: point(x.into(), y.into()),
|
origin: point(x.into(), y.into()),
|
||||||
size: size(width.into(), height.into()),
|
size: size(width.into(), height.into()),
|
||||||
})
|
}))
|
||||||
|
}
|
||||||
|
"Maximized" => {
|
||||||
|
let ((x, y, width, height), _) = Column::column(statement, next_index)?;
|
||||||
|
let x: i32 = x;
|
||||||
|
let y: i32 = y;
|
||||||
|
let width: i32 = width;
|
||||||
|
let height: i32 = height;
|
||||||
|
SerializedWindowBounds(WindowBounds::Maximized(Bounds {
|
||||||
|
origin: point(x.into(), y.into()),
|
||||||
|
size: size(width.into(), height.into()),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
"FullScreen" => {
|
||||||
|
let ((x, y, width, height), _) = Column::column(statement, next_index)?;
|
||||||
|
let x: i32 = x;
|
||||||
|
let y: i32 = y;
|
||||||
|
let width: i32 = width;
|
||||||
|
let height: i32 = height;
|
||||||
|
SerializedWindowBounds(WindowBounds::Fullscreen(Bounds {
|
||||||
|
origin: point(x.into(), y.into()),
|
||||||
|
size: size(width.into(), height.into()),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
_ => bail!("Window State did not have a valid string"),
|
_ => bail!("Window State did not have a valid string"),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((bounds, next_index + 4))
|
Ok((status, next_index + 4))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,6 +328,8 @@ define_connection! {
|
||||||
ALTER TABLE pane_groups ADD COLUMN flexes TEXT;
|
ALTER TABLE pane_groups ADD COLUMN flexes TEXT;
|
||||||
),
|
),
|
||||||
// Add fullscreen field to workspace
|
// Add fullscreen field to workspace
|
||||||
|
// Deprecated, `WindowBounds` holds the fullscreen state now.
|
||||||
|
// Preserving so users can downgrade Zed.
|
||||||
sql!(
|
sql!(
|
||||||
ALTER TABLE workspaces ADD COLUMN fullscreen INTEGER; //bool
|
ALTER TABLE workspaces ADD COLUMN fullscreen INTEGER; //bool
|
||||||
),
|
),
|
||||||
|
@ -328,19 +379,17 @@ impl WorkspaceDb {
|
||||||
workspace_id,
|
workspace_id,
|
||||||
local_paths,
|
local_paths,
|
||||||
dev_server_project_id,
|
dev_server_project_id,
|
||||||
bounds,
|
window_bounds,
|
||||||
display,
|
display,
|
||||||
fullscreen,
|
|
||||||
centered_layout,
|
centered_layout,
|
||||||
docks,
|
docks,
|
||||||
): (
|
): (
|
||||||
WorkspaceId,
|
WorkspaceId,
|
||||||
Option<LocalPaths>,
|
Option<LocalPaths>,
|
||||||
Option<u64>,
|
Option<u64>,
|
||||||
Option<SerializedWindowsBounds>,
|
Option<SerializedWindowBounds>,
|
||||||
Option<Uuid>,
|
Option<Uuid>,
|
||||||
Option<bool>,
|
Option<bool>,
|
||||||
Option<bool>,
|
|
||||||
DockStructure,
|
DockStructure,
|
||||||
) = self
|
) = self
|
||||||
.select_row_bound(sql! {
|
.select_row_bound(sql! {
|
||||||
|
@ -354,7 +403,6 @@ impl WorkspaceDb {
|
||||||
window_width,
|
window_width,
|
||||||
window_height,
|
window_height,
|
||||||
display,
|
display,
|
||||||
fullscreen,
|
|
||||||
centered_layout,
|
centered_layout,
|
||||||
left_dock_visible,
|
left_dock_visible,
|
||||||
left_dock_active_panel,
|
left_dock_active_panel,
|
||||||
|
@ -398,8 +446,7 @@ impl WorkspaceDb {
|
||||||
.get_center_pane_group(workspace_id)
|
.get_center_pane_group(workspace_id)
|
||||||
.context("Getting center group")
|
.context("Getting center group")
|
||||||
.log_err()?,
|
.log_err()?,
|
||||||
bounds: bounds.map(|bounds| bounds.0),
|
window_bounds,
|
||||||
fullscreen: fullscreen.unwrap_or(false),
|
|
||||||
centered_layout: centered_layout.unwrap_or(false),
|
centered_layout: centered_layout.unwrap_or(false),
|
||||||
display,
|
display,
|
||||||
docks,
|
docks,
|
||||||
|
@ -549,13 +596,12 @@ impl WorkspaceDb {
|
||||||
|
|
||||||
pub(crate) fn last_window(
|
pub(crate) fn last_window(
|
||||||
&self,
|
&self,
|
||||||
) -> anyhow::Result<(Option<Uuid>, Option<SerializedWindowsBounds>, Option<bool>)> {
|
) -> anyhow::Result<(Option<Uuid>, Option<SerializedWindowBounds>)> {
|
||||||
let mut prepared_query =
|
let mut prepared_query =
|
||||||
self.select::<(Option<Uuid>, Option<SerializedWindowsBounds>, Option<bool>)>(sql!(
|
self.select::<(Option<Uuid>, Option<SerializedWindowBounds>)>(sql!(
|
||||||
SELECT
|
SELECT
|
||||||
display,
|
display,
|
||||||
window_state, window_x, window_y, window_width, window_height,
|
window_state, window_x, window_y, window_width, window_height
|
||||||
fullscreen
|
|
||||||
FROM workspaces
|
FROM workspaces
|
||||||
WHERE local_paths
|
WHERE local_paths
|
||||||
IS NOT NULL
|
IS NOT NULL
|
||||||
|
@ -563,10 +609,7 @@ impl WorkspaceDb {
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
))?;
|
))?;
|
||||||
let result = prepared_query()?;
|
let result = prepared_query()?;
|
||||||
Ok(result
|
Ok(result.into_iter().next().unwrap_or_else(|| (None, None)))
|
||||||
.into_iter()
|
|
||||||
.next()
|
|
||||||
.unwrap_or_else(|| (None, None, None)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
query! {
|
query! {
|
||||||
|
@ -829,7 +872,7 @@ impl WorkspaceDb {
|
||||||
}
|
}
|
||||||
|
|
||||||
query! {
|
query! {
|
||||||
pub(crate) async fn set_window_bounds(workspace_id: WorkspaceId, bounds: SerializedWindowsBounds, display: Uuid) -> Result<()> {
|
pub(crate) async fn set_window_open_status(workspace_id: WorkspaceId, bounds: SerializedWindowBounds, display: Uuid) -> Result<()> {
|
||||||
UPDATE workspaces
|
UPDATE workspaces
|
||||||
SET window_state = ?2,
|
SET window_state = ?2,
|
||||||
window_x = ?3,
|
window_x = ?3,
|
||||||
|
@ -841,14 +884,6 @@ impl WorkspaceDb {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query! {
|
|
||||||
pub(crate) async fn set_fullscreen(workspace_id: WorkspaceId, fullscreen: bool) -> Result<()> {
|
|
||||||
UPDATE workspaces
|
|
||||||
SET fullscreen = ?2
|
|
||||||
WHERE workspace_id = ?1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
query! {
|
query! {
|
||||||
pub(crate) async fn set_centered_layout(workspace_id: WorkspaceId, centered_layout: bool) -> Result<()> {
|
pub(crate) async fn set_centered_layout(workspace_id: WorkspaceId, centered_layout: bool) -> Result<()> {
|
||||||
UPDATE workspaces
|
UPDATE workspaces
|
||||||
|
@ -938,10 +973,9 @@ mod tests {
|
||||||
id: WorkspaceId(1),
|
id: WorkspaceId(1),
|
||||||
location: LocalPaths::new(["/tmp", "/tmp2"]).into(),
|
location: LocalPaths::new(["/tmp", "/tmp2"]).into(),
|
||||||
center_group: Default::default(),
|
center_group: Default::default(),
|
||||||
bounds: Default::default(),
|
window_bounds: Default::default(),
|
||||||
display: Default::default(),
|
display: Default::default(),
|
||||||
docks: Default::default(),
|
docks: Default::default(),
|
||||||
fullscreen: false,
|
|
||||||
centered_layout: false,
|
centered_layout: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -949,10 +983,9 @@ mod tests {
|
||||||
id: WorkspaceId(2),
|
id: WorkspaceId(2),
|
||||||
location: LocalPaths::new(["/tmp"]).into(),
|
location: LocalPaths::new(["/tmp"]).into(),
|
||||||
center_group: Default::default(),
|
center_group: Default::default(),
|
||||||
bounds: Default::default(),
|
window_bounds: Default::default(),
|
||||||
display: Default::default(),
|
display: Default::default(),
|
||||||
docks: Default::default(),
|
docks: Default::default(),
|
||||||
fullscreen: false,
|
|
||||||
centered_layout: false,
|
centered_layout: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1049,10 +1082,9 @@ mod tests {
|
||||||
id: WorkspaceId(5),
|
id: WorkspaceId(5),
|
||||||
location: LocalPaths::new(["/tmp", "/tmp2"]).into(),
|
location: LocalPaths::new(["/tmp", "/tmp2"]).into(),
|
||||||
center_group,
|
center_group,
|
||||||
bounds: Default::default(),
|
window_bounds: Default::default(),
|
||||||
display: Default::default(),
|
display: Default::default(),
|
||||||
docks: Default::default(),
|
docks: Default::default(),
|
||||||
fullscreen: false,
|
|
||||||
centered_layout: false,
|
centered_layout: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1079,10 +1111,9 @@ mod tests {
|
||||||
id: WorkspaceId(1),
|
id: WorkspaceId(1),
|
||||||
location: LocalPaths::new(["/tmp", "/tmp2"]).into(),
|
location: LocalPaths::new(["/tmp", "/tmp2"]).into(),
|
||||||
center_group: Default::default(),
|
center_group: Default::default(),
|
||||||
bounds: Default::default(),
|
window_bounds: Default::default(),
|
||||||
display: Default::default(),
|
display: Default::default(),
|
||||||
docks: Default::default(),
|
docks: Default::default(),
|
||||||
fullscreen: false,
|
|
||||||
centered_layout: false,
|
centered_layout: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1090,10 +1121,9 @@ mod tests {
|
||||||
id: WorkspaceId(2),
|
id: WorkspaceId(2),
|
||||||
location: LocalPaths::new(["/tmp"]).into(),
|
location: LocalPaths::new(["/tmp"]).into(),
|
||||||
center_group: Default::default(),
|
center_group: Default::default(),
|
||||||
bounds: Default::default(),
|
window_bounds: Default::default(),
|
||||||
display: Default::default(),
|
display: Default::default(),
|
||||||
docks: Default::default(),
|
docks: Default::default(),
|
||||||
fullscreen: false,
|
|
||||||
centered_layout: false,
|
centered_layout: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1128,10 +1158,9 @@ mod tests {
|
||||||
id: WorkspaceId(3),
|
id: WorkspaceId(3),
|
||||||
location: LocalPaths::new(&["/tmp", "/tmp2"]).into(),
|
location: LocalPaths::new(&["/tmp", "/tmp2"]).into(),
|
||||||
center_group: Default::default(),
|
center_group: Default::default(),
|
||||||
bounds: Default::default(),
|
window_bounds: Default::default(),
|
||||||
display: Default::default(),
|
display: Default::default(),
|
||||||
docks: Default::default(),
|
docks: Default::default(),
|
||||||
fullscreen: false,
|
|
||||||
centered_layout: false,
|
centered_layout: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1163,10 +1192,9 @@ mod tests {
|
||||||
id: WorkspaceId(4),
|
id: WorkspaceId(4),
|
||||||
location: LocalPaths::new(workspace_id).into(),
|
location: LocalPaths::new(workspace_id).into(),
|
||||||
center_group: center_group.clone(),
|
center_group: center_group.clone(),
|
||||||
bounds: Default::default(),
|
window_bounds: Default::default(),
|
||||||
display: Default::default(),
|
display: Default::default(),
|
||||||
docks: Default::default(),
|
docks: Default::default(),
|
||||||
fullscreen: false,
|
|
||||||
centered_layout: false,
|
centered_layout: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::SerializedAxis;
|
use super::{SerializedAxis, SerializedWindowBounds};
|
||||||
use crate::{item::ItemHandle, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId};
|
use crate::{item::ItemHandle, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
|
@ -7,7 +7,7 @@ use db::sqlez::{
|
||||||
bindable::{Bind, Column, StaticColumnCount},
|
bindable::{Bind, Column, StaticColumnCount},
|
||||||
statement::Statement,
|
statement::Statement,
|
||||||
};
|
};
|
||||||
use gpui::{AsyncWindowContext, Bounds, DevicePixels, Model, Task, View, WeakView};
|
use gpui::{AsyncWindowContext, Model, Task, View, WeakView};
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -110,8 +110,7 @@ pub(crate) struct SerializedWorkspace {
|
||||||
pub(crate) id: WorkspaceId,
|
pub(crate) id: WorkspaceId,
|
||||||
pub(crate) location: SerializedWorkspaceLocation,
|
pub(crate) location: SerializedWorkspaceLocation,
|
||||||
pub(crate) center_group: SerializedPaneGroup,
|
pub(crate) center_group: SerializedPaneGroup,
|
||||||
pub(crate) bounds: Option<Bounds<DevicePixels>>,
|
pub(crate) window_bounds: Option<SerializedWindowBounds>,
|
||||||
pub(crate) fullscreen: bool,
|
|
||||||
pub(crate) centered_layout: bool,
|
pub(crate) centered_layout: bool,
|
||||||
pub(crate) display: Option<Uuid>,
|
pub(crate) display: Option<Uuid>,
|
||||||
pub(crate) docks: DockStructure,
|
pub(crate) docks: DockStructure,
|
||||||
|
|
|
@ -32,7 +32,7 @@ use gpui::{
|
||||||
ElementId, Entity as _, EntityId, EventEmitter, FocusHandle, FocusableView, Global,
|
ElementId, Entity as _, EntityId, EventEmitter, FocusHandle, FocusableView, Global,
|
||||||
GlobalElementId, KeyContext, Keystroke, LayoutId, ManagedView, Model, ModelContext,
|
GlobalElementId, KeyContext, Keystroke, LayoutId, ManagedView, Model, ModelContext,
|
||||||
PathPromptOptions, Point, PromptLevel, Render, Size, Subscription, Task, View, WeakView,
|
PathPromptOptions, Point, PromptLevel, Render, Size, Subscription, Task, View, WeakView,
|
||||||
WindowHandle, WindowOptions,
|
WindowBounds, WindowHandle, WindowOptions,
|
||||||
};
|
};
|
||||||
use item::{
|
use item::{
|
||||||
FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, PreviewTabsSettings,
|
FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, PreviewTabsSettings,
|
||||||
|
@ -46,7 +46,7 @@ use node_runtime::NodeRuntime;
|
||||||
use notifications::{simple_message_notification::MessageNotification, NotificationHandle};
|
use notifications::{simple_message_notification::MessageNotification, NotificationHandle};
|
||||||
pub use pane::*;
|
pub use pane::*;
|
||||||
pub use pane_group::*;
|
pub use pane_group::*;
|
||||||
use persistence::{model::SerializedWorkspace, SerializedWindowsBounds, DB};
|
use persistence::{model::SerializedWorkspace, SerializedWindowBounds, DB};
|
||||||
pub use persistence::{
|
pub use persistence::{
|
||||||
model::{ItemId, LocalPaths, SerializedDevServerProject, SerializedWorkspaceLocation},
|
model::{ItemId, LocalPaths, SerializedDevServerProject, SerializedWorkspaceLocation},
|
||||||
WorkspaceDb, DB as WORKSPACE_DB,
|
WorkspaceDb, DB as WORKSPACE_DB,
|
||||||
|
@ -785,31 +785,17 @@ impl Workspace {
|
||||||
.await;
|
.await;
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
if let Some(display) = cx.display() {
|
if let Some(display) = cx.display() {
|
||||||
let window_bounds = cx.window_bounds();
|
|
||||||
let fullscreen = cx.is_fullscreen();
|
|
||||||
|
|
||||||
if let Some(display_uuid) = display.uuid().log_err() {
|
if let Some(display_uuid) = display.uuid().log_err() {
|
||||||
// Only update the window bounds when not full screen,
|
let window_bounds = cx.window_bounds();
|
||||||
// so we can remember the last non-fullscreen bounds
|
|
||||||
// across restarts
|
|
||||||
if fullscreen {
|
|
||||||
cx.background_executor()
|
cx.background_executor()
|
||||||
.spawn(DB.set_fullscreen(workspace_id, true))
|
.spawn(DB.set_window_open_status(
|
||||||
.detach_and_log_err(cx);
|
|
||||||
} else if !cx.is_minimized() {
|
|
||||||
cx.background_executor()
|
|
||||||
.spawn(DB.set_fullscreen(workspace_id, false))
|
|
||||||
.detach_and_log_err(cx);
|
|
||||||
cx.background_executor()
|
|
||||||
.spawn(DB.set_window_bounds(
|
|
||||||
workspace_id,
|
workspace_id,
|
||||||
SerializedWindowsBounds(window_bounds),
|
SerializedWindowBounds(window_bounds),
|
||||||
display_uuid,
|
display_uuid,
|
||||||
))
|
))
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
this.bounds_save_task_queued.take();
|
this.bounds_save_task_queued.take();
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
|
@ -947,30 +933,27 @@ impl Workspace {
|
||||||
} else {
|
} else {
|
||||||
let window_bounds_override = window_bounds_env_override();
|
let window_bounds_override = window_bounds_env_override();
|
||||||
|
|
||||||
let (bounds, display, fullscreen) = if let Some(bounds) = window_bounds_override {
|
let (window_bounds, display) = if let Some(bounds) = window_bounds_override {
|
||||||
(Some(bounds), None, false)
|
(Some(WindowBounds::Windowed(bounds)), None)
|
||||||
} else {
|
} else {
|
||||||
let restorable_bounds = serialized_workspace
|
let restorable_bounds = serialized_workspace
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|workspace| {
|
.and_then(|workspace| Some((workspace.display?, workspace.window_bounds?)))
|
||||||
Some((workspace.display?, workspace.bounds?, workspace.fullscreen))
|
|
||||||
})
|
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
let (display, bounds, fullscreen) = DB.last_window().log_err()?;
|
let (display, window_bounds) = DB.last_window().log_err()?;
|
||||||
Some((display?, bounds?.0, fullscreen.unwrap_or(false)))
|
Some((display?, window_bounds?))
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some((serialized_display, bounds, fullscreen)) = restorable_bounds {
|
if let Some((serialized_display, serialized_status)) = restorable_bounds {
|
||||||
(Some(bounds), Some(serialized_display), fullscreen)
|
(Some(serialized_status.0), Some(serialized_display))
|
||||||
} else {
|
} else {
|
||||||
(None, None, false)
|
(None, None)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use the serialized workspace to construct the new window
|
// Use the serialized workspace to construct the new window
|
||||||
let mut options = cx.update(|cx| (app_state.build_window_options)(display, cx))?;
|
let mut options = cx.update(|cx| (app_state.build_window_options)(display, cx))?;
|
||||||
options.bounds = bounds;
|
options.window_bounds = window_bounds;
|
||||||
options.fullscreen = fullscreen;
|
|
||||||
let centered_layout = serialized_workspace
|
let centered_layout = serialized_workspace
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|w| w.centered_layout)
|
.map(|w| w.centered_layout)
|
||||||
|
@ -3667,14 +3650,14 @@ impl Workspace {
|
||||||
if let Some(location) = location {
|
if let Some(location) = location {
|
||||||
let center_group = build_serialized_pane_group(&self.center.root, cx);
|
let center_group = build_serialized_pane_group(&self.center.root, cx);
|
||||||
let docks = build_serialized_docks(self, cx);
|
let docks = build_serialized_docks(self, cx);
|
||||||
|
let window_bounds = Some(SerializedWindowBounds(cx.window_bounds()));
|
||||||
let serialized_workspace = SerializedWorkspace {
|
let serialized_workspace = SerializedWorkspace {
|
||||||
id: self.database_id,
|
id: self.database_id,
|
||||||
location,
|
location,
|
||||||
center_group,
|
center_group,
|
||||||
bounds: Default::default(),
|
window_bounds,
|
||||||
display: Default::default(),
|
display: Default::default(),
|
||||||
docks,
|
docks,
|
||||||
fullscreen: cx.is_fullscreen(),
|
|
||||||
centered_layout: self.centered_layout,
|
centered_layout: self.centered_layout,
|
||||||
};
|
};
|
||||||
return cx.spawn(|_| persistence::DB.save_workspace(serialized_workspace));
|
return cx.spawn(|_| persistence::DB.save_workspace(serialized_workspace));
|
||||||
|
@ -4867,7 +4850,8 @@ pub fn join_hosted_project(
|
||||||
let window_bounds_override = window_bounds_env_override();
|
let window_bounds_override = window_bounds_env_override();
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
let mut options = (app_state.build_window_options)(None, cx);
|
let mut options = (app_state.build_window_options)(None, cx);
|
||||||
options.bounds = window_bounds_override;
|
options.window_bounds =
|
||||||
|
window_bounds_override.map(|bounds| WindowBounds::Windowed(bounds));
|
||||||
cx.open_window(options, |cx| {
|
cx.open_window(options, |cx| {
|
||||||
cx.new_view(|cx| {
|
cx.new_view(|cx| {
|
||||||
Workspace::new(Default::default(), project, app_state.clone(), cx)
|
Workspace::new(Default::default(), project, app_state.clone(), cx)
|
||||||
|
@ -4931,7 +4915,8 @@ pub fn join_dev_server_project(
|
||||||
let window_bounds_override = window_bounds_env_override();
|
let window_bounds_override = window_bounds_env_override();
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
let mut options = (app_state.build_window_options)(None, cx);
|
let mut options = (app_state.build_window_options)(None, cx);
|
||||||
options.bounds = window_bounds_override;
|
options.window_bounds =
|
||||||
|
window_bounds_override.map(|bounds| WindowBounds::Windowed(bounds));
|
||||||
cx.open_window(options, |cx| {
|
cx.open_window(options, |cx| {
|
||||||
cx.new_view(|cx| {
|
cx.new_view(|cx| {
|
||||||
Workspace::new(Default::default(), project, app_state.clone(), cx)
|
Workspace::new(Default::default(), project, app_state.clone(), cx)
|
||||||
|
@ -4993,7 +4978,8 @@ pub fn join_in_room_project(
|
||||||
let window_bounds_override = window_bounds_env_override();
|
let window_bounds_override = window_bounds_env_override();
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
let mut options = (app_state.build_window_options)(None, cx);
|
let mut options = (app_state.build_window_options)(None, cx);
|
||||||
options.bounds = window_bounds_override;
|
options.window_bounds =
|
||||||
|
window_bounds_override.map(|bounds| WindowBounds::Windowed(bounds));
|
||||||
cx.open_window(options, |cx| {
|
cx.open_window(options, |cx| {
|
||||||
cx.new_view(|cx| {
|
cx.new_view(|cx| {
|
||||||
Workspace::new(Default::default(), project, app_state.clone(), cx)
|
Workspace::new(Default::default(), project, app_state.clone(), cx)
|
||||||
|
|
|
@ -96,13 +96,12 @@ pub fn build_window_options(display_uuid: Option<Uuid>, cx: &mut AppContext) ->
|
||||||
appears_transparent: true,
|
appears_transparent: true,
|
||||||
traffic_light_position: Some(point(px(9.0), px(9.0))),
|
traffic_light_position: Some(point(px(9.0), px(9.0))),
|
||||||
}),
|
}),
|
||||||
bounds: None,
|
window_bounds: None,
|
||||||
focus: false,
|
focus: false,
|
||||||
show: false,
|
show: false,
|
||||||
kind: WindowKind::Normal,
|
kind: WindowKind::Normal,
|
||||||
is_movable: true,
|
is_movable: true,
|
||||||
display_id: display.map(|display| display.id()),
|
display_id: display.map(|display| display.id()),
|
||||||
fullscreen: false,
|
|
||||||
window_background: cx.theme().window_background_appearance(),
|
window_background: cx.theme().window_background_appearance(),
|
||||||
app_id: Some(app_id.to_owned()),
|
app_id: Some(app_id.to_owned()),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue