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

API — ICoreEssentials/UI/Widgets

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

HeaderDefinesDeclarationsBases
ICoreButton.h—0—
ICoreComboBox.hICoreComboBox14public ICoreWidget
ICoreCompleter.hICoreCompleter15—
ICoreCompletionToken.h—0—
ICoreDocumentView.hICoreDocumentView17public ICoreNativeWidget
ICoreDoubleSpinBox.hICoreDoubleSpinBox26public ICoreLineEdit
ICoreHBoxButton.hICoreHBoxButton14public ICoreWidget
ICoreInfoLabel.hICoreInfoLabel5public ICoreWidget
ICoreItemDelegate.hICoreItemRenderContext, ICoreItemDelegate8public ICoreNativeObject
ICoreLabel.h—0—
ICoreLineEdit.hICoreLineEdit56public ICoreNativeWidget
ICoreListBox.hICoreListBoxItem, ICoreListBox28public ICoreNativeWidget
ICoreProgressBar.hICoreProgressBar12public ICoreNativeWidget
ICoreRadioButton.hICoreRadioButton9public ICoreNativeWidget
ICoreRichTextEdit.h—0—
ICoreRotatableArrowIcon.h—0—
ICoreScrollPane.hICoreScrollPane21public ICoreNativeWidget
ICoreSlider.hICoreSlider8public ICoreNativeWidget
ICoreSpinBox.hICoreSpinBox22public ICoreLineEdit
ICoreSplitter.hICoreSplitter19public ICoreNativeWidget
ICoreStackedWidget.hICoreStackedWidget8public ICoreNativeWidget
ICoreTabBar.hICoreTabBar24public ICoreWidget
ICoreTable.hICoreTable29public ICoreWidget
ICoreTableEntryRow.hICoreTableEntryRow13public ICoreWidget
ICoreTableTitlesRow.hICoreTableTitlesRow9public ICoreWidget
ICoreTextEdit.hICoreTextEdit32public ICoreNativeWidget
ICoreToggleButton.hICoreToggleButton12public ICoreWidget
ICoreTree.hICoreTreeItem, ICoreTree54public ICoreNativeWidget
ICoreTreeView.hICoreTreeRow, ICoreStandardItem, ICoreTreeView67public ICoreNativeWidget
ICoreTreeViewModel.hICoreTreeViewModel14public ICoreNativeObject
ICoreWidget.hICoreWidget146public ICoreNativeWidget
ICoreWidgetGrid.hICoreWidgetGrid7public ICoreWidget
ICoreWidgetPaintCommands.h—0—

ICoreButton.h#

ICoreEssentials/UI/Widgets/ICoreButton.h

Declares no class of its own — see the file.

ICoreComboBox.h#

ICoreEssentials/UI/Widgets/ICoreComboBox.h

ICoreComboBox#

ICoreComboBox.h:21 · class · bases public ICoreWidget · pImpl · 14 declaration(s)

An inline option picker: the chosen option and a pair of arrows, opening a drop-down list styled like ICoreGlobalSearchDialog (rounded raised surface, hairline, drop shadow, themed rows) instead of...

class ICoreComboBox : public ICoreWidget {
public:
    // Mirror of the optionUpdated signal, for clients outside the wrapper zone.
    ICoreSignal<ICoreString> onOptionUpdated;

    // Fixed is the default and is what all thirteen existing call sites get:
    // the value must be one of the offered options and anything else is a bug
    // worth logging. Editable adds a typed value -- the user may enter a name
    // the list does not contain, which is what a "category" field needs, since
    // offering the categories already in use is a convenience rather than a
    // constraint (C21).
    enum class Entry { Fixed, Editable };

    explicit ICoreComboBox(ICoreNativeWidget* parent = nullptr);
    ICoreComboBox(Entry entry, ICoreNativeWidget* parent = nullptr);

    [[nodiscard]] Entry entry() const;

    // In Editable mode `chosenOption` need NOT appear in `availableOption`.
    void loadOptions(const std::string& chosenOption, const std::vector<std::string>& availableOption);

    void reset();

    void onOptionUpdate();

    std::string getChosenOption();

    ~ICoreComboBox() override;

protected:
    void paintContent(ICorePainter& painter) override;
    void pointerEntered() override;
    void pointerLeft() override;
    bool mousePressed(const ICoreMouseEvent& event) override;
    void resized(const ICoreSizeF& newSize, const ICoreSizeF& oldSize) override;

public:
    [[nodiscard]] ICoreSizeF preferredSize() const override;

protected:

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

ICoreCompleter.h#

ICoreEssentials/UI/Widgets/ICoreCompleter.h

⚠ class QCompleter; and class QStringListModel; RETIRED BY A9.4 (2026-08-21) -- both dead. Q1.5 removed class QWidget; here when its constructor parameter went and left its two neighbours behind, which is how a declaration outlives the member that needed it.

ICoreCompleter#

ICoreCompleter.h:45 · class · pImpl · 15 declaration(s)

Live name completion for a text field or code editor: the popup list, the prefix filtering behind it, and which entry is currently picked.

class ICoreCompleter {
public:
    // `host` is the widget the popup positions itself against and whose key
    // events walk the list. Not owned.
    explicit ICoreCompleter(ICoreNativeWidget* host);
    ~ICoreCompleter();

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

    // The names offered. Replacing them re-filters against the current prefix
    // the next time showFor() runs.
    void setEntries(const ICoreStringList& entries);

    // Entries fetched on first use instead of up front, for a catalog that is
    // still empty when the field is built. Called at most once, by showFor(),
    // and only while the list is still empty.
    void setEntrySource(std::function<ICoreStringList()> source);

    // How many entries the popup shows before it scrolls. Defaults to 8.
    void setMaxVisibleEntries(int count);

    // Filter to the entries starting with `prefix` (case-insensitively) and
    // show the popup at the host's caret. An empty prefix, or a prefix nothing
    // matches, hides the popup instead.
    //
    // Returns whether the popup is showing afterwards, with the FIRST match
    // picked -- see the .cpp's note in prepareFor() for why "nothing is picked
    // yet" was reversed, and what it costs.
    //
    // `showOnEmptyPrefix` offers the whole list instead of hiding when the
    // prefix is empty -- what an explicit "show me the completions" gesture
    // (Ctrl+Space in the script editor) means, as opposed to typing.
    bool showFor(const ICoreString& prefix, bool showOnEmptyPrefix = false);

    // Same, but anchored to `anchor` in the host's coordinates rather than to
    // the host widget itself. A multi-line editor needs it: the list belongs
    // under the CARET, which is somewhere inside a widget that may be the whole
    // window tall.
    //
    // The anchor's WIDTH is ignored -- the popup is widened to fit its longest
    // entry plus its scroll bar. A caller passing a caret rect cannot know that
    // width, and the two call sites that needed it were both reaching into the
    // popup's item view to compute it by hand.
    bool showFor(const ICoreString& prefix, const ICoreRect& anchor,
                 bool showOnEmptyPrefix = false);

    void hidePopup();
    [[nodiscard]] bool isPopupVisible() const;

    // ------------------------------------------------------------------
    // Walking the list from the keyboard.
    //
    // ⚠⚠ THE POPUP DOES NOT HAVE THE KEYBOARD AND MUST NOT TAKE IT. The host
    // is the field being typed into; a popup that stole focus to receive its
    // own arrow keys would stop the next keystroke reaching the text. So the
    // keys arrive at the HOST and are offered here, which is the shape
    // QCompleter has and the shape the seat's own note said it was leaving to
    // the caller -- and leaving it to the caller meant the arrow keys and Enter
    // did nothing at all, on every backend, at every call site.
    //
    // A host that is an ICoreLineEdit is wired automatically at construction,
    // through ICoreLineEdit::setKeyInterceptor -- the seam that exists for
    // exactly this. Any other host calls handleKey() from its own key hook.
    // ------------------------------------------------------------------

    // Offer a key to the popup. Answers true when the popup consumed it, which
    // is the caller's "do not also let the field see this".
    //
    // Up/Down walk the list (and wrap), Enter/Tab accept the pick and raise
    // onActivated, Escape closes without one. Everything else is refused, so a
    // field with a completer up still types normally.
    bool handleKey(const ICoreKeyEvent& event);

    // Move the pick by `delta` entries, wrapping at both ends. A popup with
    // nothing picked yet lands on the first entry going down and the last one
    // going up, which is what "nothing is picked yet" has to mean for the first
    // Down to be useful.
    void moveHighlight(int delta);

    // Accept the current pick: raise onActivated and close. Answers false, and
    // does nothing, when there is no pick to accept.
    bool activateHighlighted();

    // The entry currently picked -- the first match when the popup has just
    // opened, and whatever the arrows or the pointer have moved onto since. ""
    // only while the popup is down.
    [[nodiscard]] ICoreString highlightedEntry() const;

    // Whether there is a pick for Enter/Tab to accept. Still two conditions --
    // the popup is up AND something is picked -- but the second is now true
    // from the moment the popup opens: see showFor().
    [[nodiscard]] bool hasHighlightedEntry() const;

    // Raised as the user walks the popup. This is what makes an entry "picked".
    ICoreSignal<ICoreString> onHighlighted;

    // Raised when an entry is chosen -- clicked, or Enter while the popup owns
    // the key. The client splices the name into its own line.
    ICoreSignal<ICoreString> onActivated;

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

ICoreCompletionToken.h#

ICoreEssentials/UI/Widgets/ICoreCompletionToken.h

Where a completable name starts. Split out of ICoreCompletionPopup.h by A8.1, and the split is the point of the file rather than a tidy-up.

⚠ THE BODY NEVER NEEDED A TOOLKIT AND WAS TRAPPED IN A BACKEND DIRECTORY ANYWAY. It is string arithmetic -- walk back over name characters, then over spaces, then look at one character -- living in Backends/Qt/Widgets/ICoreCompletionPopup.cpp because that is where the painted popup beside it had to live. On the AppKit backend that file is not compiled at all, so the two SDK call sites linked against nothing: the function was undefined on a whole backend for no reason except its address. The AppKit port board's §0.31 is this pattern and §0.25 is the answer (its filename is kept out of headers: gen_api.py publishes these comments, so a board name here trips D15 for whoever next rebuilds the docs) -- the fix for a Qt-only tier is usually a SPLIT, not a second seat, because a second

Declares no class of its own — see the file.

ICoreDocumentView.h#

ICoreEssentials/UI/Widgets/ICoreDocumentView.h

ICoreDocumentView#

ICoreDocumentView.h:22 · class · final · bases public ICoreNativeWidget · pImpl · 17 declaration(s)

A read-only pane of formatted text that scrolls: the library card's body, and anything else that shows HTML rather than accepting typing.

class ICoreDocumentView final : public ICoreNativeWidget {
public:
    explicit ICoreDocumentView(ICoreNativeWidget* parent = nullptr);
    ~ICoreDocumentView() override;

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

    // No frame of its own -- the owner draws whatever border there is.
    void setChromeless(bool chromeless);

    // False makes the text unselectable, i.e. purely something to read.
    void setSelectable(bool selectable);

    void setLinksClickable(bool clickable);

    void setScrollBars(bool vertical, bool horizontal);

    // CSS applied to the HTML this view renders. Distinct from setStyleSheet:
    // this styles the DOCUMENT, that styles the WIDGET around it.
    void setDocumentCss(const ICoreString& css);

    void setDocumentMargin(double margin);

    void setHtml(const ICoreString& html);

    // Lay the document out at this width, so contentHeight() can be asked.
    void setContentWidth(double width);
    [[nodiscard]] double contentHeight() const;

    // What a vertical scroll bar would take from the content width.
    [[nodiscard]] int verticalScrollBarWidth() const;

    void scrollToTop();

    // The pane's own QSS. Inherited before; explicit now.
    void setStyleSheet(const ICoreString& styleSheet);

    ICoreNativeHandle nativeWidgetHandle() const override;

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

ICoreDoubleSpinBox.h#

ICoreEssentials/UI/Widgets/ICoreDoubleSpinBox.h

ICoreDoubleSpinBox#

ICoreDoubleSpinBox.h:40 · class · bases public ICoreLineEdit · pImpl · 26 declaration(s)

A double-valued field with a stepper: the ICoreSpinBox of real numbers.

class ICoreDoubleSpinBox : public ICoreLineEdit {
public:
    explicit ICoreDoubleSpinBox(ICoreNativeWidget* parent = nullptr);
    ~ICoreDoubleSpinBox() override;

    [[nodiscard]] double value() const;
    [[nodiscard]] double minimum() const;
    [[nodiscard]] double maximum() const;
    [[nodiscard]] double singleStep() const;
    // Digits after the point in the field: 0 to 17, or -1 for the shortest
    // exact text. Display only; the value is never rounded to it.
    [[nodiscard]] int decimals() const;
    [[nodiscard]] ICoreString suffix() const;

    void setValue(double value);
    // An inverted range becomes an empty one pinned at the minimum, never a
    // swapped one; a NaN bound is ignored.
    void setRange(double minimum, double maximum);
    void setMinimum(double minimum);
    void setMaximum(double maximum);
    // Not finite or not positive: the step stays as it was.
    void setSingleStep(double step);
    void setDecimals(int decimals);
    void setSuffix(const ICoreString& suffix);
    // Steps from the current value; fractional steps are allowed. The result
    // lands on the shown grid and inside the range.
    void stepBy(double steps);

    ICoreSignal<double> onValueChanged;

protected:
    void paintContent(ICorePainter& painter) override;
    bool mousePressed(const ICoreMouseEvent& event) override;
    bool mouseReleased(const ICoreMouseEvent& event) override;
    bool mouseMoved(const ICoreMouseEvent& event) override;
    bool mouseDoubleClicked(const ICoreMouseEvent& event) override;
    void pointerLeft() override;
    bool keyPressed(const ICoreKeyEvent& event) override;
    bool wheelScrolled(const ICoreWheelEvent& event) override;
    void focusGained(const ICoreFocusEvent& event) override;
    void focusLost(const ICoreFocusEvent& event) override;

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

ICoreHBoxButton.h#

ICoreEssentials/UI/Widgets/ICoreHBoxButton.h

⚠⚠ setIcon(icoreThemedIcon(path)) EXCEPT THAT IT COMES BACK AFTER A THEME SWITCH, which is the whole of what this verb adds. An ICoreIcon is a RASTER resolved at the moment it is built (ICoreThemedIconFactory.h says so and offers the subscription a holder is supposed to write); the twenty left-panel menu buttons set one in their constructors and none of them wrote it, so every glyph in that rail kept the ink of whichever theme the window was built under.

Recorded rather than converted, so the path can be re-asked on each switch -- which is the one thing a finished raster cannot do for itself.

ICoreHBoxButton#

ICoreHBoxButton.h:18 · class · bases public ICoreWidget · pImpl · 14 declaration(s)

class ICoreHBoxButton : public ICoreWidget {
public:
    explicit ICoreHBoxButton(ICoreNativeWidget* parent = nullptr);

    void setText(const std::string& text) const;
    void setIcon(const ICoreIcon& icon);

    // ⚠⚠ setIcon(icoreThemedIcon(path)) EXCEPT THAT IT COMES BACK AFTER A THEME
    // SWITCH, which is the whole of what this verb adds. An ICoreIcon is a
    // RASTER resolved at the moment it is built (ICoreThemedIconFactory.h says
    // so and offers the subscription a holder is supposed to write); the twenty
    // left-panel menu buttons set one in their constructors and none of them
    // wrote it, so every glyph in that rail kept the ink of whichever theme the
    // window was built under.
    //
    // Recorded rather than converted, so the path can be re-asked on each
    // switch -- which is the one thing a finished raster cannot do for itself.
    void setThemedIcon(const ICoreString& svgPath);
    void setIconSize(const ICoreSizeF& size);
    void setHeight(const double& height);
    void setWidth(const double& width);
    void setLabelXPos(const int& xPos);
    void setIconPos(const int& newIconPosX, const int& newIconPosY);
    void setHoldHoverStyle(const bool& holdHoverStyle);

    // Out of line: m_hoverAnimation's unique_ptr needs the complete type there.
    ~ICoreHBoxButton() override;

protected:
    // The hover treatment is painted rather than styled, so it can animate and
    // carry the accent edge. See the paint order in the .cpp.
    void paintContent(ICorePainter& painter) override;
    void pointerEntered() override;
    void pointerLeft() override;

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

ICoreInfoLabel.h#

ICoreEssentials/UI/Widgets/ICoreInfoLabel.h

ICoreInfoLabel#

ICoreInfoLabel.h:17 · class · bases public ICoreWidget · pImpl · 5 declaration(s)

class ICoreInfoLabel : public ICoreWidget {
public:
    explicit ICoreInfoLabel(ICoreNativeWidget* parent);

    // `technique` places the card relative to objectUnderCursor: "middleRight",
    // "aboveCenter", "belowLeft", or anything else for the default below-right.
    //
    // `prominentTitle` is opt-in and off by default, so every existing caller
    // keeps the uniform toolbar-tooltip look. Turning it on draws the title at
    // the theme's TITLE size and the description in the MONO face -- the shape
    // a card wants when its title is a name and its description is a path, not
    // a sentence. It is a per-call flag rather than a setter because this
    // widget is a per-window singleton shared by every hover site in the app:
    // state left on it would leak into the next caller's card.
    void showInfoLabel(const ICoreString &title, const ICoreString &description, const ICoreNativeWidget* objectUnderCursor, const ICoreString &technique, double delayDuration, bool prominentTitle = false);
    void hideInfoLabel();

    // Pin the card to `width` pixels and WRAP its text into that width instead
    // of letting one long line decide how wide the card is. 0 (the default)
    // restores the shrink-wrap behaviour every existing caller has.
    //
    // ⚠ IT IS NOT `ICoreWidget::setFixedWidth`, AND THE DIFFERENT NAME IS THE
    // POINT RATHER THAN AN OMISSION. The base's setter is not virtual, so an
    // override would be a HIDDEN member: right when called through an
    // ICoreInfoLabel*, silently back to the base behaviour when called through
    // an ICoreWidget*, which is the kind of split nobody finds by reading the
    // call site. It also would not mean the same thing -- this width has to
    // survive the adjustSize() that every showInfoLabel() performs, and it has
    // to reach the two labels inside, because a card is only as narrow as the
    // longest line it is willing to break.
    void setFixedCardWidth(int width);

    ~ICoreInfoLabel() override;

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

ICoreItemDelegate.h#

ICoreEssentials/UI/Widgets/ICoreItemDelegate.h

ICoreItemRenderContext#

ICoreItemDelegate.h:14 · struct · 0 declaration(s)

Everything a row-painting override needs to know, as ICore values.

struct ICoreItemRenderContext {
public:
    ICoreRect rect;
    bool selected = false;
    bool hovered = false;
    int row = -1;
    int column = -1;
    ICoreString text;
    ICoreString tag;

    // The colour the row was given for its text (what setForeground put on the
    // item). INVALID when the row was given none -- an ICoreColor that no call
    // site set, which is the same thing the model says by holding nothing.
    ICoreColor foreground;
};
};

ICoreItemDelegate#

ICoreItemDelegate.h:51 · class · bases public ICoreNativeObject · pImpl · 8 declaration(s)

ICoreItemDelegate -- custom row painting for the item views.

class ICoreItemDelegate : public ICoreNativeObject {
public:
    ICoreItemDelegate();
    ~ICoreItemDelegate() override;

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

