388 lines · 11.1 KB · Rust
RawDownload
use crate::{    geometry::{        rect::RectF,        vector::{vec2f, Vector2F},    },    platform::{        self,        mac::{platform::NSViewLayerContentsRedrawDuringViewResize, renderer::Renderer},        Event, FontSystem, WindowBounds,    },    Scene,};use cocoa::{    appkit::{NSScreen, NSSquareStatusItemLength, NSStatusBar, NSStatusItem, NSView, NSWindow},    base::{id, nil, YES},    foundation::{NSPoint, NSRect, NSSize},};use ctor::ctor;use foreign_types::ForeignTypeRef;use objc::{    class,    declare::ClassDecl,    msg_send,    rc::StrongPtr,    runtime::{Class, Object, Protocol, Sel},    sel, sel_impl,};use std::{    cell::RefCell,    ffi::c_void,    ptr,    rc::{Rc, Weak},    sync::Arc,}; use super::screen::Screen; static mut VIEW_CLASS: *const Class = ptr::null();const STATE_IVAR: &str = "state"; #[ctor]unsafe fn build_classes() {    VIEW_CLASS = {        let mut decl = ClassDecl::new("GPUIStatusItemView", class!(NSView)).unwrap();        decl.add_ivar::<*mut c_void>(STATE_IVAR);         decl.add_method(sel!(dealloc), dealloc_view as extern "C" fn(&Object, Sel));         decl.add_method(            sel!(mouseDown:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(mouseUp:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(rightMouseDown:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(rightMouseUp:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(otherMouseDown:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(otherMouseUp:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(mouseMoved:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(mouseDragged:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(scrollWheel:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(flagsChanged:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(makeBackingLayer),            make_backing_layer as extern "C" fn(&Object, Sel) -> id,        );        decl.add_method(            sel!(viewDidChangeEffectiveAppearance),            view_did_change_effective_appearance as extern "C" fn(&Object, Sel),        );         decl.add_protocol(Protocol::get("CALayerDelegate").unwrap());        decl.add_method(            sel!(displayLayer:),            display_layer as extern "C" fn(&Object, Sel, id),        );         decl.register()    };} pub struct StatusItem(Rc<RefCell<StatusItemState>>); struct StatusItemState {    native_item: StrongPtr,    native_view: StrongPtr,    renderer: Renderer,    scene: Option<Scene>,    event_callback: Option<Box<dyn FnMut(Event) -> bool>>,    appearance_changed_callback: Option<Box<dyn FnMut()>>,} impl StatusItem {    pub fn add(fonts: Arc<dyn FontSystem>) -> Self {        unsafe {            let renderer = Renderer::new(false, fonts);            let status_bar = NSStatusBar::systemStatusBar(nil);            let native_item =                StrongPtr::retain(status_bar.statusItemWithLength_(NSSquareStatusItemLength));             let button = native_item.button();            let _: () = msg_send![button, setHidden: YES];             let native_view = msg_send![VIEW_CLASS, alloc];            let state = Rc::new(RefCell::new(StatusItemState {                native_item,                native_view: StrongPtr::new(native_view),                renderer,                scene: None,                event_callback: None,                appearance_changed_callback: None,            }));             let parent_view = button.superview().superview();            NSView::initWithFrame_(                native_view,                NSRect::new(NSPoint::new(0., 0.), NSView::frame(parent_view).size),            );            (*native_view).set_ivar(                STATE_IVAR,                Weak::into_raw(Rc::downgrade(&state)) as *const c_void,            );            native_view.setWantsBestResolutionOpenGLSurface_(YES);            native_view.setWantsLayer(YES);            let _: () = msg_send![                native_view,                setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize            ];             parent_view.addSubview_(native_view);             {                let state = state.borrow();                let layer = state.renderer.layer();                let scale_factor = state.scale_factor();                let size = state.content_size() * scale_factor;                layer.set_contents_scale(scale_factor.into());                layer.set_drawable_size(metal::CGSize::new(size.x().into(), size.y().into()));            }             Self(state)        }    }} impl platform::Window for StatusItem {    fn bounds(&self) -> WindowBounds {        self.0.borrow().bounds()    }     fn content_size(&self) -> Vector2F {        self.0.borrow().content_size()    }     fn scale_factor(&self) -> f32 {        self.0.borrow().scale_factor()    }     fn appearance(&self) -> platform::Appearance {        unsafe {            let appearance: id =                msg_send![self.0.borrow().native_item.button(), effectiveAppearance];            platform::Appearance::from_native(appearance)        }    }     fn screen(&self) -> Rc<dyn platform::Screen> {        unsafe {            Rc::new(Screen {                native_screen: self.0.borrow().native_window().screen(),            })        }    }     fn mouse_position(&self) -> Vector2F {        unimplemented!()    }     fn as_any_mut(&mut self) -> &mut dyn std::any::Any {        self    }     fn set_input_handler(&mut self, _: Box<dyn platform::InputHandler>) {}     fn prompt(        &self,        _: crate::platform::PromptLevel,        _: &str,        _: &[&str],    ) -> postage::oneshot::Receiver<usize> {        unimplemented!()    }     fn activate(&self) {        unimplemented!()    }     fn set_title(&mut self, _: &str) {        unimplemented!()    }     fn set_edited(&mut self, _: bool) {        unimplemented!()    }     fn show_character_palette(&self) {        unimplemented!()    }     fn minimize(&self) {        unimplemented!()    }     fn zoom(&self) {        unimplemented!()    }     fn present_scene(&mut self, scene: Scene) {        self.0.borrow_mut().scene = Some(scene);        unsafe {            let _: () = msg_send![*self.0.borrow().native_view, setNeedsDisplay: YES];        }    }     fn toggle_fullscreen(&self) {        unimplemented!()    }     fn on_event(&mut self, callback: Box<dyn FnMut(platform::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_moved(&mut self, _: Box<dyn FnMut()>) {}     fn on_should_close(&mut self, _: Box<dyn FnMut() -> bool>) {}     fn on_close(&mut self, _: Box<dyn FnOnce()>) {}     fn on_appearance_changed(&mut self, callback: Box<dyn FnMut()>) {        self.0.borrow_mut().appearance_changed_callback = Some(callback);    }     fn is_topmost_for_position(&self, _: Vector2F) -> bool {        true    }} impl StatusItemState {    fn bounds(&self) -> WindowBounds {        unsafe {            let window: id = self.native_window();            let screen_frame = window.screen().visibleFrame();            let window_frame = NSWindow::frame(window);            let origin = vec2f(                window_frame.origin.x as f32,                (window_frame.origin.y - screen_frame.size.height - window_frame.size.height)                    as f32,            );            let size = vec2f(                window_frame.size.width as f32,                window_frame.size.height as f32,            );            WindowBounds::Fixed(RectF::new(origin, size))        }    }     fn content_size(&self) -> Vector2F {        unsafe {            let NSSize { width, height, .. } =                NSView::frame(self.native_item.button().superview().superview()).size;            vec2f(width as f32, height as f32)        }    }     fn scale_factor(&self) -> f32 {        unsafe {            let window: id = msg_send![self.native_item.button(), window];            NSScreen::backingScaleFactor(window.screen()) as f32        }    }     pub fn native_window(&self) -> id {        unsafe { msg_send![self.native_item.button(), window] }    }} extern "C" fn dealloc_view(this: &Object, _: Sel) {    unsafe {        drop_state(this);         let _: () = msg_send![super(this, class!(NSView)), dealloc];    }} extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {    unsafe {        if let Some(state) = get_state(this).upgrade() {            let mut state_borrow = state.as_ref().borrow_mut();            if let Some(event) =                Event::from_native(native_event, Some(state_borrow.content_size().y()))            {                if let Some(mut callback) = state_borrow.event_callback.take() {                    drop(state_borrow);                    callback(event);                    state.borrow_mut().event_callback = Some(callback);                }            }        }    }} extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {    if let Some(state) = unsafe { get_state(this).upgrade() } {        let state = state.borrow();        state.renderer.layer().as_ptr() as id    } else {        nil    }} extern "C" fn display_layer(this: &Object, _: Sel, _: id) {    unsafe {        if let Some(state) = get_state(this).upgrade() {            let mut state = state.borrow_mut();            if let Some(scene) = state.scene.take() {                state.renderer.render(&scene);            }        }    }} extern "C" fn view_did_change_effective_appearance(this: &Object, _: Sel) {    unsafe {        if let Some(state) = get_state(this).upgrade() {            let mut state_borrow = state.as_ref().borrow_mut();            if let Some(mut callback) = state_borrow.appearance_changed_callback.take() {                drop(state_borrow);                callback();                state.borrow_mut().appearance_changed_callback = Some(callback);            }        }    }} unsafe fn get_state(object: &Object) -> Weak<RefCell<StatusItemState>> {    let raw: *mut c_void = *object.get_ivar(STATE_IVAR);    let weak1 = Weak::from_raw(raw as *mut RefCell<StatusItemState>);    let weak2 = weak1.clone();    let _ = Weak::into_raw(weak1);    weak2} unsafe fn drop_state(object: &Object) {    let raw: *const c_void = *object.get_ivar(STATE_IVAR);    Weak::from_raw(raw as *const RefCell<StatusItemState>);}