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

API — ICoreEssentials/UI/Portable

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

HeaderDefinesDeclarationsBases
ICoreAnimationClock.hICoreAnimationUpdate0—
ICoreAnimationTimeline.hICoreAnimationTimeline, ICoreAnimationSample0—
ICoreButtonChrome.hICoreButtonRectF, ICoreButtonChromeState, ICoreButtonLabelMetrics, ICoreButtonLabelLayout, ICoreButtonIntrinsicSize, ICoreButtonInk, ICoreButtonPalette1—
ICoreButtonInput.hICoreButtonInputSpec, ICoreButtonInputState, ICoreButtonInputOutcome0—
ICoreColorState.hICoreColorState0—
ICoreComboPopupCore.hICoreComboOptionRect, ICoreComboCheckGeometry, ICoreComboPopupMetrics, ICoreComboPopupSpace, ICoreComboPopupPlacement0—
ICoreDoubleSpinBoxInput.hICoreDoubleSpinBoxRange, ICoreDoubleSpinBoxScrub0—
ICoreFieldAffordance.hICoreFieldAffordanceInput, ICoreFieldAffordanceLayout0—
ICoreFieldChrome.hICoreFieldRect, ICoreFieldGroundPalette0—
ICoreFrameSource.hICoreFrameSource8—
ICoreGestureRecognizer.hICoreGestureRecognizer12—
ICoreInfoLabelCore.hICoreInfoLabelRect, ICoreInfoLabelPoint, ICoreInfoLabelSchedule0—
ICoreInlineMarkup.hICoreInlineMarkupRun0—
ICoreItemViewCore.hICoreItemViewRect, ICoreItemViewHit, ICoreItemViewClick0—
ICoreItemViewInput.hICoreItemViewInputState, ICoreItemViewInputOutcome0—
ICoreLabelCore.hICoreLabelContent, ICoreLabelMeasurements, ICoreLabelSize0—
ICoreLayoutEngine.hICoreLayoutRect, ICoreLayoutMargins, ICoreLayoutItem, ICoreBoxLayoutSpec, ICoreGridCell, ICoreGridLayoutSpec, ICoreFormLayoutSpec, ICoreFormRow, ICoreFlowLayoutSpec0—
ICoreLayoutSeatCore.hICoreLayoutConstraints, ICoreLayoutNativeAccess12—
ICoreLineIntersect.h—0—
ICoreMenuChrome.hICoreMenuRectF, ICoreMenuPaintMetrics, ICoreMenuRowPaintState, ICoreMenuPalette, ICoreMenuRowInk, ICoreMenuRowLayout, ICoreMenuBarTitleInk0—
ICoreMenuCore.hICoreMenuRow, ICoreMenuMetrics, ICoreMenuSize, ICoreMenuPoint, ICoreMenuBarMetrics0—
ICoreMenuInput.hICoreMenuOpenPanel, ICoreMenuChainState, ICoreMenuChainOutcome, ICoreMenuBarInputState, ICoreMenuBarOutcome0—
ICoreMotionFrameClock.hICoreMotionFrameClock0—
ICoreMotionOwnerRegistry.h—0—
ICoreMotionRuntimeCore.h—0—
ICorePanelEdges.hICorePanelEdges1—
ICorePathBuilder.hICorePathBuilderElement, ICorePathBuilder, ICorePathRect0—
ICorePixelConvert.h—0—
ICoreRgbaShade.h—0—
ICoreSceneCore.hICoreScenePoint, ICoreSceneRect, ICoreSceneTransform, ICoreSceneItemId, ICoreSceneItem, ICoreSceneSlot, ICoreScene, ICoreSceneDrawCommand, ICoreSceneHoverChange, ICoreScenePressResult, ICoreSceneDragResult, ICoreSceneReleaseResult1—
ICoreSceneViewCore.hICoreSceneViewState, ICoreSceneViewDelivery, ICoreSceneViewOutcome1—
ICoreScrollCore.hICoreScrollModel, ICoreScrollBarSpec, ICoreScrollBarGeometry0—
ICoreScrollInput.hICoreScrollInputState, ICoreScrollInputOutcome0—
ICoreScrollPaneChrome.hICoreScrollPaneRect, ICoreScrollPaneChromeMetrics0—
ICoreSpinBoxChrome.hICoreSpinBoxRect, ICoreSpinBoxChevron, ICoreSpinBoxArrowInk, ICoreSpinBoxPalette2—
ICoreSpinBoxInput.hICoreSpinBoxRange0—
ICoreSplitterCore.hICoreSplitterPane, ICoreSplitterSpec, ICoreSplitterRange0—
ICoreSplitterInput.hICoreSplitterInputState, ICoreSplitterInputOutcome0—
ICoreSvgColor.hICoreSvgColor, ICoreSvgGradientStop2—
ICoreSvgDocument.hICoreSvgDocMatrix, ICoreSvgDocPaint, ICoreSvgDocGradient, ICoreSvgDocText, ICoreSvgDrawCommand, ICoreSvgMask, ICoreSvgDrawing, ICoreSvgNode3—
ICoreSvgEmitter.hICoreSvgEmitColor, ICoreSvgEmitOp0—
ICoreSvgGeometry.hICoreSvgMatrix, ICoreSvgSegment, ICoreSvgPath12—
ICoreSvgPath.hICoreSvgDocSegment0—
ICoreSvgRender.hICoreSvgGradient, ICoreSvgPaint, ICoreSvgText, ICoreSvgEntry, ICoreSvgClip, ICoreSvgRenderList, ICoreSvgRenderOptions1—
ICoreSvgWrite.hICoreSvgWriteOptions0—
ICoreTableLayoutCore.hICoreTableMetrics, ICoreTableRect, ICoreTableGeometry0—
ICoreTextDecorationSet.hICoreTextDecorationSet9—
ICoreTextItemEditCore.hICoreTextItemEditState, ICoreTextItemEditOutcome, ICoreTextItemEditCaretPlace0—
ICoreTouchHover.hICoreTouchPresence, ICoreTouchHoverAction, ICoreTouchHover24—
ICoreTouchTargets.h—0—
ICoreWatchRegistry.hICoreWatchLink, ICoreWatchRegistry0—
ICoreWidgetCore.hICoreWidgetPointerOutcome, ICoreWidgetPointerSpec, ICoreWidgetPointerState0—

ICoreAnimationClock.h#

ICoreEssentials/UI/Portable/ICoreAnimationClock.h

ICoreAnimationClock -- the thing that keeps a set of animations running, without owning a timer, a frame or a toolkit.

The timeline beside this file answers "where is ONE animation at time t". This answers the other half: which animations are running, what each of them is worth at this instant, and which of them have just finished. It is what a backend's frame callback drives -- a display link on one platform, a timer on another -- and the reason the driver is the only part that needs writing per backend:

THE CLOCK DOES NOT KNOW WHAT TIME IT IS. advanceTo() is handed the reading. Nothing here calls the system clock, sleeps, or schedules.

That is what makes a whole animation system testable in a unit test: the

ICoreAnimationUpdate#

ICoreAnimationClock.h:37 · struct · 0 declaration(s)

What one running animation is worth at the moment of a tick.

struct ICoreAnimationUpdate {
public:
    // The handle start() returned. Stable for the life of the run, never
    // reused while that run is alive.
    int id = 0;

    ICoreAnimationSample sample;

    // True on the tick that carries the run's final sample. The run is removed
    // AFTER this update is reported, so a caller that applies `sample` on every
    // update lands on the end value exactly once and is never left one frame
    // short of it.
    bool justFinished = false;
};
};

ICoreAnimationTimeline.h#

ICoreEssentials/UI/Portable/ICoreAnimationTimeline.h

ICoreAnimationTimeline -- the arithmetic under an animation, and nothing else: where a value sits at a given moment, and when the run is over.

Two questions, answered as free functions over plain numbers:

  • given an easing and a fraction of the way through, how far along is the

VALUE (the eleven named curves the editor actually uses)

  • given a duration, a loop count and a stopwatch reading, what fraction of

the way through are we, which loop is this, and has it finished

⚠ THIS FILE NAMES NO TOOLKIT TYPE AND OWNS NO TIMER. It does not know what a frame is, cannot be started or stopped, and never asks what time it is: the stopwatch reading is a parameter. That is the point -- a driver that ticks it is a per-backend concern (a display link on one platform, a timer on

ICoreAnimationTimeline#

ICoreAnimationTimeline.h:72 · struct · 0 declaration(s)

The shape of one animation's run.

struct ICoreAnimationTimeline {
public:
    // Total length of ONE loop. Zero is legal and is the collapse the
    // animation policy performs when the user turns animation off: a
    // zero-duration run is finished the moment it starts, AT ITS END VALUE.
    // That is the contract that says turning animations off changes how the UI
    // reaches a state and never which state it lands in.
    int durationMs = 0;

    // How many times the run repeats. 1 is once; a negative count loops
    // forever and never reports finished. 0 is treated as 1 rather than as
    // "never run", because an animation asked for zero loops that then never
    // reaches its end value would strand whatever it was animating.
    int loopCount = 1;

    // Ping-pong: even loops run forward, odd loops run backward. Off by
    // default. Note this reverses the LINEAR fraction, so the easing is
    // evaluated on the reversed fraction -- an OutCubic return trip decelerates
    // into the start value, which is what "reverse the animation" is expected
    // to look like, and is not the same shape as playing OutCubic backwards.
    bool alternate = false;

    // The curve and its two knobs, in the same sentinel form the spec uses: a
    // negative amplitude or period means "keep the family default".
    ICoreEasing easing = ICoreEasing::Linear;
    double easingAmplitude = -1.0;
    double easingPeriod = -1.0;
};
};

ICoreAnimationSample#

ICoreAnimationTimeline.h:111 · struct · 0 declaration(s)

Where a run is at one stopwatch reading.

struct ICoreAnimationSample {
public:
    // Fraction of the current loop, before easing, in [0, 1].
    double linearProgress = 0.0;

    // The same fraction with the timeline's curve applied. This is the number
    // an interpolation wants; it can leave [0, 1] for the overshooting curves.
    double easedProgress = 0.0;

    // Which pass this is, counting from 0. Stays at the final loop's index
    // once the run is finished.
    int loop = 0;

    // True once every loop has been served. Never true for a looping-forever
    // timeline, and true immediately for a zero-duration one.
    bool finished = false;

    // True when this pass is a backward one (alternate timelines only).
    bool reversed = false;
};
};

ICoreButtonChrome.h#

ICoreEssentials/UI/Portable/ICoreButtonChrome.h

ICoreButtonChrome -- which treatment a branded button wears, where each part of it goes, and how far the pointer has moved it.

THIS FILE NAMES NO TOOLKIT TYPE AND DRAWS NOTHING. Like the layout engine and the splitter core beside it, it answers questions and returns rectangles; a backend takes the answers and paints.

WHY A CORE. ICoreButton is not a QPushButton with a stylesheet -- its appearance is 200 lines of QPainter in ICoreButton.cpp, and every one of the decisions in it is a PRODUCT decision rather than a Qt one:

  • a caption-only button wears a resting plate and an icon-carrying one

does not (because a word needs something saying it is pressable and a glyph does not),

ICoreButtonRectF#

ICoreButtonChrome.h:54 · struct · 0 declaration(s)

A rectangle in LOGICAL pixels, with a fractional origin.

struct ICoreButtonRectF {
public:
    double x = 0.0;
    double y = 0.0;
    double width = 0.0;
    double height = 0.0;
};
};

ICoreButtonChromeState#

ICoreButtonChrome.h:82 · struct · 0 declaration(s)

Everything about a button that changes what it looks like right now.

struct ICoreButtonChromeState {
public:
    bool enabled = true;
    bool glassFinish = false;
    bool navItemStyle = true;       // the DEFAULT since the nav treatment landed
    bool restPlate = true;
    bool hasCustomBgColor = false;
    bool hasCustomRestOpacity = false;
    bool hasCustomHoverOpacity = false;
    bool hasIcon = false;
    bool hasText = true;

    // The one animation. `opacity` travels between `opacityOnMouseLeave` and
    // `hoverOpacity`, and past it to `pressedOpacity` while held. Since the
    // variants went solid it no longer reaches an alpha channel on the glass
    // path -- it is read as a progress and spent on the lightening instead.
    double opacity = 0.0;
    double opacityOnMouseLeave = 0.0;
    double hoverOpacity = 0.5;

    // The pointer is HELD on this button right now.
    //
    // ⚠⚠ IT IS NOT DERIVABLE FROM `opacity`, WHICH IS WHY IT IS A SEPARATE
    // FIELD RATHER THAN A THRESHOLD. A press used to be expressed by writing
    // `spec.pressedOpacity` over the animation's value -- readable on the two
    // bodies that treat `opacity` as an alpha, and MEANINGLESS on the nav
    // treatment, where the same number is the treatment's PROGRESS and is
    // clamped at 1. Since the nav row is the default body, the commonest button
    // in the product had no held state at all: it lit on hover, it fired on
    // click, and it did nothing under the finger in between.
    //
    // ⚠ EACH BODY SPENDS IT IN ITS OWN TERMS, which is what a flag buys over a
    // number: the solid variants already overshoot their tint past full hover
    // and simply keep doing so, and the nav row deepens its wash and squares up
    // its accent bar. Neither has to agree with the other about how much
    // "pressed" is worth.
    bool pressed = false;
};
};

ICoreButtonLabelMetrics#

ICoreButtonChrome.h:228 · struct · 0 declaration(s)

What the backend measured, in logical pixels.

struct ICoreButtonLabelMetrics {
public:
    double width = 0.0;             // the button
    double height = 0.0;
    double iconWidth = 0.0;         // 0 if there is no icon
    double iconHeight = 0.0;
    double captionWidth = 0.0;      // 0 if there is no caption
    // >= 0 pins the label to the left at that padding; < 0 centres the pair.
    double labelLeftPadding = -1.0;
};
};

ICoreButtonLabelLayout#

ICoreButtonChrome.h:238 · struct · 0 declaration(s)

struct ICoreButtonLabelLayout {
public:
    bool hasIcon = false;
    ICoreButtonRectF iconRect;
    bool hasCaption = false;
    // The caption is drawn left-aligned and vertically centred INSIDE this
    // rect, which spans the button's full height -- the same instruction the
    // Qt path gives QPainter::drawText, and the reason the caption's baseline
    // does not move when the font changes.
    ICoreButtonRectF captionRect;
    // How much room the caption had before eliding. Feed it back to the
    // backend's elide, then measure and lay out again.
    double elideRoom = 0.0;
};
};

ICoreButtonIntrinsicSize#

ICoreButtonChrome.h:290 · struct · 0 declaration(s)

the size a button asks for when nobody sized it ------------------------ ⚠⚠ A BUTTON THAT STATES NO SIZE GETS WHATEVER THE LAYOUT HAS LEFT, AND IN A ROW WITH A STRETCH THAT IS NOTHING (W10.55, AppK...

struct ICoreButtonIntrinsicSize {
public:
    int preferredWidth = 0;
    int minimumHeight = 0;
    int preferredHeight = 0;
};
};

ICoreButtonInk#

ICoreButtonChrome.h:325 · struct · 0 declaration(s)

The colours one button is drawn with right now, after the state has been resolved.

struct ICoreButtonInk {
public:
    ICoreRgba body;             // the flat wash, the plate, or the lit variant fill
    ICoreRgba restPlate;        // invalid unless the button wears one
    ICoreRgba caption;          // the text ink, already switched for disabled
    // What the GLYPH is drawn at -- 1.0 unless the button is disabled. The
    // other half of the same answer as `caption`: both halves of a label go
    // quiet together or the button only half looks dead. A backend multiplies
    // it into whatever opacity its painter already carries rather than
    // assigning it, so a faded ancestor cannot be brightened by a dead child.
    double glyphOpacity = 1.0;
    ICoreRgba navWash;          // invalid at rest, or on a body that lights itself
    ICoreRgba navStroke;        // the hairline and the accent edge -- ONE colour
    double navProgress = 0.0;   // what the strokes are drawn at
    double navWashScale = 1.0;

    // How much MORE than full hover the held state is worth, 0 when the button
    // is not held. A backend multiplies its wash alpha by (1 + this) and may
    // square up the accent bar by the same amount.
    //
    // ⚠ IT IS A SEPARATE NUMBER FROM navProgress RATHER THAN navProgress
    // OVERSHOOTING, and the difference is the hairline. The ring and the bar
    // are already at full strength at full hover -- pushing navProgress past 1
    // would ask for an alpha above 255, which truncates to no change and makes
    // the press invisible on exactly the two marks a user is looking at. The
    // wash has room; the strokes do not.
    double navPressBoost = 0.0;
};
};

ICoreButtonPalette#

ICoreButtonChrome.h:355 · struct · 1 declaration(s)

The theme's side of the question, so the core can be called with no theme manager standing.

struct ICoreButtonPalette {
public:
    ICoreRgba bgColor;          // the button's own fill (variant plate, or wash)
    ICoreRgba restFill;         // button.restFill
    ICoreRgba hoverWash;        // button.hoverWash
    ICoreRgba hoverEdge;        // button.hoverEdge
    ICoreRgba tintedEdge;       // button.tintedEdge -- the SOLID variants' stroke
    ICoreRgba textInk;
    ICoreRgba disabledTextInk;
    int hoverWashAlpha = 0;     // button.hoverWashAlpha
    int hoverEdgeAlpha = 0;     // button.hoverEdgeAlpha
    int tintedHoverLighter = 100;
};
};

File-scope declarations#

// Which body a button paints. Exactly one of the three, and the order they are
// tested in is load-bearing -- see icoreButtonBody().
enum class ICoreButtonBody {
    // The default: the app's nav-row treatment, invisible at rest.
    NavRow,
    // A flat wash of the button's own colour, faded by the hover animation.
    // What a button that chose a colour, a rest opacity or a hover opacity
    // gets, because the nav treatment would throw that choice away.
    Flat,
    // A solid variant plate (Primary/Danger/Success), opaque in every state,
    // whose hover response is a lightening rather than a fade.
    Glass,
};

ICoreButtonInput.h#

ICoreEssentials/UI/Portable/ICoreButtonInput.h

ICoreButtonInput -- what a pointer DOES to a button, as distinct from what a button LOOKS like.

ICoreButtonChrome next door answers "given this state, which body, which rectangle, which alpha". This file answers the other half: given a pointer crossing, a press or a release, WHICH STATE the button moves to, which of its signals fire, and whether anything repaints.

THIS FILE NAMES NO TOOLKIT TYPE. It takes an input kind and two booleans and returns a record of what to do, so every rule below is testable with no window, no view and no event loop -- which is the point, because none of them is obvious and several are the OPPOSITE of what a seat written from first principles would choose.

ICoreButtonInputSpec#

ICoreButtonInput.h:47 · struct · 0 declaration(s)

The button's own settings that the CHROME state does not already carry.

struct ICoreButtonInputSpec {
public:
    // setHoldHoverStyle(). A button holding its hover does not fade back out on
    // leave -- the caller is lighting it for a reason of its own.
    bool holdHoverStyle = false;

    // A checkable button toggles on click, before the click fires.
    bool checkable = false;

    // Where the fill goes while the button is held. Not on the chrome state,
    // because it is never a resting value -- it is written straight over
    // `opacity` for the duration of the press.
    double pressedOpacity = 1.0;

    // Whether this button has an info card to show and hide. Both halves must
    // be true at the call site (hover events on AND a card attached); the seat
    // resolves that to this one flag.
    bool infoCardArmed = false;
};
};

ICoreButtonInputState#

ICoreButtonInput.h:68 · struct · 0 declaration(s)

What a seat has to remember between inputs.

struct ICoreButtonInputState {
public:
    bool underPointer = false;
    bool pressedInside = false;
    bool checked = false;
};
};

ICoreButtonInputOutcome#

ICoreButtonInput.h:76 · struct · 0 declaration(s)

What the seat should DO about one input.

struct ICoreButtonInputOutcome {
public:
    // The wrapper's signals.
    bool fireEntered = false;
    bool fireLeft = false;
    bool fireClicked = false;

    // The info card.
    bool showInfoCard = false;
    bool hideInfoCard = false;

    // The background. A crossing FADES (over the theme's duration); a press or
    // a release sets the value outright, because a dip that animated would
    // still be travelling when the finger came up.
    bool fadeBackground = false;
    double fadeTarget = 0.0;
    bool setOpacity = false;
    double opacity = 0.0;

    // The HELD state, which is a different thing from the opacity above.
    //
    // ⚠⚠ IT EXISTS BECAUSE THE DEFAULT BODY HAD NO PRESSED LOOK AT ALL. A press
    // used to be expressed only by overwriting `opacity` with
    // `spec.pressedOpacity`, and the nav treatment -- ICoreButton's DEFAULT
    // since the treatment landed -- reads `opacity` as a PROGRESS rather than
    // an alpha, so it was excluded from that and got nothing. What the user
    // saw: almost every button in the product responded to hover, responded to
    // the click, and did nothing whatever while the finger was down.
    //
    // ⚠ A FLAG RATHER THAN A SECOND NUMBER, so each body spends it in the terms
    // it already has. See ICoreButtonChromeState::pressed.
    bool setPressed = false;
    bool pressed = false;

    // Whether the button needs redrawing for a reason the fade will not cover.
    bool repaint = false;

    // `checked` was flipped. The seat repaints and, if it mirrors the flag
    // anywhere else, updates that too.
    bool checkedToggled = false;
};
};

File-scope declarations#

// The four things a seat forwards in. There is no Move: a button's chrome does
// not track the pointer WITHIN itself, only across its edge, which is why one
// hover hook stands in for both crossings on a backend that has one.
enum class ICoreButtonPointerInput {
    Enter,
    Leave,
    Press,
    Release
};

ICoreColorState.h#

ICoreEssentials/UI/Portable/ICoreColorState.h

ICoreColorState -- what ICoreColor IS, once the toolkit is taken away.

THIS FILE NAMES NO TOOLKIT TYPE. It is the shared body of every non-Qt seat for ICoreColor: the AppKit one (A1.6) and the WinUI one (W1.8) are now both twenty-line forwards onto it.

⚠ IT WAS NOT WRITTEN HERE. Every line below is the AppKit seat's, lifted unchanged from Backends/AppKit/Values/ICoreAppKitColor.mm, whose 594-check differential suite against a live QColor is what says it is right. It moved because that file turned out to contain NO Objective-C and NO CoreGraphics at all -- not one @, not one NS, not one CG outside its own comments -- so the second backend to need it had the choice of transcribing a measured HSV port a second time or moving it once. Measured before moving: the .mm compiles with g++ -x c++ on Windows and its suite passes 594/0 there

ICoreColorState#

ICoreColorState.h:57 · struct · 0 declaration(s)

A colour's whole state.

struct ICoreColorState {
public:
    std::uint16_t a = 0xFFFF;
    std::uint16_t r = 0;
    std::uint16_t g = 0;
    std::uint16_t b = 0;
    bool valid = false;
};
};

ICoreComboPopupCore.h#

ICoreEssentials/UI/Portable/ICoreComboPopupCore.h

ICoreComboOptionRect#

ICoreComboPopupCore.h:19 · struct · 0 declaration(s)

Where a combo box's option list puts its rows, its check marks and its labels -- and which row a point is in.

struct ICoreComboOptionRect {
public:
    double x = 0.0;
    double y = 0.0;
    double width = 0.0;
    double height = 0.0;
};
};

ICoreComboCheckGeometry#

ICoreComboPopupCore.h:33 · struct · 0 declaration(s)

Where the check mark and the label sit inside one option row.

struct ICoreComboCheckGeometry {
public:
    double side = 0.0;
    double x = 0.0;
    double y = 0.0;
    double labelX = 0.0;     // where the option's text starts
};
};

ICoreComboPopupMetrics#

ICoreComboPopupCore.h:42 · struct · 0 declaration(s)

The metrics of a popup.

struct ICoreComboPopupMetrics {
public:
    double optionHeight = 25.0;
    double padding = 5.0;            // around the list, all four sides
    double checkLeftInset = 4.0;
    double checkMaximumSide = 14.0;
    double checkTextGap = 2.0;
};
};

ICoreComboPopupSpace#

ICoreComboPopupCore.h:97 · struct · 0 declaration(s)

The room the popup has, IN THE COMBO BOX'S OWN COORDINATES -- so the box's own origin is (0, 0) and the box occupies y in [0, comboHeight].

struct ICoreComboPopupSpace {
public:
    double left = 0.0;
    double top = 0.0;
    double width = 0.0;
    double height = 0.0;
};
};

ICoreComboPopupPlacement#

ICoreComboPopupCore.h:128 · struct · 0 declaration(s)

Where the popup ends up once the room around the box is known.

struct ICoreComboPopupPlacement {
public:
    double x = 0.0;
    double y = 0.0;
    double width = 0.0;
    bool flippedUp = false;
};
};

ICoreDoubleSpinBoxInput.h#

ICoreEssentials/UI/Portable/ICoreDoubleSpinBoxInput.h

ICoreDoubleSpinBoxInput -- everything a double spin box DECIDES, as distinct from how a backend shows it.

ICoreDoubleSpinBox's five seats (AppKit, GTK4, UIKit, web, WinUI) differ in how they draw a stepper and how they report accessibility. What the value is, what the field shows, what a typed expression means, what a step or a scrub lands on -- all of that is here, once, so the five cannot drift apart and all of it is checkable with no field, no window and no event loop (testingLabs/tests/double_spinbox).

The integer spin box's rules are ICoreSpinBoxInput next door; the chevron geometry and ink are ICoreSpinBoxChrome, which the painted seats share with the integer box unchanged.

ICoreDoubleSpinBoxRange#

ICoreDoubleSpinBoxInput.h:41 · struct · 0 declaration(s)

The range, step and display precision a double spin box works in.

struct ICoreDoubleSpinBoxRange {
public:
    double minimum = 0.0;
    double maximum = 99.99;
    double singleStep = 1.0;
    // Digits after the point in the field. -1 shows the SHORTEST text that
    // reads back as the very same double (ICoreDoubleText), for a field that
    // must show a CAD coordinate exactly.
    int decimals = 2;
};
};

ICoreDoubleSpinBoxScrub#

ICoreDoubleSpinBoxInput.h:147 · struct · 0 declaration(s)

struct ICoreDoubleSpinBoxScrub {
public:
    bool pressed = false;
    bool scrubbing = false;
    double startValue = 0.0;
    double startX = 0.0;
    double startY = 0.0;
};
};

ICoreFieldAffordance.h#

ICoreEssentials/UI/Portable/ICoreFieldAffordance.h

ICoreFieldAffordance -- the two buttons that live INSIDE a text field, when each of them is there, and how much of the field's width they take away from the text.

THIS FILE NAMES NO TOOLKIT TYPE AND DRAWS NOTHING, exactly as ICoreFieldChrome beside it does not: it takes a size and three booleans and answers with rectangles. A backend turns those into ink and into hit tests.

⚠⚠ WHY A CORE, WHEN THIS COULD HAVE BEEN TWELVE LINES INSIDE ONE SEAT. Because the rules are not obvious and they are not a toolkit's:

  • a clear button appears only when there is something to clear, and only

while the field is being used. A field showing a permanent grey cross next to an empty value reads as a control that is broken.

ICoreFieldAffordanceInput#

ICoreFieldAffordance.h:54 · struct · 0 declaration(s)

What a field is asked to show.

struct ICoreFieldAffordanceInput {
public:
    double width = 0.0;
    double height = 0.0;
    bool clearButtonEnabled = false;
    bool masked = false;
    bool focused = false;
    bool hasText = false;
    bool enabled = true;
};
};

ICoreFieldAffordanceLayout#

ICoreFieldAffordance.h:67 · struct · 0 declaration(s)

Where the two buttons are, and whether each is there at all.

struct ICoreFieldAffordanceLayout {
public:
    bool clearVisible = false;
    ICoreFieldRect clear;

    bool revealVisible = false;
    ICoreFieldRect reveal;

    // What the value has left after the buttons have taken theirs -- 0 when
    // neither is showing, so a field with no affordances lays its text out
    // exactly as it did before this file existed.
    double reservedRight = 0.0;
};
};

ICoreFieldChrome.h#

ICoreEssentials/UI/Portable/ICoreFieldChrome.h

ICoreFieldChrome -- what a text field's frame looks like in each of its states, and nothing about the text inside it.

THIS FILE NAMES NO TOOLKIT TYPE AND DRAWS NOTHING. It takes a size and a few booleans and answers with a rectangle and two numbers; a backend turns those into ink. That split is what lets the rules below be checked with no window, no field and no toolkit -- which matters here more than usual, because a text field is the one family whose VALUE is drawn by a real native control, so the only part a suite could ever reach is this one.

⚠ AND SINCE W10.75 ONE OF ITS ANSWERS IS A COLOUR. The paragraph above read "a rectangle and two numbers" and nothing else for as long as this unit has existed -- it is kept in those words because it is still what the GEOMETRY and the BORDER answer with, and the sentence is what the split above argues from.

ICoreFieldRect#

ICoreFieldChrome.h:48 · struct · 0 declaration(s)

The frame, in the field's own coordinates.

struct ICoreFieldRect {
public:
    double x = 0.0;
    double y = 0.0;
    double width = 0.0;
    double height = 0.0;
};
};

ICoreFieldGroundPalette#

ICoreFieldChrome.h:150 · struct · 0 declaration(s)

The theme's side of the fill question, so the rule below can be answered with no theme manager standing.

struct ICoreFieldGroundPalette {
public:
    ICoreRgba editableFill;   // field.background
    ICoreRgba readOnlyFill;   // field.readOnlyBackground
    ICoreRgba callerFill;     // invalid unless a call site set one
};
};

ICoreFrameSource.h#

ICoreEssentials/UI/Portable/ICoreFrameSource.h

ICoreFrameSource#

ICoreFrameSource.h:13 · class · pImpl · 8 declaration(s)

The one piece of ICoreFrameTicker each seat writes: start a display-paced tick, stop it, and say what time it is on the tick's own clock.

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

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

    void setTick(std::function<void(long long nowMs)> tick);
    void start();
    void stop();
    [[nodiscard]] bool isRunning() const;

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

ICoreGestureRecognizer.h#

ICoreEssentials/UI/Portable/ICoreGestureRecognizer.h

ICoreGestureRecognizer#

ICoreGestureRecognizer.h:42 · class · pImpl · 12 declaration(s)

ICoreGestureRecognizer -- raw touches in, pan / pinch / long-press out.

class ICoreGestureRecognizer {
public:
    ICoreGestureRecognizer();
    ~ICoreGestureRecognizer();
    ICoreGestureRecognizer(const ICoreGestureRecognizer&) = delete;
    ICoreGestureRecognizer& operator=(const ICoreGestureRecognizer&) = delete;

    // How far a contact may wander, in the event's units, and still be "held
    // still". 10 by default: UIKit's figure, and Android's 8 dp at 1.25x.
    void setSlop(double distance);
    [[nodiscard]] double slop() const;

    // How long a still contact must be held to be a long press. 500 ms by
    // default, the figure UIKit and Android both ship.
    void setLongPressDelayMs(double ms);
    [[nodiscard]] double longPressDelayMs() const;

    // Feed every touch event, in order. Returns the gesture events this one
    // produced, in the order to deliver them -- usually none or one; two when
    // a second finger ends one gesture and a pinch cannot yet have begun.
    std::vector<ICoreGestureEvent> feed(const ICoreTouchEvent& event);

    // Time passing with no event. A backend calls it from a timer while a
    // finger is down; returns a LongPress Began once the delay has run out.
    std::vector<ICoreGestureEvent> tick(double nowMs);

    // True while a finger is down and a long press could still begin, which is
    // when a backend's timer needs to be running.
    [[nodiscard]] bool wantsTick() const;

    // Abandon everything: a gesture in progress is reported Cancelled, and
    // every contact is forgotten. For a window that loses its pointers (focus
    // lost, a modal opened).
    std::vector<ICoreGestureEvent> cancel();

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

ICoreInfoLabelCore.h#

ICoreEssentials/UI/Portable/ICoreInfoLabelCore.h

The hover-label's two decisions, as arithmetic and as a state machine, with no toolkit, no timer and no thread.

A hover label answers "how big am I, where do I go, and should I still appear?". The first two are geometry. The third is the one that goes wrong, because it is a question about WHICH request is current, and the obvious implementation answers a different question -- see the schedule below.

ICoreInfoLabelRect#

ICoreInfoLabelCore.h:16 · struct · 0 declaration(s)

struct ICoreInfoLabelRect {
public:
    double x = 0.0;
    double y = 0.0;
    double width = 0.0;
    double height = 0.0;
};
};

ICoreInfoLabelPoint#

ICoreInfoLabelCore.h:23 · struct · 0 declaration(s)

struct ICoreInfoLabelPoint {
public:
    double x = 0.0;
    double y = 0.0;
};
};

ICoreInfoLabelSchedule#

ICoreInfoLabelCore.h:79 · struct · 0 declaration(s)

Which pending show is allowed to appear.

struct ICoreInfoLabelSchedule {
public:
    unsigned int issued = 0;    // tickets handed out; never reused
    unsigned int live = 0;      // the one allowed to reveal, or 0 for none
};
};

File-scope declarations#

// Which side of the hovered item the label sits on. The vertical placement is
// the same either way and is not a choice.
enum class ICoreInfoLabelSide { Left, Right };

ICoreInlineMarkup.h#

ICoreEssentials/UI/Portable/ICoreInlineMarkup.h

ICoreInlineMarkup -- the small subset of HTML that ~a dozen call sites in this tree append to a log pane, turned into coloured runs of plain text.

THIS FILE NAMES NO TOOLKIT TYPE AND MEASURES NOTHING. Like the layout engine and the menu core beside it, it answers a question about a string; a backend takes the runs and paints them.

⚠⚠ WHY IT EXISTS AT ALL, WHICH IS NOT "SOMEBODY WANTED RICH TEXT" (W10.24). QTextEdit::append INTERPRETS its argument as HTML when the string looks like HTML, and that is not an opt-in a caller can see: the call reads append(text) either way. So the call sites in this tree were written against it -- ICoreCommandWindow::appendEcho builds two <span>s, one for the >>> prompt in the accent ink and one for the command in the primary ink, and the diagnostics log view wraps an error line in a red span -- and every

ICoreInlineMarkupRun#

ICoreInlineMarkup.h:60 · struct · 0 declaration(s)

One stretch of text drawn in one ink.

struct ICoreInlineMarkupRun {
public:
    std::wstring text;

    // Invalid means "the view's own ink" -- the same thing a null colour means
    // to a paint engine's append. It is NOT black: a log pane on a dark theme
    // whose unstyled runs came out black would be unreadable, which is the
    // defect the call sites' own comments say they already fixed once by
    // taking these colours from the theme instead of baking them.
    ICoreRgba color;
};
};

ICoreItemViewCore.h#

ICoreEssentials/UI/Portable/ICoreItemViewCore.h

ICoreItemViewCore -- how an item view is SHAPED, and nothing else.

The three item views in this tree (ICoreTree with its own rows, ICoreTreeView over ICoreTreeViewModel, and ICoreListBox) ask a toolkit the same six questions, and no two toolkits answer them the same way:

  1. which rows exist, and which row is whose child;
  2. which rows are EXPANDED, and therefore which rows a user can see;
  3. what the visible rows are in order, so a virtualised list can be

indexed by position instead of walked;

  1. what the selection is after this click, given the mode and the

modifier keys;

  1. how wide each column ends up once stretch, minimums and the resize

modes have been applied to a viewport;

ICoreItemViewRect#

ICoreItemViewCore.h:69 · struct · 0 declaration(s)

A rectangle in content coordinates.

struct ICoreItemViewRect {
public:
    int x = 0;
    int y = 0;
    int width = 0;
    int height = 0;
};
};

ICoreItemViewHit#

ICoreItemViewCore.h:92 · struct · 0 declaration(s)

struct ICoreItemViewHit {
public:
    int visibleIndex = -1;
    ICoreItemRowId row = 0;
    int column = -1;
    ICoreItemViewPart part = ICoreItemViewPart::Nothing;
};
};

ICoreItemViewClick#

ICoreItemViewCore.h:101 · struct · 0 declaration(s)

The modifiers held during a press.

struct ICoreItemViewClick {
public:
    bool shift = false;
    bool control = false;
};
};

File-scope declarations#

// A row's identity. Never zero, and never reused -- not even after clear(),
// which starts the next id where the last one left off rather than back at 1.
// A stale id is therefore always REJECTED and never silently resolves to some
// other row, which is the failure ICoreTree's "a wrapper pointer held across
// clearRows() DANGLES" note is warning about one level up.
using ICoreItemRowId = unsigned int;

// What a point landed on. The first four are a ROW, left to right across it;
// the last two are the HEADER BAND above the rows, which is not a row at all.
// 
// ⚠ hitTest() CAN NEVER ANSWER EITHER OF THE LAST TWO, and that is the
// coordinate convention rather than an omission: the content frame EXCLUDES the
// header (see the banner), so a y in the header band is negative in it. The
enum class ICoreItemViewPart : int {
    Nothing = 0,   // above, below or beside the rows
    Indent  = 1,   // blank gutter belonging to an ancestor's indentation level
    Branch  = 2,   // the expander -- ONLY ever reported for a row that has one
    Cell    = 3,   // the cell body, and `column` says which
    Header        = 4,   // a header section, and `column` says which (-1 past the last)
    HeaderDivider = 5,   // the grab strip over a DRAGGABLE interior boundary
};

ICoreItemViewInput.h#

ICoreEssentials/UI/Portable/ICoreItemViewInput.h

ICoreItemViewInput -- what a pointer and a keyboard DO to an item view: which row is lit, what a press selects, what an expander press does INSTEAD of selecting, how a drag paints a selection across rows, and where the arrow keys leave the current row.

THIS FILE NAMES NO TOOLKIT TYPE. It takes plain numbers and an ICoreItemViewCore and answers with a small record saying what changed; a backend does the capturing, the painting, the scrolling and the signal emission. It is the third of the three layers a virtualised item view needs on this backend, and the other two already existed:

ICoreItemViewCore WHERE things are and WHAT the selection becomes -- the visible list, the column layout, the row bands, the expander rectangle, hit testing, and the whole

ICoreItemViewInputState#

ICoreItemViewInput.h:90 · struct · 0 declaration(s)

Everything an item view remembers between inputs.

struct ICoreItemViewInputState {
public:
    // The row under the pointer, or kICoreItemNoRow. Set by Move and cleared
    // by Leave; a seat draws the hover wash from it.
    ICoreItemRowId hoveredRow = 0;

    // Which part of that row the pointer is over. `Branch` is what lights an
    // expander, and it is why this is a part rather than a bool: an expander
    // that lights whenever its ROW is hovered lights on every row of a tree
    // whose rows all have children, which is most trees.
    ICoreItemViewPart hoveredPart = ICoreItemViewPart::Nothing;

    // The row a press took hold of, and what it landed on. Both survive until
    // the release, so that a release can be matched against them.
    ICoreItemRowId pressedRow = 0;
    ICoreItemViewPart pressedPart = ICoreItemViewPart::Nothing;

    // A left drag is carrying the selection across rows. False for a press
    // that took an expander, for a press on the indent gutter, for a right
    // press, and in ICoreSelectionMode::None.
    bool dragSelecting = false;

    // Which KIND of drag it is, decided once at the press and never revisited:
    //
    //   false -- an EXTEND. The run from the core's anchor to the row under
    //            the pointer, re-stated on every move. Extended and Contiguous
    //            without Control, and Single (where the run is one row).
    //   true  -- a PAINT. Every row the pointer reaches is driven to
    //            `dragSelects`. Multi, and Extended with Control held, which
    //            are the two modes whose press TOGGLES rather than replaces.
    bool dragPaints = false;

    // -- the header band ----------------------------------------------------
    //
    // ⚠ A DIVIDER INDEX, NOT A COLUMN, which is the spelling the whole of the
    // resize path uses: divider `h` is the line between columns h-1 and h, and
    // -1 is "none". `hoveredDivider` is what lights a column rule and what puts
    // the resize cursor up BEFORE the button goes down -- a handle a user cannot
    // see until they have already pressed is a handle they never find.
    // `draggedDivider` is the line a press took hold of.
    //
    // Ints rather than a pointer for the reason the whole struct is plain data:
    // the columns are reconfigured under a live gesture (a model rebuild
    // re-applies every width), and an index that no longer names a draggable
    // divider is REJECTED by the core rather than resolving to some other line.
    int hoveredDivider = -1;
    int draggedDivider = -1;

    // ⚠ WHETHER THE RESIZE CURSOR IS CURRENTLY UP, remembered so that the
    // outcome can report a CHANGE rather than a state. Without it every pointer
    // move over a header would ask its seat to write the same shape again, which
    // is a call into the toolkit per frame for a value that did not move --
    // measured on the gtk4 splitter, whose applyCursor() tracks the same bool
    // for the same reason.
    bool resizeCursorUp = false;

    // ⚠ WHAT A DRAGGED-OVER ROW BECOMES IN A PAINT DRAG, and it is the
    // difference between a drag and a drag that flickers. The applyClick a
    // paint drag has available TOGGLES, so pumping it once per move re-flips a
    // row every time the pointer crosses back over a boundary it has already
    // crossed -- and a pointer wobbling one pixel on a row edge crosses it many
    // times. The drag therefore decides ONCE, from what the press did to the
    // press row, and every row it reaches is driven TO that value: a row
    // already at it is left alone. Meaningless in an extend drag.
    bool dragSelects = true;
};
};

ICoreItemViewInputOutcome#

ICoreItemViewInput.h:158 · struct · 0 declaration(s)

What one input did.

struct ICoreItemViewInputOutcome {
public:
    // The view used this input, so the routed event is Handled and does not
    // bubble. False for a hover that changed nothing and for a press the view
    // declined -- a press in ICoreSelectionMode::None on empty space is not
    // the view's to eat.
    bool consumed = false;

    // Something the view draws changed -- a hover moved, a selection moved, an
    // expander lit or a branch opened. Repaint.
    bool repaint = false;

    // The VISIBLE LIST changed length, so contentHeight() is stale and so is
    // every scroll bar sized from it. Set by an expansion change and by
    // nothing else, because nothing else here adds or removes rows.
    bool relayout = false;

    // isSelected() answers differently for at least one row. A seat emits its
    // selection signal from this and repaints from `repaint`.
    bool selectionChanged = false;

    // currentRow() moved. Separate from selectionChanged because Ctrl+Arrow
    // moves one without the other, and because ICoreTree::onCurrentRowChanged
    // and a selection signal are two different signals with two different
    // subscriber lists.
    bool currentChanged = false;

    // A row opened or closed. `expansionRow` is the row and `expandedNow` says
    // which way, so a seat can emit onItemExpanded or onItemCollapsed without
    // asking the core again.
    bool expansionChanged = false;
    ICoreItemRowId expansionRow = 0;
    bool expandedNow = false;

    // A press and its release both landed on the same row -- ICoreTree's
    // onItemClicked, and QAbstractItemView's `clicked`, which is emitted on
    // the RELEASE and not on the press. A press that slid off the row before
    // the button came up is a cancelled click and reports nothing.
    bool clicked = false;
    ICoreItemRowId clickedRow = 0;
    int clickedColumn = -1;

    // A double click landed on a row -- ICoreTree::onItemDoubleClicked.
    bool activated = false;
    ICoreItemRowId activatedRow = 0;
    int activatedColumn = -1;

    // A right press landed on the rows -- ICoreTree::onContextMenuRequested.
    // The seat supplies the SCREEN position; this layer only says that one is
    // wanted and which CELL it is for.
    //
    // ⚠ THE COLUMN IS HERE FOR THE SAME REASON clickedColumn IS. A menu built
    // for "the selected rows" needs the selection, which the core already
    // answers; a menu built for the cell under the pointer needs to know which
    // cell that was, and the row alone does not say. Both readings are ordinary
    // -- copy this cell, delete these rows -- so both are reported, and a seat
    // that only wants one ignores the other. -1 when no column was hit, which
    // is the same spelling clickedColumn uses.
    bool contextMenu = false;
    ICoreItemRowId contextRow = 0;
    int contextColumn = -1;

    // A column divider was dragged, so every laid-out column width has moved and
    // contentWidth() is stale with them. Separate from `relayout`, which is
    // about the VISIBLE LIST changing length: a resize adds and removes no rows,
    // and a seat sizing a horizontal scroll bar has to hear about both.
    bool columnsResized = false;

    // ⚠ THE POINTER SHAPE, AS A CHANGE RATHER THAN A STATE -- `cursorChanged` is
    // set only on the crossing, never on the moves either side of it, which is
    // what `ICoreItemViewInputState::resizeCursorUp` is remembered for.
    //
    // ICoreCursorShape::Arrow means "the view wants no shape of its own": a seat
    // CLEARS rather than setting an arrow, because clearing is what lets a
    // parent chain's shape through and setting Arrow overrides it. Both backend
    // elements draw that distinction in as many words.
    bool cursorChanged = false;
    ICoreCursorShape cursor = ICoreCursorShape::Arrow;

    // Only ever set by the KEY router, and only when the current row moved
    // somewhere the viewport does not show. `scrollY` is what
    // ICoreItemViewCore::scrollToReveal answered; the pointer router never
    // scrolls, because a drag that auto-scrolls needs a timer and a timer is
    // the seat's.
    bool scrollChanged = false;
    int scrollY = 0;
};
};

File-scope declarations#

// The pointer inputs an item view cares about.
// 
// There is no Enter: entering an element tells an item view nothing a Move
// does not, and the position is what picks a row. `Leave` is the crossing the
// host element reports when the pointer genuinely left -- which, because a
// grab suppresses crossings, never arrives mid-drag. `Cancel` is the grab
enum class ICoreItemViewPointerInput { Move, Press, Release, DoubleClick, Leave, Cancel };

ICoreLabelCore.h#

ICoreEssentials/UI/Portable/ICoreLabelCore.h

ICoreLabelCore -- the nine things a QLabel does that a painted label has to be told, and that are wrong in a plausible way if they are guessed.

THIS FILE NAMES NO TOOLKIT TYPE AND MEASURES NOTHING. Text measurement belongs to whatever owns the font, so a backend measures and this decides: every function here takes plain numbers and returns plain numbers.

⚠ THE NINE FACTS WERE MEASURED AGAINST THE OTHER TOOLKIT (Qt 6.10.2, offscreen, Helvetica 12) AND ARE PRODUCT BEHAVIOUR, NOT BACKEND BEHAVIOUR. They were written down once, in prose, in the first non-Qt seat to need them; they are here so the second one inherits them instead of re-deriving the ones it happens to notice. Each has a check and a control beside it.

  1. THE DEFAULT ALIGNMENT IS Left | VCenter, NOT Left | Top. A seat that

ICoreLabelContent#

ICoreLabelCore.h:63 · struct · 0 declaration(s)

What the label is showing.

struct ICoreLabelContent {
public:
    bool hasText = false;
    bool hasPixmap = false;
};
};

ICoreLabelMeasurements#

ICoreLabelCore.h:74 · struct · 0 declaration(s)

What the backend measured, in logical pixels.

struct ICoreLabelMeasurements {
public:
    // The text's advance and the font's height. Both are the BACKEND's answer
    // for the label's own font.
    double textAdvance = 0.0;
    double fontHeight = 0.0;

    // ⚠ THE ADVANCE OF ONE SPACE, WHICH IS HOW FACT 4 IS ACTUALLY BUILT.
    // An empty label is not given a made-up 7x15; it is measured as though its
    // text were a single space, which produces 7x15 at Helvetica 12 and the
    // right answer at every other size. A seat that hard-coded 7 would be
    // wrong on every font but the one it was measured on.
    double spaceAdvance = 0.0;

    // The label's own contents margins, summed per axis. Two call sites in
    // this tree set them and both were dropped by a seat that forgot -- three
    // labels drew flush against their own edge.
    double marginsWidth = 0.0;
    double marginsHeight = 0.0;

