iOS: allow text selection inside mouse-reporting apps (TUIs)#580
iOS: allow text selection inside mouse-reporting apps (TUIs)#580AlexDemzz wants to merge 1 commit into
Conversation
Double tap (word) and triple tap (line) now select even when the application has mouse reporting on (alt buffer or normal buffer with tracking), instead of forwarding the tap as a click. A single tap with an active selection dismisses it and re-enables scroll forwarding, matching the basic-shell behaviour; a single tap with no selection still forwards a click so interaction with mouse-reporting apps stays intact. The tap routing is factored into a pure, platform-independent TerminalTapPolicy so the behaviour is covered by the test suite without a UIKit gesture pipeline.
|
Up @migueldeicaza. I've pushed my app with the npm patch but it would be cool if it can be merged |
|
Hello! One challenge with this, is that I think that the way to do this on Mac is to call an API to flip this (I think it is Command-R on Terminal.app), and I think that generally, we need a gesture where the user would do this on their app (like "Toggle reporting"). Your idea has merit, but it immediately triggers the "Let us think about whether there are any downsides or this causes problems downstream). So I do not really have an objection to it in principle, but I have to cross the t's and dot the i's. That is the sole reason for my delay. But since I have you here, do you have thoughts on having a UI button to flip this on/off? |
|
Hello @migueldeicaza, i think a toggle button will be weird, as this PR report the actual SwiftTerm text selection behavior on TUI interface. Here SwiftTerm behavior for non TUI (double tap + drag) Here with the PR on TUI. (double tap + drag) |
|
Ok, since we are adding a policy enumeration, we could have a flag that drives the policy "Should the selection toggle be driven by an API call, or can the user use these gestures". Additionally I had Claude do a code review, and these are worth addressing:
Old non-reporting path (.orig:750-765): clear the selection and then fall through to the near-cursor check, which could showContextMenu. New .dismissSelection case (757-760) only clears + This is the one genuinely-undocumented behavior loss. It's narrow (usually the copy/paste menu is visible during a selection, so the old code took the hideMenu() path anyway — the 2 & 3. Framed as regressions, but your own docs say they're intentional
I'm calling these out only so you can confirm the tradeoffs are what you want; they match the intent you wrote down, so I would not treat them as defects.
action(tapCount: 2, …) unconditionally returns .selectWord (and 3 → .selectLine), so guard … == .selectWord else { return } is always true. The hasActiveSelection / mouseReportingActive |
What
On iOS, tap-to-select already works in a normal shell (mouse reporting off): a double tap selects a word, a triple tap selects a line, and a single tap clears the selection. The same gestures do nothing inside an application that captures the mouse (full-screen apps with mouse reporting on, such as vim, htop, or any TUI), because every tap is forwarded to the application as a mouse event before selection can run.
Concretely, each tap handler today branches on mouse reporting:
So the selection path is only ever reached in a normal shell. Under mouse reporting the early return wins, which means a word or line can never be selected inside a TUI, and an existing selection cannot be cleared by tapping. The only existing bypass is a hardware shift key, which touch devices do not have.
The existing normal-shell selection code
The selection logic already lives in the
elsebranch of each handler onmain(Sources/SwiftTerm/iOS/iOSTerminalView.swift); it is simply unreachable under mouse reporting.doubleTap(word):tripleTap(line):singleTap(clear an active selection):This change does not add new selection behaviour; it lets these existing paths run under mouse reporting too.
Change
The three tap handlers now route through a small pure policy so the existing normal-shell selection behaviour also applies under mouse reporting:
The decision is factored into
TerminalTapPolicy.action(tapCount:hasActiveSelection:mouseReportingActive:), a pure, platform-independent function. The gesture handlers only run on a device or simulator, so keeping the decision separate lets it be unit-tested in the regularswift testsuite without standing up a UIKit gesture pipeline.Tests
TerminalTapPolicyTestscovers the full routing table:Notes
allowSelectionUnderMouseReporting) rather than being the default, happy to adjust.