Generated reference › API — ICoreEssentials/UI/Painting
kind: generated#api#icoreessentials-ui-painting

API — ICoreEssentials/UI/Painting

The public contract of 12 header(s) under ICoreEssentials/UI/Painting — 10 class/struct definition(s), 181 declaration(s). Each section shows the header's banner and its public (and protected-virtual) surface exactly as the file writes it.

ICoreBrush.h#

ICoreEssentials/UI/Painting/ICoreBrush.h

ICoreBrush#

ICoreBrush.h:27 · class · 13 declaration(s)

class ICoreBrush {
public:
    // Default is Qt::NoBrush -- "no fill". Fifteen call sites spell it as a
    // bare ICoreBrush() to turn filling off before stroking an outline, so it
    // is the single most-used constructor here.
    ICoreBrush();

    ICoreBrush(const ICoreColor& color);
    ICoreBrush(const ICoreLinearGradient& gradient);
    ICoreBrush(const ICoreRadialGradient& gradient);

    ICoreBrush(const ICoreBrush& other);
    ICoreBrush(ICoreBrush&& other) noexcept;
    ICoreBrush& operator=(const ICoreBrush& other);
    ICoreBrush& operator=(ICoreBrush&& other) noexcept;
    ~ICoreBrush();

    // ------------------------------------------------------------------
    //  Reading a brush back. Same story as ICorePen's: four constructors and
    //  no accessors at all, which was invisible while every consumer was a
    //  painter unwrapping the native object through the seam below. A
    //  destination that is not a painter can see that a brush arrived and not
    //  what is in it.
    // ------------------------------------------------------------------

    // The flat colour. Meaningless for a gradient brush -- ask isGradient()
    // first, because a gradient's colour reads as a plausible flat one and
    // filling with it is a wrong answer that looks like a right one.
    [[nodiscard]] ICoreColor color() const;

    // True for a gradient fill of either kind. A consumer that cannot express
    // gradients reports the loss instead of inventing a flat fill.
    [[nodiscard]] bool isGradient() const;

    // False for the default brush, which is "no fill" rather than a colour --
    // fifteen call sites spell a bare ICoreBrush() to turn filling OFF, so a
    // consumer treating the default as black fills fifteen shapes that should
    // have been left alone.
    [[nodiscard]] bool isFilled() const;

    // The seam, per §1. Call icoreQt() from a .cpp rather than reaching in.
    const void* nativeStorage() const;

    // Public so the .cpp can pin them; §7 measured QBrush at 8 bytes (plain
    // d-pointer) against this tree's Qt 6.10.2 on macOS arm64.
    static constexpr std::size_t kNativeStorageSize = 8;
    static constexpr std::size_t kNativeStorageAlign = 8;

};

ICoreGradient.h#

ICoreEssentials/UI/Painting/ICoreGradient.h

ICoreLinearGradient#

ICoreGradient.h:29 · class · 10 declaration(s)

The two gradient shapes the app paints with.

class ICoreLinearGradient {
public:
    ICoreLinearGradient();

    ICoreLinearGradient(const ICorePoint& start, const ICorePoint& stop);
    ICoreLinearGradient(double x1, double y1, double x2, double y2);

    ICoreLinearGradient(const ICoreLinearGradient& other);
    ICoreLinearGradient(ICoreLinearGradient&& other) noexcept;
    ICoreLinearGradient& operator=(const ICoreLinearGradient& other);
    ICoreLinearGradient& operator=(ICoreLinearGradient&& other) noexcept;
    ~ICoreLinearGradient();

    // ⚠ setStop is the ONLY spelling. The inherited QGradient::setColorAt used
    // to be reachable through the old base and three sites had drifted onto it;
    // they are migrated. Adding a setColorAt forwarder would have given the
    // tree two names for one operation -- the addition P0.5, P3.4, P7.3 and
    // P7.6's painter decision each refused.
    void setStop(double position, const ICoreColor& color);

    // The seam, per §1. Call icoreQt() from a .cpp rather than reaching in.
    const void* nativeStorage() const;

    // Public so the .cpp can pin them. See the buffer note above: 88, not 8.
    static constexpr std::size_t kNativeStorageSize = 88;
    static constexpr std::size_t kNativeStorageAlign = 8;

};

ICoreRadialGradient#

ICoreGradient.h:60 · class · 10 declaration(s)

class ICoreRadialGradient {
public:
    ICoreRadialGradient();

    ICoreRadialGradient(const ICorePoint& center, double radius);
    ICoreRadialGradient(double cx, double cy, double radius);

    ICoreRadialGradient(const ICoreRadialGradient& other);
    ICoreRadialGradient(ICoreRadialGradient&& other) noexcept;
    ICoreRadialGradient& operator=(const ICoreRadialGradient& other);
    ICoreRadialGradient& operator=(ICoreRadialGradient&& other) noexcept;
    ~ICoreRadialGradient();

    void setStop(double position, const ICoreColor& color);

    const void* nativeStorage() const;

    static constexpr std::size_t kNativeStorageSize = 88;
    static constexpr std::size_t kNativeStorageAlign = 8;

};

