Merge pull request #9401 from zfox23/PAL_DisplayName

PAL: Editable Display Names & Immediate Update
This commit is contained in:
Zach Fox 2017-01-17 16:00:41 -08:00 committed by GitHub
commit 7a568ed8a7
9 changed files with 138 additions and 26 deletions

View file

@ -262,8 +262,12 @@ void AvatarMixer::broadcastAvatarData() {
// setup a PacketList for the avatarPackets // setup a PacketList for the avatarPackets
auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData);
if (avatar.getSessionDisplayName().isEmpty() && // We haven't set it yet... if (nodeData->getAvatarSessionDisplayNameMustChange()) {
nodeData->getReceivedIdentity()) { // ... but we have processed identity (with possible displayName). const QString& existingBaseDisplayName = nodeData->getBaseDisplayName();
if (--_sessionDisplayNames[existingBaseDisplayName].second <= 0) {
_sessionDisplayNames.remove(existingBaseDisplayName);
}
QString baseName = avatar.getDisplayName().trimmed(); QString baseName = avatar.getDisplayName().trimmed();
const QRegularExpression curses{ "fuck|shit|damn|cock|cunt" }; // POC. We may eventually want something much more elaborate (subscription?). const QRegularExpression curses{ "fuck|shit|damn|cock|cunt" }; // POC. We may eventually want something much more elaborate (subscription?).
baseName = baseName.replace(curses, "*"); // Replace rather than remove, so that people have a clue that the person's a jerk. baseName = baseName.replace(curses, "*"); // Replace rather than remove, so that people have a clue that the person's a jerk.
@ -276,11 +280,14 @@ void AvatarMixer::broadcastAvatarData() {
QPair<int, int>& soFar = _sessionDisplayNames[baseName]; // Inserts and answers 0, 0 if not already present, which is what we want. QPair<int, int>& soFar = _sessionDisplayNames[baseName]; // Inserts and answers 0, 0 if not already present, which is what we want.
int& highWater = soFar.first; int& highWater = soFar.first;
nodeData->setBaseDisplayName(baseName); nodeData->setBaseDisplayName(baseName);
avatar.setSessionDisplayName((highWater > 0) ? baseName + "_" + QString::number(highWater) : baseName); QString sessionDisplayName = (highWater > 0) ? baseName + "_" + QString::number(highWater) : baseName;
avatar.setSessionDisplayName(sessionDisplayName);
highWater++; highWater++;
soFar.second++; // refcount soFar.second++; // refcount
nodeData->flagIdentityChange(); nodeData->flagIdentityChange();
sendIdentityPacket(nodeData, node); // Tell new node about its sessionUUID. Others will find out below. nodeData->setAvatarSessionDisplayNameMustChange(false);
sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name. Others will find out below.
qDebug() << "Giving session display name" << sessionDisplayName << "to node with ID" << node->getUUID();
} }
// this is an AGENT we have received head data from // this is an AGENT we have received head data from
@ -584,7 +591,7 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> mes
if (avatar.processAvatarIdentity(identity)) { if (avatar.processAvatarIdentity(identity)) {
QMutexLocker nodeDataLocker(&nodeData->getMutex()); QMutexLocker nodeDataLocker(&nodeData->getMutex());
nodeData->flagIdentityChange(); nodeData->flagIdentityChange();
nodeData->setReceivedIdentity(); nodeData->setAvatarSessionDisplayNameMustChange(true);
} }
} }
} }

View file

@ -53,8 +53,8 @@ public:
HRCTime getIdentityChangeTimestamp() const { return _identityChangeTimestamp; } HRCTime getIdentityChangeTimestamp() const { return _identityChangeTimestamp; }
void flagIdentityChange() { _identityChangeTimestamp = p_high_resolution_clock::now(); } void flagIdentityChange() { _identityChangeTimestamp = p_high_resolution_clock::now(); }
bool getReceivedIdentity() const { return _gotIdentity; } bool getAvatarSessionDisplayNameMustChange() const { return _avatarSessionDisplayNameMustChange; }
void setReceivedIdentity() { _gotIdentity = true; } void setAvatarSessionDisplayNameMustChange(bool set = true) { _avatarSessionDisplayNameMustChange = set; }
void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; } void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; }
float getFullRateDistance() const { return _fullRateDistance; } float getFullRateDistance() const { return _fullRateDistance; }
@ -112,7 +112,7 @@ private:
std::unordered_set<QUuid> _hasReceivedFirstPacketsFrom; std::unordered_set<QUuid> _hasReceivedFirstPacketsFrom;
HRCTime _identityChangeTimestamp; HRCTime _identityChangeTimestamp;
bool _gotIdentity { false }; bool _avatarSessionDisplayNameMustChange{ false };
float _fullRateDistance = FLT_MAX; float _fullRateDistance = FLT_MAX;
float _maxAvatarDistance = FLT_MAX; float _maxAvatarDistance = FLT_MAX;