    // How a view is handed this delegate -- ICoreTree::setItemDelegate resolves
    // it through here. The delegate is NOT owned by the view: hold the wrapper
    // as a member for at least as long as the view that paints with it.
    ICoreNativeHandle nativeObjectHandle() const override;

protected:
    // Return true if the row was painted; false falls back to the default
    // rendering (so a delegate can special-case only some rows).
    virtual bool paintItem(ICorePainter& painter, const ICoreItemRenderContext& context);

    // A zero size means "use the default".
    virtual ICoreSizeF itemSizeHint(const ICoreItemRenderContext& context);

    // The colour a SELECTED row's text should be drawn in, asked just before
    // the default painting runs. An invalid return -- the default -- leaves the
    // style's own highlighted-text colour in place.
    //
    // This is the one thing paintItem cannot express. The style paints selected
    // text in one flat colour for the whole view, so a view whose rows carry
    // meaning in their colour (a log's severities) loses it the moment a block
    // is selected; recovering that through paintItem would mean redrawing
    // indent, branch strip, icon and text by hand, i.e. reimplementing the
    // selection itself. Answering with the row's own colour changes the colour
    // and nothing else.
    virtual ICoreColor selectedTextColor(const ICoreItemRenderContext& context);

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

ICoreLabel.h#

ICoreEssentials/UI/Widgets/ICoreLabel.h

Declares no class of its own — see the file.

ICoreLineEdit.h#

ICoreEssentials/UI/Widgets/ICoreLineEdit.h

⚠ NO class QLineEdit; HERE ANY MORE (A9.23, 2026-08-24) -- see where nativeLineEdit() went, further down.

ICoreLineEdit#

ICoreLineEdit.h:27 · class · bases public ICoreNativeWidget · pImpl · 56 declaration(s)

P5.11: converted.

class ICoreLineEdit : public ICoreNativeWidget {
public:

    // ⚠ Takes ICoreFont, and deliberately does NOT re-export a QFont overload.
    // P7.6 converted ICoreFont, so it no longer converts to QFont and every
    // call site passing one needs a sink that speaks the wrapper.
    void setFont(const ICoreFont& font);

    // The Impl QLineEdit -- out of line because the header only
    // forward-declares Impl. Was `this` while the Qt base existed.
    ICoreNativeHandle nativeWidgetHandle() const override;

    // Which of the field's own chrome this instance wears. Field is the default
    // and is everything described above; None paints and styles NOTHING, so the
    // widget is behaviourally a plain QLineEdit.
    //
    // None exists for the fields that sit INSIDE something that already owns the
    // chrome -- ICoreSearchTextBox lives in a search bar that draws the border
    // and the ground, ICoreNavBarLineEdit carries getStyle_DirectoryTextField().
    // Painting the field's own rounded border on top of those would show two
    // borders, so those subclasses opt out rather than stay on QLineEdit and
    // leave the library with two unrelated line-edit bases.
    //
    // Opt-IN, exactly like ICoreWidget::Surface::None (C1): the default is the
    // behaviour every existing call site already has, so adopting the component
    // somewhere new can never silently restyle it.
    enum class Chrome { Field, None };

    // Q1.2: one parent spelling. The QWidget* twin and the std::nullptr_t
    // delegate that broke their tie both went; with a single pointer
    // overload a literal nullptr is unambiguous and can carry the default.
    explicit ICoreLineEdit(ICoreNativeWidget *parent = nullptr, Chrome chrome = Chrome::Field);
    // Mirrors QLineEdit(text, parent) so call sites that were plain QLineEdits
    // swap over without splitting the construction into two lines.
    explicit ICoreLineEdit(const ICoreString &text, ICoreNativeWidget *parent = nullptr);

    [[nodiscard]] Chrome chrome() const;

    // Both spellings kept as members now that the `using QLineEdit::…`
    // re-export died with the base. The ICore enum values are pinned to the
    // toolkit's by the static_asserts in ICoreInputEnumsVerify.cpp.
    // Q3.2 deleted the Qt::FocusPolicy twin; this is the only spelling now.
    void setFocusPolicy(ICoreFocusPolicy policy);

    void setSizeBehavior(ICoreSizeBehavior horizontal, ICoreSizeBehavior vertical);

    // See a key BEFORE the field acts on it. Return true to consume it, false to
    // let the field have it -- the same "handled?" convention ICoreWidget's hooks
    // use.
    //
    // Runs from the Impl's event(), NOT keyPressEvent, which is the only level
    // that can see Tab: focus traversal eats Tab before keyPressEvent is ever
    // reached, so an interceptor installed lower could not implement
    // Tab-completion.
    //
    // This is what an owner reaches for instead of installing an event filter on
    // the field from outside the wrapper zone: a filter costs the owner a
    // QObject/QEvent signature it otherwise never needs, and it sees the key
    // only as an untyped QEvent it has to cast itself. The history recall and
    // the Ctrl+C interrupt in the Terminal panel are both this.
    void setKeyInterceptor(std::function<bool(const ICoreKeyEvent&)> interceptor);

    // Mirrors of the QLineEdit signals client code subscribes to.
    ICoreSignal<ICoreString> onTextChanged;
    // NOT the same as onTextChanged: this fires only for edits the USER made,
    // never for a programmatic setText(). Anything that reacts by re-deriving
    // state from the field -- a completion prefix, say -- wants this one, or a
    // setText() of its own re-enters it.
    ICoreSignal<ICoreString> onTextEdited;
    ICoreSignal<> onReturnPressed;
    ICoreSignal<> onEditingFinished;
    // NOT the same moment as onEditingFinished: that one also fires on Return,
    // while this is the caret genuinely leaving. The fields that commit their
    // value when the user clicks away subscribe here.
    ICoreSignal<> onFocusLost;

    double borderOpacity() const;
    void setBorderOpacity(double opacity);

    // The field's ground. Defaults to the field.background token and follows a
    // theme switch on its own; pass a colour to put this one field on a
    // different ground (the read-only cells do), or an invalid ICoreColor to hand
    // it back to the token.
    //
    // It is a colour rather than a style sheet because the ground is painted
    // here now: a style sheet background is a RECTANGLE, and this field's
    // border is rounded, so the fill's square corners sat outside the arc --
    // four little tabs of field colour poking out of every field in the app.
    // Painting it as the same rounded path the border strokes is the only way
    // the two can agree.
    void setFieldBackground(const ICoreColor& color);

    // ------------------------------------------------------------------
    // Facade members now that the QLineEdit base is gone. Seeded with the
    // text/selection/state operations the tier itself needs; the rest were
    // enumerated by the flip's harvest, exactly as ICoreLabel's were.
    // ------------------------------------------------------------------
    void setText(const ICoreString& text);
    [[nodiscard]] ICoreString text() const;
    void clear();
    void selectAll();
    void setPlaceholderText(const ICoreString& text);
    void setReadOnly(bool readOnly);
    [[nodiscard]] bool isReadOnly() const;
    [[nodiscard]] bool hasFocus() const;
    void setFocus();
    void setFocus(ICoreFocusReason reason);
    void update();
    void setEnabled(bool enabled);
    [[nodiscard]] bool isEnabled() const;
    // Q2.2: was setCursor(const QCursor&). Renamed rather than retyped to
    // ICoreCursor, because ICoreWidget, ICoreButton and ICoreGraphicsObject all
    // already publish this operation as setCursorShape() and §9 says take the
    // existing name. Same one-line body those three use.
    //
    // ⚠ A grep for `->setCursor(` found NO callers and that was WRONG: the one
    // caller is ICoreSpinBox::mouseMoved, which derives from this class and
    // calls it UNQUALIFIED, so the receiver never appears in the pattern. Only
    // the compiler found it. That is one of the five documented ways a
    // call-site survey lies at a wrapper seam -- do not size one of these swaps
    // from grep alone.
    void setCursorShape(ICoreCursorShape shape);
    void setToolTip(const ICoreString& tip);
    void setStyleSheet(const ICoreString& styleSheet);
    void setMinimumHeight(int height);
    void setVisible(bool visible);
    [[nodiscard]] int width() const;
    [[nodiscard]] int height() const;
    void setFixedHeight(int height);
    void setFixedWidth(int width);
    void setObjectName(const ICoreString& name);
    void setMinimumWidth(int width);
    void setTextMargins(int left, int top, int right, int bottom);
    // Where the text sits inside the field. Same enum and same spelling
    // ICoreLabel::setAlignment takes; the values are pinned to the toolkit's by
    // the static_asserts in ICoreInputEnumsVerify.cpp.
    void setAlignment(ICoreAlignment alignment);
    void setCursorPosition(int position);
    [[nodiscard]] int cursorPosition() const;
    void setClearButtonEnabled(bool enabled);
    [[nodiscard]] bool hasSelectedText() const;
    [[nodiscard]] ICoreFont font() const;

    // Unscoped on purpose, matching QLineEdit's spelling at the one call site
    // (ICoreCopilotConnectionSettings writes ICoreLineEdit::Password). Pinned
    // against the toolkit's values by static_asserts in the .cpp.
    enum EchoMode { Normal = 0, NoEcho = 1, Password = 2, PasswordEchoOnEdit = 3 };
    void setEchoMode(EchoMode mode);

    // Whether the SYSTEM may spell-check what is typed here -- the red underline
    // under a word it does not know, and, where the platform has them, the
    // automatic correction and the grammar pass that travel with it. On by
    // default, because that is every platform's own default and because most of
    // this app's fields hold prose a user would want checked.
    //
    // ⚠ TURN IT OFF FOR A FIELD THAT HOLDS A NAME RATHER THAN A SENTENCE. A
    // variable name, an identifier, a path, a units string and a type are all
    // "misspelled" by construction, so the checker marks every row red and says
    // nothing true; and where the platform also auto-CORRECTS, it does not just
    // annotate the value, it rewrites it. The Variables Space is the case this
    // was added for (W10.96), and the Quick Code prompt is the second: a
    // command is not a sentence, and the system capitalised its first word.
    //
    // ⚠ OFF MEANS NO AUTOMATIC REWRITE OF ANY KIND, not only the checker:
    // capitalisation, text replacement, smart quotes and dashes, completion and
    // inline prediction go with it wherever the platform has them (AppKit and
    // UIKit both do; WinUI's IsTextPredictionEnabled is its whole rewrite).
    //
    // ⚠ WHAT EACH SEAT DOES WITH IT, because they genuinely differ and a caller
    // deserves to know which of the three it is really buying:
    //
    //   - WinUI answers it FULLY -- IsSpellCheckEnabled is the underline and
    //     IsTextPredictionEnabled is the rewrite, and both are real;
    //   - AppKit answers it fully, through the window's shared field editor,
    //     re-asserted at every edit start (the editor does not remember a
    //     field, so "off" set once would travel to the next field edited);
    //   - GTK4 answers it ADVISORY ONLY. GTK has no built-in checker -- red
    //     squiggles there come from libspelling/GtkSourceView, which this tree
    //     does not use -- so the hint reaches input methods and assistive tech
    //     and nothing underlines anything either way. It is set regardless, so
    //     that a Linux seat which later gains a checker is already correct.
    //
    // 📌 A backend that answers by doing nothing says so at its own definition,
    // the way setPaintedField does.
    void setSpellCheckEnabled(bool enabled);

    // Ask this field to be DRAWN BY THIS TREE rather than by the platform's own
    // text control -- same value, same signals, same caret and selection API,
    // and this tree's selection band, caret and focus treatment instead of the
    // system's.
    //
    // ⚠ IT IS FOR A FIELD THAT SITS INSIDE SOMETHING THIS TREE PAINTS, and
    // that is the whole of when to reach for it. A stand-alone ICoreLineEdit
    // should be the platform's control: it brings the IME, the drag and drop
    // and the context menu the user expects, and none of that is worth trading
    // for a colour. Inside an editable ICoreComboBox the trade goes the other
    // way -- there the platform's selection colour and focus ring appear in the
    // middle of a control this tree drew, which is the one place they read as a
    // defect rather than as the platform.
    //
    // ⚠ WHAT IT COSTS, NAMED RATHER THAN DISCOVERED: a painted field has no
    // system context menu and no IME candidate window. Copy, cut, paste,
    // select-all and the caret keys are this tree's own and are all present;
    // composing Japanese in one is not.
    //
    // ⚠ AND IT IS ORTHOGONAL TO setEchoMode. A masked field is painted whether
    // this was asked for or not -- that is how masking is implemented -- so
    // this only ever decides the UNMASKED case. Backends whose native entry
    // already wears the application's palette answer it by doing nothing, and
    // say so at their own definition.
    void setPaintedField(bool painted);

    // Out of line: the unique_ptr member's deleter needs Impl complete, and
    // this header only forward-declares it.
    virtual ~ICoreLineEdit();

protected:
    // ------------------------------------------------------------------
    // Hook surface (P2.9d-2's shape): subclasses override THESE. The Qt
    // handlers that call them live in the Impl now -- a subclass cannot even
    // spell the Qt signatures. Contracts match ICoreWidget's hooks: the
    // bool hooks answer "handled?" (true consumes the event, false forwards
    // it to the toolkit), paintContent runs after the field chrome is drawn
    // (both Chrome modes), focusGained/focusLost run after the base handler
    // repaints and BEFORE the onFocusLost mirror fires -- ICoreSpinBox's
    // commit-on-blur depends on running ahead of the mirror's subscribers.
    // ------------------------------------------------------------------
    virtual void paintContent(ICorePainter& painter);
    virtual bool mousePressed(const ICoreMouseEvent& event);
    virtual bool mouseReleased(const ICoreMouseEvent& event);
    virtual bool mouseMoved(const ICoreMouseEvent& event);
    virtual bool mouseDoubleClicked(const ICoreMouseEvent& event);
    virtual void pointerLeft();
    virtual bool keyPressed(const ICoreKeyEvent& event);
    virtual bool wheelScrolled(const ICoreWheelEvent& event);
    virtual void focusGained(const ICoreFocusEvent& event);
    virtual void focusLost(const ICoreFocusEvent& event);

    // The Impl QLineEdit, for SUBCLASSES (all wrapper zone) whose behaviour
    // genuinely needs toolkit API the facade does not carry -- the validator,
    // style hints. Client code outside the zone never sees this.
    //
    // ⚠ H4.6 LEFT THIS ONE VIOLATION STANDING, DELIBERATELY, AND THE ROW LANDS
    // AT 1 RATHER THAN 0. It is a protected NON-VIRTUAL, which exemption 2 does
    // not cover, so the rule's only answers are publish or delete -- and unlike
    // ICoreTextEdit::nativeTextEdit(), ICoreRichTextEdit::nativeRichTextEdit()
    // and ICoreWindow::nativeWindow(), all of which had ZERO callers and were
    // deleted on their own rows, this one has SEVEN live callers in
    // ICoreSpinBox.cpp (the QIntValidator it installs, two SH_SpinBox style
    // hints, and a themed connect). Neither answer is available to one header:
    //
    //   - publishing it puts `QLineEdit*` on the PUBLIC contract of a wrapper
    //     whose whole purpose is that its header names no toolkit type;
    //   - deleting it means re-expressing those seven reaches as named
    //     operations, which is a redesign of ICoreSpinBox -- row H4.2, claimed
    //     by another session and mid-flight in this same directory.
    //
    // ⚠⚠ RESOLVED 2026-08-24 (A9.23) BY A THIRD OPTION THE NOTE ABOVE DID NOT
    // HAVE, and the dilemma is kept above because the reasoning is still the
    // reason this took so long. `QLineEdit* nativeLineEdit() const` is DELETED
    // from this header and is `icoreQtLineEdit(const ICoreLineEdit*)` in
    // UI/Backends/Qt/ICoreNativeHandleAccess.h -- which needs NO new member at
    // all, because this class already publishes a neutral
    // `nativeWidgetHandle()` through ICoreNativeWidget and the Qt seat returns
    // the very same object through it (`static_cast<QWidget*>(impl.get())`,
    // and the Impl derives QLineEdit). So the toolkit name leaves the public
    // contract, ICoreSpinBox's seven reaches keep working unchanged in spelling
    // but for the call, and H4.2's redesign is neither done nor blocked.

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

ICoreListBox.h#

ICoreEssentials/UI/Widgets/ICoreListBox.h

ICoreListBoxItem#

ICoreListBox.h:44 · class · pImpl · 10 declaration(s)

A flat list of selectable rows, wrapping QListWidget.

class ICoreListBoxItem {
public:
    ICoreListBoxItem();
    explicit ICoreListBoxItem(const ICoreString& text);
    ~ICoreListBoxItem();

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

    void setLabel(const ICoreString& text);
    ICoreString label() const;

    void setIcon(const ICoreIcon& icon);

    void setTag(const ICoreString& tag);
    ICoreString tag() const;

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

ICoreListBox#

ICoreListBox.h:74 · class · final · bases public ICoreNativeWidget · pImpl · 18 declaration(s)

class ICoreListBox final : public ICoreNativeWidget {
public:
    explicit ICoreListBox(ICoreNativeWidget* parent = nullptr);
    ~ICoreListBox() override;

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

    // Takes ownership of `item`'s toolkit half. Passing an item that is
    // already in a view does nothing -- it has no half left to give.
    void addRow(ICoreListBoxItem* item);
    void addRow(const ICoreString& text);

    // ⚠ Both may return nullptr: for an out-of-range index, and for a row this
    // library did not create. See the ownership note above.
    ICoreListBoxItem* row(int index) const;
    ICoreListBoxItem* currentRow() const;

    int rowCount() const;
    void clearRows();
    void setCurrentRow(int index);

    void setFont(const ICoreFont& font);
    void setStyleSheet(const ICoreString& styleSheet);
    void setHorizontalScrollBarVisibility(ICoreScrollBarPolicy policy);
    void setTextElideMode(ICoreTextElide mode);

    // Move the current row to whatever the pointer is over, without a click.
    //
    // ⚠⚠ OFF BY DEFAULT AND OPT-IN, BECAUSE IT IS THE MENU RULE AND NOT THE
    // LIST RULE. A list is a thing you pick FROM: the highlight is a commitment
    // the user made and moving it under a passing pointer would throw away a
    // selection somebody chose. A MENU -- and a completion popup is a menu -- is
    // a thing you pick IN: it has no state to lose, the pointer and the arrow
    // keys are two ways of saying the same thing, and a highlight that does not
    // follow the pointer is a menu where the mouse and the keyboard disagree
    // about which entry Enter would take.
    //
    // So the behaviour is named at the ONE call site that wants it rather than
    // given to every list in the product.
    void setSelectionFollowsHover(bool follows);

    // Suppresses the ICoreSignals below while a caller repopulates the list.
    // It is the toolkit's blockSignals underneath, which is what the one call
    // site used: the signals here are fired from lambdas connected to the Qt
    // ones, so blocking those stops these too.
    void setSignalsBlocked(bool blocked);

    ICoreNativeHandle nativeWidgetHandle() const override;

    ICoreSignal<int> onCurrentRowChanged;
    ICoreSignal<ICoreListBoxItem*> onRowDoubleClicked;
    ICoreSignal<ICoreListBoxItem*> onRowClicked;

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

ICoreProgressBar.h#

ICoreEssentials/UI/Widgets/ICoreProgressBar.h

ICoreProgressBar#

ICoreProgressBar.h:17 · class · final · bases public ICoreNativeWidget · pImpl · 12 declaration(s)

Determinate/indeterminate progress.

class ICoreProgressBar final : public ICoreNativeWidget {
public:
    explicit ICoreProgressBar(ICoreNativeWidget* parent = nullptr);
    ~ICoreProgressBar() override;

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