ICoreGradientDirection.h#

ICoreEssentials/UI/Painting/ICoreGradientDirection.h

File-scope declarations#

// Which way a surface gradient runs, named START corner first: the lit end is
// always the one named first, and the surface deepens toward the second.
// 
// ⚠ A HEADER OF ITS OWN, and deliberately so. ICoreWidget and ICoreToolBar both
// take this in a setter, and ICoreWidget.h is named by ~150 headers in this
// tree -- so it may not reach ICoreSurfaceGradient.h, whose return type drags
enum class ICoreGradientDirection {
    TopToBottom,
    BottomLeftToTopRight,
    BottomRightToTopLeft,
};

ICoreIconRenderer.h#

ICoreEssentials/UI/Painting/ICoreIconRenderer.h

ICoreIconRenderer#

ICoreIconRenderer.h:44 · class · pImpl · 7 declaration(s)

An icon drawn at the resolution it will REALLY be shown at, in one place.

class ICoreIconRenderer {
public:
    ICoreIconRenderer();
    ~ICoreIconRenderer();

    // Replaces the art, dropping any rasterization of the old.
    void setIcon(const ICoreIcon& icon);

    // ⚠⚠ THE SAME ART NAMED BY ITS SOURCE, WHICH IS WHAT MAKES THE PARAGRAPH
    // ABOVE TRUE ON THIS TOOLKIT.
    //
    // That paragraph describes a QIcon: it defers to a live QIconEngine, so
    // dropping the pixmap cache and asking again genuinely re-inks the glyph.
    // An ICoreIcon on any native seat is a finished RASTER
    // (ICoreThemedIconFactory.h: *"a holder must rebuild it on a theme
    // change"*), so re-asking it returns the same pixels it returned before and
    // draw()'s theme check bought nothing at all.
    //
    // Given the PATH, this renderer can do what that comment promised: rebuild
    // the icon itself, not merely its rasterization, the first time it draws
    // under a theme it has not drawn under.
    void setThemedIcon(const ICoreString& svgPath);

    // Whether there is art to draw -- the same question ICoreIcon::isNull
    // answers, so an owner does not have to keep the icon around beside this.
    [[nodiscard]] bool isNull() const;

    // Drop the cache. Not needed for a theme switch or a change of art (both
    // are handled), only for an owner recycling this for something new.
    void invalidate();

    // Rasterize at the scale `painter` really lands on, then draw the art
    // fitted to its own aspect and centred inside `box`, in the painter's
    // coordinates. A no-op for null art or an empty box.
    void draw(ICorePainter& painter, const ICoreRect& box);

private:
    class Impl;                    // the two-line residue; state lives here
    std::unique_ptr<Impl> impl;
};

ICoreNavItemStyle.h#

ICoreEssentials/UI/Painting/ICoreNavItemStyle.h

The app's nav-row hover/selection treatment, in one place.

A row lit by this reads the same wherever it lives: an accent wash over the resting surface, an accent hairline around it, and an accent edge growing out of the left side. It started on the left fixed panel's menu buttons (ICoreHBoxButton) and is now what every list-of-things-you-can-open wears — the settings panel's menu chooser included — so the two cannot drift apart by someone tuning one copy of the numbers.

progress is 0 at rest and 1 fully lit; a caller animating a hover passes the in-between values and the whole treatment fades with it. Nothing is drawn at 0, which is the point: a nav row stays invisible until the pointer reaches it.

Declares no class of its own — see the file.

ICorePaintRecorder.h#

ICoreEssentials/UI/Painting/ICorePaintRecorder.h

ICorePaintRecorder#

ICorePaintRecorder.h:55 · class · 40 declaration(s)

A destination for painting that is NOT a surface.

class ICorePaintRecorder {
public:
    virtual ~ICorePaintRecorder() = default;

    // ⚠ THE ONE METHOD AN IMPLEMENTATION MUST WRITE, because it is the one
    // whose absence is invisible. `what` is the painter method's name, a
    // literal with static storage duration. Count it, log it, refuse -- but
    // decide, because the default would be to lose the drawing in silence.
    virtual void unsupported(const char* what) = 0;

    // -- state ------------------------------------------------------------
    virtual void save();
    virtual void restore();
    virtual void setAntialiasing(bool);
    virtual void setTextAntialiasing(bool);
    virtual void setPen(const ICorePen&);
    virtual void setPenColor(const ICoreColor&);
    virtual void setNoPen();
    virtual void setBrush(const ICoreBrush&);
    virtual void setNoBrush();
    virtual void setOpacity(double);
    virtual void setFont(const ICoreFont&);

