Refactor ScrollBars to their own widget

Closes #181
This commit is contained in:
Jonathan Johnson 2024-10-19 08:16:26 -07:00
parent 5adb37d2f1
commit 171cf3f733
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
14 changed files with 693 additions and 438 deletions

View file

@ -57,6 +57,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `MakeWidgetList::into_layers` -> `IntoWidgetList::into_layers`
- `MakeWidgetList::into_wrap` -> `IntoWidgetList::into_wrap`
- `MakeWidgetList::into_list` -> `IntoWidgetList::into_list`
- `ConstraintLimit::fit_measured` and `FitMeasuredSize::fit_measured` now accept
either a `Px` or `UPx` measurement, and does not perform scaling adjustments.
To convert `Lp` use `into_upx()` first.
### Changed
@ -115,6 +118,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
allow overdrawing the widget's bounds. This was noticable in the
nested-scroll.rs example when reducing the height of the window below 6
inches.
- `Scroll` now uses the new `ScrollBar` widget for its bars rather than manually
drawing them. By making this change, the bars now have input priority over the
contents. This means that the scroll bars are now clickable even in areas
where interactive widgets are beneath them.
### Added
@ -252,6 +259,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `Scroll` now exposes its scroll amount, maximum scroll, and more information
that allows completely customizing a scroll view's behavior. Thanks to
@danbulant for helping with this change!
- `ScrollBar` is a new widget that renders a scroll bar meant to scroll through
a large container.
[139]: https://github.com/khonsulabs/cushy/issues/139

107
Cargo.lock generated
View file

