Compare commits
11 Commits
acp-rewind
...
wayland-wi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fcf9213b0a | ||
|
|
ded63f70ba | ||
|
|
e2c9d622cc | ||
|
|
de18e950ed | ||
|
|
7e29131253 | ||
|
|
4420d8c45d | ||
|
|
f8e37b922a | ||
|
|
e0fd8fc32a | ||
|
|
2b1896c0c4 | ||
|
|
502f652ddb | ||
|
|
262657eb59 |
@@ -14,7 +14,7 @@ pub use collab_panel::CollabPanel;
|
||||
pub use collab_titlebar_item::CollabTitlebarItem;
|
||||
use gpui::{
|
||||
actions, point, AppContext, Pixels, PlatformDisplay, Size, Task, WindowBackgroundAppearance,
|
||||
WindowBounds, WindowContext, WindowKind, WindowOptions,
|
||||
WindowBounds, WindowContext, WindowDecorations, WindowKind, WindowOptions,
|
||||
};
|
||||
use panel_settings::MessageEditorSettings;
|
||||
pub use panel_settings::{
|
||||
@@ -122,8 +122,9 @@ fn notification_window_options(
|
||||
kind: WindowKind::PopUp,
|
||||
is_movable: false,
|
||||
display_id: Some(screen.id()),
|
||||
window_background: WindowBackgroundAppearance::default(),
|
||||
window_background: WindowBackgroundAppearance::Transparent,
|
||||
app_id: Some(app_id.to_owned()),
|
||||
window_min_size: None,
|
||||
window_decorations: Some(WindowDecorations::Client),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +159,10 @@ path = "examples/image/image.rs"
|
||||
name = "set_menus"
|
||||
path = "examples/set_menus.rs"
|
||||
|
||||
[[example]]
|
||||
name = "window_shadow"
|
||||
path = "examples/window_shadow.rs"
|
||||
|
||||
[[example]]
|
||||
name = "input"
|
||||
path = "examples/input.rs"
|
||||
|
||||
@@ -23,7 +23,7 @@ impl Render for HelloWorld {
|
||||
|
||||
fn main() {
|
||||
App::new().run(|cx: &mut AppContext| {
|
||||
let bounds = Bounds::centered(None, size(px(600.0), px(600.0)), cx);
|
||||
let bounds = Bounds::centered(None, size(px(300.0), px(300.0)), cx);
|
||||
cx.open_window(
|
||||
WindowOptions {
|
||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
|
||||
@@ -52,6 +52,7 @@ fn main() {
|
||||
is_movable: false,
|
||||
app_id: None,
|
||||
window_min_size: None,
|
||||
window_decorations: None,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
203
crates/gpui/examples/window_shadow.rs
Normal file
203
crates/gpui/examples/window_shadow.rs
Normal file
@@ -0,0 +1,203 @@
|
||||
use gpui::*;
|
||||
use prelude::FluentBuilder;
|
||||
|
||||
struct WindowShadow {}
|
||||
|
||||
/*
|
||||
Things to do:
|
||||
1. We need a way of calculating which edge or corner the mouse is on,
|
||||
and then dispatch on that
|
||||
2. We need to improve the shadow rendering significantly
|
||||
3. We need to implement the techniques in here in Zed
|
||||
*/
|
||||
|
||||
impl Render for WindowShadow {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let decorations = cx.window_decorations();
|
||||
let tiling = cx.window_tiling();
|
||||
let rounding = px(10.0);
|
||||
let shadow_size = px(10.0);
|
||||
let border_size = px(1.0);
|
||||
let grey = rgb(0x808080);
|
||||
|
||||
div()
|
||||
.id("window-backdrop")
|
||||
.when(decorations == WindowDecorations::Client, |div| {
|
||||
div.bg(gpui::transparent_black())
|
||||
.child(
|
||||
canvas(
|
||||
|_bounds, cx| {
|
||||
cx.insert_hitbox(
|
||||
Bounds::new(
|
||||
point(px(0.0), px(0.0)),
|
||||
cx.window_bounds().get_bounds().size,
|
||||
),
|
||||
false,
|
||||
)
|
||||
},
|
||||
move |_bounds, hitbox, cx| {
|
||||
let mouse = cx.mouse_position();
|
||||
let size = cx.window_bounds().get_bounds().size;
|
||||
let Some(edge) = resize_edge(mouse, shadow_size, size) else {
|
||||
return;
|
||||
};
|
||||
cx.set_cursor_style(
|
||||
match edge {
|
||||
ResizeEdge::Top | ResizeEdge::Bottom => {
|
||||
CursorStyle::ResizeUpDown
|
||||
}
|
||||
ResizeEdge::Left | ResizeEdge::Right => {
|
||||
CursorStyle::ResizeLeftRight
|
||||
}
|
||||
ResizeEdge::TopLeft | ResizeEdge::BottomRight => {
|
||||
CursorStyle::ResizeUpLeftDownRight
|
||||
}
|
||||
ResizeEdge::TopRight | ResizeEdge::BottomLeft => {
|
||||
CursorStyle::ResizeUpRightDownLeft
|
||||
}
|
||||
},
|
||||
&hitbox,
|
||||
);
|
||||
},
|
||||
)
|
||||
.size_full()
|
||||
.absolute(),
|
||||
)
|
||||
.when(!(tiling.top || tiling.right), |div| {
|
||||
div.rounded_tr(rounding)
|
||||
})
|
||||
.when(!(tiling.top || tiling.left), |div| div.rounded_tl(rounding))
|
||||
.when(!tiling.top, |div| div.pt(shadow_size))
|
||||
.when(!tiling.bottom, |div| div.pb(shadow_size))
|
||||
.when(!tiling.left, |div| div.pl(shadow_size))
|
||||
.when(!tiling.right, |div| div.pr(shadow_size))
|
||||
.on_mouse_move(|_e, cx| cx.refresh())
|
||||
.on_mouse_down(MouseButton::Left, move |e, cx| {
|
||||
let size = cx.window_bounds().get_bounds().size;
|
||||
let pos = e.position;
|
||||
|
||||
let edge = match resize_edge(pos, shadow_size, size) {
|
||||
Some(value) => value,
|
||||
None => return,
|
||||
};
|
||||
|
||||
cx.start_window_resize(edge);
|
||||
})
|
||||
})
|
||||
.size_full()
|
||||
.child(
|
||||
div()
|
||||
.cursor(CursorStyle::Arrow)
|
||||
.when(decorations == WindowDecorations::Client, |div| {
|
||||
div.border_color(grey)
|
||||
.when(!(tiling.top || tiling.right), |div| {
|
||||
div.rounded_tr(rounding)
|
||||
})
|
||||
.when(!(tiling.top || tiling.left), |div| div.rounded_tl(rounding))
|
||||
.when(!tiling.top, |div| div.border_t(border_size))
|
||||
.when(!tiling.bottom, |div| div.border_b(border_size))
|
||||
.when(!tiling.left, |div| div.border_l(border_size))
|
||||
.when(!tiling.right, |div| div.border_r(border_size))
|
||||
})
|
||||
.on_mouse_move(|_e, cx| {
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.bg(gpui::rgb(0xCCCCFF))
|
||||
.shadow(smallvec::smallvec![gpui::BoxShadow {
|
||||
color: Hsla {
|
||||
h: 0.,
|
||||
s: 0.,
|
||||
l: 0.,
|
||||
a: 0.4,
|
||||
},
|
||||
blur_radius: shadow_size / 2.,
|
||||
spread_radius: px(0.),
|
||||
offset: point(px(0.0), px(0.0)),
|
||||
}])
|
||||
.size_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.justify_around()
|
||||
.child(
|
||||
div().w_full().flex().flex_row().justify_around().child(
|
||||
div()
|
||||
.id("hello")
|
||||
.flex()
|
||||
.bg(white())
|
||||
.size(Length::Definite(Pixels(300.0).into()))
|
||||
.justify_center()
|
||||
.items_center()
|
||||
.shadow_lg()
|
||||
.border_1()
|
||||
.border_color(rgb(0x0000ff))
|
||||
.text_xl()
|
||||
.text_color(rgb(0xffffff))
|
||||
.child(div().w(px(100.0)).h(px(50.0)).bg(green()).shadow(
|
||||
smallvec::smallvec![gpui::BoxShadow {
|
||||
color: Hsla {
|
||||
h: 0.,
|
||||
s: 0.,
|
||||
l: 0.,
|
||||
a: 1.0,
|
||||
},
|
||||
blur_radius: px(20.0),
|
||||
spread_radius: px(0.0),
|
||||
offset: point(px(0.0), px(0.0)),
|
||||
}],
|
||||
))
|
||||
.on_mouse_move(|e, cx| {
|
||||
if e.dragging() {
|
||||
cx.start_window_move();
|
||||
}
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn resize_edge(pos: Point<Pixels>, shadow_size: Pixels, size: Size<Pixels>) -> Option<ResizeEdge> {
|
||||
let edge = if pos.y < shadow_size && pos.x < shadow_size {
|
||||
ResizeEdge::TopLeft
|
||||
} else if pos.y < shadow_size && pos.x > size.width - shadow_size {
|
||||
ResizeEdge::TopRight
|
||||
} else if pos.y < shadow_size {
|
||||
ResizeEdge::Top
|
||||
} else if pos.y > size.height - shadow_size && pos.x < shadow_size {
|
||||
ResizeEdge::BottomLeft
|
||||
} else if pos.y > size.height - shadow_size && pos.x > size.width - shadow_size {
|
||||
ResizeEdge::BottomRight
|
||||
} else if pos.y > size.height - shadow_size {
|
||||
ResizeEdge::Bottom
|
||||
} else if pos.x < shadow_size {
|
||||
ResizeEdge::Left
|
||||
} else if pos.x > size.width - shadow_size {
|
||||
ResizeEdge::Right
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
Some(edge)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
App::new().run(|cx: &mut AppContext| {
|
||||
let bounds = Bounds::centered(None, size(px(600.0), px(600.0)), cx);
|
||||
cx.open_window(
|
||||
WindowOptions {
|
||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
window_background: WindowBackgroundAppearance::Transparent,
|
||||
..Default::default()
|
||||
},
|
||||
|cx| {
|
||||
cx.new_view(|cx| {
|
||||
cx.observe_window_appearance(|_, cx| {
|
||||
cx.notify();
|
||||
})
|
||||
.detach();
|
||||
WindowShadow {}
|
||||
})
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
@@ -309,6 +309,16 @@ pub fn transparent_black() -> Hsla {
|
||||
}
|
||||
}
|
||||
|
||||
/// Transparent black in [`Hsla`]
|
||||
pub fn transparent_white() -> Hsla {
|
||||
Hsla {
|
||||
h: 0.,
|
||||
s: 0.,
|
||||
l: 1.,
|
||||
a: 0.,
|
||||
}
|
||||
}
|
||||
|
||||
/// Opaque grey in [`Hsla`], values will be clamped to the range [0, 1]
|
||||
pub fn opaque_grey(lightness: f32, opacity: f32) -> Hsla {
|
||||
Hsla {
|
||||
|
||||
@@ -210,6 +210,70 @@ impl Debug for DisplayId {
|
||||
|
||||
unsafe impl Send for DisplayId {}
|
||||
|
||||
/// Which part of the window to resize
|
||||
#[derive(Debug)]
|
||||
pub enum ResizeEdge {
|
||||
/// The top edge
|
||||
Top,
|
||||
/// The top right corner
|
||||
TopRight,
|
||||
/// The right edge
|
||||
Right,
|
||||
/// The bottom right corner
|
||||
BottomRight,
|
||||
/// The bottom edge
|
||||
Bottom,
|
||||
/// The bottom left corner
|
||||
BottomLeft,
|
||||
/// The left edge
|
||||
Left,
|
||||
/// The top left corner
|
||||
TopLeft,
|
||||
}
|
||||
|
||||
/// A type to describe the appearance of a window
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
|
||||
pub enum WindowDecorations {
|
||||
/// Client side decorations
|
||||
Client,
|
||||
#[default]
|
||||
/// Server side decorations
|
||||
Server,
|
||||
}
|
||||
|
||||
/// What window controls this platform supports
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
|
||||
pub struct WindowControls {
|
||||
/// Whether this platform supports fullscreen
|
||||
pub fullscreen: bool,
|
||||
/// Whether this platform supports maximize
|
||||
pub maximize: bool,
|
||||
/// Whether this platform supports minimize
|
||||
pub minimize: bool,
|
||||
/// Whether this platform supports a window menu
|
||||
pub window_menu: bool,
|
||||
}
|
||||
|
||||
/// A type to describe which sides of the window are currently tiled in some way
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
|
||||
pub struct Tiling {
|
||||
/// Whether the top edge is tiled
|
||||
pub top: bool,
|
||||
/// Whether the left edge is tiled
|
||||
pub left: bool,
|
||||
/// Whether the right edge is tiled
|
||||
pub right: bool,
|
||||
/// Whether the bottom edge is tiled
|
||||
pub bottom: bool,
|
||||
}
|
||||
|
||||
impl Tiling {
|
||||
/// Whether any edge is tiled
|
||||
pub fn is_tiled(&self) -> bool {
|
||||
self.top || self.left || self.right || self.bottom
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
|
||||
fn bounds(&self) -> Bounds<Pixels>;
|
||||
fn is_maximized(&self) -> bool;
|
||||
@@ -232,10 +296,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
|
||||
fn activate(&self);
|
||||
fn is_active(&self) -> bool;
|
||||
fn set_title(&mut self, title: &str);
|
||||
fn set_app_id(&mut self, app_id: &str);
|
||||
fn set_background_appearance(&mut self, background_appearance: WindowBackgroundAppearance);
|
||||
fn set_edited(&mut self, edited: bool);
|
||||
fn show_character_palette(&self);
|
||||
fn minimize(&self);
|
||||
fn zoom(&self);
|
||||
fn toggle_fullscreen(&self);
|
||||
@@ -252,12 +313,38 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
|
||||
fn completed_frame(&self) {}
|
||||
fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
|
||||
|
||||
// macOS specific methods
|
||||
fn set_edited(&mut self, _edited: bool) {}
|
||||
fn show_character_palette(&self) {}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn get_raw_handle(&self) -> windows::HWND;
|
||||
|
||||
fn show_window_menu(&self, position: Point<Pixels>);
|
||||
fn start_system_move(&self);
|
||||
fn should_render_window_controls(&self) -> bool;
|
||||
// Linux specific methods
|
||||
fn request_decorations(&self, _decorations: WindowDecorations) {}
|
||||
fn show_window_menu(&self, _position: Point<Pixels>) {}
|
||||
fn start_window_move(&self) {}
|
||||
fn start_window_resize(&self, _edge: ResizeEdge) {}
|
||||
fn window_decorations(&self) -> WindowDecorations {
|
||||
WindowDecorations::Client
|
||||
}
|
||||
fn set_app_id(&mut self, _app_id: &str) {}
|
||||
fn tiling(&self) -> Tiling {
|
||||
Tiling {
|
||||
top: false,
|
||||
left: false,
|
||||
right: false,
|
||||
bottom: false,
|
||||
}
|
||||
}
|
||||
fn window_controls(&self) -> WindowControls {
|
||||
WindowControls {
|
||||
fullscreen: true,
|
||||
maximize: true,
|
||||
minimize: true,
|
||||
window_menu: false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
fn as_test(&mut self) -> Option<&mut TestWindow> {
|
||||
@@ -570,6 +657,10 @@ pub struct WindowOptions {
|
||||
|
||||
/// Window minimum size
|
||||
pub window_min_size: Option<Size<Pixels>>,
|
||||
|
||||
/// Whether to use client or server side decorations. Wayland only
|
||||
/// Note that this may be ignored.
|
||||
pub window_decorations: Option<WindowDecorations>,
|
||||
}
|
||||
|
||||
/// The variables that can be configured when creating a new window
|
||||
@@ -649,6 +740,7 @@ impl Default for WindowOptions {
|
||||
window_background: WindowBackgroundAppearance::default(),
|
||||
app_id: None,
|
||||
window_min_size: None,
|
||||
window_decorations: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -805,6 +897,14 @@ pub enum CursorStyle {
|
||||
/// corresponds to the CSS cursor value `ns-resize`
|
||||
ResizeUpDown,
|
||||
|
||||
/// A resize cursor directing up-left and down-right
|
||||
/// corresponds to the CSS cursor value `nesw-resize`
|
||||
ResizeUpLeftDownRight,
|
||||
|
||||
/// A resize cursor directing up-right and down-left
|
||||
/// corresponds to the CSS cursor value `nwse-resize`
|
||||
ResizeUpRightDownLeft,
|
||||
|
||||
/// A cursor indicating that the item/column can be resized horizontally.
|
||||
/// corresponds to the CSS curosr value `col-resize`
|
||||
ResizeColumn,
|
||||
|
||||
@@ -558,6 +558,8 @@ impl CursorStyle {
|
||||
CursorStyle::ResizeUp => Shape::NResize,
|
||||
CursorStyle::ResizeDown => Shape::SResize,
|
||||
CursorStyle::ResizeUpDown => Shape::NsResize,
|
||||
CursorStyle::ResizeUpLeftDownRight => Shape::NwseResize,
|
||||
CursorStyle::ResizeUpRightDownLeft => Shape::NeswResize,
|
||||
CursorStyle::ResizeColumn => Shape::ColResize,
|
||||
CursorStyle::ResizeRow => Shape::RowResize,
|
||||
CursorStyle::IBeamCursorForVerticalLayout => Shape::VerticalText,
|
||||
@@ -585,6 +587,8 @@ impl CursorStyle {
|
||||
CursorStyle::ResizeUp => "n-resize",
|
||||
CursorStyle::ResizeDown => "s-resize",
|
||||
CursorStyle::ResizeUpDown => "ns-resize",
|
||||
CursorStyle::ResizeUpLeftDownRight => "nwse-resize",
|
||||
CursorStyle::ResizeUpRightDownLeft => "nesw-resize",
|
||||
CursorStyle::ResizeColumn => "col-resize",
|
||||
CursorStyle::ResizeRow => "row-resize",
|
||||
CursorStyle::IBeamCursorForVerticalLayout => "vertical-text",
|
||||
|
||||
@@ -138,7 +138,7 @@ impl Globals {
|
||||
primary_selection_manager: globals.bind(&qh, 1..=1, ()).ok(),
|
||||
shm: globals.bind(&qh, 1..=1, ()).unwrap(),
|
||||
seat,
|
||||
wm_base: globals.bind(&qh, 1..=1, ()).unwrap(),
|
||||
wm_base: globals.bind(&qh, 2..=2, ()).unwrap(),
|
||||
viewporter: globals.bind(&qh, 1..=1, ()).ok(),
|
||||
fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(),
|
||||
decoration_manager: globals.bind(&qh, 1..=1, ()).ok(),
|
||||
|
||||
@@ -26,8 +26,9 @@ use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow};
|
||||
use crate::scene::Scene;
|
||||
use crate::{
|
||||
px, size, AnyWindowHandle, Bounds, Globals, Modifiers, Output, Pixels, PlatformDisplay,
|
||||
PlatformInput, Point, PromptLevel, Size, WaylandClientStatePtr, WindowAppearance,
|
||||
WindowBackgroundAppearance, WindowBounds, WindowParams,
|
||||
PlatformInput, Point, PromptLevel, ResizeEdge, Size, Tiling, WaylandClientStatePtr,
|
||||
WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControls, WindowDecorations,
|
||||
WindowParams,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -66,6 +67,7 @@ struct InProgressConfigure {
|
||||
size: Option<Size<Pixels>>,
|
||||
fullscreen: bool,
|
||||
maximized: bool,
|
||||
tiling: Tiling,
|
||||
}
|
||||
|
||||
pub struct WaylandWindowState {
|
||||
@@ -84,14 +86,17 @@ pub struct WaylandWindowState {
|
||||
bounds: Bounds<Pixels>,
|
||||
scale: f32,
|
||||
input_handler: Option<PlatformInputHandler>,
|
||||
decoration_state: WaylandDecorationState,
|
||||
decoration_state: WindowDecorations,
|
||||
fullscreen: bool,
|
||||
maximized: bool,
|
||||
tiling: Tiling,
|
||||
windowed_bounds: Bounds<Pixels>,
|
||||
client: WaylandClientStatePtr,
|
||||
handle: AnyWindowHandle,
|
||||
active: bool,
|
||||
in_progress_configure: Option<InProgressConfigure>,
|
||||
in_progress_window_controls: Option<WindowControls>,
|
||||
window_controls: WindowControls,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -160,15 +165,23 @@ impl WaylandWindowState {
|
||||
bounds: options.bounds,
|
||||
scale: 1.0,
|
||||
input_handler: None,
|
||||
decoration_state: WaylandDecorationState::Client,
|
||||
decoration_state: WindowDecorations::Client,
|
||||
fullscreen: false,
|
||||
maximized: false,
|
||||
tiling: Tiling::default(),
|
||||
windowed_bounds: options.bounds,
|
||||
in_progress_configure: None,
|
||||
client,
|
||||
appearance,
|
||||
handle,
|
||||
active: false,
|
||||
in_progress_window_controls: None,
|
||||
window_controls: WindowControls {
|
||||
fullscreen: false,
|
||||
maximize: false,
|
||||
minimize: false,
|
||||
window_menu: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -246,13 +259,7 @@ impl WaylandWindow {
|
||||
.decoration_manager
|
||||
.as_ref()
|
||||
.map(|decoration_manager| {
|
||||
let decoration = decoration_manager.get_toplevel_decoration(
|
||||
&toplevel,
|
||||
&globals.qh,
|
||||
surface.id(),
|
||||
);
|
||||
decoration.set_mode(zxdg_toplevel_decoration_v1::Mode::ClientSide);
|
||||
decoration
|
||||
decoration_manager.get_toplevel_decoration(&toplevel, &globals.qh, surface.id())
|
||||
});
|
||||
|
||||
let viewport = globals
|
||||
@@ -311,6 +318,18 @@ impl WaylandWindowStatePtr {
|
||||
pub fn handle_xdg_surface_event(&self, event: xdg_surface::Event) {
|
||||
match event {
|
||||
xdg_surface::Event::Configure { serial } => {
|
||||
{
|
||||
let mut state = self.state.borrow_mut();
|
||||
if let Some(window_controls) = state.in_progress_window_controls.take() {
|
||||
state.window_controls = window_controls;
|
||||
|
||||
drop(state);
|
||||
let mut callbacks = self.callbacks.borrow_mut();
|
||||
if let Some(appearance_changed) = callbacks.appearance_changed.as_mut() {
|
||||
appearance_changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
let mut state = self.state.borrow_mut();
|
||||
|
||||
@@ -318,6 +337,7 @@ impl WaylandWindowStatePtr {
|
||||
let got_unmaximized = state.maximized && !configure.maximized;
|
||||
state.fullscreen = configure.fullscreen;
|
||||
state.maximized = configure.maximized;
|
||||
state.tiling = configure.tiling;
|
||||
|
||||
if got_unmaximized {
|
||||
configure.size = Some(state.windowed_bounds.size);
|
||||
@@ -351,10 +371,20 @@ impl WaylandWindowStatePtr {
|
||||
match event {
|
||||
zxdg_toplevel_decoration_v1::Event::Configure { mode } => match mode {
|
||||
WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ServerSide) => {
|
||||
self.set_decoration_state(WaylandDecorationState::Server)
|
||||
self.state.borrow_mut().decoration_state = WindowDecorations::Server;
|
||||
if let Some(mut appearance_changed) =
|
||||
self.callbacks.borrow_mut().appearance_changed.as_mut()
|
||||
{
|
||||
appearance_changed();
|
||||
}
|
||||
}
|
||||
WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ClientSide) => {
|
||||
self.set_decoration_state(WaylandDecorationState::Client)
|
||||
self.state.borrow_mut().decoration_state = WindowDecorations::Client;
|
||||
if let Some(mut appearance_changed) =
|
||||
self.callbacks.borrow_mut().appearance_changed.as_mut()
|
||||
{
|
||||
appearance_changed();
|
||||
}
|
||||
}
|
||||
WEnum::Value(_) => {
|
||||
log::warn!("Unknown decoration mode");
|
||||
@@ -389,14 +419,44 @@ impl WaylandWindowStatePtr {
|
||||
Some(size(px(width as f32), px(height as f32)))
|
||||
};
|
||||
|
||||
let fullscreen = states.contains(&(xdg_toplevel::State::Fullscreen as u8));
|
||||
let maximized = states.contains(&(xdg_toplevel::State::Maximized as u8));
|
||||
let states = extract_states::<xdg_toplevel::State>(&states);
|
||||
|
||||
let mut tiling = Tiling::default();
|
||||
let mut fullscreen = false;
|
||||
let mut maximized = false;
|
||||
|
||||
for state in states {
|
||||
match state {
|
||||
xdg_toplevel::State::Maximized => {
|
||||
maximized = true;
|
||||
}
|
||||
xdg_toplevel::State::Fullscreen => {
|
||||
fullscreen = true;
|
||||
}
|
||||
xdg_toplevel::State::TiledTop => {
|
||||
tiling.top = true;
|
||||
}
|
||||
xdg_toplevel::State::TiledLeft => {
|
||||
tiling.left = true;
|
||||
}
|
||||
xdg_toplevel::State::TiledRight => {
|
||||
tiling.right = true;
|
||||
}
|
||||
xdg_toplevel::State::TiledBottom => {
|
||||
tiling.bottom = true;
|
||||
}
|
||||
_ => {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut state = self.state.borrow_mut();
|
||||
state.in_progress_configure = Some(InProgressConfigure {
|
||||
size,
|
||||
fullscreen,
|
||||
maximized,
|
||||
tiling,
|
||||
});
|
||||
|
||||
false
|
||||
@@ -415,6 +475,33 @@ impl WaylandWindowStatePtr {
|
||||
true
|
||||
}
|
||||
}
|
||||
xdg_toplevel::Event::WmCapabilities { capabilities } => {
|
||||
let mut window_controls = WindowControls::default();
|
||||
|
||||
let states = extract_states::<xdg_toplevel::WmCapabilities>(&capabilities);
|
||||
|
||||
for state in states {
|
||||
match state {
|
||||
xdg_toplevel::WmCapabilities::Maximize => {
|
||||
window_controls.maximize = true;
|
||||
}
|
||||
xdg_toplevel::WmCapabilities::Minimize => {
|
||||
window_controls.minimize = true;
|
||||
}
|
||||
xdg_toplevel::WmCapabilities::Fullscreen => {
|
||||
window_controls.fullscreen = true;
|
||||
}
|
||||
xdg_toplevel::WmCapabilities::WindowMenu => {
|
||||
window_controls.window_menu = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut state = self.state.borrow_mut();
|
||||
state.in_progress_window_controls = Some(window_controls);
|
||||
false
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -545,18 +632,6 @@ impl WaylandWindowStatePtr {
|
||||
self.set_size_and_scale(None, Some(scale));
|
||||
}
|
||||
|
||||
/// Notifies the window of the state of the decorations.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This API is indirectly called by the wayland compositor and
|
||||
/// not meant to be called by a user who wishes to change the state
|
||||
/// of the decorations. This is because the state of the decorations
|
||||
/// is managed by the compositor and not the client.
|
||||
pub fn set_decoration_state(&self, state: WaylandDecorationState) {
|
||||
self.state.borrow_mut().decoration_state = state;
|
||||
}
|
||||
|
||||
pub fn close(&self) {
|
||||
let mut callbacks = self.callbacks.borrow_mut();
|
||||
if let Some(fun) = callbacks.close.take() {
|
||||
@@ -599,6 +674,17 @@ impl WaylandWindowStatePtr {
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_states<'a, S: TryFrom<u32> + 'a>(states: &'a [u8]) -> impl Iterator<Item = S> + 'a
|
||||
where
|
||||
<S as TryFrom<u32>>::Error: 'a,
|
||||
{
|
||||
states
|
||||
.chunks_exact(4)
|
||||
.flat_map(TryInto::<[u8; 4]>::try_into)
|
||||
.map(u32::from_ne_bytes)
|
||||
.flat_map(S::try_from)
|
||||
}
|
||||
|
||||
fn primary_output_scale(state: &mut RefMut<WaylandWindowState>) -> i32 {
|
||||
let mut scale = 1;
|
||||
let mut current_output = state.display.take();
|
||||
@@ -758,14 +844,6 @@ impl PlatformWindow for WaylandWindow {
|
||||
region.destroy();
|
||||
}
|
||||
|
||||
fn set_edited(&mut self, _edited: bool) {
|
||||
log::info!("ignoring macOS specific set_edited");
|
||||
}
|
||||
|
||||
fn show_character_palette(&self) {
|
||||
log::info!("ignoring macOS specific show_character_palette");
|
||||
}
|
||||
|
||||
fn minimize(&self) {
|
||||
self.borrow().toplevel.set_minimized();
|
||||
}
|
||||
@@ -850,22 +928,61 @@ impl PlatformWindow for WaylandWindow {
|
||||
);
|
||||
}
|
||||
|
||||
fn start_system_move(&self) {
|
||||
fn start_window_move(&self) {
|
||||
let state = self.borrow();
|
||||
let serial = state.client.get_serial(SerialKind::MousePress);
|
||||
state.toplevel._move(&state.globals.seat, serial);
|
||||
}
|
||||
|
||||
fn should_render_window_controls(&self) -> bool {
|
||||
self.borrow().decoration_state == WaylandDecorationState::Client
|
||||
fn start_window_resize(&self, edge: crate::ResizeEdge) {
|
||||
let state = self.borrow();
|
||||
state.toplevel.resize(
|
||||
&state.globals.seat,
|
||||
state.client.get_serial(SerialKind::MousePress),
|
||||
edge.to_xdg(),
|
||||
)
|
||||
}
|
||||
|
||||
fn window_decorations(&self) -> WindowDecorations {
|
||||
self.borrow().decoration_state
|
||||
}
|
||||
|
||||
fn tiling(&self) -> Tiling {
|
||||
self.borrow().tiling
|
||||
}
|
||||
|
||||
fn request_decorations(&self, decorations: WindowDecorations) {
|
||||
let mut state = self.borrow_mut();
|
||||
if let Some(decoration) = state.decoration.as_ref() {
|
||||
decoration.set_mode(decorations.to_xdg())
|
||||
}
|
||||
}
|
||||
|
||||
fn window_controls(&self) -> WindowControls {
|
||||
self.borrow().window_controls
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum WaylandDecorationState {
|
||||
/// Decorations are to be provided by the client
|
||||
Client,
|
||||
|
||||
/// Decorations are provided by the server
|
||||
Server,
|
||||
impl WindowDecorations {
|
||||
fn to_xdg(&self) -> zxdg_toplevel_decoration_v1::Mode {
|
||||
match self {
|
||||
WindowDecorations::Client => zxdg_toplevel_decoration_v1::Mode::ClientSide,
|
||||
WindowDecorations::Server => zxdg_toplevel_decoration_v1::Mode::ServerSide,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResizeEdge {
|
||||
fn to_xdg(&self) -> xdg_toplevel::ResizeEdge {
|
||||
match self {
|
||||
ResizeEdge::Top => xdg_toplevel::ResizeEdge::Top,
|
||||
ResizeEdge::TopRight => xdg_toplevel::ResizeEdge::TopRight,
|
||||
ResizeEdge::Right => xdg_toplevel::ResizeEdge::Right,
|
||||
ResizeEdge::BottomRight => xdg_toplevel::ResizeEdge::BottomRight,
|
||||
ResizeEdge::Bottom => xdg_toplevel::ResizeEdge::Bottom,
|
||||
ResizeEdge::BottomLeft => xdg_toplevel::ResizeEdge::BottomLeft,
|
||||
ResizeEdge::Left => xdg_toplevel::ResizeEdge::Left,
|
||||
ResizeEdge::TopLeft => xdg_toplevel::ResizeEdge::TopLeft,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::{
|
||||
platform::blade::{BladeRenderer, BladeSurfaceConfig},
|
||||
px, size, AnyWindowHandle, Bounds, DevicePixels, ForegroundExecutor, Modifiers, Pixels,
|
||||
PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
|
||||
PromptLevel, Scene, Size, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
|
||||
PromptLevel, Scene, Size, Tiling, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
|
||||
WindowKind, WindowParams, X11ClientStatePtr,
|
||||
};
|
||||
|
||||
@@ -990,7 +990,7 @@ impl PlatformWindow for X11Window {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn start_system_move(&self) {
|
||||
fn start_window_move(&self) {
|
||||
let state = self.0.state.borrow();
|
||||
let pointer = self
|
||||
.0
|
||||
@@ -1023,7 +1023,18 @@ impl PlatformWindow for X11Window {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn should_render_window_controls(&self) -> bool {
|
||||
false
|
||||
// TODO: implement X11 decoration management
|
||||
fn window_decorations(&self) -> crate::WindowDecorations {
|
||||
crate::WindowDecorations::Server
|
||||
}
|
||||
|
||||
// TODO: implement X11 decoration management
|
||||
fn tiling(&self) -> Tiling {
|
||||
Tiling {
|
||||
top: false,
|
||||
left: false,
|
||||
right: false,
|
||||
bottom: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -796,14 +796,24 @@ impl Platform for MacPlatform {
|
||||
CursorStyle::ClosedHand => msg_send![class!(NSCursor), closedHandCursor],
|
||||
CursorStyle::OpenHand => msg_send![class!(NSCursor), openHandCursor],
|
||||
CursorStyle::PointingHand => msg_send![class!(NSCursor), pointingHandCursor],
|
||||
CursorStyle::ResizeLeftRight => msg_send![class!(NSCursor), resizeLeftRightCursor],
|
||||
CursorStyle::ResizeUpDown => msg_send![class!(NSCursor), verticalResizeCursor],
|
||||
CursorStyle::ResizeLeft => msg_send![class!(NSCursor), resizeLeftCursor],
|
||||
CursorStyle::ResizeRight => msg_send![class!(NSCursor), resizeRightCursor],
|
||||
CursorStyle::ResizeLeftRight => msg_send![class!(NSCursor), resizeLeftRightCursor],
|
||||
CursorStyle::ResizeColumn => msg_send![class!(NSCursor), resizeLeftRightCursor],
|
||||
CursorStyle::ResizeRow => msg_send![class!(NSCursor), resizeUpDownCursor],
|
||||
CursorStyle::ResizeUp => msg_send![class!(NSCursor), resizeUpCursor],
|
||||
CursorStyle::ResizeDown => msg_send![class!(NSCursor), resizeDownCursor],
|
||||
CursorStyle::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor],
|
||||
CursorStyle::ResizeRow => msg_send![class!(NSCursor), resizeUpDownCursor],
|
||||
|
||||
// Undocumented, private class methods:
|
||||
// https://stackoverflow.com/questions/27242353/cocoa-predefined-resize-mouse-cursor
|
||||
CursorStyle::ResizeUpLeftDownRight => {
|
||||
msg_send![class!(NSCursor), _windowResizeNorthWestSouthEastCursor]
|
||||
}
|
||||
CursorStyle::ResizeUpRightDownLeft => {
|
||||
msg_send![class!(NSCursor), _windowResizeNorthEastSouthWestCursor]
|
||||
}
|
||||
|
||||
CursorStyle::IBeamCursorForVerticalLayout => {
|
||||
msg_send![class!(NSCursor), IBeamCursorForVerticalLayout]
|
||||
}
|
||||
|
||||
@@ -1092,14 +1092,6 @@ impl PlatformWindow for MacWindow {
|
||||
fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
|
||||
self.0.lock().renderer.sprite_atlas().clone()
|
||||
}
|
||||
|
||||
fn show_window_menu(&self, _position: Point<Pixels>) {}
|
||||
|
||||
fn start_system_move(&self) {}
|
||||
|
||||
fn should_render_window_controls(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl rwh::HasWindowHandle for MacWindow {
|
||||
|
||||
@@ -262,13 +262,9 @@ impl PlatformWindow for TestWindow {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn start_system_move(&self) {
|
||||
fn start_window_move(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn should_render_window_controls(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct TestAtlasState {
|
||||
|
||||
@@ -511,8 +511,6 @@ impl PlatformWindow for WindowsWindow {
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn set_app_id(&mut self, _app_id: &str) {}
|
||||
|
||||
fn set_background_appearance(&mut self, background_appearance: WindowBackgroundAppearance) {
|
||||
self.0
|
||||
.state
|
||||
@@ -521,12 +519,6 @@ impl PlatformWindow for WindowsWindow {
|
||||
.update_transparency(background_appearance != WindowBackgroundAppearance::Opaque);
|
||||
}
|
||||
|
||||
// todo(windows)
|
||||
fn set_edited(&mut self, _edited: bool) {}
|
||||
|
||||
// todo(windows)
|
||||
fn show_character_palette(&self) {}
|
||||
|
||||
fn minimize(&self) {
|
||||
unsafe { ShowWindowAsync(self.0.hwnd, SW_MINIMIZE).ok().log_err() };
|
||||
}
|
||||
@@ -645,14 +637,6 @@ impl PlatformWindow for WindowsWindow {
|
||||
fn get_raw_handle(&self) -> HWND {
|
||||
self.0.hwnd
|
||||
}
|
||||
|
||||
fn show_window_menu(&self, _position: Point<Pixels>) {}
|
||||
|
||||
fn start_system_move(&self) {}
|
||||
|
||||
fn should_render_window_controls(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[implement(IDropTarget)]
|
||||
|
||||
@@ -9,11 +9,11 @@ use crate::{
|
||||
MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels,
|
||||
PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
|
||||
PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams,
|
||||
RenderSvgParams, ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style,
|
||||
SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement,
|
||||
TransformationMatrix, Underline, UnderlineStyle, View, VisualContext, WeakView,
|
||||
WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowOptions, WindowParams,
|
||||
WindowTextSystem, SUBPIXEL_VARIANTS,
|
||||
RenderSvgParams, ResizeEdge, ScaledPixels, Scene, Shadow, SharedString, Size,
|
||||
StrikethroughStyle, Style, SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle,
|
||||
TextStyleRefinement, TransformationMatrix, Underline, UnderlineStyle, View, VisualContext,
|
||||
WeakView, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControls,
|
||||
WindowDecorations, WindowOptions, WindowParams, WindowTextSystem, SUBPIXEL_VARIANTS,
|
||||
};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use collections::{FxHashMap, FxHashSet};
|
||||
@@ -633,6 +633,7 @@ impl Window {
|
||||
window_background,
|
||||
app_id,
|
||||
window_min_size,
|
||||
window_decorations,
|
||||
} = options;
|
||||
|
||||
let bounds = window_bounds
|
||||
@@ -666,6 +667,9 @@ impl Window {
|
||||
let next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>> = Default::default();
|
||||
let last_input_timestamp = Rc::new(Cell::new(Instant::now()));
|
||||
|
||||
platform_window
|
||||
.request_decorations(window_decorations.unwrap_or(WindowDecorations::Server));
|
||||
|
||||
if let Some(ref window_open_state) = window_bounds {
|
||||
match window_open_state {
|
||||
WindowBounds::Fullscreen(_) => platform_window.toggle_fullscreen(),
|
||||
@@ -984,6 +988,21 @@ impl<'a> WindowContext<'a> {
|
||||
self.window.platform_window.is_maximized()
|
||||
}
|
||||
|
||||
/// Check if the platform window is current tiled
|
||||
pub fn window_tiling(&self) -> crate::Tiling {
|
||||
self.window.platform_window.tiling()
|
||||
}
|
||||
|
||||
/// request a certain window decoration (Wayland)
|
||||
pub fn request_decorations(&self, decorations: WindowDecorations) {
|
||||
self.window.platform_window.request_decorations(decorations);
|
||||
}
|
||||
|
||||
/// Start a window resize operation (Wayland)
|
||||
pub fn start_window_resize(&self, edge: ResizeEdge) {
|
||||
self.window.platform_window.start_window_resize(edge);
|
||||
}
|
||||
|
||||
/// Return the `WindowBounds` to indicate that how a window should be opened
|
||||
/// after it has been closed
|
||||
pub fn window_bounds(&self) -> WindowBounds {
|
||||
@@ -1211,13 +1230,18 @@ impl<'a> WindowContext<'a> {
|
||||
/// Tells the compositor to take control of window movement (Wayland and X11)
|
||||
///
|
||||
/// Events may not be received during a move operation.
|
||||
pub fn start_system_move(&self) {
|
||||
self.window.platform_window.start_system_move()
|
||||
pub fn start_window_move(&self) {
|
||||
self.window.platform_window.start_window_move()
|
||||
}
|
||||
|
||||
/// Returns whether the title bar window controls need to be rendered by the application (Wayland and X11)
|
||||
pub fn should_render_window_controls(&self) -> bool {
|
||||
self.window.platform_window.should_render_window_controls()
|
||||
pub fn window_decorations(&self) -> WindowDecorations {
|
||||
self.window.platform_window.window_decorations()
|
||||
}
|
||||
|
||||
/// Returns which window controls are currently visible (Wayland)
|
||||
pub fn window_controls(&self) -> WindowControls {
|
||||
self.window.platform_window.window_controls()
|
||||
}
|
||||
|
||||
/// Updates the window's title at the platform level.
|
||||
|
||||
@@ -19,6 +19,7 @@ impl LinuxWindowControls {
|
||||
|
||||
impl RenderOnce for LinuxWindowControls {
|
||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||
let controls = cx.window_controls();
|
||||
let close_button_hover_color = Rgba {
|
||||
r: 232.0 / 255.0,
|
||||
g: 17.0 / 255.0,
|
||||
@@ -49,22 +50,26 @@ impl RenderOnce for LinuxWindowControls {
|
||||
.content_stretch()
|
||||
.max_h(self.button_height)
|
||||
.min_h(self.button_height)
|
||||
.child(TitlebarButton::new(
|
||||
"minimize",
|
||||
TitlebarButtonType::Minimize,
|
||||
button_hover_color,
|
||||
self.close_window_action.boxed_clone(),
|
||||
))
|
||||
.child(TitlebarButton::new(
|
||||
"maximize-or-restore",
|
||||
if cx.is_maximized() {
|
||||
TitlebarButtonType::Restore
|
||||
} else {
|
||||
TitlebarButtonType::Maximize
|
||||
},
|
||||
button_hover_color,
|
||||
self.close_window_action.boxed_clone(),
|
||||
))
|
||||
.children(controls.minimize.then(|| {
|
||||
TitlebarButton::new(
|
||||
"minimize",
|
||||
TitlebarButtonType::Minimize,
|
||||
button_hover_color,
|
||||
self.close_window_action.boxed_clone(),
|
||||
)
|
||||
}))
|
||||
.children(controls.maximize.then(|| {
|
||||
TitlebarButton::new(
|
||||
"maximize-or-restore",
|
||||
if cx.is_maximized() {
|
||||
TitlebarButtonType::Restore
|
||||
} else {
|
||||
TitlebarButtonType::Maximize
|
||||
},
|
||||
button_hover_color,
|
||||
self.close_window_action.boxed_clone(),
|
||||
)
|
||||
}))
|
||||
.child(TitlebarButton::new(
|
||||
"close",
|
||||
TitlebarButtonType::Close,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use gpui::{Action, AnyElement, Interactivity, Stateful};
|
||||
use gpui::{Action, AnyElement, Interactivity, Stateful, WindowDecorations};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::components::title_bar::linux_window_controls::LinuxWindowControls;
|
||||
@@ -117,16 +117,18 @@ impl RenderOnce for TitleBar {
|
||||
.when(
|
||||
self.platform_style == PlatformStyle::Linux
|
||||
&& !cx.is_fullscreen()
|
||||
&& cx.should_render_window_controls(),
|
||||
&& cx.window_decorations() == WindowDecorations::Client,
|
||||
|title_bar| {
|
||||
title_bar
|
||||
.child(LinuxWindowControls::new(height, self.close_window_action))
|
||||
.on_mouse_down(gpui::MouseButton::Right, move |ev, cx| {
|
||||
cx.show_window_menu(ev.position)
|
||||
.when(cx.window_controls().window_menu, |title_bar| {
|
||||
title_bar.on_mouse_down(gpui::MouseButton::Right, move |ev, cx| {
|
||||
cx.show_window_menu(ev.position)
|
||||
})
|
||||
})
|
||||
.on_mouse_move(move |ev, cx| {
|
||||
if ev.dragging() {
|
||||
cx.start_system_move();
|
||||
cx.start_window_move();
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -105,6 +105,7 @@ pub fn build_window_options(display_uuid: Option<Uuid>, cx: &mut AppContext) ->
|
||||
display_id: display.map(|display| display.id()),
|
||||
window_background: cx.theme().window_background_appearance(),
|
||||
app_id: Some(app_id.to_owned()),
|
||||
window_decorations: Some(gpui::WindowDecorations::Client),
|
||||
window_min_size: Some(gpui::Size {
|
||||
width: px(360.0),
|
||||
height: px(240.0),
|
||||
|
||||
Reference in New Issue
Block a user