bevy 防抖机制研究

use bevy::prelude::*;
use bevy::window::WindowResized;
use std::time::Duration;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(UIScalingPlugin)
        .add_systems(Startup, ui_demo_setup)
        .run();
}

#[derive(Resource)]
struct CanvasConfig {
    width: f32,
    height: f32,
}

impl Default for CanvasConfig {
    fn default() -> Self {
        Self {
            width: 1920.0,
            height: 1080.0,
        }
    }
}

#[derive(Resource)]
struct VirtualScreenScale {
    scale: f32,
    offset_x: f32,
    offset_y: f32,
}

impl Default for VirtualScreenScale {
    fn default() -> Self {
        Self {
            scale: 1.0,
            offset_x: 0.0,
            offset_y: 0.0,
        }
    }
}

#[derive(Resource)]
struct ResizeDebounce {
    last_resize_time: f64,  // 改为 f64,用来存储绝对时间
    pending_resize: Option<(f32, f32)>,
    debounce_duration: f64,  // 改为 f64,单位秒
}

impl Default for ResizeDebounce {
    fn default() -> Self {
        Self {
            last_resize_time: 0.0,
            pending_resize: None,
            debounce_duration: 0.016, 
        }
    }
}

#[derive(Component)]
struct ScalableUI {
    original_width: f32,
    original_height: f32,
    original_font_size: Option<f32>,
    original_margin: UiRect,
    original_padding: UiRect,
    original_border: UiRect,
    // 新增:绝对定位属性
    original_left: Option<f32>,
    original_right: Option<f32>,
    original_top: Option<f32>,
    original_bottom: Option<f32>,
}

impl ScalableUI {
    fn new() -> Self {
        Self {
            original_width: 0.0,
            original_height: 0.0,
            original_font_size: None,
            original_margin: UiRect::all(Val::Px(0.0)),
            original_padding: UiRect::all(Val::Px(0.0)),
            original_border: UiRect::all(Val::Px(0.0)),
            original_left: None,
            original_right: None,
            original_top: None,
            original_bottom: None,
        }
    }

    fn with_size(mut self, width: f32, height: f32) -> Self {
        self.original_width = width;
        self.original_height = height;
        self
    }

    fn with_font_size(mut self, font_size: f32) -> Self {
        self.original_font_size = Some(font_size);
        self
    }

    fn with_margin(mut self, margin: UiRect) -> Self {
        self.original_margin = margin;
        self
    }

    fn with_padding(mut self, padding: UiRect) -> Self {
        self.original_padding = padding;
        self
    }

    fn with_position(mut self, left: Option<f32>, right: Option<f32>, top: Option<f32>, bottom: Option<f32>) -> Self {
        self.original_left = left;
        self.original_right = right;
        self.original_top = top;
        self.original_bottom = bottom;
        self
    }

    fn with_left(mut self, left: f32) -> Self {
        self.original_left = Some(left);
        self
    }

    fn with_right(mut self, right: f32) -> Self {
        self.original_right = Some(right);
        self
    }

    fn with_top(mut self, top: f32) -> Self {
        self.original_top = Some(top);
        self
    }

    fn with_bottom(mut self, bottom: f32) -> Self {
        self.original_bottom = Some(bottom);
        self
    }
}

struct UIScalingPlugin;

impl Plugin for UIScalingPlugin {
    fn build(&self, app: &mut App) {
        app.init_resource::<CanvasConfig>()
            .init_resource::<VirtualScreenScale>()
            .init_resource::<ResizeDebounce>()
            .add_systems(Update, (
                window_resize_debounce_system,
                apply_resize_system,
            ).chain()); // 确保按顺序执行
    }
}

// 第一个系统:收集并防抖调整大小事件
fn window_resize_debounce_system(
    mut resize_events: MessageReader<WindowResized>,
    mut debounce: ResMut<ResizeDebounce>,
    time: Res<Time>,
) {
    for event in resize_events.read() {
        debounce.pending_resize = Some((event.width, event.height));
        debounce.last_resize_time = time.elapsed_secs_f64();  // 记录当前时间(秒)
    }
}

// 第二个系统:实际应用调整大小
fn apply_resize_system(
    mut ui_query: Query<(&mut Node, &ScalableUI), Without<Text>>,
    mut text_query: Query<(&mut TextFont, &ScalableUI), With<Text>>,
    canvas_config: Res<CanvasConfig>,
    mut virtual_scale: ResMut<VirtualScreenScale>,
    mut debounce: ResMut<ResizeDebounce>,
    time: Res<Time>,
) {
    // 检查是否有待处理的调整大小且已经过了防抖时间
    if let Some((width, height)) = debounce.pending_resize {
        let current_time = time.elapsed_secs_f64();
        let time_since_resize = current_time - debounce.last_resize_time;
        
        // 修正:直接比较时间(秒)
        if time_since_resize >= debounce.debounce_duration {
            // 执行实际的 UI 更新
            update_ui_scale(
                width, height, 
                &mut ui_query, 
                &mut text_query, 
                &canvas_config, 
                &mut virtual_scale
            );
            
            // 清除待处理的调整大小
            debounce.pending_resize = None;
            
            println!("窗口缩放: {}x{}, 缩放比例: {:.2}", width, height, virtual_scale.scale);
        }
    }
}