    void setRange(int minimum, int maximum);
    void setValue(int value);
    void setTextVisible(bool visible);

    void setIndeterminate(bool indeterminate);

    void setLabel(const ICoreString& format);

    void setFixedWidth(int width);
    void setFixedHeight(int height);

    ICoreNativeHandle nativeWidgetHandle() const override;

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

ICoreRadioButton.h#

ICoreEssentials/UI/Widgets/ICoreRadioButton.h

ICoreRadioButton#

ICoreRadioButton.h:21 · class · final · bases public ICoreNativeWidget · pImpl · 9 declaration(s)

A radio button that paints its own indicator from the theme tokens, the way ICoreLineEdit and ICoreSpinBox paint their field.

class ICoreRadioButton final : public ICoreNativeWidget {
public:
    explicit ICoreRadioButton(ICoreNativeWidget* parent = nullptr);
    explicit ICoreRadioButton(const ICoreString& text, ICoreNativeWidget* parent = nullptr);
    ~ICoreRadioButton() override;

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

    void setChecked(bool checked);
    [[nodiscard]] bool isChecked() const;

    void setText(const ICoreString& text);

    // Mirror of the Qt toggled signal, so client code subscribes without
    // QObject::connect: notchRadio->onToggled.connect(m_signals, [this](bool on){ ... });
    // Carries the new checked state, exactly as toggled(bool) did -- including
    // programmatic setChecked and the auto-uncheck of the sibling that just
    // lost the group.
    ICoreSignal<bool> onToggled;

    ICoreNativeHandle nativeWidgetHandle() const override;

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

ICoreRichTextEdit.h#

ICoreEssentials/UI/Widgets/ICoreRichTextEdit.h

Declares no class of its own — see the file.

ICoreRotatableArrowIcon.h#

ICoreEssentials/UI/Widgets/ICoreRotatableArrowIcon.h

Declares no class of its own — see the file.

ICoreScrollPane.h#

ICoreEssentials/UI/Widgets/ICoreScrollPane.h

ICoreScrollPane#

ICoreScrollPane.h:55 · class · final · bases public ICoreNativeWidget · pImpl · 21 declaration(s)

ICoreScrollPane -- the editor's scrolling container.

class ICoreScrollPane final : public ICoreNativeWidget {
public:
    explicit ICoreScrollPane(ICoreNativeWidget* parent);
    ~ICoreScrollPane() override;

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

    // The scrolled content. Takes ownership the way QScrollArea::setWidget
    // does: the pane deletes it, and a widget handed here must not also be
    // parented elsewhere.
    void setWidget(ICoreNativeWidget* content);

    // Whether the content is resized to fill the pane. On by default -- the
    // constructor sets it -- so the four call sites that spell it are either
    // turning it off or restating it.
    void setWidgetResizable(bool resizable);

    // Scrolls `child` into view. All five call sites pass both margins, so
    // neither has a default here; Qt's own 50/50 would be a number nobody in
    // this tree has ever asked for.
    void ensureWidgetVisible(ICoreNativeWidget* child, int xMargin, int yMargin);

    void disableHorizontalScrolling();
    void disableVerticalScrolling();

    // Forces the horizontal bar to exist even when the content fits. The tab
    // strip pairs this with setHorizontalScrollBarHidden(true): the bar has to
    // be present for the pane to scroll sideways, and invisible because a bar
    // under the tab row would be a second line under a row that already has
    // one. Spelled as its own method because "always on" and "off" are the two
    // policies this tree uses and neither is the Qt default.
    void keepHorizontalScrollBarEnabled();

    // Draws a themed hairline around the pane, using the same field border
    // token as ICoreLineEdit / ICoreTextEdit so a bordered pane and a bordered
    // field read as the same family. Off by default — most panes sit flush
    // inside a panel, where a border would only box in nothing.
    void setBordered(bool bordered);

    // Padding between the pane's edge and the scrolled content, in px. Set on
    // the viewport rather than the content widget's layout, so a caller does
    // not have to reach into whatever it happened to put inside.
    void setContentPadding(int padding);

    // Collapses the horizontal scrollbar to nothing while leaving it ENABLED,
    // so the pane still scrolls sideways by wheel or by keyboard.
    //
    // It lives here rather than in a stylesheet at the call site because
    // applyPaneStyle() rewrites the whole sheet on every theme switch (Qt
    // stylesheets replace, they do not merge) -- a caller's own rule would
    // survive exactly until the first Light/Dark switch.
    void setHorizontalScrollBarHidden(bool hidden);

    // Scrolls the content sideways by `pixels` (negative scrolls left), and
    // answers whether there was a horizontal bar to scroll. For the panes that
    // turn a vertical wheel into horizontal travel -- a tab strip, a filmstrip.
    bool scrollHorizontallyBy(int pixels);

    // Jump to the end of the content -- what a transcript does after an append.
    void scrollToBottom();

    // ---- what replaced viewport() -----------------------------------------

    // Gives the scrolled area an opaque ground in `color`.
    //
    // ⚠ PRESERVED DIVERGENCE, DO NOT "FIX": this sets the palette's Window
    // role, and a scroll area gives its viewport the Base role -- so on this
    // widget it paints nothing and always has. The three call sites have been
    // inert since they were written. That is exactly what the free helper they
    // used did, so it is what this does; making them suddenly take effect is a
    // visual change, not a port, and belongs to whoever owns that look.
    void fillViewportBackground(const ICoreColor& color);

    // Whether the scrolled area paints its own ground at all. One caller turns
    // it off so a themed frame behind the pane shows through.
    void setViewportAutoFill(bool autoFill);

    void setMinimumHeight(int height);
    void setFixedHeight(int height);
    void setVisible(bool visible);

    ICoreNativeHandle nativeWidgetHandle() const override;

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

ICoreSlider.h#

ICoreEssentials/UI/Widgets/ICoreSlider.h

ICoreSlider#

ICoreSlider.h:17 · class · final · bases public ICoreNativeWidget · pImpl · 8 declaration(s)

A value slider.

class ICoreSlider final : public ICoreNativeWidget {
public:
    explicit ICoreSlider(ICoreNativeWidget* parent = nullptr);
    ~ICoreSlider() override;

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

    void setRange(int minimum, int maximum);
    void setValue(int value);
    [[nodiscard]] int value() const;

    // Carries the new value, exactly as QSlider::valueChanged(int) did.
    ICoreSignal<int> onValueChanged;

    ICoreNativeHandle nativeWidgetHandle() const override;

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

ICoreSpinBox.h#

ICoreEssentials/UI/Widgets/ICoreSpinBox.h

⚠ THIRTEEN DEAD Qt INCLUDES AND TWO DEAD FORWARD DECLARATIONS RETIRED HERE (A9.4, 2026-08-21): <QEvent>, <QFocusEvent>, <QIntValidator>, <QKeyEvent>, <QMouseEvent>, <QObject>, <QPaintEvent>, <QPainter>, <QPoint>, <QRect>, <QTimer>, <QWheelEvent>, <QWidget>, plus class QTimer; and class QIntValidator;. Fifteen Qt references in one header, and NOT ONE of those types is named by a member below -- counted over the file rather than assumed.

⚠ TWO OF THEM WERE THE SAME NEED SPELLED TWICE: QTimer and QIntValidator had an include AND a forward declaration, four lines apart. That is the tell this block had stopped being read years before it stopped being true -- the class converted to pImpl (its banner below explains it is an ICoreLineEdit and not a QSpinBox, with every event handler and the validator behind the Impl), the members that needed these went with it, and the include block never moved

ICoreSpinBox#

ICoreSpinBox.h:47 · class · bases public ICoreLineEdit · pImpl · 22 declaration(s)

An integer field with up/down chevrons, wearing the app's own field styling.

class ICoreSpinBox : public ICoreLineEdit {
public:
    // Q1.2: one parent spelling. The QWidget* twin and the std::nullptr_t
    // delegate that broke their tie both went; with a single pointer
    // overload a literal nullptr is unambiguous and can carry the default.
    explicit ICoreSpinBox(ICoreNativeWidget* parent = nullptr);
    ~ICoreSpinBox() override;

    [[nodiscard]] int value() const;
    [[nodiscard]] int minimum() const;
    [[nodiscard]] int maximum() const;
    [[nodiscard]] int singleStep() const;

    // No sizeHint() of its own: the chevrons live inside the right text margin,
    // and QLineEdit's hint already counts that margin in.

    // Same names and semantics as QSpinBox's, so call sites read unchanged.
    // (Were `public slots:` while the metaobject existed; nothing connected to
    // them by name, so the label was cost without a consumer.)
    // Out-of-range values are clamped, and valueChanged is emitted whenever the
    // value actually moves -- typed, stepped or set from code.
    void setValue(int value);
    void setRange(int minimum, int maximum);
    void setMinimum(int minimum);
    void setMaximum(int maximum);
    void setSingleStep(int step);
    void stepBy(int steps);

    // The one notification this class emits. Was a Qt signal + this mirror;
    // the audit at the base's flip found zero Qt-signal subscribers, so the
    // mirror is the whole mechanism now.
    ICoreSignal<int> onValueChanged;

protected:
    // P5.11 prep: these override ICoreLineEdit's HOOK surface, not the Qt
    // handlers -- the base translates and forwards, so the flip never touches
    // this class again.
    void paintContent(ICorePainter& painter) override;
    bool mousePressed(const ICoreMouseEvent& event) override;
    bool mouseReleased(const ICoreMouseEvent& event) override;
    bool mouseMoved(const ICoreMouseEvent& event) override;
    bool mouseDoubleClicked(const ICoreMouseEvent& event) override;
    void pointerLeft() override;
    bool keyPressed(const ICoreKeyEvent& event) override;
    bool wheelScrolled(const ICoreWheelEvent& event) override;
    void focusGained(const ICoreFocusEvent& event) override;
    void focusLost(const ICoreFocusEvent& event) override;

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

ICoreSplitter.h#

ICoreEssentials/UI/Widgets/ICoreSplitter.h

ICoreSplitter#

ICoreSplitter.h:26 · class · final · bases public ICoreNativeWidget · pImpl · 19 declaration(s)

User-resizable panes.

class ICoreSplitter final : public ICoreNativeWidget {
public:
    explicit ICoreSplitter(ICoreNativeWidget* parent = nullptr);
    ICoreSplitter(ICoreOrientation orientation, ICoreNativeWidget* parent = nullptr);
    ~ICoreSplitter() override;

    // Non-copyable: this owns a live toolkit object with a parent-child
    // lifetime, and there is no meaningful second one.
    ICoreSplitter(const ICoreSplitter&) = delete;
    ICoreSplitter& operator=(const ICoreSplitter&) = delete;

    // --- pane sizing -------------------------------------------------------

    // Sizes cross the boundary as std::vector<int> rather than QList<int>.
    void setPaneSizes(const std::vector<int>& sizes);
    std::vector<int> paneSizes() const;

    // How the pane at `index` shares out space the splitter gains as it grows.
    // 0 means "keep your width"; the call site uses that to pin an explorer
    // pane while the editor beside it takes the slack.
    void setStretchFactor(int index, int stretch);

    // Whether a DRAG may take the pane at `index` all the way to nothing.
    //
    // ⚠ A MINIMUM ALONE DOES NOT STOP IT, WHICH IS THE WHOLE REASON THIS
    // FORWARDER EXISTS. A pane's minimum is what it may not be *resized* below;
    // collapsing is the separate move that skips past it, so a collapsible pane's
    // drag floor is zero however large its minimum is (ICoreSplitterCore.cpp's
    // lowerBoundOf). A navigation pane given a 180 px minimum and left
    // collapsible therefore still vanishes under the handle, which is what the
    // gallery's page list did.
    //
    // ⚠ true IS THE DEFAULT AND IS QT PARITY -- QSplitter::childrenCollapsible
    // is true and QSplitter::setCollapsible(int, bool) is this method, name for
    // name. Pass false for a pane that must stay reachable; its minimum then
    // becomes the floor a drag actually stops at.
    //
    // Out of range does nothing, the same answer setStretchFactor gives.
    void setPaneCollapsible(int index, bool collapsible);

    // Draw the handles only while the pointer is over one or drags it -- at
    // rest they are not drawn, and the panes' own grounds meet across them.
    // For a splitter whose first pane is a window's RAIL (A10.87, the Script
    // IDE's explorer): the stock handle, beside the rail's rim, read as a
    // second edge. The hit area is unchanged, so the handle is still found and
    // dragged where it always was.
    //
    // ⚠ IMPLEMENTED ON APPKIT ONLY, the one seat built and seen with it. GTK4,
    // WinUI, the web and UIKit accept the call and keep drawing their handle
    // at rest until each is taught it.
    void setHandleVisibleAtRest(bool visible);

    // --- children ----------------------------------------------------------

    // Append a pane. Takes ownership in the toolkit sense: the widget is
    // reparented to this splitter.
    void addWidget(ICoreNativeWidget* widget);

    // Insert a pane at `index`, shifting the rest right.
    void insertWidget(int index, ICoreNativeWidget* widget);

    // Take a pane out. The widget is NOT destroyed -- it is unparented and
    // handed back to whoever made it, which is the only thing a splitter can
    // honestly do with a widget it was merely given. Does nothing if the
    // widget is not one of its panes.
    //
    // Added when ICoreSplitPane::removePane() needed it, which is this
    // header's own rule for forwarders: never speculatively, always against a
    // call site. It is also what made that method implementable at all -- its
    // body had been commented out since it was written.
    void removeWidget(ICoreNativeWidget* widget);

    // How many panes the splitter holds.
    int paneCount() const;

    // --- appearance --------------------------------------------------------

    void setStyleSheet(const ICoreString& styleSheet);

    void setVisible(bool visible);
    void show();
    void hide();

    // --- signals -----------------------------------------------------------

    ICoreSignal<> onPaneResized;

    // --- boundary ----------------------------------------------------------

    ICoreNativeHandle nativeWidgetHandle() const override;

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

ICoreStackedWidget.h#

ICoreEssentials/UI/Widgets/ICoreStackedWidget.h

ICoreStackedWidget#

ICoreStackedWidget.h:14 · class · final · bases public ICoreNativeWidget · pImpl · 8 declaration(s)

One-visible-at-a-time pages.

class ICoreStackedWidget final : public ICoreNativeWidget {
public:
    explicit ICoreStackedWidget(ICoreNativeWidget* parent = nullptr);
    ~ICoreStackedWidget() override;

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

    // Insert a page at `index`. The page is reparented onto this stack.
    void insertPage(int index, ICoreNativeWidget* page);

    void setCurrentIndex(int index);
    [[nodiscard]] int currentIndex() const;

    ICoreSignal<int> onCurrentChanged;

    ICoreNativeHandle nativeWidgetHandle() const override;

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

ICoreTabBar.h#

ICoreEssentials/UI/Widgets/ICoreTabBar.h

ICoreTabBar#

ICoreTabBar.h:39 · class · bases public ICoreWidget · pImpl · 24 declaration(s)

A row of tabs -- one per open document -- painted by this widget on every backend, the way ICoreMenuBar paints its titles (S2.8).

class ICoreTabBar : public ICoreWidget {
public:
    explicit ICoreTabBar(ICoreNativeWidget* parent = nullptr);
    ~ICoreTabBar() override;

    // ---- the tabs ------------------------------------------------------------
    int  addTab(const ICoreString& title);                // answers its index
    int  insertTab(int index, const ICoreString& title);
    void removeTab(int index);                            // emits nothing
    void moveTab(int from, int to);                       // emits nothing
    [[nodiscard]] int count() const;

    void setTabTitle(int index, const ICoreString& title);
    [[nodiscard]] ICoreString tabTitle(int index) const;
    // The owner's handle for a tab (a file name, an id); tabs move, keys do not.
    void setTabKey(int index, const ICoreString& key);
    [[nodiscard]] ICoreString tabKey(int index) const;
    [[nodiscard]] int indexOfKey(const ICoreString& key) const;   // -1 if none

    void setTabDirty(int index, bool dirty);
    [[nodiscard]] bool isTabDirty(int index) const;

    // -1 when there are no tabs. Setting it scrolls it into view and emits
    // onCurrentChanged when it changed.
    void setCurrentIndex(int index);
    [[nodiscard]] int currentIndex() const;

    void setTabsClosable(bool closable);   // default true
    void setTabsDetachable(bool detachable);   // default false (tear-off, above)
    [[nodiscard]] bool tabsDetachable() const;
    // A tab being dragged is torn off while it is beyond this many pixels
    // above or below the strip.
    static constexpr int DETACH_DISTANCE = 28;

    // ---- geometry (widget coordinates) ------------------------------------------
    [[nodiscard]] ICoreRect tabRect(int index) const;     // empty when scrolled out
    [[nodiscard]] ICoreRect closeRect(int index) const;   // empty when not closable
    [[nodiscard]] int tabAt(const ICorePoint& point) const;   // -1 when on no tab
    [[nodiscard]] bool isOverflowing() const;             // the ‹ › buttons are shown
    [[nodiscard]] int firstVisibleTab() const;
    void scrollBy(int tabs);
    // The lifted copy that follows a dragged tab, on the screen: the tab's own
    // rectangle, without its shadow. Empty while no tab is dragged.
    [[nodiscard]] ICoreRect dragPreviewRect() const;

    static constexpr int BAR_HEIGHT = 30;

    // ---- what the user did ------------------------------------------------------
    ICoreSignal<int>      onCurrentChanged;     // new index
    ICoreSignal<int>      onCloseRequested;     // index of the tab to close
    ICoreSignal<int, int> onTabMoved;           // (from, to), already applied
    // (index, screen point of the release): a torn-off tab was dropped. The
    // tab is still in the strip; removing it is the owner's call.
    ICoreSignal<int, ICorePoint> onTabDetached;

protected:
    void paintContent(ICorePainter& painter) override;
    bool mousePressed(const ICoreMouseEvent& event) override;
    bool mouseMoved(const ICoreMouseEvent& event) override;
    bool mouseReleased(const ICoreMouseEvent& event) override;
    void mouseGestureCancelled() override;
    void pointerLeft() override;
    void resized(const ICoreSizeF& newSize, const ICoreSizeF& oldSize) override;

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

ICoreTable.h#

ICoreEssentials/UI/Widgets/ICoreTable.h

ICoreTable#

ICoreTable.h:66 · class · bases public ICoreWidget · pImpl · 29 declaration(s)

ICoreTable -- a header band over a scrolling body of widget-celled rows.

class ICoreTable : public ICoreWidget {
public:
    enum class SelectionMode {
        None,     // rows are inert chrome; what a row does is its cells' business
        Single
    };

    explicit ICoreTable(ICoreNativeWidget* parent = nullptr,
                        const std::vector<std::string>& columnsTitles = {});

    ~ICoreTable() override;

    // ---- columns ----------------------------------------------------------

    // Replaces the whole column set. Rows already added keep their cells, and a
    // row whose cell count no longer matches simply lays out the cells it has
    // against the columns that exist -- a table mid-reconfiguration is not a
    // table that should crash.
    void setColumnsTitles(const std::vector<std::string>& titles);
    [[nodiscard]] std::size_t getColumnsCount() const;

