Commit 186b1b29 authored by Florian Blasius's avatar Florian Blasius 🤘
Browse files

[wip | extend] rectangle shape creation, different builder structs.

parent ae9d8726
Pipeline #2497 passed with stages
in 5 minutes and 5 seconds
......@@ -7,10 +7,10 @@ impl Widget for MainView {
Template::default()
.as_parent_type(ParentType::Single)
.with_debug_name("MainView")
.with_child(
ImageWidget::create()
.with_property(Image::from_path("res/orbtk-space.png").unwrap()),
)
// .with_child(
// ImageWidget::create()
// .with_property(Image::from_path("res/orbtk-space.png").unwrap()),
// )
}
}
......
......@@ -15,7 +15,7 @@ use crate::{
event::EventQueue,
properties::{Bounds, Point},
theme::Theme,
core::{RenderContext2D, Shape2D},
core::orbrender::{RenderContext2D, Shape2D},
};
/// Is used to provides data from the `Backend` to the `RenderSystem`.
......@@ -97,8 +97,6 @@ pub trait FontMeasure {
pub use self::target::*;
pub use self::render_context_2d::*;
#[path = "orbital/mod.rs"]
mod target;
mod render_context_2d;
pub mod orbrender;
......@@ -8,8 +8,11 @@ use orbimage::Image;
use crate::{
core::{
Brush, FillRule, GradientStop, ImageElement, Position, RenderContext2D, Renderer, Size,
TextMetrics,
orbrender::{
Brush, FillRule, GradientStop, ImageElement, Position, RenderContext2D, Size,
TextMetrics,
},
Renderer,
},
properties::{Bounds, Point},
theme::{material_font_icons::MATERIAL_ICONS_REGULAR_FONT, ROBOTO_REGULAR_FONT},
......@@ -80,7 +83,7 @@ impl<'a> RenderContext2D for OrbRenderContext2D<'a> {
}
/// Adds a circular arc to the current sub-path, using the given control points and radius. The arc is automatically connected to the path's latest point with a straight line, if necessary for the specified parameters.
fn arc_to(&mut self, x: f64, y: f64) {
fn arc_to(&mut self, x1: f64, y1: f64, x2: f64, y2: f64, radius: f64) {
self.switch_context();
// todo: needs implementation
}
......
pub use self::shapes::*;
pub use self::structs::*;
pub use self::render_context_2d::*;
mod shapes;
mod structs;
mod render_context_2d;
// todos
//
// Step One Render Object refactoring
//
// *[*] Rename backend to core
// *[*] Add render context sub module to core (enums, traits, basis struct)
// *[*] Move Render Context related stuff to render context module
// *[*] Move render shapes to folders
// *[ ] Implement render shapes: Rectangle, Text, Image, Canvas
// *[ ] FontBuilder from RenderContext -> build from configuration, build frome file. Store fonts.
// *[ ] Canvas widgets needs something like instruction builder for the user
// *[ ] Think about how to implement shadows
// *[ ] Adjust Template to work with new render objects e.g. .with_render_shape(RectangleBuilder::new())
// *[ ] Store render shapes in ecs
// *[*] Implement RenderContext for OrbClient
// *[*] Cache images
// *[ ] Remove old Renderer trait
// *[ ] Adjust Render system to work with render context trait
// *[ ] Write tests for e.g. render shapes
// *[ ] Test everthing
// *[ ] Render cache, store for each shape a layer. Layer part of pixel buffer with shape. Store layer id in shape.
// *[ ] Dirty flag for shapes. Only dirty shape will redraw. All other will draw by its layers. Dirty after any change and at startup.
// *[ ] MR
//
// Step Two Expand Window
//
// *[ ] is_resizable
// *[ ] min_size
// *[ ] max_size
// *[ ] fix position on windows
// *[ ] Eventuelly refactor backend struct
// *[ ] Test everything
// *[ ] MR
//
// Step Three Web backend
//
// *[ ] implement web backend
// *[ ] Test everything
// *[ ] MR
//
// Step Four switch to OrbGL for OrbClient backend
//
// *[ ] convert more drawing functions to OrbGl in OrbClient backend
// *[ ] Test everything
// *[ ] MR
//
// Optional
//
// *[ ] Switch to winit for default client
pub use self::shapes::*;
pub use self::structs::*;
mod shapes;
mod structs;
use crate::{
core::orbrender::{ImageElement, Brush, TextMetrics, Shape2D},
};
/// The algorithm by which to determine if a point is inside or outside the filling region.
#[derive(PartialEq, Debug, Copy, Clone)]
......@@ -27,7 +25,7 @@ pub enum Instruction {
Arc(f64, f64, f64, f64, f64, bool),
/// Adds a circular arc to the current sub-path, using the given control points and radius. The arc is automatically connected to the path's latest point with a straight line, if necessary for the specified parameters.
ArcTo(f64, f64),
ArcTo(f64, f64, f64, f64, f64),
/// Starts a new path by emptying the list of sub-paths. Call this when you want to create a new path.
BeginPath(),
......@@ -122,7 +120,7 @@ pub trait RenderContext2D {
);
/// Adds a circular arc to the current sub-path, using the given control points and radius. The arc is automatically connected to the path's latest point with a straight line, if necessary for the specified parameters.
fn arc_to(&mut self, x: f64, y: f64);
fn arc_to(&mut self, x1: f64, y1: f64, x2: f64, y2: f64, radius: f64);
/// Starts a new path by emptying the list of sub-paths. Call this when you want to create a new path.
fn begin_path(&mut self);
......@@ -220,7 +218,7 @@ pub trait RenderContext2D {
Instruction::Arc(x, y, radius, start_angle, end_engle, anti_clockwise) => {
self.arc(*x, *y, *radius, *start_angle, *end_engle, *anti_clockwise)
}
Instruction::ArcTo(x, y) => self.arc_to(*x, *y),
Instruction::ArcTo(x1, y1, x2, y2, radius) => self.arc_to(*x1, *y1, *x2, *y2, *radius),
Instruction::BeginPath() => self.begin_path(),
Instruction::BezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) => self.bezier_curve_to(*cp1x, *cp1y, *cp2x, *cp2y, *x, *y),
Instruction::ClearRect(x, y, width, height) => self.clear_rect(*x, *y, *width, *height),
......
use super::Instruction;
pub use self::image::Image;
pub use self::rectangle::Rectangle;
pub use self::rectangle::{Rectangle, RectangleBuilder};
mod image;
mod rectangle;
......
use crate::core::orbrender::{
Border, Bordered, Brush, FillRule, Instruction, Position, Rect, Shape2D, Size, Thickness,
};
#[derive(Default)]
pub struct RectangleBuilder {
pub background: Brush,
pub rect: Rect,
pub border: Border,
pub radius: f64,
}
impl RectangleBuilder {
pub fn new() -> Self {
RectangleBuilder::default()
}
pub fn with_background(mut self, background: Brush) -> Self {
self.background = background;
self
}
pub fn with_rect(mut self, rect: Rect) -> Self {
self.rect = rect;
self
}
pub fn with_border(mut self, border: Border) -> Self {
self.border = border;
self
}
pub fn with_radius(mut self, radius: f64) -> Self {
self.radius = radius;
self
}
pub fn build(self) -> Rectangle {
let mut rect = Rectangle {
instructions: vec![],
rect: self.rect,
border: self.border,
radius: self.radius,
background: self.background,
};
rect.create_shape();
rect
}
}
pub struct Rectangle {
instructions: Vec<Instruction>,
rect: Rect,
border: Border,
radius: f64,
background: Brush,
}
impl Rectangle {
pub fn get_background(&self) -> &Brush {
&self.background
}
pub fn set_background(&mut self, background: Brush) {
self.background = background;
}
fn create_shape(&mut self) {
self.instructions.clear();
let has_thickness = self.border.thickness.left > 0.0
|| self.border.thickness.top > 0.0
|| self.border.thickness.right > 0.0
|| self.border.thickness.bottom > 0.0;
if self.radius > 0.0 {
if has_thickness {
self.create_rounded_bordered_rect_shape();
} else {
self.create_rounded_rect_shape(
self.rect.x,
self.rect.y,
self.rect.width,
self.rect.height,
self.radius,
self.background.clone(),
);
}
} else {
if has_thickness {
self.create_bordered_rect_shape();
} else {
self.create_rect_shape(
self.rect.x,
self.rect.y,
self.rect.width,
self.rect.height,
self.background.clone(),
);
}
}
}
fn create_rect_shape(&mut self, x: f64, y: f64, width: f64, height: f64, brush: Brush) {
self.instructions
.push(Instruction::SetFillStyleBrush(brush));
self.instructions
.push(Instruction::FillRect(x, y, width, height));
}
fn create_bordered_rect_shape(&mut self) {
// border
self.create_rect_shape(
self.rect.x,
self.rect.y,
self.rect.width,
self.rect.height,
self.border.brush.clone(),
);
// content
self.create_rect_shape(
self.rect.x,
self.rect.y,
self.rect.width,
self.rect.height,
self.border.brush.clone(),
);
self.instructions
.push(Instruction::SetFillStyleBrush(self.border.brush.clone()));
self.instructions.push(Instruction::FillRect(
self.rect.x,
self.rect.y,
self.rect.width,
self.rect.height,
));
self.instructions
.push(Instruction::SetFillStyleBrush(self.background.clone()));
self.instructions.push(Instruction::FillRect(
self.rect.x + self.border.thickness.left,
self.rect.y + self.border.thickness.top,
self.rect.width - self.border.thickness.left - self.border.thickness.right,
self.rect.height - self.border.thickness.top - self.border.thickness.right,
));
}
fn create_rounded_rect_shape(
&mut self,
x: f64,
y: f64,
width: f64,
height: f64,
radius: f64,
brush: Brush,
) {
let mut radius = radius;
if width < 2.0 * radius {
radius = width / 2.0;
}
if height < 2.0 * radius {
radius = height / 2.0;
}
self.instructions
.push(Instruction::SetFillStyleBrush(brush));
self.instructions.push(Instruction::BeginPath());
self.instructions.push(Instruction::MoveTo(x + radius, y));
self.instructions.push(Instruction::ArcTo(
x + width,
y,
x + width,
y + height,
radius,
));
self.instructions.push(Instruction::ArcTo(
x + width,
y + height,
x,
y + height,
radius,
));
self.instructions
.push(Instruction::ArcTo(x, y + height, x, y, radius));
self.instructions
.push(Instruction::ArcTo(x, y, x + width, y, radius));
self.instructions.push(Instruction::ClosePath());
self.instructions
.push(Instruction::Fill(FillRule::default()));
}
fn create_rounded_bordered_rect_shape(&mut self) {
// border
self.create_rounded_rect_shape(
self.rect.x,
self.rect.y,
self.rect.width,
self.rect.height,
self.radius,
self.border.brush.clone(),
);
// content
self.create_rounded_rect_shape(
self.rect.x + self.border.thickness.left,
self.rect.y + self.border.thickness.top,
self.rect.width - self.border.thickness.left - self.border.thickness.right,
self.rect.height - self.border.thickness.top - self.border.thickness.right,
self.radius,
self.background.clone(),
);
}
}
impl Shape2D for Rectangle {
fn instructions(&self) -> &[Instruction] {
&self.instructions
}
}
impl Size for Rectangle {
fn set_with(&mut self, width: f64) {
self.rect.width = width;
self.create_shape();
}
fn get_width(&self) -> f64 {
self.rect.height
}
fn set_height(&mut self, height: f64) {
self.rect.height = height;
self.create_shape();
}
fn get_height(&self) -> f64 {
self.rect.height
}
fn set_size(&mut self, width: f64, height: f64) {
self.rect.width = width;
self.rect.height = height;
self.create_shape();
}
fn get_size(&self) -> (f64, f64) {
(self.rect.width, self.rect.height)
}
}
impl Position for Rectangle {
fn set_x(&mut self, x: f64) {
self.rect.x = x;
self.create_shape();
}
fn get_x(&self) -> f64 {
self.rect.y
}
fn set_y(&mut self, y: f64) {
self.rect.y = y;
self.create_shape();
}
fn get_y(&self) -> f64 {
self.rect.y
}
fn set_position(&mut self, x: f64, y: f64) {
self.rect.x = x;
self.rect.y = y;
self.create_shape();
}
fn get_position(&self) -> (f64, f64) {
(self.rect.x, self.rect.y)
}
}
impl Bordered for Rectangle {
fn get_border_thickness(&self) -> &Thickness {
&self.border.thickness
}
fn set_border_thickness(&mut self, thickness: Thickness) {
self.border.thickness = thickness;
self.create_shape();
}
fn get_border_brush(&self) -> &Brush {
&self.border.brush
}
fn set_border_brush(&mut self, brush: Brush) {
self.border.brush = brush;
self.create_shape();
}
fn get_border(&self) -> &Border {
&self.border
}
fn set_border(&mut self, border: Border) {
self.border = border;
self.create_shape();
}
}
use super::{Brush, Thickness};
pub trait Bordered {
fn get_border_thickness(&self) -> &Thickness;
fn set_border_thickness(&mut self, thickness: Thickness);
fn get_border_brush(&self) -> &Brush;
fn set_border_brush(&mut self, brush: Brush);
fn get_border(&self) -> &Border;
fn set_border(&mut self, border: Border);
}
#[derive(Default)]
pub struct BorderBuilder {
brush: Brush,
thickness: Thickness,
}
impl BorderBuilder {
pub fn new() -> BorderBuilder {
BorderBuilder::default()
}
pub fn with_brush(mut self, brush: Brush) -> Self {
self.brush = brush;
self
}
pub fn with_thickness(mut self, thickness: Thickness) -> Self {
self.thickness = thickness;
self
}
pub fn build(self) -> Border {
Border {
brush: self.brush,
thickness: self.thickness,
}
}
}
#[derive(Clone, Default, Debug, PartialEq)]
pub struct Border {
pub brush: Brush,
pub thickness: Thickness,
}
impl Border {
pub fn new(brush: Brush, thickness: Thickness) -> Self {
Border {
brush,
thickness,
}
}
}
......@@ -4,6 +4,8 @@ pub struct GradientStop {
pub color: String,
}
// todo: Color struct
// impl Default for Gradient {
// fn default() -> Self {
// Gradient {
......
use crate::core::{Position, Rect, Size};
use crate::core::orbrender::{Position, Rect, Size};
// todo: ImageElement and Font as Trait. Implementation by backend wrapping native images and fonts.
pub struct ImageElementBuilder {
pub path: String,
......@@ -27,7 +29,7 @@ impl ImageElementBuilder {
self
}
pub fn with_rect(mut self, x: f64, y: f64, width: f64, height: f64) -> Self {
pub fn with_rect(self, x: f64, y: f64, width: f64, height: f64) -> Self {
self.with_position(x, y).with_size(width, height)
}
......@@ -109,7 +111,7 @@ impl Size for ImageElement {
}
impl Position for ImageElement {
fn set_with(&mut self, x: f64) {
fn set_x(&mut self, x: f64) {
self.rect.x = x;
}
......
pub use self::border::Border;
pub use self::border::{Border, BorderBuilder, Bordered};
pub use self::brush::{Brush, GradientStop};
pub use self::font::TextMetrics;
pub use self::image_element::{ImageElement, ImageElementBuilder};
pub use self::rect::{Rect, Size, Position};
pub use self::rect::{Position, Rect, Size};
pub use self::thickness::Thickness;
mod border;
......@@ -10,4 +10,4 @@ mod brush;
mod font;
mod image_element;
mod rect;
mod thickness;
\ No newline at end of file
mod thickness;
......@@ -6,6 +6,17 @@ pub struct Rect {
pub height: f64,
}
impl Rect {
pub fn new(x: f64, y: f64, width: f64, height: f64) -> Self {
Rect {
x,
y,
width,
height,
}
}
}
pub trait Size {