// 提取的 UI 更新逻辑
fn update_ui_scale(
    window_width: f32,
    window_height: f32,
    ui_query: &mut Query<(&mut Node, &ScalableUI), Without<Text>>,
    text_query: &mut Query<(&mut TextFont, &ScalableUI), With<Text>>,
    canvas_config: &CanvasConfig,
    virtual_scale: &mut VirtualScreenScale,
) {
    let virtual_width = canvas_config.width;
    let virtual_height = canvas_config.height;

    let scale_x = window_width / virtual_width;
    let scale_y = window_height / virtual_height;
    let scale = scale_x.min(scale_y);

    let scaled_width = virtual_width * scale;
    let scaled_height = virtual_height * scale;
    let offset_x = (window_width - scaled_width) / 2.0;
    let offset_y = (window_height - scaled_height) / 2.0;

    virtual_scale.scale = scale;
    virtual_scale.offset_x = offset_x;
    virtual_scale.offset_y = offset_y;

    // 更新 UI 节点
    for (mut node, scalable_ui) in ui_query.iter_mut() {
        update_node_scale(&mut node, scalable_ui, scale, offset_x, offset_y, virtual_width, virtual_height);
    }

    // 更新文本
    for (mut text_font, scalable_ui) in text_query.iter_mut() {
        if let Some(original_font_size) = scalable_ui.original_font_size {
            text_font.font_size = original_font_size * scale;
        }
    }
}

// 提取的节点更新逻辑
fn update_node_scale(
    node: &mut Node,
    scalable_ui: &ScalableUI,
    scale: f32,
    offset_x: f32,
    offset_y: f32,
    virtual_width: f32,
    virtual_height: f32,
) {
    // 尺寸缩放
    if scalable_ui.original_width > 0.0 {
        node.width = Val::Px(scalable_ui.original_width * scale);
    }
    if scalable_ui.original_height > 0.0 {
        node.height = Val::Px(scalable_ui.original_height * scale);
    }

    node.margin = scale_ui_rect(&scalable_ui.original_margin, scale);
    node.padding = scale_ui_rect(&scalable_ui.original_padding, scale);
    node.border = scale_ui_rect(&scalable_ui.original_border, scale);

    // 绝对定位缩放
    if let Some(left) = scalable_ui.original_left {
        node.left = Val::Px(left * scale);
    }
    if let Some(right) = scalable_ui.original_right {
        node.right = Val::Px(right * scale);
    }
    if let Some(top) = scalable_ui.original_top {
        node.top = Val::Px(top * scale);
    }
    if let Some(bottom) = scalable_ui.original_bottom {
        node.bottom = Val::Px(bottom * scale);
    }

    // 为根UI容器设置偏移
    if scalable_ui.original_width == virtual_width && scalable_ui.original_height == virtual_height {
        node.left = Val::Px(offset_x);
        node.top = Val::Px(offset_y);
    }
}

fn scale_ui_rect(rect: &UiRect, scale: f32) -> UiRect {
    UiRect {
        left: scale_val(&rect.left, scale),
        right: scale_val(&rect.right, scale),
        top: scale_val(&rect.top, scale),
        bottom: scale_val(&rect.bottom, scale),
    }
}

fn scale_val(val: &Val, scale: f32) -> Val {
    match val {
        Val::Px(px) => Val::Px(*px * scale),
        Val::Percent(percent) => Val::Percent(*percent),
        _ => *val,
    }
}

// === 演示系统 ===

