Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/flterm/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## Unreleased

### Added

- **Tracked touch policy**: `TerminalGestureSettings.touchMouseTracking` can
preserve terminal taps while routing touch and stylus drags to scrolling.

## 0.0.4

### Breaking
Expand Down
3 changes: 2 additions & 1 deletion packages/flterm/lib/flterm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ export 'src/foundation/terminal_gesture_settings.dart'
GestureModifier,
LineSelectMode,
TerminalGestureSettings,
TerminalSelectionShape;
TerminalSelectionShape,
TouchMouseTracking;
export 'src/foundation/terminal_theme.dart'
show
CursorTheme,
Expand Down
2 changes: 1 addition & 1 deletion packages/flterm/lib/src/foundation/callbacks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ typedef OnResize = void Function(int cols, int rows);
/// ```
typedef TerminalMouseEvent = ({
MouseAction action,
MouseButton button,
MouseButton? button,
double pixelX,
double pixelY,
});
27 changes: 22 additions & 5 deletions packages/flterm/lib/src/foundation/terminal_gesture_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,9 @@ enum LineSelectMode {
full,
}

/// Controls which terminal selection affordances are enabled and how press
/// gestures behave.
/// Controls terminal selection and tracked-pointer gesture behavior.
///
/// Passed to [TerminalView.gestureSettings]. Only affects selection
/// behavior: mouse tracking (for terminal programs) and focus gestures
/// work regardless of these settings.
/// Passed to [TerminalView.gestureSettings].
///
/// ```dart
/// TerminalView(
Expand Down Expand Up @@ -61,6 +58,14 @@ final class TerminalGestureSettings {
/// [TerminalController.selectAll] still selects programmatically.
final bool selectAllShortcut;

/// How touch-like pointers interact with terminal mouse tracking.
///
/// [TouchMouseTracking.direct] (default) forwards touch down, motion, and up
/// events directly to the terminal program. [TouchMouseTracking.tapAndScroll]
/// forwards recognized taps as clicks while leaving drags to terminal
/// scrolling.
final TouchMouseTracking touchMouseTracking;

/// How triple-click line selection determines the end column.
///
/// [LineSelectMode.content] (default) trims trailing empty cells.
Expand Down Expand Up @@ -103,6 +108,7 @@ final class TerminalGestureSettings {
this.dragSelection = true,
this.selectAllShortcut = true,
this.longPressSelection = true,
this.touchMouseTracking = .direct,
this.lineSelectMode = .content,
this.blockSelectionModifier = .alt,
this.selectionBehaviors = .standard,
Expand All @@ -121,6 +127,7 @@ final class TerminalGestureSettings {
dragSelection,
longPressSelection,
selectAllShortcut,
touchMouseTracking,
wordBoundaries,
);

Expand All @@ -140,9 +147,19 @@ final class TerminalGestureSettings {
dragSelection == other.dragSelection &&
longPressSelection == other.longPressSelection &&
selectAllShortcut == other.selectAllShortcut &&
touchMouseTracking == other.touchMouseTracking &&
wordBoundaries == other.wordBoundaries;
}

/// How touch-like pointers interact with terminal mouse tracking.
enum TouchMouseTracking {
/// Forwards touch down, motion, and up as terminal mouse events.
direct,

/// Forwards recognized taps as clicks and leaves drags to scrolling.
tapAndScroll,
}

/// Selection shape used for gestures that start without a keyboard modifier.
enum TerminalSelectionShape {
/// Selects contiguous terminal text.
Expand Down
43 changes: 31 additions & 12 deletions packages/flterm/lib/src/widgets/terminal_controller_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ class TerminalControllerImpl extends TerminalController

@override
final Terminal terminal;
final _renderState = RenderState();
final _keyEncoder = KeyEncoder();
final _mouseEncoder = MouseEncoder();
late final SelectionGestureDriver _selectionGesture;
Expand All @@ -52,6 +51,7 @@ class TerminalControllerImpl extends TerminalController
var _cursorKeyApplication = false;
Brightness _brightness = .dark;
var _cursorBlinking = true;
var _mouseButtonPressed = false;
var _wasFocused = false;
var _selectionMutationDepth = 0;

Expand All @@ -78,6 +78,8 @@ class TerminalControllerImpl extends TerminalController
maxScrollback: config.scrollbackLimit,
),
super.base() {
_lastCols = config.cols;
_lastRows = config.rows;
_selectionGesture = SelectionGestureDriver(terminal);
installDefaultKittyPngDecoder();
_textInput
Expand Down Expand Up @@ -258,7 +260,6 @@ class TerminalControllerImpl extends TerminalController
_selectionGesture.dispose();
_keyEncoder.dispose();
_mouseEncoder.dispose();
_renderState.dispose();
terminal.dispose();
super.dispose();
}
Expand Down Expand Up @@ -332,16 +333,32 @@ class TerminalControllerImpl extends TerminalController

@override
void handleMouseEvent(TerminalMouseEvent event) {
final button = event.button;
_mouseEvent
..action = event.action
..button = event.button
..mods = _currentMods()
..setPosition(
x: event.pixelX * _lastDevicePixelRatio,
y: event.pixelY * _lastDevicePixelRatio,
);
if (button == null) {
_mouseEvent.clearButton();
} else {
_mouseEvent.button = button;
}
if (event.action == .press &&
button != .four &&
button != .five &&
button != null) {
_mouseButtonPressed = true;
}
_mouseEncoder.sync(terminal);
_mouseEncoder.setAnyButtonPressed(pressed: _mouseButtonPressed);
final result = _mouseEncoder.encode(_mouseEvent);
if (event.action == .release) {
_mouseButtonPressed = false;
_mouseEncoder.setAnyButtonPressed(pressed: false);
}
if (result.isEmpty) return;
_emitOutput(utf8.encode(result));
}
Expand Down Expand Up @@ -386,10 +403,11 @@ class TerminalControllerImpl extends TerminalController
}

@override
void handleScroll(int lines) {
void handleScroll(int lines, {Offset? localPosition}) {
if (_activeScreen != .alternate || lines == 0) return;

if (_mouseTracking != .none) {
if (localPosition == null) return;
final button = lines < 0 ? MouseButton.four : MouseButton.five;
final count = lines.abs();

Expand All @@ -400,7 +418,10 @@ class TerminalControllerImpl extends TerminalController
..action = .press
..button = button
..mods = _currentMods()
..setPosition(x: 0, y: 0);
..setPosition(
x: localPosition.dx * _lastDevicePixelRatio,
y: localPosition.dy * _lastDevicePixelRatio,
);
final result = _mouseEncoder.encode(_mouseEvent);
if (result.isNotEmpty) _emitOutput(utf8.encode(result));
}
Expand Down Expand Up @@ -716,10 +737,8 @@ class TerminalControllerImpl extends TerminalController
void _emitOutput(Uint8List bytes) => onOutput?.call(bytes);

void _ensureGridSize() {
if (_lastRows > 0 && _lastCols > 0) return;
_renderState.update(terminal);
_lastRows = _renderState.rows;
_lastCols = _renderState.cols;
if (_lastRows <= 0) _lastRows = _config.rows;
if (_lastCols <= 0) _lastCols = _config.cols;
}

bool _extendSelection(LogicalKeyboardKey arrowKey) {
Expand Down Expand Up @@ -791,10 +810,10 @@ class TerminalControllerImpl extends TerminalController
}

TerminalSizeInfo _handleSizeQuery() {
_renderState.update(terminal);
_ensureGridSize();
return TerminalSizeInfo(
rows: _renderState.rows,
columns: _renderState.cols,
rows: _lastRows,
columns: _lastCols,
cellWidth: (_lastMetrics.cellWidth * _lastDevicePixelRatio).round(),
cellHeight: (_lastMetrics.cellHeight * _lastDevicePixelRatio).round(),
);
Expand Down
Loading