Detailed changes
@@ -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<I, F>(&mut self, build_item: F)
- // where
- // I: View,
- // F: FnOnce(&mut ViewContext<I>) -> I,
- // {
- // mem::forget(self.platform.add_status_item());
- // }
+ pub fn add_status_bar_item<T, F>(&mut self, build_root_view: F) -> (usize, ViewHandle<T>)
+ where
+ T: View,
+ F: FnOnce(&mut ViewContext<T>) -> 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<T, F>(&mut self, window_id: usize, build_root_view: F) -> ViewHandle<T>
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,
@@ -52,7 +52,7 @@ pub trait Platform: Send + Sync {
) -> Box<dyn Window>;
fn key_window_id(&self) -> Option<usize>;
- fn add_status_item(&self) -> Box<dyn StatusItem>;
+ fn add_status_item(&self) -> Box<dyn Window>;
fn write_to_clipboard(&self, item: ClipboardItem);
fn read_from_clipboard(&self) -> Option<ClipboardItem>;
@@ -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,
@@ -495,8 +495,8 @@ impl platform::Platform for MacPlatform {
Window::key_window_id()
}
- fn add_status_item(&self) -> Box<dyn platform::StatusItem> {
- Box::new(StatusItem::add())
+ fn add_status_item(&self) -> Box<dyn platform::Window> {
+ Box::new(StatusItem::add(self.fonts()))
}
fn fonts(&self) -> Arc<dyn platform::FontSystem> {
@@ -1,60 +1,121 @@
+use crate::{
+ geometry::vector::{vec2f, Vector2F},
+ platform::{self, mac::renderer::Renderer},
+ Event, FontSystem, Scene,
+};
use cocoa::{
- appkit::{NSSquareStatusItemLength, NSStatusBar, NSStatusItem, NSView},
- base::{id, nil, NO, YES},
- quartzcore::AutoresizingMask,
+ appkit::{
+ NSSquareStatusItemLength, NSStatusBar, NSStatusItem, NSView, NSViewHeightSizable,
+ NSViewWidthSizable, NSWindow,
+ },
+ base::{id, nil, YES},
+ foundation::NSSize,
};
-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 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<RefCell<StatusItemState>>);
-impl StatusItem {
- pub fn add() -> Self {
- const PIXEL_FORMAT: metal::MTLPixelFormat = metal::MTLPixelFormat::BGRA8Unorm;
+struct StatusItemState {
+ native_item: StrongPtr,
+ renderer: Renderer,
+ event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
+}
+impl StatusItem {
+ pub fn add(fonts: Arc<dyn FontSystem>) -> 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 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)
+
+ let button = native_item.button();
+ button.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable);
+ button.setWantsBestResolutionOpenGLSurface_(YES);
+ button.setLayer(renderer.layer().as_ptr() as id);
+
+ 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<dyn FnMut(crate::Event) -> bool>) {
+ self.0.borrow_mut().event_callback = Some(callback);
+ }
+
+ fn on_active_status_change(&mut self, _: Box<dyn FnMut(bool)>) {}
+
+ fn on_resize(&mut self, _: Box<dyn FnMut()>) {}
+
+ fn on_fullscreen(&mut self, _: Box<dyn FnMut(bool)>) {}
+
+ fn on_should_close(&mut self, _: Box<dyn FnMut() -> bool>) {}
+
+ fn on_close(&mut self, _: Box<dyn FnOnce()>) {}
+
+ fn set_input_handler(&mut self, _: Box<dyn crate::InputHandler>) {}
+
+ fn prompt(
+ &self,
+ _: crate::PromptLevel,
+ _: &str,
+ _: &[&str],
+ ) -> postage::oneshot::Receiver<usize> {
+ 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
+ }
+ }
+}
@@ -144,8 +144,8 @@ impl super::Platform for Platform {
None
}
- fn add_status_item(&self) -> Box<dyn crate::StatusItem> {
- todo!()
+ fn add_status_item(&self) -> Box<dyn crate::Window> {
+ Box::new(Window::new(vec2f(24., 24.)))
}
fn write_to_clipboard(&self, item: ClipboardItem) {