Skip to main content

Affordance

Enum Affordance 

Source
#[non_exhaustive]
pub enum Affordance { Hover, Hint, Ancillary, Detail, Anchored, Overflow, Gesture, Haptic, }
Expand description

An affordance whose existence depends on the surface it is offered on.

Eight members, drawn from what the two measured apps already gate by hand rather than from a taxonomy. makeover-layout’s warning applies and is the reason each addition has to point at call sites rather than at a category: guessing is how a description becomes a framework.

Each answers Self::available against the two axes and nothing else. An affordance that is always available is not an affordance this crate has anything to say about, and every_member_is_an_adaptation asserts none has snuck in.

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

Hover

Anything a consumer reveals on hover: a row’s action cluster, a hover toolbar, a preview popover.

Both webview apps arrived at hover-revealed row actions independently (goingson .task-row-action, Balanced Breakfast .row-actions), which is why makeover-layout records the reveal as behaviour of RowPart::Actions rather than as app policy. What neither app can say is that a fingertip has no hover state at all, so the affordance is not hidden on touch — it does not exist there, and something else has to carry the same actions.

§

Hint

Chrome documenting a keyboard interaction: shortcut badges, key hints, a “press / to search” line.

goingson hides .kbd-hint on touch. Strictly this is a claim about having a keyboard rather than about the contact patch, and Density is the closest honest proxy the family carries. Stated rather than hidden, because a detachable-keyboard tablet is where the proxy breaks and a third axis is what fixing it would cost.

§

Ancillary

A secondary panel standing beside the primary content: a saved-views rail, a filter sidebar, an inspector.

goingson hides .saved-views-sidebar below its widest layout. Purely a screen-budget claim — a touchscreen laptop should keep it — so this reads SizeClass alone.

§

Detail

The detail half of a list-detail split, shown alongside the list rather than navigated to.

goingson’s .main-content and Balanced Breakfast’s .detail-panel. Unavailable is not the same as absent: the detail still exists, it is reached by navigation instead of by adjacency, and which of the two a screen gets is what makeover-layout’s Arrangement is describing.

§

Anchored

Navigation or a primary action cluster pinned to a fixed screen edge instead of sitting in the flow of the page.

The reposition bucket, and the largest single one in goingson at 20%. It exists to compensate for what a compact window cannot hold in flow, so unlike the two above it is available at the narrow end and not the wide one. That inversion is the point: an adaptation that only ever removes things describes a degraded layout rather than a different one.

§

Overflow

An action cluster collapsed behind one control rather than laid out inline.

The other compensating member. makeover-layout’s Column::kept_at already handles a table narrowing by dropping columns; this is the same pressure on a cluster of controls, which cannot drop any of them and folds instead.

§

Gesture

A direct-manipulation gesture on content: swipe-to-action, long-press to select, pull to refresh, swipe to navigate, drag to dismiss.

The density axis’s compensating member, and the answer to the question Self::Hover asks and cannot answer. A fingertip has no hover state, so the row actions a pointer reveals by hovering have to arrive some other way; on both webview apps that way is a swipe.

One member for five gestures, on purpose. goingson and Balanced Breakfast each gate all five behind a single boolean, so one member is what is measured and five would be minted from one fact. If a surface ever offers swipe without long-press, splitting this is additive and the call sites that named Gesture keep meaning what they meant.

§

Haptic

Confirmation delivered through the contact patch rather than the eye: the tick as a drag crosses a threshold, the thump as a gesture fires.

A contact-patch claim, which is what makes it this crate’s business despite being the one member nothing on screen shows. goingson frontend/js/haptics.js.

The proxy breaks where the hardware has haptics and the user or the OS has switched them off. That is neither Density nor SizeClass, and it is stated here rather than fixed for the same reason Self::Hint states the detachable-keyboard case: a third axis is what fixing it would cost. This member says the surface can, never that the user wants. Asking the platform whether haptics are enabled is the renderer’s job.

Implementations§

Source§

impl Affordance

Source

pub const fn available(self, density: Density, size: SizeClass) -> bool

Whether this affordance exists on a surface with the given input class and screen budget.

The whole crate in one call. A renderer asks per affordance and never branches on a width.

Source

pub const fn gained_by(self) -> Option<Density>

Which Density this affordance belongs to, or None when it reads the screen budget instead.

The replacement for the withdrawn one-directional rule (see the crate doc). Until 0.3.0 the direction was a property of the whole crate and a test enforced it globally; now each member declares its own, and a_density_member_is_available_on_exactly_the_density_it_declares holds the declaration to the rule.

Exposed rather than kept private for the same reason Self::reads_density is: it is the crate’s claim about itself, and a renderer that has one density can read it directly instead of probing Self::available twice.

Source

pub const fn reads_density(self) -> bool

Whether this affordance’s availability reads Density at all.

Exposed rather than kept private because it is the crate’s own claim about itself: exactly the members gating a contact-patch interaction say yes. A renderer with one density can skip the rest entirely.

Source

pub const fn reads_size(self) -> bool

Whether this affordance’s availability reads SizeClass at all.

Source

pub const fn all() -> [Self; 8]

Every member, in declaration order.

Source

pub const fn token(self) -> &'static str

The CSS class name an app may hang off this, without the leading dot.

Present for the same reason SizeClass::token is: a webview renderer needs a stable name, and minting it per app is how two apps end up with has-hover and hover-capable.

Trait Implementations§

Source§

impl Clone for Affordance

Source§

fn clone(&self) -> Affordance

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for Affordance

Source§

impl Debug for Affordance

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for Affordance

Source§

impl Hash for Affordance

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for Affordance

Source§

fn eq(&self, other: &Affordance) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Affordance

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.