Skip to content

Commit

Permalink
Merge pull request #50 from devmil/feature/composing_state
Browse files Browse the repository at this point in the history
Adds the composing state
  • Loading branch information
xtyxtyx authored Aug 2, 2021
2 parents 420f4c9 + f346e4c commit 73d1e58
Show file tree
Hide file tree
Showing 12 changed files with 169 additions and 49 deletions.
6 changes: 4 additions & 2 deletions lib/frontend/cache.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@ class TextLayoutCache {
return _cache[key];
}

Paragraph performAndCacheLayout(String text, TextStyle style, int key) {
Paragraph performAndCacheLayout(String text, TextStyle style, int? key) {
final builder = ParagraphBuilder(style.getParagraphStyle());
builder.pushStyle(style.getTextStyle());
builder.addText(text);

final paragraph = builder.build();
paragraph.layout(ParagraphConstraints(width: double.infinity));

_cache[key] = paragraph;
if (key != null) {
_cache[key] = paragraph;
}
return paragraph;
}

Expand Down
23 changes: 20 additions & 3 deletions lib/frontend/input_behavior_default.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import 'package:xterm/frontend/input_map.dart';
import 'package:xterm/xterm.dart';

class InputBehaviorDefault extends InputBehavior {
const InputBehaviorDefault();
InputBehaviorDefault();

@override
bool get acceptKeyStroke => true;
Expand All @@ -32,11 +32,28 @@ class InputBehaviorDefault extends InputBehavior {
}
}

String? _composingString = null;

@override
TextEditingValue? onTextEdit(
TextEditingValue value, TerminalUiInteraction terminal) {
terminal.raiseOnInput(value.text);
if (value == TextEditingValue.empty) {
var inputText = value.text;
// we just want to detect if a composing is going on and notify the terminal
// about it
if (value.composing.start != value.composing.end) {
_composingString = inputText;
terminal.updateComposingString(_composingString!);
return null;
}
//when we reach this point the composing state is over
if (_composingString != null) {
_composingString = null;
terminal.updateComposingString('');
}

terminal.raiseOnInput(inputText);

if (value == TextEditingValue.empty || inputText == '') {
return null;
} else {
return TextEditingValue.empty;
Expand Down
2 changes: 1 addition & 1 deletion lib/frontend/input_behavior_desktop.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:xterm/frontend/input_behavior_default.dart';

class InputBehaviorDesktop extends InputBehaviorDefault {
const InputBehaviorDesktop();
InputBehaviorDesktop();
}
2 changes: 1 addition & 1 deletion lib/frontend/input_behavior_mobile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import 'package:xterm/input/keys.dart';
import 'package:xterm/xterm.dart';

class InputBehaviorMobile extends InputBehaviorDefault {
const InputBehaviorMobile();
InputBehaviorMobile();

final acceptKeyStroke = false;

Expand Down
4 changes: 2 additions & 2 deletions lib/frontend/input_behaviors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import 'package:xterm/frontend/input_behavior_desktop.dart';
import 'package:xterm/frontend/input_behavior_mobile.dart';

class InputBehaviors {
static const desktop = InputBehaviorDesktop();
static final desktop = InputBehaviorDesktop();

static const mobile = InputBehaviorMobile();
static final mobile = InputBehaviorMobile();

static InputBehavior get platform {
if (Platform.I.isMobile) {
Expand Down
20 changes: 12 additions & 8 deletions lib/frontend/input_listener.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ typedef FocusHandler = void Function(bool);

abstract class InputListenerController {
void requestKeyboard();
void setCaretRect(Rect rect);
}

class InputListener extends StatefulWidget {
Expand Down Expand Up @@ -123,6 +124,7 @@ class InputListenerState extends State<InputListener>
);
}

@override
void requestKeyboard() {
if (widget.focusNode.hasFocus) {
openInputConnection();
Expand All @@ -131,6 +133,11 @@ class InputListenerState extends State<InputListener>
}
}

@override
void setCaretRect(Rect rect) {
_conn?.setCaretRect(rect);
}

void onFocusChange() {
if (widget.onFocus != null) {
widget.onFocus?.call(widget.focusNode.hasFocus);
Expand Down Expand Up @@ -184,6 +191,8 @@ class InputListenerState extends State<InputListener>

if (newValue != null) {
_conn?.setEditingState(newValue);
} else {
_conn?.setEditingState(TextEditingValue.empty);
}
}

Expand Down Expand Up @@ -211,14 +220,9 @@ class TerminalTextInputClient extends TextInputClient {
void updateEditingValue(TextEditingValue value) {
// print('updateEditingValue $value');

onInput(value);

// if (_savedValue == null || _savedValue.text == '') {
// onInput(value.text);
// } else if (_savedValue.text.length < value.text.length) {
// final diff = value.text.substring(_savedValue.text.length);
// onInput(diff);
// }
if (value.text != '') {
onInput(value);
}

_savedValue = value;
// print('updateEditingValue $value');
Expand Down
6 changes: 6 additions & 0 deletions lib/frontend/oscillator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@ class Oscillator with Observable {
return _value;
}

void restart() {
stop();
start();
}

void start() {
_value = true;
_shouldRun = true;
// only start right away when anyone is listening.
// the moment a listener gets registered the Oscillator will start
Expand Down
87 changes: 56 additions & 31 deletions lib/frontend/terminal_painters.dart
Original file line number Diff line number Diff line change
Expand Up @@ -177,35 +177,13 @@ class TerminalPainter extends CustomPainter {
color = color.withOpacity(0.5);
}

final styleToUse = (style.textStyleProvider != null)
? style.textStyleProvider!(
color: color,
fontSize: style.fontSize,
fontWeight: flags.hasFlag(CellFlags.bold)
? FontWeight.bold
: FontWeight.normal,
fontStyle: flags.hasFlag(CellFlags.italic)
? FontStyle.italic
: FontStyle.normal,
decoration: flags.hasFlag(CellFlags.underline)
? TextDecoration.underline
: TextDecoration.none,
)
: TextStyle(
color: color,
fontSize: style.fontSize,
fontWeight: flags.hasFlag(CellFlags.bold)
? FontWeight.bold
: FontWeight.normal,
fontStyle: flags.hasFlag(CellFlags.italic)
? FontStyle.italic
: FontStyle.normal,
decoration: flags.hasFlag(CellFlags.underline)
? TextDecoration.underline
: TextDecoration.none,
fontFamily: 'monospace',
fontFamilyFallback: style.fontFamily,
);
final styleToUse = PaintHelper.getStyleToUse(
style,
color,
bold: flags.hasFlag(CellFlags.bold),
italic: flags.hasFlag(CellFlags.italic),
underline: flags.hasFlag(CellFlags.underline),
);

character = textLayoutCache.performAndCacheLayout(
String.fromCharCode(codePoint), styleToUse, cellHash);
Expand All @@ -226,18 +204,27 @@ class CursorPainter extends CustomPainter {
final bool focused;
final bool blinkVisible;
final int cursorColor;
final int textColor;
final String composingString;
final TextLayoutCache textLayoutCache;
final TerminalStyle style;

CursorPainter({
required this.visible,
required this.charSize,
required this.focused,
required this.blinkVisible,
required this.cursorColor,
required this.textColor,
required this.composingString,
required this.textLayoutCache,
required this.style,
});

@override
void paint(Canvas canvas, Size size) {
if (blinkVisible && visible) {
bool isVisible = visible && (blinkVisible || composingString != '');
if (isVisible) {
_paintCursor(canvas);
}
}
Expand All @@ -249,7 +236,8 @@ class CursorPainter extends CustomPainter {
focused != oldDelegate.focused ||
visible != oldDelegate.visible ||
charSize.cellWidth != oldDelegate.charSize.cellWidth ||
charSize.cellHeight != oldDelegate.charSize.cellHeight;
charSize.cellHeight != oldDelegate.charSize.cellHeight ||
composingString != oldDelegate.composingString;
}
return true;
}
Expand All @@ -262,5 +250,42 @@ class CursorPainter extends CustomPainter {

canvas.drawRect(
Rect.fromLTWH(0, 0, charSize.cellWidth, charSize.cellHeight), paint);

if (composingString != '') {
final styleToUse = PaintHelper.getStyleToUse(style, Color(textColor));
final character = textLayoutCache.performAndCacheLayout(
composingString, styleToUse, null);
canvas.drawParagraph(character, Offset(0, 0));
}
}
}

class PaintHelper {
static TextStyle getStyleToUse(
TerminalStyle style,
Color color, {
bool bold = false,
bool italic = false,
bool underline = false,
}) {
return (style.textStyleProvider != null)
? style.textStyleProvider!(
color: color,
fontSize: style.fontSize,
fontWeight: bold ? FontWeight.bold : FontWeight.normal,
fontStyle: italic ? FontStyle.italic : FontStyle.normal,
decoration:
underline ? TextDecoration.underline : TextDecoration.none,
)
: TextStyle(
color: color,
fontSize: style.fontSize,
fontWeight: bold ? FontWeight.bold : FontWeight.normal,
fontStyle: italic ? FontStyle.italic : FontStyle.normal,
decoration:
underline ? TextDecoration.underline : TextDecoration.none,
fontFamily: 'monospace',
fontFamilyFallback: style.fontFamily,
);
}
}
31 changes: 31 additions & 0 deletions lib/frontend/terminal_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ class _TerminalViewState extends State<TerminalView> {
super.dispose();
}

GlobalKey _keyCursor = GlobalKey();

@override
Widget build(BuildContext context) {
return InputListener(
Expand All @@ -171,6 +173,22 @@ class _TerminalViewState extends State<TerminalView> {
onWidgetSize(constraints.maxWidth - widget.padding * 2,
constraints.maxHeight - widget.padding * 2);

if (_keyCursor.currentContext != null) {
/// this gets set so that the accent selection menu on MacOS pops up
/// at the right spot
final RenderBox cursorRenderObj =
_keyCursor.currentContext!.findRenderObject() as RenderBox;
final offset = cursorRenderObj.localToGlobal(Offset.zero);
InputListener.of(context)!.setCaretRect(
Rect.fromLTWH(
offset.dx,
offset.dy,
_cellSize.cellWidth,
_cellSize.cellHeight,
),
);
}

// use flutter's Scrollable to manage scrolling to better integrate
// with widgets such as Scrollbar.
return NotificationListener<ScrollNotification>(
Expand Down Expand Up @@ -282,11 +300,14 @@ class _TerminalViewState extends State<TerminalView> {
),
),
Positioned(
key: _keyCursor,
child: CursorView(
terminal: widget.terminal,
cellSize: _cellSize,
focusNode: widget.focusNode,
blinkOscillator: blinkOscillator,
style: widget.style,
textLayoutCache: textLayoutCache,
),
width: _cellSize.cellWidth,
height: _cellSize.cellHeight,
Expand Down Expand Up @@ -367,6 +388,7 @@ class _TerminalViewState extends State<TerminalView> {
}

void onKeyStroke(RawKeyEvent event) {
blinkOscillator.restart();
// TODO: find a way to stop scrolling immediately after key stroke.
widget.inputBehavior.onKeyStroke(event, widget.terminal);
widget.terminal.setScrollOffsetFromBottom(0);
Expand Down Expand Up @@ -395,11 +417,16 @@ class CursorView extends StatefulWidget {
final TerminalUiInteraction terminal;
final FocusNode? focusNode;
final Oscillator blinkOscillator;
final TerminalStyle style;
final TextLayoutCache textLayoutCache;

CursorView({
required this.terminal,
required this.cellSize,
required this.focusNode,
required this.blinkOscillator,
required this.style,
required this.textLayoutCache,
});

@override
Expand Down Expand Up @@ -432,6 +459,10 @@ class _CursorViewState extends State<CursorView> {
charSize: widget.cellSize,
blinkVisible: widget.blinkOscillator.value,
cursorColor: widget.terminal.cursorColor,
textColor: widget.terminal.backgroundColor,
style: widget.style,
composingString: widget.terminal.composingString,
textLayoutCache: widget.textLayoutCache,
),
);
}
Expand Down
11 changes: 11 additions & 0 deletions lib/terminal/terminal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -722,4 +722,15 @@ class Terminal with Observable implements TerminalUiInteraction {
_selection.init(Position(0, 0));
_selection.update(Position(terminalWidth, bufferHeight));
}

String _composingString = '';

@override
String get composingString => _composingString;

@override
void updateComposingString(String value) {
_composingString = value;
refresh();
}
}
Loading

0 comments on commit 73d1e58

Please sign in to comment.