ribir_core 0.4.0-alpha.55

A non-intrusive declarative GUI framework, to build modern native/wasm cross-platform applications.
Documentation
use ribir_geom::{Point, Rect, Size};

use super::{LayoutCtx, WidgetCtxImpl};
use crate::{
  prelude::ProviderCtx,
  widget::{VisualBox, WidgetTree},
  widget_tree::WidgetId,
};
pub struct VisualCtx<'a> {
  id: WidgetId,
  tree: &'a mut WidgetTree,
  provider_ctx: &'a mut ProviderCtx,
  clip_area: Option<Rect>,
}

impl<'a> WidgetCtxImpl for VisualCtx<'a> {
  #[inline]
  fn id(&self) -> WidgetId { self.id }

  #[inline]
  fn tree(&self) -> &WidgetTree { self.tree }
}

impl<'a> VisualCtx<'a> {
  pub(crate) fn from_layout_ctx<'c: 'a, 'b: 'a>(ctx: &'c mut LayoutCtx<'b>) -> Self {
    let id = ctx.id();
    let LayoutCtx { provider_ctx, tree, .. } = ctx;
    Self { id, tree: *tree, provider_ctx, clip_area: None }
  }

  pub fn update_visual_box(&mut self) -> VisualBox {
    let id = self.id();

    let tree = unsafe { &*(self.tree as *mut WidgetTree) };
    let w = id.assert_get(tree);
    let rect = w.visual_box(self);

    let mut subtree = self.descendants_bounds();
    if let Some(clip) = &self.clip_area {
      subtree = subtree.and_then(|rect| clip.intersection(&rect));
    }
    if let Some(transform) = w.get_transform() {
      subtree = subtree.map(|rect| transform.outer_transformed_rect(&rect));
    }

    let visual_box = VisualBox { rect, subtree };

    let info = self.tree.store.layout_info_or_default(id);
    info.visual_box = visual_box;
    info.visual_box
  }

  pub fn descendants_bounds(&self) -> Option<Rect> {
    let id = self.id;
    let children = id.children(self.tree);

    let mut rect = None;
    for child in children {
      if let Some(mut child_view) = self.visual_rect(child) {
        let pos = self
          .position(child)
          .expect("child position should be set");

        child_view.origin += pos.to_vector();
        if rect.is_none() {
          rect = Some(child_view);
        } else {
          rect = rect.map(|rect| rect.union(&child_view));
        }
      }
    }
    rect
  }

  pub fn clip(&mut self, rect: Rect) {
    self.clip_area = self
      .clip_area
      .and_then(|r| {
        r.intersection(&rect)
          .or(Some(Rect::from_size(Size::zero())))
      })
      .or(Some(rect));
  }

  fn visual_rect(&self, id: WidgetId) -> Option<Rect> {
    self
      .tree
      .store
      .layout_info(id)
      .and_then(|info| info.visual_box.bounds_rect())
  }

  fn position(&self, id: WidgetId) -> Option<Point> {
    self
      .tree
      .store
      .layout_info(id)
      .map(|info| info.pos)
  }
}

impl<'w> AsRef<ProviderCtx> for VisualCtx<'w> {
  fn as_ref(&self) -> &ProviderCtx { self.provider_ctx }
}

impl<'w> AsMut<ProviderCtx> for VisualCtx<'w> {
  fn as_mut(&mut self) -> &mut ProviderCtx { self.provider_ctx }
}