overte/scripts/simplifiedUI/simplifiedEmote/ui/qml/SimplifiedEmoteIndicator.qml

335 lines
12 KiB
QML

//
// SimplifiedEmoteIndicator.qml
//
// Created by Milad Nazeri on 2019-08-05
// Based on work from Zach Fox on 2019-07-08
// Copyright 2019 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.10
import QtQuick.Controls 2.3
import QtGraphicalEffects 1.0
import stylesUit 1.0 as HifiStylesUit
import TabletScriptingInterface 1.0
import hifi.simplifiedUI.simplifiedConstants 1.0 as SimplifiedConstants
import hifi.simplifiedUI.simplifiedControls 1.0 as SimplifiedControls
Rectangle {
id: root
color: simplifiedUI.colors.white
anchors.fill: parent
property int originalWidth: 48
property int expandedWidth: mainEmojiContainer.width + drawerContainer.width
// For the below to work, the Repeater's Item's second child must be the individual button's `MouseArea`
property int requestedWidth: (
root.allowEmoteDrawerExpansion && (
drawerContainer.keepDrawerExpanded ||
emoteIndicatorMouseArea.containsMouse ||
emoteButtonsRepeater.itemAt(0).hovered ||
emoteButtonsRepeater.itemAt(1).hovered ||
emoteButtonsRepeater.itemAt(2).hovered ||
emoteButtonsRepeater.itemAt(3).hovered ||
emoteButtonsRepeater.itemAt(4).hovered ||
emoteButtonsRepeater.itemAt(5).hovered)
) ? expandedWidth : originalWidth;
readonly property int totalEmojiDurationMS: 7000 // Must match `TOTAL_EMOJI_DURATION_MS` in `simplifiedEmoji.js`
readonly property string emoteIconSource: "images/emote_Icon.svg"
property bool allowEmoteDrawerExpansion: Settings.getValue("simplifiedUI/allowEmoteDrawerExpansion", true)
onRequestedWidthChanged: {
root.requestNewWidth(root.requestedWidth);
}
Behavior on requestedWidth {
enabled: false // Set this to `true` once we have a different windowing system that better supports on-screen widgets
// like the Emote Indicator.
SmoothedAnimation { duration: 220 }
}
Connections {
target: Settings
onValueChanged: {
if (setting === "simplifiedUI/allowEmoteDrawerExpansion") {
root.allowEmoteDrawerExpansion = value;
}
}
}
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
Rectangle {
id: mainEmojiContainer
color: simplifiedUI.colors.darkBackground
anchors.top: parent.top
anchors.left: parent.left
height: parent.height
width: root.originalWidth
Image {
id: emoteIndicatorLowOpacity
width: emoteIndicator.width
height: emoteIndicator.height
anchors.centerIn: parent
source: emoteIndicator.source
opacity: 0.5
fillMode: Image.PreserveAspectFit
// All "reactions" have associated icon filenames that contain "Icon.svg"; emojis don't.
visible: emoteIndicator.source.toString().indexOf("Icon.svg") === -1
mipmap: true
}
Image {
id: emoteIndicator
width: 30
height: 30
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
source: root.emoteIconSource
mipmap: true
visible: false
onSourceChanged: {
// All "reactions" have associated icon filenames that contain "Icon.svg"; emojis don't.
progressCircle.endAnimation = false;
progressCircle.arcEnd = 360;
progressCircle.endAnimation = true;
var sourceIsEmojiImage = source.toString().indexOf("Icon.svg") === -1;
// This kicks off the progress circle animation
if (sourceIsEmojiImage) {
progressCircle.arcEnd = 0;
restoreEmoteIconTimer.restart();
} else {
restoreEmoteIconTimer.stop();
}
}
}
Timer {
id: restoreEmoteIconTimer
running: false
repeat: false
interval: root.totalEmojiDurationMS
onTriggered: {
emoteIndicator.source = root.emoteIconSource;
}
}
// The overlay used during the pie timeout
SimplifiedControls.ProgressCircle {
id: progressCircle
animationDuration: root.totalEmojiDurationMS
anchors.centerIn: emoteIndicator
size: emoteIndicator.width * 2
opacity: 0.5
colorCircle: "#FFFFFF"
colorBackground: "#E6E6E6"
showBackground: false
isPie: true
arcBegin: 0
arcEnd: 360
visible: false
}
OpacityMask {
anchors.fill: emoteIndicator
source: emoteIndicator
maskSource: progressCircle
}
ColorOverlay {
id: emoteIndicatorColorOverlay
anchors.fill: emoteIndicator
source: emoteIndicator
color: "#ffffff"
// All "reactions" have associated icon filenames that contain "Icon.svg"; emojis don't.
opacity: emoteIndicator.source.toString().indexOf("Icon.svg") > -1 ? 1.0 : 0.0
}
Image {
id: lockIcon
width: 12
height: 12
anchors.top: parent.top
anchors.topMargin: 2
anchors.left: parent.left
anchors.leftMargin: 0
source: "images/lock_Icon.svg"
fillMode: Image.PreserveAspectFit
mipmap: true
visible: false
}
ColorOverlay {
id: lockIconColorOverlay
anchors.fill: lockIcon
source: lockIcon
color: "#ffffff"
visible: root.allowEmoteDrawerExpansion && drawerContainer.keepDrawerExpanded
}
MouseArea {
id: emoteIndicatorMouseArea
anchors.fill: parent
hoverEnabled: enabled
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
drawerContainer.keepDrawerExpanded = !drawerContainer.keepDrawerExpanded;
// If the drawer is no longer expanded, disable this MouseArea (which will close
// the emote tray) until the user's cursor leaves the MouseArea (see `onExited()` below).
if (!drawerContainer.keepDrawerExpanded) {
emoteIndicatorMouseArea.enabled = false;
}
}
onEntered: {
Tablet.playSound(TabletEnums.ButtonHover);
}
onExited: {
emoteIndicatorMouseArea.enabled = true;
}
}
}
Row {
id: drawerContainer
property bool keepDrawerExpanded: false
anchors.top: parent.top
anchors.left: mainEmojiContainer.right
height: parent.height
width: childrenRect.width
Repeater {
id: emoteButtonsRepeater
model: ListModel {
id: buttonsModel
ListElement { imageURL: "images/positive_Icon.svg"; hotkey: "Z"; method: "positive" }
ListElement { imageURL: "images/negative_Icon.svg"; hotkey: "X"; method: "negative" }
ListElement { imageURL: "images/applaud_Icon.svg"; hotkey: "C"; method: "applaud" }
ListElement { imageURL: "images/raiseHand_Icon.svg"; hotkey: "V"; method: "raiseHand" }
ListElement { imageURL: "images/point_Icon.svg"; hotkey: "B"; method: "point" }
ListElement { imageURL: "images/emoji_Icon.svg"; hotkey: "F"; method: "toggleEmojiApp" }
}
Rectangle {
width: mainEmojiContainer.width
height: drawerContainer.height
// For the below to work, the This Rectangle's second child must be the `MouseArea`
color: hovered ? "#000000" : simplifiedUI.colors.white
property alias hovered: emoteTrayMouseArea.containsMouse
Image {
id: emoteTrayButtonImage
width: 30
height: 30
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
source: model.imageURL
mipmap: true
visible: false
}
ColorOverlay {
anchors.fill: emoteTrayButtonImage
source: emoteTrayButtonImage
color: parent.hovered ? "#ffffff" : "#000000"
}
Rectangle {
visible: parent.hovered
anchors.left: parent.left
anchors.bottom: parent.bottom
width: toolTipText.width + 4
height: toolTipText.height - 3
color: "#000000"
opacity: 0.8
radius: 4
HifiStylesUit.GraphikSemiBold {
id: toolTipText
anchors.left: parent.left
anchors.leftMargin: 2
anchors.bottom: parent.bottom
width: paintedWidth
height: paintedHeight
text: model.hotkey
verticalAlignment: TextInput.AlignBottom
horizontalAlignment: TextInput.AlignLeft
color: simplifiedUI.colors.text.white
size: 20
}
}
MouseArea {
id: emoteTrayMouseArea
anchors.fill: parent
hoverEnabled: true
onEntered: {
Tablet.playSound(TabletEnums.ButtonHover);
}
onPressed: {
Tablet.playSound(TabletEnums.ButtonClick);
sendToScript({
"source": "EmoteAppBar.qml",
"method": model.method,
"data": { "isPressingAndHolding": true }
});
}
onReleased: {
sendToScript({
"source": "EmoteAppBar.qml",
"method": model.method,
"data": { "isPressingAndHolding": false }
});
}
onExited: {
if (pressed) {
sendToScript({
"source": "EmoteAppBar.qml",
"method": model.method,
"data": { "isPressingAndHolding": false }
});
}
}
}
}
}
}
function fromScript(message) {
if (message.source !== "simplifiedEmote.js") {
return;
}
switch (message.method) {
case "updateEmoteIndicator":
if (message.data.iconURL) {
emoteIndicator.source = message.data.iconURL;
} else {
console.log("SimplifiedEmoteIndicator.qml: Error! `updateEmoteIndicator()` called without a new `iconURL`!");
}
break;
default:
console.log('SimplifiedEmoteIndicator.qml: Unrecognized message from JS');
break;
}
}
signal sendToScript(var message);
signal requestNewWidth(int newWidth);
}