Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions claude.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,11 @@ the comment there about not caching "nothing staged" asks for.
tenth of a second for a window that is ordered out, miniaturised or covered, which the managed
side is not told. A wheel is told from a trackpad by `hasPreciseScrollingDeltas`, and a
control-click is taken in `mouseDown`, since the view has no `NSMenu` for AppKit to ask for.
A drag reports the frame's own centre on an axis its picture cannot move on, as the Linux head
does: only when the space is what cut the picture short can it move. A repeat of a held key is
A drag moves the centre the frame asked for and clamps it to the wider of the two panes'
ranges (`PictureSpace.dragged(by:other:)`), with the same arithmetic in the shim and in
WinForms (`PicturePlacement.Dragged`): clamped to the dragged pane's own range, a drag pulled
the other pane's picture into it. `grid(for:)` reports no more rows than the last window draw
left the body under a tall footer. A repeat of a held key is
dropped for accept, accept all and discard. A picture both panes name keeps a scaled copy for
each pane (`Picture.spare`), since the panes can be a point apart in width and one copy was
made again for each in turn without end.
Expand Down Expand Up @@ -320,7 +323,12 @@ the comment there about not caching "nothing staged" asks for.
three quarters of a picture's size up, and mipmaps made only for a picture drawn smaller than
that, on the decoder thread when first needed. A picture past `GL_MAX_TEXTURE_SIZE` is brought
down to one that fits as it is read. `PixelTests` has two tests that show the window and count
draws through a GL query, `AWindowLeftAloneIsNotDrawn` and `AHiddenWindowIsNotDrawn`.
draws through a GL query, `AWindowLeftAloneIsNotDrawn` and `AHiddenWindowIsNotDrawn`, and a
third that shows it to ask for its rows, `ATallFooterTakesRowsFromTheBody`. A minimised window
is left alone as a hidden one is. `MeasureGrid` reports no more rows than the body had room for
in the last window frame plus the model's eight chrome lines (`chromeRows`, kept in step with
`ScreenBuilder.Chrome` here and in `Renderer.swift`), so a footer taller than its allowance
takes rows from the body and not from under it.
- Group headers fold. `SessionState.Collapsed` holds `QueueItem.GroupKey`s and `QueueProjection`
skips their members, so the marker rides in the label and no head or ABI field knows about it.
Whether an entry is hidden is always read back out of `VisibleEntries`, never recomputed — the
Expand Down Expand Up @@ -595,7 +603,11 @@ the comment there about not caching "nothing staged" asks for.
refuses a library whose version is not an exact match, so a bump and a binaries rebuild land
together: change `native/`, run `build-native`, merge the PR it opens. Between the two, the
`native` CI job — the one that loads the committed binaries — reports the mismatch, which is the
check working.
check working. Version 12 is two things: `DeviewInput.rows` capped by the body's room, and
`DeviewInput.unseen`, a state like the grid and not an event, which is a head saying nobody can
see its window (hidden or minimised on Linux; ordered out, miniaturised or occluded on macOS;
minimised on Windows, through `ViewerInput.Unseen`). `ViewerProgram.Loop` keeps its own `hidden`
and the head's `unseen` apart and slows `OwnerLink`, `TrackedWatch` and `DocumentWatch` on either.
- Built binaries are **committed** to `src/DiffEngineViewer.{Linux,Mac}/runtimes/{rid}/native/`, so a plain
`dotnet build` produces a shippable package and contributors never need CMake. Regenerate them
with the `build-native` GitHub workflow, which opens a PR.
Expand Down Expand Up @@ -703,12 +715,21 @@ the comment there about not caching "nothing staged" asks for.
- A move that writes a file marks the delete pending on it (`TrackedDelete.Written`), however
the move was accepted, and no accept-all carries a marked delete out until a run raises it
again; accepting it on its own still does. `Tracker.HeldReason` is what the menu and the debug
view show. "Discard (n)" runs wholly on a worker. A scan that fails three times running is one
view show, and it rides a full listing as a `held: key|reason` line of its own
(`ViewerResponseDelete.Held`), since the `delete` line is parsed by field count and an older
reader skips a line it does not know. An attached viewer shows it as the entry's status and
leaves held deletes out of "Accept all in" a group (`OwnerLink.AcceptGroup`), and the wire's
accept-all answer ends with `Tracker.DeletesKept`. An owning viewer goes by the same rules for
its own files: a move arriving withdraws the delete on its target (`EnqueueTracked`), a move
carried out marks it (`QueueEntry.Written`), a batch keeps a marked or awaited delete
(`ViewerSession.HeldReason`), a single accept still deletes, and `TrackedEntry.DeleteAgain`
carries the mark across the watch's re-read. "Discard (n)" runs wholly on a worker. A scan that fails three times running is one
balloon for the run. Every tray keeps the `SessionEndWindow` now, and `Program.SessionEnding`
stages the queue where it is held here and then removes the version marker.
- `ITrackedFiles.Version` is what `ListingTag` asks about the tracked files: the identity of the
tracked objects, so `TrackedMove` and `TrackedDelete` must stay immutable in everything a
listing carries. The scan removes a move by key and value, so one staged again since it looked
listing carries. `TrackedDelete.Written` is the exception, and its changes are counted into the
version as the restores are. The scan removes a move by key and value, so one staged again since it looked
is left. `Tracker.Clear` hands the queue's discard to a worker, as a single discard does. A hot
key's action is caught in `KeyRegister`, because a throw from a message filter comes out of
`Application.Run()`.
Expand Down
40 changes: 36 additions & 4 deletions native/include/deview.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ typedef struct DeviewPane {
*
* The managed side does not know how many pixels a pane has, so it may ask for a centre that
* would leave part of the space empty. A renderer moves the centre in as far as it takes to
* keep the space full, and that clamped centre is the one a drag starts from.
* keep the space full. That is for drawing only. A drag starts from the centre asked for,
* moved in only as far as the pane that can go further would move it: see DeviewInput.panX.
*/
float imageZoom;
float imageCenterX;
Expand Down Expand Up @@ -294,6 +295,15 @@ typedef struct DeviewInput {
* The window size in character cells, not pixels. Measured here from the font that was
* actually loaded, because this side is the only one that knows it. Reporting pixels and
* having the managed side divide by a constant is what left the viewer with no DPI handling.
*
* rows is the window's height in rows, and no more than the body has room for with the eight
* lines the managed side keeps for everything that is not a row (ScreenBuilder.Chrome) added
* back. Those eight lines are more than a title, the headers and a footer of one row take, and
* a footer that fits in what is over costs nothing. One that does not - buttons that wrap onto
* a third and fourth row in a narrow window - is laid out over the bottom of the body, so the
* rows it covers are taken off here, and the managed side slices a body that ends above it.
* Counted from the last frame laid out for the window, the only place a footer's height is
* known.
*/
int32_t columns;
int32_t rows;
Expand Down Expand Up @@ -327,8 +337,11 @@ typedef struct DeviewInput {
int32_t zoomDelta;

/*
* Where a drag has left an enlarged picture: the point now at the middle of what shows, as
* fractions of the picture's width and height, already kept inside what the space can show.
* Where a drag has left an enlarged picture: the centre both panes are to draw about, as
* fractions of the picture's width and height. It is the centre the frame asked for that the
* drag moves, and it is kept inside what the pane showing less of its picture can show: the
* two pictures need not be the same shape, and kept to the dragged pane's own range a drag
* brought the other pane's picture in from wherever beyond it that one had been taken.
* On an axis the dragged picture cannot move on, because all of it shows, it is the centre
* the frame was drawn with, unchanged: the other pane's picture may be able to move there.
* panX is -1 on the frames with no such drag, which is almost all of them.
Expand All @@ -346,6 +359,19 @@ typedef struct DeviewInput {
* says which pane it is for in DeviewScreen.menuPane.
*/
int32_t rightClickedPane;

/*
* 1 while nobody can see the window, and 0 otherwise: minimised, hidden by deview_set_hidden,
* or with nothing of it showing where the window system can say so, which AppKit can and X11
* cannot. A state, as the grid is, and not an event: reported by every poll for as long as
* it is so.
*
* The managed side keeps things going beside the window for whoever is reading it - it reads
* the files behind the rows again, lists the queue's owner, draws a document's pages - and
* knew to slow those only for a window it had hidden itself. A window in the taskbar or the
* Dock, or behind another, was kept up as one being read.
*/
int32_t unseen;
} DeviewInput;

/*
Expand Down Expand Up @@ -402,8 +428,14 @@ typedef struct DeviewPlacement {
* widened array element again, in the same bump because they shipped together.
* And DeviewInput reports a right-click over a pane, which DeviewScreen answers with a menu
* that names the pane rather than a queue row.
* 12: DeviewInput.rows is fewer than the window's height in rows where the footer is taller than
* the managed side allows for, by the rows of the body that footer covers. No struct moved:
* what a field means did, and a library from before reports rows that a tall footer hides.
* DeviewInput also reports a window nobody can see, at its end, in the same bump because
* they shipped together. And panX and panY are kept inside what either pane can show, where
* they were kept inside what the dragged one can.
*/
#define DEVIEW_VERSION 11
#define DEVIEW_VERSION 12

/*
* The Swift implementation imports this header for the struct layouts, because Swift does not
Expand Down
Loading
Loading