use bevy::prelude::*;
use bevy::window::PrimaryWindow;
use bevy::window::WindowResized;
use bevy::window::WindowScaleFactorChanged;
use crate::types::SCALE_FACTOR_EPSILON;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CorrectionState {
WaitingForDragBack,
PendingCorrection {
corrected_size: UVec2,
wrong_cached_size: UVec2,
},
}
#[derive(Resource)]
pub struct DragBackSizeProtection {
pub expected_physical_size: UVec2,
pub launch_scale: f64,
pub restored_scale: f64,
pub phase1_cached_size: UVec2,
pub state: CorrectionState,
}
pub fn init(app: &mut App) {
app.add_systems(
Update,
(
detect_user_resize.run_if(resource_exists::<DragBackSizeProtection>),
handle_drag_back_scale_change.run_if(resource_exists::<DragBackSizeProtection>),
apply_pending_correction.run_if(resource_exists::<DragBackSizeProtection>),
)
.chain(),
);
}
fn detect_user_resize(
mut commands: Commands,
protection: Res<DragBackSizeProtection>,
window: Single<&Window, With<PrimaryWindow>>,
mut resize_messages: MessageReader<WindowResized>,
) {
if protection.state != CorrectionState::WaitingForDragBack {
return;
}
if resize_messages.read().last().is_none() {
return;
}
let current_scale = f64::from(window.resolution.scale_factor());
if (current_scale - protection.restored_scale).abs() > SCALE_FACTOR_EPSILON {
return;
}
let current_size = UVec2::new(
window.resolution.physical_width(),
window.resolution.physical_height(),
);
if current_size != protection.expected_physical_size {
debug!(
"[W4 fix] User resize detected: {}x{} -> {}x{}, removing protection",
protection.expected_physical_size.x,
protection.expected_physical_size.y,
current_size.x,
current_size.y
);
commands.remove_resource::<DragBackSizeProtection>();
}
}
fn handle_drag_back_scale_change(
mut protection: ResMut<DragBackSizeProtection>,
mut scale_changed_messages: MessageReader<WindowScaleFactorChanged>,
) {
if protection.state != CorrectionState::WaitingForDragBack {
return;
}
let Some(scale_event) = scale_changed_messages.read().last() else {
return;
};
let new_scale = scale_event.scale_factor;
if (new_scale - protection.launch_scale).abs() > SCALE_FACTOR_EPSILON {
debug!(
"[W4 fix] Scale changed to {} (not launch_scale {}), ignoring",
new_scale, protection.launch_scale
);
return;
}
let ratio = protection.launch_scale / protection.restored_scale;
let corrected_width = (f64::from(protection.expected_physical_size.x) * ratio) as u32;
let corrected_height = (f64::from(protection.expected_physical_size.y) * ratio) as u32;
let corrected_size = UVec2::new(corrected_width, corrected_height);
debug!(
"[W4 fix] Drag-back detected: scale {} -> {}, queueing correction {}x{} -> {}x{} (waiting for wrong size {}x{})",
protection.restored_scale,
protection.launch_scale,
protection.expected_physical_size.x,
protection.expected_physical_size.y,
corrected_width,
corrected_height,
protection.phase1_cached_size.x,
protection.phase1_cached_size.y,
);
protection.state = CorrectionState::PendingCorrection {
corrected_size,
wrong_cached_size: protection.phase1_cached_size,
};
}
fn apply_pending_correction(
mut commands: Commands,
protection: Res<DragBackSizeProtection>,
mut window: Single<&mut Window, With<PrimaryWindow>>,
mut resize_messages: MessageReader<WindowResized>,
) {
let CorrectionState::PendingCorrection {
corrected_size,
wrong_cached_size,
} = protection.state
else {
return;
};
if resize_messages.read().last().is_none() {
return;
}
let current_size = UVec2::new(
window.resolution.physical_width(),
window.resolution.physical_height(),
);
let size_matches = current_size.x.abs_diff(wrong_cached_size.x) <= 2
&& current_size.y.abs_diff(wrong_cached_size.y) <= 2;
if !size_matches {
debug!(
"[W4 fix] Resize to {}x{}, waiting for wrong size ~{}x{}",
current_size.x, current_size.y, wrong_cached_size.x, wrong_cached_size.y
);
return;
}
debug!(
"[W4 fix] W4 detected (size={}x{}), applying correction: {}x{}, removing protection",
current_size.x, current_size.y, corrected_size.x, corrected_size.y
);
window
.resolution
.set_physical_resolution(corrected_size.x, corrected_size.y);
commands.remove_resource::<DragBackSizeProtection>();
}