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