mirror of
https://github.com/danbulant/cushy
synced 2026-06-24 17:12:11 +00:00
More combinators
Maybe I went overboard.
This commit is contained in:
parent
8e268615a1
commit
22fb955dca
12 changed files with 168 additions and 86 deletions
|
|
@ -2,8 +2,9 @@ use std::time::Duration;
|
||||||
|
|
||||||
use gooey::animation::{AnimationHandle, AnimationTarget, IntoAnimate, Spawn};
|
use gooey::animation::{AnimationHandle, AnimationTarget, IntoAnimate, Spawn};
|
||||||
use gooey::value::Dynamic;
|
use gooey::value::Dynamic;
|
||||||
|
use gooey::widget::MakeWidget;
|
||||||
use gooey::widgets::{Button, Label, Stack};
|
use gooey::widgets::{Button, Label, Stack};
|
||||||
use gooey::{children, Run, WithClone};
|
use gooey::{Run, WithClone};
|
||||||
|
|
||||||
fn main() -> gooey::Result {
|
fn main() -> gooey::Result {
|
||||||
let animation = Dynamic::new(AnimationHandle::new());
|
let animation = Dynamic::new(AnimationHandle::new());
|
||||||
|
|
@ -17,11 +18,12 @@ fn main() -> gooey::Result {
|
||||||
.on_complete(|| println!("Gooey animations are neat!"))
|
.on_complete(|| println!("Gooey animations are neat!"))
|
||||||
.launch();
|
.launch();
|
||||||
|
|
||||||
Stack::columns(children![
|
Stack::columns(
|
||||||
Button::new("To 0").on_click(animate_to(&animation, &value, 0)),
|
Button::new("To 0")
|
||||||
Label::new(label),
|
.on_click(animate_to(&animation, &value, 0))
|
||||||
Button::new("To 100").on_click(animate_to(&animation, &value, 100)),
|
.and(Label::new(label))
|
||||||
])
|
.and(Button::new("To 100").on_click(animate_to(&animation, &value, 100))),
|
||||||
|
)
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,28 @@
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
|
|
||||||
use gooey::value::Dynamic;
|
use gooey::value::Dynamic;
|
||||||
use gooey::widgets::{Align, Button, Expand, Label, Resize, Stack};
|
use gooey::widget::MakeWidget;
|
||||||
use gooey::{children, Run};
|
use gooey::widgets::{Button, Label, Resize, Stack};
|
||||||
|
use gooey::Run;
|
||||||
use kludgine::figures::units::Lp;
|
use kludgine::figures::units::Lp;
|
||||||
|
|
||||||
fn main() -> gooey::Result {
|
fn main() -> gooey::Result {
|
||||||
let counter = Dynamic::new(0i32);
|
let counter = Dynamic::new(0i32);
|
||||||
let label = counter.map_each(ToString::to_string);
|
let label = counter.map_each(ToString::to_string);
|
||||||
Expand::new(Align::centered(Stack::columns(children![
|
Stack::columns(
|
||||||
Resize::width(Lp::points(100), Label::new(label)),
|
Resize::width(Lp::points(100), Label::new(label))
|
||||||
Button::new("+").on_click(counter.with_clone(|counter| {
|
.and(Button::new("+").on_click(counter.with_clone(|counter| {
|
||||||
move |_| {
|
move |_| {
|
||||||
counter.set(counter.get() + 1);
|
counter.set(counter.get() + 1);
|
||||||
}
|
}
|
||||||
})),
|
})))
|
||||||
Button::new("-").on_click(counter.with_clone(|counter| {
|
.and(Button::new("-").on_click(counter.with_clone(|counter| {
|
||||||
move |_| {
|
move |_| {
|
||||||
counter.set(counter.get() - 1);
|
counter.set(counter.get() - 1);
|
||||||
}
|
}
|
||||||
})),
|
}))),
|
||||||
])))
|
)
|
||||||
|
.centered()
|
||||||
|
.expand()
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use gooey::value::Dynamic;
|
use gooey::value::Dynamic;
|
||||||
use gooey::widget::{MakeWidget, HANDLED, IGNORED};
|
use gooey::widget::{MakeWidget, HANDLED, IGNORED};
|
||||||
use gooey::widgets::{Canvas, Expand, Input, Label, Scroll, Stack};
|
use gooey::widgets::{Canvas, Input, Label, Stack};
|
||||||
use gooey::{children, Run};
|
use gooey::Run;
|
||||||
use kludgine::app::winit::event::ElementState;
|
use kludgine::app::winit::event::ElementState;
|
||||||
use kludgine::app::winit::keyboard::Key;
|
use kludgine::app::winit::keyboard::Key;
|
||||||
use kludgine::figures::{Point, Rect};
|
use kludgine::figures::{Point, Rect};
|
||||||
|
|
@ -12,11 +12,9 @@ fn main() -> gooey::Result {
|
||||||
let chat_log = Dynamic::new("Chat log goes here.\n".repeat(100));
|
let chat_log = Dynamic::new("Chat log goes here.\n".repeat(100));
|
||||||
let chat_message = Dynamic::new(String::new());
|
let chat_message = Dynamic::new(String::new());
|
||||||
|
|
||||||
Expand::new(Stack::rows(children![
|
Stack::rows(
|
||||||
Expand::new(Stack::columns(children![
|
Stack::columns(
|
||||||
Expand::new(Scroll::vertical(Label::new(chat_log.clone()))),
|
Label::new(chat_log.clone()).vertical_scroll().expand().and(
|
||||||
Expand::weighted(
|
|
||||||
2,
|
|
||||||
Canvas::new(|context| {
|
Canvas::new(|context| {
|
||||||
let entire_canvas = Rect::from(context.graphics.size());
|
let entire_canvas = Rect::from(context.graphics.size());
|
||||||
context.graphics.draw_shape(
|
context.graphics.draw_shape(
|
||||||
|
|
@ -26,10 +24,12 @@ fn main() -> gooey::Result {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
)
|
.expand_weighted(2),
|
||||||
])),
|
),
|
||||||
Input::new(chat_message.clone())
|
)
|
||||||
.on_key(move |input| match (input.state, input.logical_key) {
|
.expand()
|
||||||
|
.and(Input::new(chat_message.clone()).on_key(move |input| {
|
||||||
|
match (input.state, input.logical_key) {
|
||||||
(ElementState::Pressed, Key::Enter) => {
|
(ElementState::Pressed, Key::Enter) => {
|
||||||
let new_message = chat_message.map_mut(|text| std::mem::take(text));
|
let new_message = chat_message.map_mut(|text| std::mem::take(text));
|
||||||
chat_log.map_mut(|chat_log| {
|
chat_log.map_mut(|chat_log| {
|
||||||
|
|
@ -39,8 +39,9 @@ fn main() -> gooey::Result {
|
||||||
HANDLED
|
HANDLED
|
||||||
}
|
}
|
||||||
_ => IGNORED,
|
_ => IGNORED,
|
||||||
})
|
}
|
||||||
.make_widget(),
|
})),
|
||||||
]))
|
)
|
||||||
|
.expand()
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use gooey::widgets::{Expand, Input};
|
use gooey::widget::MakeWidget;
|
||||||
|
use gooey::widgets::Input;
|
||||||
use gooey::Run;
|
use gooey::Run;
|
||||||
|
|
||||||
fn main() -> gooey::Result {
|
fn main() -> gooey::Result {
|
||||||
Expand::new(Input::new("Hello")).run()
|
Input::new("Hello").expand().run()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ use std::process::exit;
|
||||||
|
|
||||||
use gooey::value::{Dynamic, MapEach};
|
use gooey::value::{Dynamic, MapEach};
|
||||||
use gooey::widget::MakeWidget;
|
use gooey::widget::MakeWidget;
|
||||||
use gooey::widgets::{Align, Button, Expand, Input, Label, Resize, Stack};
|
use gooey::widgets::{Button, Expand, Input, Label, Resize, Stack};
|
||||||
use gooey::{children, Run};
|
use gooey::Run;
|
||||||
use kludgine::figures::units::Lp;
|
use kludgine::figures::units::Lp;
|
||||||
|
|
||||||
fn main() -> gooey::Result {
|
fn main() -> gooey::Result {
|
||||||
|
|
@ -13,42 +13,49 @@ fn main() -> gooey::Result {
|
||||||
let valid =
|
let valid =
|
||||||
(&username, &password).map_each(|(username, password)| validate(username, password));
|
(&username, &password).map_each(|(username, password)| validate(username, password));
|
||||||
|
|
||||||
Expand::new(Align::centered(Resize::width(
|
Resize::width(
|
||||||
// TODO We need a min/max range for the Resize widget
|
// TODO We need a min/max range for the Resize widget
|
||||||
Lp::points(400),
|
Lp::points(400),
|
||||||
Stack::rows(children![
|
Stack::rows(
|
||||||
Stack::columns(children![
|
Stack::columns(
|
||||||
Label::new("Username"),
|
Label::new("Username").and(
|
||||||
Expand::new(Align::centered(Input::new(username.clone())).fit_horizontally()),
|
Input::new(username.clone())
|
||||||
]),
|
.centered()
|
||||||
Stack::columns(children![
|
.fit_horizontally()
|
||||||
Label::new("Password"),
|
.expand(),
|
||||||
Expand::new(
|
|
||||||
Align::centered(
|
|
||||||
// TODO secure input
|
|
||||||
Input::new(password.clone())
|
|
||||||
)
|
|
||||||
.fit_horizontally()
|
|
||||||
),
|
),
|
||||||
]),
|
)
|
||||||
Stack::columns(children![
|
.and(Stack::columns(
|
||||||
|
Label::new("Password").and(
|
||||||
|
// TODO secure input
|
||||||
|
Input::new(password.clone())
|
||||||
|
.centered()
|
||||||
|
.fit_horizontally()
|
||||||
|
.expand(),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.and(Stack::columns(
|
||||||
Button::new("Cancel")
|
Button::new("Cancel")
|
||||||
.on_click(|_| {
|
.on_click(|_| {
|
||||||
eprintln!("Login cancelled");
|
eprintln!("Login cancelled");
|
||||||
exit(0)
|
exit(0)
|
||||||
})
|
})
|
||||||
.into_escape(),
|
.into_escape()
|
||||||
Expand::empty(),
|
.and(Expand::empty())
|
||||||
Button::new("Log In")
|
.and(
|
||||||
.enabled(valid)
|
Button::new("Log In")
|
||||||
.on_click(move |_| {
|
.enabled(valid)
|
||||||
println!("Welcome, {}", username.get());
|
.on_click(move |_| {
|
||||||
exit(0);
|
println!("Welcome, {}", username.get());
|
||||||
})
|
exit(0);
|
||||||
.into_default(),
|
})
|
||||||
]),
|
.into_default(),
|
||||||
]),
|
),
|
||||||
)))
|
)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.centered()
|
||||||
|
.expand()
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
use gooey::widgets::{Label, Scroll};
|
use gooey::widget::MakeWidget;
|
||||||
|
use gooey::widgets::Label;
|
||||||
use gooey::Run;
|
use gooey::Run;
|
||||||
|
|
||||||
fn main() -> gooey::Result {
|
fn main() -> gooey::Result {
|
||||||
Scroll::new(Label::new(include_str!("../src/widgets/scroll.rs"))).run()
|
Label::new(include_str!("../src/widgets/scroll.rs"))
|
||||||
|
.scroll()
|
||||||
|
.run()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,15 @@
|
||||||
use gooey::styles::components::TextColor;
|
use gooey::styles::components::TextColor;
|
||||||
use gooey::styles::Styles;
|
use gooey::styles::Styles;
|
||||||
use gooey::widget::{Children, MakeWidget, Widget};
|
use gooey::widget::{MakeWidget, Widget};
|
||||||
use gooey::widgets::stack::Stack;
|
use gooey::widgets::stack::Stack;
|
||||||
use gooey::widgets::{Button, Style};
|
use gooey::widgets::{Button, Style};
|
||||||
use gooey::window::Window;
|
|
||||||
use gooey::{styles, Run};
|
use gooey::{styles, Run};
|
||||||
use kludgine::Color;
|
use kludgine::Color;
|
||||||
|
|
||||||
fn main() -> gooey::Result {
|
fn main() -> gooey::Result {
|
||||||
Window::for_widget(
|
Stack::rows(Button::new("Green").and(red_text(Button::new("Red"))))
|
||||||
Stack::rows(
|
.with_styles(Styles::new().with(&TextColor, Color::GREEN))
|
||||||
Children::new()
|
.run()
|
||||||
.with_widget(Button::new("Default"))
|
|
||||||
.with_widget(red_text(Button::new("Styled"))),
|
|
||||||
)
|
|
||||||
.with_styles(Styles::new().with(&TextColor, Color::GREEN)),
|
|
||||||
)
|
|
||||||
.run()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creating reusable style helpers that work with any Widget is straightfoward
|
/// Creating reusable style helpers that work with any Widget is straightfoward
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ pub trait Run: Sized {
|
||||||
/// Creates a [`Children`](crate::widget::Children) instance with the given list
|
/// Creates a [`Children`](crate::widget::Children) instance with the given list
|
||||||
/// of widgets.
|
/// of widgets.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
#[deprecated = "use MakeWidget.and()/Children.and() to chain widgets without a macro"]
|
||||||
macro_rules! children {
|
macro_rules! children {
|
||||||
() => {
|
() => {
|
||||||
$crate::widget::Children::new()
|
$crate::widget::Children::new()
|
||||||
|
|
|
||||||
|
|
@ -396,8 +396,8 @@ impl TreeData {
|
||||||
let node = &self.nodes[&perspective];
|
let node = &self.nodes[&perspective];
|
||||||
if let Some(styles) = &node.styles {
|
if let Some(styles) = &node.styles {
|
||||||
query.retain(|name| {
|
query.retain(|name| {
|
||||||
if let Some(component) = styles.get(name) {
|
if let Some(component) = styles.get(dbg!(name)) {
|
||||||
resolved.insert(name, component.clone());
|
resolved.insert(name, dbg!(component.clone()));
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
|
|
|
||||||
26
src/value.rs
26
src/value.rs
|
|
@ -752,14 +752,36 @@ macro_rules! impl_tuple_for_each {
|
||||||
//
|
//
|
||||||
[$type:ident $field:tt $var:ident, $($rtype:ident $rfield:tt $rvar:ident),+]
|
[$type:ident $field:tt $var:ident, $($rtype:ident $rfield:tt $rvar:ident),+]
|
||||||
) => {
|
) => {
|
||||||
|
|
||||||
impl_tuple_for_each!(
|
impl_tuple_for_each!(
|
||||||
invoke
|
invoke
|
||||||
$self $for_each
|
$self $for_each
|
||||||
$type $field $var
|
$type $field $var
|
||||||
[$($ltype $lfield $lvar,)* $type $field $var, $($rtype $rfield $rvar),+]
|
[$($ltype $lfield $lvar,)* $type $field $var, $($rtype $rfield $rvar),+]
|
||||||
[$($ltype $lfield $lvar,)* $($rtype $rfield $rvar),+]
|
[$($ltype $lfield $lvar,)* $($rtype $rfield $rvar),+]
|
||||||
)
|
);
|
||||||
|
impl_tuple_for_each!(
|
||||||
|
invoke
|
||||||
|
$self $for_each
|
||||||
|
[$($ltype $lfield $lvar,)* $type $field $var]
|
||||||
|
[$($rtype $rfield $rvar),+]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
(
|
||||||
|
invoke
|
||||||
|
// Identifiers used from the outer method
|
||||||
|
$self:ident $for_each:ident
|
||||||
|
// List of all tuple fields that have already been positioned as the focused call
|
||||||
|
[$($ltype:ident $lfield:tt $lvar:ident),+]
|
||||||
|
//
|
||||||
|
[$type:ident $field:tt $var:ident]
|
||||||
|
) => {
|
||||||
|
impl_tuple_for_each!(
|
||||||
|
invoke
|
||||||
|
$self $for_each
|
||||||
|
$type $field $var
|
||||||
|
[$($ltype $lfield $lvar,)+ $type $field $var]
|
||||||
|
[$($ltype $lfield $lvar),+]
|
||||||
|
);
|
||||||
};
|
};
|
||||||
(
|
(
|
||||||
invoke
|
invoke
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ use crate::styles::components::VisualOrder;
|
||||||
use crate::styles::Styles;
|
use crate::styles::Styles;
|
||||||
use crate::tree::Tree;
|
use crate::tree::Tree;
|
||||||
use crate::value::{IntoValue, Value};
|
use crate::value::{IntoValue, Value};
|
||||||
use crate::widgets::Style;
|
use crate::widgets::{Align, Expand, Scroll, Style};
|
||||||
use crate::window::{RunningWindow, Window, WindowBehavior};
|
use crate::window::{RunningWindow, Window, WindowBehavior};
|
||||||
use crate::{ConstraintLimit, Run};
|
use crate::{ConstraintLimit, Run};
|
||||||
|
|
||||||
|
|
@ -479,6 +479,51 @@ pub trait MakeWidget: Sized {
|
||||||
fn into_escape(self) -> WidgetInstance {
|
fn into_escape(self) -> WidgetInstance {
|
||||||
self.make_widget().into_escape()
|
self.make_widget().into_escape()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a collection of widgets using `self` and `other`.
|
||||||
|
fn and(self, other: impl MakeWidget) -> Children {
|
||||||
|
let mut children = Children::new();
|
||||||
|
children.push(self);
|
||||||
|
children.push(other);
|
||||||
|
children
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expands `self` to grow to fill its parent.
|
||||||
|
#[must_use]
|
||||||
|
fn expand(self) -> Expand {
|
||||||
|
Expand::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expands `self` to grow to fill its parent proportionally with other
|
||||||
|
/// weighted siblings.
|
||||||
|
#[must_use]
|
||||||
|
fn expand_weighted(self, weight: u8) -> Expand {
|
||||||
|
Expand::weighted(weight, self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Aligns `self` to the center vertically and horizontally.
|
||||||
|
#[must_use]
|
||||||
|
fn centered(self) -> Align {
|
||||||
|
Align::centered(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows scrolling `self` both vertically and horizontally.
|
||||||
|
#[must_use]
|
||||||
|
fn scroll(self) -> Scroll {
|
||||||
|
Scroll::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows scrolling `self` vertically.
|
||||||
|
#[must_use]
|
||||||
|
fn vertical_scroll(self) -> Scroll {
|
||||||
|
Scroll::vertical(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows scrolling `self` horizontally.
|
||||||
|
#[must_use]
|
||||||
|
fn horizontal_scroll(self) -> Scroll {
|
||||||
|
Scroll::horizontal(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type that can create a [`WidgetInstance`] with a preallocated
|
/// A type that can create a [`WidgetInstance`] with a preallocated
|
||||||
|
|
@ -945,7 +990,7 @@ impl Children {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds `widget` to self and returns the updated list.
|
/// Adds `widget` to self and returns the updated list.
|
||||||
pub fn with_widget<W>(mut self, widget: W) -> Self
|
pub fn and<W>(mut self, widget: W) -> Self
|
||||||
where
|
where
|
||||||
W: MakeWidget,
|
W: MakeWidget,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -365,6 +365,10 @@ where
|
||||||
|
|
||||||
if self.initial_frame {
|
if self.initial_frame {
|
||||||
self.initial_frame = false;
|
self.initial_frame = false;
|
||||||
|
self.root
|
||||||
|
.lock()
|
||||||
|
.as_widget()
|
||||||
|
.mounted(&mut layout_context.as_event_context());
|
||||||
layout_context.focus();
|
layout_context.focus();
|
||||||
layout_context.as_event_context().apply_pending_state();
|
layout_context.as_event_context().apply_pending_state();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue