Generated reference › API — ICoreEssentials/Theme
kind: generated#api#icoreessentials-theme

API — ICoreEssentials/Theme

The public contract of 6 header(s) under src/ICoreEssentials/Theme — 23 class/struct definition(s), 114 declaration(s). Each section shows the header's banner and its public (and protected-virtual) surface exactly as the file writes it.

ICoreTheme.h#

src/ICoreEssentials/Theme/ICoreTheme.h

ICoreSurfaceTokens#

ICoreTheme.h:24 · struct · 1 declaration(s)

Surfaces: what things sit on -----------------------------------------

struct ICoreSurfaceTokens {
public:
    ICoreRgba window;          // root window background
    ICoreRgba titleBar;        // title bar strip
    ICoreRgba panel;           // standard panel background
    ICoreRgba panelRaised;     // content areas raised above panels (tab pane, menus)
    // The plate for a card whose CONTENT brings its own bright surface --
    // the Terminal panel is the case: its grid paints the editor's white over
    // most of the card, so the chrome around it (title, path, the action row)
    // on a panelRaised white too is one undifferentiated slab. A step under
    // panelRaised gives that chrome its own ground to sit on and leaves the
    // grid reading as the brightest thing in the panel, which is what it is.
    // The dark theme spells it exactly panelRaised -- a dark card has no glare
    // to separate itself from -- so this only ever moves the light one.
    ICoreRgba panelRecessed;
    ICoreRgba canvas;          // block-diagram canvas
    ICoreRgba sidebar;         // left tool bar container
    ICoreRgba hairline;        // hairline borders between surfaces
    ICoreRgba hairlineAccent;  // grayed-royal hairline for outlined containers & fields
    ICoreRgba panelBorder;     // outline around the window's fixed panels
    ICoreRgba divider;         // layout divider lines
    ICoreRgba shadow;          // default drop-shadow color
};
};

ICoreContentTokens#

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

Content: text & icons -------------------------------------------------

struct ICoreContentTokens {
public:
    ICoreRgba textPrimary;
    ICoreRgba textSecondary;
    ICoreRgba textTertiary;    // faint labels, inactive items
    ICoreRgba textAccent;      // brand-colored text
    ICoreRgba textOnAccent;    // text on DARK filled surfaces (royal, and the
                               // dark theme's deepened variant plates)
    // ...and text on PALE filled surfaces. The pair exists because a solid
    // variant plate is not one brightness: the light theme lifts its fills
    // toward the panel, and a near-white caption that reads at 4.2:1 on that
    // theme's royal reads at 3.1:1 on its red and 2.3:1 on its green. Which of
    // the two a plate takes is not a per-theme decision and cannot be one token
    // -- ICoreThemeBinding::inkOn measures the fill and picks.
    ICoreRgba textOnLightFill;
    ICoreRgba textDisabled;

    // How much of its own background a disabled rich-text view washes back over
    // itself. Character formats -- syntax colours, logger severities -- outrank
    // a stylesheet's disabled colour, so those views fade rather than recolour,
    // and this is what they fade by.
    double  disabledScrimOpacity;
};
};

ICoreInteractionTokens#

ICoreTheme.h:72 · struct · 5 declaration(s)

Interaction: hover / press / selection --------------------------------

struct ICoreInteractionTokens {
public:
    ICoreRgba hoverFill;       // generic hover wash (translucent)
    ICoreRgba pressedFill;     // generic pressed wash (translucent)
    ICoreRgba controlFill;         // rest fill of inline controls (combo boxes, toggles)
    ICoreRgba controlFillHover;    // their hover fill
    ICoreRgba selectionFill;   // canvas selection marquee fill (translucent)
    ICoreRgba selectionBorder; // canvas selection border / selected object tint
    ICoreRgba selectionShadow; // shadow color of selected objects
    ICoreRgba accent;          // the royal, as usable on this theme's surfaces
    ICoreRgba accentDeep;
    ICoreRgba accentContainer; // prominent brand container (run section, etc.)
};
};

ICoreButtonTokens#

ICoreTheme.h:94 · struct · 2 declaration(s)

⚠ ICoreGlassTokens — the tinted-GLASS finish the ICoreStyles variants used to wear (a fill lit from above inside a rim-lit border) — was deleted on 2026-08-15.

struct ICoreButtonTokens {
public:
    // The FILL of the nav-row hover treatment — its wash only. Its hairline and
    // its left edge stay on interaction.accent (ICoreNavItemStyle), so this is
    // the one part of the treatment that differs per theme: the dark theme needs
    // a lighter body behind the pointer without lightening the strokes that
    // frame it, and without moving interaction.accent, which is also the
    // selection border and the canvas selection.
    ICoreRgba hoverWash;
    // How much of that wash lands, 0-255, at full hover. Per theme for the same
    // reason as the colour above: over a near-white panel the wash needs very
    // little body before it reads, and over a g950 one it needs a good deal
    // more. It was a shared constant in ICoreNavItemStyle.cpp, which meant the
    // only way to lift the dark theme was to lift the light theme with it.
    int hoverWashAlpha;
    // The treatment's two STROKES — the hairline around it and the edge growing
    // out of its left side. Separate from hoverWash above because the two halves
    // move in opposite directions per theme: the dark theme wants a lighter body
    // inside a darker frame, which is what gives the wash something to sit
    // against. Both drew interaction.accent before this split.
    ICoreRgba hoverEdge;
    // How much of the hairline lands, 0-255, at full hover. The left edge is
    // always full strength; this is the ring around the body. Per theme because
    // a selection blue at part strength over a dark panel reads as a dull line
    // rather than a lit one — the dark theme runs it at full to keep the colour
    // as rich as it is in the swatch.
    int hoverEdgeAlpha;
    ICoreRgba hoverFill;           // base color of the animated hover background
    // The RESTING plate under a caption-only button — one that carries no icon.
    // A button whose whole content is a word has nothing but that word to say it
    // is a button, so it gets a surface at rest; a button carrying a glyph is
    // already legible as one and stays transparent until the pointer reaches it.
    // (ICoreButton::wearsRestPlate is where that rule is spelled out.)
    //
    // WELL OFF interaction.controlFill, in OPPOSITE directions per theme: darker
    // than the inline control fill on light, lighter on dark. It began as that
    // token exactly, which was the wrong read — a combo box and a text field are
    // things you type INTO and sit level with the panel, while a button is a
    // thing you press and has to sit proud of it. Both moves are the same move
    // said in each theme's own vocabulary: away from the surface, which on a
    // near-white panel means down the grey ramp and on a g950 one means up.
    //
    // The distance is NOT symmetric, and the asymmetry is the finding rather
    // than an accident: dark went a clean two stops out to g700 and settled
    // first time, while light came back through five passes to sit halfway
    // between g100 and g200 — barely off its own panel. A dark panel gives a
    // plate room to be a surface; a near-white one has almost none between
    // invisible and loud, and this theme wants the quiet end of it. The .cpp
    // carries both ladders. Retune each on its own theme; a move that looks like
    // restoring symmetry is a move nobody looked at.
    //
    // ⚠ DARK sits one stop off content.textDisabled (g700 against g600), so a
    // disabled caption there would be drawn in very nearly its own background.
    // The plate is not drawn at all while a button is disabled, which is what
    // makes that safe. Light has since retreated clear of the same collision —
    // it was g300, that theme's textDisabled EXACTLY, three passes ago — but the
    // clause is dark's and stays whatever light does. Do not restore the plate on
    // disabled buttons, and do not push either value further from its panel
    // without checking that clause is still in ICoreButton::wearsRestPlate.
    ICoreRgba restFill;
    double  hoverOpacity;        // target opacity on hover
    double  pressedOpacity;      // target opacity while pressed
    // ⚠ THE THREE OPACITIES BELOW NO LONGER REACH AN ALPHA CHANNEL. Since the
    // tinted variants went solid (2026-08-15) their fill is opaque in every
    // state; what these still do is carry the button's ONE hover animation, and
    // the painters read the travel between them as a 0..1 progress and spend it
    // lightening the plate by tintedHoverLighter. Retuning them therefore
    // changes how far a hover lifts the colour, not how much of it lands.
    double  tintedRestOpacity;   // rest end of a tinted variant's hover animation
    double  tintedHoverOpacity;  // ... and its hover end. Separate from the plain
                                // button's hoverOpacity above: a tint has to
                                // carry its own body over the surface, and how
                                // much body that takes differs per theme, where
                                // the plain grey wash is the same job in both
    double  dangerHoverOpacity;  // hover end for danger buttons — deliberately
                                // above hoverOpacity: a muted fill at the shared
                                // 0.5 barely moves against the rest state, and
                                // Cancel/Delete must clearly answer the pointer
    // How far a solid variant's fill LIGHTENS at full hover, as an
    // ICoreColor::lighter() factor (100 = unchanged). This is the whole of the
    // pointer response now that the fill is opaque, so it is the knob to turn if
    // a hovered Commit/Delete does not read — and it is per theme because the
    // same lift over a near-white panel and over a g950 one are not the same
    // move. Press pushes past it: the progress is clamped a little above 1, so a
    // held button lifts further in the same direction it always did.
    int     tintedHoverLighter;
    // The variant's HOVER STROKES — the nav treatment's hairline and its left
    // accent edge — as ONE colour per theme, shared by all three plates.
    //
    // ⚠ IT IS NOT DERIVED FROM THE PLATE, and two attempts at deriving it were
    // deleted on 2026-08-15 before this landed. Stepping the plate's own hue
    // away from itself (the `tintedEdgeLighter` factor that stood here) makes
    // the stroke a shade of the button, so it reads as the surface catching
    // light rather than as a mark appearing on it. Measuring the caption's ink
    // per plate (ICoreThemeBinding::inkOn) fixes that but splits the treatment:
    // the light theme's royal plate then took a near-white frame while its red
    // and green took a near-black one, so the three siblings no longer hovered
    // alike. Owner chose one colour per theme over either.
    //
    // The two themes go OPPOSITE ways, which is the same finding
    // ICoreNavItemStyle records for its own strokes: over pale plates on a
    // near-white panel only a dark step defines an edge, and over deepened
    // plates on a g950 one only a lit step is seen. Neutral on light so it is
    // never any plate's own hue; the near-white on dark is already neutral
    // enough at that ground.
    //
    // Measured against the three plates it lands on (light g800 / dark onRoyal):
    //
    //   light   royal 2.8:1     danger 3.7:1     success 5.0:1
    //   dark    royal 9.6:1     danger 6.3:1     success 4.7:1
    //
    // ⚠ The light theme's royal is still the weak one, a shade under the 3:1
    // that non-text contrast wants — it was 2.5:1 until that plate was lifted to
    // royalBright 0.45, which is the lever this note named and the owner then
    // pulled for other reasons. It is accepted because the frame is not the only
    // thing that moves: the plate lightens by tintedHoverLighter underneath it.
    // Lifting the plate further is still the only fix that does not split the
    // treatment again — but it spends the caption, which is already past AA
    // there. Do not spend it silently.
    ICoreRgba tintedEdge;
    ICoreRgba primaryFill;         // brand variant (royal)
    ICoreRgba primaryText;
    ICoreRgba ghostBorder;         // outline variant
    ICoreRgba ghostText;
    ICoreRgba dangerFill;          // semantic variants
    ICoreRgba successFill;
    ICoreRgba warningFill;
    ICoreRgba graphicsHoverFill;   // hover pill of buttons floating on the canvas (translucent, royal-tinted)
    int    radius;
    int    fadeMs;              // hover fade duration
};
};

ICoreToggleTokens#

ICoreTheme.h:225 · struct · 0 declaration(s)

struct ICoreToggleTokens {
public:
    ICoreRgba track;               // switch track at rest
    ICoreRgba trackHover;
    ICoreRgba trackOn;             // the lit track, when checked
    ICoreRgba trackOnBorder;       // and its rim
    ICoreRgba knobOn;              // knob when checked
    ICoreRgba knobOff;
};
};

ICoreSyntaxTokens#

ICoreTheme.h:234 · struct · 0 declaration(s)

struct ICoreSyntaxTokens {
public:
    ICoreRgba background;          // code editor background
    ICoreRgba text;                // default code text
    ICoreRgba keyword;
    ICoreRgba function;
    ICoreRgba number;
    ICoreRgba string;
    ICoreRgba comment;
};
};

ICoreTabTokens#

ICoreTheme.h:244 · struct · 0 declaration(s)

struct ICoreTabTokens {
public:
    ICoreRgba barBackground;       // strip holding the tab headers
    ICoreRgba activeBackground;    // active tab pill
    ICoreRgba activeText;
    ICoreRgba inactiveText;
    ICoreRgba headerSelected;      // focused tab header widget
    ICoreRgba headerUnselected;
    int    radius;
};
};

ICoreMenuTokens#

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

struct ICoreMenuTokens {
public:
    ICoreRgba barBackground;       // context menu bar
    ICoreRgba background;          // popup background
    ICoreRgba text;
    ICoreRgba hoverBackground;     // highlighted item
    ICoreRgba hoverText;
    int    radius;
    int    itemPaddingV;
    int    itemPaddingH;
};
};

ICoreFieldTokens#

ICoreTheme.h:265 · struct · 0 declaration(s)

struct ICoreFieldTokens {
public:
    ICoreRgba background;          // line edits, directory field
    ICoreRgba border;
    ICoreRgba text;
    int    radius;

    // Fill for a box showing a value the user cannot type in, sitting beside
    // ones they can. Both themes need it and neither can borrow another token:
    // on dark, background is the near-black both kinds would wear; on light it
    // is pure white, and so is panelRaised. Pair it with content.textSecondary.
    ICoreRgba readOnlyBackground;

    // Text selection INSIDE an editable field -- the wash behind the selected
    // characters and the ink drawn on it. Without these a field falls back to
    // the toolkit's own highlight, which is the platform blue and belongs to
    // no theme here.
    //
    // Solid rather than translucent, unlike interaction.selectionFill: that one
    // washes over a canvas whose ink is not underneath it, whereas this one has
    // the field's text on top of it, so the pairing must be legible on its own
    // terms. A translucent wash over `background` gives an ink contrast that
    // depends on the field's fill, which call sites are free to override
    // (setFieldBackground), so it cannot be checked once and trusted.
    ICoreRgba selectionBackground;
    ICoreRgba selectionText;

    // Field metrics. A themed field draws its own border rather than using the
    // Qt frame, so nothing else gives the text room -- these are what keep it
    // off the border, and what a field falls back to when a layout does not
    // pin its height.
    int    paddingH;
    int    paddingV;
    int    minHeight;
};
};

ICoreBlockTokens#

ICoreTheme.h:300 · struct · 2 declaration(s)

struct ICoreBlockTokens {
public:
    ICoreRgba frameFill;           // canvas block body
    ICoreRgba frameBorder;
    double  frameBorderWidth;
    ICoreRgba selectionFill;       // block selection overlay (translucent)
    ICoreRgba selectionBorder;
    int    radius;

    // ---- The live readout plate -----------------------------------------
    //
    // What a block wears when it shows a VALUE on its own face — today the
    // Display block, which paints its input there instead of logging it.
    //
    // It is its own pair rather than a borrowed one because the plate has to
    // separate from `frameFill` in BOTH themes, and nothing already in the
    // theme does. field.readOnlyBackground is the closest idea and is the
    // wrong value twice over: on light it resolves near white, a hair off the
    // g50 block body, and on dark it is panelRaised, a few percent off
    // royalTintDark. A readout the same colour as the block it sits on is not
    // a readout.
    //
    // The plate INVERTS its block in each theme, and that is the whole idea:
    // dark face with light digits under the light theme's near-white body,
    // light face with dark digits under the dark theme's near-black one. Each
    // is the furthest a plate can get from the block it is set into, which is
    // what makes it read as a separate surface rather than a shaded corner.
    //
    // ⚠ Consequently readoutText is NOT light in both themes any more, and
    // nothing may pin it: it is the ink that travels with readoutFill, and a
    // reader that snapshots it in a constructor shows dark-on-dark after a live
    // theme switch. Read it on paint, or re-read it from a theme subscription —
    // ICoreBlockViewFaceLabel does the latter.
    ICoreRgba readoutFill;
    ICoreRgba readoutBorder;
    ICoreRgba readoutText;         // the ink the CURRENT theme's plate carries,
                                   // dark or light — see the ⚠ above.

    // ---- The configuration overlay --------------------------------------
    //
    // What a block wears while its config UI is open: a SCRIM directly over the
    // block, and the two floating PANES (parameters, description) that sit
    // beside it. Two roles rather than one wash, because they answer different
    // questions — the scrim must let the block stay visible through it, and the
    // panes carry text and must not.
    //
    // The scrim is the selection's own hue at a fraction of its weight, and
    // that is deliberate. Right-clicking a block selects it, so the scrim and
    // the selection ring are almost always drawn concentrically; giving the
    // scrim a foreign hue made two rings that disagreed. Same family, separated
    // by weight, reads as one state — "selected, and open for editing" — with
    // the saturated ring still on top.
    //
    // The panes take the app's ordinary panel surface and its grayed-royal
    // outline, because that is what they are: panels, floating over the canvas
    // exactly as a menu does.
    ICoreRgba configScrimFill;     // wash over the block itself (translucent)
    ICoreRgba configScrimBorder;   // the scrim's outline
    ICoreRgba configPaneFill;      // surface of the parameter / description panes
    ICoreRgba configPaneBorder;    // their outline

    // The port stubs on a block's edge. They are drawn as strokes ON the block
    // body, so they read against frameFill rather than against the canvas.
    ICoreRgba portStub;
    ICoreRgba portStubHover;

    // The ink of the block library's own art — the glyph inside the block body
    // (a Gain's triangle, an Integrator's 1/s). That art is registered as
    // literal SVG next to each block, drawn in a single flat ink, so this is
    // the one colour a theme has to say about all of it.
    ICoreRgba iconInk;

    // A handful of glyphs carry one part in colour, because the colour IS the
    // thing being drawn: the Scope's trace, the Subsystem's wiring. Those parts
    // are the exception to the flat-ink rule above, so they get their own pair
    // rather than being flooded with iconInk.
    //
    // Both are picked to clear frameFill on their own theme — the art sits on a
    // near-white body on light and a near-black one on dark, so a single value
    // could not have held on both.
    ICoreRgba iconAccent;      // the wiring/signal teal
    ICoreRgba iconAccentAlt;   // the live-trace green
    ICoreRgba iconAccentWarm;  // the second channel / output side
    ICoreRgba iconAccentCool;  // cursors, and the die inside a package

    // Four rather than two because a glyph that draws more than one of a thing
    // has to tell them apart: the Scope shows two traces and the Subsystem's
    // pins run in and out, and one accent for both halves of either pair says
    // nothing. They are the plot series' own hues, so a two-trace Scope icon
    // and a two-trace chart agree on which colour is which channel.
};
};

ICoreLinkTokens#

ICoreTheme.h:396 · struct · 1 declaration(s)

Links: the wires between ports ---------------------------------------- A link is drawn in one of four states, and the pairs matter as much as the individual colours: hovering must clearly lift the...

struct ICoreLinkTokens {
public:
    ICoreRgba wire;                // connected, at rest
    ICoreRgba wireHover;
    ICoreRgba unconnected;         // a dangling end — drawn dashed
    ICoreRgba unconnectedHover;
    ICoreRgba tailDot;             // the dot at a branch's tail, at rest
    ICoreRgba tailDotHover;
    ICoreRgba selectionBand;       // wash behind a selected segment (translucent)
};
};

ICoreCanvasAreaTokens#

ICoreTheme.h:407 · struct · 1 declaration(s)

Canvas areas: the grouping rectangles blocks are dropped into ----------

struct ICoreCanvasAreaTokens {
public:
    ICoreRgba defaultFill;         // wash a newly created area starts with (translucent)
    ICoreRgba titleBarFill;
    ICoreRgba titleBarBorder;
};
};

ICoreTimeLineTokens#

ICoreTheme.h:426 · struct · 0 declaration(s)

Timeline: the ticked ruler inside the floating simulation bar --------- The ruler is a plate INSIDE the timeline pill, not the pill itself, so it takes its own ground rather than surface.panel — on...

struct ICoreTimeLineTokens {
public:
    ICoreRgba rulerFill;           // the plate the dashes are drawn on
    ICoreRgba rulerBorder;         // its outline against the pill
    ICoreRgba dashMajor;           // whole- and half-unit ticks
    ICoreRgba dashMinor;           // the tenth ticks between them
};
};

ICoreChartTokens#

ICoreTheme.h:444 · struct · 0 declaration(s)

Charts: the plot itself, as opposed to the window around it ----------- Qt Charts paints its own background, grid, axis rules and tick labels from a built-in theme of its own, and that theme wins o...

struct ICoreChartTokens {
public:
    ICoreRgba plotBackground;      // the chart rectangle, behind everything
    ICoreRgba plotAreaBackground;  // the data region bounded by the axes
    ICoreRgba gridLine;            // major grid
    ICoreRgba minorGridLine;       // minor grid — only a log ruler draws one
    ICoreRgba axisLine;            // the axis rule and its tick marks
    ICoreRgba tickLabel;           // the numbers running along an axis
    ICoreRgba legendText;
    ICoreRgba pointLabel;          // per-point value labels drawn onto the plot

    // The colors successive line paths are handed. Theme-independent by
    // design — see ICoreVisualIdentity::plotSeries for why.
    std::vector<ICoreRgba> seriesPalette;
};
};

ICoreInfoTokens#

ICoreTheme.h:459 · struct · 0 declaration(s)

struct ICoreInfoTokens {
public:
    ICoreRgba background;          // info label container
    ICoreRgba border;
    int    radius;
    int    padding;
};
};

ICoreTypographyTokens#

ICoreTheme.h:466 · struct · 0 declaration(s)

struct ICoreTypographyTokens {
public:
    ICoreFontSpec display;
    ICoreFontSpec title;
    ICoreFontSpec body;
    ICoreFontSpec control;              // buttons, tabs, list items
    ICoreFontSpec caption;
    ICoreFontSpec label;                // uppercase section labels, wide tracking
    ICoreFontSpec mono;                 // numeric output, paths, technical labels
};
};

ICoreTheme#

ICoreTheme.h:477 · struct · 2 declaration(s)

The resolved theme ------------------------------------------------------

struct ICoreTheme {
public:
    std::string name;
    bool isDark = false;

    ICoreSurfaceTokens     surface;
    ICoreContentTokens     content;
    ICoreInteractionTokens interaction;
    ICoreButtonTokens      button;
    ICoreToggleTokens      toggle;
    ICoreTabTokens         tab;
    ICoreMenuTokens        menu;
    ICoreFieldTokens       field;
    ICoreBlockTokens       block;
    ICoreLinkTokens        link;
    ICoreCanvasAreaTokens  canvasArea;
    ICoreTimeLineTokens    timeline;
    ICoreChartTokens       chart;
    ICoreInfoTokens        info;
    ICoreSyntaxTokens      syntax;
    ICoreTypographyTokens  type;

    static ICoreTheme deriveLight(const ICoreVisualIdentity& id);
    static ICoreTheme deriveDark(const ICoreVisualIdentity& id);
};
};

ICoreThemeManager.h#

src/ICoreEssentials/Theme/ICoreThemeManager.h

ICoreThemeManager#

ICoreThemeManager.h:43 · class · pImpl · 6 declaration(s)

I-Core Theme Manager — Layer 2 of the styling module.

class ICoreThemeManager {
public:
    static ICoreThemeManager& instance();

    // The ACTIVE theme's name ("Light"/"Dark") -- this manager's own state,
    // not the user's stored preference. Until D4 this read
    // ICoreUserPreferences::getEffectiveTheme(); the two agree from startup on
    // because Initialization.cpp applies the preference via setActiveTheme()
    // and re-applies it on every preference/system-appearance change. Asking
    // the styling module what theme is ACTIVE is an SDK question; asking what
    // the user CHOSE is an app question, and now lives with the app
    // (ESSENTIALS_INDEPENDENCE D4 -- initializeTheme() moved to
    // Initialization.cpp whole for the same reason).
    static std::string getThemeName();

    // The active theme's tokens — the one access point for all styling.
    static const ICoreTheme& theme();
    static const ICoreVisualIdentity& identity();

    // Runtime theme switch ("Light" / "Dark"). Fires onThemeChanged.
    //
    // std::string, not ICoreString: ICoreString forwards to QString, so taking
    // one here would reach Qt through the back door. This was caught by
    // theme_qt_free_selftest rather than by the boundary scan — the scan sees
    // no Qt token, and the type resolved anyway in normal builds because pch.h
    // delivers it. That is the whole reason the selftest target exists.
    void setActiveTheme(const std::string& themeName);

    // The theme-change notification. Fired from the single site in
    // setActiveTheme() — there is exactly one, which is what made retiring the
    // old Qt signal safe: a mirror wired at only some emit sites is the failure
    // mode that leaves subscribers silently un-notified.
    //
    // Subscribers that own a toolkit object should go through ICoreThemeBinding
    // rather than calling connect() here: it ties the subscription to that
    // object's destruction, which is what the old three-argument connect did.
    // Subscribers that own an ICoreSignalScope can connect to this directly.
    ICoreSignal<>& onThemeChanged();

    // The "Customize UI Performance" forwards to ICoreUserPreferences that
    // stood here are gone (ESSENTIALS_INDEPENDENCE D4). The rendering-budget
    // flags are ICoreRenderingPolicy's (UI/System/), which the app's
    // preferences feed; the two link-path flags were never styling and their
    // single callers each read ICoreUserPreferences directly now.

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

ICoreVisualIdentity.h#

src/ICoreEssentials/Theme/ICoreVisualIdentity.h

ICoreVisualIdentity#

ICoreVisualIdentity.h:25 · class · 19 declaration(s)

I-Core Visual Identity — Layer 0 of the styling module.

class ICoreVisualIdentity {
public:
    static const ICoreVisualIdentity& get();

    // Rebuilds the shared identity from the shipped values with the user's
    // appearance preferences folded in: the UI font family (empty = platform
    // default), a density scale over the type/spacing primitives, and a motion
    // scale over the durations below. Always starts from the pristine values,
    // so calling it repeatedly cannot compound its own scaling.
    //
    // This only changes the primitives. ICoreThemeManager has to re-derive its
    // tokens afterwards for anything on screen to move — ICoreUserPreferences
    // does both together in applyAppearanceToStylingModule().
    // sansFamily is UTF-8. std::string rather than ICoreString because
    // ICoreString forwards to QString, and this header has to stay free of Qt
    // — both callers already hold a std::string and were wrapping it.
    static void applyUserOverrides(const std::string& sansFamily, int densityPercent, int motionPercent);

    // ------------------------------------------------------------------
    // Neutral ramp — "G" scale. Cool, navy-tinted grays.
    // g50..g950 come straight from the identity sheet's color system row;
    // the in-between stops (g200/g400/g600/g800) appear throughout the
    // sheet's UI examples (borders, secondary text, faint text on dark).
    // ------------------------------------------------------------------
    ICoreRgba g50   = ICoreRgba("#f2f3f8");
    ICoreRgba g100  = ICoreRgba("#d8dae8");
    ICoreRgba g200  = ICoreRgba("#c8cad8");
    ICoreRgba g300  = ICoreRgba("#a8aac0");
    ICoreRgba g400  = ICoreRgba("#8a8ca8");
    ICoreRgba g500  = ICoreRgba("#686a80");
    ICoreRgba g600  = ICoreRgba("#4a4c68");
    ICoreRgba g700  = ICoreRgba("#38394c");
    ICoreRgba g800  = ICoreRgba("#2a2d42");
    ICoreRgba g850  = ICoreRgba("#1e2030");
    ICoreRgba g950  = ICoreRgba("#0d0f18");

    // Brightest content color on dark surfaces (wordmark on dark).
    ICoreRgba inkOnDark = ICoreRgba("#e8eaf4");

    // ------------------------------------------------------------------
    // Royal family — the brand accent ("Navy royal").
    // ------------------------------------------------------------------
    ICoreRgba royal          = ICoreRgba("#2850d0");   // primary
    ICoreRgba royalDeep      = ICoreRgba("#1c38a8");   // deep
    ICoreRgba royalBright    = ICoreRgba("#6888e8");   // accent text on dark surfaces
    ICoreRgba royalTintDark  = ICoreRgba("#141828");   // royal-tinted dark surface
    ICoreRgba royalTintLight = ICoreRgba("#eceeff");   // royal-tinted light surface
    ICoreRgba royalContainer = ICoreRgba("#1c2848");   // filled container on dark
    ICoreRgba onRoyal        = ICoreRgba("#e8ecff");   // content on royal fills
    ICoreRgba onRoyalMuted   = ICoreRgba("#aabcf8");   // secondary content on royal

    // ------------------------------------------------------------------
    // Data / code accents (numeric output, strings in the editor).
    // ------------------------------------------------------------------
    ICoreRgba dataOrange = ICoreRgba("#e8a87c");
    ICoreRgba dataPurple = ICoreRgba("#a898e8");

    // ------------------------------------------------------------------
    // Plot series palette — the colors a chart hands out to successive line
    // paths. [PROPOSED] — not in the identity sheet.
    //
    // This is the one part of the plot that is deliberately NOT theme-aware.
    // A path's color is saved with the model, so a palette that changed with
    // the theme would reopen a plot in colors it was never saved in, and a
    // figure exported yesterday would not match the one exported today.
    //
    // The cost of one shared list is that no entry can sit at either end of
    // the value range: it has to carry against a white plot area and against
    // a near-black one. That is why the royal is lifted here and the two data
    // accents are deepened — the shipped tones are tuned for one background
    // each, and these have to do both.
    // ------------------------------------------------------------------
    std::vector<ICoreRgba> plotSeries = {
        ICoreRgba("#4a7ce8"),   // blue    — the royal, lifted to clear a dark plot
        ICoreRgba("#d8905c"),   // amber   — dataOrange, deepened to clear a white one
        ICoreRgba("#30a06a"),   // green   — success, mid-toned as shipped
        ICoreRgba("#8878d8"),   // violet  — dataPurple, deepened likewise
        ICoreRgba("#c24a56"),   // crimson — danger, mid-toned as shipped
        ICoreRgba("#2ba0b8"),   // teal
        ICoreRgba("#c89628"),   // gold
        ICoreRgba("#d868a8"),   // pink
    };

    // ------------------------------------------------------------------
    // Semantic accents. [PROPOSED] — not in the identity sheet.
    // Chosen muted/cool to sit next to the royal family.
    // ------------------------------------------------------------------
    ICoreRgba success = ICoreRgba("#30a06a");
    // Cool, muted crimson rather than a fiery vermillion: this fills Cancel and
    // Delete buttons, which should read as "stop" without alarming — and its blue
    // side sits with the royal family instead of fighting it. (The old #d04438
    // had B < G, pulling it orange, away from the rest of the palette.)
    ICoreRgba danger  = ICoreRgba("#c24a56");
    ICoreRgba warning = ICoreRgba("#e8a87c");          // reuses dataOrange

    // ------------------------------------------------------------------
    // Typography. Pixel sizes, matching the identity sheet.
    // Empty family = platform default UI font; mono resolved by the theme.
    // ------------------------------------------------------------------
    // UTF-8, std::string for the same reason applyUserOverrides takes one.
    std::string fontSansFamily = "";             // system UI font
    std::string fontMonoFamily = "";             // system fixed font

    int fontSizeDisplay  = 36;                   // hero text,   weight 500
    int fontSizeHeadline = 26;                   // wordmark,    weight 500
    int fontSizeTitle    = 22;                   // titles,      weight 500
    int fontSizeBody     = 16;                   // body copy,   weight 400
    int fontSizeControl  = 13;                   // buttons,     weight 500
    int fontSizeCaption  = 12;                   // captions,    weight 400
    int fontSizeLabel    = 11;                   // UPPERCASE section labels
    int fontSizeMicro    = 10;                   // smallest legible labels

    // Letter spacing as a percent of character width, 100 = normal.
    // Display/title track tight; labels track wide.
    double trackingTight = 97.0;                  // -3%  (display / titles)
    double trackingWide  = 112.0;                 // +12% (uppercase labels)

    // ------------------------------------------------------------------
    // Corner radii.
    // ------------------------------------------------------------------
    int radiusSmall   = 4;                       // list items, tags
    int radiusTab     = 5;                       // tabs, canvas blocks
    int radiusControl = 7;                       // buttons
    int radiusField   = 8;                       // inputs, swatches
    int radiusPanel   = 12;                      // cards, panels
    int radiusLarge   = 16;                      // hero containers, windows
    int radiusPill    = 9999;                    // pills / tags

    // ------------------------------------------------------------------
    // Spacing & strokes.
    // ------------------------------------------------------------------
    int spacingUnit       = 4;                   // base grid
    int controlPaddingV   = 7;                   // button padding (7px 16px)
    int controlPaddingH   = 16;
    int itemPaddingV      = 3;                   // list/tab item padding
    int itemPaddingH      = 10;
    int fieldPaddingV     = 3;                   // input padding (3px 10px); kept
    int fieldPaddingH     = 10;                  // low enough not to clip the
                                                 // panels that pin a 24-25px field
    int fieldMinHeight    = 28;                  // inputs, when no layout pins one
    double hairline        = 0.5;                 // hairline borders/dividers

    // ------------------------------------------------------------------
    // Motion. [PROPOSED] standard = existing app behavior (200ms OutCubic).
    // ------------------------------------------------------------------
    int motionFastMs     = 120;
    int motionStandardMs = 200;
    // Kept as the brand's stated motion curve even though nothing reads it yet
    // — it is a value from the identity sheet, not dead plumbing. ICoreEasing
    // is the wrapper layer's own enum (ICoreInputEnums.h) and names no Qt type.
    ICoreEasing motionEasing = ICoreEasing::OutCubic;
};
};

ICoreStyles.h#

src/ICoreEssentials/Theme/QtBinding/ICoreStyles.h

The scene-tier twin of configureAsPurpleButton is NOT here: it is ICoreGraphicsButton::setVariant(Variant::Primary), called directly. ICoreGraphicsButton lives in StudioObjects/, above this layer, so a forwarder here would make the theme depend on a component that depends on it.

ICoreStyles#

ICoreStyles.h:12 · class · 10 declaration(s)

class ICoreStyles {
public:
    static void configureAsPurpleButton(ICoreButton* buttonToConfigure);

    static void configureAsRedButton(ICoreButton *buttonToConfigure);

    static void configureAsGreenButton(ICoreButton *buttonToConfigure);

    // The scene-tier twin of configureAsPurpleButton is NOT here: it is
    // ICoreGraphicsButton::setVariant(Variant::Primary), called directly.
    // ICoreGraphicsButton lives in StudioObjects/, above this layer, so a
    // forwarder here would make the theme depend on a component that depends
    // on it.

    // The app's overlay scrollbars — 6px, transparent track, translucent rounded
    // handle that brightens on hover, no arrow buttons — as a QSS fragment built
    // from the active theme's tokens. Scrollbars need pseudo-state selectors
    // (::handle:hover) that typed tokens can't express, so this is one of the
    // "complex pseudo-state styling" cases ICoreThemeManager's header calls out.
    //
    // Append it to a scrollable widget's own stylesheet, or set it alone on a
    // widget that has none:
    //     view->setStyleSheet(ICoreStyles::overlayScrollBarsQss());
    //
    // Panes that predate this helper (ICoreLoggerTextBox, ICoreNotificationCenter,
    // the console/Script Runner logs, ...) still carry their own inline copy of
    // the same rules; this is where new call sites should get them.
    static ICoreString overlayScrollBarsQss();

    // The OLDER overlay bar, which three panes still wear: same 6px track and
    // rounded handle, but the handle is the wash the caller names and it does
    // NOT brighten on hover, and there is no corner rule. It is a second
    // overload rather than a retune of the one above because the difference is
    // visible -- adopting the newer look here would be a change to three panes
    // dressed up as a refactor, and it is not this task's to make.
    //
    // `handleWash` is the handle's colour, alpha included (the call sites pass
    // a tertiary-text tone thinned to 120-140). `includeHorizontal` is false
    // for a pane that never scrolls sideways, matching what those panes wrote
    // by hand.
    static ICoreString overlayScrollBarsQss(const ICoreColor& handleWash,
                                            bool includeHorizontal = true);

    // The subsystem navigator's bars, which are a third look again: an OPAQUE
    // track in the panel colour rather than a transparent one, a handle that
    // does brighten on hover, and a horizontal bar collapsed to 0px because
    // that tree scrolls vertically only. Built from the same tokens the block
    // was written against.
    static ICoreString navigatorScrollBarsQss();

    // Renders an SVG flooded with `color` through SourceIn. The app's glyph
    // library ships as flat black art, so a plain setIcon() is unreadable on
    // any of the three glass fills above (or on a dark theme's own icons
    // elsewhere) — this is the one-shot fix, the same recipe ICoreProjectCard
    // and ICoreMenu use for their own theme-tinted glyphs.
    //
    // A one-shot render, not a live view: callers whose icon color can change
    // under them (a button's tint switching with the theme) must re-call this
    // from an ICoreThemeBinding::subscribe callback rather than call it once.
    static QIcon tintedIcon(const ICoreString& svgPath, const ICoreColor& color, int size);

    // Same, rasterised for a display of `devicePixelRatio`. The no-dpr
    // overload bakes at 1x, which a Retina panel then upscales -- visibly
    // softer on any glyph small enough to matter. Pass the target widget's
    // ratio when the icon is drawn into a widget rather than handed to a
    // style that will re-render it.
    static QIcon tintedIcon(const ICoreString& svgPath, const ICoreColor& color, int size,
                            double devicePixelRatio);

    // The logger pane's whole rule set (ground, text, thin overlay
    // scrollbars), derived from the active theme's tokens. Lives here rather
    // than in the panel so the panel names no toolkit selector (Qt-boundary)
    // and the look keeps one home.
    static ICoreString loggerTextBoxQss();

    // The hover card's document pane: transparent ground, no border, text in
    // `textColor`. Keyed on the pane's class, because a selector-less rule
    // would also restyle its scroll bars.
    static ICoreString hoverCardBodyQss(const ICoreColor& textColor);

    // tintedIcon()'s sibling for art we did not ship: floods an ALREADY-BUILT
    // QIcon, keeping its alpha. The two exist separately because they start
    // from different things -- that one renders one of our SVGs, this one
    // re-colours whatever the toolkit handed us, and there is no SVG path to
    // give it.
    //
    // Its reason to exist is the toolkit's own menus: since Qt 6.7 the standard
    // Cut/Copy/Paste rows carry fallback glyphs, and those are flat dark art
    // that does not follow a palette -- so on the dark theme they are a row of
    // near-black marks on a near-black menu. Null in, null out.
    static QIcon recolouredIcon(const QIcon& source, const ICoreColor& color, int size,
                                double devicePixelRatio = 1.0);

    // Re-colour every icon in a toolkit-built menu, submenus included. Pass the
    // menu's own ink (theme().menu.text); `size` is the row's icon size.
    //
    // One tint for every row, not one per state: a highlighted row's ink is
    // menu.hoverText, and following that would mean re-rendering on hover for a
    // menu the toolkit owns. ICoreMenu does track state, because it draws its
    // own rows.
    static void tintMenuIcons(QMenu& menu, const ICoreColor& color, int size,
                              double devicePixelRatio = 1.0);

    // Everything a menu the TOOLKIT built needs before it is shown: the app's
    // menu ground, and ink on the fallback glyphs above. `host` is the widget
    // that raised it -- read for the row icon size and the display ratio only.
    //
    // The ground is set on the MENU rather than left to the host's sheet, and
    // that is the whole point of this helper. A sheet does reach the popup a
    // widget spawns, so ICoreLineEdit's field rule carrying
    // toolkitContextMenuCss() is enough for a field wearing it -- but a pane
    // that writes its OWN sheet REPLACES that rule (every Chrome::None pane
    // does: the logger box, the terminal console, the script editors), and the
    // menu it raises then comes up in the platform's dress with no ground of
    // ours at all. That is what a right-click on the Quick Code output or the
    // terminal output looked like. Setting it here cannot be lost that way,
    // and costs a widget that already carries the rule nothing but a second
    // copy of the same declarations.
    //
    // For a widget WE wrote, called from its contextMenuEvent override -- the
    // menu is built per click and freed by WA_DeleteOnClose, so this runs
    // fresh each time and picks up a theme switch between two right-clicks.
    // For a widget we did not write, use followMenuIcons() below.
    static void dressToolkitMenu(QMenu& menu, const QWidget& host);

    // The same tint, for a widget whose menus we cannot intercept because we
    // did not write the widget. Watches `host` and its descendants and tints
    // every toolkit-built popup as it appears, reading the ink at that moment
    // so a theme switch between two right-clicks is picked up.
    //
    // This is for the toolkit's OWN dialogs -- QMessageBox, QInputDialog --
    // where the selectable text belongs to a label the box built for itself.
    // A widget we do own overrides contextMenuEvent instead, the way
    // ICoreLineEdit does; that is the cheaper and more direct route, and it is
    // available exactly when the class is ours.
    //
    // The watcher is parented to `host`, so it lives and dies with it. Safe to
    // call before the box has built its labels.
    static void followMenuIcons(QWidget& host);
};
};

ICoreThemeBinding.h#

src/ICoreEssentials/Theme/QtBinding/ICoreThemeBinding.h

ICoreThemeBinding#

ICoreThemeBinding.h:47 · class · 12 declaration(s)

ICoreThemeBinding -- attaches toolkit objects to the active theme.

class ICoreThemeBinding {
public:
    ICoreThemeBinding() = delete;

    // Token colour with a different alpha (for translucent washes of a token).
    static ICoreColor withAlpha(ICoreColor c, int alpha);

    // The caption ink an OPAQUE fill of `surface` can carry: whichever of
    // content.textOnAccent (near-white) and content.textOnLightFill
    // (near-black) lands further from it, by WCAG relative luminance.
    //
    // It exists because the solid button variants are three plates of very
    // different brightness in the same theme, so no single token is right for
    // all of them: on the light theme the royal plate wants the near-white ink
    // at 4.2:1 while the red and green plates want the near-black one — the
    // near-white on green measures 2.3:1, which is the failure this replaced.
    // Measuring rather than tabulating also means a retune of any fill carries
    // its caption with it instead of silently going unreadable.
    //
    // ⚠ ALPHA IS IGNORED — the answer is only correct for a fill that actually
    // covers its ground. A translucent wash's effective brightness is its
    // ground's, which this cannot see.
    static ICoreColor inkOn(const ICoreColor& surface);

    template <typename ApplyFn>
    static void subscribe(QObject* owner, ApplyFn apply) {
        apply();
        // Was QObject::connect(&instance(), &ICoreThemeManager::themeChanged,
        // owner, apply). icoreConnectUntilDestroyed severs on exactly the same
        // event -- it parents a scope holder to `owner` -- so the lifetime
        // contract every call site was written against is unchanged.
        icoreConnectUntilDestroyed(ICoreThemeManager::instance().onThemeChanged(),
                                   owner, apply);
    }

    // The same subscription for a converted wrapper, which is no QObject.
    // Resolves through the wrapper's handle, so the subscription is owned by
    // the Impl -- which dies with the wrapper, giving the identical lifetime
    // contract the QObject* overload has always had.
    //
    // ⚠ THIS WAS `subscribeNative` FROM P0.1 UNTIL P8.2 COLLAPSED IT, exactly
    // as its note promised. It could not be an overload while any wrapper
    // derived BOTH QWidget and ICoreNativeWidget -- `subscribe(this, ...)`
    // was R4-ambiguous inside every such class (~80 sites). Phase 5 removed
    // the last dual base, so the pair below is unambiguous now: no class in
    // the tree is both a QObject and an ICoreNativeWidget. If a class like
    // that ever returns, this overload is what breaks -- restore the distinct
    // name rather than casting at 80 call sites.
    template <typename ApplyFn>
    static void subscribe(ICoreNativeWidget* owner, ApplyFn apply) {
        subscribe(icoreNativeObjectOfWidget(owner), apply);
    }

    // The ITEM flavour (P2.10b-6c). The text tier is graphics items, not
    // widgets, so the widget overload above does not reach it -- and the scene
    // tier's P2.9d-4 needs this same function for its 19 sites, so it is
    // shared plumbing rather than text-tier plumbing.
    //
    // ICoreNativeWidget and ICoreNativeItem are DISJOINT -- checked, no class
    // in the tree implements both -- so the pair of wrapper overloads is never
    // ambiguous with each other. ⚠ The one residual ambiguity is a STYLED
    // SUBCLASS that is both a QObject and an ICoreNativeItem (the chart pair,
    // P1.10's recorded decision): its one `subscribe(this, ...)` site spells
    // `static_cast<QObject*>(this)` to pick the QObject overload.
    //
    // ⚠ icoreNativeObjectOfItem() returns NULL for an item that is not a
    // QGraphicsObject, and subscribe() must tolerate that -- see its note.
    template <typename ApplyFn>
    static void subscribe(ICoreNativeItem* owner, ApplyFn apply) {
        subscribe(icoreNativeObjectOfItem(owner), apply);
    }

    static void repaintOnThemeChange(QWidget* widget);

    // P5.10: the widget twin of the ICoreNativeItem* flavour below, for the
    // same reason -- `repaintOnThemeChange(this)` from a converted ICoreWidget.
    // An OVERLOAD here where the item flavour is a REPLACEMENT, because the
    // QWidget* form still has live not-yet-converted callers.
    static void repaintOnThemeChange(ICoreNativeWidget* widget);
    // QGraphicsItems are not QWidgets and have their own update(); a graphics
    // item still needs a QObject to own the subscription, which QGraphicsObject
    // is.
    //
    // ⚠ THIS IS A REPLACEMENT OF THE OLD `QGraphicsObject*` FLAVOUR, NOT AN
    // OVERLOAD BESIDE IT, AND THE DIFFERENCE IS R4 (P2.9d-5c). An
    // `ICoreNativeItem*` overload sitting next to a `QGraphicsObject*` one is
    // AMBIGUOUS for every class that still has both bases -- which is the whole
    // scene tier until P2.9d-5e flips it. A replacement is never ambiguous, and
    // it is available here only because the survey showed **all 16 callers are
    // ICoreGraphicsObject descendants**; the other 15 `repaintOnThemeChange`
    // sites in the tree take the QWidget flavour and are untouched.
    //
    // Note this is the OPPOSITE resolution to subscribe()'s item overload, which
    // could safely overload the name. Both are correct: that one adds a
    // wrapper-interface parameter where the existing overload takes a DIFFERENT
    // wrapper interface (disjoint), this one would add it beside a raw QT BASE
    // the same class still derives from. **Read which situation you are in
    // before copying either.**
    //
    // Costs the call sites NOTHING: all 16 spell `repaintOnThemeChange(this)`
    // and an ICoreGraphicsObject* converts to ICoreNativeItem* implicitly.
    static void repaintOnThemeChange(ICoreNativeItem* item);

    // Installs the canvas drop shadow on `target` and KEEPS FOLLOWING the
    // preference -- the effect is added when Canvas Items Shadows is switched
    // on and deleted when it is switched off, for items already on the canvas.
    // Asking the preference once in a constructor instead is what used to leave
    // the toggle looking dead until the next launch: canvas items live for the
    // whole session, so they never re-read it.
    //
    //      ICoreThemeBinding::followCanvasShadowPreference(this);   // constructor
    //
    // Two overloads, not the template this used to be (P2.9d-5e). The template
    // spelled `target->graphicsEffect()` on the deduced type, which stopped
    // compiling for the scene tier the moment ICoreCanvasObjectsRoot lost its
    // Qt base -- and would have stopped for the widget tier at P5.x the same
    // way. Overloads on the two wrapper interfaces unwrap in the .cpp instead,
    // so BOTH tiers' flips leave this seam untouched: the unwrap keeps
    // answering (it reaches the Impl once there is one). Each call site's
    // `this` converts to exactly one of the two -- no class implements both
    // interfaces -- so the calls did not move. This also retires the
    // P5.10/P5.11 gate the d-5b board bullet recorded for this pair.
    //
    // The subscription is owned by the target's native QObject (the widget, or
    // the item's QGraphicsObject), so it dies with the toolkit object and can
    // never fire into a destroyed one -- the same lifetime the template's
    // QObject context gave it. The shadow's colour follows the theme on its
    // own, so the call site needs nothing further.
    //
    // Only for targets whose graphics effect is this shadow and nothing else:
    // the presence of an effect is what tells the two states apart.
    static void followCanvasShadowPreference(ICoreNativeWidget* target);
    static void followCanvasShadowPreference(ICoreNativeItem* target);

    // The same contract for a scene-tier FLOATING PANEL -- a root that sits OVER
    // the canvas rather than being canvas content. Two things differ from the
    // pair above, and both are what "floating panel" means:
    //
    //   - it reads floatingPanelShadowsEnabled(), the preference the menus and
    //     popups honour, not the canvas-item one (which is off by default
    //     because it costs one effect per diagram item);
    //   - it leaves ICoreDropShadow's preset geometry alone, so the panel casts
    //     EXACTLY the shadow the canvas context menu casts -- blur 24, offset
    //     (0, 6). The canvas-item flavour flattens both on purpose; a panel is
    //     meant to read as lifted.
    //
    // There is deliberately no widget counterpart: every widget popup is built
    // per show, so ICoreDropShadow::installOnFloatingPanel's read-once is right
    // for those. A canvas panel is RECYCLED (ICoreStudioGarbageRecycling) and
    // outlives the preferences dialog many times over, so it has to keep
    // following or the toggle looks dead on a panel that has been shown once.
    //
    // Only for targets whose graphics effect is this shadow and nothing else --
    // the same tell, and the same reason, as the pair above.
    static void followFloatingPanelShadowPreference(ICoreNativeItem* target);
};
};

ICoreThemeSubscription#

ICoreThemeBinding.h:215 · class · pImpl · 4 declaration(s)

A subscription owner for classes that are NOT QObjects -- plain QGraphicsItems, which is most of the canvas.

class ICoreThemeSubscription {
public:
    ICoreThemeSubscription();

    // Out of line: Impl is incomplete here.
    ~ICoreThemeSubscription();

    // ⚠ THE SEAM THAT LETS THE MEMBER TEMPLATE BELOW KEEP WORKING (H6.2).
    // bind() is a template, so exemption 1 keeps its body in this header --
    // but the QObject it used to name directly is private DATA, which the rule
    // bans and which Impl hides behind an incomplete type. A template body
    // cannot reach impl->m_connectionOwner here; it CAN call a non-template
    // member whose definition lives in the .cpp. Same answer H4.52 reached for
    // ICoreWeakObject's three member templates.
    [[nodiscard]] QObject* connectionOwner();

    template <typename ApplyFn>
    void bind(ApplyFn apply) {
        ICoreThemeBinding::subscribe(connectionOwner(), apply);
    }

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

ICoreThemeQss.h#

src/ICoreEssentials/Theme/QtBinding/ICoreThemeQss.h

ICoreThemeQss#

ICoreThemeQss.h:28 · class · 49 declaration(s)

ICoreThemeQss -- theme tokens rendered as toolkit stylesheet text.

class ICoreThemeQss {
public:
    ICoreThemeQss() = delete;

    // A token colour as stylesheet text. The spaces after the commas are part
    // of the contract -- the theme suite asserts this exact spelling, because
    // it is what lands in every stylesheet the shims below build.
    static ICoreString cssRgb(const ICoreColor& c);
    static ICoreString cssRgba(const ICoreColor& c);

    // The two declarations that put an editable field's text selection on the
    // theme -- `selection-background-color` and `selection-color`, from the
    // field tokens. Returned WITHOUT a trailing semicolon, so it composes into
    // a rule body the same way getStyle_DirectoryTextField() does.
    //
    // It is one function rather than two lines at each site because a field
    // whose selection does not match the field next to it is the exact fault
    // this retires: every themed field in the app builds its sheet somewhere
    // different (the base's applyFieldStyle, and one per Chrome::None subclass,
    // which the base cannot reach), and they must all say the same thing.
    // Anything that draws a field and writes its own sheet belongs here too.
    static ICoreString fieldSelectionCss();

    // The toolkit's OWN right-click menu, themed. Whole rules, not a rule body:
    // append it to any sheet set on a widget whose text the toolkit builds a
    // standard Cut/Copy/Paste or Copy/Select All popup for.
    //
    // ⚠ It is not optional on such a widget, and that is a toolkit rule rather
    // than a taste call. Setting ANY sheet hands the whole subtree to the
    // stylesheet engine, that popup included -- and a QMenu the engine styles
    // with no background rule is drawn with NO GROUND AT ALL, floating over
    // whatever is behind the window. Every one of these menus is a QMenu the
    // toolkit made; the app's own menus are ICoreMenu, which paints itself and
    // reads nothing here.
    static ICoreString toolkitContextMenuCss();

    // ======================= LEGACY QSS SHIM ==============================
    // Generated from the active theme's tokens. Do not use in new code.
    static ICoreString getStyle_ThemePrimaryColor();
    static ICoreString getStyle_ThemeSecondaryColor();
    static ICoreString getStyle_windowRootWidget();
    static ICoreString getStyle_Widget();
    static ICoreString getStyle_TitleBar();
    static ICoreString getStyle_LeftSideToolBar();
    static ICoreString getStyle_ContextMenuBar();
    static ICoreString getStyle_ContextMenu();
    static ICoreString getStyle_ScrollPane();
    static ICoreString getStyle_SplitPane();
    static ICoreString getStyle_Label();
    static ICoreString getStyle_RunBackground();
    static ICoreString getStyle_DividerLine();
    static ICoreString getStyle_InfoLabelContainer();
    static ICoreString getStyle_StudioSurface();
    static ICoreString getStyle_TabBar();
    static ICoreString getStyle_DirectoryTextField();
    static ICoreColor getStyle_UnselectedTabHeader();
    static ICoreColor getStyle_SelectedTabHeader();
    static ICoreColor getStyle_Canvas();

    static ICoreColor getStyle_DefaultShadow();

    static ICoreColor getStyle_Selection();
    static ICoreColor getStyle_SelectionShadow();

    static ICoreString getStyle_BlockFrame();
    static ICoreString getStyle_BlockSelection();

    // ------------------------------------------------------------------
    // X5 (STUDIO_QT_INDEPENDENCE.md §10, owner-decided 2026-08-14): the QSS
    // blocks that used to live inline in Studio panels. The strings are moved
    // VERBATIM — pImpl rule R1 still requires their selectors to name the Qt
    // class, and this module is where stylesheet text is chartered to live;
    // the panels now call these and name no Qt at all. Each reads the live
    // theme; callers re-invoke from their ICoreThemeBinding::subscribe.
    // ------------------------------------------------------------------

    // A panel-surface dialog body (Copilot connection, subsystem picker).
    static ICoreString getStyle_PanelDialog();

    // The quick modal prompts — the alert boxes (ICoreMessageBox) and the
    // one-field prompts (ICoreInputDialog). Surface, ink, the field and the
    // BUTTONS: these are toolkit dialogs whose buttons are QPushButtons, so
    // the only way they wear the app's affirmative/dismissive look is a rule
    // that names them. The affirmative is reached as :default, which is the
    // button every one of those call sites already marks.
    static ICoreString getStyle_ModalPrompt();

    // Git menu's read-only text panes: panel ground, secondary ink, no frame.
    static ICoreString getStyle_GitReadOnlyPane();

    // Target verification's read-only panes (Compiler Log, Signal Map):
    // raised ground, primary ink, hairline frame at the field radius.
    static ICoreString getStyle_VerificationReadOnlyPane();

    // The verification report grid, scoped by its container's object name
    // ("ICoreVerificationReportContainer" — the caller sets it) so the label
    // rule reaches the grid's labels and nothing else.
    static ICoreString getStyle_VerificationReportContainer();

    // The Script Runner explorer's list rows (hover wash, accent selection).
    static ICoreString getStyle_ScriptExplorerList();

    // The Script Runner output log's pane (accent selection colours).
    static ICoreString getStyle_ScriptOutputPane();

    // The run-diagnosis log tree: washes over the whole row incl. the indent
    // strip, and pre-tinted branch arrows (a rule can only name a resource).
    static ICoreString getStyle_DiagnosisTree();

    // The subsystem navigator tree: transparent ground over the panel,
    // rounded hover/selection washes, the same pre-tinted arrows.
    static ICoreString getStyle_NavigatorTree();

    // -----------------------------------------------------------------------
    //  The code-editor chrome (2026-08-15), worn by all fifteen language
    //  editors. It replaced a transparent-QScrollArea rule plus a bare code
    //  surface; `getStyle_CodeEditorFrame()` was the first of those and is
    //  gone, the second is the getStyle_CodeSurface() below.
    //
    //  The sheet is set on the editor's ROOT and keyed entirely on the object
    //  names below, which the editor stamps onto its own widgets. Nothing here
    //  uses a type selector: a bare `QWidget` rule set on the root would also
    //  reach the code surface, the line edit and the combo box, all three of
    //  which dress themselves.
    // -----------------------------------------------------------------------

    // The object names getStyle_CodeEditorChrome() keys on. Constants rather
    // than literals repeated at both ends: the sheet lives here and the
    // setObjectName() calls live in ICoreStudio, so a typo in either would be
    // a rule that silently matches nothing.
    static constexpr const char* kCodeEditorRootName    = "icoreCodeEditorRoot";
    static constexpr const char* kCodeEditorToolBarName = "icoreCodeEditorToolBar";
    static constexpr const char* kCodeEditorBadgeName   = "icoreCodeEditorBadge";

    // Root ground, tool bar strip and the language badge.
    static ICoreString getStyle_CodeEditorChrome();

    // The tone of the editor's two BANDS -- the tool bar across the top of the
    // editor, and the button bar at the foot of the window hosting it. Exposed
    // as a colour rather than left inside the sheet above because the second
    // band is built by a Studio panel that is not covered by that sheet, and
    // two call sites reading surface.panel on their own is how a matched pair
    // drifts apart.
    static ICoreColor codeEditorBarFill();

    // The hairline separating either band from the code pane.
    static ICoreColor codeEditorBarEdge();

    // The code surface: ground and ink from the syntax tokens, full-bleed with
    // no frame of its own (the tool bar's bottom hairline is the only rule on
    // the pane), themed text selection.
    //
    // ⚠ NO border-radius, and that is a decision rather than an omission. A
    // QPlainTextEdit is a QAbstractScrollArea: a rounded frame is drawn behind
    // a SQUARE viewport, so the corners come back as four hard notches over
    // the curve. A code pane is full-bleed in every editor worth copying
    // anyway; the card treatment belongs to the fields, not to the surface.
    static ICoreString getStyle_CodeSurface();

    // The eyebrow caption over a chrome control ("TITLE", "SIZE"). A label
    // cannot be reached by an `#id` rule -- ICoreLabel has no setObjectName --
    // so its owner sets this on the label itself, which is also what stops
    // ICoreLabel's own themed default from flattening it.
    static ICoreString getStyle_CodeEditorEyebrow();

    // ----------------------------------------------------------------------
    //  X6 (STUDIO_QT_INDEPENDENCE.md §10, 2026-08-17). Six sheets were written
    //  INLINE in Studio panels after X5 closed that row; these carry them
    //  verbatim on X5's terms. R1 still requires the selectors to name the
    //  toolkit class -- the reasoning moved with each string, into the .cpp.
    //  Each reads the live theme; callers re-invoke from their
    //  ICoreThemeBinding::subscribe.
    // ----------------------------------------------------------------------

    // The three Chrome::None fields whose surrounding bar draws the border:
    // the command prompt, the search query, and the navigation path. Separate
    // functions rather than one, because the three sheets differ in ways a
    // move is not allowed to reconcile -- see bareFieldSheet() in the .cpp.
    static ICoreString getStyle_CommandBarField();
    static ICoreString getStyle_SearchBarField();
    static ICoreString getStyle_NavBarField();

    // A read-only cell field inside an ICoreTable (variables space's derived
    // column, pointer inspector's values). One function for both: two tables
    // spelling the same load-bearing `background-color: transparent` in two
    // places is how one of them loses it.
    static ICoreString getStyle_ReadOnlyCellField();

    // The script editor's code surface. NOT getStyle_CodeSurface(): that one
    // selects QPlainTextEdit and this editor is a rich-text widget, and their
    // selection tokens differ too.
    static ICoreString getStyle_ScriptCodeSurface();
};
};