OLD | NEW |
---|---|
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library trydart.interaction_manager; | 5 library trydart.interaction_manager; |
6 | 6 |
7 import 'dart:html'; | 7 import 'dart:html'; |
8 | 8 |
9 import 'dart:convert' show | 9 import 'dart:convert' show |
10 JSON; | 10 JSON; |
11 | 11 |
12 import 'dart:math' show | 12 import 'dart:math' show |
13 max, | 13 max, |
14 min; | 14 min; |
15 | 15 |
16 import 'dart:async' show | |
17 Future; | |
18 | |
16 import 'package:compiler/implementation/scanner/scannerlib.dart' | 19 import 'package:compiler/implementation/scanner/scannerlib.dart' |
17 show | 20 show |
18 EOF_TOKEN, | 21 EOF_TOKEN, |
19 StringScanner, | 22 StringScanner, |
20 Token; | 23 Token; |
21 | 24 |
22 import 'package:compiler/implementation/source_file.dart' show | 25 import 'package:compiler/implementation/source_file.dart' show |
23 StringSourceFile; | 26 StringSourceFile; |
24 | 27 |
25 import 'compilation.dart' show | 28 import 'compilation.dart' show |
26 scheduleCompilation; | 29 scheduleCompilation; |
27 | 30 |
28 import 'ui.dart' show | 31 import 'ui.dart' show |
29 currentTheme, | 32 currentTheme, |
30 hackDiv, | 33 hackDiv, |
31 mainEditorPane, | 34 mainEditorPane, |
32 observer, | 35 observer, |
33 outputDiv; | 36 outputDiv; |
34 | 37 |
35 import 'decoration.dart' show | 38 import 'decoration.dart' show |
36 CodeCompletionDecoration, | 39 CodeCompletionDecoration, |
37 Decoration, | 40 Decoration, |
38 DiagnosticDecoration, | 41 DiagnosticDecoration, |
39 error, | 42 error, |
40 info, | 43 info, |
41 warning; | 44 warning; |
42 | 45 |
43 import 'html_to_text.dart' show | 46 import 'html_to_text.dart' show |
44 htmlToText; | 47 htmlToText; |
48 | |
49 import 'compilation_unit.dart' show | |
50 CompilationUnit; | |
51 | |
45 import 'editor.dart' as editor; | 52 import 'editor.dart' as editor; |
46 | 53 |
47 import 'mock.dart' as mock; | 54 import 'mock.dart' as mock; |
48 | 55 |
49 import 'settings.dart' as settings; | 56 import 'settings.dart' as settings; |
50 | 57 |
51 /** | 58 /** |
52 * UI interaction manager for the entire application. | 59 * UI interaction manager for the entire application. |
53 */ | 60 */ |
54 abstract class InteractionManager { | 61 abstract class InteractionManager { |
(...skipping 15 matching lines...) Expand all Loading... | |
70 | 77 |
71 InteractionManager.internal(); | 78 InteractionManager.internal(); |
72 | 79 |
73 void onInput(Event event); | 80 void onInput(Event event); |
74 | 81 |
75 void onKeyUp(KeyboardEvent event); | 82 void onKeyUp(KeyboardEvent event); |
76 | 83 |
77 void onMutation(List<MutationRecord> mutations, MutationObserver observer); | 84 void onMutation(List<MutationRecord> mutations, MutationObserver observer); |
78 | 85 |
79 void onSelectionChange(Event event); | 86 void onSelectionChange(Event event); |
87 | |
88 /// Called when the content of a CompilationUnit changed. | |
89 void onCompilationUnitChanged(CompilationUnit unit); | |
90 | |
91 Future<List<String>> projectFileNames(); | |
92 | |
93 /// Called when the user selected a new project file. | |
94 void onProjectFileSelected(String projectFile); | |
80 } | 95 } |
81 | 96 |
82 /** | 97 /** |
83 * State machine for UI interactions. | 98 * State machine for UI interactions. |
84 */ | 99 */ |
85 class InteractionContext extends InteractionManager { | 100 class InteractionContext extends InteractionManager { |
86 InteractionState state; | 101 InteractionState state; |
87 | 102 |
103 final Map<String, CompilationUnit> projectFiles = <String, CompilationUnit>{}; | |
104 | |
105 CompilationUnit currentCompilationUnit = new CompilationUnit('fake', ''); | |
kasperl
2014/03/28 10:24:51
Maybe chose a fake name that isn't at all likely (
ahe
2014/03/28 11:53:14
This is a temporary hack. I'll add a todo.
| |
106 | |
88 InteractionContext() | 107 InteractionContext() |
89 : super.internal() { | 108 : super.internal() { |
90 state = new InitialState(this); | 109 state = new InitialState(this); |
91 } | 110 } |
92 | 111 |
93 void onInput(Event event) => state.onInput(event); | 112 void onInput(Event event) => state.onInput(event); |
94 | 113 |
95 void onKeyUp(KeyboardEvent event) => state.onKeyUp(event); | 114 void onKeyUp(KeyboardEvent event) => state.onKeyUp(event); |
96 | 115 |
97 void onMutation(List<MutationRecord> mutations, MutationObserver observer) { | 116 void onMutation(List<MutationRecord> mutations, MutationObserver observer) { |
98 return state.onMutation(mutations, observer); | 117 return state.onMutation(mutations, observer); |
99 } | 118 } |
100 | 119 |
101 void onSelectionChange(Event event) => state.onSelectionChange(event); | 120 void onSelectionChange(Event event) => state.onSelectionChange(event); |
121 | |
122 void onCompilationUnitChanged(CompilationUnit unit) { | |
123 return state.onCompilationUnitChanged(unit); | |
124 } | |
125 | |
126 Future<List<String>> projectFileNames() => state.projectFileNames(); | |
127 | |
128 void onProjectFileSelected(String projectFile) { | |
129 return state.onProjectFileSelected(projectFile); | |
130 } | |
102 } | 131 } |
103 | 132 |
104 abstract class InteractionState implements InteractionManager { | 133 abstract class InteractionState implements InteractionManager { |
105 void onStateChanged(InteractionState previous) { | 134 void onStateChanged(InteractionState previous) { |
106 print('State change ${previous.runtimeType} -> ${runtimeType}.'); | 135 print('State change ${previous.runtimeType} -> ${runtimeType}.'); |
107 } | 136 } |
108 } | 137 } |
109 | 138 |
110 class InitialState extends InteractionState { | 139 class InitialState extends InteractionState { |
111 final InteractionContext context; | 140 final InteractionContext context; |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
226 while (child != null) { | 255 while (child != null) { |
227 walk4(child); | 256 walk4(child); |
228 if (hasSelection) return; | 257 if (hasSelection) return; |
229 child = child.nextNode; | 258 child = child.nextNode; |
230 } | 259 } |
231 } | 260 } |
232 if (selection.isCollapsed) { | 261 if (selection.isCollapsed) { |
233 walk4(mainEditorPane); | 262 walk4(mainEditorPane); |
234 } | 263 } |
235 | 264 |
236 editor.currentSource = mainEditorPane.text; | 265 String currentText = mainEditorPane.text; |
266 context.currentCompilationUnit.content = currentText; | |
237 mainEditorPane.nodes.clear(); | 267 mainEditorPane.nodes.clear(); |
238 mainEditorPane.appendText(editor.currentSource); | 268 mainEditorPane.appendText(currentText); |
239 if (hasSelection) { | 269 if (hasSelection) { |
240 selection.collapse(mainEditorPane.firstChild, anchorOffset); | 270 selection.collapse(mainEditorPane.firstChild, anchorOffset); |
241 } | 271 } |
242 | 272 |
243 editor.isMalformedInput = false; | 273 editor.isMalformedInput = false; |
244 for (var n in new List.from(mainEditorPane.nodes)) { | 274 for (var n in new List.from(mainEditorPane.nodes)) { |
245 if (n is! Text) continue; | 275 if (n is! Text) continue; |
246 Text node = n; | 276 Text node = n; |
247 String text = node.text; | 277 String text = node.text; |
248 | 278 |
(...skipping 24 matching lines...) Expand all Loading... | |
273 selectionOffset -= str.length; | 303 selectionOffset -= str.length; |
274 selection.collapse(after, selectionOffset); | 304 selection.collapse(after, selectionOffset); |
275 } else { | 305 } else { |
276 selection.collapse(str, selectionOffset); | 306 selection.collapse(str, selectionOffset); |
277 } | 307 } |
278 } | 308 } |
279 node = after; | 309 node = after; |
280 } | 310 } |
281 } | 311 } |
282 | 312 |
283 window.localStorage['currentSource'] = editor.currentSource; | |
284 print('Saved source'); | |
285 | |
286 // Discard highlighting mutations. | 313 // Discard highlighting mutations. |
287 observer.takeRecords(); | 314 observer.takeRecords(); |
288 } | 315 } |
289 | 316 |
290 void onSelectionChange(Event event) { | 317 void onSelectionChange(Event event) { |
291 } | 318 } |
292 | 319 |
293 void onStateChanged(InteractionState previous) { | 320 void onStateChanged(InteractionState previous) { |
294 super.onStateChanged(previous); | 321 super.onStateChanged(previous); |
295 scheduleCompilation(); | 322 scheduleCompilation(); |
296 } | 323 } |
324 | |
325 void onCompilationUnitChanged(CompilationUnit unit) { | |
326 if (unit == context.currentCompilationUnit) { | |
327 window.localStorage['currentSource'] = unit.content; | |
kasperl
2014/03/28 10:24:51
Add a setter for currentSource next to the getter?
ahe
2014/03/28 11:53:14
Also a temporary hack, but encapsulating the hacks
| |
328 print('Saved source'); | |
329 scheduleCompilation(); | |
330 } else { | |
331 print("Unexpected change to compilation unit '${unit.name}'."); | |
332 } | |
333 } | |
334 | |
335 Future<List<String>> projectFileNames() { | |
336 return getString('project?list').then((String response) { | |
337 return new List<String>.from(JSON.decode(response)); | |
338 }); | |
339 } | |
340 | |
341 void onProjectFileSelected(String projectFile) { | |
342 // Disable editing whilst fetching data. | |
343 mainEditorPane.contentEditable = 'false'; | |
344 | |
345 CompilationUnit unit = context.projectFiles[projectFile]; | |
346 Future<CompilationUnit> future; | |
347 if (unit != null) { | |
348 // This project file had been fetched already. | |
349 future = new Future<CompilationUnit>.value(unit); | |
350 } else { | |
351 // This project file has to be fetched. | |
352 future = getString('project/$projectFile').then((String text) { | |
353 CompilationUnit unit = context.projectFiles[projectFile]; | |
354 if (unit == null) { | |
355 // Only create a new unit if the value hadn't arrived already. | |
356 unit = new CompilationUnit(projectFile, text); | |
357 context.projectFiles[projectFile] = unit; | |
358 } | |
359 return unit; | |
360 }); | |
361 } | |
362 future.then((CompilationUnit unit) { | |
363 mainEditorPane | |
364 ..contentEditable = 'true' | |
365 ..nodes.clear(); | |
366 observer.takeRecords(); // Discard mutations. | |
367 | |
368 // Install the code, which will trigger a call to onMutation. | |
369 mainEditorPane.appendText(unit.content); | |
370 }); | |
371 } | |
372 } | |
373 | |
374 Future<String> getString(uri) { | |
375 return new Future<String>.sync(() => HttpRequest.getString('$uri')); | |
297 } | 376 } |
298 | 377 |
299 class PendingInputState extends InitialState { | 378 class PendingInputState extends InitialState { |
300 PendingInputState(InteractionContext context) | 379 PendingInputState(InteractionContext context) |
301 : super(context); | 380 : super(context); |
302 | 381 |
303 void onInput(Event event) { | 382 void onInput(Event event) { |
304 // Do nothing. | 383 // Do nothing. |
305 } | 384 } |
306 | 385 |
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
593 event.getModifierState("Fn") || | 672 event.getModifierState("Fn") || |
594 event.getModifierState("Meta") || | 673 event.getModifierState("Meta") || |
595 event.getModifierState("NumLock") || | 674 event.getModifierState("NumLock") || |
596 event.getModifierState("ScrollLock") || | 675 event.getModifierState("ScrollLock") || |
597 event.getModifierState("Scroll") || | 676 event.getModifierState("Scroll") || |
598 event.getModifierState("Win") || | 677 event.getModifierState("Win") || |
599 event.getModifierState("Shift") || | 678 event.getModifierState("Shift") || |
600 event.getModifierState("SymbolLock") || | 679 event.getModifierState("SymbolLock") || |
601 event.getModifierState("OS"); | 680 event.getModifierState("OS"); |
602 } | 681 } |
OLD | NEW |