    // Relative shares of the body's width. They need not be normalised, whole,
    // or sum to anything in particular: `{0.35, 0.35, 0.15, 0.15}` and
    // `{35, 35, 15, 15}` are the same table. A share <= 0 gives that column
    // nothing; a set that is entirely <= 0 falls back to equal columns.
    //
    // ⚠ THE RESULTING WIDTHS TILE THE BODY EXACTLY, because the remainder is
    // spent rather than rounded away (icoreTableColumnWidths). There is no
    // last-column fudge to add, and none may be added: a splitter drag feeds
    // the current widths back in as the new ratios, so anything this function
    // adds is re-added on every pointer move of every drag.
    void setColumnsWidthRatios(const std::vector<double>& newRatios);
    [[nodiscard]] std::vector<double> getColumnsWidthRatios() const;

    // The widths those ratios currently resolve to, in whole pixels. Empty
    // until the table has been laid out once.
    [[nodiscard]] std::vector<int> getColumnsWidths() const;

    // How a column's TITLE is drawn in the header band. It does not reach the
    // cells: a cell is an arbitrary widget that fills its column, and a caller
    // that wants its content aligned aligns the widget (ICoreLabel::setAlignment
    // and friends) -- which is the only spelling that can work for a widget the
    // table has never heard of.
    void setColumnAlignment(std::size_t column, ICoreAlignment alignment);

    // Whether the boundaries between columns can be dragged. On by default.
    void setColumnsResizable(bool resizable);

    // How narrow a drag may make a column. 24 by default; a drag that would put
    // either neighbour below it is refused rather than clamped silently.
    void setMinimumColumnWidth(int width);

    // ---- rows -------------------------------------------------------------

    // Adds a row of cells, one per column, and takes ownership of every widget
    // passed. Answers nullptr -- and adds nothing -- when the count does not
    // match the column count.
    ICoreTableEntryRow* addRow(const std::vector<ICoreNativeWidget*>& entries);

    // The same, at a position. An index past the end appends.
    ICoreTableEntryRow* insertRow(std::size_t index,
                                  const std::vector<ICoreNativeWidget*>& entries);

    void deleteRow(ICoreTableEntryRow* entry);
    void clearAllRows();

    [[nodiscard]] std::size_t getRowsCount() const;
    [[nodiscard]] ICoreTableEntryRow* getRowAt(std::size_t index) const;

    // The row's position in insertion order, or -1. NOT its position on screen:
    // a filter ranks matches to the top of the body and leaves this untouched.
    [[nodiscard]] std::ptrdiff_t getRowIndex(const ICoreTableEntryRow* entry) const;

    // ---- filtering --------------------------------------------------------

    // Hides every row whose text does not contain the query, and ranks the rows
    // that DO match to the top of the body. An empty query restores insertion
    // order exactly. Case-insensitive; the query is trimmed.
    //
    // ⚠ WHAT COUNTS AS "THE ROW'S TEXT" IS WHAT THE TABLE CAN ASK FOR. Cells
    // are arbitrary widgets, so the table reads the ones that publish text
    // (ICoreLabel, ICoreLineEdit) and nothing else -- an ICoreComboBox publishes
    // no current-text accessor at all, and a composite cell's descendants are
    // not walked. A caller whose cells the table cannot read says so directly
    // with ICoreTableEntryRow::setFilterText(), which is matched alongside.
    void filterRows(const ICoreString& query);
    [[nodiscard]] ICoreString getActiveFilter() const;
    [[nodiscard]] int getVisibleRowsCount() const;

    // ---- metrics and chrome ------------------------------------------------

    void setTitleBarHeight(int newHeight);
    void setRowsHeight(int newHeight);

    // Space kept clear at each end of a cell, so text does not butt against the
    // grid line. 4 by default.
    void setCellPadding(int padding);

    // The hairlines between columns and under each row. On by default.
    void setGridVisible(bool visible);

    // Tints every other row, which is what makes a wide row scannable across.
    // Off by default.
    void setAlternatingRowColors(bool alternating);

    // ---- selection ---------------------------------------------------------

    // None by default, which is what the historical table did: rows were inert
    // and every click belonged to whatever cell widget was under it. Turning
    // this on does not take clicks away from those widgets -- a press consumed
    // by a cell's own button never reaches the row.
    void setSelectionMode(SelectionMode mode);
    [[nodiscard]] SelectionMode getSelectionMode() const;

    [[nodiscard]] ICoreTableEntryRow* getSelectedRow() const;
    void setSelectedRow(ICoreTableEntryRow* entry);
    void clearSelection();

    // ---- signals ------------------------------------------------------------

    ICoreSignal<ICoreTableEntryRow*> onRowClicked;
    ICoreSignal<ICoreTableEntryRow*> onRowDoubleClicked;

    // The selected row, or nullptr when the selection was cleared.
    ICoreSignal<ICoreTableEntryRow*> onSelectionChanged;

    // Fired once when a splitter drag ENDS, with the widths it settled on. Not
    // on every pointer move: a caller persisting column widths wants the
    // result, not the fifty intermediate frames.
    ICoreSignal<std::vector<double>> onColumnsResized;

protected:
    void resized(const ICoreSizeF& newSize, const ICoreSizeF& oldSize) override;

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

ICoreTableEntryRow.h#

ICoreEssentials/UI/Widgets/ICoreTableEntryRow.h

ICoreTableEntryRow#

ICoreTableEntryRow.h:50 · class · bases public ICoreWidget · pImpl · 13 declaration(s)

ICoreTableEntryRow -- one row of an ICoreTable: N cells, each holding one caller-supplied widget.

class ICoreTableEntryRow : public ICoreWidget {
public:
    // Takes ownership of every widget passed. The row is created detached; the
    // table parents it.
    explicit ICoreTableEntryRow(const std::vector<ICoreNativeWidget*>& entries);

    ~ICoreTableEntryRow() override;

    [[nodiscard]] std::size_t getCellsCount() const;

    // The widget the caller put in column `index`, or nullptr.
    [[nodiscard]] ICoreNativeWidget* getCellAt(std::size_t index) const;

    // Text the table's filter matches IN ADDITION to whatever it can read out
    // of the cells themselves.
    //
    // ⚠ THIS IS THE PUBLISHED ANSWER TO A REAL GAP, NOT A CONVENIENCE. The
    // filter asks each cell for its text through that cell's own accessor, and
    // a widget with no such accessor -- ICoreComboBox is the live case, and any
    // composite cell is another -- contributes nothing and can never be
    // searched. The alternative is a toolkit child-tree walk, which the wrapper
    // boundary exists to prevent. A row that knows what it represents can just
    // say so.
    void setFilterText(const ICoreString& text);
    [[nodiscard]] ICoreString getFilterText() const;

    [[nodiscard]] bool isSelected() const;

protected:
    void paintContent(ICorePainter& painter) override;
    void resized(const ICoreSizeF& newSize, const ICoreSizeF& oldSize) override;

    bool mousePressed(const ICoreMouseEvent& event) override;
    bool mouseDoubleClicked(const ICoreMouseEvent& event) override;
    void pointerEntered() override;
    void pointerLeft() override;

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

ICoreTableTitlesRow.h#

ICoreEssentials/UI/Widgets/ICoreTableTitlesRow.h

ICoreTableTitlesRow#

ICoreTableTitlesRow.h:54 · class · bases public ICoreWidget · pImpl · 9 declaration(s)

ICoreTableTitlesRow -- the header band of an ICoreTable.

class ICoreTableTitlesRow : public ICoreWidget {
public:
    explicit ICoreTableTitlesRow(ICoreTable* table);

    ~ICoreTableTitlesRow() override;

    // The cumulative column boundaries the band is currently drawing, size
    // N + 1 with edges[0] == 0. The table's copy is the authority; this is what
    // reached the header.
    [[nodiscard]] std::vector<int> getColumnEdges() const;

    // The interior boundary under the pointer (1..N-1), or -1. Also what is
    // being dragged, while a drag is in flight.
    [[nodiscard]] std::ptrdiff_t getHotSplitter() const;

protected:
    void paintContent(ICorePainter& painter) override;

    bool mousePressed(const ICoreMouseEvent& event) override;
    bool mouseMoved(const ICoreMouseEvent& event) override;
    bool mouseReleased(const ICoreMouseEvent& event) override;
    void pointerLeft() override;

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

ICoreTextEdit.h#

ICoreEssentials/UI/Widgets/ICoreTextEdit.h

ICoreTextEdit#

ICoreTextEdit.h:54 · class · bases public ICoreNativeWidget · pImpl · 32 declaration(s)

The multi-line sibling of ICoreLineEdit: same themed field background, same rounded border, same hover fade.

class ICoreTextEdit : public ICoreNativeWidget {
public:

    // ⚠ Takes ICoreFont, and deliberately does NOT re-export a QFont overload.
    // P7.6 converted ICoreFont, so it no longer converts to QFont and every
    // call site passing one needs a sink that speaks the wrapper.
    void setFont(const ICoreFont& font);

    // The Impl QPlainTextEdit -- out of line because the header only
    // forward-declares Impl. Was `this` while the Qt base existed.
    ICoreNativeHandle nativeWidgetHandle() const override;

    // Which of the field's own chrome this instance wears -- the same opt-in
    // ICoreLineEdit::Chrome describes, and for the same reason. Field is the
    // default and is everything above; None styles and sizes NOTHING, so the
    // widget is behaviourally a plain QPlainTextEdit that happens to sit under
    // this class instead of beside it.
    //
    // None is what the read-only and console views need: they are given their
    // whole appearance by the panel that owns them, and they must NOT inherit
    // the field's border, its padding or -- the one that would actually break
    // them -- setVisibleLines(3)'s fixed height, since a console sizes itself
    // from the layout it is stretched into.
    enum class Chrome { Field, None };

    // Q1.2: one parent spelling. The QWidget* twin and the std::nullptr_t
    // delegate that broke their tie both went; with a single pointer
    // overload a literal nullptr is unambiguous and can carry the default.
    explicit ICoreTextEdit(ICoreNativeWidget* parent = nullptr, Chrome chrome = Chrome::Field);

    [[nodiscard]] Chrome chrome() const;

    double borderOpacity() const;
    void  setBorderOpacity(double opacity);

    // Sizes the field to a whole number of text lines, border and padding
    // included. A layout otherwise has to guess a pixel height that happens to
    // land on a line boundary, which then breaks whenever the theme's font or
    // padding changes.
    void setVisibleLines(int lines);

    // ------------------------------------------------------------------
    // Streaming coloured output (the console case).
    //
    // These exist so a client outside the wrapper zone never has to name
    // QTextCursor / QTextCharFormat / QTextBlockFormat. The whole document
    // manipulation stays in the .cpp here, which is the only place that
    // knows the chunk-vs-line distinction below matters.
    // ------------------------------------------------------------------

    // Append `text` at the very end in `colour` and pin the view to the bottom.
    //
    // Inserts rather than appends a paragraph: output arrives in chunks that do
    // not line up with line boundaries, and appending each one would break every
    // chunk onto a line of its own.
    void appendColouredChunk(const ICoreString& text, const ICoreColor& colour);

    // Same, but also marks the block the chunk STARTS in as the beginning of a
    // section, so visibleSectionTops() reports it. The mark lives on the block
    // format, so it survives the trimming maximumBlockCount does.
    //
    // The document's first block is never marked: there is nothing above it to
    // be separated from.
    void appendColouredChunkStartingSection(const ICoreString& text, const ICoreColor& colour);

    // Viewport-relative y of the top of every section-start block whose top
    // falls at or above `bottomY` -- for an owner drawing a rule between
    // sections. Walks only the blocks on screen and stops at the first one past
    // `bottomY`, so a long scrollback costs nothing.
    [[nodiscard]] std::vector<double> visibleSectionTops(double bottomY) const;

    // The width a decoration drawn in paintContent has to work with. Not the
    // widget's: a QPlainTextEdit paints into its VIEWPORT, which is narrower
    // whenever the vertical scroll bar is up.
    [[nodiscard]] double viewportWidth() const;

    // ------------------------------------------------------------------
    // Facade forwarders the flip's harvest enumerated (same treatment as
    // ICoreLabel/ICoreLineEdit).
    // ------------------------------------------------------------------
    void setPlainText(const ICoreString& text);
    [[nodiscard]] ICoreString toPlainText() const;
    void insertPlainText(const ICoreString& text);
    void appendPlainText(const ICoreString& text);
    void appendHtml(const ICoreString& html);
    void clear();
    void setReadOnly(bool readOnly);

    // Q5.1. Distinct from setReadOnly: a disabled edit is greyed and takes no
    // focus, a read-only one still looks and selects normally. The Git panel
    // wants disabled, and used to reach it by unwrapping to QWidget.
    void setEnabled(bool enabled);
    void setPlaceholderText(const ICoreString& text);
    void setStyleSheet(const ICoreString& styleSheet);
    void setFixedHeight(int height);
    void setMinimumHeight(int height);
    void setMaximumBlockCount(int count);
    // Named operations instead of document() for the SCANNED-zone callers --
    // P2.10b-6b's lesson: the caller wants the margin and the emptiness test,
    // never the document.
    void setDocumentMargin(double margin);
    [[nodiscard]] bool isDocumentEmpty() const;
    void update();
    [[nodiscard]] ICoreFont font() const;

    // Unscoped on purpose so existing `ICoreTextEdit::NoWrap` call sites read
    // unchanged; pinned to the toolkit's values by static_asserts in the .cpp.
    enum LineWrapMode { NoWrap = 0, WidgetWidth = 1 };
    void setLineWrapMode(LineWrapMode mode);

    // ⚠ `QTextDocument* document() const` STOOD HERE AND IS DELETED, NOT MOVED
    // (A9.23, 2026-08-24). It was declared here, defined ONLY in the Qt seat,
    // and left deliberately UNDEFINED on AppKit and WinUI -- a Qt return type
    // is a link blocker where a parameter is not. Measured before deleting: it
    // has **zero** live callers in the whole tree; the `connect(document(), …)`
    // lines in the Qt seats are QPlainTextEdit's own member, not this one.
    // `documentHandle()` below is what it existed for, and it is defined on
    // every backend. A9.7's rule: deleted rather than relocated.

    // The same document as an opaque handle — what a language editor hands
    // its syntax highlighter's constructor (E6, STUDIO_QT_INDEPENDENCE.md §3).
    [[nodiscard]] ICoreTextDocumentHandle documentHandle() const;

    // Out of line: the unique_ptr member's deleter needs Impl complete, and
    // this header only forward-declares it.
    virtual ~ICoreTextEdit();

protected:
    // Called after the base class has painted, for owner-drawn decoration over
    // the viewport. `dirty` is the region being repainted, in viewport
    // coordinates. The painter is only valid for the duration of the call.
    virtual void paintContent(ICorePainter& painter, const ICoreRect& dirty);

    // H4.12 (header surface rule): `QPlainTextEdit* nativeTextEdit() const`
    // stood here, a protected non-virtual for wrapper-zone subclasses. It had
    // ZERO callers tree-wide -- declaration and definition only -- so it was
    // DELETED rather than published, the same call H4.24/H4.25 made for
    // ICoreWindow::nativeWindow and ICoreDialog::nativeDialog. A subclass that
    // genuinely needs the toolkit object again should take it back as a named
    // operation, not as a raw QPlainTextEdit*.

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

ICoreToggleButton.h#

ICoreEssentials/UI/Widgets/ICoreToggleButton.h

ICoreToggleButton#

ICoreToggleButton.h:32 · class · bases public ICoreWidget · pImpl · 12 declaration(s)

class ICoreToggleButton : public ICoreWidget {
public:
    // Mirror of the optionUpdated signal, for clients outside the wrapper zone.
    ICoreSignal<bool> onOptionUpdated;

    explicit ICoreToggleButton(ICoreNativeWidget* parent = nullptr);

    void setValueFromString(const std::string& stringValue);
    void onOptionUpdate();

    void setChecked(const bool& checked);

    bool isChecked() const;

    // 0 = knob fully left (off), 1 = knob fully right (on). The slide and the
    // colour are both derived from it, so they cannot disagree.
    [[nodiscard]] double knobPosition() const;
    void  setKnobPosition(double position);

    ~ICoreToggleButton() override;

protected:
    void paintContent(ICorePainter& painter) override;
    void pointerEntered() override;
    void pointerLeft() override;
    bool mousePressed(const ICoreMouseEvent& event) override;

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

ICoreTree.h#

ICoreEssentials/UI/Widgets/ICoreTree.h

ICoreTreeItem#

ICoreTree.h:43 · class · pImpl · 18 declaration(s)

The item-based tree (rows owned by the widget), wrapping QTreeWidget.

class ICoreTreeItem {
public:
    ICoreTreeItem();
    explicit ICoreTreeItem(const ICoreStringList& columnTexts);
    ~ICoreTreeItem();

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

    void setText(int column, const ICoreString& text);
    ICoreString text(int column) const;

    void setIcon(int column, const ICoreIcon& icon);

    void setForeground(int column, const ICoreColor& color);
    void setBackground(int column, const ICoreColor& color);

    void setToolTip(int column, const ICoreString& text);

    // Takes ownership of `child`'s toolkit half, exactly as ICoreTree's
    // addTopLevelRow does for a top-level row. Passing a row that is already in
    // a tree does nothing — it has no half left to give.
    void addChildRow(ICoreTreeItem* child);

    // ⚠ A caller-owned tag riding on the row, and it is a PLAIN MEMBER of this
    // wrapper rather than the Qt::UserRole data role ICoreListBoxItem uses.
    // That divergence is preserved deliberately, not overlooked: nothing reads
    // this tag through the model (the log delegate paints from the foreground
    // brush, not from a role), so moving it into a role would put a QVariant
    // round trip in front of a value the C++ side already owns. It also means
    // the tag does NOT survive into the toolkit item — which is correct, since
    // the toolkit item is not what callers hold.
    void setTag(const ICoreString& tag);
    ICoreString tag() const;

    // ⚠ All three may return nullptr — for a row this library did not create,
    // and for an out-of-range index. See the ownership note above.
    ICoreTreeItem* parentItem() const;
    ICoreTreeItem* childItem(int index) const;
    int childItemCount() const;

    bool isSelected() const;

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

ICoreTree#

ICoreTree.h:110 · class · final · bases public ICoreNativeWidget · pImpl · 36 declaration(s)

class ICoreTree final : public ICoreNativeWidget {
public:
    explicit ICoreTree(ICoreNativeWidget* parent = nullptr);

    // Out of line, and required rather than stylistic: both m_itemDelegate and
    // the Impl are unique_ptrs to forward-declared types, so their deleters
    // have to be instantiated where those types are complete.
    ~ICoreTree() override;

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

    void setHeaderLabels(const ICoreStringList& labels);

    // Takes ownership of `item`'s toolkit half.
    void addTopLevelRow(ICoreTreeItem* item);

    // ⚠ Both may return nullptr — see the ownership note above.
    ICoreTreeItem* topLevelRow(int index) const;
    ICoreTreeItem* currentRow() const;

    int topLevelRowCount() const;
    void clearRows();

    // ---------------- behaviour
    //
    // None of these carry a using-declaration any more. Before the conversion
    // setSelectionMode and setTextElideMode needed one to keep the toolkit
    // overload reachable; this class no longer has a Qt base for them to hide,
    // which is P6.3's observation arriving here too — the transitional name
    // disappears the moment the Qt base does.

    void setSelectionMode(ICoreSelectionMode mode);

