mirror of
https://github.com/danbulant/cushy
synced 2026-06-19 22:41:10 +00:00
parent
1714948174
commit
724f6d7b18
12 changed files with 319 additions and 58 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
|
@ -855,7 +855,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
|||
[[package]]
|
||||
name = "kludgine"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/khonsulabs/kludgine#fd2078efb7212db0f07120b80440699b00ec2a2b"
|
||||
source = "git+https://github.com/khonsulabs/kludgine#5d728e775b9bf64ac30e1e673c9971fc2184cb97"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"alot",
|
||||
|
|
@ -923,9 +923,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.10"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
|
||||
checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
|
|
@ -1572,9 +1572,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.1"
|
||||
version = "1.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
|
||||
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||
|
||||
[[package]]
|
||||
name = "smithay-client-toolkit"
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ tracing-subscriber = { version = "0.3", optional = true }
|
|||
|
||||
|
||||
# [patch."https://github.com/khonsulabs/kludgine"]
|
||||
# kludgine = { path = "../kludgine2" }
|
||||
# kludgine = { path = "../kludgine" }
|
||||
# [patch."https://github.com/khonsulabs/appit"]
|
||||
# appit = { path = "../appit" }
|
||||
# [patch."https://github.com/khonsulabs/figures"]
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@ fn main() -> gooey::Result {
|
|||
);
|
||||
|
||||
Resize::width(
|
||||
// TODO We need a min/max range for the Resize widget
|
||||
Lp::points(400),
|
||||
Lp::points(300)..Lp::points(600),
|
||||
Stack::rows(username_row.and(password_row).and(buttons)),
|
||||
)
|
||||
.centered()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use gooey::styles::components::TextColor;
|
||||
use gooey::widget::{MakeWidget, Widget};
|
||||
use gooey::widget::MakeWidget;
|
||||
use gooey::widgets::stack::Stack;
|
||||
use gooey::widgets::{Button, Style};
|
||||
use gooey::Run;
|
||||
|
|
@ -12,6 +12,6 @@ fn main() -> gooey::Result {
|
|||
}
|
||||
|
||||
/// Creating reusable style helpers that work with any Widget is straightfoward
|
||||
fn red_text(w: impl Widget) -> Style {
|
||||
fn red_text(w: impl MakeWidget) -> Style {
|
||||
w.with(&TextColor, Color::RED)
|
||||
}
|
||||
|
|
|
|||
164
src/styles.rs
164
src/styles.rs
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{hash_map, HashMap};
|
||||
use std::ops::Add;
|
||||
use std::ops::{
|
||||
Add, Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::animation::{EasingFunction, ZeroToOne};
|
||||
|
|
@ -172,8 +174,8 @@ use std::any::Any;
|
|||
use std::fmt::Debug;
|
||||
use std::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use kludgine::figures::units::{Lp, Px};
|
||||
use kludgine::figures::{ScreenScale, Size};
|
||||
use kludgine::figures::units::{Lp, Px, UPx};
|
||||
use kludgine::figures::{Fraction, IntoUnsigned, ScreenScale, Size};
|
||||
use kludgine::Color;
|
||||
|
||||
/// A value of a style component.
|
||||
|
|
@ -183,6 +185,8 @@ pub enum Component {
|
|||
Color(Color),
|
||||
/// A single-dimension measurement.
|
||||
Dimension(Dimension),
|
||||
/// A single-dimension measurement.
|
||||
DimensionRange(DimensionRange),
|
||||
/// A percentage between 0.0 and 1.0.
|
||||
Percent(ZeroToOne),
|
||||
/// A custom component type.
|
||||
|
|
@ -302,7 +306,7 @@ impl From<Lp> for FlexibleDimension {
|
|||
}
|
||||
|
||||
/// A 1-dimensional measurement.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Dimension {
|
||||
/// Physical Pixels
|
||||
Px(Px),
|
||||
|
|
@ -360,6 +364,158 @@ impl ScreenScale for Dimension {
|
|||
}
|
||||
}
|
||||
|
||||
/// A range of [`Dimension`]s.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct DimensionRange {
|
||||
/// The start bound of the range.
|
||||
pub start: Bound<Dimension>,
|
||||
/// The end bound of the range.
|
||||
pub end: Bound<Dimension>,
|
||||
}
|
||||
|
||||
impl DimensionRange {
|
||||
/// Returns this range's dimension if the range represents a single
|
||||
/// dimension.
|
||||
#[must_use]
|
||||
pub fn exact_dimension(&self) -> Option<Dimension> {
|
||||
match (self.start, self.end) {
|
||||
(Bound::Excluded(start), Bound::Included(end)) if start == end => Some(start),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Clamps `size` to the dimensions of this range, converting to unsigned
|
||||
/// pixels in the process.
|
||||
#[must_use]
|
||||
pub fn clamp(&self, mut size: UPx, scale: Fraction) -> UPx {
|
||||
if let Some(min) = self.minimum() {
|
||||
size = size.max(min.into_px(scale).into_unsigned());
|
||||
}
|
||||
if let Some(max) = self.maximum() {
|
||||
size = size.min(max.into_px(scale).into_unsigned());
|
||||
}
|
||||
size
|
||||
}
|
||||
|
||||
/// Returns the minimum measurement, if the start is bounded.
|
||||
#[must_use]
|
||||
pub fn minimum(&self) -> Option<Dimension> {
|
||||
match self.start {
|
||||
Bound::Unbounded => None,
|
||||
Bound::Excluded(Dimension::Lp(lp)) => Some(Dimension::Lp(lp + 1)),
|
||||
Bound::Excluded(Dimension::Px(px)) => Some(Dimension::Px(px + 1)),
|
||||
Bound::Included(value) => Some(value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the maximum measurement, if the end is bounded.
|
||||
#[must_use]
|
||||
pub fn maximum(&self) -> Option<Dimension> {
|
||||
match self.end {
|
||||
Bound::Unbounded => None,
|
||||
Bound::Excluded(Dimension::Lp(lp)) => Some(Dimension::Lp(lp - 1)),
|
||||
Bound::Excluded(Dimension::Px(px)) => Some(Dimension::Px(px - 1)),
|
||||
Bound::Included(value) => Some(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for DimensionRange
|
||||
where
|
||||
T: Into<Dimension>,
|
||||
{
|
||||
fn from(value: T) -> Self {
|
||||
let dimension = value.into();
|
||||
Self::from(dimension..=dimension)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Range<T>> for DimensionRange
|
||||
where
|
||||
T: Into<Dimension>,
|
||||
{
|
||||
fn from(value: Range<T>) -> Self {
|
||||
Self {
|
||||
start: Bound::Included(value.start.into()),
|
||||
end: Bound::Excluded(value.end.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RangeFull> for DimensionRange {
|
||||
fn from(_: RangeFull) -> Self {
|
||||
Self {
|
||||
start: Bound::Unbounded,
|
||||
end: Bound::Unbounded,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<RangeInclusive<T>> for DimensionRange
|
||||
where
|
||||
T: Into<Dimension> + Clone,
|
||||
{
|
||||
fn from(value: RangeInclusive<T>) -> Self {
|
||||
Self {
|
||||
start: Bound::Included(value.start().clone().into()),
|
||||
end: Bound::Excluded(value.end().clone().into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<RangeFrom<T>> for DimensionRange
|
||||
where
|
||||
T: Into<Dimension>,
|
||||
{
|
||||
fn from(value: RangeFrom<T>) -> Self {
|
||||
Self {
|
||||
start: Bound::Included(value.start.into()),
|
||||
end: Bound::Unbounded,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<RangeTo<T>> for DimensionRange
|
||||
where
|
||||
T: Into<Dimension>,
|
||||
{
|
||||
fn from(value: RangeTo<T>) -> Self {
|
||||
Self {
|
||||
start: Bound::Unbounded,
|
||||
end: Bound::Excluded(value.end.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<RangeToInclusive<T>> for DimensionRange
|
||||
where
|
||||
T: Into<Dimension>,
|
||||
{
|
||||
fn from(value: RangeToInclusive<T>) -> Self {
|
||||
Self {
|
||||
start: Bound::Unbounded,
|
||||
end: Bound::Included(value.end.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DimensionRange> for Component {
|
||||
fn from(value: DimensionRange) -> Self {
|
||||
Component::DimensionRange(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Component> for DimensionRange {
|
||||
type Error = Component;
|
||||
|
||||
fn try_from(value: Component) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Component::DimensionRange(value) => Ok(value),
|
||||
other => Err(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A custom component value.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CustomComponent(Arc<dyn AnyComponent>);
|
||||
|
|
|
|||
|
|
@ -156,6 +156,13 @@ pub trait Widget: Send + UnwindSafe + Debug + 'static {
|
|||
) -> EventHandling {
|
||||
IGNORED
|
||||
}
|
||||
|
||||
/// Returns a reference to a single child widget if this widget is a widget
|
||||
/// that primarily wraps a single other widget to customize its behavior.
|
||||
#[must_use]
|
||||
fn wraps(&mut self) -> Option<&WidgetInstance> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Run for T
|
||||
|
|
@ -170,7 +177,7 @@ where
|
|||
/// A [`Widget`] that contains a single child.
|
||||
pub trait WrapperWidget: Debug + Send + UnwindSafe + 'static {
|
||||
/// Returns the child widget.
|
||||
fn child(&mut self) -> &mut WidgetRef;
|
||||
fn child_mut(&mut self) -> &mut WidgetRef;
|
||||
|
||||
/// Returns the rectangle that the child widget should occupy given
|
||||
/// `available_space`.
|
||||
|
|
@ -180,7 +187,7 @@ pub trait WrapperWidget: Debug + Send + UnwindSafe + 'static {
|
|||
available_space: Size<ConstraintLimit>,
|
||||
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
|
||||
) -> Rect<Px> {
|
||||
let child = self.child().mounted(&mut context.as_event_context());
|
||||
let child = self.child_mut().mounted(&mut context.as_event_context());
|
||||
|
||||
context
|
||||
.for_other(&child)
|
||||
|
|
@ -312,8 +319,12 @@ impl<T> Widget for T
|
|||
where
|
||||
T: WrapperWidget,
|
||||
{
|
||||
fn wraps(&mut self) -> Option<&WidgetInstance> {
|
||||
Some(self.child_mut().widget())
|
||||
}
|
||||
|
||||
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
|
||||
let child = self.child().mounted(&mut context.as_event_context());
|
||||
let child = self.child_mut().mounted(&mut context.as_event_context());
|
||||
context.for_other(&child).redraw();
|
||||
}
|
||||
|
||||
|
|
@ -322,7 +333,7 @@ where
|
|||
available_space: Size<ConstraintLimit>,
|
||||
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
|
||||
) -> Size<UPx> {
|
||||
let child = self.child().mounted(&mut context.as_event_context());
|
||||
let child = self.child_mut().mounted(&mut context.as_event_context());
|
||||
|
||||
let layout = self.layout_child(available_space, context);
|
||||
context.set_child_layout(&child, layout);
|
||||
|
|
@ -1121,6 +1132,15 @@ impl WidgetRef {
|
|||
};
|
||||
widget.clone()
|
||||
}
|
||||
|
||||
/// Returns the a reference to the underlying widget instance.
|
||||
#[must_use]
|
||||
pub fn widget(&self) -> &WidgetInstance {
|
||||
match self {
|
||||
WidgetRef::Unmounted(widget) => widget,
|
||||
WidgetRef::Mounted(managed) => &managed.widget,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<WidgetId> for WidgetRef {
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ impl FrameInfo {
|
|||
}
|
||||
|
||||
impl WrapperWidget for Align {
|
||||
fn child(&mut self) -> &mut WidgetRef {
|
||||
fn child_mut(&mut self) -> &mut WidgetRef {
|
||||
&mut self.child
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ impl Expand {
|
|||
}
|
||||
|
||||
impl WrapperWidget for Expand {
|
||||
fn child(&mut self) -> &mut WidgetRef {
|
||||
fn child_mut(&mut self) -> &mut WidgetRef {
|
||||
&mut self.child
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
use kludgine::figures::{Fraction, IntoSigned, IntoUnsigned, Rect, ScreenScale, Size};
|
||||
|
||||
use crate::context::{AsEventContext, LayoutContext};
|
||||
use crate::styles::Dimension;
|
||||
use crate::styles::DimensionRange;
|
||||
use crate::widget::{MakeWidget, WidgetRef, WrapperWidget};
|
||||
use crate::ConstraintLimit;
|
||||
|
||||
/// A widget that resizes its contained widget to an explicit size.
|
||||
#[derive(Debug)]
|
||||
pub struct Resize {
|
||||
/// If present, the width to apply to the child widget.
|
||||
pub width: Option<Dimension>,
|
||||
/// If present, the height to apply to the child widget.
|
||||
pub height: Option<Dimension>,
|
||||
/// The range of allowed width for the child widget.
|
||||
pub width: DimensionRange,
|
||||
/// The range of allowed height for the child widget.
|
||||
pub height: DimensionRange,
|
||||
child: WidgetRef,
|
||||
}
|
||||
|
||||
|
|
@ -26,38 +26,38 @@ impl Resize {
|
|||
#[must_use]
|
||||
pub fn to<T>(size: Size<T>, child: impl MakeWidget) -> Self
|
||||
where
|
||||
T: Into<Dimension>,
|
||||
T: Into<DimensionRange>,
|
||||
{
|
||||
Self {
|
||||
child: WidgetRef::new(child),
|
||||
width: Some(size.width.into()),
|
||||
height: Some(size.height.into()),
|
||||
width: size.width.into(),
|
||||
height: size.height.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Resizes `child`'s width to `width`.
|
||||
#[must_use]
|
||||
pub fn width(width: impl Into<Dimension>, child: impl MakeWidget) -> Self {
|
||||
pub fn width(width: impl Into<DimensionRange>, child: impl MakeWidget) -> Self {
|
||||
Self {
|
||||
child: WidgetRef::new(child),
|
||||
width: Some(width.into()),
|
||||
height: None,
|
||||
width: width.into(),
|
||||
height: DimensionRange::from(..),
|
||||
}
|
||||
}
|
||||
|
||||
/// Resizes `child`'s height to `height`.
|
||||
#[must_use]
|
||||
pub fn height(height: impl Into<Dimension>, child: impl MakeWidget) -> Self {
|
||||
pub fn height(height: impl Into<DimensionRange>, child: impl MakeWidget) -> Self {
|
||||
Self {
|
||||
child: WidgetRef::new(child),
|
||||
width: None,
|
||||
height: Some(height.into()),
|
||||
width: DimensionRange::from(..),
|
||||
height: height.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WrapperWidget for Resize {
|
||||
fn child(&mut self) -> &mut WidgetRef {
|
||||
fn child_mut(&mut self) -> &mut WidgetRef {
|
||||
&mut self.child
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +67,9 @@ impl WrapperWidget for Resize {
|
|||
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
|
||||
) -> Rect<kludgine::figures::units::Px> {
|
||||
let child = self.child.mounted(&mut context.as_event_context());
|
||||
let size = if let (Some(width), Some(height)) = (self.width, self.height) {
|
||||
let size = if let (Some(width), Some(height)) =
|
||||
(self.width.exact_dimension(), self.height.exact_dimension())
|
||||
{
|
||||
Size::new(
|
||||
width.into_px(context.gfx.scale()).into_unsigned(),
|
||||
height.into_px(context.gfx.scale()).into_unsigned(),
|
||||
|
|
@ -85,12 +87,13 @@ impl WrapperWidget for Resize {
|
|||
|
||||
fn override_constraint(
|
||||
constraint: ConstraintLimit,
|
||||
explicit: Option<Dimension>,
|
||||
range: DimensionRange,
|
||||
scale: Fraction,
|
||||
) -> ConstraintLimit {
|
||||
if let Some(size) = explicit {
|
||||
ConstraintLimit::Known(size.into_px(scale).into_unsigned())
|
||||
} else {
|
||||
constraint
|
||||
match constraint {
|
||||
ConstraintLimit::Known(size) => ConstraintLimit::Known(range.clamp(size, scale)),
|
||||
ConstraintLimit::ClippedAfter(clipped_after) => {
|
||||
ConstraintLimit::ClippedAfter(range.clamp(clipped_after, scale))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! A widget that combines a collection of [`Children`] widgets into one.
|
||||
// TODO on scale change, all `Lp` children need to resize
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::ops::{Bound, Deref};
|
||||
|
||||
use alot::{LotId, OrderedLots};
|
||||
use kludgine::figures::units::{Lp, UPx};
|
||||
|
|
@ -95,14 +95,22 @@ impl Stack {
|
|||
)
|
||||
} else if let Some((child, size)) =
|
||||
guard.downcast_ref::<Resize>().and_then(|r| {
|
||||
match self.layout.orientation.orientation {
|
||||
let range = match self.layout.orientation.orientation {
|
||||
StackOrientation::Row => r.height,
|
||||
StackOrientation::Column => r.width,
|
||||
}
|
||||
.map(|size| (r.child().clone(), size))
|
||||
};
|
||||
range.minimum().map(|min| {
|
||||
(
|
||||
r.child().clone(),
|
||||
StackDimension::Measured {
|
||||
min,
|
||||
_max: range.end,
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
{
|
||||
(child, StackDimension::Exact(size))
|
||||
(child, size)
|
||||
} else {
|
||||
(
|
||||
WidgetRef::Unmounted(widget.clone()),
|
||||
|
|
@ -260,7 +268,7 @@ pub enum StackOrientation {
|
|||
|
||||
/// The strategy to use when laying a widget out inside of an [`Stack`].
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum StackDimension {
|
||||
enum StackDimension {
|
||||
/// Attempt to lay out the widget based on its contents.
|
||||
FitContent,
|
||||
/// Use a fractional amount of the available space.
|
||||
|
|
@ -269,8 +277,13 @@ pub enum StackDimension {
|
|||
/// fractionally.
|
||||
weight: u8,
|
||||
},
|
||||
/// Use an exact measurement for this widget's size.
|
||||
Exact(Dimension),
|
||||
/// Use a range for this widget's size.
|
||||
Measured {
|
||||
/// The minimum size for the widget.
|
||||
min: Dimension,
|
||||
/// The optional maximum size for the widget.
|
||||
_max: Bound<Dimension>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -322,7 +335,7 @@ impl Layout {
|
|||
self.fractional.retain(|(measured, _)| *measured != id);
|
||||
self.total_weights -= u32::from(weight);
|
||||
}
|
||||
StackDimension::Exact(size) => match size {
|
||||
StackDimension::Measured { min, .. } => match min {
|
||||
Dimension::Px(pixels) => {
|
||||
self.allocated_space.0 -= pixels.into_unsigned();
|
||||
}
|
||||
|
|
@ -357,12 +370,12 @@ impl Layout {
|
|||
self.fractional.push((id, weight));
|
||||
UPx(0)
|
||||
}
|
||||
StackDimension::Exact(size) => {
|
||||
match size {
|
||||
StackDimension::Measured { min, .. } => {
|
||||
match min {
|
||||
Dimension::Px(size) => self.allocated_space.0 += size.into_unsigned(),
|
||||
Dimension::Lp(size) => self.allocated_space.1 += size,
|
||||
}
|
||||
size.into_px(scale).into_unsigned()
|
||||
min.into_px(scale).into_unsigned()
|
||||
}
|
||||
};
|
||||
self.layouts.insert(
|
||||
|
|
@ -464,6 +477,7 @@ impl Deref for Layout {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::Bound;
|
||||
|
||||
use kludgine::figures::units::UPx;
|
||||
use kludgine::figures::{Fraction, IntoSigned, Size};
|
||||
|
|
@ -490,7 +504,10 @@ mod tests {
|
|||
}
|
||||
|
||||
pub fn fixed_size(mut self, size: UPx) -> Self {
|
||||
self.dimension = StackDimension::Exact(Dimension::Px(size.into_signed()));
|
||||
self.dimension = StackDimension::Measured {
|
||||
min: Dimension::Px(size.into_signed()),
|
||||
_max: Bound::Unbounded,
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ impl Style {
|
|||
}
|
||||
|
||||
impl WrapperWidget for Style {
|
||||
fn child(&mut self) -> &mut WidgetRef {
|
||||
fn child_mut(&mut self) -> &mut WidgetRef {
|
||||
&mut self.child
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ use std::path::Path;
|
|||
use std::string::ToString;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use kludgine::app::winit::dpi::PhysicalPosition;
|
||||
use kludgine::app::winit::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use kludgine::app::winit::event::{
|
||||
DeviceId, ElementState, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase,
|
||||
};
|
||||
use kludgine::app::winit::keyboard::Key;
|
||||
use kludgine::app::WindowBehavior as _;
|
||||
use kludgine::figures::units::Px;
|
||||
use kludgine::figures::{IntoSigned, Point, Rect, Size};
|
||||
use kludgine::figures::units::{Px, UPx};
|
||||
use kludgine::figures::{IntoSigned, IntoUnsigned, Point, Rect, ScreenScale, Size};
|
||||
use kludgine::render::Drawing;
|
||||
use kludgine::Kludgine;
|
||||
use tracing::Level;
|
||||
|
|
@ -34,6 +34,7 @@ use crate::value::{Dynamic, IntoDynamic};
|
|||
use crate::widget::{
|
||||
EventHandling, ManagedWidget, Widget, WidgetId, WidgetInstance, HANDLED, IGNORED,
|
||||
};
|
||||
use crate::widgets::Resize;
|
||||
use crate::window::sealed::WindowCommand;
|
||||
use crate::{initialize_tracing, ConstraintLimit, Run};
|
||||
|
||||
|
|
@ -248,6 +249,8 @@ struct GooeyWindow<T> {
|
|||
occluded: Dynamic<bool>,
|
||||
focused: Dynamic<bool>,
|
||||
keyboard_activated: Option<ManagedWidget>,
|
||||
min_inner_size: Option<Size<UPx>>,
|
||||
max_inner_size: Option<Size<UPx>>,
|
||||
}
|
||||
|
||||
impl<T> GooeyWindow<T>
|
||||
|
|
@ -337,6 +340,8 @@ where
|
|||
occluded,
|
||||
focused,
|
||||
keyboard_activated: None,
|
||||
min_inner_size: None,
|
||||
max_inner_size: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -348,6 +353,55 @@ where
|
|||
self.redraw_status.refresh_received();
|
||||
graphics.reset_text_attributes();
|
||||
self.root.tree.reset_render_order();
|
||||
|
||||
let resizable = window.winit().is_resizable();
|
||||
{
|
||||
let mut root_or_child = self.root.widget.clone();
|
||||
loop {
|
||||
let mut widget = root_or_child.lock();
|
||||
if let Some(resize) = widget.downcast_ref::<Resize>() {
|
||||
let min_width = resize
|
||||
.width
|
||||
.minimum()
|
||||
.map_or(Px(0), |width| width.into_px(graphics.scale()));
|
||||
let max_width = resize
|
||||
.width
|
||||
.maximum()
|
||||
.map_or(Px::MAX, |width| width.into_px(graphics.scale()));
|
||||
let min_height = resize
|
||||
.height
|
||||
.minimum()
|
||||
.map_or(Px(0), |height| height.into_px(graphics.scale()));
|
||||
let max_height = resize
|
||||
.height
|
||||
.maximum()
|
||||
.map_or(Px::MAX, |height| height.into_px(graphics.scale()));
|
||||
|
||||
let new_min_size = (min_width > 0 || min_height > 0)
|
||||
.then_some(Size::<Px>::new(min_width, min_height).into_unsigned());
|
||||
|
||||
if new_min_size != self.min_inner_size {
|
||||
window.set_min_inner_size(new_min_size);
|
||||
self.min_inner_size = new_min_size;
|
||||
}
|
||||
let new_max_size = (max_width > 0 || max_height > 0)
|
||||
.then_some(Size::<Px>::new(max_width, max_height).into_unsigned());
|
||||
|
||||
if new_max_size != self.max_inner_size && resizable {
|
||||
window.set_max_inner_size(new_max_size);
|
||||
}
|
||||
self.max_inner_size = new_max_size;
|
||||
break;
|
||||
} else if let Some(wraps) = widget.as_widget().wraps().cloned() {
|
||||
drop(widget);
|
||||
|
||||
root_or_child = wraps;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let graphics = self.contents.new_frame(graphics);
|
||||
let mut window = RunningWindow::new(window, &self.focused, &self.occluded);
|
||||
let mut context = GraphicsContext {
|
||||
|
|
@ -361,6 +415,18 @@ where
|
|||
ConstraintLimit::ClippedAfter(window_size.height),
|
||||
));
|
||||
let render_size = actual_size.min(window_size);
|
||||
if render_size != window_size && !resizable {
|
||||
let mut new_size = actual_size;
|
||||
if let Some(min_size) = self.min_inner_size {
|
||||
new_size = new_size.max(min_size);
|
||||
}
|
||||
if let Some(max_size) = self.max_inner_size {
|
||||
new_size = new_size.min(max_size);
|
||||
}
|
||||
let _ = layout_context
|
||||
.winit()
|
||||
.request_inner_size(PhysicalSize::from(new_size));
|
||||
}
|
||||
self.root.set_layout(Rect::from(render_size.into_signed()));
|
||||
|
||||
if self.initial_frame {
|
||||
|
|
|
|||
Loading…
Reference in a new issue