From eeeca67e55885618284181f7988fb5b03cab49ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sat, 26 Sep 2026 21:12:38 -0400 Subject: [PATCH] Files: a press on a download's button stays with the button gpui runs the click of every pressed element under the pointer, so a press on a finished download's Show button also opened the file through the pressable row. The actions' box stops the press on mouse down, so the row starts no click of its own. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 1 + TASKS.md | 1 + src/files/tests/transfers.rs | 21 +++++++++++++++++++-- src/files/transfer.rs | 6 ++++-- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 91a4510..2cf25af 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -70,6 +70,7 @@ Ely GPUI Component. A component library for GPUI, in light and dark. - A palette goes through `navigation::palette::Palette`: query field, grouped rows, cursor, keys and focus live there. CommandPalette, QuickOpen, QuickSwitcher, SearchPalette and QuickLauncher only build rows. - A menu goes through `menus::menu`: rows, submenus, keys, focus and presses outside live there, for DropdownMenu, OverflowMenu, SplitButton, ContextMenu, SearchableMenu and MenuBar. A press on the host's own box is the host's to handle, so a trigger toggles and a right click moves a context menu without a close and reopen in one event. - gpui focuses the nearest focusable ancestor on mouse down, and apps wrap everything in a `FocusScope`. A press that must leave focus alone calls `prevent_default`, as buttons and context regions do. +- gpui runs the click of every pressed element under the pointer, a row's with its button's. A pressable row that holds buttons stops their press at their box on mouse down (`files::transfer`), as `overlays::media_button` stops its own. - gpui draws a window's first frame inside `open_window`. State the first frame reads, such as `cx.set_menus`, is set before it opens. - gpui clicks a focused element when Enter or Space is released. An overlay that hands focus back picks on release too; a pick on press returns focus first, and the release clicks the opener again. An edit in place that hands focus back keeps its text on Enter's release (`forms::InlineEdit`). The press itself bubbles, so a container that takes Space leaves a focused button its own: `overlays::media_button` stops it. - A focus handle a component hands back lives across all its modes: take it with `tab_stop` before any early return, or keyed state drops it while the other mode draws and the handed-back focus lands nowhere. diff --git a/TASKS.md b/TASKS.md index 5b127d4..9dca411 100644 --- a/TASKS.md +++ b/TASKS.md @@ -327,3 +327,4 @@ Per-component lines live in `tasks/`. Tags there: | T27b | 2 | PASS | | | T27c | 1 | FAIL | recent and found files were keyed by their place, so the cursor left a file an open moved | | T27c | 2 | PASS | | +| T27d | 1 | FAIL | a press on a finished download's Show button also opened the file through its row | diff --git a/src/files/tests/transfers.rs b/src/files/tests/transfers.rs index ff4110c..9c5951c 100644 --- a/src/files/tests/transfers.rs +++ b/src/files/tests/transfers.rs @@ -1,6 +1,6 @@ use gpui::{ - Context, Entity, IntoElement, ParentElement, Render, SharedString, Styled, TestAppContext, - VisualTestContext, Window, div, px, + Context, Entity, IntoElement, Modifiers, ParentElement, Render, SharedString, Styled, + TestAppContext, VisualTestContext, Window, div, px, }; use super::{press, settle, setup}; @@ -255,3 +255,20 @@ fn a_download_comes_down() { up.upload = true; let _ = DownloadManager::new("downloads", [up]); } + +#[gpui::test] +fn a_press_on_show_shows_and_opens_nothing(cx: &mut TestAppContext) { + setup(cx); + let (host, cx) = cx.add_window_view(|_, _| Downloading { asked: Vec::new() }); + cx.update(|window, _| window.activate_window()); + settle(cx); + let actions = cx + .debug_bounds("transfer-actions done") + .expect("the finished row's actions draw"); + cx.simulate_click(actions.center(), Modifiers::none()); + settle(cx); + assert_eq!( + host.read_with(cx, |host, _| host.asked.clone()), + ["show done"] + ); +} diff --git a/src/files/transfer.rs b/src/files/transfer.rs index 2999353..48c882b 100644 --- a/src/files/transfer.rs +++ b/src/files/transfer.rs @@ -1,8 +1,8 @@ use std::rc::Rc; use gpui::{ - AnyElement, App, ElementId, InteractiveElement, IntoElement, ParentElement, SharedString, - StatefulInteractiveElement, Styled, Window, div, prelude::*, + AnyElement, App, ElementId, InteractiveElement, IntoElement, MouseButton, ParentElement, + SharedString, StatefulInteractiveElement, Styled, Window, div, prelude::*, }; use super::FileIcon; @@ -201,8 +201,10 @@ pub(super) fn row(id: ElementId, transfer: &Transfer, asks: &Asks, cx: &App) -> ) .child( div() + .debug_selector(move || format!("transfer-actions {key}")) .flex_none() .flex() + .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation()) .children(actions.into_iter().flatten()), ) .into_any_element()