Skip to content

Commit

Permalink
Helpers galore
Browse files Browse the repository at this point in the history
  • Loading branch information
ecton committed Nov 14, 2023
1 parent 494fa68 commit 4c7c3be
Show file tree
Hide file tree
Showing 23 changed files with 338 additions and 176 deletions.
28 changes: 17 additions & 11 deletions .crate-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,30 @@ reactive data models work, consider this example that displays a button that
increments its own label:

```rust,ignore
// Create a dynamic usize.
let count = Dynamic::new(0_usize);
// Create a new button with a label that is produced by mapping the contents
// of `count`.
Button::new(count.map_each(ToString::to_string))
// Set the `on_click` callback to a closure that increments the counter.
.on_click(count.with_clone(|count| move |_| count.set(count.get() + 1)))
// Run the button as an an application.
.run()
fn main() -> gooey::Result {
// Create a dynamic usize.
let count = Dynamic::new(0_isize);
// Create a dynamic that contains `count.to_string()`
let count_label = count.map_each(ToString::to_string);
// Create a new button whose text is our dynamic string.
count_label
.into_button()
// Set the `on_click` callback to a closure that increments the counter.
.on_click(count.with_clone(|count| move |_| count.set(count.get() + 1)))
// Position the button in the center
.centered()
// Run the application
.run()
}
```

[widget]: crate::widget::Widget
[kludgine]: https://github.com/khonsulabs/kludgine
[wgpu]: https://github.com/gfx-rs/wgpu
[winit]: https://github.com/rust-windowing/winit
[widgets]: mod@crate::widgets
[button-example]: https://github.com/khonsulabs/gooey/tree/main/examples/button.rs
[button-example]: https://github.com/khonsulabs/gooey/tree/main/examples/basic-button.rs

## Open-source Licenses

Expand Down
4 changes: 2 additions & 2 deletions .rustme/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ reactive data models work, consider this example that displays a button that
increments its own label:

```rust,ignore
$../examples/button.rs:readme$
$../examples/basic-button.rs:readme$
```

[widget]: $widget$
[kludgine]: https://github.com/khonsulabs/kludgine
[wgpu]: https://github.com/gfx-rs/wgpu
[winit]: https://github.com/rust-windowing/winit
[widgets]: $widgets$
[button-example]: https://github.com/khonsulabs/gooey/tree/$ref-name$/examples/button.rs
[button-example]: https://github.com/khonsulabs/gooey/tree/$ref-name$/examples/basic-button.rs
28 changes: 17 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,30 @@ reactive data models work, consider this example that displays a button that
increments its own label:

```rust,ignore
// Create a dynamic usize.
let count = Dynamic::new(0_usize);
// Create a new button with a label that is produced by mapping the contents
// of `count`.
Button::new(count.map_each(ToString::to_string))
// Set the `on_click` callback to a closure that increments the counter.
.on_click(count.with_clone(|count| move |_| count.set(count.get() + 1)))
// Run the button as an an application.
.run()
fn main() -> gooey::Result {
// Create a dynamic usize.
let count = Dynamic::new(0_isize);
// Create a dynamic that contains `count.to_string()`
let count_label = count.map_each(ToString::to_string);
// Create a new button whose text is our dynamic string.
count_label
.into_button()
// Set the `on_click` callback to a closure that increments the counter.
.on_click(count.with_clone(|count| move |_| count.set(count.get() + 1)))
// Position the button in the center
.centered()
// Run the application
.run()
}
```

[widget]: https://gooey.rs/main/gooey/widget/trait.Widget.html
[kludgine]: https://github.com/khonsulabs/kludgine
[wgpu]: https://github.com/gfx-rs/wgpu
[winit]: https://github.com/rust-windowing/winit
[widgets]: https://gooey.rs/main/gooey/widgets/index.html
[button-example]: https://github.com/khonsulabs/gooey/tree/main/examples/button.rs
[button-example]: https://github.com/khonsulabs/gooey/tree/main/examples/basic-button.rs

## Open-source Licenses

Expand Down
21 changes: 12 additions & 9 deletions examples/animation.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use std::time::Duration;

use gooey::animation::{AnimationHandle, AnimationTarget, IntoAnimate, Spawn};
use gooey::value::Dynamic;
use gooey::value::{Dynamic, StringValue};
use gooey::widget::MakeWidget;
use gooey::widgets::{Button, Label, Stack};
use gooey::{Run, WithClone};

fn main() -> gooey::Result {
Expand All @@ -18,13 +17,17 @@ fn main() -> gooey::Result {
.on_complete(|| println!("Gooey animations are neat!"))
.launch();

Stack::columns(
Button::new("To 0")
.on_click(animate_to(&animation, &value, 0))
.and(Label::new(label))
.and(Button::new("To 100").on_click(animate_to(&animation, &value, 100))),
)
.run()
"To 0"
.into_button()
.on_click(animate_to(&animation, &value, 0))
.and(label)
.and(
"To 100"
.into_button()
.on_click(animate_to(&animation, &value, 100)),
)
.into_columns()
.run()
}

fn animate_to(
Expand Down
22 changes: 22 additions & 0 deletions examples/basic-button.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use gooey::value::{Dynamic, StringValue};
use gooey::widget::MakeWidget;
use gooey::Run;

// begin rustme snippet: readme
fn main() -> gooey::Result {
// Create a dynamic usize.
let count = Dynamic::new(0_isize);
// Create a dynamic that contains `count.to_string()`
let count_label = count.map_each(ToString::to_string);

// Create a new button whose text is our dynamic string.
count_label
.into_button()
// Set the `on_click` callback to a closure that increments the counter.
.on_click(count.with_clone(|count| move |_| count.set(count.get() + 1)))
// Position the button in the center
.centered()
// Run the application
.run()
}
// end rustme snippet
File renamed without changes.
18 changes: 9 additions & 9 deletions examples/containers.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use gooey::value::Dynamic;
use gooey::value::{Dynamic, StringValue};
use gooey::widget::{MakeWidget, WidgetInstance};
use gooey::widgets::{Button, Label};
use gooey::window::ThemeMode;
use gooey::Run;

Expand All @@ -10,28 +9,29 @@ fn main() -> gooey::Result {
.centered()
.expand()
.into_window()
.with_theme_mode(theme_mode)
.themed_mode(theme_mode)
.run()
}

fn set_of_containers(repeat: usize, theme_mode: Dynamic<ThemeMode>) -> WidgetInstance {
let inner = if let Some(remaining_iters) = repeat.checked_sub(1) {
set_of_containers(remaining_iters, theme_mode)
} else {
Button::new("Toggle Theme Mode")
"Toggle Theme Mode"
.into_button()
.on_click(move |_| {
theme_mode.map_mut(|mode| mode.toggle());
})
.make_widget()
};
Label::new("Lowest")
"Lowest"
.and(
Label::new("Low")
"Low"
.and(
Label::new("Mid")
"Mid"
.and(
Label::new("High")
.and(Label::new("Highest").and(inner).into_rows().contain())
"High"
.and("Highest".and(inner).into_rows().contain())
.into_rows()
.contain(),
)
Expand Down
9 changes: 4 additions & 5 deletions examples/counter.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
use std::string::ToString;

use gooey::value::Dynamic;
use gooey::value::{Dynamic, StringValue};
use gooey::widget::MakeWidget;
use gooey::widgets::{Button, Label};
use gooey::Run;
use kludgine::figures::units::Lp;

fn main() -> gooey::Result {
let counter = Dynamic::new(0i32);
let label = counter.map_each(ToString::to_string);

Label::new(label)
label
.width(Lp::points(100))
.and(Button::new("+").on_click(counter.with_clone(|counter| {
.and("+".into_button().on_click(counter.with_clone(|counter| {
move |_| {
*counter.lock() += 1;
}
})))
.and(Button::new("-").on_click(counter.with_clone(|counter| {
.and("-".into_button().on_click(counter.with_clone(|counter| {
move |_| {
*counter.lock() -= 1;
}
Expand Down
9 changes: 5 additions & 4 deletions examples/gameui.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use gooey::value::Dynamic;
use gooey::value::{Dynamic, StringValue};
use gooey::widget::{MakeWidget, HANDLED, IGNORED};
use gooey::widgets::{Input, Label, Space};
use gooey::widgets::Space;
use gooey::Run;
use kludgine::app::winit::event::ElementState;
use kludgine::app::winit::keyboard::Key;
Expand All @@ -10,13 +10,14 @@ fn main() -> gooey::Result {
let chat_log = Dynamic::new("Chat log goes here.\n".repeat(100));
let chat_message = Dynamic::new(String::new());

Label::new(chat_log.clone())
chat_log
.clone()
.vertical_scroll()
.expand()
.and(Space::colored(Color::RED).expand_weighted(2))
.into_columns()
.expand()
.and(Input::new(chat_message.clone()).on_key(move |input| {
.and(chat_message.clone().into_input().on_key(move |input| {
match (input.state, input.logical_key) {
(ElementState::Pressed, Key::Enter) => {
let new_message = chat_message.map_mut(std::mem::take);
Expand Down
4 changes: 2 additions & 2 deletions examples/input.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use gooey::value::StringValue;
use gooey::widget::MakeWidget;
use gooey::widgets::Input;
use gooey::Run;

fn main() -> gooey::Result {
Input::new("Hello").expand().run()
"Hello".into_input().expand().run()
}
18 changes: 10 additions & 8 deletions examples/login.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::process::exit;

use gooey::value::{Dynamic, MapEach};
use gooey::value::{Dynamic, MapEach, StringValue};
use gooey::widget::MakeWidget;
use gooey::widgets::{Button, Expand, Input, Label};
use gooey::widgets::Expand;
use gooey::Run;
use kludgine::figures::units::Lp;

Expand All @@ -14,26 +14,28 @@ fn main() -> gooey::Result {
(&username, &password).map_each(|(username, password)| validate(username, password));

// TODO this should be a grid layout to ensure proper visual alignment.
let username_row = Label::new("Username")
.and(Input::new(username.clone()).expand())
let username_row = "Username"
.and(username.clone().into_input().expand())
.into_columns();

let password_row = Label::new("Password")
let password_row = "Password"
.and(
// TODO secure input
Input::new(password.clone()).expand(),
password.clone().into_input().expand(),
)
.into_columns();

let buttons = Button::new("Cancel")
let buttons = "Cancel"
.into_button()
.on_click(|_| {
eprintln!("Login cancelled");
exit(0)
})
.into_escape()
.and(Expand::empty())
.and(
Button::new("Log In")
"Log In"
.into_button()
.enabled(valid)
.on_click(move |_| {
println!("Welcome, {}", username.get());
Expand Down
3 changes: 1 addition & 2 deletions examples/scroll.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use gooey::widget::MakeWidget;
use gooey::widgets::Label;
use gooey::Run;

fn main() -> gooey::Result {
Label::new(include_str!("../src/widgets/scroll.rs"))
include_str!("../src/widgets/scroll.rs")
.scroll()
.expand()
.run()
Expand Down
53 changes: 53 additions & 0 deletions examples/stack-align-test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use gooey::value::StringValue;
use gooey::widget::MakeWidget;
use gooey::Run;

/// This example shows a tricky layout problem. The hierarchy of widgets is
/// this:
///
/// ```text
/// Expand (.expand())
/// | Align (.centered())
/// | | Stack (.into_rows())
/// | | | Label
/// | | | Align (.centered())
/// | | | | Button
/// ```
///
/// When the Stack widget attempted to implmement a single-pass layout, this
/// caused the Button to be aligned to the left inside of the stack. The Stack
/// widget now utilizes two `layout()` operations for layouts like this. Here's
/// the reasoning:
///
/// At the window root, we have an Align wrapped by an Expand. The Align widget
/// during layout asks its children to size-to-fit. This means the Stack is
/// asking its children to size-to-fit as well.
///
/// The Stack's orientation is Rows, and since the children are Resizes or
/// Expands, the widgets are size-to-fit. This means that the Stack will measure
/// these widgets asking them to size to fit.
///
/// After running this pass of measurement, we can assign the heights of each of
/// the rows to the measurements we received. The width of the stack becomes the
/// maximum width of all children measured.
///
/// In a single-pass layout, this means the Align widget inside of the Stack
/// never receives an opportunity to lay its children out with the final width.
/// The Button does end up centered because of this. Fixing it also becomes
/// tricky, because if surround the button in an Expand, it now instructs the
/// Stack to expand to fill its parent.
///
/// After some careful deliberation, @ecton reasoned that in the situation where
/// a Stack is asked to layout with the Stack's non-primary being a size-to-fit
/// measurement, a second layout call for all children is required with Known
/// measurements to allow layouts like this example to work correctly.
fn main() -> gooey::Result {
// TODO once we have offscreen rendering, turn this into a test case
"Really Long Label"
.and("Short".into_button().centered())
.into_rows()
.contain()
.centered()
.expand()
.run()
}
4 changes: 2 additions & 2 deletions examples/style.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use gooey::styles::components::TextColor;
use gooey::widget::MakeWidget;
use gooey::widgets::stack::Stack;
use gooey::widgets::{Button, Style};
use gooey::widgets::Style;
use gooey::Run;
use kludgine::Color;

fn main() -> gooey::Result {
Stack::rows(Button::new("Green").and(red_text(Button::new("Red"))))
Stack::rows("Green".and(red_text("Red")))
.with(&TextColor, Color::GREEN)
.run()
}
Expand Down
Loading

0 comments on commit 4c7c3be

Please sign in to comment.