diff --git a/crates/yakui-widgets/src/shorthand.rs b/crates/yakui-widgets/src/shorthand.rs index bfcbb68f..b2dbe2f4 100644 --- a/crates/yakui-widgets/src/shorthand.rs +++ b/crates/yakui-widgets/src/shorthand.rs @@ -14,9 +14,10 @@ use crate::widgets::{ CheckboxResponse, Circle, CircleResponse, ColoredBox, ColoredBoxResponse, ConstrainedBox, ConstrainedBoxResponse, CountGrid, Divider, DividerResponse, Draggable, DraggableResponse, Flexible, FlexibleResponse, Image, ImageResponse, List, ListResponse, MaxWidth, - MaxWidthResponse, NineSlice, Offset, OffsetResponse, Opaque, OpaqueResponse, Pad, PadResponse, - Reflow, ReflowResponse, Scrollable, ScrollableResponse, Slider, SliderResponse, Spacer, Stack, - StackResponse, State, StateResponse, Text, TextBox, TextBoxResponse, TextResponse, + MaxWidthResponse, NineSlice, Offset, OffsetResponse, Opaque, OpaqueResponse, Outline, + OutlineSide, Pad, PadResponse, Reflow, ReflowResponse, Scrollable, ScrollableResponse, Slider, + SliderResponse, Spacer, Stack, StackResponse, State, StateResponse, Text, TextBox, + TextBoxResponse, TextResponse, }; /// See [List]. @@ -197,6 +198,15 @@ pub fn stack(children: impl FnOnce()) -> Response { Stack::new().show(children) } +/// See [Outline]. +pub fn outline( + color: Color, + width: f32, + side: OutlineSide, + children: F, +) -> Response<()> { + Outline::new(color, width, side).show(children) +} pub fn use_state(default: F) -> Response> where F: FnOnce() -> T + 'static, diff --git a/crates/yakui-widgets/src/widgets/mod.rs b/crates/yakui-widgets/src/widgets/mod.rs index 2e75a8b7..224557df 100644 --- a/crates/yakui-widgets/src/widgets/mod.rs +++ b/crates/yakui-widgets/src/widgets/mod.rs @@ -17,6 +17,7 @@ mod max_width; mod nineslice; mod offset; mod opaque; +mod outline; mod pad; mod panel; mod reflow; @@ -52,6 +53,7 @@ pub use self::max_width::*; pub use self::nineslice::*; pub use self::offset::*; pub use self::opaque::*; +pub use self::outline::*; pub use self::pad::*; pub use self::panel::*; pub use self::reflow::*; diff --git a/crates/yakui-widgets/src/widgets/outline.rs b/crates/yakui-widgets/src/widgets/outline.rs new file mode 100644 index 00000000..438117ef --- /dev/null +++ b/crates/yakui-widgets/src/widgets/outline.rs @@ -0,0 +1,76 @@ +use crate::widgets::PadResponse; +use crate::{shapes, shorthand::pad, util::widget_children, widgets::pad::Pad}; +use yakui_core::geometry::Color; +use yakui_core::{ + widget::{PaintContext, Widget}, + Response, +}; + +/** +Applies a colored outline around its children. + */ +#[derive(Debug)] +#[must_use = "yakui widgets do nothing if you don't `show` them"] +pub struct Outline { + color: Color, + width: f32, + side: OutlineSide, +} + +#[derive(Copy, Clone, Debug)] +pub enum OutlineSide { + Inside, + Outside, +} +impl Outline { + pub fn new(color: Color, width: f32, side: OutlineSide) -> Self { + Self { color, width, side } + } + + pub fn show(self, children: impl FnOnce()) -> Response<()> { + let width = self.width; + let side = self.side; + widget_children::( + || match side { + OutlineSide::Inside => { + children(); + } + OutlineSide::Outside => { + pad(Pad::all(width), children); + } + }, + self, + ) + } +} + +#[derive(Debug)] +pub struct OutlineWidget { + props: Option, +} + +impl Widget for OutlineWidget { + type Props<'a> = Outline; + type Response = (); + + fn new() -> Self { + Self { props: None } + } + + fn update(&mut self, props: Self::Props<'_>) -> Self::Response { + self.props = Some(props); + } + + fn paint(&self, mut ctx: PaintContext<'_>) { + let props = self.props.as_ref().unwrap(); + let Outline { color, width, .. } = *props; + + let node = ctx.dom.get_current(); + for &child in &node.children { + ctx.paint(child); + } + + let rect = ctx.layout.get(ctx.dom.current()).unwrap().rect; + shapes::outline(ctx.paint, rect, width, color); + } +} diff --git a/crates/yakui/examples/outline.rs b/crates/yakui/examples/outline.rs new file mode 100644 index 00000000..f55e66b1 --- /dev/null +++ b/crates/yakui/examples/outline.rs @@ -0,0 +1,45 @@ +use yakui::{pad, widgets::Pad}; + +use bootstrap::ExampleState; +use yakui_core::geometry::Color; +use yakui_widgets::widgets::OutlineSide::{Inside, Outside}; +use yakui_widgets::{button, column, outline, text}; + +pub fn run(_state: &mut ExampleState) { + column(|| { + pad(Pad::all(20.0), || { + outline(Color::RED, 5.0, Inside, || { + text(20.0, "internal outline"); + }); + }); + pad(Pad::all(20.0), || { + outline(Color::RED, 5.0, Outside, || { + text(20.0, "external outline"); + }); + }); + pad(Pad::all(20.0), || { + outline(Color::RED, 1.0, Outside, || { + text(20.0, "varying width"); + }); + }); + pad(Pad::all(20.0), || { + outline(Color::RED, 2.0, Outside, || { + text(20.0, "varying width"); + }); + }); + pad(Pad::all(20.0), || { + outline(Color::GREEN, 3.0, Outside, || { + text(20.0, "other colors"); + }); + }); + pad(Pad::all(20.0), || { + outline(Color::GREEN, 3.0, Outside, || { + button("other widgets"); + }); + }); + }); +} + +fn main() { + bootstrap::start(run as fn(&mut ExampleState)); +}