Finally got flex working
parent
291a92d740
commit
1f3b1dde50
|
@ -0,0 +1 @@
|
|||
pub enum Event {}
|
13
src/lib.rs
13
src/lib.rs
|
@ -11,6 +11,7 @@ use std::{
|
|||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
mod box_constraints;
|
||||
mod event;
|
||||
mod point;
|
||||
mod rect;
|
||||
mod size;
|
||||
|
@ -18,6 +19,7 @@ mod theme;
|
|||
mod vec2;
|
||||
mod widget;
|
||||
pub use box_constraints::*;
|
||||
pub use event::Event;
|
||||
pub use point::*;
|
||||
pub use rect::*;
|
||||
pub use size::*;
|
||||
|
@ -71,6 +73,7 @@ impl<T> DerefMut for DataWrapper<T> {
|
|||
pub struct Window<T: Data> {
|
||||
out: Stdout,
|
||||
buf: Vec<char>,
|
||||
size: Size,
|
||||
data: DataWrapper<T>,
|
||||
root_widget: Box<dyn Widget<T>>,
|
||||
}
|
||||
|
@ -83,23 +86,24 @@ impl<'a, T: Data> Window<T> {
|
|||
Self {
|
||||
out: stdout(),
|
||||
buf: vec![],
|
||||
size: Size::ZERO,
|
||||
data: DataWrapper::new(data),
|
||||
root_widget: Box::new(root),
|
||||
}
|
||||
}
|
||||
fn draw(&mut self) -> CTRes<()> {
|
||||
self.root_widget.event(&mut self.data);
|
||||
// self.root_widget.event(&mut self.data, );
|
||||
self.root_widget.update(&self.data);
|
||||
if self.data.changed() {
|
||||
let terminal_size = size()?;
|
||||
if self.data.changed() || self.size != terminal_size.into() {
|
||||
queue![self.out, Clear(ClearType::All)]?;
|
||||
self.out.flush()?;
|
||||
let terminal_size = size()?;
|
||||
self.buf = vec![' '; terminal_size.0 as usize * terminal_size.1 as usize];
|
||||
self.root_widget.deref_mut().layout(&terminal_size.into());
|
||||
self
|
||||
.root_widget
|
||||
.deref_mut()
|
||||
.paint(&mut self.buf, &terminal_size.into());
|
||||
.paint(&mut self.buf, Point::ZERO, &terminal_size.into());
|
||||
for ch in &self.buf {
|
||||
print!["{}", ch];
|
||||
}
|
||||
|
@ -111,6 +115,5 @@ impl<'a, T: Data> Window<T> {
|
|||
loop {
|
||||
self.draw()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
39
src/main.rs
39
src/main.rs
|
@ -1,6 +1,3 @@
|
|||
use std::ops::Deref;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use tuid::Data;
|
||||
use tuid::*;
|
||||
|
||||
|
@ -11,23 +8,35 @@ struct ExampleAppState {
|
|||
|
||||
impl Data for ExampleAppState {}
|
||||
|
||||
fn make_ui() -> impl Widget<Arc<Mutex<ExampleAppState>>> {
|
||||
let t1 = Text::new(Box::new(
|
||||
|data: &DataWrapper<Arc<Mutex<ExampleAppState>>>| {
|
||||
let s = format!["Hello, {}!", data.deref().lock().unwrap().value];
|
||||
s
|
||||
},
|
||||
));
|
||||
fn make_ui() -> impl Widget<ExampleAppState> {
|
||||
let t1 = Text::new(Box::new(|_: &DataWrapper<ExampleAppState>| {
|
||||
"Hello, Text2!".to_string()
|
||||
}));
|
||||
let t2 = Text::new(Box::new(|_: &DataWrapper<ExampleAppState>| {
|
||||
"And hello to you, Text1!".to_string()
|
||||
}));
|
||||
let t3 = Text::new(Box::new(|_: &DataWrapper<ExampleAppState>| {
|
||||
"Whoah- are we all saying hello in here? Hello! :D".to_string()
|
||||
}));
|
||||
let t4 = Text::new(Box::new(|_: &DataWrapper<ExampleAppState>| {
|
||||
"Hey guys! I saw you were saying hello and wanted to see what's up! xD".to_string()
|
||||
}));
|
||||
|
||||
// let flex = Flex::new();
|
||||
let flex0 = Flex::column()
|
||||
.with_child(t2)
|
||||
.with_flex_spacer(1.0)
|
||||
.with_child(t3);
|
||||
|
||||
// flex
|
||||
|
||||
t1
|
||||
Flex::row()
|
||||
.with_child(t1)
|
||||
.with_flex_spacer(1.0)
|
||||
.with_child(Flex::column().with_child(t4).with_flex_spacer(1.0))
|
||||
.with_flex_spacer(1.0)
|
||||
.with_child(flex0)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let main_state = Arc::new(Mutex::new(ExampleAppState::default()));
|
||||
let main_state = ExampleAppState::default();
|
||||
let mut window = Window::new(main_state, make_ui());
|
||||
window.run().unwrap();
|
||||
}
|
||||
|
|
124
src/point.rs
124
src/point.rs
|
@ -1,5 +1,125 @@
|
|||
use std::ops::{Add, AddAssign, Sub, SubAssign};
|
||||
|
||||
use crate::Vec2;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Point {
|
||||
pub x: usize,
|
||||
pub y: usize,
|
||||
pub x: usize,
|
||||
pub y: usize,
|
||||
}
|
||||
|
||||
impl Point {
|
||||
/// The point (0, 0).
|
||||
pub const ZERO: Point = Point::new(0, 0);
|
||||
|
||||
/// The point at the origin; (0, 0).
|
||||
pub const ORIGIN: Point = Point::new(0, 0);
|
||||
|
||||
/// Create a new `Point` with the provided `x` and `y` coordinates.
|
||||
#[inline]
|
||||
pub const fn new(x: usize, y: usize) -> Self {
|
||||
Point { x, y }
|
||||
}
|
||||
|
||||
/// Convert this point into a `Vec2`.
|
||||
#[inline]
|
||||
pub const fn to_vec2(self) -> Vec2 {
|
||||
Vec2::new(self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(usize, usize)> for Point {
|
||||
#[inline]
|
||||
fn from(v: (usize, usize)) -> Point {
|
||||
Point { x: v.0, y: v.1 }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Point> for (usize, usize) {
|
||||
#[inline]
|
||||
fn from(v: Point) -> (usize, usize) {
|
||||
(v.x, v.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Vec2> for Point {
|
||||
type Output = Point;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Vec2) -> Self {
|
||||
Point::new(self.x + other.x, self.y + other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<Vec2> for Point {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Vec2) {
|
||||
*self = Point::new(self.x + other.x, self.y + other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<Vec2> for Point {
|
||||
type Output = Point;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Vec2) -> Self {
|
||||
Point::new(self.x - other.x, self.y - other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<Vec2> for Point {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Vec2) {
|
||||
*self = Point::new(self.x - other.x, self.y - other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<(usize, usize)> for Point {
|
||||
type Output = Point;
|
||||
|
||||
#[inline]
|
||||
fn add(self, (x, y): (usize, usize)) -> Self {
|
||||
Point::new(self.x + x, self.y + y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Point {
|
||||
type Output = Point;
|
||||
|
||||
#[inline]
|
||||
fn add(self, Self { x, y }: Self) -> Self {
|
||||
Self::new(self.x + x, self.y + y)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<(usize, usize)> for Point {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, (x, y): (usize, usize)) {
|
||||
*self = Point::new(self.x + x, self.y + y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<(usize, usize)> for Point {
|
||||
type Output = Point;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, (x, y): (usize, usize)) -> Self {
|
||||
Point::new(self.x - x, self.y - y)
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<(usize, usize)> for Point {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, (x, y): (usize, usize)) {
|
||||
*self = Point::new(self.x - x, self.y - y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<Point> for Point {
|
||||
type Output = Vec2;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Point) -> Vec2 {
|
||||
Vec2::new(self.x - other.x, self.y - other.y)
|
||||
}
|
||||
}
|
||||
|
|
280
src/rect.rs
280
src/rect.rs
|
@ -1,7 +1,279 @@
|
|||
use std::ops::Sub;
|
||||
|
||||
use crate::{Point, Size};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Rect {
|
||||
pub x0: usize,
|
||||
pub y0: usize,
|
||||
pub x1: usize,
|
||||
pub y1: usize,
|
||||
pub x0: usize,
|
||||
pub y0: usize,
|
||||
pub x1: usize,
|
||||
pub y1: usize,
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
/// The empty rectangle at the origin.
|
||||
pub const ZERO: Rect = Rect::new(0, 0, 0, 0);
|
||||
|
||||
/// A new rectangle from minimum and maximum coordinates.
|
||||
#[inline]
|
||||
pub const fn new(x0: usize, y0: usize, x1: usize, y1: usize) -> Rect {
|
||||
Rect { x0, y0, x1, y1 }
|
||||
}
|
||||
|
||||
/// A new rectangle from two points.
|
||||
///
|
||||
/// The result will have non-negative width and height.
|
||||
#[inline]
|
||||
pub fn from_points(p0: impl Into<Point>, p1: impl Into<Point>) -> Rect {
|
||||
let p0 = p0.into();
|
||||
let p1 = p1.into();
|
||||
Rect::new(p0.x, p0.y, p1.x, p1.y)
|
||||
}
|
||||
|
||||
/// A new rectangle from origin and size.
|
||||
///
|
||||
/// The result will have non-negative width and height.
|
||||
#[inline]
|
||||
pub fn from_origin_size(origin: impl Into<Point>, size: impl Into<Size>) -> Rect {
|
||||
let origin = origin.into();
|
||||
Rect::from_points(origin, origin + size.into().to_vec2())
|
||||
}
|
||||
|
||||
/// A new rectangle from center and size.
|
||||
#[inline]
|
||||
pub fn from_center_size(center: impl Into<Point>, size: impl Into<Size>) -> Rect {
|
||||
let center = center.into();
|
||||
let size = size.into() / 2;
|
||||
Rect::new(
|
||||
center.x - size.width,
|
||||
center.y - size.height,
|
||||
center.x + size.width,
|
||||
center.y + size.height,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new `Rect` with the same size as `self` and a new origin.
|
||||
#[inline]
|
||||
pub fn with_origin(self, origin: impl Into<Point>) -> Rect {
|
||||
Rect::from_origin_size(origin, self.size())
|
||||
}
|
||||
|
||||
/// Create a new `Rect` with the same origin as `self` and a new size.
|
||||
#[inline]
|
||||
pub fn with_size(self, size: impl Into<Size>) -> Rect {
|
||||
Rect::from_origin_size(self.origin(), size)
|
||||
}
|
||||
|
||||
/// The width of the rectangle.
|
||||
///
|
||||
/// Note: nothing forbids negative width.
|
||||
#[inline]
|
||||
pub fn width(&self) -> usize {
|
||||
self.x1 - self.x0
|
||||
}
|
||||
|
||||
/// The height of the rectangle.
|
||||
///
|
||||
/// Note: nothing forbids negative height.
|
||||
#[inline]
|
||||
pub fn height(&self) -> usize {
|
||||
self.y1 - self.y0
|
||||
}
|
||||
|
||||
/// Returns the minimum value for the x-coordinate of the rectangle.
|
||||
#[inline]
|
||||
pub fn min_x(&self) -> usize {
|
||||
self.x0.min(self.x1)
|
||||
}
|
||||
|
||||
/// Returns the maximum value for the x-coordinate of the rectangle.
|
||||
#[inline]
|
||||
pub fn max_x(&self) -> usize {
|
||||
self.x0.max(self.x1)
|
||||
}
|
||||
|
||||
/// Returns the minimum value for the y-coordinate of the rectangle.
|
||||
#[inline]
|
||||
pub fn min_y(&self) -> usize {
|
||||
self.y0.min(self.y1)
|
||||
}
|
||||
|
||||
/// Returns the maximum value for the y-coordinate of the rectangle.
|
||||
#[inline]
|
||||
pub fn max_y(&self) -> usize {
|
||||
self.y0.max(self.y1)
|
||||
}
|
||||
|
||||
/// The origin of the rectangle.
|
||||
///
|
||||
/// This is the top left corner in a y-down space and with
|
||||
/// non-negative width and height.
|
||||
#[inline]
|
||||
pub fn origin(&self) -> Point {
|
||||
Point::new(self.x0, self.y0)
|
||||
}
|
||||
|
||||
/// The size of the rectangle.
|
||||
#[inline]
|
||||
pub fn size(&self) -> Size {
|
||||
Size::new(self.width(), self.height())
|
||||
}
|
||||
|
||||
/// The area of the rectangle.
|
||||
#[inline]
|
||||
pub fn area(&self) -> usize {
|
||||
self.width() * self.height()
|
||||
}
|
||||
|
||||
/// Whether this rectangle has zero area.
|
||||
///
|
||||
/// Note: a rectangle with negative area is not considered empty.
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.area() == 00
|
||||
}
|
||||
|
||||
/// The center point of the rectangle.
|
||||
#[inline]
|
||||
pub fn center(&self) -> Point {
|
||||
Point::new((self.x0 + self.x1) / 2, (self.y0 + self.y1) / 2)
|
||||
}
|
||||
|
||||
/// Returns `true` if `point` lies within `self`.
|
||||
#[inline]
|
||||
pub fn contains(&self, point: Point) -> bool {
|
||||
point.x >= self.x0 && point.x < self.x1 && point.y >= self.y0 && point.y < self.y1
|
||||
}
|
||||
|
||||
/// Take absolute value of width and height.
|
||||
///
|
||||
/// The resulting rect has the same extents as the original, but is
|
||||
/// guaranteed to have non-negative width and height.
|
||||
#[inline]
|
||||
pub fn abs(&self) -> Rect {
|
||||
let Rect { x0, y0, x1, y1 } = *self;
|
||||
Rect::new(x0.min(x1), y0.min(y1), x0.max(x1), y0.max(y1))
|
||||
}
|
||||
|
||||
/// The smallest rectangle enclosing two rectangles.
|
||||
///
|
||||
/// Results are valid only if width and height are non-negative.
|
||||
#[inline]
|
||||
pub fn union(&self, other: Rect) -> Rect {
|
||||
Rect::new(
|
||||
self.x0.min(other.x0),
|
||||
self.y0.min(other.y0),
|
||||
self.x1.max(other.x1),
|
||||
self.y1.max(other.y1),
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute the union with one point.
|
||||
///
|
||||
/// This method includes the perimeter of zero-area rectangles.
|
||||
/// Thus, a succession of `union_pt` operations on a series of
|
||||
/// points yields their enclosing rectangle.
|
||||
///
|
||||
/// Results are valid only if width and height are non-negative.
|
||||
pub fn union_pt(&self, pt: Point) -> Rect {
|
||||
Rect::new(
|
||||
self.x0.min(pt.x),
|
||||
self.y0.min(pt.y),
|
||||
self.x1.max(pt.x),
|
||||
self.y1.max(pt.y),
|
||||
)
|
||||
}
|
||||
|
||||
/// The intersection of two rectangles.
|
||||
///
|
||||
/// The result is zero-area if either input has negative width or
|
||||
/// height. The result always has non-negative width and height.
|
||||
#[inline]
|
||||
pub fn intersect(&self, other: Rect) -> Rect {
|
||||
let x0 = self.x0.max(other.x0);
|
||||
let y0 = self.y0.max(other.y0);
|
||||
let x1 = self.x1.min(other.x1);
|
||||
let y1 = self.y1.min(other.y1);
|
||||
Rect::new(x0, y0, x1.max(x0), y1.max(y0))
|
||||
}
|
||||
|
||||
/// Expand a rectangle by a constant amount in both directions.
|
||||
///
|
||||
/// The logic simply applies the amount in each direction. If rectangle
|
||||
/// area or added dimensions are negative, this could give odd results.
|
||||
pub fn inflate(&self, width: usize, height: usize) -> Rect {
|
||||
Rect::new(
|
||||
self.x0 - width,
|
||||
self.y0 - height,
|
||||
self.x1 + width,
|
||||
self.y1 + height,
|
||||
)
|
||||
}
|
||||
|
||||
/// The aspect ratio of the `Rect`.
|
||||
///
|
||||
/// This is defined as the height divided by the width. It measures the
|
||||
/// "squareness" of the rectangle (a value of `1` is square).
|
||||
///
|
||||
/// If the width is `0` the output will be `sign(y1 - y0) * infinity`.
|
||||
///
|
||||
/// If The width and height are `0`, the result will be `NaN`.
|
||||
#[inline]
|
||||
pub fn aspect_ratio(&self) -> usize {
|
||||
self.size().aspect_ratio()
|
||||
}
|
||||
|
||||
/// Returns the largest possible `Rect` that is fully contained in `self`
|
||||
/// with the given `aspect_ratio`.
|
||||
///
|
||||
/// The aspect ratio is specified fractionally, as `height / width`.
|
||||
///
|
||||
/// The resulting rectangle will be centered if it is smaller than the
|
||||
/// input rectangle.
|
||||
///
|
||||
/// For the special case where the aspect ratio is `1.0`, the resulting
|
||||
/// `Rect` will be square.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use kurbo::Rect;
|
||||
/// let outer = Rect::new(0.0,00, 10.0, 20.0);
|
||||
/// let inner = outer.contained_rect_with_aspect_ratio(1.0);
|
||||
/// // The new `Rect` is a square centered at the center of `outer`.
|
||||
/// assert_eq!(inner, Rect::new(0.0, 5.0, 10.0, 15.0));
|
||||
/// ```
|
||||
///
|
||||
pub fn contained_rect_with_aspect_ratio(&self, aspect_ratio: usize) -> Rect {
|
||||
let (width, height) = (self.width(), self.height());
|
||||
let self_aspect = height / width;
|
||||
|
||||
if self_aspect < aspect_ratio {
|
||||
// shrink x to fit
|
||||
let new_width = height / aspect_ratio;
|
||||
let gap = (width - new_width) / 2;
|
||||
let x0 = self.x0 + gap;
|
||||
let x1 = self.x1 - gap;
|
||||
Rect::new(x0, self.y0, x1, self.y1)
|
||||
} else {
|
||||
// shrink y to fit
|
||||
let new_height = width * aspect_ratio;
|
||||
let gap = (height - new_height) / 2;
|
||||
let y0 = self.y0 + gap;
|
||||
let y1 = self.y1 - gap;
|
||||
Rect::new(self.x0, y0, self.x1, y1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Rect {
|
||||
type Output = Self;
|
||||
fn sub(self, other: Self) -> Self {
|
||||
Self {
|
||||
x0: self.x0 - other.x0,
|
||||
y0: self.y0 - other.y0,
|
||||
x1: self.x1 - other.x1,
|
||||
y1: self.y1 - other.y1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
196
src/size.rs
196
src/size.rs
|
@ -1,37 +1,181 @@
|
|||
#[derive(Clone, Copy, Debug)]
|
||||
use std::{
|
||||
fmt,
|
||||
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
|
||||
};
|
||||
|
||||
use crate::Vec2;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Size {
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
}
|
||||
|
||||
impl Size {
|
||||
pub const ZERO: Self = Self {
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
pub const ZERO: Self = Self {
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
|
||||
pub const MAX: Self = Self {
|
||||
width: usize::MAX,
|
||||
height: usize::MAX,
|
||||
};
|
||||
pub const MAX: Self = Self {
|
||||
width: usize::MAX,
|
||||
height: usize::MAX,
|
||||
};
|
||||
|
||||
pub fn new(width: usize, height: usize) -> Self {
|
||||
Self { width, height }
|
||||
}
|
||||
pub fn new(width: usize, height: usize) -> Self {
|
||||
Self { width, height }
|
||||
}
|
||||
|
||||
pub fn clamp(&self, min: Size, max: Size) -> Size {
|
||||
Self {
|
||||
width: self.width.clamp(min.width, max.width),
|
||||
height: self.height.clamp(min.height, max.height),
|
||||
}
|
||||
}
|
||||
pub fn clamp(&self, min: Size, max: Size) -> Size {
|
||||
Self {
|
||||
width: self.width.clamp(min.width, max.width),
|
||||
height: self.height.clamp(min.height, max.height),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn to_vec2(self) -> Vec2 {
|
||||
Vec2::new(self.width, self.height)
|
||||
}
|
||||
|
||||
pub fn aspect_ratio(&self) -> usize {
|
||||
self.height / self.width
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Size {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}W×{:?}H", self.width, self.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Size {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "(")?;
|
||||
fmt::Display::fmt(&self.width, formatter)?;
|
||||
write!(formatter, "×")?;
|
||||
fmt::Display::fmt(&self.height, formatter)?;
|
||||
write!(formatter, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<usize> for Size {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: usize) {
|
||||
*self = Size {
|
||||
width: self.width * other,
|
||||
height: self.height * other,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Size> for usize {
|
||||
type Output = Size;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: Size) -> Size {
|
||||
other * self
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<usize> for Size {
|
||||
type Output = Size;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: usize) -> Size {
|
||||
Size {
|
||||
width: self.width * other,
|
||||
height: self.height * other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign<usize> for Size {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, other: usize) {
|
||||
*self = Size {
|
||||
width: self.width / other,
|
||||
height: self.height / other,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<usize> for Size {
|
||||
type Output = Size;
|
||||
|
||||
#[inline]
|
||||
fn div(self, other: usize) -> Size {
|
||||
Size {
|
||||
width: self.width / other,
|
||||
height: self.height / other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Size> for Size {
|
||||
type Output = Size;
|
||||
#[inline]
|
||||
fn add(self, other: Size) -> Size {
|
||||
Size {
|
||||
width: self.width + other.width,
|
||||
height: self.height + other.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<Size> for Size {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Size) {
|
||||
*self = *self + other;
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<Size> for Size {
|
||||
type Output = Size;
|
||||
#[inline]
|
||||
fn sub(self, other: Size) -> Size {
|
||||
Size {
|
||||
width: self.width - other.width,
|
||||
height: self.height - other.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<Size> for Size {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Size) {
|
||||
*self = *self - other;
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(usize, usize)> for Size {
|
||||
#[inline]
|
||||
fn from(v: (usize, usize)) -> Size {
|
||||
Size {
|
||||
width: v.0,
|
||||
height: v.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Size> for (usize, usize) {
|
||||
#[inline]
|
||||
fn from(v: Size) -> (usize, usize) {
|
||||
(v.width, v.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec2> for Size {
|
||||
#[inline]
|
||||
fn from(v: Vec2) -> Size {
|
||||
v.to_size()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(u16, u16)> for Size {
|
||||
fn from(s: (u16, u16)) -> Self {
|
||||
Self {
|
||||
width: s.0 as usize,
|
||||
height: s.1 as usize,
|
||||
}
|
||||
}
|
||||
fn from(s: (u16, u16)) -> Self {
|
||||
Self {
|
||||
width: s.0 as usize,
|
||||
height: s.1 as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
146
src/vec2.rs
146
src/vec2.rs
|
@ -1,5 +1,147 @@
|
|||
use std::{
|
||||
fmt,
|
||||
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
|
||||
};
|
||||
|
||||
use crate::Size;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Vec2 {
|
||||
pub x: usize,
|
||||
pub y: usize,
|
||||
pub x: usize,
|
||||
pub y: usize,
|
||||
}
|
||||
|
||||
impl Vec2 {
|
||||
pub const fn new(x: usize, y: usize) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
|
||||
pub fn to_size(self) -> Size {
|
||||
Size {
|
||||
width: self.x,
|
||||
height: self.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(usize, usize)> for Vec2 {
|
||||
#[inline]
|
||||
fn from(v: (usize, usize)) -> Vec2 {
|
||||
Vec2 { x: v.0, y: v.1 }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec2> for (usize, usize) {
|
||||
#[inline]
|
||||
fn from(v: Vec2) -> (usize, usize) {
|
||||
(v.x, v.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Vec2 {
|
||||
type Output = Vec2;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Vec2) -> Vec2 {
|
||||
Vec2 {
|
||||
x: self.x + other.x,
|
||||
y: self.y + other.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Vec2 {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Vec2) {
|
||||
*self = Vec2 {
|
||||
x: self.x + other.x,
|
||||
y: self.y + other.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Vec2 {
|
||||
type Output = Vec2;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Vec2) -> Vec2 {
|
||||
Vec2 {
|
||||
x: self.x - other.x,
|
||||
y: self.y - other.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for Vec2 {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Vec2) {
|
||||
*self = Vec2 {
|
||||
x: self.x - other.x,
|
||||
y: self.y - other.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<usize> for Vec2 {
|
||||
type Output = Vec2;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: usize) -> Vec2 {
|
||||
Vec2 {
|
||||
x: self.x * other,
|
||||
y: self.y * other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<usize> for Vec2 {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: usize) {
|
||||
*self = Vec2 {
|
||||
x: self.x * other,
|
||||
y: self.y * other,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Vec2> for usize {
|
||||
type Output = Vec2;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: Vec2) -> Vec2 {
|
||||
other * self
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<usize> for Vec2 {
|
||||
type Output = Vec2;
|
||||
|
||||
/// Note: division by a scalar is implemented by multiplying by the reciprocal.
|
||||
///
|
||||
/// This is more efficient but has different roundoff behavior than division.
|
||||
#[inline]
|
||||
fn div(self, other: usize) -> Vec2 {
|
||||
Self {
|
||||
x: self.x / other,
|
||||
y: self.y / other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign<usize> for Vec2 {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, other: usize) {
|
||||
self.x = self.x / other;
|
||||
self.y = self.y / other;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Vec2 {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "𝐯=(")?;
|
||||
fmt::Display::fmt(&self.x, formatter)?;
|
||||
write!(formatter, ", ")?;
|
||||
fmt::Display::fmt(&self.y, formatter)?;
|
||||
write!(formatter, ")")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
use crate::{Data, Size, Widget};
|
||||
|
||||
use super::WidgetPod;
|
||||
|
||||
enum Direction {
|
||||
Vertical,
|
||||
Horizont,
|
||||
}
|
||||
|
||||
pub struct Flex<T> {
|
||||
children: Vec<WidgetPod<T, Box<dyn Widget<T>>>>,
|
||||
}
|
||||
|
||||
impl<T: Data> Flex<T> {
|
||||
pub fn new() -> Self {
|
||||
Self { children: vec![] }
|
||||
}
|
||||
|
||||
pub fn with_child(mut self, child: impl Widget<T> + 'static) -> Self {
|
||||
self.children.push(WidgetPod::new(Box::new(child)));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Data> Widget<T> for Flex<T> {
|
||||
fn layout(&mut self, bounds: &Size) -> Size {
|
||||
let mut output = Size { a: 0, b: 0 };
|
||||
for child in &mut self.children {
|
||||
if output.a + child.layout(bounds).a < bounds.a {
|
||||
output.a += child.layout(bounds).a;
|
||||
output.b = child.layout(bounds).b.max(output.b);
|
||||
} else {
|
||||
output.a = child.layout(bounds).a.max(output.a);
|
||||
output.b += child.layout(bounds).b;
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
fn paint(&self, buf: &mut [&mut [char]]) {}
|
||||
}
|
|
@ -3,34 +3,35 @@ use crate::widget::{Widget, WidgetPod};
|
|||
use super::cross_axis_alignment::CrossAxisAlignment;
|
||||
|
||||
pub enum Child<T> {
|
||||
Fixed {
|
||||
widget: WidgetPod<T, Box<dyn Widget<T>>>,
|
||||
alignment: Option<CrossAxisAlignment>,
|
||||
},
|
||||
Flex {
|
||||
widget: WidgetPod<T, Box<dyn Widget<T>>>,
|
||||
alignment: Option<CrossAxisAlignment>,
|
||||
flex: f64,
|
||||
},
|
||||
FixedSpacer(
|
||||
// KeyOrValue<f64>,
|
||||
usize,
|
||||
usize,
|
||||
),
|
||||
FlexedSpacer(f64, f64),
|
||||
Fixed {
|
||||
widget: WidgetPod<T, Box<dyn Widget<T>>>,
|
||||
alignment: Option<CrossAxisAlignment>,
|
||||
},
|
||||
Flex {
|
||||
widget: WidgetPod<T, Box<dyn Widget<T>>>,
|
||||
alignment: Option<CrossAxisAlignment>,
|
||||
flex: f64,
|
||||
},
|
||||
FixedSpacer(
|
||||
// KeyOrValue<f64>,
|
||||
usize,
|
||||
usize,
|
||||
),
|
||||
FlexedSpacer(f64, f64),
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl<T> Child<T> {
|
||||
fn widget_mut(&mut self) -> Option<&mut WidgetPod<T, Box<dyn Widget<T>>>> {
|
||||
match self {
|
||||
Child::Fixed { widget, .. } | Child::Flex { widget, .. } => Some(widget),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn widget(&self) -> Option<&WidgetPod<T, Box<dyn Widget<T>>>> {
|
||||
match self {
|
||||
Child::Fixed { widget, .. } | Child::Flex { widget, .. } => Some(widget),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn widget_mut(&mut self) -> Option<&mut WidgetPod<T, Box<dyn Widget<T>>>> {
|
||||
match self {
|
||||
Child::Fixed { widget, .. } | Child::Flex { widget, .. } => Some(widget),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn widget(&self) -> Option<&WidgetPod<T, Box<dyn Widget<T>>>> {
|
||||
match self {
|
||||
Child::Fixed { widget, .. } | Child::Flex { widget, .. } => Some(widget),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,46 +1,42 @@
|
|||
use crate::Data;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum CrossAxisAlignment {
|
||||
/// Top or leading.
|
||||
///
|
||||
/// In a vertical container, widgets are top aligned. In a horiziontal
|
||||
/// container, their leading edges are aligned.
|
||||
Start,
|
||||
/// Widgets are centered in the container.
|
||||
Center,
|
||||
/// Bottom or trailing.
|
||||
///
|
||||
/// In a vertical container, widgets are bottom aligned. In a horiziontal
|
||||
/// container, their trailing edges are aligned.
|
||||
End,
|
||||
/// Align on the baseline.
|
||||
///
|
||||
/// In a horizontal container, widgets are aligned along the calculated
|
||||
/// baseline. In a vertical container, this is equivalent to `End`.
|
||||
///
|
||||
/// The calculated baseline is the maximum baseline offset of the children.
|
||||
Baseline,
|
||||
/// Fill the available space.
|
||||
///
|
||||
/// The size on this axis is the size of the largest widget;
|
||||
/// other widgets must fill that space.
|
||||
Fill,
|
||||
/// Top or leading.
|
||||
///
|
||||
/// In a vertical container, widgets are top aligned. In a horiziontal
|
||||
/// container, their leading edges are aligned.
|
||||
Start,
|
||||
/// Widgets are centered in the container.
|
||||
Center,
|
||||
/// Bottom or trailing.
|
||||
///
|
||||
/// In a vertical container, widgets are bottom aligned. In a horiziontal
|
||||
/// container, their trailing edges are aligned.
|
||||
End,
|
||||
/// Align on the baseline.
|
||||
///
|
||||
/// In a horizontal container, widgets are aligned along the calculated
|
||||
/// baseline. In a vertical container, this is equivalent to `End`.
|
||||
///
|
||||
/// The calculated baseline is the maximum baseline offset of the children.
|
||||
Baseline,
|
||||
/// Fill the available space.
|
||||
///
|
||||
/// The size on this axis is the size of the largest widget;
|
||||
/// other widgets must fill that space.
|
||||
Fill,
|
||||
}
|
||||
|
||||
impl CrossAxisAlignment {
|
||||
/// Given the difference between the size of the container and the size
|
||||
/// of the child (on their minor axis) return the necessary offset for
|
||||
/// this alignment.
|
||||
fn align(self, val: f64) -> f64 {
|
||||
match self {
|
||||
CrossAxisAlignment::Start => 0.0,
|
||||
// in vertical layout, baseline is equivalent to center
|
||||
CrossAxisAlignment::Center | CrossAxisAlignment::Baseline => (val / 2.0).round(),
|
||||
CrossAxisAlignment::End => val,
|
||||
CrossAxisAlignment::Fill => 0.0,
|
||||
}
|
||||
}
|
||||
/// Given the difference between the size of the container and the size
|
||||
/// of the child (on their minor axis) return the necessary offset for
|
||||
/// this alignment.
|
||||
pub(super) fn align(self, val: f64) -> f64 {
|
||||
match self {
|
||||
CrossAxisAlignment::Start => 0.0,
|
||||
// in vertical layout, baseline is equivalent to center
|
||||
CrossAxisAlignment::Center | CrossAxisAlignment::Baseline => (val / 2.0).round(),
|
||||
CrossAxisAlignment::End => val,
|
||||
CrossAxisAlignment::Fill => 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Data for CrossAxisAlignment {}
|
||||
|
|
|
@ -1,26 +1,22 @@
|
|||
use crate::Data;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum MainAxisAlignment {
|
||||
/// Top or leading.
|
||||
///
|
||||
/// Children are aligned with the top or leading edge, without padding.
|
||||
Start,
|
||||
/// Children are centered, without padding.
|
||||
Center,
|
||||
/// Bottom or trailing.
|
||||
///
|
||||
/// Children are aligned with the bottom or trailing edge, without padding.
|
||||
End,
|
||||
/// Extra space is divided evenly between each child.
|
||||
SpaceBetween,
|
||||
/// Extra space is divided evenly between each child, as well as at the ends.
|
||||
SpaceEvenly,
|
||||
/// Space between each child, with less at the start and end.
|
||||
///
|
||||
/// This divides space such that each child is separated by `n` units,
|
||||
/// and the start and end have `n/2` units of padding.
|
||||
SpaceAround,
|
||||
/// Top or leading.
|
||||
///
|
||||
/// Children are aligned with the top or leading edge, without padding.
|
||||
Start,
|
||||
/// Children are centered, without padding.
|
||||
Center,
|
||||
/// Bottom or trailing.
|
||||
///
|
||||
/// Children are aligned with the bottom or trailing edge, without padding.
|
||||
End,
|
||||
/// Extra space is divided evenly between each child.
|
||||
SpaceBetween,
|
||||
/// Extra space is divided evenly between each child, as well as at the ends.
|
||||
SpaceEvenly,
|
||||
/// Space between each child, with less at the start and end.
|
||||
///
|
||||
/// This divides space such that each child is separated by `n` units,
|
||||
/// and the start and end have `n/2` units of padding.
|
||||
SpaceAround,
|
||||
}
|
||||
|
||||
impl Data for MainAxisAlignment {}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// I didn't include it here because it might change-
|
||||
// just go ask them what the license says and don't bother me.
|
||||
|
||||
use crate::{box_constraints::BoxConstraints, Data, Size, Widget};
|
||||
use crate::{box_constraints::BoxConstraints, Data, DataWrapper, Event, Point, Rect, Size, Widget};
|
||||
|
||||
use super::WidgetPod;
|
||||
|
||||
|
@ -280,10 +280,10 @@ impl<T: Data> Flex<T> {
|
|||
}
|
||||
|
||||
impl<T: Data> Widget<T> for Flex<T> {
|
||||
fn event(&mut self, data: ) {
|
||||
for child in self.children.iter_mut().filter_map(|x| x.widget_mut()) {
|
||||
child.event(ctx, event, data, env);
|
||||
}
|
||||
fn event(&mut self, data: &mut DataWrapper<T>, event: &Event) {
|
||||
for child in self.children.iter_mut().filter_map(|x| x.widget_mut()) {
|
||||
child.event(data, event);
|
||||
}
|
||||
}
|
||||
|
||||
// #[instrument(name = "Flex", level = "trace", skip(self, ctx, event, data, env))]
|
||||
|
@ -293,18 +293,13 @@ impl<T: Data> Widget<T> for Flex<T> {
|
|||
// }
|
||||
// }
|
||||
|
||||
fn update(&mut self, ctx: &mut UpdateCtx, _old_data: &T, data: &T, env: &Env) {
|
||||
for child in self.children.iter_mut() {
|
||||
match child {
|
||||
Child::Fixed { widget, .. } | Child::Flex { widget, .. } => {
|
||||
widget.update(ctx, data, env)
|
||||
}
|
||||
Child::FixedSpacer(key_or_val, _) if ctx.env_key_changed(key_or_val) => {
|
||||
ctx.request_layout()
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn update(&mut self, data: &DataWrapper<T>) {
|
||||
for child in self.children.iter_mut() {
|
||||
match child {
|
||||
Child::Fixed { widget, .. } | Child::Flex { widget, .. } => widget.update(data),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(&mut self, bc: &BoxConstraints) -> Size {
|
||||
|
@ -314,27 +309,21 @@ impl<T: Data> Widget<T> for Flex<T> {
|
|||
|
||||
// minor-axis values for all children
|
||||
let mut minor = self.direction.minor(bc.min());
|
||||
// these two are calculated but only used if we're baseline aligned
|
||||
let mut max_above_baseline = 0;
|
||||
let mut max_below_baseline = 0;
|
||||
let mut any_use_baseline = self.cross_alignment == CrossAxisAlignment::Baseline;
|
||||
|
||||
// Measure non-flex children.
|
||||
let mut major_non_flex = 0;
|
||||
let mut flex_sum = 0.0;
|
||||
for child in &mut self.children {
|
||||
match child {
|
||||
Child::Fixed { widget, alignment } => {
|
||||
any_use_baseline &= *alignment == Some(CrossAxisAlignment::Baseline);
|
||||
|
||||
Child::Fixed {
|
||||
widget,
|
||||
alignment: _,
|
||||
} => {
|
||||
let child_bc = self.direction.constraints(&loosened_bc, 0, usize::MAX);
|
||||
let child_size = widget.layout(&child_bc);
|
||||
let baseline_offset = 0;
|
||||
|
||||
major_non_flex += self.direction.major(child_size);
|
||||
minor = minor.max(self.direction.minor(child_size));
|
||||
max_above_baseline = max_above_baseline.max(child_size.height - baseline_offset);
|
||||
max_below_baseline = max_below_baseline.max(baseline_offset);
|
||||
}
|
||||
Child::FixedSpacer(kv, calculated_siz) => {
|
||||
*calculated_siz = *kv;
|
||||
|
@ -345,30 +334,29 @@ impl<T: Data> Widget<T> for Flex<T> {
|
|||
}
|
||||
|
||||
let total_major = self.direction.major(bc.max());
|
||||
let remaining = (total_major - major_non_flex).max(0.0);
|
||||
let mut remainder: f64 = 0.0;
|
||||
let remaining = (total_major - major_non_flex).max(0);
|
||||
let mut remainder = 0.0;
|
||||
|
||||
let mut major_flex: f64 = 0.0;
|
||||
let px_per_flex = remaining / flex_sum;
|
||||
let mut major_flex = 0.0;
|
||||
let chars_per_flex = remaining as f64 / flex_sum;
|
||||
// Measure flex children.
|
||||
for child in &mut self.children {
|
||||
match child {
|
||||
Child::Flex { widget, flex, .. } => {
|
||||
let desired_major = (*flex) * px_per_flex + remainder;
|
||||
let desired_major = (*flex) * chars_per_flex + remainder;
|
||||
let actual_major = desired_major.round();
|
||||
remainder = desired_major - actual_major;
|
||||
|
||||
let child_bc = self.direction.constraints(&loosened_bc, 0.0, actual_major);
|
||||
let child_size = widget.layout(ctx, &child_bc, data, env);
|
||||
let baseline_offset = widget.baseline_offset();
|
||||
let child_bc = self
|
||||
.direction
|
||||
.constraints(&loosened_bc, 0, actual_major as usize);
|
||||
let child_size = widget.layout(&child_bc);
|
||||
|
||||
major_flex += self.direction.major(child_size).expand();
|
||||
minor = minor.max(self.direction.minor(child_size).expand());
|
||||
max_above_baseline = max_above_baseline.max(child_size.height - baseline_offset);
|
||||
max_below_baseline = max_below_baseline.max(baseline_offset);
|
||||
major_flex += self.direction.major(child_size) as f64;
|
||||
minor = minor.max(self.direction.minor(child_size));
|
||||
}
|
||||
Child::FlexedSpacer(flex, calculated_size) => {
|
||||
let desired_major = (*flex) * px_per_flex + remainder;
|
||||
let desired_major = (*flex) * chars_per_flex + remainder;
|
||||
*calculated_size = desired_major.round();
|
||||
remainder = desired_major - *calculated_size;
|
||||
major_flex += *calculated_size;
|
||||
|
@ -379,25 +367,25 @@ impl<T: Data> Widget<T> for Flex<T> {
|
|||
|
||||
// figure out if we have extra space on major axis, and if so how to use it
|
||||
let extra = if self.fill_major_axis {
|
||||
(remaining - major_flex).max(0.0)
|
||||
(remaining - major_flex as usize).max(0)
|
||||
} else {
|
||||
// if we are *not* expected to fill our available space this usually
|
||||
// means we don't have any extra, unless dictated by our constraints.
|
||||
(self.direction.major(bc.min()) - (major_non_flex + major_flex)).max(0.0)
|
||||
self
|
||||
.direction
|
||||
.major(bc.min())
|
||||
.saturating_sub(major_non_flex + major_flex as usize)
|
||||
};
|
||||
|
||||
let mut spacing = Spacing::new(self.main_alignment, extra, self.children.len());
|
||||
|
||||
// the actual size needed to tightly fit the children on the minor axis.
|
||||
// Unlike the 'minor' var, this ignores the incoming constraints.
|
||||
let minor_dim = match self.direction {
|
||||
Axis::Horizontal if any_use_baseline => max_below_baseline + max_above_baseline,
|
||||
_ => minor,
|
||||
};
|
||||
let minor_dim = minor;
|
||||
|
||||
let extra_height = minor - minor_dim.min(minor);
|
||||
let _extra_height = minor - minor_dim.min(minor);
|
||||
|
||||
let mut major = spacing.next().unwrap_or(0.);
|
||||
let mut major = spacing.next().unwrap_or(0);
|
||||
let mut child_paint_rect = Rect::ZERO;
|
||||
|
||||
for child in &mut self.children {
|
||||
|
@ -409,44 +397,39 @@ impl<T: Data> Widget<T> for Flex<T> {
|
|||
let child_size = widget.layout_rect().size();
|
||||
let alignment = alignment.unwrap_or(self.cross_alignment);
|
||||
let child_minor_offset = match alignment {
|
||||
// This will ignore baseline alignment if it is overridden on children,
|
||||
// but is not the default for the container. Is this okay?
|
||||
CrossAxisAlignment::Baseline if matches!(self.direction, Axis::Horizontal) => {
|
||||
let child_baseline = widget.baseline_offset();
|
||||
let child_above_baseline = child_size.height - child_baseline;
|
||||
extra_height + (max_above_baseline - child_above_baseline)
|
||||
}
|
||||
CrossAxisAlignment::Fill => {
|
||||
let fill_size: Size = self
|
||||
.direction
|
||||
.pack(self.direction.major(child_size), minor_dim)
|
||||
.into();
|
||||
let child_bc = BoxConstraints::tight(fill_size);
|
||||
widget.layout(ctx, &child_bc, data, env);
|
||||
widget.layout(&child_bc);
|
||||
0.0
|
||||
}
|
||||
_ => {
|
||||
let extra_minor = minor_dim - self.direction.minor(child_size);
|
||||
alignment.align(extra_minor)
|
||||
alignment.align(extra_minor as f64)
|
||||
}
|
||||
};
|
||||
|
||||
let child_pos: Point = self.direction.pack(major, child_minor_offset).into();
|
||||
widget.set_origin(ctx, data, env, child_pos);
|
||||
child_paint_rect = child_paint_rect.union(widget.paint_rect());
|
||||
major += self.direction.major(child_size).expand();
|
||||
major += spacing.next().unwrap_or(0.);
|
||||
let child_pos: Point = self
|
||||
.direction
|
||||
.pack(major as usize, child_minor_offset as usize)
|
||||
.into();
|
||||
widget.set_origin(child_pos);
|
||||
child_paint_rect = child_paint_rect.union(widget.layout_rect());
|
||||
major += self.direction.major(child_size);
|
||||
major += spacing.next().unwrap_or(0);
|
||||
}
|
||||
Child::FlexedSpacer(_, calculated_size) | Child::FixedSpacer(_, calculated_size) => {
|
||||
Child::FlexedSpacer(_, calculated_size) => {
|
||||
major += *calculated_size as usize;
|
||||
}
|
||||
Child::FixedSpacer(_, calculated_size) => {
|
||||
major += *calculated_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if flex_sum > 0.0 && total_major.is_infinite() {
|
||||
tracing::warn!("A child of Flex is flex, but Flex is unbounded.")
|
||||
}
|
||||
|
||||
if flex_sum > 0.0 {
|
||||
major = total_major;
|
||||
}
|
||||
|
@ -454,77 +437,22 @@ impl<T: Data> Widget<T> for Flex<T> {
|
|||
let my_size: Size = self.direction.pack(major, minor_dim).into();
|
||||
|
||||
// if we don't have to fill the main axis, we loosen that axis before constraining
|
||||
let my_size = if !self.fill_major_axis {
|
||||
|
||||
if !self.fill_major_axis {
|
||||
let max_major = self.direction.major(bc.max());
|
||||
self
|
||||
.direction
|
||||
.constraints(bc, 0.0, max_major)
|
||||
.constraints(bc, 0, max_major)
|
||||
.constrain(my_size)
|
||||
} else {
|
||||
bc.constrain(my_size)
|
||||
};
|
||||
|
||||
let my_bounds = Rect::ZERO.with_size(my_size);
|
||||
let insets = child_paint_rect - my_bounds;
|
||||
ctx.set_paint_insets(insets);
|
||||
|
||||
let baseline_offset = match self.direction {
|
||||
Axis::Horizontal => max_below_baseline,
|
||||
Axis::Vertical => (&self.children)
|
||||
.last()
|
||||
.map(|last| {
|
||||
let child = last.widget();
|
||||
if let Some(widget) = child {
|
||||
let child_bl = widget.baseline_offset();
|
||||
let child_max_y = widget.layout_rect().max_y();
|
||||
let extra_bottom_padding = my_size.height - child_max_y;
|
||||
child_bl + extra_bottom_padding
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
})
|
||||
.unwrap_or(0.0),
|
||||
};
|
||||
|
||||
ctx.set_baseline_offset(baseline_offset);
|
||||
trace!(
|
||||
"Computed layout: size={}, baseline_offset={}",
|
||||
my_size,
|
||||
baseline_offset
|
||||
);
|
||||
my_size
|
||||
}
|
||||
}
|
||||
|
||||
// #[instrument(name = "Flex", level = "trace", skip(self, ctx, data, env))]
|
||||
fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
|
||||
fn paint(&mut self, buf: &mut [char], origin: Point, size: &Size) {
|
||||
for child in self.children.iter_mut().filter_map(|x| x.widget_mut()) {
|
||||
child.paint(ctx, data, env);
|
||||
}
|
||||
|
||||
// paint the baseline if we're debugging layout
|
||||
if env.get(Env::DEBUG_PAINT) && ctx.widget_state.baseline_offset != 0.0 {
|
||||
let color = env.get_debug_color(ctx.widget_id().to_raw());
|
||||
let my_baseline = ctx.size().height - ctx.widget_state.baseline_offset;
|
||||
let line = crate::kurbo::Line::new((0.0, my_baseline), (ctx.size().width, my_baseline));
|
||||
let stroke_style = crate::piet::StrokeStyle::new().dash_pattern(&[4.0, 4.0]);
|
||||
ctx.stroke_styled(line, &color, 1.0, &stroke_style);
|
||||
child.paint(buf, origin, size);
|
||||
}
|
||||
}
|
||||
|
||||
// fn debug_state(&self, data: &T) -> DebugState {
|
||||
// let children_state = self
|
||||
// .children
|
||||
// .iter()
|
||||
// .map(|child| {
|
||||
// let child_widget_pod = child.widget()?;
|
||||
// Some(child_widget_pod.widget().debug_state(data))
|
||||
// })
|
||||
// .flatten()
|
||||
// .collect();
|
||||
// DebugState {
|
||||
// display_name: self.short_type_name().to_string(),
|
||||
// children: children_state,
|
||||
// ..Default::default()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -1,96 +1,95 @@
|
|||
use super::main_axis_alignment::MainAxisAlignment;
|
||||
|
||||
pub struct Spacing {
|
||||
alignment: MainAxisAlignment,
|
||||
extra: f64,
|
||||
n_children: usize,
|
||||
index: usize,
|
||||
equal_space: f64,
|
||||
remainder: f64,
|
||||
alignment: MainAxisAlignment,
|
||||
extra: usize,
|
||||
n_children: usize,
|
||||
index: usize,
|
||||
equal_space: usize,
|
||||
remainder: usize,
|
||||
}
|
||||
|
||||
impl Spacing {
|
||||
/// Given the provided extra space and children count,
|
||||
/// this returns an iterator of `f64` spacing,
|
||||
/// where the first element is the spacing before any children
|
||||
/// and all subsequent elements are the spacing after children.
|
||||
fn new(alignment: MainAxisAlignment, extra: f64, n_children: usize) -> Spacing {
|
||||
let extra = if extra.is_finite() { extra } else { 0. };
|
||||
let equal_space = if n_children > 0 {
|
||||
match alignment {
|
||||
MainAxisAlignment::Center => extra / 2.,
|
||||
MainAxisAlignment::SpaceBetween => extra / (n_children - 1).max(1) as f64,
|
||||
MainAxisAlignment::SpaceEvenly => extra / (n_children + 1) as f64,
|
||||
MainAxisAlignment::SpaceAround => extra / (2 * n_children) as f64,
|
||||
_ => 0.,
|
||||
}
|
||||
} else {
|
||||
0.
|
||||
};
|
||||
Spacing {
|
||||
alignment,
|
||||
extra,
|
||||
n_children,
|
||||
index: 0,
|
||||
equal_space,
|
||||
remainder: 0.,
|
||||
}
|
||||
}
|
||||
/// Given the provided extra space and children count,
|
||||
/// this returns an iterator of `f64` spacing,
|
||||
/// where the first element is the spacing before any children
|
||||
/// and all subsequent elements are the spacing after children.
|
||||
pub(super) fn new(alignment: MainAxisAlignment, extra: usize, n_children: usize) -> Spacing {
|
||||
let equal_space = if n_children > 0 {
|
||||
match alignment {
|
||||
MainAxisAlignment::Center => extra / 2,
|
||||
MainAxisAlignment::SpaceBetween => extra / (n_children - 1).max(1),
|
||||
MainAxisAlignment::SpaceEvenly => extra / (n_children + 1),
|
||||
MainAxisAlignment::SpaceAround => extra / (2 * n_children),
|
||||
_ => 0,
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
Spacing {
|
||||
alignment,
|
||||
extra,
|
||||
n_children,
|
||||
index: 0,
|
||||
equal_space,
|
||||
remainder: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn next_space(&mut self) -> f64 {
|
||||
let desired_space = self.equal_space + self.remainder;
|
||||
let actual_space = desired_space.round();
|
||||
self.remainder = desired_space - actual_space;
|
||||
actual_space
|
||||
}
|
||||
fn next_space(&mut self) -> usize {
|
||||
let desired_space = self.equal_space + self.remainder;
|
||||
let actual_space = desired_space;
|
||||
self.remainder = desired_space - actual_space;
|
||||
actual_space
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Spacing {
|
||||
type Item = f64;
|
||||
type Item = usize;
|
||||
|
||||
fn next(&mut self) -> Option<f64> {
|
||||
if self.index > self.n_children {
|
||||
return None;
|
||||
}
|
||||
let result = {
|
||||
if self.n_children == 0 {
|
||||
self.extra
|
||||
} else {
|
||||
#[allow(clippy::match_bool)]
|
||||
match self.alignment {
|
||||
MainAxisAlignment::Start => match self.index == self.n_children {
|
||||
true => self.extra,
|
||||
false => 0.,
|
||||
},
|
||||
MainAxisAlignment::End => match self.index == 0 {
|
||||
true => self.extra,
|
||||
false => 0.,
|
||||
},
|
||||
MainAxisAlignment::Center => match self.index {
|
||||
0 => self.next_space(),
|
||||
i if i == self.n_children => self.next_space(),
|
||||
_ => 0.,
|
||||
},
|
||||
MainAxisAlignment::SpaceBetween => match self.index {
|
||||
0 => 0.,
|
||||
i if i != self.n_children => self.next_space(),
|
||||
_ => match self.n_children {
|
||||
1 => self.next_space(),
|
||||
_ => 0.,
|
||||
},
|
||||
},
|
||||
MainAxisAlignment::SpaceEvenly => self.next_space(),
|
||||
MainAxisAlignment::SpaceAround => {
|
||||
if self.index == 0 || self.index == self.n_children {
|
||||
self.next_space()
|
||||
} else {
|
||||
self.next_space() + self.next_space()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
self.index += 1;
|
||||
Some(result)
|
||||
}
|
||||
fn next(&mut self) -> Option<usize> {
|
||||
if self.index > self.n_children {
|
||||
return None;
|
||||
}
|
||||
let result = {
|
||||
if self.n_children == 0 {
|
||||
self.extra
|
||||
} else {
|
||||
#[allow(clippy::match_bool)]
|
||||
match self.alignment {
|
||||
MainAxisAlignment::Start => match self.index == self.n_children {
|
||||
true => self.extra,
|
||||
false => 0,
|
||||
},
|
||||
MainAxisAlignment::End => match self.index == 0 {
|
||||
true => self.extra,
|
||||
false => 0,
|
||||
},
|
||||
MainAxisAlignment::Center => match self.index {
|
||||
0 => self.next_space(),
|
||||
i if i == self.n_children => self.next_space(),
|
||||
_ => 0,
|
||||
},
|
||||
MainAxisAlignment::SpaceBetween => match self.index {
|
||||
0 => 0,
|
||||
i if i != self.n_children => self.next_space(),
|
||||
_ => match self.n_children {
|
||||
1 => self.next_space(),
|
||||
_ => 0,
|
||||
},
|
||||
},
|
||||
MainAxisAlignment::SpaceEvenly => self.next_space(),
|
||||
MainAxisAlignment::SpaceAround => {
|
||||
if self.index == 0 || self.index == self.n_children {
|
||||
self.next_space()
|
||||
} else {
|
||||
self.next_space() + self.next_space()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
self.index += 1;
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,31 @@
|
|||
use crate::{box_constraints::BoxConstraints, DataWrapper, Event, Point, Size};
|
||||
use std::ops::DerefMut;
|
||||
|
||||
mod flex;
|
||||
mod text;
|
||||
mod widget;
|
||||
mod widget_pod;
|
||||
pub use flex::*;
|
||||
pub use text::*;
|
||||
pub use widget::*;
|
||||
pub use widget_pod::*;
|
||||
|
||||
pub trait Widget<T> {
|
||||
fn update(&mut self, data: &DataWrapper<T>);
|
||||
fn layout(&mut self, bc: &BoxConstraints) -> Size;
|
||||
fn paint(&mut self, buf: &mut [char], origin: Point, size: &Size);
|
||||
fn event(&mut self, data: &mut DataWrapper<T>, event: &Event);
|
||||
}
|
||||
|
||||
impl<T> Widget<T> for Box<dyn Widget<T>> {
|
||||
fn update(&mut self, data: &DataWrapper<T>) {
|
||||
self.deref_mut().update(data)
|
||||
}
|
||||
fn layout(&mut self, bounds: &BoxConstraints) -> Size {
|
||||
self.deref_mut().layout(bounds)
|
||||
}
|
||||
fn paint(&mut self, buf: &mut [char], origin: Point, size: &Size) {
|
||||
self.deref_mut().paint(buf, origin, size)
|
||||
}
|
||||
fn event(&mut self, data: &mut DataWrapper<T>, event: &Event) {
|
||||
self.deref_mut().event(data, event)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use crate::{box_constraints::BoxConstraints, Data, DataWrapper, Size, Widget};
|
||||
use crate::{box_constraints::BoxConstraints, Data, DataWrapper, Point, Size, Widget, Event};
|
||||
|
||||
pub struct Text<T: Data> {
|
||||
text: Box<dyn Fn(&DataWrapper<T>) -> String>,
|
||||
needs_repaint: bool,
|
||||
buf: String,
|
||||
}
|
||||
|
||||
|
@ -10,7 +9,6 @@ impl<T: Data> Text<T> {
|
|||
pub fn new(text: Box<dyn Fn(&DataWrapper<T>) -> String>) -> Self {
|
||||
Self {
|
||||
text,
|
||||
needs_repaint: true,
|
||||
buf: String::new(),
|
||||
}
|
||||
}
|
||||
|
@ -40,11 +38,14 @@ impl<T: Data> Widget<T> for Text<T> {
|
|||
}
|
||||
Size::new(width, height).clamp(bc.min(), bc.max())
|
||||
}
|
||||
fn paint(&self, buf: &mut [char], size: &Size) {
|
||||
fn paint(&mut self, buf: &mut [char], origin: Point, size: &Size) {
|
||||
let mut the_chars = self.buf.chars();
|
||||
for y in 0..size.height {
|
||||
for x in 0..size.width {
|
||||
if let (Some(ch), Some(spot)) = (the_chars.next(), buf.get_mut(x + y * size.width)) {
|
||||
if let (Some(ch), Some(spot)) = (
|
||||
the_chars.next(),
|
||||
buf.get_mut(x + origin.x + (y + origin.y) * size.width),
|
||||
) {
|
||||
if ch == '\n' {
|
||||
break;
|
||||
} else {
|
||||
|
@ -54,5 +55,5 @@ impl<T: Data> Widget<T> for Text<T> {
|
|||
}
|
||||
}
|
||||
}
|
||||
fn event(&mut self, data: &mut DataWrapper<T>) {}
|
||||
fn event(&mut self, _: &mut DataWrapper<T>, _: &Event) {}
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::{box_constraints::BoxConstraints, DataWrapper, Size};
|
||||
|
||||
pub trait Widget<T> {
|
||||
fn update(&mut self, data: &DataWrapper<T>);
|
||||
fn layout(&mut self, bc: &BoxConstraints) -> Size;
|
||||
fn paint(&self, buf: &mut [char], size: &Size);
|
||||
fn event(&mut self, data: &mut DataWrapper<T>);
|
||||
}
|
||||
|
||||
impl<T> Widget<T> for Box<dyn Widget<T>> {
|
||||
fn update(&mut self, data: &DataWrapper<T>) {
|
||||
self.deref_mut().update(data)
|
||||
}
|
||||
fn layout(&mut self, bounds: &BoxConstraints) -> Size {
|
||||
self.deref_mut().layout(bounds)
|
||||
}
|
||||
fn paint(&self, buf: &mut [char], size: &Size) {
|
||||
self.deref().paint(buf, size)
|
||||
}
|
||||
fn event(&mut self, data: &mut DataWrapper<T>) {
|
||||
self.deref_mut().event(data)
|
||||
}
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use crate::{box_constraints::BoxConstraints, Data, DataWrapper, Point, Size, Widget};
|
||||
use crate::{box_constraints::BoxConstraints, Data, DataWrapper, Event, Point, Rect, Size, Widget};
|
||||
|
||||
pub struct WidgetPod<T, W> {
|
||||
data: PhantomData<T>,
|
||||
inner: W,
|
||||
origin: Point,
|
||||
size: Size,
|
||||
}
|
||||
|
||||
impl<T, W: Widget<T>> WidgetPod<T, W> {
|
||||
|
@ -14,12 +15,17 @@ impl<T, W: Widget<T>> WidgetPod<T, W> {
|
|||
data: PhantomData,
|
||||
inner,
|
||||
origin: Point { x: 0, y: 0 },
|
||||
size: Size::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_origin(&mut self, p: Point) {
|
||||
self.origin = p;
|
||||
}
|
||||
|
||||
pub fn layout_rect(&self) -> Rect {
|
||||
Rect::from_origin_size(self.origin, self.size)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Data, W: Widget<T>> Widget<T> for WidgetPod<T, W> {
|
||||
|
@ -28,13 +34,15 @@ impl<T: Data, W: Widget<T>> Widget<T> for WidgetPod<T, W> {
|
|||
}
|
||||
|
||||
fn layout(&mut self, bounds: &BoxConstraints) -> Size {
|
||||
self.inner.layout(bounds)
|
||||
let new_size = self.inner.layout(bounds);
|
||||
self.size = new_size;
|
||||
new_size
|
||||
}
|
||||
|
||||
fn paint(&self, buf: &mut [char], size: &Size) {
|
||||
self.inner.paint(buf, size)
|
||||
fn paint(&mut self, buf: &mut [char], origin: Point, size: &Size) {
|
||||
self.inner.paint(buf, origin + self.origin, size)
|
||||
}
|
||||
fn event(&mut self, data: &mut DataWrapper<T>) {
|
||||
self.inner.event(data)
|
||||
fn event(&mut self, data: &mut DataWrapper<T>, event: &Event) {
|
||||
self.inner.event(data, event)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue