x-graphics 0.2.1

Graphics framework for X
Documentation
use std::mem;

use bitflags::bitflags;
use core_foundation::{
    base::{CFRange, CFTypeID, TCFType},
    declare_TCFType,
    dictionary::CFDictionaryRef,
    impl_CFTypeDescription, impl_TCFType,
};
use core_graphics::{context::CGContextRef, path::CGPathRef};
use core_text::{frame::CTFrameRef, framesetter::CTFramesetterRef, line::CTLineRef};
use libc::{c_void, size_t};

use crate::text::TextAlignment;

// CTLine
#[link(name = "CoreText", kind = "framework")]
extern "C" {
    pub fn CTLineDraw(line: CTLineRef, context: CGContextRef);
}

// CTFrame

#[link(name = "CoreText", kind = "framework")]
extern "C" {
    pub fn CTFrameDraw(frame: CTFrameRef, context: CGContextRef);
}

// CTFramesetter

#[link(name = "CoreText", kind = "framework")]
extern "C" {
    pub fn CTFramesetterCreateFrame(framesetter: CTFramesetterRef, string_range: CFRange, path: CGPathRef, attributes: CFDictionaryRef)
        -> CTFrameRef;
}

// CTParagraphStyle

#[repr(C)]
pub struct __CTParagraphStyle(c_void);

pub type CTParagraphStyleRef = *const __CTParagraphStyle;

#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CTTextAlignment {
    #[doc(alias = "kCTTextAlignmentLeft")]
    Left      = 0,
    #[doc(alias = "kCTTextAlignmentRight")]
    Right     = 1,
    #[doc(alias = "kCTTextAlignmentCenter")]
    Center    = 2,
    #[doc(alias = "kCTTextAlignmentJustified")]
    Justified = 3,
    #[doc(alias = "kCTTextAlignmentNatural")]
    Natural   = 4,
}

#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CTLineBreakMode {
    #[doc(alias = "kCTLineBreakByWordWrapping")]
    WordWrapping     = 0,
    #[doc(alias = "kCTLineBreakByCharWrapping")]
    CharWrapping     = 1,
    #[doc(alias = "kCTLineBreakByClipping")]
    Clipping         = 2,
    #[doc(alias = "kCTLineBreakByTruncatingHead")]
    TruncatingHead   = 3,
    #[doc(alias = "kCTLineBreakByTruncatingTail")]
    TruncatingTail   = 4,
    #[doc(alias = "kCTLineBreakByTruncatingMiddle")]
    TruncatingMiddle = 5,
}

#[repr(i8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CTWritingDirection {
    #[doc(alias = "kCTWritingDirectionNatural")]
    Natural     = -1,
    #[doc(alias = "kCTWritingDirectionLeftToRight")]
    LeftToRight = 0,
    #[doc(alias = "kCTWritingDirectionRightToLeft")]
    RightToLeft = 1,
}

#[repr(u32)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CTParagraphStyleSpecifier {
    #[doc(alias = "kCTParagraphStyleSpecifierAlignment")]
    Alignment              = 0,
    #[doc(alias = "kCTParagraphStyleSpecifierFirstLineHeadIndent")]
    FirstLineHeadIndent    = 1,
    #[doc(alias = "kCTParagraphStyleSpecifierHeadIndent")]
    HeadIndent             = 2,
    #[doc(alias = "kCTParagraphStyleSpecifierTailIndent")]
    TailIndent             = 3,
    #[doc(alias = "kCTParagraphStyleSpecifierTabStops")]
    TabStops               = 4,
    #[doc(alias = "kCTParagraphStyleSpecifierDefaultTabInterval")]
    DefaultTabInterval     = 5,
    #[doc(alias = "kCTParagraphStyleSpecifierLineBreakMode")]
    LineBreakMode          = 6,
    #[doc(alias = "kCTParagraphStyleSpecifierLineHeightMultiple")]
    LineHeightMultiple     = 7,
    #[doc(alias = "kCTParagraphStyleSpecifierMaximumLineHeight")]
    MaximumLineHeight      = 8,
    #[doc(alias = "kCTParagraphStyleSpecifierMinimumLineHeight")]
    MinimumLineHeight      = 9,
    #[doc(alias = "kCTParagraphStyleSpecifierLineSpacing")]
    LineSpacing            = 10,
    #[doc(alias = "kCTParagraphStyleSpecifierParagraphSpacing")]
    ParagraphSpacing       = 11,
    #[doc(alias = "kCTParagraphStyleSpecifierParagraphSpacingBefore")]
    ParagraphSpacingBefore = 12,
    #[doc(alias = "kCTParagraphStyleSpecifierBaseWritingDirection")]
    BaseWritingDirection   = 13,
    #[doc(alias = "kCTParagraphStyleSpecifierMaximumLineSpacing")]
    MaximumLineSpacing     = 14,
    #[doc(alias = "kCTParagraphStyleSpecifierMinimumLineSpacing")]
    MinimumLineSpacing     = 15,
    #[doc(alias = "kCTParagraphStyleSpecifierLineSpacingAdjustment")]
    LineSpacingAdjustment  = 16,
    #[doc(alias = "kCTParagraphStyleSpecifierLineBoundsOptions")]
    LineBoundsOptions      = 17,
    Count,
}

