float-clock-wayland 0.2.1

Always-on-top floating desktop clock widget for Wayland compositors.
use crate::platform;

pub struct DragController {
    initial_margin: (i32, i32),
    current_margin: (i32, i32),
    click_pos: (i32, i32),
    is_dragging: bool,
    use_absolute: bool,
}

impl DragController {
    pub fn new(initial_margin: (i32, i32), use_absolute: bool) -> Self {
        Self {
            initial_margin,
            current_margin: initial_margin,
            click_pos: (0, 0),
            is_dragging: false,
            use_absolute,
        }
    }

    pub fn press(&mut self, event_root_pos: (i32, i32)) {
        self.is_dragging = true;
        self.initial_margin = self.current_margin;

        if self.use_absolute
            && let Some(abs_pos) = platform::get_absolute_cursor_pos() {
                self.click_pos = abs_pos;
                return;
            }
        // Fallback to relative pointer event position
        self.click_pos = event_root_pos;
    }

    pub fn motion(&mut self, event_root_pos: (i32, i32)) -> Option<(i32, i32)> {
        if !self.is_dragging {
            return None;
        }

        if self.use_absolute
            && let Some((abs_x, abs_y)) = platform::get_absolute_cursor_pos() {
                let delta_x = abs_x - self.click_pos.0;
                let delta_y = abs_y - self.click_pos.1;
                let new_margin = (
                    self.initial_margin.0 + delta_x,
                    self.initial_margin.1 + delta_y,
                );
                self.current_margin = new_margin;
                return Some(new_margin);
            }

        // Relative / Compensated Dragging Logic
        let (raw_x, raw_y) = event_root_pos;
        let old_margin = self.current_margin;

        let delta_x = raw_x - self.click_pos.0;
        let delta_y = raw_y - self.click_pos.1;

        let new_margin = (
            self.initial_margin.0 + delta_x,
            self.initial_margin.1 + delta_y,
        );

        let moved_x = new_margin.0 - old_margin.0;
        let moved_y = new_margin.1 - old_margin.1;

        self.click_pos.0 -= moved_x;
        self.click_pos.1 -= moved_y;

        self.current_margin = new_margin;
        Some(new_margin)
    }

    pub fn release(&mut self) -> (i32, i32) {
        self.is_dragging = false;
        self.current_margin
    }

    pub fn current_margin(&self) -> (i32, i32) {
        self.current_margin
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn compensated_drag_delta_calculation() {
        let mut controller = DragController::new((50, 50), false);
        controller.press((10, 10));

        let m1 = controller.motion((15, 10)).unwrap();
        assert_eq!(m1, (55, 50));

        // Simulated local coordinate jump due to window movement
        let m2 = controller.motion((10, 10)).unwrap();
        assert_eq!(m2, (55, 50));
    }
}