material-ui-rs 0.4.2

A Material3 inspired theme for `iced`
Documentation
use super::*;

#[test]
fn plain_tooltip_text_shrinks_under_material_max_width() {
    let type_scale = tokens::component::tooltip::PLAIN_SUPPORTING_TEXT;
    let text: Text<'_, Theme, iced_widget::Renderer> =
        plain_supporting_text("Material 3 plain tooltip", type_scale);

    assert_eq!(
        Widget::<(), Theme, iced_widget::Renderer>::size(&text).width,
        Length::Shrink
    );
    assert_eq!(PlainTooltipMetrics::horizontal_padding(), 4.0);
    assert_eq!(PlainTooltipMetrics::max_width(), 192.0);
    assert_eq!(
        PlainTooltipMetrics::max_width() + tokens::component::tooltip::PLAIN_VERTICAL_SPACE * 2.0,
        tokens::component::tooltip::PLAIN_MAX_WIDTH
    );
}

#[test]
fn rich_tooltip_padding_matches_androidx_material_layout_constants() {
    assert_eq!(rich_title_top_padding(), 8.0);
    assert_eq!(RichTooltipSurface::shadow_padding(), 8.0);
    assert_eq!(
        rich_supporting_text_padding(false, false),
        Padding {
            top: 4.0,
            right: 0.0,
            bottom: 4.0,
            left: 0.0,
        }
    );
    assert_eq!(
        rich_supporting_text_padding(true, true),
        Padding {
            top: 4.0,
            right: 0.0,
            bottom: 16.0,
            left: 0.0,
        }
    );
}

#[test]
fn rich_tooltip_surface_keeps_material_gap_from_anchor() {
    let content = Rectangle {
        x: 120.0,
        y: 160.0,
        width: 80.0,
        height: 32.0,
    };
    let clip_padding = RichTooltipSurface::shadow_padding();
    let tooltip_node = layout::Node::new(Size::new(180.0, 96.0));
    let tooltip = rich_tooltip_surface_bounds(
        &tooltip_node,
        RichTooltipPlacement {
            content_bounds: content,
            viewport: Rectangle::new(Point::ORIGIN, Size::new(400.0, 400.0)),
            cursor_position: Point::ORIGIN,
            position: Position::Top,
            gap: tokens::component::tooltip::SPACING_BETWEEN_TOOLTIP_AND_ANCHOR,
            padding: 0.0,
            clip_padding,
            snap_within_viewport: true,
        },
    );
    let surface = RichTooltipSurface {
        bounds: tooltip,
        clip_padding,
    }
    .visual_bounds();

    assert_eq!(
        content.y - (surface.y + surface.height),
        tokens::component::tooltip::SPACING_BETWEEN_TOOLTIP_AND_ANCHOR
    );
    assert_eq!(tooltip.width, 180.0 + clip_padding * 2.0);
    assert_eq!(surface.width, 180.0);
}

#[test]
fn rich_tooltip_corridor_spans_anchor_surface_gap() {
    let content = Rectangle {
        x: 120.0,
        y: 160.0,
        width: 80.0,
        height: 32.0,
    };
    let tooltip = Rectangle {
        x: 70.0,
        y: 60.0,
        width: 180.0,
        height: 96.0,
    };
    let corridor = RichTooltipHitArea {
        content,
        tooltip,
        position: Position::Top,
    }
    .corridor()
    .expect("top tooltip should have a corridor to its anchor");

    assert!(corridor.contains(Point::new(160.0, 158.0)));
    assert!(!corridor.contains(Point::new(160.0, 120.0)));
    assert!(!corridor.contains(Point::new(80.0, 158.0)));
}

