|
@@ -16,18 +16,15 @@ import 'package:xterm/frontend/input_listener.dart';
|
|
|
import 'package:xterm/frontend/oscillator.dart';
|
|
import 'package:xterm/frontend/oscillator.dart';
|
|
|
import 'package:xterm/frontend/cache.dart';
|
|
import 'package:xterm/frontend/cache.dart';
|
|
|
import 'package:xterm/mouse/position.dart';
|
|
import 'package:xterm/mouse/position.dart';
|
|
|
-import 'package:xterm/terminal/terminal.dart';
|
|
|
|
|
|
|
+import 'package:xterm/terminal/terminal_ui_interaction.dart';
|
|
|
import 'package:xterm/theme/terminal_style.dart';
|
|
import 'package:xterm/theme/terminal_style.dart';
|
|
|
import 'package:xterm/util/bit_flags.dart';
|
|
import 'package:xterm/util/bit_flags.dart';
|
|
|
import 'package:xterm/util/hash_values.dart';
|
|
import 'package:xterm/util/hash_values.dart';
|
|
|
|
|
|
|
|
-typedef TerminalResizeHandler = void Function(int width, int height);
|
|
|
|
|
-
|
|
|
|
|
class TerminalView extends StatefulWidget {
|
|
class TerminalView extends StatefulWidget {
|
|
|
TerminalView({
|
|
TerminalView({
|
|
|
Key? key,
|
|
Key? key,
|
|
|
required this.terminal,
|
|
required this.terminal,
|
|
|
- this.onResize,
|
|
|
|
|
this.style = const TerminalStyle(),
|
|
this.style = const TerminalStyle(),
|
|
|
this.opacity = 1.0,
|
|
this.opacity = 1.0,
|
|
|
FocusNode? focusNode,
|
|
FocusNode? focusNode,
|
|
@@ -39,8 +36,7 @@ class TerminalView extends StatefulWidget {
|
|
|
inputBehavior = inputBehavior ?? InputBehaviors.platform,
|
|
inputBehavior = inputBehavior ?? InputBehaviors.platform,
|
|
|
super(key: key ?? ValueKey(terminal));
|
|
super(key: key ?? ValueKey(terminal));
|
|
|
|
|
|
|
|
- final Terminal terminal;
|
|
|
|
|
- final TerminalResizeHandler? onResize;
|
|
|
|
|
|
|
+ final TerminalUiInteraction terminal;
|
|
|
final FocusNode focusNode;
|
|
final FocusNode focusNode;
|
|
|
final bool autofocus;
|
|
final bool autofocus;
|
|
|
final ScrollController scrollController;
|
|
final ScrollController scrollController;
|
|
@@ -105,7 +101,7 @@ class _TerminalViewState extends State<TerminalView> {
|
|
|
|
|
|
|
|
void onTerminalChange() {
|
|
void onTerminalChange() {
|
|
|
_terminalScrollExtent =
|
|
_terminalScrollExtent =
|
|
|
- _cellSize.cellHeight * widget.terminal.buffer.scrollOffsetFromTop;
|
|
|
|
|
|
|
+ _cellSize.cellHeight * widget.terminal.scrollOffsetFromTop;
|
|
|
|
|
|
|
|
if (mounted) {
|
|
if (mounted) {
|
|
|
setState(() {});
|
|
setState(() {});
|
|
@@ -184,20 +180,23 @@ class _TerminalViewState extends State<TerminalView> {
|
|
|
// set viewport height.
|
|
// set viewport height.
|
|
|
offset.applyViewportDimension(constraints.maxHeight);
|
|
offset.applyViewportDimension(constraints.maxHeight);
|
|
|
|
|
|
|
|
- final minScrollExtent = 0.0;
|
|
|
|
|
|
|
+ if (widget.terminal.isReady) {
|
|
|
|
|
+ final minScrollExtent = 0.0;
|
|
|
|
|
|
|
|
- final maxScrollExtent = math.max(
|
|
|
|
|
- 0.0,
|
|
|
|
|
- _cellSize.cellHeight * widget.terminal.buffer.height -
|
|
|
|
|
- constraints.maxHeight);
|
|
|
|
|
|
|
+ final maxScrollExtent = math.max(
|
|
|
|
|
+ 0.0,
|
|
|
|
|
+ _cellSize.cellHeight * widget.terminal.bufferHeight -
|
|
|
|
|
+ constraints.maxHeight);
|
|
|
|
|
|
|
|
- // set how much the terminal can scroll
|
|
|
|
|
- offset.applyContentDimensions(minScrollExtent, maxScrollExtent);
|
|
|
|
|
|
|
+ // set how much the terminal can scroll
|
|
|
|
|
+ offset.applyContentDimensions(
|
|
|
|
|
+ minScrollExtent, maxScrollExtent);
|
|
|
|
|
|
|
|
- // syncronize pending terminal scroll extent to ScrollController
|
|
|
|
|
- if (_terminalScrollExtent != null) {
|
|
|
|
|
- position.correctPixels(_terminalScrollExtent!);
|
|
|
|
|
- _terminalScrollExtent = null;
|
|
|
|
|
|
|
+ // syncronize pending terminal scroll extent to ScrollController
|
|
|
|
|
+ if (_terminalScrollExtent != null) {
|
|
|
|
|
+ position.correctPixels(_terminalScrollExtent!);
|
|
|
|
|
+ _terminalScrollExtent = null;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return buildTerminal(context);
|
|
return buildTerminal(context);
|
|
@@ -217,26 +216,26 @@ class _TerminalViewState extends State<TerminalView> {
|
|
|
print('details : $details');
|
|
print('details : $details');
|
|
|
},
|
|
},
|
|
|
onTapDown: (detail) {
|
|
onTapDown: (detail) {
|
|
|
- if (widget.terminal.selection.isEmpty) {
|
|
|
|
|
|
|
+ if (widget.terminal.selection?.isEmpty ?? true) {
|
|
|
InputListener.of(context)!.requestKeyboard();
|
|
InputListener.of(context)!.requestKeyboard();
|
|
|
} else {
|
|
} else {
|
|
|
- widget.terminal.selection.clear();
|
|
|
|
|
|
|
+ widget.terminal.clearSelection();
|
|
|
}
|
|
}
|
|
|
final pos = detail.localPosition;
|
|
final pos = detail.localPosition;
|
|
|
final offset = getMouseOffset(pos.dx, pos.dy);
|
|
final offset = getMouseOffset(pos.dx, pos.dy);
|
|
|
- widget.terminal.mouseMode.onTap(widget.terminal, offset);
|
|
|
|
|
|
|
+ widget.terminal.onMouseTap(offset);
|
|
|
widget.terminal.refresh();
|
|
widget.terminal.refresh();
|
|
|
},
|
|
},
|
|
|
onPanStart: (detail) {
|
|
onPanStart: (detail) {
|
|
|
final pos = detail.localPosition;
|
|
final pos = detail.localPosition;
|
|
|
final offset = getMouseOffset(pos.dx, pos.dy);
|
|
final offset = getMouseOffset(pos.dx, pos.dy);
|
|
|
- widget.terminal.mouseMode.onPanStart(widget.terminal, offset);
|
|
|
|
|
|
|
+ widget.terminal.onPanStart(offset);
|
|
|
widget.terminal.refresh();
|
|
widget.terminal.refresh();
|
|
|
},
|
|
},
|
|
|
onPanUpdate: (detail) {
|
|
onPanUpdate: (detail) {
|
|
|
final pos = detail.localPosition;
|
|
final pos = detail.localPosition;
|
|
|
final offset = getMouseOffset(pos.dx, pos.dy);
|
|
final offset = getMouseOffset(pos.dx, pos.dy);
|
|
|
- widget.terminal.mouseMode.onPanUpdate(widget.terminal, offset);
|
|
|
|
|
|
|
+ widget.terminal.onPanUpdate(offset);
|
|
|
widget.terminal.refresh();
|
|
widget.terminal.refresh();
|
|
|
},
|
|
},
|
|
|
child: Container(
|
|
child: Container(
|
|
@@ -251,7 +250,7 @@ class _TerminalViewState extends State<TerminalView> {
|
|
|
),
|
|
),
|
|
|
),
|
|
),
|
|
|
color:
|
|
color:
|
|
|
- Color(widget.terminal.theme.background).withOpacity(widget.opacity),
|
|
|
|
|
|
|
+ Color(widget.terminal.backgroundColor).withOpacity(widget.opacity),
|
|
|
),
|
|
),
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
@@ -262,8 +261,8 @@ class _TerminalViewState extends State<TerminalView> {
|
|
|
final row = (py / _cellSize.cellHeight).floor();
|
|
final row = (py / _cellSize.cellHeight).floor();
|
|
|
|
|
|
|
|
final x = col;
|
|
final x = col;
|
|
|
- final y = widget.terminal.buffer.convertViewLineToRawLine(row) -
|
|
|
|
|
- widget.terminal.buffer.scrollOffsetFromBottom;
|
|
|
|
|
|
|
+ final y = widget.terminal.convertViewLineToRawLine(row) -
|
|
|
|
|
+ widget.terminal.scrollOffsetFromBottom;
|
|
|
|
|
|
|
|
return Position(x, y);
|
|
return Position(x, y);
|
|
|
}
|
|
}
|
|
@@ -272,6 +271,9 @@ class _TerminalViewState extends State<TerminalView> {
|
|
|
int? _lastTerminalHeight;
|
|
int? _lastTerminalHeight;
|
|
|
|
|
|
|
|
void onSize(double width, double height) {
|
|
void onSize(double width, double height) {
|
|
|
|
|
+ if (!widget.terminal.isReady) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
final termWidth = (width / _cellSize.cellWidth).floor();
|
|
final termWidth = (width / _cellSize.cellWidth).floor();
|
|
|
final termHeight = (height / _cellSize.cellHeight).floor();
|
|
final termHeight = (height / _cellSize.cellHeight).floor();
|
|
|
|
|
|
|
@@ -282,7 +284,6 @@ class _TerminalViewState extends State<TerminalView> {
|
|
|
_lastTerminalWidth = termWidth;
|
|
_lastTerminalWidth = termWidth;
|
|
|
_lastTerminalHeight = termHeight;
|
|
_lastTerminalHeight = termHeight;
|
|
|
|
|
|
|
|
- widget.onResize?.call(termWidth, termHeight);
|
|
|
|
|
widget.terminal.resize(termWidth, termHeight);
|
|
widget.terminal.resize(termWidth, termHeight);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -293,7 +294,7 @@ class _TerminalViewState extends State<TerminalView> {
|
|
|
void onKeyStroke(RawKeyEvent event) {
|
|
void onKeyStroke(RawKeyEvent event) {
|
|
|
// TODO: find a way to stop scrolling immediately after key stroke.
|
|
// TODO: find a way to stop scrolling immediately after key stroke.
|
|
|
widget.inputBehavior.onKeyStroke(event, widget.terminal);
|
|
widget.inputBehavior.onKeyStroke(event, widget.terminal);
|
|
|
- widget.terminal.buffer.setScrollOffsetFromBottom(0);
|
|
|
|
|
|
|
+ widget.terminal.setScrollOffsetFromBottom(0);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void onFocus(bool focused) {
|
|
void onFocus(bool focused) {
|
|
@@ -310,10 +311,7 @@ class _TerminalViewState extends State<TerminalView> {
|
|
|
void onScroll(double offset) {
|
|
void onScroll(double offset) {
|
|
|
final topOffset = (offset / _cellSize.cellHeight).ceil();
|
|
final topOffset = (offset / _cellSize.cellHeight).ceil();
|
|
|
final bottomOffset = widget.terminal.invisibleHeight - topOffset;
|
|
final bottomOffset = widget.terminal.invisibleHeight - topOffset;
|
|
|
-
|
|
|
|
|
- setState(() {
|
|
|
|
|
- widget.terminal.buffer.setScrollOffsetFromBottom(bottomOffset);
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ widget.terminal.setScrollOffsetFromBottom(bottomOffset);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -326,7 +324,7 @@ class TerminalPainter extends CustomPainter {
|
|
|
required this.charSize,
|
|
required this.charSize,
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- final Terminal terminal;
|
|
|
|
|
|
|
+ final TerminalUiInteraction terminal;
|
|
|
final TerminalView view;
|
|
final TerminalView view;
|
|
|
final Oscillator oscillator;
|
|
final Oscillator oscillator;
|
|
|
final bool focused;
|
|
final bool focused;
|
|
@@ -334,6 +332,9 @@ class TerminalPainter extends CustomPainter {
|
|
|
|
|
|
|
|
@override
|
|
@override
|
|
|
void paint(Canvas canvas, Size size) {
|
|
void paint(Canvas canvas, Size size) {
|
|
|
|
|
+ if (!terminal.isReady) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
_paintBackground(canvas);
|
|
_paintBackground(canvas);
|
|
|
|
|
|
|
|
// if (oscillator.value) {
|
|
// if (oscillator.value) {
|
|
@@ -355,7 +356,7 @@ class TerminalPainter extends CustomPainter {
|
|
|
final line = lines[row];
|
|
final line = lines[row];
|
|
|
final offsetY = row * charSize.cellHeight;
|
|
final offsetY = row * charSize.cellHeight;
|
|
|
// final cellCount = math.min(terminal.viewWidth, line.length);
|
|
// final cellCount = math.min(terminal.viewWidth, line.length);
|
|
|
- final cellCount = terminal.viewWidth;
|
|
|
|
|
|
|
+ final cellCount = terminal.terminalWidth;
|
|
|
|
|
|
|
|
for (var col = 0; col < cellCount; col++) {
|
|
for (var col = 0; col < cellCount; col++) {
|
|
|
final cellWidth = line.cellGetWidth(col);
|
|
final cellWidth = line.cellGetWidth(col);
|
|
@@ -373,6 +374,11 @@ class TerminalPainter extends CustomPainter {
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // when a program reports black as background then it "really" means transparent
|
|
|
|
|
+ if (effectBgColor == 0xFF000000) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// final cellFlags = line.cellGetFlags(i);
|
|
// final cellFlags = line.cellGetFlags(i);
|
|
|
// final cell = line.getCell(i);
|
|
// final cell = line.getCell(i);
|
|
|
// final attr = cell.attr;
|
|
// final attr = cell.attr;
|
|
@@ -395,19 +401,22 @@ class TerminalPainter extends CustomPainter {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void _paintSelection(Canvas canvas) {
|
|
void _paintSelection(Canvas canvas) {
|
|
|
|
|
+ final selection = terminal.selection;
|
|
|
|
|
+ if (selection == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
final paint = Paint()..color = Colors.white.withOpacity(0.3);
|
|
final paint = Paint()..color = Colors.white.withOpacity(0.3);
|
|
|
|
|
|
|
|
- for (var y = 0; y < terminal.viewHeight; y++) {
|
|
|
|
|
|
|
+ for (var y = 0; y < terminal.terminalHeight; y++) {
|
|
|
final offsetY = y * charSize.cellHeight;
|
|
final offsetY = y * charSize.cellHeight;
|
|
|
- final absoluteY = terminal.buffer.convertViewLineToRawLine(y) -
|
|
|
|
|
- terminal.buffer.scrollOffsetFromBottom;
|
|
|
|
|
|
|
+ final absoluteY = terminal.convertViewLineToRawLine(y) -
|
|
|
|
|
+ terminal.scrollOffsetFromBottom;
|
|
|
|
|
|
|
|
- for (var x = 0; x < terminal.viewWidth; x++) {
|
|
|
|
|
|
|
+ for (var x = 0; x < terminal.terminalWidth; x++) {
|
|
|
var cellCount = 0;
|
|
var cellCount = 0;
|
|
|
|
|
|
|
|
- while (
|
|
|
|
|
- terminal.selection.contains(Position(x + cellCount, absoluteY)) &&
|
|
|
|
|
- x + cellCount < terminal.viewWidth) {
|
|
|
|
|
|
|
+ while (selection.contains(Position(x + cellCount, absoluteY)) &&
|
|
|
|
|
+ x + cellCount < terminal.terminalWidth) {
|
|
|
cellCount++;
|
|
cellCount++;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -436,7 +445,7 @@ class TerminalPainter extends CustomPainter {
|
|
|
final line = lines[row];
|
|
final line = lines[row];
|
|
|
final offsetY = row * charSize.cellHeight;
|
|
final offsetY = row * charSize.cellHeight;
|
|
|
// final cellCount = math.min(terminal.viewWidth, line.length);
|
|
// final cellCount = math.min(terminal.viewWidth, line.length);
|
|
|
- final cellCount = terminal.viewWidth;
|
|
|
|
|
|
|
+ final cellCount = terminal.terminalWidth;
|
|
|
|
|
|
|
|
for (var col = 0; col < cellCount; col++) {
|
|
for (var col = 0; col < cellCount; col++) {
|
|
|
final width = line.cellGetWidth(col);
|
|
final width = line.cellGetWidth(col);
|
|
@@ -527,18 +536,18 @@ class TerminalPainter extends CustomPainter {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void _paintCursor(Canvas canvas) {
|
|
void _paintCursor(Canvas canvas) {
|
|
|
- final screenCursorY = terminal.cursorY + terminal.scrollOffset;
|
|
|
|
|
- if (screenCursorY < 0 || screenCursorY >= terminal.viewHeight) {
|
|
|
|
|
|
|
+ final screenCursorY = terminal.cursorY + terminal.scrollOffsetFromBottom;
|
|
|
|
|
+ if (screenCursorY < 0 || screenCursorY >= terminal.terminalHeight) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
final width = charSize.cellWidth *
|
|
final width = charSize.cellWidth *
|
|
|
- terminal.buffer.currentLine.cellGetWidth(terminal.cursorX).clamp(1, 2);
|
|
|
|
|
|
|
+ (terminal.currentLine?.cellGetWidth(terminal.cursorX).clamp(1, 2) ?? 1);
|
|
|
|
|
|
|
|
final offsetX = charSize.cellWidth * terminal.cursorX;
|
|
final offsetX = charSize.cellWidth * terminal.cursorX;
|
|
|
final offsetY = charSize.cellHeight * screenCursorY;
|
|
final offsetY = charSize.cellHeight * screenCursorY;
|
|
|
final paint = Paint()
|
|
final paint = Paint()
|
|
|
- ..color = Color(terminal.theme.cursor)
|
|
|
|
|
|
|
+ ..color = Color(terminal.cursorColor)
|
|
|
..strokeWidth = focused ? 0.0 : 1.0
|
|
..strokeWidth = focused ? 0.0 : 1.0
|
|
|
..style = focused ? PaintingStyle.fill : PaintingStyle.stroke;
|
|
..style = focused ? PaintingStyle.fill : PaintingStyle.stroke;
|
|
|
|
|
|