Rip out buffer and widgets
I've decided to use the "taffy" crate for layouting. To make the API somewhat bearable to use, I've decided to perform all rendering in a good ol' rgb image buffer (in this case, an ImageBuffer from the "image" crate). The pixels will be converted to their final representation just before being sent to whatever device will be displaying them. I've removed the buffer and the color traits because they're no longer necessary. I've also removed the existing widgets because the widget system will have to be redesigned to work well with taffy.
This commit is contained in:
parent
04ed092c4f
commit
eb04b3fb50
13 changed files with 38 additions and 300 deletions
|
|
@ -5,6 +5,7 @@ edition.workspace = true
|
|||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
image.workspace = true
|
||||
palette.workspace = true
|
||||
|
||||
[lints]
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
use crate::Vec2;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Buffer<C> {
|
||||
size: Vec2,
|
||||
data: Vec<C>,
|
||||
}
|
||||
|
||||
impl<C> Buffer<C> {
|
||||
pub fn new(size: Vec2, color: C) -> Self
|
||||
where
|
||||
C: Copy,
|
||||
{
|
||||
assert!(size.x >= 0);
|
||||
assert!(size.y >= 0);
|
||||
|
||||
let len = (size.x * size.y) as usize;
|
||||
let data = vec![color; len];
|
||||
|
||||
Self { size, data }
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Vec2 {
|
||||
self.size
|
||||
}
|
||||
|
||||
fn index(&self, pos: Vec2) -> Option<usize> {
|
||||
let in_bounds_x = pos.x >= 0 || pos.x < self.size.x;
|
||||
let in_bounds_y = pos.y >= 0 || pos.y < self.size.y;
|
||||
let in_bounds = in_bounds_x && in_bounds_y;
|
||||
|
||||
if in_bounds {
|
||||
Some((pos.y * self.size.x + pos.x) as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn at(&self, pos: Vec2) -> Option<&C> {
|
||||
let index = self.index(pos)?;
|
||||
let pixel = self.data.get(index)?;
|
||||
Some(pixel)
|
||||
}
|
||||
|
||||
pub fn at_mut(&mut self, pos: Vec2) -> Option<&mut C> {
|
||||
let index = self.index(pos)?;
|
||||
let pixel = self.data.get_mut(index)?;
|
||||
Some(pixel)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
use palette::Srgb;
|
||||
|
||||
pub trait Color: Copy {
|
||||
/// Convert to an sRGB color.
|
||||
///
|
||||
/// Useful for debugging or dithering.
|
||||
fn to_srgb(self) -> Srgb;
|
||||
|
||||
/// Combine two colors by putting one "over" the other.
|
||||
fn over(self, below: Self) -> Self;
|
||||
}
|
||||
|
|
@ -1,9 +1,5 @@
|
|||
pub use crate::{buffer::*, color::*, rect::*, vec2::*, view::*, widget::*};
|
||||
pub use crate::{rect::*, vec2::*, view::*};
|
||||
|
||||
mod buffer;
|
||||
mod color;
|
||||
mod rect;
|
||||
mod vec2;
|
||||
mod view;
|
||||
mod widget;
|
||||
pub mod widgets;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,18 @@ impl Vec2 {
|
|||
Self { x, y }
|
||||
}
|
||||
|
||||
pub fn from_u32(x: u32, y: u32) -> Self {
|
||||
let x: i32 = x.try_into().expect("x too large");
|
||||
let y: i32 = y.try_into().expect("y too large");
|
||||
Self::new(x, y)
|
||||
}
|
||||
|
||||
pub fn to_u32(self) -> (u32, u32) {
|
||||
let x: u32 = self.x.try_into().expect("x too small");
|
||||
let y: u32 = self.y.try_into().expect("y too small");
|
||||
(x, y)
|
||||
}
|
||||
|
||||
/// The vector pointing from `self` to `other`.
|
||||
///
|
||||
/// ```
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
use crate::{Buffer, Color, Rect, Vec2};
|
||||
use image::RgbImage;
|
||||
use palette::Srgb;
|
||||
|
||||
// TODO Add Orientation (from inkfo)
|
||||
use crate::{Rect, Vec2};
|
||||
|
||||
pub struct View<'a, C> {
|
||||
pub struct View<'a> {
|
||||
area: Rect,
|
||||
buffer: &'a mut Buffer<C>,
|
||||
buffer: &'a mut RgbImage,
|
||||
}
|
||||
|
||||
impl<'a, C> View<'a, C> {
|
||||
pub fn new(buffer: &'a mut Buffer<C>) -> Self {
|
||||
Self {
|
||||
area: Rect::from_nw(Vec2::ZERO, buffer.size()),
|
||||
buffer,
|
||||
}
|
||||
impl<'a> View<'a> {
|
||||
pub fn new(buffer: &'a mut RgbImage) -> Self {
|
||||
let size = Vec2::from_u32(buffer.width(), buffer.height());
|
||||
let area = Rect::from_nw(Vec2::ZERO, size);
|
||||
Self { area, buffer }
|
||||
}
|
||||
|
||||
pub fn dup(&mut self) -> View<'_, C> {
|
||||
pub fn dup(&mut self) -> View<'_> {
|
||||
View {
|
||||
area: self.area,
|
||||
buffer: self.buffer,
|
||||
|
|
@ -35,15 +35,19 @@ impl<'a, C> View<'a, C> {
|
|||
pos + self.area.corner_nw()
|
||||
}
|
||||
|
||||
pub fn at(&self, pos: Vec2) -> Option<&C> {
|
||||
self.buffer.at(self.pos_to_buffer_pos(pos))
|
||||
pub fn get(&self, pos: Vec2) -> Option<Srgb> {
|
||||
let (x, y) = self.pos_to_buffer_pos(pos).to_u32();
|
||||
let pixel = self.buffer.get_pixel_checked(x, y)?;
|
||||
let [r, g, b] = pixel.0;
|
||||
let color = Srgb::new(r, g, b);
|
||||
Some(color.into_format())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Color> View<'_, C> {
|
||||
pub fn set(&mut self, pos: Vec2, color: C) {
|
||||
if let Some(pixel) = self.buffer.at_mut(self.pos_to_buffer_pos(pos)) {
|
||||
*pixel = color.over(*pixel);
|
||||
pub fn set(&mut self, pos: Vec2, color: Srgb) {
|
||||
let (x, y) = self.pos_to_buffer_pos(pos).to_u32();
|
||||
if let Some(pixel) = self.buffer.get_pixel_mut_checked(x, y) {
|
||||
let color = color.into_format::<u8>();
|
||||
pixel.0 = [color.red, color.green, color.blue];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,122 +0,0 @@
|
|||
use crate::{widgets::Background, Rect, Vec2, View};
|
||||
|
||||
pub trait Widget<C> {
|
||||
/// Size that the widget wants to be, given the width and height
|
||||
/// constraints.
|
||||
fn size(&self, max_width: Option<i32>, max_height: Option<i32>) -> Vec2;
|
||||
|
||||
/// Recalculate the area (size and position) of all inner widgets given the
|
||||
/// widget's own area.
|
||||
///
|
||||
/// # Implement if...
|
||||
///
|
||||
/// - There are inner widgets
|
||||
fn set_area(&mut self, _area: Rect) {}
|
||||
|
||||
/// Perform any updates (e.g. fetching map tiles) that require the widget's
|
||||
/// area and may fail.
|
||||
///
|
||||
/// # Implement if...
|
||||
///
|
||||
/// - There are inner widgets
|
||||
/// - This widget needs to perform updates
|
||||
fn update(&mut self, _area: Rect) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw(self, view: &mut View<'_, C>);
|
||||
}
|
||||
|
||||
/// Extension trait for [`Widget`]s.
|
||||
pub trait WidgetExt<C> {
|
||||
fn boxed(self) -> BoxedWidget<C>;
|
||||
}
|
||||
|
||||
impl<C, W> WidgetExt<C> for W
|
||||
where
|
||||
W: Widget<C> + 'static,
|
||||
{
|
||||
fn boxed(self) -> BoxedWidget<C> {
|
||||
BoxedWidget::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper trait around [`Widget`] that turns `Box<Self>` into a `Self` to get
|
||||
/// around the "size cannot be statically determined" error with the naïve
|
||||
/// approach of `Box<Widget>`.
|
||||
trait WidgetWrapper<C> {
|
||||
fn size(&self, max_width: Option<i32>, max_height: Option<i32>) -> Vec2;
|
||||
fn set_area(&mut self, _area: Rect);
|
||||
fn update(&mut self, _area: Rect) -> anyhow::Result<()>;
|
||||
fn draw(self: Box<Self>, view: &mut View<'_, C>);
|
||||
}
|
||||
|
||||
impl<C, W: Widget<C>> WidgetWrapper<C> for W {
|
||||
// These implementations explicitly use `Widget::*` to call the widget
|
||||
// methods even though they have priority over the `WidgetWrapper::*`
|
||||
// methods for some reason. Just a bit of rustc magic, I guess.
|
||||
|
||||
fn size(&self, max_width: Option<i32>, max_height: Option<i32>) -> Vec2 {
|
||||
Widget::size(self, max_width, max_height)
|
||||
}
|
||||
|
||||
fn set_area(&mut self, area: Rect) {
|
||||
// Widget::set_area(self, area);
|
||||
self.set_area(area);
|
||||
}
|
||||
|
||||
fn update(&mut self, area: Rect) -> anyhow::Result<()> {
|
||||
Widget::update(self, area)
|
||||
}
|
||||
|
||||
fn draw(self: Box<Self>, view: &mut View<'_, C>) {
|
||||
Widget::draw(*self, view);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BoxedWidget<C> {
|
||||
area: Rect,
|
||||
widget: Box<dyn WidgetWrapper<C>>,
|
||||
}
|
||||
|
||||
impl<C> BoxedWidget<C> {
|
||||
pub fn new<W>(widget: W) -> Self
|
||||
where
|
||||
W: Widget<C> + 'static,
|
||||
{
|
||||
Self {
|
||||
area: Rect::ZERO,
|
||||
widget: Box::new(widget),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn area(&self) -> Rect {
|
||||
self.area
|
||||
}
|
||||
|
||||
// Widget-like functions
|
||||
|
||||
pub fn size(&self, max_width: Option<i32>, max_height: Option<i32>) -> Vec2 {
|
||||
self.widget.size(max_width, max_height)
|
||||
}
|
||||
|
||||
pub fn set_area(&mut self, area: Rect) {
|
||||
self.area = area;
|
||||
self.widget.set_area(area);
|
||||
}
|
||||
|
||||
pub fn update(&mut self) -> anyhow::Result<()> {
|
||||
self.widget.update(self.area)
|
||||
}
|
||||
|
||||
pub fn draw(self, view: &mut View<'_, C>) {
|
||||
let mut view = view.dup().zoom(self.area);
|
||||
self.widget.draw(&mut view);
|
||||
}
|
||||
|
||||
// Widget constructors
|
||||
|
||||
pub fn background(self, color: C) -> Background<C> {
|
||||
Background::new(self, color)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
pub use background::*;
|
||||
pub use empty::*;
|
||||
|
||||
mod background;
|
||||
mod empty;
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
use crate::{BoxedWidget, Color, Vec2, Widget};
|
||||
|
||||
pub struct Background<C> {
|
||||
inner: BoxedWidget<C>,
|
||||
color: C,
|
||||
}
|
||||
|
||||
impl<C> Background<C> {
|
||||
pub fn new(inner: BoxedWidget<C>, color: C) -> Self {
|
||||
Self { inner, color }
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Color> Widget<C> for Background<C> {
|
||||
fn size(&self, max_width: Option<i32>, max_height: Option<i32>) -> crate::Vec2 {
|
||||
self.inner.size(max_width, max_height)
|
||||
}
|
||||
|
||||
fn set_area(&mut self, area: crate::Rect) {
|
||||
self.inner.set_area(area);
|
||||
}
|
||||
|
||||
fn update(&mut self, _area: crate::Rect) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw(self, view: &mut crate::View<'_, C>) {
|
||||
for y in 0..view.size().y {
|
||||
for x in 0..view.size().x {
|
||||
view.set(Vec2::new(x, y), self.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
use crate::{Vec2, View, Widget};
|
||||
|
||||
pub struct Empty {}
|
||||
|
||||
impl Empty {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Empty {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Widget<C> for Empty {
|
||||
fn size(&self, _max_width: Option<i32>, _max_height: Option<i32>) -> Vec2 {
|
||||
Vec2::ZERO
|
||||
}
|
||||
|
||||
fn draw(self, _view: &mut View<'_, C>) {}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue