zsui 0.3.0-alpha.2

Rust-first feature-gated native UI framework for Win32, AppKit, and Linux.
Documentation
impl WindowsWin32ViewInputRoute {
    fn dispatch_click(&mut self, point: crate::Point) -> WindowsWin32ViewInputDispatchReport {
        let target = self.shared_target_at(point);
        let down = self.shared_runtime.dispatch_pointer_down(point, false);
        let mut report =
            self.adapt_shared_report(down, WindowsSharedInputKind::PointerDown(target));
        let up = self.shared_runtime.dispatch_pointer_up(point);
        report.merge(self.adapt_shared_report(
            up,
            WindowsSharedInputKind::PointerUp(target),
        ));
        report.click_count = 1;
        report
    }

    fn dispatch_pointer_down(
        &mut self,
        point: crate::Point,
        shift: bool,
    ) -> WindowsWin32ViewInputDispatchReport {
        self.dispatch_pointer_down_with_button(
            point,
            crate::ZsPointerButton::Primary,
            crate::ZsPointerModifiers {
                shift,
                ..crate::ZsPointerModifiers::default()
            },
        )
    }

    fn dispatch_pointer_down_with_button(
        &mut self,
        point: crate::Point,
        button: crate::ZsPointerButton,
        modifiers: crate::ZsPointerModifiers,
    ) -> WindowsWin32ViewInputDispatchReport {
        let target = self.shared_target_at(point);
        let report = self
            .shared_runtime
            .dispatch_pointer_down_with_button(point, button, modifiers);
        self.adapt_shared_report(report, WindowsSharedInputKind::PointerDown(target))
    }

    fn dispatch_pointer_move(
        &mut self,
        point: crate::Point,
    ) -> WindowsWin32ViewInputDispatchReport {
        self.dispatch_pointer_move_with_modifiers(point, crate::ZsPointerModifiers::default())
    }

    fn dispatch_pointer_move_with_modifiers(
        &mut self,
        point: crate::Point,
        modifiers: crate::ZsPointerModifiers,
    ) -> WindowsWin32ViewInputDispatchReport {
        let report = self
            .shared_runtime
            .dispatch_pointer_move_with_modifiers(point, modifiers);
        #[cfg(feature = "resize")]
        self.update_resize_cursor(point);
        self.adapt_shared_report(report, WindowsSharedInputKind::PointerMove)
    }

    #[allow(dead_code)]
    fn dispatch_pointer_move_at(
        &mut self,
        point: crate::Point,
        now: std::time::Instant,
    ) -> WindowsWin32ViewInputDispatchReport {
        let report = self.shared_runtime.dispatch_pointer_move_at(point, now);
        self.adapt_shared_report(report, WindowsSharedInputKind::PointerMove)
    }

    fn dispatch_pointer_up(
        &mut self,
        point: crate::Point,
    ) -> WindowsWin32ViewInputDispatchReport {
        self.dispatch_pointer_up_with_button(
            point,
            crate::ZsPointerButton::Primary,
            crate::ZsPointerModifiers::default(),
        )
    }

    fn dispatch_pointer_up_with_button(
        &mut self,
        point: crate::Point,
        button: crate::ZsPointerButton,
        modifiers: crate::ZsPointerModifiers,
    ) -> WindowsWin32ViewInputDispatchReport {
        let target = self.shared_target_at(point);
        let report = self
            .shared_runtime
            .dispatch_pointer_up_with_button(point, button, modifiers);
        self.adapt_shared_report(report, WindowsSharedInputKind::PointerUp(target))
    }

    #[cfg(feature = "resize")]
    fn update_resize_cursor(&self, point: crate::Point) {
        use windows_sys::Win32::UI::WindowsAndMessaging::{SetCursor, IDC_SIZENS, IDC_SIZEWE};
        let cursor = match self.shared_runtime.resize_cursor_at(point) {
            Some(crate::ZsResizeAxis::Horizontal) => IDC_SIZEWE,
            Some(crate::ZsResizeAxis::Vertical) => IDC_SIZENS,
            None => IDC_ARROW,
        };
        unsafe { SetCursor(LoadCursorW(std::ptr::null_mut(), cursor)); }
    }

