Fix GPUI crash when using cached view with Input (#50665)
The `draw()` method was calling `.pop()` on `next_frame.input_handlers` to extract the active input handler for the platform window. This reduced the Vec length, making cached `paint_range` indices stale. On the next frame, when a cached view called `reuse_paint()`, it would index into `rendered_frame.input_handlers` with out-of-bounds indices, causing a panic: `range start index 1 out of range for slice of length 0`. **Fix:** Use `.last_mut().and_then(|h| h.take())` instead of `.pop()` to extract the handler without changing the Vec length. The slot becomes `None`, which is already handled by `reuse_paint`'s `.take()` logic. Closes #50456 ### Testing - All 83 existing GPUI unit tests pass - Manually verified with [longbridge/gpui-component](https://github.com/longbridge/gpui-component) `cargo run --example dock`: - Switch to Input panel → type text → no crash ✅ - Before fix: crash immediately on first keystroke --- Before you mark this PR as ready for review, make sure that you have: - [x] Added a solid test coverage and/or screenshots from doing manual testing - [x] Done a self-review taking into account security and performance aspects - [ ] Aligned any UI changes with the [UI checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) *(N/A — no UI changes)* Release Notes: - Fixed a crash in GPUI when typing into an Input widget inside a cached view ([#50456](https://github.com/zed-industries/zed/issues/50456)) Co-authored-by: Mikayla Maki <mikayla@zed.dev>
This commit is contained in:
1 parent
af6f7d1004
commit
b3b7459e3e
1 file changed
+26
-4
@@ -2494,8 +2494,21 @@ impl Window {
|
||||
self.requested_autoscroll = None;
|
||||
|
||||
// Restore the previously-used input handler.
|
||||
// Place it back into a None slot (left by a previous .take()) so that
|
||||
// cached paint_range indices in reuse_paint find the handler at the
|
||||
// expected position.
|
||||
if let Some(input_handler) = self.platform_window.take_input_handler() {
|
||||
self.rendered_frame.input_handlers.push(Some(input_handler));
|
||||
if let Some(slot) = self
|
||||
.rendered_frame
|
||||
.input_handlers
|
||||
.iter_mut()
|
||||
.rev()
|
||||
.find(|h| h.is_none())
|
||||
{
|
||||
*slot = Some(input_handler);
|
||||
} else {
|
||||
self.rendered_frame.input_handlers.push(Some(input_handler));
|
||||
}
|
||||
}
|
||||
if !cx.mode.skip_drawing() {
|
||||
self.draw_roots(cx);
|
||||
@@ -2504,9 +2517,18 @@ impl Window {
|
||||
self.next_frame.window_active = self.active.get();
|
||||
|
||||
// Register requested input handler with the platform window.
|
||||
if let Some(input_handler) = self.next_frame.input_handlers.pop() {
|
||||
self.platform_window
|
||||
.set_input_handler(input_handler.unwrap());
|
||||
// Use .take() instead of .pop() to preserve Vec length, so that cached
|
||||
// paint_range indices remain valid for reuse_paint on the next frame.
|
||||
// Search backwards to find the last Some entry, since reuse_paint may
|
||||
// have copied None slots from the previous frame. (Fixes #50456)
|
||||
if let Some(input_handler) = self
|
||||
.next_frame
|
||||
.input_handlers
|
||||
.iter_mut()
|
||||
.rev()
|
||||
.find_map(|h| h.take())
|
||||
{
|
||||
self.platform_window.set_input_handler(input_handler);
|
||||
}
|
||||
|
||||
self.layout_engine.as_mut().unwrap().clear();
|
||||
|
||||
Reference in new issue
Block a user