Split into more modules and add bounds checking to widgets
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
use raqote::{DrawTarget, PathBuilder, Source, StrokeStyle, DrawOptions, SolidSource, Color};
|
||||
use raqote::{DrawTarget, Source, DrawOptions, Color};
|
||||
|
||||
struct UIColor {
|
||||
use crate::wayland::{
|
||||
draw::{Draw, DrawArea},
|
||||
Position,
|
||||
};
|
||||
|
||||
pub(super) struct UIColors {
|
||||
draw_area: Color,
|
||||
button: Color,
|
||||
button_accent: Color,
|
||||
@@ -10,7 +15,7 @@ struct UIColor {
|
||||
_text: Color,
|
||||
}
|
||||
|
||||
impl Default for UIColor {
|
||||
impl Default for UIColors {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
draw_area: Color::new(0xff, 0x15, 0x15, 0x15),
|
||||
@@ -24,139 +29,63 @@ impl Default for UIColor {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct Window<'a, 'b> {
|
||||
pub(super) draw: DrawTarget<&'a mut [u32]>,
|
||||
pub(super) options: DrawOptions,
|
||||
pub(super) colors: UIColor,
|
||||
pub(super) widgets: Vec<Widget<'b>>,
|
||||
pub(super) width: usize,
|
||||
pub(super) height: usize,
|
||||
|
||||
pub(super) enum Widget {
|
||||
Button(Button),
|
||||
TextArea(TextArea)
|
||||
}
|
||||
|
||||
impl<'a, 'b> Window<'a, 'b> {
|
||||
pub(super) fn new(buffer: &'a mut [u32], width: usize, height: usize) -> Self {
|
||||
pub(super) struct Window {
|
||||
pub(super) width: usize,
|
||||
pub(super) height: usize,
|
||||
pub(super) widgets: Vec<Widget>,
|
||||
pub(super) draw_area: DrawArea,
|
||||
pub(super) colors: UIColors,
|
||||
pub(super) options: DrawOptions,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub(super) fn new(width: usize, height: usize, draw_area: DrawArea) -> Self {
|
||||
Self {
|
||||
draw: DrawTarget::from_backing(width.try_into().unwrap(), height.try_into().unwrap(), buffer),
|
||||
options: DrawOptions::new(),
|
||||
colors: UIColor::default(),
|
||||
widgets: Vec::new(),
|
||||
width,
|
||||
height,
|
||||
widgets: Vec::new(),
|
||||
draw_area,
|
||||
colors: UIColors::default(),
|
||||
options: DrawOptions::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn add_widget(&mut self, widget: Widget<'b>) {
|
||||
pub(super) fn add_widget(&mut self, widget: Widget) {
|
||||
self.widgets.push(widget);
|
||||
}
|
||||
|
||||
pub(super) fn render(&mut self) {
|
||||
pub(super) fn render(&self, canvas: &mut [u8]) {
|
||||
|
||||
self.draw.fill_rect(
|
||||
// TODO: Make sure this is actually safe
|
||||
let draw_buffer: &mut [u32] = unsafe {canvas.align_to_mut::<u32>().1};
|
||||
|
||||
let mut target = DrawTarget::from_backing(self.width as i32, self.height as i32, draw_buffer);
|
||||
|
||||
target.fill_rect(
|
||||
0.0,
|
||||
0.0,
|
||||
self.width as f32,
|
||||
self.height as f32,
|
||||
&Source::Solid(self.colors.background.into()),
|
||||
&self.options
|
||||
);
|
||||
);
|
||||
|
||||
self.draw_area.render(&mut target);
|
||||
|
||||
for widget in self.widgets.iter() {
|
||||
match widget {
|
||||
Widget::Button(button) => button.render(&mut self.draw),
|
||||
Widget::DrawArea(draw_area) => draw_area.render(&mut self.draw),
|
||||
Widget::TextArea(text_area) => text_area.render(&mut self.draw),
|
||||
Widget::Button(button) => button.render(&mut target),
|
||||
Widget::TextArea(text_area) => text_area.render(&mut target),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(super) struct Position {
|
||||
pub(super) x: f32,
|
||||
pub(super) y: f32,
|
||||
}
|
||||
|
||||
pub(super) struct Path(Vec<Position>);
|
||||
|
||||
impl IntoIterator for Path {
|
||||
type Item = Position;
|
||||
type IntoIter = std::vec::IntoIter<Position>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Path {
|
||||
type Item = &'a Position;
|
||||
type IntoIter = std::slice::Iter<'a, Position>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct DrawPath {
|
||||
pub(super) paths: Vec<Path>,
|
||||
pub(super) newline: bool,
|
||||
}
|
||||
|
||||
impl DrawPath {
|
||||
pub(super) fn new() -> Self {
|
||||
Self {
|
||||
paths: Vec::new(),
|
||||
newline: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn add_point(&mut self, point: Position, newline: bool) {
|
||||
if newline {
|
||||
self.paths.push(Path(vec![point]))
|
||||
} else if self.paths.len() > 0 {
|
||||
self.paths
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.0
|
||||
.push(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for DrawPath {
|
||||
type Item = Path;
|
||||
type IntoIter = std::vec::IntoIter<Path>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.paths.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a DrawPath {
|
||||
type Item = &'a Path;
|
||||
type IntoIter = std::slice::Iter<'a, Path>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.paths.iter()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Position {
|
||||
pub(super) fn new(x: f32, y: f32) -> Self {
|
||||
Position { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) enum Widget<'a> {
|
||||
Button(Button),
|
||||
DrawArea(DrawArea<'a>),
|
||||
TextArea(TextArea)
|
||||
}
|
||||
|
||||
pub(super) trait Draw {
|
||||
fn render(&self, area: &mut DrawTarget<&mut [u32]>);
|
||||
}
|
||||
|
||||
pub(super) struct ButtonColors {
|
||||
background: Color,
|
||||
@@ -178,6 +107,7 @@ pub(super) struct Button {
|
||||
pub(super) width: f32,
|
||||
pub(super) height: f32,
|
||||
pub(super) position: Position,
|
||||
pub(super) shape: raqote::Path,
|
||||
pub(super) colors: ButtonColors,
|
||||
pub(super) options: DrawOptions,
|
||||
}
|
||||
@@ -186,208 +116,24 @@ impl Button {
|
||||
fn background_source(&self) -> Source {
|
||||
Source::Solid(self.colors.background.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct ButtonBuilder {
|
||||
pub(super) width: Option<f32>,
|
||||
pub(super) height: Option<f32>,
|
||||
pub(super) position: Option<Position>,
|
||||
pub(super) colors: Option<ButtonColors>,
|
||||
pub(super) options: Option<DrawOptions>,
|
||||
}
|
||||
|
||||
impl<'a> ButtonBuilder {
|
||||
pub(super) fn new() -> Self {
|
||||
Self {
|
||||
width: None,
|
||||
height: None,
|
||||
position: None,
|
||||
colors: None,
|
||||
options: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn width(&mut self, width: f32) {
|
||||
self.width = Some(width)
|
||||
}
|
||||
|
||||
pub(super) fn height(&mut self, height: f32) {
|
||||
self.height = Some(height)
|
||||
}
|
||||
|
||||
pub(super) fn position(&mut self, x: f32, y: f32) {
|
||||
self.position = Some(Position::new(x, y))
|
||||
}
|
||||
|
||||
pub(super) fn colors(&mut self, colors: ButtonColors) {
|
||||
self.colors = Some(colors)
|
||||
}
|
||||
|
||||
pub(super) fn finish(self) -> Widget<'a> {
|
||||
Widget::Button(
|
||||
Button {
|
||||
width: self.width.expect("Button must have width"),
|
||||
height: self.height.expect("Button must have height"),
|
||||
position: self.position.expect("Button must have a position"),
|
||||
colors: self.colors.unwrap_or_default(),
|
||||
options: self.options.unwrap_or(DrawOptions::new()),
|
||||
}
|
||||
)
|
||||
pub(crate) fn contains_point(&self, point: Position) -> bool {
|
||||
self.shape.contains_point(0.1, point.x, point.y)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Draw for Button {
|
||||
fn render(&self, area: &mut DrawTarget<&mut [u32]>) {
|
||||
|
||||
(*area).fill_rect(
|
||||
self.position.x,
|
||||
self.position.y,
|
||||
self.width,
|
||||
self.height,
|
||||
&self.background_source(),
|
||||
&self.options,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct DrawAreaColors {
|
||||
background_color: Color,
|
||||
drawing_color: Color,
|
||||
border_color: Color,
|
||||
}
|
||||
|
||||
impl Default for DrawAreaColors {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
background_color: Color::new(0xff, 0x4a, 0x4e, 0x64),
|
||||
drawing_color: Color::new(0xff, 0xea, 0xea, 0xe6),
|
||||
border_color: Color::new(0xff, 0x77, 0x7f, 0x97),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct DrawArea<'a> {
|
||||
pub(super) width: f32,
|
||||
pub(super) height: f32,
|
||||
pub(super) position: Position,
|
||||
pub(super) path: &'a DrawPath,
|
||||
pub(super) colors: DrawAreaColors,
|
||||
pub(super) options: DrawOptions,
|
||||
}
|
||||
|
||||
impl DrawArea<'_> {
|
||||
fn background_source(&self) -> Source {
|
||||
Source::Solid(self.colors.background_color.into())
|
||||
}
|
||||
|
||||
fn drawing_source(&self) -> Source {
|
||||
Source::Solid(self.colors.drawing_color.into())
|
||||
}
|
||||
|
||||
fn border_source(&self) -> Source {
|
||||
Source::Solid(self.colors.border_color.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub(super) struct DrawAreaBuilder<'a> {
|
||||
pub(super) width: Option<f32>,
|
||||
pub(super) height: Option<f32>,
|
||||
pub(super) position: Option<Position>,
|
||||
pub(super) path: Option<&'a DrawPath>,
|
||||
pub(super) colors: Option<DrawAreaColors>,
|
||||
pub(super) options: Option<DrawOptions>,
|
||||
}
|
||||
|
||||
impl<'a> DrawAreaBuilder<'a> {
|
||||
pub(super) fn new() -> Self {
|
||||
Self {
|
||||
width: None,
|
||||
height: None,
|
||||
position: None,
|
||||
path: None,
|
||||
colors: None,
|
||||
options: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn width(&mut self, width: f32) {
|
||||
self.width = Some(width)
|
||||
}
|
||||
|
||||
pub(super) fn height(&mut self, height: f32) {
|
||||
self.height = Some(height)
|
||||
}
|
||||
|
||||
pub(super) fn position(&mut self, x: f32, y: f32) {
|
||||
self.position = Some(Position::new(x, y))
|
||||
}
|
||||
|
||||
pub(super) fn colors(&mut self, colors: DrawAreaColors) {
|
||||
self.colors = Some(colors)
|
||||
}
|
||||
|
||||
pub(super) fn path(&mut self, path: &'a DrawPath) {
|
||||
self.path = Some(path);
|
||||
}
|
||||
|
||||
pub(super) fn finish(self) -> Widget<'a> {
|
||||
Widget::DrawArea(
|
||||
DrawArea {
|
||||
width: self.width.expect("Draw area must have width"),
|
||||
height: self.height.expect("Draw area must have height"),
|
||||
position: self.position.expect("Draw area must have a position"),
|
||||
path: self.path.expect("Draw must have a path"),
|
||||
colors: self.colors.unwrap_or_default(),
|
||||
options: self.options.unwrap_or(DrawOptions::new()),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Draw for DrawArea<'_> {
|
||||
fn render(&self, area: &mut DrawTarget<&mut [u32]>) {
|
||||
let mut draw_path = PathBuilder::new();
|
||||
|
||||
for path in self.path.into_iter() {
|
||||
if path.0.len() > 1 {
|
||||
let mut path = path.into_iter();
|
||||
|
||||
let first_point = path
|
||||
.by_ref()
|
||||
.next();
|
||||
|
||||
draw_path.move_to(first_point.unwrap().x, first_point.unwrap().y);
|
||||
|
||||
for point in path {
|
||||
draw_path.line_to(point.x, point.y);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
let path = draw_path.finish();
|
||||
area.fill_rect(
|
||||
self.position.x,
|
||||
self.position.y,
|
||||
self.width,
|
||||
self.height,
|
||||
area.fill(
|
||||
&self.shape,
|
||||
&self.background_source(),
|
||||
&self.options,
|
||||
);
|
||||
|
||||
area.stroke(
|
||||
&path,
|
||||
&self.drawing_source(),
|
||||
&StrokeStyle {width: 3.0, ..StrokeStyle::default()},
|
||||
&self.options,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub(super) struct TextAreaColors {
|
||||
pub(super) background: Color,
|
||||
pub(super) text: Color,
|
||||
@@ -420,60 +166,9 @@ impl TextArea {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct TextAreaBuilder {
|
||||
pub(super) width: Option<f32>,
|
||||
pub(super) height: Option<f32>,
|
||||
pub(super) position: Option<Position>,
|
||||
pub(super) colors: Option<TextAreaColors>,
|
||||
pub(super) options: Option<DrawOptions>,
|
||||
}
|
||||
|
||||
impl<'a> TextAreaBuilder {
|
||||
pub(super) fn new() -> Self {
|
||||
Self {
|
||||
width: None,
|
||||
height: None,
|
||||
position: None,
|
||||
colors: None,
|
||||
options: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn width(&mut self, width: f32) {
|
||||
self.width = Some(width)
|
||||
}
|
||||
|
||||
pub(super) fn height(&mut self, height: f32) {
|
||||
self.height = Some(height)
|
||||
}
|
||||
|
||||
pub(super) fn position(&mut self, x: f32, y: f32) {
|
||||
self.position = Some(Position::new(x, y))
|
||||
}
|
||||
|
||||
|
||||
pub(super) fn colors(&mut self, colors: TextAreaColors) {
|
||||
self.colors = Some(colors);
|
||||
}
|
||||
|
||||
pub(super) fn finish(self) -> Widget<'a> {
|
||||
Widget::TextArea(
|
||||
TextArea {
|
||||
width: self.width.expect("Button must have width"),
|
||||
height: self.height.expect("Button must have height"),
|
||||
position: self.position.expect("Button must have a position"),
|
||||
colors: self.colors.unwrap_or_default(),
|
||||
options: self.options.unwrap_or(DrawOptions::new()),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Draw for TextArea {
|
||||
fn render(&self, area: &mut DrawTarget<&mut [u32]>) {
|
||||
|
||||
|
||||
|
||||
area.fill_rect(
|
||||
self.position.x,
|
||||
self.position.y,
|
||||
@@ -482,7 +177,6 @@ impl Draw for TextArea {
|
||||
&self.background_source(),
|
||||
&self.options,
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user