From 244797110ee0872bfbb13705a25ec996c5cac0f9 Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Tue, 2 Jan 2024 15:05:52 -0800 Subject: [PATCH] Added to_ variants for into_ functions --- .crate-docs.md | 3 +-- CHANGELOG.md | 5 +++++ README.md | 3 +-- examples/basic-button.rs | 3 +-- examples/checkbox.rs | 2 +- examples/collapse.rs | 3 +-- examples/contacts.rs | 6 +++--- examples/counter.rs | 3 +-- examples/focus-order.rs | 10 ++-------- examples/gameui.rs | 28 +++++++++++++++------------- examples/login.rs | 6 ++---- examples/theme.rs | 3 +-- examples/tic-tac-toe.rs | 1 - examples/validation.rs | 6 ++---- src/animation.rs | 10 +++++++++- src/value.rs | 32 ++++++++++++++++++++++++++++++++ src/widget.rs | 28 +++++++++++++++++++++++++++- src/widgets/checkbox.rs | 8 ++++++++ src/widgets/input.rs | 7 +++++++ src/widgets/switcher.rs | 6 +++--- 20 files changed, 122 insertions(+), 51 deletions(-) diff --git a/.crate-docs.md b/.crate-docs.md index c7309ad..10fec4d 100644 --- a/.crate-docs.md +++ b/.crate-docs.md @@ -36,8 +36,7 @@ fn main() -> cushy::Result { // Create a new label displaying `count` count - .clone() - .into_label() + .to_label() // Use the label as the contents of a button .into_button() // Set the `on_click` callback to a closure that increments the counter. diff --git a/CHANGELOG.md b/CHANGELOG.md index c1f129a..03c1b6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Label` has been refactored to accept any `Display` type. As a result of this, `Label::text` is now named `display` and `Label::new()` now accepts an `IntoReadOnly` instead of `IntoValue`. +- `Dynamic::wrap` has been renamed to `into_wrap` for consistency. ### Fixed @@ -110,6 +111,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `Mutex`. `Owned` implements `Source` and `Destination`. - `GenerationalValue` now implements `Default` when `T` does. - `Value` now implements `From>`. +- Most `into_` functions that create widgets now have `to_` variations that + clone `self` before calling the `into_` function. This has only been done in + situations where it is known or likely that the clone being performed is + cheap. [99]: https://github.com/khonsulabs/cushy/issues/99 [120]: https://github.com/khonsulabs/cushy/issues/120 diff --git a/README.md b/README.md index 7dc5eaf..86d288e 100644 --- a/README.md +++ b/README.md @@ -38,8 +38,7 @@ fn main() -> cushy::Result { // Create a new label displaying `count` count - .clone() - .into_label() + .to_label() // Use the label as the contents of a button .into_button() // Set the `on_click` callback to a closure that increments the counter. diff --git a/examples/basic-button.rs b/examples/basic-button.rs index 6a33f28..8d2b5b3 100644 --- a/examples/basic-button.rs +++ b/examples/basic-button.rs @@ -9,8 +9,7 @@ fn main() -> cushy::Result { // Create a new label displaying `count` count - .clone() - .into_label() + .to_label() // Use the label as the contents of a button .into_button() // Set the `on_click` callback to a closure that increments the counter. diff --git a/examples/checkbox.rs b/examples/checkbox.rs index eb09aea..f519c26 100644 --- a/examples/checkbox.rs +++ b/examples/checkbox.rs @@ -9,7 +9,7 @@ fn main() -> cushy::Result { checkbox_state .clone() - .into_checkbox(label) + .to_checkbox(label) .and("Maybe".into_button().on_click(move |()| { checkbox_state.set(CheckboxState::Indeterminant); })) diff --git a/examples/collapse.rs b/examples/collapse.rs index 3e3dc62..37ce24a 100644 --- a/examples/collapse.rs +++ b/examples/collapse.rs @@ -10,8 +10,7 @@ fn main() -> cushy::Result { let collapse = Dynamic::new(false); collapse - .clone() - .into_checkbox("Collapse") + .to_checkbox("Collapse") .and( "Content Above" .contain() diff --git a/examples/contacts.rs b/examples/contacts.rs index 340d64b..1fc8e61 100644 --- a/examples/contacts.rs +++ b/examples/contacts.rs @@ -76,11 +76,11 @@ fn edit_contact_form(contact: &Contact, db: &Dynamic>) -> let title = Dynamic::new(contact.title.clone()); "First Name" - .and(first.clone().into_input()) + .and(first.to_input()) .and("Last Name") - .and(last.clone().into_input()) + .and(last.to_input()) .and("Title") - .and(title.clone().into_input()) + .and(title.to_input()) .and( "Save" .into_button() diff --git a/examples/counter.rs b/examples/counter.rs index 27f2d7a..224f346 100644 --- a/examples/counter.rs +++ b/examples/counter.rs @@ -7,8 +7,7 @@ fn main() -> cushy::Result { let counter = Dynamic::new(0i32); counter - .clone() - .into_label() + .to_label() .width(Lp::points(100)) .and("+".into_button().on_click(counter.with_clone(|counter| { move |_| { diff --git a/examples/focus-order.rs b/examples/focus-order.rs index 1d31113..501c9b4 100644 --- a/examples/focus-order.rs +++ b/examples/focus-order.rs @@ -22,15 +22,9 @@ fn main() -> cushy::Result { let (cancel_tag, cancel_id) = WidgetTag::new(); let (username_tag, username_id) = WidgetTag::new(); - let username_row = ( - "Username", - username.clone().into_input().make_with_tag(username_tag), - ); + let username_row = ("Username", username.to_input().make_with_tag(username_tag)); - let password_row = ( - "Password", - password.clone().into_input().with_next_focus(login_id), - ); + let password_row = ("Password", password.to_input().with_next_focus(login_id)); let buttons = "Cancel" .into_button() diff --git a/examples/gameui.rs b/examples/gameui.rs index 68053c1..5ad5d42 100644 --- a/examples/gameui.rs +++ b/examples/gameui.rs @@ -17,19 +17,21 @@ fn main() -> cushy::Result { .and(Color::RED.expand_weighted(2)) .into_columns() .expand() - .and(chat_message.clone().into_input().on_key(move |input| { - match (input.state, input.logical_key) { - (ElementState::Pressed, Key::Named(NamedKey::Enter)) => { - let new_message = chat_message.take(); - chat_log.map_mut(|mut chat_log| { - chat_log.push_str(&new_message); - chat_log.push('\n'); - }); - HANDLED - } - _ => IGNORED, - } - })) + .and( + chat_message + .to_input() + .on_key(move |input| match (input.state, input.logical_key) { + (ElementState::Pressed, Key::Named(NamedKey::Enter)) => { + let new_message = chat_message.take(); + chat_log.map_mut(|mut chat_log| { + chat_log.push_str(&new_message); + chat_log.push('\n'); + }); + HANDLED + } + _ => IGNORED, + }), + ) .into_rows() .expand() .run() diff --git a/examples/login.rs b/examples/login.rs index 7e61eff..eb7688e 100644 --- a/examples/login.rs +++ b/examples/login.rs @@ -18,8 +18,7 @@ fn main() -> cushy::Result { .align_left() .and( username - .clone() - .into_input() + .to_input() .placeholder("Username") .validation(validations.validate(&username, |u: &String| { if u.is_empty() { @@ -40,8 +39,7 @@ fn main() -> cushy::Result { .align_left() .and( password - .clone() - .into_input() + .to_input() .placeholder("Password") .validation( validations.validate(&password, |u: &MaskedString| match u.len() { diff --git a/examples/theme.rs b/examples/theme.rs index 36f0615..0a81a4f 100644 --- a/examples/theme.rs +++ b/examples/theme.rs @@ -206,8 +206,7 @@ fn optional_editor(label: &str, color: &Dynamic) -> (Dynamic, ( enabled.clone(), enabled - .clone() - .into_checkbox(swatch_label(label, color)) + .to_checkbox(swatch_label(label, color)) .and(color_editor(color).collapse_vertically(hide_editor)) .into_rows(), ) diff --git a/examples/tic-tac-toe.rs b/examples/tic-tac-toe.rs index ba09414..76b69b9 100644 --- a/examples/tic-tac-toe.rs +++ b/examples/tic-tac-toe.rs @@ -194,7 +194,6 @@ fn square(row: usize, column: usize, game: &Dynamic) -> impl MakeWidg }); label - .clone() .into_button() .kind(ButtonKind::Outline) .on_click(move |_| game.lock().play(row, column)) diff --git a/examples/validation.rs b/examples/validation.rs index d501c88..f272020 100644 --- a/examples/validation.rs +++ b/examples/validation.rs @@ -10,15 +10,13 @@ fn main() -> cushy::Result { "Hinted" .and( - text.clone() - .into_input() + text.to_input() .validation(validations.validate(&text, validate_input)) .hint("* required"), ) .and("Not Hinted") .and( - text.clone() - .into_input() + text.to_input() .validation(validations.validate(&text, validate_input)), ) .and( diff --git a/src/animation.rs b/src/animation.rs index e0b0f6f..256cdc6 100644 --- a/src/animation.rs +++ b/src/animation.rs @@ -345,6 +345,14 @@ pub trait IntoAnimate: Sized + Send + Sync { /// Return this change as a running animation. fn into_animate(self) -> Self::Animate; + /// Returns a clone of this change as a running animation. + fn to_animate(&self) -> Self::Animate + where + Self: Clone, + { + self.clone().into_animate() + } + /// Returns an combined animation that performs `self` and `other` in /// sequence. fn and_then(self, other: Other) -> Chain { @@ -674,7 +682,7 @@ where } if self.keep_cycling() { - self.running = Some(self.animation.clone().into_animate()); + self.running = Some(self.animation.to_animate()); } else { self.running = None; return ControlFlow::Break(elapsed); diff --git a/src/value.rs b/src/value.rs index 3cdbba7..265d931 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1164,9 +1164,32 @@ impl Dynamic { /// Returns a new [`Switcher`] widget whose contents is the value of this /// dynamic. #[must_use] + pub fn into_switcher(self) -> Switcher { + self.into_reader().into_switcher() + } + + /// Returns a new [`Switcher`] widget whose contents is the value of this + /// dynamic. + #[must_use] + pub fn to_switcher(&self) -> Switcher { + self.create_reader().into_switcher() + } +} + +impl DynamicReader { + /// Returns a new [`Switcher`] widget whose contents is the value of this + /// dynamic reader. + #[must_use] pub fn into_switcher(self) -> Switcher { Switcher::new(self) } + + /// Returns a new [`Switcher`] widget whose contents is the value of this + /// dynamic reader. + #[must_use] + pub fn to_switcher(&self) -> Switcher { + Switcher::new(self.clone()) + } } impl MakeWidgetWithTag for Dynamic { @@ -2261,6 +2284,15 @@ pub trait IntoReader { { Label::new(self.into_reader()) } + + /// Returns `self` being `Display`ed in a [`Label`] widget. + fn to_label(&self) -> Label + where + Self: Clone, + T: Debug + Display + Send + 'static, + { + self.clone().into_label() + } } impl IntoReader for Dynamic { diff --git a/src/widget.rs b/src/widget.rs index 4767a04..927d9e5 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -2074,12 +2074,24 @@ impl Dynamic { Stack::rows(self) } + /// Returns `self` as a vertical [`Stack`] of rows. + #[must_use] + pub fn to_rows(&self) -> Stack { + self.clone().into_rows() + } + /// Returns `self` as a horizontal [`Stack`] of columns. #[must_use] pub fn into_columns(self) -> Stack { Stack::columns(self) } + /// Returns `self` as a horizontal [`Stack`] of columns. + #[must_use] + pub fn to_columns(&self) -> Stack { + self.clone().into_columns() + } + /// Returns `self` as [`Layers`], with the widgets being stacked in the Z /// direction. #[must_use] @@ -2087,12 +2099,26 @@ impl Dynamic { Layers::new(self) } + /// Returns `self` as [`Layers`], with the widgets being stacked in the Z + /// direction. + #[must_use] + pub fn to_layers(&self) -> Layers { + self.clone().into_layers() + } + /// Returns a [`Wrap`] that lays the children out horizontally, wrapping /// into additional rows as needed. #[must_use] - pub fn wrap(self) -> Wrap { + pub fn into_wrap(self) -> Wrap { Wrap::new(self) } + + /// Returns a [`Wrap`] that lays the children out horizontally, wrapping + /// into additional rows as needed. + #[must_use] + pub fn to_wrap(&self) -> Wrap { + self.clone().into_wrap() + } } impl FromIterator for Children diff --git a/src/widgets/checkbox.rs b/src/widgets/checkbox.rs index 11f949e..31c09e4 100644 --- a/src/widgets/checkbox.rs +++ b/src/widgets/checkbox.rs @@ -257,6 +257,14 @@ pub trait Checkable: IntoDynamic + Sized { fn into_checkbox(self, label: impl MakeWidget) -> Checkbox { Checkbox::new(self.into_dynamic(), label) } + + /// Returns a new checkbox using `self` as the value and `label`. + fn to_checkbox(&self, label: impl MakeWidget) -> Checkbox + where + Self: Clone, + { + self.clone().into_checkbox(label) + } } impl Checkable for T where T: IntoDynamic {} diff --git a/src/widgets/input.rs b/src/widgets/input.rs index bcfbc93..7dbca9b 100644 --- a/src/widgets/input.rs +++ b/src/widgets/input.rs @@ -1351,6 +1351,13 @@ where fn into_input(self) -> Input { Input::new(self.into_dynamic()) } + /// Returns this string as a text input widget. + fn to_input(&self) -> Input + where + Self: Clone, + { + self.clone().into_input() + } } impl InputValue for T where T: IntoDynamic {} diff --git a/src/widgets/switcher.rs b/src/widgets/switcher.rs index a412123..0d9bc31 100644 --- a/src/widgets/switcher.rs +++ b/src/widgets/switcher.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use figures::Size; use crate::context::LayoutContext; -use crate::value::{Dynamic, DynamicReader, IntoDynamic, Source}; +use crate::value::{Dynamic, DynamicReader, IntoDynamic, IntoReader, Source}; use crate::widget::{WidgetInstance, WidgetRef, WrapperWidget}; use crate::ConstraintLimit; @@ -36,8 +36,8 @@ impl Switcher { /// Returns a new widget that replaces its contents with the result of /// `widget_factory` each time `value` changes. #[must_use] - pub fn new(source: impl IntoDynamic) -> Self { - let source = source.into_dynamic().into_reader(); + pub fn new(source: impl IntoReader) -> Self { + let source = source.into_reader(); let child = WidgetRef::new(source.get()); Self { source, child } }