#[repr(C)]
pub struct CTParagraphStyleSetting {
    spec: CTParagraphStyleSpecifier,
    value_size: usize,
    value: *const c_void,
}

#[link(name = "CoreText", kind = "framework")]
extern "C" {
    pub fn CTParagraphStyleCreate(settings: *const CTParagraphStyleSetting, settingCount: size_t) -> CTParagraphStyleRef;
    pub fn CTParagraphStyleCreateCopy(paragraphStyle: CTParagraphStyleRef) -> CTParagraphStyleRef;
    pub fn CTParagraphStyleGetTypeID() -> CFTypeID;
}

impl CTParagraphStyleSetting {
    pub fn from_alignment(alignment: CTTextAlignment) -> Self {
        // Convert the reference of CTTextAlignment enum into a raw pointer
        // maintain the immutability of the value
        let alignment = match alignment {
            CTTextAlignment::Left => &CTTextAlignment::Left,
            CTTextAlignment::Right => &CTTextAlignment::Right,
            CTTextAlignment::Center => &CTTextAlignment::Center,
            CTTextAlignment::Justified => &CTTextAlignment::Justified,
            CTTextAlignment::Natural => &CTTextAlignment::Natural,
        } as *const _ as *const c_void;

        Self {
            spec: CTParagraphStyleSpecifier::Alignment,
            value_size: mem::size_of::<CTTextAlignment>(),
            value: alignment,
        }
    }
}

declare_TCFType!(CTParagraphStyle, CTParagraphStyleRef);
impl_TCFType!(CTParagraphStyle, CTParagraphStyleRef, CTParagraphStyleGetTypeID);
impl_CFTypeDescription!(CTParagraphStyle);

impl CTParagraphStyle {
    pub fn new(settings: &[CTParagraphStyleSetting]) -> Self {
        unsafe {
            let paragraph_style = CTParagraphStyleCreate(settings.as_ptr(), settings.len() as size_t);
            CTParagraphStyle::wrap_under_create_rule(paragraph_style)
        }
    }

    pub fn copy(&self) -> Self {
        unsafe {
            let paragraph_style = CTParagraphStyleCreateCopy(self.as_concrete_TypeRef());
            CTParagraphStyle::wrap_under_create_rule(paragraph_style)
        }
    }
}

// CTStringAttributes

bitflags! {
    #[repr(C)]
    #[derive(Clone, Copy, Debug, Default, PartialEq)]
    pub struct CTUnderlineStyle: i32 {
        #[doc(alias = "kCTUnderlineStyleNone")]
        const None = 0x00;
        #[doc(alias = "kCTUnderlineStyleSingle")]
        const Single = 0x01;
        #[doc(alias = "kCTUnderlineStyleThick")]
        const Thick = 0x02;
        #[doc(alias = "kCTUnderlineStyleDouble")]
        const Double = 0x09;
    }
}

impl From<TextAlignment> for CTTextAlignment {
    fn from(alignment: TextAlignment) -> Self {
        match alignment {
            TextAlignment::Left => CTTextAlignment::Left,
            TextAlignment::Right => CTTextAlignment::Right,
            TextAlignment::Center => CTTextAlignment::Center,
        }
    }
}