gooey-rs 0.12.1

Tile-based UI library with audio support
Documentation
use std::num::NonZeroU32;
use derive_more::{From, TryInto};
use crate::math::{num, Normalized};
use crate::interface::controller::{size, Alignment, Area, Offset, Orientation, Size};
use super::impl_kind;

#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Layout {
  /// Orientation controls how *child* elements are *tiled* (if any). The area
  /// specifier determines whether child elements are arranged inside the border
  /// or extending to the edge.
  pub orientation : (Orientation, Area),
  pub variant     : Variant
}

impl_kind!(Layout);

#[derive(Clone, Debug, Eq, PartialEq, From, TryInto)]
pub enum Variant {
  /// Size and position controlled by parent, given the *weight* for this frame
  /// in relation to the others.
  ///
  /// ⚠ *Note*: this does not refer to *tile coordinates*, but to "tiling"
  /// of frames, as in a "tiling window manager"
  Tiled (Tiled),
  /// Size and position, either relative or absolute, relative to an anchor
  /// point. The area specifier determines whether the anchor point is inside
  /// the border or on the exterior edge of the parent.
  Free  (Free, Area)
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Tiled {
  Weighted (NonZeroU32),
  Absolute (NonZeroU32)
}

/// Note that offset coordinates are relative to the anchor point, with positive
/// pointing *into* the parent for edges, up for vertically centered, and right
/// for horizontally centered.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Free {
  pub anchor : Alignment,
  pub offset : Offset,
  pub size   : Size
}

impl From <Variant> for Layout {
  /// From variant with default orientation
  fn from (variant : Variant) -> Self {
    Layout {
      orientation: Default::default(),
      variant
    }
  }
}

impl From <Free> for Variant {
  /// From free layout with default area specifier
  fn from (free : Free) -> Self {
    Variant::Free (free, Area::default())
  }
}

impl Default for Variant {
  fn default() -> Self {
    (Free::default(), Area::default()).into()
  }
}

impl Free {
  /// Free layout with upper-left anchor and absolute offset and absolute size
  /// of 24 width by 12 height
  #[inline]
  pub fn default_tile() -> Self {
    Free {
      anchor: Alignment::tile(),
      offset: Offset::default_absolute(),
      size:   Size {
        width:  24.into(),
        height: 12.into()
      }
    }
  }
  /// Free layout with lower-left anchor and absolute offset and absolute size
  /// of 640 width by 480 height
  #[inline]
  pub fn default_pixel() -> Self {
    Free {
      anchor: Alignment::pixel(),
      offset: Offset::default_absolute(),
      size:   Size {
        width:  640.into(),
        height: 480.into()
      }
    }
  }

  /// Free layout with upper-left anchor and absolute offset and given size
  pub fn tile (size : Size) -> Self {
    Free {
      anchor: Alignment::tile(),
      offset: Offset::default_absolute(),
      size
    }
  }

  /// Free layout with lower-left anchor and absolute offset and given size
  pub fn pixel (size : Size) -> Self {
    Free {
      anchor: Alignment::pixel(),
      offset: Offset::default_absolute(),
      size
    }
  }

  /// Centered frame with given size and no offset
  pub fn middle_center (size : Size) -> Self {
    Free {
      anchor: Alignment::middle_center(),
      offset: Offset::default_absolute(),
      size
    }
  }

  /// Free layout with middle+centered anchor, 100% relative width and height,
  /// and default absolute offset
  #[inline]
  pub fn middle_center_max() -> Self {
    use num::One;
    Free {
      anchor: Alignment::middle_center(),
      size:   Size {
        width:  Normalized::<f32>::one().into(),
        height: Normalized::<f32>::one().into()
      },
      offset: Offset::default_absolute()
    }
  }

  #[inline]
  pub fn offset_move_right (&mut self) {
    self.offset = self.offset.move_right (self.anchor, 1);
  }
  #[inline]
  pub fn offset_move_left (&mut self) {
    self.offset = self.offset.move_left (self.anchor, 1);
  }
  #[inline]
  pub fn offset_move_up (&mut self) {
    self.offset = self.offset.move_up (self.anchor, 1);
  }
  #[inline]
  pub fn offset_move_down (&mut self) {
    self.offset = self.offset.move_down (self.anchor, 1);
  }
  /// Grows absolute size by one unit or relative size by 1/60th.
  #[inline]
  pub fn size_grow_width (&mut self) {
    match self.size.width {
      size::Unsigned::Absolute (ref mut width) =>
        *width = std::cmp::max (1, *width as i32 + 1) as u32,
      size::Unsigned::Relative (ref mut width) =>
        *width = Normalized::clamp (**width + 1.0/60.0)
    }
  }
  /// Grows absolute size by one unit or relative size by 1/60th.
  #[inline]
  pub fn size_grow_height (&mut self) {
    match self.size.height {
      size::Unsigned::Absolute (ref mut height) =>
        *height = std::cmp::max (1, *height as i32 + 1) as u32,
      size::Unsigned::Relative (ref mut height) =>
        *height = Normalized::clamp (**height + 1.0/60.0)
    }
  }
  /// Shrinks absolute size by one unit or relative size by 1/60th.
  ///
  /// Enforces a minimum absolute size of 1 unit and minimum relative size of
  /// 1/60th.
  #[inline]
  pub fn size_shrink_width (&mut self) {
    match self.size.width {
      size::Unsigned::Absolute (ref mut width) =>
        *width = std::cmp::max (1, *width as i32 - 1) as u32,
      size::Unsigned::Relative (ref mut width) =>
        *width = Normalized::clamp ((**width - 1.0/60.0).max (1.0/60.0))
    }
  }
  /// Shrinks absolute size by one unit or relative size by 1/60th.
  ///
  /// Enforces a minimum absolute size of 1 unit and minimum relative size of 1/60th.
  #[inline]
  pub fn size_shrink_height (&mut self) {
    match self.size.height {
      size::Unsigned::Absolute (ref mut height) =>
        *height = std::cmp::max (1, *height as i32 - 1) as u32,
      size::Unsigned::Relative (ref mut height) =>
        *height = Normalized::clamp ((**height - 1.0/60.0).max (1.0/60.0))
    }
  }
}

impl Default for Free {
  /// Default tile coordinates
  fn default() -> Self {
    Self::default_tile()
  }
}

impl Tiled {
  /// Clamps to the range `[1, u32::MAX]`
  pub fn modify_size (&mut self, modify : i32) {
    let size = match self {
      Tiled::Weighted (n) | Tiled::Absolute (n) => n
    };
    let s = size.get() as i64 + modify as i64;
    let new_size = s.max (1).min (u32::MAX as i64) as u32;
    *size = NonZeroU32::new (new_size).unwrap();
  }
}

impl Default for Tiled {
  fn default() -> Self {
    Tiled::Weighted (NonZeroU32::new (1).unwrap())
  }
}