diff --git a/packages/flutter/lib/src/material/input.dart b/packages/flutter/lib/src/material/input.dart
index e1092dbb92..1597ef4059 100644
--- a/packages/flutter/lib/src/material/input.dart
+++ b/packages/flutter/lib/src/material/input.dart
@@ -6,6 +6,7 @@ import 'package:flutter/animation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
+import 'package:sky_services/editing/editing.mojom.dart' as mojom;
import 'colors.dart';
import 'debug.dart';
@@ -13,7 +14,7 @@ import 'icon.dart';
import 'theme.dart';
export 'package:flutter/rendering.dart' show ValueChanged;
-export 'package:flutter/services.dart' show KeyboardType;
+export 'package:sky_services/editing/editing.mojom.dart' show KeyboardType;
/// A material design text input field.
class Input extends StatefulComponent {
@@ -110,10 +111,11 @@ class _InputState extends State {
void _attachOrDetachKeyboard(bool focused) {
if (focused && !_isAttachedToKeyboard) {
- _keyboardHandle = keyboard.show(_editableString.createStub(), config.keyboardType);
- _keyboardHandle.setText(_editableString.text);
- _keyboardHandle.setSelection(_editableString.selection.start,
- _editableString.selection.end);
+ _keyboardHandle = keyboard.attach(_editableString.createStub(),
+ new mojom.KeyboardConfiguration()
+ ..type = config.keyboardType);
+ _keyboardHandle.setEditingState(_editableString.editingState);
+ _keyboardHandle.show();
} else if (!focused && _isAttachedToKeyboard) {
_keyboardHandle.release();
_keyboardHandle = null;
@@ -124,7 +126,7 @@ class _InputState extends State {
void _requestKeyboard() {
if (Focus.at(context)) {
assert(_isAttachedToKeyboard);
- _keyboardHandle.showByRequest();
+ _keyboardHandle.show();
} else {
Focus.moveTo(config.key);
// we'll get told to rebuild and we'll take care of the keyboard then
@@ -149,7 +151,8 @@ class _InputState extends State {
void _handleSelectionChanged(TextSelection selection) {
if (_isAttachedToKeyboard) {
- _keyboardHandle.setSelection(selection.start, selection.end);
+ _editableString.setSelection(selection);
+ _keyboardHandle.setEditingState(_editableString.editingState);
} else {
_editableString.setSelection(selection);
_requestKeyboard();
diff --git a/packages/flutter/lib/src/services/keyboard.dart b/packages/flutter/lib/src/services/keyboard.dart
index 8e58165ef4..43751f8253 100644
--- a/packages/flutter/lib/src/services/keyboard.dart
+++ b/packages/flutter/lib/src/services/keyboard.dart
@@ -4,11 +4,11 @@
import 'dart:async';
-import 'package:mojo_services/keyboard/keyboard.mojom.dart';
+import 'package:sky_services/editing/editing.mojom.dart' as mojom;
import 'shell.dart';
-export 'package:mojo_services/keyboard/keyboard.mojom.dart';
+export 'package:sky_services/editing/editing.mojom.dart' show KeyboardType;
/// An interface to the system's keyboard.
///
@@ -19,22 +19,24 @@ class Keyboard {
// The service is exposed in case you need direct access.
// However, as a general rule, you should be able to do
// most of what you need using only this class.
- final KeyboardService service;
+ final mojom.Keyboard service;
KeyboardHandle _currentHandle;
bool _hidePending = false;
- KeyboardHandle show(KeyboardClientStub stub, KeyboardType keyboardType) {
+ KeyboardHandle attach(mojom.KeyboardClientStub stub, mojom.KeyboardConfiguration configuration) {
assert(stub != null);
_currentHandle?.release();
assert(_currentHandle == null);
- _currentHandle = new KeyboardHandle._show(this, stub, keyboardType);
+ _currentHandle = new KeyboardHandle._(this);
+ service.setClient(stub, configuration);
return _currentHandle;
}
void _scheduleHide() {
- if (_hidePending) return;
+ if (_hidePending)
+ return;
_hidePending = true;
// Schedule a deferred task that hides the keyboard. If someone else shows
@@ -50,21 +52,17 @@ class Keyboard {
}
class KeyboardHandle {
-
- KeyboardHandle._show(Keyboard keyboard, KeyboardClientStub stub, KeyboardType keyboardType) : _keyboard = keyboard {
- _keyboard.service.show(stub, keyboardType);
- _attached = true;
- }
+ KeyboardHandle._(Keyboard keyboard) : _keyboard = keyboard, _attached = true;
final Keyboard _keyboard;
bool _attached;
bool get attached => _attached;
- void showByRequest() {
+ void show() {
assert(_attached);
assert(_keyboard._currentHandle == this);
- _keyboard.service.showByRequest();
+ _keyboard.service.show();
}
void release() {
@@ -77,25 +75,18 @@ class KeyboardHandle {
assert(_keyboard._currentHandle != this);
}
- void setText(String text) {
+ void setEditingState(mojom.EditingState state) {
assert(_attached);
assert(_keyboard._currentHandle == this);
- _keyboard.service.setText(text);
+ _keyboard.service.setEditingState(state);
}
-
- void setSelection(int start, int end) {
- assert(_attached);
- assert(_keyboard._currentHandle == this);
- _keyboard.service.setSelection(start, end);
- }
-
}
-KeyboardServiceProxy _initKeyboardProxy() {
- KeyboardServiceProxy proxy = new KeyboardServiceProxy.unbound();
+mojom.KeyboardProxy _initKeyboardProxy() {
+ mojom.KeyboardProxy proxy = new mojom.KeyboardProxy.unbound();
shell.connectToService(null, proxy);
return proxy;
}
-final KeyboardServiceProxy _keyboardProxy = _initKeyboardProxy();
+final mojom.KeyboardProxy _keyboardProxy = _initKeyboardProxy();
final Keyboard keyboard = new Keyboard(_keyboardProxy.ptr);
diff --git a/packages/flutter/lib/src/widgets/editable.dart b/packages/flutter/lib/src/widgets/editable.dart
index 361db24cd6..3eb9e8e6df 100644
--- a/packages/flutter/lib/src/widgets/editable.dart
+++ b/packages/flutter/lib/src/widgets/editable.dart
@@ -3,9 +3,8 @@
// found in the LICENSE file.
import 'dart:async';
-import 'dart:math' as math;
-import 'package:mojo_services/keyboard/keyboard.mojom.dart';
+import 'package:sky_services/editing/editing.mojom.dart' as mojom;
import 'package:flutter/painting.dart';
import 'package:flutter/rendering.dart';
@@ -18,7 +17,16 @@ export 'package:flutter/painting.dart' show TextSelection;
const Duration _kCursorBlinkHalfPeriod = const Duration(milliseconds: 500);
-class _KeyboardClientImpl implements KeyboardClient {
+TextSelection _getTextSelectionFromEditingState(mojom.EditingState state) {
+ return new TextSelection(
+ baseOffset: state.selectionBase,
+ extentOffset: state.selectionExtent,
+ affinity: TextAffinity.values[state.selectionAffinity.mojoEnumValue],
+ isDirectional: state.selectionIsDirectional
+ );
+}
+
+class _KeyboardClientImpl implements mojom.KeyboardClient {
_KeyboardClientImpl({
String text: '',
TextSelection selection,
@@ -45,86 +53,29 @@ class _KeyboardClientImpl implements KeyboardClient {
TextSelection selection;
/// A keyboard client stub that can be attached to a keyboard service.
- KeyboardClientStub createStub() {
- return new KeyboardClientStub.unbound()..impl = this;
+ mojom.KeyboardClientStub createStub() {
+ return new mojom.KeyboardClientStub.unbound()..impl = this;
}
- void _delete(TextRange range) {
- if (range.isCollapsed || !range.isValid) return;
- text = range.textBefore(text) + range.textAfter(text);
+ mojom.EditingState get editingState {
+ return new mojom.EditingState()
+ ..text = text
+ ..selectionBase = selection.baseOffset
+ ..selectionExtent = selection.extentOffset
+ ..selectionAffinity = mojom.TextAffinity.values[selection.affinity.index]
+ ..selectionIsDirectional = selection.isDirectional
+ ..composingBase = composing.start
+ ..composingExtent = composing.end;
}
- TextRange _append(String newText) {
- int start = text.length;
- text += newText;
- return new TextRange(start: start, end: start + newText.length);
- }
-
- TextRange _replace(TextRange range, String newText) {
- assert(range.isValid);
-
- String before = range.textBefore(text);
- String after = range.textAfter(text);
-
- text = before + newText + after;
- return new TextRange(
- start: before.length, end: before.length + newText.length);
- }
-
- TextRange _replaceOrAppend(TextRange range, String newText) {
- if (!range.isValid) return _append(newText);
- return _replace(range, newText);
- }
-
- void commitCompletion(CompletionData completion) {
- // TODO(abarth): Not implemented.
- }
-
- void commitCorrection(CorrectionData correction) {
- // TODO(abarth): Not implemented.
- }
-
- void commitText(String text, int newCursorPosition) {
- // TODO(abarth): Why is |newCursorPosition| always 1?
- TextRange committedRange = _replaceOrAppend(composing, text);
- selection = new TextSelection.collapsed(offset: committedRange.end);
- composing = TextRange.empty;
+ void updateEditingState(mojom.EditingState state) {
+ text = state.text;
+ selection = _getTextSelectionFromEditingState(state);
+ composing = new TextRange(start: state.composingBase, end: state.composingExtent);
onUpdated();
}
- void deleteSurroundingText(int beforeLength, int afterLength) {
- TextRange beforeRange = new TextRange(
- start: selection.start - beforeLength, end: selection.start);
- int afterRangeEnd = math.min(selection.end + afterLength, text.length);
- TextRange afterRange =
- new TextRange(start: selection.end, end: afterRangeEnd);
- _delete(afterRange);
- _delete(beforeRange);
- selection = new TextSelection(
- baseOffset: math.max(selection.start - beforeLength, 0),
- extentOffset: math.max(selection.end - beforeLength, 0)
- );
- onUpdated();
- }
-
- void setComposingRegion(int start, int end) {
- composing = new TextRange(start: start, end: end);
- onUpdated();
- }
-
- void setComposingText(String text, int newCursorPosition) {
- // TODO(abarth): Why is |newCursorPosition| always 1?
- composing = _replaceOrAppend(composing, text);
- selection = new TextSelection.collapsed(offset: composing.end);
- onUpdated();
- }
-
- void setSelection(int start, int end) {
- selection = new TextSelection(baseOffset: start, extentOffset: end);
- onUpdated();
- }
-
- void submit(SubmitAction action) {
+ void submit(mojom.SubmitAction action) {
composing = TextRange.empty;
onSubmitted();
}
@@ -162,10 +113,12 @@ class EditableString {
_client.selection = selection;
}
+ mojom.EditingState get editingState => _client.editingState;
+
/// A keyboard client stub that can be attached to a keyboard service.
///
/// See [Keyboard].
- KeyboardClientStub createStub() => _client.createStub();
+ mojom.KeyboardClientStub createStub() => _client.createStub();
void didDetachKeyboard() {
_client.composing = TextRange.empty;
diff --git a/packages/flutter/test/widget/input_test.dart b/packages/flutter/test/widget/input_test.dart
index 904025dde9..a885ca2595 100644
--- a/packages/flutter/test/widget/input_test.dart
+++ b/packages/flutter/test/widget/input_test.dart
@@ -5,31 +5,28 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
-import 'package:flutter/services.dart';
-import 'package:mojo_services/keyboard/keyboard.mojom.dart';
+import 'package:sky_services/editing/editing.mojom.dart' as mojom;
import 'package:test/test.dart';
import '../services/mock_services.dart';
-class MockKeyboard implements KeyboardService {
- KeyboardClient client;
+class MockKeyboard implements mojom.Keyboard {
+ mojom.KeyboardClient client;
- void show(KeyboardClientStub client, KeyboardType type) {
+ void setClient(mojom.KeyboardClientStub client, mojom.KeyboardConfiguration configuraiton) {
this.client = client.impl;
}
- void showByRequest() {}
+ void show() {}
void hide() {}
- void setText(String text) {}
-
- void setSelection(int start, int end) {}
+ void setEditingState(mojom.EditingState state) {}
}
void main() {
MockKeyboard mockKeyboard = new MockKeyboard();
- serviceMocker.registerMockService(KeyboardService.serviceName, mockKeyboard);
+ serviceMocker.registerMockService(mojom.Keyboard.serviceName, mockKeyboard);
test('Editable text has consistent size', () {
testWidgets((WidgetTester tester) {
@@ -56,7 +53,10 @@ void main() {
void enterText(String testValue) {
// Simulate entry of text through the keyboard.
expect(mockKeyboard.client, isNotNull);
- mockKeyboard.client.setComposingText(testValue, testValue.length);
+ mockKeyboard.client.updateEditingState(new mojom.EditingState()
+ ..text = testValue
+ ..composingBase = 0
+ ..composingExtent = testValue.length);
// Check that the onChanged event handler fired.
expect(inputValue, equals(testValue));
@@ -109,46 +109,14 @@ void main() {
checkCursorToggle();
// Try the test again with a nonempty EditableText.
- mockKeyboard.client.setComposingText('X', 1);
+ mockKeyboard.client.updateEditingState(new mojom.EditingState()
+ ..text = 'X'
+ ..selectionBase = 1
+ ..selectionExtent = 1);
checkCursorToggle();
});
});
- test('Selection remains valid', () {
- testWidgets((WidgetTester tester) {
- GlobalKey inputKey = new GlobalKey();
-
- Widget builder() {
- return new Center(
- child: new Material(
- child: new Input(
- key: inputKey,
- hintText: 'Placeholder'
- )
- )
- );
- }
-
- tester.pumpWidget(builder());
-
- const String testValue = 'ABC';
- mockKeyboard.client.commitText(testValue, testValue.length);
- dynamic input = inputKey.currentState;
-
- // Delete characters and verify that the selection follows the length
- // of the text.
- for (int i = 0; i < testValue.length; i++) {
- mockKeyboard.client.deleteSurroundingText(1, 0);
- expect(input.editableValue.selection.start, equals(testValue.length - i - 1));
- }
-
- // Delete a characters when the text is empty. The selection should
- // remain at zero.
- mockKeyboard.client.deleteSurroundingText(1, 0);
- expect(input.editableValue.selection.start, equals(0));
- });
- });
-
test('hideText control test', () {
testWidgets((WidgetTester tester) {
GlobalKey inputKey = new GlobalKey();
@@ -168,7 +136,10 @@ void main() {
tester.pumpWidget(builder());
const String testValue = 'ABC';
- mockKeyboard.client.commitText(testValue, testValue.length);
+ mockKeyboard.client.updateEditingState(new mojom.EditingState()
+ ..text = testValue
+ ..selectionBase = testValue.length
+ ..selectionExtent = testValue.length);
tester.pump();
});