    // Whether a click takes the whole row or the single cell under it.
    void setSelectsWholeRows(bool wholeRows);

    // ⚠ THIS TREE TAKES OWNERSHIP, WHICH THE TOOLKIT'S setItemDelegate DOES
    // NOT — hence the unique_ptr, which says so in the signature instead of in
    // a comment nobody reads at the call site. The divergence is deliberate and
    // it is the safe direction:
    //
    //   - Qt documents only that it does not take ownership. It says nothing
    //     about a delegate destroyed BEFORE its view, which is exactly what a
    //     caller-held member produces: a QWidget's children are destroyed by
    //     ~QWidget, after every member of the class holding them, so any
    //     panel-owned delegate necessarily dies while its tree is still alive.
    //   - Owning it here inverts that. The delegate is a member of this class,
    //     so it dies in ~ICoreTree, before the Impl it paints for.
    //
    // It also removes the stack hazard: a unique_ptr parameter cannot be handed
    // an automatic object. Replacing a delegate deletes the previous one.
    void setItemDelegate(std::unique_ptr<ICoreItemDelegate> delegate);

    void setFocusBehavior(ICoreFocusPolicy policy);

    // Horizontal scrolling by pixel rather than by column, so a long line
    // slides instead of jumping a column at a time.
    void setSmoothHorizontalScrolling(bool smooth);

    void setHorizontalScrollBarVisibility(ICoreScrollBarPolicy policy);

    // Whether the last column swallows the width the others leave over.
    void setStretchLastColumn(bool stretch);

    void setColumnResizeMode(ICoreColumnResize mode);
    void setTextElideMode(ICoreTextElide mode);

    // ---------------- appearance and layout of the view itself
    //
    // These arrived free from QTreeWidget before the conversion and are
    // hand-written forwarders now. They are here because the ONE consumer
    // (ICoreRunDiagnosisPanel) calls every one of them — the count came from
    // reading the call site, not from QTreeWidget's method list, which is the
    // sizing lesson P4.1 wrote down.
    void setHeaderHidden(bool hidden);
    void setRootDecorated(bool decorated);
    void setExpandAnimated(bool animated);
    void setIndentation(int pixels);
    void setWordWrapEnabled(bool enabled);
    void setStyleSheet(const ICoreString& styleSheet);

    void resizeColumnToContents(int column);
    int columnWidth(int column) const;
    void setColumnWidth(int column, int width);

    // ⚠ Replaces viewport()->width() at the call site. The viewport is a
    // toolkit-created child with no wrapper of its own, so handing it out was
    // never an option — the same resolution P1.8 reached when it retired
    // ICoreScrollPane::viewport() in favour of named questions.
    int viewportWidth() const;

    void selectAllRows();

    // ⚠⚠ THE READ HALF OF THE SELECTION, WHICH THIS CLASS DID NOT HAVE. There
    // was selectAllRows() and setSelectionMode(Extended) and no way to ask what
    // was selected -- so a view could be told to let the user pick a group and
    // could never be asked which group they picked. Every menu built for a
    // multi-selection needs this, which is why it arrives with the context-menu
    // signals below.
    //
    // In the order the rows were BUILT, not the order they were picked and not
    // the order they are drawn: a collapsed branch's selected child is still
    // selected and is still reported here, which is the same rule
    // ICoreItemViewCore::selectedRows() states for the layer underneath.
    //
    // ⚠ A row this library did not create is skipped rather than reported as
    // nullptr, so the vector never contains one -- the opposite convention from
    // the signals, and deliberate: a signal reports ONE row and has to be able
    // to say "not mine", while a list of selected rows with holes in it would
    // make every caller filter before using it.
    [[nodiscard]] std::vector<ICoreTreeItem*> selectedRows() const;
    void expandAllRows();
    void collapseAllRows();
    void collapseRow(ICoreTreeItem* item);
    void scrollToBottom();

    // Mirrors of the QTreeWidget signals client code subscribes to.
    //
    // ⚠ Each may deliver nullptr, for a row this library did not create. That
    // is new: before the conversion these carried an unchecked static_cast and
    // could not report "not mine", they could only be wrong about it.
    ICoreSignal<ICoreTreeItem*, int> onItemClicked;
    ICoreSignal<ICoreTreeItem*, int> onItemDoubleClicked;
    ICoreSignal<ICoreTreeItem*> onCurrentRowChanged;
    ICoreSignal<ICoreTreeItem*> onItemExpanded;
    ICoreSignal<ICoreTreeItem*> onItemCollapsed;

    // A right-click on the rows, carrying a SCREEN position -- which is what a
    // menu is popped up at, and what the press position on its own is not: the
    // press lands on the viewport, whose origin is not the view's.
    ICoreSignal<ICorePoint> onContextMenuRequested;

    // ⚠ THE SAME RIGHT-CLICK, NAMING THE CELL IT LANDED ON, AND BOTH FIRE.
    // This one carries everything the one above does and two things it cannot:
    // the row and the column. A subscriber wants one or the other, never both,
    // and which one is a question about the MENU rather than about the view --
    // "delete the selected rows" needs selectedRows() and a position, "copy
    // this cell" needs the cell. The older signal is kept rather than widened
    // because widening it would break every existing subscriber to fix none of
    // them.
    //
    // ⚠ THE SELECTION IS ALREADY CORRECT WHEN THIS FIRES, and that is the part
    // that makes a group menu possible at all. A right press inside an existing
    // multi-selection LEAVES IT ALONE; one outside it selects the row under the
    // pointer first, exactly as both toolkits do. So a handler may read
    // selectedRows() straight away and get what the user believes they picked
    // -- it does not have to reconstruct it from the row reported here.
    //
    // The row may be nullptr for a row this library did not create. The column
    // is -1 when the press did not land in one.
    ICoreSignal<ICoreTreeItem*, int, ICorePoint> onCellContextMenuRequested;

    // The scrolled area changed size. It is the viewport rather than the
    // widget because that is the width a column is sized against; the widget
    // also holds the header and the scroll bars, and one of those appearing is
    // itself a viewport resize.
    ICoreSignal<ICoreSizeF> onViewportResized;

    ICoreNativeHandle nativeWidgetHandle() const override;

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

ICoreTreeView.h#

ICoreEssentials/UI/Widgets/ICoreTreeView.h

The caller-owned data roles of an item view. A row carries its display text and icon in roles the toolkit owns; everything a call site attaches for its own use starts at User and counts up (User + 1, User + 2, ...).

The value is the toolkit's own first free role, and it MUST NOT move: roles are written into models by one class and read out by another (the subsystem entries are written by ICoreSubsystemTreeNode and read by ICoreNewTabPanel), so a shift here would not fail to compile -- it would silently read nothing. ICoreInputEnumsVerify.cpp pins it to the toolkit constant.

ICoreTreeRow#

ICoreTreeView.h:41 · class · pImpl · 11 declaration(s)

A row of a model-based tree, as an opaque handle.

class ICoreTreeRow {
public:
    // The opaque nested name only -- its definition is in the .cpp. Public so
    // the .cpp's buffer helpers can spell it, exactly as kNativeStorage* below
    // are public so they can be pinned (ICoreTextBlock's precedent).
    class Impl;

    ICoreTreeRow();
    ~ICoreTreeRow();

    // A row is a value: copied through tree walks and stored in containers.
    ICoreTreeRow(const ICoreTreeRow& other);
    ICoreTreeRow& operator=(const ICoreTreeRow& other);
    ICoreTreeRow(ICoreTreeRow&& other) noexcept;
    ICoreTreeRow& operator=(ICoreTreeRow&& other) noexcept;

    // Public only so the .cpp can pin them.
    static constexpr std::size_t kNativeStorageSize = sizeof(std::shared_ptr<void>);
    static constexpr std::size_t kNativeStorageAlign = alignof(std::shared_ptr<void>);

    bool isValid() const;
    ICoreTreeRow parent() const;

    // Two handles are the same row when they name the same row of the same
    // model -- what a call site comparing "is this the row I stored?" means.
    bool operator==(const ICoreTreeRow& other) const;
    bool operator!=(const ICoreTreeRow& other) const;

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

ICoreStandardItem#

ICoreTreeView.h:122 · class · pImpl · 17 declaration(s)

The row type for models this library fills.

class ICoreStandardItem {
public:
    ICoreStandardItem();
    explicit ICoreStandardItem(const ICoreString& text);
    ~ICoreStandardItem();

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

    void setLabel(const ICoreString& text);
    ICoreString label() const;

    void setIcon(const ICoreIcon& icon);

    // Whether the row can be renamed in place.
    void setEditable(bool editable);

    // Caller-owned data on the row, off ICoreItemRoles::User. Two overloads
    // rather than a QVariant: those are the only two kinds this tree writes
    // (an entry-type enumerator and a registry path), and they are what
    // ICoreTreeView::rowData reads back. Unambiguous -- an enumerator converts
    // to int and never to ICoreString.
    void setData(int value, int role);
    void setData(const ICoreString& value, int role);

    // Caller-owned tag riding on the row (the ICoreItemRoles::User idiom).
    void setTag(const ICoreString& tag);
    ICoreString tag() const;

    // Takes ownership of `child`'s toolkit half.
    void appendChildRow(ICoreStandardItem* child);

    // The multi-column form: `cells` is one whole child row, left to right, and
    // ownership of every one of them transfers. cells[0] is the row proper --
    // the cell that carries the expand arrow, owns the children, and is the one
    // ICoreTreeView's row handles resolve to (see rowAt below).
    //
    // ⚠ Put the icon and any caller-owned roles on cells[0] and nowhere else.
    // A row handle always names column 0, so rowData() reads that cell; a role
    // written onto cells[1] is stored, costs memory, and can never be read back
    // through this API.
    void appendChildRow(const std::vector<ICoreStandardItem*>& cells);

    // ⚠ Both may return nullptr, for a row this library did not create.
    ICoreStandardItem* childItem(int row) const;
    ICoreStandardItem* parentItem() const;

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

ICoreTreeView#

ICoreTreeView.h:190 · class · bases public ICoreNativeWidget · pImpl · 39 declaration(s)

The model-based tree.

class ICoreTreeView : public ICoreNativeWidget {
public:
    explicit ICoreTreeView(ICoreNativeWidget* parent = nullptr);
    ~ICoreTreeView() override;

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

    // Row activation/selection/clicks, delivered as a row handle. An invalid
    // handle means "no row" (the selection emptying, a click on empty space).
    ICoreSignal<ICoreTreeRow> onRowActivated;
    ICoreSignal<ICoreTreeRow> onCurrentRowChanged;
    ICoreSignal<ICoreTreeRow> onRowDoubleClicked;
    ICoreSignal<ICoreTreeRow> onRowClicked;

    // ---------------- the model
    //
    // ⚠ Returns the model this view was GIVEN, remembered. It is NOT a
    // dynamic_cast of the toolkit's model() and must not become one: P6.5's
    // finding, and now unavoidable -- after this conversion there is no
    // toolkit view for a call site to ask in the first place.
    void setModel(ICoreTreeViewModel* model);
    ICoreTreeViewModel* treeModel() const;

    // ---------------- rows
    //
    // The model's invisible root: the handle whose children are the top-level
    // rows. Invalid on purpose -- that is what "no parent" is.
    ICoreTreeRow rootRow() const;

    // The row under a point in the view's own coordinates. Invalid when the
    // point is not on a row.
    //
    // ⚠ ALWAYS COLUMN 0, whichever column the point actually fell in. A row
    // handle names a ROW -- the header says so, and operator== below compares
    // two handles on that basis -- so a click in a detail column has to resolve
    // to the same handle as a click on the name. Without that, rowData() would
    // read the clicked CELL: on a multi-column tree, double-clicking a date
    // column would find no entry-path role and do nothing, which reads as a
    // dead spot in the widget rather than as a bug. Every row handle this class
    // hands out is normalised the same way, including the four signals'.
    ICoreTreeRow rowAt(const ICorePoint& viewPos) const;

    // The same, for a point in screen coordinates -- which is what the
    // contextMenuRequested hook below is handed, and a context menu's first
    // question is always which row it was opened on.
    ICoreTreeRow rowAtGlobal(const ICorePoint& globalPos) const;

    int childRowCount(const ICoreTreeRow& row) const;
    ICoreTreeRow childRow(const ICoreTreeRow& row, int index) const;

    // The row's displayed text (the role with no name).
    ICoreString rowText(const ICoreTreeRow& row) const;

    // A caller-owned role off ICoreItemRoles::User, as text. Numbers written
    // into a role come back through ICoreString::toInt() -- the conversion is
    // the model's, so an int role round-trips.
    ICoreString rowData(const ICoreTreeRow& row, int role) const;

    // The row's position among its siblings.
    int rowNumber(const ICoreTreeRow& row) const;

    // The toolkit spells hiding as (row number, parent, hidden); a handle
    // already knows both halves. ⚠ No using-declaration any more -- there is
    // no Qt base left for a same-named member to hide, which is P6.3's
    // observation arriving here.
    void setRowHidden(const ICoreTreeRow& row, bool hidden);

    void expandRow(const ICoreTreeRow& row);
    void setCurrentRow(const ICoreTreeRow& row);
    void scrollToRow(const ICoreTreeRow& row);
    void collapseAllRows();

    // ---------------- behaviour and appearance
    //
    // Everything below arrived free from QTreeView before the conversion and is
    // a hand-written forwarder now. The list came from reading the one
    // subclass's constructor, not from QTreeView's method list -- P4.1's sizing
    // lesson, which P4.3 then had to re-learn.

    // How one column takes its width. The names are the toolkit's, but the
    // values are this class's own and are mapped by an explicit switch in the
    // .cpp -- nothing here is pinned to a toolkit constant.
    //
    //   Interactive      -- the user drags the header divider; setColumnWidth
    //                       gives the starting width.
    //   Stretch          -- shares the leftover width with the other Stretch
    //                       columns. This is how a tree's name column takes the
    //                       slack while its detail columns stay put.
    //   ResizeToContents -- as wide as its widest cell, and not draggable.
    //   Fixed            -- exactly setColumnWidth's width, never anything else.
    enum class ColumnSizing { Interactive, Stretch, ResizeToContents, Fixed };

    // ⚠ BOTH ONLY BITE ONCE THE MODEL HAS COLUMNS. The header has no sections
    // to size before setModel() -- calling either first is silently ignored, not
    // an error -- so a caller that rebuilds its model must re-apply them after
    // every setModel().
    void setColumnWidth(int column, int width);
    void setColumnSizing(int column, ColumnSizing sizing);

    // What a column is CURRENTLY laid out at, which is not the same as what it
    // was last set to: a stretch section, or one the user has dragged, answers
    // its real width. Zero for a column that does not exist yet, which is also
    // what a header with no sections answers -- callers remembering a width
    // across a model rebuild have to treat 0 as "nothing to remember".
    [[nodiscard]] int columnWidth(int column) const;

    // Whether the rightmost column absorbs leftover width. The toolkit's default
    // is ON, which fights any Stretch column to its left; a tree that sizes its
    // own columns wants it off.
    void setStretchLastColumn(bool stretch);

    // Whether a row can be renamed in place. Off means no edit trigger at all.
    void setEditingEnabled(bool enabled);

    void setSelectionMode(ICoreSelectionMode mode);

    void setStyleSheet(const ICoreString& styleSheet);
    void resize(int width, int height);
    void setIconSize(const ICoreSizeF& size);
    void setHeaderHidden(bool hidden);
    void setExpandAnimated(bool animated);
    void setMouseTracking(bool enabled);
    void setUniformRowHeights(bool uniform);

    // The height of every row, in logical points. 0 or less restores the seat's
    // own default.
    //
    // ⚠ IT IS NOT setIconSize()'s JOB, AND THAT IS WHY THIS EXISTS. A tree with
    // a 28-point icon in a 22-point row draws the icon clipped top and bottom,
    // and every seat's row height is a CONSTANT it was built with. The two are
    // genuinely separate choices -- a dense tree of small icons and a roomy tree
    // of small icons are both things a caller asks for -- so the row height is
    // stated rather than inferred from the icon.
    void setRowHeight(double height);

    // Tints every other row, which is what makes a deep tree scannable down.
    // Off by default.
    //
    // ⚠ THE TWO COLOURS ARE THE THEME'S, NOT THE CALLER'S. This is an on/off
    // switch over the active style spec's `alternateBackground`; a view whose
    // spec leaves that invalid does not stripe however this is set. That is the
    // same division ICoreTable::setAlternatingRowColors() draws, and it is what
    // keeps the two colours related across a theme switch.
    void setAlternatingRowColors(bool alternating);

    void setAcceptDrops(bool accept);
    void setDropIndicatorShown(bool shown);

    ICoreNativeHandle nativeWidgetHandle() const override;

protected:
    // ------------------------------------------------------------------
    // The hook surface, mirroring ICoreWidget's. Both return "handled?":
    // true consumes the event, false forwards it to the base implementation,
    // so a subclass that overrides neither behaves exactly as it did before
    // the hooks existed.
    //
    // ⚠ The Qt handlers are GONE from this header -- they live on the Impl
    // now, so "kept overridable for the wrapper zone's own components" is no
    // longer true and no longer possible. Nothing used that route; a future
    // component needing one has to grow a hook, which is the same capability
    // P4.2 recorded losing for ICoreItemDelegate::initStyleOption.
    // ------------------------------------------------------------------
    virtual bool mouseDoubleClicked(const ICoreMouseEvent& event);
    virtual bool contextMenuRequested(const ICorePoint& globalPos);

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

ICoreTreeViewModel.h#

ICoreEssentials/UI/Widgets/ICoreTreeViewModel.h

ICoreTreeViewModel#

ICoreTreeViewModel.h:24 · class · final · bases public ICoreNativeObject · pImpl · 14 declaration(s)

The rows behind an ICoreTreeView, wrapping QStandardItemModel.

class ICoreTreeViewModel final : public ICoreNativeObject {
public:
    // ⚠ The parent is an ICoreNativeWidget, not an ICoreNativeObject, and that
    // is the "widget as object parent" decision this task existed to make.
    // Every model in the tree is parented to the VIEW that shows it -- a
    // widget -- and nothing parents one to a bare object. Taking the widget
    // interface and resolving it through icoreNativeObjectOfWidget() once,
    // inside, means no call site writes that step. Taking ICoreNativeObject*
    // instead would push an unwrap onto the one call site and buy nothing.
    explicit ICoreTreeViewModel(ICoreNativeWidget* parent = nullptr);
    ~ICoreTreeViewModel() override;

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

    // Empties the model. ⚠ This is the ONLY spelling -- the pre-conversion
    // class had resetToInitialState() AND an inherited clear(), which were the
    // same operation under two names, and the two call sites used one each
    // (§9: a second name for one thing is the P0.5 mistake).
    void resetToInitialState();

    // Takes the subsystem tree's root row. The item is owned by the model from
    // here, which is what QStandardItemModel::appendRow already meant.
    void appendRootRow(ICoreStandardItem* item);

    // The multi-column form -- one whole root row, left to right, all of it
    // owned by the model from here. cells[0] is the row proper; see
    // ICoreStandardItem::appendChildRow's note on where roles belong.
    void appendRootRow(const std::vector<ICoreStandardItem*>& cells);

    // The column titles, which also SET THE COLUMN COUNT. An empty list leaves
    // the model single-column.
    //
    // ⚠ resetToInitialState() clears these along with the rows, so a model that
    // is emptied and refilled has to be given them again.
    void setColumnHeaders(const std::vector<ICoreString>& labels);

