API — ICoreEssentials/UI/Backends/AppKit/Graphics
The public contract of 4 header(s) under ICoreEssentials/UI/Backends/AppKit/Graphics — 3 class/struct definition(s), 5 declaration(s). Each section shows the header's banner and its public (and protected-virtual) surface exactly as the file writes it.
ICoreAppKitGraphicsObjectAccess.h#
ICoreEssentials/UI/Backends/AppKit/Graphics/ICoreAppKitGraphicsObjectAccess.h
Reaching from a NODE back to the wrapper that owns it, inside the AppKit zone. (The planning row is named in the .cpp, not here.)
The Qt seat needs nothing like this: its Impl IS the toolkit item, so a QGraphicsItem* found in the scene graph is downcast to an Impl and asked for its owner. Here the scene core stores ids, numbers and one opaque tag, and the tag is a node address -- so the step from "the core says item 7" to "the wrapper whose virtuals answer for item 7" needs a registry.
⚠ IT IS A REGISTRY RATHER THAN A FIELD ON THE NODE, AND THAT IS DELIBERATE. ICoreAppKitSceneNode is a plain struct compiled into every translation unit of the item tier, so adding a member to it changes its LAYOUT -- and a layout change is the one edit that links cleanly across a half-updated build and then misbehaves at run time, because no mangled name moves with it. A
Declares no class of its own — see the file.
ICoreAppKitGraphicsView.h#
ICoreEssentials/UI/Backends/AppKit/Graphics/ICoreAppKitGraphicsView.h
Where the portable scene core meets a native view: the core says what to paint and in what order, and this file runs a painter down that list, turns pointer coordinates into scene coordinates, and turns the core's dirty region into repaint requests.
NO OBJECTIVE-C IN THIS HEADER, the rule this zone follows throughout.
⚠ THE SPLIT WITH THE CORE IS A CONTRACT, NOT AN INTERNAL DETAIL.
UI/Portable/ICoreSceneCore.{h,cpp}is maintained separately from this file, so the division is written down rather than left to be inferred:THE CORE OWNS z-order, item flags, hit-testing, hover, drag, transforms, dirty-rect accumulation -- all in SCENE coordinates, with no view, no device pixel ratio and no notion of a screen.
ICoreAppKitGraphicsItemPaint#
ICoreAppKitGraphicsView.h:51 · struct · 0 declaration(s)
-- what the owner is told when it is time to paint one item --------------- The core stores geometry and flags and a tag it never reads; it does not know what an item LOOKS like.
struct ICoreAppKitGraphicsItemPaint {
public:
ICoreSceneItemId id;
// The owner's own identity for this item, straight from the core.
long long tag = 0;
// The item's own area, origin-relative -- what to draw into.
ICoreSceneRect contentBounds;
bool selected = false;
// True when this item is the one under the pointer. Read from the scene,
// not remembered here, so it cannot drift out of step with the core.
bool hovered = false;
// The scale the item is being drawn AT, view zoom and every ancestor's
// scale folded together. An owner that draws a hairline or picks a font
// size needs this; one that draws plain shapes can ignore it.
double effectiveScale = 1.0;
};
};
ICoreAppKitSceneNode.h#
ICoreEssentials/UI/Backends/AppKit/Graphics/ICoreAppKitSceneNode.h
What a scene-tier wrapper's Impl IS on this backend. (The planning row is named in the .cpp, not here -- a published header's comments are published verbatim by the API generator, so the tree's own working machinery does not belong in one.)
The Qt seat's Impl derives QGraphicsItem: the toolkit object holds the item's position, z value, visibility, parent and children, and the wrapper forwards to it. There is no such object here.
ICoreSceneis DATA -- ids, structs and vectors -- so an item's state has to live somewhere, and the two candidates are not equivalent:
- in the core, with the node holding only an id: then an item that is not
yet in a scene has nowhere to put anything, and
setWidthbeforeaddItem-- which every construction site in this tree does -- would
ICoreAppKitSceneEvent#
ICoreAppKitSceneNode.h:130 · struct · 0 declaration(s)
struct ICoreAppKitSceneEvent {
public:
ICoreAppKitSceneEventKind kind = ICoreAppKitSceneEventKind::Pressed;
// Where the pointer is, in the ITEM's own coordinates and in the scene's.
// Both, because the wrapper hooks publish both and deriving one from the
// other at each seat is how the two drift apart.
ICoreScenePoint itemPoint;
ICoreScenePoint scenePoint;
// ⚠⚠ AND WHERE IT IS ON THE SCREEN, WHICH THIS STRUCT DID NOT CARRY AT ALL.
// The two points above are the only ones the node knew, so every
// ICoreMouseEvent built from one of these left `globalPos` at its default
// (0, 0) -- and the one consumer that needs a SCREEN point is the one that
// shows something at it. ICoreCanvas::contextMenuRequested hands
// `event.globalPos` straight to ICoreMenu::popupAt, so the canvas menu
// opened at the top-left corner of the display every time, whatever was
// right-clicked. Filled by the view, which is the only tier that can map a
// scene point through its own transform and out to the screen.
ICoreScenePoint screenPoint;
int button = 0; // ICoreMouseButton, as an int
// ⚠ EVERY BUTTON HELD, WHICH IS NOT `button`. `button` is what caused the
// event; this is the mask a DRAG reads. ICoreLinkBranchSegment::mouseMoved
// is the site that pins it: it moves a wire only while
// `event.buttons & Left` is set, and an unfilled mask makes every segment
// in the editor unresponsive to a drag.
unsigned int buttons = 0; // ICoreMouseButtons
unsigned int modifiers = 0; // ICoreKeyModifiers
// The wheel travel, for WheelScrolled and meaningless for every other kind.
// In the backend's own units -- POINTS on this one, which is what
// ICoreGraphicsView.mm fills an ICoreWheelEvent with and says so.
double deltaX = 0.0;
double deltaY = 0.0;
// -- KeyPressed only, and meaningless for every other kind ---------------
// The key, as an `ICoreKey` cast to int. 0 for a keystroke this platform's
// table does not name -- which is NOT the same as "nothing happened": most
// punctuation answers 0 and still produces `keyText`, which is the trap
// ICoreWidget's seat records at length in its own key hook.
int key = 0;
// What the keystroke actually produced, UTF-8. Empty for a bare modifier, a
// cursor key or a dead key mid-composition.
//
// ⚠ std::string, NOT ICoreString, FOR THIS HEADER'S OWN RULE: the banner
// above ICoreAppKitSceneEvent says every field is a scene-core or standard
// type, because ICoreString reaches <QString> and a header the whole scene
// tier includes cannot carry a toolkit. The wrapper seats convert.
std::string keyText;
bool autoRepeat = false;
};
};
ICoreAppKitSceneNode#
ICoreAppKitSceneNode.h:187 · struct · 5 declaration(s)
struct ICoreAppKitSceneNode {
public:
// Null until the item is put into a scene, directly or by being parented
// to an item that is already in one.
ICoreScene* scene = nullptr;
ICoreSceneItemId id{};
ICoreAppKitSceneNode* parent = nullptr;
std::vector<ICoreAppKitSceneNode*> children;
// -- the authoritative state, mirrored into the core on attach and on
// -- every change while attached.
double width = 10.0; // the Qt seat's default, reproduced deliberately:
double height = 10.0; // an unsized item is 10x10, not 0x0, so it is
// visible while a caller is still wiring it up.
ICoreScenePoint position{};
double zValue = 0.0;
bool visible = true;
ICoreColor fillColor = ICoreColor::transparent();
ICoreColor borderColor = ICoreColor::transparent();
// ⚠⚠ OPACITY IS ON THE NODE, NOT ONLY IN THE WRAPPER'S Impl, AND THAT IS
// WHAT MAKES IT INHERITABLE. `ICoreScene` is data and models no opacity at
// all, so each item's paint closure applies its own -- which is correct for
// one item and WRONG for a subtree, because the other toolkit's
// QGraphicsItem multiplies a child's opacity by its parent's unless the
// child sets ItemIgnoresParentOpacity.
//
// The tree depends on the inheriting form and says so out loud:
// ICoreBlockView::setCommentedOutStyle sets 0.4 with the comment "opacity
// propagates to all child items (frame, ports, labels), so the whole block
// dims as one when commented out". It did not. A commented-out block kept a
// full-strength frame, ports, name label and face label with a ghost behind
// them -- which reads as "commenting out is half-broken" rather than as a
// missing multiply (A10.18).
//
// Stored here rather than walked through the registry because the paint
// closure has the node and the node has its `parent`: the effective value
// is one walk up the chain, with no lookup and no knowledge of which
// wrapper class any ancestor is.
double opacity = 1.0;
// The wrapper's own virtuals, reached without knowing which wrapper class
// this is. Set once by the Impl that owns the node.
//
// ⚠ contentBounds IS A CALLBACK RATHER THAN A CACHED RECT because it is
// VIRTUAL on the wrapper and subclasses override it to state their size --
// the Qt seat calls it from boundingRect() for the same reason. Caching it
// here would freeze a subclass's answer at whatever it was when the node
// was built, which for a subclass sized after construction is 10x10.
std::function<ICoreSceneRect()> contentBounds;
std::function<void(ICorePainter&)> paint;
// ⚠ THE ITEM'S EVENT HOOKS, AND WITHOUT THIS NOTHING ON THIS BACKEND EVER
// CALLED ONE. The scene core resolves which item a press or a hover
// belongs to, and the view seat hands that result out -- but nothing turned
// it into a call on the item wrapper's `mousePressed()` / `pointerEntered()`
// family, so every item's hook surface was dead while every geometry check
// passed. It is the exact twin of the paint hook A4.1 left unwired, on the
// input side.
//
// Set once by the Impl that owns the node, like `paint`, and reached
// without knowing which wrapper class this is. Returns true when the
// wrapper handled the event.
std::function<bool(const ICoreAppKitSceneEvent&)> event;
};
};
File-scope declarations#
// What a wrapper is told when the pointer reaches its item.
//
// ⚠ EVERY FIELD IS A SCENE-CORE TYPE AND NOT AN ICoreMouseEvent, WHICH IS A
// BUILD FACT RATHER THAN A PREFERENCE. `ICoreMouseEvent` reaches `ICorePoint`,
// which includes `<QPoint>`; putting one in this header would pull Qt into
// every translation unit that includes it, which is the trap the drop tier hit
enum class ICoreAppKitSceneEventKind {
Pressed,
Moved,
Released,
PointerEntered,
// ⚠ MOVEMENT INSIDE THE ITEM ALREADY HOVERED, WHICH IS NOT `Moved`. `Moved`
// is the DRAG hook: it fires only while a grab is open, and it goes to the
// item that took the grab whatever the pointer is over. This one fires with
// no button down and goes to the item under the pointer, which is the pair
// Qt spells hoverMoveEvent/mouseMoveEvent and the pair the wrapper tier
// already publishes as pointerMoved()/mouseMoved(). Without it an item
// hears a position from the pointer exactly once -- see
// ICoreAppKitGraphicsView::setPointerMovedHook.
PointerMoved,
PointerLeft,
// ⚠ OFFERED DOWN THE STACK, NOT TO THE TOPMOST ALONE, which is the rule
// that makes it worth having at all: the item that DRAWS at a point is
// routinely not the item that scrolls it. Over an ICoreGraphicsScrollPane
// the topmost hit is the content, which publishes no wheelScrolled(), while
// the hook lives on the scrollable area two levels up. The view walks the
// stack topmost-first and stops at the first item whose hook answers true.
WheelScrolled,
// ⚠⚠ THE SECOND PRESS OF A DOUBLE CLICK, WHICH REPLACES THE PRESS RATHER
// THAN FOLLOWING IT -- the other toolkit's rule, and the portable view core
// states the same one (ICoreSceneViewCore.cpp: *"THE DOUBLE CLICK REPLACES
// THE PRESS, IT DOES NOT FOLLOW IT"*). Until this enumerator existed
// `ICoreGraphicsObject::mouseDoubleClicked()` -- a published virtual with
// five overrides in the editor -- was a definition nothing on this backend
// ever reached, so the canvas's auto-inserter never opened, a block could
// not be opened into its subsystem by double click, and a scope block could
// not be raised into its chart window.
DoubleClicked,
// ⚠⚠ A RIGHT PRESS, OFFERED DOWN THE STACK EXACTLY AS A WHEEL NOTCH IS.
// The item that DRAWS at a point is routinely not the item that owns the
// menu there: a port's description label declines and the block frame
// underneath opens the block's config pane (ICorePortViewDescriptionLabel's
// own comment says so), and empty canvas is the canvas item several levels
// down. So the view walks the hit stack topmost-first and stops at the
// first item whose `contextMenuRequested()` answers true, which is what
// QGraphicsScene::contextMenuEvent does.
//
// ⚠ IT IS RAISED IN ADDITION TO THE PRESS, NOT INSTEAD OF IT, and that is
// the other toolkit's behaviour rather than a convenience: the editor's
// handlers depend on the right press having already run the selection
// logic (ICoreBlockViewFrame::contextMenuRequested -- *"The block is
// already selected by then: the right press ran the selection logic in
// mousePressEvent() above"*).
ContextMenu,
// ⚠⚠ A KEYSTROKE, GOING TO THE SCENE'S FOCUS ITEM -- AND THE ABSENCE OF
// THIS ENUMERATOR IS WHY NOTHING ON A CANVAS COULD BE TYPED INTO.
// `ICoreGraphicsText::keyPressed` and `keyPressHandled` are published
// virtuals with a real override in the editor
// (ICoreCanvasAreaViewTitleBarLabel, which vetoes Return so a title cannot
// grow a second line) -- and until this kind existed the scene dispatched
// no key at all, so both were definitions nothing ever reached. An
// in-canvas label could take the focus, draw its focus ring, and then
// ignore the keyboard entirely (A10.19; the debt is A4.2's editing half).
//
// ⚠ IT DOES NOT GO DOWN THE HIT STACK. A key has no position: it belongs to
// whatever holds `ICoreScene::focusItem` and to nothing else, which is the
// other toolkit's rule too.
KeyPressed,
};
ICoreAppKitSceneRender.h#
ICoreEssentials/UI/Backends/AppKit/Graphics/ICoreAppKitSceneRender.h
Run a whole scene down a painter: the body ICoreGraphicsScene::renderTo has had since A4.2, lifted out so a SECOND caller does not become a second copy of it (A7.4).
⚠ WHY IT MOVED. The chart tier needs the same operation from the other end: ICoreChartBase's three export routes render THE SCENE THE CHART SITS IN, and they reach it from an item rather than from a scene wrapper. The other toolkit answers both with one call (
QGraphicsScene::render); here the walk is ours, so the choice was a shared body or a duplicated one -- and a duplicated draw loop is the shape that ends with two backends disagreeing about clipping in a way nothing reports.
sourceis in scene coordinates andtargetin the painter's. The source is stretched onto the target EXACTLY -- no letterbox -- which is the
Declares no class of its own — see the file.