mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
fix(tui): keep in-progress selection drags away from overlays
Mouse dispatch asked overlays first and returned on `{ handled: true }`, so an
excluded overlay that handles mouse events swallowed the motion and release of a
drag that had started on selectable content. A persistent interactive sidebar broke
the drag at its edge and copied nothing.
An in-progress selection drag now owns the pointer until release, the way a component
that received the press owns its gesture. Overlays, the scroll-to-end indicator, the
scrollbar and the layout are skipped for those events; a press still routes normally,
so clicking inside an overlay keeps working.
This commit is contained in:
parent
aa5a86deee
commit
3245ae2217
2 changed files with 50 additions and 0 deletions
|
|
@ -910,6 +910,20 @@ export class TuiAltScreen extends TuiBase implements ViewportTUI {
|
|||
|
||||
if (this.handleSearchMouseEvent(raw)) return;
|
||||
|
||||
// A selection drag that is already in progress keeps the pointer, the same way a component
|
||||
// that received the press owns the whole gesture. Without this, an overlay that handles mouse
|
||||
// events swallows the motion and the release, and the drag dies at its edge.
|
||||
if (
|
||||
type !== "press" &&
|
||||
((raw.button & 3) === 0 || ((raw.button & 3) === 3 && type === "release")) &&
|
||||
this.selectionPressActive &&
|
||||
!this.mouseCapture &&
|
||||
!this.mousePressTarget
|
||||
) {
|
||||
this.handleSelectionMouseEvent(raw);
|
||||
return;
|
||||
}
|
||||
|
||||
const overlay = this.dispatchMouseToOverlay(event);
|
||||
if (!overlay.hit) {
|
||||
if (this.handleScrollToEndIndicatorMouseEvent(raw)) return;
|
||||
|
|
|
|||
|
|
@ -1562,6 +1562,42 @@ describe("TuiAltScreen", () => {
|
|||
tui.stop();
|
||||
});
|
||||
|
||||
it("keeps an in-progress selection drag away from a mouse-handling excluded overlay", async () => {
|
||||
const terminal = new RecordingTerminal(20, 2);
|
||||
const copied: string[] = [];
|
||||
const overlayEvents: string[] = [];
|
||||
const tui = new TuiAltScreen(terminal, undefined, undefined, {
|
||||
copySelection: async (text) => {
|
||||
copied.push(text);
|
||||
return true;
|
||||
},
|
||||
});
|
||||
tui.setLayoutRoot(new ScrollView(new Text("abcdefghijklmnopqrst", 0, 0), { primary: true }));
|
||||
tui.showOverlay(
|
||||
new MouseRegion(new Text("SECRET", 0, 0), (event) => {
|
||||
overlayEvents.push(event.type);
|
||||
return { handled: true };
|
||||
}),
|
||||
{ row: 0, col: 5, width: 6, margin: 0, nonCapturing: true, selection: "exclude" },
|
||||
);
|
||||
tui.start();
|
||||
await terminal.waitForRender();
|
||||
|
||||
terminal.sendInput("\x1b[<0;1;1M");
|
||||
terminal.sendInput("\x1b[<32;7;1M");
|
||||
terminal.sendInput("\x1b[<0;7;1m");
|
||||
await terminal.waitForRender();
|
||||
assert.deepStrictEqual(copied, ["abcde"]);
|
||||
assert.deepStrictEqual(overlayEvents, []);
|
||||
|
||||
terminal.sendInput("\x1b[<0;7;1M");
|
||||
terminal.sendInput("\x1b[<0;7;1m");
|
||||
await terminal.waitForRender();
|
||||
assert.deepStrictEqual(overlayEvents, ["press", "release", "click"]);
|
||||
assert.deepStrictEqual(copied, ["abcde"]);
|
||||
tui.stop();
|
||||
});
|
||||
|
||||
it("does not repaint idle or zero-width selections on focus loss", async () => {
|
||||
const terminal = new RecordingTerminal(20, 4);
|
||||
const tui = new TuiAltScreen(terminal);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue