diff --git a/AGENTS.md b/AGENTS.md index 16f4ce8..245f532 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -95,7 +95,7 @@ Platform adaptations, each forced by a missing Qt analog: - The Type tool (8.9a): `Document/TypeTool.h/.cpp` twin Swift's file, `TypeTool+Session.h` its session half. `TextAlignment`'s raw values and `textAlignment(text)` serve the manifest; `LayerTextStyle`, `LayerText` and `TextDraft` keep Swift's fields. A `LayerText` holds its image's `ImageIdentity`, as a `LayerShape` does; `ImageLayer.text` joins equality and `liveText()` compares identities. TextKit has no Linux twin, so `Rendering/TextLayout` is Linux-only: `TextLines` lays a style out with `QTextLayout` as `textBoxSize` and `textImage` lay it out with TextKit. Lines have a fixed `lineHeight` with the baseline at their bottom, so extra leading sits above the letters (Swift's equal minimum and maximum line heights); they wrap at word boundaries, a word longer than the line anywhere (`byWordWrapping`); a line that would pass the container's bottom is not laid, nor anything after it (`overflows`); a final newline adds no line to a measure (its line holds the caret alone, 8.9b), and an empty paragraph lays one, the caret's room. Paragraphs part at Cocoa's separators, LF, CR, CRLF and U+2029, and Qt breaks a line at U+2028 by itself. Tabs stop every 28 points, as `NSParagraphStyle`'s twelve default stops do up to 336. Past the twelfth Cocoa has no stop and moves a tab down a line (a line of tabs wraps after 336 points in an `NSTextView`); here the stops go on every 28 points (known difference): moving such a tab down would lay the rest of its paragraph again for each one, quadratic in a run of tabs, and measure bidi lines by hand, while `QTextLine::setNumColumns` leaves the tab hanging on the line above. Faces go by PostScript name, as macOS names them: fontconfig lists each installed face's `FC_POSTSCRIPT_NAME` with its family and style (`availableFonts`, the bar's list). A name it does not list is Qt's match for that family, where Swift falls back to the system font; fontconfig's aliases put Helvetica, the default, on a metric twin where one is installed. The size is the style's in pixels, as a CoreGraphics context counts points, rounded: Qt draws a face in whole pixels (`QFontPrivate::engineForScript` rounds a point size's pixels too), so a fractional size draws at the nearest whole pixel where CoreText draws it exactly (known difference; the default is whole, and the bar's steps keep a whole size whole). Kerning is off, since Swift always sets `.kern`; tracking is absolute letter spacing after every letter, the last included, as CoreText kerns; hinting is off, so Qt lays out design advances by itself (`QFontEngineFT::shouldUseDesignMetrics`) and the layout sets no flag for them. Qt 6.4 scales absolute letter spacing by the device's dpi over its own in whole numbers, so on a 72-dpi image it vanishes (probed): lines are laid out without a device, where that scale is one. Letters differ from macOS's where the faces differ; the rules are the same. `Dockerfile.dev` installs `libfontconfig-dev`; CMake links `Fontconfig::Fontconfig`. In the session `textDraft` has a public setter, as Swift writes it freely, and `textDefaults` a getter. `applyText` takes its draft by value, as Swift's struct is passed: `finishText` hands it the session's own draft, which it clears. It clears the draft first (the gates read it) and puts it back on refusal, silently, and before an error is announced, so the error's signal sees the draft; blank new text and an unchanged edit close the draft, announced, with no step and no canvas focus request. `layerName` keeps 40 graphemes (`QTextBoundaryFinder`), Swift's `prefix(40)` of Characters; `simplified()` makes whitespace runs one space. `recolorText` logs a failed redraw where Swift's `try?` is silent. Left out as dead: the `isFinite` terms of `isValid` and `boxIsValid` (the ranges refuse NaN and infinities); the draft and document guards of `beginText`, `editActiveText` and `recolorText` (`canEditLayers` reads both); `textBoxSize`'s `max(16, …)` on both sides (the padding alone makes 24) and its caret line (an empty paragraph lays a line); `textImage`'s `width >= 1` terms; `recolorText`'s validity guard (`textImage` refuses an invalid style, and the refusal is logged); `setPaletteColor`'s Type-tool test (a draft lives only under the Type tool: every way in sets the tool, and a switch finishes text first); `fillSelection`'s `liveText` test (`recolorText` asks it); the store rule's folder term (text needs pixels, and `LayerHierarchy::validate` refuses a folder that has them). Hooks: the gates and `canStartProjectOperation` read `textDraft`; `selectLayer`, `selectLayers` and `selectTool` finish text first when the choice changes, and stop when that is refused; the foreground swatch colours an open draft; Fill recolours live text when neither a selection nor a mask is in play; `addPixelLayer` takes the style, as it takes a shape's; `installProject` loads text and `applyDocumentSize` does not; the record carries live text; every rebuild that drops a shape drops text; the canvas resizer passes it on, the image resizer not. The manifest's `text` is Swift's `Codable`: every key required but `boxSize` (synthesized decoding ignores the defaults), the alignment by raw value, the box as two numbers; the store refuses a project whose text fails `isValid` or has no pixels (Swift's `validate`; its adjustment term is dead, since an adjustment layer's own rule refuses pixels, 9.5a), and a snapshot installed past the store loads such text as pixels (`LayerText::loaded`). `TypeControls` is the tool's `ToolHeaderBar`: the face as a `TypeFontPicker` (since 12.1: its list of fontconfig's names and the style's own, sorted, loads when the menu first opens); size, tracking and leading as `TextStyleField`s, `QLineEdit`s that take each readable edit as Swift's value bindings do, show the session's number unless focused, step by one on the arrows (Shift ten), keep their typing while a menu or popup borrows the focus, and keep the focus on Return, showing the number again, selected, as an `NSTextField` does: Swift's Type bar alone has no `releasesFocusOnCommit`; Escape is the field's own. They print Swift's `.number`: at most three decimals, none trailing; digits are not grouped, as in every field of the twin. The size's parse check is left out: text that is no number reads as zero, which no size passes. The size steps within 1–2000; the leading shows Auto as its placeholder, clamps what is typed to 0–5000 and steps from the height Auto works out to. The alignments are checkable `QToolButton`s with Swift's `text.align` glyphs drawn as paths, redrawn on `PaletteChange` as the Move bar's lock is; Cancel and Done show while a draft is open, else Edit Text, enabled on live text; the bar rests without a document or while busy shows. The fields scroll as the Move tool's do. The colour swatch and `openTextColorPicker` came with 8.10a. A live text row in the panel shows the rail's Type glyph 1.2 times up, centred (Swift's `textformat`, 1.21 times up), "Editable text layer", "Select text: " and "Text" (Swift 1.1.5's words); `ThumbnailKey` carries `editableText`, so text that loads as pixels redraws. A double click on a control makes the row active, then opens its text (Swift's `renameClickedLayer`); on the name it renames. The inline editor, the canvas's gestures and the edited layer hidden under its draft come with 8.9b. Equivalent mutants: `ImageLayer`'s equality without its text term (every text change brings new pixels); `TextLines` without its stop after a paragraph that overflowed (every later paragraph overflows at once, so the stop spares only their layouts). - The inline editor (8.9b): `Rendering/InlineTextEditor.h/.cpp` twin Swift's file; `InlineTextEditor+Editing.cpp` holds its text view's keys, moves and undo, `InlineTextEditor+Input.cpp` its input methods and clipboard. Swift's editor hosts an `NSTextView` that AppKit turns and mirrors with the layer; a Qt child widget cannot turn, so the canvas paints the editor itself, as it paints `TransformOverlay`, and hands it its keys and input methods, and what `NSTextView` gives for free (selection, typing, deletions, movement, undo, the clipboard, marked text) is written here on `TextLines`, the layout `textImage` draws. `synchronize` follows Swift's: the logical size is `textBoxSize`, point text on a layer grows by the width's factor with its corner kept, handles are six view points (two at least), a style changed elsewhere clamps the selection unless text is marked, and a fresh draft clears the undo steps and takes the keys, posted, unless a text field (`QLineEdit`) holds them. The canvas synchronizes on every `changed()`, so a draft that ends takes its editor with it; Swift's `fresh` branch is reached here only when the session's `setTextDraft` sets one draft over another, as SwiftUI's coalesced updates reach it, and a test does that. The box maps layer pixels through the layer's unit square without flips; the text surface mirrors about its middle. The frame (accent, a sixth of a handle wide, inset a twelfth), the eight white handles with accent rims and the black overflow plus draw first and the text over them, as a subview draws over its view (clear glyphs since 12.12a); the caret is a unit wide and a line tall in the text's colour, shown with an empty selection while the canvas has focus, blinking at half the system's flash time; a selection is `Highlight`, the inactive one without focus, drawn per line from Qt's own visual runs (a `FormatRange` background, the line's own height stretched to the fixed one), so mixed directions highlight what is selected, and where it passes a line's start or end it runs on to that line's edge on the side the paragraph reads, as TextKit's block selection; marked text is underlined unless the input method styles it. `TextLines` gains `TextRange`, `lineOf`, `caret`, `lineStart`, `lineEnd` (a line that ends short of its paragraph ends before its breaking space, laid next line or not: the caret has no affinity, where TextKit's upstream caret sits after it), `position` and a draw with a selection and marked text; it flags overflow only when a character is left unlaid, TextKit's glyph test, so an empty box shows no plus. After the text's final break (LF, CR, U+2029 or U+2028) it lays TextKit's extra line for the caret, even past the container's bottom (point text's measure, `boundingRect`'s, leaves it out), so Home, Up and typing there stay on it; measures and overflow ignore it, and the editor's repaint reaches a line below the box. A U+2028 that ends any paragraph gets the empty line Qt's own `createLine` makes after it, as TextKit breaks there, and a line ends before the space or separator that breaks it. A position past the laid text has no line (`lineOf` is empty past `m_laidEnd`, where Qt's `lineForTextPosition` would answer the last laid line for a paragraph's end): there the caret hides, Home and End go to the text's start and end, and Up and Down stay put. Keys: Swift's own (Escape cancels, Alt with the arrows sets spacing, Ctrl+Return finishes), the fills' text meanings (Alt+Backspace and Alt+Delete a word, Ctrl+Backspace and Ctrl+Delete to the line's start or end; Shift adds nothing, as `NSTextView`'s Shift-Delete deletes back, and the keypad's Delete deletes), then Qt's standard keys for undo, redo, the clipboard, select all and movement, as the app's text fields take them; X11 binds Alt+Backspace to Undo and Shift+Delete to Cut, so deletions come first and Shift+Delete stays Cut. Return types a newline, Tab a tab, and typing is what `QInputControl::isAcceptableInput` accepts, term for term (a format character before the Ctrl test, AltGr's Ctrl with Alt typing). Characters are graphemes and words `QTextBoundaryFinder`'s; in a paragraph that reads right to left (its first strong letter, as Qt judges), Right goes back and Left on, words alike, and a plain arrow collapses a selection to its own side, as Qt's fields move in their logical style; Up and Down keep their first x and reach the start or end past the lines. Undo is the editor's own stack: typing and deleting each coalesce until the caret moves, paste and cut are steps of their own, and a fresh draft starts empty (Swift's `removeAllActions`). Marked text lives in the draft's content, as `NSTextView` keeps it in its storage, but history follows Qt's input method contract: the preedit records nothing, and what commits or removes text makes a typing step from the text before the preedit (joining a typing run, else its own), so a cancelled composition keeps the redo and undo while composing takes the last step back. Each event follows `QWidgetLineControl::processInputMethodEvent` on the text without the preedit: input (a commit, a changed preedit, a replacement) takes the selection first, then the replacement counts from the cursor, a range removed whole or not at all, a zero length inserting at its offset, and where nothing commits the cursor goes where Qt leaves it; an event that is no input changes nothing (Qt's fields remove a selection only on input, and ibus sends empty preedits when an engine switches modes). A `Selection` attribute sets the selection after the commit; while a preedit shows, that selection stays beside it (the preedit's `anchor`, in the text without it), drawn and answered to queries, until the next input takes it, and the attribute ends a typing run as a move does; a draft set over a composing one ends the composing; `TextFormat` attributes style the preedit, counted from its start (a format that is none is passed over, as Qt's fields do), else it is underlined; a `Cursor` attribute places the caret, hides it at length zero and colours it with a `QColor` value. Copy, Cut, Paste and Select All, from the menu or their keys, work around a preedit as Qt's fields do: on the text without it and the selection beside it (`plain`), the preedit then going back at the cursor (`settle`), so Select All then a commit replaces everything; undo takes a preedit away with the step, since it records nothing. Other keys reach the editor while a preedit shows only when the input method lets them through, and act on the text as it stands: typing and deletions end the composing, the preedit staying as text, as NSTextView unmarks it. Text holds 100,000 UTF-16 units (`shouldChangeTextIn`). The canvas makes the editor in `synchronizeDisplay` (Swift's `synchronizeInlineText`, whose twin lives in `InlineTextEditor.cpp`), draws the edited layer as its typed pixels since 12.12a; a draft that begins, ends or is replaced repaints the whole canvas, as Swift's `needsDisplay`, because the editor's box need not cover the layer's (text shrunk while edited, a layer stretched one way, which point text shows at its width's scale); while a draft is open every key goes to the editor (a key it leaves goes on to `QWidget::keyPressEvent`, which ignores it for the ancestors, none of which takes keys: its mutant is equivalent; so is the blend keys' focus term, since `canEditLayers` refuses a blend step while text is open), the text view's standard keys and Backspace with any modifier (the fills' keys; Delete has no menu key and needs no claim) win `ShortcutOverride` ahead of the menus, Shift+Tab stays put, focus shows and hides the caret (its blink is `caretBlink`, a named child timer of the canvas, running only with focus, so tests read it), input methods are enabled through `setAttribute` alone (Qt updates the input method itself and commits a preedit on the way out, so the editor goes first and that commit finds none) and follow the caret: every edit, move and view change updates them while the canvas is the focus object, as `QWidget::updateMicroFocus`, and a focus request hands open text the keys, as Swift's `consumeFocusRequest` hands them to the text view since 12.14d2. The menus follow Swift's text branch: with a draft open Undo and Redo are bare and enabled, and Undo, Redo, Cut, Copy, Paste and Select All reach the editor of the focused canvas, a text field's first. The pointer (placing the caret, dragging a selection, the handles, the text box, the cursors) comes with 8.9c. - The pointer on open text (8.9c): `InlineTextEditor+Pointer.cpp` holds the pointer half of Swift's file: the canvas's text gestures (the `CanvasView` extension there: `beginTextGesture`, `dragTextGesture`, `finishTextGesture`, `drawTextBoxDraft`) and what the editor view and its `NSTextView` do with the mouse. A Type-tool press goes to the open editor first (`press`, Swift's `hitTest`): a handle's band (Swift's `handle(at:)`: ten points to a six-point handle, a third of the box at most) resizes; a press inside the box but in the text's padding does nothing, as Swift's editor view; inside the text it is the text view's click; elsewhere `beginTextGesture` finishes the open text, then opens the topmost visible live text under the point, its click forwarded to the text view wherever it lands (`pressText`, as Swift forwards `mouseDown`), or starts a box. A release under four pixels both ways is point text, else `beginText(in:)`; Escape (ahead of the editor's keys) and another tool drop the box, drawn a point wide in the accent, last; QPainter strokes a one-point pen without joins (its cosmetic stroker, probed), so the box's four outer corner pixels stay clear where CoreGraphics' miter covers a quarter (known difference). Swift's `needsDisplay` at the end of `beginTextGesture` is left out as dead: opened text repaints the canvas, and a box of no size draws nothing until its drag repaints it. A box whose document goes mid-drag (an undone New Canvas) draws and drags nothing and makes nothing on release, Swift's guards in `dragTextGesture` and `drawTextBoxDraft` (the twin's in `textBoxDraftRect`, which both read). Clicks follow `NSTextView`: one places the caret at the nearest position, two select the word under the pointer (`QTextBoundaryFinder` words; the character under it by `CursorOnCharacter`, `TextLines::character`), three the paragraph with its break, and a drag extends by the click's unit from the unit it began on, only while the left button is held. Above the lines is the text's start and below them the end of the laid text, where `NSTextView` puts a click or drag past its lines. An empty last line has no word or paragraph (the unit is empty there, where a lookup at the last character took the line before); past the text's end a double click takes its last word. Shift extends by the last click's unit (`NSTextView`'s kept granularity, `QWidgetTextControl`'s double-clicked word) while the selection that click made stands: its press drags on from that unit, and a selection set otherwise brings the caret's own unit back, extending from the anchor: keys, and any change of style or text, since Swift's style sync sets the selection anew (`setSelectedRange`, which resets the granularity); a fresh draft of the same style keeps it, as Swift's. The row under the pointer is judged as a double before it becomes an `int`: a far drag over a squashed layer passes `int`'s range. Qt has no triple click: the press right after a double click counts three when it comes within the double-click interval and the drag distance, as `QWidgetTextControl` times it; a press elsewhere ends the wait, as AppKit counts each click from the one before. Clicks map through the text's own mirror, so the caret lands where the letters show; Swift's `NSTextView` takes them unmirrored, because AppKit's hit testing ignores the layer transform that mirrors it. A click while a preedit shows asks the input method to commit, Qt's rule for a moved cursor (`QInputMethod::commit`, a no-op offscreen, so the call and the guard before it are equivalent mutants there), and lands the preedit as typed if none does, as `NSTextView` unmarks. A handle's press makes point text a box of the size it shows (Swift's `mouseDown`, whose `fixed.origin` is left out as dead: new point text shows at its own origin, and text on a layer commits its transform); its drag sets the box in its own turned frame, 16 source pixels a side at least, rounded, the far edges kept, and refuses a box or transform that is not valid. The cursor over a band is the frame's resize cursor turned with the box by eighths (Swift's `handleCursor` on Qt's four resize shapes; the rotation is reduced by 360 before the eighths are counted, since Swift's `Int` holds turn counts an `int` cannot), the I-beam elsewhere; a resize keeps its cursor, and its release shows the one under the pointer, as every release on the canvas does. AppKit always sends `mouseUp` to the view that took `mouseDown`; Qt can lose a release (a popup or modal dialog opened mid-drag, a grab the compositor takes), so a move without the left button, a lost focus or the next press ends a text gesture: a box being drawn goes, repainted away, and a selection or resize stops where it is. An edit (typing, an input method, undo, the spacing keys) ends a drag or a resize as a press would: Swift's resize would write back the draft it began with, dropping the edit, and AppKit's tracking loop holds a selection drag's keys until the release (known differences, the first a fix). Equivalent mutants: `drawTextBoxDraft`'s save, restore and brush (it draws last, on a painter each overlay before it restores) and its guard (no box maps to a null rect, which draws nothing). Known differences: `NSTextView`'s context menu and its dragging of selected text are AppKit's own; the Edit menu carries Cut, Copy and Paste. `arrowPath` and `duplicateCursor` moved to `EditorCanvas+Cursors.cpp`, keeping the navigation file under 500 lines. -- The palette and its picker (8.10a): `Document/ColorPalette.h/.cpp` twin Swift's file. `PaletteColor` gains `quantized`, `hex` and `fromHex` (Swift's `init?(hex:)`): spaces and tabs trimmed as Swift's `.whitespaces` trims them (Unicode space separators and the tab, never a line break), one leading `#`, three digits doubled, six required, then Swift's `UInt32(_:radix:)` read by hand: an optional sign, a minus holding zero alone, no `0x` (Qt's `toUInt` takes `0x` and white space). `PickerHSB` keeps Swift's maths to the letter (`fmod` for `truncatingRemainder`, `Int(h)` truncating) and its `setRGB`: grays keep the hue, black the saturation. `ColorPickerTarget` is a struct with a `Kind`, as `TransformDrag::Mode` twins an enum with payloads: the palette and text, the Gradient Map's ends since 9.4a, an effect's colour with 9.6. `ColorPickerState` is a plain struct in the session with an `id` for Swift's object identity (a new picker opens its panel anew); Swift's unused `background` and `PaletteColor(NSColor)` are left out. The sheet writes the working colour through `setColorPickerHSB`, which notifies, where Swift writes `colorPicker.hsb` in place; `closeColorPicker` clears the picker before applying it, so each signal sees it closed. `sampleCompositeColor` draws the live composite into one premultiplied pixel at the floored point and undoes the premultiplication as Swift does; a composite that throws logs on `omaphoto.app` and samples nothing. `UI/FloatingPanel` is Swift's `FloatingPanelController`, brought forward from 9.1: a `QDialog` with `Qt::Tool`, non-modal as a dialog is unless asked, fixed to its content's size, remembering its top-left by name for the run: every show and every move while shown remember it, so a panel dropped without closing keeps its place and a close has nothing to add (Swift's `close` and `windowWillClose` remember again); first shown over the canvas's middle (the owner window's `CanvasView`), else centred on the window, as a `QDialog` centres itself on its parent; a shown panel keeps its place, since Qt keeps a window's top-left as its size changes where AppKit keeps the bottom-left (Swift's re-pin has no twin); every show raises it and hands it the keys, Swift's `makeKeyAndOrderFront`; its close button and Escape report through `onClose`, as `windowWillClose` does, and `close` hides without reporting (Qt's `hide` sends no close, so Swift's `dismissing` has no twin); `refocus` activates the shown panel of a name; new content is shown at once, so the panel takes its size at once (a layout shows late children from the event loop), and old content is hidden at once and deleted later, since a signal from it may still run (the layout lets it go then). `QDialog::closeEvent` would reject, and so close, again: the panel's close accepts directly. Known differences: a Wayland compositor places windows itself, so the remembered places hold on X11; `hidesOnDeactivate` has no Linux twin. `UI/ColorPickerSheet` twins Swift's file: the 256-point field (white to the hue across, clear to black down, a 60% black frame, the marker's black ring behind its white one), the hue strip (360 on top, arrows in the text colour), the preview (a 60% black rim), OK as the default button and Cancel, the R, G, B and hex entries and "Click the canvas to sample" (sampling arrives with 8.10b). The field and the strip pick with the left button alone, as SwiftUI's `DragGesture`. The strip's arrows reach five points past its ends, as SwiftUI lets them overflow; a widget clips its painting, so the sheet draws them over the strip's place and repaints that room. Every control carries Swift's accessibility label as its accessible name (Saturation and brightness, Hue, New color, Red, Green, Blue, Hex color) and every entry Swift's title as its placeholder; Qt's plain widgets hold no settable accessible value, so the hue's rounded degrees are the strip's description. The entries are `PickerField`s, committing on Return and on leaving, where a menu or popup borrowing the focus keeps the typing; an entry keeps its typing only while it holds the keys and was typed in, and a pick on the field or the strip, which take no focus, replaces the channels' typing (9.6f): a channel takes digits alone, clamped as Swift's binding (an overflow reads as 255, an empty or untouched entry changes nothing), Up and Down stepping one, or ten with Shift, from the colour's own value; the hex commits on every leave, as Swift's `onChange(of: hexFocused)`, which snaps the working colour to its 8 bits. Return in an entry commits it and goes on to OK, since a `QLineEdit` passes Return to its dialog; Escape cancels. The channels are made before the hex entry, so the focus chain reads the grid and the keys start at R, which follows the colour until typed in; the hex entry follows only while it lacks the keys, and holding them first it would put its old text back on leaving. `ColorPickerPanelController` shows a new sheet per picker, titled by its target; its close button cancels, and Swift's guard there is left out, since closing no picker does nothing. `UI/ColorPaletteControls` twins Swift's file under the rail: two 24-point swatches (a 1.5 white ring inside a black rim), the swap (X) and defaults (D) glyphs drawn as paths in the secondary ink, dimmed while disabled, the whole resting while the palette cannot be edited, both swatches repainted whole on every change (they overlap); a mask's swatch offers Black · Hide or White · Reveal in a popup under the swatches (Swift's popover), which a change of target closes, and a mask chosen cancels an open picker; the panel follows the session's picker by its id. Swift's frame is 36 points and its two buttons reach 3 points past it, so the twin's widget is 42 points square with the frame inset by 3, in a rail column 42 wide with the tools centred, the swatches 18 points below the last tool (Swift's spacing of 10 and padding of 8). `SwatchButton` draws every swatch: the palette's (radius 6, white ring 1.5), the Brush bar's (34 by 18, radius 4, white ring 1, black rim), the Shape bar's Fill and the Type bar's (36 by 18, radius 3, a half-black rim). The Brush bar's shows the foreground for the Brush and Spot Healing, not Clone Stamp, the Smear or a mask, resting while the palette cannot be edited; the Type bar's shows `typeColor` and opens `openTextColorPicker`. X and D swap and reset the palette on the canvas and in the list, Shift allowed, not with Ctrl, Alt or Meta; `ProjectWorkspace::canSwitch` refuses while a picker is open. Tests wait for the panel to take the keys (`QApplication::activeWindow`): a tool window reads as active whenever its parent is, and offscreen a hidden panel stays the focus window, where a window manager hands the keys back. Equivalent mutants: `raise` (offscreen keeps no stacking order), `refocus`'s `isVisible` (offscreen activates no hidden window), `show`'s `wasVisible` guard (a shown panel stands where it was remembered), and in `sampleCompositeColor` Swift's `min(alpha, value)` (a premultiplied channel never passes its alpha), `painter.end()` before the read (the raster engine writes at once) and the warning. +- The palette and its picker (8.10a): `Document/ColorPalette.h/.cpp` twin Swift's file. `PaletteColor` gains `quantized`, `hex` and `fromHex` (Swift's `init?(hex:)`): spaces and tabs trimmed as Swift's `.whitespaces` trims them (`trimmedWhitespaces` in `EditorSession+Model.h`, shared with `validDimension` and New Canvas since 14.3: the tab, Unicode's space separators and U+200B, as Swift 6.0.3's Foundation lists them, never a line break), one leading `#`, three digits doubled, six required, then Swift's `UInt32(_:radix:)` read by hand: an optional sign, a minus holding zero alone, no `0x` (Qt's `toUInt` takes `0x` and white space). `PickerHSB` keeps Swift's maths to the letter (`fmod` for `truncatingRemainder`, `Int(h)` truncating) and its `setRGB`: grays keep the hue, black the saturation. `ColorPickerTarget` is a struct with a `Kind`, as `TransformDrag::Mode` twins an enum with payloads: the palette and text, the Gradient Map's ends since 9.4a, an effect's colour with 9.6. `ColorPickerState` is a plain struct in the session with an `id` for Swift's object identity (a new picker opens its panel anew); Swift's unused `background` and `PaletteColor(NSColor)` are left out. The sheet writes the working colour through `setColorPickerHSB`, which notifies, where Swift writes `colorPicker.hsb` in place; `closeColorPicker` clears the picker before applying it, so each signal sees it closed. `sampleCompositeColor` draws the live composite into one premultiplied pixel at the floored point and undoes the premultiplication as Swift does; a composite that throws logs on `omaphoto.app` and samples nothing. `UI/FloatingPanel` is Swift's `FloatingPanelController`, brought forward from 9.1: a `QDialog` with `Qt::Tool`, non-modal as a dialog is unless asked, fixed to its content's size, remembering its top-left by name for the run: every show and every move while shown remember it, so a panel dropped without closing keeps its place and a close has nothing to add (Swift's `close` and `windowWillClose` remember again); first shown over the canvas's middle (the owner window's `CanvasView`), else centred on the window, as a `QDialog` centres itself on its parent; a shown panel keeps its place, since Qt keeps a window's top-left as its size changes where AppKit keeps the bottom-left (Swift's re-pin has no twin); every show raises it and hands it the keys, Swift's `makeKeyAndOrderFront`; its close button and Escape report through `onClose`, as `windowWillClose` does, and `close` hides without reporting (Qt's `hide` sends no close, so Swift's `dismissing` has no twin); `refocus` activates the shown panel of a name; new content is shown at once, so the panel takes its size at once (a layout shows late children from the event loop), and old content is hidden at once and deleted later, since a signal from it may still run (the layout lets it go then). `QDialog::closeEvent` would reject, and so close, again: the panel's close accepts directly. Known differences: a Wayland compositor places windows itself, so the remembered places hold on X11; `hidesOnDeactivate` has no Linux twin. `UI/ColorPickerSheet` twins Swift's file: the 256-point field (white to the hue across, clear to black down, a 60% black frame, the marker's black ring behind its white one), the hue strip (360 on top, arrows in the text colour), the preview (a 60% black rim), OK as the default button and Cancel, the R, G, B and hex entries and "Click the canvas to sample" (sampling arrives with 8.10b). The field and the strip pick with the left button alone, as SwiftUI's `DragGesture`. The strip's arrows reach five points past its ends, as SwiftUI lets them overflow; a widget clips its painting, so the sheet draws them over the strip's place and repaints that room. Every control carries Swift's accessibility label as its accessible name (Saturation and brightness, Hue, New color, Red, Green, Blue, Hex color) and every entry Swift's title as its placeholder; Qt's plain widgets hold no settable accessible value, so the hue's rounded degrees are the strip's description. The entries are `PickerField`s, committing on Return and on leaving, where a menu or popup borrowing the focus keeps the typing; an entry keeps its typing only while it holds the keys and was typed in, and a pick on the field or the strip, which take no focus, replaces the channels' typing (9.6f): a channel takes digits alone, clamped as Swift's binding (an overflow reads as 255, an empty or untouched entry changes nothing), Up and Down stepping one, or ten with Shift, from the colour's own value; the hex commits on every leave, as Swift's `onChange(of: hexFocused)`, which snaps the working colour to its 8 bits. Return in an entry commits it and goes on to OK, since a `QLineEdit` passes Return to its dialog; Escape cancels. The channels are made before the hex entry, so the focus chain reads the grid and the keys start at R, which follows the colour until typed in; the hex entry follows only while it lacks the keys, and holding them first it would put its old text back on leaving. `ColorPickerPanelController` shows a new sheet per picker, titled by its target; its close button cancels, and Swift's guard there is left out, since closing no picker does nothing. `UI/ColorPaletteControls` twins Swift's file under the rail: two 24-point swatches (a 1.5 white ring inside a black rim), the swap (X) and defaults (D) glyphs drawn as paths in the secondary ink, dimmed while disabled, the whole resting while the palette cannot be edited, both swatches repainted whole on every change (they overlap); a mask's swatch offers Black · Hide or White · Reveal in a popup under the swatches (Swift's popover), which a change of target closes, and a mask chosen cancels an open picker; the panel follows the session's picker by its id. Swift's frame is 36 points and its two buttons reach 3 points past it, so the twin's widget is 42 points square with the frame inset by 3, in a rail column 42 wide with the tools centred, the swatches 18 points below the last tool (Swift's spacing of 10 and padding of 8). `SwatchButton` draws every swatch: the palette's (radius 6, white ring 1.5), the Brush bar's (34 by 18, radius 4, white ring 1, black rim), the Shape bar's Fill and the Type bar's (36 by 18, radius 3, a half-black rim). The Brush bar's shows the foreground for the Brush and Spot Healing, not Clone Stamp, the Smear or a mask, resting while the palette cannot be edited; the Type bar's shows `typeColor` and opens `openTextColorPicker`. X and D swap and reset the palette on the canvas and in the list, Shift allowed, not with Ctrl, Alt or Meta; `ProjectWorkspace::canSwitch` refuses while a picker is open. Tests wait for the panel to take the keys (`QApplication::activeWindow`): a tool window reads as active whenever its parent is, and offscreen a hidden panel stays the focus window, where a window manager hands the keys back. Equivalent mutants: `raise` (offscreen keeps no stacking order), `refocus`'s `isVisible` (offscreen activates no hidden window), `show`'s `wasVisible` guard (a shown panel stands where it was remembered), and in `sampleCompositeColor` Swift's `min(alpha, value)` (a premultiplied channel never passes its alpha), `painter.end()` before the read (the raster engine writes at once) and the warning. - The Eyedropper and picking (8.10b): `EditorCanvas+Picking.cpp` holds Swift's sampling. `palettePicking` is the Eyedropper, or Alt with the Brush, Spot Healing or the Gradient while no stroke runs and no gradient end is dragged; `picking` adds an open picker (the Levels and hue sample modes arrive with 9.1 and 9.3). A press while picking, Space aside, samples at once and keeps the colour it began from; a drag samples on; the release ends it and, with a picker open, hands the picker the keys back (`ColorPickerPanelController::refocus`, which the canvas includes, as Swift's calls it). `sampleColor` samples into the open picker, else sets the foreground through `setForegroundColor` while the palette can be edited (a chosen mask too, as Swift sets `foregroundColor`), and rings the point. Qt can lose a release: a move without the left button, a lost focus or the next press ends a sampling. Picking outranks every tool's press, a double click (the second click is a press, so the inline editor and a polygonal Lasso stand aside while the picker is open, Swift's `hitTest`), a hover with no button held (the eyedropper alone: the brush circle neither moves nor shows, as Swift's `mouseMoved` returns first; a right-button drag still sizes the tip, as Swift's `rightMouseDragged` ignores picking) and the cursor (after the drag cursors, before Space). `syncPicking` ends a sampling when picking begins or ends: `synchronizeDisplay` runs it, as does every modifier change (Swift's monitor), a sampling press before it begins (Alt may first show in the press, and the foreground's signal would otherwise end the new sampling) and every move (Alt let go unseen). `pickingRelease` returns nothing, where Swift returns from `mouseUp`: a sampling press begins no other gesture, so the rest of the release finds nothing to end. `SampleRingOverlay` is no widget, like `BrushCursorOverlay`: 116 points round the pointer, a 24-point ring of gray 0.45 under a 16-point one, the sampled colour on the top half and the original below, each half replacing the clip and one restore dropping it, drawn over the overlays and under the inline editor; `showsSampleRing` (a getter and a setter that notifies) hides it. The Eyedropper's bar is Swift's inline one, built in `ContentView`: the title and a "Sample Ring" `QCheckBox` 16 points on, following the session under a `QSignalBlocker`. `eyedropperCursor` draws the symbol into Swift's 20-point box of a 24-point image, sixteen white offsets of 1.25 and then black; its hot spot is the drawn tip, (5, 19), as Swift's (3, 21) is its symbol's tip. The glyph is `Rendering/EyedropperIcon`, which the rail and the cursor share, as they share `SelectionIcons`. A mounted canvas leaves the keys to an open picker (Swift's `viewDidMoveToWindow`). The application event filter moved, unchanged, beside the modifier helpers in `EditorCanvas+Cursors.cpp`, keeping the navigation file under 500 lines. Equivalent mutant: the ring's `setBrush(Qt::NoBrush)` (the painter reaches it with no brush). - Levels in the session (9.1a): `Document/Levels.h/.cpp` twin Swift's file, `Levels+Session.h` its session half; `LevelsAutomatic.h/.cpp` and `LevelsAutomatic+Session.h` the other file. `LevelsPixels.c` compiles in place beside the other kernels. `LevelsChannel`, `LevelsSample` and `LevelsAuto` keep Swift's raw values (`rawValue`) and case order (`allLevelsChannels`, `allLevelsSamples`, `allLevelsAutos`); `LevelsSample` is declared opaquely in `Levels.h`, since `LevelsEdit` holds one, and defined with the automatic levels, as Swift's. `LevelsSettings::sampling` is declared in `Levels.h` and defined in `LevelsAutomatic.cpp`, as Swift's extension; `settings(mode, histogram)` stands for `LevelsAuto.settings(histogram:)`; `setCurrent` is Swift's `current` setter, storing the range normalized. A histogram is `LevelsHistogram`, four arrays of 256 bins where Swift has `[[Double]]`, and `LevelsHistogramDisplay::scale` takes one channel's bins. `LevelsFilter::run` leaves soft edges to the kernel. Swift's cc5aac4 once unpremultiplied each soft edge before `levels_apply`, which unpremultiplies again; Swift agrees with the twin since 1.2.0 (`f6a74c9` removed that pass), and its `inputClippingGammaOutputInversionAndAlpha` holds (64, 96, 128, 128) as the twin does. The histogram hands the kernel one row at a time with the mask's own row, since Qt pads a gray image's rows and the kernel accumulates; neither it nor the filter's tables take heap memory of their own, whose failure would throw `std::bad_alloc` past the worker's catch. `LevelsEdit` is a plain class in a `std::optional` of the session, like `FilterEdit`, its `id` standing for Swift's object identity; past 8000 pixels a side its preview source is a scaled copy (sides truncated, one pixel at least), drawn from a painted layer's tiles without flattening them. The session keeps a watcher each for the histogram, the preview and the commit, with the edit each serves: where Swift cancels a task, the twin forgets whose it is (`m_levelsPreviewFor`; a histogram needs no token, since a new edit's count replaces the watcher's future, whose signal Qt then drops), so a result for a turned-off, closed or committing edit drops; one preview runs at a time, the newest job waiting, and a commit drops the waiting job too. A run counts as running until its finish has run (`m_levelsRendering`), as Swift's task clears itself only after its await: Qt's `setFuture` drops a replaced future's pending signal, and `isRunning` and `isFinished` both end with the worker (probed on 6.4.2). A histogram or preview that fails logs on `omaphoto.app` where Swift's `try?` is silent; a failed histogram leaves the bins empty and ready, as Swift's. `setLevelsSampleMode` is the sheet's write (Swift's sheet sets `sampleMode` and bumps `brushRevision` itself); it notifies. The commit writes the layer in place and drops shape and text, as the filter's does. `beginLevels` leaves out Swift's `levels == nil` and its `activeLayer` and `document` guard (`canAdjustColors` reads all three); `hueSaturation` arrives with 9.3, `cancelCrop` with 10.1, the adjustment layer hooks of `updateLevels`, `cancelLevels` and `commitLevels` came with 9.5c. The gates, `canAdjustColors` and `canStartProjectOperation` read `levels`, `selectLayer` and `selectTool` refuse while it is open, and closing it resumes file requests, Swift's `didSet`; the commit's end resumes them through `setIsProjectBusy(false)`, after the edit is gone. `commitLevels` hands its worker the asset, so a painted layer flattens there, where a failure is caught (Swift's lazy image flattens as its task draws). Left out as dead: the display scale's zero-peak guard (with no bin counted the interior is empty too), the preview flag in `previewImage` and in a landing preview (turning it off clears the preview and forgets the run), and in the automatic levels contrast's `low < high` (each channel's low lies below its high, so theirs do), neutral's `0 < mean < 1` and its 9.99 cap (weight at both ends of a range keeps the mean a thousandth inside, so gamma stays under 9.97), and the sample's 0 and 255 clamps (a sample is 0 to 1). Equivalent mutants: the white sample's `max(black + 1, …)` (`normalized` keeps white above black), `sampleLevels`' bounds on the layer's own pixels (a pixel off the image draws clear, which the alpha test refuses), the histogram's `committing` check (a count finishing during a commit, a race no test orders), the commit's check that the layer kept its pixels and place (nothing edits a layer while the project is busy), the tables' reservation and the warnings. `LevelsFailureTests` starves allocations through `AddressSpaceLimit` in a binary of its own: a canvas-wide stroke leaves hundreds of MB free inside malloc's arenas, which `malloc_trim` cannot unmap, so its painted layers are rasters built directly; worker threads allocate within their own reserved arenas, so only asks over 64 MB fail there. - The Levels sheet (9.1b): `UI/LevelsSheet.h/.cpp` twin Swift's sheet; `Histogram`, `Gradient`, `Track` and `Handle` are private widgets of that file, and the histogram and the tracks follow the session's `changed()` themselves. Each handle is a 22 by 20 widget of its own, a child of the sheet placed over its track, as Swift's `ForEach` makes a view each: a later handle lies on top (white over gamma over black), a press drags only its own, each carries Swift's label, and at either end half of it lies in the sheet's padding, where a child of the track would be clipped. The output gradient is made before the output handles, so they lie on its last row as SwiftUI stacks the later view. A drag reads the pointer along its track, Swift's named coordinate space. The triangle is a path (apex 3.75, base 14.25, 12 wide, Swift's 12-point `triangle.fill`); its half-point gray shadow is a gray rim, since Qt has no blur. The histogram's colours are SwiftUI's gray and, from Apple's HIG for macOS 26 (the Swift target), its dark red, green and blue: (142, 142, 147), (255, 66, 69), (48, 209, 88), (0, 145, 255), data colours like the canvas's grays, whatever the theme. The entries are the picker's `PickerField`s: a number applies on Return or on leaving, as Swift's value binding, other text goes back, and an entry keeps its typing until then, unless its own handle is dragged, which takes no focus and replaces the typing with its value (9.6f); numbers are read and shown in the widget's locale with Swift's decimals, as Swift's `.number` format follows the user's (a German gamma reads `2,5`; no level reaches a thousand, so nothing is grouped). A click on OK takes the focus first, so a typed entry applies before the commit, where AppKit's buttons take no focus and the typing is lost; Return in an entry goes on to OK, the panel's default, as in the picker. The eyedroppers are checkable buttons set from the session, the chosen glyph in the accent and the others in the secondary ink, for Swift's `.tint`, redrawn on `PaletteChange`. The channel picker is named by its label (`setBuddy`): on Linux Qt names a combo box by its label relation, never by `accessibleName`. Alt+P is the Preview box's own shortcut in the panel and the canvas's key when the canvas has the keys. For an adjustment layer's editor the caption says the underlying pixels (9.5d). `ContentView+Panels.cpp` holds `synchronizePanels`, Swift's `onChange(of: levels == nil)`, and since 10.1 the importer and the alerts, because `ContentView.cpp` runs to 500 lines: a `FloatingPanel` named `levelsPanel` shows a new sheet when an edit opens, its close cancelling as Swift's `onClose`, and hides when the edit closes. The canvas draws `previewImage` in the layer's place after a filter's; a press in a sample mode is `sampleLevels` and hands the panel the keys (`FloatingPanel::refocus`); other presses wait for Space, the Hand or the Zoom while Levels is open; Escape, Return, Enter and Alt+P are its keys and every other key but Space passes on; `picking` reads the sample mode, so the eyedropper cursor shows; the application filter's brackets rest. `ContentView::acceptsDrop` needs no term of its own: `canSwitch` reads `canStartProjectOperation`, which reads `levels`. The Image menu's Levels… is Ctrl+L and follows `canAdjustColors` (its `hueSaturation` term arrives with 9.3); Import Images and the New canvas button rest while Levels is open. Undo and Redo are bare, since Levels closes the history, and enabled only while a field has focus, as for a busy project: Swift's bare entries act only on a text view. Equivalent mutants: the histogram's zero-peak guard and its `max(0, …)`, each standing in for the other (with no bin counted, 0/0), its `min(1, …)` (the widget clips a taller bar) and the bars' `+ 0.1` (Qt covers abutting rectangles of one path exactly); the drag's clamp of the tone, white's `max(black + 1, …)` and gamma's `min(0.999, …)` (`normalized` does each); either of a track's move and resize events alone (a layout moves and sizes it together); the canvas's `!preview` before the Levels preview (a filter edit and Levels never overlap); passing an unhandled key on (nothing above the canvas takes keys) and the base `changeEvent` (the sheet paints nothing of its own). Qt sizes the loading note itself: a shown child without a layout is adjusted to its hint. @@ -183,7 +183,7 @@ Platform adaptations, each forced by a missing Qt analog: - The Smear's 1.4.2–1.4.3 fixes (13.16, part one: `7164dd4`, `ad9ad7c`, `504129d`, and `095da2f`'s commit half): Liquify is Swift's new GPU stroke on the CPU, as the twin has no Metal: `WarpStroke` keeps the layer as it found it (`m_original`, a copy made in the constructor) and a source offset per pixel (`m_offsets`, two floats each, zero at first); each dab copies the offsets under it to `m_scratch`, reads them bilinearly from behind the brush's travel, less the travel times the weight, writes them back, and draws the pixel afresh from the untouched layer, bilinear, held to its edges, each byte rounded once: `warp_push` of `MetalWarp.swift`, float for float (bytes over 255, mixed, times 255). Swift allocates the two textures on the first push; the twin allocates them with the stroke, before any dab (nothing changes the copy between), so a failure is `ExportError(render)` where `beginWarp` catches it, where Swift's guard returns in silence; Smudge's carried colour, allocated at the first dab inside `beginWarp`, fails the same way, and a dab's scratch that finds no memory skips that dab with a warning on `omaphoto.rendering`, Swift's guard on its scratch texture (review 2: it ran outside any catch). The colour reads keep the shader's `size - 2` as they are: the offsets' read above needs two columns and rows of the dab's area (`ix`, `iy`), which a canvas a pixel wide or tall lacks, so no read leaves the image; the clamps first written here for such canvases were dead and are removed (review 2). Smudge is `ad9ad7c` in both of Swift's paths: dabs every 0.005 of the width (a pixel at least), each dab lays what it carries at weight × strength, and carries what it just left, nothing older. `BrushSettings.blurRadius` (5) is the Blur's own Radius: the sample's sigma follows the Radius (see part two for the merged formula). `BrushControls` shows Radius for the Blur mode alone, after Strength: a scrubbable label (a tenth a point, 0.5–50), a 100-point slider over 0.5–20 in hundredths (past 20 Qt rests it at its end, so Swift's `min(20, …)` is left out), and a 42-point `PickerField` with "px": a readable number applies on Return or leaving, clamped, a number that is not finite takes 5 (Swift's binding), other text goes back, arrows step one (Shift ten) from the session's value, clamped; it prints Swift's `fractionLength(0...1)`, never grouped. `BrushStroke::maxDiameter` is 2100, so `finishWarp`'s tip four wider than a 2000 brush is no longer refused; `finishWarp` appends a dab centre only every twentieth of the brush (the last always), as Swift's; the thinning is `WarpStroke::committedPoints`, so the test reads it directly (`aCommitKeepsATwentiethAndTheLastPoint`). Part two (`095da2f`'s Blur half, on 13.11's grid sampling, cc9bf95 merged into port-d): `BrushStroke::Clone` is 13.11's `(image, placed, inGrid)` plus `render`, a function that makes a part of `image` (Swift's `cloneRender`; Swift keeps it beside `clone`, the twin inside it, so one assignment sets both), and `clone` is private behind `clone()` and `setClone()`, which drops the pieces cut so far (Swift's two `didSet`s). `clonePixels(key, tile)` is Swift's `clonePiece`: the tile mapped into sample pixels through 13.11's chain (grid, `pixelToDocument` unless `inGrid`, then `placed` to the image's pixels, Swift's `scaleX` and `scaleY`), two pixels round, made whole outward (`toAlignedRect`, Swift's `integral`), held to the image; an empty part paints nothing on that tile (the fill is skipped, as Swift's `if let piece`); otherwise the piece is `render(part)` or a copy, cut once per tile key and kept in `m_clonePieces`, and the tile's sample is drawn from it through the same transform shifted by the piece's corner, so every path reads the pixels the whole image would give. A piece that cannot be allocated is `ExportError(render)`, thrown on the calling thread with `publish`'s other copies, where Swift's `cropping` returning nil paints nothing. `blurSample(stroke)` returns the sharp sample in the stroke's grid (13.11's `region`, `fit` and budget unchanged) with `render` blurring a part at `sigma × fit`, padded by the Gaussian's reach (`ceil(3σ)`) and cut back, clear past the sample for pixels (`QImage::copy` fills outside with zeros), its edges repeated for a mask (Swift's `clampedToExtent`), so a piece equals that part of the whole byte for byte (Swift allows one level). The Radius sigma is Swift's `min(min(50, max(0.5, blurRadius)) / perPixel, sidePixels / 2)`. Merge: port-c's direct `clone =` writes became `setClone` (`beginBrush`, `finishWarp`, `cloneSample`'s callers, the tests and `documentClone`), every `Clone` initialiser names `render` (GCC's `-Wmissing-field-initializers`), and port-c's Blur sample tests compare the sharp sample (`framed`) and its softening through `render` (`soft`), with each brush size's old sigma now set as the Radius. Equivalent mutants and untested paths for part two: a piece, a padded copy or a cut-back blur that cannot be allocated (`ExportError`) is untested, as no starvation binary reaches one copy here. Allocating the Liquify buffers for every mode is caught: Smudge then needs room it lacks in `WarpFailureTests`. Held since review 2: the Radius field's non-finite branch ("inf" takes 5); the colour read from the original rather than the context is held by the stripes (`liquifyDrawsFromTheUntouchedLayer`, mutant caught). Left out: Swift's `placed.width > 0` guard in `clonePiece` (every `placed` is a layer's rect, the canvas's or a padded region, never empty), and `gaussianBlur`'s edge flag inside `render` (the pad holds the edges, so the blur's own edge rule never reaches a part; it passes `false`). Equivalent under starvation: the untouched copy's null check (the offsets, twice its size, fail after it). Tests: `SmudgeLiquifyTests` (the oracle rewritten from Swift's new `smudge` and `warp_push`, every byte; pushes in from each edge over ramps; the session's smudge values pinned; a rim pixel the liquify moved), `WarpStrokeTests` (Swift's `liquifyStaysSharp` on Swift's photo, under one level where Swift measured 0.49; half-pixel pushes keep stripes crisp; Swift's `smudgeLeavesOneFadingTrail`; the commit's points a twentieth apart, exactly that apart kept, a pixel at least), `WarpFailureTests` (a Liquify dab without scratch memory skipped, Smudge without room to carry and Liquify without room for its offsets a render error), `LargeCanvasBrushTests` (Swift's `largestBrushCommits` for Liquify, Smudge and Blur on 5000 square at 2000; Swift's `blurPiecesMatchTheWhole` for pixels and a mask, exact; pieces cut once per tile, two pixels round, dropped by a new clone, none past the clone's end where the tile stays as it was; a thinned commit lands all a wide hard warp moved, straight and round a tight circle), `BlurBrushTests` (Swift's `radiusSetsHowFarItSoftens`, 6 and 42 columns, size alone; the clamps), `BlurToolTests` (port-c's sample tests on the Radius: the sharp sample and its softening, 0.5 and half the layer's side, 50 on a wide layer), `BrushControlsTests` (the Radius row, "inf" taking 5), `BrushTests` (2100 is taken, past it refused). Judgments: `7164dd4` drops Liquify from Swift's GPU-against-CPU test, and Swift's CPU fallback keeps resampling; the twin has one path, so it takes the GPU's (what every Mac with Metal runs), and its oracle replays the shader. - Inner effects keep a pixel's alpha (14.1, Swift 1.4.6: `e7e295e`, `9cb57db`, `f25effa`): `LayerEffectsKernel` follows v1.4.9's `effects_compose`: the colour overlay, then the inner glow, then the inner shadow move each pixel's colour toward theirs times its alpha (MSL's `mix`, `recolour`), the overlay by its opacity, the inner two by their strength (`effects_inside`: 1 − the softened or moved shape, no longer times the shape); then the pixels go over the shadow, the outer glow and an outside stroke, and an inside stroke goes on top. So a half-clear pixel takes a colour fully and stays half clear, and effects beneath are not tinted. Swift's CPU path (`overlaid`, `recolor`, the surface's inner glow) serves machines without Metal and stays dead here, as 9.6b's. The effects button's tooltip says "Add layer effect". Tests: `ColorOverlayTests` (Swift's three; the inner shadow and inner glow on half-clear pixels, a shadow beneath untinted), the shader replay in `LayerEffectsKernelTests`. - RAW rounding and Importing… (14.2, Swift 1.4.6: `6345432`, `196342c`, `6e6ca38`, `0907f51`): `kernels/DitherPixels.c/.h` gain `196342c`'s `hash_noise` and `dither_quantize16`, its block body moved into `quantize_band` with a context struct, as 13.17's bands. `RawImporter::develop` works the look at 16 bits (`lround(65535 × …)`, opaque) into a buffer it allocates first (`ExportError(render)` without memory), then rounds it to 8 bits through `dither_quantize16`, where Swift renders Core Image at 16 bits and does the same; Swift's half-float working format names Core Image's own and has no twin. The session's `rawImporting` and `endRawDevelop` are Swift's: Import keeps the request and sets `rawImporting` while an answer waits, Cancel or an answer nobody waits for ends at once, and `finishDecode` ends it once the frame lands or fails (Swift's `defer`), after the layer is in. `RawDevelopSheet::setImporting` shows Swift's plate (a 32-point busy bar over "Importing…" in `PlaceholderText`, padded 16, on the window colour at 85% rounded 10), hides the preview's spinner, rests the sliders, Reset, Cancel and Import, and filters its dialog's Close and Escape while importing (Swift's `interactiveDismissDisabled`). Tests read RAW tones as a mean over the frame, rounded, since the noise moves single pixels a level; Qt's area average truncates, so a fitted preview reads a level low (`theQueueKeepsThePreviewsDecodeUntilReleased`). Holding the global pool freezes painting (Qt's raster engine fills on it), so the sheet's tests read its state before the event loop runs the develop, or use a frame large enough to take a while. Tests: `RawDitherTests` (Swift's two on the kernel; the triangular noise's three in four kept, alpha never passed, a develop dithered and repeatable), `RawDevelopTests` (importing, cancelling, failing, an answer nobody waits for, the plate, the locked dialog, the spinner and Reset while importing). -- New Canvas in units, at a DPI, on a background (14.3, Swift 1.4.6: `b0b4f01`, `ab9779a`, `a00d8b6`): `NewCanvasUnit` and `NewCanvasBackground` (in `NewCanvasSheet.h`, Swift's file) are enums with free functions for Swift's members (`rawValue`, `name`, `next`, `pixels`, `text`; `title`, `next`, `color`). `pixels` trims spaces and tabs, reads a comma as the point and parses as Swift's `Double(text)` (the whole text through `strtod_l` in the C locale, a leading white space refused), takes a positive number, turns a print size into pixels at the DPI rounded half away from zero, and wants a whole count from 1 to 30,000; Swift's `isFinite` term is dead (infinity fails the range, NaN the whole number) and left out. `text` prints whole pixels, or a print size to two decimals with none trailing. The sheet starts in pixels at 72 DPI on a clear canvas each time (`a00d8b6`), and its three settings are Swift's `CyclePill`s, `QAbstractButton`s that paint their text in `PlaceholderText` at 12 pixels and, hovered, a capsule of the text colour at 10% (macOS's quaternary label): the background (Transparent, White, Black canvas), the unit (switching rewrites both fields in the new unit when both read, else leaves them) and the DPI (72 or 300; in a print unit its tooltip adds the pixels it makes). The fields' units follow the unit; the note shows only for a size that does not read, in the unit's words; presets and the clipboard's size are written in the unit. `createNewProject` and `createDocument` take the resolution (1 to 9,600, else 72) and the background; `firstLayer` makes a Background layer of that colour at full size with its thumbnail, or a blank Layer 1, and a background that cannot be allocated logs on `omaphoto.app` and falls back to the blank layer, Swift's `try?` (untested: only starvation reaches it). Tests: `NewCanvasUnitTests` (Swift's four; rounding, trimming, the DPI's ends, the thumbnail), `NewCanvasSheetTests` (the pills, the units, the DPI's tooltip, presets in units, Swift's number parsing). +- New Canvas in units, at a DPI, on a background (14.3, Swift 1.4.6: `b0b4f01`, `ab9779a`, `a00d8b6`): `NewCanvasUnit` and `NewCanvasBackground` (in `NewCanvasSheet.h`, Swift's file) are enums with free functions for Swift's members (`rawValue`, `name`, `next`, `pixels`, `text`; `title`, `next`, `color`). `pixels` trims Foundation's `.whitespaces` (`trimmedWhitespaces`), reads a comma as the point and parses as Swift's `Double(text)` (the whole text through `strtod_l` in the C locale, a leading white space refused), takes a positive number, turns a print size into pixels at the DPI rounded half away from zero, and wants a whole count from 1 to 30,000; Swift's `isFinite` term is dead (infinity fails the range, NaN the whole number) and left out. `text` prints whole pixels, or a print size to two decimals with none trailing, through glibc's `snprintf` in the C locale, whose exact halves go to the even digit as Foundation's do (Qt's own formatting rounds them up: 2.125 inches is 2.12). The sheet starts in pixels at 72 DPI on a clear canvas each time (`a00d8b6`), and its three settings are Swift's `CyclePill`s, `QAbstractButton`s that paint their text in `PlaceholderText` at 12 pixels and, hovered, a capsule of the text colour at 10% (macOS's quaternary label): the background (Transparent, White, Black canvas), the unit (switching rewrites both fields in the new unit when both read, else leaves them) and the DPI (72 or 300; in a print unit its tooltip adds the pixels it makes). The fields' units follow the unit; the note shows only for a size that does not read, in the unit's words; presets and the clipboard's size are written in the unit. `createNewProject` and `createDocument` take the resolution (1 to 9,600, else 72) and the background; `firstLayer` makes a Background layer of that colour at full size with its thumbnail, or a blank Layer 1, and a background that cannot be allocated logs on `omaphoto.app` and falls back to the blank layer, Swift's `try?` (untested: only starvation reaches it). Tests: `NewCanvasUnitTests` (Swift's four; rounding, trimming, the DPI's ends, the thumbnail), `NewCanvasSheetTests` (the pills, the units, the DPI's tooltip, presets in units, Swift's number parsing). - Drops between projects: `ProjectWorkspace::draggedLayerSource` finds the tab whose document holds the dragged id (Swift reads the drag pasteboard; the twin reads the drag's `QMimeData`, which every drop target has), `canReceiveDrag(data, destination)` refuses only a layer's own tab, and `receiveProviders` copies several ids one after another, as Swift awaits each provider, so a two-row drop on the New button opens two tabs as Swift's does. `ContentView` takes such a drop over its whole area (`setWorkspace` hands it the workspace; `acceptsDrop` keeps Swift's guards: busy, the sheet, the importer, a rename; `levels` arrives with 9.x), shows the 3-point accent ring inside the canvas while targeted and hands the drop's document point on when it fell on the canvas. `ProjectTabButton` takes drops for its tab (accent fill and rim, "Add to