Index: sky/examples/terminal/terminal.sky |
diff --git a/sky/examples/terminal/terminal.sky b/sky/examples/terminal/terminal.sky |
new file mode 100644 |
index 0000000000000000000000000000000000000000..03d784c25de99624fc7890787a11a88fe18478c0 |
--- /dev/null |
+++ b/sky/examples/terminal/terminal.sky |
@@ -0,0 +1,115 @@ |
+<!-- |
+// 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/sky-element.sky" /> |
+<import src="/sky/framework/sky-scrollable.sky" /> |
+<sky-element> |
+<template> |
+ <style> |
+ #control { |
+ height: -webkit-fill-available; |
+ background-color: black; |
+ color: rgb(255, 191, 0); |
+ font-family: 'Courier', 'monospace'; |
+ } |
+ span { |
+ white-space: nowrap; |
+ } |
+ </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('keypress', _handleKeyPress); |
+ |
+ // Initialize with the first line. |
+ _newLine(); |
+ |
+ _connect(getAttribute('url')); |
+ } |
+ |
+ void _handleKeyPress(KeyboardEvent event) { |
+ // TODO(vtl): Add "echo" mode; do |putChar(event.charCode);| if echo is on. |
+ |
+ if (_readerQueue.isEmpty) { |
+ _inputQueue.add(event.charCode); |
+ } else { |
+ _readerQueue.removeAt(0).complete(event.charCode); |
+ } |
+ |
+ event.preventDefault(); |
+ } |
+ |
+ void _newLine() { |
+ _control.appendChild(document.createElement('span')); |
+ } |
+ |
+ // 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) { |
+ if (byte == 10 || byte == 13) { |
+ _newLine(); |
+ return; |
+ } |
+ _control.lastChild.textContent += new String.fromCharCode(byte); |
+ } |
+ |
+ @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> |