    // -- shapes -----------------------------------------------------------
    virtual void drawRect(const ICoreRect&);
    virtual void drawRoundedRect(const ICoreRect&, double, double);
    virtual void fillRect(const ICoreRect&, const ICoreBrush&);
    virtual void floodKeepingAlpha(const ICoreRect&, const ICoreColor&);
    virtual void drawEllipse(const ICoreRect&);
    virtual void drawEllipse(const ICorePoint&, double, double);
    virtual void drawLine(const ICoreLine&);
    virtual void drawLine(const ICorePoint&, const ICorePoint&);
    virtual void drawPolyline(const std::vector<ICorePoint>&);
    virtual void drawPolygon(const std::vector<ICorePoint>&);
    virtual void drawPath(const ICorePainterPath&);
    virtual void fillPath(const ICorePainterPath&, const ICoreBrush&);
    virtual void fillPathEvenOdd(const ICorePainterPath&, const ICoreBrush&);
    virtual void strokePath(const ICorePainterPath&, const ICorePen&);

    // -- text and images ---------------------------------------------------
    virtual void drawText(const ICorePoint&, const ICoreString&);
    virtual void drawText(const ICoreRect&, ICoreAlignment, const ICoreString&);
    virtual void drawWrappedText(const ICoreRect&, ICoreAlignment, const ICoreString&);
    virtual void drawRichText(const ICoreRect&, ICoreAlignment, const ICoreString&);
    virtual void drawPixmap(const ICorePoint&, const ICorePixmap&);
    virtual void drawPixmap(const ICoreRect&, const ICorePixmap&);

    // -- transform ---------------------------------------------------------
    virtual void translate(double, double);
    virtual void rotate(double);
    virtual void scale(double, double);

    // -- clip --------------------------------------------------------------
    virtual void setClipRect(const ICoreRect&);
    virtual void setClipPath(const ICorePainterPath&);
    virtual void setClipPathEvenOdd(const ICorePainterPath&);
    virtual void clearClip();
};
};

ICorePainter.h#

ICoreEssentials/UI/Painting/ICorePainter.h

ICorePainter#

ICorePainter.h:38 · class · pImpl · 51 declaration(s)

class ICorePainter {
public:
    // The SAME painter, constructed from the backend's drawing surface as an
    // opaque handle: a `QPainter*` on the Qt backend, an `ICoreWinUIPainter*`
    // on the WinUI one.
    //
    // ⚠ THIS EXISTS BECAUSE THE CONSTRUCTOR ABOVE NAMES A TOOLKIT TYPE, AND A
    // SECOND BACKEND THEREFORE CANNOT DEFINE IT. Every other Qt-typed entry
    // point in this tier is a CONVERSION that a non-Qt backend can simply leave
    // undefined -- ICorePixmap(const QPixmap&) and toImageArgb32() are, on both
    // AppKit and WinUI. A constructor cannot be left undefined and still leave
    // the class usable: with only ICorePainter(QPainter&) there is no way to
    // make an ICorePainter at all on a backend that has no QPainter, so the
    // whole painting tier is unreachable rather than merely narrower. (Found
    // while seating the WinUI painter; the AppKit board's A1.5 stops one step
    // earlier and has not met it yet.)
    //
    // ⚠ THE HANDLE IS UNCHECKED, AND IT IS void* ON PURPOSE. A
    // forward-declared per-backend type would either put a toolkit name in this
    // header or force every backend to define the same class, and this tree
    // already answers exactly this question once -- ICoreNativeHandle.h: "the
    // one thing a Qt-free header is allowed to say about the toolkit object
    // behind a wrapper: that it exists, and that it has an address". The cast
    // back lives in one .cpp per backend, as it does there.
    //
    // ⚠ WHAT A CLIENT MUST NOT DO WITH IT: pass a handle from a different
    // backend than the one it is linked against. Nothing can catch that -- the
    // types are gone by then -- so the only callers are the backend zone's own
    // widget base and its test suites, which is the same rule
    // ICoreNativeWidget's handles carry.
    explicit ICorePainter(ICoreNativeHandle nativePainter);
    // Construction from a native drawing surface, for backends whose surface
    // is not a QPainter.
    //
    // ⚠ THIS IS THE PEER OF THE CONSTRUCTOR ABOVE, NOT A NEW IDEA. This header
    // already carried a BACKEND-SPECIFIC constructor -- `QPainter&` -- which no
    // non-Qt backend can define, and which was therefore the one thing stopping
    // an AppKit or WinUI painter from existing at all. Rather than make the Qt
    // one generic (it has a dozen call sites, all inside `UI/Backends/Qt/`),
    // this adds its counterpart. Each backend defines exactly one of the two
    // and leaves the other undefined; an appkit build has no definition for the
    // QPainter form and nothing in an appkit build reaches for it, because
    // every one of its call sites is inside the Qt backend's own directory.
    //
    // `nativeSurface` is the backend's drawing context -- a `CGContextRef` on
    // AppKit. It is `void*` for the same reason `ICoreNativeHandle` is: naming
    // the type here is what the wrapper exists to avoid.
    //
    // ⚠ `surfaceHeight` IS NOT OPTIONAL ON A FLIPPED BACKEND, and it is a
    // CONSTRUCTOR argument rather than a setter on purpose. AppKit's device
    // space is y-up and every wrapper coordinate is y-down, so the context is
    // flipped once at construction -- and a flip needs to know how tall the
    // surface is. A painter built on the wrong height draws everything
    // mirrored, correctly, and silently. Pass the height of the surface being
    // drawn into, in points. A backend that does not flip may ignore it.
    ICorePainter(void* nativeSurface, double surfaceHeight);

