API — ICoreEssentials/UI/Backends/WinUI/Widgets
The public contract of 16 header(s) under ICoreEssentials/UI/Backends/WinUI/Widgets — 17 class/struct definition(s), 170 declaration(s). Each section shows the header's banner and its public (and protected-virtual) surface exactly as the file writes it.
ICoreWinUIAccessibility.h#
ICoreEssentials/UI/Backends/WinUI/Widgets/ICoreWinUIAccessibility.h
The accessibility floor for the painted lane: what a screen reader is told a painted
Canvasis, called from the seats that already know.⚠⚠ THIS HEADER NAMES WINRT AND THEREFORE BELONGS TO THIS BACKEND ZONE AND NOWHERE ELSE. Including it from a widget, a panel or anything above the backend is what the toolkit-boundary census row refuses everywhere else in this tree.
⚠ IT DECIDES NOTHING. Which role a control has, what a client is told about a state UIA cannot carry, whether a name is a name, whether a position is a position -- all of it is answered by ICoreWinUIAccessibilityFields.h, which names no toolkit and can be driven by a suite on a machine with no Windows App SDK. What is left here is the
AutomationPropertiescalls and the static_asserts that pin the two files together. That is the same split the
Declares no class of its own — see the file.
ICoreWinUIAccessibilityFields.h#
ICoreEssentials/UI/Backends/WinUI/Widgets/ICoreWinUIAccessibilityFields.h
The DECISIONS behind the accessibility floor for the painted lane, with no WinRT in them: which shape gets which UIA control type, what a client is told about a state that UIA's attached properties cannot carry, and which of the two "remove this from the tree" mechanisms applies.
⚠ IT IS THE SAME SPLIT AS System/ICoreWinUIDropFields.h AND FOR THE SAME REASON. The seat beside it (ICoreWinUIAccessibility.h) holds nothing but the
AutomationProperties::Set*calls; everything that could be WRONG rather than merely unwritten is here, in a file a suite can drive on a machine with no Windows App SDK.⚠ NOTHING ABOVE src/ICoreEssentials/UI/Backends/WinUI/ MAY INCLUDE THIS.
⚠⚠ WHY THE FILE EXISTS AT ALL, IN ONE PARAGRAPH
ICoreWinUIAccessibleState#
ICoreWinUIAccessibilityFields.h:253 · struct · 0 declaration(s)
The states a painted control may have.
struct ICoreWinUIAccessibleState {
public:
bool hasEnabled = false;
bool enabled = true;
ICoreWinUIAccessibleTristate checked = ICoreWinUIAccessibleTristate::Undefined;
ICoreWinUIAccessibleTristate expanded = ICoreWinUIAccessibleTristate::Undefined;
bool hasSelected = false;
bool selected = false;
bool busy = false;
bool hasValue = false;
double value = 0.0;
double minimum = 0.0;
double maximum = 0.0;
// The spoken form when the number alone would mean nothing ("70 percent").
// Empty leaves the number to speak for itself.
std::string valueText;
};
};
File-scope declarations#
// The shapes this product actually has.
//
// ⚠ DELIBERATELY NOT A RE-EXPORT of `AutomationControlType`. That enum has 42
// values, most of which name a control this tree does not contain, and a seat
// choosing from all of them will eventually pick one it does not mean. Each
// value here maps to exactly one UIA control type, in
enum class ICoreWinUIAccessibleRole {
Button,
CheckBox,
RadioButton,
SplitButton,
ComboBox,
Edit,
Spinner,
Slider,
ProgressBar,
ScrollBar,
Separator,
Thumb,
Text,
Image,
ToolBar,
ToolTip,
Menu,
MenuBar,
MenuItem,
List,
ListItem,
Tree,
TreeItem,
Table,
DataItem,
Tab,
TabItem,
Document,
Group,
Pane,
Window,
// The honest answer when nothing above fits. It is a real UIA control type
// and it is NOT a way of declining to choose: `Custom` with a
// LocalizedControlType reads better to a client than a wrong concrete role.
Custom,
};
// Which of UIA's three views an element appears in.
//
// ⚠ `Raw` IS THE NEAREST THING TO ARIA'S `presentation` AND IS NOT THE SAME
// THING -- see measurement 5 in the banner. It is what the painted surface
// itself is given: the `Image` inside every painted widget carries a picture of
// the control, not a picture, and left alone it arrives at a client as a
enum class ICoreWinUIAccessibleView { Raw, Control, Content };
// A checkable thing is three-valued in UIA and two-valued in most of this
// product; `Mixed` exists because UIA's ToggleState does and a partially
// checked group is expressible.
//
// ⚠ `Undefined` IS A FOURTH VALUE AND UIA'S TOGGLE STATE HAS THREE,
// deliberately. "This control is not checkable" and "this control is checkable
enum class ICoreWinUIAccessibleTristate { False, True, Mixed, Undefined };
// The facts a caller might want a client to know, and the question of whether
// this floor can actually deliver them.
enum class ICoreWinUIAccessibleFact {
Role,
Name,
Description,
HelpText,
AutomationId,
LocalizedRoleName,
PositionInSet,
Level,
ItemStatus,
LabelledBy,
DescribedBy,
KeyShortcut,
View,
// Everything from here down needs an automation PEER, because in UIA it is
// a peer property or a control pattern rather than an attached property.
Enabled,
Checked,
Expanded,
Selected,
Value,
Orientation,
ReadOnly,
MultiLine,
MultiSelectable,
GridSize,
};
ICoreWinUIButtonPainter.h#
ICoreEssentials/UI/Backends/WinUI/Widgets/ICoreWinUIButtonPainter.h
The WinUI backend's branded controls: the half of W2.2 that draws.
TWO SURFACES, AND THEY ARE GENUINELY DIFFERENT SHAPES.
- THE BUTTON, which no stylesheet describes. Its look is a set of product
decisions -- which body, whether it wears a resting plate, how far the pointer has moved it -- and those live in ICoreButtonChrome, proved with no toolkit linked. This file puts ink on the numbers it returns.
- THE PLAIN-GROUND SURFACES -- labels, cards, toolbars, panels, tab bars,
the settings containers -- which ARE described by a stylesheet today, and become an ICoreStyleSpec under W0.3. There are eleven of them and they are one shape: a ground, sometimes a border, sometimes a radius. One function draws all of them, and a theme edit moves every one with
ICoreWinUISurfaceInk#
ICoreWinUIButtonPainter.h:49 · struct · 0 declaration(s)
One surface's resolved ink, after the spec's state rules have been applied.
struct ICoreWinUISurfaceInk {
public:
ICoreRgba background;
ICoreRgba foreground; // the caption ink, for a surface that has one
ICoreRgba border;
double borderWidth = -1.0; // < 0 is unset, per ICoreStyleEdge
double borderRadius = -1.0; // < 0 is unset -- draw square corners
};
};
ICoreWinUIItemDelegateAccess.h#
ICoreEssentials/UI/Backends/WinUI/Widgets/ICoreWinUIItemDelegateAccess.h
ICoreItemDelegate's toolkit half on this backend, as an IMPL-SIDE header.
⚠ This is not part of the public surface and must not be included from one. It exists because TWO files in this zone need the Impl complete: the delegate's own seat defines it, and every item VIEW has to call its three hooks while painting a row. Same precedent and same reasoning as UI/Backends/Qt/Widgets/ICoreStandardItemImpl.h and Values/ICoreWinUIPixmapAccess.h -- the access lives in an impl-side header rather than in the zone's shared native-handle header, because nothing outside these files needs it.
A nested class may be DEFINED outside its enclosing class as long as it was DECLARED inside, which is what
class Impl;in ICoreItemDelegate.h does. Being nested is also what makes this file possible at all: the three hooks are PROTECTED, and a nested class has its enclosing class's access -- so the
ICoreItemDelegateHooks#
ICoreWinUIItemDelegateAccess.h:52 · class · 4 declaration(s)
⚠ WHAT A VIEW ACTUALLY HOLDS, AND WHY IT IS THIS RATHER THAN THE Impl.
class ICoreItemDelegateHooks {
public:
virtual ~ICoreItemDelegateHooks() = default;
// The three hooks, forwarded. Non-const because the hooks are: an override
// is allowed to keep a per-paint cache.
virtual bool paint(ICorePainter& painter, const ICoreItemRenderContext& context) = 0;
virtual ICoreSizeF sizeHint(const ICoreItemRenderContext& context) = 0;
virtual ICoreColor selectedTextColorFor(const ICoreItemRenderContext& context) = 0;
};
};
ICoreItemDelegate#
ICoreWinUIItemDelegateAccess.h:64 · class · bases :Impl : public ICoreItemDelegateHooks · 4 declaration(s)
class ICoreItemDelegate : :Impl : public ICoreItemDelegateHooks {
public:
explicit Impl(ICoreItemDelegate& owner);
bool paint(ICorePainter& painter, const ICoreItemRenderContext& context) override;
ICoreSizeF sizeHint(const ICoreItemRenderContext& context) override;
ICoreColor selectedTextColorFor(const ICoreItemRenderContext& context) override;
ICoreItemDelegate* m_owner;
};
};
ICoreWinUIMenuPainter.h#
ICoreEssentials/UI/Backends/WinUI/Widgets/ICoreWinUIMenuPainter.h
The WinUI backend's menus: the half of W3.3 that draws.
⚠ THE ROW'S OWN HEADLINE IS "ZERO NEW WORK", AND IT IS RIGHT ABOUT THIS FILE AND WRONG ABOUT THE ONE UNDER IT. Windows has no system menu bar, so the in-window painted strip already IS the Windows behaviour and no decision about WHAT a menu looks like is made here or anywhere in this backend. What there was work in is the arithmetic behind it: ICoreMenu.cpp reaches the toolkit in five places that draw nothing, four of which are decisions rather than toolkit jobs. Those became ICoreMenuCore and ICoreMenuChrome. This file is the part the row's headline meant -- ink on numbers somebody else worked out.
It draws NOTHING of its own invention:
ICoreWinUIMenuRowText#
ICoreWinUIMenuPainter.h:60 · struct · 0 declaration(s)
The text of one row, already measured by the caller.
struct ICoreWinUIMenuRowText {
public:
const wchar_t* label = nullptr;
const wchar_t* shortcut = nullptr;
// The tick glyph. Defaulted to the check mark ICoreMenu.cpp draws, which is
// U+2713, so a caller that wants that one need not spell it.
const wchar_t* tick = L"✓";
};
};
ICoreWinUIMenuFonts#
ICoreWinUIMenuPainter.h:72 · struct · 0 declaration(s)
The three fonts a menu draws in, as ICoreWinUIPainter takes them.
struct ICoreWinUIMenuFonts {
public:
const wchar_t* labelFamily = L"Segoe UI";
double labelPixelSize = 13.0;
int labelWeight = 400;
const wchar_t* shortcutFamily = L"Segoe UI";
double shortcutPixelSize = 12.0;
int shortcutWeight = 400;
const wchar_t* captionFamily = L"Segoe UI";
double captionPixelSize = 11.0;
int captionWeight = 600;
};
};
ICoreWinUIObjectWatch.h#
ICoreEssentials/UI/Backends/WinUI/Widgets/ICoreWinUIObjectWatch.h
Declares no class of its own — see the file.
ICoreWinUIPaintedText.h#
ICoreEssentials/UI/Backends/WinUI/Widgets/ICoreWinUIPaintedText.h
The painted multi-line text engine, shared by ICoreTextEdit and ICoreRichTextEdit on this backend.
⚠⚠⚠ WHY THERE IS NO RichEditBox UNDER EITHER OF THEM ANY MORE. Read this before reaching for one again -- the reasons are the control's, not this tree's, and none of them is reachable from a wrapper:
- COLOURED RUNS.
appendColouredChunkis the reason ICoreTextEdit has astreaming API at all -- a log pane whose severities are all one colour is a log pane with no severities. A RichEditBox pushes its
Foregroundinto the rich edit engine underneath it, and its default template re-pushes it out ofTextControlForeground*on every visual state change: normal, pointer-over, focused, disabled. So a run given its own ink through the document was repainted in the theme's ink by nothing
ICoreWinUIPaintedText#
ICoreWinUIPaintedText.h:120 · class · pImpl · nested Line · 77 declaration(s)
⚠⚠ THE ENGINE IS IN ICoreWinUIPaintedText.cpp, AND IT USED TO BE HERE.
class ICoreWinUIPaintedText {
public:
// Which of the field's own chrome the owner wears. The same opt-in both
// wrappers publish, spelled once here.
enum class Chrome { Field, None };
// One line of glyphs on screen. `block` is which paragraph it belongs to and
// `wrapped` says it is a continuation rather than the start of one -- the
// pair a section ruler needs to answer for BLOCKS while measuring LINES.
struct Line {
int start = 0;
int length = 0;
int block = 0;
bool wrapped = false;
};
// ---- the hooks a seat installs ---------------------------------------
//
// ⚠ EVERY ONE IS OPTIONAL AND EVERY ONE IS TESTED BEFORE IT IS CALLED. The
// two seats want different subsets -- ICoreTextEdit has no block-count
// signal and ICoreRichTextEdit has no coloured-chunk API -- and an engine
// that required both to answer everything would be an engine with two
// half-empty implementations hanging off it.
std::function<void()> onContentsChanged;
std::function<void(int)> onBlockCountChanged;
std::function<void()> onCursorMoved;
std::function<void(int)> onVerticalScrollChanged;
// Script editor, S2.3: after an edit, an undo, a redo or a fresh history --
// the seat compares canUndo()/canRedo() with what it last reported.
std::function<void()> onUndoStateChanged;
// The subclass key hook, asked BEFORE the caret sees the key, and its true
// is final. Then the "processed" half, which runs after -- ICoreRichTextEdit
// publishes both and their order is its contract.
std::function<bool(const ICoreKeyEvent&)> onKeyBeforeCaret;
std::function<void(const ICoreKeyEvent&)> onKeyAfterCaret;
std::function<bool(const ICoreWheelEvent&)> onWheelBeforeScroll;
std::function<void(int, int)> onResized;
// Drawn under the text and over it. Both are given the whole element, not
// the viewport: their callers draw rules and gutters in the margin the text
// is inset by.
std::function<void(ICorePainter&, const ICoreRect&)> onPaintUnderlay;
std::function<void(ICorePainter&, const ICoreRect&)> onPaintOverlay;
// The pointer over the text, in the element's coordinates (caretRect()'s),
// for the pointer hooks. onPointerBeforeCaret runs before a press
// moves the caret -- `canTake` false for a right-click, whose answer is
// ignored -- and true takes the press; onContextMenu true keeps the
// engine's own menu shut.
std::function<bool(const ICoreMouseEvent&, bool canTake)> onPointerBeforeCaret;
std::function<void(const ICoreMouseEvent&)> onPointerMoved;
std::function<void()> onPointerLeft;
std::function<bool(const ICoreMouseEvent&)> onContextMenu;
// The face to draw in when no caller has set one. The two seats disagree --
// a code pane defaults to the mono face and a prose field to the control
// face -- so the default is the seat's answer rather than this file's.
std::function<ICoreFont()> themeFont;
// ⚠ THE OWNER IS TAKEN FOR ONE REASON: a context menu has to open at the
// pointer, and "where is the pointer in the WINDOW" is a question only the
// wrapper can be asked (icoreFloatOverWindowOf walks from it). The engine
// never dereferences it for anything else.
ICoreWinUIPaintedText(ICoreWinUIWidgetElement& element, ICoreNativeWidget* owner,
Chrome chrome);
~ICoreWinUIPaintedText();
ICoreWinUIPaintedText(const ICoreWinUIPaintedText&) = delete;
ICoreWinUIPaintedText& operator=(const ICoreWinUIPaintedText&) = delete;
[[nodiscard]] const std::wstring& text() const;
[[nodiscard]] bool isEmpty() const;
[[nodiscard]] int blockCount() const;
void setText(const std::wstring& text);
void insertAtCaret(const std::wstring& raw);
void appendAtEnd(const std::wstring& raw, const ICoreColor* const colour, const bool startsSection);
void setMaximumBlockCount(const int count);
[[nodiscard]] std::vector<double> sectionTops(const double bottomY) const;
[[nodiscard]] Chrome chrome() const;
[[nodiscard]] bool isReadOnly() const;
void setReadOnly(const bool readOnly);
[[nodiscard]] bool isEnabled() const;
void setEnabled(const bool enabled);
void setPlaceholder(const ICoreString& text);
void setFont(const ICoreFont& font);
[[nodiscard]] ICoreFont font() const;
void setWrapped(const bool wrapped);
void setDocumentMargin(const double margin);
void setTabStopDistance(const double pixels);
void setViewportMargins(const int left, const int top, const int right, const int bottom);
void setBorderOpacity(const double opacity);
[[nodiscard]] double borderOpacity() const;
[[nodiscard]] double lineHeight() const;
[[nodiscard]] double frameInset() const;
[[nodiscard]] double textInset() const;
[[nodiscard]] ICoreRect viewport() const;
[[nodiscard]] double textWidthFor(const bool withBar) const;
[[nodiscard]] bool scrollBarNeeded() const;
[[nodiscard]] double contentHeight() const;
[[nodiscard]] double verticalScrollOffset() const;
[[nodiscard]] ICoreRect caretRect() const;
[[nodiscard]] ICoreRect rectAt(int offset) const;
[[nodiscard]] double blockTop(int block) const;
void setVerticalScrollOffset(double offset);
// Blocks laid out as nothing (folding). Block 0 is never hidden.
void setBlocksHidden(int first, int last, bool hidden);
void showAllBlocks();
[[nodiscard]] bool isBlockHidden(int block) const;
[[nodiscard]] bool hasSelectedText() const;
void selectAll();
void copySelection() const;
void cutSelection();
void pasteAtCaret();
void deleteSelection();
[[nodiscard]] int caretPosition() const;
void setCaretPosition(const int position);
// Script editor, S2.2: the selection's other end, the selected text, a
// range setter that leaves the caret at `caret`, and the hit test the
// mouse already uses, in caretRect()'s coordinates.
[[nodiscard]] int anchorPosition() const;
[[nodiscard]] std::wstring selectedText() const;
void setSelectionRange(const int anchor, const int caret);
[[nodiscard]] int positionAt(const double x, const double y) const;
// Script editor, S2.3: this engine's own history (it had none).
void undo();
void redo();
[[nodiscard]] bool canUndo() const;
[[nodiscard]] bool canRedo() const;
void moveCaretToStart();
void ensureCaretVisible();
void scrollToBottom();
void scrollToLeftEdge();
void paint(ICorePainter& p);
private:
class Impl; // the two-line residue; state lives here
std::unique_ptr<Impl> impl;
};
ICoreWinUIPointerBroadcast.h#
ICoreEssentials/UI/Backends/WinUI/Widgets/ICoreWinUIPointerBroadcast.h
The application-wide half of the pointer stream, for the two watches that name no target.
⚠ THESE EXIST BECAUSE THE ROSTERS THEY FEED WERE WRITE-ONLY. ICoreWidget's WinUI seat has always kept
applicationPointerRoster()andoutsidePressRoster(), andwatchApplicationPointerMoves()/watchOutsidePointerPresses()have always added to them -- but nothing ever READ the first, and the second was walked only fromdeliverPressWatches(), which runs when an ICoreWidget is pressed. Most of this tree is not an ICoreWidget: ICoreLabel, ICoreButton, ICoreListBox, ICoreLineEdit and every other seated control derive from ICoreNativeWidget directly. So:
- ICoreMenuBar::applicationPointerMoved -- the override that switches to
the next menu when the pointer crosses another title while one is open --
Declares no class of its own — see the file.
ICoreWinUIRepaintFields.h#
ICoreEssentials/UI/Backends/WinUI/Widgets/ICoreWinUIRepaintFields.h
When a painted widget actually paints, and over what, with no WinRT in it.
⚠ IT IS THE SAME SPLIT AS System/ICoreWinUIDropFields.h AND FOR THE SAME REASON. The element beside it holds the Direct2D calls and the bitmap; every question that could be answered WRONG rather than merely left unwritten is here, in a file a suite can drive on a machine with no Windows App SDK and no event loop.
⚠ NOTHING ABOVE src/ICoreEssentials/UI/Backends/WinUI/ MAY INCLUDE THIS.
⚠⚠ THE THREE MEASUREMENTS THIS FILE EXISTS BECAUSE OF
requestRepaint()HAS 132 CALL SITES IN THIS ZONE AND EXACTLY ONEPASSES A RECTANGLE. The other 131 are the no-argument form, which means
ICoreWinUIRepaintRect#
ICoreWinUIRepaintFields.h:72 · struct · 0 declaration(s)
A rectangle in the element's own logical (DIP) coordinates.
struct ICoreWinUIRepaintRect {
public:
double x = 0.0;
double y = 0.0;
double width = 0.0;
double height = 0.0;
};
};
ICoreWinUIRepaintState#
ICoreWinUIRepaintFields.h:87 · struct · 0 declaration(s)
What one element owes.
struct ICoreWinUIRepaintState {
public:
// A flush is owed and has already been scheduled.
bool pending = false;
// The union is "all of it" -- set by any request that named no rectangle,
// and never cleared by a later narrower one.
bool wholeSurface = false;
// The union, in DIPs, meaningful only when `pending && !wholeSurface`.
double left = 0.0;
double top = 0.0;
double right = 0.0;
double bottom = 0.0;
// ⚠ COUNTERS, AND THEY ARE NOT DECORATION. This row cannot produce a timing
// number -- nothing on this machine links a window -- so "how much work did
// this avoid" has to be answered in requests-per-flush, which means somebody
// has to count both. A suite asserts the ratio; a future harness can print
// it.
std::uint64_t requests = 0;
std::uint64_t flushes = 0;
};
};
ICoreWinUIPixelRect#
ICoreWinUIRepaintFields.h:174 · struct · 0 declaration(s)
A rectangle in whole DEVICE pixels of a surface.
struct ICoreWinUIPixelRect {
public:
std::uint32_t x = 0;
std::uint32_t y = 0;
std::uint32_t width = 0;
std::uint32_t height = 0;
};
};
File-scope declarations#
// What a seat that honours a rectangle answers when its element is about to
// paint. See `ICoreWinUIWidgetElement::setPaintRegionProvider`.
//
// ⚠ `Nothing` IS A REAL ANSWER AND THE CHEAPEST ONE. A canvas is asked to
// repaint after every pointer event whether or not the event changed anything
// (the view cannot know what a hook did), and when the scene's dirty region is
enum class ICoreWinUIPaintRegion {
Nothing,
Rect,
Whole,
};
ICoreWinUIScrollBarPainter.h#
ICoreEssentials/UI/Backends/WinUI/Widgets/ICoreWinUIScrollBarPainter.h
ICoreWinUIScrollBarInk#
ICoreWinUIScrollBarPainter.h:60 · struct · 0 declaration(s)
One bar's ink, after the spec's state rules have been resolved.
struct ICoreWinUIScrollBarInk {
public:
// The groove behind the thumb. ⚠ USUALLY NOT PAINTED AT ALL: this tree's
// bar is an OVERLAY, and its sheet gives the bar `background: transparent`
// and both troughs `background: none`. Both arrive here as "no ink" -- an
// unmentioned colour and an explicitly transparent one are different
// instructions, and this is the one surface where they have the same
// outcome, because a source-over fill at alpha 0 changes no pixel. The
// field keeps them distinguishable even so; the painter treats both as
// nothing to draw and says so where it does it.
ICoreRgba track;
ICoreRgba thumb;
ICoreRgba thumbBorder;
double thumbBorderWidth = -1.0; // < 0 is unset, per ICoreStyleEdge
// Corner radius, from the rule's borderRadius. < 0 is unset and draws
// square corners.
//
// ⚠ AT A RADIUS OF HALF THE THICKNESS OR MORE THE THUMB IS A CAPSULE, not
// a rounded rectangle -- the tree's bar is 6px wide with a radius of 3, so
// its ends are semicircles and its corner pixels carry NO ink. That is not
// a rounding artefact to tolerate; it is the shape, and the suite pins the
// empty corners so that a backend which silently squared them off would be
// caught.
double radius = -1.0;
};
};
File-scope declarations#
// The WinUI backend's scroll bar: the half of W2.5 that draws.
//
// It draws NOTHING of its own invention. Where the thumb goes comes from
// ICoreScrollCore, and what it looks like comes from an ICoreStyleSpec -- the
// same split ICoreWinUISplitterPainter made, for the same reason: a theme edit
// has to move this surface with no edit here, and the two backends have to read
enum class ICoreWinUIScrollThumbState { Idle, Hover, Pressed };
ICoreWinUISpinBoxPainter.h#
ICoreEssentials/UI/Backends/WinUI/Widgets/ICoreWinUISpinBoxPainter.h
The WinUI backend's spin-box chevrons: the second control W2.2 paints.
The field underneath is NOT this file's -- on WinUI it will be a
TextBox(W2.3), restyled from anICoreStyleSpec, and these two chevrons are painted over it. That is the same arrangement Qt has, where the field is the text-entry base andpaintContentdraws the chevrons on top of everything.It decides nothing: geometry from
ICoreSpinBoxChrome, ink from the theme tokens, pixels fromICoreWinUIPainter. The suite beside it draws into a WIC bitmap and reads the bytes back -- no window, no XAML, no Windows App SDK.⚠ THE CHEVRON IS A STROKED POLYLINE, NOT A FILLED TRIANGLE, and the difference is visible at this size: three points closed and filled give a
Declares no class of its own — see the file.
ICoreWinUISplitterPainter.h#
ICoreEssentials/UI/Backends/WinUI/Widgets/ICoreWinUISplitterPainter.h
ICoreWinUISplitterHandleInk#
ICoreWinUISplitterPainter.h:42 · struct · 0 declaration(s)
The colours one bar is drawn with, after the spec's state rules have been resolved.
struct ICoreWinUISplitterHandleInk {
public:
ICoreRgba background;
ICoreRgba border;
double borderWidth = -1.0; // < 0 is unset, per ICoreStyleEdge
};
};
File-scope declarations#
// The WinUI backend's splitter divider: the half of W2.6 that draws.
//
// WinUI has no splitter control, and W0.3's XAML interpreter says so in code
// rather than in prose -- icoreWinUIPropertiesFor(SplitterHandle) returns an
// EMPTY property list, whose documented meaning is "this backend answers by
// painting, not by styling". This is the painting.
enum class ICoreWinUISplitterHandleState { Idle, Hover, Pressed };
ICoreWinUIStandardItemAccess.h#
ICoreEssentials/UI/Backends/WinUI/Widgets/ICoreWinUIStandardItemAccess.h
ICoreStandardItem's toolkit half on this backend, as an IMPL-SIDE header.
⚠ This is not part of the public surface and must not be included from one. It exists for the same reason UI/Backends/Qt/Widgets/ICoreStandardItemImpl.h exists on the other backend: TWO files need the Impl complete -- the item's own seat defines it, and the model has to adopt a released one when appendRootRow() takes ownership. A private nested class defined inside a single .cpp cannot be named from another translation unit, and that shows up as "no matching member function", which reads like a signature problem rather than a visibility one.
⚠ THE Qt Impl IS A
QStandardItem; THIS ONE IS A NODE. There the model is a real QStandardItemModel and the item IS a toolkit object, so display text, icon, editability and every caller role live in the toolkit's own role store.
ICoreStandardItem#
ICoreWinUIStandardItemAccess.h:58 · class · bases :Impl · 10 declaration(s)
class ICoreStandardItem : :Impl {
public:
explicit Impl(ICoreStandardItem& owner);
Impl(ICoreStandardItem& owner, const ICoreString& text);
~Impl();
Impl(const Impl&) = delete;
Impl& operator=(const Impl&) = delete;
// Called from ~ICoreStandardItem so this destructor does not delete a
// wrapper that is already destroying itself.
void detachOwner();
ICoreStandardItem* owner() const;
// ⚠ setData REPLACES a role whatever type it held before, which is what a
// single variant-valued role store does on the other backend. Two maps
// would quietly keep both and answer with whichever was consulted first,
// so each setter erases from the other -- see the .cpp.
void setRoleText(int role, const ICoreString& value);
void setRoleInt(int role, int value);
// The role as TEXT, which is what ICoreTreeView::rowData publishes. An int
// role reads back as its decimal spelling: the other backend gets that
// from QVariant::toString(), and the header's own note ("Numbers written
// by setData(int, role) read back as their decimal spelling") is the
// contract this has to meet rather than a description of Qt.
ICoreString roleAsText(int role) const;
// The row this item heads, one column per cell. cells is columns 1..n --
// column 0 is this record itself, which is what makes a row handle and a
// column-0 item the same thing.
ICoreString label;
ICoreIcon icon;
bool hasIcon = false;
bool editable = true;
std::map<int, ICoreString> textRoles;
std::map<int, int> intRoles;
std::vector<std::shared_ptr<Impl>> children;
std::vector<std::shared_ptr<Impl>> cells;
Impl* parentNode = nullptr;
// ⚠ A NODE'S WEAK REFERENCE TO ITSELF, SET AT INSERTION, AND IT EXISTS FOR
// ONE CALLER: ICoreTreeRow::parent(). A row handle holds a weak_ptr (it
// must not keep its row alive -- a QModelIndex does not), so building a
// handle for the PARENT needs the parent's shared_ptr. Reaching it any
// other way means searching the grandparent's children, and for a
// top-level row it means reaching the model, which a bare handle cannot
// do. enable_shared_from_this is the shorter route and is unavailable:
// these records are ALSO owned by unique_ptr before insertion, so they are
// not always in a shared_ptr at all. Empty until the record is inserted,
// which is exactly when a row handle can first name it.
std::weak_ptr<Impl> selfWeak;
ICoreStandardItem* m_owner = nullptr;
};
};
ICoreWinUIStyledWidget.h#
ICoreEssentials/UI/Backends/WinUI/Widgets/ICoreWinUIStyledWidget.h
Paint one plain-ground surface into a widget's element, from the spec that describes it.
⚠ THE ELEVEN SURFACES ARE ONE FUNCTION, NOT ELEVEN CLASSES, and that is a measurement rather than a preference.
ICoreThemeStyleSpecspublishes eleven specs -- themePrimaryColor, themeSecondaryColor, windowRootWidget, widget, titleBar, leftSideToolBar, contextMenuBar, scrollPane, runBackground, studioSurface, tabBar -- and every one of them is a singleSelfrule whose only instruction is a ground, in three cases with a border or a radius. A backend that can draw one can draw all eleven, which is what makes them the cheap half of the styled surfaces; giving each its own seat would be eleven copies of four lines, and eleven chances to fix a bug ten times.
Declares no class of its own — see the file.
ICoreWinUITreeModelAccess.h#
ICoreEssentials/UI/Backends/WinUI/Widgets/ICoreWinUITreeModelAccess.h
ICoreTreeViewModel's half on this backend, as an IMPL-SIDE header.
⚠ This is not part of the public surface and must not be included from one. It exists because TWO files need it complete: the model's own seat defines it, and ICoreTreeView has to READ the rows out of it -- on this backend the model is not a toolkit object the view can be handed, it is a plain record the view has to walk. Same precedent as ICoreWinUIStandardItemAccess.h and UI/Backends/Qt/Widgets/ICoreStandardItemImpl.h.
⚠ NAMING IT IS LEGAL ONLY INSIDE ICoreTreeView'S OWN NESTED CLASS, and that is not a detail to discover later:
Implis private, and the public header grantsfriend class ICoreTreeView. A nested class has its enclosing class's access, so ICoreTreeView::Impl may spell these types; a namespace-scope helper or a free function may not. ICoreTree.cpp in this zone
ICoreTreeViewModel#
ICoreWinUITreeModelAccess.h:54 · class · bases :State · 0 declaration(s)
The wrapper's own state -- see the header.
class ICoreTreeViewModel : :State {
public:
bool alive = true;
};
};
ICoreTreeViewModel#
ICoreWinUITreeModelAccess.h:59 · class · bases :Impl · 4 declaration(s)
class ICoreTreeViewModel : :Impl {
public:
explicit Impl(ICoreTreeViewModel& owner);
Impl(const Impl&) = delete;
Impl& operator=(const Impl&) = delete;
// ⚠ NO R3 GUARD AND NO setSelfDeleting(), AND THAT DIVERGES FROM THE Qt
// SEAT ON PURPOSE -- the same divergence every seat in this zone records.
// The Qt Impl self-deletes because a QObject parent is a SECOND owner that
// reaps the model; this record has exactly one owner, the unique_ptr on
// the wrapper, so `delete m_owner` here would delete a wrapper nobody had
// transferred. The death path is one-directional.
void detachOwner();
std::vector<std::shared_ptr<ICoreStandardItem::Impl>> rows;
std::vector<ICoreString> headers;
ICoreNativeWidget* parent = nullptr;
ICoreTreeViewModel* m_owner = nullptr;
};
};
ICoreWinUIWidgetElement.h#
ICoreEssentials/UI/Backends/WinUI/Widgets/ICoreWinUIWidgetElement.h
ICoreWinUISizeLimits#
ICoreWinUIWidgetElement.h:75 · struct · 0 declaration(s)
The XAML side of the widget base: a hostable element that turns XAML pointer, key and focus events into this tree's hooks, and gives a painted widget a surface to draw on.
struct ICoreWinUISizeLimits {
public:
int minimumWidth = 0;
int minimumHeight = 0;
int maximumWidth = 0;
int maximumHeight = 0;
int horizontalStretch = 0;
int verticalStretch = 0;
// ⚠ W10.55: WHAT A WRAPPER THAT IS NOT AN ICoreWidget WOULD LIKE, 0 = no
// opinion. An ICoreWidget states its preferred size through sizeHint(), a
// virtual the layout reads off the wrapper; ICoreButton is not one, so until
// these existed it could publish only caps -- and a button sized by width
// alone, or not at all, laid out at a height or width of 0.
int preferredWidth = 0;
int preferredHeight = 0;
};
};
ICoreWinUIWidgetElement#
ICoreWinUIWidgetElement.h:91 · class · pImpl · 71 declaration(s)
class ICoreWinUIWidgetElement {
public:
ICoreWinUIWidgetElement();
~ICoreWinUIWidgetElement();
ICoreWinUIWidgetElement(const ICoreWinUIWidgetElement&) = delete;
ICoreWinUIWidgetElement& operator=(const ICoreWinUIWidgetElement&) = delete;
// The element to put in a XAML tree. A Canvas, so children are positioned
// by frame and the layout engine's output applies directly.
[[nodiscard]] winrt::Microsoft::UI::Xaml::Controls::Canvas element() const;
// The hooks. Each is the wrapper's corresponding protected virtual, and the
// bool-returning ones answer "handled?" exactly as the wrapper's do: true
// marks the routed event Handled, false lets it bubble.
std::function<bool(const ICoreMouseEvent&)> onPointerPressed;
std::function<bool(const ICoreMouseEvent&)> onPointerReleased;
std::function<bool(const ICoreMouseEvent&)> onPointerMoved;
std::function<bool(const ICoreMouseEvent&)> onPointerDoubleClicked;
std::function<void()> onPointerEntered;
std::function<void()> onPointerLeft;
// The gesture ended with no release coming -- W10.69. Raised wherever this
// seat calls icoreWidgetForgetPointer() with GestureCancelled and the router
// answers deliverGestureCancelled: capture lost to another element, the
// pointer abandoned by the input stack, the widget hidden or disabled while a
// drag was live.
//
// ⚠ THIS SEAT IS THE ONLY ONE THAT RAISES IT, AND THAT IS NOT AN OVERSIGHT.
// AppKit's implicit per-window tracking delivers mouseUp to the view that
// saw mouseDown for the whole drag, so there is no cancel to report -- which
// is exactly why the defect W10.69 fixed was Windows-only. The HOOK is
// shared (ICoreWidget::mouseGestureCancelled, defaulted on all three seats)
// so a host clears its drag state in one place whatever it is running on.
std::function<void()> onGestureCancelled;
// Offer this element a wheel notch that arrived somewhere else, and answer
// whether it took it. Built from `args` in THIS element's own coordinates.
//
// ⚠⚠ THIS EXISTS BECAUSE A NOTCH IS NOT RELIABLY DELIVERED BY POSITION ON
// THIS BACKEND -- W10.61 / W10.64. The element's own PointerWheelChanged is
// the ordinary path and is untouched; this is the window tier's way of
// handing on a notch that never reached the surface under the pointer, which
// is what AppKit gets free from the responder chain.
[[nodiscard]] bool offerWheel(
const winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs& args);
std::function<bool(const ICoreKeyEvent&)> onKeyPressed;
std::function<bool(const ICoreKeyEvent&)> onKeyReleased;
// A character the user actually produced, after the keyboard layout, the
// dead keys and the IME have all had their say.
//
// ⚠⚠ A KEY IS NOT A CHARACTER, AND A SEAT THAT TYPES TEXT NEEDS BOTH. A
// key-down carries a VIRTUAL KEY -- the physical key, before the layout --
// so deriving "what should be inserted" from it means reimplementing the
// user's keyboard: Shift+2 is @ on one layout and " on another, AltGr
// produces a third set, and a dead key produces nothing at all until the
// next keystroke resolves it. XAML raises `CharacterReceived` after
// KeyDown with the answer already computed, and it is the only place that
// answer exists.
//
// ⚠ THE STRING RATHER THAN A CODE POINT, because a surrogate pair is one
// character and two UTF-16 units, and every consumer here works in
// ICoreString.
std::function<bool(const ICoreString&)> onCharacterTyped;
std::function<bool(const ICoreWheelEvent&)> onWheelScrolled;
std::function<void()> onFocusGained;
std::function<void()> onFocusLost;
// Drag and drop ONTO this element. Each answers "accepted?", which is what
// the wrapper's own hooks answer and what decides the cursor.
//
// ⚠⚠ THE SEQUENCING RULE IS OBEYED HERE AND DECIDED IN
// System/ICoreWinUIDropFields.h, exactly as pointer routing is decided in
// ICoreWidgetCore and obeyed here. A drag NOT accepted on enter must never
// produce the later two -- the wrapper header publishes that as Qt's
// contract -- and XAML does not enforce it: DragOver and Drop fire on any
// element whose AllowDrop is true whatever DragEnter answered. A seat that
// forwarded all three would deliver a drop to a target that refused the
// drag.
//
// ⚠ SETTING ANY OF THE THREE TURNS `AllowDrop` ON, because an element that
// never sets it receives none of these events at all -- and a hook that is
// installed and silent is the defect this whole row exists to retire, one
// tier down. The wrapper's setAcceptDrops() is what a caller uses to say
// so; this is what makes the saying take effect.
std::function<bool(const ICoreDragEvent&)> onDragEntered;
std::function<bool(const ICoreDragEvent&)> onDragMovedOver;
std::function<bool(const ICoreDragEvent&)> onPayloadDropped;
// The drag left without dropping. No event and no bool: the platform's
// leave carries neither a position nor a package, and there is nothing to
// accept once the drag is gone.
//
// ⚠ ONLY THE WIDGET TIER HAS SOMEWHERE TO SEND THIS. `ICoreWidget::dragLeft`
// exists; `ICoreGraphicsView` publishes no counterpart on any backend. The
// hook is here because the element genuinely receives the event and a seat
// that can use it should not have to re-derive it.
std::function<void()> onDragLeft;
// Whether this element accepts drops at all. Off by default, as
// QWidget::setAcceptDrops is.
void setAcceptsDrops(bool accepts);
[[nodiscard]] bool acceptsDrops() const;
// Called when the element needs its pixels, with a painter aimed at this
// element's own surface and the area that needs redrawing, in the
// element's own coordinates.
//
// ⚠ THE PAINTER IS VALID ONLY FOR THE CALL. It is a stack object over a
// render target that is between BeginDraw and EndDraw; keeping it, or the
// reference, past the return is a use-after-free with no diagnostic.
//
// ⚠ AND THE COORDINATES ARE DIPs, NOT PIXELS. The surface underneath is
// sized in physical pixels and its render target carries the matching DPI,
// so a hook draws in the same units it lays out in and the scaling happens
// once, in one place. A hook that multiplied by the rasterization scale
// itself would be right at 100% and double-scaled everywhere else.
std::function<void(ICorePainter&, double x, double y, double w, double h)> onPaint;
// The pixels of the last completed paint: BGRA premultiplied, top-down,
// `surfaceWidthPixels()` x `surfaceHeightPixels()`. Empty before the first
// one. This is what a WriteableBitmap is fed from, and what a headless
// caller reads to see what a widget drew.
[[nodiscard]] const std::vector<unsigned char>& surfacePixels() const;
[[nodiscard]] unsigned int surfaceWidthPixels() const;
[[nodiscard]] unsigned int surfaceHeightPixels() const;
// The `Image` this element appends to its own Canvas to show `surfacePixels`
// on screen -- IDENTITY, so a walk over Children() can tell this element's
// own surface from XAML a seat added.
//
// ⚠⚠ ASK THIS RATHER THAN TESTING THE TYPE, AND W8.11 IS WHY THE
// DISTINCTION IS WORTH A MEMBER. A walk that skipped every `Image` child
// would be matching a SPELLING where the question is a ROLE -- the same
// mistake `link_flags.py` made three times over `.o` vs `.obj` -- and it
// would silently skip a seat that legitimately hosts an image of its own
// the day one exists. The surface is not "an Image", it is "THIS element's
// surface", and only the element can answer that.
[[nodiscard]] winrt::Microsoft::UI::Xaml::Controls::Image surfaceImage() const;
// The density the surface is built for. 1.0 until the element is in a
// XamlRoot that says otherwise -- which is what lets a headless caller
// drive the paint path at a chosen scale.
void setRasterizationScale(double scale);
// The density this element would paint at right now, by the SAME rule the
// paint path uses: the XamlRoot's when there is one, the stored value when
// there is not.
//
// ⚠ A METHOD, NOT A MEMBER -- the rule this file states four lines above
// setScreenOrigin's getter, and for the same reason: the double is already
// on the Impl and this only publishes it.
//
// ⚠⚠ IT EXISTS SO THAT icoreDevicePixelRatio() CANNOT DISAGREE WITH THE
// SURFACE (W1.11). That free function had no seat on this backend at all,
// and the obvious body -- read XamlRoot().RasterizationScale() -- is wrong
// twice: asking an element that is in no tree THROWS, and answering a flat
// 1.0 instead would tell a glyph cache to rasterize at 1.0 for an element
// this class is painting at the scale a headless caller set. Publishing the
// rule is what keeps one answer in one place.
[[nodiscard]] double rasterizationScale() const;
// QWidget::setMouseTracking(). Off by default, as it is on QWidget.
void setMouseTracking(bool tracking);
// The two system numbers the double-click rule needs, read from
// GetDoubleClickTime() and GetSystemMetrics(). Called once at construction;
// exposed so a suite can drive the rule at a chosen value.
void setDoubleClickSpec(int timeMs, double slopX, double slopY);
// The window's top-left on the SCREEN, for the global position a mouse
// event carries. A XAML pointer event reaches no deeper than the root
// visual, so this has to be told rather than asked -- see the event
// extraction unit, which takes it for the same reason.
void setScreenOrigin(double x, double y);
// What setScreenOrigin was last told, so a seat can turn a SCREEN point
// back into one of its own -- ICoreTreeView::rowAtGlobal is the caller.
//
// ⚠ A METHOD, NOT A MEMBER: the pair of doubles has already been on the
// Impl, and this only publishes it. Adding a DATA member here would change
// this element's object layout and every widget's that embeds it, so a
// build straddling the change links and then misbehaves at run time;
// adding a method cannot. The distinction is worth keeping visible in
// this file, which many widgets embed.
//
// ⚠ AND IT IS (0, 0) TREE-WIDE TODAY, because NOTHING in a winui configure
// calls setScreenOrigin -- measured, not assumed.
//
// ⚠⚠ WHAT THAT MAKES `globalPos` IS **ROOT-LOCAL**, NOT WIDGET-LOCAL, AND
// THE SENTENCE THAT STOOD HERE SAID WIDGET-LOCAL (W10.18). It read: *"So
// every `globalPos` this backend reports is really widget-local, and a
// caller mapping through this is exactly as right as the events are."* The
// first half is wrong -- `ICoreWinUIPointerFields::globalPos` is
// `rootX + screenOriginX`, and `rootX` is `GetCurrentPoint(nullptr)`, the
// XAML ROOT -- and the second half is only true for a widget sitting at the
// root's origin.
//
// It was quoted into three files and cost one of them a whole feature:
// `ICoreTreeView::rowAtGlobal` subtracted this number alone and handed
// `rowAt()` a root coordinate, so a right-click 28 points into a tree in the
// left rail was looked up 314 points down it. Measured on the product:
// click at window (430, 346), `globalPos = (422, 314)`, row = invalid, and
// the Subsystem Navigator's context menu was never built.
//
// ⚠ WHAT IS STILL TRUE, and it is the half worth keeping: a popup on this
// backend is placed in the window's overlay canvas, so `ICoreMenu::popupAt`
// taking `globalPos` straight from an event lands where the pointer is. Root
// space is the RIGHT space for a placement and the WRONG one for a hit test.
// A caller that needs widget-local subtracts `originInWindow()` as well --
// see the note on it below.
//
// Plumbing the origin is still a ONE-place fix: every caller that subtracts
// both terms stays correct by construction.
[[nodiscard]] double screenOriginX() const;
[[nodiscard]] double screenOriginY() const;
// ⚠⚠ THIS ELEMENT'S ORIGIN IN THE WINDOW, SUMMED OFF THE VISUAL TREE, AND
// IT EXISTS FOR THE WRAPPERS THAT ARE **NOT** ICoreWidgets. ICoreWidget
// answers mapToScreen() by walking its own parent chain of Impls and adding
// each one's cached x/y -- correct, and available only to a wrapper family
// that HAS such a chain. ICoreButton, ICoreLabel and ICoreLineEdit have no
// parent pointer at all: they are ICoreNativeWidgets, their frames are
// written straight onto the element by the layout, and each one knows only
// where it sits inside its immediate parent.
//
// ⚠ WHAT THAT COST, MEASURED: ICoreButton::mapToScreen returned its own
// cached offset and nothing else, so a button nested five containers deep
// reported the position it has inside the innermost one. A menu popped
// under it -- ICoreMenu::popupUnder is the caller -- opened in the corner of
// the window instead of under the button, which reads as a menu that cannot
// position itself and is really a button that cannot say where it is.
//
// ⚠ THE XAML TREE RATHER THAN A WRAPPER CHAIN, because it is the one chain
// every element has whatever wrapper owns it -- and it is the SAME sum: the
// widget seat's cached x/y is exactly what applyFrame writes into
// Canvas.Left/Top, so the two routes agree pixel for pixel where both
// exist. The walk ends where the Canvases end, which is the window's root
// panel, so the answer is in the window's client space -- the space this
// backend calls global (see screenOriginX above).
[[nodiscard]] ICorePoint originInWindow() const;
// The same origin, asked of XAML instead of summed -- W10.70.
//
// ⚠⚠ TWO FUNCTIONS ON PURPOSE, AND THE REASON IS COST. The sum above is exact
// only while every ancestor positions its children with Canvas::SetLeft/SetTop,
// and it is silently short when one offsets its content some other way (a
// Border's padding, a Grid's row, a ScrollViewer's scroll). That gap does not
// matter to most callers -- it is the same number that PUT the element there --
// but it is fatal to a HIT TEST, which must answer in the space the pointer
// arrives in: `args.GetCurrentPoint(root)` is XAML's own hit-testing space, and
// TransformToVisual is what XAML computes it with.
//
// ⚠ SO USE THIS ONE FOR A DECISION ABOUT A POINT, AND originInWindow() FOR
// EVERYTHING ELSE. The sum feeds mapToScreen/mapFromScreen and therefore runs
// on layout paths and per item; making it ask XAML measurably slowed the
// application down (an owner report of a subsystem tab taking a long time to
// open). Do not "unify" these two without measuring that.
[[nodiscard]] ICorePoint originInWindowByTransform() const;
// The size limits, for a layout in another translation unit. See the
// struct above for why they are published here.
void setSizeLimits(const ICoreWinUISizeLimits& limits);
[[nodiscard]] const ICoreWinUISizeLimits& sizeLimits() const;
// The element was given a new frame.
//
// ⚠ THIS IS A LAYOUT'S RE-RUN, and it is the difference between a layout
// that is correct once and a layout that is correct. `ICoreWidget::resized`
// is a protected virtual, so a layout living in another translation unit
// cannot hang off it; this is the seam that lets it.
std::function<void(int width, int height)> onResized;
// ⚠ "MY SIZE HINT CHANGED", WHICH IS NOT "MY SIZE CHANGED" AND IS NOT
// ANSWERED BY ANY LAYOUT. onResized fires when something has already
// decided this element's frame; this fires when the element decides it
// wants a DIFFERENT one -- ICoreWidget::updateGeometry() is what raises it
// -- and it exists for the owner that is not a layout and therefore hears
// nothing otherwise.
//
// ⚠ ICoreScrollPane IS THAT OWNER, and its absence was a visible defect:
// a pane sizes its content from the content's preferred size, exactly once,
// when the content is set. A content widget that later grew -- the layouts
// page re-flows a grid from four columns to two and the host gets taller --
// was laid out at its new height inside a pane still scrolling the old one,
// so the section below it was drawn over.
std::function<void()> onGeometryInvalidated;
// Take the keyboard focus, and give it up again. Both answer whether the
// toolkit did what was asked.
//
// ⚠⚠ METHODS, NOT DATA. Adding either as a member would change this class's
// object layout AND the layout of every widget that embeds one, while
// changing no mangled name -- so a build straddling it links perfectly and
// is wrong at run time. The same rule the screenOrigin pair above is
// written under, restated because this file is embedded by every seat.
//
// ⚠ A Canvas IS NOT FOCUSABLE UNTIL IT IS TOLD TO BE. `IsTabStop` is on
// UIElement in WinUI 3 (it is a Control property on the older XAML), and a
// plain Canvas defaults to false -- so `Focus()` alone answers false and
// nothing moves, which is a silent no-op rather than an error. requestFocus
// sets it first.
//
// ⚠⚠ AND XAML HAS NO "UNFOCUS" PRIMITIVE, WHICH IS WHY releaseFocus TAKES
// THE SHAPE IT DOES. There is no call that means "focus nothing": focus
// moves, it is not cleared. So this hands it to the nearest ancestor that
// will take it, and answers false when there is none -- which is the honest
// answer for a detached element and is not the same as "the focus was
// cleared". A caller that needs the focus somewhere specific should focus
// that thing instead of relying on this.
[[nodiscard]] bool requestFocus();
[[nodiscard]] bool releaseFocus();
// Forget any gesture in progress: hidden, disabled or reparented mid-drag.
// Gives back the capture and puts out the hover, so a painted control does
// not stay lit.
void forgetPointer();
// ⚠⚠ THE POINTER SHAPE OVER THIS ELEMENT, WHICH THIS BACKEND RECORDED AND
// DID NOT APPLY FOR THE WHOLE OF ITS LIFE. Three seats say so in the same
// words -- ICoreWidget::setCursorShape and ICoreButton::setCursorShape are
// empty bodies, and ICoreSplitter's banner names the resulting gap as "no
// CURSOR over the bars". The stated reason is that a XAML element's cursor
// is `UIElement.ProtectedCursor`, a PROTECTED member reachable only from a
// subclass, and a custom XAML control is a runtime class, an IDL file and a
// midl step that W1.3 declined for the host element.
//
// ⚠ THE REASON IS TRUE OF THE LANGUAGE AND FALSE OF THE ABI. "Protected" in
// WinRT is a rule about which interface a projection surfaces on a class,
// not about which interfaces the object implements: `IUIElementProtected`
// is a real interface with a real IID, it is in the generated projection
// (Microsoft.UI.Xaml.0.h declares get_/put_ProtectedCursor on it), and
// QueryInterface on any UIElement answers it. `try_as` is that
// QueryInterface. No subclass, no IDL, no midl step -- and the try_as means
// an App SDK that ever stopped answering degrades to the old empty body
// rather than throwing.
//
// Blank has no counterpart: InputSystemCursorShape cannot express a hidden
// pointer, and a null ProtectedCursor means "inherit", not "none". It falls
// back to the arrow, which is the same substitution the five drag shapes
// already take on this platform.
void setCursorShape(ICoreCursorShape shape);
// Back to inheriting whatever the parent chain asks for, which for every
// element in this tree is the arrow. Null IS the inherit value, so this is
// not the same as setting Arrow -- a child that clears inside a parent that
// set SizeWestEast gets the parent's shape, as it should.
void clearCursorShape();
// The PointerPoint of the pointer event CURRENTLY being delivered, or a
// null PointerPoint outside one. This is what UIElement::StartDragAsync
// takes, and this is the only way to obtain one.
//
// ⚠⚠ IT IS A METHOD AND THE STORAGE IS ON Impl -- the rule this header
// states three times already (screenOrigin, requestFocus/releaseFocus,
// setAppliedGround). A data member here would change this element's object
// layout and every widget's that embeds it while changing no mangled name,
// so a build straddling it links perfectly and misbehaves at run time.
// W1.10 paid for a member deliberately, because a hook slot has to be
// assignable from outside; this needs nothing of the kind.
//
// ⚠⚠ WHY IT EXISTS AT ALL, because the alternative looks obviously better
// and cannot be written. `Microsoft.UI.Input`'s PointerPoint has NO static
// GetCurrentPoint(pointerId) -- `Windows.UI.Input`'s does, and the WinUI 3
// projection this tree generates carries only the former. Checked, not
// remembered: PointerRoutedEventArgs::GetCurrentPoint(UIElement) is the one
// producer of a PointerPoint anywhere in the generated headers. So a drag
// that starts outside a pointer handler cannot be started at all, and the
// element is the only object in a position to hold the args open.
//
// ⚠ VALID ONLY FOR THE DURATION OF A HOOK, and deliberately null the rest
// of the time rather than stale. A retained PointerPoint from an earlier
// gesture would make StartDragAsync fail in a way that reads like "the user
// did not drag" -- see ICoreDrag::exec, which refuses on the null and says
// which of the two happened.
[[nodiscard]] winrt::Microsoft::UI::Input::PointerPoint livePointerPoint() const;
// Whether the router currently believes the pointer is over this element.
// This is QWidget::underMouse(), and it is the ROUTER's answer rather than
// a fresh hit test: during a grab the two disagree on purpose, and the
// router's is the one a painted control is drawn from.
[[nodiscard]] bool isPointerInside() const;
// The wrapper that owns this element, as an opaque pointer.
//
// ⚠ IT EXISTS BECAUSE ICoreNativeWidget IS A ONE-MEMBER HANDLE. A widget
// being given a parent has an `ICoreNativeWidget*`, and adopting a child
// needs the PARENT's own bookkeeping -- its child list and its element's
// Children() collection -- which that interface cannot reach. Storing the
// seat here is what lets the handle be turned back into it. Never
// dereferenced by this class; it is carried, not used.
void setSeat(void* seat);
[[nodiscard]] void* seat() const;
// Keep `object` alive for exactly as long as this element.
//
// ⚠ IT EXISTS BECAUSE A LAYOUT HAS NO OWNER OTHERWISE. The 211 call sites
// in this tree that `new` a layout and hand it to a widget never delete it;
// on the other toolkit the widget's own object tree does. This element has
// no such tree, so without a slot to park it in, every installed layout
// leaks one small object. A shared_ptr<void> because the element must not
// know what it is holding -- it destroys it and nothing else.
//
// The AppKit view seat carries the same slot for the same reason.
void adoptOwnedObject(std::shared_ptr<void> object);
// Put `child` into this element's visual tree, or take it out again.
// Ownership of the WRAPPER is the seat's business; these two move only the
// XAML element.
void appendChild(ICoreWinUIWidgetElement& child);
// Draw this element's own pixels OVER its children instead of under them.
//
// ⚠⚠ THE DEFAULT IS UNDER, AND IT IS THE RIGHT DEFAULT FOR ALMOST
// EVERYTHING. A painted widget's surface is its GROUND -- the plate under a
// button's caption, the ink of a label -- and a container's children have to
// sit on top of it. The exception is a widget that paints CHROME which
// belongs in front of its own content, and this tree has exactly one family
// of those: a scroll pane, whose bars are an overlay by design (a 6px
// capsule over the content, no groove) and which was drawing them into the
// one place they could not be seen -- underneath the very children they are
// there to scroll. Reported as "scrollbars sometimes appear behind the
// scrollpane children", and "sometimes" is exactly right: a bar over the
// pane's own padding was visible and a bar over a child was not.
//
// ⚠ IT IS Canvas.ZIndex AND NOT A RE-APPEND, deliberately. Moving the Image
// to the end of `Children()` would have to be redone by every appendChild
// after it -- i.e. by a rule this class would have to remember to obey in
// two places -- while the attached property is read by the compositor on
// every frame and cannot fall out of step with the child list.
//
// ⚠ AND THE SURFACE STOPS ANSWERING HIT TESTS WHEN IT GOES ON TOP, which is
// not a nicety: an `Image` is hit-testable, so a full-bleed one in front of
// the children would swallow every press meant for them. The Canvas keeps
// its own transparent ground, so the element itself is still hit-testable
// and the seat's own bar routing is unaffected -- what changes is only that
// the pixels no longer stand between a pointer and a child.
void setSurfaceAboveChildren(bool above);
// Record a parent that is NOT an element -- a window's root panel, or a seat
// that adopts this canvas directly. XAML refuses a second parent and will
// not name the first (measured: our back-pointer, FrameworkElement::Parent()
// and VisualTreeHelper::GetParent() all answer null on an element it has
// just refused), so an unrecorded parent is an element that can never be
// reparented again (W8.1).
void noteAdoptedByPanel(const winrt::Microsoft::UI::Xaml::Controls::Panel& panel);
void removeChild(ICoreWinUIWidgetElement& child);
// The element this one was appended to, or null for one that is unparented
// or whose parent is a bare panel (a window's root Grid -- see
// noteAdoptedByPanel).
//
// ⚠⚠ ASK THIS RATHER THAN `Canvas().Parent()`. The two do not agree, and
// the disagreement is measured and recorded on Impl::parentElement:
// `FrameworkElement::Parent()` reads NULL for an element that is in a
// Canvas's Children but whose tree has never been loaded -- which is every
// element in a window that has not been shown yet. Parenthood on this
// backend is remembered, not asked for.
//
// ⚠ A METHOD, NOT A MEMBER: the pointer is already on the Impl and this
// only publishes it. The rule this file states three times over -- adding a
// DATA member here changes the object layout of every seat that embeds one
// while changing no mangled name, so a build straddling the change links
// and misbehaves at run time.
[[nodiscard]] ICoreWinUIWidgetElement* parentElement() const;
// Removes this element's Canvas from whatever XAML panel holds it and
// orphans its children's back-pointers. Called by the destructor; public
// so a seat that must leave the tree BEFORE its wrapper dies can say so.
// W8.10 (Windows backend): before this existed a deleted widget's
// subtree stayed in the live tree for the life of its parent.
void leaveXamlTree();
// Put this element at `x, y` and size it `w` x `h`, in the parent's
// coordinates, and re-run whatever is hanging off onResized.
//
// ⚠ A Canvas POSITIONS ITS CHILDREN BY ATTACHED PROPERTY, not by the
// child's own Left/Top, so the frame is applied in two halves and neither
// is optional. The wrapper seat has always done this inline; it is a
// method here because a native widget that is NOT an ICoreWidget -- an
// ICoreSplitter, an ICoreScrollPane -- has no wrapper geometry cache to go
// through and would otherwise have nowhere to be placed from.
void setFrame(double x, double y, double w, double h);
// ⚠⚠ SCALE THE COMPOSED VISUAL, FOR A SCENE OVERLAY UNDER A ZOOMED VIEW
// (`W10.106`, 2026-09-21). `setFrame` above moves and RESIZES an overlay
// exactly where its item goes, so the box tracked the canvas zoom while the
// text, icon and buttons inside it stayed at their original size -- the
// owner's *"[the search bar] doesn't respond to canvas zoom"*.
//
// `ICoreGraphicsProxyWidget`'s banner priced this gap and declined it,
// saying *"whether a canvas at 4x zoom should show a 4x-scaled text box or
// a normal one is a design question nobody has asked"*. The owner has now
// asked it, and the AppKit seat had already answered the same way by
// setting the carried view's bounds size.
//
// ⚠ IT SCALES AS A PICTURE, WHICH IS THE OTHER TOOLKIT'S PROXY SEMANTICS
// AND NOT AppKit's. A RenderTransform magnifies the element's already-
// rasterised surface, so text grows but does not re-render crisper; AppKit's
// bounds trick makes the control redraw at the new scale. Both track the
// zoom, which is what was reported; a crisp WinUI overlay is a further row
// and is stated here rather than implied.
//
// 1.0/1.0 clears the transform rather than installing an identity one, so
// an unzoomed canvas costs nothing.
void setVisualScale(double scaleX, double scaleY);
// Where this element currently sits in its parent's coordinates -- the two
// attached properties setFrame() above writes, read back.
//
// ⚠⚠ THEY EXIST BECAUSE A LEAF THAT IS NOT AN ICoreWidget IS NEVER TOLD
// WHERE IT WAS PUT. The layout seat frames such a leaf through the second
// branch of ICoreWinUILayoutAccess::setFrame -- straight onto the element,
// because there is no wrapper geometry cache to go through. The SIZE half
// comes back to the wrapper through onResized; the POSITION half is
// reported to nobody. So a wrapper that keeps its own x/y (ICoreLabel,
// ICoreButton, ICoreRadioButton all do) holds 0,0 for the whole life of
// every instance a layout has placed, and the moment it applies a frame of
// its own -- any setFixed* call -- it restates that 0,0 and teleports the
// leaf to its parent's top-left corner. Found in the Notifications panel,
// whose message label is resized after it is positioned; see
// ICoreLabel::Impl::applyOwnSize().
//
// ⚠ METHODS, NOT DATA, and this file's own banner two members down says
// why in full: a member here would change the object layout of every
// widget that embeds one while changing no mangled name.
[[nodiscard]] double frameX() const;
[[nodiscard]] double frameY() const;
// Size this element to `w` x `h` WITHOUT moving it and WITHOUT firing
// onResized -- the half of setFrame the wrapper seat applies inline,
// because it gates the resize hook on a real change itself.
//
// ⚠⚠ IT EXISTS SO THE CLIP BELOW CANNOT BE LEFT BEHIND (W10.45). The
// wrapper seat used to write Width/Height on the Canvas directly, and only
// setFrame re-cut the child clip -- so a widget that turned clipping on
// while it was 0 wide kept a 0-wide clip for the rest of its life, however
// large it was made. ICoreFixedPanelMenuPanel turns it on at the start of
// every slide, from a closed width of 0: every left-rail page and the
// Copilot panel opened to their full size, visible, and cut away to
// nothing. Every write of this element's size now goes through a method
// that re-cuts the clip, which is what "keep the clip in step as the frame
// moves" below promised and what AppKit's clipsToBounds and GTK's
// `overflow` do by construction.
void setSize(double w, double h);
// ⚠⚠ CLIP THIS ELEMENT'S CHILDREN TO ITS OWN FRAME, and keep the clip
// in step as the frame moves. Off by default, which is what a plain Canvas
// does.
//
// ⚠ A CHILD ELEMENT IS NOT CLIPPED BY ITS PARENT ON THIS PLATFORM, and
// that is the whole reason this exists. It is not like a painted item,
// where the scene applies the parent's clip on the way down -- a XAML child
// composites wherever its Canvas.Left/Top put it, including well outside
// the panel that owns it. The defect it was added for: an ICoreSlider
// carried onto a graphics canvas by ICoreGraphicsProxyWidget stayed on
// screen when the canvas was panned away from it, drawing over the layouts
// ABOVE AND BELOW the view -- a control from inside a scrolling surface
// floating across the rest of the page.
//
// ⚠ THE SCROLL PANE SEAT SPELLS THIS INLINE and predates it
// (Widgets/ICoreScrollPane.cpp, setPaneSize). It is left alone here rather
// than retrofitted: it works, and changing a control's clipping while
// fixing a different control's is how one fix becomes two bugs.
void setClipsChildren(bool clips);
// W10.130 -- rasterise on the GPU and present through a XAML
// SurfaceImageSource, instead of a WIC bitmap copied into a WriteableBitmap.
// Off by default; the graphics view turns it on, because a canvas is the
// one surface that repaints every frame of a drag.
//
// ⚠ IT APPLIES ONLY WHILE THE ELEMENT IS ON SCREEN: an element with no
// XamlRoot, every headless run, and a box whose GPU refuses the device keep
// the WIC path, and `ICORE_WINUI_SOFTWARE_CANVAS=1` forces it everywhere --
// the escape hatch for telling a GPU fault from a paint fault.
void setHardwareSurface(bool hardware);
// The BACKEND painter behind the ICorePainter that onPaint is handed, as
// an opaque handle.
//
// ⚠ IT EXISTS FOR THE PAINTED SEATS AND FOR NOTHING ELSE, and the
// reason is a link cost rather than a preference. The painted surfaces this
// backend already owns -- the splitter divider, the button chrome, the
// scrollbar -- draw through ICoreWinUIPainter, whose suites read pixels
// back out of a WIC bitmap with no Qt, no window and no Windows App SDK
// linked. Reaching them through ICorePainter instead would pull the value
// tier and, through ICoreString, Qt itself into every one of those suites.
//
// ⚠ IT IS VALID ONLY INSIDE onPaint, and NOTHING HERE ENFORCES THAT.
// Between BeginDraw and EndDraw the handle addresses a live render target;
// outside them it addresses one that will reject every call, and a caller
// that held on to it gets no diagnostic. It is not gated by a flag because
// a flag would be a data member, and a data member changes this class's
// object layout -- and the layout of every class that holds one -- while
// changing no mangled name, so a build that straddles the change links
// perfectly and is wrong at runtime.
[[nodiscard]] void* nativePainterHandle() const;
// Mark the whole element, or one rectangle of it, as needing repaint.
//
// ⚠⚠ NEITHER OF THESE PAINTS. They record what is owed and schedule ONE
// flush per turn, so a seat that moves five properties produces one frame
// and not five -- which is what the two commonest seats in this backend
// were doing twenty-two times each. The dirty rectangles are UNIONED, so
// the region a paint hook receives is everything asked for since the last
// frame rather than whatever the most recent caller happened to name.
//
// ⚠ THE FLUSH IS QUEUED THROUGH ICoreMainThread::post, whose own header
// publishes the fallback this depends on: with no application object there
// is no queue, and the callable runs synchronously instead. So a headless
// caller and every path before the application exists keep the old
// paint-right-now behaviour exactly; only a live loop coalesces.
void requestRepaint();
void requestRepaint(double x, double y, double w, double h);
// Paint what is owed, now. The scheduled callback's body, public because
// that callback reaches it through the Canvas registry rather than through
// a captured pointer -- see the .cpp.
//
// ⚠ SAFE TO CALL WITH NOTHING OWED: it answers the empty state and returns
// without touching Direct2D. A caller that wants a frame must request one
// first; calling this alone paints nothing, deliberately, because "paint
// regardless" is how a coalescing path gets defeated one call site at a
// time.
void flushRepaint();
// The style spec to draw UNDER this element's own content, on every paint
// from now on. Passing a spec whose Self rule sets no ground clears it.
//
// ⚠ IT IS A METHOD AND THE STORAGE IS ON Impl, WHICH IS THE WHOLE REASON
// THIS IS SAFE TO ADD. A data member here would change this class's object
// layout and the layout of every widget that embeds one, while changing no
// mangled name -- so a build straddling the change would link and then
// misbehave. Impl is defined in one .cpp and embedded by nobody.
//
// ⚠ IT IS THE STYLE-APPLY SEAM AND NOT A SECOND WAY TO PAINT. The painted
// seats resolve their own specs inside onPaint and are unaffected; this is
// for a widget that has NO paint hook of its own and is themed from
// outside by icoreApplyStyleSpec(), which is what a plain container is.
// Both draw through the same icoreWinUISurfaceInk / paintStyledSurface
// pair, so the two paths cannot disagree about what a spec means.
void setAppliedGround(const ICoreStyleSpec& spec);
// Opt this element into PARTIAL frames. Before each paint the provider is
// asked what the frame has to cover, given the element's logical size:
//
// * `Whole` -- paint everything, exactly as an element without one does;
// * `Rect` -- paint only `*region` (DIPs): it alone is cleared, onPaint
// is handed it as its four arguments and must draw at least
// every pixel inside it, and only its rows are copied out;
// * `Nothing` -- the frame on screen is already right; paint nothing.
//
// ⚠ THE ELEMENT OVERRULES `Rect` AND `Nothing` WHENEVER THERE IS NO
// PREVIOUS FRAME TO KEEP -- the first paint, a re-created or resized
// surface, a scale change, a frame that was dropped, a hide. The provider
// does not have to know about any of those, and cannot: they are this
// element's own state.
//
// ⚠ STORAGE ON Impl, for the reason setAppliedGround() gives. Passing an
// empty function opts back out.
void setPaintRegionProvider(
std::function<ICoreWinUIPaintRegion(double width, double height, ICoreWinUIRepaintRect* region)> provider);
private:
class Impl; // the two-line residue; state lives here
std::unique_ptr<Impl> impl;
};