diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf index 3dc3069ef5..09aefffdfe 100644 Binary files a/interface/resources/fonts/hifi-glyphs.ttf and b/interface/resources/fonts/hifi-glyphs.ttf differ diff --git a/interface/resources/qml/hifi/LetterboxMessage.qml b/interface/resources/qml/hifi/LetterboxMessage.qml new file mode 100644 index 0000000000..290cff6634 --- /dev/null +++ b/interface/resources/qml/hifi/LetterboxMessage.qml @@ -0,0 +1,52 @@ +// +// LetterboxMessage.qml +// qml/hifi +// +// Created by Zach Fox and Howard Stearns on 1/5/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// 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 "../styles-uit" + +Item { + property alias text: popupText.text + property real radius: hifi.dimensions.borderRadius + visible: false + id: letterbox + anchors.fill: parent + Rectangle { + anchors.fill: parent + color: "black" + opacity: 0.5 + radius: radius + } + Rectangle { + width: Math.max(parent.width * 0.75, 400) + height: popupText.contentHeight*1.5 + anchors.centerIn: parent + radius: radius + color: "white" + FiraSansSemiBold { + id: popupText + size: hifi.fontSizes.textFieldInput + color: hifi.colors.darkGray + horizontalAlignment: Text.AlignHCenter + anchors.fill: parent + anchors.leftMargin: 15 + anchors.rightMargin: 15 + wrapMode: Text.WordWrap + } + } + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton + onClicked: { + letterbox.visible = false + } + } +} diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 8a583bfe4c..7ad322bfa4 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -11,21 +11,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* TODO: - - prototype: - - only show kick/mute when canKick - - margins everywhere - - column head centering - - column head font - - proper button .svg on toolbar - - mvp: - - Show all participants, including ignored, and populate initial ignore/mute status. - - If name is elided, hover should scroll name left so the full name can be read. - - */ - import QtQuick 2.5 import QtQuick.Controls 1.4 import "../styles-uit" @@ -326,6 +311,11 @@ Item { visible: iAmAdmin color: hifi.colors.lightGrayText } + function letterbox(message) { + letterboxMessage.text = message; + letterboxMessage.visible = true + + } // This Rectangle refers to the [?] popup button next to "NAMES" Rectangle { color: hifi.colors.tableBackgroundLight @@ -349,7 +339,9 @@ Item { anchors.fill: parent acceptedButtons: Qt.LeftButton hoverEnabled: true - onClicked: namesPopup.visible = true + onClicked: letterbox("Bold names in the list are Avatar Display Names.\n" + + "If a Display Name isn't set, a unique Session Display Name is assigned." + + "\n\nAdministrators of this domain can also see the Username or Machine ID associated with each avatar present.") onEntered: helpText.color = hifi.colors.baseGrayHighlight onExited: helpText.color = hifi.colors.darkGray } @@ -378,87 +370,14 @@ Item { anchors.fill: parent acceptedButtons: Qt.LeftButton hoverEnabled: true - onClicked: adminPopup.visible = true + onClicked: letterbox('Silencing a user mutes their microphone. Silenced users can unmute themselves by clicking the "UNMUTE" button on their HUD.\n\n' + + "Banning a user will remove them from this domain and prevent them from returning. You can un-ban users from your domain's settings page.)") onEntered: adminHelpText.color = "#94132e" onExited: adminHelpText.color = hifi.colors.redHighlight } } - // Explanitory popup upon clicking "[?]" next to "NAMES" - Item { - visible: false - id: namesPopup - anchors.fill: pal - Rectangle { - anchors.fill: parent - color: "black" - opacity: 0.5 - radius: hifi.dimensions.borderRadius - } - Rectangle { - width: Math.max(parent.width * 0.75, 400) - height: popupText.contentHeight*1.5 - anchors.centerIn: parent - radius: hifi.dimensions.borderRadius - color: "white" - FiraSansSemiBold { - id: popupText - text: "Bold names in the list are Avatar Display Names.\n" + - "If a Display Name isn't set, a unique Session Display Name is assigned." + - "\n\nAdministrators of this domain can also see the Username or Machine ID associated with each avatar present." - size: hifi.fontSizes.textFieldInput - color: hifi.colors.darkGray - horizontalAlignment: Text.AlignHCenter - anchors.fill: parent - anchors.leftMargin: 15 - anchors.rightMargin: 15 - wrapMode: Text.WordWrap - } - } - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton - onClicked: { - namesPopup.visible = false - } - } - } - // Explanitory popup upon clicking "[?]" next to "ADMIN" - Item { - visible: false - id: adminPopup - anchors.fill: pal - Rectangle { - anchors.fill: parent - color: "black" - opacity: 0.5 - radius: hifi.dimensions.borderRadius - } - Rectangle { - width: Math.max(parent.width * 0.75, 400) - height: adminPopupText.contentHeight*1.5 - anchors.centerIn: parent - radius: hifi.dimensions.borderRadius - color: "white" - FiraSansSemiBold { - id: adminPopupText - text: 'Silencing a user mutes their microphone. Silenced users can unmute themselves by clicking the "UNMUTE" button on their HUD.\n\n' + - "Banning a user will remove them from this domain and prevent them from returning. You can un-ban users from your domain's settings page." - size: hifi.fontSizes.textFieldInput - color: hifi.colors.darkGray - horizontalAlignment: Text.AlignHCenter - anchors.fill: parent - anchors.leftMargin: 15 - anchors.rightMargin: 15 - wrapMode: Text.WordWrap - } - } - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton - onClicked: { - adminPopup.visible = false - } - } + LetterboxMessage { + id: letterboxMessage } function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml @@ -495,14 +414,20 @@ Item { sortModel(); break; case 'select': - var sessionId = message.params[0]; + var sessionIds = message.params[0]; var selected = message.params[1]; - var userIndex = findSessionIndex(sessionId); - if (selected) { - table.selection.clear(); // for now, no multi-select - table.selection.select(userIndex); + var userIndex = findSessionIndex(sessionIds[0]); + if (sessionIds.length > 1) { + letterbox('Only one user can be selected at a time.'); + } else if (userIndex < 0) { + letterbox('The last editor is not among this list of users.'); } else { - table.selection.deselect(userIndex); + if (selected) { + table.selection.clear(); // for now, no multi-select + table.selection.select(userIndex); + } else { + table.selection.deselect(userIndex); + } } break; // Received an "updateUsername()" request from the JS diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index 6ea281e467..197d8f550a 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -25,6 +25,7 @@ +
@@ -94,4 +95,4 @@
- \ No newline at end of file + diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 60aa2ebe25..1af9c1e1d6 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -39,6 +39,7 @@ function loaded() { elInView = document.getElementById("in-view") elRadius = document.getElementById("radius"); elTeleport = document.getElementById("teleport"); + elPal = document.getElementById("pal"); elEntityTable = document.getElementById("entity-table"); elInfoToggle = document.getElementById("info-toggle"); elInfoToggleGlyph = elInfoToggle.firstChild; @@ -274,6 +275,9 @@ function loaded() { elTeleport.onclick = function () { EventBridge.emitWebEvent(JSON.stringify({ type: 'teleport' })); } + elPal.onclick = function () { + EventBridge.emitWebEvent(JSON.stringify({ type: 'pal' })); + } elDelete.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); refreshEntities(); diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 3e9d3b5648..085d4f5e27 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -98,7 +98,6 @@ EntityListTool = function(opts) { webView.emitScriptEvent(JSON.stringify(data)); } - webView.webEventReceived.connect(function(data) { data = JSON.parse(data); if (data.type == "selectionUpdate") { @@ -120,6 +119,23 @@ EntityListTool = function(opts) { if (selectionManager.hasSelection()) { MyAvatar.position = selectionManager.worldPosition; } + } else if (data.type == "pal") { + var sessionIds = {}; // Collect the sessionsIds of all selected entitities, w/o duplicates. + selectionManager.selections.forEach(function (id) { + var lastEditedBy = Entities.getEntityProperties(id, 'lastEditedBy').lastEditedBy; + if (lastEditedBy) { + sessionIds[lastEditedBy] = true; + } + }); + var dedupped = Object.keys(sessionIds); + if (!selectionManager.selections.length) { + Window.alert('No objects selected.'); + } else if (!dedupped.length) { + Window.alert('There were no recent users of the ' + selectionManager.selections.length + ' selected objects.'); + } else { + // No need to subscribe if we're just sending. + Messages.sendMessage('com.highfidelity.pal', JSON.stringify({method: 'select', params: [dedupped, true]}), 'local'); + } } else if (data.type == "delete") { deleteSelectedEntities(); } else if (data.type == "toggleLocked") { @@ -140,4 +156,4 @@ EntityListTool = function(opts) { }); return that; -}; \ No newline at end of file +}; diff --git a/scripts/system/pal.js b/scripts/system/pal.js index ef16889035..c0450a0b94 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -321,7 +321,7 @@ function removeOverlays() { function handleClick(pickRay) { ExtendedOverlay.applyPickRay(pickRay, function (overlay) { // Don't select directly. Tell qml, who will give us back a list of ids. - var message = {method: 'select', params: [overlay.key, !overlay.selected]}; + var message = {method: 'select', params: [[overlay.key], !overlay.selected]}; pal.sendToQml(message); return true; }); @@ -392,6 +392,31 @@ function onClicked() { pal.setVisible(!pal.visible); } +// +// Message from other scripts, such as edit.js +// +var CHANNEL = 'com.highfidelity.pal'; +function receiveMessage(channel, messageString, senderID) { + if ((channel !== CHANNEL) || + (senderID !== MyAvatar.sessionUUID)) { + return; + } + var message = JSON.parse(messageString); + switch (message.method) { + case 'select': + if (!pal.visible) { + onClicked(); + } + pal.sendToQml(message); // Accepts objects, not just strings. + break; + default: + print('Unrecognized PAL message', messageString); + } +} +Messages.subscribe(CHANNEL); +Messages.messageReceived.connect(receiveMessage); + + var AVERAGING_RATIO = 0.05; var LOUDNESS_FLOOR = 11.0; var LOUDNESS_SCALE = 2.8 / 5.0; @@ -471,6 +496,8 @@ Script.scriptEnding.connect(function () { Users.usernameFromIDReply.disconnect(usernameFromIDReply); Window.domainChanged.disconnect(clearIgnoredInQMLAndClosePAL); Window.domainConnectionRefused.disconnect(clearIgnoredInQMLAndClosePAL); + Messages.unsubscribe(CHANNEL); + Messages.messageReceived.disconnect(receiveMessage); off(); });