mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

Also: * Fix consecutive spaces (don't collapse them). * Add support for ^L. Still doesn't work: * Totally blank lines. (I know why, but I don't know why I can't seem to fix it.) R=erg@chromium.org Review URL: https://codereview.chromium.org/997873004
167 lines
4.8 KiB
Plaintext
167 lines
4.8 KiB
Plaintext
<!--
|
|
// 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 src="/sky/framework/elements/sky-element.sky" />
|
|
<import src="/sky/framework/elements/sky-scrollable.sky" />
|
|
<sky-element>
|
|
<template>
|
|
<style>
|
|
#control {
|
|
height: -webkit-fill-available;
|
|
background-color: black;
|
|
color: rgb(255, 191, 0);
|
|
font-family: 'Courier', 'monospace';
|
|
}
|
|
.line {
|
|
white-space: pre;
|
|
}
|
|
</style>
|
|
<sky-scrollable id="control" contenteditable />
|
|
</template>
|
|
<script>
|
|
import 'dart:async';
|
|
import 'dart:core';
|
|
import 'dart:sky';
|
|
import 'package:examples/echo_terminal/terminal_client.mojom.dart' as terminal;
|
|
import '/sky/framework/embedder.dart';
|
|
import 'terminal_display.dart';
|
|
import 'terminal_file_impl.dart';
|
|
|
|
// Implements the <terminal> element, which implements a "terminal display". Has
|
|
// an |url| attribute, whose value should be a Mojo app that provides the
|
|
// |terminal.TerminalClient| service.
|
|
@Tagname('terminal')
|
|
class TerminalDisplayImpl extends SkyElement implements TerminalDisplay {
|
|
Element _control;
|
|
|
|
// Queue of unconsumed input (keystrokes), with the head at index 0.
|
|
// Keystrokes end up here if there's no reader (i.e., |getChar()|) pending,
|
|
// i.e., if |_readerQueue| is empty. Invariant: At most one of |_inputQueue|
|
|
// and |_readerQueue| may be nonempty at any given time.
|
|
List<int> _inputQueue;
|
|
|
|
// Queue of things waiting for input, with the head at index 0. If a keystroke
|
|
// is received and this is nonempty, the head is given that keystroke (and
|
|
// dequeued).
|
|
List<Completer<int>> _readerQueue;
|
|
|
|
TerminalDisplayImpl()
|
|
: _inputQueue = new List<int>(),
|
|
_readerQueue = new List<Completer<int>>() {
|
|
}
|
|
|
|
void shadowRootReady() {
|
|
_control = shadowRoot.getElementById('control');
|
|
_control.addEventListener('keydown', _handleKeyDown);
|
|
_control.addEventListener('keypress', _handleKeyPress);
|
|
|
|
// Initialize with the first line.
|
|
_newLine();
|
|
|
|
_connect(getAttribute('url'));
|
|
}
|
|
|
|
void _handleKeyDown(KeyboardEvent event) {
|
|
// TODO(vtl): In general, our key handling is a total hack (due in part to
|
|
// sky's keyboard support being incomplete) -- e.g., we shouldn't have to
|
|
// make our div contenteditable. We have to intercept backspace (^H) here,
|
|
// since we won't actually get a keypress event for it. (Possibly, we should
|
|
// only handle keydown instead of keypress, but then we'd have to handle
|
|
// shift, etc. ourselves.)
|
|
if (event.key == 8) {
|
|
_enqueueChar(8);
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
|
|
void _handleKeyPress(KeyboardEvent event) {
|
|
_enqueueChar(event.charCode);
|
|
event.preventDefault();
|
|
}
|
|
|
|
void _enqueueChar(int charCode) {
|
|
// TODO(vtl): Add "echo" mode; do |putChar(event.charCode);| if echo is on.
|
|
|
|
if (_readerQueue.isEmpty) {
|
|
_inputQueue.add(charCode);
|
|
} else {
|
|
_readerQueue.removeAt(0).complete(charCode);
|
|
}
|
|
}
|
|
|
|
void _backspace() {
|
|
var oldText = _control.lastChild.textContent;
|
|
if (oldText.length > 0) {
|
|
_control.lastChild.textContent = oldText.substring(0, oldText.length - 1);
|
|
}
|
|
}
|
|
|
|
void _newLine() {
|
|
var line = document.createElement('div');
|
|
line.setAttribute('class', 'line');
|
|
_control.appendChild(line);
|
|
}
|
|
|
|
void _clear() {
|
|
while (_control.firstChild != null) {
|
|
_control.firstChild.remove();
|
|
}
|
|
_newLine();
|
|
}
|
|
|
|
// TODO(vtl): Should we always auto-connect? Should there be facilities for
|
|
// programmatically connecting? (What if the |url| attribute isn't set?)
|
|
void _connect(String url) {
|
|
var terminalClient = new terminal.TerminalClientProxy.unbound();
|
|
embedder.connectToService(url, terminalClient);
|
|
terminalClient.ptr.connectToTerminal(new TerminalFileImpl(this).stub);
|
|
terminalClient.close();
|
|
}
|
|
|
|
// |TerminalDisplay| implementation:
|
|
|
|
@override
|
|
void putChar(int byte) {
|
|
// Fast-path for printable chars.
|
|
if (byte >= 32) {
|
|
_control.lastChild.textContent += new String.fromCharCode(byte);
|
|
return;
|
|
}
|
|
|
|
switch (byte) {
|
|
case 8: // BS (^H).
|
|
_backspace();
|
|
break;
|
|
case 10: // LF ('\n').
|
|
_newLine(); // TODO(vtl): LF and CR should be separated.
|
|
break;
|
|
case 12: // FF (^L).
|
|
_clear();
|
|
break;
|
|
case 13: // CR ('\r').
|
|
_newLine(); // TODO(vtl): LF and CR should be separated.
|
|
break;
|
|
default:
|
|
// Should beep or something.
|
|
break;
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<int> getChar() async {
|
|
if (_inputQueue.isNotEmpty) {
|
|
return new Future.value(_inputQueue.removeAt(0));
|
|
}
|
|
|
|
var completer = new Completer<int>();
|
|
_readerQueue.add(completer);
|
|
return completer.future;
|
|
}
|
|
}
|
|
|
|
_init(script) => register(script, TerminalDisplayImpl);
|
|
</script>
|
|
</sky-element>
|