    fn cancel_pointer_drag(&mut self) -> WindowsWin32ViewInputDispatchReport {
        let report = self.shared_runtime.cancel_pointer_drag();
        self.adapt_shared_report(report, WindowsSharedInputKind::PointerCancel)
    }

    fn dispatch_pointer_leave(&mut self) -> WindowsWin32ViewInputDispatchReport {
        let report = self.shared_runtime.dispatch_pointer_leave();
        self.adapt_shared_report(report, WindowsSharedInputKind::PointerLeave)
    }

    fn dispatch_scroll(
        &mut self,
        point: crate::Point,
        delta: crate::Dp,
    ) -> WindowsWin32ViewInputDispatchReport {
        let report = self.shared_runtime.dispatch_pointer_scroll(point, delta);
        self.adapt_shared_report(report, WindowsSharedInputKind::Scroll)
    }
}

#[derive(Debug, Clone)]
pub struct WindowsWin32ShellInputRoute {
    runtime: ZsShellRuntime,
    events: Vec<ZsShellInteractionEvent>,
}

impl WindowsWin32ShellInputRoute {
    pub fn new(runtime: ZsShellRuntime) -> Self {
        Self {
            runtime,
            events: Vec::new(),
        }
    }

    pub fn runtime(&self) -> &ZsShellRuntime {
        &self.runtime
    }

    pub fn events(&self) -> &[ZsShellInteractionEvent] {
        &self.events
    }
}

pub fn set_windows_win32_window_shell_input_route(
    hwnd: HWND,
    mut route: WindowsWin32ShellInputRoute,
) -> bool {
    if hwnd.is_null() {
        return false;
    }
    if let Some((bounds, dpi)) = windows_win32_shell_surface(hwnd) {
        route.runtime.set_surface(bounds, dpi);
    }
    let plan = route.runtime.draw_plan();
    let hwnd_value = hwnd as isize;
    let mut routes = window_shell_input_routes()
        .lock()
        .expect("window shell input route registry should not be poisoned");
    if let Some(record) = routes.iter_mut().find(|record| record.hwnd == hwnd_value) {
        record.route = route;
    } else {
        routes.push(WindowsWindowShellInputRouteRecord {
            hwnd: hwnd_value,
            route,
        });
    }
    drop(routes);
    set_windows_win32_window_draw_plan(hwnd, plan);
    unsafe {
        InvalidateRect(hwnd, null(), 0);
    }
    true
}

pub fn clear_windows_win32_window_shell_input_route(hwnd: HWND) {
    if hwnd.is_null() {
        return;
    }
    let hwnd = hwnd as isize;
    let mut routes = window_shell_input_routes()
        .lock()
        .expect("window shell input route registry should not be poisoned");
    routes.retain(|record| record.hwnd != hwnd);
}

pub fn clear_windows_win32_window_shell_input_routes() {
    let mut routes = window_shell_input_routes()
        .lock()
        .expect("window shell input route registry should not be poisoned");
    routes.clear();
}

pub fn windows_win32_window_shell_input_events(hwnd: HWND) -> Option<Vec<ZsShellInteractionEvent>> {
    if hwnd.is_null() {
        return None;
    }
    let hwnd = hwnd as isize;
    let routes = window_shell_input_routes()
        .lock()
        .expect("window shell input route registry should not be poisoned");
    routes
        .iter()
        .find(|record| record.hwnd == hwnd)
        .map(|record| record.route.events.clone())
}

pub fn dispatch_windows_win32_window_shell_pointer_move(
    hwnd: HWND,
    point: crate::Point,
) -> Option<ZsShellInteractionUpdate> {
    track_windows_win32_shell_pointer_leave(hwnd);
    dispatch_windows_win32_window_shell_update(hwnd, |runtime| runtime.pointer_move(point))
}

