No phone, so stop designing for one
Targets are a 12-inch tablet and a desktop (D15). That removes most of the navigation question rather than answering it. `EXPANDED_MIN_WIDTH` is 820 logical pixels and a 12-inch tablet is ~1024 across in portrait, so both orientations of both targets are the expanded class. The compact class now fires only when a desktop window is dragged narrow — graceful degradation, not a second interface. The bottom tool strip and the one-tool-at-a-time sheet were solving a phone, and there is no phone. What survives is input, not size, and the architecture had already decided it: `WidgetDemand::precise_pointing` exists for a television remote, and its own documentation says touch is fine because hit regions grow to the modality. Touch changes hit regions, not layout. The rules that fall out are worth stating because they are easy to violate by accident — no hover-only affordance and no modifier key may be the sole route to anything, since a tablet has neither. Local masking already lost its shift-click extend for exactly this reason. The guaranteed-wide viewport also pays for a better answer to the extent problem than hiding things. The complaint was never that the column is long; it is that the histogram scrolls away from the sliders it reports on. Collapsing shortens the scroll, pinning removes the problem, and ~260px of fixed height is affordable on a viewport that is never under 820 wide.
This commit is contained in:
+79
-40
@@ -88,7 +88,7 @@ overlay; Capture One makes layers a persistent selector at the top of the
|
||||
adjustments tab. Nobody makes a mask a panel that silently rewires a different
|
||||
panel — which is what DarkRoom currently does.
|
||||
|
||||
### On a phone, one family
|
||||
### On a phone, one family — and why it does not apply here
|
||||
|
||||
Lightroom Mobile, Photomator and VSCO all converge on the same shape: **a
|
||||
horizontal strip of tool icons along the bottom**, and tapping one replaces a
|
||||
@@ -105,6 +105,11 @@ The convergence is not fashion. Three physical facts drive it:
|
||||
- **There is no hover.** Disclosure triangles and hover-revealed affordances
|
||||
are worth less; a control is either visible or gone.
|
||||
|
||||
**None of the first two apply to DarkRoom's targets**, which are a 12-inch
|
||||
tablet and a desktop (§3, D-N2). Nobody thumbs a 12-inch tablet one-handed,
|
||||
and its narrow dimension is not narrow. The third does apply, and is handled
|
||||
already — see D-N2.
|
||||
|
||||
---
|
||||
|
||||
## 3. The decisions
|
||||
@@ -142,35 +147,50 @@ describe what is being looked at — and without it the instrument and the
|
||||
controls disagree about what they are measuring. Costs a mask term in the
|
||||
histogram reduction, which already runs per frame over the displayed frame.
|
||||
|
||||
### D-N2 — Diverge on **width**, not on platform · **RECOMMENDED**
|
||||
### D-N2 — One layout, because both targets are wide · **DECIDED**
|
||||
|
||||
The question was whether desktop and Android should diverge. They should
|
||||
diverge, but not along that line.
|
||||
The question was whether desktop and Android should diverge. The answer turns
|
||||
out to be that **neither the platform nor the width axis separates DarkRoom's
|
||||
targets**, so there is no divergence to build.
|
||||
|
||||
**Platform is the wrong axis.** A tablet in landscape wants what a desktop
|
||||
wants. A desktop window dragged to a third of the screen wants what a phone
|
||||
wants. Splitting on `cfg(target_os)` would give the same physical situation two
|
||||
answers depending on which binary it happened to be.
|
||||
**The targets are a 12-inch tablet and a desktop.** No phone, decided
|
||||
2026-08-22. A 12-inch tablet is roughly 1024 logical pixels across in portrait
|
||||
and 1400 in landscape; `EXPANDED_MIN_WIDTH` is 820. **Both orientations of both
|
||||
targets are the expanded class.** The compact class now fires only when a
|
||||
desktop window is dragged under 820px, which is a case to degrade gracefully
|
||||
into, not a second interface to design.
|
||||
|
||||
**Width is the right axis, and it already exists.** `apply_layout_class` in
|
||||
`lib.rs` already classifies the window as `expanded` or `compact` on width
|
||||
alone, already remembers a user override per class, and already drives panel
|
||||
visibility. The divergence proposed here is a second consumer of a decision
|
||||
the app is already making.
|
||||
**Platform would have been the wrong axis anyway**, and it is worth recording
|
||||
why so it is not proposed again. A tablet in landscape wants what a desktop
|
||||
wants; a desktop window dragged narrow wants what a small screen wants.
|
||||
Splitting on `cfg(target_os)` gives one *physical* situation two answers
|
||||
depending on which binary it happens to be. `apply_layout_class` already says
|
||||
this in its own comment — "logical pixels, not a device check" — and it was
|
||||
right.
|
||||
|
||||
So:
|
||||
**What actually differs between the two targets is input, not size**, and the
|
||||
architecture has already decided that too. `WidgetDemand::precise_pointing`
|
||||
exists for a frontend driving a television with a remote, and its own
|
||||
documentation states the position: *touch is fine, since hit regions grow to
|
||||
the modality* (FR-UI-7). Touch changes **hit regions, not layout**. A control
|
||||
is drawn where it belongs and its target grows past its own bounds — which
|
||||
`Check` and the mask rows already do.
|
||||
|
||||
| | **expanded** | **compact** |
|
||||
|---|---|---|
|
||||
| Modes | strip at the top of the canvas | strip along the bottom |
|
||||
| Tools | side column, collapsible stack | bottom sheet, one group at a time |
|
||||
| Mask stack | inline in the column | its own sheet, reached from the strip |
|
||||
So: **one develop layout, tuned for a wide viewport, with touch targets
|
||||
throughout.** The consequences worth stating:
|
||||
|
||||
**What must not diverge is the controls.** Both layouts consume the same
|
||||
`ParamRow` model, the same generated capability list and the same widgets.
|
||||
Only the container differs. That is what keeps FR-DEV-3a intact — a new
|
||||
operation appears in both layouts with no UI edit, because neither layout
|
||||
knows what an operation is.
|
||||
- **A guaranteed-wide viewport is an asset.** The extent problem (§1.2) can be
|
||||
solved by pinning rather than by hiding — see N4.
|
||||
- **No hover-only affordance may carry meaning.** Hover may *emphasise*; it may
|
||||
never be the only way to discover a control. The mask rows already obey this
|
||||
— the eye and the delete target are drawn, not revealed.
|
||||
- **No modifier key may be required.** A tablet has no shift. Local masking
|
||||
already lost its shift-click extend for this reason, and nothing should
|
||||
reintroduce one as the only route to a feature.
|
||||
- **Anything dragged needs a finger-sized target.** This is the live one:
|
||||
gradient masks have no on-canvas handles yet, and when they arrive their
|
||||
handles are the first control in the app designed to be dragged on a
|
||||
photograph rather than in a panel.
|
||||
|
||||
### D-N3 — Collapsible panels, not tool tabs · **OPEN**
|
||||
|
||||
@@ -251,25 +271,42 @@ local mode restores the frame's.
|
||||
|
||||
**Depends on** `ui-refinement.md` A. Extends C.
|
||||
|
||||
**Deliverable.** Each panel in the develop column collapses to its header;
|
||||
headers carry the modified dot C defines; state is keyed by panel identity and
|
||||
survives a slider drag. Default: image and histogram open, the rest collapsed
|
||||
until touched.
|
||||
**Deliverable, two halves.**
|
||||
|
||||
**Done when.** A fresh develop view fits without scrolling at 900px tall, and
|
||||
the histogram is reachable without scrolling past the sliders it reports on.
|
||||
*Collapse.* Each panel collapses to its header; headers carry the modified dot
|
||||
C defines; state is keyed by panel identity and survives a slider drag.
|
||||
Default: histogram open, the rest collapsed until touched.
|
||||
|
||||
### N5 — Compact layout
|
||||
*Pin.* The histogram and the scope header do **not** scroll. They sit above the
|
||||
scrolling region, always visible.
|
||||
|
||||
**Depends on** N1, N4.
|
||||
Pinning is the half a guaranteed-wide viewport buys, and it addresses §1.2
|
||||
directly rather than obliquely. The complaint is not that the column is long —
|
||||
it is that the instrument every tonal control is judged against scrolls away
|
||||
from the controls it reports on. Collapsing panels shortens the scroll;
|
||||
pinning removes the problem. Together they cost about 260px of fixed height,
|
||||
which a target that is never under 820px wide and rarely under 1000 tall can
|
||||
afford.
|
||||
|
||||
**Deliverable.** In the compact class the column becomes a bottom sheet and the
|
||||
mode strip moves to the bottom. Tool groups are reached from the strip, one at
|
||||
a time. Same models, same widgets, different container.
|
||||
**Done when.** The histogram is visible while any tonal slider is being
|
||||
dragged, at every window size the targets produce, without the user having
|
||||
scrolled to arrange it.
|
||||
|
||||
**Done when.** A phone-width window shows the photograph with a thumb-reachable
|
||||
strip and no side column, and every control reachable in expanded is reachable
|
||||
here.
|
||||
### N5 — Compact degrades, rather than diverges
|
||||
|
||||
**Depends on** N4.
|
||||
|
||||
Not a second interface. Below `EXPANDED_MIN_WIDTH` the develop column already
|
||||
overlays rather than sits beside the canvas, and `apply_layout_class` already
|
||||
remembers the user's override per class. With N4's collapse in place a narrow
|
||||
window is a one-panel-at-a-time column by consequence rather than by design.
|
||||
|
||||
**Deliverable.** Confirm the narrow case is usable and fix what is not. No
|
||||
bottom sheet, no second layout, no tool strip along the bottom — those solve a
|
||||
phone, and there is no phone.
|
||||
|
||||
**Done when.** A desktop window dragged to 700px shows the photograph and a
|
||||
usable column, and nothing is unreachable that was reachable at 1400px.
|
||||
|
||||
---
|
||||
|
||||
@@ -292,7 +329,9 @@ rearranges everything.
|
||||
layouts with no UI edit. No file under `ui/` may name an operation.
|
||||
- **ARCH §4.3a.** Composition is the frontend's decision. The core may declare
|
||||
what an operation *is*; it may not declare where the panel puts it.
|
||||
- **FR-UI-3.** Touch targets survive the compact layout — it is the layout
|
||||
most likely to be touched.
|
||||
- **FR-UI-3 / FR-UI-7.** Touch changes hit regions, not layout. Every control
|
||||
keeps a finger-sized target wherever it is drawn, and no hover-only
|
||||
affordance and no modifier key may be the sole route to anything — a 12-inch
|
||||
tablet has neither.
|
||||
- **FR-UI-5.** Escape and the Android back gesture leave the innermost state
|
||||
first. Every mode added here joins that order.
|
||||
|
||||
Reference in New Issue
Block a user