    int rowCount() const;

    // Drops the parent link so the pool can outlive the view. Called by
    // ICoreStudioGarbageCollection before a model is recycled.
    void detachFromParent();

    void kill();
    void setAlive();
    bool isAlive() const;

    ICoreNativeHandle nativeObjectHandle() const override;

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

ICoreWidget.h#

ICoreEssentials/UI/Widgets/ICoreWidget.h

The ENUM header, not ICoreSurfaceGradient.h: that one's return type would drag <QLinearGradient> into all ~150 headers that name this class.

ICoreWidget#

ICoreWidget.h:97 · class · bases public ICoreNativeWidget · pImpl · 146 declaration(s)

The base every StudioObjects widget derives from (task C1).

class ICoreWidget : public ICoreNativeWidget {
public:

    // ⚠ Takes ICoreFont, and deliberately does NOT re-export the inherited
    // QWidget::setFont(const QFont&) with a `using`. P7.6 converted ICoreFont, so
    // it no longer converts to QFont and every call site passing one needs a
    // sink that speaks the wrapper. A `using` here would reopen exactly the
    // re-export hole §9 records neither guard being able to see, since the Qt
    // name would appear only at the call site. Hiding the base overload is
    // safe: QWidget::setFont is not virtual, and a caller still holding a raw
    // QFont converts through ICoreFont's implicit inbound constructor.
    void setFont(const ICoreFont& font);

    // The Impl QWidget -- out of line because the header only forward-declares
    // Impl. Was `this` while the Qt base existed.
    ICoreNativeHandle nativeWidgetHandle() const override;

    // Parent this widget to an already-converted wrapper.
    //
    // Deliberately a named setter and NOT a constructor overload. While this
    // class still derives from BOTH QWidget and ICoreNativeWidget, an
    // `ICoreWidget(ICoreNativeWidget*)` constructor would be ambiguous against
    // `ICoreWidget(QWidget*)` for every one of the ~96 call sites that passes an
    // ICoreWidget* as its parent -- both are derived-to-base pointer
    // conversions, so neither wins. A distinct name has no such problem. Once
    // this class is itself converted and QWidget is gone from the bases, the
    // ambiguity goes with it and this can fold back into the constructor.
    void setParentWidget(ICoreNativeWidget* parent);

    enum class Surface {
        None,
        Panel
    };

    explicit ICoreWidget(ICoreNativeWidget* parent = nullptr);
    ICoreWidget(Surface surface, ICoreNativeWidget* parent = nullptr);

    // ⚠ Q1.1 DELETED THE std::nullptr_t DELEGATE that used to sit here, and
    // that is the point rather than a side effect. It existed ONLY to break the
    // tie between the QWidget* and ICoreNativeWidget* overloads for a literal
    // `nullptr`; with one pointer overload left there is no tie, and the
    // defaults above are safe for the same reason. Do not re-add a QWidget*
    // overload without bringing the delegate back with it.

    // QWidget's (parent, flags) constructor. C1 needs it because a widget that
    // is its own top-level window passes Qt::Window here and has no other way
    // to say so -- ICoreDetachedPanelWindow is the one such widget today.
    // No default on `flags` on purpose: one would make ICoreWidget(parent)
    // ambiguous against the single-argument constructor above.
    //
    // Q1.1 swapped the parent, Q3.2 the flags. ICoreWindowFlags is Q0.1's
    // `unsigned int` flag set (ICoreMouseButtons' shape), unwrapped to
    // Qt::WindowFlags once at the Impl seam in the .cpp.
    ICoreWidget(ICoreNativeWidget* parent, ICoreWindowFlags flags);

    // Out of line: the unique_ptr<Impl> needs the complete type there. The
    // release-guard pairing with ~Impl is documented at the Impl's destructor.
    ~ICoreWidget() override;

    Surface surface() const;

    // Switching to Panel late is allowed; it applies the attributes and the
    // parent filter at that point, exactly as the constructor would have.
    void setSurface(Surface newSurface);

    // Panel-only appearance. Harmless in Surface::None -- nothing reads them.
    void setFillColor(const ICoreColor& newFillColor);

    // Spread the fill colour as ICoreSurfaceGradient's ramp -- lit at the corner
    // `direction` names, deepening toward the opposite one -- instead of one
    // flat wash. Off by default, and it is the SPREAD that changes: the fill
    // colour still names the surface, so a panel that already follows the theme
    // through setFillColor keeps following it with nothing else to subscribe to.
    //
    // The ramp is rebuilt from the body's bounds on every paint rather than
    // cached, which is what makes it survive a resize -- a stored gradient
    // carries absolute coordinates and would smear the moment the window
    // changed height. See ICoreSurfaceGradient.h for why it is sampled, and
    // ICoreGradientDirection.h for why a diagonal's angle follows the body's
    // aspect rather than holding 45 degrees.
    //
    // The two strengths scale how far each end of the ramp travels from the
    // fill colour, and they are separate so the lit end can be brought down
    // without the deep end coming up with it. A short body wants less of both,
    // since the same ramp across it is a far steeper slope. ICoreToolBar
    // carries the same four for its own body.
    void setGradientFill(bool gradient,
                         ICoreGradientDirection direction = ICoreGradientDirection::TopToBottom,
                         double litStrength = 1.0,
                         double deepStrength = 1.0);

    // A translucent panel that is OPAQUE ALONG ITS TOP EDGE: the body's alpha
    // runs from 255 at the top down to the fill colour's own alpha at the
    // bottom, linearly. The colour -- flat, or setGradientFill()'s spread -- and
    // the outline are unchanged. For a frosted panel butting a solid strip
    // above it: the two meet at one opaque colour, so there is no seam, and the
    // blur shows through lower down. Off by default; an opaque fill is
    // unaffected.
    //
    // ⚠ ONLY APPKIT PAINTS IT, the one seat where a panel is translucent over a
    // blur (setBackdropBlur). The others accept the call and paint the body as
    // before.
    void setFillOpaqueAtTop(bool on);

    // The same treatment for the OUTLINE: borderColor() spread along the axis
    // `direction` names instead of drawn flat. Off by default, and like the
    // fill it changes only the spread -- borderColor() still names the colour,
    // so an override that follows the theme keeps following it.
    //
    // Meant to be given the direction the fill already runs, which is what
    // makes the body and its outline read as one lit surface rather than as a
    // gradient inside a flat frame. The ramp is border-tuned rather than the
    // fill's (ICoreSurfaceGradient::forBorder) because the same travel that
    // reads as material across a panel is invisible along a 2px line.
    void setGradientBorder(bool gradient,
                           ICoreGradientDirection direction = ICoreGradientDirection::TopToBottom,
                           double litStrength = 1.0,
                           double deepStrength = 1.0);

    // A widget that is its own top-level. Replaces passing toolkit window
    // flags to the constructor; FloatingCard also declines focus and shows
    // without activating, which is what a hover card needs.
    ICoreWidget(ICoreWindowNature nature, ICoreNativeWidget* parent = nullptr);

    // Turn an already-constructed widget into its own top-level window, frame
    // and all. The ICoreWindowNature constructor above is the way to say this
    // at construction; this is for the widgets that decide it in their own
    // body, after the base is built.
    void becomeTopLevelWindow();

    // A caption bar of this top level's OWN -- what ICoreWindow::setTitleBar()
    // is for a window, for a widget promoted with becomeTopLevelWindow()
    // (A10.87). An explicit bar means ICoreTitleBarProvider is never asked for
    // this one, as on a window.
    //
    // ⚠ ONLY APPKIT AND WINUI SEAT A BAR ON A PROMOTED WIDGET (A10.82; WinUI
    // since 2026-09-26, holding the bar until its first show builds the
    // window), so ask first: hasTopLevelTitleBarSeat() is false on every other
    // backend and on a widget
    // that is not a top level, and setTopLevelTitleBar() then returns false and
    // does NOT take `bar` -- the caller still owns it. A caller that moves
    // controls INTO its bar asks before it moves them, so a backend without a
    // seat keeps them where they were.
    [[nodiscard]] bool hasTopLevelTitleBarSeat() const;
    bool setTopLevelTitleBar(ICoreTitleBar* bar);

    // The usable area of THIS widget's screen -- the one it is currently on,
    // excluding the OS bars. Not the primary screen's: a window dragged onto a
    // second monitor is placed against the monitor it is on, and reading the
    // primary one is how a card lands off-screen on a multi-monitor desk.
    ICoreRect screenAvailableGeometry() const;

    // Whether this widget will take keyboard focus at all.
    void setFocusable(bool focusable);

    // Let whatever is behind the widget show through where it does not paint.
    void setTranslucentBackground(bool translucent);

    // Blur whatever this widget sits over inside its window -- a frosted-glass
    // ground under the widget's own painting, rounded to `cornerRadius`. The
    // widget still paints its body on top, so a body that should look frosted
    // paints it TRANSLUCENT; an opaque body hides the blur completely.
    //
    // On a `Surface::Panel` the radius is ignored: the blur takes the panel's
    // own outline -- its bare edges, corner radius and top fillets -- and
    // follows it when those change (A10.80).
    //
    // ⚠ ONLY APPKIT BLURS (A10.79). There it is an NSVisualEffectView kept as a
    // sibling directly under the widget, following its frame, visibility and
    // z-order. GTK4 has no backdrop blur for one widget over another, and the
    // WinUI seat does not wire its in-app acrylic yet, so both accept the call
    // and draw nothing: the translucent body alone is what those seats show.
    void setBackdropBlur(bool on, double cornerRadius = 0.0);

    // Whether setBackdropBlur draws anything on this platform -- true on
    // AppKit, false on GTK4 and WinUI. A widget that would paint its body
    // translucent only so a blur shows through asks this first, and stays
    // opaque where there is no blur to show (owner ruling 2026-09-23, A10.81).
    [[nodiscard]] static bool backdropBlurSupported();

    // Fill the whole widget with one flat colour, opaquely. For the widgets that
    // ARE a block of colour -- rules, dividers, spacers -- rather than something
    // with a colour.
    //
    // Not a stylesheet: a stylesheet background here would be overridden by any
    // rule an ancestor sets for its child widgets, which is exactly what a
    // one-off divider inside a themed panel runs into. This paints through the
    // widget's own palette instead, which nothing else contends for. Independent
    // of Surface -- it works in None, where m_fillColor is not read.
    void setSolidBackground(const ICoreColor& color);

    // The solid ground's corner radius, in logical pixels. 0 -- the default --
    // is the square fill described above and is unchanged by this method's
    // existence.
    //
    // ⚠ A ROUNDED GROUND CANNOT COME FROM A PALETTE FILL, which is exactly what
    // the paragraph above says the square one uses and why it says "not a
    // stylesheet". A palette paints the widget's whole rectangle; there is no
    // corner in it to round. So a NON-ZERO radius necessarily changes HOW the
    // ground is painted, and each backend answers that differently -- one asks
    // its style engine to draw the panel, the other draws a rounded path. That
    // is stated here rather than left to the seats, because a caller reading
    // only the paragraph above would reasonably expect the palette guarantee to
    // survive a radius, and it cannot.
    //
    // ⚠ IT EXISTS BECAUSE SEVEN COMPOSITES NEEDED IT, not on spec. `Panels/`
    // and `SettingsPanel/` write "a panel-coloured ground with a small radius"
    // by hand as a scoped style sheet, once each; measured over the twelve,
    // seven do it and none of them declares a paint hook it could draw in.
    // A missing capability goes INTO the wrapper rather than around it, and
    // around it would have been a second painting path per composite.
    void setSolidBackgroundRadius(double radius);

    // The corner radius of a `Surface::Panel`'s rounded body, in logical
    // pixels. 15 -- the default, and the number every seat painted before this
    // existed -- unless a panel says otherwise; 0 is square. Unrelated to the
    // solid ground above, which a Panel does not paint through.
    //
    // ⚠ IT EXISTS FOR A PANEL THAT STOPPED FLOATING (A10.75/A10.76): the left
    // rail sits flush against the window's edges, and a rounded body there
    // shows the window behind its corners.
    void setPanelCornerRadius(double radius);

    // Which edges of a `Surface::Panel` carry its border. All four -- the
    // default -- is the rounded outline every seat has always drawn. With any
    // edge bare, the same outline is painted running PAST that edge and clipped
    // to the widget (UI/Portable/ICorePanelEdges.h): the fill reaches the
    // widget's own edge there, a corner touching a bare edge comes out square,
    // and the corners between two stroked edges keep their radius.
    //
    // It exists for the two panels that stopped floating free (A10.77): the
    // left rail keeps only its right edge, the stroke that divides it from the
    // canvas; the top bar bares only its top, where it meets the title bar.
    void setPanelBorderEdges(bool left, bool top, bool right, bool bottom);

    // Flares a `Surface::Panel`'s two top corners OUTWARD into whatever it sits
    // under -- concave fillets of the panel radius instead of corners. Drawn
    // only while the top edge is bare (setPanelBorderEdges). The fillets are
    // painted inside the widget: the body is pulled in by one radius on the
    // left and right, and the caller's layout must leave that strip free
    // (UI/Portable/ICorePanelEdges.h).
    //
    // It exists for the top bar, which meets the title bar (A10.78).
    void setPanelTopFillets(bool on);

    // This widget's own area, origin at (0,0) -- what a paintContent() hook
    // draws into. The ICore spelling of rect(); paint hooks need it constantly
    // and ICoreRect's QRect constructor is explicit, so without this every hook
    // writes the conversion out by hand.
    [[nodiscard]] ICoreRect bounds() const;

    // This widget's coordinates <-> screen coordinates. The pair a popup needs
    // to place itself under a control that lives in a different parent chain.
    [[nodiscard]] ICorePoint mapToScreen(const ICorePoint& local) const;
    [[nodiscard]] ICorePoint mapFromScreen(const ICorePoint& screen) const;

    // This widget's coordinates -> DESKTOP coordinates: the space
    // ICoreCursor::pos() answers in and ICoreWindow::move() takes, and the only
    // one in which two widgets in two DIFFERENT windows can be compared.
    //
    // ⚠ mapToScreen() IS NOT THAT ON EVERY SEAT, which is why this exists
    // (`W10.123`). On WinUI it stops at the window's client area, so a question
    // that spans windows -- "which window's tab bar is the pointer over?" --
    // answered with it compares a point from one window against rectangles in
    // another's space, and is right only for a window parked at the desktop
    // origin. AppKit's mapToScreen already is desktop space and this equals it.
    // gtk4 answers mapToScreen() too, because Wayland publishes no desktop
    // space at all; a cross-window question there is answerable per window only.
    [[nodiscard]] ICorePoint mapToDesktop(const ICorePoint& local) const;

    // ---------------------------------------------------------------------
    // THE PARENT TRIO (row A1.3 of the AppKit port board, closing three of its
    // section 0.28's six members).
    //
    // These replace `sizeOf(parentWidget())`, `mapFromScreenOf(parentWidget())`
    // and `parentWidget()->width()`. They exist because the members they
    // replace handed out the toolkit's parent object, and a backend zone may
    // not name a toolkit it does not own (R2.4) -- so those members could not
    // be DEFINED outside Backends/Qt/ at all, and ICoreWidget was unfinishable
    // on any second backend.
    //
    // ⚠ THE FIX IS NOT TO NARROW THE RETURN TYPE, and 0.28's own recommendation
    // to do that is what this supersedes. Narrowing window()/parentWidget() to
    // ICoreNativeWidget* does not work: that interface is a ONE-MEMBER handle
    // (nativeWidgetHandle()) with no size, no geometry and no mapping on it, so
    // every caller would have to convert back -- and on the Qt backend a plain
    // QWidget ancestor does not implement it either, which is the header's own
    // counter-argument at the window() note below and it is correct.
    //
    // The rule applied instead is this tree's own, from QT_PARAM_SWAP.md's
    // window() group: WHEN A WRAPPER CANNOT HAND OUT AN ACCESSOR, LOOK FOR THE
    // PREDICATE THE CALLERS ACTUALLY WANTED. Measured over all six call sites
    // before writing these: not one wanted the parent object. Five asked it for
    // a size or a coordinate mapping and one null-checked it. So the parent is
    // never handed out, the counter-argument never has to be answered, and
    // these three are definable on any backend because nothing in their
    // signatures names a toolkit.
    //
    // "Parent" here is the TOOLKIT parent -- whatever is up the real parent
    // chain, converted or not -- which is what the replaced members reported.
    //
    // ⚠⚠ "CONVERTED OR NOT" MEANS WRAPPED OR NOT. IT DOES NOT MEAN "WIDGET OR
    // WINDOW", AND A RIG READ IT THAT WAY FOR FOUR MONTHS (L9.46, ruled
    // 2026-08-31, Linux backend plan §L140). The clause is answering the
    // paragraph directly above it -- a plain toolkit ancestor with no
    // ICoreWidget wrapper still counts as the parent, which is why narrowing
    // the return to ICoreNativeWidget* could not work. It says nothing about
    // windows.
    //
    // ⚠⚠ A HOSTING WINDOW IS NOT A PARENT. This trio walks the ICoreWidget
    // chain, and on every native backend ICoreWindow is an ICoreNativeWidget
    // and NOT an ICoreWidget -- so a widget installed with
    // ICoreWindow::setCentralWidget() is the TOP of its own chain and reports
    // no parent, the same as an orphan. That is the "ONE STRUCTURAL DIFFERENCE
    // FROM Qt" each native seat records: on Qt a widget MAY BE a window, so
    // QMainWindow::setCentralWidget() left the content with a real QWidget
    // parent and this same call answered the window's size. **The two answers
    // are both correct and they differ by backend, because the QUESTION differs
    // by backend.** A caller that wants the box it is laid out inside when that
    // box may be a window must ask hostWindow(), not this.
    // ---------------------------------------------------------------------

    // False for a top level or an orphan -- and a widget hosted directly in an
    // ICoreWindow is a top level, per the note above. The null-check the two
    // popup-placing call sites did as `if (!parentWidget())` before asking for
    // a mapping.
    [[nodiscard]] bool hasParentWidget() const;

    // The parent's on-screen size. An edge-anchored panel sizes itself against
    // this.
    //
    // ⚠ WITH NO PARENT THIS IS A DEFAULT ICoreSizeF, WHICH IS (-1, -1) AND NOT
    // (0, 0) -- the toolkit's "invalid size" sentinel, kept verbatim by
    // ICoreSizeF on purpose. That is not a wart to round off: on a backend
    // where a live view legitimately starts 0x0 (the AppKit port board, §0.45), a
    // zero would make "there is no parent" indistinguishable from "the parent
    // has no size yet", and those want different handling. Guard with
    // hasParentWidget() rather than testing the size.
    //
    // ⚠ THE SENTENCE HERE READ "-- every call site does", AND IT WAS NOT TRUE
    // (measured under L9.46, 2026-08-31). Two of the three parentSize() call
    // sites guard (ICoreLeftFixedPanelMenuPanel.cpp,
    // ICoreRightFixedPanelMenuPanel.cpp) -- as does the trio's fourth caller,
    // ICoreLauncherActionRail.cpp:179, which guards before
    // mapFromScreenToParent();
    // ICoreGlobalSearchDialog.cpp's `parentSize().width() - PANEL_WIDTH - 12`
    // does not. It is benign TODAY for two reasons that are both accidents --
    // its std::max(12.0, maxX) absorbs the negative, and that dialog is always
    // constructed with a widget parent -- so it is a live instruction, not a
    // description of the tree.
    [[nodiscard]] ICoreSizeF parentSize() const;

