API — ICoreEssentials/UI/Values
The public contract of 10 header(s) under src/ICoreEssentials/UI/Values — 10 class/struct definition(s), 164 declaration(s). Each section shows the header's banner and its public (and protected-virtual) surface exactly as the file writes it.
ICoreColor.h#
src/ICoreEssentials/UI/Values/ICoreColor.h
ICoreColor#
ICoreColor.h:22 · class · 32 declaration(s)
The client-facing name for a colour.
class ICoreColor {
public:
ICoreColor();
ICoreColor(int r, int g, int b, int a = 255);
// Brings a Qt-free ICoreRgba back under the Qt-backed name at the seam.
//
// Implicit ON PURPOSE, and it is the hinge the styling module's migration
// turns on: the theme's token tree holds ICoreRgba, and this is what lets
// every file that reads those tokens as colours keep compiling.
//
// ⚠ An invalid ICoreRgba stays invalid rather than becoming opaque black.
// QColor(0,0,0,255) is a perfectly valid colour, so forwarding the channels
// unconditionally would launder "nobody assigned this token" into "somebody
// chose black" -- exactly the failure the theme suite's
// every_core_token_is_a_real_colour cases exist to catch.
ICoreColor(const ICoreRgba& c);
// ⚠ THE ONLY NAMED COLOUR, AND DELIBERATELY THE ONLY ONE. 45 of the sites
// this conversion broke spelled `Qt::transparent`, reaching QColor through
// the base that has now gone. The obvious fix -- an ICoreGlobalColor enum
// mirroring Qt's 20 -- was rejected on census: of 71 Qt global-colour
// spellings tree-wide, 60 are transparent and the rest are 7 black,
// 2 white, 1 gray, 1 lightGray. Twenty names across the boundary to serve
// one idiom is the trade P7.1 refused for composition modes (one of ~30 in
// use), and P0.5, P3.4 and P7.3 each refused before that. The other eleven
// sites spell their channels: ICoreColor(0, 0, 0) and so on.
//
// Fully transparent BLACK specifically -- Qt::transparent is #00000000, so
// the RGB channels survive a later setAlpha(), which several sites rely on.
// ⚠ Do NOT "simplify" this to a default-constructed ICoreColor: that one is
// INVALID, not transparent, and the two differ everywhere isValid() is read.
static ICoreColor transparent();
// Parses "#rgb", "#rrggbb" and "#aarrggbb" -- the inverse of the two
// persisted spellings below, and NAMED rather than a constructor because a
// string that fails to parse yields an INVALID colour: an implicit
// conversion would make that failure silent at the call site.
static ICoreColor fromHexString(const ICoreString& text);
ICoreColor(const ICoreColor& other);
ICoreColor(ICoreColor&& other) noexcept;
ICoreColor& operator=(const ICoreColor& other);
ICoreColor& operator=(ICoreColor&& other) noexcept;
~ICoreColor();
bool isValid() const;
int red() const;
int green() const;
int blue() const;
int alpha() const;
double redF() const;
double greenF() const;
double blueF() const;
double alphaF() const;
void setRed(int v);
void setGreen(int v);
void setBlue(int v);
void setAlpha(int v);
void setAlphaF(double v);
ICoreColor lighter(int factor = 150) const;
ICoreColor darker(int factor = 200) const;
// Named, never an operator: the layer's rule is that a type which hands out
// its Qt representation implicitly can never be unhooked later.
ICoreRgba toRgba() const;
// "#rrggbb", or "#aarrggbb" when the colour is not fully opaque.
//
// ⚠ These two spellings are PERSISTED -- they land in project files and in
// recipe scripts -- so they are pinned here rather than left to a caller to
// format. Anything that changes the digits changes what an existing saved
// project reads back as.
ICoreString toHexString() const;
// Always "#aarrggbb", alpha included even at 255.
ICoreString toHexArgbString() const;
bool operator==(const ICoreColor& other) const;
bool operator!=(const ICoreColor& other) const;
// The seam, per §1. Call icoreQt() from a .cpp rather than reaching in;
// icoreColor(const QColor&) in ICoreNativeHandleAccess.h is the way back.
const void* nativeStorage() const;
// §7 measured QColor at 16 bytes / align 4 on this tree's Qt 6.10.2,
// macOS arm64 -- a small inline value, not a d-pointer.
static constexpr std::size_t kNativeStorageSize = 16;
static constexpr std::size_t kNativeStorageAlign = 4;
};
ICoreCursor.h#
src/ICoreEssentials/UI/Values/ICoreCursor.h
ICoreCursor#
ICoreCursor.h:20 · class · 10 declaration(s)
class ICoreCursor {
public:
ICoreCursor();
ICoreCursor(ICoreCursorShape shape);
ICoreCursor(const ICorePixmap& pixmap, int hotX = -1, int hotY = -1);
ICoreCursor(const ICoreCursor& other);
ICoreCursor(ICoreCursor&& other) noexcept;
ICoreCursor& operator=(const ICoreCursor& other);
ICoreCursor& operator=(ICoreCursor&& other) noexcept;
~ICoreCursor();
// ⚠ Was an INHERITED STATIC (QCursor::pos()) and had to be re-declared by
// hand -- the first inherited *static* this migration has had to carry
// across, as opposed to an inherited instance method. Nothing about the
// pImpl shape brings it along, and its one caller
// (ICoreNotificationCenterMessageBox, testing whether the pointer is
// inside the bubble) would otherwise fail with a plain "no member named
// pos", which reads like a typo rather than like a missing forwarder.
//
// Returns the pointer position in GLOBAL screen coordinates, unchanged
// from QCursor::pos().
static ICorePoint pos();
// The seam, per §1. Call icoreQt() from a .cpp rather than reaching in.
const void* nativeStorage() const;
// Public so the .cpp can pin them; §7 measured QCursor at 8 bytes (plain
// d-pointer) against this tree's Qt 6.10.2 on macOS arm64.
static constexpr std::size_t kNativeStorageSize = 8;
static constexpr std::size_t kNativeStorageAlign = 8;
};
ICoreFont.h#
src/ICoreEssentials/UI/Values/ICoreFont.h
ICoreFont#
ICoreFont.h:30 · class · 37 declaration(s)
class ICoreFont {
public:
ICoreFont();
ICoreFont(const ICoreString& family, int pointSize, bool bold = false);
// Resolves a Qt-free ICoreFontSpec into a real toolkit font. This is the
// seam: the styling module stores what was ASKED FOR, and the platform
// query that turns an unset family into an actual typeface happens in the
// .cpp, at the moment the font is built, rather than while a theme is
// derived.
//
// ⚠ IMPLICIT, AND THAT IS LOAD-BEARING RATHER THAN CONVENIENT. Call sites
// write ICoreFontMetrics(theme.type.mono) with a SPEC, which works only
// because converting the argument spends the single user-defined
// conversion C++ allows and the ICoreFontMetrics constructor is then an
// ordinary direct-initialisation. Make this explicit and every one of
// those sites breaks with a message that does not mention this line.
ICoreFont(const ICoreFontSpec& spec);
// Implicit inbound seam, kept PERMANENTLY -- the same decision P7.4 made
// for ICorePixmap(const QPixmap&) and ICoreIcon(const QIcon&), and for the
// same reason: QWidget::font(), QPainter::font() and QGraphicsItem::font()
// all hand back a raw QFont and will keep doing so until those wrappers
// convert. ~8 sites write `ICoreFont f = someWidget->font();`. Only the
// OUTBOUND direction becomes icoreQt(); inbound stays.
ICoreFont(const QFont& font);
ICoreFont(const ICoreFont& other);
ICoreFont(ICoreFont&& other) noexcept;
ICoreFont& operator=(const ICoreFont& other);
ICoreFont& operator=(ICoreFont&& other) noexcept;
~ICoreFont();
ICoreString family() const;
void setFamily(const ICoreString& family);
int pointSize() const;
void setPointSize(int pointSize);
double pointSizeF() const;
void setPointSizeF(double pointSize);
int pixelSize() const;
void setPixelSize(int pixelSize);
// ⚠ Returns the ICore enum, and the numeric value is a FILE FORMAT, not an
// internal detail: ICoreStudioStaticCommands::serializeFont writes
// static_cast<int>(weight()) into saved projects. ICoreFontWeight is pinned
// to QFont::Weight (400/500/600/700, ICoreInputEnumsVerify.cpp), so the
// bytes are unchanged by this conversion and old projects still load.
//
// ⚠ A weight OUTSIDE the four enumerators round-trips correctly, which the
// deserializer depends on: it casts an arbitrary int from a file. That is
// well-defined only because ICoreFontWeight has a fixed underlying type
// (`: int`). Do not remove that.
ICoreFontWeight weight() const;
void setWeight(ICoreFontWeight weight);
bool bold() const;
void setBold(bool bold);
bool italic() const;
void setItalic(bool italic);
bool underline() const;
void setUnderline(bool underline);
bool strikeOut() const;
void setStrikeOut(bool strikeOut);
int stretch() const;
void setStretch(int stretch);
bool kerning() const;
void setKerning(bool kerning);
bool fixedPitch() const;
void setFixedPitch(bool fixedPitch);
// The three below replace inherited QFont ENUMERATORS that call sites were
// reaching through the wrapper name -- ICoreFont::AllUppercase,
// ICoreFont::PercentageSpacing, QFont::Monospace. Those spellings are a
// re-export hole of exactly the kind §9 records: the Qt enumerator appears
// only at the call site, where neither guard can see it. Each is named for
// the one thing its callers ask for rather than given a general enum, per
// §9 -- there are two, two and one call sites respectively.
void setAllUppercase();
void setPercentageLetterSpacing(double percent);
void setMonospaceStyleHint();
friend bool operator==(const ICoreFont& a, const ICoreFont& b);
friend bool operator!=(const ICoreFont& a, const ICoreFont& b);
// The seam, per §1: a void* rather than a forward-declared QFont*, so the
// cast lives in one place. Call icoreQt() from a .cpp instead of reaching
// in here.
const void* nativeStorage() const;
// Public only so the .cpp can pin them. Measured, not assumed: QFont is 16
// bytes / align 8 on this tree's Qt 6.10.2, macOS arm64 -- a d-pointer
// PLUS a uint resolve_mask, which is why it is not the plain 8 that most
// of Group D uses. Non-polymorphic, so unlike QPixmap the buffer is not
// additionally vptr-bound.
static constexpr std::size_t kNativeStorageSize = 16;
static constexpr std::size_t kNativeStorageAlign = 8;
};
ICoreFontMetrics.h#
src/ICoreEssentials/UI/Values/ICoreFontMetrics.h
ICoreFontMetrics#
ICoreFontMetrics.h:27 · class · 16 declaration(s)
class ICoreFontMetrics {
public:
// ⚠ NOT explicit, deliberately. Call sites pass an ICoreFontSpec
// (ICoreFontMetrics(theme.type.mono)) and rely on spec -> ICoreFont being
// the single user-defined conversion C++ permits before this constructor
// runs. Marking it explicit breaks those sites; marking ICoreFont's spec
// constructor explicit breaks them too, from the other end.
ICoreFontMetrics(const ICoreFont& font);
ICoreFontMetrics(const ICoreFontMetrics& other);
ICoreFontMetrics(ICoreFontMetrics&& other) noexcept;
ICoreFontMetrics& operator=(const ICoreFontMetrics& other);
ICoreFontMetrics& operator=(ICoreFontMetrics&& other) noexcept;
~ICoreFontMetrics();
double textWidth(const ICoreString& text) const;
ICoreRect textBoundingRect(const ICoreString& text) const;
double height() const;
double ascent() const;
double descent() const;
double lineSpacing() const;
// Shorten `text` with an ellipsis to fit `maxWidth`, middle-elided like
// the file-path labels this exists for.
ICoreString elidedMiddle(const ICoreString& text, double maxWidth) const;
ICoreString elidedRight(const ICoreString& text, double maxWidth) const;
// Elides the HEAD, so the tail stays readable -- what a file path
// wants, since its last component is the informative one.
ICoreString elidedLeft(const ICoreString& text, double maxWidth) const;
// The seam, per §1. Call icoreQt() from a .cpp rather than reaching here.
const void* nativeStorage() const;
// Public only so the .cpp can pin them. Measured, not assumed:
// QFontMetricsF is 8 bytes / align 8 on this tree's Qt 6.10.2, macOS
// arm64 -- a plain d-pointer, §7's "everything else" row. Non-polymorphic.
static constexpr std::size_t kNativeStorageSize = 8;
static constexpr std::size_t kNativeStorageAlign = 8;
};
ICoreFontSpec.h#
src/ICoreEssentials/UI/Values/ICoreFontSpec.h
ICoreFontSpec -- a Qt-free DESCRIPTION of a font request.
ICoreFont is a QFont subclass, so a token tree holding ICoreFont holds Qt. This is the value the styling module stores instead: what was asked for, not a resolved toolkit object.
The distinction matters beyond the boundary. Resolving a family against the installed-font database is a PLATFORM QUERY, and doing it while deriving a theme meant the theme could not be derived without a live font system -- which is also why the derivation could not be unit-tested off-screen. Here an unset family stays unset, and the seam that builds the ICoreFont resolves it against the platform at the moment it is actually needed.
DO NOT let a bulk QFont->ICoreFont rename touch this file, and do not add an
ICoreFontSpec#
ICoreFontSpec.h:37 · struct · 0 declaration(s)
struct ICoreFontSpec {
public:
// Empty = use the fallback below. UTF-8; std::string rather than
// ICoreString because ICoreString forwards to QString, which would put Qt
// back into every file that includes a theme token.
std::string family;
int pixelSize = 0;
ICoreFontWeight weight = ICoreFontWeight::Normal;
// Percent of character width, 100 = normal, matching the scale the old
// percentage-spacing calls used. Exactly 100 means "do not set letter
// spacing at all", which is not the same as setting it to 100 -- the old
// code branched on that and some platforms differ.
double letterSpacingPercent = 100.0;
ICoreFontFallback fallback = ICoreFontFallback::PlatformUi;
// Hidden friends: declared here so ADL finds them, DEFINED in
// ICoreFontSpec.cpp -- a body in a header is a body wherever it sits.
friend bool operator==(const ICoreFontSpec& a, const ICoreFontSpec& b);
friend bool operator!=(const ICoreFontSpec& a, const ICoreFontSpec& b);
};
};
File-scope declarations#
// What an EMPTY family should fall back to. The old derivation resolved these
// two cases differently -- a plain default-constructed font for UI text, but an
// explicit system fixed-font lookup for the mono token -- so collapsing them
// into "empty means default" would silently give the code editor the
// proportional UI font.
enum class ICoreFontFallback {
PlatformUi,
PlatformFixed,
};
ICoreIcon.h#
src/ICoreEssentials/UI/Values/ICoreIcon.h
ICoreIcon#
ICoreIcon.h:29 · class · 12 declaration(s)
class ICoreIcon {
public:
ICoreIcon();
ICoreIcon(const ICoreString& path);
ICoreIcon(const ICorePixmap& pixmap);
// The rendered raster at width x height -- inherited from QIcon before
// this conversion, and still the only way to get from "an icon" to
// "a pixmap ready to hand a QLabel-shaped setPixmap()".
[[nodiscard]] ICorePixmap pixmap(int width, int height) const;
// Whether this icon carries no art at all. Added at Q2.1: a widget that
// paints its own glyph rather than parking it in a child (ICoreHBoxButton)
// has to skip the draw entirely, and QIcon::isNull() was what it used to
// ask before the parameter swap. Without this the converted member would
// have had to keep a QIcon beside it purely to answer the question.
[[nodiscard]] bool isNull() const;
// Implicit on purpose -- see the class comment. This is the seam that
// keeps ICoreIcons' engine-backed icons flowing through ICoreIcon-typed
// locals without naming QIcon at the call site.
ICoreIcon(const QIcon& icon);
ICoreIcon(const ICoreIcon& other);
ICoreIcon(ICoreIcon&& other) noexcept;
ICoreIcon& operator=(const ICoreIcon& other);
ICoreIcon& operator=(ICoreIcon&& other) noexcept;
~ICoreIcon();
// The seam, per §1. Call icoreQt() from a .cpp rather than reaching in
// here.
const void* nativeStorage() const;
// Public so the .cpp can pin them; §7 measured QIcon at 8 bytes (plain
// d-pointer) against this tree's Qt 6.10.2 on macOS arm64.
static constexpr std::size_t kNativeStorageSize = 8;
static constexpr std::size_t kNativeStorageAlign = 8;
};
ICoreKeySequence.h#
src/ICoreEssentials/UI/Values/ICoreKeySequence.h
ICoreKeySequence#
ICoreKeySequence.h:27 · class · 16 declaration(s)
The client-facing name for a shortcut chord.
class ICoreKeySequence {
public:
ICoreKeySequence();
ICoreKeySequence(const ICoreString& text);
ICoreKeySequence(const ICoreKeySequence& other);
ICoreKeySequence(ICoreKeySequence&& other) noexcept;
ICoreKeySequence& operator=(const ICoreKeySequence& other);
ICoreKeySequence& operator=(ICoreKeySequence&& other) noexcept;
~ICoreKeySequence();
static ICoreKeySequence standardCopy();
static ICoreKeySequence standardCut();
static ICoreKeySequence standardPaste();
static ICoreKeySequence standardSelectAll();
static ICoreKeySequence standardFind();
// Added by Q2.3, which needed them: ICoreMenu's shortcut parameter stopped
// taking QKeySequence, and the File/Edit menus were spelling these two as
// QKeySequence::Save / QKeySequence::Delete at the call site. Built here
// rather than as portable text so they keep following each OS's convention
// -- Delete in particular is Backspace on macOS and Del elsewhere.
static ICoreKeySequence standardSave();
static ICoreKeySequence standardDelete();
// The chord as the platform renders it for display (⌘O on macOS, Ctrl+O
// elsewhere) -- toString(NativeText) behind the boundary. For showing a
// shortcut in UI text; not a serialization (that would be PortableText,
// which no caller needs yet).
[[nodiscard]] ICoreString nativeText() const;
// The seam, per §1: a void* rather than a forward-declared QKeySequence*,
// because a forward declaration would put the toolkit name in this public
// header, which is the thing the rule forbids. Call icoreQt() from a .cpp
// instead of reaching in here -- the cast lives in one place on purpose.
const void* nativeStorage() const;
// Public only so the .cpp can pin them; §7 measured QKeySequence at 8
// bytes (plain d-pointer) against this tree's Qt 6.10.2 on macOS arm64.
static constexpr std::size_t kNativeStorageSize = 8;
static constexpr std::size_t kNativeStorageAlign = 8;
};
ICorePalette.h#
src/ICoreEssentials/UI/Values/ICorePalette.h
ICorePalette#
ICorePalette.h:40 · class · pImpl · 9 declaration(s)
ICorePalette -- the colour roles an application hands the toolkit.
class ICorePalette {
public:
ICorePalette();
~ICorePalette();
ICorePalette(const ICorePalette& other);
ICorePalette& operator=(const ICorePalette& other);
ICorePalette(ICorePalette&& other) noexcept;
ICorePalette& operator=(ICorePalette&& other) noexcept;
void setColor(ICoreColorRole role, const ICoreColor& colour);
[[nodiscard]] ICoreColor color(ICoreColorRole role) const;
// The seam, the same shape ICoreColor's is: a .cpp inside a sanctioned zone
// casts this back to the toolkit type. Nobody else touches it, and the
// header stays free of the name it points at.
[[nodiscard]] const void* nativeStorage() const;
private:
class Impl; // the two-line residue; state lives here
std::unique_ptr<Impl> impl;
};
File-scope declarations#
// The application-wide colour roles, pinned to the toolkit's own values in the
// .cpp with static_asserts -- the Q0.1 shape, so an or-ed or switched value
// keeps meaning what it meant before the wrapper existed.
//
// ⚠ FIVE ROLES, not the toolkit's twenty. These are the ones this application
// actually sets (Initialization::configureApplication). Adding a role is one
enum class ICoreColorRole {
WindowText = 0,
Text = 6,
ButtonText = 8,
HighlightedText = 13,
PlaceholderText = 20,
};
ICorePixmap.h#
src/ICoreEssentials/UI/Values/ICorePixmap.h
ICorePixmap#
ICorePixmap.h:33 · class · 19 declaration(s)
class ICorePixmap {
public:
ICorePixmap();
ICorePixmap(const ICoreString& path);
// Matches the two shapes call sites actually build with: a widget's own
// size() cast to ints, and a devicePixelRatio-scaled size computed by
// hand. No QSize overload -- naming QSize here would put a Qt type back
// in this header for a shape no caller needs; both existing call sites
// already have (or can trivially get) the two ints.
ICorePixmap(int width, int height);
// Implicit on purpose: theme and wrapper APIs (ICoreSvg, QIcon::pixmap())
// still hand back a raw QPixmap, and this is the one conversion point
// that lets ICorePainter::drawPixmap and friends accept either without
// every producer having to spell the wrapper name.
ICorePixmap(const QPixmap& pixmap);
ICorePixmap(const ICorePixmap& other);
ICorePixmap(ICorePixmap&& other) noexcept;
ICorePixmap& operator=(const ICorePixmap& other);
ICorePixmap& operator=(ICorePixmap&& other) noexcept;
~ICorePixmap();
bool isNull() const;
void fill(const ICoreColor& color);
void setDevicePixelRatio(double ratio);
// Scaled to exactly width x height, distorting the aspect ratio if it
// does not match the source's -- the one shape a call site in this tree
// asks for. Add a KeepAspectRatio sibling when a second caller needs one;
// do not add a mode parameter for a single caller (§9).
[[nodiscard]] ICorePixmap scaledIgnoringAspect(int width, int height) const;
// The KeepAspectRatio sibling the comment above promised, added at E3 for
// the block-frame renderer: as large as fits inside width x height with
// the source's aspect ratio kept, smooth like its sibling.
[[nodiscard]] ICorePixmap scaledToFit(int width, int height) const;
// The raster's extent in device pixels (QPixmap::width/height). Added at
// E3: the block frame's icon math reads the loaded art's size before
// deciding how to scale it.
[[nodiscard]] int width() const;
[[nodiscard]] int height() const;
// The 32-bit ARGB view of this raster, for hashing and pixel walks.
// Returns the toolkit type rather than a wrapper: there is no ICore image
// value type (ICoreRasterImage was deleted at P7.4 as dead surface).
// Every use of this accessor is `const auto`, so the name never appears
// at a call site.
QImage toImageArgb32() const;
// The seam, per §1. Call icoreQt() from a .cpp rather than reaching in
// here -- and prefer the const overload; see the note above this class
// for when the mutable one is actually the right call.
const void* nativeStorage() const;
void* nativeStorageMut();
// Public so the .cpp can pin them; §7 measured QPixmap at 24 bytes
// (QPaintDevice vptr + d-pointer, polymorphic and therefore ABI-bound)
// against this tree's Qt 6.10.2 on macOS arm64. This is the type that
// proves the 24-byte buffer, a role ICoreRasterImage would have carried
// had it not turned out to be dead.
static constexpr std::size_t kNativeStorageSize = 24;
static constexpr std::size_t kNativeStorageAlign = 8;
};
ICoreRgba.h#
src/ICoreEssentials/UI/Values/ICoreRgba.h
ICoreRgba#
ICoreRgba.h:35 · class · 13 declaration(s)
ICoreRgba -- an 8-bit-per-channel colour VALUE that owns its storage.
class ICoreRgba {
public:
// An invalid colour, matching the toolkit's default-constructed state: the
// channels read back as 0 but alpha reads 255, and isValid() is false.
// Reproduced rather than simplified because ICoreVisualIdentity relies on
// value-initialised members behaving the way the old ones did.
constexpr ICoreRgba() = default;
constexpr ICoreRgba(int r, int g, int b, int a = 255)
: m_r(clampChannel(r))
, m_g(clampChannel(g))
, m_b(clampChannel(b))
, m_a(clampChannel(a))
, m_valid(true) {
}
// Accepts "#rgb", "#rrggbb" and "#aarrggbb" -- the three spellings the
// toolkit parses that this project actually writes. ALPHA COMES FIRST in
// the 8-digit form, matching HexArgb and ICoreColor::toHexString(), which
// is a PERSISTED format: it lands in project files and recipe scripts.
// Anything that reorders those digits changes what an existing saved
// project reads back as.
//
// A string that does not parse yields an invalid colour rather than
// throwing, which is what the toolkit does with an unrecognised name.
constexpr explicit ICoreRgba(std::string_view hex) {
parseHex(hex);
}
constexpr bool isValid() const { return m_valid; }
constexpr int red() const { return m_r; }
constexpr int green() const { return m_g; }
constexpr int blue() const { return m_b; }
constexpr int alpha() const { return m_a; }
constexpr void setAlpha(int a) { m_a = clampChannel(a); }
// Returns a copy with the alpha replaced. The free-standing withAlpha()
// helper in ICoreTheme.cpp did exactly this, by value.
constexpr ICoreRgba withAlpha(int a) const {
ICoreRgba c = *this;
c.setAlpha(a);
return c;
}
// Linear interpolation between two colours, t in [0..1].
//
// Two behaviours here are inherited verbatim from the toolkit-backed
// version this replaces, and both are load-bearing for the derived themes:
// 1. The RESULT IS ALWAYS OPAQUE. The original built its result from a
// three-argument constructor, which sets alpha to 255 regardless of
// what the operands' alphas were. Alpha is NOT interpolated.
// 2. The channel maths TRUNCATES toward zero (static_cast<int>), it does
// not round. Rounding would shift derived tokens by a unit and move
// every golden image.
static constexpr ICoreRgba mix(const ICoreRgba& a, const ICoreRgba& b, double t) {
return ICoreRgba(
static_cast<int>(a.m_r + (b.m_r - a.m_r) * t),
static_cast<int>(a.m_g + (b.m_g - a.m_g) * t),
static_cast<int>(a.m_b + (b.m_b - a.m_b) * t));
}
// "#rrggbb", or "#aarrggbb" when the colour is not fully opaque.
// Mirrors ICoreColor::toHexString() exactly -- see the persistence note on
// the hex constructor above.
std::string toHexString() const {
return m_a == 255 ? toHexRgbString() : toHexArgbString();
}
std::string toHexArgbString() const {
std::string s = "#";
appendByte(s, m_a);
appendByte(s, m_r);
appendByte(s, m_g);
appendByte(s, m_b);
return s;
}
std::string toHexRgbString() const {
std::string s = "#";
appendByte(s, m_r);
appendByte(s, m_g);
appendByte(s, m_b);
return s;
}
friend constexpr bool operator==(const ICoreRgba& a, const ICoreRgba& b) {
return a.m_valid == b.m_valid && a.m_r == b.m_r && a.m_g == b.m_g
&& a.m_b == b.m_b && a.m_a == b.m_a;
}
friend constexpr bool operator!=(const ICoreRgba& a, const ICoreRgba& b) {
return !(a == b);
}
};