quadraui 0.6.0

Cross-platform Rust UI toolkit: write one AppLogic and run it unmodified on TUI, GTK4 (Linux), macOS, and Windows backends
Documentation
//! Shared CoreGraphics FFI bindings and small paint helpers used by
//! (most of) the per-primitive rasterisers in [`super`].
//!
//! This module is the single source of truth for CoreGraphics entry
//! points (save/restore state, clip, set fill/stroke colour, fill/stroke
//! a rect) and paint helpers that were previously copy-pasted across
//! multiple files. Per-file copies are deleted in favour of a glob import
//! (`use super::cg::*;`) so call sites remain unchanged.
//!
//! Not every `src/macos/` file that touches CoreGraphics draws through
//! here — `backend.rs`, `board.rs`, `headless.rs`, `image.rs`,
//! `pipeline_view.rs`, `text.rs`, `toolbar.rs`, and a handful of other
//! small rasterisers still carry their own copies of some or all of this
//! (some also need extra entry points — `CGContextTranslateCTM`,
//! path-construction for rounded rects, image drawing, font/text
//! layout — that this module intentionally does not take on).
//!
//! ## Note: divergence in `fill_rect` implementations
//!
//! `macos/minimap.rs`'s `fill_rect` early-returns on `w <= 0.0 || h <=
//! 0.0`, which the other implementations did not. This module's
//! [`fill_rect`] matches the majority shape (unguarded). [`fill_rect_alpha`]
//! does carry that guard because both of its source copies agreed on it.

use core_graphics::geometry::{CGPoint, CGRect, CGSize};
use core_graphics::sys::CGContextRef;

use crate::types::Color;

extern "C" {
    pub(crate) fn CGContextSaveGState(c: CGContextRef);
    pub(crate) fn CGContextRestoreGState(c: CGContextRef);
    pub(crate) fn CGContextClipToRect(c: CGContextRef, rect: CGRect);
    pub(crate) fn CGContextSetRGBFillColor(
        c: CGContextRef,
        red: core_graphics::base::CGFloat,
        green: core_graphics::base::CGFloat,
        blue: core_graphics::base::CGFloat,
        alpha: core_graphics::base::CGFloat,
    );
    pub(crate) fn CGContextFillRect(c: CGContextRef, rect: CGRect);
}

/// Build a `CGRect` from `(x, y, w, h)` — replaces the `CGRectExt::new_xywh`
/// trait shim that used to be copy-pasted into 11 files.
pub(crate) fn rect(x: f64, y: f64, w: f64, h: f64) -> CGRect {
    CGRect::new(&CGPoint::new(x, y), &CGSize::new(w, h))
}

/// Decompose a [`Color`] into the `(r, g, b, a)` `f64` tuple
/// `CGContextSetRGB{Fill,Stroke}Color` expects, each channel scaled to
/// `0.0..=1.0`.
pub(crate) fn color_to_cg(c: Color) -> (f64, f64, f64, f64) {
    (
        c.r as f64 / 255.0,
        c.g as f64 / 255.0,
        c.b as f64 / 255.0,
        c.a as f64 / 255.0,
    )
}

/// Fill the rect `(x, y, w, h)` with `c` (using `c`'s own alpha).
///
/// # Safety
///
/// `ctx` must be a valid `CGContextRef` borrowed for the duration of
/// the call.
pub(crate) unsafe fn fill_rect(ctx: CGContextRef, x: f64, y: f64, w: f64, h: f64, c: Color) {
    let (r, g, b, a) = color_to_cg(c);
    CGContextSetRGBFillColor(ctx, r, g, b, a);
    CGContextFillRect(ctx, rect(x, y, w, h));
}

/// Like [`fill_rect`], but the alpha channel is `alpha` instead of the
/// colour's own — mirrors GTK's `cr.set_source_rgba(r, g, b, alpha)`
/// for selection/overlay tints, where the opacity is carried separately
/// from the colour's own (always-opaque) `a` channel.
///
/// # Safety
///
/// `ctx` must be a valid `CGContextRef` borrowed for the duration of
/// the call.
pub(crate) unsafe fn fill_rect_alpha(
    ctx: CGContextRef,
    x: f64,
    y: f64,
    w: f64,
    h: f64,
    c: Color,
    alpha: f64,
) {
    if w <= 0.0 || h <= 0.0 {
        return;
    }
    let (r, g, b, _) = color_to_cg(c);
    CGContextSetRGBFillColor(ctx, r, g, b, alpha);
    CGContextFillRect(ctx, rect(x, y, w, h));
}