Refactored to MakeWidgetWithId

This commit is contained in:
Jonathan Johnson 2023-11-30 09:14:19 -08:00
parent a826b91173
commit 8a4c66e73b
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
9 changed files with 50 additions and 42 deletions

View file

@ -2,7 +2,7 @@
//! traits or using the [`Custom`] widget with callbacks.
use gooey::value::Dynamic;
use gooey::widget::{MakeWidget, Widget, HANDLED};
use gooey::widget::{MakeWidget, MakeWidgetWithId, Widget, WidgetInstance, WidgetTag, HANDLED};
use gooey::widgets::Custom;
use gooey::Run;
use kludgine::figures::units::{Lp, UPx};
@ -29,7 +29,7 @@ fn main() -> gooey::Result {
///
/// This approach was added to make it easy to create one-off widgets in a
/// hierarchy to handle events or other purpose-built functions.
fn callback_widget() -> impl MakeWidget {
fn callback_widget() -> impl MakeWidgetWithId {
// This implementation and the impl `Widget` implementation both use the
// same Dynamic value setup.
let toggle = Toggle::default();
@ -44,18 +44,26 @@ fn callback_widget() -> impl MakeWidget {
.height(Lp::inches(1))
}
/// A second approach is to implement [`MakeWidget`] for a type. This allows any
/// type to be used when composing your UI that know how to create a widget.
/// A second approach is to implement [`MakeWidgetWithId`] for a type. This
/// allows any type to be used when composing your UI that know how to create a
/// widget.
///
/// This enables using callback-based widgets (or any other combination of
/// widgets) in a reusable fashion.
///
/// [`MakeWidget`] is implemented automatically for all types that implement
/// [`MakeWidgetWithId`]. The difference between the traits is purely whether
/// allowing a caller instantiating your custom widget to provide an id for the
/// widget. These IDs are used when configuring custom tab orders, so if your
/// widget or any of its children aren't focusable, implementing [`MakeWidget`]
/// directly will make more sense.
#[derive(Default)]
struct ToggleMakeWidget(Toggle);
impl MakeWidget for ToggleMakeWidget {
fn make_widget(self) -> gooey::widget::WidgetInstance {
impl MakeWidgetWithId for ToggleMakeWidget {
fn make_with_id(self, id: WidgetTag) -> WidgetInstance {
// In a real code base, the contents of callback_widget() would go here
callback_widget().make_widget()
callback_widget().make_with_id(id)
}
}
@ -63,7 +71,7 @@ impl MakeWidget for ToggleMakeWidget {
///
/// This is the lowest-level way to implement a Widget, but it also provides the
/// most power and flexibility.
fn impl_widget() -> impl MakeWidget {
fn impl_widget() -> impl MakeWidgetWithId {
Toggle::default()
}

View file

@ -20,7 +20,7 @@ use crate::animation::{AnimationHandle, DynamicTransition, IntoAnimate, LinearIn
use crate::context::sealed::WindowHandle;
use crate::context::{self, WidgetContext};
use crate::utils::{run_in_bg, IgnorePoison, UnwindsafeCondvar, WithClone};
use crate::widget::{Children, MakeWidget, WidgetId, WidgetInstance};
use crate::widget::{Children, MakeWidget, MakeWidgetWithId, WidgetId, WidgetInstance};
use crate::widgets::{Radio, Select, Space, Switcher};
/// An instance of a value that provides APIs to observe and react to its
@ -607,20 +607,20 @@ impl Dynamic<WidgetInstance> {
}
}
impl MakeWidget for Dynamic<WidgetInstance> {
fn make_widget(self) -> WidgetInstance {
self.switcher().make_widget()
impl MakeWidgetWithId for Dynamic<WidgetInstance> {
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
self.switcher().make_with_id(id)
}
}
impl MakeWidget for Dynamic<Option<WidgetInstance>> {
fn make_widget(self) -> WidgetInstance {
impl MakeWidgetWithId for Dynamic<Option<WidgetInstance>> {
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
self.map_each(|widget| {
widget
.as_ref()
.map_or_else(|| Space::clear().make_widget(), Clone::clone)
})
.make_widget()
.make_with_id(id)
}
}

View file

@ -1077,9 +1077,9 @@ impl MakeWidget for WidgetInstance {
}
}
impl MakeWidget for Color {
fn make_widget(self) -> WidgetInstance {
Space::colored(self).make_widget()
impl MakeWidgetWithId for Color {
fn make_with_id(self, id: WidgetTag) -> WidgetInstance {
Space::colored(self).make_with_id(id)
}
}

View file

@ -10,7 +10,7 @@ use kludgine::shapes::{PathBuilder, Shape, StrokeOptions};
use crate::context::{GraphicsContext, LayoutContext};
use crate::styles::components::{LineHeight, OutlineColor, TextColor, WidgetAccentColor};
use crate::value::{Dynamic, DynamicReader, IntoDynamic, IntoValue, Value};
use crate::widget::{MakeWidget, Widget, WidgetInstance};
use crate::widget::{MakeWidget, MakeWidgetWithId, Widget, WidgetInstance};
use crate::widgets::button::ButtonKind;
use crate::ConstraintLimit;
@ -50,8 +50,8 @@ impl Checkbox {
}
}
impl MakeWidget for Checkbox {
fn make_widget(self) -> WidgetInstance {
impl MakeWidgetWithId for Checkbox {
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
CheckboxOrnament {
value: self.state.create_reader(),
}
@ -63,7 +63,7 @@ impl MakeWidget for Checkbox {
*value = !*value;
})
.kind(self.kind)
.make_widget()
.make_with_id(id)
}
}

View file

@ -8,7 +8,7 @@ use kludgine::{Color, DrawableExt};
use crate::context::{GraphicsContext, LayoutContext};
use crate::styles::components::TextColor;
use crate::value::{Dynamic, Generation, IntoValue, Value};
use crate::widget::{MakeWidget, Widget, WidgetInstance};
use crate::widget::{Widget, WidgetInstance};
use crate::ConstraintLimit;
/// A read-only text widget.
@ -90,9 +90,9 @@ impl Widget for Label {
macro_rules! impl_make_widget {
($($type:ty),*) => {
$(impl MakeWidget for $type {
fn make_widget(self) -> WidgetInstance {
Label::new(self).make_widget()
$(impl crate::widget::MakeWidgetWithId for $type {
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
Label::new(self).make_with_id(id)
}
})*
};

View file

@ -10,7 +10,7 @@ use crate::animation::{
AnimationHandle, AnimationTarget, IntoAnimate, PercentBetween, Spawn, ZeroToOne,
};
use crate::value::{Dynamic, IntoDynamic, IntoValue, MapEach, Value};
use crate::widget::{MakeWidget, WidgetInstance};
use crate::widget::{MakeWidget, MakeWidgetWithId, WidgetInstance};
use crate::widgets::slider::Slidable;
use crate::widgets::Data;
@ -47,8 +47,8 @@ pub enum Progress<T = ZeroToOne> {
Percent(T),
}
impl MakeWidget for ProgressBar {
fn make_widget(self) -> WidgetInstance {
impl MakeWidgetWithId for ProgressBar {
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
let start = Dynamic::new(ZeroToOne::ZERO);
let end = Dynamic::new(ZeroToOne::ZERO);
let value = (&start, &end).map_each(|(start, end)| *start..=*end);
@ -61,7 +61,7 @@ impl MakeWidget for ProgressBar {
&end,
);
let slider = value.slider().knobless().non_interactive();
let slider = value.slider().knobless().non_interactive().make_with_id(id);
match self.progress {
Value::Dynamic(progress) => {
progress.for_each(move |progress| {

View file

@ -10,7 +10,7 @@ use kludgine::DrawableExt;
use crate::context::{GraphicsContext, LayoutContext};
use crate::styles::components::{LineHeight, OutlineColor, WidgetAccentColor};
use crate::value::{Dynamic, DynamicReader, IntoDynamic, IntoValue, Value};
use crate::widget::{MakeWidget, Widget, WidgetInstance};
use crate::widget::{MakeWidget, MakeWidgetWithId, Widget, WidgetInstance};
use crate::widgets::button::ButtonKind;
use crate::ConstraintLimit;
@ -50,11 +50,11 @@ impl<T> Radio<T> {
}
}
impl<T> MakeWidget for Radio<T>
impl<T> MakeWidgetWithId for Radio<T>
where
T: Clone + Debug + Eq + UnwindSafe + Send + 'static,
{
fn make_widget(self) -> WidgetInstance {
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
RadioOrnament {
value: self.value.clone(),
state: self.state.create_reader(),
@ -66,7 +66,7 @@ where
self.state.set(self.value.clone());
})
.kind(self.kind)
.make_widget()
.make_with_id(id)
}
}

View file

@ -7,7 +7,7 @@ use kludgine::Color;
use crate::styles::components::OutlineColor;
use crate::styles::{Component, DynamicComponent};
use crate::value::{Dynamic, IntoDynamic, IntoValue, MapEach, Value};
use crate::widget::{MakeWidget, WidgetInstance};
use crate::widget::{MakeWidget, MakeWidgetWithId, WidgetInstance};
use crate::widgets::button::{ButtonBackground, ButtonHoverBackground, ButtonKind};
/// A selectable, labeled widget representing a value.
@ -45,11 +45,11 @@ impl<T> Select<T> {
}
}
impl<T> MakeWidget for Select<T>
impl<T> MakeWidgetWithId for Select<T>
where
T: Clone + Debug + Eq + RefUnwindSafe + UnwindSafe + Send + Sync + 'static,
{
fn make_widget(self) -> WidgetInstance {
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
let selected = self.state.map_each({
let value = self.value.clone();
move |state| state == &value
@ -79,7 +79,7 @@ where
.kind(kind)
.with_dynamic(&ButtonBackground, selected_color.clone())
.with_dynamic(&ButtonHoverBackground, selected_color)
.make_widget()
.make_with_id(id)
}
}

View file

@ -5,7 +5,7 @@ use kludgine::Color;
use crate::styles::components::{LineHeight, OutlineColor, TextColor, TextSize};
use crate::value::{Dynamic, IntoDynamic, IntoValue, MapEach, Validation, Value};
use crate::widget::{MakeWidget, WidgetInstance, WidgetRef, WrapperWidget};
use crate::widget::{MakeWidget, MakeWidgetWithId, WidgetInstance, WidgetRef, WrapperWidget};
/// A widget that displays validation information around another widget.
///
@ -42,8 +42,8 @@ impl Validated {
}
}
impl MakeWidget for Validated {
fn make_widget(self) -> WidgetInstance {
impl MakeWidgetWithId for Validated {
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
let message = match self.hint {
Value::Constant(hint) => self
.validation
@ -81,7 +81,7 @@ impl MakeWidget for Validated {
error_color,
default_color,
}
.make_widget()
.make_with_id(id)
}
}