mirror of
https://github.com/Astatin3/IntroToWebAuthoring.git
synced 2026-06-09 00:28:00 -06:00
Start work on views system
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
@@ -0,0 +1,3 @@
|
||||
mod test_activity;
|
||||
|
||||
pub use test_activity::TestActivity;
|
||||
@@ -0,0 +1,166 @@
|
||||
use crate::{
|
||||
app::{Activity, App, AppState},
|
||||
log,
|
||||
render::Renderer,
|
||||
};
|
||||
|
||||
pub struct TestActivity {
|
||||
elements: Vec<Element>,
|
||||
}
|
||||
|
||||
impl TestActivity {
|
||||
pub fn top_element_at_point(&mut self, x: f32, y: f32) -> Option<(&mut Element, usize)> {
|
||||
for (i, element) in self.elements.iter_mut().enumerate().rev() {
|
||||
if x >= element.x
|
||||
&& x <= element.x + element.width
|
||||
&& y >= element.y
|
||||
&& y <= element.y + element.height
|
||||
{
|
||||
return Some((element, i));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn bottom_element_at_point(
|
||||
&mut self,
|
||||
x: f32,
|
||||
y: f32,
|
||||
min_index: usize,
|
||||
) -> Option<(&mut Element, usize)> {
|
||||
for (i, element) in self.elements.iter_mut().enumerate().skip(min_index) {
|
||||
if x >= element.x
|
||||
&& x <= element.x + element.width
|
||||
&& y >= element.y
|
||||
&& y <= element.y + element.height
|
||||
{
|
||||
return Some((element, i));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn mark_for_redraw(&mut self, current_elements: &mut Vec<usize>, element_index: usize) {
|
||||
current_elements.push(element_index);
|
||||
|
||||
let element = &self.elements[element_index];
|
||||
|
||||
for (x, y) in vec![
|
||||
(element.x.clone(), element.y.clone()),
|
||||
(element.x.clone() + element.width, element.y.clone()),
|
||||
(element.x.clone(), element.y.clone() + element.height),
|
||||
(
|
||||
element.x.clone() + element.width,
|
||||
element.y.clone() + element.height,
|
||||
),
|
||||
] {
|
||||
let option = self.bottom_element_at_point(x, y, element_index + 1);
|
||||
if let Some((element, index)) = option {
|
||||
if element_index == index || current_elements.contains(&index) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.mark_for_redraw(current_elements, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Activity for TestActivity {
|
||||
fn new() -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self {
|
||||
elements: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, dt: f32) {
|
||||
// todo!()
|
||||
}
|
||||
|
||||
fn draw(&self, renderer: &mut Renderer, state: &AppState) {
|
||||
renderer.img.randomize(&mut renderer.rand);
|
||||
let (cx, cy) = (renderer.actual_width / 2, renderer.actual_height / 2);
|
||||
renderer.circle(cx as i32, cy as i32, 100, (127, 127, 127));
|
||||
|
||||
// renderer.rect_center_xywh(cx as i32, cy as i32, 200, 200, (155, 127, 155));
|
||||
// renderer.rect_xyxy(10, 10, cx as i32, cy as i32, (255, 127, 155));
|
||||
|
||||
for element in &self.elements {
|
||||
element.draw(renderer);
|
||||
}
|
||||
|
||||
renderer.update();
|
||||
}
|
||||
|
||||
fn l_click(&mut self, renderer: &mut Renderer, state: &AppState) {
|
||||
// log!("Mouse moved");
|
||||
let option = self.top_element_at_point(state.mouse_x, state.mouse_y);
|
||||
if let Some((_, index)) = option {
|
||||
let mut for_redraw: Vec<usize> = Vec::new();
|
||||
self.mark_for_redraw(&mut for_redraw, index);
|
||||
|
||||
for_redraw.sort();
|
||||
|
||||
for n in for_redraw.iter_mut() {
|
||||
let element = &mut self.elements[*n];
|
||||
element.color = (
|
||||
renderer.rand.next_u32() as u8,
|
||||
renderer.rand.next_u32() as u8,
|
||||
renderer.rand.next_u32() as u8,
|
||||
);
|
||||
element.draw(renderer);
|
||||
}
|
||||
renderer.update();
|
||||
} else {
|
||||
self.elements.push(Element::new(
|
||||
state.mouse_x,
|
||||
state.mouse_y,
|
||||
50.0,
|
||||
50.0,
|
||||
(
|
||||
renderer.rand.next_u32() as u8,
|
||||
renderer.rand.next_u32() as u8,
|
||||
renderer.rand.next_u32() as u8,
|
||||
),
|
||||
));
|
||||
|
||||
self.draw(renderer, state);
|
||||
renderer.update();
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_move(&mut self, renderer: &mut Renderer, state: &AppState) {}
|
||||
}
|
||||
|
||||
pub struct Element {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
pub color: (u8, u8, u8),
|
||||
}
|
||||
|
||||
impl Element {
|
||||
pub fn new(x: f32, y: f32, width: f32, height: f32, color: (u8, u8, u8)) -> Self {
|
||||
Self {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
color,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self, renderer: &mut Renderer) {
|
||||
renderer.rect_xywh(
|
||||
self.x as i32,
|
||||
self.y as i32,
|
||||
self.width as i32,
|
||||
self.height as i32,
|
||||
self.color,
|
||||
);
|
||||
}
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
use crate::render::Renderer;
|
||||
|
||||
pub trait Activity {
|
||||
fn new() -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
fn update(&mut self, dt: f32);
|
||||
fn draw(&self, ctx: &mut Renderer);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct App {
|
||||
pub(crate) activities: Vec<Box<dyn Activity>>,
|
||||
pub(crate) renderer: Renderer,
|
||||
pub(crate) current_activity: Option<usize>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(renderer: Renderer) -> Self {
|
||||
App {
|
||||
activities: Vec::new(),
|
||||
renderer,
|
||||
current_activity: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&mut self) {
|
||||
// if let Some(current_activity) = self.current_activity {
|
||||
// self.activities[current_activity].draw(&mut self.renderer);
|
||||
// }
|
||||
self.renderer.img.randomize(&mut self.renderer.rand);
|
||||
|
||||
let (cx, cy) = (
|
||||
self.renderer.actual_width / 2,
|
||||
self.renderer.actual_height / 2,
|
||||
);
|
||||
|
||||
self.renderer
|
||||
.circle(cx as i32, cy as i32, 200, (255, 255, 255));
|
||||
|
||||
self.renderer.update();
|
||||
}
|
||||
}
|
||||
|
||||
// App events
|
||||
impl App {
|
||||
pub fn resize(&mut self, width: u32, height: u32) {
|
||||
self.renderer.resize(width, height);
|
||||
self.draw();
|
||||
}
|
||||
|
||||
pub fn click(&mut self, x: f32, y: f32) {
|
||||
// self.renderer.click(x, y);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
#[wasm_bindgen(module = "/js/index.js")]
|
||||
extern "C" {
|
||||
fn cursor(name: &str);
|
||||
}
|
||||
|
||||
pub fn set_cursor(c: Cursor) {
|
||||
cursor(c.value());
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub enum Cursor {
|
||||
Alias,
|
||||
AllScroll,
|
||||
Auto,
|
||||
Cell,
|
||||
ColResize,
|
||||
ContextMenu,
|
||||
Copy,
|
||||
Crosshair,
|
||||
Default,
|
||||
EResize,
|
||||
EWResize,
|
||||
Grab,
|
||||
Grabbing,
|
||||
Help,
|
||||
Move,
|
||||
NResize,
|
||||
NEResize,
|
||||
NESWResize,
|
||||
NSResize,
|
||||
NWResize,
|
||||
NWSEResize,
|
||||
NoDrop,
|
||||
None,
|
||||
NotAllowed,
|
||||
Pointer,
|
||||
Progress,
|
||||
RowResize,
|
||||
SResize,
|
||||
SEResize,
|
||||
SWResize,
|
||||
Text,
|
||||
WResize,
|
||||
Wait,
|
||||
ZoomIn,
|
||||
ZoomOut,
|
||||
}
|
||||
|
||||
impl Cursor {
|
||||
fn value(&self) -> &str {
|
||||
match self {
|
||||
Cursor::Alias => "alias",
|
||||
Cursor::AllScroll => "all-scroll",
|
||||
Cursor::Auto => "auto",
|
||||
Cursor::Cell => "cell",
|
||||
Cursor::ColResize => "col-resize",
|
||||
Cursor::ContextMenu => "context-menu",
|
||||
Cursor::Copy => "copy",
|
||||
Cursor::Crosshair => "crosshair",
|
||||
Cursor::Default => "default",
|
||||
Cursor::EResize => "e-resize",
|
||||
Cursor::EWResize => "ew-resize",
|
||||
Cursor::Grab => "grab",
|
||||
Cursor::Grabbing => "grabbing",
|
||||
Cursor::Help => "help",
|
||||
Cursor::Move => "move",
|
||||
Cursor::NResize => "n-resize",
|
||||
Cursor::NEResize => "ne-resize",
|
||||
Cursor::NESWResize => "nesw-resize",
|
||||
Cursor::NSResize => "ns-resize",
|
||||
Cursor::NWResize => "nw-resize",
|
||||
Cursor::NWSEResize => "nwse-resize",
|
||||
Cursor::NoDrop => "no-drop",
|
||||
Cursor::None => "none",
|
||||
Cursor::NotAllowed => "not-allowed",
|
||||
Cursor::Pointer => "pointer",
|
||||
Cursor::Progress => "progress",
|
||||
Cursor::RowResize => "row-resize",
|
||||
Cursor::SResize => "s-resize",
|
||||
Cursor::SEResize => "se-resize",
|
||||
Cursor::SWResize => "sw-resize",
|
||||
Cursor::Text => "text",
|
||||
Cursor::WResize => "w-resize",
|
||||
Cursor::Wait => "wait",
|
||||
Cursor::ZoomIn => "zoom-in",
|
||||
Cursor::ZoomOut => "zoom-out",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
mod cursors;
|
||||
|
||||
pub use cursors::{Cursor, set_cursor};
|
||||
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
use crate::{
|
||||
log,
|
||||
render::Renderer,
|
||||
views::{View, default_view},
|
||||
};
|
||||
|
||||
pub trait Activity {
|
||||
fn new() -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
fn update(&mut self, dt: f32);
|
||||
fn draw(&self, renderer: &mut Renderer, state: &AppState);
|
||||
fn l_click(&mut self, renderer: &mut Renderer, state: &AppState);
|
||||
fn mouse_move(&mut self, renderer: &mut Renderer, state: &AppState);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct App {
|
||||
// pub(crate) activities: Vec<Box<dyn Activity>>,
|
||||
pub(crate) root_view: Option<Box<dyn View>>,
|
||||
pub(crate) renderer: Renderer,
|
||||
// pub(crate) current_activity: Option<usize>,
|
||||
pub(crate) state: AppState,
|
||||
}
|
||||
|
||||
pub struct AppState {
|
||||
pub(crate) mouse_x: f32,
|
||||
pub(crate) mouse_y: f32,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new() -> Self {
|
||||
AppState {
|
||||
mouse_x: 0.0,
|
||||
mouse_y: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(renderer: Renderer) -> Self {
|
||||
App {
|
||||
root_view: Some(default_view()),
|
||||
renderer,
|
||||
// current_activity: Some(0),
|
||||
state: AppState::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&mut self) {
|
||||
if let Some(view) = &self.root_view {
|
||||
let (width, height) = (
|
||||
self.renderer.actual_width.clone() as f32,
|
||||
self.renderer.actual_height.clone() as f32,
|
||||
);
|
||||
|
||||
view.draw(&mut self.renderer, 0., 0., width, height);
|
||||
self.renderer.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// App events
|
||||
impl App {
|
||||
pub fn resize(&mut self, width: u32, height: u32) {
|
||||
self.renderer.resize(width, height);
|
||||
self.draw();
|
||||
}
|
||||
|
||||
pub fn mouse_move(&mut self, x: f32, y: f32) {
|
||||
self.state.mouse_x = x;
|
||||
self.state.mouse_y = y;
|
||||
|
||||
// if let Some(current_activity) = self.current_activity {
|
||||
// self.activities[current_activity].mouse_move(&mut self.renderer, &self.state);
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn l_click(&mut self, x: f32, y: f32) {
|
||||
self.state.mouse_x = x;
|
||||
self.state.mouse_y = y;
|
||||
|
||||
// if let Some(current_activity) = self.current_activity {
|
||||
// self.activities[current_activity].l_click(&mut self.renderer, &self.state);
|
||||
// }
|
||||
}
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
use crate::render::Renderer;
|
||||
|
||||
pub fn draw(ctx: &mut Renderer) {
|
||||
// // Draw the background
|
||||
// ctx.background(0.0, 0.0, 0.0);
|
||||
|
||||
// // Draw the foreground
|
||||
// ctx.fill(1.0, 1.0, 1.0);
|
||||
// ctx.rect(50.0, 50.0, 100.0, 100.0);
|
||||
//
|
||||
|
||||
let (cx, cy) = (ctx.actual_width / 2, ctx.actual_height / 2);
|
||||
|
||||
ctx.circle(cx as i32, cy as i32, 200, (255, 255, 255));
|
||||
}
|
||||
+14
-12
@@ -1,5 +1,7 @@
|
||||
// mod activities;
|
||||
mod app;
|
||||
mod render;
|
||||
mod views;
|
||||
|
||||
use wasm_bindgen::{Clamped, prelude::*};
|
||||
use web_sys::ImageData;
|
||||
@@ -8,7 +10,10 @@ use web_sys::ImageData;
|
||||
use render::buffer::ImgBuffer;
|
||||
use render::rand::Rnd;
|
||||
|
||||
use crate::{app::App, render::Renderer};
|
||||
use crate::{
|
||||
app::{App, Cursor, set_cursor},
|
||||
render::Renderer,
|
||||
};
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
@@ -28,6 +33,7 @@ pub fn init(canvas: &web_sys::HtmlCanvasElement, width: u32, height: u32) -> App
|
||||
canvas.set_height(cheight);
|
||||
|
||||
log!("WASM Successfully initialized!");
|
||||
// set_cursor(Cursor::Auto);
|
||||
|
||||
let mut renderer = Renderer {
|
||||
ctx: canvas
|
||||
@@ -65,17 +71,13 @@ pub fn draw(app: &mut App) {
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn click(app: &mut App, x: i32, y: i32) {
|
||||
pub fn click(app: &mut App, x: f32, y: f32) {
|
||||
// ctx.img.randomize(&mut ctx.rand);
|
||||
// draw::draw(ctx);
|
||||
|
||||
app.renderer.circle(x, y, 20, (255, 255, 255));
|
||||
|
||||
let data = ImageData::new_with_u8_clamped_array_and_sh(
|
||||
Clamped(&app.renderer.img.data),
|
||||
app.renderer.img.width(),
|
||||
app.renderer.img.height(),
|
||||
)
|
||||
.unwrap();
|
||||
app.renderer.ctx.put_image_data(&data, 0., 0.).unwrap();
|
||||
app.l_click(x, y);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn mouse_move(app: &mut App, x: f32, y: f32) {
|
||||
app.mouse_move(x, y);
|
||||
}
|
||||
|
||||
+19
-1
@@ -4,7 +4,7 @@ mod renderer;
|
||||
|
||||
pub use renderer::Renderer;
|
||||
|
||||
pub const RESOLUTION: u32 = 800;
|
||||
pub const RESOLUTION: u32 = 1200;
|
||||
|
||||
pub fn calc_resolution(width: u32, height: u32) -> (u32, u32, f32, f32) {
|
||||
let aspect = width as f32 / height as f32;
|
||||
@@ -15,3 +15,21 @@ pub fn calc_resolution(width: u32, height: u32) -> (u32, u32, f32, f32) {
|
||||
let new_height = (distortion_y * RESOLUTION as f32).round() as u32;
|
||||
(new_width, new_height, distortion_x, distortion_y)
|
||||
}
|
||||
|
||||
// aspect = width / height
|
||||
// dist_x = aspect
|
||||
// dist_y = height / width
|
||||
//
|
||||
// cw = RES * width / height
|
||||
// ch = RES * height / width
|
||||
//
|
||||
// ux = x * (width / cw)
|
||||
// ux = x * (width / (RES * width / height))
|
||||
// ux = x * (height / RES)
|
||||
//
|
||||
// uy = y * (width / RES)
|
||||
//
|
||||
// e = (cw * ch) / (width * height)
|
||||
// e = ((RES * (width / height)) * RES * height / width) / (width * height)
|
||||
// e = ((RES * (1 / height)) * RES * (1 / width)
|
||||
// e = RES^2 / (width * height)
|
||||
|
||||
+50
-6
@@ -1,10 +1,7 @@
|
||||
use wasm_bindgen::{Clamped, prelude::*};
|
||||
use web_sys::ImageData;
|
||||
|
||||
use crate::{
|
||||
log,
|
||||
render::{buffer::ImgBuffer, calc_resolution, rand::Rnd},
|
||||
};
|
||||
use crate::render::{RESOLUTION, buffer::ImgBuffer, calc_resolution, rand::Rnd};
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct Renderer {
|
||||
@@ -72,6 +69,10 @@ impl Renderer {
|
||||
.unwrap();
|
||||
self.ctx.put_image_data(&data, 0., 0.).unwrap();
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.img.data = vec![0; (self.img.width() * self.img.height() * 4) as usize];
|
||||
}
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
@@ -82,6 +83,47 @@ impl Renderer {
|
||||
)
|
||||
}
|
||||
|
||||
/// Draw a rectangle centered at (cx, cy) with the given width and height.
|
||||
pub fn rect_center_xywh(
|
||||
&mut self,
|
||||
cx: i32,
|
||||
cy: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
color: (u8, u8, u8),
|
||||
) {
|
||||
self.rect_xyxy(
|
||||
cx - width / 2,
|
||||
cy - height / 2,
|
||||
cx + width / 2,
|
||||
cy + height / 2,
|
||||
color,
|
||||
);
|
||||
}
|
||||
|
||||
/// Draw a rectangle at (x, y) with the given width and height.
|
||||
pub fn rect_xywh(&mut self, x: i32, y: i32, width: i32, height: i32, color: (u8, u8, u8)) {
|
||||
self.rect_xyxy(x, y, x + width, y + height, color);
|
||||
}
|
||||
|
||||
/// Draw a rectangle at (x1, y1) with the given width and height.
|
||||
pub fn rect_xyxy(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: (u8, u8, u8)) {
|
||||
let (leftx, topy) = self.undistort(x1 as f32, y1 as f32);
|
||||
let (rightx, bottomy) = self.undistort(x2 as f32, y2 as f32);
|
||||
|
||||
let leftx = leftx.max(0f32);
|
||||
let topy = topy.max(0f32);
|
||||
let rightx = rightx.min(self.canvas_width as f32);
|
||||
let bottomy = bottomy.min(self.canvas_height as f32);
|
||||
|
||||
for x in leftx as i32..rightx as i32 {
|
||||
for y in topy as i32..bottomy as i32 {
|
||||
self.img.set_pixel(x as u32, y as u32, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the square distance between two points on the screen, and convert to canvas coordinates for processing
|
||||
pub fn screen_dist_sq(&self, x: f32, y: f32, rx: f32, ry: f32) -> f32 {
|
||||
(rx - x).powf(2.) * self.distortion_y + (ry - y).powf(2.) * self.distortion_x
|
||||
}
|
||||
@@ -99,8 +141,10 @@ impl Renderer {
|
||||
let rightx = rightx.min(self.canvas_width as f32);
|
||||
let bottomy = bottomy.min(self.canvas_height as f32);
|
||||
|
||||
let e = (self.canvas_height as f32 / self.actual_height as f32)
|
||||
* (self.canvas_width as f32 / self.actual_width as f32);
|
||||
// let e = (self.canvas_height as f32 / self.actual_height as f32)
|
||||
// * (self.canvas_width as f32 / self.actual_width as f32);
|
||||
|
||||
let e = RESOLUTION.pow(2) as f32 / (self.actual_width * self.actual_height) as f32;
|
||||
|
||||
let r2 = (radius).powf(2.) * e;
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
use crate::{
|
||||
log,
|
||||
render::Renderer,
|
||||
views::{Bounds, View},
|
||||
};
|
||||
|
||||
pub struct ColorRectView {
|
||||
color: (u8, u8, u8),
|
||||
}
|
||||
|
||||
impl ColorRectView {
|
||||
pub fn new(r: u8, g: u8, b: u8) -> Self {
|
||||
Self { color: (r, g, b) }
|
||||
}
|
||||
}
|
||||
|
||||
impl View for ColorRectView {
|
||||
fn draw(&self, renderer: &mut Renderer, x: f32, y: f32, w: f32, h: f32) {
|
||||
renderer.rect_xywh(x as i32, y as i32, w as i32, h as i32, self.color);
|
||||
log!("Draw");
|
||||
}
|
||||
fn bounds(&self, _ph: f32, _pw: f32) -> (Bounds, Bounds) {
|
||||
(Bounds::MatchParent, Bounds::Pixels(2200.))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
use crate::render::Renderer;
|
||||
|
||||
mod color_rect_view;
|
||||
mod vertical_layout;
|
||||
|
||||
use color_rect_view::ColorRectView;
|
||||
use vertical_layout::VerticalLayout;
|
||||
|
||||
pub trait View {
|
||||
fn draw(&self, renderer: &mut Renderer, x: f32, y: f32, w: f32, h: f32);
|
||||
fn bounds(&self, pw: f32, ph: f32) -> (Bounds, Bounds);
|
||||
}
|
||||
|
||||
pub enum Bounds {
|
||||
MatchParent,
|
||||
Pixels(f32),
|
||||
}
|
||||
|
||||
// pub trait ViewPosition: Drawable {
|
||||
// // fn draw(&self)
|
||||
// }
|
||||
|
||||
pub fn default_view() -> Box<dyn View> {
|
||||
Box::new(VerticalLayout::new(vec![
|
||||
Box::new(ColorRectView::new(12, 34, 56)),
|
||||
Box::new(ColorRectView::new(20, 60, 80)),
|
||||
]))
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
use crate::views::{Bounds, View};
|
||||
|
||||
pub struct VerticalLayout {
|
||||
pub views: Vec<Box<dyn View>>,
|
||||
}
|
||||
|
||||
impl VerticalLayout {
|
||||
pub fn new(views: Vec<Box<dyn View>>) -> Self {
|
||||
Self { views }
|
||||
}
|
||||
}
|
||||
|
||||
impl View for VerticalLayout {
|
||||
fn draw(&self, renderer: &mut crate::render::Renderer, x: f32, y: f32, w: f32, h: f32) {
|
||||
let mut cur_y = y;
|
||||
|
||||
for view in &self.views {
|
||||
let (vx, vy) = view.bounds(w, h);
|
||||
|
||||
let vx = match vx {
|
||||
super::Bounds::MatchParent => w,
|
||||
super::Bounds::Pixels(x) => x,
|
||||
};
|
||||
|
||||
let vy = match vy {
|
||||
super::Bounds::MatchParent => h,
|
||||
super::Bounds::Pixels(x) => x,
|
||||
};
|
||||
|
||||
view.draw(renderer, x, cur_y, w.min(vx), vy);
|
||||
cur_y += vy;
|
||||
if cur_y > h {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn bounds(&self, pw: f32, ph: f32) -> (Bounds, Bounds) {
|
||||
let (mut maxx, mut totaly): (f32, f32) = (0., 0.);
|
||||
for view in &self.views {
|
||||
let (vx, vy) = view.bounds(pw, ph);
|
||||
|
||||
let vx = match vx {
|
||||
super::Bounds::MatchParent => pw,
|
||||
super::Bounds::Pixels(x) => x,
|
||||
};
|
||||
|
||||
let vy = match vy {
|
||||
super::Bounds::MatchParent => ph,
|
||||
super::Bounds::Pixels(x) => x,
|
||||
};
|
||||
|
||||
maxx = maxx.max(vx);
|
||||
totaly += vy;
|
||||
}
|
||||
|
||||
(Bounds::Pixels(maxx), Bounds::Pixels(totaly))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user