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