mirror of
https://github.com/danbulant/cushy
synced 2026-07-08 04:30:34 +00:00
Added Layers
This commit is contained in:
parent
17847d6947
commit
288119a831
5 changed files with 211 additions and 6 deletions
12
examples/layers.rs
Normal file
12
examples/layers.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
use gooey::widget::MakeWidget;
|
||||||
|
use gooey::widgets::Space;
|
||||||
|
use gooey::Run;
|
||||||
|
use kludgine::Color;
|
||||||
|
|
||||||
|
fn main() -> gooey::Result {
|
||||||
|
Space::colored(Color::RED)
|
||||||
|
.and("Layers stack widgets on top of each other")
|
||||||
|
.into_layers()
|
||||||
|
.centered()
|
||||||
|
.run()
|
||||||
|
}
|
||||||
|
|
@ -23,7 +23,9 @@ use crate::styles::components::{
|
||||||
use crate::styles::{ComponentDefinition, Styles, Theme, ThemePair};
|
use crate::styles::{ComponentDefinition, Styles, Theme, ThemePair};
|
||||||
use crate::utils::IgnorePoison;
|
use crate::utils::IgnorePoison;
|
||||||
use crate::value::{IntoValue, Value};
|
use crate::value::{IntoValue, Value};
|
||||||
use crate::widget::{EventHandling, ManagedWidget, WidgetId, WidgetInstance, WidgetRef};
|
use crate::widget::{
|
||||||
|
EventHandling, ManagedWidget, RootBehavior, WidgetId, WidgetInstance, WidgetRef,
|
||||||
|
};
|
||||||
use crate::window::{CursorState, RunningWindow, ThemeMode};
|
use crate::window::{CursorState, RunningWindow, ThemeMode};
|
||||||
use crate::ConstraintLimit;
|
use crate::ConstraintLimit;
|
||||||
|
|
||||||
|
|
@ -461,6 +463,17 @@ impl<'context, 'window> EventContext<'context, 'window> {
|
||||||
// sets it to `true` explicitly.
|
// sets it to `true` explicitly.
|
||||||
self.pending_state.focus_is_advancing = advance;
|
self.pending_state.focus_is_advancing = advance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Invokes
|
||||||
|
/// [`Widget::root_behavior()`](crate::widget::Widget::root_behavior) on
|
||||||
|
/// this context's widget and returns the result.
|
||||||
|
pub fn root_behavior(&mut self) -> Option<(RootBehavior, WidgetInstance)> {
|
||||||
|
self.current_node
|
||||||
|
.clone()
|
||||||
|
.lock()
|
||||||
|
.as_widget()
|
||||||
|
.root_behavior(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'context, 'window> Deref for EventContext<'context, 'window> {
|
impl<'context, 'window> Deref for EventContext<'context, 'window> {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use std::clone::Clone;
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
use std::ops::{ControlFlow, Deref, DerefMut};
|
use std::ops::{ControlFlow, Deref, DerefMut};
|
||||||
use std::panic::UnwindSafe;
|
use std::panic::UnwindSafe;
|
||||||
|
use std::slice;
|
||||||
use std::sync::atomic::{self, AtomicU64};
|
use std::sync::atomic::{self, AtomicU64};
|
||||||
use std::sync::{Arc, Mutex, MutexGuard};
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
|
|
||||||
|
|
@ -38,8 +39,8 @@ use crate::utils::IgnorePoison;
|
||||||
use crate::value::{Dynamic, IntoDynamic, IntoValue, Validation, Value};
|
use crate::value::{Dynamic, IntoDynamic, IntoValue, Validation, Value};
|
||||||
use crate::widgets::checkbox::{Checkable, CheckboxState};
|
use crate::widgets::checkbox::{Checkable, CheckboxState};
|
||||||
use crate::widgets::{
|
use crate::widgets::{
|
||||||
Align, Button, Checkbox, Collapse, Container, Expand, Resize, Scroll, Space, Stack, Style,
|
Align, Button, Checkbox, Collapse, Container, Expand, Layers, Resize, Scroll, Space, Stack,
|
||||||
Themed, ThemedMode, Validated,
|
Style, Themed, ThemedMode, Validated,
|
||||||
};
|
};
|
||||||
use crate::window::{RunningWindow, ThemeMode, Window, WindowBehavior};
|
use crate::window::{RunningWindow, ThemeMode, Window, WindowBehavior};
|
||||||
use crate::{ConstraintLimit, Run};
|
use crate::{ConstraintLimit, Run};
|
||||||
|
|
@ -222,7 +223,7 @@ pub trait Widget: Send + UnwindSafe + Debug + 'static {
|
||||||
fn root_behavior(
|
fn root_behavior(
|
||||||
&mut self,
|
&mut self,
|
||||||
context: &mut EventContext<'_, '_>,
|
context: &mut EventContext<'_, '_>,
|
||||||
) -> Option<(RootBehavior, &WidgetInstance)> {
|
) -> Option<(RootBehavior, WidgetInstance)> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -531,8 +532,9 @@ where
|
||||||
fn root_behavior(
|
fn root_behavior(
|
||||||
&mut self,
|
&mut self,
|
||||||
context: &mut EventContext<'_, '_>,
|
context: &mut EventContext<'_, '_>,
|
||||||
) -> Option<(RootBehavior, &WidgetInstance)> {
|
) -> Option<(RootBehavior, WidgetInstance)> {
|
||||||
T::root_behavior(self, context).map(|behavior| (behavior, T::child_mut(self).widget()))
|
T::root_behavior(self, context)
|
||||||
|
.map(|behavior| (behavior, T::child_mut(self).widget().clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
|
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
|
||||||
|
|
@ -1675,6 +1677,13 @@ impl Children {
|
||||||
pub fn into_columns(self) -> Stack {
|
pub fn into_columns(self) -> Stack {
|
||||||
Stack::columns(self)
|
Stack::columns(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `self` as [`Layers`], with the widgets being stacked in the Z
|
||||||
|
/// direction.
|
||||||
|
#[must_use]
|
||||||
|
pub fn into_layers(self) -> Layers {
|
||||||
|
Layers::new(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Children {
|
impl Debug for Children {
|
||||||
|
|
@ -1695,6 +1704,13 @@ impl Dynamic<Children> {
|
||||||
pub fn into_columns(self) -> Stack {
|
pub fn into_columns(self) -> Stack {
|
||||||
Stack::columns(self)
|
Stack::columns(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `self` as [`Layers`], with the widgets being stacked in the Z
|
||||||
|
/// direction.
|
||||||
|
#[must_use]
|
||||||
|
pub fn into_layers(self) -> Layers {
|
||||||
|
Layers::new(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> FromIterator<W> for Children
|
impl<W> FromIterator<W> for Children
|
||||||
|
|
@ -1722,6 +1738,15 @@ impl DerefMut for Children {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a Children {
|
||||||
|
type IntoIter = slice::Iter<'a, WidgetInstance>;
|
||||||
|
type Item = &'a WidgetInstance;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.ordered.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A child widget
|
/// A child widget
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum WidgetRef {
|
pub enum WidgetRef {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ mod expand;
|
||||||
pub mod grid;
|
pub mod grid;
|
||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod label;
|
pub mod label;
|
||||||
|
mod layers;
|
||||||
mod mode_switch;
|
mod mode_switch;
|
||||||
pub mod progress;
|
pub mod progress;
|
||||||
mod radio;
|
mod radio;
|
||||||
|
|
@ -38,6 +39,7 @@ pub use data::Data;
|
||||||
pub use expand::Expand;
|
pub use expand::Expand;
|
||||||
pub use input::Input;
|
pub use input::Input;
|
||||||
pub use label::Label;
|
pub use label::Label;
|
||||||
|
pub use layers::Layers;
|
||||||
pub use mode_switch::ThemedMode;
|
pub use mode_switch::ThemedMode;
|
||||||
pub use progress::ProgressBar;
|
pub use progress::ProgressBar;
|
||||||
pub use radio::Radio;
|
pub use radio::Radio;
|
||||||
|
|
|
||||||
153
src/widgets/layers.rs
Normal file
153
src/widgets/layers.rs
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use gooey::widget::{RootBehavior, WidgetInstance};
|
||||||
|
use kludgine::figures::units::UPx;
|
||||||
|
use kludgine::figures::{IntoSigned, Rect, Size, Zero};
|
||||||
|
|
||||||
|
use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext};
|
||||||
|
use crate::value::{Generation, IntoValue, Value};
|
||||||
|
use crate::widget::{Children, ManagedWidget, Widget};
|
||||||
|
use crate::ConstraintLimit;
|
||||||
|
|
||||||
|
/// A Z-direction stack of widgets.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Layers {
|
||||||
|
/// The children that are laid out as layers with index 0 being the lowest (bottom).
|
||||||
|
pub children: Value<Children>,
|
||||||
|
mounted: Vec<ManagedWidget>,
|
||||||
|
mounted_generation: Option<Generation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layers {
|
||||||
|
/// Returns a new instance that lays out `children` as layers.
|
||||||
|
pub fn new(children: impl IntoValue<Children>) -> Self {
|
||||||
|
Self {
|
||||||
|
children: children.into_value(),
|
||||||
|
mounted: Vec::new(),
|
||||||
|
mounted_generation: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn synchronize_children(&mut self, context: &mut EventContext<'_, '_>) {
|
||||||
|
let current_generation = self.children.generation();
|
||||||
|
self.children.invalidate_when_changed(context);
|
||||||
|
if current_generation.map_or_else(
|
||||||
|
|| self.children.map(Children::len) != self.mounted.len(),
|
||||||
|
|gen| Some(gen) != self.mounted_generation,
|
||||||
|
) {
|
||||||
|
self.mounted_generation = self.children.generation();
|
||||||
|
self.children.map(|children| {
|
||||||
|
for (index, widget) in children.iter().enumerate() {
|
||||||
|
if self
|
||||||
|
.mounted
|
||||||
|
.get(index)
|
||||||
|
.map_or(true, |child| child != widget)
|
||||||
|
{
|
||||||
|
// These entries do not match. See if we can find the
|
||||||
|
// new id somewhere else, if so we can swap the entries.
|
||||||
|
if let Some((swap_index, _)) = self
|
||||||
|
.mounted
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.skip(index + 1)
|
||||||
|
.find(|(_, child)| *child == widget)
|
||||||
|
{
|
||||||
|
self.mounted.swap(index, swap_index);
|
||||||
|
} else {
|
||||||
|
// This is a brand new child.
|
||||||
|
self.mounted
|
||||||
|
.insert(index, context.push_child(widget.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any children remaining at the end of this process are ones
|
||||||
|
// that have been removed.
|
||||||
|
for removed in self.mounted.drain(children.len()..) {
|
||||||
|
context.remove_child(&removed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for Layers {
|
||||||
|
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
|
||||||
|
self.synchronize_children(&mut context.as_event_context());
|
||||||
|
|
||||||
|
for child in &self.mounted {
|
||||||
|
context.for_other(child).redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn summarize(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.children.map(|children| {
|
||||||
|
let mut f = f.debug_tuple("Layered");
|
||||||
|
for child in children {
|
||||||
|
f.field(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
f.finish()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout(
|
||||||
|
&mut self,
|
||||||
|
available_space: Size<ConstraintLimit>,
|
||||||
|
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
|
||||||
|
) -> Size<UPx> {
|
||||||
|
self.synchronize_children(&mut context.as_event_context());
|
||||||
|
|
||||||
|
let mut size = Size::ZERO;
|
||||||
|
|
||||||
|
for child in &self.mounted {
|
||||||
|
size = size.max(
|
||||||
|
context
|
||||||
|
.for_other(child)
|
||||||
|
.as_temporary()
|
||||||
|
.layout(available_space),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we know the size of the widget, we can request the widgets fill
|
||||||
|
// the allocated space.
|
||||||
|
let layout = Rect::from(size).into_signed();
|
||||||
|
for child in &self.mounted {
|
||||||
|
context
|
||||||
|
.for_other(child)
|
||||||
|
.layout(size.map(ConstraintLimit::Fill));
|
||||||
|
context.set_child_layout(child, layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
size
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mounted(&mut self, context: &mut EventContext<'_, '_>) {
|
||||||
|
self.synchronize_children(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unmounted(&mut self, context: &mut EventContext<'_, '_>) {
|
||||||
|
for child in self.mounted.drain(..) {
|
||||||
|
context.remove_child(&child);
|
||||||
|
}
|
||||||
|
self.mounted_generation = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn root_behavior(
|
||||||
|
&mut self,
|
||||||
|
context: &mut EventContext<'_, '_>,
|
||||||
|
) -> Option<(RootBehavior, WidgetInstance)> {
|
||||||
|
self.synchronize_children(context);
|
||||||
|
|
||||||
|
for child in &self.mounted {
|
||||||
|
let Some((child_behavior, next_in_chain)) = context.for_other(child).root_behavior()
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
return Some((child_behavior, next_in_chain));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue