remove dead Scrollable-era widgets + simulateScroll param

Three widgets under lib/src/terminal/src/ui/ and one TerminalView
parameter were leftovers from the era when TerminalView wrapped its
viewport in a real Scrollable. The Scrollable path was replaced with
PointerScrollEvent → PgUp/PgDown translation (alive, well-tested in
terminal_view_test.dart); these helpers stayed behind with tests but
zero production callers.

Drops:
- TerminalScrollGestureHandler (scroll_handler.dart, 100 LOC)
- InfiniteScrollView (infinite_scroll_view.dart, 117 LOC)
- KeyboardVisibilty (keyboard_visibility.dart, 59 LOC) — last
  production caller was removed in 048e835
- TerminalView.simulateScroll parameter — declared, never read
- The matching test groups + imports in ui_widget_test.dart
- The KeyboardVisibilty export from the terminal barrel

Net: -585 lines from lib/ + test/, no behavior change, and
infinite_scroll_view.dart stops being the 90%-coverage outlier we
were apologising for in the previous test sweep.

Same shape as T-93 (dead onTapUp wiring), T-95 (dead tertiary tap),
and 048e835 (dead scrollController plumbing). Public-or-tested
surface that no caller exercised.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 18:20:50 +02:00
co-authored by Claude Opus 4.7
parent 387a336520
commit 94ff623708
6 changed files with 3 additions and 585 deletions
-8
View File
@@ -48,7 +48,6 @@ class TerminalView extends StatefulWidget {
this.onKeyEvent,
this.readOnly = false,
this.hardwareKeyboardOnly = false,
this.simulateScroll = true,
});
/// The underlying terminal that this widget renders.
@@ -132,13 +131,6 @@ class TerminalView extends StatefulWidget {
/// also prevent any on-screen keyboard to be shown.
final bool hardwareKeyboardOnly;
/// If true, when the terminal is in alternate buffer (for example running
/// vim, man, etc), if the application does not declare that it can handle
/// scrolling, the terminal will simulate scrolling by sending up/down arrow
/// keys to the application. This is standard behavior for most terminal
/// emulators. True by default.
final bool simulateScroll;
@override
State<TerminalView> createState() => TerminalViewState();
}
@@ -1,117 +0,0 @@
// Based on xterm.dart v4.0.0 by xuty (MIT). See LICENSE in this directory.
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
/// The function called when the user scrolls the [InfiniteScrollView]. [offset]
/// is the current offset of the scroll view, ranging from [double.negativeInfinity]
/// to [double.infinity].
typedef ScrollCallback = void Function(double offset);
/// A [Scrollable] that can be scrolled infinitely in both directions. When
/// scroll happens, the [onScroll] callback is called with the new offset.
class InfiniteScrollView extends StatelessWidget {
const InfiniteScrollView({
super.key,
required this.onScroll,
required this.child,
});
final ScrollCallback onScroll;
final Widget child;
@override
Widget build(BuildContext context) {
return Scrollable(
viewportBuilder: (context, position) {
return _InfiniteScrollView(
position: position,
onScroll: onScroll,
child: child,
);
},
);
}
}
class _InfiniteScrollView extends SingleChildRenderObjectWidget {
const _InfiniteScrollView({
// super.key,
super.child,
required this.position,
required this.onScroll,
});
final ViewportOffset position;
final ScrollCallback onScroll;
@override
RenderObject createRenderObject(BuildContext context) {
return _RenderInfiniteScrollView(
position: position,
onScroll: onScroll,
);
}
@override
void updateRenderObject(
BuildContext context,
_RenderInfiniteScrollView renderObject,
) {
renderObject
..position = position
..onScroll = onScroll;
}
}
class _RenderInfiniteScrollView extends RenderShiftedBox {
_RenderInfiniteScrollView({
RenderBox? child,
required ViewportOffset position,
required ScrollCallback onScroll,
}) : _position = position,
_scrollCallback = onScroll,
super(child);
ViewportOffset _position;
set position(ViewportOffset value) {
if (_position == value) return;
if (attached) _position.removeListener(markNeedsLayout);
_position = value;
if (attached) _position.addListener(markNeedsLayout);
markNeedsLayout();
}
ScrollCallback _scrollCallback;
set onScroll(ScrollCallback value) {
if (_scrollCallback == value) return;
_scrollCallback = value;
markNeedsLayout();
}
void _onScroll() {
_scrollCallback(_position.pixels);
}
@override
void attach(covariant PipelineOwner owner) {
super.attach(owner);
_position.addListener(_onScroll);
}
@override
void detach() {
super.detach();
_position.removeListener(_onScroll);
}
@override
void performLayout() {
child?.layout(constraints, parentUsesSize: true);
size = child?.size ?? Size.zero;
_position.applyViewportDimension(size.height);
_position.applyContentDimensions(double.negativeInfinity, double.infinity);
}
}
@@ -1,59 +0,0 @@
// Based on xterm.dart v4.0.0 by xuty (MIT). See LICENSE in this directory.
import 'package:flutter/widgets.dart';
class KeyboardVisibilty extends StatefulWidget {
const KeyboardVisibilty({
super.key,
required this.child,
this.onKeyboardShow,
this.onKeyboardHide,
});
final Widget child;
final VoidCallback? onKeyboardShow;
final VoidCallback? onKeyboardHide;
@override
KeyboardVisibiltyState createState() => KeyboardVisibiltyState();
}
class KeyboardVisibiltyState extends State<KeyboardVisibilty> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeMetrics() {
final bottomInset = View.of(context).viewInsets.bottom;
if (bottomInset != _lastBottomInset) {
if (bottomInset > 0) {
widget.onKeyboardShow?.call();
} else {
widget.onKeyboardHide?.call();
}
}
_lastBottomInset = bottomInset;
super.didChangeMetrics();
}
var _lastBottomInset = 0.0;
@override
Widget build(BuildContext context) {
return widget.child;
}
}
-100
View File
@@ -1,100 +0,0 @@
// Based on xterm.dart v4.0.0 by xuty (MIT). See LICENSE in this directory.
import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart';
import 'package:clide/src/terminal/terminal.dart';
class TerminalScrollGestureHandler extends StatefulWidget {
const TerminalScrollGestureHandler({
super.key,
required this.terminal,
required this.getCellOffset,
required this.getLineHeight,
this.simulateScroll = true,
required this.child,
});
final Terminal terminal;
final CellOffset Function(Offset) getCellOffset;
final double Function() getLineHeight;
final bool simulateScroll;
final Widget child;
@override
State<TerminalScrollGestureHandler> createState() => _TerminalScrollGestureHandlerState();
}
class _TerminalScrollGestureHandlerState extends State<TerminalScrollGestureHandler> {
var isAltBuffer = false;
var _lastPointerPosition = Offset.zero;
@override
void initState() {
widget.terminal.addListener(_onTerminalUpdated);
isAltBuffer = widget.terminal.isUsingAltBuffer;
super.initState();
}
@override
void dispose() {
widget.terminal.removeListener(_onTerminalUpdated);
super.dispose();
}
@override
void didUpdateWidget(covariant TerminalScrollGestureHandler oldWidget) {
if (oldWidget.terminal != widget.terminal) {
oldWidget.terminal.removeListener(_onTerminalUpdated);
widget.terminal.addListener(_onTerminalUpdated);
isAltBuffer = widget.terminal.isUsingAltBuffer;
}
super.didUpdateWidget(oldWidget);
}
void _onTerminalUpdated() {
if (isAltBuffer != widget.terminal.isUsingAltBuffer) {
isAltBuffer = widget.terminal.isUsingAltBuffer;
setState(() {});
}
}
void _sendScrollEvent(bool up) {
final position = widget.getCellOffset(_lastPointerPosition);
final handled = widget.terminal.mouseInput(
up ? TerminalMouseButton.wheelUp : TerminalMouseButton.wheelDown,
TerminalMouseButtonState.down,
position,
);
if (!handled && widget.simulateScroll) {
widget.terminal.keyInput(
up ? TerminalKey.arrowUp : TerminalKey.arrowDown,
);
}
}
void _onPointerSignal(PointerSignalEvent event) {
if (event is! PointerScrollEvent) return;
_lastPointerPosition = event.position;
final lineHeight = widget.getLineHeight();
if (lineHeight <= 0) return;
final lines = (event.scrollDelta.dy / lineHeight).round().clamp(-5, 5);
for (var i = 0; i < lines.abs(); i++) {
_sendScrollEvent(lines < 0);
}
}
@override
Widget build(BuildContext context) {
if (!isAltBuffer) return widget.child;
// Intercept scroll at the pointer level so the inner Scrollable
// (normal buffer) never sees the event in alt-buffer mode.
return Listener(
onPointerSignal: _onPointerSignal,
onPointerDown: (event) => _lastPointerPosition = event.position,
child: widget.child,
);
}
}
-1
View File
@@ -27,7 +27,6 @@ export 'src/terminal.dart';
export 'src/terminal_view.dart';
export 'src/ui/controller.dart';
export 'src/ui/cursor_type.dart';
export 'src/ui/keyboard_visibility.dart';
export 'src/ui/pointer_input.dart';
export 'src/ui/selection_mode.dart';
export 'src/ui/shortcut/shortcuts.dart';