Commit 0f99e272 authored by Adam Barth's avatar Adam Barth

Move material Input component into sky/framework

The implementation details are in sky/framework/editing.

R=eseidel@chromium.org

Review URL: https://codereview.chromium.org/983213005
parent a04c6b09
// 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 'package:sky/services/keyboard/keyboard.mojom.dart';
typedef void StringUpdated();
class TextRange {
final int start;
final int end;
TextRange({this.start, this.end});
TextRange.collapsed(int position) : start = position, end = position;
const TextRange.empty() : start = -1, end = -1;
bool get isValid => start >= 0 && end >= 0;
bool get isCollapsed => start == end;
}
class EditableString implements KeyboardClient {
String text;
TextRange composing = const TextRange.empty();
TextRange selection = const TextRange.empty();
final StringUpdated onUpdated;
KeyboardClientStub stub;
EditableString({this.text: '', this.onUpdated}) {
stub = new KeyboardClientStub.unbound()..impl = this;
}
String textBefore(TextRange range) {
return text.substring(0, range.start);
}
String textAfter(TextRange range) {
return text.substring(range.end);
}
String textInside(TextRange range) {
return text.substring(range.start, range.end);
}
void _delete(TextRange range) {
if (range.isCollapsed || !range.isValid)
return;
text = textBefore(range) + textAfter(range);
}
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 = textBefore(range);
String after = textAfter(range);
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 TextRange.collapsed(committedRange.end);
composing = const TextRange.empty();
onUpdated();
}
void deleteSurroundingText(int beforeLength, int afterLength) {
TextRange beforeRange = new TextRange(start: selection.start - beforeLength,
end: selection.start);
TextRange afterRange = new TextRange(start: selection.end,
end: selection.end + afterLength);
_delete(afterRange);
_delete(beforeRange);
selection = new TextRange(start: selection.start - beforeLength,
end: selection.end - beforeLength);
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 TextRange.collapsed(composing.end);
onUpdated();
}
void setSelection(int start, int end) {
selection = new TextRange(start: start, end: end);
onUpdated();
}
}
// 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 'dart:async';
import 'editable_string.dart';
class EditableText extends Component {
static final Style _style = new Style('''
display: inline;'''
);
static final Style _cusorStyle = new Style('''
display: inline-block;
width: 2px;
height: 1.2em;
vertical-align: top;
background-color: ${Blue[500]};'''
);
static final Style _composingStyle = new Style('''
display: inline;
text-decoration: underline;'''
);
EditableString value;
bool focused;
Timer _cursorTimer;
bool _showCursor = false;
EditableText({Object key, this.value, this.focused})
: super(key: key, stateful: true) {
}
void _cursorTick(Timer timer) {
setState(() {
_showCursor = !_showCursor;
});
}
void _startCursorTimer() {
_showCursor = true;
_cursorTimer = new Timer.periodic(
new Duration(milliseconds: 500), _cursorTick);
}
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 (!value.composing.isValid) {
children.add(new Text(value.text));
} else {
String beforeComposing = value.textBefore(value.composing);
if (!beforeComposing.isEmpty)
children.add(new Text(beforeComposing));
String composing = value.textInside(value.composing);
if (!composing.isEmpty) {
children.add(new Container(
key: 'composing',
style: _composingStyle,
children: [new Text(composing)]
));
}
String afterComposing = value.textAfter(value.composing);
if (!afterComposing.isEmpty)
children.add(new Text(afterComposing));
}
if (_showCursor)
children.add(new Container(key: 'cursor', style: _cusorStyle));
return new Container(
style: _style,
children: children
);
}
}
// 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;
......@@ -2,9 +2,9 @@
<sky>
<import src="/sky/framework/debug/shake-to-reload.sky" />
<script>
import 'editor_app.dart';
import 'input_app.dart';
main() {
new EditorApp();
new InputApp();
}
</script>
</sky>
......@@ -3,9 +3,9 @@
// found in the LICENSE file.
import '../../framework/fn.dart';
import 'input.dart';
import '../../framework/components/input.dart';
class EditorApp extends App {
class InputApp extends App {
Node build() {
return new Input();
}
......
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