use base::basic_types::*;
use base::{Cursor, Window, WrappingMode};
use std::cmp::max;
use std::iter::Sum;
use std::marker::PhantomData;
use std::ops::{Add, AddAssign};
pub trait Widget {
fn space_demand(&self) -> Demand2D;
fn draw(&self, window: Window, hints: RenderingHints);
}
pub trait WidgetExt: Widget + Sized {
fn centered(self) -> Centered<Self> {
Centered(self)
}
fn with_window<F: Fn(Window, RenderingHints) -> Window>(self, f: F) -> WithWindow<Self, F> {
WithWindow(self, f)
}
fn with_hints<F: Fn(RenderingHints) -> RenderingHints>(self, f: F) -> WithHints<Self, F> {
WithHints(self, f)
}
fn with_demand<F: Fn(Demand2D) -> Demand2D>(self, f: F) -> WithDemand<Self, F> {
WithDemand(self, f)
}
}
impl<W: Widget + Sized> WidgetExt for W {}
pub struct Centered<W>(W);
impl<W: Widget> Widget for Centered<W> {
fn space_demand(&self) -> Demand2D {
self.0.space_demand()
}
fn draw(&self, mut window: Window, hints: RenderingHints) {
let demand = self.space_demand();
let window_height = window.get_height();
let window_width = window.get_width();
let max_height = demand.height.max.unwrap_or(window.get_height());
let max_width = demand.width.max.unwrap_or(window.get_width());
let start_row = ((window_height - max_height) / 2)
.from_origin()
.positive_or_zero();
let start_col = ((window_width - max_width) / 2)
.from_origin()
.positive_or_zero();
let end_row = (start_row + max_height).min(window_height.from_origin());
let end_col = (start_col + max_width).min(window_width.from_origin());
let window = window.create_subwindow(start_col..end_col, start_row..end_row);
self.0.draw(window, hints);
}
}
pub struct WithWindow<W, F>(W, F);
impl<W: Widget, F: Fn(Window, RenderingHints) -> Window> Widget for WithWindow<W, F> {
fn space_demand(&self) -> Demand2D {
self.0.space_demand()
}
fn draw(&self, window: Window, hints: RenderingHints) {
self.0.draw(self.1(window, hints), hints);
}
}
pub struct WithHints<W, F>(W, F);
impl<W: Widget, F: Fn(RenderingHints) -> RenderingHints> Widget for WithHints<W, F> {
fn space_demand(&self) -> Demand2D {
self.0.space_demand()
}
fn draw(&self, window: Window, hints: RenderingHints) {
self.0.draw(window, self.1(hints));
}
}
pub struct WithDemand<W, F>(W, F);
impl<W: Widget, F: Fn(Demand2D) -> Demand2D> Widget for WithDemand<W, F> {
fn space_demand(&self) -> Demand2D {
self.1(self.0.space_demand())
}
fn draw(&self, window: Window, hints: RenderingHints) {
self.0.draw(window, hints);
}
}
impl<S: std::convert::AsRef<str>> Widget for S {
fn space_demand(&self) -> Demand2D {
let mut width = 0;
let mut height = 0;
for line in self.as_ref().lines() {
width = width.max(crate::widget::count_grapheme_clusters(line));
height += 1;
}
Demand2D {
width: Demand::exact(width),
height: Demand::exact(height),
}
}
fn draw(&self, mut window: Window, _hints: RenderingHints) {
let mut cursor = Cursor::new(&mut window).wrapping_mode(WrappingMode::Wrap);
cursor.write(self.as_ref());
}
}
#[derive(Clone, Copy, Debug)]
pub struct RenderingHints {
pub active: bool,
pub blink: Blink,
#[doc(hidden)]
_do_not_construct: (),
}
impl Default for RenderingHints {
fn default() -> Self {
Self::new()
}
}
impl RenderingHints {
pub fn new() -> Self {
RenderingHints {
active: true,
blink: Blink::On,
_do_not_construct: (),
}
}
pub fn active(self, val: bool) -> Self {
RenderingHints {
active: val,
..self
}
}
pub fn blink(self, val: Blink) -> Self {
RenderingHints { blink: val, ..self }
}
}
#[derive(Clone, Copy, Debug)]
#[allow(missing_docs)]
pub enum Blink {
On,
Off,
}
impl Blink {
pub fn toggled(self) -> Self {
match self {
Blink::On => Blink::Off,
Blink::Off => Blink::On,
}
}
pub fn toggle(&mut self) {
*self = self.toggled();
}
}
#[derive(Eq, PartialEq, PartialOrd, Clone, Copy, Debug)]
#[allow(missing_docs)]
pub struct Demand<T: AxisDimension> {
pub min: PositiveAxisDiff<T>,
pub max: Option<PositiveAxisDiff<T>>,
_dim: PhantomData<T>,
}
impl<T: AxisDimension> Add<Demand<T>> for Demand<T> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Demand {
min: self.min + rhs.min,
max: if let (Some(l), Some(r)) = (self.max, rhs.max) {
Some(l + r)
} else {
None
},
_dim: Default::default(),
}
}
}
impl<T: AxisDimension> AddAssign for Demand<T> {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs
}
}
impl<T: AxisDimension + PartialOrd + Ord> Sum for Demand<T> {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = Self>,
{
iter.fold(Demand::exact(0), Demand::add)
}
}
impl<'a, T: AxisDimension + PartialOrd + Ord> Sum<&'a Demand<T>> for Demand<T> {
fn sum<I>(iter: I) -> Demand<T>
where
I: Iterator<Item = &'a Demand<T>>,
{
iter.fold(Demand::zero(), |d1: Demand<T>, d2: &Demand<T>| d1 + *d2)
}
}
impl<T: AxisDimension + PartialOrd + Ord> Demand<T> {
pub fn zero() -> Self {
Self::exact(0)
}
pub fn exact<I: Into<PositiveAxisDiff<T>> + Copy>(size: I) -> Self {
Demand {
min: size.into(),
max: Some(size.into()),
_dim: Default::default(),
}
}
pub fn at_least<I: Into<PositiveAxisDiff<T>> + Copy>(size: I) -> Self {
Demand {
min: size.into(),
max: None,
_dim: Default::default(),
}
}
pub fn from_to<I: Into<PositiveAxisDiff<T>> + Copy>(min: I, max: I) -> Self {
assert!(min.into() <= max.into(), "Invalid min/max");
Demand {
min: min.into(),
max: Some(max.into()),
_dim: Default::default(),
}
}
pub fn max(&self, other: Self) -> Self {
Demand {
min: max(self.min, other.min),
max: if let (Some(l), Some(r)) = (self.max, other.max) {
Some(max(l, r))
} else {
None
},
_dim: Default::default(),
}
}
pub fn max_assign(&mut self, other: Self) {
*self = self.max(other);
}
}
pub type ColDemand = Demand<ColDimension>;
pub type RowDemand = Demand<RowDimension>;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub struct Demand2D {
pub width: ColDemand,
pub height: RowDemand,
}
impl Demand2D {
pub fn add_vertical(self, other: Self) -> Self {
Demand2D {
width: self.width.max(other.width),
height: self.height + other.height,
}
}
pub fn add_horizontal(self, other: Self) -> Self {
Demand2D {
width: self.width + other.width,
height: self.height.max(other.height),
}
}
}