    // ADOPT a painter the backend has ALREADY prepared over a surface -- no
    // second flip, no second transform, no ownership.
    //
    // ⚠ THIS IS THE EXACT PEER OF `ICorePainter(QPainter&)`, and it exists for
    // the same reason that one does. Qt's widget paint path opens a QPainter on
    // the widget and wraps it; AppKit's opens an ICoreAppKitPainter in
    // -drawRect: and must wrap THAT. Building a second painter over the same
    // context instead is the obvious move and is wrong: the constructor above
    // flips the CTM and the DESTRUCTOR DOES NOT UNDO IT, so a second painter
    // flips an already-flipped context and everything draws mirrored,
    // correctly, and silently -- the one failure mode a smoke test passes.
    //
    // Adopting also inherits whatever coordinate setup the backend's own paint
    // path established -- the composition offsets a nested view carries, which
    // this side has no way to know. Reproducing that here would be copying a
    // decision instead of using it.
    //
    // ⚠ THE PAINTER IS BORROWED AND MUST OUTLIVE THIS OBJECT. Same contract as
    // the QPainter form, which is a reference member for the same reason: a
    // paint hook's painter is valid for the call and no longer.
    //
    // ⚠ NAMING A BACKEND CLASS HERE IS NOT A BOUNDARY BREAK. ICoreAppKitPainter
    // is one of THIS tree's classes, not a toolkit type -- the AppKit boundary
    // guard forbids NS/CG/CT/CA names, `#import` and `@interface`, none of
    // which this is. It is a forward
    // declaration, so no non-Apple build ever needs the type to exist.
    explicit ICorePainter(ICoreAppKitPainter& prepared);

    // RECORD every drawing call instead of performing one. No surface is
    // opened and nothing is rasterised: each call below is forwarded to the
    // recorder, which decides what it means.
    //
    // ⚠ THIS IS THE PEER OF THE TWO CONSTRUCTORS ABOVE and takes the recorder
    // the same way -- BORROWED, and it must outlive this object. Same contract,
    // same warning, one concept rather than two.
    //
    // ⚠ WHAT IT IS FOR, so nobody reaches for it as a curiosity: turning a
    // drawing into VECTOR OUTPUT on a backend whose toolkit cannot be handed a
    // paint device that writes it. One toolkit offers a generator that IS a
    // paint device, so the drawing is simply performed into it; the other emits
    // a different page description entirely, and the only way to get the shapes
    // out is to intercept the calls. See ICorePaintRecorder.h.
    //
    // ⚠ THE FOUR QUERIES ARE ANSWERED HERE, NOT BY THE RECORDER. font(),
    // opacity(), fontMetrics() and deviceScale() must return something whether
    // or not a destination can express them, so a recording painter keeps that
    // much state of its own and never asks. A recorder is a place calls GO, not
    // a thing that answers questions -- giving it queries would make it a
    // second painter that could disagree with this one.
    //
    // ⚠ AND ONE MEMBER CANNOT WORK IN THIS MODE: qt(), which hands out the
    // toolkit's own painter. There is no such painter here. It reports itself
    // through the recorder and returns an inactive one rather than a dangling
    // reference; a caller that needs it is asking to bypass this wrapper, which
    // is the one thing a recording destination cannot support.
    explicit ICorePainter(ICorePaintRecorder& recorder);

    ~ICorePainter();

    ICorePainter(const ICorePainter&) = delete;
    ICorePainter& operator=(const ICorePainter&) = delete;

    void save();
    void restore();

    void setAntialiasing(bool on);

    // Whether TEXT is antialiased -- separate from setAntialiasing because
    // the toolkit tracks it as its own hint (on by default there, so most
    // callers never touch this; the print path sets it explicitly). Note
    // setAntialiasing already carries SmoothPixmapTransform with it, so
    // there is no separate pixmap-smoothing verb.
    void setTextAntialiasing(bool on);

    void setPen(const ICorePen& pen);
    void setPenColor(const ICoreColor& color);
    void setNoPen();

    void setBrush(const ICoreBrush& brush);
    void setNoBrush();

    void setOpacity(double opacity);
    double opacity() const;

    void setFont(const ICoreFont& font);
    ICoreFont font() const;
    // The scale at which this painter's output actually lands on screen, per
    // axis -- the view's zoom and any item transform folded together with the
    // device pixel ratio.
    //
    // Exists so a renderer can rasterize vector art at the size it will REALLY
    // be shown at, instead of a fixed guess that is blurry when zoomed in and
    // wasteful when zoomed out. It replaces reaching for worldTransform() and
    // device()->devicePixelRatioF() at the call site, which were the only two
    // toolkit escapes left in the scene tier's painting code.
    [[nodiscard]] ICoreSizeF deviceScale() const;

    ICoreFontMetrics fontMetrics() const;

    void drawRect(const ICoreRect& rect);
    void drawRoundedRect(const ICoreRect& rect, double radiusX, double radiusY);
    void fillRect(const ICoreRect& rect, const ICoreBrush& brush);

