use windows::Win32::Foundation::HWND;
use crate::animation::{IVec2, WindowAnimator, WindowRef, WindowTarget};
use crate::common::Rect;
use crate::layout::types::{ActualLayout, AppliedLayout};
use crate::registry::WindowRegistry;
use super::types::FlowWM;
impl FlowWM {
pub(super) fn animate_layout(&mut self, layout: &AppliedLayout) {
self.registry
.update_tiling_slots_from_layout(&layout.virtual_layout);
self.registry.update_tiled_rects(&layout.actual_layout);
if layout.actual_layout.entries.is_empty() {
return;
}
let border_thickness = self.config.borders.thickness as i32;
let border_overlap = self.config.borders.overlap as i32;
let targets: Vec<WindowTarget> = layout
.actual_layout
.entries
.iter()
.flat_map(|entry| {
let window = self.registry.get_window(HWND(entry.window_id.0 as *mut _));
let invisible_bounds = window.map(|w| w.invisible_bounds).unwrap_or_default();
let border_hwnd = window.and_then(|w| w.border.as_ref()).map(|b| b.hwnd());
let window_rect = invisible_bounds
.visible_to_window(entry.rect)
.inset(border_thickness - border_overlap);
log::debug!(
"animate: hwnd={} target ({},{},{},{}) [visible] \
→ ({},{},{},{}) [window]",
entry.window_id.0,
entry.rect.x,
entry.rect.y,
entry.rect.width,
entry.rect.height,
window_rect.x,
window_rect.y,
window_rect.width,
window_rect.height,
);
let window_target = WindowTarget::new(
WindowRef(entry.window_id.0),
IVec2::new(window_rect.x, window_rect.y),
IVec2::new(window_rect.width, window_rect.height),
);
let border_target = border_hwnd.map(|b_hwnd| {
WindowTarget::new(
WindowRef(b_hwnd),
IVec2::new(entry.rect.x, entry.rect.y),
IVec2::new(entry.rect.width, entry.rect.height),
)
});
std::iter::once(window_target).chain(border_target)
})
.collect();
log::debug!(
"animate_layout: submitting {} targets to animator",
targets.len()
);
if let Err(e) = self.animator.animate(targets) {
log::warn!("animation error: {e}");
}
}
pub(super) fn animate_workspaces(&mut self, batches: &[(ActualLayout, i32)]) {
if batches.is_empty() {
return;
}
let border_thickness = self.config.borders.thickness as i32;
let border_overlap = self.config.borders.overlap as i32;
let mut targets: Vec<WindowTarget> = Vec::new();
let mut has_float_target = false;
for (layout, y_offset) in batches {
for entry in &layout.entries {
if !has_float_target && crate::registry::hooks::is_float_hwnd(entry.window_id.0) {
has_float_target = true;
}
let window = self.registry.get_window(HWND(entry.window_id.0 as *mut _));
let invisible_bounds = window.map(|w| w.invisible_bounds).unwrap_or_default();
let border_hwnd = window.and_then(|w| w.border.as_ref()).map(|b| b.hwnd());
let window_rect = invisible_bounds
.visible_to_window(entry.rect)
.inset(border_thickness - border_overlap);
targets.push(WindowTarget::new(
WindowRef(entry.window_id.0),
IVec2::new(window_rect.x, window_rect.y + y_offset),
IVec2::new(window_rect.width, window_rect.height),
));
if let Some(b_hwnd) = border_hwnd {
targets.push(WindowTarget::new(
WindowRef(b_hwnd),
IVec2::new(entry.rect.x, entry.rect.y + y_offset),
IVec2::new(entry.rect.width, entry.rect.height),
));
}
}
}
if targets.is_empty() {
return;
}
if has_float_target && self.config.animation.enabled {
self.arm_float_tracking_suppression();
}
log::debug!(
"animate_workspaces: submitting {} targets across {} workspace batch(es)",
targets.len(),
batches.len()
);
if let Err(e) = self.animator.animate(targets) {
log::warn!("animate_workspaces error: {e}");
}
}
pub(super) fn teleport_workspaces(&self, batches: &[(ActualLayout, i32)]) {
let border_thickness = self.config.borders.thickness as i32;
let border_overlap = self.config.borders.overlap as i32;
for (layout, y_offset) in batches {
for entry in &layout.entries {
let window = self.registry.get_window(HWND(entry.window_id.0 as *mut _));
let invisible_bounds = window.map(|w| w.invisible_bounds).unwrap_or_default();
let window_rect = invisible_bounds
.visible_to_window(entry.rect)
.inset(border_thickness - border_overlap);
let target_y = window_rect.y + y_offset;
let _ = crate::registry::win32::set_window_rect(
entry.window_id.0,
window_rect.x,
target_y,
window_rect.width,
window_rect.height,
);
if let Some(border) = window.and_then(|w| w.border.as_ref()) {
border.set_geometry(Rect {
x: entry.rect.x,
y: entry.rect.y + y_offset,
width: entry.rect.width,
height: entry.rect.height,
});
}
}
}
}
}
pub(super) fn animate_layout_raw(
animator: &mut WindowAnimator,
layout: &AppliedLayout,
registry: &WindowRegistry,
border_thickness: i32,
border_overlap: i32,
) {
if layout.actual_layout.entries.is_empty() {
return;
}
let targets: Vec<WindowTarget> = layout
.actual_layout
.entries
.iter()
.flat_map(|entry| {
let window = registry.get_window(HWND(entry.window_id.0 as *mut _));
let invisible_bounds = window.map(|w| w.invisible_bounds).unwrap_or_default();
let border_hwnd = window.and_then(|w| w.border.as_ref()).map(|b| b.hwnd());
let window_rect = invisible_bounds
.visible_to_window(entry.rect)
.inset(border_thickness - border_overlap);
let window_target = WindowTarget::new(
WindowRef(entry.window_id.0),
IVec2::new(window_rect.x, window_rect.y),
IVec2::new(window_rect.width, window_rect.height),
);
let border_target = border_hwnd.map(|b_hwnd| {
WindowTarget::new(
WindowRef(b_hwnd),
IVec2::new(entry.rect.x, entry.rect.y),
IVec2::new(entry.rect.width, entry.rect.height),
)
});
std::iter::once(window_target).chain(border_target)
})
.collect();
if let Err(e) = animator.animate(targets) {
log::warn!("animation error (initial snap): {e}");
}
}