mac.rs

  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}