use alloc::{boxed::Box, rc::Rc};
use oxivgl_sys::*;
use super::GradDir;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TextDecor(pub(crate) u32);
impl TextDecor {
pub const NONE: Self = Self(0x00);
pub const UNDERLINE: Self = Self(0x01);
pub const STRIKETHROUGH: Self = Self(0x02);
}
impl core::ops::BitOr for TextDecor {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
pub struct TransitionDsc {
pub(crate) inner: lv_style_transition_dsc_t,
}
impl core::fmt::Debug for TransitionDsc {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TransitionDsc").finish_non_exhaustive()
}
}
impl TransitionDsc {
pub fn new(
props: &'static [lv_style_prop_t],
path_cb: Option<unsafe extern "C" fn(*const lv_anim_t) -> i32>,
time: u32,
delay: u32,
) -> Self {
let mut inner = unsafe { core::mem::zeroed::<lv_style_transition_dsc_t>() };
unsafe {
lv_style_transition_dsc_init(&mut inner, props.as_ptr(), path_cb, time, delay, core::ptr::null_mut())
};
Self { inner }
}
}
pub mod props {
pub use oxivgl_sys::lv_style_prop_t;
pub const BG_COLOR: lv_style_prop_t = oxivgl_sys::_lv_style_id_t_LV_STYLE_BG_COLOR as lv_style_prop_t;
pub const BORDER_COLOR: lv_style_prop_t = oxivgl_sys::_lv_style_id_t_LV_STYLE_BORDER_COLOR as lv_style_prop_t;
pub const BORDER_WIDTH: lv_style_prop_t = oxivgl_sys::_lv_style_id_t_LV_STYLE_BORDER_WIDTH as lv_style_prop_t;
pub const BG_OPA: lv_style_prop_t = oxivgl_sys::_lv_style_id_t_LV_STYLE_BG_OPA as lv_style_prop_t;
pub const WIDTH: lv_style_prop_t = oxivgl_sys::_lv_style_id_t_LV_STYLE_WIDTH as lv_style_prop_t;
pub const OUTLINE_WIDTH: lv_style_prop_t = oxivgl_sys::_lv_style_id_t_LV_STYLE_OUTLINE_WIDTH as lv_style_prop_t;
pub const OUTLINE_OPA: lv_style_prop_t = oxivgl_sys::_lv_style_id_t_LV_STYLE_OUTLINE_OPA as lv_style_prop_t;
pub const TRANSFORM_WIDTH: lv_style_prop_t =
oxivgl_sys::_lv_style_id_t_LV_STYLE_TRANSFORM_WIDTH as lv_style_prop_t;
pub const TRANSFORM_HEIGHT: lv_style_prop_t =
oxivgl_sys::_lv_style_id_t_LV_STYLE_TRANSFORM_HEIGHT as lv_style_prop_t;
pub const TEXT_LETTER_SPACE: lv_style_prop_t =
oxivgl_sys::_lv_style_id_t_LV_STYLE_TEXT_LETTER_SPACE as lv_style_prop_t;
pub const LAST: lv_style_prop_t = 0;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BorderSide(u32);
impl BorderSide {
pub const NONE: Self = Self(0x00);
pub const BOTTOM: Self = Self(0x01);
pub const TOP: Self = Self(0x02);
pub const LEFT: Self = Self(0x04);
pub const RIGHT: Self = Self(0x08);
pub const FULL: Self = Self(0x0F);
pub const INTERNAL: Self = Self(0x10);
}
impl core::ops::BitOr for BorderSide {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
pub fn lv_pct(v: i32) -> i32 {
unsafe { oxivgl_sys::lv_pct(v) }
}
pub const LV_SIZE_CONTENT: i32 = (oxivgl_sys::LV_COORD_MAX | oxivgl_sys::LV_COORD_TYPE_SPEC) as i32;
#[repr(C)]
pub(crate) struct StyleInner {
lv: lv_style_t,
_grad: Option<Box<super::GradDsc>>,
_transition: Option<Box<TransitionDsc>>,
_color_filter: Option<Box<ColorFilter>>,
}
const _: () = assert!(core::mem::offset_of!(StyleInner, lv) == 0);
impl core::fmt::Debug for StyleInner {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("StyleInner").finish_non_exhaustive()
}
}
impl Drop for StyleInner {
fn drop(&mut self) {
unsafe { lv_style_reset(&mut self.lv) };
}
}
#[derive(Debug)]
pub struct StyleBuilder {
inner: Box<StyleInner>,
}
impl StyleBuilder {
pub fn new() -> Self {
let mut lv = unsafe { core::mem::zeroed::<lv_style_t>() };
unsafe { lv_style_init(&mut lv) };
Self { inner: Box::new(StyleInner { lv, _grad: None, _transition: None, _color_filter: None }) }
}
pub fn build(self) -> Style {
Style { inner: Rc::from(self.inner) }
}
pub fn radius(&mut self, r: i16) -> &mut Self {
unsafe { lv_style_set_radius(&mut self.inner.lv, r as lv_coord_t) };
self
}
pub fn opa(&mut self, opa: u8) -> &mut Self {
unsafe { lv_style_set_opa(&mut self.inner.lv, opa as lv_opa_t) };
self
}
pub fn bg_opa(&mut self, opa: u8) -> &mut Self {
unsafe { lv_style_set_bg_opa(&mut self.inner.lv, opa as lv_opa_t) };
self
}
pub fn bg_color(&mut self, color: lv_color_t) -> &mut Self {
unsafe { lv_style_set_bg_color(&mut self.inner.lv, color) };
self
}
pub fn bg_color_hex(&mut self, hex: u32) -> &mut Self {
let color = unsafe { lv_color_hex(hex) };
self.bg_color(color)
}
pub fn bg_grad_color(&mut self, color: lv_color_t) -> &mut Self {
unsafe { lv_style_set_bg_grad_color(&mut self.inner.lv, color) };
self
}
pub fn bg_grad_color_hex(&mut self, hex: u32) -> &mut Self {
let color = unsafe { lv_color_hex(hex) };
self.bg_grad_color(color)
}
pub fn bg_grad_dir(&mut self, dir: GradDir) -> &mut Self {
unsafe { lv_style_set_bg_grad_dir(&mut self.inner.lv, dir as lv_grad_dir_t) };
self
}
pub fn border_color(&mut self, color: lv_color_t) -> &mut Self {
unsafe { lv_style_set_border_color(&mut self.inner.lv, color) };
self
}
pub fn border_color_hex(&mut self, hex: u32) -> &mut Self {
let color = unsafe { lv_color_hex(hex) };
self.border_color(color)
}
pub fn border_opa(&mut self, opa: u8) -> &mut Self {
unsafe { lv_style_set_border_opa(&mut self.inner.lv, opa as lv_opa_t) };
self
}
pub fn border_width(&mut self, w: i16) -> &mut Self {
unsafe { lv_style_set_border_width(&mut self.inner.lv, w as lv_coord_t) };
self
}
pub fn text_opa(&mut self, opa: u8) -> &mut Self {
unsafe { lv_style_set_text_opa(&mut self.inner.lv, opa as lv_opa_t) };
self
}
pub fn text_font(&mut self, font: crate::fonts::Font) -> &mut Self {
unsafe { lv_style_set_text_font(&mut self.inner.lv, font.as_ptr()) };
self
}
pub fn text_color(&mut self, color: lv_color_t) -> &mut Self {
unsafe { lv_style_set_text_color(&mut self.inner.lv, color) };
self
}
pub fn text_color_hex(&mut self, hex: u32) -> &mut Self {
let color = unsafe { lv_color_hex(hex) };
self.text_color(color)
}
pub fn color_filter(&mut self, filter: ColorFilter, opa: u8) -> &mut Self {
let filter = Box::new(filter);
unsafe {
lv_style_set_color_filter_dsc(&mut self.inner.lv, &filter.inner);
lv_style_set_color_filter_opa(&mut self.inner.lv, opa as lv_opa_t);
}
self.inner._color_filter = Some(filter);
self
}
pub fn color_filter_shade(&mut self, opa: u8) -> &mut Self {
unsafe {
lv_style_set_color_filter_dsc(&mut self.inner.lv, &lv_color_filter_shade);
lv_style_set_color_filter_opa(&mut self.inner.lv, opa as lv_opa_t);
}
self
}
pub fn width(&mut self, w: i32) -> &mut Self {
unsafe { lv_style_set_width(&mut self.inner.lv, w) };
self
}
pub fn height(&mut self, h: i32) -> &mut Self {
unsafe { lv_style_set_height(&mut self.inner.lv, h) };
self
}
pub fn x(&mut self, x: i32) -> &mut Self {
unsafe { lv_style_set_x(&mut self.inner.lv, x) };
self
}
pub fn y(&mut self, y: i32) -> &mut Self {
unsafe { lv_style_set_y(&mut self.inner.lv, y) };
self
}
pub fn pad_gap(&mut self, p: i32) -> &mut Self {
unsafe { lv_style_set_pad_gap(&mut self.inner.lv, p) };
self
}
pub fn pad_ver(&mut self, p: i32) -> &mut Self {
unsafe { lv_style_set_pad_ver(&mut self.inner.lv, p) };
self
}
pub fn pad_left(&mut self, p: i32) -> &mut Self {
unsafe { lv_style_set_pad_left(&mut self.inner.lv, p) };
self
}
pub fn pad_right(&mut self, p: i32) -> &mut Self {
unsafe { lv_style_set_pad_right(&mut self.inner.lv, p) };
self
}
pub fn pad_top(&mut self, p: i32) -> &mut Self {
unsafe { lv_style_set_pad_top(&mut self.inner.lv, p) };
self
}
pub fn length(&mut self, l: i32) -> &mut Self {
unsafe { lv_style_set_length(&mut self.inner.lv, l) };
self
}
pub fn bg_grad(&mut self, grad: super::GradDsc) -> &mut Self {
let grad = Box::new(grad);
unsafe { lv_style_set_bg_grad(&mut self.inner.lv, &grad.inner) };
self.inner._grad = Some(grad);
self
}
pub fn border_side(&mut self, side: BorderSide) -> &mut Self {
unsafe { lv_style_set_border_side(&mut self.inner.lv, side.0 as lv_border_side_t) };
self
}
pub fn outline_width(&mut self, w: i32) -> &mut Self {
unsafe { lv_style_set_outline_width(&mut self.inner.lv, w) };
self
}
pub fn outline_color(&mut self, color: lv_color_t) -> &mut Self {
unsafe { lv_style_set_outline_color(&mut self.inner.lv, color) };
self
}
pub fn outline_pad(&mut self, pad: i32) -> &mut Self {
unsafe { lv_style_set_outline_pad(&mut self.inner.lv, pad) };
self
}
pub fn shadow_width(&mut self, w: i32) -> &mut Self {
unsafe { lv_style_set_shadow_width(&mut self.inner.lv, w) };
self
}
pub fn shadow_color(&mut self, color: lv_color_t) -> &mut Self {
unsafe { lv_style_set_shadow_color(&mut self.inner.lv, color) };
self
}
pub fn arc_color(&mut self, color: lv_color_t) -> &mut Self {
unsafe { lv_style_set_arc_color(&mut self.inner.lv, color) };
self
}
pub fn arc_width(&mut self, w: i32) -> &mut Self {
unsafe { lv_style_set_arc_width(&mut self.inner.lv, w) };
self
}
pub fn pad_all(&mut self, p: i32) -> &mut Self {
unsafe { lv_style_set_pad_all(&mut self.inner.lv, p) };
self
}
pub fn text_letter_space(&mut self, s: i32) -> &mut Self {
unsafe { lv_style_set_text_letter_space(&mut self.inner.lv, s) };
self
}
pub fn text_line_space(&mut self, s: i32) -> &mut Self {
unsafe { lv_style_set_text_line_space(&mut self.inner.lv, s) };
self
}
pub fn text_decor(&mut self, decor: TextDecor) -> &mut Self {
unsafe { lv_style_set_text_decor(&mut self.inner.lv, decor.0 as lv_text_decor_t) };
self
}
pub fn line_color(&mut self, color: lv_color_t) -> &mut Self {
unsafe { lv_style_set_line_color(&mut self.inner.lv, color) };
self
}
pub fn line_width(&mut self, w: i32) -> &mut Self {
unsafe { lv_style_set_line_width(&mut self.inner.lv, w) };
self
}
pub fn line_rounded(&mut self, rounded: bool) -> &mut Self {
unsafe { lv_style_set_line_rounded(&mut self.inner.lv, rounded) };
self
}
pub fn transition(&mut self, tr: TransitionDsc) -> &mut Self {
let tr = Box::new(tr);
unsafe { lv_style_set_transition(&mut self.inner.lv, &tr.inner) };
self.inner._transition = Some(tr);
self
}
pub fn shadow_offset_x(&mut self, x: i32) -> &mut Self {
unsafe { lv_style_set_shadow_offset_x(&mut self.inner.lv, x) };
self
}
pub fn shadow_offset_y(&mut self, y: i32) -> &mut Self {
unsafe { lv_style_set_shadow_offset_y(&mut self.inner.lv, y) };
self
}
pub fn shadow_opa(&mut self, opa: u8) -> &mut Self {
unsafe { lv_style_set_shadow_opa(&mut self.inner.lv, opa as lv_opa_t) };
self
}
pub fn shadow_spread(&mut self, s: i32) -> &mut Self {
unsafe { lv_style_set_shadow_spread(&mut self.inner.lv, s) };
self
}
pub fn outline_opa(&mut self, opa: u8) -> &mut Self {
unsafe { lv_style_set_outline_opa(&mut self.inner.lv, opa as lv_opa_t) };
self
}
pub fn anim_duration(&mut self, ms: u32) -> &mut Self {
unsafe { lv_style_set_anim_duration(&mut self.inner.lv, ms) };
self
}
pub fn translate_y(&mut self, y: i32) -> &mut Self {
unsafe { lv_style_set_translate_y(&mut self.inner.lv, y) };
self
}
pub fn flex_flow(&mut self, flow: crate::layout::FlexFlow) -> &mut Self {
unsafe { lv_style_set_flex_flow(&mut self.inner.lv, flow as lv_flex_flow_t) };
self
}
pub fn flex_main_place(&mut self, align: crate::layout::FlexAlign) -> &mut Self {
unsafe { lv_style_set_flex_main_place(&mut self.inner.lv, align as lv_flex_align_t) };
self
}
pub fn bg_image_src(&mut self, src: &'static lv_image_dsc_t) -> &mut Self {
unsafe {
lv_style_set_bg_image_src(&mut self.inner.lv, src as *const lv_image_dsc_t as *const core::ffi::c_void)
};
self
}
pub fn bg_image_opa(&mut self, opa: u8) -> &mut Self {
unsafe { lv_style_set_bg_image_opa(&mut self.inner.lv, opa) };
self
}
pub fn bg_image_tiled(&mut self, tiled: bool) -> &mut Self {
unsafe { lv_style_set_bg_image_tiled(&mut self.inner.lv, tiled) };
self
}
pub fn image_recolor(&mut self, color: lv_color_t) -> &mut Self {
unsafe { lv_style_set_image_recolor(&mut self.inner.lv, color) };
self
}
pub fn image_recolor_opa(&mut self, opa: u8) -> &mut Self {
unsafe { lv_style_set_image_recolor_opa(&mut self.inner.lv, opa) };
self
}
pub fn transform_width(&mut self, w: i32) -> &mut Self {
unsafe { lv_style_set_transform_width(&mut self.inner.lv, w) };
self
}
pub fn transform_height(&mut self, h: i32) -> &mut Self {
unsafe { lv_style_set_transform_height(&mut self.inner.lv, h) };
self
}
pub fn transform_rotation(&mut self, angle: i32) -> &mut Self {
unsafe { lv_style_set_transform_rotation(&mut self.inner.lv, angle) };
self
}
pub fn transform_scale(&mut self, scale: i32) -> &mut Self {
unsafe {
lv_style_set_transform_scale_x(&mut self.inner.lv, scale);
lv_style_set_transform_scale_y(&mut self.inner.lv, scale);
}
self
}
pub fn transform_pivot_x(&mut self, x: i32) -> &mut Self {
unsafe { lv_style_set_transform_pivot_x(&mut self.inner.lv, x) };
self
}
pub fn transform_pivot_y(&mut self, y: i32) -> &mut Self {
unsafe { lv_style_set_transform_pivot_y(&mut self.inner.lv, y) };
self
}
pub fn layout(&mut self, layout: crate::layout::Layout) -> &mut Self {
unsafe { lv_style_set_layout(&mut self.inner.lv, layout as u16) };
self
}
}
#[derive(Clone, Debug)]
pub struct Style {
pub(crate) inner: Rc<StyleInner>,
}
impl Style {
pub(crate) fn lv_ptr(&self) -> *const lv_style_t {
Rc::as_ptr(&self.inner) as *const lv_style_t
}
}
pub struct ColorFilter {
pub(crate) inner: lv_color_filter_dsc_t,
}
impl core::fmt::Debug for ColorFilter {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ColorFilter").finish_non_exhaustive()
}
}
impl ColorFilter {
pub fn new(cb: unsafe extern "C" fn(*const lv_color_filter_dsc_t, lv_color_t, lv_opa_t) -> lv_color_t) -> Self {
let mut inner = unsafe { core::mem::zeroed::<lv_color_filter_dsc_t>() };
inner.filter_cb = Some(cb);
Self { inner }
}
}
pub unsafe extern "C" fn darken_filter_cb(
_dsc: *const lv_color_filter_dsc_t,
color: lv_color_t,
opa: lv_opa_t,
) -> lv_color_t {
unsafe { lv_color_darken(color, opa) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn text_decor_values() {
assert_eq!(TextDecor::NONE.0, 0);
assert_eq!(TextDecor::UNDERLINE.0, 1);
assert_eq!(TextDecor::STRIKETHROUGH.0, 2);
}
#[test]
fn text_decor_bitor() {
let combined = TextDecor::UNDERLINE | TextDecor::STRIKETHROUGH;
assert_eq!(combined.0, 0x03);
}
#[test]
fn border_side_values() {
assert_eq!(BorderSide::NONE.0, 0x00);
assert_eq!(BorderSide::BOTTOM.0, 0x01);
assert_eq!(BorderSide::TOP.0, 0x02);
assert_eq!(BorderSide::LEFT.0, 0x04);
assert_eq!(BorderSide::RIGHT.0, 0x08);
assert_eq!(BorderSide::FULL.0, 0x0F);
}
#[test]
fn border_side_bitor() {
let combined = BorderSide::TOP | BorderSide::BOTTOM;
assert_eq!(combined.0, 0x03);
}
#[test]
fn border_side_full_is_all_sides() {
let all = BorderSide::BOTTOM | BorderSide::TOP | BorderSide::LEFT | BorderSide::RIGHT;
assert_eq!(all, BorderSide::FULL);
}
#[test]
fn lv_pct_monotonic() {
assert!(lv_pct(50) > lv_pct(0));
assert!(lv_pct(100) > lv_pct(50));
}
#[test]
fn lv_pct_difference_matches_input() {
assert_eq!(lv_pct(100) - lv_pct(0), 100);
assert_eq!(lv_pct(50) - lv_pct(0), 50);
}
#[test]
fn size_content_uses_spec_type() {
let expected = (oxivgl_sys::LV_COORD_MAX | oxivgl_sys::LV_COORD_TYPE_SPEC) as i32;
assert_eq!(LV_SIZE_CONTENT, expected);
}
}