Switcher unmounts in all windows

Fixes #139
This commit is contained in:
Jonathan Johnson 2024-08-28 08:49:26 -07:00
parent 53d3c58cbb
commit 0dd18826c9
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
6 changed files with 197 additions and 103 deletions

View file

@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
animating the transition. The previous behavior caused nested collapsed
widgets to grow and shrink in an accordian-like fashion rather than animating
together.
- `Switcher` now unmounts child widgets in all windows it is mounted in. Fixes
[#139][139].
### Added
@ -35,6 +37,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `MakeWidget::to_checkbox()`
- `WidgetInstance::to_window()`
[139]: https://github.com/khonsulabs/cushy/issues/139
## v0.4.0 (2024-08-20)
### Breaking Changes

166
Cargo.lock generated
View file

@ -123,9 +123,9 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "appit"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6869c02a8fdc42d78e60388824dbd5e331f763a7e1b8530eff4ea5c56df3438"
checksum = "7a158c9d2660ce603c741d513b44cdd97a4553e0749d4e49e45b9480fc72c162"
dependencies = [
"winit",
]
@ -171,7 +171,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -209,32 +209,32 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "attribute-derive"
version = "0.10.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f6763469f78790650fe2b25ea8c2947b1bf5889b7be7833e23f383437fa8fc5"
checksum = "36f18fc482cf559bca9efe778ba2fd0d1c16a31d5d24a2c886ed16b2d217e454"
dependencies = [
"attribute-derive-macro",
"derive-where",
"manyhow",
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
name = "attribute-derive-macro"
version = "0.10.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1318b422e4ad618775982a521842870d74ab4e07cf7588a968bc6d68c62a4ff"
checksum = "a85958950e587256a16c72325ff3c4f3e4db25999173e9ca2864665be84ff63b"
dependencies = [
"collection_literals",
"interpolator",
"manyhow",
"proc-macro-utils 0.10.0",
"proc-macro-utils",
"proc-macro2",
"quote",
"quote-use",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -316,9 +316,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bitstream-io"
version = "2.5.1"
version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "586ebf43072a4f103bc979b12dc36bc82bed5c2a340e16344e1253d53263c3fa"
checksum = "b81e1519b0d82120d2fd469d5bfb2919a9361c48b02d82d04befc1cdd2002452"
[[package]]
name = "block"
@ -355,9 +355,9 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06"
[[package]]
name = "bytemuck"
version = "1.17.0"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31"
checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2"
dependencies = [
"bytemuck_derive",
]
@ -370,7 +370,7 @@ checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -419,9 +419,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.1.13"
version = "1.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48"
checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6"
dependencies = [
"jobserver",
"libc",
@ -704,7 +704,7 @@ dependencies = [
"proc-macro2",
"quote",
"quote-use",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -726,7 +726,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -817,9 +817,9 @@ dependencies = [
[[package]]
name = "euclid"
version = "0.22.10"
version = "0.22.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0f0eb73b934648cd7a4a61f1b15391cd95dab0b4da6e2e66c2a072c144b4a20"
checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48"
dependencies = [
"num-traits",
]
@ -870,9 +870,9 @@ dependencies = [
[[package]]
name = "flate2"
version = "1.0.32"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c0596c1eac1f9e04ed902702e9878208b336edc9d6fddc8a48387349bab3666"
checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253"
dependencies = [
"crc32fast",
"miniz_oxide 0.8.0",
@ -943,7 +943,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -1214,7 +1214,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -1476,23 +1476,23 @@ dependencies = [
[[package]]
name = "manyhow"
version = "0.11.3"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5b8ea82a2287fe7b26aea89c0c02957886d7e97eabffc1f9d2031feaa6f82e6"
checksum = "b33efb3ca6d3b07393750d4030418d594ab1139cee518f0dc88db70fec873587"
dependencies = [
"manyhow-macros",
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
name = "manyhow-macros"
version = "0.11.3"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae36e8d9b4095531e43de72ed424f0f4a98cba40f7e5a99366f9818769489272"
checksum = "46fce34d199b78b6e6073abf984c9cf5fd3e9330145a93ee0738a7443e371495"
dependencies = [
"proc-macro-utils 0.8.0",
"proc-macro-utils",
"proc-macro2",
"quote",
]
@ -1686,7 +1686,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -1737,7 +1737,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -2012,7 +2012,7 @@ dependencies = [
"by_address",
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -2080,7 +2080,7 @@ dependencies = [
"phf_shared",
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -2109,7 +2109,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -2193,32 +2193,21 @@ checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
[[package]]
name = "prettyplease"
version = "0.2.20"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba"
dependencies = [
"proc-macro2",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
name = "proc-macro-crate"
version = "3.1.0"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
dependencies = [
"toml_edit 0.21.1",
]
[[package]]
name = "proc-macro-utils"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f59e109e2f795a5070e69578c4dc101068139f74616778025ae1011d4cd41a8"
dependencies = [
"proc-macro2",
"quote",
"smallvec",
"toml_edit",
]
[[package]]
@ -2257,7 +2246,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd"
dependencies = [
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -2286,18 +2275,18 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.36"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "quote-use"
version = "0.8.3"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e96ac59974192a2fa6ee55a41211cf1385c5b2a8636a4c3068b3b3dd599ece"
checksum = "9619db1197b497a36178cfc736dc96b271fe918875fbf1344c436a7e93d0321e"
dependencies = [
"quote",
"quote-use-macros",
@ -2305,15 +2294,14 @@ dependencies = [
[[package]]
name = "quote-use-macros"
version = "0.8.3"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c57308e9dde4d7be9af804f6deeaa9951e1de1d5ffce6142eb964750109f7e"
checksum = "82ebfb7faafadc06a7ab141a6f67bcfb24cb8beb158c6fe933f2f035afa99f35"
dependencies = [
"derive-where",
"proc-macro-utils 0.8.0",
"proc-macro-utils",
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -2540,9 +2528,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
version = "0.38.34"
version = "0.38.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f"
dependencies = [
"bitflags 2.6.0",
"errno",
@ -2610,22 +2598,22 @@ checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a"
[[package]]
name = "serde"
version = "1.0.208"
version = "1.0.209"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2"
checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.208"
version = "1.0.209"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -2807,9 +2795,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.75"
version = "2.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9"
checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525"
dependencies = [
"proc-macro2",
"quote",
@ -2870,7 +2858,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -2953,7 +2941,7 @@ dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.22.20",
"toml_edit",
]
[[package]]
@ -2965,17 +2953,6 @@ dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
dependencies = [
"indexmap",
"toml_datetime",
"winnow 0.5.40",
]
[[package]]
name = "toml_edit"
version = "0.22.20"
@ -2986,7 +2963,7 @@ dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"winnow 0.6.18",
"winnow",
]
[[package]]
@ -3008,7 +2985,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]
@ -3195,7 +3172,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
"wasm-bindgen-shared",
]
@ -3229,7 +3206,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -3803,15 +3780,6 @@ dependencies = [
"xkbcommon-dl",
]
[[package]]
name = "winnow"
version = "0.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
dependencies = [
"memchr",
]
[[package]]
name = "winnow"
version = "0.6.18"
@ -3914,7 +3882,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.75",
"syn 2.0.76",
]
[[package]]

View file

@ -0,0 +1,53 @@
//! Shows the ability to share widgets between multiple windows.
//!
//! This example was created to test a fix for
//! <https://github.com/khonsulabs/cushy/issues/139>. The issue was that if the
//! same Switcher widget was shown on two separate windows, only one window
//! would unmount the existing widget.
//!
//! When running this example after the bug has been fixed, unmounted messages
//! should be printed twice: once per each window.
use cushy::value::{Dynamic, Switchable};
use cushy::widget::MakeWidget;
use cushy::widgets::Custom;
use cushy::{Open, PendingApp, Run};
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
enum Contents {
A,
B,
}
fn main() -> cushy::Result {
let mut app = PendingApp::default();
let selected = Dynamic::new(Contents::A);
// Open up another window containing our controls
selected
.new_radio(Contents::A, "A")
.and(selected.new_radio(Contents::B, "B"))
.into_rows()
.open(&mut app)?;
let display = selected
.switcher(|contents, _| match contents {
Contents::A => Custom::new("A")
.on_unmounted(|_| {
println!("A unmounted");
})
.make_widget(),
Contents::B => Custom::new("B")
.on_unmounted(|_| {
println!("B unmounted");
})
.make_widget(),
})
.make_widget();
// Open two windows with the same switcher instance
display.to_window().open(&mut app)?;
display.to_window().open(&mut app)?;
app.run()
}

View file

@ -2499,6 +2499,12 @@ impl WidgetRef {
}
}
impl From<WidgetRef> for WindowLocal<MountedWidget> {
fn from(value: WidgetRef) -> Self {
value.mounted
}
}
impl AsRef<WidgetId> for WidgetRef {
fn as_ref(&self) -> &WidgetId {
self.instance.as_ref()

View file

@ -1,10 +1,14 @@
use std::fmt::Debug;
use std::mem;
use ahash::HashMap;
use figures::Size;
use kludgine::KludgineId;
use crate::context::LayoutContext;
use crate::context::{AsEventContext, LayoutContext};
use crate::value::{Dynamic, DynamicReader, IntoDynamic, IntoReader, Source};
use crate::widget::{WidgetInstance, WidgetRef, WrapperWidget};
use crate::widget::{MountedWidget, WidgetInstance, WidgetRef, WrapperWidget};
use crate::window::WindowLocal;
use crate::ConstraintLimit;
/// A widget that switches its contents based on a value of `T`.
@ -12,6 +16,7 @@ use crate::ConstraintLimit;
pub struct Switcher {
source: DynamicReader<WidgetInstance>,
child: WidgetRef,
pending_unmount: HashMap<KludgineId, MountedWidget>,
}
impl Switcher {
@ -39,7 +44,11 @@ impl Switcher {
pub fn new(source: impl IntoReader<WidgetInstance>) -> Self {
let source = source.into_reader();
let child = WidgetRef::new(source.get());
Self { source, child }
Self {
source,
child,
pending_unmount: HashMap::default(),
}
}
}
@ -54,12 +63,32 @@ impl WrapperWidget for Switcher {
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<ConstraintLimit> {
if let Some(pending_unmount) = self.pending_unmount.remove(&context.kludgine_id()) {
context.remove_child(&pending_unmount);
}
let current_source = self.source.get_tracking_invalidate(context);
if &current_source != self.child.widget() {
// immediately unmount in the current context.
self.child.unmount_in(context);
self.child = WidgetRef::new(current_source);
let old_mounts = <WindowLocal<MountedWidget>>::from(mem::replace(
&mut self.child,
WidgetRef::new(current_source),
));
// For all other contexts, we have to wait until this callback to
// try unmounting.
for (id, mounted) in old_mounts {
let existing = self.pending_unmount.insert(id, mounted);
debug_assert!(
existing.is_none(),
"Existing unmount found, but should have already been unmounted"
);
}
}
context.invalidate_when_changed(&self.source);
available_space
}
}

View file

@ -2439,10 +2439,26 @@ impl<T> WindowLocal<T> {
self.by_window.get(&context.kludgine_id())
}
/// Looks up an exclusive reference to the value for this window, returning
/// None if not found.
///
/// Internally this API uses [`HashMap::get`](hash_map::HashMap::get).
#[must_use]
pub fn get_mut(&mut self, context: &WidgetContext<'_>) -> Option<&mut T> {
self.by_window.get_mut(&context.kludgine_id())
}
/// Removes any stored value for this window.
pub fn clear_for(&mut self, context: &WidgetContext<'_>) -> Option<T> {
self.by_window.remove(&context.kludgine_id())
}
/// Returns an iterator over the per-window values stored in this
/// collection.
#[must_use]
pub fn iter(&self) -> hash_map::Iter<'_, KludgineId, T> {
self.into_iter()
}
}
impl<T> Default for WindowLocal<T> {
@ -2453,6 +2469,24 @@ impl<T> Default for WindowLocal<T> {
}
}
impl<T> IntoIterator for WindowLocal<T> {
type IntoIter = hash_map::IntoIter<KludgineId, T>;
type Item = (KludgineId, T);
fn into_iter(self) -> Self::IntoIter {
self.by_window.into_iter()
}
}
impl<'a, T> IntoIterator for &'a WindowLocal<T> {
type IntoIter = hash_map::Iter<'a, KludgineId, T>;
type Item = (&'a KludgineId, &'a T);
fn into_iter(self) -> Self::IntoIter {
self.by_window.iter()
}
}
/// The state of a [`VirtualWindow`].
pub struct VirtualState {
/// State that may be updated outside of the window's event callbacks.