    // The pixmap's size, read only when the content has one.
    double pixmapWidth = 0.0;
    double pixmapHeight = 0.0;

    // The height the text wraps to at the width being asked about. Read only
    // by icoreLabelHeightForWidth.
    double wrappedHeight = 0.0;

    // ⚠ THE WIDEST SPACE-SEPARATED RUN IN THE TEXT, WHICH IS WHAT "WRAPPABLE"
    // MEANS -- the narrowest column the text can be poured into without a word
    // hanging out of it. Read only by icoreLabelMinimumWidth, and only for a
    // label that wraps.
    //
    // ⚠ A SEAT MAY CAP IT, AND ONE DOES. The AppKit seat stops at twelve ems
    // (kWrapFloorEms), because a floor a URL or a filesystem path can push to
    // 600 points is a taste rule that has stopped being about taste. The cap is
    // in EMS, so it is a text measurement, so it belongs to whoever owns the
    // font -- this file measures nothing and therefore takes whatever number
    // the measuring seat decided to hand it.
    double widestWordAdvance = 0.0;

    // The advance of the three-dot string, for fact 6's minimum. See
    // icoreLabelMinimumElidedWidth for why it is three dots and not U+2026.
    double threeDotAdvance = 0.0;
};
};

ICoreLabelSize#

ICoreLabelCore.h:119 · struct · 0 declaration(s)

struct ICoreLabelSize {
public:
    double width = 0.0;
    double height = 0.0;
};
};

ICoreLayoutEngine.h#

ICoreEssentials/UI/Portable/ICoreLayoutEngine.h

ICoreLayoutEngine -- where a layout puts its children, and nothing else.

One question, asked once per layout kind: given a rectangle to fill, a list of items and their size limits, what frame does each item get. The answers are free functions returning frames; nothing here owns a widget, touches a toolkit or knows what a widget IS.

⚠ THIS FILE NAMES NO TOOLKIT TYPE AND MOVES NOTHING. It computes rectangles. A backend takes the frames and sets them on its own native children -- which is the whole reason the family can be swapped: every toolkit can be told "put this child at this frame", and none of them agree about anything else. It also means the arithmetic is testable with no window, no widget and no event loop, which is what the test beside it does.

ICoreLayoutRect#

ICoreLayoutEngine.h:40 · struct · 0 declaration(s)

A frame, in the container's coordinates.

struct ICoreLayoutRect {
public:
    int x = 0;
    int y = 0;
    int width = 0;
    int height = 0;
};
};

ICoreLayoutMargins#

ICoreLayoutEngine.h:47 · struct · 0 declaration(s)

struct ICoreLayoutMargins {
public:
    int left = 0;
    int top = 0;
    int right = 0;
    int bottom = 0;
};
};

ICoreLayoutItem#

ICoreLayoutEngine.h:66 · struct · 0 declaration(s)

One thing a layout arranges: a widget, a nested layout, or a spacer.

struct ICoreLayoutItem {
public:
    int minimumPrimary = 0;
    int preferredPrimary = 0;
    int maximumPrimary = 0;          // 0 means "use kICoreLayoutUnbounded"

    int minimumCross = 0;
    int preferredCross = 0;
    int maximumCross = 0;            // 0 means "use kICoreLayoutUnbounded"

    // Share of the SURPLUS, once every item has its preferred size. 0 is the
    // common case and means "do not grow me unless nobody else wants it".
    int stretch = 0;

    // How the item sits ACROSS the layout's axis when it is narrower than the
    // rectangle it was given. The default of 0 means "fill", which is what a
    // layout does unless a call site says otherwise.
    unsigned int alignment = 0;

    // A spacer contributes size and takes no frame. Its frame comes back
    // zero-width and zero-height at the position it occupied, so a caller can
    // still walk items and frames in step.
    bool isSpacer = false;

    // PLACEHOLDER ONLY: the item stays in the list so a caller can still walk
    // items and frames in step, and it contributes NOTHING -- no size, no share
    // of the surplus, no gap beside it, and no frame.
    //
    // ⚠⚠ THIS IS A THIRD STATE AND NOT "AN ITEM WHOSE SIZES HAPPEN TO BE
    // ZERO", WHICH IS THE WHOLE REASON IT IS A FLAG. A zero-sized item still
    // counts in the gap arithmetic: icoreBoxLayoutFrames charges
    // `spacing * (n - 1)` and spreads leftover slack into `n + 1` gaps, so a
    // hidden child in a row of four leaves a hole exactly one spacing wide plus
    // a share of the slack, at the position it used to occupy. That hole is what
    // the owner sees at the head of the editor's top bar's first row when
    // `refreshMenuBarToggle()` puts its bar away (W10.16).
    //
    // It is `QLayoutItem::isEmpty()`, which is the other toolkit's answer for a
    // hidden widget and therefore the spec -- see ICoreLayoutSeatCore's banner
    // on why that toolkit's semantics are the spec here rather than a starting
    // point.
    //
    // ⚠ EVERY ENGINE IN THIS FILE HONOURS IT, and a half-done version would be
    // worse than none: a box that skipped an item while the grid beside it did
    // not would give two layouts of the same tree opposite answers about the
    // same widget, and neither would report anything.
    bool isIgnored = false;
};
};

ICoreBoxLayoutSpec#

ICoreLayoutEngine.h:135 · struct · 0 declaration(s)

-- the layouts ------------------------------------------------------------

struct ICoreBoxLayoutSpec {
public:
    ICoreOrientation orientation = ICoreOrientation::Vertical;
    ICoreLayoutMargins margins;
    int spacing = 0;

    // How the RUN OF ITEMS sits along the layout's own axis when the items
    // cannot absorb the whole content rectangle. 0 -- the default -- keeps the
    // measured toolkit behaviour: the slack is shared out into the n+1 gaps, so
    // a column of fixed-height rows spreads down the panel.
    //
    // ⚠⚠ AND IT WAS UNREADABLE FROM THE LAYOUT UNTIL NOW, WHICH IS WHY FOUR
    // CALL SITES IN THIS TREE ASKED FOR IT AND NONE OF THEM GOT IT.
    // ICoreVBoxLayout/ICoreHBoxLayout::setAlignment() has always taken this
    // question -- ICoreApplyButtonsBar asks for Right, ICoreTogglePanel and
    // ICoreSettingsPanelContainer for Top, the combo box's option list for Top
    // -- and the seat core stored the bits and passed them to the FORM engine
    // only. The visible cost: an Ok/Cancel bar whose two buttons sat a third of
    // the bar apart instead of together at the right, and a fixed-height nav
    // column whose four rows were spread over 240 pixels.
    //
    // ⚠ IT IS THE *LAYOUT'S* ALIGNMENT AND NOT AN ITEM'S. An item's own bits
    // (ICoreLayoutItem::alignment) say where THAT item sits inside the cell it
    // was given; this says where the whole run of cells sits inside the
    // container. Both are read, and neither replaces the other.
    unsigned int alignment = 0;
};
};

ICoreGridCell#

ICoreLayoutEngine.h:177 · struct · 0 declaration(s)

Where one cell sits in a grid.

struct ICoreGridCell {
public:
    int row = 0;
    int column = 0;
    int rowSpan = 1;
    int columnSpan = 1;
};
};

ICoreGridLayoutSpec#

ICoreLayoutEngine.h:184 · struct · 0 declaration(s)

struct ICoreGridLayoutSpec {
public:
    ICoreLayoutMargins margins;
    int horizontalSpacing = 0;
    int verticalSpacing = 0;

    // Per-column and per-row share of the surplus, by index. Short vectors are
    // fine: an index past the end is a stretch of 0.
    std::vector<int> columnStretch;
    std::vector<int> rowStretch;
};
};

ICoreFormLayoutSpec#

ICoreLayoutEngine.h:232 · struct · 0 declaration(s)

-- form ------------------------------------------------------------------

struct ICoreFormLayoutSpec {
public:
    ICoreLayoutMargins margins;

    // Between a row's label and its field.
    int horizontalSpacing = 0;

    // Between one row and the next.
    int verticalSpacing = 0;

    // How a label sits in the label column when it is narrower than the
    // column, and how both sit within the row's height. 0 fills, as elsewhere.
    unsigned int labelAlignment = 0;
};
};

ICoreFormRow#

ICoreLayoutEngine.h:250 · struct · 0 declaration(s)

One label-and-field pair.

struct ICoreFormRow {
public:
    ICoreLayoutItem label;
    ICoreLayoutItem field;
};
};

ICoreFlowLayoutSpec#

ICoreLayoutEngine.h:270 · struct · 0 declaration(s)

-- flow ------------------------------------------------------------------

struct ICoreFlowLayoutSpec {
public:
    ICoreLayoutMargins margins;
    int horizontalSpacing = 0;
    int verticalSpacing = 0;
};
};

ICoreLayoutSeatCore.h#

ICoreEssentials/UI/Portable/ICoreLayoutSeatCore.h

ICoreLayoutSeatCore -- what a layout REMEMBERS, and when it asks the engine.

The layout tier splits three ways, and this is the middle one:

ICoreLayoutEngine given a rectangle and some items, WHICH FRAMES. Arithmetic; no state, no toolkit. this class the retained half: the item list, the margins and the spacing, the ownership rules, the three ways of emptying a layout, the reverse lookups, and the decision of WHEN to re-run. the native access puts a frame on a native child and reads that child's size limits. One implementation per backend, and the only half a toolkit appears in.

ICoreLayoutConstraints#

ICoreLayoutSeatCore.h:55 · struct · 0 declaration(s)

A native child's size limits, as the layout tier sees them.

struct ICoreLayoutConstraints {
public:
    int minimumWidth = 0;
    int minimumHeight = 0;

    int preferredWidth = 0;
    int preferredHeight = 0;

    int maximumWidth = 0;
    int maximumHeight = 0;

    // The share of the surplus the WIDGET asks for, as distinct from the share
    // the layout gives it. A widget whose size behaviour expands wants room
    // even at stretch 0 -- see itemFor() in the .cpp, which is where the two
    // meet and where the precedence between them is decided.
    int horizontalStretch = 0;
    int verticalStretch = 0;
};
};

ICoreLayoutNativeAccess#

ICoreLayoutSeatCore.h:81 · class · 12 declaration(s)

What the retained half needs from the native tier, and the whole of it.

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

    // The child's limits, and where to put it. The frame is in the CONTAINER's
    // coordinates, y-down, integer pixels -- exactly what the engine returns.
    [[nodiscard]] virtual ICoreLayoutConstraints constraintsOf(const ICoreNativeWidget* child) const = 0;
    virtual void setFrame(ICoreNativeWidget* child, const ICoreLayoutRect& frame) = 0;

    // ⚠⚠ AND THE ONE LIMIT THAT IS A FUNCTION OF WHERE YOU PUT IT (W10.71).
    // Every number constraintsOf() answers is one the child holds whatever
    // rectangle it ends up in. A wrapped label holds no such number: its height
    // is a function of its width, so the only honest answer is a QUESTION,
    // asked at the width the layout is about to hand over.
    //
    // ⚠⚠ WITHOUT THESE TWO THE ANSWER ARRIVES ONE PASS LATE, AND "LATE" IS WHAT
    // THE DEFECT LOOKS LIKE ON SCREEN. A backend with no such question has only
    // the other order available: the layout hands out a width, the child
    // re-measures itself at the width it was just given, pushes a new hint, and
    // the layout re-runs (kMaxSettlePasses in the .cpp, and the deferral on
    // applyIn, are that loop). It converges when it is allowed to -- and when
    // the settle is exhausted, or when a pass-local memo served an answer taken
    // at a width the child no longer has, the container keeps a one-line height
    // while the child paints all of its lines. Text over text, and only
    // SOMETIMES, which is what a convergence loop looks like from outside. The
    // AppKit seat has asked its own views this question since the wrapped-label
    // fix (ICoreAppKitView::setHeightForWidthHook, and the vertical branch of
    // that seat's itemFor) and has never had the defect; this is the same
    // question on the shared seat, so the two cannot drift.
    //
    // ⚠ NEARLY EVERY CHILD ANSWERS false, AND THAT IS THE POINT. The pair costs
    // one virtual call per item in a VERTICAL arrangement and nothing at all
    // anywhere else, because `hasHeightForWidth` is the test: a backend that
    // answers false is served exactly as it was before -- the ordinary hint,
    // with the settle loop still behind it.
    //
    // ⚠ `offeredWidth` IS THE CHILD'S WHOLE WIDTH, frame edge to frame edge --
    // the same number setFrame() is about to be given -- and not a content
    // width. The child's own contents margins are the CHILD's to subtract, as
    // they are in every other answer on this interface.
    //
    // A child the backend cannot resolve answers false, and 0 for the height.
    // 0 is also how a child that answers the question but has nothing to say
    // spells it -- an empty label, a width of nothing -- and the core leaves
    // such an item on its ordinary hint rather than collapsing it.
    [[nodiscard]] virtual bool hasHeightForWidth(const ICoreNativeWidget* child) const = 0;
    [[nodiscard]] virtual int heightForWidth(const ICoreNativeWidget* child,
                                             int offeredWidth) const = 0;

    // Shown or hidden. A stacked layout shows one page and hides the rest, and
    // a drain hides what it is about to destroy.
    virtual void setVisible(ICoreNativeWidget* child, bool visible) = 0;

    // ⚠⚠ AND WHETHER IT IS SHOWN RIGHT NOW, WHICH THIS INTERFACE PUBLISHED
    // THE SETTER FOR AND NOT THE GETTER (W10.16). Without it the tier measures
    // and places a hidden child exactly as it does a shown one, and the other
    // toolkit does not: `QLayoutItem::isEmpty()` answers true for a hidden
    // widget and a QBoxLayout gives it no cell, no size and no spacing.
    //
    // What that cost, and it is visible in the product: the editor's top bar's
    // top row hides `menuBarToggleBar` when the window it is in does not own the
    // menu strip, and the row kept a hole one spacing wide at its head.
    // `ICoreTogglePanel::hideCancelOkButtons()` could not put its bar away at
    // all, because the only route it had -- `setParentWidget(nullptr)` -- is a
    // no-op on a backend that needs a parent Impl to adopt through.
    //
    // ⚠ IT IS THE WIDGET'S OWN FLAG AND NOT "IS ON SCREEN". A child inside a
    // collapsed ancestor is still `visible` here, exactly as `QWidget::isHidden`
    // is false for it -- asking about the ancestor chain would make a layout's
    // answer depend on a widget it does not hold, and re-running one layout
    // would then be able to change another's.
    //
    // A child the backend cannot resolve answers TRUE: "no opinion" has to mean
    // "lay it out", because the alternative silently drops widgets.
    [[nodiscard]] virtual bool isVisible(const ICoreNativeWidget* child) const = 0;

    // Make `child` a child of `container` in the NATIVE tree. The other
    // toolkit's addWidget() reparents, and a backend that did not would place
    // a widget correctly inside a parent it is not in: the frame is right, the
    // widget is invisible, and nothing reports either.
    virtual void adopt(ICoreNativeWidget* container, ICoreNativeWidget* child) = 0;

    // The container's own size, and its own contents margins.
    //
    // ⚠ TWO SETS OF MARGINS COMPOSE HERE AND THEY ARE NOT THE SAME MARGINS.
    // These are the WIDGET's, and they shrink the rectangle before the engine
    // ever sees it; the LAYOUT's own margins are applied by the engine inside
    // that rectangle. The other toolkit composes them the same way round -- a
    // QLayout is handed its widget's contentsRect and applies its own margins
    // in it -- so a backend that added them together would inset twice.
    virtual void containerSize(const ICoreNativeWidget* container, int& width, int& height) const = 0;
    virtual void containerContentsMargins(const ICoreNativeWidget* container,
                                          int& left, int& top, int& right, int& bottom) const = 0;

    // Call `run` whenever the container's size changes. THE RE-RUN IS THE
    // POINT: everything else here produces one correct layout and then never
    // another, which looks exactly like a correct layout until the window is
    // resized.
    virtual void setContainerResizeHook(ICoreNativeWidget* container,
                                        std::function<void(int width, int height)> run) = 0;

    // Hand `layout` to `container` to destroy when the container dies. This is
    // "a widget owns its layout" on a backend whose toolkit owns nothing for
    // us; a backend whose toolkit does own it implements this as a no-op and
    // says so there.
    virtual void adoptOwnedLayout(ICoreNativeWidget* container, ICoreNativeLayout* layout) = 0;

    // A label for a form row. The wrapper's addRow takes TEXT rather than a
    // widget, so the backend has to produce something showable; the core owns
    // what comes back and destroys it through destroyWidget below. Returning
    // null is allowed and means "this backend has nothing to show a label
    // with yet" -- the row still lays its field out, and the label column
    // still takes the width the access reported for it.
    [[nodiscard]] virtual ICoreNativeWidget* createFormLabel(const std::string& utf8) = 0;

    // Destroy a widget the layout was told to remove. Separate from a plain
    // `delete` at the call site because the other toolkit defers it to the
    // event loop, and a backend that destroyed it inside a drain would free a
    // widget whose event is still on the queue.
    virtual void destroyWidget(ICoreNativeWidget* child) = 0;
};
};

ICoreLineIntersect.h#

ICoreEssentials/UI/Portable/ICoreLineIntersect.h

Segment intersection, in this project's own arithmetic.

This exists to remove the ONE place in the geometry tier that delegated real work to the toolkit. icoreIntersect() in ICoreGeometryOps.h called QLineF::intersects, and that call is INLINE IN A HEADER -- so every file that used any geometry op pulled <QLineF> in behind it, including files under src/ICoreSDK, which R2 says never includes Qt. The census could not see it: those files name no Qt type, they include a header that does.

⚠ THE PREVIOUS COMMENT SAID NOT TO DO THIS, AND IT WAS RIGHT AT THE TIME. Its words: "Delegates to the toolkit rather than reimplementing: its parameterisation is an out-of-line Graphics-Gems-III variant that is not in the shipped headers, and the link router's optimised routes are pinned by the diagram export suites. Hand-rolling it would move wires for no gain."

File-scope declarations#

// How two segments meet. Bounded means they cross WITHIN both segments;
// Unbounded means their infinite extensions cross but the segments do not.
// 
// ⚠ These three enumerators are ICoreGeometryOps.h's ICoreLineIntersection,
// spelled again here so this core can be compiled and tested with nothing else
// on the include path. The two are converted at one site, in that header, and
enum class ICoreSegmentIntersection {
    None,
    Unbounded,
    Bounded,
};

ICoreMenuChrome.h#

ICoreEssentials/UI/Portable/ICoreMenuChrome.h

ICoreMenuChrome -- where the parts of a painted menu row go, and what colour each of them is.

THIS FILE NAMES NO TOOLKIT TYPE AND DRAWS NOTHING. ICoreMenuCore answers "how big is the panel and where does each ROW start"; this answers "inside one row, where does the tick go, where does the label go, where does the shortcut go, and what ink is each of them in". A backend takes the answers and paints. Same split as ICoreButtonChrome beside it, for the same reason.

WHY A SECOND CORE RATHER THAN MORE OF THE FIRST. ICoreMenuCore's suite is 205 checks of landed, green geometry (W3.3's arithmetic half); its header is a peer's file and its row struct is deliberately the minimum the LAYOUT needs -- a row's checkable and checked flags change no frame, so they are correctly absent from it. Painting needs them, and it needs the column

ICoreMenuRectF#

ICoreMenuChrome.h:52 · struct · 0 declaration(s)

A rectangle in LOGICAL pixels, with a fractional origin.

struct ICoreMenuRectF {
public:
    double x = 0.0;
    double y = 0.0;
    double width = 0.0;
    double height = 0.0;
};
};

ICoreMenuPaintMetrics#

ICoreMenuChrome.h:69 · struct · 0 declaration(s)

The two numbers ICoreMenu.cpp keeps that ICoreMenuMetrics does not, because neither of them moves a frame: they are spent INSIDE a row or ON the panel's own surface, and the core that sizes panels ...

struct ICoreMenuPaintMetrics {
public:
    // The identity's radiusPanel. ⚠ NOT the control radius the nav treatment
    // uses (button.radius, ~6) and NOT radiusTab (5). A menu drawn at the
    // canvas furniture's radius reads as another object dropped on the diagram
    // rather than as a layer above it -- one of the three things ICoreMenu.cpp's
    // comment records having got wrong once.
    double panelRadius = 12.0;

    // The tick/icon column is 26 wide and the icon inside it is 16.
    int iconSize = 16;

    // The panel's framing stroke. ⚠ IT IS A METRIC AND NOT A CONSTANT AT THE
    // PAINT SITE because TWO things are derived from it and they must not be
    // able to disagree: the pen the backend strokes with, and the half-pen
    // inset icoreMenuPanelSurface() takes so that the stroke lands inside the
    // panel instead of straddling its edge.
    double panelBorderWidth = 2.0;
};
};

ICoreMenuRowPaintState#

ICoreMenuChrome.h:94 · struct · 0 declaration(s)

Everything about ONE row that changes what it looks like, and that ICoreMenuRow does not carry because it changes no frame.

struct ICoreMenuRowPaintState {
public:
    bool highlighted = false;

    // A checkable row spends the lead column on a tick; every other row spends
    // it on an icon. Both are separate from ICoreMenuRow::needsLeadColumn,
    // which says the row WANTS the column -- these say what goes in it.
    bool checkable = false;
    bool checked = false;
    bool hasIcon = false;
};
};

ICoreMenuPalette#

ICoreMenuChrome.h:108 · struct · 0 declaration(s)

The theme's side of the question, so the core can be called with no theme manager standing.

struct ICoreMenuPalette {
public:
    ICoreRgba panelBackground;      // menu.background
    ICoreRgba panelBorder;          // interaction.selectionBorder
    ICoreRgba text;                 // menu.text
    ICoreRgba hoverText;            // menu.hoverText
    ICoreRgba disabledText;         // content.textDisabled
    ICoreRgba tertiaryText;         // content.textTertiary -- captions AND shortcuts
    ICoreRgba secondaryText;        // content.textSecondary -- the icon and the arrow
    ICoreRgba hairlineAccent;       // surface.hairlineAccent -- the separator
    ICoreRgba accent;               // interaction.accent -- the tick, at rest
    ICoreRgba barBackground;        // menu.barBackground -- the strip behind the titles

    // The nav treatment's two colours and their token alphas, which scale with
    // the hover progress while the colours do not. Same three fields
    // ICoreButtonPalette carries, and the same tokens.
    ICoreRgba hoverWash;            // button.hoverWash
    ICoreRgba hoverEdge;            // button.hoverEdge
    int hoverWashAlpha = 0;         // button.hoverWashAlpha
    int hoverEdgeAlpha = 0;         // button.hoverEdgeAlpha

    double controlRadius = 6.0;     // button.radius -- the nav treatment's corner
};
};

ICoreMenuRowInk#

ICoreMenuChrome.h:139 · struct · 0 declaration(s)

The colours one row is drawn with right now, after the state has been resolved.

struct ICoreMenuRowInk {
public:
    ICoreRgba label;
    ICoreRgba shortcut;
    ICoreRgba tick;                 // invalid unless the row draws one
    ICoreRgba icon;                 // invalid unless the row draws one
    ICoreRgba arrow;                // invalid unless the row has a submenu
    ICoreRgba caption;              // invalid unless the row IS a caption
    ICoreRgba separator;            // invalid unless the row IS a separator

    // The nav treatment, or nothing. `navProgress` is 0 on every row the
    // pointer is not on, and NOTHING is drawn at 0 -- that is the point of the
    // treatment rather than an optimisation, and it is why a menu at rest is
    // its own text and nothing else.
    ICoreRgba navWash;
    ICoreRgba navStroke;
    double navProgress = 0.0;
};
};

ICoreMenuRowLayout#

ICoreMenuChrome.h:181 · struct · 0 declaration(s)

Where the parts of one row go, given the row's own frame.

struct ICoreMenuRowLayout {
public:
    // The row's pill: the frame held off the panel's inner edge by rowInset on
    // each side. Everything except the nav treatment is laid out inside it.
    ICoreMenuRectF pill;

