use std::ops::{Deref, DerefMut};
use crate::common::*;
use crate::layout::*;
use crate::renderer::*;
#[derive(Clone)]
struct Group {
rect: Rect,
layout: Layout,
cursor: Vector,
line_cap: LineCap,
}
#[allow(unused)]
use crate::build;
pub struct Ui<T: Renderer> {
stack: Vec<Group>,
renderer: T,
}
impl<T: Renderer> Ui<T> {
pub fn new(renderer: T) -> Self {
Self {
stack: Vec::new(),
renderer,
}
}
pub fn renderer(&self) -> &T {
&self.renderer
}
pub fn render(&mut self) -> &mut T {
&mut self.renderer
}
pub fn position(&self) -> Point {
self.top().rect.position
}
pub fn set_position(&mut self, new_position: Point) {
self.top_mut().rect.position = new_position;
}
pub fn size(&self) -> Vector {
self.top().rect.size
}
pub fn width(&self) -> f32 {
self.top().rect.width()
}
pub fn height(&self) -> f32 {
self.top().rect.height()
}
pub fn rect(&self) -> Rect {
self.top().rect
}
pub fn root_rect(&self) -> Rect {
self.stack.first().expect("no root group").rect
}
pub fn remaining_size(&self) -> Vector {
let top = self.top();
match top.layout {
Layout::Freeform => vector(0.0, 0.0),
Layout::Horizontal | Layout::Vertical => top.rect.size - top.cursor,
Layout::HorizontalRev | Layout::VerticalRev => top.rect.size + top.cursor,
}
}
pub fn remaining_width(&self) -> f32 {
let top = self.top();
match top.layout {
Layout::Freeform => 0.0,
Layout::Horizontal => top.rect.width() - top.cursor.x,
Layout::Vertical => top.rect.width(),
Layout::HorizontalRev => top.rect.width() + top.cursor.x,
Layout::VerticalRev => top.rect.width(),
}
}
pub fn remaining_height(&self) -> f32 {
let top = self.top();
match top.layout {
Layout::Freeform => 0.0,
Layout::Horizontal => top.rect.height(),
Layout::Vertical => top.rect.height() - top.cursor.y,
Layout::HorizontalRev => top.rect.height(),
Layout::VerticalRev => top.rect.height() + top.cursor.y,
}
}
pub fn root(&mut self, size: impl Into<Vector>, layout: Layout) {
self.stack.clear();
self.stack.push(Group {
rect: Rect::new(point(0.0, 0.0), size),
layout,
cursor: vector(0.0, 0.0),
line_cap: LineCap::Butt,
});
}
pub fn push(&mut self, size: impl Into<Vector>, layout: Layout) {
let size = size.into();
let top = self.top().clone();
let position = match top.layout {
Layout::Freeform | Layout::Horizontal | Layout::Vertical => {
top.rect.position + top.cursor
}
Layout::HorizontalRev => top.rect.top_right() + top.cursor - point(size.x, 0.0),
Layout::VerticalRev => top.rect.bottom_left() + top.cursor - point(0.0, size.y),
};
self.stack.push(Group {
rect: Rect::new(position, size),
layout,
cursor: point(0.0, 0.0),
..top
});
}
pub fn pop(&mut self) {
let group = self
.stack
.pop()
.expect("the root group got popped of the stack");
let top = self.top_mut();
match top.layout {
Layout::Freeform => (),
Layout::Horizontal => top.cursor.x += group.rect.width(),
Layout::Vertical => top.cursor.y += group.rect.height(),
Layout::HorizontalRev => top.cursor.x -= group.rect.width(),
Layout::VerticalRev => top.cursor.y -= group.rect.height(),
}
}
pub fn cursor(&self) -> Vector {
self.top().cursor
}
pub fn set_cursor(&mut self, new_cursor: Vector) {
self.top_mut().cursor = new_cursor;
}
pub fn offset(&mut self, by: Vector) {
self.top_mut().cursor += by;
}
pub fn pad(&mut self, padding: impl Into<Padding>) {
let padding = padding.into();
let rect = &mut self.top_mut().rect;
rect.position.x += padding.left;
rect.position.y += padding.top;
rect.size.x -= padding.left + padding.right;
rect.size.y -= padding.top + padding.bottom;
}
pub fn align(&mut self, alignment: Alignment) {
let parent = self
.stack
.get(self.stack.len() - 2)
.expect("no parent group on the stack to align to")
.rect;
let subject = &mut self
.stack
.last_mut()
.expect("no group on the stack to align")
.rect;
subject.position.x = match alignment.0 {
Left => parent.left(),
Center => parent.center_x() - subject.width() / 2.0,
Right => parent.right() - subject.width(),
};
subject.position.y = match alignment.1 {
Top => parent.top(),
Middle => parent.center_y() - subject.height() / 2.0,
Bottom => parent.bottom() - subject.height(),
};
}
pub fn space(&mut self, amount: f32) {
let top = self.top_mut();
match top.layout {
Layout::Freeform => panic!("using space() on Freeform layout is forbidden"),
Layout::Horizontal => top.cursor.x += amount,
Layout::Vertical => top.cursor.y += amount,
Layout::HorizontalRev => top.cursor.x -= amount,
Layout::VerticalRev => top.cursor.y -= amount,
}
}
pub fn fit(&mut self) {
let top = self.top_mut();
match top.layout {
Layout::Freeform => top.rect.size = top.cursor,
Layout::Horizontal => top.rect.size.x = top.cursor.x,
Layout::Vertical => top.rect.size.y = top.cursor.y,
Layout::HorizontalRev | Layout::VerticalRev => {
panic!("reverse layout containers can't be fit()ted")
}
}
}
fn top(&self) -> &Group {
self.stack
.last()
.expect("no groups on the stack to read from. check your push() and pop()s")
}
fn top_mut(&mut self) -> &mut Group {
self.stack
.last_mut()
.expect("no groups on the stack left to modify. check your push() and pop()s")
}
}
impl<T: Renderer> Ui<T> {
pub fn draw<F>(&mut self, do_draw: F)
where
F: FnOnce(&mut Self),
{
let translation = self.top().rect.position;
self.render().push();
self.render().translate(translation);
do_draw(self);
self.render().pop();
}
pub fn clip(&mut self) {
let rect = self.top().rect;
self.render().clip(rect);
}
pub fn fill(&mut self, color: impl Into<Color>) {
self.fill_rounded(color, 0.0);
}
pub fn fill_rounded(&mut self, color: impl Into<Color>, radius: f32) {
let rect = self.top().rect;
self.render().fill(rect, color.into(), radius);
}
pub fn outline(&mut self, color: impl Into<Color>, thickness: f32) {
self.outline_rounded(color, 0.0, thickness);
}
pub fn outline_rounded(&mut self, color: impl Into<Color>, radius: f32, thickness: f32) {
let rect = self.top().rect;
self.render().outline(rect, color.into(), radius, thickness);
}
pub fn line_cap(&self) -> LineCap {
self.top().line_cap
}
pub fn set_line_cap(&mut self, new_line_cap: LineCap) {
self.top_mut().line_cap = new_line_cap;
}
fn border(&mut self, a: Point, b: Point, color: Color, thickness: f32) {
let line_cap = self.top().line_cap;
self.render().line(a, b, color, line_cap, thickness);
}
pub fn border_left(&mut self, color: impl Into<Color>, thickness: f32) {
let rect = self.top().rect;
self.border(rect.top_left(), rect.bottom_left(), color.into(), thickness);
}
pub fn border_top(&mut self, color: impl Into<Color>, thickness: f32) {
let rect = self.top().rect;
self.border(rect.top_left(), rect.top_right(), color.into(), thickness);
}
pub fn border_right(&mut self, color: impl Into<Color>, thickness: f32) {
let rect = self.top().rect;
self.border(
rect.top_right(),
rect.bottom_right(),
color.into(),
thickness,
);
}
pub fn border_bottom(&mut self, color: impl Into<Color>, thickness: f32) {
let rect = self.top().rect;
self.border(
rect.bottom_left(),
rect.bottom_right(),
color.into(),
thickness,
);
}
pub fn text(
&mut self,
font: &T::Font,
text: &str,
color: impl Into<Color>,
alignment: Alignment,
) {
let rect = self.top().rect;
self.render()
.text(rect, font, text, color.into(), alignment);
}
}
impl<T: Renderer> Deref for Ui<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.renderer
}
}
impl<T: Renderer> DerefMut for Ui<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.renderer
}
}