use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use super::DockPanel;
use super::id::NodeId;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PanelStore<P: DockPanel> {
tiles: HashMap<NodeId, Tile<P>>,
next_id: u64,
}
impl<P: DockPanel> PanelStore<P> {
pub fn new() -> Self {
Self {
tiles: HashMap::new(),
next_id: 1,
}
}
pub fn next_id(&mut self) -> NodeId {
let mut id = NodeId(self.next_id);
self.next_id += 1;
while self.tiles.contains_key(&id) {
id = NodeId(self.next_id);
self.next_id += 1;
}
id
}
pub fn insert(&mut self, tile: Tile<P>) -> NodeId {
let id = self.next_id();
self.tiles.insert(id, tile);
id
}
pub fn get(&self, id: NodeId) -> Option<&Tile<P>> {
self.tiles.get(&id)
}
pub fn get_mut(&mut self, id: NodeId) -> Option<&mut Tile<P>> {
self.tiles.get_mut(&id)
}
pub fn remove(&mut self, id: NodeId) -> Option<Tile<P>> {
self.tiles.remove(&id)
}
pub fn iter(&self) -> impl Iterator<Item = (NodeId, &Tile<P>)> {
self.tiles.iter().map(|(&id, tile)| (id, tile))
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (NodeId, &mut Tile<P>)> {
self.tiles.iter_mut().map(|(&id, tile)| (id, tile))
}
pub fn len(&self) -> usize {
self.tiles.len()
}
pub fn is_empty(&self) -> bool {
self.tiles.is_empty()
}
pub fn insert_with_id(&mut self, id: NodeId, tile: Tile<P>) {
self.tiles.insert(id, tile);
if id.0 >= self.next_id {
self.next_id = id.0 + 1;
}
}
}
impl<P: DockPanel> Default for PanelStore<P> {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PanelTree<P: DockPanel> {
pub root: Option<NodeId>,
pub tiles: PanelStore<P>,
}
impl<P: DockPanel> PanelTree<P> {
pub fn new() -> Self {
Self {
root: None,
tiles: PanelStore::new(),
}
}
pub fn with_panel(panel: P) -> Self {
let mut tree = Self::new();
let id = tree.tiles.insert(Tile::Panel(panel));
tree.root = Some(id);
tree
}
pub fn with_tabs(panels: Vec<P>) -> Self {
let mut tree = Self::new();
let panel_ids: Vec<NodeId> = panels
.into_iter()
.map(|p| tree.tiles.insert(Tile::Panel(p)))
.collect();
let tabs = Tabs {
children: panel_ids.clone(),
active: panel_ids.first().copied(),
};
let id = tree.tiles.insert(Tile::Container(Container::Tabs(tabs)));
tree.root = Some(id);
tree
}
}
impl<P: DockPanel> Default for PanelTree<P> {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Tile<P: DockPanel> {
Panel(P),
Container(Container),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Container {
Tabs(Tabs),
Linear(Linear),
Grid(Grid),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Tabs {
pub children: Vec<NodeId>,
pub active: Option<NodeId>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Linear {
pub children: Vec<NodeId>,
pub direction: LinearDirection,
pub shares: Shares,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum LinearDirection {
Horizontal, Vertical, }
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Grid {
pub children: Vec<NodeId>, pub layout: GridLayout,
pub col_shares: Vec<f32>, pub row_shares: Vec<f32>, }
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum GridLayout {
Columns(usize), Auto, }
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Shares {
shares: HashMap<NodeId, f32>,
}
impl Shares {
pub fn new() -> Self {
Self {
shares: HashMap::new(),
}
}
pub fn set(&mut self, id: NodeId, share: f32) {
self.shares.insert(id, share);
}
pub fn get(&self, id: NodeId) -> f32 {
self.shares.get(&id).copied().unwrap_or(1.0)
}
pub fn split(&self, children: &[NodeId], total_size: f32) -> Vec<f32> {
if children.is_empty() {
return vec![];
}
let total_shares: f32 = children.iter().map(|&id| self.get(id)).sum();
if total_shares <= 0.0 {
let size = total_size / children.len() as f32;
return vec![size; children.len()];
}
children
.iter()
.map(|&id| (self.get(id) / total_shares) * total_size)
.collect()
}
}
impl Default for Shares {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Debug)]
struct TestPanel {
title: String,
}
impl DockPanel for TestPanel {
fn title(&self) -> &str {
&self.title
}
fn type_id(&self) -> &'static str {
"test"
}
}
#[test]
fn test_panel_store_sequential_ids() {
let mut store: PanelStore<TestPanel> = PanelStore::new();
let id1 = store.next_id();
let id2 = store.next_id();
assert_eq!(id1.0, 1);
assert_eq!(id2.0, 2);
}
#[test]
fn test_panel_store_insert_get() {
let mut store: PanelStore<TestPanel> = PanelStore::new();
let panel = TestPanel { title: "Test".to_string() };
let id = store.insert(Tile::Panel(panel));
assert!(store.get(id).is_some());
}
#[test]
fn test_panel_tree_single() {
let panel = TestPanel { title: "Chart".to_string() };
let tree = PanelTree::with_panel(panel);
assert!(tree.root.is_some());
assert_eq!(tree.tiles.len(), 1);
}
#[test]
fn test_panel_tree_tabs() {
let panels = vec![
TestPanel { title: "Tab 1".to_string() },
TestPanel { title: "Tab 2".to_string() },
];
let tree = PanelTree::with_tabs(panels);
assert!(tree.root.is_some());
assert_eq!(tree.tiles.len(), 3); }
#[test]
fn test_shares_split_equal() {
let shares = Shares::new();
let ids = vec![NodeId(1), NodeId(2), NodeId(3)];
let sizes = shares.split(&ids, 300.0);
assert_eq!(sizes.len(), 3);
for size in sizes {
assert!((size - 100.0).abs() < 0.01);
}
}
#[test]
fn test_shares_split_proportional() {
let mut shares = Shares::new();
let ids = vec![NodeId(1), NodeId(2), NodeId(3)];
shares.set(NodeId(1), 1.0);
shares.set(NodeId(2), 2.0);
shares.set(NodeId(3), 1.0);
let sizes = shares.split(&ids, 400.0);
assert_eq!(sizes.len(), 3);
assert!((sizes[0] - 100.0).abs() < 0.01); assert!((sizes[1] - 200.0).abs() < 0.01); assert!((sizes[2] - 100.0).abs() < 0.01); }
}