scirs2_metrics/visualization/advanced_interactive/
layout.rs

1//! Layout management for interactive visualization
2//!
3//! This module provides layout algorithms and management for positioning
4//! and sizing dashboard widgets.
5
6#![allow(clippy::too_many_arguments)]
7#![allow(dead_code)]
8
9use super::core::{LayoutConfig, Position, Size};
10use super::widgets::WidgetConfig;
11use crate::error::{MetricsError, Result};
12use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14
15/// Layout manager for dashboard widgets
16#[derive(Debug)]
17pub struct LayoutManager {
18    /// Layout configuration
19    config: LayoutConfig,
20    /// Widget layouts
21    widget_layouts: HashMap<String, WidgetLayout>,
22    /// Container constraints
23    container_constraints: ContainerConstraints,
24}
25
26/// Widget layout information
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct WidgetLayout {
29    /// Widget ID
30    pub widget_id: String,
31    /// Current position
32    pub position: Position,
33    /// Current size
34    pub size: Size,
35    /// Layout constraints
36    pub constraints: LayoutConstraints,
37    /// Grid position (if using grid layout)
38    pub grid_position: Option<GridPosition>,
39    /// Z-index for layering
40    pub z_index: i32,
41}
42
43/// Layout constraints
44#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct LayoutConstraints {
46    /// Minimum size
47    pub min_size: Size,
48    /// Maximum size
49    pub max_size: Size,
50    /// Aspect ratio constraints
51    pub aspect_ratio: Option<f64>,
52    /// Fixed width
53    pub fixed_width: Option<f64>,
54    /// Fixed height
55    pub fixed_height: Option<f64>,
56    /// Margin constraints
57    pub margin: Margin,
58}
59
60/// Grid position for grid-based layouts
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct GridPosition {
63    /// Column start
64    pub col_start: u32,
65    /// Column span
66    pub col_span: u32,
67    /// Row start
68    pub row_start: u32,
69    /// Row span
70    pub row_span: u32,
71}
72
73/// Margin specification
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct Margin {
76    /// Top margin
77    pub top: f64,
78    /// Right margin
79    pub right: f64,
80    /// Bottom margin
81    pub bottom: f64,
82    /// Left margin
83    pub left: f64,
84}
85
86/// Container constraints
87#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct ContainerConstraints {
89    /// Container width
90    pub width: f64,
91    /// Container height
92    pub height: f64,
93    /// Padding
94    pub padding: Margin,
95    /// Minimum widget size
96    pub min_widget_size: Size,
97    /// Maximum widget size
98    pub max_widget_size: Size,
99}
100
101impl LayoutManager {
102    /// Create new layout manager
103    pub fn new(config: LayoutConfig, container_constraints: ContainerConstraints) -> Self {
104        Self {
105            config,
106            widget_layouts: HashMap::new(),
107            container_constraints,
108        }
109    }
110
111    /// Add widget to layout
112    pub fn add_widget(&mut self, widget_config: &WidgetConfig) -> Result<()> {
113        let layout = WidgetLayout {
114            widget_id: widget_config.id.clone(),
115            position: widget_config.position.clone(),
116            size: widget_config.size.clone(),
117            constraints: LayoutConstraints::from_widget_config(widget_config),
118            grid_position: None,
119            z_index: widget_config.z_index,
120        };
121
122        self.widget_layouts.insert(widget_config.id.clone(), layout);
123        self.update_layout()?;
124        Ok(())
125    }
126
127    /// Remove widget from layout
128    pub fn remove_widget(&mut self, widget_id: &str) -> Result<()> {
129        self.widget_layouts.remove(widget_id);
130        self.update_layout()?;
131        Ok(())
132    }
133
134    /// Update layout calculations
135    pub fn update_layout(&mut self) -> Result<()> {
136        match &self.config.layout_type {
137            super::core::LayoutType::Grid => self.update_grid_layout(),
138            super::core::LayoutType::Fixed => Ok(()), // Fixed layout doesn't need updates
139            super::core::LayoutType::Flexbox => self.update_flexbox_layout(),
140            super::core::LayoutType::Masonry => self.update_masonry_layout(),
141            super::core::LayoutType::Custom(_) => self.update_custom_layout(),
142        }
143    }
144
145    /// Update grid layout
146    fn update_grid_layout(&mut self) -> Result<()> {
147        if let Some(grid_config) = &self.config.grid_config {
148            let cell_width = (self.container_constraints.width
149                - (grid_config.column_gap * (grid_config.columns - 1)) as f64)
150                / grid_config.columns as f64;
151            let cell_height = (self.container_constraints.height
152                - (grid_config.row_gap * (grid_config.rows - 1)) as f64)
153                / grid_config.rows as f64;
154
155            // Auto-arrange widgets in grid
156            let mut col = 0;
157            let mut row = 0;
158
159            for layout in self.widget_layouts.values_mut() {
160                layout.grid_position = Some(GridPosition {
161                    col_start: col,
162                    col_span: 1,
163                    row_start: row,
164                    row_span: 1,
165                });
166
167                layout.position = Position {
168                    x: col as f64 * (cell_width + grid_config.column_gap as f64),
169                    y: row as f64 * (cell_height + grid_config.row_gap as f64),
170                };
171
172                layout.size = Size {
173                    width: cell_width,
174                    height: cell_height,
175                };
176
177                col += 1;
178                if col >= grid_config.columns {
179                    col = 0;
180                    row += 1;
181                }
182            }
183        }
184        Ok(())
185    }
186
187    /// Update flexbox layout
188    fn update_flexbox_layout(&mut self) -> Result<()> {
189        // Simplified flexbox implementation
190        let widget_count = self.widget_layouts.len() as f64;
191        if widget_count == 0.0 {
192            return Ok(());
193        }
194
195        let available_width = self.container_constraints.width;
196        let widget_width = available_width / widget_count;
197
198        let mut x = 0.0;
199        for layout in self.widget_layouts.values_mut() {
200            layout.position.x = x;
201            layout.position.y = 0.0;
202            layout.size.width = widget_width;
203            layout.size.height = self.container_constraints.height;
204            x += widget_width;
205        }
206
207        Ok(())
208    }
209
210    /// Update masonry layout
211    fn update_masonry_layout(&mut self) -> Result<()> {
212        // Simplified masonry implementation
213        let columns = 3; // Fixed for simplicity
214        let column_width = self.container_constraints.width / columns as f64;
215        let mut column_heights = vec![0.0; columns];
216
217        for layout in self.widget_layouts.values_mut() {
218            // Find shortest column
219            let shortest_col = column_heights
220                .iter()
221                .enumerate()
222                .min_by(|a, b| a.1.partial_cmp(b.1).unwrap())
223                .map(|(i, _)| i)
224                .unwrap_or(0);
225
226            layout.position.x = shortest_col as f64 * column_width;
227            layout.position.y = column_heights[shortest_col];
228            layout.size.width = column_width;
229
230            column_heights[shortest_col] += layout.size.height;
231        }
232
233        Ok(())
234    }
235
236    /// Update custom layout
237    fn update_custom_layout(&mut self) -> Result<()> {
238        // Placeholder for custom layout implementation
239        Ok(())
240    }
241
242    /// Get widget layout
243    pub fn get_widget_layout(&self, widget_id: &str) -> Option<&WidgetLayout> {
244        self.widget_layouts.get(widget_id)
245    }
246
247    /// Update container constraints
248    pub fn update_container_constraints(
249        &mut self,
250        constraints: ContainerConstraints,
251    ) -> Result<()> {
252        self.container_constraints = constraints;
253        self.update_layout()
254    }
255}
256
257impl LayoutConstraints {
258    /// Create layout constraints from widget config
259    pub fn from_widget_config(config: &WidgetConfig) -> Self {
260        Self {
261            min_size: Size {
262                width: 50.0,
263                height: 50.0,
264            },
265            max_size: Size {
266                width: f64::INFINITY,
267                height: f64::INFINITY,
268            },
269            aspect_ratio: None,
270            fixed_width: None,
271            fixed_height: None,
272            margin: Margin::default(),
273        }
274    }
275}
276
277impl Default for LayoutConstraints {
278    fn default() -> Self {
279        Self {
280            min_size: Size {
281                width: 50.0,
282                height: 50.0,
283            },
284            max_size: Size {
285                width: f64::INFINITY,
286                height: f64::INFINITY,
287            },
288            aspect_ratio: None,
289            fixed_width: None,
290            fixed_height: None,
291            margin: Margin::default(),
292        }
293    }
294}
295
296impl Default for Margin {
297    fn default() -> Self {
298        Self {
299            top: 0.0,
300            right: 0.0,
301            bottom: 0.0,
302            left: 0.0,
303        }
304    }
305}
306
307impl Default for ContainerConstraints {
308    fn default() -> Self {
309        Self {
310            width: 1200.0,
311            height: 800.0,
312            padding: Margin::default(),
313            min_widget_size: Size {
314                width: 50.0,
315                height: 50.0,
316            },
317            max_widget_size: Size {
318                width: 500.0,
319                height: 500.0,
320            },
321        }
322    }
323}