Merge pull request #9202 from howard-stearns/pal-proto

People Action List prototype
This commit is contained in:
Howard Stearns 2016-12-15 13:56:12 -08:00 committed by GitHub
commit ba8b585368
5 changed files with 670 additions and 0 deletions

View file

@ -0,0 +1,36 @@
//
// NameCard.qml
// qml/hifi
//
// Created by Howard Stearns on 12/9/2016
// Copyright 2016 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 Hifi 1.0
import QtQuick 2.5
import "../styles-uit"
Column {
property string displayName: "";
property string userName: "";
property int displayTextHeight: 18;
property int usernameTextHeight: 12;
RalewaySemiBold {
text: parent.displayName;
size: parent.displayTextHeight;
elide: Text.ElideRight;
width: parent.width;
}
RalewayLight {
visible: parent.displayName;
text: parent.userName;
size: parent.usernameTextHeight;
elide: Text.ElideRight;
width: parent.width;
}
}

View file

@ -0,0 +1,207 @@
//
// Pal.qml
// qml/hifi
//
// People Action List
//
// Created by Howard Stearns on 12/12/2016
// Copyright 2016 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
//
/* 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
Rectangle {
id: pal;
property int keepFromHorizontalScroll: 1;
width: parent.width - keepFromHorizontalScroll;
height: parent.height;
property int nameWidth: width/2;
property int actionWidth: nameWidth / (table.columnCount - 1);
property int rowHeight: 50;
property var userData: [];
property var myData: ({displayName: "", userName: ""}); // valid dummy until set
property bool iAmAdmin: false;
function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml
var i, data = optionalData || userData, length = data.length;
for (var i = 0; i < length; i++) {
if (data[i].sessionId === sessionId) {
return i;
}
}
return -1;
}
function fromScript(message) {
switch (message.method) {
case 'users':
var data = message.params;
var myIndex = findSessionIndex('', data);
iAmAdmin = Users.canKick;
myData = data[myIndex];
data.splice(myIndex, 1);
userData = data;
sortModel();
break;
case 'select':
var sessionId = 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);
} else {
table.selection.deselect(userIndex);
}
break;
default:
console.log('Unrecognized message:', JSON.stringify(message));
}
}
ListModel {
id: userModel
}
function sortModel() {
var sortProperty = table.getColumn(table.sortIndicatorColumn).role;
var before = (table.sortIndicatorOrder === Qt.AscendingOrder) ? -1 : 1;
var after = -1 * before;
userData.sort(function (a, b) {
var aValue = a[sortProperty].toString().toLowerCase(), bValue = b[sortProperty].toString().toLowerCase();
switch (true) {
case (aValue < bValue): return before;
case (aValue > bValue): return after;
default: return 0;
}
});
table.selection.clear();
userModel.clear();
var userIndex = 0;
userData.forEach(function (datum) {
function init(property) {
if (datum[property] === undefined) {
datum[property] = false;
}
}
['ignore', 'spacer', 'mute', 'kick'].forEach(init);
datum.userIndex = userIndex++;
userModel.append(datum);
});
}
signal sendToScript(var message);
function noticeSelection() {
var userIds = [];
table.selection.forEach(function (userIndex) {
userIds.push(userData[userIndex].sessionId);
});
pal.sendToScript({method: 'selected', params: userIds});
}
Connections {
target: table.selection
onSelectionChanged: pal.noticeSelection()
}
Column {
NameCard {
id: myCard;
width: nameWidth;
displayName: myData.displayName;
userName: myData.userName;
}
TableView {
id: table;
TableViewColumn {
role: "displayName";
title: "Name";
width: nameWidth
}
TableViewColumn {
role: "ignore";
title: "Ignore"
width: actionWidth
}
TableViewColumn {
title: "";
width: actionWidth
}
TableViewColumn {
visible: iAmAdmin;
role: "mute";
title: "Mute";
width: actionWidth
}
TableViewColumn {
visible: iAmAdmin;
role: "kick";
title: "Ban"
width: actionWidth
}
model: userModel;
rowDelegate: Rectangle { // The only way I know to specify a row height.
height: rowHeight;
// The rest of this is cargo-culted to restore the default styling
SystemPalette {
id: myPalette;
colorGroup: SystemPalette.Active
}
color: {
var baseColor = styleData.alternate?myPalette.alternateBase:myPalette.base
return styleData.selected?myPalette.highlight:baseColor
}
}
itemDelegate: Item {
id: itemCell;
property bool isCheckBox: typeof(styleData.value) === 'boolean';
NameCard {
id: nameCard;
visible: !isCheckBox;
width: nameWidth;
displayName: styleData.value;
userName: model.userName;
}
Rectangle {
radius: itemCell.height / 4;
visible: isCheckBox;
color: styleData.value ? "green" : "red";
anchors.fill: parent;
MouseArea {
anchors.fill: parent;
acceptedButtons: Qt.LeftButton;
hoverEnabled: true;
onClicked: {
var newValue = !model[styleData.role];
var datum = userData[model.userIndex];
datum[styleData.role] = model[styleData.role] = newValue;
Users[styleData.role](model.sessionId);
// Just for now, while we cannot undo things:
userData.splice(model.userIndex, 1);
sortModel();
}
}
}
}
height: pal.height - myCard.height;
width: pal.width;
sortIndicatorVisible: true;
onSortIndicatorColumnChanged: sortModel();
onSortIndicatorOrderChanged: sortModel();
}
}
}

View file

@ -1849,6 +1849,7 @@ void Application::initializeUi() {
rootContext->setContextProperty("Assets", new AssetMappingsScriptingInterface());
rootContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
rootContext->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());
rootContext->setContextProperty("Camera", &_myCamera);

View file

@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 200.1" style="enable-background:new 0 0 50 200.1;" xml:space="preserve">
<style type="text/css">
.st0{fill:#414042;}
.st1{fill:#FFFFFF;}
.st2{fill:#1E1E1E;}
.st3{fill:#333333;}
</style>
<g id="Layer_2">
<g>
<g>
<path class="st0" d="M50.1,146.1c0,2.2-1.8,4-4,4h-42c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V146.1z"/>
</g>
</g>
<g>
<g>
<path class="st0" d="M50,196.1c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V196.1z"/>
</g>
</g>
<g>
<g>
<path class="st1" d="M50,46c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4V4c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V46z"/>
</g>
</g>
<g>
<path class="st2" d="M50,96.1c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V96.1z"/>
</g>
</g>
<g id="Layer_3">
</g>
<g>
<path class="st1" d="M8.1,92.5v-6.4h2.7c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
c0,0.3,0,0.5-0.1,0.8c-0.1,0.3-0.2,0.5-0.4,0.7c-0.2,0.2-0.4,0.4-0.6,0.5c-0.2,0.1-0.5,0.2-0.8,0.2H9.3v2.1H8.1z M9.3,89.2h1.4
c0.2,0,0.4-0.1,0.6-0.3c0.2-0.2,0.2-0.4,0.2-0.8c0-0.2,0-0.3-0.1-0.4c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2
c-0.1,0-0.2-0.1-0.3-0.1H9.3V89.2z"/>
<path class="st1" d="M18.3,91.4v1.1h-4.4v-6.4h4.4v1.1h-3.1v1.5h2.7v1h-2.7v1.7H18.3z"/>
<path class="st1" d="M22.1,92.5c-0.5,0-0.9-0.1-1.2-0.3s-0.7-0.4-1-0.7c-0.3-0.3-0.5-0.6-0.6-1c-0.1-0.4-0.2-0.8-0.2-1.2
c0-0.4,0.1-0.8,0.2-1.2s0.4-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7s0.8-0.3,1.2-0.3c0.5,0,0.9,0.1,1.2,0.3s0.7,0.4,1,0.7
c0.3,0.3,0.5,0.7,0.6,1c0.1,0.4,0.2,0.8,0.2,1.2c0,0.4-0.1,0.8-0.2,1.2c-0.2,0.4-0.4,0.7-0.6,1c-0.3,0.3-0.6,0.5-1,0.7
C22.9,92.4,22.5,92.5,22.1,92.5z M20.3,89.3c0,0.3,0,0.5,0.1,0.8c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.3,0.4,0.6,0.5s0.5,0.2,0.8,0.2
c0.3,0,0.5-0.1,0.8-0.2s0.4-0.3,0.6-0.5c0.1-0.2,0.3-0.4,0.3-0.7s0.1-0.5,0.1-0.8c0-0.3,0-0.5-0.1-0.8c-0.1-0.3-0.2-0.5-0.4-0.7
c-0.2-0.2-0.3-0.4-0.6-0.5c-0.2-0.1-0.5-0.2-0.7-0.2c-0.3,0-0.5,0.1-0.8,0.2s-0.4,0.3-0.6,0.5c-0.2,0.2-0.3,0.4-0.3,0.7
C20.4,88.7,20.3,89,20.3,89.3z"/>
<path class="st1" d="M26.3,92.5v-6.4H29c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
c0,0.3,0,0.5-0.1,0.8c-0.1,0.3-0.2,0.5-0.4,0.7c-0.2,0.2-0.4,0.4-0.6,0.5c-0.2,0.1-0.5,0.2-0.8,0.2h-1.5v2.1H26.3z M27.6,89.2H29
c0.2,0,0.4-0.1,0.6-0.3c0.2-0.2,0.2-0.4,0.2-0.8c0-0.2,0-0.3-0.1-0.4c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2
c-0.1,0-0.2-0.1-0.3-0.1h-1.4V89.2z"/>
<path class="st1" d="M32.1,92.5v-6.4h1.2v5.3h3.3v1.1H32.1z"/>
<path class="st1" d="M42.1,91.4v1.1h-4.4v-6.4H42v1.1h-3.1v1.5h2.7v1h-2.7v1.7H42.1z"/>
</g>
<g>
<circle class="st1" cx="25" cy="64" r="4.5"/>
<path class="st1" d="M28.2,70.9h-6.1c-2.6,0-4.6,2.2-4.6,4.7V78c2.1,1.6,4.5,2.5,7.3,2.5c3.1,0,5.9-1.2,8-3.1v-1.8
C32.8,73,30.8,70.9,28.2,70.9z"/>
<circle class="st1" cx="34" cy="65.3" r="2"/>
<path class="st1" d="M35.2,69.1h-2.3c-0.5,0-0.9,0.2-1.3,0.5c0.7,0.3,1.3,0.9,1.7,1.4c0.6,0.7,0.9,1.5,0.9,2.4
c0.2,0.5,0.2,1,0.1,1.6v0.3c0.9-1.2,1.8-3.3,2.2-4.6v-0.2C36.7,70.1,36,69.1,35.2,69.1z"/>
<circle class="st1" cx="16" cy="65.8" r="2"/>
<path class="st1" d="M15.4,75.8c-0.1-0.4-0.1-0.8,0-1.2l0-0.1c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0-0.3
c0.1-1.2,0.9-2.5,1.8-3.2c0.2-0.2,0.5-0.3,0.8-0.5c-0.3-0.3-0.7-0.4-1.1-0.4h-2.3c-0.8,0-1.5,1-1.4,1.5v0.2
C13.5,72.4,14.5,74.5,15.4,75.8z"/>
</g>
<g>
<path class="st1" d="M8.1,142.5v-6.4h2.7c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
c0,0.3,0,0.5-0.1,0.8c-0.1,0.3-0.2,0.5-0.4,0.7c-0.2,0.2-0.4,0.4-0.6,0.5c-0.2,0.1-0.5,0.2-0.8,0.2H9.3v2.1H8.1z M9.3,139.2h1.4
c0.2,0,0.4-0.1,0.6-0.3c0.2-0.2,0.2-0.4,0.2-0.8c0-0.2,0-0.3-0.1-0.4c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2
c-0.1,0-0.2-0.1-0.3-0.1H9.3V139.2z"/>
<path class="st1" d="M18.3,141.4v1.1h-4.4v-6.4h4.4v1.1h-3.1v1.5h2.7v1h-2.7v1.7H18.3z"/>
<path class="st1" d="M22.1,142.5c-0.5,0-0.9-0.1-1.2-0.3s-0.7-0.4-1-0.7c-0.3-0.3-0.5-0.6-0.6-1c-0.1-0.4-0.2-0.8-0.2-1.2
c0-0.4,0.1-0.8,0.2-1.2s0.4-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7s0.8-0.3,1.2-0.3c0.5,0,0.9,0.1,1.2,0.3s0.7,0.4,1,0.7
c0.3,0.3,0.5,0.7,0.6,1c0.1,0.4,0.2,0.8,0.2,1.2c0,0.4-0.1,0.8-0.2,1.2c-0.2,0.4-0.4,0.7-0.6,1c-0.3,0.3-0.6,0.5-1,0.7
C22.9,142.4,22.5,142.5,22.1,142.5z M20.3,139.3c0,0.3,0,0.5,0.1,0.8c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.3,0.4,0.6,0.5
s0.5,0.2,0.8,0.2c0.3,0,0.5-0.1,0.8-0.2s0.4-0.3,0.6-0.5c0.1-0.2,0.3-0.4,0.3-0.7s0.1-0.5,0.1-0.8c0-0.3,0-0.5-0.1-0.8
c-0.1-0.3-0.2-0.5-0.4-0.7c-0.2-0.2-0.3-0.4-0.6-0.5c-0.2-0.1-0.5-0.2-0.7-0.2c-0.3,0-0.5,0.1-0.8,0.2s-0.4,0.3-0.6,0.5
c-0.2,0.2-0.3,0.4-0.3,0.7C20.4,138.7,20.3,139,20.3,139.3z"/>
<path class="st1" d="M26.3,142.5v-6.4H29c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
c0,0.3,0,0.5-0.1,0.8c-0.1,0.3-0.2,0.5-0.4,0.7c-0.2,0.2-0.4,0.4-0.6,0.5c-0.2,0.1-0.5,0.2-0.8,0.2h-1.5v2.1H26.3z M27.6,139.2H29
c0.2,0,0.4-0.1,0.6-0.3c0.2-0.2,0.2-0.4,0.2-0.8c0-0.2,0-0.3-0.1-0.4c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2
c-0.1,0-0.2-0.1-0.3-0.1h-1.4V139.2z"/>
<path class="st1" d="M32.1,142.5v-6.4h1.2v5.3h3.3v1.1H32.1z"/>
<path class="st1" d="M42.1,141.4v1.1h-4.4v-6.4H42v1.1h-3.1v1.5h2.7v1h-2.7v1.7H42.1z"/>
</g>
<g>
<circle class="st1" cx="25" cy="114" r="4.5"/>
<path class="st1" d="M28.2,120.9h-6.1c-2.6,0-4.6,2.2-4.6,4.7v2.4c2.1,1.6,4.5,2.5,7.3,2.5c3.1,0,5.9-1.2,8-3.1v-1.8
C32.8,123,30.8,120.9,28.2,120.9z"/>
<circle class="st1" cx="34" cy="115.3" r="2"/>
<path class="st1" d="M35.2,119.1h-2.3c-0.5,0-0.9,0.2-1.3,0.5c0.7,0.3,1.3,0.9,1.7,1.4c0.6,0.7,0.9,1.5,0.9,2.4
c0.2,0.5,0.2,1,0.1,1.6v0.3c0.9-1.2,1.8-3.3,2.2-4.6v-0.2C36.7,120.1,36,119.1,35.2,119.1z"/>
<circle class="st1" cx="16" cy="115.8" r="2"/>
<path class="st1" d="M15.4,125.8c-0.1-0.4-0.1-0.8,0-1.2l0-0.1c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0-0.3
c0.1-1.2,0.9-2.5,1.8-3.2c0.2-0.2,0.5-0.3,0.8-0.5c-0.3-0.3-0.7-0.4-1.1-0.4h-2.3c-0.8,0-1.5,1-1.4,1.5v0.2
C13.5,122.4,14.5,124.5,15.4,125.8z"/>
</g>
<g>
<path class="st3" d="M8.1,42.5v-6.4h2.7c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
c0,0.3,0,0.5-0.1,0.8c-0.1,0.3-0.2,0.5-0.4,0.7c-0.2,0.2-0.4,0.4-0.6,0.5c-0.2,0.1-0.5,0.2-0.8,0.2H9.3v2.1H8.1z M9.3,39.2h1.4
c0.2,0,0.4-0.1,0.6-0.3c0.2-0.2,0.2-0.4,0.2-0.8c0-0.2,0-0.3-0.1-0.4c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2
c-0.1,0-0.2-0.1-0.3-0.1H9.3V39.2z"/>
<path class="st3" d="M18.3,41.4v1.1h-4.4v-6.4h4.4v1.1h-3.1v1.5h2.7v1h-2.7v1.7H18.3z"/>
<path class="st3" d="M22.1,42.5c-0.5,0-0.9-0.1-1.2-0.3s-0.7-0.4-1-0.7c-0.3-0.3-0.5-0.6-0.6-1c-0.1-0.4-0.2-0.8-0.2-1.2
c0-0.4,0.1-0.8,0.2-1.2s0.4-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7s0.8-0.3,1.2-0.3c0.5,0,0.9,0.1,1.2,0.3s0.7,0.4,1,0.7
c0.3,0.3,0.5,0.7,0.6,1c0.1,0.4,0.2,0.8,0.2,1.2c0,0.4-0.1,0.8-0.2,1.2c-0.2,0.4-0.4,0.7-0.6,1c-0.3,0.3-0.6,0.5-1,0.7
C22.9,42.4,22.5,42.5,22.1,42.5z M20.3,39.3c0,0.3,0,0.5,0.1,0.8c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.3,0.4,0.6,0.5s0.5,0.2,0.8,0.2
c0.3,0,0.5-0.1,0.8-0.2s0.4-0.3,0.6-0.5c0.1-0.2,0.3-0.4,0.3-0.7s0.1-0.5,0.1-0.8c0-0.3,0-0.5-0.1-0.8c-0.1-0.3-0.2-0.5-0.4-0.7
c-0.2-0.2-0.3-0.4-0.6-0.5c-0.2-0.1-0.5-0.2-0.7-0.2c-0.3,0-0.5,0.1-0.8,0.2s-0.4,0.3-0.6,0.5c-0.2,0.2-0.3,0.4-0.3,0.7
C20.4,38.7,20.3,39,20.3,39.3z"/>
<path class="st3" d="M26.3,42.5v-6.4H29c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
c0,0.3,0,0.5-0.1,0.8c-0.1,0.3-0.2,0.5-0.4,0.7c-0.2,0.2-0.4,0.4-0.6,0.5c-0.2,0.1-0.5,0.2-0.8,0.2h-1.5v2.1H26.3z M27.6,39.2H29
c0.2,0,0.4-0.1,0.6-0.3c0.2-0.2,0.2-0.4,0.2-0.8c0-0.2,0-0.3-0.1-0.4c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2
c-0.1,0-0.2-0.1-0.3-0.1h-1.4V39.2z"/>
<path class="st3" d="M32.1,42.5v-6.4h1.2v5.3h3.3v1.1H32.1z"/>
<path class="st3" d="M42.1,41.4v1.1h-4.4v-6.4H42v1.1h-3.1v1.5h2.7v1h-2.7v1.7H42.1z"/>
</g>
<g>
<circle class="st3" cx="25" cy="14" r="4.5"/>
<path class="st3" d="M28.2,20.9h-6.1c-2.6,0-4.6,2.2-4.6,4.7V28c2.1,1.6,4.5,2.5,7.3,2.5c3.1,0,5.9-1.2,8-3.1v-1.8
C32.8,23,30.8,20.9,28.2,20.9z"/>
<circle class="st3" cx="34" cy="15.3" r="2"/>
<path class="st3" d="M35.2,19.1h-2.3c-0.5,0-0.9,0.2-1.3,0.5c0.7,0.3,1.3,0.9,1.7,1.4c0.6,0.7,0.9,1.5,0.9,2.4
c0.2,0.5,0.2,1,0.1,1.6v0.3c0.9-1.2,1.8-3.3,2.2-4.6v-0.2C36.7,20.1,36,19.1,35.2,19.1z"/>
<circle class="st3" cx="16" cy="15.8" r="2"/>
<path class="st3" d="M15.4,25.8c-0.1-0.4-0.1-0.8,0-1.2l0-0.1c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0-0.3
c0.1-1.2,0.9-2.5,1.8-3.2c0.2-0.2,0.5-0.3,0.8-0.5c-0.3-0.3-0.7-0.4-1.1-0.4h-2.3c-0.8,0-1.5,1-1.4,1.5v0.2
C13.5,22.4,14.5,24.5,15.4,25.8z"/>
</g>
<g>
<path class="st1" d="M8.1,192.6v-6.4h2.7c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
c0,0.3,0,0.5-0.1,0.8c-0.1,0.3-0.2,0.5-0.4,0.7c-0.2,0.2-0.4,0.4-0.6,0.5c-0.2,0.1-0.5,0.2-0.8,0.2H9.3v2.1H8.1z M9.3,189.3h1.4
c0.2,0,0.4-0.1,0.6-0.3c0.2-0.2,0.2-0.4,0.2-0.8c0-0.2,0-0.3-0.1-0.4c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2
c-0.1,0-0.2-0.1-0.3-0.1H9.3V189.3z"/>
<path class="st1" d="M18.3,191.5v1.1h-4.4v-6.4h4.4v1.1h-3.1v1.5h2.7v1h-2.7v1.7H18.3z"/>
<path class="st1" d="M22.1,192.6c-0.5,0-0.9-0.1-1.2-0.3s-0.7-0.4-1-0.7c-0.3-0.3-0.5-0.6-0.6-1c-0.1-0.4-0.2-0.8-0.2-1.2
c0-0.4,0.1-0.8,0.2-1.2s0.4-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7s0.8-0.3,1.2-0.3c0.5,0,0.9,0.1,1.2,0.3s0.7,0.4,1,0.7
c0.3,0.3,0.5,0.7,0.6,1c0.1,0.4,0.2,0.8,0.2,1.2c0,0.4-0.1,0.8-0.2,1.2c-0.2,0.4-0.4,0.7-0.6,1c-0.3,0.3-0.6,0.5-1,0.7
C22.9,192.5,22.5,192.6,22.1,192.6z M20.3,189.4c0,0.3,0,0.5,0.1,0.8c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.3,0.4,0.6,0.5
s0.5,0.2,0.8,0.2c0.3,0,0.5-0.1,0.8-0.2s0.4-0.3,0.6-0.5c0.1-0.2,0.3-0.4,0.3-0.7s0.1-0.5,0.1-0.8c0-0.3,0-0.5-0.1-0.8
c-0.1-0.3-0.2-0.5-0.4-0.7c-0.2-0.2-0.3-0.4-0.6-0.5c-0.2-0.1-0.5-0.2-0.7-0.2c-0.3,0-0.5,0.1-0.8,0.2s-0.4,0.3-0.6,0.5
c-0.2,0.2-0.3,0.4-0.3,0.7C20.4,188.8,20.3,189.1,20.3,189.4z"/>
<path class="st1" d="M26.3,192.6v-6.4H29c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
c0,0.3,0,0.5-0.1,0.8c-0.1,0.3-0.2,0.5-0.4,0.7c-0.2,0.2-0.4,0.4-0.6,0.5c-0.2,0.1-0.5,0.2-0.8,0.2h-1.5v2.1H26.3z M27.6,189.3H29
c0.2,0,0.4-0.1,0.6-0.3c0.2-0.2,0.2-0.4,0.2-0.8c0-0.2,0-0.3-0.1-0.4c0-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2
c-0.1,0-0.2-0.1-0.3-0.1h-1.4V189.3z"/>
<path class="st1" d="M32.1,192.6v-6.4h1.2v5.3h3.3v1.1H32.1z"/>
<path class="st1" d="M42.1,191.5v1.1h-4.4v-6.4H42v1.1h-3.1v1.5h2.7v1h-2.7v1.7H42.1z"/>
</g>
<g>
<circle class="st1" cx="25" cy="164.2" r="4.5"/>
<path class="st1" d="M28.2,171h-6.1c-2.6,0-4.6,2.2-4.6,4.7v2.4c2.1,1.6,4.5,2.5,7.3,2.5c3.1,0,5.9-1.2,8-3.1v-1.8
C32.8,173.1,30.8,171,28.2,171z"/>
<circle class="st1" cx="34" cy="165.4" r="2"/>
<path class="st1" d="M35.2,169.2h-2.3c-0.5,0-0.9,0.2-1.3,0.5c0.7,0.3,1.3,0.9,1.7,1.4c0.6,0.7,0.9,1.5,0.9,2.4
c0.2,0.5,0.2,1,0.1,1.6v0.3c0.9-1.2,1.8-3.3,2.2-4.6v-0.2C36.7,170.2,36,169.2,35.2,169.2z"/>
<circle class="st1" cx="16" cy="165.9" r="2"/>
<path class="st1" d="M15.4,175.9c-0.1-0.4-0.1-0.8,0-1.2l0-0.1c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0-0.3
c0.1-1.2,0.9-2.5,1.8-3.2c0.2-0.2,0.5-0.3,0.8-0.5c-0.3-0.3-0.7-0.4-1.1-0.4h-2.3c-0.8,0-1.5,1-1.4,1.5v0.2
C13.5,172.5,14.5,174.6,15.4,175.9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

265
scripts/system/pal.js Normal file
View file

@ -0,0 +1,265 @@
"use strict";
/*jslint vars: true, plusplus: true, forin: true*/
/*globals Script, AvatarList, Camera, Overlays, OverlayWindow, Toolbars, Vec3, Quat, Controller, print, getControllerWorldLocation */
//
// pal.js
//
// Created by Howard Stearns on December 9, 2016
// Copyright 2016 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
//
// FIXME when we make this a defaultScript: (function() { // BEGIN LOCAL_SCOPE
Script.include("/~/system/libraries/controllers.js");
//
// Overlays.
//
var overlays = {}; // Keeps track of all our extended overlay data objects, keyed by target identifier.
function ExtendedOverlay(key, type, properties, selected) { // A wrapper around overlays to store the key it is associated with.
overlays[key] = this;
this.key = key;
this.selected = selected || false; // not undefined
this.activeOverlay = Overlays.addOverlay(type, properties); // We could use different overlays for (un)selected...
}
// Instance methods:
ExtendedOverlay.prototype.deleteOverlay = function () { // remove display and data of this overlay
Overlays.deleteOverlay(this.activeOverlay);
delete overlays[this.key];
};
ExtendedOverlay.prototype.editOverlay = function (properties) { // change display of this overlay
Overlays.editOverlay(this.activeOverlay, properties);
};
const UNSELECTED_COLOR = {red: 20, green: 250, blue: 20};
const SELECTED_COLOR = {red: 250, green: 20, blue: 20};
function color(selected) { return selected ? SELECTED_COLOR : UNSELECTED_COLOR; }
ExtendedOverlay.prototype.select = function (selected) {
if (this.selected === selected) {
return;
}
this.editOverlay({color: color(selected)});
this.selected = selected;
};
// Class methods:
var selectedIds = [];
ExtendedOverlay.isSelected = function (id) {
return -1 !== selectedIds.indexOf(id);
}
ExtendedOverlay.get = function (key) { // answer the extended overlay data object associated with the given avatar identifier
return overlays[key];
};
ExtendedOverlay.some = function (iterator) { // Bails early as soon as iterator returns truthy.
var key;
for (key in overlays) {
if (iterator(ExtendedOverlay.get(key))) {
return;
}
}
};
ExtendedOverlay.applyPickRay = function (pickRay, cb) { // cb(overlay) on the one overlay intersected by pickRay, if any.
var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones.
if (!pickedOverlay.intersects) {
return;
}
ExtendedOverlay.some(function (overlay) { // See if pickedOverlay is one of ours.
if ((overlay.activeOverlay) === pickedOverlay.overlayID) {
cb(overlay);
return true;
}
});
};
//
// The qml window and communications.
//
var pal = new OverlayWindow({
title: 'People Action List',
source: 'hifi/Pal.qml',
width: 480,
height: 640,
visible: false
});
pal.fromQml.connect(function (message) { // messages are {method, params}, like json-rpc. See also sendToQml.
print('From PAL QML:', JSON.stringify(message));
switch (message.method) {
case 'selected':
selectedIds = message.params;
ExtendedOverlay.some(function (overlay) {
var id = overlay.key;
var selected = ExtendedOverlay.isSelected(id);
overlay.select(selected);
});
break;
default:
print('Unrecognized message from Pal.qml:', JSON.stringify(message));
}
});
//
// Main operations.
//
function addAvatarNode(id) {
var selected = ExtendedOverlay.isSelected(id);
return new ExtendedOverlay(id, "sphere", { // 3d so we don't go cross-eyed looking at it, but on top of everything
solid: true,
alpha: 0.8,
color: color(selected),
drawInFront: true
}, selected);
}
function populateUserList() {
var data = [];
var counter = 1;
AvatarList.getAvatarIdentifiers().sort().forEach(function (id) { // sorting the identifiers is just an aid for debugging
var avatar = AvatarList.getAvatar(id);
var avatarPalDatum = {
displayName: avatar.displayName || ('anonymous ' + counter++),
userName: "fakeAcct" + (id || "Me"),
sessionId: id || ''
};
data.push(avatarPalDatum);
if (id) { // No overlay for ourself.
addAvatarNode(id);
}
print('PAL data:', JSON.stringify(avatarPalDatum));
});
pal.sendToQml({method: 'users', params: data});
}
var pingPong = true;
function updateOverlays() {
var eye = Camera.position;
AvatarList.getAvatarIdentifiers().forEach(function (id) {
if (!id) {
return; // don't update ourself
}
var overlay = ExtendedOverlay.get(id);
if (!overlay) { // For now, we're treating this as a temporary loss, as from the personal space bubble. Add it back.
print('Adding non-PAL avatar node', id);
overlay = addAvatarNode(id);
}
var avatar = AvatarList.getAvatar(id);
var target = avatar.position;
var distance = Vec3.distance(target, eye);
overlay.ping = pingPong;
overlay.editOverlay({
position: target,
dimensions: 0.05 * distance // constant apparent size
});
});
pingPong = !pingPong;
ExtendedOverlay.some(function (overlay) { // Remove any that weren't updated. (User is gone.)
if (overlay.ping === pingPong) {
overlay.deleteOverlay();
}
});
// We could re-populateUserList if anything added or removed, but not for now.
}
function removeOverlays() {
selectedIds = [];
ExtendedOverlay.some(function (overlay) { overlay.deleteOverlay(); });
}
//
// Clicks.
//
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]};
pal.sendToQml(message);
return true;
});
}
function handleMouseEvent(mousePressEvent) { // handleClick if we get one.
if (!mousePressEvent.isLeftButton) {
return;
}
handleClick(Camera.computePickRay(mousePressEvent.x, mousePressEvent.y));
}
// We get mouseMoveEvents from the handControllers, via handControllerPointer.
// But we don't get mousePressEvents.
var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click');
function controllerComputePickRay(hand) {
var controllerPose = getControllerWorldLocation(hand, true);
if (controllerPose.valid) {
return { origin: controllerPose.position, direction: Quat.getUp(controllerPose.orientation) };
}
}
function makeClickHandler(hand) {
return function (clicked) {
if (clicked > 0.85) {
var pickRay = controllerComputePickRay(hand);
handleClick(pickRay);
}
};
}
triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand));
triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand));
//
// Manage the connection between the button and the window.
//
var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system");
var buttonName = "pal";
var button = toolBar.addButton({
objectName: buttonName,
imageURL: Script.resolvePath("assets/images/tools/people.svg"),
visible: true,
hoverState: 2,
defaultState: 1,
buttonState: 1,
alpha: 0.9
});
var isWired = false;
function off() {
if (isWired) { // It is not ok to disconnect these twice, hence guard.
Script.update.disconnect(updateOverlays);
Controller.mousePressEvent.disconnect(handleMouseEvent);
isWired = false;
}
triggerMapping.disable(); // It's ok if we disable twice.
removeOverlays();
}
function onClicked() {
if (!pal.visible) {
populateUserList();
pal.raise();
isWired = true;
Script.update.connect(updateOverlays);
Controller.mousePressEvent.connect(handleMouseEvent);
triggerMapping.enable();
} else {
off();
}
pal.setVisible(!pal.visible);
}
//
// Button state.
//
function onVisibileChanged() {
button.writeProperty('buttonState', pal.visible ? 0 : 1);
button.writeProperty('defaultState', pal.visible ? 0 : 1);
button.writeProperty('hoverState', pal.visible ? 2 : 3);
}
button.clicked.connect(onClicked);
pal.visibleChanged.connect(onVisibileChanged);
pal.closed.connect(off);
//
// Cleanup.
//
Script.scriptEnding.connect(function () {
button.clicked.disconnect(onClicked);
toolBar.removeButton(buttonName);
pal.visibleChanged.disconnect(onVisibileChanged);
pal.closed.disconnect(off);
off();
});
// FIXME: }()); // END LOCAL_SCOPE