Skip to content

Commit

Permalink
Adjusting focus rings
Browse files Browse the repository at this point in the history
Refs #203
  • Loading branch information
ecton committed Oct 30, 2024
1 parent d032299 commit 5e9abb6
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 35 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- If the root widget of a window is a `Resize` with an exact width and height,
the window will have its resizable attribute disabled. This will not update
the resizable `Dynamic<bool>` on `Window`.
- Transparent buttons' focus rings are now drawn using the same corner radius as
the button and have padding between the label and the focus ring.

### Fixed

Expand Down Expand Up @@ -269,6 +271,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
@danbulant for helping with this change!
- `ScrollBar` is a new widget that renders a scroll bar meant to scroll through
a large container.
- `OutlineWidth` is a new component that is used to control the width of most
outlines drawn in the user interface.
- `FocusColor` is a new component that controls the color of the keyboard focus
indicator.


[139]: https://github.com/khonsulabs/cushy/issues/139
Expand Down
3 changes: 2 additions & 1 deletion examples/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ fn theme_editor(cushy: Cushy) -> impl MakeWidget {
}))
.into_rows()
.pad()
.vertical_scroll();
.vertical_scroll()
.expand();

editors
.and(fixed_themes(
Expand Down
5 changes: 3 additions & 2 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::fonts::{LoadedFont, LoadedFontFace};
use crate::graphics::{FontState, Graphics};
use crate::styles::components::{
CornerRadius, FontFamily, FontStyle, FontWeight, HighlightColor, LayoutOrder, LineHeight,
Opacity, TextSize, WidgetBackground,
Opacity, OutlineWidth, TextSize, WidgetBackground,
};
use crate::styles::{ComponentDefinition, FontFamilyList, Styles, Theme, ThemePair};
use crate::tree::Tree;
Expand Down Expand Up @@ -672,7 +672,8 @@ impl<'context, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'clip, 'gfx, 'pass>
}

let color = self.get(&HighlightColor);
self.stroke_outline::<Lp>(color, StrokeOptions::lp_wide(Lp::points(2)));
let width = self.get(&OutlineWidth).into_px(self.gfx.scale()).ceil();
self.stroke_outline(color, StrokeOptions::px_wide(width));
}

