From aff0fa46204e5f2c7a77151ce845abd6c4e64f09 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 14 Aug 2021 13:05:46 +0100 Subject: [PATCH 01/16] FlatTheme: fix check/radio-box inner size --- crates/kas-theme/src/flat_theme.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/kas-theme/src/flat_theme.rs b/crates/kas-theme/src/flat_theme.rs index be7f72b96..188f1a983 100644 --- a/crates/kas-theme/src/flat_theme.rs +++ b/crates/kas-theme/src/flat_theme.rs @@ -80,7 +80,7 @@ const DIMS: dim::Parameters = dim::Parameters { frame_size: 2.4, // NOTE: visual thickness is (button_frame * scale_factor).round() * (1 - BG_SHRINK_FACTOR) button_frame: 2.4, - checkbox_inner: 5.0, + checkbox_inner: 9.0, scrollbar_size: Vec2::splat(8.0), slider_size: Vec2(16.0, 16.0), progress_bar: Vec2::splat(8.0), From 799a69d13f6a59ee7b37fc61c903f91b14f3fd97 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 14 Aug 2021 18:39:34 +0100 Subject: [PATCH 02/16] Add FlatTheme::button_frame abstraction method --- crates/kas-theme/src/flat_theme.rs | 56 ++++++++++++++---------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/crates/kas-theme/src/flat_theme.rs b/crates/kas-theme/src/flat_theme.rs index 188f1a983..1ce8860fa 100644 --- a/crates/kas-theme/src/flat_theme.rs +++ b/crates/kas-theme/src/flat_theme.rs @@ -203,6 +203,23 @@ impl ThemeApi for FlatTheme { } } +impl<'a, DS: DrawSharedImpl> DrawHandle<'a, DS> +where + DS::Draw: DrawRoundedImpl, +{ + fn button_frame(&mut self, outer: Quad, col_frame: Rgba, col_bg: Rgba) -> Quad { + if col_bg != self.cols.background { + let inner = outer.shrink(self.window.dims.button_frame as f32 * BG_SHRINK_FACTOR); + self.draw.rect(inner, col_bg); + } + + let inner = outer.shrink(self.window.dims.button_frame as f32); + self.draw + .rounded_frame(outer, inner, BG_SHRINK_FACTOR, col_frame); + inner + } +} + impl<'a, DS: DrawSharedImpl> draw::DrawHandle for DrawHandle<'a, DS> where DS::Draw: DrawRoundedImpl, @@ -382,39 +399,26 @@ where fn button(&mut self, rect: Rect, col: Option, state: InputState) { let outer = Quad::from(rect); - let col = if state.nav_focus && !state.disabled { + let col_bg = if state.nav_focus && !state.disabled { self.cols.accent_soft } else { col.map(|c| c.into()).unwrap_or(self.cols.background) }; - let col = ColorsLinear::adjust_for_state(col, state); - if col != self.cols.background { - let inner = outer.shrink(self.window.dims.button_frame as f32 * BG_SHRINK_FACTOR); - self.draw.rect(inner, col); - } - - let col = self.cols.nav_region(state).unwrap_or(self.cols.frame); - let inner = outer.shrink(self.window.dims.button_frame as f32); - self.draw.rounded_frame(outer, inner, BG_SHRINK_FACTOR, col); + let col_bg = ColorsLinear::adjust_for_state(col_bg, state); + let col_frame = self.cols.nav_region(state).unwrap_or(self.cols.frame); + self.button_frame(outer, col_frame, col_bg); } fn edit_box(&mut self, rect: Rect, mut state: InputState) { let outer = Quad::from(rect); state.depress = false; - let col = match state.error { + let col_bg = match state.error { true => self.cols.edit_bg_error, false => self.cols.edit_bg, }; - let col = ColorsLinear::adjust_for_state(col, state); - if col != self.cols.background { - let inner = outer.shrink(self.window.dims.button_frame as f32 * BG_SHRINK_FACTOR); - self.draw.rect(inner, col); - } - - let inner = outer.shrink(self.window.dims.button_frame as f32); - self.draw - .rounded_frame(outer, inner, BG_SHRINK_FACTOR, self.cols.frame); + let col_bg = ColorsLinear::adjust_for_state(col_bg, state); + self.button_frame(outer, self.cols.frame, col_bg); if state.nav_focus { let r = 0.5 * self.window.dims.button_frame as f32; @@ -428,15 +432,9 @@ where fn checkbox(&mut self, rect: Rect, checked: bool, state: InputState) { let outer = Quad::from(rect); - let col = ColorsLinear::adjust_for_state(self.cols.background, state); - if col != self.cols.background { - let inner = outer.shrink(self.window.dims.button_frame as f32 * BG_SHRINK_FACTOR); - self.draw.rect(inner, col); - } - - let col = self.cols.nav_region(state).unwrap_or(self.cols.frame); - let inner = outer.shrink(self.window.dims.button_frame as f32); - self.draw.rounded_frame(outer, inner, BG_SHRINK_FACTOR, col); + let col_bg = ColorsLinear::adjust_for_state(self.cols.background, state); + let col_frame = self.cols.nav_region(state).unwrap_or(self.cols.frame); + let inner = self.button_frame(outer, col_frame, col_bg); if let Some(col) = self.cols.check_mark_state(state, checked) { let inner = inner.shrink((2 * self.window.dims.inner_margin) as f32); From bcddc9dd0c31226796d97a38a329f3f581fe4114 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 16 Aug 2021 10:11:17 +0100 Subject: [PATCH 03/16] Add documentation for render pipe Vertex type --- crates/kas-wgpu/src/draw/flat_round.rs | 11 +++++++++++ crates/kas-wgpu/src/draw/shaded_round.rs | 9 +++++++++ crates/kas-wgpu/src/draw/shaded_square.rs | 7 +++++++ 3 files changed, 27 insertions(+) diff --git a/crates/kas-wgpu/src/draw/flat_round.rs b/crates/kas-wgpu/src/draw/flat_round.rs index 5154f8414..3e1ae7e39 100644 --- a/crates/kas-wgpu/src/draw/flat_round.rs +++ b/crates/kas-wgpu/src/draw/flat_round.rs @@ -20,6 +20,14 @@ const AA_OFFSET: f32 = 0.5 * std::f32::consts::FRAC_1_SQRT_2; // "frame" shape could support four-triangle strips. However, this would require // many rpass.draw() commands or shaders unpacking vertices from instance data. +/// Vertex +/// +/// - `screen_pos: Vec2` — screen coordinate +/// - `colour: Rgba` +/// - `inner: f32` — inner radius, relative to outer (range: 0 to 1) +/// - `circle_pos: Vec2` — coordinate on virtual circle with radius 1 centred +/// on the origin +/// - `pix_offset: Vec2` — size of a pixel on the virtual circle; used for AA #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct Vertex(Vec2, Rgba, f32, Vec2, Vec2); @@ -35,6 +43,8 @@ impl Vertex { pub type Window = common::Window; /// A pipeline for rendering rounded shapes +/// +/// Uses 4x sampling for anti-aliasing. pub struct Pipeline { render_pipeline: wgpu::RenderPipeline, } @@ -250,6 +260,7 @@ impl Window { let n0a = Vec2(0.0, na.1); let n0b = Vec2(0.0, nb.1); + // Size of each corner may differ, hence need for separate pixel offsets: let paa = na / (aa - cc) * AA_OFFSET; let pab = nab / (ab - cd) * AA_OFFSET; let pba = nba / (ba - dc) * AA_OFFSET; diff --git a/crates/kas-wgpu/src/draw/shaded_round.rs b/crates/kas-wgpu/src/draw/shaded_round.rs index efeafb0fa..1781031c5 100644 --- a/crates/kas-wgpu/src/draw/shaded_round.rs +++ b/crates/kas-wgpu/src/draw/shaded_round.rs @@ -16,6 +16,13 @@ use std::mem::size_of; /// implement 4x multi-sampling. The pattern is defined by the fragment shader. const OFFSET: f32 = 0.5 * std::f32::consts::FRAC_1_SQRT_2; +/// Vertex +/// +/// - `screen_pos: Vec2` — screen coordinate +/// - `colour: Rgba` +/// - `dir: Vec2` — normalised direction of slope (from (-1, -1) to (1, 1)) +/// - `adjust: Vec2` +/// - `pix_offset: Vec2` — offset for a pixel; used for AA #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct Vertex(Vec2, Rgba, Vec2, Vec2, Vec2); @@ -31,6 +38,8 @@ impl Vertex { pub type Window = common::Window; /// A pipeline for rendering rounded shapes +/// +/// Uses 4x sampling for anti-aliasing. pub struct Pipeline { render_pipeline: wgpu::RenderPipeline, } diff --git a/crates/kas-wgpu/src/draw/shaded_square.rs b/crates/kas-wgpu/src/draw/shaded_square.rs index d6513db1a..7b2208482 100644 --- a/crates/kas-wgpu/src/draw/shaded_square.rs +++ b/crates/kas-wgpu/src/draw/shaded_square.rs @@ -11,6 +11,11 @@ use kas::draw::{color::Rgba, PassId}; use kas::geom::{Quad, Vec2}; use std::mem::size_of; +/// Vertex +/// +/// - `screen_pos: Vec2` — screen coordinate +/// - `colour: Rgba` +/// - `dir: Vec2` — normalised direction of slope (from (-1, -1) to (1, 1)) #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct Vertex(Vec2, Rgba, Vec2); @@ -20,6 +25,8 @@ unsafe impl bytemuck::Pod for Vertex {} pub type Window = common::Window; /// A pipeline for rendering with flat and square-corner shading +/// +/// Does not use anti-aliasing since edges are usually pixel-aligned. pub struct Pipeline { render_pipeline: wgpu::RenderPipeline, } From 9393c75399d58c45954c3dddb835b4d7f738980e Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 16 Aug 2021 11:09:31 +0100 Subject: [PATCH 04/16] Add round_2col pipeline: 2-colour rounded blending --- crates/kas-core/src/draw/draw_rounded.rs | 41 +++- crates/kas-wgpu/src/draw/draw_pipe.rs | 30 ++- crates/kas-wgpu/src/draw/mod.rs | 3 + crates/kas-wgpu/src/draw/round_2col.rs | 227 ++++++++++++++++++ crates/kas-wgpu/src/draw/shaders.rs | 6 + .../kas-wgpu/src/draw/shaders/round_2col.frag | 25 ++ .../src/draw/shaders/round_2col.frag.spv | Bin 0 -> 1496 bytes .../kas-wgpu/src/draw/shaders/round_2col.vert | 30 +++ .../src/draw/shaders/round_2col.vert.spv | Bin 0 -> 1936 bytes 9 files changed, 344 insertions(+), 18 deletions(-) create mode 100644 crates/kas-wgpu/src/draw/round_2col.rs create mode 100644 crates/kas-wgpu/src/draw/shaders/round_2col.frag create mode 100644 crates/kas-wgpu/src/draw/shaders/round_2col.frag.spv create mode 100644 crates/kas-wgpu/src/draw/shaders/round_2col.vert create mode 100644 crates/kas-wgpu/src/draw/shaders/round_2col.vert.spv diff --git a/crates/kas-core/src/draw/draw_rounded.rs b/crates/kas-core/src/draw/draw_rounded.rs index f651b55d1..682b9959e 100644 --- a/crates/kas-core/src/draw/draw_rounded.rs +++ b/crates/kas-core/src/draw/draw_rounded.rs @@ -32,6 +32,14 @@ pub trait DrawRounded: Draw { /// painted, while `1.0` will result in a zero-width line on the outer edge. fn circle(&mut self, rect: Quad, inner_radius: f32, col: Rgba); + /// Draw a circle or oval with two colours + /// + /// More generally, this shape is an axis-aligned oval which may be hollow. + /// + /// Colour `col1` is used at the centre and `col2` at the edge with linear + /// blending. The edge is not anti-aliased. + fn circle_2col(&mut self, rect: Quad, col1: Rgba, col2: Rgba); + /// Draw a frame with rounded corners and uniform colour /// /// All drawing occurs within the `outer` rect and outside of the `inner` @@ -44,22 +52,40 @@ pub trait DrawRounded: Draw { /// When `inner_radius > 0`, the frame will be visually thinner than the /// allocated area. fn rounded_frame(&mut self, outer: Quad, inner: Quad, inner_radius: f32, col: Rgba); + + /// Draw a frame with rounded corners with two colours + /// + /// This is a variant of `rounded_frame` which blends between two colours, + /// `c1` at the inner edge and `c2` at the outer edge. + fn rounded_frame_2col(&mut self, outer: Quad, inner: Quad, c1: Rgba, c2: Rgba); } impl<'a, DS: DrawSharedImpl> DrawRounded for DrawIface<'a, DS> where DS::Draw: DrawRoundedImpl, { + #[inline] fn rounded_line(&mut self, p1: Vec2, p2: Vec2, radius: f32, col: Rgba) { self.draw.rounded_line(self.pass, p1, p2, radius, col); } + #[inline] fn circle(&mut self, rect: Quad, inner_radius: f32, col: Rgba) { self.draw.circle(self.pass, rect, inner_radius, col); } + #[inline] + fn circle_2col(&mut self, rect: Quad, col1: Rgba, col2: Rgba) { + self.draw.circle_2col(self.pass, rect, col1, col2); + } + #[inline] fn rounded_frame(&mut self, outer: Quad, inner: Quad, inner_radius: f32, col: Rgba) { self.draw .rounded_frame(self.pass, outer, inner, inner_radius, col); } + #[inline] + fn rounded_frame_2col(&mut self, outer: Quad, inner: Quad, c1: Rgba, c2: Rgba) { + self.draw + .rounded_frame_2col(self.pass, outer, inner, c1, c2); + } } /// Drawing commands for rounded shapes @@ -78,13 +104,12 @@ pub trait DrawRoundedImpl: DrawImpl { /// Draw a circle or oval of uniform colour fn circle(&mut self, pass: PassId, rect: Quad, inner_radius: f32, col: Rgba); + /// Draw a circle or oval with two colours + fn circle_2col(&mut self, pass: PassId, rect: Quad, col1: Rgba, col2: Rgba); + /// Draw a frame with rounded corners and uniform colour - fn rounded_frame( - &mut self, - pass: PassId, - outer: Quad, - inner: Quad, - inner_radius: f32, - col: Rgba, - ); + fn rounded_frame(&mut self, pass: PassId, outer: Quad, inner: Quad, r1: f32, col: Rgba); + + /// Draw a frame with rounded corners with two colours + fn rounded_frame_2col(&mut self, pass: PassId, outer: Quad, inner: Quad, c1: Rgba, c2: Rgba); } diff --git a/crates/kas-wgpu/src/draw/draw_pipe.rs b/crates/kas-wgpu/src/draw/draw_pipe.rs index 50127b0ac..4c1703ed9 100644 --- a/crates/kas-wgpu/src/draw/draw_pipe.rs +++ b/crates/kas-wgpu/src/draw/draw_pipe.rs @@ -75,6 +75,7 @@ impl DrawPipe { let shaded_square = shaded_square::Pipeline::new(&device, &shaders, &bgl_common); let shaded_round = shaded_round::Pipeline::new(&device, &shaders, &bgl_common); let flat_round = flat_round::Pipeline::new(&device, &shaders, &bgl_common); + let round_2col = round_2col::Pipeline::new(&device, &shaders, &bgl_common); let custom = custom.build(&device, &bgl_common, RENDER_TEX_FORMAT); let text = text_pipe::Pipeline::new(&device, &shaders, &bgl_common, raster_config); @@ -90,6 +91,7 @@ impl DrawPipe { shaded_square, shaded_round, flat_round, + round_2col, custom, text, } @@ -106,6 +108,7 @@ impl DrawPipe { shaded_square: Default::default(), shaded_round: Default::default(), flat_round: Default::default(), + round_2col: Default::default(), custom, text: Default::default(), } @@ -216,6 +219,9 @@ impl DrawPipe { window .flat_round .write_buffers(&self.device, &mut self.staging_belt, &mut encoder); + window + .round_2col + .write_buffers(&self.device, &mut self.staging_belt, &mut encoder); self.custom.prepare( &mut window.custom, &self.device, @@ -264,6 +270,8 @@ impl DrawPipe { .render(&window.shaded_round, pass, &mut rpass, bg_common); self.flat_round .render(&window.flat_round, pass, &mut rpass, bg_common); + self.round_2col + .render(&window.round_2col, pass, &mut rpass, bg_common); self.custom.render_pass( &mut window.custom, &self.device, @@ -428,16 +436,18 @@ impl DrawRoundedImpl for DrawWindow { } #[inline] - fn rounded_frame( - &mut self, - pass: PassId, - outer: Quad, - inner: Quad, - inner_radius: f32, - col: Rgba, - ) { - self.flat_round - .rounded_frame(pass, outer, inner, inner_radius, col); + fn circle_2col(&mut self, pass: PassId, rect: Quad, col1: Rgba, col2: Rgba) { + self.round_2col.circle(pass, rect, col1, col2); + } + + #[inline] + fn rounded_frame(&mut self, pass: PassId, outer: Quad, inner: Quad, r1: f32, col: Rgba) { + self.flat_round.rounded_frame(pass, outer, inner, r1, col); + } + + #[inline] + fn rounded_frame_2col(&mut self, pass: PassId, outer: Quad, inner: Quad, c1: Rgba, c2: Rgba) { + self.round_2col.frame(pass, outer, inner, c1, c2); } } diff --git a/crates/kas-wgpu/src/draw/mod.rs b/crates/kas-wgpu/src/draw/mod.rs index b6c3a3b12..1377455e0 100644 --- a/crates/kas-wgpu/src/draw/mod.rs +++ b/crates/kas-wgpu/src/draw/mod.rs @@ -13,6 +13,7 @@ mod custom; mod draw_pipe; mod flat_round; mod images; +mod round_2col; mod shaded_round; mod shaded_square; mod shaders; @@ -45,6 +46,7 @@ pub struct DrawPipe { shaded_square: shaded_square::Pipeline, shaded_round: shaded_round::Pipeline, flat_round: flat_round::Pipeline, + round_2col: round_2col::Pipeline, custom: C, pub(crate) text: text_pipe::Pipeline, } @@ -57,6 +59,7 @@ pub struct DrawWindow { shaded_square: shaded_square::Window, shaded_round: shaded_round::Window, flat_round: flat_round::Window, + round_2col: round_2col::Window, custom: CW, pub(crate) text: text_pipe::Window, } diff --git a/crates/kas-wgpu/src/draw/round_2col.rs b/crates/kas-wgpu/src/draw/round_2col.rs new file mode 100644 index 000000000..6cc633d0e --- /dev/null +++ b/crates/kas-wgpu/src/draw/round_2col.rs @@ -0,0 +1,227 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License in the LICENSE-APACHE file or at: +// https://www.apache.org/licenses/LICENSE-2.0 + +//! Rounded two-colour pipeline + +use super::common; +use crate::draw::ShaderManager; +use kas::draw::{color::Rgba, PassId}; +use kas::geom::{Quad, Vec2}; +use std::mem::size_of; + +// NOTE(opt): in theory we could reduce data transmission to the GPU by 1/3 by +// sending quads (two triangles) as instances in triangle-strip mode. The +// "frame" shape could support four-triangle strips. However, this would require +// many rpass.draw() commands or shaders unpacking vertices from instance data. + +/// Vertex +/// +/// - `screen_pos: Vec2` — screen coordinate +/// - `col1: Rgba` +/// - `col2: Rgba` +/// - `circle_pos: Vec2` — coordinate on virtual circle with radius 1 centred +/// on the origin +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Vertex(Vec2, Rgba, Rgba, Vec2); +unsafe impl bytemuck::Zeroable for Vertex {} +unsafe impl bytemuck::Pod for Vertex {} + +pub type Window = common::Window; + +/// A pipeline for rendering rounded shapes +/// +/// Does not use anti-aliasing since edges usually have low alpha (opacity). +pub struct Pipeline { + render_pipeline: wgpu::RenderPipeline, +} + +impl Pipeline { + /// Construct + pub fn new( + device: &wgpu::Device, + shaders: &ShaderManager, + bgl_common: &wgpu::BindGroupLayout, + ) -> Self { + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("R2C pipeline_layout"), + bind_group_layouts: &[bgl_common], + push_constant_ranges: &[], + }); + + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("R2C render_pipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shaders.vert_round_2col, + entry_point: "main", + buffers: &[wgpu::VertexBufferLayout { + array_stride: size_of::() as wgpu::BufferAddress, + step_mode: wgpu::InputStepMode::Vertex, + attributes: &wgpu::vertex_attr_array![ + 0 => Float32x2, + 1 => Float32x4, + 2 => Float32x4, + 3 => Float32x2, + ], + }], + }, + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Cw, + cull_mode: Some(wgpu::Face::Back), // not required + clamp_depth: false, + polygon_mode: wgpu::PolygonMode::Fill, + conservative: false, + }, + depth_stencil: None, + multisample: Default::default(), + fragment: Some(wgpu::FragmentState { + module: &shaders.frag_round_2col, + entry_point: "main", + targets: &[wgpu::ColorTargetState { + format: super::RENDER_TEX_FORMAT, + blend: Some(wgpu::BlendState::ALPHA_BLENDING), + write_mask: wgpu::ColorWrite::ALL, + }], + }), + }); + + Pipeline { render_pipeline } + } + + /// Enqueue render commands + pub fn render<'a>( + &'a self, + window: &'a Window, + pass: usize, + rpass: &mut wgpu::RenderPass<'a>, + bg_common: &'a wgpu::BindGroup, + ) { + window.render(pass, rpass, &self.render_pipeline, bg_common); + } +} + +impl Window { + pub fn circle(&mut self, pass: PassId, rect: Quad, col1: Rgba, col2: Rgba) { + let aa = rect.a; + let bb = rect.b; + + if !aa.lt(bb) || (col1.a == 0.0 && col2.a == 0.0) { + // zero / negative size or transparent: nothing to draw + return; + } + + let ab = Vec2(aa.0, bb.1); + let ba = Vec2(bb.0, aa.1); + let mid = (aa + bb) * 0.5; + + let n0 = Vec2::splat(0.0); + let nb = Vec2::ONE; // = (bb - aa).sign(); + let na = -nb; + let nab = Vec2(na.0, nb.1); + let nba = Vec2(nb.0, na.1); + + let aa = Vertex(aa, col1, col2, na); + let ab = Vertex(ab, col1, col2, nab); + let ba = Vertex(ba, col1, col2, nba); + let bb = Vertex(bb, col1, col2, nb); + let mid = Vertex(mid, col1, col2, n0); + + #[rustfmt::skip] + self.add_vertices(pass.pass(), &[ + ba, mid, aa, + bb, mid, ba, + ab, mid, bb, + aa, mid, ab, + ]); + } + + /// Bounds on input: `aa < cc < dd < bb`. + pub fn frame(&mut self, pass: PassId, outer: Quad, inner: Quad, col1: Rgba, col2: Rgba) { + let aa = outer.a; + let bb = outer.b; + let mut cc = inner.a; + let mut dd = inner.b; + + if !aa.lt(bb) || (col1.a == 0.0 && col2.a == 0.0) { + // zero / negative size or transparent: nothing to draw + return; + } + if !aa.le(cc) || !cc.le(bb) { + cc = aa; + } + if !aa.le(dd) || !dd.le(bb) { + dd = bb; + } + if !cc.le(dd) { + dd = cc; + } + + let ab = Vec2(aa.0, bb.1); + let ba = Vec2(bb.0, aa.1); + let cd = Vec2(cc.0, dd.1); + let dc = Vec2(dd.0, cc.1); + + let n0 = Vec2::splat(0.0); + let nb = (bb - aa).sign(); + let na = -nb; + let nab = Vec2(na.0, nb.1); + let nba = Vec2(nb.0, na.1); + let na0 = Vec2(na.0, 0.0); + let nb0 = Vec2(nb.0, 0.0); + let n0a = Vec2(0.0, na.1); + let n0b = Vec2(0.0, nb.1); + + // We must add corners separately to ensure correct interpolation of dir + // values, hence need 16 points: + let ab = Vertex(ab, col1, col2, nab); + let ba = Vertex(ba, col1, col2, nba); + let cd = Vertex(cd, col1, col2, n0); + let dc = Vertex(dc, col1, col2, n0); + + let ac = Vertex(Vec2(aa.0, cc.1), col1, col2, na0); + let ad = Vertex(Vec2(aa.0, dd.1), col1, col2, na0); + let bc = Vertex(Vec2(bb.0, cc.1), col1, col2, nb0); + let bd = Vertex(Vec2(bb.0, dd.1), col1, col2, nb0); + + let ca = Vertex(Vec2(cc.0, aa.1), col1, col2, n0a); + let cb = Vertex(Vec2(cc.0, bb.1), col1, col2, n0b); + let da = Vertex(Vec2(dd.0, aa.1), col1, col2, n0a); + let db = Vertex(Vec2(dd.0, bb.1), col1, col2, n0b); + + let aa = Vertex(aa, col1, col2, na); + let bb = Vertex(bb, col1, col2, nb); + let cc = Vertex(cc, col1, col2, n0); + let dd = Vertex(dd, col1, col2, n0); + + // TODO: the four sides are simple rectangles, hence could use simpler rendering + + #[rustfmt::skip] + self.add_vertices(pass.pass(), &[ + // top bar: ba - dc - cc - aa + ba, dc, da, + da, dc, cc, + da, cc, ca, + ca, cc, aa, + // left bar: aa - cc - cd - ab + aa, cc, ac, + ac, cc, cd, + ac, cd, ad, + ad, cd, ab, + // bottom bar: ab - cd - dd - bb + ab, cd, cb, + cb, cd, dd, + cb, dd, db, + db, dd, bb, + // right bar: bb - dd - dc - ba + bb, dd, bd, + bd, dd, dc, + bd, dc, bc, + bc, dc, ba, + ]); + } +} diff --git a/crates/kas-wgpu/src/draw/shaders.rs b/crates/kas-wgpu/src/draw/shaders.rs index 5257aa8a5..96422ed6d 100644 --- a/crates/kas-wgpu/src/draw/shaders.rs +++ b/crates/kas-wgpu/src/draw/shaders.rs @@ -10,11 +10,13 @@ use wgpu::{include_spirv, ShaderModule}; /// Shader manager pub struct ShaderManager { pub vert_flat_round: ShaderModule, + pub vert_round_2col: ShaderModule, pub vert_shaded_square: ShaderModule, pub vert_shaded_round: ShaderModule, pub vert_image: ShaderModule, pub vert_glyph: ShaderModule, pub frag_flat_round: ShaderModule, + pub frag_round_2col: ShaderModule, pub frag_shaded_square: ShaderModule, pub frag_shaded_round: ShaderModule, pub frag_image: ShaderModule, @@ -30,12 +32,14 @@ macro_rules! create { impl ShaderManager { pub fn new(device: &wgpu::Device) -> Self { let vert_flat_round = create!(device, "shaders/flat_round.vert.spv"); + let vert_round_2col = create!(device, "shaders/round_2col.vert.spv"); let vert_shaded_square = create!(device, "shaders/shaded_square.vert.spv"); let vert_shaded_round = create!(device, "shaders/shaded_round.vert.spv"); let vert_image = create!(device, "shaders/image.vert.spv"); let vert_glyph = create!(device, "shaders/glyph.vert.spv"); let frag_flat_round = create!(device, "shaders/flat_round.frag.spv"); + let frag_round_2col = create!(device, "shaders/round_2col.frag.spv"); let frag_shaded_square = create!(device, "shaders/shaded_square.frag.spv"); let frag_shaded_round = create!(device, "shaders/shaded_round.frag.spv"); let frag_image = create!(device, "shaders/image.frag.spv"); @@ -45,9 +49,11 @@ impl ShaderManager { vert_image, vert_glyph, vert_flat_round, + vert_round_2col, vert_shaded_square, vert_shaded_round, frag_flat_round, + frag_round_2col, frag_shaded_square, frag_shaded_round, frag_image, diff --git a/crates/kas-wgpu/src/draw/shaders/round_2col.frag b/crates/kas-wgpu/src/draw/shaders/round_2col.frag new file mode 100644 index 000000000..d3479c242 --- /dev/null +++ b/crates/kas-wgpu/src/draw/shaders/round_2col.frag @@ -0,0 +1,25 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License in the LICENSE-APACHE file or at: +// https://www.apache.org/licenses/LICENSE-2.0 + +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +precision mediump float; + +layout(location = 0) flat in vec4 col1; +layout(location = 1) flat in vec4 col2; +layout(location = 2) in vec2 pos; + +layout(location = 0) out vec4 outColor; + +void main() { + vec2 pos2 = pos * pos; + float ss = pos2.x + pos2.y; + if (!(ss <= 1.0)) { + discard; + } + float r = sqrt(ss); + outColor = mix(col1, col2, r); +} diff --git a/crates/kas-wgpu/src/draw/shaders/round_2col.frag.spv b/crates/kas-wgpu/src/draw/shaders/round_2col.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..c14e29778c6583bb7bf40d4194f522faf0b8a3fa GIT binary patch literal 1496 zcmYk5+iuf95Qdi~=^;(eC(dnJC_NEc1PBQMDhT-|QW1RsD~ zuliCTM5X@U+KYCi&HnSxKfB}ENo8bX%$QrIZl=sLQ`npt5o64x8MS-RK5nGMT5?; zlSg6pt<#H!VfytX>gE}mI-20yHhCUHLsd+|`J_$)#d-84CHKHn_g zo*O8idoVC}V1e?$54GqAv4(aGJXJu8r#(zv7@EZbTD;=n+aA8-VZH++cfEiXZ+Mt@ z!>A8TeHb~sC+0A9p@a8z+lXZ7=mqAm%Yi3{86vi;9Ru$b(Bh{aZk2dJ9iwMWX_-Xu zve2mZHLVrZ#H`^HTa!Po9gc-x*UsJeZ%Uu{fFnLr#yDe1{+#9YBik+g`NIA0FUcpL zIC*z%{Kxx6RdG+4x*lmK#w>ErW9@tcJayo2mK^aX!kp>i`1BhNJwCm}f2C>UdpR59 z+&^M!+R@SjX3>2!-^e`qezWx7N)OLW(ii75m-rVtl{oK-2L7FH0g82C=>Px# literal 0 HcmV?d00001 diff --git a/crates/kas-wgpu/src/draw/shaders/round_2col.vert b/crates/kas-wgpu/src/draw/shaders/round_2col.vert new file mode 100644 index 000000000..7145250ff --- /dev/null +++ b/crates/kas-wgpu/src/draw/shaders/round_2col.vert @@ -0,0 +1,30 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License in the LICENSE-APACHE file or at: +// https://www.apache.org/licenses/LICENSE-2.0 + +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +precision mediump float; + +layout(location = 0) in vec2 a_pos; +layout(location = 1) in vec4 a_col1; +layout(location = 2) in vec4 a_col2; +layout(location = 3) in vec2 a_v; + +layout(location = 0) flat out vec4 b_col1; +layout(location = 1) flat out vec4 b_col2; +layout(location = 2) out vec2 b_v; + +layout(set = 0, binding = 0) uniform VertexCommon { + vec2 offset; + vec2 scale; +}; + +void main() { + gl_Position = vec4(scale * (a_pos.xy + offset), 0.0, 1.0); + b_col1 = a_col1; + b_col2 = a_col2; + b_v = a_v; +} diff --git a/crates/kas-wgpu/src/draw/shaders/round_2col.vert.spv b/crates/kas-wgpu/src/draw/shaders/round_2col.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..8596f4e6a572377952a3e8c3e3d1c34a15e4304b GIT binary patch literal 1936 zcmYk6T~8BH5QdMGmM;Yn0Y9)7Q2eMO8Z{JhNaB7JbmO6-;?sz@$x>Y0?<6PP zazIrP7C1Isn#FmX_JvOPc<$}-xSt=!-*qy33XXe; z+ezZVyEx0kekVHP=o;KflALi>^`hvYbJR1hn4*%MwAa(2lA0KBBt1UPqWnKbEmVFAT#5V>T@@*u?D#v3tw)@#e1sNGygk5ONZOPt2s0oM7Z^DJMNz zlOpD?7Osz7F4^=DAr8Cly&~telD$&0ZXyN9|hS9%1Yi zvT9lz(q-+{kB#@4^`d;{(~u&~kv}Dy`>x2J*2;dcx-8y?<$yyQiaW2BGvQy3e7-9% z&Y?FADS0^ue@==}*F~+IL(O&hycaN({Fk)Cfs-GfcLZknF3ToA@rxtQ_Foxs`0U5$ zJy|`ReN&2%&wA$Ek2mDIC7io(clP6Nj4=2+vdLvUK5qxi;&~V3z=!88z_b6MRyeB< zpEqFf)XUt7f39zgdox3Xm_I_QTDSF|yDv%U4?TIMNb>Ssft%0wLl11vb=mA;E*8Ug z1^!cc$-%e9KJZ^7jJHT`xc9>7-KrEEF>FmX7 Date: Mon, 16 Aug 2021 14:40:05 +0100 Subject: [PATCH 05/16] Add shadows to button/checkbox/radiobox --- crates/kas-core/src/draw/draw_rounded.rs | 6 +++ crates/kas-theme/src/dim.rs | 12 +++++ crates/kas-theme/src/flat_theme.rs | 63 +++++++++++++++++++----- crates/kas-theme/src/shaded_theme.rs | 2 + crates/kas-wgpu/src/draw/draw_pipe.rs | 4 +- 5 files changed, 72 insertions(+), 15 deletions(-) diff --git a/crates/kas-core/src/draw/draw_rounded.rs b/crates/kas-core/src/draw/draw_rounded.rs index 682b9959e..68b03355f 100644 --- a/crates/kas-core/src/draw/draw_rounded.rs +++ b/crates/kas-core/src/draw/draw_rounded.rs @@ -38,6 +38,9 @@ pub trait DrawRounded: Draw { /// /// Colour `col1` is used at the centre and `col2` at the edge with linear /// blending. The edge is not anti-aliased. + /// + /// Note: this is drawn *before* other drawables, allowing it to be used + /// for shadows without masking. fn circle_2col(&mut self, rect: Quad, col1: Rgba, col2: Rgba); /// Draw a frame with rounded corners and uniform colour @@ -57,6 +60,9 @@ pub trait DrawRounded: Draw { /// /// This is a variant of `rounded_frame` which blends between two colours, /// `c1` at the inner edge and `c2` at the outer edge. + /// + /// Note: this is drawn *before* other drawables, allowing it to be used + /// for shadows without masking. fn rounded_frame_2col(&mut self, outer: Quad, inner: Quad, c1: Rgba, c2: Rgba); } diff --git a/crates/kas-theme/src/dim.rs b/crates/kas-theme/src/dim.rs index bc817e46c..a88ca4cce 100644 --- a/crates/kas-theme/src/dim.rs +++ b/crates/kas-theme/src/dim.rs @@ -42,6 +42,10 @@ pub struct Parameters { pub slider_size: Vec2, /// Progress bar size (horizontal) pub progress_bar: Vec2, + /// Shadow size (average) + pub shadow_size: Vec2, + /// Proportional offset of shadow (range: -1..=1) + pub shadow_rel_offset: Vec2, } /// Dimensions available within [`Window`] @@ -63,6 +67,8 @@ pub struct Dimensions { pub scrollbar: Size, pub slider: Size, pub progress_bar: Size, + pub shadow_a: Vec2, + pub shadow_b: Vec2, } impl Dimensions { @@ -81,6 +87,10 @@ impl Dimensions { let frame_margin = (params.frame_margin * scale_factor).cast_nearest(); let text_margin = (params.text_margin * scale_factor).cast_nearest(); let frame = (params.frame_size * scale_factor).cast_nearest(); + + let shadow_size = params.shadow_size * scale_factor; + let shadow_offset = shadow_size * params.shadow_rel_offset; + Dimensions { scale_factor, dpp, @@ -99,6 +109,8 @@ impl Dimensions { scrollbar: Size::from(params.scrollbar_size * scale_factor), slider: Size::from(params.slider_size * scale_factor), progress_bar: Size::from(params.progress_bar * scale_factor), + shadow_a: shadow_offset - shadow_size, + shadow_b: shadow_offset + shadow_size, } } } diff --git a/crates/kas-theme/src/flat_theme.rs b/crates/kas-theme/src/flat_theme.rs index 1ce8860fa..6a22ea9bb 100644 --- a/crates/kas-theme/src/flat_theme.rs +++ b/crates/kas-theme/src/flat_theme.rs @@ -25,6 +25,9 @@ use kas::TkAction; // Also the maximum inner radius of circular borders to overlap with this rect. const BG_SHRINK_FACTOR: f32 = 1.0 - std::f32::consts::FRAC_1_SQRT_2; +// Shadow enlargement on hover +const SHADOW_HOVER: f32 = 1.2; + /// A theme with flat (unshaded) rendering #[derive(Clone, Debug)] pub struct FlatTheme { @@ -84,6 +87,8 @@ const DIMS: dim::Parameters = dim::Parameters { scrollbar_size: Vec2::splat(8.0), slider_size: Vec2(16.0, 16.0), progress_bar: Vec2::splat(8.0), + shadow_size: Vec2(5.0, 4.0), + shadow_rel_offset: Vec2(0.3, 0.8), }; pub struct DrawHandle<'a, DS: DrawSharedImpl> { @@ -207,13 +212,30 @@ impl<'a, DS: DrawSharedImpl> DrawHandle<'a, DS> where DS::Draw: DrawRoundedImpl, { - fn button_frame(&mut self, outer: Quad, col_frame: Rgba, col_bg: Rgba) -> Quad { - if col_bg != self.cols.background { - let inner = outer.shrink(self.window.dims.button_frame as f32 * BG_SHRINK_FACTOR); - self.draw.rect(inner, col_bg); + fn button_frame( + &mut self, + outer: Quad, + col_frame: Rgba, + col_bg: Rgba, + state: InputState, + ) -> Quad { + let inner = outer.shrink(self.window.dims.button_frame as f32); + let col_bg = ColorsLinear::adjust_for_state(col_bg, state); + + if !(state.disabled || state.depress) { + let (mut a, mut b) = (self.window.dims.shadow_a, self.window.dims.shadow_b); + if state.hover || state.nav_focus { + a = a * SHADOW_HOVER; + b = b * SHADOW_HOVER; + } + let shadow_outer = Quad::with_coords(a + inner.a, b + inner.b); + self.draw + .rounded_frame_2col(shadow_outer, inner, Rgba::BLACK, Rgba::TRANSPARENT); } - let inner = outer.shrink(self.window.dims.button_frame as f32); + let bgr = outer.shrink(self.window.dims.button_frame as f32 * BG_SHRINK_FACTOR); + self.draw.rect(bgr, col_bg); + self.draw .rounded_frame(outer, inner, BG_SHRINK_FACTOR, col_frame); inner @@ -404,9 +426,8 @@ where } else { col.map(|c| c.into()).unwrap_or(self.cols.background) }; - let col_bg = ColorsLinear::adjust_for_state(col_bg, state); let col_frame = self.cols.nav_region(state).unwrap_or(self.cols.frame); - self.button_frame(outer, col_frame, col_bg); + self.button_frame(outer, col_frame, col_bg, state); } fn edit_box(&mut self, rect: Rect, mut state: InputState) { @@ -418,7 +439,15 @@ where false => self.cols.edit_bg, }; let col_bg = ColorsLinear::adjust_for_state(col_bg, state); - self.button_frame(outer, self.cols.frame, col_bg); + + if col_bg != self.cols.background { + let inner = outer.shrink(self.window.dims.button_frame as f32 * BG_SHRINK_FACTOR); + self.draw.rect(inner, col_bg); + } + + let inner = outer.shrink(self.window.dims.button_frame as f32); + self.draw + .rounded_frame(outer, inner, BG_SHRINK_FACTOR, self.cols.frame); if state.nav_focus { let r = 0.5 * self.window.dims.button_frame as f32; @@ -432,9 +461,8 @@ where fn checkbox(&mut self, rect: Rect, checked: bool, state: InputState) { let outer = Quad::from(rect); - let col_bg = ColorsLinear::adjust_for_state(self.cols.background, state); let col_frame = self.cols.nav_region(state).unwrap_or(self.cols.frame); - let inner = self.button_frame(outer, col_frame, col_bg); + let inner = self.button_frame(outer, col_frame, self.cols.background, state); if let Some(col) = self.cols.check_mark_state(state, checked) { let inner = inner.shrink((2 * self.window.dims.inner_margin) as f32); @@ -445,11 +473,20 @@ where fn radiobox(&mut self, rect: Rect, checked: bool, state: InputState) { let outer = Quad::from(rect); - let col = ColorsLinear::adjust_for_state(self.cols.background, state); - if col != self.cols.background { - self.draw.circle(outer, 0.0, col); + if !(state.disabled || state.depress) { + let (mut a, mut b) = (self.window.dims.shadow_a, self.window.dims.shadow_b); + if state.hover || state.nav_focus { + a = a * SHADOW_HOVER; + b = b * SHADOW_HOVER; + } + let shadow_outer = Quad::with_coords(a + outer.a, b + outer.b); + self.draw + .circle_2col(shadow_outer, Rgba::BLACK, Rgba::TRANSPARENT); } + let col = ColorsLinear::adjust_for_state(self.cols.background, state); + self.draw.circle(outer, 0.0, col); + let col = self.cols.nav_region(state).unwrap_or(self.cols.frame); const F: f32 = 2.0 * (1.0 - BG_SHRINK_FACTOR); // match checkbox frame let r = 1.0 - F * self.window.dims.button_frame as f32 / rect.size.0 as f32; diff --git a/crates/kas-theme/src/shaded_theme.rs b/crates/kas-theme/src/shaded_theme.rs index 0c0ea367e..164b0a682 100644 --- a/crates/kas-theme/src/shaded_theme.rs +++ b/crates/kas-theme/src/shaded_theme.rs @@ -66,6 +66,8 @@ const DIMS: dim::Parameters = dim::Parameters { scrollbar_size: Vec2::splat(8.0), slider_size: Vec2(12.0, 25.0), progress_bar: Vec2::splat(12.0), + shadow_size: Vec2::ZERO, + shadow_rel_offset: Vec2::ZERO, }; pub struct DrawHandle<'a, DS: DrawSharedImpl> { diff --git a/crates/kas-wgpu/src/draw/draw_pipe.rs b/crates/kas-wgpu/src/draw/draw_pipe.rs index 4c1703ed9..408cd844a 100644 --- a/crates/kas-wgpu/src/draw/draw_pipe.rs +++ b/crates/kas-wgpu/src/draw/draw_pipe.rs @@ -262,6 +262,8 @@ impl DrawPipe { rect.size.1.cast(), ); + self.round_2col + .render(&window.round_2col, pass, &mut rpass, bg_common); self.shaded_square .render(&window.shaded_square, pass, &mut rpass, bg_common); self.images @@ -270,8 +272,6 @@ impl DrawPipe { .render(&window.shaded_round, pass, &mut rpass, bg_common); self.flat_round .render(&window.flat_round, pass, &mut rpass, bg_common); - self.round_2col - .render(&window.round_2col, pass, &mut rpass, bg_common); self.custom.render_pass( &mut window.custom, &self.device, From a79e61e73edf2aa734ca0efa5dfa6adc3d710055 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 16 Aug 2021 14:48:57 +0100 Subject: [PATCH 06/16] =?UTF-8?q?Themes:=20rename=20window=20=E2=86=92=20w?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/kas-theme/src/flat_theme.rs | 54 +++++++++++++--------------- crates/kas-theme/src/shaded_theme.rs | 36 +++++++++---------- 2 files changed, 41 insertions(+), 49 deletions(-) diff --git a/crates/kas-theme/src/flat_theme.rs b/crates/kas-theme/src/flat_theme.rs index 6a22ea9bb..8c8bf0181 100644 --- a/crates/kas-theme/src/flat_theme.rs +++ b/crates/kas-theme/src/flat_theme.rs @@ -93,7 +93,7 @@ const DIMS: dim::Parameters = dim::Parameters { pub struct DrawHandle<'a, DS: DrawSharedImpl> { pub(crate) draw: DrawIface<'a, DS>, - pub(crate) window: &'a mut dim::Window, + pub(crate) w: &'a mut dim::Window, pub(crate) cols: &'a ColorsLinear, } @@ -139,16 +139,12 @@ where dim::Window::new(DIMS, self.config.font_size(), dpi_factor, fonts) } - fn update_window(&self, window: &mut Self::Window, dpi_factor: f32) { - window.update(DIMS, self.config.font_size(), dpi_factor); + fn update_window(&self, w: &mut Self::Window, dpi_factor: f32) { + w.update(DIMS, self.config.font_size(), dpi_factor); } #[cfg(not(feature = "gat"))] - unsafe fn draw_handle( - &self, - draw: DrawIface, - window: &mut Self::Window, - ) -> Self::DrawHandle { + unsafe fn draw_handle(&self, draw: DrawIface, w: &mut Self::Window) -> Self::DrawHandle { unsafe fn extend_lifetime<'b, T: ?Sized>(r: &'b T) -> &'static T { std::mem::transmute::<&'b T, &'static T>(r) } @@ -161,7 +157,7 @@ where shared: extend_lifetime_mut(draw.shared), pass: draw.pass, }, - window: extend_lifetime_mut(window), + w: extend_lifetime_mut(w), cols: extend_lifetime(&self.cols), } } @@ -169,11 +165,11 @@ where fn draw_handle<'a>( &'a self, draw: DrawIface<'a, DS>, - window: &'a mut Self::Window, + w: &'a mut Self::Window, ) -> Self::DrawHandle<'a> { DrawHandle { draw, - window, + w, cols: &self.cols, } } @@ -219,11 +215,11 @@ where col_bg: Rgba, state: InputState, ) -> Quad { - let inner = outer.shrink(self.window.dims.button_frame as f32); + let inner = outer.shrink(self.w.dims.button_frame as f32); let col_bg = ColorsLinear::adjust_for_state(col_bg, state); if !(state.disabled || state.depress) { - let (mut a, mut b) = (self.window.dims.shadow_a, self.window.dims.shadow_b); + let (mut a, mut b) = (self.w.dims.shadow_a, self.w.dims.shadow_b); if state.hover || state.nav_focus { a = a * SHADOW_HOVER; b = b * SHADOW_HOVER; @@ -233,7 +229,7 @@ where .rounded_frame_2col(shadow_outer, inner, Rgba::BLACK, Rgba::TRANSPARENT); } - let bgr = outer.shrink(self.window.dims.button_frame as f32 * BG_SHRINK_FACTOR); + let bgr = outer.shrink(self.w.dims.button_frame as f32 * BG_SHRINK_FACTOR); self.draw.rect(bgr, col_bg); self.draw @@ -247,7 +243,7 @@ where DS::Draw: DrawRoundedImpl, { fn size_handle(&mut self) -> &mut dyn SizeHandle { - self.window + self.w } fn draw_device(&mut self) -> &mut dyn Draw { @@ -263,7 +259,7 @@ where ) { let mut outer_rect = inner_rect; if class == PassType::Overlay { - outer_rect = inner_rect.expand(self.window.dims.frame); + outer_rect = inner_rect.expand(self.w.dims.frame); } let mut draw = self.draw.new_pass(outer_rect, offset, class); @@ -271,12 +267,12 @@ where let outer = Quad::from(outer_rect + offset); let inner = Quad::from(inner_rect + offset); draw.rounded_frame(outer, inner, BG_SHRINK_FACTOR, self.cols.frame); - let inner = outer.shrink(self.window.dims.frame as f32 * BG_SHRINK_FACTOR); + let inner = outer.shrink(self.w.dims.frame as f32 * BG_SHRINK_FACTOR); draw.rect(inner, self.cols.background); } let mut handle = DrawHandle { - window: self.window, + w: self.w, draw, cols: self.cols, }; @@ -289,7 +285,7 @@ where fn outer_frame(&mut self, rect: Rect) { let outer = Quad::from(rect); - let inner = outer.shrink(self.window.dims.frame as f32); + let inner = outer.shrink(self.w.dims.frame as f32); self.draw .rounded_frame(outer, inner, BG_SHRINK_FACTOR, self.cols.frame); } @@ -304,14 +300,14 @@ where fn nav_frame(&mut self, rect: Rect, state: InputState) { if let Some(col) = self.cols.nav_region(state) { let outer = Quad::from(rect); - let inner = outer.shrink(self.window.dims.inner_margin as f32); + let inner = outer.shrink(self.w.dims.inner_margin as f32); self.draw.rounded_frame(outer, inner, 0.0, col); } } fn selection_box(&mut self, rect: Rect) { let inner = Quad::from(rect); - let outer = inner.grow(self.window.dims.inner_margin.into()); + let outer = inner.grow(self.w.dims.inner_margin.into()); // TODO: this should use its own colour and a stippled pattern let col = self.cols.text_sel_bg; self.draw.frame(outer, inner, col); @@ -383,7 +379,7 @@ where } fn edit_marker(&mut self, pos: Coord, text: &TextDisplay, _: TextClass, byte: usize) { - let width = self.window.dims.font_marker_width; + let width = self.w.dims.font_marker_width; let pos = Vec2::from(pos); let mut col = self.cols.nav_focus; @@ -441,16 +437,16 @@ where let col_bg = ColorsLinear::adjust_for_state(col_bg, state); if col_bg != self.cols.background { - let inner = outer.shrink(self.window.dims.button_frame as f32 * BG_SHRINK_FACTOR); + let inner = outer.shrink(self.w.dims.button_frame as f32 * BG_SHRINK_FACTOR); self.draw.rect(inner, col_bg); } - let inner = outer.shrink(self.window.dims.button_frame as f32); + let inner = outer.shrink(self.w.dims.button_frame as f32); self.draw .rounded_frame(outer, inner, BG_SHRINK_FACTOR, self.cols.frame); if state.nav_focus { - let r = 0.5 * self.window.dims.button_frame as f32; + let r = 0.5 * self.w.dims.button_frame as f32; let y = outer.b.1 - r; let a = Vec2(outer.a.0 + r, y); let b = Vec2(outer.b.0 - r, y); @@ -465,7 +461,7 @@ where let inner = self.button_frame(outer, col_frame, self.cols.background, state); if let Some(col) = self.cols.check_mark_state(state, checked) { - let inner = inner.shrink((2 * self.window.dims.inner_margin) as f32); + let inner = inner.shrink((2 * self.w.dims.inner_margin) as f32); self.draw.rect(inner, col); } } @@ -474,7 +470,7 @@ where let outer = Quad::from(rect); if !(state.disabled || state.depress) { - let (mut a, mut b) = (self.window.dims.shadow_a, self.window.dims.shadow_b); + let (mut a, mut b) = (self.w.dims.shadow_a, self.w.dims.shadow_b); if state.hover || state.nav_focus { a = a * SHADOW_HOVER; b = b * SHADOW_HOVER; @@ -489,11 +485,11 @@ where let col = self.cols.nav_region(state).unwrap_or(self.cols.frame); const F: f32 = 2.0 * (1.0 - BG_SHRINK_FACTOR); // match checkbox frame - let r = 1.0 - F * self.window.dims.button_frame as f32 / rect.size.0 as f32; + let r = 1.0 - F * self.w.dims.button_frame as f32 / rect.size.0 as f32; self.draw.circle(outer, r, col); if let Some(col) = self.cols.check_mark_state(state, checked) { - let r = self.window.dims.button_frame + 2 * self.window.dims.inner_margin as i32; + let r = self.w.dims.button_frame + 2 * self.w.dims.inner_margin as i32; let inner = outer.shrink(r as f32); self.draw.circle(inner, 0.0, col); } diff --git a/crates/kas-theme/src/shaded_theme.rs b/crates/kas-theme/src/shaded_theme.rs index 164b0a682..40685aa49 100644 --- a/crates/kas-theme/src/shaded_theme.rs +++ b/crates/kas-theme/src/shaded_theme.rs @@ -72,7 +72,7 @@ const DIMS: dim::Parameters = dim::Parameters { pub struct DrawHandle<'a, DS: DrawSharedImpl> { draw: DrawIface<'a, DS>, - window: &'a mut dim::Window, + w: &'a mut dim::Window, cols: &'a ColorsLinear, } @@ -105,16 +105,12 @@ where dim::Window::new(DIMS, self.flat.config.font_size(), dpi_factor, fonts) } - fn update_window(&self, window: &mut Self::Window, dpi_factor: f32) { - window.update(DIMS, self.flat.config.font_size(), dpi_factor); + fn update_window(&self, w: &mut Self::Window, dpi_factor: f32) { + w.update(DIMS, self.flat.config.font_size(), dpi_factor); } #[cfg(not(feature = "gat"))] - unsafe fn draw_handle( - &self, - draw: DrawIface, - window: &mut Self::Window, - ) -> Self::DrawHandle { + unsafe fn draw_handle(&self, draw: DrawIface, w: &mut Self::Window) -> Self::DrawHandle { unsafe fn extend_lifetime<'b, T: ?Sized>(r: &'b T) -> &'static T { std::mem::transmute::<&'b T, &'static T>(r) } @@ -127,7 +123,7 @@ where shared: extend_lifetime_mut(draw.shared), pass: draw.pass, }, - window: extend_lifetime_mut(window), + w: extend_lifetime_mut(w), cols: extend_lifetime(&self.flat.cols), } } @@ -135,11 +131,11 @@ where fn draw_handle<'a>( &'a self, draw: DrawIface<'a, DS>, - window: &'a mut Self::Window, + w: &'a mut Self::Window, ) -> Self::DrawHandle<'a> { DrawHandle { draw, - window, + w, cols: &self.flat.cols, } } @@ -175,7 +171,7 @@ where { super::flat_theme::DrawHandle { draw: self.draw.reborrow(), - window: self.window, + w: self.w, cols: self.cols, } } @@ -188,7 +184,7 @@ where /// - `nav_col`: colour of navigation highlight, if visible fn draw_edit_box(&mut self, outer: Rect, bg_col: Rgba, nav_col: Option) -> Quad { let mut outer = Quad::from(outer); - let mut inner = outer.shrink(self.window.dims.frame as f32); + let mut inner = outer.shrink(self.w.dims.frame as f32); let col = self.cols.background; self.draw @@ -196,7 +192,7 @@ where if let Some(col) = nav_col { outer = inner; - inner = outer.shrink(self.window.dims.inner_margin as f32); + inner = outer.shrink(self.w.dims.inner_margin as f32); self.draw.frame(outer, inner, col); } @@ -224,7 +220,7 @@ where DS::Draw: DrawRoundedImpl + DrawShadedImpl, { fn size_handle(&mut self) -> &mut dyn SizeHandle { - self.window + self.w } fn draw_device(&mut self) -> &mut dyn Draw { @@ -240,7 +236,7 @@ where ) { let mut outer_rect = inner_rect; if class == PassType::Overlay { - outer_rect = inner_rect.expand(self.window.dims.frame); + outer_rect = inner_rect.expand(self.w.dims.frame); } let mut draw = self.draw.new_pass(outer_rect, offset, class); @@ -253,7 +249,7 @@ where } let mut handle = DrawHandle { - window: self.window, + w: self.w, draw, cols: self.cols, }; @@ -266,7 +262,7 @@ where fn outer_frame(&mut self, rect: Rect) { let outer = Quad::from(rect); - let inner = outer.shrink(self.window.dims.frame as f32); + let inner = outer.shrink(self.w.dims.frame as f32); let norm = (0.7, -0.7); let col = self.cols.background; self.draw.shaded_round_frame(outer, inner, norm, col); @@ -320,7 +316,7 @@ where fn button(&mut self, rect: Rect, col: Option, state: InputState) { let outer = Quad::from(rect); - let inner = outer.shrink(self.window.dims.button_frame as f32); + let inner = outer.shrink(self.w.dims.button_frame as f32); let col = col.map(|c| c.into()).unwrap_or(self.cols.accent_soft); let col = ColorsLinear::adjust_for_state(col, state); @@ -328,7 +324,7 @@ where self.draw.rect(inner, col); if let Some(col) = self.cols.nav_region(state) { - let outer = outer.shrink(self.window.dims.inner_margin as f32); + let outer = outer.shrink(self.w.dims.inner_margin as f32); self.draw.rounded_frame(outer, inner, 0.6, col); } } From 4d5f958402d52d9d0f4cd4301e5beea5da756093 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 16 Aug 2021 15:04:02 +0100 Subject: [PATCH 07/16] FlatTheme::edit_box: draw shadow on underline --- crates/kas-theme/src/flat_theme.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/crates/kas-theme/src/flat_theme.rs b/crates/kas-theme/src/flat_theme.rs index 8c8bf0181..18f35ad47 100644 --- a/crates/kas-theme/src/flat_theme.rs +++ b/crates/kas-theme/src/flat_theme.rs @@ -87,8 +87,8 @@ const DIMS: dim::Parameters = dim::Parameters { scrollbar_size: Vec2::splat(8.0), slider_size: Vec2(16.0, 16.0), progress_bar: Vec2::splat(8.0), - shadow_size: Vec2(5.0, 4.0), - shadow_rel_offset: Vec2(0.3, 0.8), + shadow_size: Vec2(5.0, 5.0), + shadow_rel_offset: Vec2(0.3, 0.6), }; pub struct DrawHandle<'a, DS: DrawSharedImpl> { @@ -445,12 +445,25 @@ where self.draw .rounded_frame(outer, inner, BG_SHRINK_FACTOR, self.cols.frame); - if state.nav_focus { + if state.nav_focus || state.hover { let r = 0.5 * self.w.dims.button_frame as f32; let y = outer.b.1 - r; let a = Vec2(outer.a.0 + r, y); let b = Vec2(outer.b.0 - r, y); - self.draw.rounded_line(a, b, r, self.cols.nav_focus); + + const F: f32 = 0.5; + let (sa, sb) = (self.w.dims.shadow_a * F, self.w.dims.shadow_b * F); + let outer = Quad::with_coords(a + sa, b + sb); + let inner = Quad::with_coords(a, b); + let (c1, c2) = (Rgba::BLACK, Rgba::TRANSPARENT); + self.draw.rounded_frame_2col(outer, inner, c1, c2); + + let col = if state.nav_focus { + self.cols.nav_focus + } else { + Rgba::BLACK + }; + self.draw.rounded_line(a, b, r, col); } } From 212303ccdaa60f3f0bb49b317e1b8694bdf10031 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 16 Aug 2021 15:15:03 +0100 Subject: [PATCH 08/16] Dark theme: use coloured shadows --- crates/kas-theme/src/colors.rs | 7 ++++++ crates/kas-theme/src/flat_theme.rs | 37 +++++++++++++++++++----------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/crates/kas-theme/src/colors.rs b/crates/kas-theme/src/colors.rs index b73fbecb0..e72222d67 100644 --- a/crates/kas-theme/src/colors.rs +++ b/crates/kas-theme/src/colors.rs @@ -17,6 +17,8 @@ const MIN_HIGHLIGHT: f32 = 0.2; #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "config", derive(serde::Serialize, serde::Deserialize))] pub struct Colors { + /// True if this is a dark theme + pub is_dark: bool, /// Background colour pub background: C, /// Colour for frames (not always used) @@ -60,6 +62,7 @@ pub type ColorsLinear = Colors; impl From for ColorsLinear { fn from(col: ColorsSrgb) -> Self { Colors { + is_dark: col.is_dark, background: col.background.into(), frame: col.frame.into(), accent: col.accent.into(), @@ -78,6 +81,7 @@ impl From for ColorsLinear { impl From for ColorsSrgb { fn from(col: ColorsLinear) -> Self { Colors { + is_dark: col.is_dark, background: col.background.into(), frame: col.frame.into(), accent: col.accent.into(), @@ -111,6 +115,7 @@ impl ColorsSrgb { /// Default "light" scheme pub fn light() -> Self { Colors { + is_dark: false, background: Rgba8Srgb::from_str("#FAFAFA").unwrap(), frame: Rgba8Srgb::from_str("#BCBCBC").unwrap(), accent: Rgba8Srgb::from_str("#7E3FF2").unwrap(), @@ -128,6 +133,7 @@ impl ColorsSrgb { /// Dark scheme pub fn dark() -> Self { Colors { + is_dark: true, background: Rgba8Srgb::from_str("#404040").unwrap(), frame: Rgba8Srgb::from_str("#AAAAAA").unwrap(), accent: Rgba8Srgb::from_str("#F74C00").unwrap(), @@ -145,6 +151,7 @@ impl ColorsSrgb { /// Blue scheme pub fn blue() -> Self { Colors { + is_dark: false, background: Rgba8Srgb::from_str("#FFFFFF").unwrap(), frame: Rgba8Srgb::from_str("#DADADA").unwrap(), accent: Rgba8Srgb::from_str("#7CDAFF").unwrap(), diff --git a/crates/kas-theme/src/flat_theme.rs b/crates/kas-theme/src/flat_theme.rs index 18f35ad47..d4b4e2214 100644 --- a/crates/kas-theme/src/flat_theme.rs +++ b/crates/kas-theme/src/flat_theme.rs @@ -225,8 +225,15 @@ where b = b * SHADOW_HOVER; } let shadow_outer = Quad::with_coords(a + inner.a, b + inner.b); + let col1 = if self.cols.is_dark { + col_frame + } else { + Rgba::BLACK + }; + let mut col2 = col1; + col2.a = 0.0; self.draw - .rounded_frame_2col(shadow_outer, inner, Rgba::BLACK, Rgba::TRANSPARENT); + .rounded_frame_2col(shadow_outer, inner, col1, col2); } let bgr = outer.shrink(self.w.dims.button_frame as f32 * BG_SHRINK_FACTOR); @@ -450,19 +457,21 @@ where let y = outer.b.1 - r; let a = Vec2(outer.a.0 + r, y); let b = Vec2(outer.b.0 - r, y); + let col = if state.nav_focus { + self.cols.nav_focus + } else { + self.cols.text + }; const F: f32 = 0.5; let (sa, sb) = (self.w.dims.shadow_a * F, self.w.dims.shadow_b * F); let outer = Quad::with_coords(a + sa, b + sb); let inner = Quad::with_coords(a, b); - let (c1, c2) = (Rgba::BLACK, Rgba::TRANSPARENT); - self.draw.rounded_frame_2col(outer, inner, c1, c2); + let col1 = if self.cols.is_dark { col } else { Rgba::BLACK }; + let mut col2 = col1; + col2.a = 0.0; + self.draw.rounded_frame_2col(outer, inner, col1, col2); - let col = if state.nav_focus { - self.cols.nav_focus - } else { - Rgba::BLACK - }; self.draw.rounded_line(a, b, r, col); } } @@ -481,6 +490,7 @@ where fn radiobox(&mut self, rect: Rect, checked: bool, state: InputState) { let outer = Quad::from(rect); + let col = self.cols.nav_region(state).unwrap_or(self.cols.frame); if !(state.disabled || state.depress) { let (mut a, mut b) = (self.w.dims.shadow_a, self.w.dims.shadow_b); @@ -489,14 +499,15 @@ where b = b * SHADOW_HOVER; } let shadow_outer = Quad::with_coords(a + outer.a, b + outer.b); - self.draw - .circle_2col(shadow_outer, Rgba::BLACK, Rgba::TRANSPARENT); + let col1 = if self.cols.is_dark { col } else { Rgba::BLACK }; + let mut col2 = col1; + col2.a = 0.0; + self.draw.circle_2col(shadow_outer, col1, col2); } - let col = ColorsLinear::adjust_for_state(self.cols.background, state); - self.draw.circle(outer, 0.0, col); + let col_bg = ColorsLinear::adjust_for_state(self.cols.background, state); + self.draw.circle(outer, 0.0, col_bg); - let col = self.cols.nav_region(state).unwrap_or(self.cols.frame); const F: f32 = 2.0 * (1.0 - BG_SHRINK_FACTOR); // match checkbox frame let r = 1.0 - F * self.w.dims.button_frame as f32 / rect.size.0 as f32; self.draw.circle(outer, r, col); From 255e5983b44278370d0c2555d475576545081b6c Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 16 Aug 2021 15:47:46 +0100 Subject: [PATCH 09/16] FlatTheme: adjust shadow offset in dark mode --- crates/kas-theme/src/dim.rs | 6 ++-- crates/kas-theme/src/flat_theme.rs | 47 +++++++++++++++++++++------- crates/kas-theme/src/shaded_theme.rs | 4 +-- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/crates/kas-theme/src/dim.rs b/crates/kas-theme/src/dim.rs index a88ca4cce..d4668f876 100644 --- a/crates/kas-theme/src/dim.rs +++ b/crates/kas-theme/src/dim.rs @@ -72,7 +72,7 @@ pub struct Dimensions { } impl Dimensions { - pub fn new(params: Parameters, pt_size: f32, scale_factor: f32) -> Self { + pub fn new(params: &Parameters, pt_size: f32, scale_factor: f32) -> Self { let font_id = Default::default(); let dpp = scale_factor * (96.0 / 72.0); let dpem = dpp * pt_size; @@ -123,7 +123,7 @@ pub struct Window { impl Window { pub fn new( - dims: Parameters, + dims: &Parameters, pt_size: f32, scale_factor: f32, fonts: Rc>, @@ -134,7 +134,7 @@ impl Window { } } - pub fn update(&mut self, dims: Parameters, pt_size: f32, scale_factor: f32) { + pub fn update(&mut self, dims: &Parameters, pt_size: f32, scale_factor: f32) { self.dims = Dimensions::new(dims, pt_size, scale_factor); } } diff --git a/crates/kas-theme/src/flat_theme.rs b/crates/kas-theme/src/flat_theme.rs index d4b4e2214..105c45365 100644 --- a/crates/kas-theme/src/flat_theme.rs +++ b/crates/kas-theme/src/flat_theme.rs @@ -33,6 +33,7 @@ const SHADOW_HOVER: f32 = 1.2; pub struct FlatTheme { pub(crate) config: Config, pub(crate) cols: ColorsLinear, + dims: dim::Parameters, pub(crate) fonts: Option>>, } @@ -46,9 +47,15 @@ impl FlatTheme { /// Construct #[inline] pub fn new() -> Self { + let cols = ColorsLinear::default(); + let mut dims = DIMS; + if cols.is_dark { + dims.shadow_rel_offset = Vec2::ZERO; + } FlatTheme { config: Default::default(), - cols: ColorsLinear::default(), + cols, + dims, fonts: None, } } @@ -66,13 +73,29 @@ impl FlatTheme { /// /// If no scheme by this name is found the scheme is left unchanged. #[inline] - pub fn with_colours(mut self, scheme: &str) -> Self { - self.config.set_active_scheme(scheme); - if let Some(scheme) = self.config.get_color_scheme(scheme) { - self.cols = scheme.into(); + pub fn with_colours(mut self, name: &str) -> Self { + if let Some(scheme) = self.config.get_color_scheme(name) { + self.config.set_active_scheme(name); + let _ = self.set_colors(scheme.into()); } self } + + pub fn set_colors(&mut self, cols: ColorsLinear) -> TkAction { + let mut action = TkAction::REDRAW; + if cols.is_dark != self.cols.is_dark { + action |= TkAction::THEME_UPDATE; + if cols.is_dark { + self.dims.shadow_size = DARK_SHADOW_SIZE; + self.dims.shadow_rel_offset = DARK_SHADOW_OFFSET; + } else { + self.dims.shadow_size = DIMS.shadow_size; + self.dims.shadow_rel_offset = DIMS.shadow_rel_offset; + } + } + self.cols = cols; + action + } } const DIMS: dim::Parameters = dim::Parameters { @@ -90,6 +113,8 @@ const DIMS: dim::Parameters = dim::Parameters { shadow_size: Vec2(5.0, 5.0), shadow_rel_offset: Vec2(0.3, 0.6), }; +const DARK_SHADOW_SIZE: Vec2 = Vec2::splat(4.0); +const DARK_SHADOW_OFFSET: Vec2 = Vec2::ZERO; pub struct DrawHandle<'a, DS: DrawSharedImpl> { pub(crate) draw: DrawIface<'a, DS>, @@ -114,9 +139,9 @@ where } fn apply_config(&mut self, config: &Self::Config) -> TkAction { - let action = self.config.apply_config(config); + let mut action = self.config.apply_config(config); if let Some(scheme) = self.config.get_active_scheme() { - self.cols = scheme.into(); + action |= self.set_colors(scheme.into()); } action } @@ -136,11 +161,12 @@ where fn new_window(&self, dpi_factor: f32) -> Self::Window { let fonts = self.fonts.as_ref().unwrap().clone(); - dim::Window::new(DIMS, self.config.font_size(), dpi_factor, fonts) + dim::Window::new(&self.dims, self.config.font_size(), dpi_factor, fonts) } fn update_window(&self, w: &mut Self::Window, dpi_factor: f32) { - w.update(DIMS, self.config.font_size(), dpi_factor); + w.update(&self.dims, self.config.font_size(), dpi_factor); + println!("update_window: dims.shadow_b = {:?}", w.dims.shadow_b); } #[cfg(not(feature = "gat"))] @@ -195,9 +221,8 @@ impl ThemeApi for FlatTheme { fn set_scheme(&mut self, name: &str) -> TkAction { if name != self.config.active_scheme() { if let Some(scheme) = self.config.get_color_scheme(name) { - self.cols = scheme.into(); self.config.set_active_scheme(name); - return TkAction::REDRAW; + return self.set_colors(scheme.into()); } } TkAction::empty() diff --git a/crates/kas-theme/src/shaded_theme.rs b/crates/kas-theme/src/shaded_theme.rs index 40685aa49..ce646ce28 100644 --- a/crates/kas-theme/src/shaded_theme.rs +++ b/crates/kas-theme/src/shaded_theme.rs @@ -102,11 +102,11 @@ where fn new_window(&self, dpi_factor: f32) -> Self::Window { let fonts = self.flat.fonts.as_ref().unwrap().clone(); - dim::Window::new(DIMS, self.flat.config.font_size(), dpi_factor, fonts) + dim::Window::new(&DIMS, self.flat.config.font_size(), dpi_factor, fonts) } fn update_window(&self, w: &mut Self::Window, dpi_factor: f32) { - w.update(DIMS, self.flat.config.font_size(), dpi_factor); + w.update(&DIMS, self.flat.config.font_size(), dpi_factor); } #[cfg(not(feature = "gat"))] From 592dbadd380c24b68402f26b34eee56f65b959c7 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 16 Aug 2021 15:53:24 +0100 Subject: [PATCH 10/16] Tweak hover shadow --- crates/kas-theme/src/flat_theme.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/kas-theme/src/flat_theme.rs b/crates/kas-theme/src/flat_theme.rs index 105c45365..9d06948c6 100644 --- a/crates/kas-theme/src/flat_theme.rs +++ b/crates/kas-theme/src/flat_theme.rs @@ -113,7 +113,7 @@ const DIMS: dim::Parameters = dim::Parameters { shadow_size: Vec2(5.0, 5.0), shadow_rel_offset: Vec2(0.3, 0.6), }; -const DARK_SHADOW_SIZE: Vec2 = Vec2::splat(4.0); +const DARK_SHADOW_SIZE: Vec2 = Vec2::splat(5.0); const DARK_SHADOW_OFFSET: Vec2 = Vec2::ZERO; pub struct DrawHandle<'a, DS: DrawSharedImpl> { @@ -245,7 +245,7 @@ where if !(state.disabled || state.depress) { let (mut a, mut b) = (self.w.dims.shadow_a, self.w.dims.shadow_b); - if state.hover || state.nav_focus { + if state.hover { a = a * SHADOW_HOVER; b = b * SHADOW_HOVER; } @@ -519,10 +519,12 @@ where if !(state.disabled || state.depress) { let (mut a, mut b) = (self.w.dims.shadow_a, self.w.dims.shadow_b); - if state.hover || state.nav_focus { - a = a * SHADOW_HOVER; - b = b * SHADOW_HOVER; + let mut mult = 0.6; + if state.hover { + mult *= SHADOW_HOVER; } + a = a * mult; + b = b * mult; let shadow_outer = Quad::with_coords(a + outer.a, b + outer.b); let col1 = if self.cols.is_dark { col } else { Rgba::BLACK }; let mut col2 = col1; From 2c347ede0e442ad3bbb9e590106411bedfcc6d49 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 16 Aug 2021 15:59:56 +0100 Subject: [PATCH 11/16] FlatTheme::slider: add shadow to handle --- crates/kas-theme/src/flat_theme.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/crates/kas-theme/src/flat_theme.rs b/crates/kas-theme/src/flat_theme.rs index 9d06948c6..4d391eb84 100644 --- a/crates/kas-theme/src/flat_theme.rs +++ b/crates/kas-theme/src/flat_theme.rs @@ -600,6 +600,22 @@ where self.cols.background }; let col = ColorsLinear::adjust_for_state(col, state); + + if !(state.disabled || state.depress) { + let (mut a, mut b) = (self.w.dims.shadow_a, self.w.dims.shadow_b); + let mut mult = 0.6; + if state.hover { + mult *= SHADOW_HOVER; + } + a = a * mult; + b = b * mult; + let shadow_outer = Quad::with_coords(a + outer.a, b + outer.b); + let col1 = if self.cols.is_dark { col } else { Rgba::BLACK }; + let mut col2 = col1; + col2.a = 0.0; + self.draw.circle_2col(shadow_outer, col1, col2); + } + self.draw.circle(outer, 0.0, col); let col = self.cols.nav_region(state).unwrap_or(self.cols.frame); self.draw.circle(outer, 14.0 / 16.0, col); From 5b81c8ce26d7a5c940e60cfe995c346f59461ba7 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 16 Aug 2021 16:39:10 +0100 Subject: [PATCH 12/16] FlatTheme: add shadows to pop-up menus --- crates/kas-core/src/geom/vector.rs | 26 +++++++++++++++++++++++++- crates/kas-theme/src/flat_theme.rs | 16 ++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/crates/kas-core/src/geom/vector.rs b/crates/kas-core/src/geom/vector.rs index 82cd05ffb..cc567ce04 100644 --- a/crates/kas-core/src/geom/vector.rs +++ b/crates/kas-core/src/geom/vector.rs @@ -9,7 +9,7 @@ use crate::cast::{CastFloat, Conv}; use crate::geom::{Coord, Offset, Rect, Size}; -use std::ops::{Add, Div, Mul, Neg, Sub}; +use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub}; /// Axis-aligned 2D cuboid, specified via two corners `a` and `b` /// @@ -103,6 +103,14 @@ impl Quad { } } +impl AddAssign for Quad { + #[inline] + fn add_assign(&mut self, rhs: Vec2) { + self.a += rhs; + self.b += rhs; + } +} + impl From for Quad { #[inline] fn from(rect: Rect) -> Quad { @@ -290,6 +298,22 @@ macro_rules! impl_vec2 { } } + impl AddAssign<$T> for $T { + #[inline] + fn add_assign(&mut self, rhs: $T) { + self.0 += rhs.0; + self.1 += rhs.1; + } + } + + impl AddAssign<$f> for $T { + #[inline] + fn add_assign(&mut self, rhs: $f) { + self.0 += rhs; + self.1 += rhs; + } + } + impl Sub<$T> for $T { type Output = $T; #[inline] diff --git a/crates/kas-theme/src/flat_theme.rs b/crates/kas-theme/src/flat_theme.rs index 4d391eb84..6563cf42d 100644 --- a/crates/kas-theme/src/flat_theme.rs +++ b/crates/kas-theme/src/flat_theme.rs @@ -289,15 +289,27 @@ where class: PassType, f: &mut dyn FnMut(&mut dyn draw::DrawHandle), ) { + let mut frame_rect = Default::default(); + let mut shadow = Default::default(); let mut outer_rect = inner_rect; if class == PassType::Overlay { - outer_rect = inner_rect.expand(self.w.dims.frame); + frame_rect = inner_rect.expand(self.w.dims.frame); + shadow = Quad::from(frame_rect); + shadow.a += self.w.dims.shadow_a; + shadow.b += self.w.dims.shadow_b; + let a = shadow.a.floor(); + let b = shadow.b.ceil(); + outer_rect = Rect::new(a.into(), (b - a).into()); } let mut draw = self.draw.new_pass(outer_rect, offset, class); if class == PassType::Overlay { - let outer = Quad::from(outer_rect + offset); + shadow += offset.into(); + let outer = Quad::from(frame_rect + offset); let inner = Quad::from(inner_rect + offset); + + draw.rounded_frame_2col(shadow, inner, Rgba::BLACK, Rgba::TRANSPARENT); + draw.rounded_frame(outer, inner, BG_SHRINK_FACTOR, self.cols.frame); let inner = outer.shrink(self.w.dims.frame as f32 * BG_SHRINK_FACTOR); draw.rect(inner, self.cols.background); From 12d393e1ec3c20705348813e3790470fa12e168a Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 16 Aug 2021 16:45:17 +0100 Subject: [PATCH 13/16] Add shadow enlargement for pop-ups --- crates/kas-theme/src/flat_theme.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/kas-theme/src/flat_theme.rs b/crates/kas-theme/src/flat_theme.rs index 6563cf42d..0c30a4873 100644 --- a/crates/kas-theme/src/flat_theme.rs +++ b/crates/kas-theme/src/flat_theme.rs @@ -27,6 +27,8 @@ const BG_SHRINK_FACTOR: f32 = 1.0 - std::f32::consts::FRAC_1_SQRT_2; // Shadow enlargement on hover const SHADOW_HOVER: f32 = 1.2; +// Shadow enlargement for pop-ups +const SHADOW_POPUP: f32 = 1.4; /// A theme with flat (unshaded) rendering #[derive(Clone, Debug)] @@ -295,8 +297,8 @@ where if class == PassType::Overlay { frame_rect = inner_rect.expand(self.w.dims.frame); shadow = Quad::from(frame_rect); - shadow.a += self.w.dims.shadow_a; - shadow.b += self.w.dims.shadow_b; + shadow.a += self.w.dims.shadow_a * SHADOW_POPUP; + shadow.b += self.w.dims.shadow_b * SHADOW_POPUP; let a = shadow.a.floor(); let b = shadow.b.ceil(); outer_rect = Rect::new(a.into(), (b - a).into()); From a883305497df085a8d17519e392191e639b7e608 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 16 Aug 2021 17:19:30 +0100 Subject: [PATCH 14/16] ShadedTheme: use rounded shadow for pop-ups --- crates/kas-theme/src/shaded_theme.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/crates/kas-theme/src/shaded_theme.rs b/crates/kas-theme/src/shaded_theme.rs index ce646ce28..84f296472 100644 --- a/crates/kas-theme/src/shaded_theme.rs +++ b/crates/kas-theme/src/shaded_theme.rs @@ -66,7 +66,7 @@ const DIMS: dim::Parameters = dim::Parameters { scrollbar_size: Vec2::splat(8.0), slider_size: Vec2(12.0, 25.0), progress_bar: Vec2::splat(12.0), - shadow_size: Vec2::ZERO, + shadow_size: Vec2::splat(6.0), shadow_rel_offset: Vec2::ZERO, }; @@ -234,17 +234,23 @@ where class: PassType, f: &mut dyn FnMut(&mut dyn draw::DrawHandle), ) { + let mut shadow = Default::default(); let mut outer_rect = inner_rect; if class == PassType::Overlay { - outer_rect = inner_rect.expand(self.w.dims.frame); + shadow = Quad::from(inner_rect); + shadow.a += self.w.dims.shadow_a; + shadow.b += self.w.dims.shadow_b; + let a = shadow.a.floor(); + let b = shadow.b.ceil(); + outer_rect = Rect::new(a.into(), (b - a).into()); } let mut draw = self.draw.new_pass(outer_rect, offset, class); if class == PassType::Overlay { - let outer = Quad::from(outer_rect + offset); + shadow += offset.into(); let inner = Quad::from(inner_rect + offset); - let norm = (0.0, 0.0); - draw.shaded_square_frame(outer, inner, norm, Rgba::TRANSPARENT, Rgba::BLACK); + draw.rounded_frame_2col(shadow, inner, Rgba::BLACK, Rgba::TRANSPARENT); + draw.rect(inner, self.cols.background); } From 7764ca4170c24616fba6c1dc596f81fd50f4687c Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 17 Aug 2021 08:40:50 +0100 Subject: [PATCH 15/16] FlatTheme: reduce shadows a little --- crates/kas-theme/src/flat_theme.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/kas-theme/src/flat_theme.rs b/crates/kas-theme/src/flat_theme.rs index 0c30a4873..3724e6b11 100644 --- a/crates/kas-theme/src/flat_theme.rs +++ b/crates/kas-theme/src/flat_theme.rs @@ -26,9 +26,9 @@ use kas::TkAction; const BG_SHRINK_FACTOR: f32 = 1.0 - std::f32::consts::FRAC_1_SQRT_2; // Shadow enlargement on hover -const SHADOW_HOVER: f32 = 1.2; +const SHADOW_HOVER: f32 = 1.1; // Shadow enlargement for pop-ups -const SHADOW_POPUP: f32 = 1.4; +const SHADOW_POPUP: f32 = 1.2; /// A theme with flat (unshaded) rendering #[derive(Clone, Debug)] @@ -112,8 +112,8 @@ const DIMS: dim::Parameters = dim::Parameters { scrollbar_size: Vec2::splat(8.0), slider_size: Vec2(16.0, 16.0), progress_bar: Vec2::splat(8.0), - shadow_size: Vec2(5.0, 5.0), - shadow_rel_offset: Vec2(0.3, 0.6), + shadow_size: Vec2(4.0, 4.0), + shadow_rel_offset: Vec2(0.2, 0.3), }; const DARK_SHADOW_SIZE: Vec2 = Vec2::splat(5.0); const DARK_SHADOW_OFFSET: Vec2 = Vec2::ZERO; @@ -502,7 +502,7 @@ where self.cols.text }; - const F: f32 = 0.5; + const F: f32 = 0.6; let (sa, sb) = (self.w.dims.shadow_a * F, self.w.dims.shadow_b * F); let outer = Quad::with_coords(a + sa, b + sb); let inner = Quad::with_coords(a, b); @@ -533,7 +533,7 @@ where if !(state.disabled || state.depress) { let (mut a, mut b) = (self.w.dims.shadow_a, self.w.dims.shadow_b); - let mut mult = 0.6; + let mut mult = 0.65; if state.hover { mult *= SHADOW_HOVER; } From cd2c7caef8744aca4b659d53db3cece8f7589eff Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 17 Aug 2021 08:46:10 +0100 Subject: [PATCH 16/16] FlatTheme::scrollbar: use accent colours --- crates/kas-theme/src/flat_theme.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/kas-theme/src/flat_theme.rs b/crates/kas-theme/src/flat_theme.rs index 3724e6b11..0d6f95411 100644 --- a/crates/kas-theme/src/flat_theme.rs +++ b/crates/kas-theme/src/flat_theme.rs @@ -573,7 +573,11 @@ where let r = outer.size().min_comp() * 0.125; let outer = outer.shrink(r); let inner = outer.shrink(3.0 * r); - let col = ColorsLinear::adjust_for_state(self.cols.frame, state); + let col = if state.depress || state.nav_focus { + self.cols.nav_focus + } else { + self.cols.accent_soft + }; self.draw.rounded_frame(outer, inner, 0.0, col); }