#[test]
fn rich_tooltip_keep_alive_does_not_cover_adjacent_anchor() {
    let rich_anchor = Rectangle {
        x: 289.0,
        y: 290.0,
        width: 118.0,
        height: 62.0,
    };
    let rich_tooltip = Rectangle {
        x: 28.0,
        y: 9.0,
        width: 640.0,
        height: 272.0,
    };

    let hit_area = RichTooltipHitArea {
        content: rich_anchor,
        tooltip: rich_tooltip,
        position: Position::Top,
    };

    assert!(hit_area.contains(mouse::Cursor::Available(Point::new(348.0, 286.0))));
    assert!(!hit_area.contains(mouse::Cursor::Available(Point::new(200.0, 322.0))));
}

#[test]
fn rich_tooltip_anchor_exit_defers_dismissal_to_overlay() {
    let start = Instant::now();
    let mut state = RichTooltipState::default();

    state.show(start, Point::new(10.0, 20.0));

    assert!(!RichTooltipHitArea::anchor_exit_dismisses(true, &state));
    assert!(RichTooltipHitArea::anchor_exit_dismisses(false, &state));
}

#[test]
fn rich_tooltip_transition_matches_androidx_compose_motion() {
    let start = Instant::now();
    let mut state = RichTooltipState::default();

    state.show(start, Point::new(10.0, 20.0));
    assert_eq!(state.phase, TooltipPhase::Showing);
    assert_eq!(
        state.animation_frame(start),
        TooltipAnimationFrame {
            scale: tokens::component::tooltip::SCALE_START,
            alpha: 0.0,
        }
    );

    let halfway = start + duration_ms(tokens::component::tooltip::FADE_IN_DURATION_MS / 2);
    let half_frame = state.animation_frame(halfway);
    assert!((half_frame.alpha - 0.5).abs() < 0.001);
    assert!(
        (half_frame.scale
            - lerp(
                tokens::component::tooltip::SCALE_START,
                1.0,
                tooltip_scale_easing(0.5)
            ))
        .abs()
            < 0.001
    );

    let shown = start + duration_ms(tokens::component::tooltip::FADE_IN_DURATION_MS);
    state.advance(shown);
    assert_eq!(state.phase, TooltipPhase::Shown);
    assert_eq!(
        state.animation_frame(shown),
        TooltipAnimationFrame {
            scale: 1.0,
            alpha: 1.0,
        }
    );

    state.dismiss(shown);
    assert_eq!(state.phase, TooltipPhase::Dismissing);
    assert_eq!(
        state.animation_frame(shown),
        TooltipAnimationFrame {
            scale: 1.0,
            alpha: 1.0,
        }
    );

    let exit_half = shown + duration_ms(tokens::component::tooltip::FADE_OUT_DURATION_MS / 2);
    let exit_progress = state.progress(exit_half);
    let exit_half_frame = state.animation_frame(exit_half);
    assert!((exit_half_frame.alpha - (1.0 - exit_progress)).abs() < 0.001);
    assert!(
        (exit_half_frame.scale
            - lerp(
                1.0,
                tokens::component::tooltip::SCALE_START,
                tooltip_scale_easing(exit_progress)
            ))
        .abs()
            < 0.001
    );

    let hidden = shown + duration_ms(tokens::component::tooltip::FADE_OUT_DURATION_MS);
    state.advance(hidden);
    assert_eq!(state.phase, TooltipPhase::Hidden);
    assert_eq!(
        state.animation_frame(hidden),
        TooltipAnimationFrame {
            scale: tokens::component::tooltip::SCALE_START,
            alpha: 0.0,
        }
    );
}

#[test]
fn tooltip_alpha_style_scales_container_colors() {
    let theme = Theme::Light;
    let style = tooltip_container_style_alpha(tooltip_style::rich(&theme), 0.5);

    assert_eq!(
        style.background,
        Some(Background::Color(alpha_color(
            theme.colors().surface.container.base,
            0.5
        )))
    );
    assert_eq!(
        style.text_color,
        Some(alpha_color(theme.colors().surface.text_variant, 0.5))
    );
}