Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 34 additions & 23 deletions scripts/developer/debugging/debugWindow.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@
//

(function() { // BEGIN LOCAL_SCOPE

"use strict"
//check if script already running.
var scriptData = ScriptDiscoveryService.getRunning();
var scripts = scriptData.filter(function (datum) { return datum.name === 'debugWindow.js'; });
const scriptData = ScriptDiscoveryService.getRunning();
const scripts = scriptData.filter(function (datum) { return datum.name === 'debugWindow.js'; });
if (scripts.length >= 2) {
//2nd instance of the script is too much
ScriptDiscoveryService.stopScript(scripts[1].url);
return;
}

var SUPPRESS_DEFAULT_SCRIPTS_MENU_NAME = "Developer"
var SUPPRESS_DEFAULT_SCRIPTS_ITEM_NAME = "Suppress messages from default scripts in Debug Window";
var DEBUG_WINDOW_SUPPRESS_DEFAULTS_SCRIPTS = 'debugWindowSuppressDefaultScripts';
var suppressDefaultScripts = Settings.getValue(DEBUG_WINDOW_SUPPRESS_DEFAULTS_SCRIPTS, false)
const SUPPRESS_DEFAULT_SCRIPTS_MENU_NAME = "Developer"
const SUPPRESS_DEFAULT_SCRIPTS_ITEM_NAME = "Suppress messages from default scripts in Debug Window";
const DEBUG_WINDOW_SUPPRESS_DEFAULTS_SCRIPTS = 'debugWindowSuppressDefaultScripts';
let suppressDefaultScripts = Settings.getValue(DEBUG_WINDOW_SUPPRESS_DEFAULTS_SCRIPTS, false)
Menu.addMenuItem({
menuName: SUPPRESS_DEFAULT_SCRIPTS_MENU_NAME,
menuItemName: SUPPRESS_DEFAULT_SCRIPTS_ITEM_NAME,
Expand All @@ -37,30 +37,36 @@ Menu.menuItemEvent.connect(function(menuItem) {
});

// Set up the qml ui
var qml = Script.resolvePath('debugWindow.qml');
const qml = Script.resolvePath('debugWindow.qml');

var HMD_DEBUG_WINDOW_GEOMETRY_KEY = 'hmdDebugWindowGeometry';
var hmdDebugWindowGeometryValue = Settings.getValue(HMD_DEBUG_WINDOW_GEOMETRY_KEY);
const HMD_DEBUG_WINDOW_GEOMETRY_KEY = 'hmdDebugWindowGeometry';
const hmdDebugWindowGeometryValue = Settings.getValue(HMD_DEBUG_WINDOW_GEOMETRY_KEY);

var windowWidth = 400;
var windowHeight = 900;
let windowWidth = 400;
let windowHeight = 900;

var hasPosition = false;
var windowX = 0;
var windowY = 0;
let hasPosition = false;
let windowX = 0;
let windowY = 0;

if (hmdDebugWindowGeometryValue !== '') {
var geometry = JSON.parse(hmdDebugWindowGeometryValue);
const geometry = JSON.parse(hmdDebugWindowGeometryValue);
if ((geometry.x !== 0) && (geometry.y !== 0)) {
windowWidth = geometry.width;
windowHeight = geometry.height;
windowX = geometry.x;
windowY = geometry.y;

// Constrain window position to within the viewport
const viewportDimensions = Controller.getViewportDimensions();
if (windowX > viewportDimensions.x) windowX = viewportDimensions.x - windowWidth;
if (windowY > viewportDimensions.y) windowX = viewportDimensions.y - windowHeight;

hasPosition = true;
}
}

var window = new OverlayWindow({
const window = new OverlayWindow({
title: 'Debug Window',
source: qml,
width: windowWidth, height: windowHeight,
Expand Down Expand Up @@ -92,18 +98,23 @@ function shouldLogMessage(scriptFileName) {
|| (scriptFileName !== "defaultScripts.js" && scriptFileName != "controllerScripts.js");
}

var getFormattedDate = function() {
var date = new Date();
const getFormattedDate = function() {
const date = new Date();
return date.getMonth() + "/" + date.getDate() + " " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
};

var sendToLogWindow = function(type, message, scriptFileName) {
const sendToLogWindow = function(type, message, scriptFileName) {
if (shouldLogMessage(scriptFileName)) {
var typeFormatted = "";
let typeFormatted = "";
if (type) {
typeFormatted = type + " - ";
}
window.sendToQml("[" + getFormattedDate() + "] " + "[" + scriptFileName + "] " + typeFormatted + message);
window.sendToQml({
date: getFormattedDate(),
scriptFileName: scriptFileName,
type: type,
message: message,
});
}
};

Expand Down Expand Up @@ -132,7 +143,7 @@ Script.scriptEnding.connect(function () {
Menu.isOptionChecked(SUPPRESS_DEFAULT_SCRIPTS_ITEM_NAME));
Menu.removeMenuItem(SUPPRESS_DEFAULT_SCRIPTS_MENU_NAME, SUPPRESS_DEFAULT_SCRIPTS_ITEM_NAME);

var geometry = JSON.stringify({
const geometry = JSON.stringify({
x: window.position.x,
y: window.position.y,
width: window.size.x,
Expand Down
261 changes: 245 additions & 16 deletions scripts/developer/debugging/debugWindow.qml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//

import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.0
import Hifi 1.0 as Hifi
import TabletScriptingInterface 1.0

import stylesUit 1.0

Rectangle {

ColumnLayout {
id: root
HifiConstants { id: hifi }
width: parent ? parent.width : 100
height: parent ? parent.height : 100

Expand All @@ -23,22 +28,246 @@ Rectangle {

property var channel;

TextArea {
id: textArea
width: parent.width
height: parent.height
text:""
Rectangle {
Layout.fillHeight: true
Layout.preferredWidth: parent.width
color: hifi.colors.darkGray

ScrollView {
id: logView
anchors.fill: parent
clip: true

ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AlwaysOn

TextArea {
id: textArea
width: root.width
height: parent.height - bottomRow.height
wrapMode: TextArea.Wrap
placeholderText: qsTr("Debug messages from your running scripts will appear here...")
placeholderTextColor: hifi.colors.lightGrayText
readOnly: true
selectByMouse: true
textFormat: TextEdit.RichText

background: Rectangle {
color: "transparent"
}
text:""

onTextChanged: {
if (logView.scrollDelay) {
// Scroll to the bottom, but only once within a short period of time
// rather than for every message.
scrollTimer.restart();
} else {
// Scroll to the bottom, immediately!
logView.scrollToBottom();
logView.scrollDelay = true;
}
}
}

// Do we want to use the timer for the next scroll (true),
// or scroll immediately (false);
property bool scrollDelay: true

Timer {
id: scrollTimer
interval: 1000; running: false; repeat: false;
onTriggered: {
logView.scrollToBottom();
}
}

function scrollToBottom() { // This is not strictly necessary now, but is kept as a fail-safe
logView.ScrollBar.vertical.position = 1.0 - logView.ScrollBar.vertical.size;
}
}

}

RowLayout {
id: bottomRow
Layout.preferredWidth: parent.width

// Helpful text
Text {
Layout.fillWidth: true
Layout.preferredWidth: parent.width
Layout.maximumHeight: selectScriptName.height
text: "Clear view and filter logs by script name"
color: hifi.colors.lightGrayText
horizontalAlignment: Text.AlignRight
wrapMode: Text.WordWrap
clip: true


}

Text {
text: "-->"
color: hifi.colors.lightGrayText
horizontalAlignment: Text.AlignHCenter
clip:true
}
Comment on lines +110 to +115

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A label next to the control is good, the arrow isn't necessary.


// Drop down menu with all seen script names
ComboBox {
id: selectScriptName
Layout.preferredWidth: 150
Layout.preferredHeight: 35;
padding: 0
model: scriptNames

background: Rectangle {
id: comboBoxBackground;
color: "#333";
radius: 10;
}

contentItem: Text {
anchors.centerIn: parent;
width: parent.width;
text: selectScriptName.displayText;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
elide: Text.ElideRight;
color: "white";
}

popup: Popup {
width: selectScriptName.width
padding: 1

contentItem: ListView {
clip: true
implicitHeight: contentHeight
model: selectScriptName.popup.visible ? selectScriptName.delegateModel : null
currentIndex: selectScriptName.highlightedIndex

ScrollIndicator.vertical: ScrollIndicator { }
}

background: Rectangle {
color: Qt.rgba(0,0,0,0.9)
radius: 10
}

onVisibleChanged: {
Tablet.playSound(TabletEnums.ButtonClicked);
}
}

indicator: Canvas {
id: canvas
x: selectScriptName.width - width - selectScriptName.rightPadding
y: selectScriptName.topPadding + (selectScriptName.availableHeight - height) / 2
width: 12
height: 8
contextType: "2d"

Connections {
target: selectScriptName
function onPressedChanged() { canvas.requestPaint(); }
}

onPaint: {
context.reset();
context.moveTo(0, 0);
context.lineTo(width, 0);
context.lineTo(width / 2, height);
context.closePath();
context.fillStyle = "white";
context.fill();
}
}

delegate: ItemDelegate {
contentItem: Text {
text: model.name
color: "white"
font: selectScriptName.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
background: Rectangle {
color: highlighted ? "gray" : "transparent";
radius: 10
}
highlighted: scriptNames.highlightedIndex == index
}

onActivated: {
console.log("index: ", index);
console.log("currentText:", currentText);
if (index == 0) {
currentFilter = null;
} else {
currentFilter = currentText;
}
textArea.clear()
}

}
}

function fromScript(message) {
var MAX_LINE_COUNT = 2000;
var TRIM_LINES = 500;
if (textArea.lineCount > MAX_LINE_COUNT) {
var lines = textArea.text.split('\n');
lines.splice(0, TRIM_LINES);
textArea.text = lines.join('\n');
ListModel {
id: scriptNames
ListElement {
name: "No Filter"
}
}

property var scriptNamesSet: new Set()

property var currentFilter: null;

function fromScript(line) {
const MAX_LINE_COUNT = 2000; // post-wrap lines
const TRIM_LINES = 50; // pre-wrap lines
if (textArea.lineCount > MAX_LINE_COUNT) { // textArea.lineCount represents the number of visual line breaks after text wrap, so is not an accurate count of the number of log lines.

let charIndex = -1;
for (let i = 0; i < TRIM_LINES; i++) {
charIndex = textArea.text.indexOf('</span>', charIndex + 1); // This seems to result in removing ~3x the number of log lines then we requested.
}
// Here we use .remove, as editing the .text property directly results in breaking the built-in automatic scrolling when new lines are added.
textArea.remove(0, charIndex + 7); // + the length of </span>
}
switch(line.type) {
case "WARNING":
line.color = hifi.colors.orangeHighlight
break;
case "ERROR":
line.color = hifi.colors.redHighlight
break;
case "INFO":
line.color = hifi.colors.indigoHighlight
break
default:
line.color = hifi.colors.faintGray
}

// Preserved formatting of text when used in html
const message = line.message
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/\n/g, "<br>");

if (!currentFilter || currentFilter == line.scriptFileName) {
// `white-space: pre-wrap` preserves white space at the start of lines
textArea.append(`<span style="color: ${line.color}; white-space: pre-wrap">[${line.date}] [${line.scriptFileName}] ${line.type.length > 0 ? line.type+' - ' : ""}${message}</span>`);
}

if (!scriptNamesSet.has(line.scriptFileName)) {
const scriptNameItem = {"name": line.scriptFileName};
scriptNamesSet.add(line.scriptFileName);
scriptNames.append(scriptNameItem);
}
textArea.append(message);
}

function clearWindow() {
Expand Down
Loading