Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API change: Window implements Clone and has an explicit close method #1

Merged
merged 4 commits into from
Oct 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/backend/cocoa/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ use icrate::Foundation::{NSPoint, NSSize, NSThread};

use super::display_links::DisplayLinks;
use super::timer::{TimerInner, Timers};
use super::window::View;
use super::window::{View, WindowState};
use crate::{App, AppContext, AppMode, AppOptions, Error, IntoInnerError, Result};

pub struct AppState {
pub class: &'static AnyClass,
pub empty_cursor: Id<NSCursor>,
pub timers: Timers,
pub display_links: DisplayLinks,
pub windows: RefCell<HashMap<*const View, Id<View>>>,
pub windows: RefCell<HashMap<*const WindowState, Rc<WindowState>>>,
pub data: RefCell<Option<Box<dyn Any>>>,
}

Expand Down Expand Up @@ -131,6 +131,10 @@ impl<T> Drop for AppInner<T> {
autoreleasepool(|_| {
if let Ok(mut data) = self.state.data.try_borrow_mut() {
drop(data.take());

for window_state in self.state.windows.take().into_values() {
window_state.close();
}
}
})
}
Expand Down
17 changes: 10 additions & 7 deletions src/backend/cocoa/display_links.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use core_foundation::runloop::*;

use super::app::AppState;
use super::ffi::display_link::*;
use super::window::View;
use super::window::{View, WindowState};
use crate::Event;