    // ⚠ THE NAV TREATMENT TAKES THE WHOLE FRAME, NOT THE PILL, and this is the
    // single easiest thing to get wrong in a second implementation. ICoreMenu.cpp
    // says so in a comment because it had to: the treatment draws its accent
    // edge from the origin of what it is given and carries its own body inset,
    // so handing it the pill insets the row twice and the accent bar lands four
    // pixels inside the panel instead of on its edge.
    ICoreMenuRectF navBounds;

    // The tick-or-icon column. Zero-width when the menu reserves no lead
    // column; its width is leadColumnWidth even on a row that puts nothing in
    // it, because the column is a PANEL decision and the labels of a mixed menu
    // have to line up.
    ICoreMenuRectF leadColumn;

    // The icon's 16x16 box inside the lead column. ⚠ The horizontal centring is
    // INTEGER division plus a one-pixel nudge -- (26 - 16) / 2 + 1 = 6, not 5 --
    // and the vertical centring is integer division with no nudge. Two
    // different roundings in one call, and both are measured.
    ICoreMenuRectF iconBox;

    // The label, after the shortcut column has been taken out of it.
    //
    // ⚠ ITS WIDTH CAN BE NEGATIVE, and that is reproduced rather than clamped.
    // A row whose shortcut is wider than the space left narrows the label past
    // zero; Qt hands the negative width to elidedText, which returns an
    // ellipsis. Clamping here would draw a label where the Qt menu beside it
    // draws none.
    ICoreMenuRectF labelRect;

    // The shortcut, right-aligned in the space the label came out of. Zero-width
    // when the row has no shortcut.
    ICoreMenuRectF shortcutRect;

    // A separator's rule: the pill inset by textInset on each side, one pixel
    // tall, on the row's vertical centre.
    ICoreMenuRectF separatorLine;

    // A caption's text rect: the pill inset by textInset on each side.
    ICoreMenuRectF captionRect;

    // The submenu arrow, as a triangle in x,y,x,y,x,y order.
    //
    // ⚠ IT IS NOT SYMMETRIC. The tip reaches 3.0 to the right of centre and the
    // two tails only 2.5 to the left, so the glyph's own centre of area sits
    // left of the point it is centred on. Measured from ICoreMenu.cpp; a
    // "corrected" symmetric triangle moves every submenu arrow in the product
    // half a pixel.
    double arrow[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
    bool hasArrow = false;
};
};

ICoreMenuBarTitleInk#

ICoreMenuChrome.h:280 · struct · 0 declaration(s)

A menu-bar title's ink.

struct ICoreMenuBarTitleInk {
public:
    ICoreRgba label;
    ICoreRgba navWash;
    ICoreRgba navStroke;
    double navProgress = 0.0;
};
};

ICoreMenuCore.h#

ICoreEssentials/UI/Portable/ICoreMenuCore.h

ICoreMenuCore -- where a menu's rows sit, how wide its panel wants to be, which corner it opens from when the screen edge is in the way, and which row the arrow keys move to next.

THIS FILE NAMES NO TOOLKIT TYPE AND MEASURES NO TEXT. Like the layout engine and the splitter core beside it, it computes rectangles and answers questions about a point and an index; a backend measures the strings, takes the frames, and paints.

WHY A CORE, WHEN W3.3 SAYS "ZERO NEW WORK". The row is right that the menu stays painted on Windows -- Windows has no system menu bar, and the in-window painted strip already IS the Windows behaviour. It is wrong that this costs nothing, and the arithmetic is where the cost is. ICoreMenu.cpp reaches the toolkit in five places that have nothing to do with drawing:

ICoreMenuRow#

ICoreMenuCore.h:82 · struct · 0 declaration(s)

One row, as the core needs to see it.

struct ICoreMenuRow {
public:
    ICoreMenuRowKind kind = ICoreMenuRowKind::Action;

    // MEASURED BY THE BACKEND: the advance of the row's label in the row's own
    // font -- theme().type.control for an action, theme().type.label for a
    // caption. A CAPTION IS DRAWN UPPERCASED, so measure the uppercased
    // string; measuring "Project" and drawing "PROJECT" under-measures the row
    // by the width of the case difference, which is where a caption picks up
    // an ellipsis nothing asked for.
    int labelWidth = 0;

    // MEASURED BY THE BACKEND: the advance of the shortcut text in
    // theme().type.caption. 0 means the row has no shortcut column at all --
    // it is the presence of the column that is being stated here, not just its
    // size, so a row whose shortcut somehow measures 0 must still pass 0.
    int shortcutWidth = 0;

    bool hasSubMenu = false;

    // Own flag rather than the widget's enabled state: a disabled toolkit
    // widget stops receiving pointer events, which would leave the previous
    // row highlighted while the pointer sits on this one. Same reason the Qt
    // row keeps its own.
    bool enabled = true;

    // A hidden row keeps its place in the index space and takes no height --
    // callers walk rows and frames in step, exactly as the splitter core walks
    // collapsed panes.
    bool hidden = false;

    // Whether this row needs the leading tick-or-icon column. The PANEL turns
    // the column on for every row once any one row asks for it, so the labels
    // of a mixed menu still line up -- see icoreMenuReservesLeadColumn.
    bool needsLeadColumn = false;
};
};

ICoreMenuMetrics#

ICoreMenuCore.h:121 · struct · 0 declaration(s)

Every number ICoreMenu.cpp keeps in its anonymous namespace, in one place a second backend can read.

struct ICoreMenuMetrics {
public:
    int rowHeight = 28;
    int separatorHeight = 9;
    int captionHeight = 22;
    int rowSpacing = 1;

    // How far a row's pill is held off the panel's inner edge, and how far the
    // label is held off the pill.
    int rowInset = 4;
    int textInset = 10;

    int leadColumnWidth = 26;      // the tick / icon column, when reserved
    int arrowColumnWidth = 18;     // the trailing submenu arrow
    int columnGap = 28;            // label to shortcut

    // Slack on every measured label. A font metric taken with no paint device
    // behind it can land a pixel or two under what the painter draws, and that
    // is the difference between a label and a label with an ellipsis.
    int textSlack = 6;

    int panelPadding = 8;
    int minPanelWidth = 176;
    int maxPanelWidth = 460;

    int anchorGap = 4;             // between a title and its drop-down
    int subMenuOverlap = 4;        // a submenu tucks slightly under its parent

    // Transparent room kept around the panel for its drop shadow. The popup
    // window is larger than the surface it paints, and anything past this
    // margin is cut off -- so it must clear the shadow's blur PLUS its
    // downward offset (24 + 6 in ICoreDropShadow). A backend that composites
    // its shadow instead of drawing it into the window sets this to 0.
    int shadowMargin = 32;

    // Hover timings, in milliseconds. Opening waits, so running the pointer
    // down a column does not flash every submenu on the way; closing waits
    // too, so a diagonal cut across a neighbouring row on the way to an open
    // submenu does not shut it in the pointer's face. They are here rather
    // than in either backend for the same reason every other number is: two
    // backends that pick their own feel different on the same build.
    int subMenuOpenDelayMs = 180;
    int subMenuCloseDelayMs = 150;
};
};

ICoreMenuSize#

ICoreMenuCore.h:165 · struct · 0 declaration(s)

struct ICoreMenuSize {
public:
    int width = 0;
    int height = 0;
};
};

ICoreMenuPoint#

ICoreMenuCore.h:170 · struct · 0 declaration(s)

struct ICoreMenuPoint {
public:
    int x = 0;
    int y = 0;
};
};

ICoreMenuBarMetrics#

ICoreMenuCore.h:320 · struct · 0 declaration(s)

-- the strip --------------------------------------------------------------

struct ICoreMenuBarMetrics {
public:
    int barHeight = 30;
    int barPaddingH = 8;
    int titleHeight = 22;
    int titlePaddingH = 10;
    int titleSpacing = 2;

    // Extra width on a title beyond its measured advance and its padding. A
    // title is centred in its pill, so the slack keeps it off both ends.
    int titleSlack = 4;
};
};

File-scope declarations#

// Three kinds share a row because they share the column: an action, a
// separator rule, and a caption that titles a group.
enum class ICoreMenuRowKind {
    Action,
    Separator,
    Caption,
};

// What the pointer arriving on a row does to the submenu that is open, if any.
// The whole state machine is three cases and one of them is easy to miss.
enum class ICoreMenuHoverAction {
    // The row owns a submenu that is not open: start the open countdown.
    OpenAfterDelay,
    // The row owns the submenu that IS open: cancel any pending close and do
    // nothing else. THIS IS THE CASE THAT IS EASY TO MISS. Without it the
    // pointer coming back onto the row that owns an open submenu restarts the
    // open countdown against a menu that is already open, and the submenu
    // closes and reopens under the pointer.
    KeepOpen,
    // A row that opens nothing: whatever is open should go, once the pointer
    // has stayed away long enough to mean it.
    CloseAfterDelay,
};

ICoreMenuInput.h#

ICoreEssentials/UI/Portable/ICoreMenuInput.h

ICoreMenuInput -- what a POINTER and a KEYBOARD do to an open menu: which row is lit, when a submenu opens, when the chain collapses, and which press takes the whole thing down.

THIS FILE NAMES NO TOOLKIT TYPE. It takes plain numbers and a description of the panels that are currently up, and answers with a record saying what the seat must then do; the seat owns the windows, the timers, the focus and the painting. It is the third of the three layers a painted menu needs, and the other two already existed:

ICoreMenuCore WHERE things are -- row frames, panel size, the three placements, the hit test, the arrow-key model and the three-case hover verdict. Arithmetic, no state. ICoreMenuChrome WHAT a row LOOKS like -- the pill, the columns, the

ICoreMenuOpenPanel#

ICoreMenuInput.h:80 · struct · 0 declaration(s)

One panel that is currently up.

struct ICoreMenuOpenPanel {
public:
    // The rows this panel shows, in the core's own vocabulary. Copied rather
    // than referenced for the reason the whole state is plain data: a menu can
    // be rebuilt by a hook while it is open, and a state holding a pointer
    // into the old rows would be a dangling read on the next move.
    std::vector<ICoreMenuRow> rows;

    // The PANEL in screen coordinates -- what icoreMenuPanelSize measured and
    // one of the three placements placed. ⚠ NOT the window: the window is that
    // rectangle grown by shadowMargin on every side (icoreMenuWindowSize), and
    // hit-testing the window instead makes a menu answer for the 32
    // transparent pixels around it, which is a press that lands on nothing
    // "landing on a row".
    ICoreLayoutRect panel;

    // The row wearing the highlight, or -1. At most one per panel, and the
    // panels above the deepest keep theirs -- the row that opened a submenu
    // stays lit for as long as the submenu is up.
    int highlighted = -1;

    // The row whose submenu is the panel BELOW this one, or -1 when this is
    // the deepest panel. Kept so that hovering back onto that row is
    // recognisable as KeepOpen rather than as a request to reopen what is
    // already open -- the case ICoreMenuHoverAction warns is easy to miss.
    int openSubMenuRow = -1;
};
};

ICoreMenuChainState#

ICoreMenuInput.h:109 · struct · 0 declaration(s)

Everything a chain remembers between inputs.

struct ICoreMenuChainState {
public:
    std::vector<ICoreMenuOpenPanel> panels;

    // The row counting down to open its submenu, as (panel, row), or -1 / -1.
    int pendingOpenPanel = -1;
    int pendingOpenRow = -1;

    // The panel counting down to close whatever it has open, or -1.
    int pendingClosePanel = -1;
};
};

ICoreMenuChainOutcome#

ICoreMenuInput.h:138 · struct · 0 declaration(s)

What one input did.

struct ICoreMenuChainOutcome {
public:
    // The chain used this input, so the routed event is Handled and does not
    // bubble.
    bool consumed = false;

    // A highlight moved, so some panel is stale. Repaint.
    bool repaint = false;

    // Countdown bookkeeping. Start and stop are separate fields rather than a
    // tri-state because one input can stop one countdown and start the other.
    bool startOpenTimer = false;
    bool stopOpenTimer = false;
    bool startCloseTimer = false;
    bool stopCloseTimer = false;

    // Open the submenu of this row NOW, and highlight the first action row of
    // the panel that appears when highlightFirstRowOfOpened is set.
    //
    // ⚠ IT IS TRUE FOR THE KEYBOARD AND FALSE FOR THE POINTER, exactly as the
    // Qt body has it. A submenu opened by hovering must not steal the
    // highlight: the pointer is still on the parent row, and the highlight
    // would jump out from under it.
    int openSubMenuPanel = -1;
    int openSubMenuRow = -1;
    bool highlightFirstRowOfOpened = false;

    // Close every panel BELOW this one, keeping it. -1 for nothing to close.
    int closeBelowPanel = -1;

    // The whole chain goes, root included.
    bool closeChain = false;

    // Run this row's action. ⚠ ALWAYS PAIRED WITH closeChain, and the order
    // is load-bearing: the menu must be gone before the action runs, because an
    // action may raise a panel or a dialog of its own and a menu still up
    // behind it is one more thing for that dialog to fight. Close, then run.
    //
    // ⚠ THE CHECKABLE TOGGLE IS THE SEAT'S, NOT THIS LAYER'S. ICoreMenuRow has
    // no checked flag -- checkable/checked live in ICoreMenuChrome's row,
    // because they are a PAINTING input and this layer never paints. A seat
    // toggles its own model on activation and repaints from the chrome.
    int activatedPanel = -1;
    int activatedRow = -1;

    // Walk the strip: +1 for the next title, -1 for the previous, 0 for no.
    // Set when an arrow key runs out of menu -- Right on a row that opens
    // nothing, Left in a root menu -- from wherever in the chain the highlight
    // happens to be, which is why it is answered here and not by the bar.
    int siblingDelta = 0;

    // Give this panel the keyboard back, or -1. Set when Left or Esc steps out
    // of a submenu: the panel that survives is the one the next key must reach,
    // and a seat that forgets it leaves the keyboard on a window it just
    // destroyed.
    int focusPanel = -1;
};
};

ICoreMenuBarInputState#

ICoreMenuInput.h:306 · struct · 0 declaration(s)

What the menu BAR remembers.

struct ICoreMenuBarInputState {
public:
    // The title whose menu is open, or -1.
    int openIndex = -1;

    // When the last menu closed, on the seat's own millisecond clock, or -1 if
    // none has. Only ever compared against a later reading of the same clock.
    std::int64_t lastClosedMs = -1;
};
};

ICoreMenuBarOutcome#

ICoreMenuInput.h:321 · struct · 0 declaration(s)

struct ICoreMenuBarOutcome {
public:
    bool consumed = false;

    // A title's appearance changed -- opened, closed, or the mnemonics coming
    // in or out with the bar's own state. Repaint the strip.
    bool repaint = false;

    // Open this title's menu, or -1. The seat places it with
    // icoreMenuDropDownOrigin under the title's frame.
    int openTitle = -1;

    // Close the open menu, whatever it is.
    bool closeOpen = false;

    // Whether the Alt letters should be underlined. They are advertised only
    // while the bar is in use, so a strip at rest reads as a strip rather than
    // as a keyboard reference -- true exactly while a menu is open.
    bool mnemonicsVisible = false;

    // Give the newly opened menu the keyboard. Set for the keyboard's own
    // openings -- a mnemonic, an arrow off the end of a menu -- and NOT for a
    // press, which leaves the focus where the user's hand already is.
    bool focusOpenedMenu = false;
};
};

File-scope declarations#

// The two countdowns, named so an outcome can say which one it means.
enum class ICoreMenuTimer { SubMenuOpen, SubMenuClose };

ICoreMotionFrameClock.h#

ICoreEssentials/UI/Portable/ICoreMotionFrameClock.h

ICoreMotionFrameClock#

ICoreMotionFrameClock.h:51 · struct · 0 declaration(s)

ICoreMotionFrameClock -- turning a frame TIMESTAMP into an ELAPSED reading.

struct ICoreMotionFrameClock {
public:
    // Whether a frame has been seen. Until then `elapsedMs` is 0, which is the
    // correct reading for a run started before the compositor ever ticked.
    bool haveFrame = false;

    // The last raw frame timestamp, in milliseconds.
    long long lastFrameMs = 0;

    // Milliseconds since the FIRST frame. This is the reading the animation
    // clock is driven with, and the one a run must be started from.
    long long elapsedMs = 0;

    // The wall-clock reading taken when the last frame was handled, in
    // milliseconds. Only icoreMotionFrameAdvanceAgainstWall() reads it.
    long long lastWallMs = 0;
};
};

ICoreMotionOwnerRegistry.h#

ICoreEssentials/UI/Portable/ICoreMotionOwnerRegistry.h

"Stop every animation belonging to this widget, and to everything beneath it" -- the question a toolkit's object tree used to answer for free.

⚠ WHY IT IS A REGISTRY AND NOT A WALK. On the Qt backend this was owner->findChildren<QAbstractAnimation*>(): an animation created with a parent WAS an object in that parent's tree, so the toolkit could hand back every one of them. Re-seating the Motion tier on the portable clock removed the last toolkit animation object from the tree, so that walk went on compiling, went on running, and started finding NOTHING -- silently, at the 23 ICoreGraphicsCommands::stopChildAnimationsOf* call sites, every one of which quiets an object about to be torn down or handed back to a pool. The association has to be KEPT rather than looked up. This is the smallest thing that keeps it.

File-scope declarations#

// "Is `candidate` the root itself, or anywhere beneath it?"
// 
// ⚠ ASKED UPWARD FROM THE WATCHER, NOT DOWNWARD FROM THE ROOT, and that is the
// cheap direction rather than a preference: a watcher's ancestry is a handful
// of hops, a root's descendants are the whole subtree, and there are far more
// widgets under a panel than there are animations on it. Both older backends
using ICoreMotionAncestryFn = std::function<bool(const void* candidate, const void* root)>;

ICoreMotionRuntimeCore.h#

ICoreEssentials/UI/Portable/ICoreMotionRuntimeCore.h

Declares no class of its own — see the file.

ICorePanelEdges.h#

ICoreEssentials/UI/Portable/ICorePanelEdges.h

ICorePanelEdges#

ICorePanelEdges.h:44 · struct · 1 declaration(s)

Which edges of a Surface::Panel carry its border, and the outline that then gets painted (A10.77).

struct ICorePanelEdges {
public:
    bool left = true;
    bool top = true;
    bool right = true;
    bool bottom = true;
    // Flare the two top corners outward (see above). Only drawn when `top` is
    // bare: a stroked top has no seam to flare into.
    bool topFillets = false;

    [[nodiscard]] bool all() const;
};
};

ICorePathBuilder.h#

ICoreEssentials/UI/Portable/ICorePathBuilder.h

ICorePathBuilder -- what an ICorePainterPath IS, once the toolkit is taken away: an ordered list of move / line / cubic elements, plus a fill rule.

THIS FILE NAMES NO TOOLKIT TYPE. It is the shared body of every non-Qt seat for ICorePainterPath, written for the WinUI one (W1.8) and deliberately put here rather than in the backend, for the reason §40.1 records: the AppKit colour seat had to be MOVED out of a backend zone because a second backend needed it, and doing that once is cheaper than doing it twice.

⚠ THIS IS A TRANSCRIPTION OF QPainterPath'S GEOMETRY, NOT A REDESIGN OF IT, and the difference is the whole point of the file. A rounded corner is four numbers, and "a reasonable circular arc" is not one answer but a family of them: Qt fits each 90° quadrant with a cubic at KAPPA and then re-parameterises a partial sweep by NEWTON'S METHOD on the resulting polynomial, which lands on

ICorePathBuilderElement#

ICorePathBuilder.h:62 · struct · 0 declaration(s)

⚠ RENAMED FROM ICorePathElement BY THE 2026-08-22 MERGE.

struct ICorePathBuilderElement {
public:
    double x = 0.0;
    double y = 0.0;
    ICorePathElementType type = ICorePathElementType::MoveTo;
};
};

ICorePathBuilder#

ICorePathBuilder.h:81 · struct · 0 declaration(s)

The builder's whole state.

struct ICorePathBuilder {
public:
    std::vector<ICorePathBuilderElement> elements;
    std::size_t subpathStart = 0;
    bool requireMoveTo = false;
    bool started = false;
};
};

ICorePathRect#

ICorePathBuilder.h:92 · struct · 0 declaration(s)

A rectangle, in the terms this file needs.

struct ICorePathRect {
public:
    double x = 0.0, y = 0.0, w = 0.0, h = 0.0;
};
};

File-scope declarations#

// Mirrors QPainterPath::ElementType exactly, values included, so the
// differential suite can compare the two lists without a translation table --
// and so that a mismatch is a mismatch rather than a mapping bug.
enum class ICorePathElementType : int {
    MoveTo = 0,
    LineTo = 1,
    CurveTo = 2,        // followed by exactly two CurveToData elements
    CurveToData = 3,
};

ICorePixelConvert.h#

ICoreEssentials/UI/Portable/ICorePixelConvert.h

Copy width x height 32-bit pixels from one ICorePixelLayout to another, row by row, honouring both row pitches. The one conversion every seat's ICorePixmap::writePixels() is built on: AppKit, gtk4 and WinUI store Bgra8Premultiplied, the web's canvas stores Rgba8.

Premultiplying rounds to nearest: c * a / 255. Un-premultiplying a pixel with alpha 0 gives 0,0,0,0. The same layout in and out is a plain row copy.

Declares no class of its own — see the file.

ICoreRgbaShade.h#

ICoreEssentials/UI/Portable/ICoreRgbaShade.h

ICoreRgbaShade -- lighter()/darker() on a toolkit-free colour value.

THIS FILE NAMES NO TOOLKIT TYPE. It exists because one painted control in this tree lightens its own fill as its whole answer to the pointer: ICoreButton's solid variants spend their hover animation on m_bgColor.lighter(tintedLighterFactor()) (ICoreButton.cpp, paintGlassBody), and theme.button.tintedHoverLighter is a token stated in exactly those units. A backend that cannot lighten a colour cannot paint that button, and ICoreRgba deliberately offers no colour-space maths of its own -- its header says so, and widening it would put an HSV pipeline inside the type the styling module keeps small on purpose.

⚠ THIS IS A PORT OF QColor::lighter(), NOT A REDESIGN OF IT, and the difference is the whole point of the file. The tokens were tuned by eye

Declares no class of its own — see the file.

ICoreSceneCore.h#

ICoreEssentials/UI/Portable/ICoreSceneCore.h

ICoreSceneCore -- what a retained scene KNOWS, and nothing about how it is drawn: which item is where, which one is on top, which one is under the pointer, what moved, and what therefore has to be repainted.

Seven questions, and they are the seven the board row names:

  • z-order -- in what order do items paint, and which is topmost
  • item flags -- movable / selectable / hoverable / clipping / a scale

the view may not apply

  • hit-testing -- which item is under this scene point, respecting clips
  • hover -- which item the pointer entered and which it left, as a

PAIR, so no enter is ever delivered without its leave

  • drag -- who holds the mouse grab, and how far a movable item

has been dragged since the press

ICoreScenePoint#

ICoreSceneCore.h:78 · struct · 0 declaration(s)

A point in some coordinate space.

struct ICoreScenePoint {
public:
    double x = 0.0;
    double y = 0.0;
};
};

ICoreSceneRect#

ICoreSceneCore.h:90 · struct · 0 declaration(s)

An axis-aligned rectangle, y-DOWN, given as an origin and a size.

struct ICoreSceneRect {
public:
    double x = 0.0;
    double y = 0.0;
    double width = 0.0;
    double height = 0.0;
};
};

ICoreSceneTransform#

ICoreSceneCore.h:107 · struct · 0 declaration(s)

An affine 2x3, in the order a point is mapped: x' = m11 * x + m21 * y + dx y' = m12 * x + m22 * y + dy ⚠ THE NAMING IS QMatrix/QTransform's, DELIBERATELY -- m12 is the y-shear of the x row, NOT "ro...

struct ICoreSceneTransform {
public:
    double m11 = 1.0;
    double m12 = 0.0;
    double m21 = 0.0;
    double m22 = 1.0;
    double dx = 0.0;
    double dy = 0.0;
};
};

ICoreSceneItemId#

ICoreSceneCore.h:186 · struct · 0 declaration(s)

A handle to an item, valid only in the scene that issued it.

struct ICoreSceneItemId {
public:
    int index = -1;
    int generation = 0;
};
};

ICoreSceneItem#

ICoreSceneCore.h:201 · struct · 0 declaration(s)

The state of a single item.

struct ICoreSceneItem {
public:
    // Where the item's own origin sits in its PARENT's coordinates -- scene
    // coordinates for a root item.
    ICoreScenePoint position;

    // The item's own area with its origin at (0,0), which is the same thing
    // ICoreGraphicsItem::contentBounds() means. It may start left of or above
    // the origin; a centred item usually does.
    ICoreSceneRect contentBounds;

    // Painting order among SIBLINGS. Ties break by insertion order, so a
    // scene that never sets a z value paints in the order it was built.
    double zValue = 0.0;

    // The item's own transform, applied about its origin, before its position
    // is added. Degrees clockwise; scale is uniform because every one of the
    // tree's setScale() sites is uniform and a two-axis scale would have to
    // answer which axis a pen width follows.
    double rotationDegrees = 0.0;
    double scale = 1.0;

    // An invisible item paints nothing, is hit by nothing and hovers nothing
    // -- AND NEITHER DO ITS CHILDREN, which is why every walk below prunes at
    // the first invisible ancestor rather than testing each item alone.
    bool visible = true;

    // -- the five flags the tier actually sets, as named booleans. §7 of the
    // -- board rejects mirroring Qt's 20-value enum, and the wrapper above
    // -- already spells them this way (setMovable, setSelectable, ...).

    // Take part in hover enter/leave at all. Off by default, matching
    // QGraphicsItem: a scene where every item hovered would repaint on every
    // pointer move.
    bool acceptsHover = false;

    bool acceptsDrops = false;

    // A press inside a movable item begins a drag that moves it by the
    // pointer delta -- see icoreSceneMouseMoved.
    bool movable = false;

    bool selectable = false;
    bool selected = false;

    // Qt's ItemIsFocusable: whether this item may hold the KEYBOARD focus.
    // Off by default, exactly as QGraphicsItem has it -- a scene where every
    // item were focusable would take the focus away from the one text item on
    // the first click anywhere.
    //
    // ⚠ THE FLAG IS THE ITEM'S; THE FOCUS ITSELF IS THE SCENE'S, and the two
    // are separate for the reason hover and grab are: focus is EXCLUSIVE, so
    // it cannot be a per-item bool without every setter having to walk the
    // whole table to clear the previous holder. `ICoreScene::focusItem` below
    // is the single place that answers "who has it".
    bool focusable = false;

    // Clip the item's own painting to its content bounds.
    bool clipsToShape = false;

    // Clip the item's CHILDREN to its content bounds. Separate from the above
    // because the tree sets them separately, and because a container that
    // clips its children usually paints its own frame past that edge.
    bool clipsChildrenToShape = false;

    // Paint under the clip the PARENT inherited, not the one the parent imposes
    // on its children -- so a child hanging outside a clipping parent's edge
    // (a side toolbar) stays visible while its siblings are clipped. Only the
    // parent's own clip is skipped; every clipping ancestor above it still
    // applies. Qt has no such flag; the canvas panels' reveal needs it.
    bool ignoresParentClip = false;

    // Qt's ItemIgnoresTransformations: the item keeps its own size and
    // orientation however the VIEW is zoomed, and only its POSITION follows.
    // Its ancestors' transforms still place it; the view's is cancelled.
    bool ignoresViewTransform = false;

    // Which buttons may start a grab on this item, as a bitmask of
    // ICoreMouseButton.
    //
    // ⚠⚠ THE DEFAULT IS SPELLED OUT AND USED TO BE A SENTINEL, AND THE SENTINEL
    // COST THE TIER A WHOLE CAPABILITY. It read *"Zero means the left button,
    // which is what an item that has never been asked expects"* -- true of the
    // default, and it made `setAcceptedMouseButtons(ICoreMouseButton::None)`
    // INDISTINGUISHABLE FROM NEVER HAVING CALLED IT, because that enumerator is
    // 0. So "this item accepts no button" could not be said at all.
    //
    // That is not a corner: it is what every DECORATION needs. An
    // ICoreGraphicsButton's caption, a table cell's label, a node's title --
    // each is a child item drawn over something clickable, each takes the grab
    // because the topmost item accepting the button wins, and each then
    // delivers nothing. The gallery works around it with a transparent shield
    // item over every scene button; the canvas works around it by forwarding
    // three gestures from every caption. Both workarounds exist because of this
    // line.
    //
    // Qt has no such problem -- it tests the mask plainly, so Qt::NoButton
    // means none. This is now that, exactly: the default is stated here, and
    // itemAcceptsButton is a mask test with no special case.
    //
    // ⚠⚠ AND THE DEFAULT IS EVERY BUTTON, NOT Left. THE LINE ABOVE USED TO READ
    // `ICoreMouseButton::Left` AND SAID *"QGraphicsItem stores Qt::LeftButton
    // as its default"*, WHICH IS NOT WHAT THAT CLASS DOES: its default is
    // Qt::MouseButtonMask -- every button -- and neither native seat sets one
    // of its own, so the whole editor was built and proved against "all".
    //
    // What the wrong default cost, and it is not a corner: `icoreScenePressed`
    // walks the hit stack and takes the FIRST item whose mask contains the
    // button, so with Right missing everywhere a right press matched nothing
    // and returned an invalid item. Every right-click gesture in the editor
    // went dead at once --
    //
    //     ICoreBlockViewFrame::mousePressed   branches on `Left || Right` and
    //                                         runs the selection logic that
    //                                         contextMenuRequested() below it
    //                                         says it depends on
    //     ICoreLinkBranchSegment::mousePressed  the same pair; the right press
    //                                         is how a junction is started
    //
    // -- with no error anywhere, because "no item accepted the button" is the
    // same code path as "the pointer was over empty space".
    //
    // 📌 *A default copied from a comment about another toolkit is a
    // measurement nobody took. The seat that keeps the toolkit's behaviour by
    // NOT setting the property is the one that records what the property was.*
    unsigned int acceptedMouseButtons = ~0u;

    // The caller's own identity for this item -- a row index, an enum, a
    // pointer cast by the backend. The core never reads it.
    long long tag = 0;
};
};

ICoreSceneSlot#

ICoreSceneCore.h:337 · struct · 0 declaration(s)

One slot in the scene's item table.

struct ICoreSceneSlot {
public:
    ICoreSceneItem item;

    // Bumped every time the slot is reused, so a handle from a previous
    // tenant fails to match.
    int generation = 0;

    bool occupied = false;

    // Index of the parent slot, or -1 for a root item. Children are held by
    // index in insertion order; z-order is applied when the draw list is
    // built, not here, so that a z change costs nothing until the next paint.
    int parent = -1;
    std::vector<int> children;
};
};

ICoreScene#

ICoreSceneCore.h:360 · struct · 1 declaration(s)

The retained scene: a table of slot records, a free list, and the interaction state that has to survive between events.

struct ICoreScene {
public:
    // ⚠ NOT `slots`, AND NOT `signals` OR `emit` EITHER -- those are Qt keyword
    // macros (`qtmetamacros.h` lines 42, 43, 51: `slots`->`Q_SLOTS`,
    // `emit`->nothing). This file never sees them, but every translation unit
    // in the LIBRARY is force-fed `ICoreEssentials/pch.h`, which reaches them
    // transitively -- so `scene.slots.size()` preprocesses to `scene. .size()`
    // there and this line preprocesses to a declaration that declares nothing,
    // silently deleting the member. The errors say "expected unqualified-id"
    // and never mention Qt. Cost this file a broken library build once; the
    // same trap is recorded at
    // `Backends/AppKit/Values/ICoreAppKitKeySequence.mm:346`.
    std::vector<ICoreSceneSlot> itemSlots;
    std::vector<int> freeSlots;

    // Roots, in insertion order.
    std::vector<int> roots;

    // The scene rectangle a caller SET, honoured only when `sceneRectExplicit`
    // is true. Otherwise icoreSceneBounds() computes the union of the items,
    // which is what a scene that was never given a rect does.
    ICoreSceneRect sceneRect;
    bool sceneRectExplicit = false;

    // -- interaction state

    // The item the pointer is currently inside, among those accepting hover.
    ICoreSceneItemId hoveredItem;

    // The item holding the mouse grab between a press and its release.
    ICoreSceneItemId grabItem;

    // Which button opened the grab, and where the press landed in SCENE
    // coordinates. The origin is kept so that a drag reports its total travel
    // and not just the last step -- a caller that integrates per-move deltas
    // accumulates rounding, which is visible as a dragged item lagging the
    // pointer.
    ICoreMouseButton grabButton = ICoreMouseButton::None;
    ICoreScenePoint grabScenePos;

    // Where the grabbed item's origin sat at the moment of the press, in its
    // parent's coordinates. A drag sets position = this + (now - press),
    // mapped into the parent -- never position += delta.
    ICoreScenePoint grabItemOrigin;

    // Whether the grab has actually moved yet. A press that is released
    // without motion is a click, not a zero-length drag.
    bool grabMoved = false;

    // The item holding the KEYBOARD focus, among those with `focusable` set.
    //
    // ⚠ ADDED FOR THE TEXT TIER (A4.2), WHICH IS THE FIRST THING ON THIS
    // BACKEND THAT CAN BE TYPED INTO. The scene had hover and grab because the
    // items that came first were clicked and dragged; a text item is FOCUSED,
    // draws its selection ring while it holds the focus, and is the target a
    // key press is routed to. All three are the same shape of state -- one
    // exclusive id, cleared when the item it names is removed or hidden -- so
    // this sits beside them rather than being invented somewhere else.
    ICoreSceneItemId focusItem;

    // -- repaint bookkeeping

    // Scene-coordinate rectangles changed since the last take. Coalesced on
    // the way in; see icoreSceneMarkDirtyRect.
    std::vector<ICoreSceneRect> dirtyRects;

    // Called when the dirty list GROWS -- the scene telling whoever is showing
    // it that something needs repainting. Empty is an ordinary state: a scene
    // nobody is showing damages nothing.
    //
    // ⚠⚠ THIS EXISTS BECAUSE MARKING THE CORE DIRTY IS ONLY HALF OF WHAT A
    // MUTATION OWES, AND THE OTHER HALF WAS LEFT TO EACH SEAT TO REMEMBER.
    // A dirty core repaints nothing on its own -- something has to turn the
    // damage into a toolkit invalidation. Every seat wrote that half itself:
    // AppKit as `icoreAppKitSceneDamaged()` -> `scheduleFlush()`, gtk4 as
    // `ICoreGtk4SceneNode::markDirty()` -> `notifyDamaged()` -> a hook the view
    // installs. The WinUI seat wrote the FIRST half four times --
    // `Graphics/ICoreGraphicsObject.cpp`, `ICoreGraphicsRect.cpp`,
    // `ICoreGraphicsText.cpp`, `ICoreGraphicsPixmap.cpp`, all of them
    // `if (host != nullptr) icoreSceneMarkDirty(host->scene(), id);` and
    // nothing else -- and never wrote the second, so on that seat NOTHING a
    // canvas item did ever scheduled a repaint. The only thing that painted a
    // WinUI canvas was the input path's one `requestRepaint()` per delivered
    // event, which is why an animation advanced only while the mouse happened
    // to be moving, a hover affordance that hid on a timer after the pointer
    // stopped never disappeared, and a correct `Ctrl+A` selected five blocks
    // and drew no rings at all. It read as a dead keyboard; it was a stale
    // screen. (W10.82; the gtk4 header had already written this paragraph for
    // its own seat, which is the tell that it belonged here instead.)
    //
    // ⚠ SO THE RULE LIVES WITH THE DIRTY LIST NOW, not in three seats' private
    // helpers: `icoreSceneMarkDirtyRect` fires this, and a seat's only job is
    // to install one. A seat that installs nothing behaves exactly as it did
    // before, which is what lets AppKit and gtk4 keep their existing pumps
    // until someone converges them deliberately.
    //
    // ⚠ IT MUST COALESCE, because it fires per mutation and a settling layout
    // makes thousands. Every seat's invalidation does so WHILE A LOOP EXISTS --
    // AppKit posts one flush, gtk4 queues one draw, WinUI's `requestRepaint()`
    // bottoms out in `icoreWinUIScheduleFlush()`, which posts at most one per
    // turn.
    //
    // ⚠⚠ AND "POSTS" IS A LIE IN A HEADLESS RUN, WHICH IS THE TRAP THIS HOOK
    // SETS FOR THE NEXT SEAT. All three seats' `ICoreMainThread::post()` have a
    // documented no-loop-YET fallback that RUNS THE CALLABLE INLINE, so with no
    // dispatcher queue -- every `--console` run -- "schedule a repaint" means
    // "paint now, on this stack, inside the mutation". The per-request
    // coalescing flag does not help, because the inline flush clears it before
    // the next mutation arrives, so the cost is one full synchronous paint PER
    // MUTATION. Measured on the WinUI seat the day this hook was written: the
    // regression suite went from minutes to still running at 43. A seat's hook
    // must therefore ask whether a loop exists before it schedules -- see
    // `ICoreGraphicsView::setScene` on the WinUI seat for the shape.
    // 📌 *A scheduler that degrades to an inline call turns "do this later"
    // into "do this now, re-entrantly", and only the headless run can see it.*
    //
    // ⚠ AND IT MUST NOT MUTATE THE SCENE. It is called with `scene` mid-update;
    // a hook that dirties, adds or removes an item re-enters this function.
    // Schedule, and do the work later.
    std::function<void()> damageHook;
};
};

ICoreSceneDrawCommand#

ICoreSceneCore.h:604 · struct · 0 declaration(s)

One entry in the paint list: an item, the matrix to concatenate before painting it, and the clip it inherits.

struct ICoreSceneDrawCommand {
public:
    ICoreSceneItemId id;

    // Item coordinates -> VIEW coordinates, view transform already folded in
    // (and cancelled again for an ignoresViewTransform item).
    ICoreSceneTransform transform;

    // The clip to install before painting, in VIEW coordinates, valid only
    // when `clipped` is true. It is the intersection of every clipping
    // ancestor's box, so a backend installs one rectangle rather than walking
    // the chain itself.
    ICoreSceneRect clipRect;
    bool clipped = false;
};
};

ICoreSceneHoverChange#

ICoreSceneCore.h:678 · struct · 0 declaration(s)

What a pointer move did to hover state.

struct ICoreSceneHoverChange {
public:
    ICoreSceneItemId left;      // invalid when nothing was hovered before
    ICoreSceneItemId entered;   // invalid when nothing is hovered now
    bool changed = false;
};
};

ICoreScenePressResult#

ICoreSceneCore.h:698 · struct · 0 declaration(s)

-- press, drag, release ----------------------------------------------------

struct ICoreScenePressResult {
public:
    ICoreSceneItemId item;   // what was pressed, invalid for empty scene space
    bool grabbed = false;    // whether the press opened a grab
    bool selectionChanged = false;

    // ⚠⚠ THE ITEM WHOSE EDIT THIS PRESS ENDS, AND THE SEAT MUST TELL IT SO
    // BEFORE DISPATCHING THE PRESS (`W10.89`, 2026-09-20). Invalid when nothing
    // held the focus, when the pressed item is the holder, or when the pressed
    // item can hold focus itself -- in that last case the dispatch hands the
    // focus over and clearing first would undo the hand-over.
    //
    // ⚠⚠ "CLICK AWAY TO COMMIT" IS A PRODUCT RULE AND IT LIVED IN ONE SEAT.
    // Every editable text item in this tree writes its value back in
    // `focusLosing()` -- ICoreBlockViewNameLabel puts the block's NAME there --
    // so the only path that commits an edit is the one that runs that hook. The
    // AppKit seat cleared the focus through the wrapper before dispatch and was
    // right; the WinUI seat did it AFTER the press had been delivered, behind
    // two further gates that fail silently; and the gtk4 seat did not do it at
    // all, so on Linux clicking away from a canvas label NEVER committed it.
    // Three seats, three answers, one rule -- so the rule is decided here, where
    // all three already call, and each seat only has to perform it.
    //
    // ⚠ THE SEAT STILL PERFORMS IT THROUGH THE WRAPPER'S clearFocus(), not by
    // writing `scene.focusItem`. Clearing the field directly leaves the item
    // that held it never hearing `focusLosing`, which is the commit -- the
    // AppKit seat's own banner is explicit that going through the wrapper is
    // what makes "click away to commit" true.
    ICoreSceneItemId releaseFocusFrom;
};
};

ICoreSceneDragResult#

ICoreSceneCore.h:735 · struct · 0 declaration(s)

struct ICoreSceneDragResult {
public:
    ICoreSceneItemId item;
    bool dragging = false;      // a grab is open and the pointer has moved

    // Total travel since the PRESS, in scene coordinates -- not since the last
    // move. See ICoreScene::grabScenePos for why.
    double totalDx = 0.0;
    double totalDy = 0.0;

    // True when this move actually moved a movable item.
    bool itemMoved = false;
};
};

ICoreSceneReleaseResult#

ICoreSceneCore.h:758 · struct · 0 declaration(s)

struct ICoreSceneReleaseResult {
public:
    ICoreSceneItemId item;
    bool wasDragging = false;   // the grab had moved: a drag, not a click
    bool released = false;      // a grab was actually open
};
};

ICoreSceneViewCore.h#

ICoreEssentials/UI/Portable/ICoreSceneViewCore.h

The VIEWPORT half of the graphics tier, with no toolkit in it: what a view's zoom and scroll mean, how a view point becomes a scene point, and which item hooks one pointer gesture has to reach and in what order.

⚠ IT NAMES NO TOOLKIT AND DRAWS NOTHING. ICoreSceneCore decides what is painted and in what order; ICoreWinUIPainter paints; this decides where the viewport is looking and where an event lands. Those are three separable questions and this file answers exactly the third.

⚠⚠ WHY THIS IS PORTABLE RATHER THAN A MEMBER OF THE VIEW SEAT. The other native backend put these same decisions INSIDE a toolkit-typed class (Backends/AppKit/Graphics/ICoreAppKitGraphicsView.h), which is why its scroll clamp, its anchor-under-pointer arithmetic and its hover ordering can only be tested on macOS. Every one of them is arithmetic over ICoreSceneCore and none

ICoreSceneViewState#

ICoreSceneViewCore.h:35 · struct · 1 declaration(s)

The view's own state.

struct ICoreSceneViewState {
public:
    // The viewport, in view coordinates. Zero until a frame arrives, and a
    // zero-sized viewport is a legal state that maps and clamps sanely rather
    // than dividing by anything.
    double viewportWidth = 0.0;
    double viewportHeight = 0.0;

    double zoom = 1.0;

    // ⚠ THE LIMITS ARE THE VIEW'S, NOT THE SCENE'S, and they are not Qt's --
    // QGraphicsView has none at all, so a caller can scale to 1e-300 and lose
    // the transform to floating point. The two call sites in this product that
    // zoom (the canvas and the chart) both clamp by hand today; the clamp moves
    // here so they cannot disagree. Setting them equal pins the zoom.
    double minimumZoom = 0.01;
    double maximumZoom = 100.0;

    // ⚠⚠ SCROLL IS THE VIEW'S LEFT/TOP EDGE IN SCALED SCENE COORDINATES, WHICH
    // IS THE VALUE OF A SCROLL BAR ON THE OTHER TOOLKIT. It is not a scene
    // point and it is not a pixel offset from the scene origin; it is what
    // ICoreGraphicsView's sixteen call sites already mean by horizontalScroll(),
    // and nothing converts on the way through. The AppKit seat states the same
    // definition for the same reason, having measured it first.
    double scrollX = 0.0;
    double scrollY = 0.0;

    // QGraphicsView::AnchorUnderMouse vs AnchorViewCenter. Off by default,
    // exactly as the wrapper's setZoomAnchoredUnderPointer() documents: both
    // views that zoom ask for it, and it is not what a view that says nothing
    // gets.
    bool zoomAnchoredUnderPointer = false;

    // Whether scroll is held inside the scene's own extent. On by default,
    // because a view that lets scroll run free passes every check written
    // against it and then pans a canvas into empty space at swap time.
    bool clampScrollToScene = true;

    // What the scene covers, in scene coordinates.
    //
    // ⚠⚠ IT IS IN THE STATE RATHER THAN A PARAMETER, AND THAT IS THE ONE
    // STRUCTURAL DECISION IN THIS FILE. A fitting axis does not scroll and its
    // content is CENTRED, so the view's left edge is not `scrollX` on that axis
    // -- and knowing whether an axis fits needs the scene's extent. Every
    // mapping would therefore have to take the bounds, or the edge would have
    // to be cached beside the scroll and refreshed by whoever remembered to.
    // The second is a second source of truth for a number two dozen call sites
    // read; the first puts an argument on mapToScene() that an overlay placing
    // itself does not have. So the bounds are pushed here once, by the seat,
    // whenever the scene changes -- which is exactly when the other toolkit
    // recomputes its scroll range.
    //
    // ⚠ AN EMPTY BOUNDS MEANS "NOTHING TO SCROLL OVER" and pins both axes at
    // zero. It is the honest state of a view with no scene, and it is not the
    // same as a scene whose bounds happen to be at the origin.
    ICoreSceneRect sceneBounds;

    // Where the pointer last was, in VIEW coordinates, and whether it is
    // inside at all.
    //
    // ⚠ IT IS KEPT IN VIEW COORDINATES RATHER THAN SCENE, and the difference is
    // load-bearing: a zoom with nothing moving must still re-hover the item now
    // under the unchanged pointer, and only the view coordinate is unchanged
    // across a zoom. Storing the scene point would re-hover at the OLD scene
    // position, which is the item the pointer just stopped being over.
    double pointerViewX = 0.0;
    double pointerViewY = 0.0;
    bool pointerInside = false;

    // The button that opened the grab and the modifiers that came with it, so
    // a delivery can carry them. The core's own press/release functions track
    // the grab; these are what the EVENT looked like.
    ICoreMouseButton pressedButton = ICoreMouseButton::None;
    ICoreKeyModifiers pressedModifiers = ICoreKeyModifiers();
};
};

ICoreSceneViewDelivery#

ICoreSceneViewCore.h:127 · struct · 0 declaration(s)

One call the caller must make on one item's wrapper.

struct ICoreSceneViewDelivery {
public:
    ICoreSceneViewEventKind kind = ICoreSceneViewEventKind::Moved;
    ICoreSceneItemId item;

    // Where it happened, in SCENE coordinates. The caller maps it into the
    // item's own coordinates with icoreSceneMapFromScene(); that is one call
    // and it needs the scene, which this struct deliberately does not carry.
    //
    // ⚠ A PointerLeft CARRIES NO USEFUL POINT and the wrapper's hook takes
    // none. It is left at the pointer's current position rather than zeroed,
    // so a caller that does read it gets something true.
    ICoreScenePoint scenePoint;
};
};

ICoreSceneViewOutcome#

ICoreSceneViewCore.h:172 · struct · 0 declaration(s)

⚠ THERE IS DELIBERATELY NO consumed FLAG HERE, and it was removed rather than never written.

struct ICoreSceneViewOutcome {
public:
    // The view point that came in, mapped. Always filled, including for a
    // gesture that delivers nothing.
    ICoreScenePoint scenePoint;

    // The topmost item under the point, or invalid. This is the HIT TEST, and
    // it is not the same as the item a delivery goes to: during a grab, every
    // move goes to the grabber whatever is under the pointer.
    ICoreSceneItemId itemAt;

    // The view's own transform moved -- zoom or scroll changed. The caller
    // repaints everything and re-places any overlay.
    bool viewChanged = false;

    // Something in the scene needs redrawing. Hover and selection are drawn,
    // so both set this.
    bool repaint = false;

    // ⚠⚠ THE ITEM WHOSE EDIT THIS PRESS ENDS, CARRIED UP FROM
    // ICoreScenePressResult (`W10.89`). The seat must tell it -- through the
    // WRAPPER's clearFocus(), which is what runs `focusLosing()` and therefore
    // what COMMITS the edit -- BEFORE it runs the deliveries below. After is too
    // late: the press hook it would run first is free to change the selection,
    // hide overlays or re-parent items, and every one of those can make the
    // decision unrecoverable. Invalid when this press ends no edit.
    ICoreSceneItemId releaseFocusFrom;

    bool selectionChanged = false;
    bool itemMoved = false;      // a grab dragged a movable item
    bool grabOpened = false;
    bool grabClosed = false;
    bool wasDragging = false;    // a release that ended a real drag, not a click

    ICoreSceneViewDelivery deliveries[kICoreSceneViewMaxDeliveries];
    int deliveryCount = 0;

    // ⚠⚠ THE DELIVERIES ARE A STACK TO WALK, NOT A LIST TO RUN. The caller must
    // stop at the first delivery whose hook returns handled -- everything below
    // it in the stack is an item that was under the pointer and did NOT get the
    // notch.
    //
    // ⚠ *"AND ONLY THE WHEEL SETS THIS"* stood in the line above and stopped
    // being true twice: the double click joined at `W10.87`, and the RIGHT
    // PRESS at `W10.104`. It is quoted rather than deleted because the rule it
    // states is unchanged -- only the list of users grew.
    //
    // ⚠ IT IS A FLAG RATHER THAN A SECOND ARRAY BECAUSE THE OTHER GESTURES
    // GENUINELY WANT EVERY DELIVERY RUN. A pointer move's two hover hooks are
    // a LEFT and an ENTERED on two different items and both must fire; running
    // only the first would leave an item wearing a hover it no longer has.
    // Collapsing the two meanings into one loop with no flag is how that
    // regression gets written.
    //
    // ⚠ AND IT IS **NOT** THE `consumed` FLAG THE NOTE ABOVE REFUSES. That one
    // would have claimed to know whether a hook handled the event, which this
    // file cannot call and therefore cannot know. This says what the CALLER
    // should do with the answers it collects, which is a routing rule and is
    // exactly this file's business.
    bool deliverUntilHandled = false;

    // ⚠⚠ WHERE THE STACK STARTS, BECAUSE ONE GESTURE IS BOTH SHAPES AT ONCE
    // (`W10.104`, 2026-09-21). A right press delivers a `Pressed` that must
    // ALWAYS run -- it is what runs the selection logic the editor's context
    // menus are documented to depend on -- and then offers `ContextMenu` down
    // the hit stack until one item answers. With a single flag the two cannot
    // both be expressed: setting it would let a handled press swallow the menu,
    // and clearing it would open every menu under the pointer at once.
    //
    // Deliveries BELOW this index are a list and every one of them runs.
    // Deliveries from it onward are the stack `deliverUntilHandled` describes.
    // Zero -- the default -- means the whole array is the stack, which is
    // exactly what the wheel and the double click already asked for, so neither
    // of them changed.
    //
    // 📌 *A flag answers "which rule", an index answers "from where". The
    // gesture that needed both is the one that proved the flag was a rule with
    // an unstated scope.*
    int deliverUntilHandledFrom = 0;
};
};

File-scope declarations#

// What one gesture asks the view to do.
enum class ICoreSceneViewEventKind {
    Pressed,
    Moved,           // a grab is open and the pointer moved: the drag hook
    Released,
    DoubleClicked,
    PointerEntered,
    PointerMoved,    // hover movement inside the item already hovered
    PointerLeft,
    ContextMenu,
    WheelScrolled
};

ICoreScrollCore.h#

ICoreEssentials/UI/Portable/ICoreScrollCore.h

ICoreScrollCore -- how far a scroll pane may scroll, where its thumb sits, what is under a point on the bar, and where a drag of that thumb lands.

THIS FILE NAMES NO TOOLKIT TYPE AND MOVES NOTHING. Like the splitter core and the layout engine beside it, it answers with numbers and rectangles; a backend takes them and paints, or positions a native child with them.

WHY A CORE AND NOT A CONTROL. On Qt every number here belongs to somebody else: QScrollArea sets the range from the content and the viewport, QAbstractSlider clamps and steps the value, and QStyle turns the value into a thumb rectangle. ICoreScrollPane forwards and owns none of it. A backend with no QScrollArea inherits nothing, so a WinUI seat either reproduces the arithmetic or the two backends scroll by different amounts from the same wheel -- which is the kind of divergence nobody files a bug about and

ICoreScrollModel#

ICoreScrollCore.h:55 · struct · 0 declaration(s)

One axis of a scrolling pane.

struct ICoreScrollModel {
public:
    int contentExtent = 0;
    int viewportExtent = 0;

    // Where the viewport's leading edge sits inside the content. Always
    // between 0 and icoreScrollMaximum(); the functions below never return an
    // unclamped one, and icoreScrollClampValue() is there for a caller holding
    // a value from before a resize.
    int value = 0;

    // What one arrow press, one keyboard line or one wheel notch moves.
    //
    // MEASURED, not chosen: 20. It is QScrollArea's own default and it does
    // NOT come from the content -- a pane of 200px rows and a pane of 5000px
    // rows both step 20, because QAbstractScrollArea sets the number once and
    // never derives it. A backend that helpfully derived it from a row height
    // would scroll a different distance from the same key on Windows only.
    int singleStep = 20;

    // How many single steps one wheel notch moves.
    //
    // MEASURED: QApplication::wheelScrollLines() is 3 here, so a notch is 60px
    // with the step above. This is a SYSTEM setting on both platforms and the
    // backend should read it rather than keep 3 -- the field exists so that a
    // backend CAN, and the default is what an unconfigured Windows reports.
    int wheelLinesPerNotch = 3;
};
};

ICoreScrollBarSpec#

ICoreScrollCore.h:205 · struct · 0 declaration(s)

A scroll bar's own geometry, independent of any particular bar.

struct ICoreScrollBarSpec {
public:
    int thickness = 6;

    // The floor under the thumb, so a long document still leaves something to
    // grab. ⚠ It is also the number that makes a drag NON-PROPORTIONAL: once
    // the thumb stops shrinking, the distance it travels is the groove minus
    // the thumb, not the groove. See icoreScrollValueFromThumbOffset.
    int minimumThumbExtent = 20;

    // The stepper button at each end. ZERO IS THIS TREE'S BAR: the sheet gives
    // both line buttons a zero extent, so the groove is the whole bar and
    // there are THREE regions on it, not five.
    //
    // ⚠ A ONE-WORD "FIX" HERE BRINGS THE BUTTONS BACK. The sheet zeroes the
    // horizontal buttons with `width: 0px` and the vertical ones with
    // `height: 0px` -- the cross-axis spelling in each case. Adding the other
    // dimension to the horizontal rule (`width: 0px; height: 0px`) makes Qt
    // restore a 7x2 button and pull the groove in by 12px on each end.
    // Measured, twice, because it reads like the more thorough rule.
    int buttonExtent = 0;
};
};

ICoreScrollBarGeometry#

ICoreScrollCore.h:240 · struct · 0 declaration(s)

Every rectangle a backend needs to paint one bar, in the bar frame's own coordinates.

struct ICoreScrollBarGeometry {
public:
    ICoreLayoutRect subButton;
    ICoreLayoutRect groove;      // what is left between the two buttons
    ICoreLayoutRect subPage;
    ICoreLayoutRect thumb;
    ICoreLayoutRect addPage;
    ICoreLayoutRect addButton;
};
};

File-scope declarations#

// Which part of the bar a point landed on.
enum class ICoreScrollRegion : int {
    None = 0,        // not on the bar at all
    SubButton,       // the leading stepper -- absent from this tree's bar
    SubPage,         // the trough before the thumb: one page back
    Thumb,           // the draggable part
    AddPage,         // the trough after the thumb: one page forward
    AddButton,       // the trailing stepper -- absent from this tree's bar
};

ICoreScrollInput.h#

ICoreEssentials/UI/Portable/ICoreScrollInput.h

ICoreScrollInput -- what a pointer DOES to a scrollbar: which region is lit, which is held, where a thumb drag lands, and how a held trough keeps paging.

THIS FILE NAMES NO TOOLKIT TYPE. It takes plain numbers and answers with a record of what to do; a backend does the capturing, the painting and the timer. It is the third of the three layers a painted scrollbar needs, and the other two already existed:

ICoreScrollCore WHERE everything is and WHAT each move is worth -- the maximum, the page and wheel steps, the thumb's extent, travel and offset, the drag and press mappings. Qt's own arithmetic, integer for integer, checked against a live Qt on this machine 46 005 times with 0 disagreements. ICoreWidgetCore WHICH pointer inputs reach a widget at all -- the

ICoreScrollInputState#

ICoreScrollInput.h:56 · struct · 0 declaration(s)

Everything a scrollbar remembers between inputs.

struct ICoreScrollInputState {
public:
    // The region under the pointer, or None. During a thumb drag this is
    // PINNED to Thumb -- see the routing rules.
    ICoreScrollRegion hoveredRegion = ICoreScrollRegion::None;

    // The region being held, or None for no gesture in progress.
    ICoreScrollRegion pressedRegion = ICoreScrollRegion::None;

    // ⚠ HOW FAR INTO THE THUMB THE POINTER TOOK HOLD, CAPTURED ONCE AT PRESS
    // AND HELD FOR THE WHOLE DRAG. `ICoreScrollCore`'s own header is explicit
    // about why: the position/value round trip is NOT an identity -- a 278px
    // travel cannot address 3701 distinct values, and 3422 of them come back as
    // a neighbour -- so a drag that re-derives the grab offset from the value
    // it just wrote makes the thumb jitter under the pointer. Measured there,
    // held here.
    int grabOffsetInThumb = 0;

    // Where the pointer was at the last Press or Move, along and across the
    // axis, in the bar frame's own coordinates.
    //
    // ⚠ IT IS REMEMBERED RATHER THAN PASSED because the auto-repeat needs it
    // and the pointer is not moving. A trough held down with a still hand
    // produces no further Moves at all, and the tick has to be able to ask
    // "has the thumb reached the pointer yet" -- see the RepeatTick rule.
    int pointerX = 0;
    int pointerY = 0;

    // Whether an auto-repeat is armed. The backend owns the timer; this is the
    // record of having asked for one, so a second Press cannot start two.
    bool repeating = false;
};
};

ICoreScrollInputOutcome#

ICoreScrollInput.h:89 · struct · 0 declaration(s)

What one input did.

struct ICoreScrollInputOutcome {
public:
    // The scrollbar used this input, so the routed event is Handled.
    bool consumed = false;

    // The bar's appearance changed -- a region lit, unlit, pressed, released,
    // or the thumb moved. Repaint.
    bool repaint = false;

    // `model.value` was changed. The model is updated in place; this says so,
    // so a seat can scroll its content and emit exactly when something moved.
    bool valueChanged = false;

    // Start or stop the auto-repeat timer. Never both in one outcome. The two
    // intervals are below.
    bool startAutoRepeat = false;
    bool stopAutoRepeat = false;
};
};

File-scope declarations#

// The pointer inputs a scrollbar cares about.
// 
// `RepeatTick` is the auto-repeat firing: a trough held down pages again. It
// is an input rather than something this file schedules, because a timer is a
// toolkit object and this file has none -- the outcome says when to start and
// stop one, and the backend owns it.
enum class ICoreScrollPointerInput { Move, Press, Release, Leave, Cancel, RepeatTick };

ICoreScrollPaneChrome.h#

ICoreEssentials/UI/Portable/ICoreScrollPaneChrome.h

ICoreScrollPaneRect#

ICoreScrollPaneChrome.h:19 · struct · 0 declaration(s)

Where a graphics scroll pane puts its viewport and its bar.

struct ICoreScrollPaneRect {
public:
    double x = 0.0;
    double y = 0.0;
    double width = 0.0;
    double height = 0.0;
};
};

ICoreScrollPaneChromeMetrics#

ICoreScrollPaneChrome.h:35 · struct · 0 declaration(s)

The pane's chrome measurements.

struct ICoreScrollPaneChromeMetrics {
public:
    double barWidth = 8.0;
    double barMargin = 2.0;      // between the bar and the pane's right edge
    double contentGap = 4.0;     // between the content's right edge and the bar
};
};

ICoreSpinBoxChrome.h#

ICoreEssentials/UI/Portable/ICoreSpinBoxChrome.h

ICoreSpinBoxChrome -- where a spin box's two chevrons sit, which one a point is on, and what each is drawn in.

THIS FILE NAMES NO TOOLKIT TYPE AND DRAWS NOTHING, like every other unit in this directory. It is the second half of what W2.2 owes the branded controls: ICoreButtonChrome holds the button's decisions, and this holds the one other painted control in the widget tier whose geometry is entirely its own.

WHY IT IS NOT PART OF THE FIELD. The field, its value, its focus ring and its hover border are the text-entry base's and are the same in every field in the app; the chevrons are drawn ON TOP by paintContent, and the spin box has no children, so nothing can cover them. That split is Qt's today and it is the reason this core describes a COLUMN inside a control rather than a

ICoreSpinBoxRect#

ICoreSpinBoxChrome.h:48 · struct · 2 declaration(s)

A rectangle in WHOLE pixels, with the toolkit's inclusive-edge convention for containment.

struct ICoreSpinBoxRect {
public:
    int x = 0;
    int y = 0;
    int width = 0;
    int height = 0;

    bool isEmpty() const;

    // ⚠ INCLUSIVE on the right and bottom, matching QRect::contains -- a point
    // at x + width - 1 IS inside. QRectF would say the opposite about the same
    // number, which is why the two rect types in this directory do not share
    // one containment rule.
    bool contains(int px, int py) const;
};
};

ICoreSpinBoxChevron#

ICoreSpinBoxChrome.h:84 · struct · 0 declaration(s)

The three points of one chevron, as x,y pairs: left, apex, right.

struct ICoreSpinBoxChevron {
public:
    double x[3] = {0.0, 0.0, 0.0};
    double y[3] = {0.0, 0.0, 0.0};
};
};

ICoreSpinBoxArrowInk#

ICoreSpinBoxChrome.h:99 · struct · 0 declaration(s)

What one chevron is drawn in, once its state is known.

struct ICoreSpinBoxArrowInk {
public:
    // Invalid when the chevron takes no wash, which is the resting state --
    // and "no wash" rather than "a transparent wash", so a backend cannot draw
    // an invisible rounded rectangle and call it the same thing.
    ICoreRgba wash;
    ICoreRgba chevron;
};
};

ICoreSpinBoxPalette#

ICoreSpinBoxChrome.h:108 · struct · 0 declaration(s)

The theme's side of the question.

struct ICoreSpinBoxPalette {
public:
    ICoreRgba controlFillHover;   // interaction.controlFillHover
    ICoreRgba pressedFill;        // interaction.pressedFill
    ICoreRgba textPrimary;        // content.textPrimary
    ICoreRgba textSecondary;      // content.textSecondary
    ICoreRgba textDisabled;       // content.textDisabled
};
};

File-scope declarations#

enum class ICoreSpinBoxArrow { None, Up, Down };

ICoreSpinBoxInput.h#

ICoreEssentials/UI/Portable/ICoreSpinBoxInput.h

ICoreSpinBoxInput -- what a spin box ACCEPTS and what it becomes, as distinct from where its chevrons sit.

ICoreSpinBoxChrome next door answers the geometry: which rectangle each chevron gets, which one a point is in, how a chevron is stroked. This file answers the other half -- which keystrokes the field may receive, what the value clamps to, what a step does, and what an unparsable field commits to.

THIS FILE NAMES NO TOOLKIT TYPE. Every function takes plain numbers or a single character and returns plain numbers, so all of it is checkable with no field, no window and no event loop.

⚠ WHY IT EXISTS, given that the geometry already had a core. Everything here was inline in the AppKit seat, each rule with a paragraph of reasoning and

ICoreSpinBoxRange#

ICoreSpinBoxInput.h:28 · struct · 0 declaration(s)

The range and step a spin box is currently working in.

struct ICoreSpinBoxRange {
public:
    int minimum = 0;
    int maximum = 99;
    int singleStep = 1;
};
};

ICoreSplitterCore.h#

ICoreEssentials/UI/Portable/ICoreSplitterCore.h

ICoreSplitterCore -- where a splitter's panes and grab bars sit, how far a grab bar may travel, and what a drag does to the panes it is NOT touching.

THIS FILE NAMES NO TOOLKIT TYPE AND MOVES NOTHING. Like the layout engine beside it, it computes rectangles and answers questions about a point; a backend takes the frames and paints or positions with them.

WHY A CORE AND NOT A CONTROL. On Qt this behaviour is QSplitter's, and none of it is ours. WinUI has no splitter at all -- W0.3's XAML interpreter says so out loud: icoreWinUIPropertiesFor(SplitterHandle) returns an EMPTY property list, meaning "this backend answers by painting". Painting it means owning the arithmetic Qt was doing for us, and the arithmetic is the half that has to agree between backends, so it lives here rather than in either of them.

ICoreSplitterPane#

ICoreSplitterCore.h:51 · struct · 0 declaration(s)

One pane of a splitter, along the splitter's axis.

struct ICoreSplitterPane {
public:
    int extent = 0;
    int minimumExtent = 0;
    int maximumExtent = 0;          // 0 means "use kICoreLayoutUnbounded"

    // Share of the surplus once every pane has its sizer. 0 means "keep your
    // size unless nobody else wants the space", which is how a call site pins
    // an explorer pane while the editor beside it takes the slack.
    int stretch = 0;

    // A collapsed pane takes zero pixels and KEEPS ITS SIZER, so restoring it
    // is one flag away. It still has a handle: that is the only thing left to
    // drag it back out by.
    bool collapsed = false;

    // Whether a drag may collapse it in the first place. A pane that is not
    // collapsible stops at its minimum.
    bool collapsible = true;
};
};

ICoreSplitterSpec#

ICoreSplitterCore.h:77 · struct · 0 declaration(s)

NO hidden FLAG, deliberately.

struct ICoreSplitterSpec {
public:
    // Horizontal lays the panes out left to right with upright grab bars
    // between them; Vertical stacks them with flat bars. Same sense as the box
    // layout's orientation, and the same sense as QSplitter's.
    ICoreOrientation orientation = ICoreOrientation::Horizontal;

    ICoreLayoutMargins margins;

    // What the grab bar OCCUPIES, and therefore what gets painted.
    //
    // THE THEME DOES NOT SAY. It is worth being exact about this, because the
    // spec looks like it does: ICoreThemeStyleSpecs::splitPane sets
    // fixedWidth/fixedHeight to 1.0 -- but on the `Self` rule, which is the
    // SPLITTER, not the bar. The `SplitterHandle` rule beside it states a
    // background and a border and no size at all, and neither does the QSS.
    // On Qt the number comes from the toolkit: QSplitter::handleWidth(), which
    // is QStyle::PM_SplitterWidth. A backend with no splitter control has no
    // such default to inherit, so the number has to live here or the two
    // backends quietly disagree by a few pixels at every seam.
    //
    // MEASURED, not chosen: Qt 6.9.2 mingw on this machine reports 5 under the
    // live `windows11` style and 5 under `windowsvista`, 4 under `Fusion` and
    // `Windows`. 5 is what a Windows user sees today, so 5 is the default.
    int handleThickness = 5;

    // HOW FAR OUTSIDE THE PAINTED BAR A GRAB STILL COUNTS, on both sides.
    //
    // 0 is Qt parity and therefore the default: a QSplitterHandle is a real
    // widget exactly handleWidth wide, and the grab area is the widget. The
    // field exists because a PAINTED divider has no widget to be wider than it
    // looks -- the moment a theme paints a hairline instead of a 5px bar, the
    // hit target shrinks with it and there is nothing to stop it, whereas on
    // Qt the handle stays grabbable however thin the sheet draws it. Set this
    // when the paint and the target should differ; leave it at 0 when they
    // should not.
    int grabTolerance = 0;

    // A drag that would leave a collapsible pane at or below this many pixels
    // snaps it to zero instead of leaving an unusable sliver. 0 disables the
    // snap, and a pane then collapses only when a drag reaches it exactly.
    //
    // SET THIS TO THE PANE'S MINIMUM TO GET QT'S RULE EXACTLY: a collapsible
    // pane's drag floor here is zero rather than its minimum -- collapsing is
    // the move that skips PAST the minimum, not one that respects it -- so
    // with the two numbers equal there is no size at all between zero and the
    // minimum, which is how a QSplitter behaves.
    int collapseThreshold = 0;
};
};

ICoreSplitterRange#

ICoreSplitterCore.h:204 · struct · 0 declaration(s)

How far a grab bar may travel: the offsets its leading edge may take, given every pane's minimum, maximum and collapsibility on both sides of it.

struct ICoreSplitterRange {
public:
    int minimum = 0;
    int maximum = 0;
};
};

ICoreSplitterInput.h#

ICoreEssentials/UI/Portable/ICoreSplitterInput.h

ICoreSplitterInput -- what a pointer DOES to a splitter: which bar is lit, which one is being dragged, and where a drag puts the panes.

THIS FILE NAMES NO TOOLKIT TYPE. It takes plain numbers and answers with a small record saying what changed; a backend does the capturing, the painting and the child positioning. It is the third of the three layers a painted divider needs, and the other two already existed:

ICoreSplitterCore WHERE things are -- pane frames, bar frames, the travel range, and what a drag to a given offset does to every pane. Arithmetic, no state. ICoreWidgetCore WHICH pointer inputs reach a widget at all -- the implicit grab, the suppressed crossings, the double click. Per-element state, no widget knowledge.

ICoreSplitterInputState#

ICoreSplitterInput.h:56 · struct · 0 declaration(s)

Everything a splitter remembers between pointer inputs.

struct ICoreSplitterInputState {
public:
    // The bar the pointer is over, or -1. During a drag this is PINNED to the
    // dragged bar -- see the routing rules.
    int hoveredHandle = -1;

    // The bar being dragged, or -1 for no drag in progress.
    int pressedHandle = -1;

    // ⚠ HOW FAR INSIDE THE BAR THE POINTER TOOK HOLD, and it is the difference
    // between a divider and a divider that jumps. Without it the bar's LEADING
    // EDGE lands under the pointer on the first move, so grabbing a 5-pixel bar
    // anywhere but its first pixel snaps it up to 4 pixels sideways before it
    // starts tracking. Small enough to read as jitter rather than as a bug,
    // which is why it gets a control of its own.
    int grabOffset = 0;
};
};

ICoreSplitterInputOutcome#

ICoreSplitterInput.h:75 · struct · 0 declaration(s)

What one input did.

struct ICoreSplitterInputOutcome {
public:
    // The splitter used this input, so the routed event is Handled and does not
    // bubble. False for a press on the splitter's own background, which is not
    // the splitter's to eat.
    bool consumed = false;

    // The divider's appearance changed -- a bar lit, unlit, pressed, released
    // or moved. Repaint.
    bool repaint = false;

    // The pane extents changed, so the child frames are stale. Reposition.
    bool relayout = false;

    // Emit ICoreSplitter::onPaneResized. Set on the moves of a drag that
    // actually moved something, which is QSplitter::splitterMoved's contract
    // and the AppKit seat's -- NOT on the release, and never on a resize.
    bool paneResized = false;
};
};

File-scope declarations#

// The pointer inputs a splitter cares about. There is no Enter: entering an
// element tells a splitter nothing a Move does not, and the position is what
// picks a bar.
// 
// `Leave` is the crossing the host element reports when the pointer genuinely
// left -- which, because a grab suppresses crossings, never arrives mid-drag.
enum class ICoreSplitterPointerInput { Move, Press, Release, Leave, Cancel };

ICoreSvgColor.h#

ICoreEssentials/UI/Portable/ICoreSvgColor.h

THE VOCABULARY THE TWO SVG READERS SHARE -- every type here has exactly ONE definition in this tree, and each is here because it used to have two (rows W0.17 and W0.18 of the Windows backend plan).

The rule this file exists to hold: a type belongs here when both readers mean the SAME thing by it, and belongs to one reader under a name that says so when they do not. ICoreSvgDocument.h's remaining vocabulary carries a Doc infix for exactly that reason -- a ICoreSvgDocGradient and an ICoreSvgGradient are two designs and are named as two, rather than colliding silently in one archive.

⚠ THIS FILE EXISTS BECAUSE THERE USED TO BE TWO OF IT, AND THAT IS AN ODR TRAP, NOT A DUPLICATION SMELL. ICoreSvgDocument.h and ICoreSvgRender.h each declared a struct ICoreSvgColor at FILE SCOPE with a different layout

ICoreSvgColor#

ICoreSvgColor.h:47 · struct · 2 declaration(s)

Straight RGBA, 0-255, premultiplied by nothing.

struct ICoreSvgColor {
public:
    int r = 0, g = 0, b = 0, a = 255;

    [[nodiscard]] bool operator==(const ICoreSvgColor& other) const;
    [[nodiscard]] bool operator!=(const ICoreSvgColor& other) const;
};
};

ICoreSvgGradientStop#

ICoreSvgColor.h:93 · struct · 0 declaration(s)

A gradient ramp stop: a position along the ramp and the colour there.

struct ICoreSvgGradientStop {
public:
    double offset = 0.0;
    ICoreSvgColor color;
};
};

ICoreSvgDocument.h#

ICoreEssentials/UI/Portable/ICoreSvgDocument.h

SVG markup turned into a flat DRAW LIST: outlines in user space, each with the paint it resolved to and the transform that places it.

ICoreSvgDocMatrix#

ICoreSvgDocument.h:77 · struct · 0 declaration(s)

An affine 2x3, spelled the way every transform in this tree is spelled: x' = m11 * x + m21 * y + dx y' = m12 * x + m22 * y + dy ⚠ m12 IS THE Y-SHEAR OF THE X ROW, not "row 1 column 2".

struct ICoreSvgDocMatrix {
public:
    double m11 = 1.0;
    double m12 = 0.0;
    double m21 = 0.0;
    double m22 = 1.0;
    double dx = 0.0;
    double dy = 0.0;
};
};

ICoreSvgDocPaint#

ICoreSvgDocument.h:109 · struct · 0 declaration(s)

ICoreSvgColor and icoreSvgParseColor come from ICoreSvgColor.h, included above.

struct ICoreSvgDocPaint {
public:
    enum class Kind {
        None,       // `fill="none"` -- draw nothing, which is NOT the same as black
        Color,
        Reference,  // `url(#id)`: a gradient this file has resolved the id of
    };

    Kind kind = Kind::None;
    ICoreSvgColor color;
    std::string reference;
};
};

ICoreSvgDocGradient#

ICoreSvgDocument.h:127 · struct · 0 declaration(s)

ICoreSvgGradientStop comes from ICoreSvgColor.h with the colour inside it: both readers had declared it identically, which made it ONE type only by luck, and the luck ran out once before (W0.17).

struct ICoreSvgDocGradient {
public:
    enum class Kind { Linear, Radial };

    Kind kind = Kind::Linear;
    std::string id;

    // Linear: the ramp's two ends.
    double x1 = 0.0;
    double y1 = 0.0;
    double x2 = 1.0;
    double y2 = 0.0;

    // Radial: the circle, and the focal point (which defaults to the centre).
    double cx = 0.5;
    double cy = 0.5;
    double radius = 0.5;
    double fx = 0.5;
    double fy = 0.5;

    // ⚠ `userSpaceOnUse` IS THE ONLY UNIT THIS CORPUS USES -- all 12 of the
    // gradients that state units say so. The default in SVG is the OTHER one
    // (`objectBoundingBox`, where the coordinates are fractions of the shape's
    // own box), so a reader that assumed user space for an unstated gradient
    // would place it in the wrong coordinate system entirely. The flag is
    // carried rather than assumed, and the renderer is told which it got.
    bool userSpace = false;

    ICoreSvgDocMatrix transform;

    std::vector<ICoreSvgGradientStop> stops;
};
};

ICoreSvgDocText#

ICoreSvgDocument.h:171 · struct · 0 declaration(s)

A text run, carried through the draw list rather than converted to outlines.

struct ICoreSvgDocText {
public:
    std::string utf8;

    double x = 0.0;
    double y = 0.0;

    double fontSize = 16.0;

    // 400 regular, 700 bold -- the same numbering the painter core's font
    // request uses, so the backend hands it straight over.
    int weight = 400;
    bool italic = false;

    // Empty means "the system's default face", which is what both text assets
    // in this corpus rely on by naming no family at all.
    std::string family;

    // ⚠ THE ANCHOR IS NOT AN ALIGNMENT WITHIN A BOX -- there is no box. It
    // says which point of the rendered run lands on (x, y): its start, its
    // middle or its end. Both text assets here say `middle`, and treating it
    // as "left-align" puts the label half its own width to the right.
    enum class Anchor { Start, Middle, End };
    Anchor anchor = Anchor::Start;
};
};

ICoreSvgDrawCommand#

ICoreSvgDocument.h:198 · struct · 0 declaration(s)

-- one thing to draw ------------------------------------------------------

struct ICoreSvgDrawCommand {
public:
    // ⚠ ONE ORDERED LIST HOLDS BOTH OUTLINES AND TEXT, deliberately. Painting
    // order is document order, and text interleaves with shapes -- a label
    // drawn over a badge, a badge drawn over a label. Two lists would have to
    // be merged back by the renderer, and the merge is where the order gets
    // lost.
    bool isText = false;
    ICoreSvgDocText text;

    // The outline, in the element's OWN user space. `transform` places it.
    std::vector<ICoreSvgDocSegment> segments;

    ICoreSvgDocMatrix transform;

    ICoreSvgDocPaint fill;

    // ⚠ EVEN-ODD IS THE COMMON CASE HERE, not the exotic one: 18 elements in
    // this corpus ask for it against 2 that ask for nonzero. A renderer that
    // only fills nonzero draws the holes in every one of them solid.
    bool fillEvenOdd = false;

    ICoreSvgDocPaint stroke;
    double strokeWidth = 1.0;
    ICorePenCap strokeCap = ICorePenCap::Flat;
    ICorePenJoin strokeJoin = ICorePenJoin::Miter;

    // Group and element opacity, already multiplied together.
    double opacity = 1.0;

    // ⚠ THE SPACE THE MASK'S OWN CONTENT LIVES IN, which is NOT this command's.
    // A mask is resolved in the user space of the element that REFERENCED it,
    // and that element is usually an ancestor: `RustIcon.svg` puts the mask on
    // a group inside `<g transform="translate(53,53)">`, and the shapes it
    // masks are `use` instances each carrying a rotation of their own. Building
    // the mask with the SHAPE's transform therefore rotates the holes with the
    // cog they are meant to cut, and translates them off the artwork entirely.
    // Measured: doing that dropped agreement with an independent renderer from
    // 99.8% to 62.4% on that asset.
    ICoreSvgDocMatrix maskTransform;

    // The id of a mask to apply, or empty. ⚠ THE MASK IS NOT RESOLVED INTO
    // THIS COMMAND, because masking is compositing and compositing is the
    // renderer's: the mask's own content is rendered to a luminance buffer and
    // multiplied into this shape's alpha. What this layer can do -- and does --
    // is resolve WHICH mask and hand over its draw list.
    std::string maskReference;
};
};

ICoreSvgMask#

ICoreSvgDocument.h:254 · struct · 0 declaration(s)

A mask's own content, as its own little draw list.

struct ICoreSvgMask {
public:
    std::string id;
    std::vector<ICoreSvgDrawCommand> commands;
};
};

ICoreSvgDrawing#

ICoreSvgDocument.h:261 · struct · 2 declaration(s)

-- the whole drawing ------------------------------------------------------

struct ICoreSvgDrawing {
public:
    // The drawing's own size in user units -- what `ICoreSvg::defaultSize()`
    // answers. Taken from `width`/`height` when they are there and from the
    // viewBox when they are not.
    double width = 0.0;
    double height = 0.0;

    bool hasViewBox = false;
    double viewBoxX = 0.0;
    double viewBoxY = 0.0;
    double viewBoxWidth = 0.0;
    double viewBoxHeight = 0.0;

    std::vector<ICoreSvgDrawCommand> commands;

    // Every gradient the document defined, by id. A command whose paint is a
    // `Reference` names one of these; the renderer looks it up and builds the
    // toolkit's own gradient from it.
    std::vector<ICoreSvgDocGradient> gradients;

    // Every mask the document defined, by id.
    std::vector<ICoreSvgMask> masks;

    const ICoreSvgMask* mask(const std::string& id) const;

    // The gradient with this id, or null. ⚠ A REFERENCE THAT RESOLVES TO
    // NOTHING IS NOT AN ERROR AND MUST NOT BE PAINTED BLACK -- SVG says an
    // unresolvable paint reference means the element is not filled, and
    // painting it black instead puts a solid slab where a subtle gradient was.
    const ICoreSvgDocGradient* gradient(const std::string& id) const;
};
};

ICoreSvgNode#

ICoreSvgDocument.h:310 · struct · 1 declaration(s)

-- the markup reader, exposed because it is separately wrong-able ----------

struct ICoreSvgNode {
public:
    std::string name;
    std::vector<std::pair<std::string, std::string> > attributes;

    // Character data directly inside this element -- what `<style>` and
    // `<text>` need and nothing else uses.
    std::string text;

    std::vector<ICoreSvgNode> children;

    // The attribute's value, or null. ⚠ NOT an empty string on absence:
    // `fill=""` and no `fill` at all mean different things to the cascade.
    const std::string* attribute(const std::string& name) const;
};
};

ICoreSvgEmitter.h#

ICoreEssentials/UI/Portable/ICoreSvgEmitter.h

ICoreSvgEmitColor#

ICoreSvgEmitter.h:30 · struct · 0 declaration(s)

Drawing operations turned into SVG markup -- the WRITING half of this tier, and the exact inverse of ICoreSvgDocument.

struct ICoreSvgEmitColor {
public:
    int red = 0;
    int green = 0;
    int blue = 0;
    int alpha = 255;
};
};

ICoreSvgEmitOp#

ICoreSvgEmitter.h:39 · struct · 0 declaration(s)

One recorded operation.

struct ICoreSvgEmitOp {
public:
    enum class Kind {
        // -- state. ⚠ THESE ARE RECORDED, NOT RESOLVED BY THE RECORDER. A pen
        // set inside a save/restore pair applies until the restore and not
        // after it, and only something walking the operations in order knows
        // that -- the same argument the clip already made. Keeping paint state
        // here means the recorder has no state at all and cannot get it wrong;
        // a recorder that tracked it would be a second painter.
        Save,
        Restore,
        ClipRect,
        ClearClip,
        SetPen,
        NoPen,
        SetBrush,
        NoBrush,
        SetFont,
        SetOpacity,

        // -- drawing
        Line,
        Rect,
        FillRect,
        Ellipse,
        Polygon,
        Path,
        Text,
    };

    Kind kind = Kind::Save;

    double x = 0.0;
    double y = 0.0;
    double width = 0.0;
    double height = 0.0;
    double x2 = 0.0;
    double y2 = 0.0;

    // The paint this operation CARRIES.
    //
    // ⚠ FOR A DRAWING OPERATION THESE ARE AN OVERRIDE AND ARE USUALLY UNSET --
    // the emitter stamps whatever the state operations have established. They
    // are set on `SetPen` / `SetBrush` (which is how that state arrives) and on
    // `FillRect`, whose painter signature carries its own brush.
    //
    // ⚠ AND THE ATTRIBUTES GO ON EACH ELEMENT, NOT ON A `<g>`. SVG inherits
    // presentation attributes down the tree, so emitting a group every time a
    // pen changed would produce a document whose NESTING depended on the order
    // the caller happened to set things in. Writing them per element is longer
    // and is the same picture every time.
    bool hasFill = false;
    ICoreSvgEmitColor fill;
    bool hasStroke = false;
    ICoreSvgEmitColor stroke;
    double strokeWidth = 1.0;
    // `SetPen`: the dash pattern as `stroke-dasharray` lengths, in user units
    // -- on, off, on, off... Empty is a solid line. The recorder fills it from
    // the pen's style, scaled by its width, the way the EPS recorder does.
    std::vector<double> dash;
    double opacity = 1.0;

    std::vector<ICoreSvgDocSegment> segments;   // Path
    std::vector<double> points;              // Polygon, as x,y,x,y…
    std::string text;                        // Text
    std::string fontFamily;
    double fontSize = 12.0;
    bool bold = false;

    // ⚠ HORIZONTAL ALIGNMENT IS THE MARKUP'S JOB, NOT THE RECORDER'S, and that
    // is why this field exists rather than a pre-measured x. A caller drawing
    // text CENTRED IN A RECTANGLE would otherwise have to measure the string to
    // find where it starts -- which needs a font stack, gives a different
    // answer on every backend, and bakes one machine's metrics into the file.
    // `text-anchor` says "this x is the middle" and lets the renderer place it,
    // exactly and identically everywhere. This tree's own reader honours it.
    //
    // ⚠ THERE IS NO VERTICAL TWIN. `dominant-baseline` is the equivalent and
    // this tree's reader does NOT implement it, so a vertical alignment has to
    // arrive here as a baseline already computed from font metrics -- the one
    // part of text placement that is measured rather than declared.
    enum class TextAnchor { Start, Middle, End };
    TextAnchor anchor = TextAnchor::Start;
};
};

ICoreSvgGeometry.h#

ICoreEssentials/UI/Portable/ICoreSvgGeometry.h

The geometry half of the in-house SVG reader: transforms, path data, and the basic shapes reduced to paths.

TOOLKIT-FREE BY SPECIFICATION. No Qt, no Windows, no CoreGraphics, no ICore value types -- plain doubles and std::string. That is what lets it be written once for every backend and proved on a machine that cannot build the UI at all. A backend adapter walks the output and calls its own painter; nothing here knows one exists.

EVERYTHING BECOMES A PATH, and that is the load-bearing simplification. SVG has seven ways to describe an outline -- path, rect, circle, ellipse, line, polygon, polyline -- and a renderer that keeps them apart pays for seven fill routines, seven stroke routines and seven transform paths. All seven reduce to move/line/cubic/close here, so everything downstream sees one

ICoreSvgMatrix#

ICoreSvgGeometry.h:34 · struct · 3 declaration(s)

A 2-D affine transform, in SVG's own naming: | a c e | | b d f | | 0 0 1 |

struct ICoreSvgMatrix {
public:
    double a = 1.0, b = 0.0, c = 0.0, d = 1.0, e = 0.0, f = 0.0;

    // `this` applied AFTER `inner` -- i.e. the result maps a point by inner
    // first. That is the order SVG nests transforms in: a child's transform is
    // inner, its parent's is outer.
    [[nodiscard]] ICoreSvgMatrix multipliedBy(const ICoreSvgMatrix& inner) const;
    void apply(double inX, double inY, double& outX, double& outY) const;
    [[nodiscard]] bool isIdentity() const;
};
};

ICoreSvgSegment#

ICoreSvgGeometry.h:55 · struct · 2 declaration(s)

One outline.

struct ICoreSvgSegment {
public:
    enum class Kind { MoveTo, LineTo, CubicTo, Close };
    Kind kind = Kind::MoveTo;
    // MoveTo/LineTo use x[2],y[2] (the endpoint). CubicTo uses all three:
    // two controls then the endpoint. Close uses none.
    double x[3] = {0.0, 0.0, 0.0};
    double y[3] = {0.0, 0.0, 0.0};

    [[nodiscard]] double endX() const;
    [[nodiscard]] double endY() const;
};
};

ICoreSvgPath#

ICoreSvgGeometry.h:67 · struct · 7 declaration(s)

struct ICoreSvgPath {
public:
    std::vector<ICoreSvgSegment> segments;

    void moveTo(double x, double y);
    void lineTo(double x, double y);
    void cubicTo(double c1x, double c1y, double c2x, double c2y, double x, double y);
    void close();

    [[nodiscard]] bool isEmpty() const;

    // Axis-aligned bounds of the CONTROL POINTS, not of the drawn curve. Cheap,
    // never too small, and enough for the viewBox arithmetic and for tests; a
    // tight curve bound is not needed by anything here and would be a
    // different, slower function.
    void controlBounds(double& minX, double& minY, double& maxX, double& maxY) const;

    [[nodiscard]] ICoreSvgPath transformed(const ICoreSvgMatrix& matrix) const;
};
};

ICoreSvgPath.h#

ICoreEssentials/UI/Portable/ICoreSvgPath.h

ICoreSvgDocSegment#

ICoreSvgPath.h:50 · struct · 0 declaration(s)

One segment, in absolute user units.

struct ICoreSvgDocSegment {
public:
    ICoreSvgDocSegmentKind kind = ICoreSvgDocSegmentKind::Move;

    double x = 0.0;
    double y = 0.0;

    double c1x = 0.0;
    double c1y = 0.0;
    double c2x = 0.0;
    double c2y = 0.0;
};
};

File-scope declarations#

// SVG path data (`d="..."`) turned into a flat list of absolute segments.
// 
// ⚠ ITS SEGMENT IS `ICoreSvgDocSegment`, NOT `ICoreSvgSegment`, and the
// difference is not cosmetic: `UI/Portable/ICoreSvgGeometry.h` declares an
// `ICoreSvgSegment` of its own, `{kind, x[3], y[3]}` against this file's
// `{kind, x, y, c1x, c1y, c2x, c2y}`. Both are 56 bytes, so the two were
enum class ICoreSvgDocSegmentKind {
    Move,
    Line,
    Cubic,
    Close,
};

ICoreSvgRender.h#

ICoreEssentials/UI/Portable/ICoreSvgRender.h

The paint half of the in-house SVG reader: the document is read, its styles are resolved, and what comes out is a flat RENDER LIST -- one entry per drawn outline, with its transform composed and its paint already decided.

TOOLKIT-FREE BY SPECIFICATION, exactly like the geometry half it sits on. No Qt, no Windows, no CoreGraphics, no ICore value types -- plain doubles, std::string and the geometry structs. A backend adapter walks entries and issues its own painter calls; nothing here knows a painter exists, and that is what lets the correctness-heavy part be proved on a machine that cannot build the UI at all.

IT RESOLVES, IT DOES NOT DRAW. The cascade, inheritance, <style> classes, <use> expansion, gradient plumbing and unit conversion are the hard, spec-shaped parts, and they happen once, here, for every backend. Filling a

ICoreSvgGradient#

ICoreSvgRender.h:66 · struct · 1 declaration(s)

ICoreSvgGradientStop comes from ICoreSvgColor.h -- see the note there for why an identically-spelled struct in two headers was still two types.

struct ICoreSvgGradient {
public:
    enum class Kind { Linear, Radial };
    enum class Spread { Pad, Reflect, Repeat };

    Kind kind = Kind::Linear;
    Spread spread = Spread::Pad;

    // Linear: the vector (x1,y1)->(x2,y2). Radial: centre (cx,cy), radius r,
    // focus (fx,fy). The coordinates live in the space `objectBoundingBox`
    // selects.
    double x1 = 0.0, y1 = 0.0, x2 = 1.0, y2 = 0.0;
    double cx = 0.5, cy = 0.5, r = 0.5, fx = 0.5, fy = 0.5;

    // SVG's `gradientUnits`. TRUE (the default) means the coordinates above are
    // fractions of the filled shape's bounding box; false means user space.
    // A backend that ignores this draws a gradient sized to the document rather
    // than to the shape, which usually shows up as a flat wash of one stop.
    bool objectBoundingBox = true;

    ICoreSvgMatrix transform;       // gradientTransform
    std::vector<ICoreSvgGradientStop> stops;

    // The colour at `t` along the gradient, with `spread` applied outside 0..1.
    // Provided because every backend needs the same answer at the ends and in
    // the gaps between stops, and because it makes the stop plumbing provable
    // without a rasteriser.
    [[nodiscard]] ICoreSvgColor colorAt(double t) const;
};
};

ICoreSvgPaint#

ICoreSvgRender.h:101 · struct · 0 declaration(s)

One resolved paint: a colour, a gradient, or nothing at all.

struct ICoreSvgPaint {
public:
    enum class Kind { None, Color, Gradient };

    Kind kind = Kind::None;
    ICoreSvgColor color;
    int gradient = -1;              // index into ICoreSvgRenderList::gradients

    // fill-opacity / stroke-opacity, already multiplied by every enclosing
    // group's `opacity`. Kept out of `color.a` because a gradient has no single
    // alpha to fold it into, and the two must not disagree.
    double opacity = 1.0;
};
};

ICoreSvgText#

ICoreSvgRender.h:123 · struct · 0 declaration(s)

A run of text, resolved down to what a font engine needs and no further.

struct ICoreSvgText {
public:
    enum class Anchor { Start, Middle, End };

    std::string text;                 // whitespace already collapsed, as SVG says
    std::string fontFamily;           // empty means "the backend's default"
    double fontSizePx = 16.0;         // SVG's initial font-size
    int fontWeight = 400;             // 100..900; 700 is bold
    bool italic = false;
    Anchor anchor = Anchor::Start;

    // The anchor point, ON THE BASELINE, in the element's own user space --
    // not a top-left corner. SVG positions text by its baseline, and a
    // renderer that treats y as a top edge drops every string by an ascent.
    double x = 0.0;
    double y = 0.0;
};
};

ICoreSvgEntry#

ICoreSvgRender.h:140 · struct · 0 declaration(s)

struct ICoreSvgEntry {
public:
    enum class Cap { Butt, Round, Square };
    enum class Join { Miter, Round, Bevel };

    // ⚠ An entry is a SHAPE or a RUN OF TEXT, and a consumer must check.
    // A text entry's `path` is empty, so a walker that skips empty paths draws
    // no text rather than crashing -- but it also draws no text, which is why
    // this is a kind and not a flag hidden in the text struct.
    enum class Kind { Shape, Text };

    Kind kind = Kind::Shape;
    ICoreSvgText text;                // meaningful when kind == Kind::Text

    // The outline in the element's OWN user space. It is deliberately not
    // pre-transformed: stroke width is a user-space quantity, so a backend
    // handed a flattened path plus a width cannot stroke a scaled shape
    // correctly. Apply `transform`, then stroke with `strokeWidth`.
    ICoreSvgPath path;
    ICoreSvgMatrix transform;       // composed root -> this element

    ICoreSvgPaint fill;
    ICoreSvgPaint stroke;

    double strokeWidth = 1.0;
    Cap cap = Cap::Butt;
    Join join = Join::Miter;
    double miterLimit = 4.0;
    std::vector<double> dashArray;  // empty means solid
    double dashOffset = 0.0;

    // SVG's `fill-rule`. False is nonzero, the initial value.
    bool evenOddFill = false;

    // The mask this entry is drawn through: an index into
    // ICoreSvgRenderList::clips, or -1 for the usual unmasked case.
    //
    // ⚠ APPENDED, NEVER INSERTED. Every member above keeps the offset it had
    // before masks existed, so a translation unit compiled against the older
    // header still reads the same fields out of the same places. Inserting one
    // would change no mangled name and silently move every field after it --
    // the enum-shift hazard W0.3 wrote up, in struct form.
    int clip = -1;
};
};

ICoreSvgClip#

ICoreSvgRender.h:205 · struct · 0 declaration(s)

A <mask> reduced to ONE OUTLINE, in root user space.

struct ICoreSvgClip {
public:
    ICoreSvgPath path;
    bool evenOddFill = true;

    // The mask element's id, for diagnostics. Two entries clipped by the same
    // mask share one index, so this is not unique per entry.
    std::string sourceId;
};
};

ICoreSvgRenderList#

ICoreSvgRender.h:214 · struct · 0 declaration(s)

struct ICoreSvgRenderList {
public:
    std::vector<ICoreSvgEntry> entries;      // draw order is document order
    std::vector<ICoreSvgGradient> gradients;

    // The document's own coordinate system. `width`/`height` are the intrinsic
    // size when the document states one; when it does not, they are the viewBox
    // extent, which is the fallback a renderer has to use.
    bool hasViewBox = false;
    double viewBoxX = 0.0, viewBoxY = 0.0, viewBoxWidth = 0.0, viewBoxHeight = 0.0;
    double width = 0.0, height = 0.0;

    // TRUE when the document says `preserveAspectRatio="none"` -- that is, it
    // consents to being stretched. Everything else is uniform-scale-and-centre,
    // which is SVG's default (`xMidYMid meet`).
    bool stretchToFit = false;

    // Maps the document onto a target rectangle: uniform scale, centred, unless
    // `stretchToFit`. Compose this OUTSIDE an entry's own transform.
    [[nodiscard]] ICoreSvgMatrix transformForTarget(double targetX, double targetY,
                                                    double targetWidth,
                                                    double targetHeight) const;

    // Anything the document asked for that this reader does not do, one line
    // each: "<text> is not rendered", "<mask> is ignored", and so on.
    //
    // READ THESE. An icon that uses a feature listed here renders wrong but not
    // empty, and that is the failure which passes every check that only asks
    // whether rendering succeeded. A caller showing art to a user should treat
    // a non-empty `warnings` as "this asset needs a look", not as debug noise.
    std::vector<std::string> warnings;

    // The masks that converted, indexed by `ICoreSvgEntry::clip`. APPENDED
    // after `warnings` on purpose -- see the note on ICoreSvgEntry::clip.
    std::vector<ICoreSvgClip> clips;
};
};

ICoreSvgRenderOptions#

ICoreSvgRender.h:250 · struct · 0 declaration(s)

struct ICoreSvgRenderOptions {
public:
    // What `currentColor` resolves to. A themed icon is exactly this: one
    // document, drawn in whatever ink the palette currently holds.
    ICoreSvgColor currentColor{0, 0, 0, 255};

    // `<use>` may reference an element that references it back. Expansion stops
    // at this depth and records a warning rather than recursing.
    int maxUseDepth = 8;
};
};

ICoreSvgWrite.h#

ICoreEssentials/UI/Portable/ICoreSvgWrite.h

ICoreSvgWriteOptions#

ICoreSvgWrite.h:36 · struct · 0 declaration(s)

The write half of the in-house SVG work (W0.8 / A0.8): a render list back out as an SVG document.

struct ICoreSvgWriteOptions {
public:
    // Digits after the decimal point on every coordinate. Three is a tenth of a
    // device pixel on a 1000-unit document and keeps the text readable;
    // trailing zeros are stripped either way, so integers stay integers.
    int decimals = 3;

    // Two spaces per level, or one line per element with no indentation. Off is
    // smaller; on is what a person diffing two exports wants.
    bool indent = true;
};
};

ICoreTableLayoutCore.h#

ICoreEssentials/UI/Portable/ICoreTableLayoutCore.h

Where a graphics table's columns, rows and splitter handles are -- as arithmetic, with no toolkit and no drawing.

A table is four questions and none of them needs a painter: how wide is each column, where does each row sit, which column is under the pointer, and what happens when a splitter is dragged. Answering them here means they can be proved exactly, and it leaves the backend adapter with nothing to decide.

⚠ THE COLUMN WIDTHS COME FROM icoreDistributeAlongAxis(), NOT FROM A SECOND DISTRIBUTOR. The layout engine already spends the remainder instead of dropping it, which is the whole difference between columns that tile a table exactly and columns that leave a seam at the right edge. A ratio is a stretch; that is all this file has to say to reuse it.

ICoreTableMetrics#

ICoreTableLayoutCore.h:28 · struct · 0 declaration(s)

The fixed metrics of a table's chrome.

struct ICoreTableMetrics {
public:
    int rowHeight = 22;
    int rowGap = 2;         // between the titles row and the first entry too
    int padding = 2;        // around the whole grid, all four sides
};
};

ICoreTableRect#

ICoreTableLayoutCore.h:34 · struct · 0 declaration(s)

struct ICoreTableRect {
public:
    int x = 0;
    int y = 0;
    int width = 0;
    int height = 0;
};
};

ICoreTableGeometry#

ICoreTableLayoutCore.h:94 · struct · 0 declaration(s)

Where every row sits, and how tall the table is as a result.

struct ICoreTableGeometry {
public:
    ICoreTableRect titles;
    std::vector<ICoreTableRect> rows;
    int totalHeight = 0;
};
};

ICoreTextDecorationSet.h#

ICoreEssentials/UI/Portable/ICoreTextDecorationSet.h

ICoreTextDecorationSet#

ICoreTextDecorationSet.h:24 · class · pImpl · 9 declaration(s)

ICoreTextDecorationSet -- the store and the painting behind ICoreRichTextEdit::setDecorations, shared by every text seat.

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

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

    // Replace `layer`'s marks. An empty vector clears it. Answers whether
    // anything changed, so the seat repaints only when it has to.
    bool set(int layer, const std::vector<ICoreTextDecoration>& ranges);
    bool clear(int layer);

    [[nodiscard]] std::vector<ICoreTextDecoration> layer(int layer) const;

    // Every mark whose range holds `position` (from <= position < to), lowest
    // layer first. An empty range holds nothing.
    [[nodiscard]] std::vector<ICoreTextDecoration> at(int position) const;
    [[nodiscard]] bool empty() const;

    enum class Pass { Under, Over };

    // The caret rectangle at a position, in the coordinates the pass paints in
    // (a pane's cursorRect() convention: viewport, scrolled).
    using RectAt = std::function<ICoreRect(int position)>;

    // Paint the marks this pass owns: Background and LineBackground under the
    // glyphs, Box, Underline and WavyUnderline over them. `text` is the
    // document (lines are split at '\n'); `rowRight` is where a row ends for a
    // range that runs on past a wrap or fills a whole line. Rows outside
    // `bounds` cost one rectAt() each and are not drawn.
    void paint(ICorePainter& painter, const ICoreRect& bounds, Pass pass,
               const ICoreString& text, const RectAt& rectAt, double rowRight) const;

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

ICoreTextItemEditCore.h#

ICoreEssentials/UI/Portable/ICoreTextItemEditCore.h

ICoreTextItemEditCore -- what a keystroke DOES to a run of text with a caret in it: where the caret lands, what the selection becomes, and what the new string is. It is the arithmetic half of typing into a SCENE TEXT ITEM -- a block's name, a port's description, a canvas area's title, the text box on the canvas -- none of which is a native field with an editor of its own.

THIS FILE NAMES NO TOOLKIT TYPE AND MEASURES NOTHING. It takes a string, two integers and a key, and answers with a record of what changed; a backend owns the font metrics, the caret rule it draws, the clipboard and the repaint. That split is the reason this unit exists at all, and the reason is a defect rather than a preference:

⚠⚠ THE RULE BELOW EXISTED IN EXACTLY ONE SEAT, AND THE OTHER TWO SILENTLY SWALLOWED EVERY KEYSTROKE. Backends/AppKit/Graphics/ICoreGraphicsText.cpp

ICoreTextItemEditState#

ICoreTextItemEditCore.h:69 · struct · 0 declaration(s)

Everything a caret needs remembered between keystrokes.

struct ICoreTextItemEditState {
public:
    // Where the insertion point is, as an index into the text.
    int caret = 0;

    // ⚠ THE SELECTION IS THE PAIR (anchor, caret) AND NOT A THIRD NUMBER. The
    // anchor is where the gesture started -- the press, or the caret's position
    // when the first Shift+arrow was pressed -- and the caret is where it is
    // now, so dragging or extending in EITHER direction needs no special case
    // and "no selection" is simply anchor == caret. A start/length pair cannot
    // express which end the user is moving, which is the one thing a selection
    // has to know.
    int anchor = 0;
};
};

ICoreTextItemEditOutcome#

ICoreTextItemEditCore.h:86 · struct · 0 declaration(s)

What one keystroke did.

struct ICoreTextItemEditOutcome {
public:
    // The editor used this keystroke. False means NOT MINE: the seat must let
    // it fall through, because something else owns it.
    //
    // ⚠ CONSUMED IS NOT THE SAME AS CHANGED, AND THE DIFFERENCE IS LOAD-BEARING.
    // A Left arrow at the start of the text consumed the key and moved nothing;
    // letting it fall through instead would hand an arrow to the canvas
    // underneath, which PANS. Every navigation key is consumed whether or not
    // the caret could move.
    bool consumed = false;

    // `text` below is the new value and the seat must publish it -- through its
    // own public setter, so the item re-lays out and raises its content hook.
    bool textReplaced = false;

    // The caret moved, or the selection's extent changed, with the text left
    // alone. Repaint; nothing else.
    bool caretMoved = false;
    bool selectionChanged = false;

    // ⚠ ESCAPE COMMITS RATHER THAN REVERTING, AND THIS IS HOW IT SAYS SO. Every
    // label in the editor tier writes its value back in `focusLosing`, so
    // dropping the focus IS the commit and there is no pre-edit copy anywhere to
    // revert to. Reverting would mean this file keeping one, which is the second
    // source of truth its banner refuses. The seat answers this by calling its
    // own clearFocus().
    bool releaseFocus = false;

    // Put `clipboardText` on the system clipboard. Raised by Copy and by Cut;
    // a Cut also carries textReplaced, in the same outcome, because the two
    // halves of a cut are one keystroke.
    bool writeClipboard = false;

    // ⚠⚠ A PASTE IS ASKED FOR RATHER THAN PERFORMED, AND THE CLIPBOARD IS WHY
    // THIS FILE HAS NO INCLUDE FOR IT. `ICoreClipboard::text()` reaches the
    // platform; a rule that read it would be unrunnable without a window
    // standing, which is the property this whole unit exists to keep. So the
    // seat sees this flag, reads the clipboard, and calls
    // icoreTextItemEditInsert() with what came back -- which is the same
    // function the character path uses, so a paste and a typed character cannot
    // insert differently.
    //
    // ⚠ AND IT IS NOT READ SPECULATIVELY ON EVERY KEYSTROKE. Handing the
    // clipboard's contents IN as an argument was the other available shape and
    // was declined on cost: it is a cross-process read, and it would happen for
    // every arrow key in every rename.
    bool readClipboard = false;

    // The new text, when textReplaced. Empty is a legitimate value -- selecting
    // a whole name and pressing Delete produces exactly it -- so a seat must
    // read the FLAG and never test this for emptiness.
    ICoreString text;

    // The run to copy, when writeClipboard.
    ICoreString clipboardText;
};
};

ICoreTextItemEditCaretPlace#

ICoreTextItemEditCore.h:150 · struct · 0 declaration(s)

Where a caret falls in the PARAGRAPH stack, for a seat that has to draw it.

struct ICoreTextItemEditCaretPlace {
public:
    int paragraph = 0;   // which '\n'-separated paragraph the caret is in
    int offset = 0;      // how far into that paragraph, in ICoreChar units
};
};

ICoreTouchHover.h#

ICoreEssentials/UI/Portable/ICoreTouchHover.h

ICoreTouchHover -- tooltips and hover states for a hand that cannot hover.

A finger is either on the glass or nowhere, so the two things a desktop shows only under a resting pointer -- a tooltip, and a control that lights up or reveals a button while the pointer is over it -- are out of reach on a tablet. Both tablet platforms answer the same way, and so does this file: a LONG PRESS is the finger's hover. Held still on a control, it enters that control's hover state and shows its tooltip; the hover stays after the finger lifts, so what it revealed can then be tapped.

Two pieces, both toolkit-free and both inert until a backend feeds them:

  • ICoreTouchPresence -- the latch that says a finger or a pen has been

seen in this process. A widget that hides something behind hover asks it

ICoreTouchPresence#

ICoreTouchHover.h:48 · class · pImpl · nested Watch · 8 declaration(s)

The "a finger has been here" latch.

class ICoreTouchPresence {
public:
    // The process's latch: the one backends note contacts on and widgets ask.
    static ICoreTouchPresence& application();

    // A standalone latch, for a test or for a host that keeps its own.
    ICoreTouchPresence();
    ~ICoreTouchPresence();
    ICoreTouchPresence(const ICoreTouchPresence&) = delete;
    ICoreTouchPresence& operator=(const ICoreTouchPresence&) = delete;

    // A backend's half: call on every contact that goes down. A Touch or Pen
    // contact flips the latch (once, for good); a Mouse contact is ignored, so
    // a backend may call it for every pointer without asking which it was.
    void noteContact(ICorePointerKind kind);

    // True once a finger or a pen has gone down. Never goes back to false.
    [[nodiscard]] bool seen() const;

    // Runs `onFirstContact` the moment the latch flips, and never again. The
    // registration lives as long as the returned watch; destroy it to stop.
    // A latch that has already flipped does not call it -- ask seen() first.
    class Watch {
    public:
        Watch();
        ~Watch();
        Watch(Watch&& other) noexcept;
        Watch& operator=(Watch&& other) noexcept;
        Watch(const Watch&) = delete;
        Watch& operator=(const Watch&) = delete;

    private:
        friend class ICoreTouchPresence;
        class Impl;
        std::unique_ptr<Impl> impl;
    };
    [[nodiscard]] Watch watch(std::function<void()> onFirstContact);

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

ICoreTouchHoverAction#

ICoreTouchHover.h:105 · struct · 0 declaration(s)

One action.

struct ICoreTouchHoverAction {
public:
    ICoreTouchHoverActionKind kind = ICoreTouchHoverActionKind::HoverEnter;
    const void* target = nullptr;
    ICorePoint pos;
};
};

ICoreTouchHover#

ICoreTouchHover.h:142 · class · pImpl · 16 declaration(s)

The long-press machine.

class ICoreTouchHover {
public:
    ICoreTouchHover();
    ~ICoreTouchHover();
    ICoreTouchHover(const ICoreTouchHover&) = delete;
    ICoreTouchHover& operator=(const ICoreTouchHover&) = delete;

    // Distance a held finger may wander, in the event's units. 10 by default.
    void setSlop(double distance);
    [[nodiscard]] double slop() const;

    // How long a still finger must be held. 500 ms by default.
    void setLongPressDelayMs(double ms);
    [[nodiscard]] double longPressDelayMs() const;

    // How long a tooltip stays after the finger that summoned it lifts.
    // 1500 ms by default.
    void setToolTipLingerMs(double ms);
    [[nodiscard]] double toolTipLingerMs() const;

    // Feed every touch event of a sequence the backend is routing down the
    // MOUSE path (a seat that took the sequence takes it from here too), in
    // order. `target` is the backend's handle for what is under the first
    // finger -- the thing the mouse path would hover -- and `hasToolTip` says
    // whether it has one. Both are read only on the event where the first
    // finger goes down. Returns the actions to apply, in order.
    std::vector<ICoreTouchHoverAction> feed(const ICoreTouchEvent& event, const void* target,
                                            bool hasToolTip);

    // Time passing with no event: a long press firing under a still finger, a
    // lingering tooltip running out. A backend calls it from a timer while
    // wantsTick() is true.
    std::vector<ICoreTouchHoverAction> tick(double nowMs);
    [[nodiscard]] bool wantsTick() const;

    // True from a CancelPress until every finger of that sequence is up: the
    // backend routes none of the sequence's remaining events down the mouse
    // path. (It still feeds them here.)
    [[nodiscard]] bool ownsSequence() const;

    // The target whose hover a long press entered and has not left, or null.
    [[nodiscard]] const void* hoverTarget() const;

    // A real pointer moved. Drops the touch hover and its tooltip.
    std::vector<ICoreTouchHoverAction> noteMouseMoved();

    // Abandon everything: the window lost its pointers (focus lost, a modal
    // opened) or `target`'s owner is going away.
    std::vector<ICoreTouchHoverAction> cancel();

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

File-scope declarations#

// What a backend does in answer to a touch. Append-only (PS0.1).
enum class ICoreTouchHoverActionKind : int {
    HoverEnter = 0,    // deliver the target's pointer-enter (its hover state), at `pos`
    HoverLeave = 1,    // deliver the target's pointer-leave
    ShowToolTip = 2,   // show the target's tooltip now, anchored at `pos`
    HideToolTip = 3,   // hide the tooltip ShowToolTip showed
    CancelPress = 4,   // the press already delivered to the target must not click:
                       // deliver mouseGestureCancelled(), and route no more of this
                       // sequence down the mouse path (ownsSequence() is now true)
};

ICoreTouchTargets.h#

ICoreEssentials/UI/Portable/ICoreTouchTargets.h

ICoreTouchTargets -- how big a painted control's hit target is for a finger. TB1.5.

A mouse cursor hits a pixel; a fingertip covers about a centimetre, so a 16 px close box or a 5 px splitter bar that is fine under a cursor is a target a finger misses more often than it hits. Both platforms this is for publish a minimum: Apple's HIG 44 pt, Material 48 dp. This file answers 44 in the same logical units every painted control here lays out in.

⚠ TOUCH ONLY, AND BY CONSTRUCTION RATHER THAN BY CARE. Every function below asks icoreDispatchPointerKind() and returns its input UNCHANGED when the event being dispatched is a mouse's -- which is every event on macOS, Linux, Windows and a desktop browser until a backend opens an ICorePointerDetailScope for a finger. A mouse therefore hits exactly the

Declares no class of its own — see the file.

ICoreWatchRegistry.h#

ICoreEssentials/UI/Portable/ICoreWatchRegistry.h

"Tell me when something happens to THAT widget" -- for a toolkit with no event filter.

THIS FILE NAMES NO TOOLKIT TYPE. Every widget is a const void* identity that is compared and erased and never dereferenced, so the registry can be driven and tested with integers standing in for widgets.

WHY IT EXISTS. Qt answers all eight of the watch members on the widget base with ONE QObject event filter installed on the TARGET: the watcher asks Qt to route the target's events through it, and Qt does the bookkeeping. XAML has no event filter at all. So the relationship is inverted -- the TARGET holds a list of who is interested, and the target's own hooks look them up. The AppKit backend reached the same answer for the same reason.

ICoreWatchRegistry.h:74 · struct · 0 declaration(s)

One "watcher is interested in target, for this reason".

struct ICoreWatchLink {
public:
    ICoreWatchKind kind = ICoreWatchKind::PointerPress;
    const void* target = nullptr;
    const void* watcher = nullptr;
};
};

ICoreWatchRegistry#

ICoreWatchRegistry.h:83 · struct · 0 declaration(s)

The links, and nothing else.

struct ICoreWatchRegistry {
public:
    std::vector<ICoreWatchLink> links;
};
};

File-scope declarations#

// The four watches that name a target. The two that do not -- "my parent
// resized" and "the pointer moved anywhere in the application" -- are plain
// per-widget flags and are not registry entries, because there is nothing to
// key them on.
// 
// ⚠ THE KIND IS PART OF THE KEY AND THAT IS A FIX, NOT BOOKKEEPING. One
enum class ICoreWatchKind {
    PointerPress,
    Resize,
    KeyPress,
    PointerCrossing
};

ICoreWidgetCore.h#

ICoreEssentials/UI/Portable/ICoreWidgetCore.h

ICoreWidgetCore -- the pointer bookkeeping a widget base needs and that a XAML host element does NOT get for free (W1.3).

THIS FILE NAMES NO TOOLKIT TYPE. It takes plain numbers and answers with a small record saying which hooks to fire and whether to capture the pointer; the backend does the firing and the capturing. Its peer one layer down is the event ASSEMBLY (the WinUI backend's event-fields unit), which turns a PointerRoutedEventArgs into an ICoreMouseEvent. This layer decides what happens to the event once it exists, which is the half Qt was doing for us.

WHY IT EXISTS AT ALL. QWidget answers seven questions that XAML's UIElement either answers differently or does not answer:

  1. A press implicitly GRABS the mouse, so the matching release arrives at

ICoreWidgetPointerOutcome#

ICoreWidgetCore.h:68 · struct · 0 declaration(s)

What the host element should do about one input.

struct ICoreWidgetPointerOutcome {
public:
    bool deliverPress = false;
    bool deliverDoubleClick = false;
    bool deliverRelease = false;
    bool deliverMove = false;
    bool deliverEnter = false;
    bool deliverLeave = false;

    // ⚠⚠ THE GESTURE ENDED AND NO RELEASE IS COMING -- W10.69. Until this
    // existed, `deliverRelease` was set on `Release` and NOWHERE ELSE, so a
    // widget whose drag was taken away heard `deliverLeave` at most and never
    // learned that the gesture it started was over. Every widget that keeps
    // state for the duration of a drag therefore kept it for ever, and the only
    // way out was a release that would never arrive.
    //
    // ⚠ IT IS NOT A RELEASE AND MUST NOT BE DELIVERED AS ONE. A release carries
    // a position and means "the user finished here"; a cancel has no position
    // worth having and means "the user finished nowhere". Feeding a cancel into
    // mouseReleased() would make a tab being dragged DROP wherever the pointer
    // happened to be when the window lost activation -- a silent wrong action
    // in place of a stuck style.
    //
    // ⚠ AND IT IS NOT deliverLeave EITHER. A leave says the pointer is elsewhere
    // and is delivered on plain hover changes too; a host that cleared its drag
    // state on every leave would drop a drag that merely wandered outside the
    // widget, which is most of them.
    //
    // 📌 *A router that reports only the endings it likes leaves its hosts
    // holding state with no way to know it is stale.*
    bool deliverGestureCancelled = false;

    // Call CapturePointer / ReleasePointerCapture on the host element. Never
    // both in one outcome.
    bool capturePointer = false;
    bool releaseCapture = false;
};
};

ICoreWidgetPointerSpec#

ICoreWidgetCore.h:113 · struct · 0 declaration(s)

The two system numbers the double-click rule needs.

struct ICoreWidgetPointerSpec {
public:
    int doubleClickTimeMs = 500;
    double doubleClickSlopX = 4.0;
    double doubleClickSlopY = 4.0;

    // QWidget::setMouseTracking(). False is the Qt default and means "moves
    // with no button held are not my business".
    bool mouseTracking = false;
};
};

ICoreWidgetPointerState#

ICoreWidgetCore.h:130 · struct · 0 declaration(s)

Everything the router remembers between inputs.

struct ICoreWidgetPointerState {
public:
    // The pointer id that owns the current gesture, and whether one does.
    // XAML pointer ids are unsigned and 0 is a legal id, so the flag carries
    // "is there a gesture" rather than a sentinel id.
    unsigned int activePointerId = 0;
    bool hasActivePointer = false;

    // Buttons currently held by the active pointer, as an ICoreMouseButtons
    // mask. The grab ends when this reaches zero -- rule 5.
    unsigned int heldButtons = 0;

    // Whether the host element is holding a XAML pointer capture right now.
    bool capturing = false;

    // Whether the pointer is, as far as this element is concerned, inside it.
    bool inside = false;

    // The last press, for the double-click test. lastPressButton is 0 when
    // there has been no press yet, which no real button mask ever is.
    unsigned int lastPressButton = 0;
    long long lastPressTimeMs = 0;
    double lastPressX = 0.0;
    double lastPressY = 0.0;
};
};

File-scope declarations#

// The kinds of pointer input a host element forwards in. `Cancel` covers both
// of the XAML ways of taking a grab away (PointerCaptureLost and
// PointerCanceled); they differ in cause and not in what a widget must do.
enum class ICoreWidgetPointerInput {
    Press,
    Release,
    Move,
    Enter,
    Exit,
    Cancel
};

// Why a gesture is being forgotten. ⚠⚠ THE DISTINCTION IS NOT COSMETIC AND ITS
// ABSENCE COST THIS TREE EVERY DOUBLE CLICK ON ONE BACKEND.
// 
// Forgetting a gesture has always also broken the double-click RUN, on the
// reasoning written at that line: "a press, a cancel, then a press in the same
// pixel is not a double click". True -- of a CANCELLATION. But the WinUI seat
enum class ICoreWidgetForgetReason {
    // The gesture was taken away: a cancel, a drag starting, a popup opening,
    // the window losing activation. Nothing that happened before it can be half
    // of a double click.
    GestureCancelled,

    // The capture ended because the gesture ENDED normally. The press history
    // is still the user's own and survives.
    CaptureEndedNormally,
};