    // Replace the ink already drawn in `box` with `tint`, keeping its alpha --
    // the "tint a glyph" operation. What is on the device keeps its shape and
    // its soft edges; only its colour changes.
    //
    // ⚠ THIS IS DELIBERATELY A NAMED OPERATION AND NOT A COMPOSITION-MODE
    // SETTER (P7.1, decided by the owner 2026-08-11). Underneath it is
    // CompositionMode_SourceIn + fillRect, which was written out by hand at
    // three sites; the toolkit publishes ~30 composition modes and this tree
    // uses exactly ONE, so an ICoreCompositionMode enum would put 30 knobs
    // across the boundary to serve one idiom. §9's rule -- name the operation,
    // not the toolkit knob -- and the same reading that rejected
    // ICorePointerShape (P0.5) and runOnce() (P3.4).
    //
    // ⚠ It leaves the composition mode where it found it. The three call sites
    // all ended their painter immediately afterwards, so none of them observed
    // the mode again -- but a caller that keeps painting must not inherit
    // SourceIn from a call that reads like a fill.
    void floodKeepingAlpha(const ICoreRect& box, const ICoreColor& tint);
    void drawEllipse(const ICoreRect& rect);
    void drawEllipse(const ICorePoint& center, double radiusX, double radiusY);

    void drawLine(const ICoreLine& line);
    void drawLine(const ICorePoint& p1, const ICorePoint& p2);
    void drawPolyline(const std::vector<ICorePoint>& points);
    void drawPolygon(const std::vector<ICorePoint>& points);

    void drawPath(const ICorePainterPath& path);
    void fillPath(const ICorePainterPath& path, const ICoreBrush& brush);

    // The same fill under the EVEN-ODD rule instead of nonzero.
    //
    // ⚠ THE RULE BELONGS TO THE PATH'S WINDING, NOT TO THE PAINT, which is why
    // it is a parameter here and not a property of ICoreBrush. A brush that
    // carried a fill rule would fill two different shapes depending on where
    // it came from, and a caller reusing one brush for a ring and a blob would
    // get the wrong one silently.
    //
    // ⚠ THE TWO RULES DIFFER ONLY WHERE A PATH ENCLOSES ITSELF -- a ring, a
    // counter, the hole in a letter. Nonzero fills such a hole SOLID and
    // reports nothing, so the failure looks like a design choice rather than a
    // wrong answer. Measured need: 16 fills across 12 of this tree's own icons
    // ask for even-odd, against 2 that ask for nonzero.
    //
    // An OVERLOAD rather than a defaulted parameter: nothing about the
    // existing signature changes, so no call site anywhere has to move and the
    // two spellings cannot be confused at a glance.
    void fillPathEvenOdd(const ICorePainterPath& path, const ICoreBrush& brush);
    void strokePath(const ICorePainterPath& path, const ICorePen& pen);

    // Text at a baseline point, or laid out in a rect under ICoreAlignment
    // flags (or-able, e.g. Left | VCenter).
    void drawText(const ICorePoint& baseline, const ICoreString& text);
    void drawText(const ICoreRect& rect, ICoreAlignment alignment, const ICoreString& text);

    // Same, but breaking the text across lines at word boundaries to fit the
    // rect's width. A separate method rather than a flag on drawText because
    // wrapping is the whole difference: the single-line form deliberately
    // clips, and a caller that wants one behaviour never wants the other by
    // accident.
    void drawWrappedText(const ICoreRect& rect, ICoreAlignment alignment, const ICoreString& text);

    // RICH text -- markup laid out in the rect, in as many fonts, weights and
    // sizes as the markup asks for. The three calls above draw a run in ONE
    // font and one colour; this is the one that does not.
    //
    // ⚠ IT TAKES MARKUP, NOT A RUN STACK, AND THAT IS THE MEASUREMENT RATHER
    // THAN A PREFERENCE. Both backends already carry a complete HTML reader --
    // QTextDocument on one, NSAttributedString's NSHTMLTextDocumentType on the
    // other -- so a neutral run model would mean writing a THIRD HTML parser in
    // portable code to feed two parsers that already exist. The corpus this
    // exists for is 311 block descriptions using exactly ten tags: p, b, i, h3,
    // li, ul, sub, code, sup, ol. Both readers handle all ten, and none of the
    // 311 carries an inline <svg> or an <img>.
    //
    // ⚠ THE PAINTER'S CURRENT FONT AND PEN COLOUR ARE THE DOCUMENT'S DEFAULTS,
    // which is what makes this drop into a call site that used to draw plain
    // text: markup that says nothing about a face or a colour inherits the one
    // the caller already set, exactly as drawText would have.
    //
    // ⚠ MEASURE WITH icoreRichTextSize BELOW THIS CLASS, NEVER BY GUESSING
    // FROM LINE COUNTS. A rich document's height is not lines x lineSpacing --
    // a heading and a list item are different heights -- so the plain-text
    // arithmetic the graphics text tier uses is wrong here by construction. The
    // two calls are seated from the same reader per backend, so they cannot
    // disagree about where the text ends.
    void drawRichText(const ICoreRect& rect, ICoreAlignment alignment, const ICoreString& html);

