| 319 |
319 |
|
Touch,
|
| 320 |
320 |
|
}
|
| 321 |
321 |
|
|
|
322 |
+ |
impl Density {
|
|
323 |
+ |
/// The media condition selecting exactly this density, without the
|
|
324 |
+ |
/// `@media`.
|
|
325 |
+ |
///
|
|
326 |
+ |
/// A capability question rather than a width or a device, which is the
|
|
327 |
+ |
/// policy this crate already settled for [`density_css`] and which three
|
|
328 |
+ |
/// consumers previously answered three ways.
|
|
329 |
+ |
///
|
|
330 |
+ |
/// The two are **not** each other's textual negation, and that is the
|
|
331 |
+ |
/// reason they live in one place. Touch is comma-joined, so it is an OR,
|
|
332 |
+ |
/// and negating an OR gives an AND with both halves inverted. Deriving one
|
|
333 |
+ |
/// from the other by eye is how the pair drifts apart, and it drifts
|
|
334 |
+ |
/// silently: a wrong negation still parses, still minifies, and only shows
|
|
335 |
+ |
/// up as hover states surviving on a phone.
|
|
336 |
+ |
///
|
|
337 |
+ |
/// Pointer's condition is what a renderer wraps a hover rule in.
|
|
338 |
+ |
/// `makeover-touch` decides *whether* a hover rule should be gated;
|
|
339 |
+ |
/// this decides what the gate is spelled as.
|
|
340 |
+ |
#[must_use]
|
|
341 |
+ |
pub const fn media_condition(self) -> &'static str {
|
|
342 |
+ |
match self {
|
|
343 |
+ |
Self::Pointer => "(hover: hover) and (pointer: fine)",
|
|
344 |
+ |
Self::Touch => "(hover: none), (pointer: coarse)",
|
|
345 |
+ |
}
|
|
346 |
+ |
}
|
|
347 |
+ |
}
|
|
348 |
+ |
|
| 322 |
349 |
|
/// How much screen there is, independent of what is pointing at it.
|
| 323 |
350 |
|
///
|
| 324 |
351 |
|
/// The second axis, and the one [`Density`] kept being asked to carry. A phone
|
| 685 |
712 |
|
pub fn density_css(explicit_touch: Option<&str>) -> String {
|
| 686 |
713 |
|
let mut css = geometry_css_vars(Density::Pointer);
|
| 687 |
714 |
|
css.push_str("\n/* Touch: targets separate, shells hold. */\n");
|
| 688 |
|
- |
css.push_str("@media (hover: none), (pointer: coarse) {\n");
|
|
715 |
+ |
css.push_str("@media ");
|
|
716 |
+ |
css.push_str(Density::Touch.media_condition());
|
|
717 |
+ |
css.push_str(" {\n");
|
| 689 |
718 |
|
for line in gap_css_overrides(":root", Density::Touch).lines() {
|
| 690 |
719 |
|
css.push_str(" ");
|
| 691 |
720 |
|
css.push_str(line);
|
| 729 |
758 |
|
assert!(!css.contains("ui-mode"), "a device mode crept in");
|
| 730 |
759 |
|
}
|
| 731 |
760 |
|
|
|
761 |
+ |
#[test]
|
|
762 |
+ |
fn the_two_density_conditions_are_complements_and_not_negations() {
|
|
763 |
+ |
let pointer = Density::Pointer.media_condition();
|
|
764 |
+ |
let touch = Density::Touch.media_condition();
|
|
765 |
+ |
|
|
766 |
+ |
// Both halves are inverted, feature for feature.
|
|
767 |
+ |
assert!(pointer.contains("hover: hover") && touch.contains("hover: none"));
|
|
768 |
+ |
assert!(pointer.contains("pointer: fine") && touch.contains("pointer: coarse"));
|
|
769 |
+ |
|
|
770 |
+ |
// And the joins are inverted too, which is the part that gets written
|
|
771 |
+ |
// wrong by hand: touch is an OR, so not-touch is an AND. A pointer
|
|
772 |
+ |
// condition joined with a comma would match every touchscreen.
|
|
773 |
+ |
assert!(touch.contains(", "), "touch must be an OR");
|
|
774 |
+ |
assert!(pointer.contains(" and "), "pointer must be an AND");
|
|
775 |
+ |
assert!(!pointer.contains(','), "pointer must not be an OR");
|
|
776 |
+ |
}
|
|
777 |
+ |
|
|
778 |
+ |
#[test]
|
|
779 |
+ |
fn the_emitted_touch_block_is_the_condition_and_not_a_second_copy_of_it() {
|
|
780 |
+ |
// The literal used to be inline here. Nothing may re-spell it.
|
|
781 |
+ |
let css = density_css(None);
|
|
782 |
+ |
assert!(css.contains(&format!("@media {}", Density::Touch.media_condition())));
|
|
783 |
+ |
}
|
|
784 |
+ |
|
| 732 |
785 |
|
#[test]
|
| 733 |
786 |
|
fn an_explicit_choice_is_emitted_after_the_detection() {
|
| 734 |
787 |
|
let css = density_css(Some(".ui-mode-mobile"));
|