Draft Scrollable widget (no clipping yet!)

This commit is contained in:
Héctor Ramón Jiménez 2019-10-25 03:47:34 +02:00
parent 4769272122
commit 719c073fc6
13 changed files with 526 additions and 50 deletions

View file

@ -23,4 +23,9 @@ pub enum Primitive {
path: String,
bounds: Rectangle,
},
Scrollable {
bounds: Rectangle,
offset: u32,
content: Box<Primitive>,
},
}

View file

@ -20,6 +20,7 @@ mod column;
mod image;
mod radio;
mod row;
mod scrollable;
mod slider;
mod text;
@ -31,8 +32,6 @@ pub struct Renderer {
quad_pipeline: quad::Pipeline,
image_pipeline: crate::image::Pipeline,
quads: Vec<Quad>,
images: Vec<Image>,
glyph_brush: Rc<RefCell<GlyphBrush<'static, ()>>>,
}
@ -43,6 +42,24 @@ pub struct Target {
swap_chain: SwapChain,
}
pub struct Layer {
quads: Vec<Quad>,
images: Vec<Image>,
layers: Vec<Layer>,
y_offset: u32,
}
impl Layer {
pub fn new(y_offset: u32) -> Self {
Self {
quads: Vec::new(),
images: Vec::new(),
layers: Vec::new(),
y_offset,
}
}
}
impl Renderer {
fn new<W: HasRawWindowHandle>(window: &W) -> Self {
let adapter = Adapter::request(&RequestAdapterOptions {
@ -79,8 +96,6 @@ impl Renderer {
quad_pipeline,
image_pipeline,
quads: Vec::new(),
images: Vec::new(),
glyph_brush: Rc::new(RefCell::new(glyph_brush)),
}
}
@ -132,27 +147,10 @@ impl Renderer {
depth_stencil_attachment: None,
});
self.draw_primitive(primitive);
let mut layer = Layer::new(0);
self.quad_pipeline.draw(
&mut self.device,
&mut encoder,
&self.quads,
target.transformation,
&frame.view,
);
self.quads.clear();
self.image_pipeline.draw(
&mut self.device,
&mut encoder,
&self.images,
target.transformation,
&frame.view,
);
self.images.clear();
self.draw_primitive(primitive, &mut layer);
self.flush(target.transformation, &layer, &mut encoder, &frame.view);
self.glyph_brush
.borrow_mut()
@ -170,13 +168,13 @@ impl Renderer {
*mouse_cursor
}
fn draw_primitive(&mut self, primitive: &Primitive) {
fn draw_primitive(&mut self, primitive: &Primitive, layer: &mut Layer) {
match primitive {
Primitive::None => {}
Primitive::Group { primitives } => {
// TODO: Inspect a bit and regroup (?)
for primitive in primitives {
self.draw_primitive(primitive)
self.draw_primitive(primitive, layer)
}
}
Primitive::Text {
@ -244,7 +242,7 @@ impl Renderer {
background,
border_radius,
} => {
self.quads.push(Quad {
layer.quads.push(Quad {
position: [bounds.x, bounds.y],
scale: [bounds.width, bounds.height],
color: match background {
@ -254,12 +252,55 @@ impl Renderer {
});
}
Primitive::Image { path, bounds } => {
self.images.push(Image {
layer.images.push(Image {
path: path.clone(),
position: [bounds.x, bounds.y],
scale: [bounds.width, bounds.height],
});
}
Primitive::Scrollable {
bounds,
offset,
content,
} => {
let mut new_layer = Layer::new(layer.y_offset + offset);
// TODO: Primitive culling
self.draw_primitive(content, &mut new_layer);
layer.layers.push(new_layer);
}
}
}
fn flush(
&mut self,
transformation: Transformation,
layer: &Layer,
encoder: &mut wgpu::CommandEncoder,
target: &wgpu::TextureView,
) {
let translated = transformation
* Transformation::translate(0.0, -(layer.y_offset as f32));
self.quad_pipeline.draw(
&mut self.device,
encoder,
&layer.quads,
transformation,
target,
);
self.image_pipeline.draw(
&mut self.device,
encoder,
&layer.images,
translated,
target,
);
for layer in layer.layers.iter() {
self.flush(transformation, layer, encoder, target);
}
}
}

View file

@ -0,0 +1,70 @@
use crate::{Primitive, Renderer};
use iced_native::{
scrollable, Background, Color, Layout, Point, Rectangle, Scrollable, Widget,
};
impl scrollable::Renderer for Renderer {
fn draw<Message>(
&mut self,
scrollable: &Scrollable<'_, Message, Self>,
layout: Layout<'_>,
cursor_position: Point,
) -> Self::Output {
let bounds = layout.bounds();
let is_mouse_over = bounds.contains(cursor_position);
let content = layout.children().next().unwrap();
let content_bounds = content.bounds();
let cursor_position = if bounds.contains(cursor_position) {
Point::new(
cursor_position.x,
cursor_position.y + scrollable.state.offset as f32,
)
} else {
Point::new(cursor_position.x, -1.0)
};
let (content, mouse_cursor) =
scrollable.content.draw(self, content, cursor_position);
let primitive = Primitive::Scrollable {
bounds,
offset: scrollable.state.offset,
content: Box::new(content),
};
(
Primitive::Group {
primitives: if is_mouse_over
&& content_bounds.height > bounds.height
{
let ratio = bounds.height / content_bounds.height;
let scrollbar_height = bounds.height * ratio;
let y_offset = scrollable.state.offset as f32 * ratio;
let scrollbar = Primitive::Quad {
bounds: Rectangle {
x: bounds.x + bounds.width - 12.0,
y: bounds.y + y_offset,
width: 10.0,
height: scrollbar_height,
},
background: Background::Color(Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.5,
}),
border_radius: 5,
};
vec![primitive, scrollbar]
} else {
vec![primitive]
},
},
mouse_cursor,
)
}
}

View file

@ -1,30 +1,59 @@
#[derive(Debug, Clone, Copy)]
pub struct Transformation([f32; 16]);
use nalgebra::Matrix3;
use std::ops::Mul;
/// A 2D transformation matrix.
///
/// It can be used to apply a transformation to a [`Target`].
///
/// [`Target`]: struct.Target.html
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Transformation(Matrix3<f32>);
impl Transformation {
#[rustfmt::skip]
pub fn identity() -> Self {
Transformation([
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
])
/// Get the identity transformation.
pub fn identity() -> Transformation {
Transformation(Matrix3::identity())
}
/// Creates an orthographic projection.
///
/// You should rarely need this. On creation, a [`Target`] is automatically
/// set up with the correct orthographic projection.
///
/// [`Target`]: struct.Target.html
#[rustfmt::skip]
pub fn orthographic(width: u16, height: u16) -> Self {
Transformation([
2.0 / width as f32, 0.0, 0.0, 0.0,
0.0, 2.0 / height as f32, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
-1.0, -1.0, 0.0, 1.0,
])
pub fn orthographic(width: u16, height: u16) -> Transformation {
Transformation(nalgebra::Matrix3::new(
2.0 / f32::from(width), 0.0, -1.0,
0.0, 2.0 / f32::from(height), -1.0,
0.0, 0.0, 1.0
))
}
/// Creates a translate transformation.
///
/// You can use this to pan your camera, for example.
pub fn translate(x: f32, y: f32) -> Transformation {
Transformation(Matrix3::new_translation(&nalgebra::Vector2::new(x, y)))
}
}
impl Mul for Transformation {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
Transformation(self.0 * rhs.0)
}
}
impl From<Transformation> for [f32; 16] {
fn from(transformation: Transformation) -> [f32; 16] {
transformation.0
#[rustfmt::skip]
fn from(t: Transformation) -> [f32; 16] {
[
t.0[0], t.0[1], 0.0, t.0[2],
t.0[3], t.0[4], 0.0, t.0[5],
0.0, 0.0, -1.0, 0.0,
t.0[6], t.0[7], 0.0, t.0[8]
]
}
}