diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml new file mode 100644 index 0000000000..2c25f871b3 --- /dev/null +++ b/interface/resources/qml/hifi/NameCard.qml @@ -0,0 +1,37 @@ +// +// 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 { + visible: !isCheckBox; + property string displayName: styleData.value; + property string userName: model.userName; + property int displayTextHeight: 18; + property int usernameTextHeight: 12; + + RalewaySemiBold { + text: parent.displayName; + size: parent.displayTextHeight; + elide: Text.ElideRight; + width: parent.width; + } + RalewayLight { + visible: styleData.value; + text: parent.userName; + size: parent.usernameTextHeight; + elide: Text.ElideRight; + width: parent.width; + } +} diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml new file mode 100644 index 0000000000..c0250b2b67 --- /dev/null +++ b/interface/resources/qml/hifi/Pal.qml @@ -0,0 +1,166 @@ +// +// 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 +import "../styles-uit" // fixme should end up removeable + +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: null; + function fromScript(data) { + var myIndex = 0; + while ((myIndex < data.length) && data[myIndex].sessionId) myIndex++; // no findIndex in .qml + myData = data[myIndex]; + data.splice(myIndex, 1); + userData = data; + console.log('FIXME', JSON.stringify(myData), myIndex, JSON.stringify(userData)); + sortModel(); + } + ListModel { + id: userModel + } + function sortModel() { + var sortProperty = table.getColumn(table.sortIndicatorColumn).role; + var before = (table.sortIndicatorOrder === Qt.AscendingOrder) ? -1 : 1; + var after = -1 * before; + console.log('fixme sort', table.sortIndicatorColumn, sortProperty, before, after); + userData.sort(function (a, b) { + var aValue = a[sortProperty].toString().toLowerCase(), bValue = b[sortProperty].toString().toLowerCase(); + switch (true) { + case (aValue < bValue): console.log(aValue, bValue, before); return before; + case (aValue > bValue): console.log(aValue, bValue, after); return after; + default: return 0; + } + }); + userModel.clear(); + var userIndex = 0; + userData.forEach(function (datum) { + function init(property) { + if (datum[property] === undefined) { + datum[property] = false; + } + } + ['mute', 'spacer', 'ignore', 'ban'].forEach(init); + datum.userIndex = userIndex++; + userModel.append(datum); + }); + } + 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 { + role: "mute"; + title: "Mute"; + width: actionWidth + } + TableViewColumn { + 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(); + } + } +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4951e4d0ec..6e1e1a5b01 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1805,6 +1805,7 @@ void Application::initializeUi() { rootContext->setContextProperty("Assets", new AssetMappingsScriptingInterface()); rootContext->setContextProperty("AvatarList", DependencyManager::get().data()); + rootContext->setContextProperty("Users", DependencyManager::get().data()); rootContext->setContextProperty("Camera", &_myCamera); diff --git a/scripts/system/pal.js b/scripts/system/pal.js new file mode 100644 index 0000000000..87ca7ac61a --- /dev/null +++ b/scripts/system/pal.js @@ -0,0 +1,77 @@ +// +// 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: (function() { // BEGIN LOCAL_SCOPE + +var pal = new OverlayWindow({ + title: 'People Action List', + source: Script.resolvePath('../../resources/qml/hifi/Pal.qml'), + width: 480, + height: 640, + visible: false +}); + +function populateUserList() { + var data = [ + { + displayName: "Dummy Debugging User", + userName: "foo.bar", + sessionId: 'XXX' + } + ]; + var counter = 1; + AvatarList.getAvatarIdentifiers().forEach(function (id) { + var avatar = AvatarList.getAvatar(id); + data.push({ + displayName: avatar.displayName || ('anonymous ' + counter++), + userName: "fakeAcct" + (id || "Me"), + sessionId: id + }); + }); + pal.sendToQml(data); +} + + +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); +var buttonName = "pal"; +var button = toolBar.addButton({ + objectName: buttonName, + imageURL: Script.resolvePath("assets/images/tools/ignore.svg"), + visible: true, + hoverState: 2, + defaultState: 1, + buttonState: 1, + alpha: 0.9 +}); + +function onClicked() { + if (!pal.visible) { + populateUserList(); + pal.raise(); + } + pal.setVisible(!pal.visible); +} +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); + +Script.scriptEnding.connect(function () { + toolBar.removeButton(buttonName); + pal.visibleChanged.disconnect(onVisibileChanged); + button.clicked.disconnect(onClicked); +}); + + +// FIXME: }()); // END LOCAL_SCOPE