Flesh out the NanoVG example even more

This commit is contained in:
Patrick Walton 2020-03-28 12:45:23 -07:00
parent ed2807eb84
commit 257067e368
2 changed files with 301 additions and 30 deletions

View File

@ -117,6 +117,13 @@ fn render_demo(canvas: &mut CanvasRenderingContext2D,
RectF::new(position + Vector2F::new(170.0, 0.0), Vector2F::new(110.0, 28.0)),
ColorU::transparent_black());
// Draw thumbnails.
draw_thumbnails(canvas,
RectF::new(Vector2F::new(365.0, popup_position.y() - 30.0),
Vector2F::new(160.0, 300.0)),
time,
12);
canvas.restore();
}
@ -567,10 +574,14 @@ fn draw_window(canvas: &mut CanvasRenderingContext2D, title: &str, rect: RectF)
fn draw_search_box(canvas: &mut CanvasRenderingContext2D, text: &str, rect: RectF) {
let corner_radius = rect.height() * 0.5 - 1.0;
// TODO(pcwalton): Box gradients.
canvas.set_fill_style(FillStyle::Color(ColorU::new(0, 0, 0, 54)));
canvas.fill_path(create_rounded_rect_path(rect, corner_radius), FillRule::Winding);
fill_path_with_box_gradient(canvas,
create_rounded_rect_path(rect, corner_radius),
FillRule::Winding,
rect + Vector2F::new(0.0, 1.5),
rect.height() * 0.5,
5.0,
ColorU::new(0, 0, 0, 16),
ColorU::new(0, 0, 0, 92));
}
fn draw_dropdown(canvas: &mut CanvasRenderingContext2D, text: &str, rect: RectF) {
@ -593,17 +604,15 @@ fn draw_dropdown(canvas: &mut CanvasRenderingContext2D, text: &str, rect: RectF)
fn draw_edit_box(canvas: &mut CanvasRenderingContext2D, rect: RectF) {
const CORNER_RADIUS: f32 = 4.0;
// TODO(pcwalton): Box gradient.
let mut background_gradient =
Gradient::linear(LineSegment2F::new(rect.origin() + Vector2F::new(0.0, 1.0),
rect.origin() + Vector2F::new(0.0, 4.0)));
background_gradient.add_color_stop(ColorStop::new(ColorU::new(32, 32, 32, 32), 0.0));
background_gradient.add_color_stop(ColorStop::new(ColorU::new(255, 255, 255, 32), 1.0));
canvas.set_fill_style(FillStyle::Gradient(background_gradient));
canvas.fill_path(create_rounded_rect_path(rect.contract(Vector2F::splat(1.0)),
fill_path_with_box_gradient(canvas,
create_rounded_rect_path(rect.contract(Vector2F::splat(1.0)),
CORNER_RADIUS - 1.0),
FillRule::Winding);
FillRule::Winding,
rect.contract(Vector2F::splat(1.0)) + Vector2F::new(0.0, 1.5),
3.0,
4.0,
ColorU::new(255, 255, 255, 32),
ColorU::new(32, 32, 32, 32));
canvas.set_stroke_style(FillStyle::Color(ColorU::new(0, 0, 0, 48)));
canvas.stroke_path(create_rounded_rect_path(rect.contract(Vector2F::splat(0.5)),
@ -624,17 +633,17 @@ fn draw_numeric_edit_box(canvas: &mut CanvasRenderingContext2D,
fn draw_check_box(canvas: &mut CanvasRenderingContext2D, text: &str, rect: RectF) {
const CORNER_RADIUS: f32 = 3.0;
// TODO(pcwalton): Box gradients.
let check_box_rect =
RectF::new(Vector2F::new(rect.origin_x(), rect.center().y().floor() - 9.0),
Vector2F::splat(20.0)).contract(Vector2F::splat(1.0));
let mut background_gradient = Gradient::linear(LineSegment2F::new(rect.origin(),
rect.lower_left()));
background_gradient.add_color_stop(ColorStop::new(ColorU::new(0, 0, 0, 32), 0.0));
background_gradient.add_color_stop(ColorStop::new(ColorU::new(0, 0, 0, 92), 1.0));
canvas.set_fill_style(FillStyle::Gradient(background_gradient));
canvas.fill_path(create_rounded_rect_path(check_box_rect, CORNER_RADIUS), FillRule::Winding);
fill_path_with_box_gradient(canvas,
create_rounded_rect_path(check_box_rect, CORNER_RADIUS),
FillRule::Winding,
check_box_rect + Vector2F::new(0.0, 1.0),
CORNER_RADIUS,
3.0,
ColorU::new(0, 0, 0, 32),
ColorU::new(0, 0, 0, 92));
}
fn draw_button(canvas: &mut CanvasRenderingContext2D, text: &str, rect: RectF, color: ColorU) {
@ -667,13 +676,14 @@ fn draw_slider(canvas: &mut CanvasRenderingContext2D, value: f32, rect: RectF) {
// TODO(pcwalton): Box gradient.
let track_rect = RectF::new(Vector2F::new(rect.origin_x(), center_y - 2.0),
Vector2F::new(rect.width(), 4.0));
let mut background_gradient =
Gradient::linear(LineSegment2F::new(track_rect.origin(), track_rect.lower_left()) +
Vector2F::new(0.0, 1.0));
background_gradient.add_color_stop(ColorStop::new(ColorU::new(0, 0, 0, 32), 0.0));
background_gradient.add_color_stop(ColorStop::new(ColorU::new(0, 0, 0, 128), 1.0));
canvas.set_fill_style(FillStyle::Gradient(background_gradient));
canvas.fill_path(create_rounded_rect_path(track_rect, 2.0), FillRule::Winding);
fill_path_with_box_gradient(canvas,
create_rounded_rect_path(track_rect, 2.0),
FillRule::Winding,
track_rect + Vector2F::new(0.0, 1.0),
2.0,
2.0,
ColorU::new(0, 0, 0, 32),
ColorU::new(0, 0, 0, 128));
// Draw knob shadow.
let knob_position = Vector2F::new(rect.origin_x() + (value * rect.width()).floor(), center_y);
@ -712,6 +722,258 @@ fn draw_slider(canvas: &mut CanvasRenderingContext2D, value: f32, rect: RectF) {
canvas.restore();
}
fn draw_thumbnails(canvas: &mut CanvasRenderingContext2D,
rect: RectF,
time: f32,
image_count: usize) {
const CORNER_RADIUS: f32 = 3.0;
const THUMB_HEIGHT: f32 = 60.0;
const ARROW_Y_POSITION: f32 = 30.5;
let stack_height = image_count as f32 * 0.5 * (THUMB_HEIGHT + 10.0) + 10.0;
let scroll_height = rect.height() / stack_height * (rect.height() - 8.0);
let scroll_y = (1.0 + f32::cos(time * 0.5)) * 0.5;
let load_y = (1.0 - f32::cos(time * 0.2)) * 0.5;
let image_y_scale = 1.0 / (image_count as f32 - 1.0);
canvas.save();
// Draw drop shadow.
let mut path = create_rounded_rect_path(rect, CORNER_RADIUS);
path.rect(RectF::new(rect.origin() - Vector2F::splat(10.0),
rect.size() + Vector2F::new(20.0, 30.0)));
fill_path_with_box_gradient(canvas,
path,
FillRule::EvenOdd,
rect + Vector2F::new(0.0, 4.0),
CORNER_RADIUS * 2.0,
20.0,
ColorU::new(0, 0, 0, 128),
ColorU::transparent_black());
// Draw window.
let mut path = create_rounded_rect_path(rect, CORNER_RADIUS);
path.move_to(rect.origin() + Vector2F::new(-10.0, ARROW_Y_POSITION));
path.line_to(rect.origin() + Vector2F::new(1.0, ARROW_Y_POSITION - 11.0));
path.line_to(rect.origin() + Vector2F::new(1.0, ARROW_Y_POSITION + 11.0));
canvas.set_fill_style(FillStyle::Color(ColorU::new(200, 200, 200, 255)));
canvas.fill_path(path, FillRule::Winding);
// Draw images.
canvas.save();
let mut clip_path = Path2D::new();
clip_path.rect(rect);
canvas.clip_path(clip_path, FillRule::Winding);
canvas.set_current_transform(&Transform2F::from_translation(
Vector2F::new(0.0, -scroll_y * (stack_height - rect.height()))));
for image_index in 0..image_count {
let image_origin = rect.origin() + Vector2F::splat(10.0) +
Vector2I::new(image_index as i32 % 2,
image_index as i32 / 2).to_f32().scale(THUMB_HEIGHT + 10.0);
let image_rect = RectF::new(image_origin, Vector2F::splat(THUMB_HEIGHT));
let image_path = create_rounded_rect_path(image_rect, 5.0);
canvas.set_fill_style(FillStyle::Color(ColorU::new(32, 32, 32, 255)));
canvas.fill_path(image_path, FillRule::Winding);
let image_y = image_index as f32 * image_y_scale;
let alpha = util::clamp((load_y - image_y) / image_y_scale, 0.0, 1.0);
if alpha < 1.0 {
draw_spinner(canvas, image_rect.center(), THUMB_HEIGHT * 0.25, time);
}
let mut shadow_path = create_rounded_rect_path(image_rect, 6.0);
shadow_path.rect(image_rect.dilate(Vector2F::splat(5.0)));
// TODO(pcwalton): Union clip paths.
/*fill_path_with_box_gradient(
canvas,
shadow_path,
FillRule::EvenOdd,
image_rect.dilate(Vector2F::splat(1.0)) + Vector2F::new(0.0, 1.0),
5.0,
3.0,
ColorU::new(0, 0, 0, 128),
ColorU::transparent_black());*/
canvas.set_stroke_style(FillStyle::Color(ColorU::new(255, 255, 255, 192)));
canvas.stroke_path(create_rounded_rect_path(image_rect.dilate(Vector2F::splat(0.5)), 3.5));
}
canvas.restore();
// Draw fade-away gradients.
let mut fade_gradient =
Gradient::linear(LineSegment2F::new(rect.origin(),
rect.origin() + Vector2F::new(0.0, 6.0)));
fade_gradient.add_color_stop(ColorStop::new(ColorU::new(200, 200, 200, 255), 0.0));
fade_gradient.add_color_stop(ColorStop::new(ColorU::new(200, 200, 200, 0), 1.0));
canvas.set_fill_style(FillStyle::Gradient(fade_gradient));
canvas.fill_rect(RectF::new(rect.origin() + Vector2F::new(4.0, 0.0),
Vector2F::new(rect.width() - 8.0, 6.0)));
let mut fade_gradient =
Gradient::linear(LineSegment2F::new(rect.lower_left(),
rect.lower_left() - Vector2F::new(0.0, 6.0)));
fade_gradient.add_color_stop(ColorStop::new(ColorU::new(200, 200, 200, 255), 0.0));
fade_gradient.add_color_stop(ColorStop::new(ColorU::new(200, 200, 200, 0), 1.0));
canvas.set_fill_style(FillStyle::Gradient(fade_gradient));
canvas.fill_rect(RectF::new(rect.lower_left() + Vector2F::new(4.0, -6.0),
Vector2F::new(rect.width() - 8.0, 6.0)));
// Draw scroll bar.
let scroll_bar_rect = RectF::new(rect.upper_right() + Vector2F::new(-12.0, 4.0),
Vector2F::new(8.0, rect.height() - 8.0));
fill_path_with_box_gradient(canvas,
create_rounded_rect_path(scroll_bar_rect, CORNER_RADIUS),
FillRule::Winding,
scroll_bar_rect + Vector2F::new(0.0, 1.0),
CORNER_RADIUS,
4.0,
ColorU::new(0, 0, 0, 32),
ColorU::new(0, 0, 0, 92));
let knob_rect =
RectF::new(rect.upper_right() +
Vector2F::new(-11.0, 5.0 + (rect.height() - 8.0 - scroll_height) * scroll_y),
Vector2F::new(6.0, scroll_height - 2.0));
fill_path_with_box_gradient(canvas,
create_rounded_rect_path(knob_rect, 2.0),
FillRule::Winding,
knob_rect.dilate(Vector2F::splat(2.0)) + Vector2F::new(0.0, 1.0),
3.0,
4.0,
ColorU::new(220, 220, 220, 255),
ColorU::new(128, 128, 128, 255));
canvas.restore();
}
fn draw_spinner(canvas: &mut CanvasRenderingContext2D, center: Vector2F, radius: f32, time: f32) {
let (start_angle, end_angle) = (time * 6.0, PI + time * 6.0);
let (outer_radius, inner_radius) = (radius, radius * 0.75);
let average_radius = util::lerp(outer_radius, inner_radius, 0.5);
canvas.save();
let mut path = Path2D::new();
path.arc(center, outer_radius, start_angle, end_angle, ArcDirection::CW);
path.arc(center, inner_radius, end_angle, start_angle, ArcDirection::CCW);
path.close_path();
set_linear_gradient_fill_style(
canvas,
center + Vector2F::new(outer_radius.cos(), outer_radius.sin()).scale(average_radius),
center + Vector2F::new(inner_radius.cos(), inner_radius.sin()).scale(average_radius),
ColorU::transparent_black(),
ColorU::new(0, 0, 0, 128));
canvas.fill_path(path, FillRule::Winding);
canvas.restore();
}
fn fill_path_with_box_gradient(canvas: &mut CanvasRenderingContext2D,
path: Path2D,
fill_rule: FillRule,
rect: RectF,
corner_radius: f32,
blur_radius: f32,
inner_color: ColorU,
outer_color: ColorU) {
// TODO(pcwalton): Fill the corners with radial gradients.
let window_rect = RectF::new(Vector2F::default(), Vector2F::new(800.0, 600.0));
let inner_rect = rect.contract(Vector2F::splat(blur_radius));
let outer_rect = rect.dilate(Vector2F::splat(blur_radius));
canvas.save();
canvas.clip_path(path, fill_rule);
// Draw left part.
let mut section = Path2D::new();
section.move_to(window_rect.origin());
section.line_to(outer_rect.origin());
section.line_to(inner_rect.origin());
section.line_to(rect.center());
section.line_to(inner_rect.lower_left());
section.line_to(outer_rect.lower_left());
section.line_to(window_rect.lower_left());
section.close_path();
set_linear_gradient_fill_style(canvas,
outer_rect.origin(),
Vector2F::new(inner_rect.min_x(), outer_rect.min_y()),
outer_color,
inner_color);
canvas.fill_path(section, FillRule::Winding);
// Draw top part.
let mut section = Path2D::new();
section.move_to(window_rect.origin());
section.line_to(outer_rect.origin());
section.line_to(inner_rect.origin());
section.line_to(rect.center());
section.line_to(inner_rect.upper_right());
section.line_to(outer_rect.upper_right());
section.line_to(window_rect.upper_right());
section.close_path();
set_linear_gradient_fill_style(canvas,
outer_rect.origin(),
Vector2F::new(outer_rect.min_x(), inner_rect.min_y()),
outer_color,
inner_color);
canvas.fill_path(section, FillRule::Winding);
// Draw right part.
let mut section = Path2D::new();
section.move_to(window_rect.upper_right());
section.line_to(outer_rect.upper_right());
section.line_to(inner_rect.upper_right());
section.line_to(rect.center());
section.line_to(inner_rect.lower_right());
section.line_to(outer_rect.lower_right());
section.line_to(window_rect.lower_right());
section.close_path();
set_linear_gradient_fill_style(canvas,
outer_rect.upper_right(),
Vector2F::new(inner_rect.max_x(), outer_rect.min_y()),
outer_color,
inner_color);
canvas.fill_path(section, FillRule::Winding);
// Draw bottom part.
let mut section = Path2D::new();
section.move_to(window_rect.lower_right());
section.line_to(outer_rect.lower_right());
section.line_to(inner_rect.lower_right());
section.line_to(rect.center());
section.line_to(inner_rect.lower_left());
section.line_to(outer_rect.lower_left());
section.line_to(window_rect.lower_left());
section.close_path();
set_linear_gradient_fill_style(canvas,
outer_rect.lower_left(),
Vector2F::new(outer_rect.min_x(), inner_rect.max_y()),
outer_color,
inner_color);
canvas.fill_path(section, FillRule::Winding);
canvas.restore();
}
fn set_linear_gradient_fill_style(canvas: &mut CanvasRenderingContext2D,
from_position: Vector2F,
to_position: Vector2F,
from_color: ColorU,
to_color: ColorU) {
let mut gradient = Gradient::linear(LineSegment2F::new(from_position, to_position));
gradient.add_color_stop(ColorStop::new(from_color, 0.0));
gradient.add_color_stop(ColorStop::new(to_color, 1.0));
canvas.set_fill_style(FillStyle::Gradient(gradient));
}
fn create_graph_path(sample_points: &[Vector2F], sample_spread: f32, offset: Vector2F) -> Path2D {
let mut path = Path2D::new();
path.move_to(sample_points[0] + Vector2F::new(0.0, 2.0));

View File

@ -12,6 +12,7 @@
use crate::vector::{Vector2F, Vector2I};
use pathfinder_simd::default::{F32x4, I32x4};
use std::ops::Add;
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct RectF(pub F32x4);
@ -197,6 +198,14 @@ impl RectF {
}
}
impl Add<Vector2F> for RectF {
type Output = RectF;
#[inline]
fn add(self, other: Vector2F) -> RectF {
RectF::new(self.origin() + other, self.size())
}
}
/// NB: The origin is inclusive, while the lower right point is exclusive.
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct RectI(pub I32x4);