mirror of
https://github.com/danbulant/cushy
synced 2026-06-18 05:51:20 +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::value::Dynamic;
|
||||
use gooey::widget::MakeWidget;
|
||||
use gooey::widgets::{Button, Label, Stack};
|
||||
use gooey::{children, Run, WithClone};
|
||||
use gooey::{Run, WithClone};
|
||||
|
||||
fn main() -> gooey::Result {
|
||||
let animation = Dynamic::new(AnimationHandle::new());
|
||||
|
|
@ -17,11 +18,12 @@ fn main() -> gooey::Result {
|
|||
.on_complete(|| println!("Gooey animations are neat!"))
|
||||
.launch();
|
||||
|
||||
Stack::columns(children![
|
||||
Button::new("To 0").on_click(animate_to(&animation, &value, 0)),
|
||||
Label::new(label),
|
||||
Button::new("To 100").on_click(animate_to(&animation, &value, 100)),
|
||||
])
|
||||
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()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,28 @@
|
|||
use std::string::ToString;
|
||||
|
||||
use gooey::value::Dynamic;
|
||||
use gooey::widgets::{Align, Button, Expand, Label, Resize, Stack};
|
||||
use gooey::{children, Run};
|
||||
use gooey::widget::MakeWidget;
|
||||
use gooey::widgets::{Button, Label, Resize, Stack};
|
||||
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);
|
||||
Expand::new(Align::centered(Stack::columns(children![
|
||||
Resize::width(Lp::points(100), Label::new(label)),
|
||||
Button::new("+").on_click(counter.with_clone(|counter| {
|
||||
move |_| {
|
||||
counter.set(counter.get() + 1);
|
||||
}
|
||||
})),
|
||||
Button::new("-").on_click(counter.with_clone(|counter| {
|
||||
move |_| {
|
||||
counter.set(counter.get() - 1);
|
||||
}
|
||||
})),
|
||||
])))
|
||||
Stack::columns(
|
||||
Resize::width(Lp::points(100), Label::new(label))
|
||||
.and(Button::new("+").on_click(counter.with_clone(|counter| {
|
||||
move |_| {
|
||||
counter.set(counter.get() + 1);
|
||||
}
|
||||
})))
|
||||
.and(Button::new("-").on_click(counter.with_clone(|counter| {
|
||||
move |_| {
|
||||
counter.set(counter.get() - 1);
|
||||
}
|
||||
}))),
|
||||
)
|
||||
.centered()
|
||||
.expand()
|
||||
.run()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use gooey::value::Dynamic;
|
||||
use gooey::widget::{MakeWidget, HANDLED, IGNORED};
|
||||
use gooey::widgets::{Canvas, Expand, Input, Label, Scroll, Stack};
|
||||
use gooey::{children, Run};
|
||||
use gooey::widgets::{Canvas, Input, Label, Stack};
|
||||
use gooey::Run;
|
||||
use kludgine::app::winit::event::ElementState;
|
||||
use kludgine::app::winit::keyboard::Key;
|
||||
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_message = Dynamic::new(String::new());
|
||||
|
||||
Expand::new(Stack::rows(children![
|
||||
Expand::new(Stack::columns(children![
|
||||
Expand::new(Scroll::vertical(Label::new(chat_log.clone()))),
|
||||
Expand::weighted(
|
||||
2,
|
||||
Stack::rows(
|
||||
Stack::columns(
|
||||
Label::new(chat_log.clone()).vertical_scroll().expand().and(
|
||||
Canvas::new(|context| {
|
||||
let entire_canvas = Rect::from(context.graphics.size());
|
||||
context.graphics.draw_shape(
|
||||
|
|
@ -26,10 +24,12 @@ fn main() -> gooey::Result {
|
|||
None,
|
||||
);
|
||||
})
|
||||
)
|
||||
])),
|
||||
Input::new(chat_message.clone())
|
||||
.on_key(move |input| match (input.state, input.logical_key) {
|
||||
.expand_weighted(2),
|
||||
),
|
||||
)
|
||||
.expand()
|
||||
.and(Input::new(chat_message.clone()).on_key(move |input| {
|
||||
match (input.state, input.logical_key) {
|
||||
(ElementState::Pressed, Key::Enter) => {
|
||||
let new_message = chat_message.map_mut(|text| std::mem::take(text));
|
||||
chat_log.map_mut(|chat_log| {
|
||||
|
|
@ -39,8 +39,9 @@ fn main() -> gooey::Result {
|
|||
HANDLED
|
||||
}
|
||||
_ => IGNORED,
|
||||
})
|
||||
.make_widget(),
|
||||
]))
|
||||
}
|
||||
})),
|
||||
)
|
||||
.expand()
|
||||
.run()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use gooey::widgets::{Expand, Input};
|
||||
use gooey::widget::MakeWidget;
|
||||
use gooey::widgets::Input;
|
||||
use gooey::Run;
|
||||
|
||||
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::widget::MakeWidget;
|
||||
use gooey::widgets::{Align, Button, Expand, Input, Label, Resize, Stack};
|
||||
use gooey::{children, Run};
|
||||
use gooey::widgets::{Button, Expand, Input, Label, Resize, Stack};
|
||||
use gooey::Run;
|
||||
use kludgine::figures::units::Lp;
|
||||
|
||||
fn main() -> gooey::Result {
|
||||
|
|
@ -13,42 +13,49 @@ fn main() -> gooey::Result {
|
|||
let valid =
|
||||
(&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
|
||||
Lp::points(400),
|
||||
Stack::rows(children![
|
||||
Stack::columns(children![
|
||||
Label::new("Username"),
|
||||
Expand::new(Align::centered(Input::new(username.clone())).fit_horizontally()),
|
||||
]),
|
||||
Stack::columns(children![
|
||||
Label::new("Password"),
|
||||
Expand::new(
|
||||
Align::centered(
|
||||
// TODO secure input
|
||||
Input::new(password.clone())
|
||||
)
|
||||
.fit_horizontally()
|
||||
Stack::rows(
|
||||
Stack::columns(
|
||||
Label::new("Username").and(
|
||||
Input::new(username.clone())
|
||||
.centered()
|
||||
.fit_horizontally()
|
||||
.expand(),
|
||||
),
|
||||
]),
|
||||
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")
|
||||
.on_click(|_| {
|
||||
eprintln!("Login cancelled");
|
||||
exit(0)
|
||||
})
|
||||
.into_escape(),
|
||||
Expand::empty(),
|
||||
Button::new("Log In")
|
||||
.enabled(valid)
|
||||
.on_click(move |_| {
|
||||
println!("Welcome, {}", username.get());
|
||||
exit(0);
|
||||
})
|
||||
.into_default(),
|
||||
]),
|
||||
]),
|
||||
)))
|
||||
.into_escape()
|
||||
.and(Expand::empty())
|
||||
.and(
|
||||
Button::new("Log In")
|
||||
.enabled(valid)
|
||||
.on_click(move |_| {
|
||||
println!("Welcome, {}", username.get());
|
||||
exit(0);
|
||||
})
|
||||
.into_default(),
|
||||
),
|
||||
)),
|
||||
),
|
||||
)
|
||||
.centered()
|
||||
.expand()
|
||||
.run()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
use gooey::widgets::{Label, Scroll};
|
||||
use gooey::widget::MakeWidget;
|
||||
use gooey::widgets::Label;
|
||||
use gooey::Run;
|
||||
|
||||
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::Styles;
|
||||
use gooey::widget::{Children, MakeWidget, Widget};
|
||||
use gooey::widget::{MakeWidget, Widget};
|
||||
use gooey::widgets::stack::Stack;
|
||||
use gooey::widgets::{Button, Style};
|
||||
use gooey::window::Window;
|
||||
use gooey::{styles, Run};
|
||||
use kludgine::Color;
|
||||
|
||||
fn main() -> gooey::Result {
|
||||
Window::for_widget(
|
||||
Stack::rows(
|
||||
Children::new()
|
||||
.with_widget(Button::new("Default"))
|
||||
.with_widget(red_text(Button::new("Styled"))),
|
||||
)
|
||||
.with_styles(Styles::new().with(&TextColor, Color::GREEN)),
|
||||
)
|
||||
.run()
|
||||
Stack::rows(Button::new("Green").and(red_text(Button::new("Red"))))
|
||||
.with_styles(Styles::new().with(&TextColor, Color::GREEN))
|
||||
.run()
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// of widgets.
|
||||
#[macro_export]
|
||||
#[deprecated = "use MakeWidget.and()/Children.and() to chain widgets without a macro"]
|
||||
macro_rules! children {
|
||||
() => {
|
||||
$crate::widget::Children::new()
|
||||
|
|
|
|||
|
|
@ -396,8 +396,8 @@ impl TreeData {
|
|||
let node = &self.nodes[&perspective];
|
||||
if let Some(styles) = &node.styles {
|
||||
query.retain(|name| {
|
||||
if let Some(component) = styles.get(name) {
|
||||
resolved.insert(name, component.clone());
|
||||
if let Some(component) = styles.get(dbg!(name)) {
|
||||
resolved.insert(name, dbg!(component.clone()));
|
||||
false
|
||||
} else {
|
||||
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),+]
|
||||
) => {
|
||||
|
||||
impl_tuple_for_each!(
|
||||
invoke
|
||||
$self $for_each
|
||||
$type $field $var
|
||||
[$($ltype $lfield $lvar,)* $type $field $var, $($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
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use crate::styles::components::VisualOrder;
|
|||
use crate::styles::Styles;
|
||||
use crate::tree::Tree;
|
||||
use crate::value::{IntoValue, Value};
|
||||
use crate::widgets::Style;
|
||||
use crate::widgets::{Align, Expand, Scroll, Style};
|
||||
use crate::window::{RunningWindow, Window, WindowBehavior};
|
||||
use crate::{ConstraintLimit, Run};
|
||||
|
||||
|
|
@ -479,6 +479,51 @@ pub trait MakeWidget: Sized {
|
|||
fn into_escape(self) -> WidgetInstance {
|
||||
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
|
||||
|
|
@ -945,7 +990,7 @@ impl Children {
|
|||
}
|
||||
|
||||
/// 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
|
||||
W: MakeWidget,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -365,6 +365,10 @@ where
|
|||
|
||||
if self.initial_frame {
|
||||
self.initial_frame = false;
|
||||
self.root
|
||||
.lock()
|
||||
.as_widget()
|
||||
.mounted(&mut layout_context.as_event_context());
|
||||
layout_context.focus();
|
||||
layout_context.as_event_context().apply_pending_state();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue