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();
};
};