    void drawPixmap(const ICorePoint& topLeft, const ICorePixmap& pixmap);
    void drawPixmap(const ICoreRect& target, const ICorePixmap& pixmap);

    void translate(double dx, double dy);
    void rotate(double degrees);
    void scale(double sx, double sy);

    void setClipRect(const ICoreRect& rect);
    void setClipPath(const ICorePainterPath& path);

    // NARROW the clip to `path` instead of replacing it -- the region that
    // survives is the overlap of this path with whatever was clipped already.
    //
    // ⚠ THIS IS THE ONE A NESTED DRAW WANTS, and setClipPath is the one that
    // silently is not. A routine handed a painter cannot see the clip it was
    // given (there is no getter, deliberately), so setClipPath's replace makes
    // it paint outside a boundary its caller had already set -- and only when
    // the caller happened to set one, which is how it survives every test that
    // draws into a bare painter. Added for the SVG mask path (W0.8), where an
    // icon drawn into a clipped cell would otherwise spill across the cell's
    // edge exactly when a mask was involved.
    void intersectClipPath(const ICorePainterPath& path);

    // The same clip under the EVEN-ODD rule.
    //
    // ⚠ WHAT THIS IS ACTUALLY FOR, so nobody reaches for it as a curiosity: a
    // BINARY MASK -- one made only of opaque light and dark shapes -- IS a
    // clip. The light region keeps, the dark region cuts, and with the dark
    // shapes nested inside the light one that is exactly what an even-odd clip
    // over all of them describes. Without this, a masked drawing has to be
    // composited through an offscreen luminance buffer, which is a much larger
    // capability for a case that does not need it.
    //
    // ⚠ IT IS NOT A GENERAL MASK. A clip is a hard edge: it cannot express a
    // mask with a gradient, a partial alpha or a soft border. A caller must
    // establish that its mask is binary before using this, and do the real
    // thing otherwise.
    void setClipPathEvenOdd(const ICorePainterPath& path);
    void clearClip();

    // The backend's own painting object, as an opaque address, for wrapper-zone
    // code composing with toolkit APIs directly. A `QPainter*` on the Qt
    // backend; unnameable by clients, which is the point.
    //
    // ⚠ THIS USED TO BE `QPainter& qt()` AND THE CHANGE IS A SEAL, NOT A
    // WIDENING (A9.23). Call `icoreQt(painter)` from a .cpp in the backend
    // zone rather than casting here; that overload is the one place that knows
    // what this address points at, exactly as A9.5's `void* nativeState()`
    // does for ICoreRegex.
    //
    // ⚠ A RECORDING PAINTER HAS NO SUCH OBJECT and never had: it reports the
    // loss and answers an inactive instance rather than a dangling one. A
    // caller reaching for this is asking to bypass the wrapper, which is what
    // a non-surface destination cannot support.
    void* nativeState();

private:
    class Impl;                    // the two-line residue; state lives here
    std::unique_ptr<Impl> impl;
};

ICorePainterPath.h#

ICoreEssentials/UI/Painting/ICorePainterPath.h

ICorePathElement#

ICorePainterPath.h:59 · struct · 0 declaration(s)

struct ICorePathElement {
public:
    ICorePathElementKind kind = ICorePathElementKind::MoveTo;

    // The element's END point, for all three kinds.
    ICorePoint point;

    // CubicTo only; both are (0, 0) otherwise, which is not a position.
    ICorePoint control1;
    ICorePoint control2;
};
};

ICorePainterPath#

ICorePainterPath.h:70 · class · 21 declaration(s)

class ICorePainterPath {
public:
    ICorePainterPath();

    ICorePainterPath(const ICorePainterPath& other);
    ICorePainterPath(ICorePainterPath&& other) noexcept;
    ICorePainterPath& operator=(const ICorePainterPath& other);
    ICorePainterPath& operator=(ICorePainterPath&& other) noexcept;
    ~ICorePainterPath();

    void moveTo(const ICorePoint& p);
    void lineTo(const ICorePoint& p);
    void cubicTo(const ICorePoint& c1, const ICorePoint& c2, const ICorePoint& end);

    void addRect(const ICoreRect& rect);
    void addRoundedRect(const ICoreRect& rect, double radiusX, double radiusY);
    void addEllipse(const ICoreRect& rect);
    void arcTo(const ICoreRect& rect, double startAngle, double sweepLength);

    void closeSubpath();

