extern crate alloc;
use alloc::{boxed::Box, string::String, vec::Vec};
use core::{borrow::Borrow, ffi::CStr, fmt, ops::Deref};
use crate::{
alloc::PebbleAlloc,
resource::font::Font,
sys,
ui::{
AsLayer,
graphics::{Colour, Rectangle, Size, grect, size},
},
};
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum TextAlignment {
Left = sys::GTextAlignment_GTextAlignmentLeft,
Center = sys::GTextAlignment_GTextAlignmentCenter,
Right = sys::GTextAlignment_GTextAlignmentRight,
}
#[derive(Default)]
pub struct TextBuf {
bytes: Vec<u8>,
}
impl TextBuf {
pub const fn new() -> Self {
Self { bytes: Vec::new() }
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
bytes: Vec::with_capacity(capacity + 1),
}
}
pub fn clear(&mut self) {
self.bytes.clear();
}
#[inline(never)]
pub fn push_str(&mut self, s: &str) {
self.bytes.pop();
self.bytes.reserve(s.len() + 1);
self.bytes.extend(s.bytes().filter(|&b| b != 0));
self.bytes.push(0);
}
pub fn as_c_str(&self) -> &CStr {
if self.bytes.is_empty() {
c""
} else {
unsafe { CStr::from_bytes_with_nul_unchecked(&self.bytes) }
}
}
}
impl Deref for TextBuf {
type Target = CStr;
fn deref(&self) -> &CStr {
self.as_c_str()
}
}
impl fmt::Write for TextBuf {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.push_str(s);
Ok(())
}
}
#[cfg(panic = "immediate-abort")]
impl ufmt::uWrite for TextBuf {
type Error = core::convert::Infallible;
fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
self.push_str(s);
Ok(())
}
}
impl From<&str> for TextBuf {
fn from(s: &str) -> Self {
let mut buf = Self::with_capacity(s.len());
buf.push_str(s);
buf
}
}
impl From<String> for TextBuf {
fn from(s: String) -> Self {
let mut bytes = s.into_bytes();
bytes.retain(|&b| b != 0);
bytes.push(0);
Self { bytes }
}
}
pub struct TextLayer<S: Deref<Target = CStr> = &'static CStr, F: Borrow<Font> = Font> {
ptr: *mut sys::TextLayer,
text: Option<Box<S, PebbleAlloc>>,
font: Option<F>,
}
impl<S: Deref<Target = CStr>, F: Borrow<Font>> TextLayer<S, F> {
pub fn new(frame: Rectangle) -> Option<Self> {
let ptr = unsafe { sys::text_layer_create(grect(frame)).as_mut()? };
Some(Self {
ptr,
text: None,
font: None,
})
}
pub fn set_text(&mut self, text: impl Into<S>) -> &mut Self {
let text = text.into();
match &mut self.text {
Some(boxed) => **boxed = text,
None => self.text = Some(Box::new_in(text, PebbleAlloc)),
}
self.repoint();
self
}
pub fn text(&self) -> Option<&CStr> {
self.text.as_deref().map(Deref::deref)
}
fn repoint(&mut self) {
if let Some(text) = self.text.as_deref() {
unsafe { sys::text_layer_set_text(self.ptr, text.as_ptr()) };
}
}
pub fn set_font(&mut self, font: F) -> &mut Self {
unsafe { sys::text_layer_set_font(self.ptr, font.borrow().as_ptr()) };
self.font = Some(font);
self
}
pub fn set_text_colour(&mut self, colour: Colour) -> &mut Self {
unsafe { sys::text_layer_set_text_color(self.ptr, colour.into()) };
self
}
pub fn set_background_colour(&mut self, colour: Colour) -> &mut Self {
unsafe { sys::text_layer_set_background_color(self.ptr, colour.into()) };
self
}
pub fn set_alignment(&mut self, alignment: TextAlignment) -> &mut Self {
unsafe { sys::text_layer_set_text_alignment(self.ptr, alignment as u8) };
self
}
pub fn content_size(&self) -> Size {
size(unsafe { sys::text_layer_get_content_size(self.ptr) })
}
}
impl<S: Deref<Target = CStr> + Default, F: Borrow<Font>> TextLayer<S, F> {
pub fn text_mut<R>(&mut self, f: impl FnOnce(&mut S) -> R) -> R {
let text = self
.text
.get_or_insert_with(|| Box::new_in(S::default(), PebbleAlloc));
let result = f(text);
self.repoint();
result
}
}
unsafe impl<S: Deref<Target = CStr>, F: Borrow<Font>> AsLayer for TextLayer<S, F> {
fn as_ptr(&self) -> *mut sys::Layer {
unsafe { sys::text_layer_get_layer(self.ptr) }
}
}
impl<S: Deref<Target = CStr>, F: Borrow<Font>> Drop for TextLayer<S, F> {
fn drop(&mut self) {
unsafe {
sys::layer_remove_child_layers(self.as_ptr());
sys::text_layer_destroy(self.ptr);
}
}
}