    // A screen point in the PARENT's coordinates -- "place me under that, in
    // the coordinate system I am laid out in". Returns the point unchanged when
    // there is no parent, which is the top-level case where parent coordinates
    // ARE screen coordinates.
    [[nodiscard]] ICorePoint mapFromScreenToParent(const ICorePoint& screen) const;

    // The widget's own palette background. The counterpart to
    // setSolidBackground -- NOT whatever a style sheet paints, which the
    // palette never learns about.
    [[nodiscard]] ICoreColor backgroundColor() const;

    // Let a style sheet's background/border actually paint on this widget.
    // A plain widget ignores both unless this is on; a widget that dresses
    // itself through the theme's style sheets needs it.
    void setStyledBackground(bool styled);

    // Q1.1: ICoreSizeF per Q0.2's decision (no integer ICoreSize). The member
    // stays a QSize and the setter rounds once -- every call site passes
    // integer literals, which are exact in a double.
    void setFilterCoefficient(const ICoreSizeF& coefficient);

    // How this widget wants its layout to treat its size hint, per axis.
    void setSizeBehavior(ICoreSizeBehavior horizontal, ICoreSizeBehavior vertical);

    // Watch `target` for pointer presses and report them to the
    // watchedPointerPressed hook below. The press still reaches `target`
    // untouched -- this only observes, which is what the panels that dismiss
    // a search or a popup on "user clicked over there" need.
    //
    // ⚠ ICoreNativeWidget* since Q5.1, and the history is worth keeping because
    // the type has now moved TWICE for the same underlying reason. P5.1 widened
    // it from ICoreWidget* to ICoreAnyWidget* (= QWidget*) because the dispatch
    // was a dynamic_cast to ICoreWidget* and a target that was not one -- an
    // ICoreButton, then a QPushButton subclass -- was silently unwatchable.
    // Post-conversion that argument is spent: ICoreButton and every other
    // wrapper implement ICoreNativeWidget, so the interface accepts everything
    // the QWidget* spelling did among wrappers, and every watcher in the tree
    // passes a wrapper. What it no longer accepts is a BARE toolkit widget that
    // no wrapper owns — nothing asks for that, and a site that needs it should
    // say so rather than have the whole surface stay Qt-typed for it.
    //
    // The win is at the other end: the hooks below now hand back the SAME
    // wrapper pointer the caller registered, so an overrider compares
    // `watched == closeButton` instead of unwrapping both sides.
    void watchPointerPressesOf(ICoreNativeWidget* target);

    // Watch `target` for resizes and report them to watchedResized below.
    void watchResizesOf(ICoreNativeWidget* target);

    // Watch `target` for KEY PRESSES and report them to watchedKeyPressed
    // below, which returns true to swallow the key.
    //
    // The fourth watch, added at P5.8 for the code editors: the editing surface
    // is an ICoreTextEdit, which is a QPlainTextEdit wrapper and so has none of
    // this class's hooks, and the base class that wants to reinterpret Tab is
    // not the widget the key is delivered to. Without this the only mechanism
    // was an eventFilter override -- which is precisely what stops being called
    // when ICoreWidget converts (P5.10) and is why P5.9 gates on zero of them.
    void watchKeyPressesOf(ICoreNativeWidget* target);

    // Watch `target` for pointer enter/leave and report them to
    // watchedPointerEntered/Left below. The counterpart of the press watch, for
    // a widget that must highlight while the pointer is over something else --
    // a row reacting to its own action button, a tooltip owner tracking its
    // anchor.
    void watchPointerCrossingsOf(ICoreNativeWidget* target);

    // Report this widget's PARENT changing size, to parentResized() below.
    //
    // Surface::Panel already watches its parent, but for its own purpose -- it
    // resizes itself to the parent minus a margin -- and that behaviour is
    // unchanged and stays automatic. This is the opt-in for a Surface::None
    // widget that needs to know without inheriting the snapping.
    void watchParentResizes(bool watch);

    // Report pointer MOVES anywhere in the application, to
    // applicationPointerMoved() below. The move twin of
    // watchOutsidePointerPresses, and it carries the same warning: an
    // application-wide filter sees every move in the app, so turn it off with
    // the gesture that needed it rather than leaving it installed.
    void watchApplicationPointerMoves(bool watch);

    // Watch for pointer presses ANYWHERE in the application and report the ones
    // that landed outside this widget to pointerPressedOutside() below.
    //
    // Distinct from watchPointerPressesOf, which needs a specific target. A
    // popup dismissing itself on "the user clicked away" has no such target:
    // the click can land on any widget in any window, including ones that did
    // not exist when the popup opened. Turn it OFF when the popup hides -- an
    // application-wide filter left installed sees every press in the app.
    void watchOutsidePointerPresses(bool watch);

    // Presses inside `widget` count as inside this one, so they do NOT reach
    // pointerPressedOutside(). For a popup whose trigger control sits outside
    // its own subtree -- a results panel under a search bar has to survive a
    // click on the bar that is driving it.
    void exemptFromOutsidePresses(ICoreNativeWidget* widget);

    // Let pointer events fall through to whatever is behind this widget --
    // for decorative children that must not steal their parent's hover.
    void setPointerTransparent(bool transparent);

    // Deliver enter/leave even when the widget is not tracking the mouse.
    void setHoverTracking(bool tracking);

    // Whether the style paints this widget's background at all. False leaves
    // whatever is behind it showing through wherever the widget does not paint.
    void setSystemBackground(bool paintSystemBackground);

    // Promise (or retract the promise) that paintContent covers every pixel,
    // which lets the toolkit skip erasing the area first. False is the safe
    // side for a widget with transparent regions.
    void setOpaquePaint(bool opaque);

    // Q5.1 group 3. A NAMED facade rather than a generic setAttribute, which
    // §9 refuses: this class publishes one method per attribute it actually
    // uses (setTranslucentBackground, setOpaquePaint, setPointerTransparent),
    // so a Studio caller never spells `Qt::WA_*`. Delete-on-close was the one
    // attribute with two callers and no facade.
    void setDeleteOnClose(bool deleteOnClose);

    // Q5.1. "Is the top-level I live in the one the OS has focused?"
    //
    // A PREDICATE rather than an accessor, and that is the whole point:
    // ICoreWindow.h refuses to hand back window() because the result is a raw
    // top-level with no wrapper to return. Every caller that reached for it was
    // asking this question and then comparing -- so the comparison moves in
    // here, where the raw pointer never escapes, and the refusal stands.
    [[nodiscard]] bool isInActiveWindow() const;

    // Q5.1. The ICoreWindow shell this widget lives inside, or nullptr if its
    // top-level is not one. Callers used to spell this
    // `icoreWindowOfNative(w->window())`, which needed BOTH the refused
    // window() accessor and an impl-side header at a non-sanctioned call
    // site. The raw top-level stays inside.
    [[nodiscard]] ICoreWindow* hostWindow() const;

    // setFocus with the reason spelled out. The reason is not cosmetic: a
    // field focused by Mouse does not select its contents, one focused by Tab
    // does, so the no-argument form would quietly change behaviour.
    void takeFocus(ICoreFocusReason reason = ICoreFocusReason::Mouse);

    // ------------------------------------------------------------------
    // Facade (P5.1). Everything below arrives from QWidget today and would
    // vanish at P5.10, so each one is declared here now -- while the Qt base
    // is still present and the forwarder is provably a no-op -- rather than
    // during the conversion, when a missing one is a compile error in ~96
    // subclasses at once.
    //
    // ⚠ setObjectName and setStyleSheet are the two that MUST be here: §2 R1
    // records that ICoreWidget has no QSS type selector of its own, so nothing
    // about the conversion looks like it touches styling -- and yet four #id
    // sheets go dark the moment either forwarder is missing, with no compile
    // error and no guard hit. They take ICoreString, which converts implicitly
    // from both a literal and a QString, so no call site changes.
    // ------------------------------------------------------------------

    void setObjectName(const ICoreString& name);
    void setStyleSheet(const ICoreString& sheet);

    // The pointer shape over this widget. ICoreCursorShape, NOT a new
    // ICorePointerShape -- P0.5 settled that this enum already exists and is
    // the vocabulary the tree speaks (§9).
    void setCursorShape(ICoreCursorShape shape);
    void clearCursorShape();

    // This widget's top-left within its parent. NOT covered by bounds(), which
    // is deliberately origin-relative because paint hooks want it that way.
    [[nodiscard]] ICorePoint position() const;

    // ⚠ Spelled requestRepaint(), not update(), because ICoreGraphicsView
    // already publishes requestRepaint() for exactly this operation
    // (ICoreGraphicsView.h:121). §9's rule, and the fifth time this check has
    // cancelled a second name after P0.5, P7.3, P3.4 and P2.7.
    void requestRepaint();

    // Repaint only `area` (widget coordinates). For a surface where redrawing
    // everything on every change is the cost worth avoiding -- the terminal
    // grid repaints the handful of lines the emulator marked dirty rather than
    // the whole screen, which is what keeps a fast writer from pinning the GUI
    // thread (TERMINAL_EMULATOR.md T3.2).
    //
    // An empty or invalid area is a no-op, not a full repaint: a caller that
    // computed "nothing changed" means it.
    void requestRepaint(const ICoreRect& area);

    // Take every keystroke, ahead of the application's shortcuts.
    //
    // A widget that IS a keyboard surface -- a terminal, a code editor's raw
    // mode -- needs the keys the application has bound at window scope:
    // Ctrl+C, Ctrl+V, Ctrl+Z, Tab. Window shortcuts are dispatched BEFORE the
    // focused widget's key handler, so without this those keys never arrive
    // and the widget silently ignores them, with nothing logged.
    //
    // Off by default. Turn it on only for a widget that genuinely consumes
    // raw keys, and expect the application's own shortcuts to stop working
    // while it has focus -- that is the point.
    void setClaimsKeyboard(bool claims);

    // Run `task` after `delayMs`, guarded by THIS WIDGET's lifetime: if the
    // widget dies first, the task never runs. The widget tier's twin of
    // ICoreGraphicsObject::deferToEventLoop (P2.9d-5c), replacing every
    // QMetaObject::invokeMethod(this, ...) -- those pass `this` as the QObject
    // context for exactly that guard, and dropping the context compiles and
    // then dangles. delayMs == 0 means "next event-loop turn". Safe to call
    // from a non-GUI thread: the task is marshalled to the GUI thread either
    // way, which is what the solver-thread callers in ICoreTimeLine rely on.
    // At the flip this body moves its context to the Impl; call sites stay.
    void deferToEventLoop(int delayMs, std::function<void()> task);

    // ---- P5.10 batch 1: the inherited geometry/visibility surface ----------
    //
    // These 22 are the operations subclasses reach through the QWidget base
    // today. MEASURED, not guessed: the probe (remove the Qt base in an
    // isolated export, -ferror-limit=0, compile the 182 TUs that can see
    // ICoreWidget or any of its 145 subclasses) reported 5 420 diagnostics with
    // ZERO "too many errors emitted", and these account for ~285 of the 495
    // no-member call sites.
    //
    // ⚠ ADDITIVE ON PURPOSE, AND THAT IS THE WHOLE POINT. Each one forwards to
    // QWidget:: today and becomes impl->… on the day the base is removed, so
    // the call sites never move. Landing them here shrinks the atomic step to
    // the base removal plus the constructor overloads, instead of one commit
    // that changes 495 call sites and the class shape together.
    //
    // ⚠ Only operations with NO existing ICore name are here. `update`,
    // `setSizePolicy`, `setFocus`, `setMouseTracking`, `setCursor`,
    // `mapToGlobal`, `mapFromGlobal`, `rect`, `pos`, `setAutoFillBackground`
    // and `setFocusPolicy` are all deliberately ABSENT: each already has a
    // facade twin (requestRepaint, setSizeBehavior, takeFocus, setHoverTracking,
    // setCursorShape, mapToScreen, mapFromScreen, bounds, position,
    // setStyledBackground, setFocusable) and adding the Qt spelling beside it
    // would be a second name for one thing -- §9's rule, which has now
    // cancelled a duplicate at P0.5, P7.3, P3.4, P2.7 and here. Those call
    // sites migrate to the existing name instead.
    //
    // ⚠ Absent for a different reason: setParent, setLayout, deleteLater,
    // findChild/findChildren, setGraphicsEffect/graphicsEffect. Each is an
    // OWNERSHIP or QObject-tree operation whose meaning changes when the wrapper
    // stops being a QObject -- deleteLater would delete the Impl and leave the
    // wrapper, and a findChildren tree walk stops seeing wrapper types at all.
    // They are decisions, not forwarders, and belong to the atomic step.

    void setFixedHeight(int height);
    void setFixedWidth(int width);
    void setFixedSize(int width, int height);
    void setMinimumWidth(int width);
    void setMinimumHeight(int height);
    void setMaximumWidth(int width);
    void setMaximumHeight(int height);
    [[nodiscard]] int maximumHeight() const;
    [[nodiscard]] int minimumHeight() const;
    [[nodiscard]] bool hasFocus() const;

    // The layout manages the Impl's children -- every child widget's QWidget
    // parent unwraps to the Impl -- so attaching it to the Impl is not a
    // relocation, it is where the layout always effectively lived. The
    // batch-1 list called setLayout an ownership decision; this is it,
    // decided: the Impl owns the layout, exactly as the QWidget base did.
    void setLayout(ICoreNativeLayout* layout);

    // The widget tier's deleteLater(), under a name that survives the flip --
    // the graphics tier's destroyDeferred() (P2.9d-5c), same contract: deletes
    // the WRAPPER (running the whole subclass destructor chain; the unique_ptr
    // takes the Impl with it), deferred a turn so it is safe from inside the
    // widget's own signal emissions. Never deleteLater on the Impl: that
    // deletes the toolkit object out from under the wrapper.
    void destroyDeferred();

    // Whether the pointer is currently over this widget.
    [[nodiscard]] bool underMouse() const;

    // Paint this widget (and children) into `target` -- the drag-preview path.
    // Q1.1: an ICorePixmap, not a bare paint device. Its one consumer is the
    // window-snapshot path, and icoreQt(ICorePixmap&) already has the MUTABLE
    // overload P7.4 added for exactly this write-into-the-storage case.
    void render(ICorePixmap& target);

    // The ICoreWindowNature constructor's switch, callable after construction
    // -- for the widgets that decide their window nature in their own body.
    // Supersedes ad-hoc setWindowFlags calls, which went with the Qt base.
    void adoptWindowNature(ICoreWindowNature nature);

    // ⚠⚠ "THIS POPUP BELONGS OVER THE WINDOW `anchor` IS IN", AND IT IS THE
    // ONLY THING A PARENTLESS POPUP CAN SAY (W10.18).
    //
    // A popup normally answers the question by being SOMEWHERE: a menu built
    // inside a menu bar is already in the right window's tree, and a backend
    // that promotes it to an overlay walks up from it. Every CONTEXT menu in
    // this tree is deliberately parentless -- `new ICoreMenu()`, so that no
    // ancestor's styling reaches it -- so it has no chain to walk and no anchor
    // to fall back on, and a backend is left guessing from a roster of live
    // windows.
    //
    // ⚠ THE GUESS HAS NOW BEEN MEASURED WRONG THREE TIMES, EACH TIME
    // DIFFERENTLY: a 900x700 chart window built per Scope block and never shown
    // (W10.10), a 642x320 window under the project switcher, and a 642x417 one
    // under the canvas's own context menu -- which clamped a menu summoned at
    // (1101, 488) to (392, 0) and opened it in the corner of the editor.
    // 📌 *A heuristic that is wrong in three different ways is not a heuristic
    // that needs a fourth tie-breaker; it is a question the caller can answer
    // and was never asked.*
    //
    // ⚠ IT DOES **NOT** PARENT, AND THAT IS THE WHOLE POINT OF HAVING IT.
    // `setParentWidget` adopts -- it moves the widget into the anchor's element
    // tree and, on the backends where a parent owns its children, hands it a
    // second owner. A context menu is a member of the control that opens it and
    // must not acquire one. This records a pointer that is COMPARED against the
    // live window roster and dereferenced only to walk upward from.
    //
    // Safe with null (clears it) and with an anchor that later dies: a stale
    // pointer matches no live window and the backend falls back exactly as it
    // does today. A backend whose popups are real top-level windows implements
    // this as a no-op and says so there -- it has nothing to be promoted into.
    void declareOverlayAnchor(ICoreNativeWidget* anchor);
    void resize(int width, int height);
    void adjustSize();
    void updateGeometry();
    void setContentsMargins(int left, int top, int right, int bottom);

    void move(int x, int y);
    void setGeometry(int x, int y, int width, int height);
    void setGeometry(const ICoreRect& rect);
    [[nodiscard]] int y() const;

    // ⚠ THESE TWO REVERSE A DECISION THIS FILE PREVIOUSLY RECORDED THE OTHER
    // WAY, and the reversal is the owner's, made 2026-08-12 with the trade in
    // front of them -- not an oversight and not a session quietly relitigating
    // §9. The note below about size() still stands; this is narrower than it.
    //
    // The board called the 115 `width()`/`height()` call sites "mechanical
    // migrations" onto bounds().width(). They are not, and the reason is a type
    // change: QWidget::width() returns int, ICoreRect::width() returns double
    // (ICoreRect.h:77 -- four plain doubles). The VALUE is identical, so most
    // sites would not care; but ~10 of them divide, and integer division
    // silently becomes floating-point division. ICoreRotatableArrowIcon.cpp:40
    // is the clearest -- ICorePoint(width() / 2, height() / 4) truncates today
    // and would not afterwards, moving the arrow half a pixel on odd sizes.
    // It compiles either way, so nothing would have reported it.
    //
    // Weighed against §9's "no second spelling" rule, the deciding fact is that
    // these are NOT a second spelling of bounds().width(): they differ in type
    // and in precision, which is exactly what the 10 dividing sites turn on.
    // Keeping them int leaves all 115 call sites untouched, which makes this
    // batch provably behaviour-neutral instead of 115 judgement calls.
    //
    // ⚠ Each declaration HIDES the inherited member of the same name, so the
    // bodies MUST be written QWidget:: qualified or they recurse -- the same
    // trap that already caught resize(int,int), move(int,int) and font().
    [[nodiscard]] int width() const;
    [[nodiscard]] int height() const;

    void show();
    void hide();
    void setVisible(bool visible);

    // ⚠⚠ CLIP THIS WIDGET'S CHILDREN TO ITS OWN FRAME (W10.37). Off by default,
    // because on two of the three backends a container does NOT clip and a
    // child that overflows is simply drawn over its neighbours -- which is what
    // several widgets in this tree rely on (a popup parented to a row, a focus
    // ring drawn a pixel outside the control).
    //
    // ⚠ IT IS THE OTHER HALF OF AN ANIMATED FRAME. A panel whose layout is
    // suspended while it slides (ICoreLayoutSeatCore::setResizeSuspended) keeps
    // content sized for the width it will FINISH at, so without this the
    // content hangs out of the panel for the length of the slide. The two are
    // meant to be used together and separately mean little.
    //
    // All three seats already clip at the element level; this is the portable
    // way to ask for it, and it is a no-op where the element is not up yet.
    void setClipsChildren(bool clips);
    [[nodiscard]] bool isVisible() const;
    [[nodiscard]] bool isHidden() const;
    void raise();
    void activateWindow();

