uzor_core/widgets/container/input.rs
1//! Container input adapter - Contract/Connector for scrollbar event handling
2//!
3//! **ContainerInputHandler is a CONTRACT/CONNECTOR trait** that connects:
4//! - Factory event handling logic (`factory/events.rs` - future)
5//! - External input systems (event loops, input managers, etc.)
6
7use crate::types::Rect;
8
9/// Input handler adapter for container scrollbar events
10///
11/// This trait defines the contract for converting raw input events into scroll actions.
12/// External projects implement this trait to customize scroll behavior (rare).
13pub trait ContainerInputHandler {
14 // =========================================================================
15 // Hit Testing
16 // =========================================================================
17
18 /// Test if mouse position is inside scrollbar rect
19 fn hit_test_scrollbar(&self, scrollbar_rect: &Rect, mouse_pos: (f64, f64)) -> bool {
20 let (mouse_x, mouse_y) = mouse_pos;
21 mouse_x >= scrollbar_rect.x
22 && mouse_x <= scrollbar_rect.x + scrollbar_rect.width
23 && mouse_y >= scrollbar_rect.y
24 && mouse_y <= scrollbar_rect.y + scrollbar_rect.height
25 }
26
27 // =========================================================================
28 // Scroll Calculations
29 // =========================================================================
30
31 /// Convert mouse Y position to scroll offset (for drag scrolling)
32 fn mouse_to_scroll_offset(
33 &self,
34 mouse_y: f64,
35 scrollbar_y: f64,
36 scrollbar_height: f64,
37 content_height: f64,
38 viewport_height: f64,
39 ) -> f64 {
40 if scrollbar_height <= 0.0 {
41 return 0.0;
42 }
43
44 let max_scroll = (content_height - viewport_height).max(0.0);
45 let ratio = ((mouse_y - scrollbar_y) / scrollbar_height).clamp(0.0, 1.0);
46 ratio * max_scroll
47 }
48
49 /// Convert scroll wheel delta to scroll offset change
50 fn scroll_to_delta(&self, scroll_delta: f64, _viewport_height: f64) -> f64 {
51 scroll_delta * 40.0
52 }
53
54 /// Clamp scroll offset to valid range [0, max_scroll]
55 fn clamp_scroll_offset(
56 &self,
57 offset: f64,
58 content_height: f64,
59 viewport_height: f64,
60 ) -> f64 {
61 let max_scroll = (content_height - viewport_height).max(0.0);
62 offset.clamp(0.0, max_scroll)
63 }
64
65 // =========================================================================
66 // Scrollbar Thumb Sizing
67 // =========================================================================
68
69 /// Calculate scrollbar thumb size based on viewport/content ratio
70 fn calculate_thumb_size(
71 &self,
72 viewport_height: f64,
73 content_height: f64,
74 scrollbar_height: f64,
75 min_thumb_height: f64,
76 ) -> f64 {
77 if content_height <= 0.0 || viewport_height >= content_height {
78 return scrollbar_height;
79 }
80
81 let ratio = viewport_height / content_height;
82 let thumb_height = ratio * scrollbar_height;
83 thumb_height.max(min_thumb_height)
84 }
85
86 /// Calculate scrollbar thumb Y position based on scroll offset
87 fn calculate_thumb_position(
88 &self,
89 scroll_offset: f64,
90 content_height: f64,
91 viewport_height: f64,
92 scrollbar_y: f64,
93 scrollbar_height: f64,
94 thumb_height: f64,
95 ) -> f64 {
96 let max_scroll = (content_height - viewport_height).max(0.0);
97 if max_scroll <= 0.0 {
98 return scrollbar_y;
99 }
100
101 let scroll_ratio = (scroll_offset / max_scroll).clamp(0.0, 1.0);
102 let max_thumb_travel = scrollbar_height - thumb_height;
103 scrollbar_y + scroll_ratio * max_thumb_travel
104 }
105}
106
107// =============================================================================
108// Default Input Handler Implementation
109// =============================================================================
110
111/// Default implementation of ContainerInputHandler
112#[derive(Clone, Copy, Debug, Default)]
113pub struct DefaultContainerInputHandler;
114
115impl ContainerInputHandler for DefaultContainerInputHandler {
116 // All methods use trait defaults (no overrides needed)
117}