From 6578af6f3b26ef01fd558e203c256a516c772c6f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 12 Sep 2022 12:03:03 +0200 Subject: [PATCH] WIP: Start rendering GPUI views to macOS status bar --- crates/gpui/src/app.rs | 202 ++++++++++++-------- crates/gpui/src/platform.rs | 4 +- crates/gpui/src/platform/mac/platform.rs | 4 +- crates/gpui/src/platform/mac/status_item.rs | 153 ++++++++++----- crates/gpui/src/platform/test.rs | 4 +- 5 files changed, 235 insertions(+), 132 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 4a749b551f..e7f0e260e3 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1920,19 +1920,134 @@ impl MutableAppContext { }, ); root_view.update(this, |view, cx| view.on_focus_in(cx.handle().into(), cx)); - this.open_platform_window(window_id, window_options); + + let mut window = + this.cx + .platform + .open_window(window_id, window_options, this.foreground.clone()); + let presenter = Rc::new(RefCell::new( + this.build_presenter(window_id, window.titlebar_height()), + )); + + { + let mut app = this.upgrade(); + let presenter = Rc::downgrade(&presenter); + window.on_event(Box::new(move |event| { + app.update(|cx| { + if let Some(presenter) = presenter.upgrade() { + if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event { + if cx.dispatch_keystroke(window_id, keystroke) { + return true; + } + } + + presenter.borrow_mut().dispatch_event(event, false, cx) + } else { + false + } + }) + })); + } + + { + let mut app = this.upgrade(); + window.on_active_status_change(Box::new(move |is_active| { + app.update(|cx| cx.window_changed_active_status(window_id, is_active)) + })); + } + + { + let mut app = this.upgrade(); + window.on_resize(Box::new(move || { + app.update(|cx| cx.window_was_resized(window_id)) + })); + } + + { + let mut app = this.upgrade(); + window.on_fullscreen(Box::new(move |is_fullscreen| { + app.update(|cx| cx.window_was_fullscreen_changed(window_id, is_fullscreen)) + })); + } + + { + let mut app = this.upgrade(); + window.on_close(Box::new(move || { + app.update(|cx| cx.remove_window(window_id)); + })); + } + + window.set_input_handler(Box::new(WindowInputHandler { + app: this.upgrade().0, + window_id, + })); + + let scene = presenter.borrow_mut().build_scene( + window.size(), + window.scale_factor(), + false, + this, + ); + window.present_scene(scene); + this.presenters_and_platform_windows + .insert(window_id, (presenter.clone(), window)); (window_id, root_view) }) } - // pub fn add_status_bar_item(&mut self, build_item: F) - // where - // I: View, - // F: FnOnce(&mut ViewContext) -> I, - // { - // mem::forget(self.platform.add_status_item()); - // } + pub fn add_status_bar_item(&mut self, build_root_view: F) -> (usize, ViewHandle) + where + T: View, + F: FnOnce(&mut ViewContext) -> T, + { + self.update(|this| { + let window_id = post_inc(&mut this.next_window_id); + let root_view = this + .build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx))) + .unwrap(); + this.cx.windows.insert( + window_id, + Window { + root_view: root_view.clone().into(), + focused_view_id: Some(root_view.id()), + is_active: false, + invalidation: None, + is_fullscreen: false, + }, + ); + root_view.update(this, |view, cx| view.on_focus_in(cx.handle().into(), cx)); + + let mut status_item = this.cx.platform.add_status_item(); + let presenter = Rc::new(RefCell::new(this.build_presenter(window_id, 0.))); + + { + let mut app = this.upgrade(); + let presenter = Rc::downgrade(&presenter); + status_item.on_event(Box::new(move |event| { + app.update(|cx| { + if let Some(presenter) = presenter.upgrade() { + presenter.borrow_mut().dispatch_event(event, cx) + } else { + false + } + }) + })); + } + + let scene = presenter.borrow_mut().build_scene( + status_item.size(), + status_item.scale_factor(), + false, + this, + ); + status_item.present_scene(scene); + this.presenters_and_platform_windows + .insert(window_id, (presenter.clone(), status_item)); + + (window_id, root_view) + }) + } pub fn replace_root_view(&mut self, window_id: usize, build_root_view: F) -> ViewHandle where @@ -1956,77 +2071,6 @@ impl MutableAppContext { self.flush_effects(); } - fn open_platform_window(&mut self, window_id: usize, window_options: WindowOptions) { - let mut window = - self.cx - .platform - .open_window(window_id, window_options, self.foreground.clone()); - let presenter = Rc::new(RefCell::new( - self.build_presenter(window_id, window.titlebar_height()), - )); - - { - let mut app = self.upgrade(); - let presenter = Rc::downgrade(&presenter); - window.on_event(Box::new(move |event| { - app.update(|cx| { - if let Some(presenter) = presenter.upgrade() { - if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event { - if cx.dispatch_keystroke(window_id, keystroke) { - return true; - } - } - - presenter.borrow_mut().dispatch_event(event, false, cx) - } else { - false - } - }) - })); - } - - { - let mut app = self.upgrade(); - window.on_active_status_change(Box::new(move |is_active| { - app.update(|cx| cx.window_changed_active_status(window_id, is_active)) - })); - } - - { - let mut app = self.upgrade(); - window.on_resize(Box::new(move || { - app.update(|cx| cx.window_was_resized(window_id)) - })); - } - - { - let mut app = self.upgrade(); - window.on_fullscreen(Box::new(move |is_fullscreen| { - app.update(|cx| cx.window_was_fullscreen_changed(window_id, is_fullscreen)) - })); - } - - { - let mut app = self.upgrade(); - window.on_close(Box::new(move || { - app.update(|cx| cx.remove_window(window_id)); - })); - } - - window.set_input_handler(Box::new(WindowInputHandler { - app: self.upgrade().0, - window_id, - })); - - let scene = - presenter - .borrow_mut() - .build_scene(window.size(), window.scale_factor(), false, self); - window.present_scene(scene); - self.presenters_and_platform_windows - .insert(window_id, (presenter.clone(), window)); - } - pub fn build_presenter(&mut self, window_id: usize, titlebar_height: f32) -> Presenter { Presenter::new( window_id, diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index b2e1995920..b739ace947 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -52,7 +52,7 @@ pub trait Platform: Send + Sync { ) -> Box; fn key_window_id(&self) -> Option; - fn add_status_item(&self) -> Box; + fn add_status_item(&self) -> Box; fn write_to_clipboard(&self, item: ClipboardItem); fn read_from_clipboard(&self) -> Option; @@ -134,8 +134,6 @@ pub trait Window { fn present_scene(&mut self, scene: Scene); } -pub trait StatusItem {} - #[derive(Debug)] pub struct WindowOptions<'a> { pub bounds: WindowBounds, diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index cda6521a8a..d1a3b7a797 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -495,8 +495,8 @@ impl platform::Platform for MacPlatform { Window::key_window_id() } - fn add_status_item(&self) -> Box { - Box::new(StatusItem::add()) + fn add_status_item(&self) -> Box { + Box::new(StatusItem::add(self.fonts())) } fn fonts(&self) -> Arc { diff --git a/crates/gpui/src/platform/mac/status_item.rs b/crates/gpui/src/platform/mac/status_item.rs index a672e1e323..6a4d754966 100644 --- a/crates/gpui/src/platform/mac/status_item.rs +++ b/crates/gpui/src/platform/mac/status_item.rs @@ -1,60 +1,121 @@ -use cocoa::{ - appkit::{NSSquareStatusItemLength, NSStatusBar, NSStatusItem, NSView}, - base::{id, nil, NO, YES}, - quartzcore::AutoresizingMask, +use crate::{ + geometry::vector::{vec2f, Vector2F}, + platform::{self, mac::renderer::Renderer}, + Event, FontSystem, Scene, }; -use core_foundation::base::TCFType; -use core_graphics::color::CGColor; -use foreign_types::ForeignType; -use objc::{class, msg_send, rc::StrongPtr, sel, sel_impl}; +use cocoa::{ + appkit::{ + NSSquareStatusItemLength, NSStatusBar, NSStatusItem, NSView, NSViewHeightSizable, + NSViewWidthSizable, NSWindow, + }, + base::{id, nil, YES}, + foundation::NSSize, +}; +use foreign_types::ForeignTypeRef; +use objc::{msg_send, rc::StrongPtr, sel, sel_impl}; +use std::{cell::RefCell, rc::Rc, sync::Arc}; -pub struct StatusItem(StrongPtr); +pub struct StatusItem(Rc>); + +struct StatusItemState { + native_item: StrongPtr, + renderer: Renderer, + event_callback: Option bool>>, +} impl StatusItem { - pub fn add() -> Self { - const PIXEL_FORMAT: metal::MTLPixelFormat = metal::MTLPixelFormat::BGRA8Unorm; - + pub fn add(fonts: Arc) -> Self { unsafe { + let renderer = Renderer::new(fonts); let status_bar = NSStatusBar::systemStatusBar(nil); let native_item = StrongPtr::retain(status_bar.statusItemWithLength_(NSSquareStatusItemLength)); - native_item.button().setWantsLayer(true); - let device: metal::Device = if let Some(device) = metal::Device::system_default() { - device - } else { - log::error!("unable to access a compatible graphics device"); - std::process::exit(1); - }; + let button = native_item.button(); + button.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable); + button.setWantsBestResolutionOpenGLSurface_(YES); + button.setLayer(renderer.layer().as_ptr() as id); - let layer: id = msg_send![class!(CAMetalLayer), layer]; - let _: () = msg_send![layer, setDevice: device.as_ptr()]; - let _: () = msg_send![layer, setPixelFormat: PIXEL_FORMAT]; - let _: () = msg_send![layer, setAllowsNextDrawableTimeout: NO]; - let _: () = msg_send![layer, setNeedsDisplayOnBoundsChange: YES]; - let _: () = msg_send![layer, setPresentsWithTransaction: YES]; - let _: () = msg_send![ - layer, - setAutoresizingMask: AutoresizingMask::WIDTH_SIZABLE - | AutoresizingMask::HEIGHT_SIZABLE - ]; - let _: () = msg_send![ - layer, - setBackgroundColor: CGColor::rgb(1., 0., 0., 1.).as_concrete_TypeRef() - ]; - - let _: () = msg_send![native_item.button(), setLayer: layer]; - let native_item_window: id = msg_send![native_item.button(), window]; - - dbg!(native_item_window.frame().as_CGRect()); - // let rect_in_window: NSRect = msg_send![native_item.button(), convertRect: native_item.button().bounds() toView: nil]; - // let screen_rect: NSRect = - // msg_send![native_item_window, convertRectToScreen: rect_in_window]; - // dbg!(screen_rect.as_CGRect()); - - StatusItem(native_item) + Self(Rc::new(RefCell::new(StatusItemState { + native_item, + renderer, + event_callback: None, + }))) } } } -impl crate::StatusItem for StatusItem {} +impl platform::Window for StatusItem { + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + fn on_event(&mut self, callback: Box bool>) { + self.0.borrow_mut().event_callback = Some(callback); + } + + fn on_active_status_change(&mut self, _: Box) {} + + fn on_resize(&mut self, _: Box) {} + + fn on_fullscreen(&mut self, _: Box) {} + + fn on_should_close(&mut self, _: Box bool>) {} + + fn on_close(&mut self, _: Box) {} + + fn set_input_handler(&mut self, _: Box) {} + + fn prompt( + &self, + _: crate::PromptLevel, + _: &str, + _: &[&str], + ) -> postage::oneshot::Receiver { + panic!() + } + + fn activate(&self) {} + + fn set_title(&mut self, _: &str) {} + + fn set_edited(&mut self, _: bool) {} + + fn show_character_palette(&self) {} + + fn minimize(&self) {} + + fn zoom(&self) {} + + fn toggle_full_screen(&self) {} + + fn size(&self) -> Vector2F { + self.0.borrow().size() + } + + fn scale_factor(&self) -> f32 { + self.0.borrow().scale_factor() + } + + fn titlebar_height(&self) -> f32 { + 0. + } + + fn present_scene(&mut self, scene: Scene) { + self.0.borrow_mut().renderer.render(&scene); + } +} + +impl StatusItemState { + fn size(&self) -> Vector2F { + let NSSize { width, height, .. } = unsafe { NSView::frame(self.native_item.button()) }.size; + vec2f(width as f32, height as f32) + } + + fn scale_factor(&self) -> f32 { + unsafe { + let window: id = msg_send![self.native_item.button(), window]; + window.screen().backingScaleFactor() as f32 + } + } +} diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index f64307debf..0ce44f0817 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -144,8 +144,8 @@ impl super::Platform for Platform { None } - fn add_status_item(&self) -> Box { - todo!() + fn add_status_item(&self) -> Box { + Box::new(Window::new(vec2f(24., 24.))) } fn write_to_clipboard(&self, item: ClipboardItem) {