    void setEnabled(bool enabled);

    // ⚠ NOT setHoverTracking, and the two must never be folded together.
    // setHoverTracking sets Qt::WA_Hover, which asks the toolkit for
    // HoverEnter/HoverMove/HoverLeave. THIS asks for mouseMoveEvent to arrive
    // while NO button is held, which is what feeds the mouseMoved() hook on a
    // widget that tracks the pointer without a drag. Five classes rely on it
    // (ICoreMenu, ICoreMenuBar's titles, ICoreComboBox, the table splitter and
    // the global search dialog) and none of them also sets WA_Hover — so
    // "migrating" these to setHoverTracking would have compiled perfectly and
    // silently stopped their move handling. It looked like a second name for
    // one thing and is not; that is why it gets a forwarder rather than a
    // migration.
    void setMouseTracking(bool tracking);

    // ⚠ NOT setStyledBackground either. That one sets WA_StyledBackground, so
    // a stylesheet's background rule is honoured. THIS is Qt's
    // autoFillBackground, which fills with the palette's window role before
    // paintEvent runs. Same shape of near-miss as setMouseTracking above.
    void setAutoFillBackground(bool autoFill);

    // ---- P5.10 batch 2c: the rest of the inherited surface -----------------
    //
    // Re-measured at HEAD feb24cfd after batches 1/2/2b: the inherited bill
    // fell from 495 sites over 58 members to 150 over 28. These are the ones
    // with no facade twin to migrate to. Same contract as batch 1 -- each
    // forwards to QWidget:: today and becomes impl->... at the conversion.
    //
    // ✅ window() IS GONE, AND WITH IT THE LAST OF THE SIX MEMBERS THAT MADE
    // THIS CLASS UNFINISHABLE ON A SECOND BACKEND (row A1.3; the AppKit port
    // board's §0.28). It returned ICoreAnyWidget* -- the raw toolkit top level
    // -- and could not be DEFINED outside the Qt zone at all.
    //
    // ⚠ IT DID NOT GO THE WAY THE OTHER FIVE DID, AND THE DIFFERENCE IS WORTH
    // KEEPING. Those were one question wearing five spellings, so one predicate
    // replaced them. window()'s callers really did ask different things -- show
    // and raise, reparent, compare identity, adapt for a parent -- and the
    // answer was FOUR separate members, three of which already existed and were
    // simply not being used:
    //
    //   raiseHostWindow()            show + raise + activate, in that order
    //   icoreSameWindow(a, b)        already existed, on BOTH backends
    //   hostWindow()                 already existed; returns the ICoreWindow
    //   hostWindowAsNativeWidget()   the top level as a parent or an anchor
    //
    // The lesson the row recorded: "five callers ask five different questions"
    // is a statement about the QUESTIONS, not about the work. Two of the five
    // were already answered elsewhere in this tree and nobody had looked.
    // Bring this widget's TOP-LEVEL to the front -- show it, raise it, and make
    // it the active window, in that order. The predicate form of the only
    // call site that ever wanted window() for its own sake
    // (ICoreStudioSurface::sendParentWindowToFront), and it is here rather than
    // there because "which window am I in" is the wrapper's question to answer.
    //
    // ⚠ ORDER IS PART OF THE CONTRACT, not an implementation detail: raising a
    // hidden window shows nothing, and activating before raising leaves a
    // window focused but behind another. A caller cannot get the order right
    // from outside without holding the top level, which is the thing this
    // exists to stop it holding.
    //
    // A no-op when the widget is not in a window yet. That is the honest answer
    // rather than an error: the same call on Qt would reach the widget itself
    // (QWidget::window() returns `this` for a top level) and on AppKit would
    // reach nothing, and "bring nothing to the front" is what both mean.
    // const because it acts on the WINDOW, not on this widget -- the same
    // reason hostWindow() and isInActiveWindow() beside it are const. Without
    // it the one caller (a const member itself) needs a const_cast, which would
    // be the signature complaining rather than the caller being wrong.
    void raiseHostWindow() const;

    // THIS WIDGET'S TOP-LEVEL, AS SOMETHING YOU CAN PASS AS A PARENT OR AN
    // ANCHOR -- the portable form of the last thing window() was used for.
    //
    // Both remaining window() call sites did the same two-step: take the raw
    // top level, wrap it in an ICoreForeignWidget on the spot, hand the wrapper
    // to something that takes ICoreNativeWidget*. That adapter's constructor
    // names QWidget, so the whole two-step is Qt-only; this member is the step
    // done once, inside, where each backend can spell it its own way.
    //
    // ⚠ IT IS NEVER NULL, AND IT IS `this` FOR A TOP LEVEL -- matching
    // QWidget::window(), which answers `this` rather than nullptr for a widget
    // that IS its own top level. A caller asking "which window am I in" while
    // being one gets itself, which is the useful answer for parenting.
    //
    // ⚠⚠ OWNED BY THIS WIDGET AND RE-POINTED ON EVERY CALL. The returned
    // adapter lives as long as this widget does and no longer, and a second
    // call may move it to a different top level (this widget having been
    // reparented). So: USE IT AND DROP IT. Do NOT store it. The header's own
    // note on ICoreForeignWidget says the same thing about the hand-built form
    // and gives the rule for checking -- before passing one to a new signature,
    // grep the callee's body to confirm it UNWRAPS rather than RETAINS. Both
    // call sites here unwrap at construction.
    [[nodiscard]] ICoreNativeWidget* hostWindowAsNativeWidget() const;

    // Q5.1: the wrapper-taking form. It was the twin of an
    // ICoreAnyWidget-taking overload; that overload had ZERO production callers
    // tree-wide -- measured at HEAD, the one call that looks like it
    // (ICoreGlobalSearchDialog.cpp, section->isAncestorOf(rows[next])) passes an
    // ICoreGlobalSearchResultRow*, which is an ICoreWidget and binds HERE --
    // so A1.3 deleted it rather than seating a member nothing called.
    [[nodiscard]] bool isAncestorOf(const ICoreWidget* child) const;

    // Returns Qt's "was it actually closed" answer; four of the nine call sites
    // read it.
    bool close();

    void setWindowTitle(const ICoreString& title);
    void setToolTip(const ICoreString& text);

    // Opts this widget in to drops: dragEntered(), dragMoved() and dropped()
    // are delivered from now on, with the payload's text, file URLs and custom
    // format (ED8.2). On AppKit a drag over a descendant that
    // registered for no drag types is offered to its nearest registered
    // ancestor -- AppKit's own rule, not this class's. ⚠ AppKit only so far: on
    // the other seats this changes nothing and acceptDrops() stays false.
    void setAcceptDrops(bool accept);
    [[nodiscard]] bool acceptDrops() const;

    // The ratio the widget's screen is drawing at -- one caller, sizing a
    // pixmap. Named without the Qt suffix: QWidget has devicePixelRatio() (int,
    // deprecated) and devicePixelRatioF() (qreal), and only the second is
    // meaningful, so carrying the F across would preserve a distinction the
    // wrapper does not have.
    [[nodiscard]] double devicePixelRatio() const;

    [[nodiscard]] ICoreFont font() const;

    // Route Qt's two size questions through preferredSize/minimumPreferredSize,
    // falling back to the toolkit's answer when the hook declines. A subclass
    // that already overrides sizeHint() directly still wins -- these are
    // ordinary virtuals and it is further down the chain. That is why landing
    // this is additive even though wrapper-zone classes (ICoreMenu,
    // ICoreComboBox, and P5.6's three sites) still answer the Qt way.
    //
    // ⚠ PUBLIC, and it has to be. Outside code asks OTHER widgets how big they
    // want to be -- ICoreGlobalSearchDialog sums its sections' hints to size
    // itself. Declaring these down in the protected hook block, which is where
    // they first went, does not just move a declaration: back when a QWidget
    // base supplied them it NARROWED the access of an inherited public member
    // and the whole tree stopped being able to ask. The base is gone and the
    // hazard with it, but the reason to keep them public is unchanged.
    //
    // ⚠ ICoreSizeF, NOT QSize. These three were the
    // last Qt-typed returns on this class, and a return type is worse than a
    // parameter: a backend outside the Qt zone may not NAME QSize, so it could
    // not define them at all -- the wrapper would compile and never link. The
    // sentinel is unchanged and is the reason ICoreSizeF fits: preferredSize()
    // already answers "no opinion" with an INVALID ICoreSizeF, so hint and hook
    // now speak one type. A caller wanting the toolkit spelling writes
    // .toQSize(), which is what the two SDK call sites' .height() reads did
    // implicitly.
    //
    // The toolkit's own answer, bypassing preferredSize -- for the wrapper
    // zone's fallback arithmetic (ICoreComboBox sizes off it).
    [[nodiscard]] ICoreSizeF nativeSizeHint() const;

    [[nodiscard]] ICoreSizeF sizeHint() const;
    [[nodiscard]] ICoreSizeF minimumSizeHint() const;

    // ⚠ NO size() ACCESSOR, deliberately, though the P5.1 gap list asks for
    // one: bounds() already answers it. Its width()/height() ARE this widget's
    // size, and adding size() would leave two spellings of one value for the
    // next reader to choose between -- the trade §9 rejects. A caller wanting
    // an ICoreSizeF writes ICoreSizeF(bounds().width(), bounds().height()),
    // and no call site in the tree wants one today.

protected:
    // ------------------------------------------------------------------
    // The hook surface (task: Qt boundary). Subclasses OUTSIDE the wrapper
    // zone override these instead of the Qt handlers -- they cannot even
    // spell the Qt signatures without tripping tools/check_qt_boundary.sh.
    // Every hook receives ICore types only. The bool-returning hooks answer
    // "handled?": true consumes the event, false forwards it to the Qt base
    // class implementation, so an unhandled event behaves exactly as before.
    // ------------------------------------------------------------------

    // Called on every repaint, after the surface (None: nothing, Panel: the
    // rounded body) has been drawn. The painter is only valid for the call.
    virtual void paintContent(ICorePainter& painter);

    // The same call, plus the region Qt actually asked to be repainted. For a
    // widget whose paint is expensive enough to want clipping -- a long list,
    // a chart grid -- where redrawing everything on a two-pixel damage rect is
    // the cost worth avoiding.
    //
    // ⚠ These are ONE hook with two shapes, not two hooks: paintEvent calls
    // ONLY this one, and its default body calls the one-argument form above.
    // Override whichever you want; overriding both means the one-argument one
    // is never reached, because this default is what was calling it. That
    // arrangement is what keeps all 43 existing paintContent overriders working
    // untouched, which an additive task cannot do any other way.
    virtual void paintContent(ICorePainter& painter, const ICoreRect& dirty);

    // What this widget would like to be, and the smallest it can usefully be.
    //
    // ⚠ The default returns an INVALID ICoreSizeF, and that is the arming
    // mechanism rather than an oversight -- see ICoreSizeF::isValid(). Invalid
    // means "no opinion", and the toolkit's own sizeHint answers instead; a
    // widget that genuinely wants to collapse returns a valid 0x0, which is why
    // the test is isValid() and not isEmpty(). Value-returning virtuals need
    // this where the void handlers need nothing: there is no "did not handle it"
    // to return, so the sentinel has to be in the value.
    virtual ICoreSizeF preferredSize() const;
    virtual ICoreSizeF minimumPreferredSize() const;

    virtual bool mousePressed(const ICoreMouseEvent& event);
    virtual bool mouseReleased(const ICoreMouseEvent& event);
    virtual bool mouseMoved(const ICoreMouseEvent& event);
    virtual bool mouseDoubleClicked(const ICoreMouseEvent& event);

    // The gesture that began with mousePressed() has ENDED WITHOUT A RELEASE:
    // the capture was taken away, the pointer was abandoned by the input stack,
    // the window lost activation, or the widget was hidden or disabled mid-drag.
    //
    // ⚠⚠ ANY WIDGET THAT KEEPS STATE FOR THE DURATION OF A DRAG MUST CLEAR IT
    // HERE AS WELL AS IN mouseReleased(), AND W10.69 IS WHAT HAPPENS OTHERWISE.
    // These four hooks were the whole mouse surface, so "the drag is over" could
    // only be heard as a release -- and a cancelled gesture produces none. The
    // tab headers bar lit a "receiving" ground while a header was dragged over
    // it and cleared it in mouseReleased(); dragging a tab raises a floating
    // window, raising it takes activation, the capture goes with it, and the
    // release never came. The bar stayed lit for the rest of the session.
    //
    // ⚠ NOT A RELEASE AT AN UNKNOWN POSITION. It carries no event because there
    // is no meaningful position: acting on one would make a dragged item drop
    // where the pointer happened to be when the gesture died. Undo the drag,
    // do not complete it.
    //
    // ⚠ NOT pointerLeft() EITHER. A leave fires on ordinary hover changes, so a
    // widget that cleared its drag state there would abandon a drag that merely
    // wandered outside its own bounds -- which most drags do.
    //
    // Default: nothing, so a widget that keeps no drag state need not care.
    virtual void mouseGestureCancelled();

    virtual void pointerEntered();
    virtual void pointerLeft();
    virtual bool keyPressed(const ICoreKeyEvent& event);
    virtual bool keyReleased(const ICoreKeyEvent& event);
    virtual bool wheelScrolled(const ICoreWheelEvent& event);

    // ⚠ The old size is a PARAMETER, not a second hook. It used to be dropped
    // on the floor, and ICoreTable is the one place in the tree that reads it
    // (ICoreTable.cpp:52, comparing the old width to decide whether a column
    // re-layout is needed) -- P5.6's list has that site. The signature was
    // simply widened rather than overloaded because this hook had ZERO
    // overriders when P5.1 landed, so widening cost nothing and left one hook
    // where a second would have left two names for one event.
    //
    // ⚠ ICoreGraphicsView::resized(const ICoreRect& viewportBounds) keeps its
    // own different shape, and P2.11b asked for that divergence to be settled
    // here. Settled: they stay different, because they carry different VALUES
    // -- a scroll view's viewport bounds are not its widget size, and its one
    // overrider wants the viewport. Nothing ever sees both: ICoreGraphicsView
    // derives from ICoreNativeWidget, not from this class, so no subclass
    // inherits the pair and no reader has to choose between them.
    virtual void resized(const ICoreSizeF& newSize, const ICoreSizeF& oldSize);

    virtual void shown();
    virtual void hidden();

    // Focus arriving and leaving. The event carries the reason because losing
    // it is not the same as gaining it by mouse: see ICoreFocusEvent (P0.6),
    // which exists for ICoreSpinBox's select-on-Tab-but-not-on-click rule.
    virtual void focusGained(const ICoreFocusEvent& event);
    virtual void focusLost(const ICoreFocusEvent& event);

    // The widget was enabled or disabled -- for the ones that repaint
    // themselves greyed rather than letting the style do it.
    virtual void enabledChanged(bool enabled);

    // Return false to veto the close (the "unsaved changes" prompt lives in
    // an override of this).
    virtual bool closing();

    virtual bool contextMenuRequested(const ICorePoint& globalPos);

    // A widget registered with watchPointerPressesOf was just pressed.
    // ICoreNativeWidget* since Q5.1 -- and it is the SAME pointer the caller
    // handed watchPointerPressesOf, not a rediscovered one, so an overrider can
    // compare it directly against the wrapper it registered.
    virtual void watchedPointerPressed(ICoreNativeWidget* watched);

    // A widget registered with watchResizesOf just changed size.
    virtual void watchedResized(ICoreNativeWidget* watched, const ICoreSizeF& newSize);

    // A widget registered with watchPointerCrossingsOf was entered or left.
    virtual void watchedPointerEntered(ICoreNativeWidget* watched);
    virtual void watchedPointerLeft(ICoreNativeWidget* watched);

    // A widget registered with watchKeyPressesOf received a key press. Return
    // TRUE to consume it, which is the eventFilter `return true` this replaces;
    // false lets the key reach the watched widget unchanged.
    virtual bool watchedKeyPressed(ICoreNativeWidget* watched, const ICoreKeyEvent& event);

    // This widget's parent changed size. Only delivered after
    // watchParentResizes(true).
    virtual void parentResized(const ICoreSizeF& newParentSize);

    // The pointer moved anywhere in the application. Only delivered while
    // watchApplicationPointerMoves(true) is in force. The position is in SCREEN
    // coordinates, because the move that matters is usually over some other
    // widget and this one's local coordinates would be meaningless for it --
    // mapFromScreen() converts when the receiver does want its own.
    virtual void applicationPointerMoved(const ICorePoint& globalPos);

    // A pointer press landed outside this widget, everything inside it, and
    // everything handed to exemptFromOutsidePresses. Only delivered while
    // watchOutsidePointerPresses(true) is in force.
    virtual void pointerPressedOutside();

    // Drag & drop, delivered only after the subclass opts in with
    // setAcceptDrops(true). Returning true from dragEntered/dragMoved accepts
    // the drag; from dropped, the drop.
    virtual bool dragEntered(const ICoreDragEvent& event);
    virtual bool dragMoved(const ICoreDragEvent& event);
    virtual bool dropped(const ICoreDragEvent& event);

    // The drag left without dropping. No ICoreDragEvent and no bool: Qt's
    // QDragLeaveEvent carries neither position nor mime data, and there is
    // nothing to accept or decline once the drag is already gone. A widget
    // showing a drop highlight clears it here.
    virtual void dragLeft();

    // The Qt event handlers that stood here moved into the Impl at P5.10: the
    // Impl is the QWidget, so it is the object the toolkit delivers events to.
    // Each Impl override forwards to the hooks above, exactly as the handlers
    // here used to. P5.9 proved no subclass overrode any of them.

    // The outline drawn around the body. Read at paint time rather than cached,
    // so an override follows the theme with no work of its own.
    virtual ICoreColor borderColor() const;

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

ICoreWidgetGrid.h#

ICoreEssentials/UI/Widgets/ICoreWidgetGrid.h

ICoreWidgetGrid#

ICoreWidgetGrid.h:12 · class · bases public ICoreWidget · pImpl · 7 declaration(s)

class ICoreWidgetGrid : public ICoreWidget {
public:
    explicit ICoreWidgetGrid(ICoreNativeWidget* parent = nullptr);

    void setColumns(int newColumns);
    void clearGrid();
    void removeWidget(ICoreWidget* widget);
    void addManagedWidget(ICoreWidget* widget);

    void enableAutoVerticalShrinking();

    ~ICoreWidgetGrid() override;

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

ICoreWidgetPaintCommands.h#

ICoreEssentials/UI/Widgets/ICoreWidgetPaintCommands.h

Give a widget an opaque ground in color, for the widgets a client does not own and cannot subclass -- a scroll pane's viewport, most of all. Free function rather than a method because the target is any widget, and there is nothing else to hang it on.

⚠ THE RAW-QWidget OVERLOAD LEFT THIS HEADER (A9.4, 2026-08-21). It was declared beside this one and had NO CALLER anywhere except the Qt Impl that defines it -- the eight call sites in src/ICoreSDK and the lab all pass an ICore type and always did. So it was a Qt name on a backend-neutral surface paying for nothing, which is Rule 3's case exactly: the API is what the call sites use and nothing else. It is declared in UI/Backends/Qt/Widgets/ICoreWidgetPaintCommands.cpp, next to its definition and its one caller.

Declares no class of its own — see the file.