mirror of
https://github.com/danbulant/cushy
synced 2026-05-23 06:09:09 +00:00
Dynamic::take, align helpers, scroll fix
Scroll was previously taking the graphics region as its control size as opposed to the constraints. This was due to this code originally living in redraw. This fixes scroll areas being able to scroll their contents fully when sharing window space with other widgts.
This commit is contained in:
parent
85928675ab
commit
a2e28cb522
6 changed files with 163 additions and 48 deletions
|
|
@ -13,46 +13,39 @@ fn main() -> gooey::Result {
|
|||
let valid =
|
||||
(&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 password_row = Stack::columns(Label::new("Password").and(
|
||||
// TODO secure input
|
||||
Input::new(password.clone()).fit_horizontally().expand(),
|
||||
));
|
||||
|
||||
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(),
|
||||
),
|
||||
);
|
||||
|
||||
Resize::width(
|
||||
// TODO We need a min/max range for the Resize widget
|
||||
Lp::points(400),
|
||||
Stack::rows(
|
||||
Stack::columns(
|
||||
Label::new("Username").and(
|
||||
Input::new(username.clone())
|
||||
.centered()
|
||||
.fit_horizontally()
|
||||
.expand(),
|
||||
),
|
||||
)
|
||||
.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()
|
||||
.and(Expand::empty())
|
||||
.and(
|
||||
Button::new("Log In")
|
||||
.enabled(valid)
|
||||
.on_click(move |_| {
|
||||
println!("Welcome, {}", username.get());
|
||||
exit(0);
|
||||
})
|
||||
.into_default(),
|
||||
),
|
||||
)),
|
||||
),
|
||||
Stack::rows(username_row.and(password_row).and(buttons)),
|
||||
)
|
||||
.centered()
|
||||
.expand()
|
||||
|
|
|
|||
|
|
@ -235,9 +235,14 @@ pub enum FlexibleDimension {
|
|||
Dimension(Dimension),
|
||||
}
|
||||
|
||||
impl FlexibleDimension {
|
||||
/// A dimension of 0 pixels.
|
||||
pub const ZERO: Self = Self::Dimension(Dimension::ZERO);
|
||||
}
|
||||
|
||||
impl Default for FlexibleDimension {
|
||||
fn default() -> Self {
|
||||
Self::Dimension(Dimension::default())
|
||||
Self::ZERO
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -268,9 +273,14 @@ pub enum Dimension {
|
|||
Lp(Lp),
|
||||
}
|
||||
|
||||
impl Dimension {
|
||||
/// A dimension of 0 pixels.
|
||||
pub const ZERO: Self = Self::Px(Px(0));
|
||||
}
|
||||
|
||||
impl Default for Dimension {
|
||||
fn default() -> Self {
|
||||
Self::Px(Px(0))
|
||||
Self::ZERO
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
26
src/value.rs
26
src/value.rs
|
|
@ -117,6 +117,32 @@ impl<T> Dynamic<T> {
|
|||
self.0.get().value
|
||||
}
|
||||
|
||||
/// Returns the currently stored value, replacing the current contents with
|
||||
/// `T::default()`.
|
||||
#[must_use]
|
||||
pub fn take(&self) -> T
|
||||
where
|
||||
T: Default,
|
||||
{
|
||||
std::mem::take(&mut self.lock())
|
||||
}
|
||||
|
||||
/// Checks if the currently stored value is different than `T::default()`,
|
||||
/// and if so, returns `Some(self.take())`.
|
||||
#[must_use]
|
||||
pub fn take_if_not_default(&self) -> Option<T>
|
||||
where
|
||||
T: Default + PartialEq,
|
||||
{
|
||||
let default = T::default();
|
||||
let mut guard = self.lock();
|
||||
if *guard == default {
|
||||
None
|
||||
} else {
|
||||
Some(std::mem::replace(&mut guard, default))
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces the contents with `new_value`, returning the previous contents.
|
||||
/// Before returning from this function, all observers will be notified that
|
||||
/// the contents have been updated.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
use std::any::Any;
|
||||
use std::clone::Clone;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::{ControlFlow, Deref};
|
||||
use std::ops::{ControlFlow, Deref, DerefMut};
|
||||
use std::panic::UnwindSafe;
|
||||
use std::sync::atomic::{self, AtomicU64};
|
||||
use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
|
||||
|
|
@ -514,6 +514,36 @@ pub trait MakeWidget: Sized {
|
|||
Align::centered(self)
|
||||
}
|
||||
|
||||
/// Aligns `self` to the left.
|
||||
fn align_left(self) -> Align {
|
||||
self.centered().align_left()
|
||||
}
|
||||
|
||||
/// Aligns `self` to the right.
|
||||
fn align_right(self) -> Align {
|
||||
self.centered().align_right()
|
||||
}
|
||||
|
||||
/// Aligns `self` to the top.
|
||||
fn align_top(self) -> Align {
|
||||
self.centered().align_top()
|
||||
}
|
||||
|
||||
/// Aligns `self` to the bottom.
|
||||
fn align_bottom(self) -> Align {
|
||||
self.centered().align_bottom()
|
||||
}
|
||||
|
||||
/// Fits `self` horizontally within its parent.
|
||||
fn fit_horizontally(self) -> Align {
|
||||
self.centered().fit_horizontally()
|
||||
}
|
||||
|
||||
/// Fits `self` vertically within its parent.
|
||||
fn fit_vertically(self) -> Align {
|
||||
self.centered().fit_vertically()
|
||||
}
|
||||
|
||||
/// Allows scrolling `self` both vertically and horizontally.
|
||||
#[must_use]
|
||||
fn scroll(self) -> Scroll {
|
||||
|
|
@ -1002,6 +1032,14 @@ impl Children {
|
|||
self.ordered.push(widget.make_widget());
|
||||
}
|
||||
|
||||
/// Inserts `widget` into the list at `index`.
|
||||
pub fn insert<W>(&mut self, index: usize, widget: W)
|
||||
where
|
||||
W: MakeWidget,
|
||||
{
|
||||
self.ordered.insert(index, widget.make_widget());
|
||||
}
|
||||
|
||||
/// Adds `widget` to self and returns the updated list.
|
||||
pub fn and<W>(mut self, widget: W) -> Self
|
||||
where
|
||||
|
|
@ -1022,6 +1060,14 @@ impl Children {
|
|||
pub fn is_empty(&self) -> bool {
|
||||
self.ordered.is_empty()
|
||||
}
|
||||
|
||||
/// Truncates the collection of children to `length`.
|
||||
///
|
||||
/// If this collection is already smaller or the same size as `length`, this
|
||||
/// function does nothing.
|
||||
pub fn truncate(&mut self, length: usize) {
|
||||
self.ordered.truncate(length);
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> FromIterator<W> for Children
|
||||
|
|
@ -1043,6 +1089,12 @@ impl Deref for Children {
|
|||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Children {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.ordered
|
||||
}
|
||||
}
|
||||
|
||||
/// A child widget
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum WidgetRef {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use kludgine::figures::units::UPx;
|
|||
use kludgine::figures::{Fraction, IntoSigned, IntoUnsigned, Point, Rect, ScreenScale, Size};
|
||||
|
||||
use crate::context::{AsEventContext, LayoutContext};
|
||||
use crate::styles::{Dimension, Edges, FlexibleDimension};
|
||||
use crate::styles::{Edges, FlexibleDimension};
|
||||
use crate::value::{IntoValue, Value};
|
||||
use crate::widget::{MakeWidget, WidgetRef, WrapperWidget};
|
||||
use crate::ConstraintLimit;
|
||||
|
|
@ -32,22 +32,54 @@ impl Align {
|
|||
Self::new(FlexibleDimension::Auto, widget)
|
||||
}
|
||||
|
||||
/// Sets the left and right edges to 0 and returns self.
|
||||
/// Sets the left edge of alignment to 0 and returns self.
|
||||
#[must_use]
|
||||
pub fn align_left(mut self) -> Self {
|
||||
self.edges
|
||||
.map_mut(|edges| edges.left = FlexibleDimension::ZERO);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the top edge of alignment to 0 and returns self.
|
||||
#[must_use]
|
||||
pub fn align_top(mut self) -> Self {
|
||||
self.edges
|
||||
.map_mut(|edges| edges.top = FlexibleDimension::ZERO);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the bottom edge of alignment to 0 and returns self.
|
||||
#[must_use]
|
||||
pub fn align_bottom(mut self) -> Self {
|
||||
self.edges
|
||||
.map_mut(|edges| edges.bottom = FlexibleDimension::ZERO);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the right edge of alignment to 0 and returns self.
|
||||
#[must_use]
|
||||
pub fn align_right(mut self) -> Self {
|
||||
self.edges
|
||||
.map_mut(|edges| edges.right = FlexibleDimension::ZERO);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the left and right edges of alignment to 0 and returns self.
|
||||
#[must_use]
|
||||
pub fn fit_horizontally(mut self) -> Self {
|
||||
self.edges.map_mut(|edges| {
|
||||
edges.left = FlexibleDimension::Dimension(Dimension::default());
|
||||
edges.right = FlexibleDimension::Dimension(Dimension::default());
|
||||
edges.left = FlexibleDimension::ZERO;
|
||||
edges.right = FlexibleDimension::ZERO;
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the top and bottom edges to 0 and returns self.
|
||||
/// Sets the top and bottom edges of alignment to 0 and returns self.
|
||||
#[must_use]
|
||||
pub fn fit_vertically(mut self) -> Self {
|
||||
self.edges.map_mut(|edges| {
|
||||
edges.top = FlexibleDimension::Dimension(Dimension::default());
|
||||
edges.bottom = FlexibleDimension::Dimension(Dimension::default());
|
||||
edges.top = FlexibleDimension::ZERO;
|
||||
edges.bottom = FlexibleDimension::ZERO;
|
||||
});
|
||||
self
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,7 +186,9 @@ impl Widget for Scroll {
|
|||
|
||||
let (mut scroll, current_max_scroll) = self.constrain_scroll();
|
||||
|
||||
let control_size = context.graphics.region().size;
|
||||
let control_size =
|
||||
Size::<UPx>::new(available_space.width.max(), available_space.height.max())
|
||||
.into_signed();
|
||||
let max_extents = Size::new(
|
||||
if self.enabled.x {
|
||||
ConstraintLimit::ClippedAfter(UPx::MAX - scroll.x.into_unsigned())
|
||||
|
|
|
|||
Loading…
Reference in a new issue