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