Make a material design Input component

We don't yet have a focus controller, which means once this control becomes
focused there's no way for it to lose focus.

R=eseidel@chromium.org

Review URL: https://codereview.chromium.org/999553002
This commit is contained in:
Adam Barth 2015-03-10 14:58:12 -07:00
parent 7e9bbbec21
commit a04c6b09ef
5 changed files with 126 additions and 67 deletions

View file

@ -4,7 +4,7 @@
import 'package:sky/services/keyboard/keyboard.mojom.dart';
typedef void StringChangedCallback(EditableString updated);
typedef void StringUpdated();
class TextRange {
final int start;
@ -23,11 +23,11 @@ class EditableString implements KeyboardClient {
TextRange composing = const TextRange.empty();
TextRange selection = const TextRange.empty();
final StringChangedCallback onChanged;
final StringUpdated onUpdated;
KeyboardClientStub stub;
EditableString({this.text: '', this.onChanged}) {
EditableString({this.text: '', this.onUpdated}) {
stub = new KeyboardClientStub.unbound()..impl = this;
}
@ -85,7 +85,7 @@ class EditableString implements KeyboardClient {
TextRange committedRange = _replaceOrAppend(composing, text);
selection = new TextRange.collapsed(committedRange.end);
composing = const TextRange.empty();
onChanged(this);
onUpdated();
}
void deleteSurroundingText(int beforeLength, int afterLength) {
@ -97,23 +97,23 @@ class EditableString implements KeyboardClient {
_delete(beforeRange);
selection = new TextRange(start: selection.start - beforeLength,
end: selection.end - beforeLength);
onChanged(this);
onUpdated();
}
void setComposingRegion(int start, int end) {
composing = new TextRange(start: start, end: end);
onChanged(this);
onUpdated();
}
void setComposingText(String text, int newCursorPosition) {
// TODO(abarth): Why is |newCursorPosition| always 1?
composing = _replaceOrAppend(composing, text);
selection = new TextRange.collapsed(composing.end);
onChanged(this);
onUpdated();
}
void setSelection(int start, int end) {
selection = new TextRange(start: start, end: end);
onChanged(this);
onUpdated();
}
}

View file

@ -3,73 +3,35 @@
// found in the LICENSE file.
import '../../framework/fn.dart';
import '../../framework/shell.dart' as shell;
import '../../framework/theme/colors.dart';
import 'dart:async';
import 'dart:math';
import 'editable_string.dart';
import 'package:sky/services/keyboard/keyboard.mojom.dart';
class EditableText extends Component {
static Style _style = new Style('''
display: paragraph;
white-space: pre-wrap;
padding: 10px;
height: 200px;
background-color: lightblue;'''
static final Style _style = new Style('''
display: inline;'''
);
static Style _cusorStyle = new Style('''
static final Style _cusorStyle = new Style('''
display: inline-block;
width: 2px;
height: 1.2em;
vertical-align: top;
background-color: blue;'''
background-color: ${Blue[500]};'''
);
static Style _composingStyle = new Style('''
static final Style _composingStyle = new Style('''
display: inline;
text-decoration: underline;'''
);
KeyboardServiceProxy _service;
EditableString _string;
EditableString value;
bool focused;
Timer _cursorTimer;
bool _showCursor = false;
EditableText({Object key}) : super(key: key, stateful: true) {
_string = new EditableString(text: '', onChanged: _handleTextChanged);
events.listen('click', _handleClick);
events.listen('focus', _handleFocus);
events.listen('blur', _handleBlur);
}
void _handleTextChanged(EditableString string) {
setState(() {
_string = string;
_showCursor = true;
_restartCursorTimer();
});
}
void _handleClick(_) {
if (_service != null)
return;
_service = new KeyboardServiceProxy.unbound();
shell.requestService(_service);
_service.ptr.show(_string.stub);
_restartCursorTimer();
setState(() {
_showCursor = true;
});
}
void _handleFocus(_) {
print("_handleFocus");
}
void _handleBlur(_) {
print("_handleBlur");
EditableText({Object key, this.value, this.focused})
: super(key: key, stateful: true) {
}
void _cursorTick(Timer timer) {
@ -78,28 +40,39 @@ class EditableText extends Component {
});
}
void _restartCursorTimer() {
if (_cursorTimer != null)
_cursorTimer.cancel();
void _startCursorTimer() {
_showCursor = true;
_cursorTimer = new Timer.periodic(
new Duration(milliseconds: 500), _cursorTick);
}
void didUnmount() {
void _stopCursorTimer() {
_cursorTimer.cancel();
_cursorTimer = null;
_showCursor = false;
}
void didUnmount() {
if (_cursorTimer != null)
_stopCursorTimer();
}
Node build() {
if (focused && _cursorTimer == null)
_startCursorTimer();
else if (!focused && _cursorTimer != null)
_stopCursorTimer();
List<Node> children = new List<Node>();
if (!_string.composing.isValid) {
children.add(new Text(_string.text));
if (!value.composing.isValid) {
children.add(new Text(value.text));
} else {
String beforeComposing = _string.textBefore(_string.composing);
String beforeComposing = value.textBefore(value.composing);
if (!beforeComposing.isEmpty)
children.add(new Text(beforeComposing));
String composing = _string.textInside(_string.composing);
String composing = value.textInside(value.composing);
if (!composing.isEmpty) {
children.add(new Container(
key: 'composing',
@ -108,7 +81,7 @@ class EditableText extends Component {
));
}
String afterComposing = _string.textAfter(_string.composing);
String afterComposing = value.textAfter(value.composing);
if (!afterComposing.isEmpty)
children.add(new Text(afterComposing));
}

View file

@ -3,10 +3,10 @@
// found in the LICENSE file.
import '../../framework/fn.dart';
import 'editable_text.dart';
import 'input.dart';
class EditorApp extends App {
Node build() {
return new EditableText();
return new Input();
}
}

View file

@ -0,0 +1,66 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import '../../framework/fn.dart';
import '../../framework/theme/colors.dart';
import 'editable_string.dart';
import 'editable_text.dart';
import 'keyboard.dart';
typedef void ValueChanged(value);
class Input extends Component {
static final Style _style = new Style('''
display: paragraph;
margin: 8px;
padding: 8px;
border-bottom: 1px solid ${Grey[200]};
align-self: center;
height: 1.2em;
white-space: pre;
overflow: hidden;'''
);
static final String _focusedInlineStyle = '''
padding: 7px;
border-bottom: 2px solid ${Blue[500]};''';
ValueChanged onChanged;
String value;
bool _focused = false;
EditableString _editableValue;
Input({Object key, this.value: ''}) : super(key: key, stateful: true) {
_editableValue = new EditableString(text: value,
onUpdated: _handleTextUpdated);
events.listen('click', _handleClick);
}
void _handleClick(_) {
keyboard.show(_editableValue.stub);
setState(() {
_focused = true;
});
}
void _handleTextUpdated() {
setState(() {});
if (value != _editableValue.text) {
value = _editableValue.text;
if (onChanged != null)
onChanged(value);
}
}
Node build() {
return new Container(
style: _style,
inlineStyle: _focused ? _focusedInlineStyle : null,
children: [
new EditableText(value: _editableValue, focused: _focused),
]
);
}
}

View file

@ -0,0 +1,20 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import '../../framework/shell.dart' as shell;
import 'package:sky/services/keyboard/keyboard.mojom.dart';
class _KeyboardConnection {
KeyboardServiceProxy proxy;
_KeyboardConnection() {
proxy = new KeyboardServiceProxy.unbound();
shell.requestService(proxy);
}
KeyboardService get keyboard => proxy.ptr;
}
final _KeyboardConnection _connection = new _KeyboardConnection();
final KeyboardService keyboard = _connection.keyboard;