View file

@ -28,7 +28,7 @@ Item {
property string uuid: "" property string uuid: ""
property string displayName: "" property string displayName: ""
property string userName: "" property string userName: ""
property int displayTextHeight: 18 property real displayNameTextPixelSize: 18
property int usernameTextHeight: 12 property int usernameTextHeight: 12
property real audioLevel: 0.0 property real audioLevel: 0.0
property bool isMyCard: false property bool isMyCard: false
@ -55,18 +55,112 @@ Item {
width: parent.width - /*avatarImage.width - parent.spacing - */parent.anchors.leftMargin - parent.anchors.rightMargin width: parent.width - /*avatarImage.width - parent.spacing - */parent.anchors.leftMargin - parent.anchors.rightMargin
height: childrenRect.height height: childrenRect.height
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
// DisplayName Text
// DisplayName field for my card
Rectangle {
id: myDisplayName
visible: isMyCard
// Size
width: parent.width + 70
height: 35
// Anchors
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: -10
// Style
color: hifi.colors.textFieldLightBackground
border.color: hifi.colors.blueHighlight
border.width: 0
TextInput {
id: myDisplayNameText
// Properties
text: thisNameCard.displayName
maximumLength: 256
clip: true
// Size
width: parent.width
height: parent.height
// Anchors
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
anchors.right: parent.right
anchors.rightMargin: editGlyph.width + editGlyph.anchors.rightMargin
// Style
color: hifi.colors.darkGray
FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; }
font.family: firaSansSemiBold.name
font.pixelSize: displayNameTextPixelSize
selectionColor: hifi.colors.blueHighlight
selectedTextColor: "black"
// Text Positioning
verticalAlignment: TextInput.AlignVCenter
horizontalAlignment: TextInput.AlignLeft
// Signals
onEditingFinished: {
pal.sendToScript({method: 'displayNameUpdate', params: text})
cursorPosition = 0
focus = false
myDisplayName.border.width = 0
color = hifi.colors.darkGray
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
hoverEnabled: true
onClicked: {
myDisplayName.border.width = 1
myDisplayNameText.focus ? myDisplayNameText.cursorPosition = myDisplayNameText.positionAt(mouseX, mouseY, TextInput.CursorOnCharacter) : myDisplayNameText.selectAll();
myDisplayNameText.focus = true
myDisplayNameText.color = "black"
}
onDoubleClicked: {
myDisplayNameText.selectAll();
myDisplayNameText.focus = true;
}
onEntered: myDisplayName.color = hifi.colors.lightGrayText
onExited: myDisplayName.color = hifi.colors.textFieldLightBackground
}
// Edit pencil glyph
HiFiGlyphs {
id: editGlyph
text: hifi.glyphs.editPencil
// Text Size
size: displayNameTextPixelSize*1.5
// Anchors
anchors.right: parent.right
anchors.rightMargin: 5
anchors.verticalCenter: parent.verticalCenter
// Style
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: hifi.colors.baseGray
}
}
// Spacer for DisplayName for my card
Rectangle {
id: myDisplayNameSpacer
width: myDisplayName.width
// Anchors
anchors.top: myDisplayName.bottom
height: 5
visible: isMyCard
opacity: 0
}
// DisplayName Text for others' cards
FiraSansSemiBold { FiraSansSemiBold {
id: displayNameText id: displayNameText
// Properties // Properties
text: thisNameCard.displayName text: thisNameCard.displayName
elide: Text.ElideRight elide: Text.ElideRight
visible: !isMyCard
// Size // Size
width: parent.width width: parent.width
// Anchors // Anchors
anchors.top: parent.top anchors.top: parent.top
// Text Size // Text Size
size: thisNameCard.displayTextHeight size: displayNameTextPixelSize
// Text Positioning // Text Positioning
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
// Style // Style
@ -83,7 +177,7 @@ Item {
// Size // Size
width: parent.width width: parent.width
// Anchors // Anchors
anchors.top: displayNameText.bottom anchors.top: isMyCard ? myDisplayNameSpacer.bottom : displayNameText.bottom
// Text Size // Text Size
size: thisNameCard.usernameTextHeight size: thisNameCard.usernameTextHeight
// Text Positioning // Text Positioning
@ -105,7 +199,7 @@ Item {
Rectangle { Rectangle {
id: nameCardVUMeter id: nameCardVUMeter
// Size // Size
width: ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * parent.width width: isMyCard ? myDisplayName.width - 20 : ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * parent.width
height: 8 height: 8
// Anchors // Anchors
anchors.top: spacer.bottom anchors.top: spacer.bottom

View file

@ -35,6 +35,17 @@ Rectangle {
// Keep a local list of per-avatar gainSliderValueDBs. Far faster than fetching this data from the server. // Keep a local list of per-avatar gainSliderValueDBs. Far faster than fetching this data from the server.
// NOTE: if another script modifies the per-avatar gain, this value won't be accurate! // NOTE: if another script modifies the per-avatar gain, this value won't be accurate!
property var gainSliderValueDB: ({}); property var gainSliderValueDB: ({});
// The letterbox used for popup messages
LetterboxMessage {
id: letterboxMessage
z: 999 // Force the popup on top of everything else
}
function letterbox(message) {
letterboxMessage.text = message
letterboxMessage.visible = true
letterboxMessage.popupRadius = 0
}
// This is the container for the PAL // This is the container for the PAL
Rectangle { Rectangle {
@ -176,8 +187,6 @@ Rectangle {
TableViewColumn { TableViewColumn {
visible: iAmAdmin visible: iAmAdmin
role: "kick" role: "kick"
// The hacky spaces used to center text over the button, since I don't know how to apply a margin
// to column header text.
title: "BAN" title: "BAN"
width: actionButtonWidth width: actionButtonWidth
movable: false movable: false
@ -337,11 +346,6 @@ Rectangle {
visible: iAmAdmin visible: iAmAdmin
color: hifi.colors.lightGrayText color: hifi.colors.lightGrayText
} }
function letterbox(message) {
letterboxMessage.text = message;
letterboxMessage.visible = true
}
// This Rectangle refers to the [?] popup button next to "NAMES" // This Rectangle refers to the [?] popup button next to "NAMES"
Rectangle { Rectangle {
color: hifi.colors.tableBackgroundLight color: hifi.colors.tableBackgroundLight
@ -402,9 +406,6 @@ Rectangle {
onExited: adminHelpText.color = hifi.colors.redHighlight onExited: adminHelpText.color = hifi.colors.redHighlight
} }
} }
LetterboxMessage {
id: letterboxMessage
}
} }
function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml

View file

@ -312,10 +312,11 @@ Item {
readonly property string error: "=" readonly property string error: "="
readonly property string settings: "@" readonly property string settings: "@"
readonly property string trash: "{" readonly property string trash: "{"
readonly property string objectGroup: "&#xe000;" readonly property string objectGroup: "\ue000"
readonly property string cm: "}" readonly property string cm: "}"
readonly property string msvg79: "~" readonly property string msvg79: "~"
readonly property string deg: "\\" readonly property string deg: "\\"
readonly property string px: "|" readonly property string px: "|"
readonly property string editPencil: "\ue00d"
} }
} }

View file

@ -1047,7 +1047,7 @@ bool AvatarData::processAvatarIdentity(const Identity& identity) {
} }
if (identity.displayName != _displayName) { if (identity.displayName != _displayName) {
setDisplayName(identity.displayName); _displayName = identity.displayName;
hasIdentityChanged = true; hasIdentityChanged = true;
} }
maybeUpdateSessionDisplayNameFromTransport(identity.sessionDisplayName); maybeUpdateSessionDisplayNameFromTransport(identity.sessionDisplayName);
@ -1094,6 +1094,9 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) {
void AvatarData::setDisplayName(const QString& displayName) { void AvatarData::setDisplayName(const QString& displayName) {
_displayName = displayName; _displayName = displayName;
_sessionDisplayName = "";
sendIdentityPacket();
qCDebug(avatars) << "Changing display name for avatar to" << displayName; qCDebug(avatars) << "Changing display name for avatar to" << displayName;
} }

View file

@ -211,7 +211,8 @@ enum class AvatarMixerPacketVersion : PacketVersion {
HandControllerJoints, HandControllerJoints,
HasKillAvatarReason, HasKillAvatarReason,
SessionDisplayName, SessionDisplayName,
Unignore Unignore,
ImmediateSessionDisplayNameUpdates
}; };
enum class DomainConnectRequestVersion : PacketVersion { enum class DomainConnectRequestVersion : PacketVersion {

View file

@ -237,6 +237,11 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like
data = message.params; data = message.params;
Users.setAvatarGain(data['sessionId'], data['gain']); Users.setAvatarGain(data['sessionId'], data['gain']);
break; break;
case 'displayNameUpdate':
if (MyAvatar.displayName != message.params) {
MyAvatar.displayName = message.params;
}
break;
default: default:
print('Unrecognized message from Pal.qml:', JSON.stringify(message)); print('Unrecognized message from Pal.qml:', JSON.stringify(message));
} }