overte-HifiExperiments/interface/resources/qml/hifi/NameCard.qml

604 lines
22 KiB
QML

//
// 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 QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
import stylesUit 1.0
import controlsUit 1.0 as HifiControls
import "toolbars"
// references Users, UserActivityLogger, MyAvatar, Vec3, Quat, AddressManager, Account from root context
Item {
id: thisNameCard
// Size
width: isMyCard ? pal.myCardWidth - anchors.leftMargin : pal.nearbyNameCardWidth;
height: isMyCard ? pal.myCardHeight : pal.rowHeight;
anchors.left: parent.left
anchors.leftMargin: 5
anchors.top: parent.top;
// Properties
property string profileUrl: "";
property string connectionStatus : ""
property string uuid: ""
property string displayName: ""
property string userName: ""
property real displayNameTextPixelSize: 18
property int usernameTextPixelSize: 14
property real audioLevel: 0.0
property real avgAudioLevel: 0.0
property bool isMyCard: false
property bool selected: false
property bool isAdmin: false
property bool isPresent: true
property bool isReplicated: false
property string placeName: ""
property string profilePicBorderColor: (connectionStatus == "connection" ? hifi.colors.indigoAccent : (connectionStatus == "friend" ? hifi.colors.greenHighlight : "transparent"))
property alias avImage: avatarImage
property bool has3DHTML: PlatformInfo.has3DHTML();
Item {
id: avatarImage
visible: profileUrl !== "" && userName !== "";
// Size
height: isMyCard ? 84 : 42;
width: visible ? height : 0;
anchors.top: parent.top;
anchors.topMargin: isMyCard ? 0 : 8;
anchors.left: parent.left
clip: true
Image {
id: userImage
source: profileUrl !== "" ? ((0 === profileUrl.indexOf("http")) ? profileUrl : (Account.metaverseServerURL + profileUrl)) : "";
mipmap: true;
// Anchors
anchors.fill: parent
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Item {
width: userImage.width;
height: userImage.height;
Rectangle {
anchors.centerIn: parent;
width: userImage.width; // This works because userImage is square
height: width;
radius: width;
}
}
}
}
AnimatedImage {
source: "../../icons/profilePicLoading.gif"
anchors.fill: parent;
visible: userImage.status != Image.Ready;
}
StateImage {
id: infoHoverImage;
visible: false;
imageURL: "../../images/info-icon-2-state.svg";
size: 32;
buttonState: 1;
anchors.centerIn: parent;
}
MouseArea {
anchors.fill: parent
enabled: (selected && activeTab == "nearbyTab") || isMyCard;
hoverEnabled: enabled
onClicked: {
if (has3DHTML) {
userInfoViewer.url = Account.metaverseServerURL + "/users/" + userName;
userInfoViewer.visible = true;
}
}
onEntered: infoHoverImage.visible = has3DHTML;
onExited: infoHoverImage.visible = false;
}
}
// Colored border around avatarImage
Rectangle {
id: avatarImageBorder;
visible: avatarImage.visible;
anchors.verticalCenter: avatarImage.verticalCenter;
anchors.horizontalCenter: avatarImage.horizontalCenter;
width: avatarImage.width + border.width;
height: avatarImage.height + border.width;
color: "transparent"
radius: avatarImage.height;
border.color: profilePicBorderColor;
border.width: 4;
}
// DisplayName field for my card
Rectangle {
id: myDisplayName
visible: isMyCard
// Size
width: parent.width - avatarImage.width - anchors.leftMargin - anchors.rightMargin*2;
height: 40
// Anchors
anchors.top: avatarImage.top
anchors.topMargin: avatarImage.visible ? 18 : 0;
anchors.left: avatarImage.right
anchors.leftMargin: avatarImage.visible ? 5 : 0;
anchors.rightMargin: 5;
// Style
color: hifi.colors.textFieldLightBackground
border.color: hifi.colors.blueHighlight
border.width: 0
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
myDisplayNameText.focus = true;
myDisplayNameText.cursorPosition = myDisplayNameText.positionAt(mouseX - myDisplayNameText.anchors.leftMargin, mouseY, TextInput.CursorOnCharacter);
}
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
}
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
font.family: "Fira Sans SemiBold"
font.pixelSize: displayNameTextPixelSize
selectionColor: hifi.colors.blueAccent
selectedTextColor: "black"
// Text Positioning
verticalAlignment: TextInput.AlignVCenter
horizontalAlignment: TextInput.AlignLeft
autoScroll: false;
// Signals
onEditingFinished: {
if (MyAvatar.displayName !== text) {
MyAvatar.displayName = text;
UserActivityLogger.palAction("display_name_change", text);
}
focus = false;
}
onFocusChanged: {
if (focus === true) {
myDisplayName.border.width = 1
color = "black"
autoScroll = true;
pal.currentlyEditingDisplayName = true
} else {
myDisplayName.border.width = 0
color: hifi.colors.darkGray
cursorPosition = 0;
autoScroll = false;
pal.currentlyEditingDisplayName = false
}
}
}
}
// DisplayName container for others' cards
Item {
id: displayNameContainer
visible: !isMyCard && pal.activeTab !== "connectionsTab"
// Size
width: parent.width - anchors.leftMargin - avatarImage.width - anchors.leftMargin;
height: displayNameTextPixelSize + 4
// Anchors
anchors.top: avatarImage.top;
anchors.left: avatarImage.right
anchors.leftMargin: avatarImage.visible ? 5 : 0;
// DisplayName Text for others' cards
FiraSansSemiBold {
id: displayNameText
// Properties
text: thisNameCard.displayName
elide: Text.ElideRight
// Size
width: isAdmin ? Math.min(displayNameTextMetrics.tightBoundingRect.width + 8, parent.width - adminLabelText.width - adminLabelQuestionMark.width + 8) : parent.width
// Anchors
anchors.top: parent.top
anchors.left: parent.left
// Text Size
size: displayNameTextPixelSize
// Text Positioning
verticalAlignment: Text.AlignTop
// Style
color: hifi.colors.darkGray;
MouseArea {
anchors.fill: parent
enabled: selected && pal.activeTab == "nearbyTab" && isPresent;
hoverEnabled: enabled
onClicked: {
goToUserInDomain(thisNameCard.uuid);
UserActivityLogger.palAction("go_to_user_in_domain", thisNameCard.uuid);
}
onEntered: {
displayNameText.color = hifi.colors.blueHighlight;
userNameText.color = hifi.colors.blueHighlight;
}
onExited: {
displayNameText.color = hifi.colors.darkGray
userNameText.color = hifi.colors.blueAccent;
}
}
}
TextMetrics {
id: displayNameTextMetrics
font: displayNameText.font
text: displayNameText.text
}
// "ADMIN" label for other users' cards
RalewaySemiBold {
id: adminLabelText
visible: isAdmin
text: "ADMIN"
// Text size
size: displayNameText.size - 4
// Anchors
anchors.verticalCenter: parent.verticalCenter
anchors.left: displayNameText.right
// Style
font.capitalization: Font.AllUppercase
color: hifi.colors.redHighlight
// Alignment
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignTop
}
// This Rectangle refers to the [?] popup button next to "ADMIN"
Item {
id: adminLabelQuestionMark
visible: isAdmin
// Size
width: 20
height: displayNameText.height
// Anchors
anchors.verticalCenter: parent.verticalCenter
anchors.left: adminLabelText.right
RalewayRegular {
id: adminLabelQuestionMarkText
text: "[?]"
size: adminLabelText.size
font.capitalization: Font.AllUppercase
color: hifi.colors.redHighlight
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
}
MouseArea {
anchors.fill: parent
enabled: isPresent
hoverEnabled: enabled
onClicked: letterbox(hifi.glyphs.question,
"Domain Admin",
"This user is an admin on this domain. Admins can <b>Silence</b> and <b>Ban</b> other users at their discretion - so be extra nice!")
onEntered: adminLabelQuestionMarkText.color = "#94132e"
onExited: adminLabelQuestionMarkText.color = hifi.colors.redHighlight
}
}
}
// UserName Text
FiraSansRegular {
id: userNameText
// Properties
text: thisNameCard.userName === "Unknown user" ? "not logged in" : thisNameCard.userName;
elide: Text.ElideRight
visible: thisNameCard.userName !== "";
// Size
width: parent.width
height: paintedHeight
// Anchors
anchors.top: isMyCard ? myDisplayName.bottom : pal.activeTab == "nearbyTab" ? displayNameContainer.bottom : avatarImage.top //(parent.height - displayNameTextPixelSize/2));
anchors.bottom: pal.activeTab === "connectionsTab" && !isMyCard ? avatarImage.bottom : undefined
anchors.left: avatarImage.right;
anchors.leftMargin: avatarImage.visible ? 5 : 0;
anchors.rightMargin: 5;
// Text Size
size: pal.activeTab == "nearbyTab" || isMyCard ? usernameTextPixelSize : displayNameTextPixelSize;
// Text Positioning
verticalAlignment: Text.AlignVCenter;
// Style
color: hifi.colors.blueAccent;
MouseArea {
anchors.fill: parent
enabled: selected && pal.activeTab == "nearbyTab" && thisNameCard.userName !== "" && isPresent;
hoverEnabled: enabled
onClicked: {
goToUserInDomain(thisNameCard.uuid);
UserActivityLogger.palAction("go_to_user_in_domain", thisNameCard.uuid);
}
onEntered: {
displayNameText.color = hifi.colors.blueHighlight;
userNameText.color = hifi.colors.blueHighlight;
}
onExited: {
displayNameText.color = hifi.colors.darkGray;
userNameText.color = hifi.colors.blueAccent;
}
}
}
StateImage {
id: nameCardConnectionInfoImage
visible: selected && !isMyCard && pal.activeTab == "connectionsTab" && has3DHTML
imageURL: "../../images/info-icon-2-state.svg" // PLACEHOLDER!!!
size: 32;
buttonState: 0;
anchors.left: avatarImage.right
anchors.bottom: parent.bottom
}
MouseArea {
anchors.fill:nameCardConnectionInfoImage
enabled: selected
hoverEnabled: true
onClicked: {
if (has3DHTML) {
userInfoViewer.url = Account.metaverseServerURL + "/users/" + userName;
userInfoViewer.visible = true;
}
}
onEntered: {
nameCardConnectionInfoImage.buttonState = 1;
}
onExited: {
nameCardConnectionInfoImage.buttonState = 0;
}
}
FiraSansRegular {
id: nameCardConnectionInfoText
visible: selected && !isMyCard && pal.activeTab == "connectionsTab" && PlatformInfo.has3DHTML()
height: displayNameTextPixelSize
size: displayNameTextPixelSize - 4
anchors.left: nameCardConnectionInfoImage.right
anchors.verticalCenter: nameCardConnectionInfoImage.verticalCenter
anchors.leftMargin: 5
verticalAlignment: Text.AlignVCenter
text: "Info"
color: hifi.colors.baseGray
}
HiFiGlyphs {
id: nameCardRemoveConnectionImage
visible: selected && !isMyCard && pal.activeTab == "connectionsTab"
text: hifi.glyphs.close
size: 24;
x: 120
anchors.verticalCenter: nameCardConnectionInfoImage.verticalCenter
anchors.left: has3DHTML ? nameCardConnectionInfoText.right : avatarImage.right
anchors.leftMargin: has3DHTML ? 10 : 0
}
MouseArea {
anchors.fill:nameCardRemoveConnectionImage
enabled: selected
hoverEnabled: true
onClicked: {
// send message to pal.js to forgetConnection
pal.sendToScript({method: 'removeConnection', params: thisNameCard.userName});
}
onEntered: {
nameCardRemoveConnectionImage.text = hifi.glyphs.closeInverted;
}
onExited: {
nameCardRemoveConnectionImage.text = hifi.glyphs.close;
}
}
FiraSansRegular {
id: nameCardRemoveConnectionText
visible: selected && !isMyCard && pal.activeTab == "connectionsTab"
width: parent.width
height: displayNameTextPixelSize
size: displayNameTextPixelSize - 4
anchors.left: nameCardRemoveConnectionImage.right
anchors.verticalCenter: nameCardRemoveConnectionImage.verticalCenter
anchors.leftMargin: 5
verticalAlignment: Text.AlignVCenter
text: "Forget"
color: hifi.colors.baseGray
}
HifiControls.Button {
id: visitConnectionButton
visible: selected && !isMyCard && pal.activeTab == "connectionsTab"
text: "Visit"
enabled: thisNameCard.placeName !== ""
anchors.verticalCenter: nameCardRemoveConnectionImage.verticalCenter
x: 240
onClicked: {
console.log("Vist user button clicked."); // Remove after debugging.
AddressManager.goToUser(thisNameCard.userName, false);
UserActivityLogger.palAction("go_to_user", thisNameCard.userName);
}
}
// VU Meter
Rectangle {
id: nameCardVUMeter
// Size
width: ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * (gainSlider.width);
height: 8
// Anchors
anchors.bottom: isMyCard ? avatarImage.bottom : parent.bottom;
anchors.bottomMargin: isMyCard ? 0 : height;
anchors.left: isMyCard ? userNameText.left : parent.left;
// Style
radius: 4
color: "#c5c5c5"
visible: (!isMyCard && (selected && pal.activeTab == "nearbyTab")) && isPresent
// Rectangle for the zero-gain point on the VU meter
Rectangle {
id: vuMeterZeroGain
visible: gainSlider.visible
// Size
width: 4
height: 18
// Style
color: hifi.colors.darkGray
// Anchors
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: (-gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue) * gainSlider.width - 4
}
// Rectangle for the VU meter line
Rectangle {
id: vuMeterLine
width: gainSlider.width
visible: gainSlider.visible
// Style
color: vuMeterBase.color
radius: nameCardVUMeter.radius
height: nameCardVUMeter.height / 2
anchors.verticalCenter: nameCardVUMeter.verticalCenter
}
// Rectangle for the VU meter base
Rectangle {
id: vuMeterBase
// Anchors
anchors.fill: parent
visible: !isMyCard && selected
// Style
color: parent.color
radius: parent.radius
}
// Rectangle for the VU meter audio level
Rectangle {
id: vuMeterLevel
visible: !isMyCard && selected
// Size
width: (thisNameCard.audioLevel) * parent.width
// Style
color: parent.color
radius: parent.radius
// Anchors
anchors.bottom: parent.bottom
anchors.top: parent.top
anchors.left: parent.left
}
// Gradient for the VU meter audio level
LinearGradient {
anchors.fill: vuMeterLevel
source: vuMeterLevel
start: Qt.point(0, 0)
end: Qt.point(parent.width, 0)
gradient: Gradient {
GradientStop { position: 0.0; color: "#2c8e72" }
GradientStop { position: 0.9; color: "#1fc6a6" }
GradientStop { position: 0.91; color: "#ea4c5f" }
GradientStop { position: 1.0; color: "#ea4c5f" }
}
}
}
// Per-Avatar Gain Slider
Slider {
id: gainSlider
// Size
width: isMyCard ? thisNameCard.width - 20 : thisNameCard.width;
height: 14
// Anchors
anchors.verticalCenter: nameCardVUMeter.verticalCenter;
anchors.left: nameCardVUMeter.left;
// Properties
visible: (!isMyCard && (selected && pal.activeTab == "nearbyTab")) && isPresent;
minimumValue: -60.0
maximumValue: 20.0
stepSize: 5
updateValueWhileDragging: true
value: Users.getAvatarGain(uuid)
onValueChanged: {
updateGainFromQML(uuid, value, false);
}
onPressedChanged: {
if (!pressed) {
updateGainFromQML(uuid, value, true)
}
}
MouseArea {
anchors.fill: parent
onWheel: {
// Do nothing.
}
onDoubleClicked: {
gainSlider.value = 0.0
}
onPressed: {
// Pass through to Slider
mouse.accepted = false
}
onReleased: {
// the above mouse.accepted seems to make this
// never get called, nonetheless...
mouse.accepted = false
}
}
style: SliderStyle {
groove: Rectangle {
color: "#c5c5c5"
implicitWidth: gainSlider.width
implicitHeight: 4
radius: 2
opacity: 0
}
handle: Rectangle {
anchors.centerIn: parent
color: (control.pressed || control.hovered) ? "#00b4ef" : "#8F8F8F"
implicitWidth: 10
implicitHeight: 16
}
}
}
function updateGainFromQML(avatarUuid, sliderValue, isReleased) {
if (Users.getAvatarGain(avatarUuid) != sliderValue) {
Users.setAvatarGain(avatarUuid, sliderValue);
if (isReleased) {
UserActivityLogger.palAction("avatar_gain_changed", avatarUuid);
}
}
}
// Function body by Howard Stearns 2017-01-08
function goToUserInDomain(avatarUuid) {
var avatar = AvatarList.getAvatar(avatarUuid);
if (!avatar || !avatar.position || !avatar.orientation) {
console.log("This avatar is no longer present. goToUserInDomain() failed.");
return;
}
// This is the last step of what AddressManager.goToUser does, but we don't need to resolve the username.
MyAvatar.goToLocation(avatar.position, true, Quat.cancelOutRollAndPitch(avatar.orientation), true);
}
}