diff --git a/flutter/lib/desktop/pages/terminal_page.dart b/flutter/lib/desktop/pages/terminal_page.dart index 44cccf112..17bd86eef 100644 --- a/flutter/lib/desktop/pages/terminal_page.dart +++ b/flutter/lib/desktop/pages/terminal_page.dart @@ -41,6 +41,7 @@ class _TerminalPageState extends State with AutomaticKeepAliveClientMixin { late FFI _ffi; late TerminalModel _terminalModel; + double? _cellHeight; @override void initState() { @@ -60,6 +61,17 @@ class _TerminalPageState extends State debugPrint( '[TerminalPage] Terminal model created for terminal ${widget.terminalId}'); + _terminalModel.onResizeExternal = (w, h, pw, ph) { + _cellHeight = ph * 1.0; + + // Schedule the setState for the next frame + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + setState(() {}); + } + }); + }; + // Register this terminal model with FFI for event routing _ffi.registerTerminalModel(widget.terminalId, _terminalModel); @@ -95,30 +107,48 @@ class _TerminalPageState extends State super.dispose(); } + // This method ensures that the number of visible rows is an integer by computing the + // extra space left after dividing the available height by the height of a single + // terminal row (`_cellHeight`) and distributing it evenly as top and bottom padding. + EdgeInsets _calculatePadding(double heightPx) { + if (_cellHeight == null) { + return const EdgeInsets.symmetric(horizontal: 5.0, vertical: 2.0); + } + final rows = (heightPx / _cellHeight!).floor(); + final extraSpace = heightPx - rows * _cellHeight!; + final topBottom = extraSpace / 2.0; + return EdgeInsets.symmetric(horizontal: 5.0, vertical: topBottom); + } + @override Widget build(BuildContext context) { super.build(context); return Scaffold( backgroundColor: Theme.of(context).scaffoldBackgroundColor, - body: TerminalView( - _terminalModel.terminal, - controller: _terminalModel.terminalController, - autofocus: true, - backgroundOpacity: 0.7, - padding: const EdgeInsets.symmetric(horizontal: 5.0, vertical: 2.0), - onSecondaryTapDown: (details, offset) async { - final selection = _terminalModel.terminalController.selection; - if (selection != null) { - final text = _terminalModel.terminal.buffer.getText(selection); - _terminalModel.terminalController.clearSelection(); - await Clipboard.setData(ClipboardData(text: text)); - } else { - final data = await Clipboard.getData('text/plain'); - final text = data?.text; - if (text != null) { - _terminalModel.terminal.paste(text); - } - } + body: LayoutBuilder( + builder: (context, constraints) { + final heightPx = constraints.maxHeight; + return TerminalView( + _terminalModel.terminal, + controller: _terminalModel.terminalController, + autofocus: true, + backgroundOpacity: 0.7, + padding: _calculatePadding(heightPx), + onSecondaryTapDown: (details, offset) async { + final selection = _terminalModel.terminalController.selection; + if (selection != null) { + final text = _terminalModel.terminal.buffer.getText(selection); + _terminalModel.terminalController.clearSelection(); + await Clipboard.setData(ClipboardData(text: text)); + } else { + final data = await Clipboard.getData('text/plain'); + final text = data?.text; + if (text != null) { + _terminalModel.terminal.paste(text); + } + } + }, + ); }, ), ); diff --git a/flutter/lib/models/terminal_model.dart b/flutter/lib/models/terminal_model.dart index c6ace8f8c..b32be65f1 100644 --- a/flutter/lib/models/terminal_model.dart +++ b/flutter/lib/models/terminal_model.dart @@ -27,6 +27,8 @@ class TerminalModel with ChangeNotifier { bool get isPeerWindows => parent.ffiModel.pi.platform == kPeerPlatformWindows; + void Function(int w, int h, int pw, int ph)? onResizeExternal; + Future _handleInput(String data) async { // If we press the `Enter` button on Android, // `data` can be '\r' or '\n' when using different keyboards. @@ -68,6 +70,10 @@ class TerminalModel with ChangeNotifier { if (w > 0 && h > 0 && pw > 0 && ph > 0) { debugPrint( '[TerminalModel] Terminal resized to ${w}x$h (pixel: ${pw}x$ph)'); + + // This piece of code must be placed before the conditional check in order to initialize properly. + onResizeExternal?.call(w, h, pw, ph); + if (_terminalOpened) { // Notify remote terminal of resize try {