• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jzombie / term-wm / 20885462867

10 Jan 2026 10:38PM UTC coverage: 57.056% (+10.0%) from 47.071%
20885462867

Pull #20

github

web-flow
Merge d8a3a10c3 into bfecf0f75
Pull Request #20: Initial clipboard and offscreen buffer support

2046 of 3183 new or added lines in 26 files covered. (64.28%)

78 existing lines in 15 files now uncovered.

6788 of 11897 relevant lines covered (57.06%)

9.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

57.35
/src/component_context.rs
1
use std::cell::RefCell;
2
use std::rc::Rc;
3

4
// Shared component rendering context
5
//
6
// `ComponentContext` carries UI metadata that components may need during
7
// rendering, resizing, and event handling. It centralizes focus and overlay
8
// state so the component trait remains stable and components do not rely on
9
// ad-hoc boolean parameters.
10

11
/// Context passed to `Component` trait methods describing UI state.
12
///
13
/// - `focused`: whether the component is currently focused.
14
/// - `overlay`: whether the component is being rendered as an overlay (e.g. dialog).
15
/// - `viewport`: logical offset describing which portion of the component's
16
///   content is currently visible inside a scrolling container.
17
#[derive(Debug, Clone)]
18
pub struct ComponentContext {
19
    focused: bool,
20
    overlay: bool,
21
    viewport: ViewportContext,
22
    viewport_handle: Option<ViewportHandle>,
23
}
24

25
/// Viewport metadata describing how the component is projected into a
26
/// potentially scrolling parent container.
27
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
28
pub struct ViewportContext {
29
    pub offset_x: usize,
30
    pub offset_y: usize,
31
    pub width: usize,
32
    pub height: usize,
33
}
34

35
#[derive(Debug, Clone)]
36
pub struct ViewportHandle {
37
    pub(crate) shared: Rc<RefCell<ViewportSharedState>>,
38
}
39

40
#[derive(Debug, Default)]
41
pub struct ViewportSharedState {
42
    pub(crate) offset_x: usize,
43
    pub(crate) offset_y: usize,
44
    pub(crate) width: usize,
45
    pub(crate) height: usize,
46
    pub(crate) content_width: usize,
47
    pub(crate) content_height: usize,
48
    pub(crate) pending_offset_x: Option<usize>,
49
    pub(crate) pending_offset_y: Option<usize>,
50
}
51

52
impl ViewportSharedState {
53
    pub(crate) fn max_offset_x(&self) -> usize {
2✔
54
        self.content_width.saturating_sub(self.width)
2✔
55
    }
2✔
56

57
    pub(crate) fn max_offset_y(&self) -> usize {
4✔
58
        self.content_height.saturating_sub(self.height)
4✔
59
    }
4✔
60
}
61

