mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-12 13:32:28 +00:00
docking: save every floating window's id (#317)
This commit is contained in:
1 parent
e9824219c7
commit
ea4c823abb
4 files changed
+39
-17
No files matched your search
@@ -61,15 +61,16 @@ pub struct StackEntry<P> {
|
||||
/// Serde reads the v1 shape `{panels, active, rect}` as one group, and a stack of groups
|
||||
/// `{groups, rect}` as a vertical split of them, sized by their heights. A window with one
|
||||
/// group (not minimized, no native rect) writes the v1 shape, so older builds can read files
|
||||
/// that use no newer feature. A native window also writes its `id`; any other window gets a
|
||||
/// fresh one on load.
|
||||
/// that use no newer feature. Every window writes its `id`; a window read without one gets a
|
||||
/// fresh one.
|
||||
///
|
||||
/// Equality ignores `id`: two windows with the same contents and place are the same layout.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Floating<P> {
|
||||
/// Names the window across panel moves, and its OS window with feature `native-windows`.
|
||||
/// Unique within a layout: [`Self::new`] and loading draw fresh ids, [`Self::with_id`]
|
||||
/// sets one, and merging windows keeps the receiving window's.
|
||||
/// Unique within a layout: [`Self::new`] draws a fresh id, [`Self::with_id`] sets one,
|
||||
/// loading keeps the saved id (or draws one when it is missing or taken), and merging
|
||||
/// windows keeps the receiving window's.
|
||||
pub id: u64,
|
||||
/// `Split`, `Tabs` and `Stack` nodes; a `Dock` is invalid here.
|
||||
pub root: Node<P>,
|
||||
@@ -181,6 +182,7 @@ mod floating_serde {
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct V1<'a, P> {
|
||||
id: u64,
|
||||
panels: &'a [P],
|
||||
active: usize,
|
||||
rect: [f32; 4],
|
||||
@@ -188,8 +190,7 @@ mod floating_serde {
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Tree<'a, P> {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
id: Option<u64>,
|
||||
id: u64,
|
||||
root: &'a Node<P>,
|
||||
rect: [f32; 4],
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -206,10 +207,10 @@ mod floating_serde {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
match &self.root {
|
||||
Node::Tabs { panels, active } if !self.minimized && self.native.is_none() => {
|
||||
V1 { panels, active: *active, rect: self.rect }.serialize(serializer)
|
||||
V1 { id: self.id, panels, active: *active, rect: self.rect }.serialize(serializer)
|
||||
}
|
||||
root => Tree {
|
||||
id: self.native.map(|_| self.id),
|
||||
id: self.id,
|
||||
root,
|
||||
rect: self.rect,
|
||||
native: self.native,
|
||||
|
||||
@@ -576,18 +576,32 @@ mod serde_tests {
|
||||
fn single_plain_floating_window_writes_the_v1_shape() {
|
||||
let rect = [1.0, 2.0, 300.0, 200.0];
|
||||
let plain = Floating::single(vec![4u32, 5], 1, rect);
|
||||
assert_eq!(serde_json::to_value(&plain).unwrap(), json!({"panels": [4, 5], "active": 1, "rect": rect}));
|
||||
assert_eq!(serde_json::to_value(&plain).unwrap(), json!({"id": plain.id, "panels": [4, 5], "active": 1, "rect": rect}));
|
||||
let minimized = Floating { minimized: true, ..Floating::single(vec![4u32], 0, rect) };
|
||||
let stacked = Floating::stack(vec![g(&[4], 0), g(&[5], 0)], rect).unwrap();
|
||||
let native = Floating { native: Some(rect), ..Floating::single(vec![4u32], 0, rect) };
|
||||
for window in [minimized, stacked, native] {
|
||||
let value = serde_json::to_value(&window).unwrap();
|
||||
assert!(value.get("root").is_some() && value.get("panels").is_none(), "{value}");
|
||||
assert_eq!(value["id"], json!(window.id), "{value}");
|
||||
assert_eq!(serde_json::from_value::<Floating<u32>>(value).unwrap(), window);
|
||||
}
|
||||
assert!(serde_json::from_value::<Floating<u32>>(json!({"rect": rect})).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_window_windows_keep_their_ids_through_a_save() {
|
||||
let rect = [1.0, 2.0, 300.0, 200.0];
|
||||
let layout = Layout {
|
||||
root: None,
|
||||
floating: vec![Floating::single(vec![4u32], 0, rect).with_id(7), Floating::stack(vec![g(&[5], 0), g(&[6], 0)], rect).unwrap().with_id(8)],
|
||||
};
|
||||
let saved = serde_json::to_string(&layout).unwrap();
|
||||
let loaded: Layout<u32> = serde_json::from_str(&saved).unwrap();
|
||||
assert_eq!(loaded, layout);
|
||||
assert_eq!(loaded.floating.iter().map(|w| w.id).collect::<Vec<_>>(), [7, 8]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v2_round_trips() {
|
||||
let mut workspace = Workspace::new(docked());
|
||||
@@ -668,8 +682,13 @@ mod serde_tests {
|
||||
}
|
||||
let layout: super::Layout<String> = serde_json::from_value(layout_value.clone()).unwrap_or_else(|e| panic!("{app}{pointer}: {e}"));
|
||||
layout.validate().unwrap_or_else(|e| panic!("{app}{pointer}: {e}"));
|
||||
// Nothing new is in use, so the layout writes back exactly as read.
|
||||
assert_eq!(serde_json::to_value(&layout).unwrap(), layout_value, "{app}{pointer}");
|
||||
// Nothing new is in use, so the layout writes back exactly as read, plus the
|
||||
// fresh id of each floating window.
|
||||
let mut written = serde_json::to_value(&layout).unwrap();
|
||||
for (window, value) in layout.floating.iter().zip(written["floating"].as_array_mut().unwrap()) {
|
||||
assert_eq!(value.as_object_mut().unwrap().remove("id"), Some(json!(window.id)), "{app}{pointer}");
|
||||
}
|
||||
assert_eq!(written, layout_value, "{app}{pointer}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,8 +581,9 @@ fn a_native_window_saves_its_id_place_and_monitor() {
|
||||
let copies: Layout<u32> = serde_json::from_str(&serde_json::to_string(&copies).unwrap()).unwrap();
|
||||
assert_eq!(copies.floating[0].id, layout.floating[0].id);
|
||||
assert_ne!(copies.floating[1].id, copies.floating[0].id);
|
||||
// An in-window window writes no id, as older builds did.
|
||||
assert!(!serde_json::to_string(&Floating::single(vec![1u32], 0, [0.0, 0.0, 9.0, 9.0])).unwrap().contains("id"));
|
||||
// An in-window window writes its id too.
|
||||
let in_window = Floating::single(vec![1u32], 0, [0.0, 0.0, 9.0, 9.0]).with_id(3);
|
||||
assert!(serde_json::to_string(&in_window).unwrap().contains(r#""id":3"#));
|
||||
}
|
||||
|
||||
/// Height of the bar at the top of a native window.
|
||||
|
||||
+5
-4
@@ -413,10 +413,11 @@ the main window has never reported its `outer_rect`; an app that forces X11 in c
|
||||
windows from the first frame. `DockArea::native_windows(bool)` overrides the choice either way.
|
||||
|
||||
- **Identity.** `Floating::id` names the OS window, so a window keeps it when its panels change,
|
||||
including when its first panel moves out. `Floating::new` and loading draw fresh ids, and
|
||||
`Floating::with_id` sets one; a layout
|
||||
loads with unique ids; a window that merges into another takes the receiver's id, and a whole
|
||||
window that moves (floated again, or its only group dragged) keeps its own.
|
||||
including when its first panel moves out. `Floating::new` draws a fresh id and
|
||||
`Floating::with_id` sets one. A saved layout stores every window's id and loads with unique
|
||||
ids: a window saved without an id, or with one an earlier window took, gets a fresh one. A
|
||||
window that merges into another takes the receiver's id, and a whole window that moves
|
||||
(floated again, or its only group dragged) keeps its own.
|
||||
- **Kind and level.** `DockContent::window_kind` sorts panels into palettes
|
||||
(`WindowKind::Palette`, the default) and viewers (`WindowKind::Normal`), such as a video
|
||||
monitor on a second display. A window holding any `Normal` panel is a normal window, whichever
|
||||
|
||||
Reference in new issue
Block a user