ribir_core 0.0.1-alpha.5

Ribir is a framework for building modern native/wasm cross-platform user interface applications.
Documentation
use crate::prelude::*;
use std::{ops::Deref, rc::Rc};

pub struct BuildCtx<'a> {
  themes: &'a mut Vec<Rc<Theme>>,
  wnd_ctx: &'a mut WindowCtx,
}

impl<'a> BuildCtx<'a> {
  #[inline]
  pub fn wnd_ctx(&self) -> &WindowCtx { self.wnd_ctx }

  #[inline]
  pub(crate) fn new(themes: &'a mut Vec<Rc<Theme>>, wnd_ctx: &'a mut WindowCtx) -> Self {
    Self { themes, wnd_ctx }
  }

  pub(crate) fn find_cfg<T>(&self, f: impl Fn(&Theme) -> Option<&T>) -> Option<&T> {
    for t in self.themes.iter().rev() {
      let v = f(t);
      if v.is_some() {
        return v;
      } else if matches!(t.deref(), Theme::Full(_)) {
        return None;
      }
    }
    f(self.app_theme())
  }

  #[inline]
  pub fn app_ctx(&self) -> &AppContext { &self.wnd_ctx.app_ctx }

  #[inline]
  // todo: should &mut self here, but we need to remove `init ctx =>` first
  pub(crate) fn push_theme(&self, theme: Rc<Theme>) {
    #[allow(clippy::cast_ref_to_mut)]
    let this = unsafe { &mut *(self as *const Self as *mut Self) };
    this.themes.push(theme);
  }

  #[inline]
  pub(crate) fn app_theme(&self) -> &Theme { self.app_ctx().app_theme() }

  #[inline]
  pub(crate) fn app_theme_mut(&self) -> &mut Theme { self.wnd_ctx.app_ctx.app_theme_mut() }
}

#[cfg(test)]
mod tests {
  use super::*;
  use crate::test_helper::*;
  use std::{cell::RefCell, rc::Rc};

  #[test]
  fn themes() {
    #[derive(Default, Clone)]
    struct LightDarkThemes(Rc<RefCell<Vec<Theme>>>);

    let themes: Stateful<Vec<Rc<Theme>>> = Stateful::new(vec![]);
    let light_palette = Palette {
      brightness: Brightness::Light,
      ..Default::default()
    };
    let dark_palette = Palette {
      brightness: Brightness::Dark,
      ..Default::default()
    };
    let light_dark = widget! {
      states { themes: themes.clone() }
      ThemeWidget {
        theme: Rc::new(Theme::Inherit(InheritTheme {
          palette: Some(Rc::new(light_palette)),
          ..<_>::default()

        })),
        MockBox {
          size: INFINITY_SIZE,
          ThemeWidget {
            theme: Rc::new(Theme::Inherit(InheritTheme {
              palette: Some(Rc::new(dark_palette)),
              ..<_>::default()
            })),
            MockBox {
              size: ZERO_SIZE,
              DynWidget {
                dyns: move |ctx: &BuildCtx| {
                  *themes = ctx.themes.clone();
                  Void
                }
              }
            }
          }
        }
      }
    };

    let mut wnd = TestWindow::new(light_dark);
    wnd.layout();
    let themes = themes.state_ref();
    assert_eq!(themes.len(), 2);
    let mut iter = themes.iter().filter_map(|t| match t.deref() {
      Theme::Full(t) => Some(t.palette.brightness),
      Theme::Inherit(i) => i.palette.as_ref().map(|palette| palette.brightness),
    });

    assert_eq!(iter.next(), Some(Brightness::Light));
    assert_eq!(iter.next(), Some(Brightness::Dark));
  }
}