62
impl ViewportHandle {
63
    pub fn info(&self) -> ViewportContext {
35✔
64
        let inner = self.shared.borrow();
35✔
65
        ViewportContext {
35✔
66
            offset_x: inner.offset_x,
35✔
67
            offset_y: inner.offset_y,
35✔
68
            width: inner.width,
35✔
69
            height: inner.height,
35✔
70
        }
35✔
71
    }
35✔
72

73
    pub fn set_content_size(&self, width: usize, height: usize) {
21✔
74
        let mut inner = self.shared.borrow_mut();
21✔
75
        inner.content_width = width;
21✔
76
        inner.content_height = height;
21✔
77
    }
21✔
78

79
    pub fn scroll_vertical_to(&self, offset: usize) {
4✔
80
        let mut inner = self.shared.borrow_mut();
4✔
81
        let max = inner.max_offset_y();
4✔
82
        let clamped = offset.min(max);
4✔
83
        inner.offset_y = clamped;
4✔
84
        inner.pending_offset_y = Some(clamped);
4✔
85
    }
4✔
86

NEW
87
    pub fn scroll_vertical_by(&self, delta: isize) {
×
NEW
88
        let mut inner = self.shared.borrow_mut();
×
NEW
89
        let max = inner.max_offset_y();
×
NEW
90
        let current = inner.offset_y as isize;
×
NEW
91
        let next = (current + delta).clamp(0, max as isize) as usize;
×
NEW
92
        inner.offset_y = next;
×
NEW
93
        inner.pending_offset_y = Some(next);
×
NEW
94
    }
×
95

NEW
96
    pub fn ensure_vertical_visible(&self, start: usize, end: usize) {
×
NEW
97
        if start >= end {
×
NEW
98
            return;
×
NEW
99
        }
×
NEW
100
        let mut inner = self.shared.borrow_mut();
×
NEW
101
        let height = inner.height;
×
NEW
102
        if height == 0 {
×
NEW
103
            return;
×
NEW
104
        }
×
NEW
105
        let current = inner.offset_y;
×
NEW
106
        if start < current {
×
NEW
107
            inner.offset_y = start;
×
NEW
108
            inner.pending_offset_y = Some(start);
×
NEW
109
        } else if end > current + height {
×
NEW
110
            let new_offset = end.saturating_sub(height);
×
NEW
111
            let max = inner.max_offset_y();
×
NEW
112
            let clamped = new_offset.min(max);
×
NEW
113
            inner.offset_y = clamped;
×
NEW
114
            inner.pending_offset_y = Some(clamped);
×
NEW
115
        }
×
NEW
116
    }
×
117

118
    pub fn scroll_horizontal_to(&self, offset: usize) {
1✔
119
        let mut inner = self.shared.borrow_mut();
1✔
120
        let max = inner.max_offset_x();
1✔
121
        let clamped = offset.min(max);
1✔
122
        inner.offset_x = clamped;
1✔
123
        inner.pending_offset_x = Some(clamped);
1✔
124
    }
1✔
125

126
    pub fn scroll_horizontal_by(&self, delta: isize) {
1✔
127
        let mut inner = self.shared.borrow_mut();
1✔
128
        let max = inner.max_offset_x();
1✔
129
        let current = inner.offset_x as isize;
1✔
130
        let next = (current + delta).clamp(0, max as isize) as usize;
1✔
131
        inner.offset_x = next;
1✔
132
        inner.pending_offset_x = Some(next);
1✔
133
    }
1✔
134

NEW
135
    pub fn ensure_horizontal_visible(&self, start: usize, end: usize) {
×
NEW
136
        if start >= end {
×
NEW
137
            return;
×
NEW
138
        }
×
NEW
139
        let mut inner = self.shared.borrow_mut();
×
NEW
140
        let width = inner.width;
×
NEW
141
        if width == 0 {
×
NEW
142
            return;
×
NEW
143
        }
×
NEW
144
        let current = inner.offset_x;
×
NEW
145
        if start < current {
×
NEW
146
            inner.offset_x = start;
×
NEW
147
            inner.pending_offset_x = Some(start);
×
NEW
148
        } else if end > current + width {
×
NEW
149
            let new_offset = end.saturating_sub(width);
×
NEW
150
            let max = inner.max_offset_x();
×
NEW
151
            let clamped = new_offset.min(max);
×
NEW
152
            inner.offset_x = clamped;
×
NEW
153
            inner.pending_offset_x = Some(clamped);
×
NEW
154
        }
×
NEW
155
    }
×
156
}
157

158
impl ComponentContext {
159
    /// Create a new `ComponentContext` with the given focus state.
160
    pub const fn new(focused: bool) -> Self {
22✔
161
        Self {
22✔
162
            focused,
22✔
163
            overlay: false,
22✔
164
            viewport: ViewportContext {
22✔
165
                offset_x: 0,
22✔
166
                offset_y: 0,
22✔
167
                width: 0,
22✔
168
                height: 0,
22✔
169
            },
22✔
170
            viewport_handle: None,
22✔
171
        }
22✔
172
    }
22✔
173

174
    /// Returns whether the component is focused.
175
    pub const fn focused(&self) -> bool {
22✔
176
        self.focused
22✔
177
    }
22✔
178

179
    /// Returns whether the component is being rendered as an overlay.
NEW
180
    pub const fn overlay(&self) -> bool {
×
NEW
181
        self.overlay
×
NEW
182
    }
×
183

184
    /// Returns the viewport offset for this component.
185
    pub const fn viewport(&self) -> ViewportContext {
25✔
186
        self.viewport
25✔
187
    }
25✔
188

189
    /// Returns a handle that allows requesting viewport adjustments, if available.
190
    pub fn viewport_handle(&self) -> Option<ViewportHandle> {
25✔
191
        self.viewport_handle.clone()
25✔
192
    }
25✔
193

194
    /// Return a new `ComponentContext` with a modified `focused` flag.
NEW
195
    pub fn with_focus(&self, focused: bool) -> Self {
×
NEW
196
        let mut ctx = self.clone();
×
NEW
197
        ctx.focused = focused;
×
NEW
198
        ctx
×
NEW
199
    }
×
200

201
    /// Return a new `ComponentContext` with a modified `overlay` flag.
202
    pub fn with_overlay(&self, overlay: bool) -> Self {
1✔
203
        let mut ctx = self.clone();
1✔
204
        ctx.overlay = overlay;
1✔
205
        ctx
1✔
206
    }
1✔
207

208
    /// Return a new `ComponentContext` with updated viewport metadata.
209
    pub fn with_viewport(&self, viewport: ViewportContext, handle: Option<ViewportHandle>) -> Self {
25✔
210
        let mut ctx = self.clone();
25✔
211
        ctx.viewport = viewport;
25✔
212
        ctx.viewport_handle = handle;
25✔
213
        ctx
25✔
214
    }
25✔
215
}
216

217
impl Default for ComponentContext {
218
    fn default() -> Self {
1✔
219
        Self::new(false)
1✔
220
    }
1✔
221
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc