#![deny(unsafe_op_in_unsafe_fn)]
use objc2::MainThreadOnly;
use objc2::rc::Retained;
use objc2_app_kit::{
NSBackingStoreType, NSWindow, NSWindowButton, NSWindowStyleMask, NSWindowTitleVisibility,
};
use objc2_foundation::{NSPoint, NSRect, NSSize, NSString};
pub(crate) fn windowed_style_mask(title_bar: bool) -> NSWindowStyleMask {
let standard = NSWindowStyleMask::Titled
| NSWindowStyleMask::Closable
| NSWindowStyleMask::Miniaturizable
| NSWindowStyleMask::Resizable;
if title_bar {
standard
} else {
standard | NSWindowStyleMask::FullSizeContentView
}
}
pub(crate) fn apply_title_bar(window: &NSWindow, title_bar: bool) {
window.setTitlebarAppearsTransparent(!title_bar);
window.setTitleVisibility(if title_bar {
NSWindowTitleVisibility::Visible
} else {
NSWindowTitleVisibility::Hidden
});
set_window_buttons_hidden(window, false);
}
pub(crate) fn set_window_buttons_hidden(window: &NSWindow, hidden: bool) {
for kind in [
NSWindowButton::CloseButton,
NSWindowButton::MiniaturizeButton,
NSWindowButton::ZoomButton,
] {
if let Some(button) = window.standardWindowButton(kind) {
button.setHidden(hidden);
}
}
}
pub(crate) fn create_window(
mtm: objc2::MainThreadMarker,
title: &str,
width: u32,
height: u32,
title_bar: bool,
) -> Result<Retained<NSWindow>, String> {
concinnity_core::window_policy::assert_windows_allowed("the AppKit window");
let content_rect = NSRect::new(
NSPoint::new(0.0, 0.0),
NSSize::new(width as f64, height as f64),
);
let window = unsafe {
NSWindow::initWithContentRect_styleMask_backing_defer(
NSWindow::alloc(mtm),
content_rect,
windowed_style_mask(title_bar),
NSBackingStoreType::Buffered,
false,
)
};
window.setTitle(&NSString::from_str(title));
apply_title_bar(&window, title_bar);
window.center();
unsafe { window.setReleasedWhenClosed(false) };
window.setAcceptsMouseMovedEvents(true);
Ok(window)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn windowed_style_mask_keeps_the_standard_controls() {
for title_bar in [true, false] {
let mask = windowed_style_mask(title_bar);
for bit in [
NSWindowStyleMask::Titled,
NSWindowStyleMask::Closable,
NSWindowStyleMask::Miniaturizable,
NSWindowStyleMask::Resizable,
] {
assert!(mask.contains(bit), "title_bar={title_bar} dropped {bit:?}");
}
}
}
#[test]
fn full_size_content_view_tracks_the_title_bar() {
assert!(
!windowed_style_mask(true).contains(NSWindowStyleMask::FullSizeContentView),
"a titled window must not extend content under the title bar"
);
assert!(
windowed_style_mask(false).contains(NSWindowStyleMask::FullSizeContentView),
"a title-bar-less window must fill the frame"
);
}
}