Skip to content

Commit

Permalink
Allow Widget::Props to borrow data (#116)
Browse files Browse the repository at this point in the history
* Abstract over responses, not widgets, in Response

* Allow Widget::Props to borrow data
  • Loading branch information
Ralith authored Nov 25, 2023
1 parent 0556ed3 commit b3435af
Show file tree
Hide file tree
Showing 38 changed files with 160 additions and 153 deletions.
4 changes: 2 additions & 2 deletions crates/yakui-core/src/dom/dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::widget::Widget;
pub(crate) struct DummyWidget;

impl Widget for DummyWidget {
type Props = ();
type Props<'a> = ();
type Response = ();

#[inline]
Expand All @@ -15,5 +15,5 @@ impl Widget for DummyWidget {
}

#[inline]
fn update(&mut self, _props: Self::Props) -> Self::Response {}
fn update(&mut self, _props: Self::Props<'_>) -> Self::Response {}
}
4 changes: 2 additions & 2 deletions crates/yakui-core/src/dom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ impl Dom {

/// Convenience method for calling [`Dom::begin_widget`] immediately
/// followed by [`Dom::end_widget`].
pub fn do_widget<T: Widget>(&self, props: T::Props) -> Response<T> {
pub fn do_widget<T: Widget>(&self, props: T::Props<'_>) -> Response<T::Response> {
let response = self.begin_widget::<T>(props);
self.end_widget::<T>(response.id);
response
Expand All @@ -184,7 +184,7 @@ impl Dom {
/// Begin building a widget with the given type and props.
///
/// After calling this method, children can be added to this widget.
pub fn begin_widget<T: Widget>(&self, props: T::Props) -> Response<T> {
pub fn begin_widget<T: Widget>(&self, props: T::Props<'_>) -> Response<T::Response> {
log::trace!("begin_widget::<{}>({props:#?}", type_name::<T>());

let (id, mut widget) = {
Expand Down
4 changes: 2 additions & 2 deletions crates/yakui-core/src/dom/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ use crate::widget::{LayoutContext, Widget};
pub struct RootWidget;

impl Widget for RootWidget {
type Props = ();
type Props<'a> = ();
type Response = ();

fn new() -> Self {
Self
}

fn update(&mut self, _props: Self::Props) -> Self::Response {}
fn update(&mut self, _props: Self::Props<'_>) -> Self::Response {}

fn layout(&self, mut ctx: LayoutContext<'_>, constraints: Constraints) -> glam::Vec2 {
ctx.layout.new_layer(ctx.dom);
Expand Down
17 changes: 8 additions & 9 deletions crates/yakui-core/src/response.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,39 @@
use std::ops::{Deref, DerefMut};

use crate::id::WidgetId;
use crate::widget::Widget;

/// Wraps the response returned by a widget when it is updated.
///
/// Widget responses can convey information like whether the widget was clicked,
/// is currently hovered, or had an update to its state.
#[derive(Debug)]
pub struct Response<T: Widget> {
inner: T::Response,
pub struct Response<T> {
inner: T,

/// The ID of the widget that responded.
pub id: WidgetId,
}

impl<T: Widget> Response<T> {
pub(crate) fn new(id: WidgetId, inner: T::Response) -> Self {
impl<T> Response<T> {
pub(crate) fn new(id: WidgetId, inner: T) -> Self {
Self { id, inner }
}

/// Unwrap the response into the underlying type.
pub fn into_inner(self) -> T::Response {
pub fn into_inner(self) -> T {
self.inner
}
}

impl<T: Widget> Deref for Response<T> {
type Target = T::Response;
impl<T> Deref for Response<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.inner
}
}

impl<T: Widget> DerefMut for Response<T> {
impl<T> DerefMut for Response<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
Expand Down
4 changes: 2 additions & 2 deletions crates/yakui-core/src/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub struct NavigateContext<'dom> {
pub trait Widget: 'static + fmt::Debug {
/// The props that this widget needs to be created or updated. Props define
/// all of the values that a widget's user can specify every render.
type Props: Props;
type Props<'a>: Props;

/// The type that the widget will return to the user when it is created or
/// updated. This type should contain information like whether the widget
Expand All @@ -90,7 +90,7 @@ pub trait Widget: 'static + fmt::Debug {
fn new() -> Self;

/// Update the widget with new props.
fn update(&mut self, props: Self::Props) -> Self::Response;
fn update(&mut self, props: Self::Props<'_>) -> Self::Response;

/// Returns whether this widget should grow to fill a flexible layout, and
/// if so, what weight should be applied to it if other widgets also want to
Expand Down
8 changes: 4 additions & 4 deletions crates/yakui-core/tests/regression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ use yakui_core::Yakui;
struct TestWidget;

impl Widget for TestWidget {
type Props = ();
type Props<'a> = ();
type Response = ();

fn new() -> Self {
Self
}

fn update(&mut self, _props: Self::Props) -> Self::Response {}
fn update(&mut self, _props: Self::Props<'_>) -> Self::Response {}
}

/// https://github.com/LPGhatguy/yakui/issues/69
Expand Down Expand Up @@ -51,7 +51,7 @@ struct KeyboardWidget {
}

impl Widget for KeyboardWidget {
type Props = ();
type Props<'a> = ();
type Response = Rc<AtomicUsize>;

fn new() -> Self {
Expand All @@ -60,7 +60,7 @@ impl Widget for KeyboardWidget {
}
}

fn update(&mut self, _props: Self::Props) -> Self::Response {
fn update(&mut self, _props: Self::Props<'_>) -> Self::Response {
self.count.clone()
}

Expand Down
74 changes: 41 additions & 33 deletions crates/yakui-widgets/src/shorthand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,59 +10,63 @@ use yakui_core::widget::PaintContext;
use yakui_core::{Alignment, ManagedTextureId, Response, TextureId};

use crate::widgets::{
Align, AlignWidget, Button, ButtonWidget, Canvas, CanvasWidget, Checkbox, CheckboxWidget,
Circle, CircleWidget, ColoredBox, ColoredBoxWidget, ConstrainedBox, ConstrainedBoxWidget,
Draggable, DraggableWidget, Flexible, FlexibleWidget, Image, ImageWidget, List, ListWidget,
MaxWidth, MaxWidthWidget, NineSlice, NineSliceWidget, Offset, OffsetWidget, Opaque,
OpaqueWidget, Pad, PadWidget, Reflow, ReflowWidget, Scrollable, ScrollableWidget, Slider,
SliderWidget, State, StateWidget, Text, TextBox, TextBoxWidget, TextWidget,
Align, AlignResponse, Button, ButtonResponse, Canvas, CanvasResponse, Checkbox,
CheckboxResponse, Circle, CircleResponse, ColoredBox, ColoredBoxResponse, ConstrainedBox,
ConstrainedBoxResponse, Draggable, DraggableResponse, Flexible, FlexibleResponse, Image,
ImageResponse, List, ListResponse, MaxWidth, MaxWidthResponse, NineSlice, Offset,
OffsetResponse, Opaque, OpaqueResponse, Pad, PadResponse, Reflow, ReflowResponse, Scrollable,
ScrollableResponse, Slider, SliderResponse, State, StateResponse, Text, TextBox,
TextBoxResponse, TextResponse,
};

/// See [List].
pub fn column<F: FnOnce()>(children: F) -> Response<ListWidget> {
pub fn column<F: FnOnce()>(children: F) -> Response<ListResponse> {
List::column().show(children)
}

/// See [List].
pub fn row<F: FnOnce()>(children: F) -> Response<ListWidget> {
pub fn row<F: FnOnce()>(children: F) -> Response<ListResponse> {
List::row().show(children)
}

/// See [Align].
pub fn center<F: FnOnce()>(children: F) -> Response<AlignWidget> {
pub fn center<F: FnOnce()>(children: F) -> Response<AlignResponse> {
Align::center().show(children)
}

/// See [Align].
pub fn align<F: FnOnce()>(alignment: Alignment, children: F) -> Response<AlignWidget> {
pub fn align<F: FnOnce()>(alignment: Alignment, children: F) -> Response<AlignResponse> {
Align::new(alignment).show(children)
}

/// See [Button].
pub fn button<S: Into<Cow<'static, str>>>(text: S) -> Response<ButtonWidget> {
pub fn button<S: Into<Cow<'static, str>>>(text: S) -> Response<ButtonResponse> {
Button::styled(text.into()).show()
}

/// See [ColoredCircle].
pub fn colored_circle<S: Into<f32>>(color: Color, size: S) -> Response<CircleWidget> {
pub fn colored_circle<S: Into<f32>>(color: Color, size: S) -> Response<CircleResponse> {
let mut circle = Circle::new();
circle.min_radius = size.into();
circle.color = color;
circle.show()
}

/// See [ColoredBox].
pub fn colored_box<S: Into<Vec2>>(color: Color, size: S) -> Response<ColoredBoxWidget> {
pub fn colored_box<S: Into<Vec2>>(color: Color, size: S) -> Response<ColoredBoxResponse> {
ColoredBox::sized(color, size.into()).show()
}

/// See [ColoredBox].
pub fn colored_box_container<F: FnOnce()>(color: Color, children: F) -> Response<ColoredBoxWidget> {
pub fn colored_box_container<F: FnOnce()>(
color: Color,
children: F,
) -> Response<ColoredBoxResponse> {
ColoredBox::container(color).show_children(children)
}

/// See [Image].
pub fn image<I, S>(image: I, size: S) -> Response<ImageWidget>
pub fn image<I, S>(image: I, size: S) -> Response<ImageResponse>
where
I: Into<TextureId>,
S: Into<Vec2>,
Expand All @@ -71,55 +75,55 @@ where
}

/// See [Pad].
pub fn pad<F: FnOnce()>(padding: Pad, children: F) -> Response<PadWidget> {
pub fn pad<F: FnOnce()>(padding: Pad, children: F) -> Response<PadResponse> {
padding.show(children)
}

/// See [Text].
pub fn text<S: Into<Cow<'static, str>>>(size: f32, text: S) -> Response<TextWidget> {
pub fn text<S: Into<Cow<'static, str>>>(size: f32, text: S) -> Response<TextResponse> {
Text::new(size, text.into()).show()
}

/// See [Text].
pub fn label<S: Into<Cow<'static, str>>>(text: S) -> Response<TextWidget> {
pub fn label<S: Into<Cow<'static, str>>>(text: S) -> Response<TextResponse> {
Text::label(text.into()).show()
}

/// See [TextBox].
pub fn textbox<S: Into<String>>(text: S) -> Response<TextBoxWidget> {
pub fn textbox<S: Into<String>>(text: S) -> Response<TextBoxResponse> {
TextBox::new(text.into()).show()
}

/// See [Flexible].
pub fn flexible<F: FnOnce()>(flex: u32, children: F) -> Response<FlexibleWidget> {
pub fn flexible<F: FnOnce()>(flex: u32, children: F) -> Response<FlexibleResponse> {
Flexible::new(flex).show(children)
}

/// See [Flexible].
pub fn expanded<F: FnOnce()>(children: F) -> Response<FlexibleWidget> {
pub fn expanded<F: FnOnce()>(children: F) -> Response<FlexibleResponse> {
Flexible::expanded().show(children)
}

/// See [ConstrainedBox].
pub fn constrained<F: FnOnce()>(
constraints: Constraints,
children: F,
) -> Response<ConstrainedBoxWidget> {
) -> Response<ConstrainedBoxResponse> {
ConstrainedBox::new(constraints).show(children)
}

/// See [Checkbox].
pub fn checkbox(checked: bool) -> Response<CheckboxWidget> {
pub fn checkbox(checked: bool) -> Response<CheckboxResponse> {
Checkbox::new(checked).show()
}

/// See [Offset].
pub fn offset<F: FnOnce()>(offset: Vec2, children: F) -> Response<OffsetWidget> {
pub fn offset<F: FnOnce()>(offset: Vec2, children: F) -> Response<OffsetResponse> {
Offset::new(offset).show(children)
}

/// See [Draggable].
pub fn draggable<F: FnOnce()>(children: F) -> Response<DraggableWidget> {
pub fn draggable<F: FnOnce()>(children: F) -> Response<DraggableResponse> {
Draggable::new().show(children)
}

Expand All @@ -129,41 +133,45 @@ pub fn nineslice(
margins: Pad,
scale: f32,
children: impl FnOnce(),
) -> Response<NineSliceWidget> {
) -> Response<()> {
NineSlice::new(texture, margins, scale).show(children)
}

/// See [Scrollable].
pub fn scroll_vertical(children: impl FnOnce()) -> Response<ScrollableWidget> {
pub fn scroll_vertical(children: impl FnOnce()) -> Response<ScrollableResponse> {
Scrollable::vertical().show(children)
}

/// See [Slider].
pub fn slider(value: f64, min: f64, max: f64) -> Response<SliderWidget> {
pub fn slider(value: f64, min: f64, max: f64) -> Response<SliderResponse> {
Slider::new(value, min, max).show()
}

/// See [Reflow].
pub fn reflow(anchor: Alignment, offset: Dim2, children: impl FnOnce()) -> Response<ReflowWidget> {
pub fn reflow(
anchor: Alignment,
offset: Dim2,
children: impl FnOnce(),
) -> Response<ReflowResponse> {
Reflow::new(anchor, offset).show(children)
}

/// See [Opaque].
pub fn opaque(children: impl FnOnce()) -> Response<OpaqueWidget> {
pub fn opaque(children: impl FnOnce()) -> Response<OpaqueResponse> {
Opaque::new().show(children)
}

/// See [Canvas].
pub fn canvas(paint: impl Fn(&mut PaintContext<'_>) + 'static) -> Response<CanvasWidget> {
pub fn canvas(paint: impl Fn(&mut PaintContext<'_>) + 'static) -> Response<CanvasResponse> {
Canvas::new(paint).show()
}

/// See [MaxWidth].
pub fn max_width(max_width: f32, children: impl FnOnce()) -> Response<MaxWidthWidget> {
pub fn max_width(max_width: f32, children: impl FnOnce()) -> Response<MaxWidthResponse> {
MaxWidth::new(max_width).show(children)
}

pub fn use_state<F, T: 'static>(default: F) -> Response<StateWidget<T>>
pub fn use_state<F, T: 'static>(default: F) -> Response<StateResponse<T>>
where
F: FnOnce() -> T + 'static,
{
Expand Down
4 changes: 2 additions & 2 deletions crates/yakui-widgets/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use yakui_core::widget::Widget;
use yakui_core::Response;

/// Show a widget with the given children and props.
pub fn widget_children<T, F>(children: F, props: T::Props) -> Response<T>
pub fn widget_children<T, F>(children: F, props: T::Props<'_>) -> Response<T::Response>
where
T: Widget,
F: FnOnce(),
Expand All @@ -16,7 +16,7 @@ where
}

/// Show a widget with the given props.
pub fn widget<T>(props: T::Props) -> Response<T>
pub fn widget<T>(props: T::Props<'_>) -> Response<T::Response>
where
T: Widget,
{
Expand Down
6 changes: 3 additions & 3 deletions crates/yakui-widgets/src/widgets/align.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl Align {
}
}

pub fn show<F: FnOnce()>(self, children: F) -> Response<AlignWidget> {
pub fn show<F: FnOnce()>(self, children: F) -> Response<AlignResponse> {
widget_children::<AlignWidget, F>(children, self)
}
}
Expand All @@ -62,7 +62,7 @@ pub struct AlignWidget {
pub type AlignResponse = ();

impl Widget for AlignWidget {
type Props = Align;
type Props<'a> = Align;
type Response = AlignResponse;

fn new() -> Self {
Expand All @@ -71,7 +71,7 @@ impl Widget for AlignWidget {
}
}

fn update(&mut self, props: Self::Props) -> Self::Response {
fn update(&mut self, props: Self::Props<'_>) -> Self::Response {
self.props = props;
}

Expand Down
Loading

0 comments on commit b3435af

Please sign in to comment.