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