fn ui_demo_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    // 加载自定义字体
    let font: Handle<Font> = asset_server.load("fonts/SarasaFixedHC-Regular.ttf");

    // 创建摄像机
    commands.spawn(Camera2d);

    commands
        .spawn((
            Node {
                width: Val::Px(1920.0),
                height: Val::Px(1080.0),
                position_type: PositionType::Absolute,
                left: Val::Px(0.0),
                top: Val::Px(0.0),
                flex_direction: FlexDirection::Column,
                justify_content: JustifyContent::Center,
                align_items: AlignItems::Center,
                ..default()
            },
            BackgroundColor(Color::srgba(0.2, 0.2, 0.2, 0.8)),
            ScalableUI::new().with_size(1920.0, 1080.0),
        ))
        .with_children(|parent| {
            // 添加一个对话框样式的UI(使用绝对定位)
            parent
                .spawn((
                    Node {
                        position_type: PositionType::Absolute,
                        left: Val::Px(200.0),
                        top: Val::Px(400.0),
                        width: Val::Px(800.0),
                        height: Val::Px(200.0),
                        padding: UiRect::all(Val::Px(20.0)),
                        flex_direction: FlexDirection::Column,
                        justify_content: JustifyContent::Center,
                        align_items: AlignItems::Center,
                        ..default()
                    },
                    BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.9)),
                    ScalableUI::new()
                        .with_size(800.0, 200.0)
                        .with_padding(UiRect::all(Val::Px(20.0)))
                        .with_left(200.0)  
                        .with_top(400.0),    
                ))
                .with_children(|parent| {
                    // 标题
                    parent.spawn((
                        Text::new("UI缩放系统演示"),
                        TextFont {
                            font: font.clone(),
                            font_size: 36.0,
                            ..default()
                        },
                        TextColor(Color::WHITE),
                        ScalableUI::new().with_font_size(36.0),
                    ));

                    parent.spawn((
                        Text::new("改变窗口大小来查看UI缩放效果"),
                        TextFont {
                            font: font.clone(),
                            font_size: 24.0,
                            ..default()
                        },
                        TextColor(Color::srgb(0.8, 0.8, 0.8)),
                        Node {
                            margin: UiRect::top(Val::Px(10.0)),
                            ..default()
                        },
                        ScalableUI::new()
                            .with_font_size(24.0)
                            .with_margin(UiRect::top(Val::Px(10.0))),
                    ));

                    parent.spawn((
                        Text::new("×"),
                        TextFont {
                            font: font.clone(),
                            font_size: 30.0,
                            ..default()
                        },
                        TextColor(Color::srgb(1.0, 0.5, 0.5)),
                        Node {
                            position_type: PositionType::Absolute,
                            right: Val::Px(15.0),
                            top: Val::Px(10.0),
                            ..default()
                        },
                        ScalableUI::new()
                            .with_font_size(30.0)
                            .with_right(15.0)
                            .with_top(10.0),
                    ));
                });

            parent.spawn((
                Node {
                    width: Val::Px(300.0),
                    height: Val::Px(100.0),
                    position_type: PositionType::Absolute,
                    right: Val::Px(20.0),
                    top: Val::Px(20.0),
                    padding: UiRect::all(Val::Px(10.0)),
                    justify_content: JustifyContent::Center,
                    align_items: AlignItems::Center,
                    ..default()
                },
                BackgroundColor(Color::srgba(0.1, 0.1, 0.1, 0.7)),
                ScalableUI::new()
                    .with_size(300.0, 100.0)
                    .with_padding(UiRect::all(Val::Px(10.0)))
                    .with_right(20.0)
                    .with_top(20.0),
            ))
            .with_children(|parent| {
                parent.spawn((
                    Text::new("虚拟分辨率: 1920x1080"),
                    TextFont {
                        font: font.clone(),
                        font_size: 20.0,
                        ..default()
                    },
                    TextColor(Color::srgb(0.9, 0.9, 0.9)),
                    ScalableUI::new().with_font_size(20.0),
                ));
            });

            parent.spawn((
                Node {
                    width: Val::Px(150.0),
                    height: Val::Px(50.0),
                    position_type: PositionType::Absolute,
                    left: Val::Px(20.0),
                    bottom: Val::Px(20.0),
                    padding: UiRect::all(Val::Px(10.0)),
                    justify_content: JustifyContent::Center,
                    align_items: AlignItems::Center,
                    ..default()
                },
                BackgroundColor(Color::srgba(0.1, 0.3, 0.1, 0.7)),
                ScalableUI::new()
                    .with_size(150.0, 50.0)
                    .with_padding(UiRect::all(Val::Px(10.0)))
                    .with_left(20.0)
                    .with_bottom(20.0),
            ))
            .with_children(|parent| {
                parent.spawn((
                    Text::new("版本 v1.0"),
                    TextFont {
                        font: font.clone(),
                        font_size: 16.0,
                        ..default()
                    },
                    TextColor(Color::srgb(0.9, 1.0, 0.9)),
                    ScalableUI::new().with_font_size(16.0),
                ));
            });

            // 添加一个中央偏右的按钮
            parent.spawn((
                Node {
                    width: Val::Px(200.0),
                    height: Val::Px(60.0),
                    position_type: PositionType::Absolute,
                    left: Val::Px(1200.0),
                    top: Val::Px(700.0),
                    padding: UiRect::all(Val::Px(15.0)),
                    justify_content: JustifyContent::Center,
                    align_items: AlignItems::Center,
                    ..default()
                },
                BackgroundColor(Color::srgba(0.2, 0.4, 0.8, 0.9)),
                ScalableUI::new()
                    .with_size(200.0, 60.0)
                    .with_padding(UiRect::all(Val::Px(15.0)))
                    .with_left(1200.0)   
                    .with_top(700.0),    
            ))
            .with_children(|parent| {
                parent.spawn((
                    Text::new("按钮示例"),
                    TextFont {
                        font: font.clone(),
                        font_size: 18.0,
                        ..default()
                    },
                    TextColor(Color::WHITE),
                    ScalableUI::new().with_font_size(18.0),
                ));
            });
        });
}