diff --git a/crates/contacts_status_item/src/contacts_popover.rs b/crates/contacts_status_item/src/contacts_popover.rs index 0365a2ee2a..7637b51cc9 100644 --- a/crates/contacts_status_item/src/contacts_popover.rs +++ b/crates/contacts_status_item/src/contacts_popover.rs @@ -1,9 +1,13 @@ -use gpui::{color::Color, elements::*, Entity, RenderContext, View}; +use gpui::{color::Color, elements::*, Entity, RenderContext, View, ViewContext}; + +pub enum Event { + Deactivated, +} pub struct ContactsPopover; impl Entity for ContactsPopover { - type Event = (); + type Event = Event; } impl View for ContactsPopover { @@ -20,7 +24,15 @@ impl View for ContactsPopover { } impl ContactsPopover { - pub fn new() -> Self { + pub fn new(cx: &mut ViewContext) -> Self { + cx.observe_window_activation(Self::window_activation_changed) + .detach(); Self } + + fn window_activation_changed(&mut self, is_active: bool, cx: &mut ViewContext) { + if !is_active { + cx.emit(Event::Deactivated); + } + } } diff --git a/crates/contacts_status_item/src/contacts_status_item.rs b/crates/contacts_status_item/src/contacts_status_item.rs index 33ca61ce6c..83cdce7e8e 100644 --- a/crates/contacts_status_item/src/contacts_status_item.rs +++ b/crates/contacts_status_item/src/contacts_status_item.rs @@ -7,7 +7,7 @@ use gpui::{ elements::*, geometry::{rect::RectF, vector::vec2f}, Appearance, Entity, MouseButton, MutableAppContext, RenderContext, View, ViewContext, - ViewHandle, + ViewHandle, WindowKind, }; actions!(contacts_status_item, [ToggleContactsPopover]); @@ -67,12 +67,28 @@ impl ContactsStatusItem { bounds: gpui::WindowBounds::Fixed(RectF::new(origin, size)), titlebar: None, center: false, - level: gpui::WindowLevel::PopUp, + kind: WindowKind::PopUp, + is_movable: false, }, - |_| ContactsPopover::new(), + |cx| ContactsPopover::new(cx), ); + cx.subscribe(&popover, Self::on_popover_event).detach(); self.popover = Some(popover); } } } + + fn on_popover_event( + &mut self, + popover: ViewHandle, + event: &contacts_popover::Event, + cx: &mut ViewContext, + ) { + match event { + contacts_popover::Event::Deactivated => { + self.popover.take(); + cx.remove_window(popover.window_id()); + } + } + } } diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 87c44ae390..89e55c9f6f 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -142,7 +142,8 @@ pub struct WindowOptions<'a> { pub bounds: WindowBounds, pub titlebar: Option>, pub center: bool, - pub level: WindowLevel, + pub kind: WindowKind, + pub is_movable: bool, } #[derive(Debug)] @@ -167,7 +168,7 @@ impl Default for Appearance { } #[derive(Copy, Clone, Debug)] -pub enum WindowLevel { +pub enum WindowKind { Normal, PopUp, } @@ -283,7 +284,8 @@ impl<'a> Default for WindowOptions<'a> { traffic_light_position: Default::default(), }), center: false, - level: WindowLevel::Normal, + kind: WindowKind::Normal, + is_movable: true, } } } diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 90dabfff4e..f21428a1a8 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -12,7 +12,7 @@ use crate::{ Event, WindowBounds, }, InputHandler, KeyDownEvent, ModifiersChangedEvent, MouseButton, MouseButtonEvent, - MouseMovedEvent, Scene, WindowLevel, + MouseMovedEvent, Scene, WindowKind, }; use block::ConcreteBlock; use cocoa::{ @@ -54,6 +54,7 @@ use std::{ const WINDOW_STATE_IVAR: &str = "windowState"; static mut WINDOW_CLASS: *const Class = ptr::null(); +static mut PANEL_CLASS: *const Class = ptr::null(); static mut VIEW_CLASS: *const Class = ptr::null(); #[allow(non_upper_case_globals)] @@ -114,50 +115,8 @@ unsafe impl objc::Encode for NSRange { #[ctor] unsafe fn build_classes() { - WINDOW_CLASS = { - let mut decl = ClassDecl::new("GPUIWindow", class!(NSWindow)).unwrap(); - decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR); - decl.add_method(sel!(dealloc), dealloc_window as extern "C" fn(&Object, Sel)); - decl.add_method( - sel!(canBecomeMainWindow), - yes as extern "C" fn(&Object, Sel) -> BOOL, - ); - decl.add_method( - sel!(canBecomeKeyWindow), - yes as extern "C" fn(&Object, Sel) -> BOOL, - ); - decl.add_method( - sel!(sendEvent:), - send_event as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(windowDidResize:), - window_did_resize 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( - sel!(windowWillExitFullScreen:), - window_will_exit_fullscreen as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(windowDidBecomeKey:), - window_did_change_key_status as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(windowDidResignKey:), - window_did_change_key_status as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(windowShouldClose:), - window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL, - ); - decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel)); - decl.register() - }; - + WINDOW_CLASS = build_window_class("GPUIWindow", class!(NSWindow)); + PANEL_CLASS = build_window_class("GPUIPanel", class!(NSPanel)); VIEW_CLASS = { let mut decl = ClassDecl::new("GPUIView", class!(NSView)).unwrap(); decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR); @@ -286,6 +245,50 @@ unsafe fn build_classes() { }; } +unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const Class { + let mut decl = ClassDecl::new(name, superclass).unwrap(); + decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR); + decl.add_method(sel!(dealloc), dealloc_window as extern "C" fn(&Object, Sel)); + decl.add_method( + sel!(canBecomeMainWindow), + yes as extern "C" fn(&Object, Sel) -> BOOL, + ); + decl.add_method( + sel!(canBecomeKeyWindow), + yes as extern "C" fn(&Object, Sel) -> BOOL, + ); + decl.add_method( + sel!(sendEvent:), + send_event as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(windowDidResize:), + window_did_resize 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( + sel!(windowWillExitFullScreen:), + window_will_exit_fullscreen as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(windowDidBecomeKey:), + window_did_change_key_status as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(windowDidResignKey:), + window_did_change_key_status as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(windowShouldClose:), + window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL, + ); + decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel)); + decl.register() +} + pub struct Window(Rc>); ///Used to track what the IME does when we send it a keystroke. @@ -352,10 +355,21 @@ impl Window { style_mask |= NSWindowStyleMask::NSFullSizeContentViewWindowMask; } } else { - style_mask = NSWindowStyleMask::empty(); + style_mask = NSWindowStyleMask::NSTitledWindowMask + | NSWindowStyleMask::NSFullSizeContentViewWindowMask; } - let native_window: id = msg_send![WINDOW_CLASS, alloc]; + let native_window: id = match options.kind { + WindowKind::Normal => msg_send![WINDOW_CLASS, alloc], + WindowKind::PopUp => { + #[allow(non_upper_case_globals)] + const NSWindowStyleMaskNonactivatingPanel: NSWindowStyleMask = + unsafe { NSWindowStyleMask::from_bits_unchecked(1 << 7) }; + + style_mask |= NSWindowStyleMaskNonactivatingPanel; + msg_send![PANEL_CLASS, alloc] + } + }; let native_window = native_window.initWithContentRect_styleMask_backing_defer_( RectF::new(Default::default(), vec2f(1024., 768.)).to_ns_rect(), style_mask, @@ -425,13 +439,17 @@ impl Window { Rc::into_raw(window.0.clone()) as *const c_void, ); - if let Some(titlebar) = options.titlebar { - if let Some(title) = titlebar.title { - native_window.setTitle_(NSString::alloc(nil).init_str(title)); - } - if titlebar.appears_transparent { - native_window.setTitlebarAppearsTransparent_(YES); - } + if let Some(title) = options.titlebar.as_ref().and_then(|t| t.title) { + native_window.setTitle_(NSString::alloc(nil).init_str(title)); + } + + native_window.setMovable_(options.is_movable); + + if options + .titlebar + .map_or(true, |titlebar| titlebar.appears_transparent) + { + native_window.setTitlebarAppearsTransparent_(YES); } native_window.setAcceptsMouseMovedEvents_(YES); @@ -458,11 +476,10 @@ impl Window { } native_window.makeKeyAndOrderFront_(nil); - let native_level = match options.level { - WindowLevel::Normal => NSNormalWindowLevel, - WindowLevel::PopUp => NSPopUpWindowLevel, - }; - native_window.setLevel_(native_level); + match options.kind { + WindowKind::Normal => native_window.setLevel_(NSNormalWindowLevel), + WindowKind::PopUp => native_window.setLevel_(NSPopUpWindowLevel), + } window.0.borrow().move_traffic_light(); pool.drain(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 077282f526..bee77d2d03 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -20,7 +20,7 @@ use gpui::{ geometry::vector::vec2f, impl_actions, platform::{WindowBounds, WindowOptions}, - AssetSource, AsyncAppContext, TitlebarOptions, ViewContext, WindowLevel, + AssetSource, AsyncAppContext, TitlebarOptions, ViewContext, WindowKind, }; use language::Rope; pub use lsp; @@ -336,7 +336,8 @@ pub fn build_window_options() -> WindowOptions<'static> { traffic_light_position: Some(vec2f(8., 8.)), }), center: false, - level: WindowLevel::Normal, + kind: WindowKind::Normal, + is_movable: true, } }