Put the adjustment groups in the rail where a finger is driving
Reported from the tablet: the tool rail is very useful there, and the same interface under a mouse and keyboard is not. That is `ui-navigation.md` D-N2's central assumption failing in use, and the interesting part is which half of it failed. D-N2 was right that platform is the wrong axis and width is the wrong axis: a tablet in landscape wants what a desktop wants, and a desktop window dragged narrow wants what a small screen wants. `apply_layout_class` still decides the layout class from the window and nothing here changes that. What D-N2 got wrong is the sentence "touch changes hit regions, not layout" — it identified input as the real difference between the targets and then assumed that difference could never reach the layout. Two controls answer one question — which group of adjustments am I looking at — and neither is better in general. A horizontal strip above the column is one gesture to a target the eye has already found, and it pans when the operation set is rich, so a group can sit off the end with nothing saying so: a pointer user tolerates that, a finger user never discovers it. The same list down the rail is every entry visible at once, each finger-sized, on the edge of the screen the hand is already holding, and it costs no width because the rail is already there. So `ToolRail` grows a second section, and `GroupStrip` stands down when it does. The two are never both on screen, which is why they can share `adjust-tab-picked`: Rust is not told which was pressed and has no reason to want to. Mode and group stay independent axes as N1 requires — one entry lit in each section, and choosing a group while a tool is held still filters without putting the tool down. They stay drawn differently, which N1 also required. The tools fill with `active-dim` and invert their ink; the groups take a bar down the leading edge — the strip's underline turned ninety degrees — so a lit entry says which kind of state it is without the reader having to remember which section it was in. The rule between the sections is the second signal. The rail scrolls now. Its own note argued against a Flickable because "this list is four entries written in this file"; with the groups in it the list comes from the operation set, which is exactly the "something the user's data decides" that note excluded this control from. The axis is input, and it is a preference because the automatic answer is a guess that cannot be made reliable. Neither platform can be asked what the user is holding: an Android tablet in a keyboard case is being driven like a desktop, and a touchscreen laptop is whichever its owner says. `dr_plat::is_touch_first` reports the usual case per platform, and `GroupNavigation` lets it be overridden. Settings names what Automatic resolves to on this device rather than leaving it to be found by pressing. D-N6 records the reversal beside the decision it reverses, including the half that still stands and the question it opens: whether Local is a mode at all, or a scope that would collapse the two sections into one list. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
//! How the interface is being driven — a finger, or a pointer.
|
||||
//!
|
||||
//! # Why this is a platform question
|
||||
//!
|
||||
//! It is the last `cfg(target_os)` anyone should want, and it earns its place
|
||||
//! the way the rest of this crate does: the answer differs per platform, every
|
||||
//! entry point needs it, and resolving it in `apps/` would mean writing it
|
||||
//! twice.
|
||||
//!
|
||||
//! # Why it is not the layout class
|
||||
//!
|
||||
//! [`crate::display`] answers "how much room is there", and `dr-ui` turns that
|
||||
//! into a layout class from the window's width, deliberately not from the
|
||||
//! device (FR-UI-1). That was the right call and this does not revisit it: a
|
||||
//! narrow desktop window still gets the compact layout, and a tablet in
|
||||
//! landscape still gets the expanded one.
|
||||
//!
|
||||
//! This answers a different question — "what is the user pointing with" —
|
||||
//! which width cannot stand in for. The two 12-inch targets DarkRoom is built
|
||||
//! for are the same size and the same layout class, and one of them has no
|
||||
//! hover, no modifier keys and a 44-pixel minimum target.
|
||||
//!
|
||||
//! # Why it is a guess, and says so
|
||||
//!
|
||||
//! There is no reliable runtime answer on either platform. Android devices
|
||||
//! have touchscreens and can have a mouse attached; desktops have mice and can
|
||||
//! have touchscreens. Reporting the *usual* case per platform and letting the
|
||||
//! photographer override it is honest; probing input devices and being
|
||||
//! confidently wrong is not. The override lives in
|
||||
//! `dr_types::GroupNavigation`, which takes this as an input rather than
|
||||
//! reading it.
|
||||
|
||||
/// What this build is usually driven with.
|
||||
///
|
||||
/// `true` on Android, `false` everywhere else. Deliberately a plain bool
|
||||
/// rather than an enum: there are exactly two answers today, and the thing
|
||||
/// that consumes it — [`dr_types::GroupNavigation::groups_in_rail`] — is a
|
||||
/// choice between two controls.
|
||||
pub const fn is_touch_first() -> bool {
|
||||
cfg!(target_os = "android")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn the_desktop_build_is_pointer_first() {
|
||||
// Asserted rather than assumed, because the value is a `cfg!` and a
|
||||
// typo in the target name compiles to a silent `false` — which on
|
||||
// desktop is the right answer for the wrong reason and would pass
|
||||
// unnoticed until the tablet build shipped with the desktop layout.
|
||||
assert_eq!(is_touch_first(), cfg!(target_os = "android"));
|
||||
#[cfg(not(target_os = "android"))]
|
||||
assert!(!is_touch_first());
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
pub mod crash;
|
||||
pub mod diagnostics;
|
||||
pub mod display;
|
||||
pub mod input;
|
||||
pub mod secrets;
|
||||
pub mod state;
|
||||
pub mod storage;
|
||||
@@ -23,6 +24,7 @@ pub use display::{
|
||||
Bounds, DisplayInfo, DisplayProfile, DisplayServer, DisplaySurvey, FallbackReason,
|
||||
ProfileSource,
|
||||
};
|
||||
pub use input::is_touch_first;
|
||||
pub use secrets::{
|
||||
EphemeralSecretStore, PlatformSecretStore, SecretError, SecretKind, SecretRef, SecretStore,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user