pub fn dispatch_windows_win32_window_shell_pointer_leave(
    hwnd: HWND,
) -> Option<ZsShellInteractionUpdate> {
    dispatch_windows_win32_window_shell_update(hwnd, ZsShellRuntime::pointer_leave)
}

pub fn dispatch_windows_win32_window_shell_pointer_down(
    hwnd: HWND,
    point: crate::Point,
) -> Option<ZsShellInteractionUpdate> {
    dispatch_windows_win32_window_shell_update(hwnd, |runtime| runtime.pointer_down(point))
}

pub fn dispatch_windows_win32_window_shell_pointer_up(
    hwnd: HWND,
) -> Option<ZsShellInteractionUpdate> {
    dispatch_windows_win32_window_shell_update(hwnd, ZsShellRuntime::pointer_up)
}

pub fn dispatch_windows_win32_window_shell_pointer_cancel(
    hwnd: HWND,
) -> Option<ZsShellInteractionUpdate> {
    dispatch_windows_win32_window_shell_update(hwnd, ZsShellRuntime::pointer_cancel)
}

pub fn dispatch_windows_win32_window_shell_scroll(
    hwnd: HWND,
    delta_y: i32,
) -> Option<ZsShellInteractionUpdate> {
    dispatch_windows_win32_window_shell_update(hwnd, |runtime| runtime.scroll_by(delta_y))
}

pub fn refresh_windows_win32_window_shell_surface(hwnd: HWND) -> Option<ZsShellInteractionUpdate> {
    dispatch_windows_win32_window_shell_update(hwnd, |_| ZsShellInteractionUpdate::default())
}

fn dispatch_windows_win32_window_shell_update(
    hwnd: HWND,
    update: impl FnOnce(&mut ZsShellRuntime) -> ZsShellInteractionUpdate,
) -> Option<ZsShellInteractionUpdate> {
    if hwnd.is_null() {
        return None;
    }
    let surface = windows_win32_shell_surface(hwnd);
    let hwnd_value = hwnd as isize;
    let (result, plan) = {
        let mut routes = window_shell_input_routes()
            .lock()
            .expect("window shell input route registry should not be poisoned");
        let record = routes.iter_mut().find(|record| record.hwnd == hwnd_value)?;
        let surface_changed = surface
            .map(|(bounds, dpi)| record.route.runtime.set_surface(bounds, dpi))
            .unwrap_or(false);
        let mut result = update(&mut record.route.runtime);
        if surface_changed {
            result.redraw = true;
        }
        record.route.events.extend(result.events.iter().cloned());
        let plan = result.redraw.then(|| record.route.runtime.draw_plan());
        (result, plan)
    };

    if let Some(plan) = plan {
        set_windows_win32_window_draw_plan(hwnd, plan);
        unsafe {
            InvalidateRect(hwnd, null(), 0);
        }
    }
    Some(result)
}

fn window_shell_input_routes() -> &'static Mutex<Vec<WindowsWindowShellInputRouteRecord>> {
    WINDOW_SHELL_INPUT_ROUTES.get_or_init(|| Mutex::new(Vec::new()))
}

fn windows_win32_shell_surface(hwnd: HWND) -> Option<(crate::Rect, crate::Dpi)> {
    let mut rect: RECT = unsafe { zeroed() };
    if unsafe { GetClientRect(hwnd, &mut rect) } == 0 {
        return None;
    }
    let dpi = unsafe { GetDpiForWindow(hwnd) }.max(96) as f32;
    Some((rect_from_win(rect), crate::Dpi(dpi)))
}

fn track_windows_win32_shell_pointer_leave(hwnd: HWND) {
    if hwnd.is_null() {
        return;
    }
    let mut event = TRACKMOUSEEVENT {
        cbSize: size_of::<TRACKMOUSEEVENT>() as u32,
        dwFlags: TME_LEAVE,
        hwndTrack: hwnd,
        dwHoverTime: HOVER_DEFAULT,
    };
    unsafe {
        TrackMouseEvent(&mut event);
    }
}