halley-wl 0.3.0

Wayland backend and rendering implementation for the Halley Wayland compositor.
use super::*;

impl<T: DerefMut<Target = Halley>> ClusterSystemController<T> {
    const CLUSTER_OVERFLOW_REVEAL_MS: u64 = 2200;

    pub(crate) fn adjust_cluster_overflow_scroll_for_monitor(
        &mut self,
        monitor: &str,
        delta: i32,
    ) -> bool {
        let overflow_len = self
            .model
            .cluster_state
            .cluster_overflow_members
            .get(monitor)
            .map(Vec::len)
            .unwrap_or(0);
        let max_offset = overflow_len.saturating_sub(Self::CLUSTER_OVERFLOW_VISIBLE_SLOTS);
        if max_offset == 0 {
            self.model
                .cluster_state
                .cluster_overflow_scroll_offsets
                .remove(monitor);
            return false;
        }
        let current = self
            .model
            .cluster_state
            .cluster_overflow_scroll_offsets
            .get(monitor)
            .copied()
            .unwrap_or(0) as i32;
        let next = (current + delta).clamp(0, max_offset as i32) as usize;
        if next == current as usize {
            return false;
        }
        self.model
            .cluster_state
            .cluster_overflow_scroll_offsets
            .insert(monitor.to_string(), next);
        true
    }

    pub(super) fn clear_cluster_overflow_for_monitor(&mut self, monitor: &str) {
        self.model
            .cluster_state
            .cluster_overflow_members
            .remove(monitor);
        self.model
            .cluster_state
            .cluster_overflow_rects
            .remove(monitor);
        self.model
            .cluster_state
            .cluster_overflow_scroll_offsets
            .remove(monitor);
        self.model
            .cluster_state
            .cluster_overflow_reveal_started_at_ms
            .remove(monitor);
        self.model
            .cluster_state
            .cluster_overflow_visible_until_ms
            .remove(monitor);
    }

    pub(super) fn refresh_cluster_overflow_for_monitor(
        &mut self,
        monitor: &str,
        now_ms: u64,
        reveal: bool,
    ) {
        let Some(_cid) = self.active_cluster_workspace_for_monitor(monitor) else {
            self.clear_cluster_overflow_for_monitor(monitor);
            return;
        };
        let Some(plan) = self
            .cluster_read_controller()
            .plan_active_cluster_layout(monitor)
        else {
            self.clear_cluster_overflow_for_monitor(monitor);
            return;
        };
        let overflow = plan.overflow_members;
        if overflow.is_empty() {
            self.clear_cluster_overflow_for_monitor(monitor);
            return;
        }

        let was_visible = self
            .model
            .cluster_state
            .cluster_overflow_visible_until_ms
            .get(monitor)
            .is_some_and(|visible_until_ms| *visible_until_ms > now_ms);

        self.model
            .cluster_state
            .cluster_overflow_members
            .insert(monitor.to_string(), overflow.clone());
        let max_offset = overflow
            .len()
            .saturating_sub(Self::CLUSTER_OVERFLOW_VISIBLE_SLOTS);
        if max_offset == 0 {
            self.model
                .cluster_state
                .cluster_overflow_scroll_offsets
                .remove(monitor);
        } else {
            let next = self
                .model
                .cluster_state
                .cluster_overflow_scroll_offsets
                .get(monitor)
                .copied()
                .unwrap_or(0)
                .min(max_offset);
            self.model
                .cluster_state
                .cluster_overflow_scroll_offsets
                .insert(monitor.to_string(), next);
        }
        if let Some(rect) = self
            .cluster_read_controller()
            .overflow_strip_rect_for_monitor(monitor, overflow.len())
        {
            self.model
                .cluster_state
                .cluster_overflow_rects
                .insert(monitor.to_string(), rect);
        }
        if reveal {
            if !was_visible {
                self.model
                    .cluster_state
                    .cluster_overflow_reveal_started_at_ms
                    .insert(monitor.to_string(), now_ms);
            }
            self.model
                .cluster_state
                .cluster_overflow_visible_until_ms
                .insert(
                    monitor.to_string(),
                    now_ms.saturating_add(Self::CLUSTER_OVERFLOW_REVEAL_MS),
                );
            self.request_maintenance();
        }
    }

    pub(crate) fn reveal_cluster_overflow_for_monitor(&mut self, monitor: &str, now_ms: u64) {
        self.refresh_cluster_overflow_for_monitor(monitor, now_ms, true);
    }

    pub(crate) fn hide_cluster_overflow_for_monitor(&mut self, monitor: &str) {
        self.model
            .cluster_state
            .cluster_overflow_scroll_offsets
            .remove(monitor);
        self.model
            .cluster_state
            .cluster_overflow_reveal_started_at_ms
            .remove(monitor);
        self.model
            .cluster_state
            .cluster_overflow_visible_until_ms
            .remove(monitor);
    }

    pub(crate) fn swap_cluster_overflow_member_with_visible(
        &mut self,
        monitor: &str,
        cid: ClusterId,
        overflow_member: NodeId,
        visible_member: NodeId,
        now_ms: u64,
    ) -> bool {
        if self.active_cluster_workspace_for_monitor(monitor) != Some(cid) {
            return false;
        }
        if !matches!(
            self.active_cluster_layout_kind(),
            ClusterWorkspaceLayoutKind::Tiling
        ) {
            return false;
        }
        let max_stack = self.runtime.tuning.tile_max_stack;
        if !self.model.field.swap_cluster_overflow_member_with_visible(
            cid,
            overflow_member,
            visible_member,
            max_stack,
        ) {
            return false;
        }
        self.layout_active_cluster_workspace_for_monitor(monitor, now_ms);
        self.reveal_cluster_overflow_for_monitor(monitor, now_ms);
        true
    }

    pub(crate) fn reorder_cluster_overflow_member(
        &mut self,
        monitor: &str,
        cid: ClusterId,
        member: NodeId,
        target_overflow_index: usize,
        now_ms: u64,
    ) -> bool {
        if self.active_cluster_workspace_for_monitor(monitor) != Some(cid) {
            return false;
        }
        if !matches!(
            self.active_cluster_layout_kind(),
            ClusterWorkspaceLayoutKind::Tiling
        ) {
            return false;
        }
        let max_stack = self.runtime.tuning.tile_max_stack;
        if !self.model.field.reorder_cluster_overflow_member(
            cid,
            member,
            target_overflow_index,
            max_stack,
        ) {
            return false;
        }
        self.refresh_cluster_overflow_for_monitor(monitor, now_ms, true);
        true
    }
}