    // ⚠ The DEFAULT here is EvenOdd, because that is what the toolkit under it
    // defaults to and changing it silently would move every existing fill in
    // the tree. Vector formats default the other way (SVG's initial fill-rule
    // is nonzero), so a caller converting art must set this explicitly rather
    // than inherit it.
    void setFillRule(ICoreFillRule rule);
    [[nodiscard]] ICoreFillRule fillRule() const;
    // ------------------------------------------------------------------
    //  Reading the path back.
    //
    //  ⚠ THE THIRD WRITE-ONLY VALUE TYPE IN THIS TIER, and they were all found
    //  the same way. ICorePen, ICoreBrush and this class between them carried
    //  more than twenty mutators and not one accessor, which stayed invisible
    //  for as long as every consumer was a PAINTER -- a painter unwraps the
    //  native object through the storage seam and asks the toolkit. The moment
    //  a consumer is not a painter, there is nothing to ask.
    // ------------------------------------------------------------------
    [[nodiscard]] std::vector<ICorePathElement> elements() const;
    [[nodiscard]] bool isEmpty() const;

    // The union of this path and `other`, as a separate subpath -- which is
    // what keeps rounded corners intact where the two join. Returns an
    // ICorePainterPath rather than the toolkit's type on purpose: its one
    // caller writes `shape = shape.united(tail)`, and before the conversion
    // that assignment only compiled because a QPainterPath converted back
    // implicitly through a constructor this class no longer has.
    ICorePainterPath united(const ICorePainterPath& other) const;

    // This path widened into a fillable outline `width` units across, with
    // ROUND caps and joins. Added for P2.8's last blocked site:
    // ICoreLinkBranchSegment::contentShape() widens its one-pixel line so a
    // thin link stays clickable as the canvas zooms, and after this class
    // converted there was no way to express that -- no QPainterPath in, and
    // no stroking operation out.
    //
    // ⚠ Deliberately NOT strokedCopy(width, cap, join). Cap and join would
    // have to cross this boundary as enums, and the single caller in the tree
    // wants round for both. §9's rule -- the API is what the call sites use
    // and nothing else. Add the general form when a second caller needs a
    // second shape, not before.
    [[nodiscard]] ICorePainterPath strokedRound(double width) const;

    // The seam, per §1 -- void* rather than a forward-declared QPainterPath*,
    // which would put the toolkit name back in this header. Call icoreQt()
    // from a .cpp rather than reaching in here.
    const void* nativeStorage() const;

    // Public so the .cpp can pin them; §7 measured QPainterPath at 8 bytes
    // (plain d-pointer) against this tree's Qt 6.10.2 on macOS arm64.
    static constexpr std::size_t kNativeStorageSize = 8;
    static constexpr std::size_t kNativeStorageAlign = 8;

};

File-scope declarations#

// An outline to fill, stroke or clip by.
// 
// ⚠ CONVERTED (P7.4). No QPainterPath base and no Qt name in this header; the
// toolkit object lives by value in the buffer below. See ICoreKeySequence.h
// for why the buffer rather than a heap Impl, and ICoreKeySequence.cpp for the
// static_asserts that keep buffer and type from drifting apart.
enum class ICoreFillRule {
    NonZero = 0,
    EvenOdd = 1,
};

// What a path is made of, when something needs to READ one back.
// 
// ⚠ THREE KINDS, AND THERE IS DELIBERATELY NO `Close`. Measured on this tree's
// toolkit: `closeSubpath()` and an explicit `lineTo` back to the subpath's
// start produce BYTE-IDENTICAL element lists -- four elements, all MoveTo/
// LineTo, with nothing marking one as closed. The information is not hidden,
enum class ICorePathElementKind {
    MoveTo,
    LineTo,
    CubicTo,
};

ICorePen.h#

ICoreEssentials/UI/Painting/ICorePen.h

ICorePen#

ICorePen.h:28 · class · 23 declaration(s)

class ICorePen {
public:
    // ⚠ BLACK, SOLID, WIDTH 1 -- AND THIS COMMENT SAID 0 UNTIL IT WAS MEASURED.
    // The toolkit's own default is `widthF=1, cosmetic=false`, checked against
    // this tree's version rather than recalled. The wrong figure had already
    // propagated: a second backend read this line and built a COSMETIC HAIRLINE
    // for its default pen, which is identical at 1:1 and diverges the moment
    // anything is zoomed. Width 0 does mean a cosmetic hairline -- it is simply
    // not the default.
    ICorePen();

    ICorePen(const ICoreColor& color);
    ICorePen(const ICoreColor& color, double width);

    // Added by P7.4 when ICoreBrush converted; its one call site is
    // ICoreNotificationCenterMessageBox's rim.
    ICorePen(const ICoreBrush& brush, double width);

    ICorePen(const ICorePen& other);
    ICorePen(ICorePen&& other) noexcept;
    ICorePen& operator=(const ICorePen& other);
    ICorePen& operator=(ICorePen&& other) noexcept;
    ~ICorePen();

    void setStyle(ICorePenStyle style);
    void setCap(ICorePenCap cap);

    // Surfaced by the conversion, like setCosmetic below: the link/port paint
    // code re-colours and re-widths a pen it built once, and used to reach
    // QPen::setColor / setWidthF through the base. Width is a double and maps
    // to the toolkit's floating setWidthF -- the integer setWidth truncates.
    void setColor(const ICoreColor& color);
    void setWidth(double width);

