Cleanup
This commit is contained in:
@@ -73,17 +73,83 @@ struct RunLoopWakeHandle {
|
||||
|
||||
struct MacosWindow {
|
||||
spec: WindowSpec,
|
||||
lifecycle: WindowLifecycle,
|
||||
state: MacosWindowState,
|
||||
configuration: WindowConfigured,
|
||||
latest_scene: Option<SceneSnapshot>,
|
||||
clipboard_text: Option<String>,
|
||||
desired_visible: bool,
|
||||
cursor_icon: CursorIcon,
|
||||
resize_pipeline: ResizePipeline,
|
||||
native: Option<NativeWindow>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
struct MacosWindowState {
|
||||
lifecycle: WindowLifecycle,
|
||||
desired_visible: bool,
|
||||
close_requested_sent: bool,
|
||||
close_in_progress: bool,
|
||||
has_presented_frame: bool,
|
||||
resize_pipeline: ResizePipeline,
|
||||
native: Option<NativeWindow>,
|
||||
}
|
||||
|
||||
impl MacosWindowState {
|
||||
fn new(desired_visible: bool) -> Self {
|
||||
Self {
|
||||
lifecycle: WindowLifecycle::LogicalClosed,
|
||||
desired_visible,
|
||||
close_requested_sent: false,
|
||||
close_in_progress: false,
|
||||
has_presented_frame: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn open_with_visibility(&mut self, visible: bool) {
|
||||
self.lifecycle = lifecycle_for_visibility(visible);
|
||||
self.desired_visible = visible;
|
||||
self.close_requested_sent = false;
|
||||
self.close_in_progress = false;
|
||||
}
|
||||
|
||||
fn set_native_visibility(&mut self, visible: bool) {
|
||||
self.lifecycle = lifecycle_for_visibility(visible);
|
||||
if visible {
|
||||
self.close_requested_sent = false;
|
||||
} else {
|
||||
self.has_presented_frame = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn request_logical_close(&mut self) {
|
||||
self.desired_visible = false;
|
||||
self.close_in_progress = true;
|
||||
}
|
||||
|
||||
fn finalize_close(&mut self) {
|
||||
self.lifecycle = WindowLifecycle::LogicalClosed;
|
||||
self.desired_visible = false;
|
||||
self.close_in_progress = false;
|
||||
self.has_presented_frame = false;
|
||||
}
|
||||
|
||||
fn note_presented(&mut self) {
|
||||
self.has_presented_frame = true;
|
||||
}
|
||||
|
||||
fn mark_close_requested_sent(&mut self) -> bool {
|
||||
if self.close_requested_sent {
|
||||
false
|
||||
} else {
|
||||
self.close_requested_sent = true;
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lifecycle_for_visibility(visible: bool) -> WindowLifecycle {
|
||||
if visible {
|
||||
WindowLifecycle::OpenVisible
|
||||
} else {
|
||||
WindowLifecycle::OpenHidden
|
||||
}
|
||||
}
|
||||
|
||||
struct NativeWindow {
|
||||
@@ -95,6 +161,62 @@ struct NativeWindow {
|
||||
renderer: Option<WgpuSceneRenderer>,
|
||||
}
|
||||
|
||||
impl NativeWindow {
|
||||
fn window_ptr(&self) -> usize {
|
||||
self.window as usize
|
||||
}
|
||||
|
||||
fn set_title(&self, title: &str) {
|
||||
let title = NSString::from_str(title);
|
||||
unsafe {
|
||||
let _: () = objc2::msg_send![self.window, setTitle: &*title];
|
||||
}
|
||||
}
|
||||
|
||||
fn set_content_size(&self, size: UiSize) {
|
||||
let content_size = CGSize::new(size.width as f64, size.height as f64);
|
||||
unsafe {
|
||||
let _: () = objc2::msg_send![self.window, setContentSize: content_size];
|
||||
}
|
||||
}
|
||||
|
||||
fn show(&self) {
|
||||
unsafe {
|
||||
let _: () = objc2::msg_send![self.window, makeKeyAndOrderFront: std::ptr::null_mut::<AnyObject>()];
|
||||
}
|
||||
}
|
||||
|
||||
fn hide(&self) {
|
||||
unsafe {
|
||||
let _: () = objc2::msg_send![self.window, orderOut: std::ptr::null_mut::<AnyObject>()];
|
||||
}
|
||||
}
|
||||
|
||||
fn close(&self) {
|
||||
unsafe {
|
||||
let _: () = objc2::msg_send![self.window, close];
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_display(&self) -> bool {
|
||||
let in_live_resize: bool = unsafe { objc2::msg_send![self.view, inLiveResize] };
|
||||
unsafe {
|
||||
let _: () = objc2::msg_send![self.view, displayIfNeeded];
|
||||
let _: () = objc2::msg_send![self.window, displayIfNeeded];
|
||||
let _: () = objc2::msg_send![objc2::class!(CATransaction), flush];
|
||||
}
|
||||
in_live_resize
|
||||
}
|
||||
|
||||
fn detach_delegate(&self) {
|
||||
unsafe {
|
||||
let _: () =
|
||||
objc2::msg_send![self.window, setDelegate: std::ptr::null_mut::<AnyObject>()];
|
||||
let _: () = objc2::msg_send![self.delegate, release];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct DelegateState {
|
||||
pending_close_requests: Vec<usize>,
|
||||
@@ -124,7 +246,6 @@ enum DelegateInputEvent {
|
||||
thread_local! {
|
||||
static ACTIVE_MACOS_STATE: RefCell<Option<Rc<RefCell<MacosState>>>> = const { RefCell::new(None) };
|
||||
static APPKIT_PUMP_ACTIVE: Cell<bool> = const { Cell::new(false) };
|
||||
static IMMEDIATE_DELEGATE_FLUSH_ACTIVE: Cell<bool> = const { Cell::new(false) };
|
||||
}
|
||||
|
||||
fn run_loop_wake_handle() -> &'static Mutex<Option<RunLoopWakeHandle>> {
|
||||
@@ -182,7 +303,7 @@ impl MacosWindow {
|
||||
.unwrap_or_else(|| UiSize::new(960.0, 640.0));
|
||||
Self {
|
||||
spec,
|
||||
lifecycle: WindowLifecycle::LogicalClosed,
|
||||
state: MacosWindowState::new(desired_visible),
|
||||
configuration: WindowConfigured {
|
||||
actual_inner_size,
|
||||
scale_factor: 1.0,
|
||||
@@ -192,11 +313,7 @@ impl MacosWindow {
|
||||
},
|
||||
latest_scene: None,
|
||||
clipboard_text: None,
|
||||
desired_visible,
|
||||
cursor_icon: CursorIcon::Default,
|
||||
close_requested_sent: false,
|
||||
close_in_progress: false,
|
||||
has_presented_frame: false,
|
||||
resize_pipeline: ResizePipeline::default(),
|
||||
native: None,
|
||||
}
|
||||
@@ -494,7 +611,7 @@ fn window_delegate_class() -> &'static AnyClass {
|
||||
"windowShouldClose:"
|
||||
);
|
||||
queue_delegate_close_request(sender);
|
||||
flush_delegate_events_immediately();
|
||||
signal_platform_run_loop_source();
|
||||
Bool::YES
|
||||
}
|
||||
extern "C-unwind" fn did_close(_this: &AnyObject, _cmd: Sel, notification: *mut AnyObject) {
|
||||
@@ -505,7 +622,7 @@ fn window_delegate_class() -> &'static AnyClass {
|
||||
"windowWillClose:"
|
||||
);
|
||||
queue_delegate_window_closed(window);
|
||||
flush_delegate_events_immediately();
|
||||
signal_platform_run_loop_source();
|
||||
}
|
||||
extern "C-unwind" fn did_resize(
|
||||
_this: &AnyObject,
|
||||
@@ -535,7 +652,7 @@ fn window_delegate_class() -> &'static AnyClass {
|
||||
UiSize::new(frame.size.width as f32, frame.size.height as f32),
|
||||
(scale_factor as f32).max(1.0),
|
||||
);
|
||||
flush_delegate_events_immediately();
|
||||
signal_platform_run_loop_source();
|
||||
}
|
||||
extern "C-unwind" fn did_change_occlusion_state(
|
||||
_this: &AnyObject,
|
||||
@@ -636,7 +753,7 @@ fn content_view_class() -> &'static AnyClass {
|
||||
|
||||
extern "C-unwind" fn mouse_moved(this: &AnyObject, _cmd: Sel, event: *mut AnyObject) {
|
||||
queue_pointer_from_view_event(this, event, PointerEventKind::Move);
|
||||
flush_delegate_events_immediately();
|
||||
signal_platform_run_loop_source();
|
||||
}
|
||||
|
||||
extern "C-unwind" fn left_mouse_down(this: &AnyObject, _cmd: Sel, event: *mut AnyObject) {
|
||||
@@ -647,7 +764,7 @@ fn content_view_class() -> &'static AnyClass {
|
||||
button: PointerButton::Primary,
|
||||
},
|
||||
);
|
||||
flush_delegate_events_immediately();
|
||||
signal_platform_run_loop_source();
|
||||
}
|
||||
|
||||
extern "C-unwind" fn left_mouse_up(this: &AnyObject, _cmd: Sel, event: *mut AnyObject) {
|
||||
@@ -658,7 +775,7 @@ fn content_view_class() -> &'static AnyClass {
|
||||
button: PointerButton::Primary,
|
||||
},
|
||||
);
|
||||
flush_delegate_events_immediately();
|
||||
signal_platform_run_loop_source();
|
||||
}
|
||||
|
||||
extern "C-unwind" fn left_mouse_dragged(
|
||||
@@ -667,7 +784,7 @@ fn content_view_class() -> &'static AnyClass {
|
||||
event: *mut AnyObject,
|
||||
) {
|
||||
queue_pointer_from_view_event(this, event, PointerEventKind::Move);
|
||||
flush_delegate_events_immediately();
|
||||
signal_platform_run_loop_source();
|
||||
}
|
||||
|
||||
extern "C-unwind" fn other_mouse_down(this: &AnyObject, _cmd: Sel, event: *mut AnyObject) {
|
||||
@@ -680,7 +797,7 @@ fn content_view_class() -> &'static AnyClass {
|
||||
button: PointerButton::Middle,
|
||||
},
|
||||
);
|
||||
flush_delegate_events_immediately();
|
||||
signal_platform_run_loop_source();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,7 +811,7 @@ fn content_view_class() -> &'static AnyClass {
|
||||
button: PointerButton::Middle,
|
||||
},
|
||||
);
|
||||
flush_delegate_events_immediately();
|
||||
signal_platform_run_loop_source();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -706,7 +823,7 @@ fn content_view_class() -> &'static AnyClass {
|
||||
let button_number: usize = unsafe { objc2::msg_send![event, buttonNumber] };
|
||||
if button_number == 2 {
|
||||
queue_pointer_from_view_event(this, event, PointerEventKind::Move);
|
||||
flush_delegate_events_immediately();
|
||||
signal_platform_run_loop_source();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -720,17 +837,17 @@ fn content_view_class() -> &'static AnyClass {
|
||||
delta: Point::new(delta_x as f32, -(delta_y as f32)),
|
||||
},
|
||||
);
|
||||
flush_delegate_events_immediately();
|
||||
signal_platform_run_loop_source();
|
||||
}
|
||||
|
||||
extern "C-unwind" fn key_down(this: &AnyObject, _cmd: Sel, event: *mut AnyObject) {
|
||||
queue_keyboard_from_view_event(this, event, KeyboardEventKind::Pressed);
|
||||
flush_delegate_events_immediately();
|
||||
signal_platform_run_loop_source();
|
||||
}
|
||||
|
||||
extern "C-unwind" fn key_up(this: &AnyObject, _cmd: Sel, event: *mut AnyObject) {
|
||||
queue_keyboard_from_view_event(this, event, KeyboardEventKind::Released);
|
||||
flush_delegate_events_immediately();
|
||||
signal_platform_run_loop_source();
|
||||
}
|
||||
|
||||
let class_name = CString::new("RuinContentView").expect("class name is valid");
|
||||
@@ -851,47 +968,6 @@ fn try_enter_appkit_pump() -> Option<AppKitPumpGuard> {
|
||||
})
|
||||
}
|
||||
|
||||
struct ImmediateDelegateFlushGuard;
|
||||
|
||||
impl Drop for ImmediateDelegateFlushGuard {
|
||||
fn drop(&mut self) {
|
||||
IMMEDIATE_DELEGATE_FLUSH_ACTIVE.with(|flag| flag.set(false));
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_delegate_events_immediately() {
|
||||
let Some(_guard) = IMMEDIATE_DELEGATE_FLUSH_ACTIVE.with(|flag| {
|
||||
if flag.replace(true) {
|
||||
None
|
||||
} else {
|
||||
Some(ImmediateDelegateFlushGuard)
|
||||
}
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(state) = ACTIVE_MACOS_STATE.with(|slot| slot.borrow().as_ref().cloned()) else {
|
||||
return;
|
||||
};
|
||||
let mut pending = Vec::new();
|
||||
{
|
||||
let Ok(mut state_ref) = state.try_borrow_mut() else {
|
||||
return;
|
||||
};
|
||||
apply_delegate_events(&mut state_ref, &mut pending);
|
||||
}
|
||||
if pending.is_empty() {
|
||||
return;
|
||||
}
|
||||
trace!(
|
||||
target: "ruin_ui_platform_macos::events",
|
||||
count = pending.len(),
|
||||
"flushing delegate events immediately during AppKit callback"
|
||||
);
|
||||
let sender = state.borrow().endpoint_events.clone();
|
||||
flush_pending_events(&sender, &mut pending);
|
||||
}
|
||||
|
||||
fn drain_macos_run_loop_work(state: &Rc<RefCell<MacosState>>) {
|
||||
let Some(_guard) = try_enter_appkit_pump() else {
|
||||
return;
|
||||
@@ -1021,15 +1097,8 @@ fn handle_create_window(state: &Rc<RefCell<MacosState>>, window_id: WindowId, sp
|
||||
if let Some(window) = state_ref.windows.get_mut(&window_id) {
|
||||
ensure_native_window(window, app);
|
||||
sync_window_metrics(window);
|
||||
window.lifecycle = if visible {
|
||||
WindowLifecycle::OpenVisible
|
||||
} else {
|
||||
WindowLifecycle::OpenHidden
|
||||
};
|
||||
window.state.open_with_visibility(visible);
|
||||
window.configuration.visible = visible;
|
||||
window.desired_visible = visible;
|
||||
window.close_requested_sent = false;
|
||||
window.close_in_progress = false;
|
||||
let _ = window
|
||||
.resize_pipeline
|
||||
.ensure_initial_request(window.configuration.actual_inner_size);
|
||||
@@ -1082,46 +1151,37 @@ fn handle_update_window(
|
||||
if let Some(title) = update.title {
|
||||
window.spec.title = title;
|
||||
if let Some(native) = window.native.as_ref() {
|
||||
let title = NSString::from_str(&window.spec.title);
|
||||
unsafe {
|
||||
let _: () = objc2::msg_send![native.window, setTitle: &*title];
|
||||
}
|
||||
native.set_title(&window.spec.title);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(open) = update.open {
|
||||
if open {
|
||||
if window.lifecycle == WindowLifecycle::LogicalClosed {
|
||||
if window.state.lifecycle == WindowLifecycle::LogicalClosed {
|
||||
ensure_native_window(window, app);
|
||||
sync_window_metrics(window);
|
||||
window.lifecycle = if window.configuration.visible {
|
||||
WindowLifecycle::OpenVisible
|
||||
} else {
|
||||
WindowLifecycle::OpenHidden
|
||||
};
|
||||
window.close_requested_sent = false;
|
||||
window.close_in_progress = false;
|
||||
window
|
||||
.state
|
||||
.open_with_visibility(window.configuration.visible);
|
||||
sync_visibility(window);
|
||||
apply_cursor_icon(window.cursor_icon);
|
||||
emit_opened = true;
|
||||
}
|
||||
} else if window.lifecycle != WindowLifecycle::LogicalClosed {
|
||||
} else if window.state.lifecycle != WindowLifecycle::LogicalClosed {
|
||||
debug!(
|
||||
target: "ruin_ui_platform_macos::lifecycle",
|
||||
?window_id,
|
||||
"platform received open(false), closing native window"
|
||||
);
|
||||
window.desired_visible = false;
|
||||
window.state.request_logical_close();
|
||||
if window.native.is_some() {
|
||||
window.close_in_progress = true;
|
||||
close_native_window(window);
|
||||
// Keep behavior aligned with other current platform backends:
|
||||
// treat a requested close as immediately closed for app shutdown.
|
||||
window.lifecycle = WindowLifecycle::LogicalClosed;
|
||||
window.close_in_progress = false;
|
||||
window.state.finalize_close();
|
||||
emit_closed = true;
|
||||
} else {
|
||||
window.lifecycle = WindowLifecycle::LogicalClosed;
|
||||
window.state.finalize_close();
|
||||
emit_closed = true;
|
||||
}
|
||||
}
|
||||
@@ -1131,15 +1191,7 @@ fn handle_update_window(
|
||||
&& window.configuration.visible != visible
|
||||
{
|
||||
window.configuration.visible = visible;
|
||||
window.desired_visible = visible;
|
||||
window.lifecycle = if visible {
|
||||
WindowLifecycle::OpenVisible
|
||||
} else {
|
||||
WindowLifecycle::OpenHidden
|
||||
};
|
||||
if visible {
|
||||
window.close_requested_sent = false;
|
||||
}
|
||||
window.state.open_with_visibility(visible);
|
||||
sync_visibility(window);
|
||||
emit_visibility = Some(visible);
|
||||
}
|
||||
@@ -1269,7 +1321,7 @@ fn try_present_scene(
|
||||
return None;
|
||||
}
|
||||
|
||||
window.has_presented_frame = true;
|
||||
window.state.note_presented();
|
||||
flush_presented_frame(window);
|
||||
if let Some(next_resize) = finish_presented_resize(window, scene_viewport) {
|
||||
trace!(
|
||||
@@ -1304,7 +1356,7 @@ fn repaint_retained_scene_for_resize(window_id: WindowId, window: &mut MacosWind
|
||||
"repainting retained scene into resized drawable"
|
||||
);
|
||||
if render_scene_to_current_drawable(window_id, window, &scene) {
|
||||
window.has_presented_frame = true;
|
||||
window.state.note_presented();
|
||||
flush_presented_frame(window);
|
||||
}
|
||||
}
|
||||
@@ -1486,10 +1538,7 @@ fn create_native_window(window: &MacosWindow, app: *mut AnyObject) -> Option<Nat
|
||||
}
|
||||
|
||||
fn resize_native_window(native: &mut NativeWindow, size: UiSize) {
|
||||
let content_size = CGSize::new(size.width as f64, size.height as f64);
|
||||
unsafe {
|
||||
let _: () = objc2::msg_send![native.window, setContentSize: content_size];
|
||||
}
|
||||
native.set_content_size(size);
|
||||
}
|
||||
|
||||
fn sync_visibility(window: &MacosWindow) {
|
||||
@@ -1498,14 +1547,9 @@ fn sync_visibility(window: &MacosWindow) {
|
||||
};
|
||||
|
||||
if window.configuration.visible {
|
||||
unsafe {
|
||||
let _: () = objc2::msg_send![native.window, makeKeyAndOrderFront: std::ptr::null_mut::<AnyObject>()];
|
||||
}
|
||||
native.show();
|
||||
} else {
|
||||
unsafe {
|
||||
let _: () =
|
||||
objc2::msg_send![native.window, orderOut: std::ptr::null_mut::<AnyObject>()];
|
||||
}
|
||||
native.hide();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1513,12 +1557,10 @@ fn close_native_window(window: &mut MacosWindow) {
|
||||
if let Some(native) = window.native.as_ref() {
|
||||
debug!(
|
||||
target: "ruin_ui_platform_macos::lifecycle",
|
||||
window_ptr = native.window as usize,
|
||||
window_ptr = native.window_ptr(),
|
||||
"sending NSWindow close"
|
||||
);
|
||||
unsafe {
|
||||
let _: () = objc2::msg_send![native.window, close];
|
||||
}
|
||||
native.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1534,11 +1576,11 @@ fn apply_delegate_events(state: &mut MacosState, pending: &mut Vec<PlatformEvent
|
||||
window
|
||||
.native
|
||||
.as_ref()
|
||||
.is_some_and(|native| native.window as usize == resize.window_ptr)
|
||||
.is_some_and(|native| native.window_ptr() == resize.window_ptr)
|
||||
}) else {
|
||||
continue;
|
||||
};
|
||||
if window.lifecycle == WindowLifecycle::LogicalClosed {
|
||||
if window.state.lifecycle == WindowLifecycle::LogicalClosed {
|
||||
continue;
|
||||
}
|
||||
let resized = resize.size.width > 0.0
|
||||
@@ -1563,11 +1605,11 @@ fn apply_delegate_events(state: &mut MacosState, pending: &mut Vec<PlatformEvent
|
||||
window
|
||||
.native
|
||||
.as_ref()
|
||||
.is_some_and(|native| native.window as usize == window_ptr)
|
||||
.is_some_and(|native| native.window_ptr() == window_ptr)
|
||||
}) else {
|
||||
continue;
|
||||
};
|
||||
if window.lifecycle != WindowLifecycle::LogicalClosed {
|
||||
if window.state.lifecycle != WindowLifecycle::LogicalClosed {
|
||||
pending.push(PlatformEvent::Pointer {
|
||||
window_id: *window_id,
|
||||
event,
|
||||
@@ -1579,11 +1621,11 @@ fn apply_delegate_events(state: &mut MacosState, pending: &mut Vec<PlatformEvent
|
||||
window
|
||||
.native
|
||||
.as_ref()
|
||||
.is_some_and(|native| native.window as usize == window_ptr)
|
||||
.is_some_and(|native| native.window_ptr() == window_ptr)
|
||||
}) else {
|
||||
continue;
|
||||
};
|
||||
if window.lifecycle != WindowLifecycle::LogicalClosed {
|
||||
if window.state.lifecycle != WindowLifecycle::LogicalClosed {
|
||||
pending.push(PlatformEvent::Keyboard {
|
||||
window_id: *window_id,
|
||||
event,
|
||||
@@ -1598,12 +1640,11 @@ fn apply_delegate_events(state: &mut MacosState, pending: &mut Vec<PlatformEvent
|
||||
window
|
||||
.native
|
||||
.as_ref()
|
||||
.is_some_and(|native| native.window as usize == window_ptr)
|
||||
.is_some_and(|native| native.window_ptr() == window_ptr)
|
||||
}) else {
|
||||
continue;
|
||||
};
|
||||
if !window.close_requested_sent {
|
||||
window.close_requested_sent = true;
|
||||
if window.state.mark_close_requested_sent() {
|
||||
debug!(
|
||||
target: "ruin_ui_platform_macos::lifecycle",
|
||||
?window_id,
|
||||
@@ -1620,7 +1661,7 @@ fn apply_delegate_events(state: &mut MacosState, pending: &mut Vec<PlatformEvent
|
||||
window
|
||||
.native
|
||||
.as_ref()
|
||||
.is_some_and(|native| native.window as usize == window_ptr)
|
||||
.is_some_and(|native| native.window_ptr() == window_ptr)
|
||||
}) else {
|
||||
continue;
|
||||
};
|
||||
@@ -1632,7 +1673,7 @@ fn apply_delegate_events(state: &mut MacosState, pending: &mut Vec<PlatformEvent
|
||||
&& state
|
||||
.windows
|
||||
.values()
|
||||
.all(|window| window.lifecycle == WindowLifecycle::LogicalClosed)
|
||||
.all(|window| window.state.lifecycle == WindowLifecycle::LogicalClosed)
|
||||
{
|
||||
debug!(
|
||||
target: "ruin_ui_platform_macos::lifecycle",
|
||||
@@ -1703,12 +1744,7 @@ fn flush_presented_frame(window: &MacosWindow) {
|
||||
let Some(native) = window.native.as_ref() else {
|
||||
return;
|
||||
};
|
||||
let in_live_resize: bool = unsafe { objc2::msg_send![native.view, inLiveResize] };
|
||||
unsafe {
|
||||
let _: () = objc2::msg_send![native.view, displayIfNeeded];
|
||||
let _: () = objc2::msg_send![native.window, displayIfNeeded];
|
||||
let _: () = objc2::msg_send![objc2::class!(CATransaction), flush];
|
||||
}
|
||||
let in_live_resize = native.flush_display();
|
||||
trace!(
|
||||
target: "ruin_ui_platform_macos::resize",
|
||||
in_live_resize,
|
||||
@@ -1765,7 +1801,7 @@ fn collect_window_state_events(
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if window.lifecycle == WindowLifecycle::LogicalClosed {
|
||||
if window.state.lifecycle == WindowLifecycle::LogicalClosed {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1788,20 +1824,14 @@ fn collect_window_state_events(
|
||||
let became_visible = is_visible && !window.configuration.visible;
|
||||
if is_visible != window.configuration.visible {
|
||||
window.configuration.visible = is_visible;
|
||||
window.lifecycle = if is_visible {
|
||||
window.close_requested_sent = false;
|
||||
WindowLifecycle::OpenVisible
|
||||
} else {
|
||||
window.has_presented_frame = false;
|
||||
WindowLifecycle::OpenHidden
|
||||
};
|
||||
window.state.set_native_visibility(is_visible);
|
||||
pending.push(PlatformEvent::VisibilityChanged {
|
||||
window_id,
|
||||
visible: is_visible,
|
||||
});
|
||||
}
|
||||
|
||||
if window.close_in_progress && !is_visible {
|
||||
if window.state.close_in_progress && !is_visible {
|
||||
finalize_native_close(window_id, window, pending);
|
||||
return;
|
||||
}
|
||||
@@ -1809,12 +1839,12 @@ fn collect_window_state_events(
|
||||
if is_visible
|
||||
&& let Some(scene) = window.latest_scene.clone()
|
||||
&& (became_visible
|
||||
|| !window.has_presented_frame
|
||||
|| !window.state.has_presented_frame
|
||||
|| window.resize_pipeline.active_resize == Some(scene.logical_size))
|
||||
{
|
||||
let _ = try_present_scene(window_id, window, &scene, pending);
|
||||
} else if is_visible
|
||||
&& !window.has_presented_frame
|
||||
&& !window.state.has_presented_frame
|
||||
&& let Some(initial_viewport) = window
|
||||
.resize_pipeline
|
||||
.ensure_initial_request(window.configuration.actual_inner_size)
|
||||
@@ -1907,18 +1937,12 @@ fn finalize_native_close(
|
||||
?window_id,
|
||||
"finalizing native close"
|
||||
);
|
||||
let should_emit_closed = window.lifecycle != WindowLifecycle::LogicalClosed;
|
||||
let should_emit_closed = window.state.lifecycle != WindowLifecycle::LogicalClosed;
|
||||
if let Some(native) = window.native.take() {
|
||||
unsafe {
|
||||
let _: () =
|
||||
objc2::msg_send![native.window, setDelegate: std::ptr::null_mut::<AnyObject>()];
|
||||
let _: () = objc2::msg_send![native.delegate, release];
|
||||
}
|
||||
native.detach_delegate();
|
||||
}
|
||||
|
||||
window.close_in_progress = false;
|
||||
window.lifecycle = WindowLifecycle::LogicalClosed;
|
||||
window.desired_visible = false;
|
||||
window.state.finalize_close();
|
||||
|
||||
if window.configuration.visible {
|
||||
window.configuration.visible = false;
|
||||
@@ -1954,8 +1978,8 @@ fn sync_window_metrics(window: &mut MacosWindow) {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ResizePipeline;
|
||||
use ruin_ui::UiSize;
|
||||
use super::{MacosWindowState, ResizePipeline};
|
||||
use ruin_ui::{UiSize, WindowLifecycle};
|
||||
|
||||
#[test]
|
||||
fn resize_pipeline_stages_latest_resize_while_active_request_is_in_flight() {
|
||||
@@ -2020,4 +2044,40 @@ mod tests {
|
||||
assert_eq!(pipeline.active_resize, Some(active));
|
||||
assert_eq!(pipeline.next_resize, Some(next));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_state_distinguishes_hide_from_close() {
|
||||
let mut state = MacosWindowState::new(true);
|
||||
state.open_with_visibility(true);
|
||||
state.note_presented();
|
||||
|
||||
state.set_native_visibility(false);
|
||||
assert_eq!(state.lifecycle, WindowLifecycle::OpenHidden);
|
||||
assert!(state.desired_visible);
|
||||
assert!(!state.close_in_progress);
|
||||
assert!(!state.has_presented_frame);
|
||||
|
||||
state.set_native_visibility(true);
|
||||
assert_eq!(state.lifecycle, WindowLifecycle::OpenVisible);
|
||||
assert!(state.desired_visible);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_state_close_request_is_single_shot_and_finalize_resets_state() {
|
||||
let mut state = MacosWindowState::new(true);
|
||||
state.open_with_visibility(true);
|
||||
|
||||
assert!(state.mark_close_requested_sent());
|
||||
assert!(!state.mark_close_requested_sent());
|
||||
|
||||
state.request_logical_close();
|
||||
assert!(!state.desired_visible);
|
||||
assert!(state.close_in_progress);
|
||||
|
||||
state.finalize_close();
|
||||
assert_eq!(state.lifecycle, WindowLifecycle::LogicalClosed);
|
||||
assert!(!state.desired_visible);
|
||||
assert!(!state.close_in_progress);
|
||||
assert!(!state.has_presented_frame);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user