use super::{MonitorInfo, WindowArrangement};
use std::collections::HashMap;
use winit::monitor::MonitorHandle;
pub fn build_monitor_mapping(
saved_monitors: &[MonitorInfo],
available: &[MonitorHandle],
) -> HashMap<usize, usize> {
let mut mapping = HashMap::new();
for saved in saved_monitors {
let matched_index = if let Some(ref saved_name) = saved.name {
available
.iter()
.position(|m| m.name().as_deref() == Some(saved_name.as_str()))
} else {
None
};
let matched_index = matched_index.unwrap_or({
if saved.index < available.len() {
saved.index
} else {
0
}
});
mapping.insert(saved.index, matched_index);
}
mapping
}
pub fn clamp_to_monitor(
x: i32,
y: i32,
width: u32,
height: u32,
monitor_pos: (i32, i32),
monitor_size: (u32, u32),
) -> (i32, i32, u32, u32) {
let clamped_width = width.min(monitor_size.0);
let clamped_height = height.min(monitor_size.1);
let min_visible = 100i32;
let clamped_x = x
.max(monitor_pos.0 - clamped_width as i32 + min_visible)
.min(monitor_pos.0 + monitor_size.0 as i32 - min_visible);
let clamped_y = y
.max(monitor_pos.1 - clamped_height as i32 + min_visible)
.min(monitor_pos.1 + monitor_size.1 as i32 - min_visible);
(clamped_x, clamped_y, clamped_width, clamped_height)
}
pub fn compute_restore_position(
snapshot: &super::WindowSnapshot,
monitor_mapping: &HashMap<usize, usize>,
available: &[MonitorHandle],
) -> Option<(i32, i32, u32, u32)> {
if available.is_empty() {
return None;
}
let target_index = monitor_mapping
.get(&snapshot.monitor.index)
.copied()
.unwrap_or(0);
let target_monitor = available.get(target_index).or(available.first())?;
let monitor_scale = target_monitor.scale_factor();
let monitor_pos = target_monitor.position();
let monitor_pos_logical = (
(monitor_pos.x as f64 / monitor_scale) as i32,
(monitor_pos.y as f64 / monitor_scale) as i32,
);
let monitor_size = target_monitor.size();
let monitor_size_logical = (
(monitor_size.width as f64 / monitor_scale) as u32,
(monitor_size.height as f64 / monitor_scale) as u32,
);
let abs_x = monitor_pos_logical.0 + snapshot.position_relative.0;
let abs_y = monitor_pos_logical.1 + snapshot.position_relative.1;
let (x, y, w, h) = clamp_to_monitor(
abs_x,
abs_y,
snapshot.size.0,
snapshot.size.1,
monitor_pos_logical,
monitor_size_logical,
);
Some((x, y, w, h))
}
pub fn tab_cwds(arrangement: &WindowArrangement, window_index: usize) -> Vec<Option<String>> {
arrangement
.windows
.get(window_index)
.map(|ws| ws.tabs.iter().map(|t| t.cwd.clone()).collect())
.unwrap_or_default()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_clamp_to_monitor_within_bounds() {
let (x, y, w, h) = clamp_to_monitor(100, 100, 800, 600, (0, 0), (1920, 1080));
assert_eq!((x, y, w, h), (100, 100, 800, 600));
}
#[test]
fn test_clamp_to_monitor_too_large() {
let (_, _, w, h) = clamp_to_monitor(0, 0, 3000, 2000, (0, 0), (1920, 1080));
assert_eq!(w, 1920);
assert_eq!(h, 1080);
}
#[test]
fn test_clamp_to_monitor_offscreen_right() {
let (x, _, _, _) = clamp_to_monitor(2000, 0, 800, 600, (0, 0), (1920, 1080));
assert!(x <= 1920 - 100);
}
#[test]
fn test_clamp_to_monitor_offscreen_left() {
let (x, _, _, _) = clamp_to_monitor(-1000, 0, 800, 600, (0, 0), (1920, 1080));
assert!(x >= -800 + 100);
}
#[test]
fn test_tab_cwds() {
use super::super::{MonitorInfo, TabSnapshot, WindowArrangement, WindowSnapshot};
use uuid::Uuid;
let arrangement = WindowArrangement {
id: Uuid::new_v4(),
name: "Test".to_string(),
monitor_layout: Vec::new(),
windows: vec![WindowSnapshot {
monitor: MonitorInfo {
name: None,
index: 0,
position: (0, 0),
size: (1920, 1080),
scale_factor: 1.0,
},
position_relative: (0, 0),
size: (800, 600),
tabs: vec![
TabSnapshot {
cwd: Some("/home/user".to_string()),
title: "tab1".to_string(),
custom_color: None,
user_title: None,
custom_icon: None,
},
TabSnapshot {
cwd: None,
title: "tab2".to_string(),
custom_color: None,
user_title: None,
custom_icon: None,
},
],
active_tab_index: 0,
tmux_session_name: None,
}],
created_at: String::new(),
order: 0,
};
let cwds = tab_cwds(&arrangement, 0);
assert_eq!(cwds.len(), 2);
assert_eq!(cwds[0], Some("/home/user".to_string()));
assert_eq!(cwds[1], None);
let cwds = tab_cwds(&arrangement, 5);
assert!(cwds.is_empty());
}
}