use core::marker::PhantomData;
use core::ptr::NonNull;
use std::ffi::{CStr, CString, c_void};
use crate::brushes::Brush;
use crate::ffi::{
noesis_base_component_release, noesis_typography_font_family_create,
noesis_typography_font_family_get_font_name, noesis_typography_font_family_get_num_fonts,
noesis_typography_font_family_get_source, noesis_typography_get_capitals,
noesis_typography_get_fraction, noesis_typography_get_kerning,
noesis_typography_get_numeral_style, noesis_typography_get_standard_ligatures,
noesis_typography_get_variants, noesis_typography_set_capitals, noesis_typography_set_fraction,
noesis_typography_set_kerning, noesis_typography_set_numeral_style,
noesis_typography_set_standard_ligatures, noesis_typography_set_variants,
noesis_typography_text_box_add_composition_underline,
noesis_typography_text_box_clear_composition_underlines,
noesis_typography_text_box_get_composition_underline,
noesis_typography_text_box_num_composition_underlines,
noesis_typography_text_element_get_font_family, noesis_typography_text_element_get_font_size,
noesis_typography_text_element_get_font_stretch, noesis_typography_text_element_get_font_style,
noesis_typography_text_element_get_font_weight, noesis_typography_text_element_get_foreground,
noesis_typography_text_element_set_font_family, noesis_typography_text_element_set_font_size,
noesis_typography_text_element_set_font_stretch, noesis_typography_text_element_set_font_style,
noesis_typography_text_element_set_font_weight, noesis_typography_text_element_set_foreground,
};
use crate::view::FrameworkElement;
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum FontWeight {
Thin = 100,
ExtraLight = 200,
Light = 300,
SemiLight = 350,
Normal = 400,
Medium = 500,
SemiBold = 600,
Bold = 700,
ExtraBold = 800,
Black = 900,
ExtraBlack = 950,
}
impl FontWeight {
fn from_raw(v: i32) -> Option<Self> {
match v {
100 => Some(Self::Thin),
200 => Some(Self::ExtraLight),
300 => Some(Self::Light),
350 => Some(Self::SemiLight),
400 => Some(Self::Normal),
500 => Some(Self::Medium),
600 => Some(Self::SemiBold),
700 => Some(Self::Bold),
800 => Some(Self::ExtraBold),
900 => Some(Self::Black),
950 => Some(Self::ExtraBlack),
_ => None,
}
}
}
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum FontStyle {
Normal = 0,
Oblique = 1,
Italic = 2,
}
impl FontStyle {
fn from_raw(v: i32) -> Option<Self> {
match v {
0 => Some(Self::Normal),
1 => Some(Self::Oblique),
2 => Some(Self::Italic),
_ => None,
}
}
}
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum FontStretch {
UltraCondensed = 1,
ExtraCondensed = 2,
Condensed = 3,
SemiCondensed = 4,
Normal = 5,
SemiExpanded = 6,
Expanded = 7,
ExtraExpanded = 8,
UltraExpanded = 9,
}
impl FontStretch {
fn from_raw(v: i32) -> Option<Self> {
match v {
1 => Some(Self::UltraCondensed),
2 => Some(Self::ExtraCondensed),
3 => Some(Self::Condensed),
4 => Some(Self::SemiCondensed),
5 => Some(Self::Normal),
6 => Some(Self::SemiExpanded),
7 => Some(Self::Expanded),
8 => Some(Self::ExtraExpanded),
9 => Some(Self::UltraExpanded),
_ => None,
}
}
}
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum FontCapitals {
Normal = 0,
AllSmallCaps = 1,
SmallCaps = 2,
AllPetiteCaps = 3,
PetiteCaps = 4,
Unicase = 5,
Titling = 6,
}
impl FontCapitals {
fn from_raw(v: i32) -> Option<Self> {
match v {
0 => Some(Self::Normal),
1 => Some(Self::AllSmallCaps),
2 => Some(Self::SmallCaps),
3 => Some(Self::AllPetiteCaps),
4 => Some(Self::PetiteCaps),
5 => Some(Self::Unicase),
6 => Some(Self::Titling),
_ => None,
}
}
}
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum FontNumeralStyle {
Normal = 0,
Lining = 1,
OldStyle = 2,
}
impl FontNumeralStyle {
fn from_raw(v: i32) -> Option<Self> {
match v {
0 => Some(Self::Normal),
1 => Some(Self::Lining),
2 => Some(Self::OldStyle),
_ => None,
}
}
}
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum FontFraction {
Normal = 0,
Slashed = 1,
Stacked = 2,
}
impl FontFraction {
fn from_raw(v: i32) -> Option<Self> {
match v {
0 => Some(Self::Normal),
1 => Some(Self::Slashed),
2 => Some(Self::Stacked),
_ => None,
}
}
}
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum FontVariants {
Normal = 0,
Superscript = 1,
Subscript = 2,
Ordinal = 3,
Inferior = 4,
Ruby = 5,
}
impl FontVariants {
fn from_raw(v: i32) -> Option<Self> {
match v {
0 => Some(Self::Normal),
1 => Some(Self::Superscript),
2 => Some(Self::Subscript),
3 => Some(Self::Ordinal),
4 => Some(Self::Inferior),
5 => Some(Self::Ruby),
_ => None,
}
}
}
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum CompositionLineStyle {
None = 0,
Solid = 1,
Dot = 2,
Dash = 3,
Squiggle = 4,
}
impl CompositionLineStyle {
fn from_raw(v: i32) -> Option<Self> {
match v {
0 => Some(Self::None),
1 => Some(Self::Solid),
2 => Some(Self::Dot),
3 => Some(Self::Dash),
4 => Some(Self::Squiggle),
_ => None,
}
}
}
pub struct FontFamily {
ptr: NonNull<c_void>,
}
unsafe impl Send for FontFamily {}
impl FontFamily {
#[must_use]
pub fn new(source: &str) -> Self {
let c = CString::new(source).expect("font family source contained NUL");
let ptr = unsafe { noesis_typography_font_family_create(c.as_ptr()) };
Self {
ptr: NonNull::new(ptr).expect("noesis_typography_font_family_create returned null"),
}
}
#[must_use]
pub fn raw(&self) -> *mut c_void {
self.ptr.as_ptr()
}
#[must_use]
pub fn source(&self) -> Option<String> {
read_source(self.ptr.as_ptr())
}
#[must_use]
pub fn num_fonts(&self) -> u32 {
unsafe { noesis_typography_font_family_get_num_fonts(self.ptr.as_ptr()) }
}
#[must_use]
pub fn font_name(&self, index: u32) -> Option<String> {
let p = unsafe { noesis_typography_font_family_get_font_name(self.ptr.as_ptr(), index) };
if p.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(p) }.to_string_lossy().into_owned())
}
}
}
impl Drop for FontFamily {
fn drop(&mut self) {
unsafe { noesis_base_component_release(self.ptr.as_ptr()) }
}
}
pub struct FontFamilyRef<'a> {
ptr: NonNull<c_void>,
_marker: PhantomData<&'a ()>,
}
impl FontFamilyRef<'_> {
#[must_use]
pub fn raw(&self) -> *mut c_void {
self.ptr.as_ptr()
}
#[must_use]
pub fn source(&self) -> Option<String> {
read_source(self.ptr.as_ptr())
}
}
fn read_source(ptr: *mut c_void) -> Option<String> {
let p = unsafe { noesis_typography_font_family_get_source(ptr) };
if p.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(p) }.to_string_lossy().into_owned())
}
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_font_size(element: &FrameworkElement, size: f32) -> bool {
unsafe { noesis_typography_text_element_set_font_size(element.raw(), size) }
}
#[must_use]
pub fn font_size(element: &FrameworkElement) -> Option<f32> {
let mut out = 0.0_f32;
if unsafe { noesis_typography_text_element_get_font_size(element.raw(), &mut out) } {
Some(out)
} else {
None
}
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_font_family(element: &FrameworkElement, family: &FontFamily) -> bool {
unsafe { noesis_typography_text_element_set_font_family(element.raw(), family.raw()) }
}
#[must_use]
pub fn get_font_family(element: &FrameworkElement) -> Option<FontFamilyRef<'_>> {
let p = unsafe { noesis_typography_text_element_get_font_family(element.raw()) };
NonNull::new(p).map(|ptr| FontFamilyRef {
ptr,
_marker: PhantomData,
})
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_foreground(element: &FrameworkElement, brush: &impl Brush) -> bool {
unsafe { noesis_typography_text_element_set_foreground(element.raw(), brush.brush_raw()) }
}
#[must_use]
pub fn get_foreground(element: &FrameworkElement) -> Option<NonNull<c_void>> {
let p = unsafe { noesis_typography_text_element_get_foreground(element.raw()) };
NonNull::new(p)
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_font_weight(element: &FrameworkElement, weight: FontWeight) -> bool {
unsafe { noesis_typography_text_element_set_font_weight(element.raw(), weight as i32) }
}
#[must_use]
pub fn font_weight(element: &FrameworkElement) -> Option<FontWeight> {
read_i32(element, noesis_typography_text_element_get_font_weight).and_then(FontWeight::from_raw)
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_font_style(element: &FrameworkElement, style: FontStyle) -> bool {
unsafe { noesis_typography_text_element_set_font_style(element.raw(), style as i32) }
}
#[must_use]
pub fn font_style(element: &FrameworkElement) -> Option<FontStyle> {
read_i32(element, noesis_typography_text_element_get_font_style).and_then(FontStyle::from_raw)
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_font_stretch(element: &FrameworkElement, stretch: FontStretch) -> bool {
unsafe { noesis_typography_text_element_set_font_stretch(element.raw(), stretch as i32) }
}
#[must_use]
pub fn font_stretch(element: &FrameworkElement) -> Option<FontStretch> {
read_i32(element, noesis_typography_text_element_get_font_stretch)
.and_then(FontStretch::from_raw)
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_capitals(element: &FrameworkElement, value: FontCapitals) -> bool {
unsafe { noesis_typography_set_capitals(element.raw(), value as i32) }
}
#[must_use]
pub fn capitals(element: &FrameworkElement) -> Option<FontCapitals> {
read_i32(element, noesis_typography_get_capitals).and_then(FontCapitals::from_raw)
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_numeral_style(element: &FrameworkElement, value: FontNumeralStyle) -> bool {
unsafe { noesis_typography_set_numeral_style(element.raw(), value as i32) }
}
#[must_use]
pub fn numeral_style(element: &FrameworkElement) -> Option<FontNumeralStyle> {
read_i32(element, noesis_typography_get_numeral_style).and_then(FontNumeralStyle::from_raw)
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_fraction(element: &FrameworkElement, value: FontFraction) -> bool {
unsafe { noesis_typography_set_fraction(element.raw(), value as i32) }
}
#[must_use]
pub fn fraction(element: &FrameworkElement) -> Option<FontFraction> {
read_i32(element, noesis_typography_get_fraction).and_then(FontFraction::from_raw)
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_variants(element: &FrameworkElement, value: FontVariants) -> bool {
unsafe { noesis_typography_set_variants(element.raw(), value as i32) }
}
#[must_use]
pub fn variants(element: &FrameworkElement) -> Option<FontVariants> {
read_i32(element, noesis_typography_get_variants).and_then(FontVariants::from_raw)
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_standard_ligatures(element: &FrameworkElement, value: bool) -> bool {
unsafe { noesis_typography_set_standard_ligatures(element.raw(), value) }
}
#[must_use]
pub fn standard_ligatures(element: &FrameworkElement) -> Option<bool> {
read_bool(element, noesis_typography_get_standard_ligatures)
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_kerning(element: &FrameworkElement, value: bool) -> bool {
unsafe { noesis_typography_set_kerning(element.raw(), value) }
}
#[must_use]
pub fn kerning(element: &FrameworkElement) -> Option<bool> {
read_bool(element, noesis_typography_get_kerning)
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct CompositionUnderline {
pub start: u32,
pub end: u32,
pub style: CompositionLineStyle,
pub bold: bool,
}
pub fn add_composition_underline(
element: &FrameworkElement,
underline: CompositionUnderline,
) -> bool {
unsafe {
noesis_typography_text_box_add_composition_underline(
element.raw(),
underline.start,
underline.end,
underline.style as i32,
underline.bold,
)
}
}
#[must_use]
pub fn num_composition_underlines(element: &FrameworkElement) -> Option<u32> {
let n = unsafe { noesis_typography_text_box_num_composition_underlines(element.raw()) };
if n < 0 { None } else { Some(n as u32) }
}
#[must_use]
pub fn composition_underline(
element: &FrameworkElement,
index: u32,
) -> Option<CompositionUnderline> {
let mut start = 0_u32;
let mut end = 0_u32;
let mut style = 0_i32;
let mut bold = false;
let ok = unsafe {
noesis_typography_text_box_get_composition_underline(
element.raw(),
index,
&mut start,
&mut end,
&mut style,
&mut bold,
)
};
if ok {
Some(CompositionUnderline {
start,
end,
style: CompositionLineStyle::from_raw(style)?,
bold,
})
} else {
None
}
}
pub fn clear_composition_underlines(element: &FrameworkElement) -> bool {
unsafe { noesis_typography_text_box_clear_composition_underlines(element.raw()) }
}
fn read_i32(
element: &FrameworkElement,
f: unsafe extern "C" fn(*mut c_void, *mut i32) -> bool,
) -> Option<i32> {
let mut out = 0_i32;
if unsafe { f(element.raw(), &mut out) } {
Some(out)
} else {
None
}
}
fn read_bool(
element: &FrameworkElement,
f: unsafe extern "C" fn(*mut c_void, *mut bool) -> bool,
) -> Option<bool> {
let mut out = false;
if unsafe { f(element.raw(), &mut out) } {
Some(out)
} else {
None
}
}