mirror of
https://github.com/danbulant/cushy
synced 2026-05-24 12:28:23 +00:00
Scroll fixes, resize helpers
This commit is contained in:
parent
ee3813f44d
commit
40343e163f
8 changed files with 150 additions and 87 deletions
|
|
@ -2,8 +2,8 @@ use gooey::value::Dynamic;
|
|||
use gooey::widgets::Button;
|
||||
use gooey::Run;
|
||||
|
||||
// begin rustme snippet: readme
|
||||
fn main() -> gooey::Result {
|
||||
// begin rustme snippet: readme
|
||||
// Create a dynamic usize.
|
||||
let count = Dynamic::new(0_usize);
|
||||
|
||||
|
|
@ -14,5 +14,5 @@ fn main() -> gooey::Result {
|
|||
.on_click(count.with_clone(|count| move |_| count.set(count.get() + 1)))
|
||||
// Run the button as an an application.
|
||||
.run()
|
||||
// end rustme snippet
|
||||
}
|
||||
// end rustme snippet
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use gooey::Run;
|
|||
fn main() -> gooey::Result {
|
||||
let theme_mode = Dynamic::default();
|
||||
set_of_containers(1, theme_mode.clone())
|
||||
.centered()
|
||||
.into_window()
|
||||
.with_theme_mode(theme_mode)
|
||||
.run()
|
||||
|
|
|
|||
|
|
@ -2,27 +2,28 @@ use std::string::ToString;
|
|||
|
||||
use gooey::value::Dynamic;
|
||||
use gooey::widget::MakeWidget;
|
||||
use gooey::widgets::{Button, Label, Resize, Stack};
|
||||
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);
|
||||
Stack::columns(
|
||||
Resize::width(Lp::points(100), Label::new(label))
|
||||
.and(Button::new("+").on_click(counter.with_clone(|counter| {
|
||||
move |_| {
|
||||
*counter.lock() += 1;
|
||||
}
|
||||
})))
|
||||
.and(Button::new("-").on_click(counter.with_clone(|counter| {
|
||||
move |_| {
|
||||
*counter.lock() -= 1;
|
||||
}
|
||||
}))),
|
||||
)
|
||||
.centered()
|
||||
.expand()
|
||||
.run()
|
||||
|
||||
Label::new(label)
|
||||
.width(Lp::points(100))
|
||||
.and(Button::new("+").on_click(counter.with_clone(|counter| {
|
||||
move |_| {
|
||||
*counter.lock() += 1;
|
||||
}
|
||||
})))
|
||||
.and(Button::new("-").on_click(counter.with_clone(|counter| {
|
||||
move |_| {
|
||||
*counter.lock() -= 1;
|
||||
}
|
||||
})))
|
||||
.into_columns()
|
||||
.centered()
|
||||
.expand()
|
||||
.run()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::process::exit;
|
|||
|
||||
use gooey::value::{Dynamic, MapEach};
|
||||
use gooey::widget::MakeWidget;
|
||||
use gooey::widgets::{Button, Expand, Input, Label, Resize, Stack};
|
||||
use gooey::widgets::{Button, Expand, Input, Label};
|
||||
use gooey::Run;
|
||||
use kludgine::figures::units::Lp;
|
||||
|
||||
|
|
@ -14,42 +14,46 @@ 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 = Stack::columns(
|
||||
Label::new("Username").and(Input::new(username.clone()).fit_horizontally().expand()),
|
||||
);
|
||||
let username_row = Label::new("Username")
|
||||
.and(Input::new(username.clone()).expand())
|
||||
.into_columns();
|
||||
|
||||
let password_row = Stack::columns(Label::new("Password").and(
|
||||
// TODO secure input
|
||||
Input::new(password.clone()).fit_horizontally().expand(),
|
||||
));
|
||||
let password_row = Label::new("Password")
|
||||
.and(
|
||||
// TODO secure input
|
||||
Input::new(password.clone()).expand(),
|
||||
)
|
||||
.into_columns();
|
||||
|
||||
let buttons = Stack::columns(
|
||||
Button::new("Cancel")
|
||||
.on_click(|_| {
|
||||
eprintln!("Login cancelled");
|
||||
exit(0)
|
||||
})
|
||||
.into_escape()
|
||||
.and(Expand::empty())
|
||||
.and(
|
||||
Button::new("Log In")
|
||||
.enabled(valid)
|
||||
.on_click(move |_| {
|
||||
println!("Welcome, {}", username.get());
|
||||
exit(0);
|
||||
})
|
||||
.into_default(),
|
||||
),
|
||||
);
|
||||
let buttons = Button::new("Cancel")
|
||||
.on_click(|_| {
|
||||
eprintln!("Login cancelled");
|
||||
exit(0)
|
||||
})
|
||||
.into_escape()
|
||||
.and(Expand::empty())
|
||||
.and(
|
||||
Button::new("Log In")
|
||||
.enabled(valid)
|
||||
.on_click(move |_| {
|
||||
println!("Welcome, {}", username.get());
|
||||
exit(0);
|
||||
})
|
||||
.into_default(),
|
||||
)
|
||||
.into_columns();
|
||||
|
||||
Resize::width(
|
||||
Lp::points(300)..Lp::points(600),
|
||||
Stack::rows(username_row.and(password_row).and(buttons)),
|
||||
)
|
||||
.scroll()
|
||||
.centered()
|
||||
.expand()
|
||||
.run()
|
||||
username_row
|
||||
.pad()
|
||||
.and(password_row.pad())
|
||||
.and(buttons.pad())
|
||||
.into_rows()
|
||||
.contain()
|
||||
.width(Lp::points(300)..Lp::points(600))
|
||||
.scroll()
|
||||
.centered()
|
||||
.expand()
|
||||
.run()
|
||||
}
|
||||
|
||||
fn validate(username: &String, password: &String) -> bool {
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ use kludgine::Color;
|
|||
|
||||
use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext, WidgetContext};
|
||||
use crate::styles::{
|
||||
ContainerLevel, Dimension, Edges, IntoComponentValue, NamedComponent, Styles, ThemePair,
|
||||
VisualOrder,
|
||||
ContainerLevel, Dimension, DimensionRange, Edges, IntoComponentValue, NamedComponent, Styles,
|
||||
ThemePair, VisualOrder,
|
||||
};
|
||||
use crate::tree::Tree;
|
||||
use crate::value::{IntoValue, Value};
|
||||
use crate::widgets::{Align, Container, Expand, Scroll, Stack, Style};
|
||||
use crate::widgets::{Align, Container, Expand, Resize, Scroll, Stack, Style};
|
||||
use crate::window::{RunningWindow, ThemeMode, Window, WindowBehavior};
|
||||
use crate::{ConstraintLimit, Run};
|
||||
|
||||
|
|
@ -622,6 +622,26 @@ pub trait MakeWidget: Sized {
|
|||
Expand::horizontal(self)
|
||||
}
|
||||
|
||||
/// Resizes `self` to `width`.
|
||||
///
|
||||
/// `width` can be an individual
|
||||
/// [`Dimension`]/[`Px`]/[`Lp`](crate::kludgine::figures::units::Lp) or a
|
||||
/// range.
|
||||
#[must_use]
|
||||
fn width(self, width: impl Into<DimensionRange>) -> Resize {
|
||||
Resize::from_width(width, self)
|
||||
}
|
||||
|
||||
/// Resizes `self` to `height`.
|
||||
///
|
||||
/// `height` can be an individual
|
||||
/// [`Dimension`]/[`Px`]/[`Lp`](crate::kludgine::figures::units::Lp) or a
|
||||
/// range.
|
||||
#[must_use]
|
||||
fn height(self, height: impl Into<DimensionRange>) -> Resize {
|
||||
Resize::from_height(height, self)
|
||||
}
|
||||
|
||||
/// Aligns `self` to the center vertically and horizontally.
|
||||
#[must_use]
|
||||
fn centered(self) -> Align {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ use kludgine::text::TextOrigin;
|
|||
use kludgine::{Color, Kludgine};
|
||||
|
||||
use crate::context::{EventContext, LayoutContext, WidgetContext};
|
||||
use crate::styles::components::{HighlightColor, LineHeight, OutlineColor, TextColor, TextSize};
|
||||
use crate::styles::components::{
|
||||
HighlightColor, IntrinsicPadding, LineHeight, OutlineColor, TextColor, TextSize,
|
||||
};
|
||||
use crate::utils::ModifiersExt;
|
||||
use crate::value::{Generation, IntoValue, Value};
|
||||
use crate::widget::{Callback, EventHandling, Widget, HANDLED, IGNORED};
|
||||
|
|
@ -195,6 +197,8 @@ impl Widget for Input {
|
|||
self.cursor_state.update(context.elapsed());
|
||||
let cursor_state = self.cursor_state;
|
||||
let size = context.gfx.size();
|
||||
let padding = context.get(&IntrinsicPadding).into_px(context.gfx.scale());
|
||||
let padding = Point::<Px>::new(padding, padding);
|
||||
let highlight = context.get(&HighlightColor);
|
||||
let editor = self.editor_mut(&mut context.gfx, &context.widget);
|
||||
let cursor = editor.cursor();
|
||||
|
|
@ -228,7 +232,7 @@ impl Widget for Input {
|
|||
Rect::new(start_position, Size::new(width, line_height)),
|
||||
highlight,
|
||||
),
|
||||
Point::default(),
|
||||
padding,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
|
@ -240,7 +244,7 @@ impl Widget for Input {
|
|||
Rect::new(start_position, Size::new(width, line_height)),
|
||||
highlight,
|
||||
),
|
||||
Point::default(),
|
||||
padding,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
|
@ -256,7 +260,7 @@ impl Widget for Input {
|
|||
),
|
||||
highlight,
|
||||
),
|
||||
Point::default(),
|
||||
padding,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
|
@ -270,7 +274,7 @@ impl Widget for Input {
|
|||
),
|
||||
highlight,
|
||||
),
|
||||
Point::default(),
|
||||
padding,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
|
@ -283,7 +287,7 @@ impl Widget for Input {
|
|||
Rect::new(start_position, Size::new(width, line_height)),
|
||||
highlight,
|
||||
),
|
||||
Point::default(),
|
||||
padding,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
|
@ -300,7 +304,7 @@ impl Widget for Input {
|
|||
),
|
||||
highlight,
|
||||
),
|
||||
Point::default(),
|
||||
padding,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
|
@ -323,7 +327,7 @@ impl Widget for Input {
|
|||
),
|
||||
highlight, // TODO cursor should be a bold color, highlight probably not. This should have its own color.
|
||||
),
|
||||
Point::default(),
|
||||
padding,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
|
@ -340,14 +344,9 @@ impl Widget for Input {
|
|||
}
|
||||
|
||||
let text_color = context.get(&TextColor);
|
||||
context.gfx.draw_text_buffer(
|
||||
buffer,
|
||||
text_color,
|
||||
TextOrigin::TopLeft,
|
||||
Point::<Px>::default(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
context
|
||||
.gfx
|
||||
.draw_text_buffer(buffer, text_color, TextOrigin::TopLeft, padding, None, None);
|
||||
}
|
||||
|
||||
fn layout(
|
||||
|
|
@ -355,22 +354,34 @@ impl Widget for Input {
|
|||
available_space: Size<ConstraintLimit>,
|
||||
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
|
||||
) -> Size<UPx> {
|
||||
let padding = context
|
||||
.get(&IntrinsicPadding)
|
||||
.into_px(context.gfx.scale())
|
||||
.into_unsigned();
|
||||
if self.needs_to_select_all {
|
||||
self.needs_to_select_all = false;
|
||||
self.select_all();
|
||||
}
|
||||
let editor = self.editor_mut(&mut context.graphics.gfx, &context.graphics.widget);
|
||||
let buffer = editor.buffer_mut();
|
||||
buffer.set_size(
|
||||
context.gfx.font_system(),
|
||||
available_space.width.max().into_float(),
|
||||
available_space.height.max().into_float(),
|
||||
);
|
||||
let width = available_space
|
||||
.width
|
||||
.max()
|
||||
.saturating_sub(padding * 2)
|
||||
.into_float();
|
||||
let height = available_space
|
||||
.height
|
||||
.max()
|
||||
.saturating_sub(padding * 2)
|
||||
.into_float();
|
||||
|
||||
buffer.set_size(context.gfx.font_system(), width, height);
|
||||
context
|
||||
.gfx
|
||||
.measure_text_buffer::<Px>(buffer, Color::WHITE)
|
||||
.size
|
||||
.into_unsigned()
|
||||
+ Size::new(padding * 2, padding * 2)
|
||||
}
|
||||
|
||||
fn keyboard_input(
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ impl Resize {
|
|||
|
||||
/// Resizes `child`'s width to `width`.
|
||||
#[must_use]
|
||||
pub fn width(width: impl Into<DimensionRange>, child: impl MakeWidget) -> Self {
|
||||
pub fn from_width(width: impl Into<DimensionRange>, child: impl MakeWidget) -> Self {
|
||||
Self {
|
||||
child: WidgetRef::new(child),
|
||||
width: width.into(),
|
||||
|
|
@ -46,9 +46,31 @@ impl Resize {
|
|||
}
|
||||
}
|
||||
|
||||
/// Resizes `self` to `width`.
|
||||
///
|
||||
/// `width` can be an individual
|
||||
/// [`Dimension`]/[`Px`]/[`Lp`](crate::kludgine::figures::units::Lp) or a
|
||||
/// range.
|
||||
#[must_use]
|
||||
pub fn width(mut self, width: impl Into<DimensionRange>) -> Self {
|
||||
self.width = width.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Resizes `self` to `height`.
|
||||
///
|
||||
/// `width` can be an individual
|
||||
/// [`Dimension`]/[`Px`]/[`Lp`](crate::kludgine::figures::units::Lp) or a
|
||||
/// range.
|
||||
#[must_use]
|
||||
pub fn height(mut self, height: impl Into<DimensionRange>) -> Self {
|
||||
self.height = height.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Resizes `child`'s height to `height`.
|
||||
#[must_use]
|
||||
pub fn height(height: impl Into<DimensionRange>, child: impl MakeWidget) -> Self {
|
||||
pub fn from_height(height: impl Into<DimensionRange>, child: impl MakeWidget) -> Self {
|
||||
Self {
|
||||
child: WidgetRef::new(child),
|
||||
width: DimensionRange::from(..),
|
||||
|
|
|
|||
|
|
@ -176,12 +176,12 @@ impl Widget for Scroll {
|
|||
.into_signed();
|
||||
let max_extents = Size::new(
|
||||
if self.enabled.x {
|
||||
ConstraintLimit::ClippedAfter(UPx::MAX - scroll.x.into_unsigned())
|
||||
ConstraintLimit::ClippedAfter((control_size.width).into_unsigned())
|
||||
} else {
|
||||
available_space.width
|
||||
},
|
||||
if self.enabled.y {
|
||||
ConstraintLimit::ClippedAfter(UPx::MAX - scroll.y.into_unsigned())
|
||||
ConstraintLimit::ClippedAfter((control_size.height).into_unsigned())
|
||||
} else {
|
||||
available_space.height
|
||||
},
|
||||
|
|
@ -242,16 +242,12 @@ impl Widget for Scroll {
|
|||
|
||||
Size::new(
|
||||
if self.enabled.x {
|
||||
available_space
|
||||
.width
|
||||
.fit_measured(self.content_size.width, context.gfx.scale())
|
||||
constrain_child(available_space.width, self.content_size.width)
|
||||
} else {
|
||||
self.content_size.width.into_unsigned()
|
||||
},
|
||||
if self.enabled.y {
|
||||
available_space
|
||||
.height
|
||||
.fit_measured(self.content_size.height, context.gfx.scale())
|
||||
constrain_child(available_space.height, self.content_size.height)
|
||||
} else {
|
||||
self.content_size.height.into_unsigned()
|
||||
},
|
||||
|
|
@ -287,6 +283,14 @@ impl Widget for Scroll {
|
|||
}
|
||||
}
|
||||
|
||||
fn constrain_child(constraint: ConstraintLimit, measured: Px) -> UPx {
|
||||
let measured = measured.into_unsigned();
|
||||
match constraint {
|
||||
ConstraintLimit::Known(size) => size.min(measured),
|
||||
ConstraintLimit::ClippedAfter(_) => measured,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct ScrollbarInfo {
|
||||
offset: Px,
|
||||
|
|
|
|||
Loading…
Reference in a new issue