blob: 8afa8920ba365eba303ead4dcd76de9b3ffc6699 [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 'package:web/web.dart';
import '../../models.dart' as M;
import 'helpers/custom_element.dart';
import 'helpers/element_utils.dart';
import 'helpers/rendering_scheduler.dart';
enum ProfileTreeMode { code, function }
class StackTraceTreeConfigChangedEvent {
final StackTraceTreeConfigElement element;
StackTraceTreeConfigChangedEvent(this.element);
}
class StackTraceTreeConfigElement extends CustomElement implements Renderable {
late 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;
late bool _showMode;
late bool _showDirection;
late bool _showFilter;
late ProfileTreeMode _mode;
late M.ProfileTreeDirection _direction;
late 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,
}) {
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 = <HTMLElement>[
new HTMLDivElement()
..className = 'content-centered-big'
..appendChildren(<HTMLElement>[
new HTMLHeadingElement.h2()..textContent = 'Tree display',
new HTMLHRElement(),
new HTMLDivElement()
..className = 'row'
..appendChildren(<HTMLElement>[
new HTMLDivElement()
..className = 'memberList'
..appendChildren(_createMembers()),
]),
]),
];
}
List<HTMLElement> _createMembers() {
var members = <HTMLElement>[];
if (_showMode) {
members.add(
new HTMLDivElement()
..className = 'memberItem'
..appendChildren(<HTMLElement>[
new HTMLDivElement()
..className = 'memberName'
..textContent = 'Mode',
new HTMLDivElement()
..className = 'memberValue'
..appendChildren(_createModeSelect()),
]),
);
}
if (_showDirection) {
members.add(
new HTMLDivElement()
..className = 'memberItem'
..appendChildren(<HTMLElement>[
new HTMLDivElement()
..className = 'memberName'
..textContent = 'Call Tree Direction',
new HTMLSpanElement()
..className = 'memberValue'
..appendChildren(_createDirectionSelect()),
]),
);
}
if (showFilter) {
members.add(
new HTMLDivElement()
..className = 'memberItem'
..appendChildren(<HTMLElement>[
new HTMLDivElement()
..className = 'memberName'
..textContent = 'Call Tree Filter'
..title = 'case-sensitive substring match',
new HTMLSpanElement()
..className = 'memberValue'
..appendChildren(_createFilter()),
]),
);
}
return members;
}
String get modeDescription {
if (_mode == ProfileTreeMode.function) {
return 'Inlined frames expanded.';
} else {
return 'Inlined frames not expanded.';
}
}
List<HTMLElement> _createModeSelect() {
final s = HTMLSelectElement()
..className = 'mode-select'
..value = modeToString(_mode)
..appendChildren(
ProfileTreeMode.values.map(
(mode) => HTMLOptionElement()
..value = modeToString(mode)
..selected = _mode == mode
..text = modeToString(mode),
),
);
return [
s
..onChange.listen((_) {
_mode = ProfileTreeMode.values[s.selectedIndex];
_r.dirty();
})
..onChange.map(_toEvent).listen(_triggerModeChange),
HTMLSpanElement()..textContent = ' $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<HTMLElement> _createDirectionSelect() {
final s = HTMLSelectElement()
..className = 'direction-select'
..value = directionToString(_direction)
..appendChildren(
M.ProfileTreeDirection.values.map((direction) {
return HTMLOptionElement()
..value = directionToString(direction)
..selected = _direction == direction
..text = directionToString(direction);
}),
);
return [
s
..onChange.listen((_) {
_direction = M.ProfileTreeDirection.values[s.selectedIndex];
_r.dirty();
})
..onChange.map(_toEvent).listen(_triggerDirectionChange),
new HTMLSpanElement()..textContent = ' $directionDescription',
];
}
List<HTMLElement> _createFilter() {
var t;
return [
t = new HTMLInputElement()
..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';
}
}
static String directionToString(M.ProfileTreeDirection direction) {
switch (direction) {
case M.ProfileTreeDirection.inclusive:
return 'Top down';
case M.ProfileTreeDirection.exclusive:
return 'Bottom up';
}
}
StackTraceTreeConfigChangedEvent _toEvent(_) {
return new StackTraceTreeConfigChangedEvent(this);
}
void _triggerModeChange(e) => _onModeChange.add(e);
void _triggerDirectionChange(e) => _onDirectionChange.add(e);
void _triggerFilterChange(e) => _onFilterChange.add(e);
}