@ -117,9 +117,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.89"
version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95"
[[package]]
name = "appit"
@ -203,9 +203,9 @@ dependencies = [
[[package]]
name = "ashpd"
version = "0.9.1"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfe7e0dd0ac5a401dc116ed9f9119cf9decc625600474cb41f0fc0a0050abc9a"
checksum = "4d43c03d9e36dd40cab48435be0b09646da362c278223ca535493877b2c1dee9"
dependencies = [
"async-fs",
"async-net",
@ -435,9 +435,9 @@ dependencies = [
[[package]]
name = "avif-serialize"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2"
checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62"
dependencies = [
"arrayvec",
]
@ -535,9 +535,9 @@ dependencies = [
[[package]]
name = "built"
version = "0.7.4"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "236e6289eda5a812bc6b53c3b024039382a2895fbbeef2d748b2931546d392c4"
checksum = "c360505aed52b7ec96a3636c3f039d99103c37d1d9b4f7a8c743d3ea9ffcd03b"
[[package]]
name = "bumpalo"
@ -553,9 +553,9 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06"
[[package]]
name = "bytemuck"
version = "1.18.0"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae"
checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
dependencies = [
"bytemuck_derive",
]
@ -617,9 +617,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.1.28"
version = "1.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1"
checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f"
dependencies = [
"jobserver",
"libc",
@ -1190,9 +1190,9 @@ checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8"
[[package]]
name = "flume"
version = "0.11.0"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
dependencies = [
"spin",
]
@ -1554,9 +1554,9 @@ dependencies = [
[[package]]
name = "image"
version = "0.25.2"
version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10"
checksum = "bc144d44a31d753b02ce64093d532f55ff8dc4ebf2ffb8a63c0dda691385acae"
dependencies = [
"bytemuck",
"byteorder-lite",
@ -1577,9 +1577,9 @@ dependencies = [
[[package]]
name = "image-webp"
version = "0.1.3"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904"
checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f"
dependencies = [
"byteorder-lite",
"quick-error",
@ -1587,9 +1587,9 @@ dependencies = [
[[package]]
name = "imgref"
version = "1.10.1"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126"
checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
[[package]]
name = "indexmap"
@ -1690,9 +1690,9 @@ checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
[[package]]
name = "js-sys"
version = "0.3.70"
version = "0.3.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
dependencies = [
"wasm-bindgen",
]
@ -1729,7 +1729,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "kludgine"
version = "0.11.0"
source = "git+https://github.com/khonsulabs/kludgine#0df62c716c13a93ee249b79d9c831933d0d49d99"
source = "git+https://github.com/khonsulabs/kludgine#2f7755a1a9b7cae67711f7c41ee2cd2c6b12fd64"
dependencies = [
"ahash",
"alot",
@ -1766,9 +1766,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]]
name = "libc"
version = "0.2.159"
version = "0.2.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
[[package]]
name = "libdbus-sys"
@ -1808,13 +1808,13 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "libredox"
version = "0.0.2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.6.0",
"libc",
"redox_syscall 0.4.1",
"redox_syscall 0.5.7",
]
[[package]]
@ -2418,9 +2418,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "orbclient"
version = "0.3.47"
version = "0.3.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166"
checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43"
dependencies = [
"libredox",
]
@ -2705,27 +2705,27 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.87"
version = "1.0.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "profiling"
version = "1.0.15"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58"
checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d"
dependencies = [
"profiling-procmacros",
]
[[package]]
name = "profiling-procmacros"
version = "1.0.15"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd"
checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30"
dependencies = [
"quote",
"syn 2.0.79",
@ -2865,9 +2865,9 @@ dependencies = [
[[package]]
name = "ravif"
version = "0.11.10"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f0bfd976333248de2078d350bfdf182ff96e168a24d23d2436cef320dd4bdd"
checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6"
dependencies = [
"avif-serialize",
"imgref",
@ -3008,9 +3008,6 @@ name = "rgb"
version = "0.8.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
dependencies = [
"bytemuck",
]
[[package]]
name = "roxmltree"
@ -3744,9 +3741,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
dependencies = [
"cfg-if",
"once_cell",
@ -3755,9 +3752,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
dependencies = [
"bumpalo",
"log",
@ -3770,9 +3767,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.43"
version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed"
checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b"
dependencies = [
"cfg-if",
"js-sys",
@ -3782,9 +3779,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -3792,9 +3789,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
dependencies = [
"proc-macro2",
"quote",
@ -3805,9 +3802,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
[[package]]
name = "wayland-backend"
@ -3920,9 +3917,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.70"
version = "0.3.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0"
checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112"
dependencies = [
"js-sys",
"wasm-bindgen",

View file

@ -1,7 +1,7 @@
use cushy::figures::units::Lp;
use cushy::kludgine::cosmic_text::FamilyOwned;
use cushy::styles::components::FontFamily;
use cushy::styles::FontFamilyList;
use cushy::styles::{Edges, FontFamilyList};
use cushy::widget::MakeWidget;
use cushy::Run;
@ -17,6 +17,7 @@ fn main() -> cushy::Result {
.height(Lp::inches(3)),
)
.into_rows()
.pad_by(Edges::default().with_right(Lp::points(7)))
.vertical_scroll()
.expand()
.run()

View file

@ -3,18 +3,15 @@ use std::cmp::Ordering;
use std::time::Duration;
use cushy::figures::units::Px;
use cushy::figures::FloatConversion;
use cushy::figures::{Point, Rect, Size};
use cushy::kludgine::app::winit::keyboard::Key;
use cushy::kludgine::app::winit::keyboard::NamedKey;
use cushy::figures::{FloatConversion, Point, Rect, Size};
use cushy::kludgine::app::winit::keyboard::{Key, NamedKey};
use cushy::kludgine::drawing::Renderer;
use cushy::kludgine::shapes::Shape;
use cushy::kludgine::sprite::{Sprite, SpriteSource};
use cushy::kludgine::tilemap::{
DebugGrid, Object, ObjectLayer, TileArray, TileKind, TileMapFocus, TILE_SIZE,
};
use cushy::kludgine::Color;
use cushy::kludgine::{include_aseprite_sprite, DrawableExt};
use cushy::kludgine::{include_aseprite_sprite, Color, DrawableExt};
use cushy::value::{Destination, Dynamic};
use cushy::widgets::TileMap;
use cushy::{Run, Tick};

View file

@ -1,15 +1,13 @@
use cushy::{
value::{Dynamic, Source},
widget::MakeWidget,
Run,
};
fn main() -> cushy::Result {
let has_unsaved_changes = Dynamic::new(true);
"Prevent Closing"
.into_checkbox(has_unsaved_changes.clone())
.into_window()
.on_close_requested(move |()| !has_unsaved_changes.get())
.run()
}
use cushy::value::{Dynamic, Source};
use cushy::widget::MakeWidget;
use cushy::Run;
fn main() -> cushy::Result {
let has_unsaved_changes = Dynamic::new(true);
"Prevent Closing"
.into_checkbox(has_unsaved_changes.clone())
.into_window()
.on_close_requested(move |()| !has_unsaved_changes.get())
.run()
}

View file

@ -801,10 +801,10 @@ impl MakeWidget for FilePickerWidget {
.with_explanation(
format!("A file named \"{name}\" already exists. Do you want to overwrite the existing file?")
)
.with_yes({
let callback = callback.clone();
.with_yes({
let callback = callback.clone();
move || {
let Some(ModeCallback::Single(cb)) = callback.lock().take() else {
let Some(ModeCallback::Single(cb)) = callback.lock().take() else {
unreachable!("re-set above");
};
cb.invoke(chosen_path.clone());

View file

@ -131,7 +131,7 @@ pub use app::{
/// ```
pub use cushy_macros::main;
use figures::units::UPx;
use figures::{Fraction, ScreenUnit, Size, Zero};
use figures::{IntoUnsigned, Size, Zero};
use kludgine::app::winit::error::EventLoopError;
pub use names::Name;
pub use utils::{Lazy, ModifiersExt, ModifiersStateExt, WithClone};
@ -186,14 +186,13 @@ impl ConstraintLimit {
/// If this constraint is of a known size, it will return the maximum of the
/// measured size and the constraint. If it is of an unknown size, it will
/// return the measured size.
pub fn fit_measured<Unit>(self, measured: Unit, scale: Fraction) -> UPx
pub fn fit_measured<Unit>(self, measured: Unit) -> UPx
where
Unit: ScreenUnit,
Unit: IntoUnsigned<Unsigned = UPx>,
{
let measured = measured.into_upx(scale);
match self {
ConstraintLimit::Fill(size) => size.max(measured),
ConstraintLimit::SizeToFit(_) => measured,
ConstraintLimit::Fill(size) => size.max(measured.into_unsigned()),
ConstraintLimit::SizeToFit(_) => measured.into_unsigned(),
}
}
}
@ -202,19 +201,19 @@ impl ConstraintLimit {
pub trait FitMeasuredSize {
/// Returns the result of calling [`ConstraintLimit::fit_measured`] for each
/// matching component in `self` and `measured`.
fn fit_measured<Unit>(self, measured: Size<Unit>, scale: Fraction) -> Size<UPx>
fn fit_measured<Unit>(self, measured: Size<Unit>) -> Size<UPx>
where
Unit: ScreenUnit;
Unit: IntoUnsigned<Unsigned = UPx>;
}
impl FitMeasuredSize for Size<ConstraintLimit> {
fn fit_measured<Unit>(self, measured: Size<Unit>, scale: Fraction) -> Size<UPx>
fn fit_measured<Unit>(self, measured: Size<Unit>) -> Size<UPx>
where
Unit: ScreenUnit,
Unit: IntoUnsigned<Unsigned = UPx>,
{
Size::new(
self.width.fit_measured(measured.width, scale),
self.height.fit_measured(measured.height, scale),
self.width.fit_measured(measured.width),
self.height.fit_measured(measured.height),
)
}
}

View file

@ -592,12 +592,8 @@ pub trait WrapperWidget: Debug + Send + 'static {
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout {
Size::new(
available_space
.width
.fit_measured(size.width, context.gfx.scale()),
available_space
.height
.fit_measured(size.height, context.gfx.scale()),
available_space.width.fit_measured(size.width),
available_space.height.fit_measured(size.height),
)
.into()
}

View file

@ -497,7 +497,7 @@ impl Widget for Button {
let mounted = self.content.mounted(context);
let available_space = available_space.map(|space| space - double_padding);
let size = context.for_other(&mounted).layout(available_space);
let size = available_space.fit_measured(size, context.gfx.scale());
let size = available_space.fit_measured(size);
context.set_child_layout(
&mounted,
Rect::new(Point::squared(padding), size).into_signed(),

View file

@ -477,12 +477,8 @@ impl WrapperWidget for Custom {
position_child.invoke(size, available_space, context)
} else {
Size::new(
available_space
.width
.fit_measured(size.width, context.gfx.scale()),
available_space
.height
.fit_measured(size.height, context.gfx.scale()),
available_space.width.fit_measured(size.width),
available_space.height.fit_measured(size.height),
)
.into()
}

View file

@ -113,24 +113,16 @@ impl WrapperWidget for Expand {
let (width, height) = match &self.kind {
ExpandKind::Weighted(_) => (
available_space
.width
.fit_measured(size.width, context.gfx.scale()),
available_space
.height
.fit_measured(size.height, context.gfx.scale()),
available_space.width.fit_measured(size.width),
available_space.height.fit_measured(size.height),
),
ExpandKind::Horizontal => (
available_space
.width
.fit_measured(size.width, context.gfx.scale()),
available_space.width.fit_measured(size.width),
size.height.min(available_space.height.max()),
),
ExpandKind::Vertical => (
size.width.min(available_space.width.max()),
available_space
.height
.fit_measured(size.height, context.gfx.scale()),
available_space.height.fit_measured(size.height),
),
};

View file

@ -88,12 +88,8 @@ impl Widget for Layers {
// Now we know the size of the widget, we can request the widgets fill
// the allocated space.
let size = Size::new(
available_space
.width
.fit_measured(size.width, context.gfx.scale()),
available_space
.height
.fit_measured(size.height, context.gfx.scale()),
available_space.width.fit_measured(size.width),
available_space.height.fit_measured(size.height),
);
let layout = Rect::from(size.into_signed());
for child in self.mounted.children() {
@ -1046,12 +1042,8 @@ impl WrapperWidget for ModalLayer {
}
}
Size::new(
available_space
.width
.fit_measured(size.width, context.gfx.scale()),
available_space
.height
.fit_measured(size.height, context.gfx.scale()),
available_space.width.fit_measured(size.width),
available_space.height.fit_measured(size.height),
)
.into()
}

View file

@ -407,6 +407,6 @@ impl Widget for Spinner {
let track_size = context.get(&TrackSize).into_px(context.gfx.scale());
let minimum_size = track_size * 4;
available_space.map(|constraint| constraint.fit_measured(minimum_size, context.gfx.scale()))
available_space.map(|constraint| constraint.fit_measured(minimum_size))
}
}

File diff suppressed because it is too large Load diff