/// Applies the current style settings for font family, text size, font
Expand Down
4 changes: 4 additions & 0 deletions src/styles/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ define_components! {
TextColorVariant(Color, "text_color_variant", .surface.on_color_variant)
/// A [`Color`] to be used as a highlight color.
HighlightColor(Color,"highlight_color", .primary.color.with_alpha(128))
/// A [`Color`] to be used as to indicate keyboard focus.
FocusColor(Color,"focus_color", @HighlightColor)
/// The width of outlines drawn around widgets.
OutlineWidth(Dimension,"outline_width", Dimension::Lp(Lp::points(1)))
/// The primary color from the current theme.
PrimaryColor(Color, "primary_color", .primary.color)
/// The secondary color from the current theme.
Expand Down
47 changes: 31 additions & 16 deletions src/widgets/button.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! A clickable, labeled button
use std::time::{Duration, Instant};

use figures::units::{Lp, Px, UPx};
use figures::{IntoSigned, Point, Rect, Round, ScreenScale, Size};
use figures::units::{Px, UPx};
use figures::{IntoSigned, Point, Rect, Round, ScreenScale, Size, Zero};
use kludgine::app::winit::event::{Modifiers, MouseButton};
use kludgine::app::winit::window::CursorIcon;
use kludgine::shapes::{Shape, StrokeOptions};
Expand All @@ -11,10 +11,11 @@ use kludgine::Color;
use crate::animation::{AnimationHandle, AnimationTarget, IntoAnimate, LinearInterpolate, Spawn};
use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext, WidgetContext};
use crate::styles::components::{
AutoFocusableControls, DefaultActiveBackgroundColor, DefaultActiveForegroundColor,
DefaultBackgroundColor, DefaultDisabledBackgroundColor, DefaultDisabledForegroundColor,
DefaultForegroundColor, DefaultHoveredBackgroundColor, DefaultHoveredForegroundColor, Easing,
HighlightColor, IntrinsicPadding, OpaqueWidgetColor, OutlineColor, SurfaceColor, TextColor,
AutoFocusableControls, CornerRadius, DefaultActiveBackgroundColor,
DefaultActiveForegroundColor, DefaultBackgroundColor, DefaultDisabledBackgroundColor,
DefaultDisabledForegroundColor, DefaultForegroundColor, DefaultHoveredBackgroundColor,
DefaultHoveredForegroundColor, Easing, HighlightColor, IntrinsicPadding, OpaqueWidgetColor,
OutlineColor, OutlineWidth, SurfaceColor, TextColor,
};
use crate::styles::{ColorExt, Styles};
use crate::value::{Destination, Dynamic, IntoValue, Source, Value};
Expand Down Expand Up @@ -370,12 +371,16 @@ impl Widget for Button {
let style = self.current_style(context);
context.fill(style.background);

let two_lp_stroke = StrokeOptions::lp_wide(Lp::points(2));
context.stroke_outline(style.outline, two_lp_stroke);
let outline_options = StrokeOptions::px_wide(
context
.get(&OutlineWidth)
.into_px(context.gfx.scale())
.ceil(),
);
context.stroke_outline(style.outline, outline_options);

if context.focused(true) {
if current_style == ButtonKind::Transparent {
let two_lp_stroke = two_lp_stroke.into_px(context.gfx.scale());
let focus_color = context.get(&HighlightColor);
// Some states of a transparent button have solid background
// colors. most_contrasting from a 0-alpha color is not a
Expand All @@ -389,17 +394,27 @@ impl Widget for Button {
.most_contrasting(&[focus_color, context.get(&TextColor)])
} else {
focus_color
};
}
.with_alpha(128);

let inset = context.get(&IntrinsicPadding).into_px(context.gfx.scale());
let inset = (context.get(&IntrinsicPadding).into_px(context.gfx.scale())
- outline_options.line_width)
/ 2;

let focus_ring = Shape::stroked_rect(
Rect::new(Point::squared(inset), context.gfx.region().size - inset * 2),
two_lp_stroke.colored(color),
);
let options = outline_options.colored(color);
let radii = context.get(&CornerRadius);
let radii = radii.map(|r| r.into_px(context.gfx.scale()));
let ring_rect =
Rect::new(Point::squared(inset), context.gfx.region().size - inset * 2);

let focus_ring = if radii.is_zero() {
Shape::stroked_rect(ring_rect, options.into_px(context.gfx.scale()))
} else {
Shape::stroked_round_rect(ring_rect, radii, options)
};
context.gfx.draw_shape(&focus_ring);
} else if context.is_default() {
context.stroke_outline(context.get(&OutlineColor), two_lp_stroke);
context.stroke_outline(context.get(&OutlineColor), outline_options);
} else {
context.draw_focus_ring();
}
Expand Down
20 changes: 13 additions & 7 deletions src/widgets/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use kludgine::{Color, DrawableExt, Origin};

use crate::animation::{LinearInterpolate, PercentBetween, ZeroToOne};
use crate::context::{EventContext, GraphicsContext, LayoutContext};
use crate::styles::components::{HighlightColor, OutlineColor, SurfaceColor, TextColor};
use crate::styles::components::{
HighlightColor, OutlineColor, OutlineWidth, SurfaceColor, TextColor,
};
use crate::styles::{ColorExt, ColorSource, Hsl, Hsla};
use crate::value::{
Destination, Dynamic, ForEachCloned, IntoDynamic, IntoReadOnly, IntoValue, MapEach, ReadOnly,
Expand Down Expand Up @@ -491,9 +493,13 @@ where
context.get(&OutlineColor)
};

let options = StrokeOptions::lp_wide(Lp::points(1))
.colored(outline_color)
.into_px(context.gfx.scale());
let options = StrokeOptions::px_wide(
context
.get(&OutlineWidth)
.into_px(context.gfx.scale())
.ceil(),
)
.colored(outline_color);
self.visible_rect = Rect::new(
Point::squared(options.line_width) + Point::new(loupe_size / 2, Px::ZERO),
size - Size::squared(options.line_width * 2) - Size::new(loupe_size, Px::ZERO),
Expand Down Expand Up @@ -664,9 +670,9 @@ impl Widget for ColorSourcePicker {
context.get(&OutlineColor)
};

let options = StrokeOptions::lp_wide(Lp::points(1))
.colored(outline_color)
.into_px(context.gfx.scale());
let options =
StrokeOptions::px_wide(context.get(&OutlineWidth).into_px(context.gfx.scale()))
.colored(outline_color);
self.visible_rect = Rect::new(
Point::squared(options.line_width) + loupe_size / 2,
size - Point::squared(options.line_width * 2) - loupe_size,
Expand Down
14 changes: 9 additions & 5 deletions src/widgets/menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::sync::Arc;
use std::time::Duration;

use alot::LotId;
use figures::units::{Lp, Px, UPx};
use figures::units::{Px, UPx};
use figures::{Angle, IntoSigned, Point, Rect, Round, ScreenScale, Size, Zero};
use kludgine::shapes::{PathBuilder, Shape, StrokeOptions};
use kludgine::DrawableExt;
Expand All @@ -19,7 +19,7 @@ use super::Button;
use crate::animation::{AnimationHandle, AnimationTarget, Spawn};
use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext};
use crate::styles::components::{
CornerRadius, Easing, IntrinsicPadding, OpaqueWidgetColor, TextColor,
CornerRadius, Easing, IntrinsicPadding, OpaqueWidgetColor, OutlineWidth, TextColor,
};
use crate::styles::Styles;
use crate::value::{Dynamic, IntoValue, Source, Value};
Expand Down Expand Up @@ -579,9 +579,13 @@ where
.line_to(Point::new(full_size.width - self.padding * 2, UPx::ZERO))
.close()
.stroke(
StrokeOptions::lp_wide(Lp::points(2))
.into_upx(context.gfx.scale())
.colored(context.theme().surface.color),
StrokeOptions::upx_wide(
context
.get(&OutlineWidth)
.into_upx(context.gfx.scale())
.ceil(),
)
.colored(context.theme().surface.color),
);
for rendered in &mut self.items {
match &mut rendered.item {
Expand Down
12 changes: 8 additions & 4 deletions src/widgets/radio.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
//! A labeled widget with a circular indicator representing a value.
use std::fmt::Debug;

use figures::units::Lp;
use figures::{Point, ScreenScale, Size};
use figures::{Point, Round, ScreenScale, Size};
use kludgine::shapes::{Shape, StrokeOptions};
use kludgine::DrawableExt;

use crate::context::{GraphicsContext, LayoutContext};
use crate::styles::components::{LineHeight, OutlineColor, WidgetAccentColor};
use crate::styles::components::{LineHeight, OutlineColor, OutlineWidth, WidgetAccentColor};
use crate::styles::Dimension;
use crate::value::{Destination, Dynamic, DynamicReader, IntoDynamic, IntoValue, Source, Value};
use crate::widget::{MakeWidget, MakeWidgetWithTag, Widget, WidgetInstance};
Expand Down Expand Up @@ -89,7 +88,12 @@ where
.min(context.gfx.region().size.height);
let vertical_center = context.gfx.region().size.height / 2;

let stroke_options = StrokeOptions::lp_wide(Lp::points(2)).into_px(context.gfx.scale());
let stroke_options = StrokeOptions::px_wide(
context
.get(&OutlineWidth)
.into_px(context.gfx.scale())
.ceil(),
);
context.redraw_when_changed(&self.state);
let selected = self.state.map_ref(|state| state == &self.value);
let color = context.get(&OutlineColor);
Expand Down

0 comments on commit 5e9abb6

Please sign in to comment.