Skip to content

Commit

Permalink
Dynamic::take, align helpers, scroll fix
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ecton committed Nov 9, 2023
1 parent 8592867 commit a2e28cb
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 48 deletions.
67 changes: 30 additions & 37 deletions examples/login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
14 changes: 12 additions & 2 deletions src/styles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down Expand Up @@ -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
}
}

Expand Down
26 changes: 26 additions & 0 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
54 changes: 53 additions & 1 deletion src/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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 {
Expand Down
46 changes: 39 additions & 7 deletions src/widgets/align.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
}
Expand Down
4 changes: 3 additions & 1 deletion src/widgets/scroll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down

0 comments on commit a2e28cb

Please sign in to comment.