Commit a04c6b09 authored by Adam Barth's avatar Adam Barth

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
parent 7e9bbbec
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import 'package:sky/services/keyboard/keyboard.mojom.dart'; import 'package:sky/services/keyboard/keyboard.mojom.dart';
typedef void StringChangedCallback(EditableString updated); typedef void StringUpdated();
class TextRange { class TextRange {
final int start; final int start;
...@@ -23,11 +23,11 @@ class EditableString implements KeyboardClient { ...@@ -23,11 +23,11 @@ class EditableString implements KeyboardClient {
TextRange composing = const TextRange.empty(); TextRange composing = const TextRange.empty();
TextRange selection = const TextRange.empty(); TextRange selection = const TextRange.empty();
final StringChangedCallback onChanged; final StringUpdated onUpdated;
KeyboardClientStub stub; KeyboardClientStub stub;
EditableString({this.text: '', this.onChanged}) { EditableString({this.text: '', this.onUpdated}) {
stub = new KeyboardClientStub.unbound()..impl = this; stub = new KeyboardClientStub.unbound()..impl = this;
} }
...@@ -85,7 +85,7 @@ class EditableString implements KeyboardClient { ...@@ -85,7 +85,7 @@ class EditableString implements KeyboardClient {
TextRange committedRange = _replaceOrAppend(composing, text); TextRange committedRange = _replaceOrAppend(composing, text);
selection = new TextRange.collapsed(committedRange.end); selection = new TextRange.collapsed(committedRange.end);
composing = const TextRange.empty(); composing = const TextRange.empty();
onChanged(this); onUpdated();
} }
void deleteSurroundingText(int beforeLength, int afterLength) { void deleteSurroundingText(int beforeLength, int afterLength) {
...@@ -97,23 +97,23 @@ class EditableString implements KeyboardClient { ...@@ -97,23 +97,23 @@ class EditableString implements KeyboardClient {
_delete(beforeRange); _delete(beforeRange);
selection = new TextRange(start: selection.start - beforeLength, selection = new TextRange(start: selection.start - beforeLength,
end: selection.end - beforeLength); end: selection.end - beforeLength);
onChanged(this); onUpdated();
} }
void setComposingRegion(int start, int end) { void setComposingRegion(int start, int end) {
composing = new TextRange(start: start, end: end); composing = new TextRange(start: start, end: end);
onChanged(this); onUpdated();
} }
void setComposingText(String text, int newCursorPosition) { void setComposingText(String text, int newCursorPosition) {
// TODO(abarth): Why is |newCursorPosition| always 1? // TODO(abarth): Why is |newCursorPosition| always 1?
composing = _replaceOrAppend(composing, text); composing = _replaceOrAppend(composing, text);
selection = new TextRange.collapsed(composing.end); selection = new TextRange.collapsed(composing.end);
onChanged(this); onUpdated();
} }
void setSelection(int start, int end) { void setSelection(int start, int end) {
selection = new TextRange(start: start, end: end); selection = new TextRange(start: start, end: end);
onChanged(this); onUpdated();
} }
} }
...@@ -3,73 +3,35 @@ ...@@ -3,73 +3,35 @@
// found in the LICENSE file. // found in the LICENSE file.
import '../../framework/fn.dart'; import '../../framework/fn.dart';
import '../../framework/shell.dart' as shell; import '../../framework/theme/colors.dart';
import 'dart:async'; import 'dart:async';
import 'dart:math';
import 'editable_string.dart'; import 'editable_string.dart';
import 'package:sky/services/keyboard/keyboard.mojom.dart';
class EditableText extends Component { class EditableText extends Component {
static Style _style = new Style(''' static final Style _style = new Style('''
display: paragraph; display: inline;'''
white-space: pre-wrap;
padding: 10px;
height: 200px;
background-color: lightblue;'''
); );
static Style _cusorStyle = new Style(''' static final Style _cusorStyle = new Style('''
display: inline-block; display: inline-block;
width: 2px; width: 2px;
height: 1.2em; height: 1.2em;
vertical-align: top; vertical-align: top;
background-color: blue;''' background-color: ${Blue[500]};'''
); );
static Style _composingStyle = new Style(''' static final Style _composingStyle = new Style('''
display: inline; display: inline;
text-decoration: underline;''' text-decoration: underline;'''
); );
KeyboardServiceProxy _service; EditableString value;
bool focused;
EditableString _string;
Timer _cursorTimer; Timer _cursorTimer;
bool _showCursor = false; bool _showCursor = false;
EditableText({Object key}) : super(key: key, stateful: true) { EditableText({Object key, this.value, this.focused})
_string = new EditableString(text: '', onChanged: _handleTextChanged); : super(key: key, stateful: true) {
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");
} }
void _cursorTick(Timer timer) { void _cursorTick(Timer timer) {
...@@ -78,28 +40,39 @@ class EditableText extends Component { ...@@ -78,28 +40,39 @@ class EditableText extends Component {
}); });
} }
void _restartCursorTimer() { void _startCursorTimer() {
if (_cursorTimer != null) _showCursor = true;
_cursorTimer.cancel();
_cursorTimer = new Timer.periodic( _cursorTimer = new Timer.periodic(
new Duration(milliseconds: 500), _cursorTick); new Duration(milliseconds: 500), _cursorTick);
} }
void didUnmount() { void _stopCursorTimer() {
_cursorTimer.cancel(); _cursorTimer.cancel();
_cursorTimer = null;
_showCursor = false;
}
void didUnmount() {
if (_cursorTimer != null)
_stopCursorTimer();
} }
Node build() { Node build() {
if (focused && _cursorTimer == null)
_startCursorTimer();
else if (!focused && _cursorTimer != null)
_stopCursorTimer();
List<Node> children = new List<Node>(); List<Node> children = new List<Node>();
if (!_string.composing.isValid) { if (!value.composing.isValid) {
children.add(new Text(_string.text)); children.add(new Text(value.text));
} else { } else {
String beforeComposing = _string.textBefore(_string.composing); String beforeComposing = value.textBefore(value.composing);
if (!beforeComposing.isEmpty) if (!beforeComposing.isEmpty)
children.add(new Text(beforeComposing)); children.add(new Text(beforeComposing));
String composing = _string.textInside(_string.composing); String composing = value.textInside(value.composing);
if (!composing.isEmpty) { if (!composing.isEmpty) {
children.add(new Container( children.add(new Container(
key: 'composing', key: 'composing',
...@@ -108,7 +81,7 @@ class EditableText extends Component { ...@@ -108,7 +81,7 @@ class EditableText extends Component {
)); ));
} }
String afterComposing = _string.textAfter(_string.composing); String afterComposing = value.textAfter(value.composing);
if (!afterComposing.isEmpty) if (!afterComposing.isEmpty)
children.add(new Text(afterComposing)); children.add(new Text(afterComposing));
} }
......
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
// found in the LICENSE file. // found in the LICENSE file.
import '../../framework/fn.dart'; import '../../framework/fn.dart';
import 'editable_text.dart'; import 'input.dart';
class EditorApp extends App { class EditorApp extends App {
Node build() { Node build() {
return new EditableText(); return new Input();
} }
} }
// 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),
]
);
}
}
// 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;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment