Rounded rect drawing

This commit is contained in:
Jonathan Johnson 2023-11-18 14:45:02 -08:00
parent 8ae315e229
commit aea9def07d
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
5 changed files with 107 additions and 11 deletions

4
Cargo.lock generated
View file

@ -623,7 +623,7 @@ dependencies = [
[[package]]
name = "figures"
version = "0.1.0"
source = "git+https://github.com/khonsulabs/figures#52d06f3623cdb47128f1537fdadfe190f7afa88e"
source = "git+https://github.com/khonsulabs/figures#4712514fbed861ad530bd48d4e62f8efcaa4df1f"
dependencies = [
"bytemuck",
"euclid",
@ -1089,7 +1089,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "kludgine"
version = "0.1.0"
source = "git+https://github.com/khonsulabs/kludgine#74db2e6be9c79f384eafbe57af8fa92de8b8d03c"
source = "git+https://github.com/khonsulabs/kludgine#ca138e79a4b8ebc3bdd463006b1dadd3269183f3"
dependencies = [
"ahash",
"alot",

View file

@ -10,12 +10,12 @@ use kludgine::app::winit::event::{
DeviceId, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase,
};
use kludgine::figures::units::{Lp, Px, UPx};
use kludgine::figures::{IntoSigned, Point, Rect, ScreenScale, Size};
use kludgine::figures::{IntoSigned, IsZero, Point, Rect, ScreenScale, Size};
use kludgine::shapes::{Shape, StrokeOptions};
use kludgine::{Color, Kludgine};
use crate::graphics::Graphics;
use crate::styles::components::{HighlightColor, LayoutOrder, WidgetBackground};
use crate::styles::components::{CornerRadius, HighlightColor, LayoutOrder, WidgetBackground};
use crate::styles::{ComponentDefinition, Styles, Theme, ThemePair};
use crate::utils::IgnorePoison;
use crate::value::{Dynamic, IntoValue, Value};
@ -519,15 +519,49 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
}
}
/// Fills the background of this widget with `color`, honoring the current
/// [`CornerRadius`] setting.
///
/// If the alpha channel of `color` is 0, this function does nothing.
pub fn fill(&mut self, color: Color) {
if color.alpha() > 0 {
let visible_rect = Rect::from(self.gfx.region().size - (Px(1), Px(1)));
let radii = self.get(&CornerRadius);
let radii = radii.map(|r| r.into_px(self.gfx.scale()));
let focus_ring = if radii.is_zero() {
Shape::filled_rect(visible_rect, color)
} else {
Shape::filled_round_rect(visible_rect, radii, color)
};
self.gfx.draw_shape(&focus_ring);
}
}
/// Strokes an outline around this widget's contents.
pub fn stroke_outline<Unit>(&mut self, color: Color, options: StrokeOptions<Unit>)
where
Unit: ScreenScale<Px = Px, Lp = Lp, UPx = UPx>,
Unit: ScreenScale<Px = Px, Lp = Lp, UPx = UPx> + IsZero,
{
let visible_rect = Rect::from(self.gfx.region().size - (Px(1), Px(1)));
let focus_ring =
Shape::stroked_rect(visible_rect, color, options.into_px(self.gfx.scale()));
self.gfx.draw_shape(&focus_ring);
if color.alpha() > 0 {
let visible_rect = Rect::from(self.gfx.region().size - (Px(1), Px(1)));
let radii = self.get(&CornerRadius);
let radii = radii.map(|r| r.into_px(self.gfx.scale()));
let focus_ring = if radii.is_zero() {
Shape::stroked_rect(visible_rect, color, options.into_px(self.gfx.scale()))
} else {
Shape::stroked_round_rect(
visible_rect,
radii,
color,
options.into_px(self.gfx.scale()),
)
};
self.gfx.draw_shape(&focus_ring);
}
}
/// Renders the default focus ring for this widget.

View file

@ -12,7 +12,8 @@ use std::sync::Arc;
use ahash::AHashMap;
use kludgine::figures::units::{Lp, Px, UPx};
use kludgine::figures::{Fraction, IntoSigned, IntoUnsigned, Rect, ScreenScale, Size};
use kludgine::figures::{Fraction, IntoSigned, IntoUnsigned, IsZero, Rect, ScreenScale, Size};
use kludgine::shapes::CornerRadii;
use kludgine::Color;
use palette::{IntoColor, Okhsl, OklabHue, Srgb};
@ -211,6 +212,18 @@ pub enum Component {
Custom(CustomComponent),
}
impl Component {
/// Returns a [`CustomComponent`] created from `component`.
///
/// Custom components allow storing nearly any type in the style system.
pub fn custom<T>(component: T) -> Self
where
T: RequireInvalidation + RefUnwindSafe + UnwindSafe + Debug + Send + Sync + 'static,
{
Self::Custom(CustomComponent::new(component))
}
}
impl From<Color> for Component {
fn from(value: Color) -> Self {
Self::Color(value)
@ -303,6 +316,42 @@ impl RequireInvalidation for Lp {
}
}
impl<Unit> From<CornerRadii<Unit>> for Component
where
Dimension: From<Unit>,
Unit: Debug + UnwindSafe + RefUnwindSafe + Send + Sync + 'static,
{
fn from(radii: CornerRadii<Unit>) -> Self {
let radii = CornerRadii {
top_left: Dimension::from(radii.top_left),
top_right: Dimension::from(radii.top_right),
bottom_right: Dimension::from(radii.bottom_right),
bottom_left: Dimension::from(radii.bottom_left),
};
Component::custom(radii)
}
}
impl<Unit> RequireInvalidation for CornerRadii<Unit> {
fn requires_invalidation(&self) -> bool {
true
}
}
impl TryFrom<Component> for CornerRadii<Dimension> {
type Error = Component;
fn try_from(value: Component) -> Result<Self, Self::Error> {
match value {
Component::Custom(custom) => custom
.downcast()
.copied()
.ok_or_else(|| Component::Custom(custom)),
other => Err(other),
}
}
}
/// A 1-dimensional measurement that may be automatically calculated.
#[derive(Debug, Clone, Copy)]
pub enum FlexibleDimension {
@ -373,6 +422,15 @@ impl From<Lp> for Dimension {
}
}
impl IsZero for Dimension {
fn is_zero(&self) -> bool {
match self {
Dimension::Px(x) => x.is_zero(),
Dimension::Lp(x) => x.is_zero(),
}
}
}
impl ScreenScale for Dimension {
type Lp = Lp;
type Px = Px;

View file

@ -1,6 +1,7 @@
//! All style components supported by the built-in widgets.
use kludgine::figures::units::Lp;
use kludgine::shapes::CornerRadii;
use kludgine::Color;
use crate::animation::easings::{EaseInOutQuadradic, EaseInQuadradic, EaseOutQuadradic};
@ -133,5 +134,8 @@ define_components! {
/// A [`Color`] to be used as a background color for widgets that render an
/// opaque background.
OpaqueWidgetColor(Color, "opaque_color", .surface.opaque_widget)
/// A set of radius descriptions for how much roundness to apply to the
/// shapes of widgets.
CornerRadius(CornerRadii<Dimension>, "corner_radius", CornerRadii::from(Dimension::Lp(Lp::points(100))))
}
}

View file

@ -336,7 +336,7 @@ impl Widget for Button {
}
let style = self.current_style(context);
context.gfx.fill(style.background);
context.fill(style.background);
let two_lp_stroke = StrokeOptions::lp_wide(Lp::points(2));
context.stroke_outline(style.outline, two_lp_stroke);