Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(12)

Side by Side Diff: sky/examples/terminal/terminal.sky

Issue 1038813005: Move terminal example from //sky/examples to //examples (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sky/examples/terminal/index.sky ('k') | sky/examples/terminal/terminal_display.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 <!--
2 // Copyright 2015 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 -->
6 <import src="/sky/framework/elements/sky-element.sky" />
7 <sky-element>
8 <template>
9 <style>
10 #container {
11 height: -webkit-fill-available;
12 }
13 #input-box {
14 position: absolute;
15 left: 0;
16 top: 0;
17 z-index: 10;
18 width: 100%;
19 height: 100%;
20 opacity: 0;
21 }
22 #output-box {
23 position: absolute;
24 left: 0;
25 top: 0;
26 z-index: 0;
27 width: 100%;
28 height: 100%;
29 background-color: black;
30 color: rgb(255, 191, 0);
31 font-family: 'Courier', 'monospace';
32 font-size: small;
33 }
34 .line {
35 height: 1em;
36 white-space: nowrap;
37 }
38 </style>
39 <div id="container">
40 <div id="input-box" contenteditable />
41 <div id="output-box" />
42 </div>
43 </div>
44 </template>
45 <script>
46 import 'dart:async';
47 import 'dart:core';
48 import 'dart:sky';
49 import 'package:mojo/services/terminal/public/interfaces/terminal_client.mojom.d art' as terminal;
50 import 'package:sky/framework/embedder.dart';
51 import 'terminal_display.dart';
52 import 'terminal_file_impl.dart';
53
54 // Implements the <terminal> element, which implements a "terminal display". Has
55 // an |url| attribute, whose value should be a Mojo app that provides the
56 // |terminal.TerminalClient| service.
57 @Tagname('terminal')
58 class TerminalDisplayImpl extends SkyElement implements TerminalDisplay {
59 Element _inputBox;
60 Element _outputBox;
61 int _maxLines;
62
63 // Queue of unconsumed input (keystrokes), with the head at index 0.
64 // Keystrokes end up here if there's no reader (i.e., |getChar()|) pending,
65 // i.e., if |_readerQueue| is empty. Invariant: At most one of |_inputQueue|
66 // and |_readerQueue| may be nonempty at any given time.
67 List<int> _inputQueue;
68
69 // Queue of things waiting for input, with the head at index 0. If a keystroke
70 // is received and this is nonempty, the head is given that keystroke (and
71 // dequeued).
72 List<Completer<int>> _readerQueue;
73
74 TerminalDisplayImpl()
75 : _inputQueue = new List<int>(),
76 _readerQueue = new List<Completer<int>>() {
77 }
78
79 void shadowRootReady() {
80 _inputBox = shadowRoot.getElementById('input-box');
81 _inputBox.addEventListener('keydown', _handleKeyDown);
82 _inputBox.addEventListener('keypress', _handleKeyPress);
83 _inputBox.addEventListener('wheel', _handleWheel);
84 _outputBox = shadowRoot.getElementById('output-box');
85
86 // Hack to allow |_newLine()| to work.
87 _maxLines = 100;
88
89 // Initialize with the first line.
90 _newLine();
91
92 // Actually compute the maximum number of lines.
93 // TODO(vtl): Recompute on resize.
94 _maxLines = _outputBox.clientHeight ~/ _outputBox.firstChild.offsetHeight;
95
96 var url = getAttribute('url');
97 if (url != null) {
98 connect(url);
99 }
100 }
101
102 void _handleKeyDown(KeyboardEvent event) {
103 // TODO(vtl): In general, our key handling is a total hack (due in part to
104 // sky's keyboard support being incomplete) -- e.g., we shouldn't have to
105 // make our div contenteditable. We have to intercept backspace (^H) here,
106 // since we won't actually get a keypress event for it. (Possibly, we should
107 // only handle keydown instead of keypress, but then we'd have to handle
108 // shift, etc. ourselves.)
109 if (event.key == 8) {
110 _enqueueChar(8);
111 event.preventDefault();
112 }
113 }
114
115 void _handleKeyPress(KeyboardEvent event) {
116 if (event.charCode != 0) {
117 _enqueueChar(event.charCode);
118 }
119 event.preventDefault();
120 }
121
122 void _handleWheel(WheelEvent event) {
123 _outputBox.dispatchEvent(event);
124 }
125
126 void _enqueueChar(int charCode) {
127 // TODO(vtl): Add "echo" mode; do |putChar(event.charCode);| if echo is on.
128
129 if (_readerQueue.isEmpty) {
130 _inputQueue.add(charCode);
131 } else {
132 _readerQueue.removeAt(0).complete(charCode);
133 }
134 }
135
136 void _backspace() {
137 var oldText = _outputBox.lastChild.textContent;
138 if (oldText.length > 0) {
139 _outputBox.lastChild.textContent = oldText.substring(0,
140 oldText.length - 1);
141 }
142 }
143
144 void _newLine() {
145 var line = document.createElement('div');
146 line.setAttribute('class', 'line');
147 _outputBox.appendChild(line);
148
149 // Scroll if necessary.
150 var children = _outputBox.getChildNodes();
151 if (children.length > _maxLines) {
152 children = new List.from(children.skip(children.length - _maxLines));
153 _outputBox.setChildren(children);
154 }
155 }
156
157 void _clear() {
158 _outputBox.setChildren([]);
159 _newLine();
160 }
161
162 void connect(String url) {
163 var terminalClient = new terminal.TerminalClientProxy.unbound();
164 embedder.connectToService(url, terminalClient);
165 terminalClient.ptr.connectToTerminal(new TerminalFileImpl(this).stub);
166 terminalClient.close();
167 }
168
169 void putString(String s) {
170 for (var i = 0; i < s.length; i++) {
171 putChar(s.codeUnitAt(i));
172 }
173 }
174
175 // |TerminalDisplay| implementation:
176
177 @override
178 void putChar(int byte) {
179 // Fast-path for printable chars.
180 if (byte >= 32) {
181 _outputBox.lastChild.textContent += new String.fromCharCode(byte);
182 return;
183 }
184
185 switch (byte) {
186 case 8: // BS (^H).
187 _backspace();
188 break;
189 case 10: // LF ('\n').
190 _newLine(); // TODO(vtl): LF and CR should be separated.
191 break;
192 case 12: // FF (^L).
193 _clear();
194 break;
195 case 13: // CR ('\r').
196 _newLine(); // TODO(vtl): LF and CR should be separated.
197 break;
198 default:
199 // Should beep or something.
200 break;
201 }
202 }
203
204 @override
205 Future<int> getChar() async {
206 if (_inputQueue.isNotEmpty) {
207 return new Future.value(_inputQueue.removeAt(0));
208 }
209
210 var completer = new Completer<int>();
211 _readerQueue.add(completer);
212 return completer.future;
213 }
214 }
215
216 _init(script) => register(script, TerminalDisplayImpl);
217 </script>
218 </sky-element>
OLDNEW
« no previous file with comments | « sky/examples/terminal/index.sky ('k') | sky/examples/terminal/terminal_display.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698