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