overte-Armored-Dragon/scripts/system/domainChat/domainChat.qml
2025-02-12 09:51:22 -06:00

501 lines
18 KiB
QML

import QtQuick 2.7
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3
import controlsUit 1.0 as HifiControlsUit
import "./qml_widgets"
Rectangle {
color: Qt.rgba(0.1,0.1,0.1,1)
signal sendToScript(var message);
property string pageVal: "local";
property date last_message_time: new Date();
property bool initialized: false;
// When the window is created on the script side, the window starts open.
// Once the QML window is created wait, then send the initialized signal.
// This signal is mostly used to close the "Desktop overlay window" script side
// https://github.com/overte-org/overte/issues/824
Timer {
interval: 10
running: true
repeat: false
onTriggered: {
toScript({type: "initialized"});
load_scroll_timer.running = true
}
}
Timer {
id: load_scroll_timer
interval: 100
running: false
repeat: false
onTriggered: {
scrollToBottom(true);
}
}
// User view
Item {
anchors.fill: parent
// Navigation Bar
Rectangle {
id: navigation_bar
width: parent.width
height: 40
color:Qt.rgba(0,0,0,1)
Item {
height: parent.height
width: parent.width
anchors.fill: parent
Rectangle {
width: pageVal === "local" ? 100 : 60
height: parent.height
color: pageVal === "local" ? "#505186" : "white"
id: local_page
Image {
source: "./img/ui/" + (pageVal === "local" ? "social_white.png" : "social_black.png")
sourceSize.width: 40
sourceSize.height: 40
anchors.centerIn: parent
}
Behavior on width {
NumberAnimation {
duration: 50
}
}
MouseArea {
anchors.fill: parent
onClicked: {
pageVal = "local";
load_scroll_timer.running = true;
}
}
}
Rectangle {
width: pageVal === "domain" ? 100 : 60
height: parent.height
color: pageVal === "domain" ? "#505186" : "white"
anchors.left: local_page.right
anchors.leftMargin: 5
id: domain_page
Image {
source: "./img/ui/" + (pageVal === "domain" ? "world_white.png" : "world_black.png")
sourceSize.width: 30
sourceSize.height: 30
anchors.centerIn: parent
}
Behavior on width {
NumberAnimation {
duration: 50
}
}
MouseArea {
anchors.fill: parent
onClicked: {
pageVal = "domain"
load_scroll_timer.running = true;
}
}
}
Rectangle {
width: pageVal === "settings" ? 100 : 60
height: parent.height
color: pageVal === "settings" ? "#505186" : "white"
anchors.right: parent.right
id: settings_page
Image {
source: "./img/ui/" + (pageVal === "settings" ? "settings_white.png" : "settings_black.png")
sourceSize.width: 30
sourceSize.height: 30
anchors.centerIn: parent
}
Behavior on width {
NumberAnimation {
duration: 50
}
}
MouseArea {
anchors.fill: parent
onClicked: {
pageVal = "settings"
}
}
}
}
}
// Pages
Item {
width: parent.width
height: parent.height - 40
anchors.top: navigation_bar.bottom
visible: ["local", "domain"].includes(pageVal) ? true : false
// Chat Message History
Flickable {
width: parent.width
height: parent.height - 40
contentWidth: parent.width
contentHeight: listview.height
clip: true
id: messageViewFlickable
ColumnLayout {
id: listview
Layout.fillWidth: true
Repeater {
model: getChannel(pageVal)
delegate: Loader {
property int delegateIndex: model.index
property var delegateText: model.message
property string delegateUsername: model.username
property string delegateDate: model.date
sourceComponent: {
if (model.type === "chat") return template_chat_message;
if (model.type === "notification") return template_notification;
}
}
}
}
ScrollBar.vertical: ScrollBar {
size: 100
minimumSize: 0.1
}
rebound: Transition {
NumberAnimation {
properties: "x,y"
duration: 1
}
}
}
ListModel {
id: local
}
ListModel {
id: domain
}
// Chat Entry
Rectangle {
width: parent.width
height: 40
color: Qt.rgba(0.9,0.9,0.9,1)
anchors.bottom: parent.bottom
Row {
width: parent.width
height: parent.height
TextField {
width: parent.width - 60
height: parent.height
placeholderText: pageVal.charAt(0).toUpperCase() + pageVal.slice(1) + " chat message..."
clip: false
font.italic: text == ""
Keys.onPressed: {
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && !(event.modifiers & Qt.ShiftModifier)) {
event.accepted = true;
toScript({type: "send_message", message: text, channel: pageVal});
text = ""
}
}
onFocusChanged: {
if (!HMD.active) return;
if (focus) return ApplicationInterface.showVRKeyboardForHudUI(true);
ApplicationInterface.showVRKeyboardForHudUI(false);
}
}
Button {
width: 60
height:parent.height
Image {
source: "./img/ui/send_black.png"
sourceSize.width: 30
sourceSize.height: 30
anchors.centerIn: parent
}
onClicked: {
toScript({type: "send_message", message: parent.children[0].text, channel: pageVal});
parent.children[0].text = ""
}
Keys.onPressed: {
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
toScript({type: "send_message", message: parent.children[0].text, channel: pageVal});
parent.children[0].text = ""
}
}
}
}
}
}
Item {
width: parent.width
height: parent.height - 40
anchors.top: navigation_bar.bottom
visible: ["local", "domain"].includes(pageVal) ? false : true
Column {
width: parent.width - 10
height: parent.height - 10
anchors.centerIn: parent
spacing: 10
// External Window
Rectangle {
width: parent.width
height: 40
color: "transparent"
Text{
text: "External window"
color: "white"
font.pointSize: 12
anchors.verticalCenter: parent.verticalCenter
}
CheckBox{
id: s_external_window
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
onCheckedChanged: {
toScript({type: 'setting_change', setting: 'external_window', value: checked})
}
}
}
// Maximum saved messages
Rectangle {
width: parent.width
height: 40
color: "transparent"
Text{
text: "Maximum saved messages"
color: "white"
font.pointSize: 12
anchors.verticalCenter: parent.verticalCenter
}
HifiControlsUit.SpinBox {
id: s_maximum_messages
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
decimals: 0
width: 100
height: parent.height
realFrom: 1
realTo: 1000
backgroundColor: "#cccccc"
onValueChanged: {
toScript({type: 'setting_change', setting: 'maximum_messages', value: value})
}
}
}
// Erase History
Rectangle {
width: parent.width
height: 40
color: Qt.rgba(0.15,0.15,0.15,1);
Text{
text: "Erase chat history"
color: "white"
font.pointSize: 12
anchors.verticalCenter: parent.verticalCenter
}
Button {
anchors.right: parent.right
text: "Erase"
height: parent.height
anchors.verticalCenter: parent.verticalCenter
onClicked: {
toScript({type: "action", action: "erase_history"})
}
}
}
// Join notification
Rectangle {
width: parent.width
height: 40
color: "transparent"
Text{
text: "Join notification"
color: "white"
font.pointSize: 12
anchors.verticalCenter: parent.verticalCenter
}
CheckBox{
id: s_join_notification
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
onCheckedChanged: {
toScript({type: 'setting_change', setting: 'join_notification', value: checked})
}
}
}
// Switch to internal on VR Mode
Rectangle {
width: parent.width
height: 40
color: "transparent"
Text {
text: "Force Virtual window in VR"
color: "white"
font.pointSize: 12
anchors.verticalCenter: parent.verticalCenter
}
CheckBox {
id: s_force_vw_in_vr
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
onCheckedChanged: {
toScript({type: 'setting_change', setting: 'switchToInternalOnHeadsetUsed', value: checked})
}
}
}
// Toggle media embedding
Rectangle {
width: parent.width
height: 40
color: "transparent"
Text {
text: "Enable media embedding"
color: "white"
font.pointSize: 12
anchors.verticalCenter: parent.verticalCenter
}
CheckBox {
id: s_enable_embedding
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
onCheckedChanged: {
toScript({type: 'setting_change', setting: 'enableEmbedding', value: checked})
}
}
}
}
}
}
// Templates
TemplateChatMessage { id: template_chat_message }
TemplateNotification { id: template_notification }
property var channels: {
"local": local,
"domain": domain,
}
function scrollToBottom(bypassDistanceCheck = false, extraMoveDistance = 0) {
const totalHeight = listview.height; // Total height of the content
const currentPosition = messageViewFlickable.contentY; // Current position of the view
const windowHeight = listview.parent.parent.height; // Total height of the window
const bottomPosition = currentPosition + windowHeight;
// Check if the view is within 300 units from the bottom
const closeEnoughToBottom = totalHeight - bottomPosition <= 300;
if (!bypassDistanceCheck && !closeEnoughToBottom) return;
if (totalHeight < windowHeight) return; // No reason to scroll, we don't have an overflow.
if (bottomPosition == totalHeight) return; // At the bottom, do nothing.
messageViewFlickable.contentY = listview.height - listview.parent.parent.height;
messageViewFlickable.returnToBounds();
}
function addMessage(username, message, date, channel, type){
channel = getChannel(channel)
// Format content
if (type === "notification"){
channel.append({ message: message, date: date, type: "notification" });
scrollToBottom(null, 30);
return;
}
channel.append({ message: message, username: username, date: date, type: type });
load_scroll_timer.running = true;
}
function getChannel(id) {
return channels[id];
}
// Messages from script
function fromScript(message) {
switch (message.type){
case "show_message":
addMessage(message.displayName, message.message, `[ ${message.timeString} - ${message.dateString} ]`, message.channel, "chat");
break;
case "notification":
addMessage("SYSTEM", message.message, `[ ${message.timeString} - ${message.dateString} ]`, "domain", "notification");
break;
case "clear_messages":
local.clear();
domain.clear();
break;
case "initial_settings":
print(`Got settings:\n ${JSON.stringify(message.settings, null, 4)}`);
if (message.settings.external_window) s_external_window.checked = true;
if (message.settings.maximum_messages) s_maximum_messages.value = message.settings.maximum_messages;
if (message.settings.join_notification) s_join_notification.checked = true;
if (message.settings.switchToInternalOnHeadsetUsed) s_force_vw_in_vr.checked = true;
if (message.settings.enableEmbedding) s_enable_embedding.checked = true;
initialized = true; // Application is ready
break;
}
}
// Send message to script
function toScript(packet){
if (packet.type === "setting_change" && !initialized) return; // Don't announce a change in settings if not ready
sendToScript(packet)
}
}