    // Added by P5.3 because a call site needed it -- ICoreComboBoxArrows'
    // round join. All 9 join sites now resolve here; the Qt-base route they
    // used to take no longer exists.
    void setJoin(ICorePenJoin join);

    // A cosmetic pen keeps its width under view transforms -- the canvas
    // origin anchor's crosshair is the consumer. Surfaced by the conversion:
    // its 3 sites used to reach QPen::setCosmetic through the base.
    void setCosmetic(bool cosmetic);

    // ------------------------------------------------------------------
    //  Reading a pen back.
    //
    //  ⚠ THIS CLASS WAS WRITE-ONLY UNTIL NOW -- six setters, four
    //  constructors and no way to ask it anything -- and nobody noticed
    //  because every consumer was a PAINTER, which unwraps the native object
    //  through the seam below and reads the toolkit's own accessors. The
    //  moment a consumer is not a painter, there is nothing to read: a
    //  destination handed setPen() can see that a pen arrived and not what is
    //  in it. Found by the vector-art writer, which has to turn a pen into
    //  stroke attributes and had no way to.
    //
    //  Backend code should still go through the seam; these exist for the
    //  layers that may not name a toolkit type at all.
    // ------------------------------------------------------------------

    // ⚠ A PEN HOLDS A BRUSH, NOT A COLOUR -- ICorePen(const ICoreBrush&,
    // double) exists and one call site strokes a rim with a gradient. This is
    // that brush's colour, which for a gradient pen is not the whole story:
    // ask hasGradient() before believing it, exactly as with ICoreBrush.
    [[nodiscard]] ICoreColor color() const;

    // True when the pen strokes with a gradient rather than a flat colour. A
    // consumer that cannot express one must report the loss rather than
    // stroke in color(), which would be a plausible wrong answer.
    [[nodiscard]] bool hasGradient() const;

    [[nodiscard]] double width() const;
    [[nodiscard]] ICorePenStyle style() const;
    [[nodiscard]] ICorePenCap cap() const;
    [[nodiscard]] ICorePenJoin join() const;
    [[nodiscard]] bool isCosmetic() const;

    // The seam, per §1. Call icoreQt() from a .cpp rather than reaching in.
    const void* nativeStorage() const;

    // QPen is a plain d-pointer on this tree's Qt 6.10.2, macOS arm64 --
    // same measurement §7 recorded for QBrush.
    static constexpr std::size_t kNativeStorageSize = 8;
    static constexpr std::size_t kNativeStorageAlign = 8;

};

ICorePixmapPainter.h#

ICoreEssentials/UI/Painting/ICorePixmapPainter.h

⚠ class QPainter; RETIRED BY A9.4 (2026-08-21) -- dead here. ICorePainter.h is INCLUDED above and declares its own; this one was a second copy that nothing in this file named.

ICorePixmapPainter#

ICorePixmapPainter.h:16 · class · pImpl · 6 declaration(s)

Draws onto a pixmap instead of onto a widget.

class ICorePixmapPainter {
public:
    explicit ICorePixmapPainter(ICorePixmap& target);
    ~ICorePixmapPainter();

    ICorePixmapPainter(const ICorePixmapPainter&) = delete;
    ICorePixmapPainter& operator=(const ICorePixmapPainter&) = delete;

    ICorePainter& painter();

    // Finish early. Idempotent; the destructor calls it too.
    void end();

private:
    class Impl;                    // the two-line residue; state lives here
    std::unique_ptr<Impl> impl;
};

ICoreSurfaceGradient.h#

ICoreEssentials/UI/Painting/ICoreSurfaceGradient.h

The app's large-surface treatment, in one place -- the gradient half of what ICoreNavItemStyle is for rows.

A surface dressed by this is lit at one end and deepens toward the other, along whichever axis the caller names -- so the left rail, the top bar and the floating time line read as the same material even where they do not run the same way. It is derived from the surface's own RESTING colour, so it follows the theme with no work at the call site and needs no second set of tokens -- on a dark ground the lift is the brand's bright royal and the fall is toward black; on a light one it is toward white and the fall is a royal tint.

This and ICoreThemeGradients.h are the only two places a gradient's colours are chosen -- the visual-identity check holds everything else to them.

Declares no class of its own — see the file.

ICoreThemeGradients.h#

ICoreEssentials/UI/Painting/ICoreThemeGradients.h

The app's NAMED gradients -- every gradient a panel draws is one of these, or ICoreSurfaceGradient's material ramp, and nothing else.

⚠ THIS FILE IS THE LOCK ON THE VISUAL IDENTITY, and that is its whole reason to exist. A panel that builds its own gradient picks its own stops, and a dozen panels each picking stops is a dozen slightly different brands: before 2026-09-21 the Project Navigator, the Account panel and the Block Wizard each painted the same backdrop from their own copy of the numbers, and two of them had already drifted from the third. So a panel says WHICH gradient it wants and WHERE; the COLOURS come from ICoreTheme::brand, and a brand change is one edit there rather than a hunt.

The shape parameters a caller may pass -- an alpha, how far round a rim the light runs, where a sweep's head is -- are geometry and animation, and stay

Declares no class of its own — see the file.