#![allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SplitLayout {
TwoByOne,
TwoByTwo,
Single,
}
#[derive(Debug, Clone)]
pub struct SplitViewportConfig {
pub layout: SplitLayout,
pub total_width: u32,
pub total_height: u32,
pub pretty: bool,
}
#[derive(Debug, Clone)]
pub struct ViewportPanel {
pub index: usize,
pub camera_label: String,
pub x: u32,
pub y: u32,
pub width: u32,
pub height: u32,
pub active: bool,
}
#[derive(Debug, Clone)]
pub struct SplitViewport {
pub panels: Vec<ViewportPanel>,
pub active_index: usize,
pub layout: SplitLayout,
pub total_width: u32,
pub total_height: u32,
}
pub fn default_split_viewport_config() -> SplitViewportConfig {
SplitViewportConfig {
layout: SplitLayout::TwoByOne,
total_width: 1280,
total_height: 720,
pretty: true,
}
}
pub fn new_split_viewport(cfg: &SplitViewportConfig) -> SplitViewport {
SplitViewport {
panels: Vec::new(),
active_index: 0,
layout: cfg.layout.clone(),
total_width: cfg.total_width,
total_height: cfg.total_height,
}
}
pub fn split_viewport_add_panel(vp: &mut SplitViewport, panel: ViewportPanel) {
vp.panels.push(panel);
}
pub fn split_viewport_panel_count(vp: &SplitViewport) -> usize {
vp.panels.len()
}
pub fn split_viewport_set_active(vp: &mut SplitViewport, index: usize) {
if index < vp.panels.len() {
for panel in &mut vp.panels {
panel.active = false;
}
vp.panels[index].active = true;
vp.active_index = index;
}
}
pub fn split_viewport_active_panel(vp: &SplitViewport) -> Option<&ViewportPanel> {
vp.panels.iter().find(|p| p.active)
}
pub fn split_viewport_resize(vp: &mut SplitViewport, new_width: u32, new_height: u32) {
let old_w = vp.total_width.max(1) as f64;
let old_h = vp.total_height.max(1) as f64;
let sx = new_width as f64 / old_w;
let sy = new_height as f64 / old_h;
for panel in &mut vp.panels {
panel.x = (panel.x as f64 * sx).round() as u32;
panel.y = (panel.y as f64 * sy).round() as u32;
panel.width = (panel.width as f64 * sx).round() as u32;
panel.height = (panel.height as f64 * sy).round() as u32;
}
vp.total_width = new_width;
vp.total_height = new_height;
}
pub fn split_viewport_to_json(vp: &SplitViewport, cfg: &SplitViewportConfig) -> String {
let indent = if cfg.pretty { " " } else { "" };
let nl = if cfg.pretty { "\n" } else { "" };
let layout = split_viewport_layout_name(vp);
let mut out = format!(
"{{{nl}{indent}\"layout\":\"{layout}\",{nl}{indent}\"total_width\":{},{nl}{indent}\"total_height\":{},{nl}{indent}\"active_index\":{},{nl}{indent}\"panels\":[{nl}",
vp.total_width, vp.total_height, vp.active_index
);
for (i, p) in vp.panels.iter().enumerate() {
let comma = if i + 1 < vp.panels.len() { "," } else { "" };
out.push_str(&format!(
"{indent}{indent}{{\"index\":{},\"camera\":\"{}\",\"x\":{},\"y\":{},\"w\":{},\"h\":{},\"active\":{}}}{}{nl}",
p.index, p.camera_label, p.x, p.y, p.width, p.height, p.active, comma
));
}
out.push_str(&format!("{indent}]{nl}}}"));
out
}
pub fn split_viewport_clear(vp: &mut SplitViewport) {
vp.panels.clear();
vp.active_index = 0;
}
pub fn split_viewport_layout_name(vp: &SplitViewport) -> &'static str {
match vp.layout {
SplitLayout::TwoByOne => "2x1",
SplitLayout::TwoByTwo => "2x2",
SplitLayout::Single => "single",
}
}
fn make_panel(index: usize, camera_label: &str, x: u32, y: u32, width: u32, height: u32) -> ViewportPanel {
ViewportPanel {
index,
camera_label: camera_label.to_string(),
x,
y,
width,
height,
active: false,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_config_values() {
let cfg = default_split_viewport_config();
assert_eq!(cfg.layout, SplitLayout::TwoByOne);
assert_eq!(cfg.total_width, 1280);
assert_eq!(cfg.total_height, 720);
}
#[test]
fn new_viewport_is_empty() {
let cfg = default_split_viewport_config();
let vp = new_split_viewport(&cfg);
assert_eq!(split_viewport_panel_count(&vp), 0);
}
#[test]
fn add_panel_increments_count() {
let cfg = default_split_viewport_config();
let mut vp = new_split_viewport(&cfg);
split_viewport_add_panel(&mut vp, make_panel(0, "Front", 0, 0, 640, 720));
assert_eq!(split_viewport_panel_count(&vp), 1);
}
#[test]
fn set_active_marks_correct_panel() {
let cfg = default_split_viewport_config();
let mut vp = new_split_viewport(&cfg);
split_viewport_add_panel(&mut vp, make_panel(0, "Front", 0, 0, 640, 720));
split_viewport_add_panel(&mut vp, make_panel(1, "Side", 640, 0, 640, 720));
split_viewport_set_active(&mut vp, 1);
assert_eq!(vp.active_index, 1);
assert!(!vp.panels[0].active);
assert!(vp.panels[1].active);
}
#[test]
fn active_panel_returns_none_on_empty() {
let cfg = default_split_viewport_config();
let vp = new_split_viewport(&cfg);
assert!(split_viewport_active_panel(&vp).is_none());
}
#[test]
fn active_panel_returns_correct_label() {
let cfg = default_split_viewport_config();
let mut vp = new_split_viewport(&cfg);
split_viewport_add_panel(&mut vp, make_panel(0, "Perspective", 0, 0, 1280, 720));
split_viewport_set_active(&mut vp, 0);
let panel = split_viewport_active_panel(&vp).expect("should succeed");
assert_eq!(panel.camera_label, "Perspective");
}
#[test]
fn resize_scales_panels() {
let cfg = default_split_viewport_config();
let mut vp = new_split_viewport(&cfg);
split_viewport_add_panel(&mut vp, make_panel(0, "Front", 0, 0, 640, 720));
split_viewport_resize(&mut vp, 2560, 1440);
assert_eq!(vp.total_width, 2560);
assert_eq!(vp.panels[0].width, 1280);
}
#[test]
fn json_contains_layout_and_panels() {
let cfg = default_split_viewport_config();
let mut vp = new_split_viewport(&cfg);
split_viewport_add_panel(&mut vp, make_panel(0, "Front", 0, 0, 640, 720));
let json = split_viewport_to_json(&vp, &cfg);
assert!(json.contains("\"layout\""));
assert!(json.contains("\"panels\""));
assert!(json.contains("Front"));
}
#[test]
fn layout_name_correct() {
let cfg = SplitViewportConfig {
layout: SplitLayout::TwoByTwo,
total_width: 1280,
total_height: 720,
pretty: false,
};
let vp = new_split_viewport(&cfg);
assert_eq!(split_viewport_layout_name(&vp), "2x2");
}
#[test]
fn clear_removes_all_panels() {
let cfg = default_split_viewport_config();
let mut vp = new_split_viewport(&cfg);
split_viewport_add_panel(&mut vp, make_panel(0, "Front", 0, 0, 640, 720));
split_viewport_add_panel(&mut vp, make_panel(1, "Side", 640, 0, 640, 720));
split_viewport_clear(&mut vp);
assert_eq!(split_viewport_panel_count(&vp), 0);
}
}