diff --git a/packages/rsnap-overlay/src/overlay.rs b/packages/rsnap-overlay/src/overlay.rs index 7716276e..7b433e01 100644 --- a/packages/rsnap-overlay/src/overlay.rs +++ b/packages/rsnap-overlay/src/overlay.rs @@ -4799,6 +4799,7 @@ impl OverlaySession { self.config.selection_flow_stroke_width_px, !self.scroll_capture.active, self.scroll_capture.active, + self.frozen_capture_source, self.frozen_capture_source == FrozenCaptureSource::FullscreenFallback, frozen_toolbar_reserved_rect, toolbar_state, diff --git a/packages/rsnap-overlay/src/overlay/hud_runtime.rs b/packages/rsnap-overlay/src/overlay/hud_runtime.rs index 5a99df69..16c790de 100644 --- a/packages/rsnap-overlay/src/overlay/hud_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/hud_runtime.rs @@ -109,6 +109,7 @@ impl OverlaySession { self.config.selection_flow_stroke_width_px, true, false, + self.frozen_capture_source, self.frozen_capture_source == FrozenCaptureSource::FullscreenFallback, None, None, diff --git a/packages/rsnap-overlay/src/overlay/rendering.rs b/packages/rsnap-overlay/src/overlay/rendering.rs index 1e68bcd6..50483a62 100644 --- a/packages/rsnap-overlay/src/overlay/rendering.rs +++ b/packages/rsnap-overlay/src/overlay/rendering.rs @@ -16,16 +16,16 @@ use crate::overlay::{ BindingType, BlendState, Buffer, BufferBindingType, BufferSize, BufferUsages, ClippedPrimitive, Color32, ColorWrites, CompositeAlphaMode, Cow, CurrentSurfaceTexture, Device, Duration, Event, ExperimentalFeatures, Features, FilterMode, FontDefinitions, FontFamily, FrontFace, - FrozenToolbarPointerState, FrozenToolbarState, FullOutput, HudAnchor, HudTheme, Id, Instant, - LayerId, LoadOp, MemoryHints, MipmapFilterMode, MonitorRect, MultisampleState, Mutex, Order, - OverlayMode, OverlaySession, OverlayState, PhysicalSize, PipelineCompilationOptions, - PointerButton, PolygonMode, Pos2, PowerPreference, PresentMode, PrimitiveTopology, Queue, Rect, - RectPoints, RenderPipeline, Renderer, Result, SLOW_OP_WARN_RENDER, Sampler, SamplerBindingType, - ScreenDescriptor, ShaderSource, ShaderStages, SlowOperationLogger, StoreOp, Surface, - SurfaceCapabilities, SurfaceFrameSkipReason, SurfaceTexture, Texture, TextureAspect, - TextureSampleType, TextureUsages, TextureView, TextureViewDescriptor, TextureViewDimension, - ThemeMode, ToolbarPlacement, Trace, Variant, Vec2, ViewportId, Visuals, WindowId, - WindowRendererPath, WrapErr, eyre, hud_helpers, mem, + FrozenCaptureSource, FrozenToolbarPointerState, FrozenToolbarState, FullOutput, HudAnchor, + HudTheme, Id, Instant, LayerId, LoadOp, MemoryHints, MipmapFilterMode, MonitorRect, + MultisampleState, Mutex, Order, OverlayMode, OverlaySession, OverlayState, PhysicalSize, + PipelineCompilationOptions, PointerButton, PolygonMode, Pos2, PowerPreference, PresentMode, + PrimitiveTopology, Queue, Rect, RectPoints, RenderPipeline, Renderer, Result, + SLOW_OP_WARN_RENDER, Sampler, SamplerBindingType, ScreenDescriptor, ShaderSource, ShaderStages, + SlowOperationLogger, StoreOp, Surface, SurfaceCapabilities, SurfaceFrameSkipReason, + SurfaceTexture, Texture, TextureAspect, TextureSampleType, TextureUsages, TextureView, + TextureViewDescriptor, TextureViewDimension, ThemeMode, ToolbarPlacement, Trace, Variant, Vec2, + ViewportId, Visuals, WindowId, WindowRendererPath, WrapErr, eyre, hud_helpers, mem, }; #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -667,6 +667,7 @@ impl WindowRenderer { selection_flow_stroke_width_px: f32, needs_frozen_surface_bg: bool, show_frozen_capture_affordance: bool, + frozen_capture_source: FrozenCaptureSource, frozen_capture_is_fullscreen_fallback: bool, frozen_toolbar_reserved_rect: Option, selection_flow_geometry_cache: &mut SelectionFlowGeometryCache, @@ -761,6 +762,7 @@ impl WindowRenderer { monitor, screen_rect, theme, + frozen_capture_source, frozen_toolbar_reserved_rect, frozen_capture_is_fullscreen_fallback, selection_flow_enabled, @@ -1290,6 +1292,7 @@ impl WindowRenderer { selection_flow_stroke_width_px: f32, allow_frozen_surface_bg: bool, show_frozen_capture_affordance: bool, + frozen_capture_source: FrozenCaptureSource, frozen_capture_is_fullscreen_fallback: bool, frozen_toolbar_reserved_rect: Option, toolbar_state: Option<&mut FrozenToolbarState>, @@ -1350,6 +1353,7 @@ impl WindowRenderer { selection_flow_stroke_width_px, hud_cfg.needs_frozen_surface_bg, show_frozen_capture_affordance, + frozen_capture_source, frozen_capture_is_fullscreen_fallback, frozen_toolbar_reserved_rect, &mut selection_flow_cache, diff --git a/packages/rsnap-overlay/src/overlay/rendering/affordances.rs b/packages/rsnap-overlay/src/overlay/rendering/affordances.rs index 382a8580..9b2354d0 100644 --- a/packages/rsnap-overlay/src/overlay/rendering/affordances.rs +++ b/packages/rsnap-overlay/src/overlay/rendering/affordances.rs @@ -10,25 +10,25 @@ use crate::overlay::rendering::{ use crate::overlay::{ self, Align, Align2, Area, Color32, CornerRadius, FROZEN_SELECTION_SCRIM_ALPHA_DARK, FROZEN_SELECTION_SCRIM_ALPHA_LIGHT, FROZEN_TOOLBAR_BUTTON_SIZE_POINTS, - FROZEN_TOOLBAR_ITEM_SPACING_POINTS, FontFamily, FontId, FrozenToolbarPointerState, - FrozenToolbarState, FrozenToolbarTool, HUD_PILL_CORNER_RADIUS_POINTS, - HUD_PILL_INNER_MARGIN_X_POINTS, HUD_PILL_INNER_MARGIN_Y_POINTS, HUD_PILL_STROKE_WIDTH_POINTS, - HudPillGeometry, HudTheme, Id, LIVE_DRAG_SELECTION_SCRIM_ALPHA_DARK, - LIVE_DRAG_SELECTION_SCRIM_ALPHA_LIGHT, LIVE_DRAG_START_THRESHOLD_PX, LayerId, Layout, Mesh, - MonitorRect, Order, OverlayMode, OverlayState, Painter, Pos2, Rect, RectPoints, - SELECTION_DASHED_BORDER_ALPHA, SELECTION_DASHED_BORDER_DASH_LENGTH_PX, - SELECTION_DASHED_BORDER_GAP_LENGTH_PX, SELECTION_DASHED_BORDER_WIDTH_PX, - SELECTION_FLOW_CORE_FLOW_WIDTH, SELECTION_FLOW_CORNER_RADIUS_PX, SELECTION_FLOW_FLOW_BOOST, - SELECTION_FLOW_FROZEN_ALPHA_SCALE, SELECTION_FLOW_FROZEN_INTENSITY, - SELECTION_FLOW_LIGHT_PALETTE, SELECTION_FLOW_MAX_SEGMENTS, SELECTION_FLOW_MIN_SEGMENTS, - SELECTION_FLOW_PALETTE, SELECTION_FLOW_SAMPLE_STEP_PX, SELECTION_FLOW_SPEED, - SELECTION_SIZE_BADGE_FAR_SHADOW_OFFSET_PX, SELECTION_SIZE_BADGE_FONT_SIZE_POINTS, - SELECTION_SIZE_BADGE_GAP_PX, SELECTION_SIZE_BADGE_INSIDE_MARGIN_PX, - SELECTION_SIZE_BADGE_NEAR_SHADOW_OFFSET_PX, SELECTION_SIZE_BADGE_OUTLINE_OFFSET_PX, - SELECTION_SIZE_BADGE_SCREEN_MARGIN_PX, SELECTION_SIZE_BADGE_TEXT_OUTSET_POINTS, - SelectionFlowStyle, Sense, Shape, Stroke, StrokeKind, TOOLBAR_CAPTURE_GAP_PX, - TOOLBAR_EXPANDED_HEIGHT_PX, TOOLBAR_SCREEN_MARGIN_PX, ToolbarPlacement, Ui, UiBuilder, Vec2, - regular, + FROZEN_TOOLBAR_ITEM_SPACING_POINTS, FontFamily, FontId, FrozenCaptureSource, + FrozenToolbarPointerState, FrozenToolbarState, FrozenToolbarTool, + HUD_PILL_CORNER_RADIUS_POINTS, HUD_PILL_INNER_MARGIN_X_POINTS, HUD_PILL_INNER_MARGIN_Y_POINTS, + HUD_PILL_STROKE_WIDTH_POINTS, HudPillGeometry, HudTheme, Id, + LIVE_DRAG_SELECTION_SCRIM_ALPHA_DARK, LIVE_DRAG_SELECTION_SCRIM_ALPHA_LIGHT, + LIVE_DRAG_START_THRESHOLD_PX, LayerId, Layout, Mesh, MonitorRect, Order, OverlayMode, + OverlayState, Painter, Pos2, Rect, RectPoints, SELECTION_DASHED_BORDER_ALPHA, + SELECTION_DASHED_BORDER_DASH_LENGTH_PX, SELECTION_DASHED_BORDER_GAP_LENGTH_PX, + SELECTION_DASHED_BORDER_WIDTH_PX, SELECTION_FLOW_CORE_FLOW_WIDTH, + SELECTION_FLOW_CORNER_RADIUS_PX, SELECTION_FLOW_FLOW_BOOST, SELECTION_FLOW_FROZEN_ALPHA_SCALE, + SELECTION_FLOW_FROZEN_INTENSITY, SELECTION_FLOW_LIGHT_PALETTE, SELECTION_FLOW_MAX_SEGMENTS, + SELECTION_FLOW_MIN_SEGMENTS, SELECTION_FLOW_PALETTE, SELECTION_FLOW_SAMPLE_STEP_PX, + SELECTION_FLOW_SPEED, SELECTION_SIZE_BADGE_FAR_SHADOW_OFFSET_PX, + SELECTION_SIZE_BADGE_FONT_SIZE_POINTS, SELECTION_SIZE_BADGE_GAP_PX, + SELECTION_SIZE_BADGE_INSIDE_MARGIN_PX, SELECTION_SIZE_BADGE_NEAR_SHADOW_OFFSET_PX, + SELECTION_SIZE_BADGE_OUTLINE_OFFSET_PX, SELECTION_SIZE_BADGE_SCREEN_MARGIN_PX, + SELECTION_SIZE_BADGE_TEXT_OUTSET_POINTS, SelectionFlowStyle, Sense, Shape, Stroke, StrokeKind, + TOOLBAR_CAPTURE_GAP_PX, TOOLBAR_EXPANDED_HEIGHT_PX, TOOLBAR_SCREEN_MARGIN_PX, ToolbarPlacement, + Ui, UiBuilder, Vec2, regular, }; impl WindowRenderer { @@ -96,6 +96,7 @@ impl WindowRenderer { screen_rect, target, None, + false, theme, ); @@ -137,6 +138,7 @@ impl WindowRenderer { monitor: MonitorRect, screen_rect: Rect, theme: HudTheme, + frozen_capture_source: FrozenCaptureSource, frozen_toolbar_reserved_rect: Option, frozen_capture_is_fullscreen_fallback: bool, selection_flow_enabled: bool, @@ -168,6 +170,7 @@ impl WindowRenderer { screen_rect, target, frozen_toolbar_reserved_rect, + frozen_capture_source == FrozenCaptureSource::DragRegion, theme, ); @@ -193,6 +196,7 @@ impl WindowRenderer { screen_rect, target, frozen_toolbar_reserved_rect, + frozen_capture_source == FrozenCaptureSource::DragRegion, theme, ); @@ -224,6 +228,7 @@ impl WindowRenderer { screen_rect, target, frozen_toolbar_reserved_rect, + frozen_capture_source == FrozenCaptureSource::DragRegion, theme, ); } @@ -499,6 +504,43 @@ impl WindowRenderer { preferred_inside_rect } + pub(in crate::overlay) fn selection_size_badge_rect_preferring_outside_with_reserved_rect( + screen_rect: Rect, + capture_rect: Rect, + badge_size: Vec2, + reserved_rect: Option, + ) -> Rect { + let min_x = screen_rect.min.x; + let max_x = (screen_rect.max.x - badge_size.x).max(min_x); + let aligned_x = capture_rect.max.x - badge_size.x; + let x = aligned_x.clamp(min_x, max_x); + let below_y = capture_rect.max.y + SELECTION_SIZE_BADGE_GAP_PX; + let below_rect = Rect::from_min_size(Pos2::new(x, below_y), badge_size); + let fits_below = below_rect.max.y + <= screen_rect.max.y - SELECTION_SIZE_BADGE_SCREEN_MARGIN_PX + && reserved_rect.is_none_or(|rect| !below_rect.intersects(rect)); + + if fits_below { + return below_rect; + } + + let above_y = capture_rect.min.y - SELECTION_SIZE_BADGE_GAP_PX - badge_size.y; + let above_rect = Rect::from_min_size(Pos2::new(x, above_y), badge_size); + let fits_above = above_rect.min.y >= screen_rect.min.y + && reserved_rect.is_none_or(|rect| !above_rect.intersects(rect)); + + if fits_above { + return above_rect; + } + + Self::selection_size_badge_rect_with_reserved_rect( + screen_rect, + capture_rect, + badge_size, + reserved_rect, + ) + } + pub(in crate::overlay) fn snap_points_to_pixel_grid(value: f32, pixels_per_point: f32) -> f32 { let pixels_per_point = pixels_per_point.max(f32::MIN_POSITIVE); @@ -565,6 +607,7 @@ impl WindowRenderer { } } + #[allow(clippy::too_many_arguments)] pub(in crate::overlay) fn render_selection_size_badge( ctx: &Context, painter: &Painter, @@ -572,17 +615,27 @@ impl WindowRenderer { screen_rect: Rect, target: SelectionSizeBadgeTarget, reserved_rect: Option, + prefer_outside_fallback: bool, theme: HudTheme, ) { let text = Self::selection_size_badge_text(monitor, target.size_points); let pixels_per_point = painter.pixels_per_point(); let layout = Self::selection_size_badge_layout(ctx, &text, theme, pixels_per_point); - let badge_rect = Self::selection_size_badge_rect_with_reserved_rect( - screen_rect, - target.rect, - layout.badge_size, - reserved_rect, - ); + let badge_rect = if prefer_outside_fallback { + Self::selection_size_badge_rect_preferring_outside_with_reserved_rect( + screen_rect, + target.rect, + layout.badge_size, + reserved_rect, + ) + } else { + Self::selection_size_badge_rect_with_reserved_rect( + screen_rect, + target.rect, + layout.badge_size, + reserved_rect, + ) + }; let font_id = FontId::new(SELECTION_SIZE_BADGE_FONT_SIZE_POINTS, FontFamily::Monospace); let points_per_pixel = 1.0 / pixels_per_point.max(f32::MIN_POSITIVE); let outline_offset = SELECTION_SIZE_BADGE_OUTLINE_OFFSET_PX * points_per_pixel; diff --git a/packages/rsnap-overlay/src/overlay/tests/rendering_behaviors.rs b/packages/rsnap-overlay/src/overlay/tests/rendering_behaviors.rs index 0598323c..5c2de2fb 100644 --- a/packages/rsnap-overlay/src/overlay/tests/rendering_behaviors.rs +++ b/packages/rsnap-overlay/src/overlay/tests/rendering_behaviors.rs @@ -743,6 +743,51 @@ fn frozen_selection_size_badge_falls_inside_when_default_bottom_toolbar_slot_ove assert!(!badge_rect.intersects(reserved_rect)); } +#[test] +fn frozen_drag_region_selection_size_badge_uses_above_slot_when_bottom_toolbar_slot_overlaps() { + let monitor = tests::test_monitor(); + let screen_rect = + Rect::from_min_size(Pos2::ZERO, Vec2::new(monitor.width as f32, monitor.height as f32)); + let capture_rect = Rect::from_min_size(Pos2::new(200.0, 180.0), Vec2::new(200.0, 300.0)); + let reserved_rect = Rect::from_min_size( + Pos2::new(200.0, 180.0 + 300.0 + TOOLBAR_CAPTURE_GAP_PX), + WindowRenderer::frozen_toolbar_size(&FrozenToolbarState::default()), + ); + let badge_rect = + WindowRenderer::selection_size_badge_rect_preferring_outside_with_reserved_rect( + screen_rect, + capture_rect, + Vec2::new(92.0, 26.0), + Some(reserved_rect), + ); + + assert_eq!(badge_rect.max.x, capture_rect.max.x); + assert_eq!(badge_rect.max.y, capture_rect.min.y - SELECTION_SIZE_BADGE_GAP_PX); + assert!(!badge_rect.intersects(reserved_rect)); +} + +#[test] +fn frozen_window_selection_size_badge_keeps_inside_fallback_when_bottom_toolbar_slot_overlaps() { + let monitor = tests::test_monitor(); + let screen_rect = + Rect::from_min_size(Pos2::ZERO, Vec2::new(monitor.width as f32, monitor.height as f32)); + let capture_rect = Rect::from_min_size(Pos2::new(200.0, 180.0), Vec2::new(200.0, 300.0)); + let reserved_rect = Rect::from_min_size( + Pos2::new(200.0, 180.0 + 300.0 + TOOLBAR_CAPTURE_GAP_PX), + WindowRenderer::frozen_toolbar_size(&FrozenToolbarState::default()), + ); + let badge_rect = WindowRenderer::selection_size_badge_rect_with_reserved_rect( + screen_rect, + capture_rect, + Vec2::new(92.0, 26.0), + Some(reserved_rect), + ); + + assert_eq!(badge_rect.max.x, capture_rect.max.x); + assert_eq!(badge_rect.max.y, capture_rect.max.y - SELECTION_SIZE_BADGE_INSIDE_MARGIN_PX); + assert!(!badge_rect.intersects(reserved_rect)); +} + #[test] fn frozen_selection_size_badge_keeps_below_placement_after_toolbar_leaves_default_slot() { let monitor = tests::test_monitor(); @@ -1395,6 +1440,7 @@ fn render_frozen_capture_affordance_keeps_tiny_frozen_badge_path() { monitor, screen_rect, HudTheme::Dark, + FrozenCaptureSource::None, None, false, true, diff --git a/packages/rsnap-overlay/src/overlay/toolbar_runtime.rs b/packages/rsnap-overlay/src/overlay/toolbar_runtime.rs index 0aae2f11..07e8ea16 100644 --- a/packages/rsnap-overlay/src/overlay/toolbar_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/toolbar_runtime.rs @@ -250,6 +250,7 @@ impl OverlaySession { self.config.selection_flow_stroke_width_px, false, false, + self.frozen_capture_source, self.frozen_capture_source == FrozenCaptureSource::FullscreenFallback, None, Some(&mut self.toolbar_state),