This repository has been archived on 2021-06-24. You can view files and clone it, but cannot push or open issues or pull requests.
ui/qml/main.qml

461 lines
16 KiB
QML

import QtGraphicalEffects 1.0
import QtQuick 2.7
import QtQuick.Controls 2.4
import QtQuick.Controls.Material 2.0
import QtQuick.Layouts 1.3
import QtQuick.Window 2.11
import "opaque"
import "opaque/fonts"
import "opaque/fonts/MutantStandard.js" as Mutant
import "opaque/theme"
import "overlays"
import "panes"
import "widgets"
import "utils.js" as Utils
ApplicationWindow {
id: windowItem
width: 1200
height: 800
visible: true
title: "cwtch" + ""
font.family: Fonts.applicationFontRegular.name
font.styleName: "Light"
readonly property real ratio: height / width
FontAwesome { // PRETTY BUTTON ICONS
id: awesome
resource: "qrc:/qml/opaque/fonts/fontawesome.ttf"
}
FontLoader {
source: "qrc:/qml/opaque/fonts/AdobeBlank.ttf"
}
function parse(text, size, isntEditable) { // REPLACE EMOJI WITH <IMG> TAGS
var retText = Utils.htmlEscaped(text)
retText = retText.replace(/\n/g,"<br/>").replace(/\s\s/g, "&nbsp;&nbsp;")
// mutant standard stickers
if (isntEditable) retText = Mutant.standard.parse(retText, 1.5 * gcd.themeScale * Theme.chatSize)
return retText
}
function ptToPx(pt) {
return Screen.pixelDensity * 25.4 * pt / 72
}
function pxToPt(px) {
return px * 72 / (Screen.pixelDensity * 25.4)
}
StackView {
id: rootStack
anchors.fill: parent
property bool splash: true
// Splash pane
initialItem: Rectangle {
color: Theme.backgroundMainColor
visible: true
SplashPane {
id: splashPane
anchors.fill: parent
running: true
}
}
replaceEnter: Transition {
PropertyAnimation{
property: "opacity"
from: 0
to: 1
duration: 200
}
}
replaceExit: Transition {
PropertyAnimation{
property: "opacity"
from: 1
to: 0
duration: 200
}
}
// The actual app
property Item mainLayout: Rectangle {
color: Theme.backgroundMainColor
Toolbar {
id: toolbar
onLeftMenu: {
gcd.requestSettings()
parentStack.pane = parentStack.settingsPane
}
onBack: { backFn() }
onRightMenu: {
// If a group is selected....
if (Utils.isGroup(gcd.selectedConversation)) {
theStack.pane = theStack.groupProfilePane
gcd.requestGroupSettings(gcd.selectedConversation)
} else {
// if a peer is selected..
if (theStack.pane == theStack.userProfilePane) {
theStack.pane = theStack.messagePane
} else {
theStack.pane = theStack.userProfilePane
gcd.requestPeerSettings(gcd.selectedConversation)
}
}
}
}
StackLayout {
id: parentStack
readonly property int managementPane: 0
readonly property int settingsPane: 1
readonly property int addEditProfilePane: 2
readonly property int profilePane: 3
readonly property int addEditServerPane: 4
currentIndex: gcd.firstTime ? parentStack.settingsPane : parentStack.managementPane
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: statusbar.top
anchors.top: toolbar.bottom
property alias pane: parentStack.currentIndex
Rectangle { // Profile login/management pane
Layout.fillHeight: true
Layout.fillWidth: true
visible: false
color: Theme.backgroundMainColor
ProfileManagerPane {
id: profilesPane
anchors.fill: parent
}
}
Rectangle { // Settings pane
Layout.fillHeight: true
Layout.fillWidth: true
color: Theme.backgroundPaneColor
SettingsPane {
id: settingsPane
anchors.fill: parent
}
}
Rectangle { // Profile Add / Edit pane
Layout.fillHeight: true
Layout.fillWidth: true
color: Theme.backgroundPaneColor
ProfileAddEditPane{
id: profileAddEditPane
anchors.fill: parent
}
}
RowLayout { // Profile Pane (contact list + overlays)
Layout.fillHeight: true
Layout.fillWidth: true
spacing: 0
Rectangle { // THE LEFT PANE WITH TOOLS AND CONTACTS
color: Theme.backgroundMainColor
Layout.fillHeight: true
Layout.minimumWidth: Theme.sidePaneMinSize * gcd.themeScale
Layout.maximumWidth: theStack.pane == theStack.emptyPane ? parent.width : (Theme.sidePaneMinSize * gcd.themeScale)
Layout.fillWidth: theStack.pane == theStack.emptyPane ? true : false
visible: (windowItem.width >= (Theme.doublePaneMinSize * gcd.themeScale) && !Qt.inputMethod.visible) || theStack.pane == theStack.emptyPane
ContactList {
anchors.top: parent.top
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.right: (divider.visible ? divider.left : parent.right)
//anchors.topMargin: 10 * gcd.themeScale
dualPane: theStack.pane != theStack.emptyPane || theStack.pane == undefined
}
Rectangle {
id: divider
width: 2
anchors.right: parent.right
height: parent.height - (20 * gcd.themeScale)
anchors.verticalCenter: parent.verticalCenter
visible: theStack.pane != theStack.emptyPane
//Layout.fillHeight: true
color: Theme.dividerColor
}
}
Rectangle { // THE RIGHT PANE WHERE THE MESSAGES AND STUFF GO
color: Theme.backgroundPaneColor
Layout.fillWidth: true
Layout.fillHeight: true
StackLayout {
id: theStack
anchors.fill: parent
currentIndex: 0
property alias pane: theStack.currentIndex
readonly property int emptyPane: 0
readonly property int messagePane: 1
readonly property int userProfilePane: 2
readonly property int groupProfilePane: 3
readonly property int addPeerGroupPane: 4
readonly property int serverInfoPane: 5
Item { anchors.fill: parent } // empty
Rectangle {
color: Theme.backgroundMainColor
Layout.fillWidth: true
Layout.fillHeight: true
OverlayPane { // messagePane
anchors.fill: parent
anchors.topMargin: 10// * gcd.themeScale
}
}
PeerSettingsPane { anchors.fill: parent }
GroupSettingsPane{ anchors.fill: parent }
AddPeerGroupPane {
id: addPeerGroupPaneInstance
anchors.fill: parent
}
ServerInfoPane { anchors.fill: parent }
onCurrentIndexChanged: {
gcd.setProfilePaneState(theStack.currentIndex)
parentStack.updateToolbar()
if (currentIndex == emptyPane) {
toolbar.hideTitle()
toolbar.rightMenuVisible = false
} else if (currentIndex == addPeerGroupPane) {
//: New Connection
toolbar.setTitle(qsTr('new-connection-pane-title'))
toolbar.rightMenuVisible = false
addPeerGroupPaneInstance.reset()
}
}
onWidthChanged: {toolbar.titleWidth = width}
}
}
}
Rectangle { // Server Add / Edit pane
Layout.fillHeight: true
Layout.fillWidth: true
color: Theme.backgroundPaneColor
ServerAddEditPane{
id: serverAddEditPane
anchors.fill: parent
}
}
Connections {
target: gcd
onChangeRootPane: function(pane) {
parentStack.currentIndex = pane
}
onChangeProfilePane: function(pane) {
theStack.currentIndex = pane
}
}
focus: true
Keys.onPressed: {
if (event.key == Qt.Key_Back) {
event.accepted = true
backFn()
}
}
onCurrentIndexChanged : {
gcd.setRootPaneState(parentStack.currentIndex)
updateToolbar();
statusbar.resetHeight()
}
function updateToolbar() {
if (rootStack.splash == true) {
toolbar.hideTitle()
toolbar.rightMenuVisible = false
toolbar.visible = false
} else {
toolbar.visible = true
if (currentIndex == managementPane) {
toolbar.hideTitle()
toolbar.rightMenuVisible = false
toolbar.color = Theme.toolbarMainColor
toolbar.leftMenuVisible = true
toolbar.backVisible = false
} else {
toolbar.leftMenuVisible = false
toolbar.backVisible = true
if (currentIndex == profilePane && theStack.currentIndex == theStack.emptyPane) {
toolbar.hideTitle()
toolbar.rightMenuVisible = false
toolbar.color = Theme.toolbarMainColor
} else {
toolbar.color = Theme.toolbarAltColor
}
}
}
}
Component.onCompleted: updateToolbar()
Connections {
target: Theme
onThemeChanged: {
parentStack.updateToolbar()
}
}
}
Statusbar {
id: statusbar
}
}
}
function backFn() {
if (parentStack.currentIndex == parentStack.managementPane) {
androidCwtchActivity.rootHomeButtonHandle()
} else if (parentStack.currentIndex != parentStack.profilePane) {
parentStack.currentIndex = parentStack.managementPane
} else {
if (theStack.currentIndex == theStack.emptyPane) {
gcd.selectedProfile = "none"
gcd.reloadProfileList()
parentStack.pane = parentStack.managementPane
} else if (theStack.currentIndex == theStack.userProfilePane || theStack.currentIndex == theStack.groupProfilePane) {
theStack.currentIndex = theStack.messagePane
} else {
theStack.currentIndex = theStack.emptyPane
}
}
}
PropertyAnimation { id: anmPopup; easing.type: Easing.InQuart; duration: 7000; target: popup; property: "opacity"; to: 0; }
Rectangle { // THE ERROR MESSAGE POPUP
id: popup
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 20
width: lblPopup.width + 30
height: lblPopup.height + 8 * gcd.themeScale
color: "#000000"
opacity: 0.5
radius: 15
visible: false
// We store error messages in QML referenced by an ID so they can be called from go
// and the qupdate / qml i18n system can easily find and translate them here
property var errorMessages: (new Map([
//: 0 profiles loaded with that password
["0-profiles", qsTr("error-0-profiles-loaded-for-password")],
]))
Label {
id: lblPopup
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 18 * gcd.themeScale
color: "#FFFFFF"
}
}
Connections { // POPUPS ARE INVOKED BY GO FUNCS
target: gcd
onInvokePopup: function(str) {
if (popup.errorMessages.get(str) != undefined) {
str = popup.errorMessages.get(str)
}
lblPopup.text = str
popup.opacity = 0.5
popup.visible = true
anmPopup.restart()
}
onLoaded: function() {
//parentStack.pane = parentStack.managementPane
rootStack.replace(rootStack.mainLayout)
splashPane.running = false
rootStack.splash = false
parentStack.updateToolbar()
statusbar.resetHeight()
}
onNotify: function(onion) {
// If we are processing QML it means the app is open, and as such we don't want to
// Send a notification - in the future we should probably use an API like this to Cancel notifications
// Until then I am leaving this here for documentation.
// androidCwtchActivity.channel = onion
// androidCwtchActivity.notification = "Message from " + onion;
// Basic Desktop notification
windowItem.alert(0)
}
}
Component.onCompleted: Mutant.standard.imagePath = gcd.assetPath;
Connections {
target: Qt.application
onStateChanged: function() {
// https://doc.qt.io/qt-5/qt.html#ApplicationState-enum
if (Qt.application.state == 4) {
// Active
gcd.onActivate()
}
}
}
}