1///! Macos screen have a y axis that goings up from the bottom of the screen and
2///! an origin at the bottom left of the main display.
3mod dispatcher;
4mod events;
5mod metal_atlas;
6mod metal_renderer;
7mod open_type;
8mod platform;
9mod screen;
10mod text_system;
11mod window;
12mod window_appearence;
13
14use crate::{px, size, Pixels, Size};
15use anyhow::anyhow;
16use cocoa::{
17 base::{id, nil},
18 foundation::{NSAutoreleasePool, NSNotFound, NSRect, NSSize, NSString, NSUInteger, NSURL},
19};
20use metal_renderer::*;
21use objc::{
22 msg_send,
23 runtime::{BOOL, NO, YES},
24 sel, sel_impl,
25};
26use std::{
27 ffi::{c_char, CStr, OsStr},
28 ops::Range,
29 os::unix::prelude::OsStrExt,
30 path::PathBuf,
31};
32
33pub use dispatcher::*;
34pub use metal_atlas::*;
35pub use platform::*;
36pub use screen::*;
37pub use text_system::*;
38pub use window::*;
39
40trait BoolExt {
41 fn to_objc(self) -> BOOL;
42}
43
44impl BoolExt for bool {
45 fn to_objc(self) -> BOOL {
46 if self {
47 YES
48 } else {
49 NO
50 }
51 }
52}
53
54#[repr(C)]
55#[derive(Copy, Clone, Debug)]
56struct NSRange {
57 pub location: NSUInteger,
58 pub length: NSUInteger,
59}
60
61impl NSRange {
62 fn invalid() -> Self {
63 Self {
64 location: NSNotFound as NSUInteger,
65 length: 0,
66 }
67 }
68
69 fn is_valid(&self) -> bool {
70 self.location != NSNotFound as NSUInteger
71 }
72
73 fn to_range(self) -> Option<Range<usize>> {
74 if self.is_valid() {
75 let start = self.location as usize;
76 let end = start + self.length as usize;
77 Some(start..end)
78 } else {
79 None
80 }
81 }
82}
83
84impl From<Range<usize>> for NSRange {
85 fn from(range: Range<usize>) -> Self {
86 NSRange {
87 location: range.start as NSUInteger,
88 length: range.len() as NSUInteger,
89 }
90 }
91}
92
93unsafe impl objc::Encode for NSRange {
94 fn encode() -> objc::Encoding {
95 let encoding = format!(
96 "{{NSRange={}{}}}",
97 NSUInteger::encode().as_str(),
98 NSUInteger::encode().as_str()
99 );
100 unsafe { objc::Encoding::from_str(&encoding) }
101 }
102}
103
104unsafe fn ns_string(string: &str) -> id {
105 NSString::alloc(nil).init_str(string).autorelease()
106}
107
108impl From<NSSize> for Size<Pixels> {
109 fn from(value: NSSize) -> Self {
110 Size {
111 width: px(value.width as f32),
112 height: px(value.height as f32),
113 }
114 }
115}
116
117pub trait NSRectExt {
118 fn size(&self) -> Size<Pixels>;
119 fn intersects(&self, other: Self) -> bool;
120}
121
122impl NSRectExt for NSRect {
123 fn size(&self) -> Size<Pixels> {
124 size(px(self.size.width as f32), px(self.size.height as f32))
125 }
126
127 fn intersects(&self, other: Self) -> bool {
128 self.size.width > 0.
129 && self.size.height > 0.
130 && other.size.width > 0.
131 && other.size.height > 0.
132 && self.origin.x <= other.origin.x + other.size.width
133 && self.origin.x + self.size.width >= other.origin.x
134 && self.origin.y <= other.origin.y + other.size.height
135 && self.origin.y + self.size.height >= other.origin.y
136 }
137}
138
139// todo!
140#[allow(unused)]
141unsafe fn ns_url_to_path(url: id) -> crate::Result<PathBuf> {
142 let path: *mut c_char = msg_send![url, fileSystemRepresentation];
143 if path.is_null() {
144 Err(anyhow!(
145 "url is not a file path: {}",
146 CStr::from_ptr(url.absoluteString().UTF8String()).to_string_lossy()
147 ))
148 } else {
149 Ok(PathBuf::from(OsStr::from_bytes(
150 CStr::from_ptr(path).to_bytes(),
151 )))
152 }
153}