fn display_from_screen(screen: &NSScreen) -> Option<CGDirectDisplayID> {
Expand Down Expand Up @@ -68,12 +68,15 @@ extern "C" fn perform(info: *const c_void) {
let state = unsafe { &*(info as *mut DisplayState) };

if let Some(app_state) = state.app_state.upgrade() {
let windows: Vec<*const View> = app_state.windows.borrow().keys().copied().collect();
for view_ptr in windows {
let view = app_state.windows.borrow().get(&view_ptr).map(|w| w.retain());
if let Some(view) = view {
if display_from_view(&*view) == Some(state.display_id) {
view.state().handle_event(Event::Frame);
let windows: Vec<*const WindowState> = app_state.windows.borrow().keys().copied().collect();
for ptr in windows {
let window_state = app_state.windows.borrow().get(&ptr).cloned();
if let Some(window_state) = window_state {
if let Some(view) = window_state.view() {
let display = display_from_view(&*view);
if display == Some(state.display_id) {
window_state.handle_event(Event::Frame);
}
}
}
}
Expand Down
197 changes: 107 additions & 90 deletions src/backend/cocoa/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,15 +171,6 @@ impl View {
unsafe { &*(self.state.get() as *const WindowState) }
}

fn new(state: Box<WindowState>, frame: NSRect) -> Id<View> {
let view: Option<Allocated<View>> = unsafe { msg_send_id![state.app_state.class, alloc] };
let view: Id<View> = unsafe { msg_send_id![view, initWithFrame: frame] };

view.state.set(Box::into_raw(state) as *mut c_void);

view
}

pub fn retain(&self) -> Id<View> {
unsafe { Id::retain(self as *const View as *mut View) }.unwrap()
}
Expand Down Expand Up @@ -292,20 +283,30 @@ impl View {
}

unsafe extern "C" fn dealloc(&self, _: Sel) {
drop(Box::from_raw(self.state.get() as *mut WindowState));
drop(Rc::from_raw(self.state.get() as *mut WindowState));

let () = msg_send![super(self, NSView::class()), dealloc];
}
}

pub struct WindowState {
view: RefCell<Option<Id<View>>>,
window: RefCell<Option<Id<NSWindow>>>,
surface: RefCell<Option<Surface>>,
cursor: Cell<Cursor>,
app_state: Rc<AppState>,
handler: RefCell<Box<dyn FnMut(&mut dyn Any, &Rc<AppState>, Event) -> Response>>,
}

impl WindowState {
pub fn view(&self) -> Option<Id<View>> {
self.view.borrow().as_ref().map(|view| view.retain())
}

pub fn window(&self) -> Option<Id<NSWindow>> {
self.window.borrow().clone()
}

pub fn handle_event(&self, event: Event) -> Option<Response> {
if let Ok(mut handler) = self.handler.try_borrow_mut() {
if let Ok(mut data) = self.app_state.data.try_borrow_mut() {
Expand Down Expand Up @@ -353,11 +354,21 @@ impl WindowState {
ns_cursor.set();
}
}

pub fn close(&self) {
if let Some(window) = self.window.take() {
unsafe { window.close() };
}

if let Some(view) = self.view.take() {
unsafe { view.removeFromSuperview() };
}
}
}

#[derive(Clone)]
pub struct WindowInner {
view: Id<View>,
window: Option<Id<NSWindow>>,
state: Rc<WindowState>,
}

impl WindowInner {
Expand All @@ -371,6 +382,8 @@ impl WindowInner {
H: FnMut(&mut T, &AppContext<T>, Event) -> Response,
H: 'static,
{
let app_state = cx.inner.state;

let parent_view = if let Some(parent) = options.parent {
if let RawParent::Cocoa(parent_view) = parent {
Some(parent_view as *const NSView)
Expand Down Expand Up @@ -399,22 +412,29 @@ impl WindowInner {
handler(data, &cx, event)
};

let state = Box::new(WindowState {
let state = Rc::new(WindowState {
view: RefCell::new(None),
window: RefCell::new(None),
surface: RefCell::new(None),
cursor: Cell::new(Cursor::Arrow),
app_state: Rc::clone(cx.inner.state),
app_state: Rc::clone(app_state),
handler: RefCell::new(Box::new(handler_wrapper)),
});
let view = View::new(state, frame);

unsafe {
let tracking_options = icrate::AppKit::NSTrackingMouseEnteredAndExited
| icrate::AppKit::NSTrackingMouseMoved
| icrate::AppKit::NSTrackingCursorUpdate
| icrate::AppKit::NSTrackingActiveAlways
| icrate::AppKit::NSTrackingInVisibleRect
| icrate::AppKit::NSTrackingEnabledDuringMouseDrag;
let view: Option<Allocated<View>> = unsafe { msg_send_id![app_state.class, alloc] };
let view: Id<View> = unsafe { msg_send_id![view, initWithFrame: frame] };
view.state.set(Rc::into_raw(Rc::clone(&state)) as *mut c_void);

state.view.replace(Some(view.retain()));

let tracking_options = icrate::AppKit::NSTrackingMouseEnteredAndExited
| icrate::AppKit::NSTrackingMouseMoved
| icrate::AppKit::NSTrackingCursorUpdate
| icrate::AppKit::NSTrackingActiveAlways
| icrate::AppKit::NSTrackingInVisibleRect
| icrate::AppKit::NSTrackingEnabledDuringMouseDrag;

unsafe {
let tracking_area = NSTrackingArea::initWithRect_options_owner_userInfo(
NSTrackingArea::alloc(),
NSRect::new(NSPoint::new(0.0, 0.0), NSSize::new(0.0, 0.0)),
Expand All @@ -423,32 +443,36 @@ impl WindowInner {
None,
);
view.addTrackingArea(&tracking_area);
}

if let Some(parent_view) = parent_view {
if let Some(parent_view) = parent_view {
unsafe {
view.setHidden(true);
(*parent_view).addSubview(&view);
}
} else {
let origin = options.position.unwrap_or(Point::new(0.0, 0.0));
let content_rect = NSRect::new(
NSPoint::new(origin.x, origin.y),
NSSize::new(options.size.width, options.size.height),
);

let window = if parent_view.is_none() {
let origin = options.position.unwrap_or(Point::new(0.0, 0.0));
let content_rect = NSRect::new(
NSPoint::new(origin.x, origin.y),
NSSize::new(options.size.width, options.size.height),
);

let style_mask = icrate::AppKit::NSWindowStyleMaskTitled
| icrate::AppKit::NSWindowStyleMaskClosable
| icrate::AppKit::NSWindowStyleMaskMiniaturizable
| icrate::AppKit::NSWindowStyleMaskResizable;
let style_mask = icrate::AppKit::NSWindowStyleMaskTitled
| icrate::AppKit::NSWindowStyleMaskClosable
| icrate::AppKit::NSWindowStyleMaskMiniaturizable
| icrate::AppKit::NSWindowStyleMaskResizable;

let window = NSWindow::initWithContentRect_styleMask_backing_defer(
let window = unsafe {
NSWindow::initWithContentRect_styleMask_backing_defer(
NSWindow::alloc(),
content_rect,
style_mask,
icrate::AppKit::NSBackingStoreBuffered,
false,
);
)
};

unsafe {
window.setReleasedWhenClosed(false);

window.setTitle(&NSString::from_str(&options.title));
Expand All @@ -460,74 +484,78 @@ impl WindowInner {
if options.position.is_none() {
window.center();
}
}

Some(window)
} else {
None
};
state.window.replace(Some(window));
}

cx.inner.state.windows.borrow_mut().insert(Id::as_ptr(&view), view.retain());
app_state.windows.borrow_mut().insert(Rc::as_ptr(&state), Rc::clone(&state));

let inner = WindowInner { view, window };
let inner = WindowInner { state };

let scale = inner.scale();
let scale = inner.scale();

let surface = Surface::new(
(scale * options.size.width).round() as usize,
(scale * options.size.height).round() as usize,
);
let surface = Surface::new(
(scale * options.size.width).round() as usize,
(scale * options.size.height).round() as usize,
);

let () = msg_send![&inner.view, setLayer: &*surface.layer];
inner.view.setWantsLayer(true);
unsafe {
let () = msg_send![&*view, setLayer: &*surface.layer];
view.setWantsLayer(true);

surface.layer.setContentsScale(scale);
}

inner.view.state().surface.replace(Some(surface));
inner.state.surface.replace(Some(surface));

Ok(inner)
}
Ok(inner)
}

pub fn show(&self) {
unsafe {
if let Some(window) = &self.window {
window.orderFront(None);
} else {
self.view.setHidden(false);
}
if let Some(window) = self.state.window() {
unsafe { window.orderFront(None) };
}

if let Some(view) = self.state.view() {
unsafe { view.setHidden(false) };
}
}

pub fn hide(&self) {
unsafe {
if let Some(window) = &self.window {
window.orderOut(None);
} else {
self.view.setHidden(true);
}
if let Some(window) = self.state.window() {
unsafe { window.orderOut(None) };
}

if let Some(view) = self.state.view() {
unsafe { view.setHidden(true) };
}
}

pub fn size(&self) -> Size {
let frame = unsafe { self.view.frame() };
if let Some(view) = self.state.view() {
let frame = unsafe { view.frame() };

Size::new(frame.size.width, frame.size.height)
Size::new(frame.size.width, frame.size.height)
} else {
Size::new(0.0, 0.0)
}
}

pub fn scale(&self) -> f64 {
unsafe {
if let Some(window) = self.view.window() {
window.backingScaleFactor()
} else if let Some(screen) = NSScreen::screens().get(0) {
screen.backingScaleFactor()
} else {
1.0
if let Some(view) = self.state.view() {
if let Some(window) = unsafe { view.window() } {
return unsafe { window.backingScaleFactor() };
} else if let Some(screen) = unsafe { NSScreen::screens() }.get(0) {
return unsafe { screen.backingScaleFactor() };
}
}

1.0
}

pub fn present(&self, bitmap: Bitmap) {
if let Some(surface) = &mut *self.view.state().surface.borrow_mut() {
if let Some(surface) = &mut *self.state.surface.borrow_mut() {
let width = surface.width;
let height = surface.height;
let copy_width = bitmap.width().min(width);
Expand All @@ -551,25 +579,14 @@ impl WindowInner {
}

pub fn set_cursor(&self, cursor: Cursor) {
let state = self.view.state();

state.cursor.set(cursor);
state.update_cursor();
self.state.cursor.set(cursor);
self.state.update_cursor();
}

pub fn set_mouse_position(&self, _position: Point) {}
}

impl Drop for WindowInner {
fn drop(&mut self) {
unsafe {
if let Some(window) = &self.window {
window.close();
} else {
self.view.removeFromSuperview();
}
}

self.view.state().app_state.windows.borrow_mut().remove(&Id::as_ptr(&self.view));
pub fn close(&self) {
self.state.app_state.windows.borrow_mut().remove(&Rc::as_ptr(&self.state));
self.state.close();
}
}
4 changes: 4 additions & 0 deletions src/backend/win32/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ impl<T> Drop for AppInner<T> {
fn drop(&mut self) {
if let Ok(mut data) = self.state.data.try_borrow_mut() {
drop(data.take());

for window_state in self.state.windows.take().into_values() {
window_state.close();
}
}
}
}
Expand Down
Loading