From a715a694f0d2ffb445e3feb0438b5f2e4af3cc98 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 3 Mar 2017 16:27:38 -0800 Subject: [PATCH 1/3] fix 'banned user doesn't disappear from pal until refresh' --- interface/resources/qml/hifi/Pal.qml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 28384f9c1c..60b29960fd 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -286,7 +286,7 @@ Rectangle { HifiControls.GlyphButton { function getGlyph() { var fileName = "vol_"; - if (model["personalMute"]) { + if (model && model.personalMute) { fileName += "x_"; } fileName += (4.0*(model ? model.avgAudioLevel : 0.0)).toFixed(0); @@ -360,10 +360,8 @@ Rectangle { Users[styleData.role](model.sessionId) UserActivityLogger["palAction"](styleData.role, model.sessionId) if (styleData.role === "kick") { - // Just for now, while we cannot undo "Ban": - userModel.remove(model.userIndex) - delete userModelData[model.userIndex] // Defensive programming - sortModel() + userModelData.splice(model.userIndex, 1) + userModel.remove(model.userIndex) // after changing userModelData, b/c ListModel can frob the data } } // muted/error glyphs From 632cdc1b38afb4a662a5c20a8356e656f3364252 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 6 Mar 2017 11:04:12 -0800 Subject: [PATCH 2/3] semicolons and whitespace per coding standard --- interface/resources/qml/hifi/Pal.qml | 816 ++++++++++++++------------- 1 file changed, 409 insertions(+), 407 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 60b29960fd..70d94908e9 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -21,45 +21,46 @@ import "../controls-uit" as HifiControls // references HMD, Users, UserActivityLogger from root context Rectangle { - id: pal + id: pal; // Size - width: parent.width - height: parent.height + width: parent.width; + height: parent.height; // Style - color: "#E3E3E3" + color: "#E3E3E3"; // Properties - property int myCardHeight: 90 - property int rowHeight: 70 - property int actionButtonWidth: 55 - property int actionButtonAllowance: actionButtonWidth * 2 - property int minNameCardWidth: palContainer.width - (actionButtonAllowance * 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth - property int nameCardWidth: minNameCardWidth + (iAmAdmin ? 0 : actionButtonAllowance) - property var myData: ({displayName: "", userName: "", audioLevel: 0.0, avgAudioLevel: 0.0, admin: true}) // valid dummy until set + property int myCardHeight: 90; + property int rowHeight: 70; + property int actionButtonWidth: 55; + property int actionButtonAllowance: actionButtonWidth * 2; + property int minNameCardWidth: palContainer.width - (actionButtonAllowance * 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth; + property int nameCardWidth: minNameCardWidth + (iAmAdmin ? 0 : actionButtonAllowance); + property var myData: ({displayName: "", userName: "", audioLevel: 0.0, avgAudioLevel: 0.0, admin: true}); // valid dummy until set property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring. - property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities. - property bool iAmAdmin: false + property var userModelData: []; // This simple list is essentially a mirror of the userModel listModel without all the extra complexities. + property bool iAmAdmin: false; - HifiConstants { id: hifi } + HifiConstants { id: hifi; } // The letterbox used for popup messages LetterboxMessage { - id: letterboxMessage - z: 999 // Force the popup on top of everything else + + id: letterboxMessage; + z: 999; // Force the popup on top of everything else } function letterbox(headerGlyph, headerText, message) { - letterboxMessage.headerGlyph = headerGlyph - letterboxMessage.headerText = headerText - letterboxMessage.text = message - letterboxMessage.visible = true - letterboxMessage.popupRadius = 0 + letterboxMessage.headerGlyph = headerGlyph; + letterboxMessage.headerText = headerText; + letterboxMessage.text = message; + letterboxMessage.visible = true; + letterboxMessage.popupRadius = 0; } Settings { - id: settings - category: "pal" - property bool filtered: false - property int nearDistance: 30 - property int sortIndicatorColumn: 1 - property int sortIndicatorOrder: Qt.AscendingOrder + id: settings; + category: "pal"; + property bool filtered: false; + property int nearDistance: 30; + property int sortIndicatorColumn: 1; + property int sortIndicatorOrder: Qt.AscendingOrder; } function refreshWithFilter() { // We should just be able to set settings.filtered to filter.checked, but see #3249, so send to .js for saving. @@ -68,418 +69,418 @@ Rectangle { // This is the container for the PAL Rectangle { - property bool punctuationMode: false - id: palContainer + property bool punctuationMode: false; + id: palContainer; // Size - width: pal.width - 10 - height: pal.height - 10 + width: pal.width - 10; + height: pal.height - 10; // Style - color: pal.color + color: pal.color; // Anchors - anchors.centerIn: pal + anchors.centerIn: pal; // Properties - radius: hifi.dimensions.borderRadius + radius: hifi.dimensions.borderRadius; - // This contains the current user's NameCard and will contain other information in the future - Rectangle { - id: myInfo - // Size - width: palContainer.width - height: myCardHeight - // Style - color: pal.color - // Anchors - anchors.top: palContainer.top - // Properties - radius: hifi.dimensions.borderRadius - // This NameCard refers to the current user's NameCard (the one above the table) - NameCard { - id: myCard - // Properties - displayName: myData.displayName - userName: myData.userName - audioLevel: myData.audioLevel - avgAudioLevel: myData.avgAudioLevel - isMyCard: true + // This contains the current user's NameCard and will contain other information in the future + Rectangle { + id: myInfo; // Size - width: minNameCardWidth - height: parent.height - // Anchors - anchors.left: parent.left - } - Row { - HifiControls.CheckBox { - id: filter - checked: settings.filtered - text: "in view" - boxSize: reload.height * 0.70 - onCheckedChanged: refreshWithFilter() - } - HifiControls.GlyphButton { - id: reload - glyph: hifi.glyphs.reload - width: reload.height - onClicked: refreshWithFilter() - } - spacing: 50 - anchors { - right: parent.right - top: parent.top - topMargin: 10 - } - } - } - // Rectangles used to cover up rounded edges on bottom of MyInfo Rectangle - Rectangle { - color: pal.color - width: palContainer.width - height: 10 - anchors.top: myInfo.bottom - anchors.left: parent.left - } - Rectangle { - color: pal.color - width: palContainer.width - height: 10 - anchors.bottom: table.top - anchors.left: parent.left - } - // Rectangle that houses "ADMIN" string - Rectangle { - id: adminTab - // Size - width: 2*actionButtonWidth + hifi.dimensions.scrollbarBackgroundWidth + 2 - height: 40 - // Anchors - anchors.bottom: myInfo.bottom - anchors.bottomMargin: -10 - anchors.right: myInfo.right - // Properties - visible: iAmAdmin - // Style - color: hifi.colors.tableRowLightEven - radius: hifi.dimensions.borderRadius - border.color: hifi.colors.lightGrayText - border.width: 2 - // "ADMIN" text - RalewaySemiBold { - id: adminTabText - text: "ADMIN" - // Text size - size: hifi.fontSizes.tableHeading + 2 - // Anchors - anchors.top: parent.top - anchors.topMargin: 8 - anchors.left: parent.left - anchors.right: parent.right - anchors.rightMargin: hifi.dimensions.scrollbarBackgroundWidth + width: palContainer.width; + height: myCardHeight; // Style - font.capitalization: Font.AllUppercase - color: hifi.colors.redHighlight - // Alignment - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignTop - } - } - // This TableView refers to the table (below the current user's NameCard) - HifiControls.Table { - id: table - // Size - height: palContainer.height - myInfo.height - 4 - width: palContainer.width - 4 - // Anchors - anchors.left: parent.left - anchors.top: myInfo.bottom - // Properties - centerHeaderText: true - sortIndicatorVisible: true - headerVisible: true - sortIndicatorColumn: settings.sortIndicatorColumn - sortIndicatorOrder: settings.sortIndicatorOrder - onSortIndicatorColumnChanged: { - settings.sortIndicatorColumn = sortIndicatorColumn - sortModel() - } - onSortIndicatorOrderChanged: { - settings.sortIndicatorOrder = sortIndicatorOrder - sortModel() - } - - TableViewColumn { - role: "avgAudioLevel" - title: "LOUD" - width: actionButtonWidth - movable: false - resizable: false - } - - TableViewColumn { - id: displayNameHeader - role: "displayName" - title: table.rowCount + (table.rowCount === 1 ? " NAME" : " NAMES") - width: nameCardWidth - movable: false - resizable: false - } - TableViewColumn { - role: "ignore" - title: "IGNORE" - width: actionButtonWidth - movable: false - resizable: false - } - TableViewColumn { - visible: iAmAdmin - role: "mute" - title: "SILENCE" - width: actionButtonWidth - movable: false - resizable: false - } - TableViewColumn { - visible: iAmAdmin - role: "kick" - title: "BAN" - width: actionButtonWidth - movable: false - resizable: false - } - model: ListModel { - id: userModel - } - - // This Rectangle refers to each Row in the table. - rowDelegate: Rectangle { // The only way I know to specify a row height. - // Size - height: styleData.selected ? rowHeight : rowHeight - 15 - color: styleData.selected - ? hifi.colors.orangeHighlight - : styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd - } - - // This Item refers to the contents of each Cell - itemDelegate: Item { - id: itemCell - property bool isCheckBox: styleData.role === "personalMute" || styleData.role === "ignore" - property bool isButton: styleData.role === "mute" || styleData.role === "kick" - property bool isAvgAudio: styleData.role === "avgAudioLevel" - - // This NameCard refers to the cell that contains an avatar's - // DisplayName and UserName + color: pal.color; + // Anchors + anchors.top: palContainer.top; + // Properties + radius: hifi.dimensions.borderRadius; + // This NameCard refers to the current user's NameCard (the one above the table) NameCard { - id: nameCard + id: myCard; // Properties - displayName: styleData.value - userName: model ? model.userName : "" - audioLevel: model ? model.audioLevel : 0.0 - avgAudioLevel: model ? model.avgAudioLevel : 0.0 - visible: !isCheckBox && !isButton && !isAvgAudio - uuid: model ? model.sessionId : "" - selected: styleData.selected - isAdmin: model && model.admin + displayName: myData.displayName; + userName: myData.userName; + audioLevel: myData.audioLevel; + avgAudioLevel: myData.avgAudioLevel; + isMyCard: true; // Size - width: nameCardWidth - height: parent.height + width: minNameCardWidth; + height: parent.height; // Anchors - anchors.left: parent.left + anchors.left: parent.left; } - HifiControls.GlyphButton { - function getGlyph() { - var fileName = "vol_"; - if (model && model.personalMute) { - fileName += "x_"; - } - fileName += (4.0*(model ? model.avgAudioLevel : 0.0)).toFixed(0); - return hifi.glyphs[fileName]; + Row { + HifiControls.CheckBox { + id: filter; + checked: settings.filtered; + text: "in view"; + boxSize: reload.height * 0.70; + onCheckedChanged: refreshWithFilter(); } - id: avgAudioVolume - visible: isAvgAudio - glyph: getGlyph() - width: 32 - size: height - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - onClicked: { - // cannot change mute status when ignoring - if (!model["ignore"]) { - var newValue = !model["personalMute"]; - userModel.setProperty(model.userIndex, "personalMute", newValue) - userModelData[model.userIndex]["personalMute"] = newValue // Defensive programming - Users["personalMute"](model.sessionId, newValue) - UserActivityLogger["palAction"](newValue ? "personalMute" : "un-personalMute", model.sessionId) - } + HifiControls.GlyphButton { + id: reload; + glyph: hifi.glyphs.reload; + width: reload.height; + onClicked: refreshWithFilter(); } + spacing: 50; + anchors { + right: parent.right; + top: parent.top; + topMargin: 10; + } + } + } + // Rectangles used to cover up rounded edges on bottom of MyInfo Rectangle + Rectangle { + color: pal.color; + width: palContainer.width; + height: 10; + anchors.top: myInfo.bottom; + anchors.left: parent.left; + } + Rectangle { + color: pal.color; + width: palContainer.width; + height: 10; + anchors.bottom: table.top; + anchors.left: parent.left; + } + // Rectangle that houses "ADMIN" string + Rectangle { + id: adminTab; + // Size + width: 2*actionButtonWidth + hifi.dimensions.scrollbarBackgroundWidth + 2; + height: 40; + // Anchors + anchors.bottom: myInfo.bottom; + anchors.bottomMargin: -10; + anchors.right: myInfo.right; + // Properties + visible: iAmAdmin; + // Style + color: hifi.colors.tableRowLightEven; + radius: hifi.dimensions.borderRadius; + border.color: hifi.colors.lightGrayText; + border.width: 2; + // "ADMIN" text + RalewaySemiBold { + id: adminTabText; + text: "ADMIN"; + // Text size + size: hifi.fontSizes.tableHeading + 2; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 8; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.rightMargin: hifi.dimensions.scrollbarBackgroundWidth; + // Style + font.capitalization: Font.AllUppercase; + color: hifi.colors.redHighlight; + // Alignment + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignTop; + } + } + // This TableView refers to the table (below the current user's NameCard) + HifiControls.Table { + id: table; + // Size + height: palContainer.height - myInfo.height - 4; + width: palContainer.width - 4; + // Anchors + anchors.left: parent.left; + anchors.top: myInfo.bottom; + // Properties + centerHeaderText: true; + sortIndicatorVisible: true; + headerVisible: true; + sortIndicatorColumn: settings.sortIndicatorColumn; + sortIndicatorOrder: settings.sortIndicatorOrder; + onSortIndicatorColumnChanged: { + settings.sortIndicatorColumn = sortIndicatorColumn; + sortModel(); + } + onSortIndicatorOrderChanged: { + settings.sortIndicatorOrder = sortIndicatorOrder; + sortModel(); } - // This CheckBox belongs in the columns that contain the stateful action buttons ("Mute" & "Ignore" for now) - // KNOWN BUG with the Checkboxes: When clicking in the center of the sorting header, the checkbox - // will appear in the "hovered" state. Hovering over the checkbox will fix it. - // Clicking on the sides of the sorting header doesn't cause this problem. - // I'm guessing this is a QT bug and not anything I can fix. I spent too long trying to work around it... - // I'm just going to leave the minor visual bug in. - HifiControls.CheckBox { - id: actionCheckBox - visible: isCheckBox - anchors.centerIn: parent - checked: model ? model[styleData.role] : false - // If this is a "Personal Mute" checkbox, disable the checkbox if the "Ignore" checkbox is checked. - enabled: !(styleData.role === "personalMute" && (model ? model["ignore"] : true)) - boxSize: 24 - onClicked: { - var newValue = !model[styleData.role] - userModel.setProperty(model.userIndex, styleData.role, newValue) - userModelData[model.userIndex][styleData.role] = newValue // Defensive programming - Users[styleData.role](model.sessionId, newValue) - UserActivityLogger["palAction"](newValue ? styleData.role : "un-" + styleData.role, model.sessionId) - if (styleData.role === "ignore") { - userModel.setProperty(model.userIndex, "personalMute", newValue) - userModelData[model.userIndex]["personalMute"] = newValue // Defensive programming - if (newValue) { - ignored[model.sessionId] = userModelData[model.userIndex] - } else { - delete ignored[model.sessionId] - } - avgAudioVolume.glyph = avgAudioVolume.getGlyph() - } - // http://doc.qt.io/qt-5/qtqml-syntax-propertybinding.html#creating-property-bindings-from-javascript - // I'm using an explicit binding here because clicking a checkbox breaks the implicit binding as set by - // "checked:" statement above. - checked = Qt.binding(function() { return (model[styleData.role])}) - } + TableViewColumn { + role: "avgAudioLevel"; + title: "LOUD"; + width: actionButtonWidth; + movable: false; + resizable: false; } - // This Button belongs in the columns that contain the stateless action buttons ("Silence" & "Ban" for now) - HifiControls.Button { - id: actionButton - color: 2 // Red - visible: isButton - anchors.centerIn: parent - width: 32 - height: 32 - onClicked: { - Users[styleData.role](model.sessionId) - UserActivityLogger["palAction"](styleData.role, model.sessionId) - if (styleData.role === "kick") { - userModelData.splice(model.userIndex, 1) - userModel.remove(model.userIndex) // after changing userModelData, b/c ListModel can frob the data - } - } - // muted/error glyphs - HiFiGlyphs { - text: (styleData.role === "kick") ? hifi.glyphs.error : hifi.glyphs.muted + TableViewColumn { + id: displayNameHeader; + role: "displayName"; + title: table.rowCount + (table.rowCount === 1 ? " NAME" : " NAMES"); + width: nameCardWidth; + movable: false; + resizable: false; + } + TableViewColumn { + role: "ignore"; + title: "IGNORE"; + width: actionButtonWidth; + movable: false; + resizable: false; + } + TableViewColumn { + visible: iAmAdmin; + role: "mute"; + title: "SILENCE"; + width: actionButtonWidth; + movable: false; + resizable: false; + } + TableViewColumn { + visible: iAmAdmin; + role: "kick"; + title: "BAN"; + width: actionButtonWidth; + movable: false; + resizable: false; + } + model: ListModel { + id: userModel; + } + + // This Rectangle refers to each Row in the table. + rowDelegate: Rectangle { // The only way I know to specify a row height. + // Size + height: styleData.selected ? rowHeight : rowHeight - 15; + color: styleData.selected + ? hifi.colors.orangeHighlight + : styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd; + } + + // This Item refers to the contents of each Cell + itemDelegate: Item { + id: itemCell; + property bool isCheckBox: styleData.role === "personalMute" || styleData.role === "ignore"; + property bool isButton: styleData.role === "mute" || styleData.role === "kick"; + property bool isAvgAudio: styleData.role === "avgAudioLevel"; + + // This NameCard refers to the cell that contains an avatar's + // DisplayName and UserName + NameCard { + id: nameCard; + // Properties + displayName: styleData.value; + userName: model ? model.userName : ""; + audioLevel: model ? model.audioLevel : 0.0; + avgAudioLevel: model ? model.avgAudioLevel : 0.0; + visible: !isCheckBox && !isButton && !isAvgAudio; + uuid: model ? model.sessionId : ""; + selected: styleData.selected; + isAdmin: model && model.admin; // Size - size: parent.height*1.3 + width: nameCardWidth; + height: parent.height; // Anchors - anchors.fill: parent - // Style - horizontalAlignment: Text.AlignHCenter - color: enabled ? hifi.buttons.textColor[actionButton.color] - : hifi.buttons.disabledTextColor[actionButton.colorScheme] + anchors.left: parent.left; + } + HifiControls.GlyphButton { + function getGlyph() { + var fileName = "vol_"; + if (model && model.personalMute) { + fileName += "x_"; + } + fileName += (4.0*(model ? model.avgAudioLevel : 0.0)).toFixed(0); + return hifi.glyphs[fileName]; + } + id: avgAudioVolume; + visible: isAvgAudio; + glyph: getGlyph(); + width: 32; + size: height; + anchors.verticalCenter: parent.verticalCenter; + anchors.horizontalCenter: parent.horizontalCenter; + onClicked: { + // cannot change mute status when ignoring + if (!model["ignore"]) { + var newValue = !model["personalMute"]; + userModel.setProperty(model.userIndex, "personalMute", newValue); + userModelData[model.userIndex]["personalMute"] = newValue; // Defensive programming + Users["personalMute"](model.sessionId, newValue); + UserActivityLogger["palAction"](newValue ? "personalMute" : "un-personalMute", model.sessionId); + } + } + } + + // This CheckBox belongs in the columns that contain the stateful action buttons ("Mute" & "Ignore" for now) + // KNOWN BUG with the Checkboxes: When clicking in the center of the sorting header, the checkbox + // will appear in the "hovered" state. Hovering over the checkbox will fix it. + // Clicking on the sides of the sorting header doesn't cause this problem. + // I'm guessing this is a QT bug and not anything I can fix. I spent too long trying to work around it... + // I'm just going to leave the minor visual bug in. + HifiControls.CheckBox { + id: actionCheckBox; + visible: isCheckBox; + anchors.centerIn: parent; + checked: model ? model[styleData.role] : false; + // If this is a "Personal Mute" checkbox, disable the checkbox if the "Ignore" checkbox is checked. + enabled: !(styleData.role === "personalMute" && (model ? model["ignore"] : true)); + boxSize: 24; + onClicked: { + var newValue = !model[styleData.role]; + userModel.setProperty(model.userIndex, styleData.role, newValue); + userModelData[model.userIndex][styleData.role] = newValue; // Defensive programming + Users[styleData.role](model.sessionId, newValue); + UserActivityLogger["palAction"](newValue ? styleData.role : "un-" + styleData.role, model.sessionId); + if (styleData.role === "ignore") { + userModel.setProperty(model.userIndex, "personalMute", newValue); + userModelData[model.userIndex]["personalMute"] = newValue; // Defensive programming + if (newValue) { + ignored[model.sessionId] = userModelData[model.userIndex]; + } else { + delete ignored[model.sessionId]; + } + avgAudioVolume.glyph = avgAudioVolume.getGlyph(); + } + // http://doc.qt.io/qt-5/qtqml-syntax-propertybinding.html#creating-property-bindings-from-javascript + // I'm using an explicit binding here because clicking a checkbox breaks the implicit binding as set by + // "checked:" statement above. + checked = Qt.binding(function() { return (model[styleData.role])}); + } + } + + // This Button belongs in the columns that contain the stateless action buttons ("Silence" & "Ban" for now) + HifiControls.Button { + id: actionButton; + color: 2; // Red + visible: isButton; + anchors.centerIn: parent; + width: 32; + height: 32; + onClicked: { + Users[styleData.role](model.sessionId); + UserActivityLogger["palAction"](styleData.role, model.sessionId); + if (styleData.role === "kick") { + userModelData.splice(model.userIndex, 1); + userModel.remove(model.userIndex); // after changing userModelData, b/c ListModel can frob the data + } + } + // muted/error glyphs + HiFiGlyphs { + text: (styleData.role === "kick") ? hifi.glyphs.error : hifi.glyphs.muted; + // Size + size: parent.height*1.3; + // Anchors + anchors.fill: parent; + // Style + horizontalAlignment: Text.AlignHCenter; + color: enabled ? hifi.buttons.textColor[actionButton.color] + : hifi.buttons.disabledTextColor[actionButton.colorScheme]; + } } } } - } - // Separator between user and admin functions - Rectangle { - // Size - width: 2 - height: table.height - // Anchors - anchors.left: adminTab.left - anchors.top: table.top - // Properties - visible: iAmAdmin - color: hifi.colors.lightGrayText - } - TextMetrics { - id: displayNameHeaderMetrics - text: displayNameHeader.title - // font: displayNameHeader.font // was this always undefined? giving error now... - } - // This Rectangle refers to the [?] popup button next to "NAMES" - Rectangle { - color: hifi.colors.tableBackgroundLight - width: 20 - height: hifi.dimensions.tableHeaderHeight - 2 - anchors.left: table.left - anchors.top: table.top - anchors.topMargin: 1 - anchors.leftMargin: actionButtonWidth + nameCardWidth/2 + displayNameHeaderMetrics.width/2 + 6 - RalewayRegular { - id: helpText - text: "[?]" - size: hifi.fontSizes.tableHeading + 2 - font.capitalization: Font.AllUppercase - color: hifi.colors.darkGray - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - anchors.fill: parent + // Separator between user and admin functions + Rectangle { + // Size + width: 2; + height: table.height; + // Anchors + anchors.left: adminTab.left; + anchors.top: table.top; + // Properties + visible: iAmAdmin; + color: hifi.colors.lightGrayText; } - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton - hoverEnabled: true - onClicked: letterbox(hifi.glyphs.question, - "Display Names", - "Bold names in the list are avatar display names.
" + - "If a display name isn't set, a unique session display name is assigned." + - "

Administrators 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 + TextMetrics { + id: displayNameHeaderMetrics; + text: displayNameHeader.title; + // font: displayNameHeader.font // was this always undefined? giving error now... } - } - // This Rectangle refers to the [?] popup button next to "ADMIN" - Rectangle { - visible: iAmAdmin - color: adminTab.color - width: 20 - height: 28 - anchors.right: adminTab.right - anchors.rightMargin: 10 + hifi.dimensions.scrollbarBackgroundWidth - anchors.top: adminTab.top - anchors.topMargin: 2 - RalewayRegular { - id: adminHelpText - text: "[?]" - size: hifi.fontSizes.tableHeading + 2 - font.capitalization: Font.AllUppercase - color: hifi.colors.redHighlight - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - anchors.fill: parent + // This Rectangle refers to the [?] popup button next to "NAMES" + Rectangle { + color: hifi.colors.tableBackgroundLight; + width: 20; + height: hifi.dimensions.tableHeaderHeight - 2; + anchors.left: table.left; + anchors.top: table.top; + anchors.topMargin: 1; + anchors.leftMargin: actionButtonWidth + nameCardWidth/2 + displayNameHeaderMetrics.width/2 + 6; + RalewayRegular { + id: helpText; + text: "[?]"; + size: hifi.fontSizes.tableHeading + 2; + font.capitalization: Font.AllUppercase; + color: hifi.colors.darkGray; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + anchors.fill: parent; + } + MouseArea { + anchors.fill: parent; + acceptedButtons: Qt.LeftButton; + hoverEnabled: true; + onClicked: letterbox(hifi.glyphs.question, + "Display Names", + "Bold names in the list are avatar display names.
" + + "If a display name isn't set, a unique session display name is assigned." + + "

Administrators 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; + } } - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton - hoverEnabled: true - onClicked: letterbox(hifi.glyphs.question, - "Admin Actions", - "Silence mutes a user's microphone. Silenced users can unmute themselves by clicking "UNMUTE" on their toolbar.

" + - "Ban removes a user from this domain and prevents them from returning. Admins can un-ban users from the Sandbox Domain Settings page.") - onEntered: adminHelpText.color = "#94132e" - onExited: adminHelpText.color = hifi.colors.redHighlight + // This Rectangle refers to the [?] popup button next to "ADMIN" + Rectangle { + visible: iAmAdmin; + color: adminTab.color; + width: 20; + height: 28; + anchors.right: adminTab.right; + anchors.rightMargin: 10 + hifi.dimensions.scrollbarBackgroundWidth; + anchors.top: adminTab.top; + anchors.topMargin: 2; + RalewayRegular { + id: adminHelpText; + text: "[?]"; + size: hifi.fontSizes.tableHeading + 2; + font.capitalization: Font.AllUppercase; + color: hifi.colors.redHighlight; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + anchors.fill: parent; + } + MouseArea { + anchors.fill: parent; + acceptedButtons: Qt.LeftButton; + hoverEnabled: true; + onClicked: letterbox(hifi.glyphs.question, + "Admin Actions", + "Silence mutes a user's microphone. Silenced users can unmute themselves by clicking "UNMUTE" on their toolbar.

" + + "Ban removes a user from this domain and prevents them from returning. Admins can un-ban users from the Sandbox Domain Settings page."); + onEntered: adminHelpText.color = "#94132e"; + onExited: adminHelpText.color = hifi.colors.redHighlight; + } } - } - HifiControls.Keyboard { - id: keyboard - raised: myCard.currentlyEditingDisplayName && HMD.active - numeric: parent.punctuationMode - anchors { - bottom: parent.bottom - left: parent.left - right: parent.right + HifiControls.Keyboard { + id: keyboard; + raised: myCard.currentlyEditingDisplayName && HMD.active; + numeric: parent.punctuationMode; + anchors { + bottom: parent.bottom; + left: parent.left; + right: parent.right; + } } } - } // Timer used when selecting table rows that aren't yet present in the model // (i.e. when selecting avatars using edit.js or sphere overlays) Timer { - property bool selected // Selected or deselected? - property int userIndex // The userIndex of the avatar we want to select - id: selectionTimer + property bool selected; // Selected or deselected? + property int userIndex; // The userIndex of the avatar we want to select + id: selectionTimer; onTriggered: { if (selected) { table.selection.clear(); // for now, no multi-select @@ -610,7 +611,8 @@ Rectangle { } } function sortModel() { - var sortProperty = table.getColumn(table.sortIndicatorColumn).role; + var column = table.getColumn(table.sortIndicatorColumn); + var sortProperty = column ? column.role : "displayName"; var before = (table.sortIndicatorOrder === Qt.AscendingOrder) ? -1 : 1; var after = -1 * before; userModelData.sort(function (a, b) { @@ -645,7 +647,7 @@ Rectangle { pal.sendToScript({method: 'selected', params: userIds}); } Connections { - target: table.selection - onSelectionChanged: pal.noticeSelection() + target: table.selection; + onSelectionChanged: pal.noticeSelection(); } } From cb41b9135bd7feca8f984bf7e48354030641d38b Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 7 Mar 2017 12:10:52 -0800 Subject: [PATCH 3/3] fix broken merge --- interface/resources/qml/hifi/Pal.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index e3fe8152da..25362d98f1 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -627,6 +627,8 @@ Rectangle { var sortProperty = column ? column.role : "displayName"; var before = (table.sortIndicatorOrder === Qt.AscendingOrder) ? -1 : 1; var after = -1 * before; + // get selection(s) before sorting + var selectedIDs = getSelectedSessionIDs(); userModelData.sort(function (a, b) { var aValue = a[sortProperty].toString().toLowerCase(), bValue = b[sortProperty].toString().toLowerCase(); switch (true) { @@ -639,7 +641,6 @@ Rectangle { userModel.clear(); var userIndex = 0; - // get selection(s) before sorting var newSelectedIndexes = []; userModelData.forEach(function (datum) { function init(property) {