blob: 6e323347f34f05cac173c99724043772b4d87f1f [file] [log] [blame]
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:html';
import 'package:observatory_2/models.dart' as M;
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
enum ProfileTreeMode {
code,
function,
}
class StackTraceTreeConfigChangedEvent {
final StackTraceTreeConfigElement element;
StackTraceTreeConfigChangedEvent(this.element);
}
class StackTraceTreeConfigElement extends CustomElement implements Renderable {
RenderingScheduler<StackTraceTreeConfigElement> _r;
Stream<RenderedEvent<StackTraceTreeConfigElement>> get onRendered =>
_r.onRendered;
StreamController<StackTraceTreeConfigChangedEvent> _onModeChange =
new StreamController<StackTraceTreeConfigChangedEvent>.broadcast();
StreamController<StackTraceTreeConfigChangedEvent> _onDirectionChange =
new StreamController<StackTraceTreeConfigChangedEvent>.broadcast();
StreamController<StackTraceTreeConfigChangedEvent> _onFilterChange =
new StreamController<StackTraceTreeConfigChangedEvent>.broadcast();
Stream<StackTraceTreeConfigChangedEvent> get onModeChange =>
_onModeChange.stream;
Stream<StackTraceTreeConfigChangedEvent> get onDirectionChange =>
_onDirectionChange.stream;
Stream<StackTraceTreeConfigChangedEvent> get onFilterChange =>
_onFilterChange.stream;
bool _showMode;
bool _showDirection;
bool _showFilter;
ProfileTreeMode _mode;
M.ProfileTreeDirection _direction;
String _filter;
bool get showMode => _showMode;
bool get showDirection => _showDirection;
bool get showFilter => _showFilter;
ProfileTreeMode get mode => _mode;
M.ProfileTreeDirection get direction => _direction;
String get filter => _filter;
set showMode(bool value) => _showMode = _r.checkAndReact(_showMode, value);
set showDirection(bool value) =>
_showDirection = _r.checkAndReact(_showDirection, value);
set showFilter(bool value) =>
_showFilter = _r.checkAndReact(_showFilter, value);
set mode(ProfileTreeMode value) => _mode = _r.checkAndReact(_mode, value);
set direction(M.ProfileTreeDirection value) =>
_direction = _r.checkAndReact(_direction, value);
set filter(String value) => _filter = _r.checkAndReact(_filter, value);
factory StackTraceTreeConfigElement(
{bool showMode: true,
bool showDirection: true,
bool showFilter: true,
String filter: '',
ProfileTreeMode mode: ProfileTreeMode.function,
M.ProfileTreeDirection direction: M.ProfileTreeDirection.exclusive,
RenderingQueue queue}) {
assert(showMode != null);
assert(showDirection != null);
assert(showFilter != null);
assert(mode != null);
assert(direction != null);
assert(filter != null);
StackTraceTreeConfigElement e = new StackTraceTreeConfigElement.created();
e._r = new RenderingScheduler<StackTraceTreeConfigElement>(e, queue: queue);
e._showMode = showMode;
e._showDirection = showDirection;
e._showFilter = showFilter;
e._mode = mode;
e._direction = direction;
e._filter = filter;
return e;
}
StackTraceTreeConfigElement.created()
: super.created('stack-trace-tree-config');
@override
void attached() {
super.attached();
_r.enable();
}
@override
void detached() {
super.detached();
_r.disable(notify: true);
children = const [];
}
void render() {
children = <Element>[
new DivElement()
..classes = ['content-centered-big']
..children = <Element>[
new HeadingElement.h2()..text = 'Tree display',
new HRElement(),
new DivElement()
..classes = ['row']
..children = <Element>[
new DivElement()
..classes = ['memberList']
..children = _createMembers()
]
]
];
}
List<Element> _createMembers() {
var members = <Element>[];
if (_showMode) {
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'Mode',
new DivElement()
..classes = ['memberValue']
..children = _createModeSelect()
]);
}
if (_showDirection) {
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'Call Tree Direction',
new SpanElement()
..classes = ['memberValue']
..children = _createDirectionSelect()
]);
}
if (showFilter) {
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'Call Tree Filter'
..title = 'case-sensitive substring match',
new SpanElement()
..classes = ['memberValue']
..children = _createFilter()
]);
}
return members;
}
String get modeDescription {
if (_mode == ProfileTreeMode.function) {
return 'Inlined frames expanded.';
} else {
return 'Inlined frames not expanded.';
}
}
List<Element> _createModeSelect() {
var s;
return [
s = new SelectElement()
..classes = ['mode-select']
..value = modeToString(_mode)
..children = ProfileTreeMode.values.map((mode) {
return new OptionElement(
value: modeToString(mode), selected: _mode == mode)
..text = modeToString(mode);
}).toList(growable: false)
..onChange.listen((_) {
_mode = ProfileTreeMode.values[s.selectedIndex];
_r.dirty();
})
..onChange.map(_toEvent).listen(_triggerModeChange),
new SpanElement()..text = ' $modeDescription'
];
}
String get directionDescription {
if (_direction == M.ProfileTreeDirection.inclusive) {
return 'Tree is rooted at "main". Child nodes are callees.';
} else {
return 'Tree is rooted at top-of-stack. Child nodes are callers.';
}
}
List<Element> _createDirectionSelect() {
var s;
return [
s = new SelectElement()
..classes = ['direction-select']
..value = directionToString(_direction)
..children = M.ProfileTreeDirection.values.map((direction) {
return new OptionElement(
value: directionToString(direction),
selected: _direction == direction)
..text = directionToString(direction);
}).toList(growable: false)
..onChange.listen((_) {
_direction = M.ProfileTreeDirection.values[s.selectedIndex];
_r.dirty();
})
..onChange.map(_toEvent).listen(_triggerDirectionChange),
new SpanElement()..text = ' $directionDescription'
];
}
List<Element> _createFilter() {
var t;
return [
t = new TextInputElement()
..placeholder = 'Search filter'
..value = filter
..onChange.listen((_) {
_filter = t.value;
})
..onChange.map(_toEvent).listen(_triggerFilterChange)
];
}
static String modeToString(ProfileTreeMode mode) {
switch (mode) {
case ProfileTreeMode.code:
return 'Code';
case ProfileTreeMode.function:
return 'Function';
}
throw new Exception('Unknown ProfileTreeMode');
}
static String directionToString(M.ProfileTreeDirection direction) {
switch (direction) {
case M.ProfileTreeDirection.inclusive:
return 'Top down';
case M.ProfileTreeDirection.exclusive:
return 'Bottom up';
}
throw new Exception('Unknown ProfileTreeDirection');
}
StackTraceTreeConfigChangedEvent _toEvent(_) {
return new StackTraceTreeConfigChangedEvent(this);
}
void _triggerModeChange(e) => _onModeChange.add(e);
void _triggerDirectionChange(e) => _onDirectionChange.add(e);
void _triggerFilterChange(e) => _onFilterChange.add(e);
}