use crate::helpers::Helpers;
use crate::wayland::Monitor;
use std::collections::HashMap;
use std::i32;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LayoutMonitor {
pub x1: i32,
pub y1: i32,
pub x2: i32,
pub y2: i32,
pub initial_width: u32,
pub initial_height: u32,
pub width: u32,
pub height: u32,
}
impl LayoutMonitor {
pub fn from_monitor(monitor: &Monitor) -> Self {
let x1 = monitor.x.min(monitor.x + monitor.width as i32);
let x2 = monitor.x.max(monitor.x + monitor.width as i32);
let y1 = monitor.y.min(monitor.y + monitor.height as i32);
let y2 = monitor.y.max(monitor.y + monitor.height as i32);
Self {
x1,
y1,
x2,
y2,
initial_width: monitor.width,
initial_height: monitor.height,
width: monitor.width,
height: monitor.height,
}
}
fn translate(&mut self, dx: i32, dy: i32) {
self.x1 += dx;
self.x2 += dx;
self.y1 += dy;
self.y2 += dy;
}
fn abs_shift_diff(&self) -> (i32, i32) {
let x_diff: i32 = self.initial_width as i32 - self.width as i32;
let y_diff: i32 = self.initial_height as i32 - self.height as i32;
((x_diff / 2).abs(), (y_diff / 2).abs())
}
fn scale(&mut self, scale_factor: f32) -> &mut Self {
self.width = Helpers::round_2((self.width as f32 * scale_factor).round() as u32);
self.height = Helpers::round_2((self.height as f32 * scale_factor).round() as u32);
let (x_shift, y_shift) = self.abs_shift_diff();
if self.initial_width < self.width {
self.x1 -= x_shift;
self.x2 += x_shift;
} else {
self.x1 += x_shift;
self.x2 -= x_shift;
}
if self.initial_height < self.height {
self.y1 -= y_shift;
self.y2 += y_shift;
} else {
self.y1 += y_shift;
self.y2 -= y_shift;
}
self
}
pub fn ppi(&self, diagonal_inches: u32) -> u32 {
let diagonal_pixels = ((self.width).pow(2) + (self.height).pow(2)).isqrt() as u64;
(diagonal_pixels as f64 / (diagonal_inches as f64)).round() as u32
}
pub fn ppi_scale(&mut self, diagonal_inches: u32, max_ppi: u32) -> &mut Self {
let factor = max_ppi as f32 / self.ppi(diagonal_inches) as f32;
self.scale(factor)
}
}
pub struct Layout {
pub monitors: Vec<LayoutMonitor>,
pub ppi_advice: bool,
}
impl Layout {
pub fn from_monitors(monitors: &[Monitor]) -> Self {
let mut layout_monitors: Vec<LayoutMonitor> = Vec::with_capacity(monitors.len());
for monitor in monitors {
layout_monitors.push(LayoutMonitor::from_monitor(monitor));
}
let all_same_resolution = layout_monitors
.iter()
.map(|this| {
let result = layout_monitors.iter().all(|monitor| {
(monitor.initial_width == this.initial_width
&& monitor.initial_height == this.initial_height)
|| (monitor.initial_height == this.initial_width
&& monitor.initial_width == this.initial_height)
});
result
})
.all(|result| result == true);
Self {
monitors: layout_monitors,
ppi_advice: !all_same_resolution,
}
}
fn normalize_to_positive(&mut self) {
let min_x = self.monitors.iter().map(|r| r.x1).fold(i32::MAX, i32::min);
let min_y = self.monitors.iter().map(|r| r.y1).fold(i32::MAX, i32::min);
let dx = if min_x < 0 { -min_x } else { 0 };
let dy = if min_y < 0 { -min_y } else { 0 };
for r in self.monitors.iter_mut() {
r.translate(dx, dy);
}
}
fn calculate_max_ppi(&self, diagonals: &HashMap<String, u32>) -> u32 {
if let Some(ppi_max) = &self
.monitors
.iter()
.zip(diagonals)
.map(|(monitor, (_, &diagonal))| monitor.ppi(diagonal))
.max()
{
return ppi_max.to_owned();
} else {
return 0;
}
}
pub fn compensate_ppi(&mut self, diagonals: &HashMap<String, u32>) {
let max_ppi = self.calculate_max_ppi(&diagonals);
for (r, (_, &d)) in self.monitors.iter_mut().zip(diagonals) {
r.ppi_scale(d, max_ppi);
}
}
pub fn resolve_layout(&mut self, padding: u32, max_iterations: usize) {
for _ in 0..max_iterations {
let mut changed = false;
for i in 0..self.monitors.len() {
for j in (i + 1)..self.monitors.len() {
let (mut a, mut b) = (self.monitors[i], self.monitors[j]);
let (x_overlap, y_overlap);
if padding > 0 {
x_overlap = a.x2 >= b.x1 && a.x1 <= b.x2;
y_overlap = a.y2 >= b.y1 && a.y1 <= b.y2;
} else {
x_overlap = a.x2 > b.x1 && a.x1 < b.x2;
y_overlap = a.y2 > b.y1 && a.y1 < b.y2;
}
if x_overlap && y_overlap {
let overlap_x = (a.x2.min(b.x2) - a.x1.max(b.x1)).max(0);
let overlap_y = (a.y2.min(b.y2) - a.y1.max(b.y1)).max(0);
if overlap_x < overlap_y {
let dir = if a.x1 < b.x1 { -1 } else { 1 };
let move_dist = (overlap_x + padding as i32) / 2;
a.translate(dir * move_dist, 0);
b.translate(-dir * move_dist, 0);
} else {
let dir = if a.y1 < b.y1 { -1 } else { 1 };
let move_dist = (overlap_y + padding as i32) / 2;
a.translate(0, dir * move_dist);
b.translate(0, -dir * move_dist);
}
self.monitors[i] = a;
self.monitors[j] = b;
changed = true;
}
}
}
if !changed {
break;
}
}
self.normalize_to_positive();
}
}