nextcloud-desktop/src/gui/tray/Window.qml

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

888 lines
38 KiB
QML
Raw Normal View History

/*
* Copyright (C) 2020 by Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Qt.labs.platform as NativeDialogs
import "../"
import "../filedetails/"
// Custom qml modules are in /theme (and included by resources.qrc)
import Style
import com.nextcloud.desktopclient
ApplicationWindow {
id: trayWindow
title: Systray.windowTitle
// If the main dialog is displayed as a regular window we want it to be quadratic
width: Systray.useNormalWindow ? Style.trayWindowHeight : Style.trayWindowWidth
height: Style.trayWindowHeight
flags: Systray.useNormalWindow ? Qt.Window : Qt.Dialog | Qt.FramelessWindowHint
color: 'transparent'
readonly property int maxMenuHeight: Style.trayWindowHeight - Style.trayWindowHeaderHeight - 2 * Style.trayWindowBorderWidth
Component.onCompleted: Systray.forceWindowInit(trayWindow)
// Close tray window when focus is lost (e.g. click somewhere else on the screen)
onActiveChanged: {
if (!Systray.useNormalWindow && !active) {
hide();
Systray.isOpen = false;
}
}
onClosing: Systray.isOpen = false
onVisibleChanged: {
// HACK: reload account Instantiator immediately by restting it - could be done better I guess
// see also id:accountMenu below
userLineInstantiator.active = false;
userLineInstantiator.active = true;
syncStatus.model.load();
}
background: Rectangle {
radius: Systray.useNormalWindow ? 0.0 : Style.trayWindowRadius
border.width: Style.trayWindowBorderWidth
border.color: palette.dark
color: palette.window
}
Connections {
target: UserModel
function onCurrentUserChanged() {
accountMenu.close();
syncStatus.model.load();
}
}
Component {
id: errorMessageDialog
NativeDialogs.MessageDialog {
id: dialog
title: Systray.windowTitle
onAccepted: destroy()
onRejected: destroy()
}
}
Connections {
target: Systray
function onIsOpenChanged() {
userStatusDrawer.close()
fileDetailsDrawer.close();
if(Systray.isOpen) {
accountMenu.close();
appsMenu.close();
openLocalFolderButton.closeMenu()
}
}
function onShowErrorMessageDialog(error) {
var newErrorDialog = errorMessageDialog.createObject(trayWindow)
newErrorDialog.text = error
newErrorDialog.open()
}
function onShowFileDetails(accountState, localPath, fileDetailsPage) {
fileDetailsDrawer.openFileDetails(accountState, localPath, fileDetailsPage);
}
}
OpacityMask {
anchors.fill: parent
anchors.margins: Style.trayWindowBorderWidth
source: ShaderEffectSource {
sourceItem: trayWindowMainItem
hideSource: true
}
maskSource: Rectangle {
width: trayWindow.width
height: trayWindow.height
radius: Systray.useNormalWindow ? 0.0 : Style.trayWindowRadius
}
}
Drawer {
id: userStatusDrawer
width: parent.width
height: parent.height - Style.trayDrawerMargin
padding: 0
edge: Qt.BottomEdge
modal: true
visible: false
background: Rectangle {
radius: Systray.useNormalWindow ? 0.0 : Style.trayWindowRadius
border.width: Style.trayWindowBorderWidth
border.color: palette.dark
color: palette.window
}
property int userIndex: 0
function openUserStatusDrawer(index) {
console.log(`About to show dialog for user with index ${index}`);
userIndex = index;
open();
}
Loader {
id: userStatusContents
anchors.fill: parent
active: userStatusDrawer.visible
sourceComponent: UserStatusSelectorPage {
anchors.fill: parent
userIndex: userStatusDrawer.userIndex
onFinished: userStatusDrawer.close()
}
}
}
Drawer {
id: fileDetailsDrawer
width: parent.width - Style.trayDrawerMargin
height: parent.height
padding: 0
edge: Qt.RightEdge
modal: true
visible: false
clip: true
background: Rectangle {
radius: Systray.useNormalWindow ? 0.0 : Style.trayWindowRadius
border.width: Style.trayWindowBorderWidth
border.color: palette.dark
color: palette.window
}
property var folderAccountState: ({})
property string fileLocalPath: ""
property var pageToShow: Systray.FileDetailsPage.Activity
function openFileDetails(accountState, localPath, fileDetailsPage) {
console.log(`About to show file details view in tray for ${localPath}`);
folderAccountState = accountState;
fileLocalPath = localPath;
pageToShow = fileDetailsPage;
if(!opened) {
open();
}
}
Loader {
id: fileDetailsContents
anchors.fill: parent
active: fileDetailsDrawer.visible
onActiveChanged: {
if (active) {
Systray.showFileDetailsPage(fileDetailsDrawer.fileLocalPath,
fileDetailsDrawer.pageToShow);
}
}
sourceComponent: FileDetailsView {
id: fileDetails
width: parent.width
height: parent.height
backgroundsVisible: false
accentColor: Style.currentUserHeaderColor
accountState: fileDetailsDrawer.folderAccountState
localPath: fileDetailsDrawer.fileLocalPath
showCloseButton: true
onCloseButtonClicked: fileDetailsDrawer.close()
}
}
}
Item {
id: trayWindowMainItem
property bool isUnifiedSearchActive: unifiedSearchResultsListViewSkeletonLoader.active
|| unifiedSearchResultNothingFound.visible
|| unifiedSearchResultsErrorLabel.visible
|| unifiedSearchResultsListView.visible
anchors.fill: parent
anchors.margins: Style.trayWindowBorderWidth
clip: true
Accessible.role: Accessible.Grouping
Accessible.name: qsTr("Nextcloud desktop main dialog")
Rectangle {
id: trayWindowHeaderBackground
anchors.left: trayWindowMainItem.left
anchors.right: trayWindowMainItem.right
anchors.top: trayWindowMainItem.top
height: Style.trayWindowHeaderHeight
color: palette.window
RowLayout {
id: trayWindowHeaderLayout
spacing: 0
anchors.fill: parent
Button {
id: currentAccountButton
Layout.preferredWidth: Style.currentAccountButtonWidth
Layout.preferredHeight: Style.trayWindowHeaderHeight
display: AbstractButton.IconOnly
flat: true
Accessible.role: Accessible.ButtonMenu
Accessible.name: qsTr("Current account")
Accessible.onPressAction: currentAccountButton.clicked()
// We call open() instead of popup() because we want to position it
// exactly below the dropdown button, not the mouse
onClicked: {
syncPauseButton.text = Systray.syncIsPaused ? qsTr("Resume sync for all") : qsTr("Pause sync for all")
if (accountMenu.visible) {
accountMenu.close()
} else {
accountMenu.open()
}
}
Menu {
id: accountMenu
// x coordinate grows towards the right
// y coordinate grows towards the bottom
x: (currentAccountButton.x + 2)
y: (currentAccountButton.y + Style.trayWindowHeaderHeight + 2)
width: (Style.currentAccountButtonWidth - 2)
height: Math.min(implicitHeight, maxMenuHeight)
closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape
background: Rectangle {
border.color: palette.dark
color: palette.window
radius: Style.trayWindowRadius
}
onClosed: {
// HACK: reload account Instantiator immediately by restting it - could be done better I guess
// see also onVisibleChanged above
userLineInstantiator.active = false;
userLineInstantiator.active = true;
}
Instantiator {
id: userLineInstantiator
model: UserModel
delegate: UserLine {
onShowUserStatusSelector: {
userStatusDrawer.openUserStatusDrawer(model.index);
accountMenu.close();
}
onClicked: UserModel.currentUserId = model.index;
}
onObjectAdded: accountMenu.insertItem(index, object)
onObjectRemoved: accountMenu.removeItem(object)
}
MenuItem {
id: addAccountButton
height: Systray.enableAddAccount ? Style.addAccountButtonHeight : 0
hoverEnabled: true
visible: Systray.enableAddAccount
RowLayout {
anchors.fill: parent
spacing: 0
Image {
Layout.leftMargin: Style.accountIconsMenuMargin
verticalAlignment: Qt.AlignVCenter
horizontalAlignment: Qt.AlignCenter
source: "image://svgimage-custom-color/add.svg/" + palette.windowText
sourceSize.width: Style.addButtonIconSize
sourceSize.height: Style.addButtonIconSize
}
EnforcedPlainTextLabel {
Layout.leftMargin: 14
text: qsTr("Add account")
font.pixelSize: Style.topLinePixelSize
}
// Filler on the right
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
onClicked: UserModel.addAccount()
Accessible.role: Accessible.MenuItem
Accessible.name: qsTr("Add new account")
Accessible.onPressAction: addAccountButton.clicked()
background: Rectangle {
radius: Style.trayWindowRadius
color: palette.highlight
visible: addAccountButton.hovered
}
}
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: 1
color: palette.dark
}
MenuItem {
id: syncPauseButton
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true
onClicked: Systray.syncIsPaused = !Systray.syncIsPaused
Accessible.role: Accessible.MenuItem
Accessible.name: Systray.syncIsPaused ? qsTr("Resume sync for all") : qsTr("Pause sync for all")
Accessible.onPressAction: syncPauseButton.clicked()
background: Rectangle {
radius: Style.trayWindowRadius
color: palette.highlight
visible: syncPauseButton.hovered
}
}
MenuItem {
id: settingsButton
text: qsTr("Settings")
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true
onClicked: Systray.openSettings()
Accessible.role: Accessible.MenuItem
Accessible.name: text
Accessible.onPressAction: settingsButton.clicked()
background: Rectangle {
radius: Style.trayWindowRadius
color: palette.highlight
visible: settingsButton.hovered
}
}
MenuItem {
id: exitButton
text: qsTr("Exit");
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true
onClicked: Systray.shutdown()
Accessible.role: Accessible.MenuItem
Accessible.name: text
Accessible.onPressAction: exitButton.clicked()
background: Rectangle {
radius: Style.trayWindowRadius
color: palette.highlight
visible: exitButton.hovered
}
}
}
RowLayout {
id: accountControlRowLayout
height: Style.trayWindowHeaderHeight
width: Style.currentAccountButtonWidth
spacing: 0
Image {
id: currentAccountAvatar
Layout.leftMargin: Style.trayHorizontalMargin
verticalAlignment: Qt.AlignCenter
cache: false
source: (UserModel.currentUser && UserModel.currentUser.avatar !== "") ? UserModel.currentUser.avatar : "image://avatars/fallbackWhite"
Layout.preferredHeight: Style.accountAvatarSize
Layout.preferredWidth: Style.accountAvatarSize
Accessible.role: Accessible.Graphic
Accessible.name: qsTr("Current account avatar")
Rectangle {
id: currentAccountStatusIndicatorBackground
visible: UserModel.currentUser && UserModel.currentUser.isConnected
&& UserModel.currentUser.serverHasUserStatus
width: Style.accountAvatarStateIndicatorSize + + Style.trayFolderStatusIndicatorSizeOffset
height: width
anchors.bottom: currentAccountAvatar.bottom
anchors.right: currentAccountAvatar.right
color: Style.currentUserHeaderColor
radius: width * Style.trayFolderStatusIndicatorRadiusFactor
}
Rectangle {
id: currentAccountStatusIndicatorMouseHover
visible: UserModel.currentUser && UserModel.currentUser.isConnected
&& UserModel.currentUser.serverHasUserStatus
width: Style.accountAvatarStateIndicatorSize + + Style.trayFolderStatusIndicatorSizeOffset
height: width
anchors.bottom: currentAccountAvatar.bottom
anchors.right: currentAccountAvatar.right
color: currentAccountButton.hovered ? Style.currentUserHeaderTextColor : palette.window
opacity: Style.trayFolderStatusIndicatorMouseHoverOpacityFactor
radius: width * Style.trayFolderStatusIndicatorRadiusFactor
}
Image {
id: currentAccountStatusIndicator
visible: UserModel.currentUser && UserModel.currentUser.isConnected
&& UserModel.currentUser.serverHasUserStatus
source: UserModel.currentUser ? UserModel.currentUser.statusIcon : ""
cache: false
x: currentAccountStatusIndicatorBackground.x + 1
y: currentAccountStatusIndicatorBackground.y + 1
sourceSize.width: Style.accountAvatarStateIndicatorSize
sourceSize.height: Style.accountAvatarStateIndicatorSize
Accessible.role: Accessible.Indicator
Accessible.name: UserModel.desktopNotificationsAllowed ? qsTr("Current account status is online") : qsTr("Current account status is do not disturb")
}
}
Column {
id: accountLabels
spacing: 0
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.leftMargin: Style.userStatusSpacing
Layout.fillWidth: true
Layout.maximumWidth: parent.width
EnforcedPlainTextLabel {
id: currentAccountUser
Layout.alignment: Qt.AlignLeft | Qt.AlignBottom
width: Style.currentAccountLabelWidth
text: UserModel.currentUser ? UserModel.currentUser.name : ""
elide: Text.ElideRight
font.pixelSize: Style.topLinePixelSize
font.bold: true
}
EnforcedPlainTextLabel {
id: currentAccountServer
Layout.alignment: Qt.AlignLeft | Qt.AlignBottom
width: Style.currentAccountLabelWidth
text: UserModel.currentUser ? UserModel.currentUser.server : ""
elide: Text.ElideRight
visible: UserModel.numUsers() > 1
}
RowLayout {
id: currentUserStatus
visible: UserModel.currentUser && UserModel.currentUser.isConnected &&
UserModel.currentUser.serverHasUserStatus
spacing: Style.accountLabelsSpacing
width: parent.width
EnforcedPlainTextLabel {
id: emoji
visible: UserModel.currentUser && UserModel.currentUser.statusEmoji !== ""
width: Style.userStatusEmojiSize
text: UserModel.currentUser ? UserModel.currentUser.statusEmoji : ""
}
EnforcedPlainTextLabel {
id: message
Layout.alignment: Qt.AlignLeft | Qt.AlignBottom
Layout.fillWidth: true
visible: UserModel.currentUser && UserModel.currentUser.statusMessage !== ""
width: Style.currentAccountLabelWidth
text: UserModel.currentUser && UserModel.currentUser.statusMessage !== ""
? UserModel.currentUser.statusMessage
: UserModel.currentUser ? UserModel.currentUser.server : ""
elide: Text.ElideRight
font.pixelSize: Style.subLinePixelSize
}
}
}
Image {
Layout.alignment: Qt.AlignRight
verticalAlignment: Qt.AlignCenter
horizontalAlignment: Qt.AlignRight
Layout.leftMargin: Style.accountDropDownCaretMargin
source: "image://svgimage-custom-color/caret-down.svg/" + palette.windowText
sourceSize.width: Style.accountDropDownCaretSize
sourceSize.height: Style.accountDropDownCaretSize
Accessible.role: Accessible.PopupMenu
Accessible.name: qsTr("Account switcher and settings menu")
}
}
}
// Add space between items
Item {
Layout.fillWidth: true
}
TrayFoldersMenuButton {
id: openLocalFolderButton
visible: currentUser.hasLocalFolder
currentUser: UserModel.currentUser
onClicked: openLocalFolderButton.userHasGroupFolders ? openLocalFolderButton.toggleMenuOpen() : UserModel.openCurrentAccountLocalFolder()
onFolderEntryTriggered: isGroupFolder ? UserModel.openCurrentAccountFolderFromTrayInfo(fullFolderPath) : UserModel.openCurrentAccountLocalFolder()
Accessible.role: Accessible.Graphic
Accessible.name: qsTr("Open local or group folders")
Accessible.onPressAction: openLocalFolderButton.userHasGroupFolders ? openLocalFolderButton.toggleMenuOpen() : UserModel.openCurrentAccountLocalFolder()
Layout.alignment: Qt.AlignRight
Layout.preferredWidth: Style.trayWindowHeaderHeight
Layout.preferredHeight: Style.trayWindowHeaderHeight
}
HeaderButton {
id: trayWindowFeaturedAppButton
visible: UserModel.currentUser.isFeaturedAppEnabled
icon.source: UserModel.currentUser.featuredAppIcon + "/"
onClicked: UserModel.openCurrentAccountFeaturedApp()
Accessible.role: Accessible.Button
Accessible.name: UserModel.currentUser.featuredAppAccessibleName
Accessible.onPressAction: trayWindowFeaturedAppButton.clicked()
Layout.alignment: Qt.AlignRight
Layout.preferredWidth: Style.trayWindowHeaderHeight
Layout.preferredHeight: Style.trayWindowHeaderHeight
}
HeaderButton {
id: trayWindowAppsButton
icon.source: "image://svgimage-custom-color/more-apps.svg/" + palette.windowText
onClicked: {
if(appsMenuListView.count <= 0) {
UserModel.openCurrentAccountServer()
} else if (appsMenu.visible) {
appsMenu.close()
} else {
appsMenu.open()
}
}
Accessible.role: Accessible.ButtonMenu
Accessible.name: qsTr("More apps")
Accessible.onPressAction: trayWindowAppsButton.clicked()
Menu {
id: appsMenu
x: Style.trayWindowMenuOffsetX
y: (trayWindowAppsButton.y + trayWindowAppsButton.height + Style.trayWindowMenuOffsetY)
width: Style.trayWindowWidth * Style.trayWindowMenuWidthFactor
height: implicitHeight + y > Style.trayWindowHeight ? Style.trayWindowHeight - y : implicitHeight
closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape
background: Rectangle {
border.color: palette.dark
radius: Style.trayWindowRadius
color: palette.window
}
contentItem: ScrollView {
id: appsMenuScrollView
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
data: WheelHandler {
target: appsMenuScrollView.contentItem
}
ListView {
id: appsMenuListView
implicitHeight: contentHeight
model: UserAppsModel
interactive: true
clip: true
currentIndex: appsMenu.currentIndex
delegate: MenuItem {
id: appEntry
anchors.left: parent.left
anchors.right: parent.right
text: model.appName
font.pixelSize: Style.topLinePixelSize
icon.source: model.appIconUrl
icon.color: palette.windowText
onTriggered: UserAppsModel.openAppUrl(appUrl)
hoverEnabled: true
Accessible.role: Accessible.MenuItem
Accessible.name: qsTr("Open %1 in browser").arg(model.appName)
Accessible.onPressAction: appEntry.triggered()
background: Rectangle {
anchors.fill: parent
anchors.margins: 1
color: appEntry.hovered ? palette.highlight : palette.window
radius: Style.trayWindowRadius
}
}
}
}
}
}
}
} // Rectangle trayWindowHeaderBackground
UnifiedSearchInputContainer {
id: trayWindowUnifiedSearchInputContainer
height: Style.trayWindowHeaderHeight * 0.65
anchors {
top: trayWindowHeaderBackground.bottom
left: trayWindowMainItem.left
right: trayWindowMainItem.right
topMargin: Style.trayHorizontalMargin + controlRoot.padding
leftMargin: Style.trayHorizontalMargin + controlRoot.padding
rightMargin: Style.trayHorizontalMargin + controlRoot.padding
}
text: UserModel.currentUser.unifiedSearchResultsListModel.searchTerm
readOnly: !UserModel.currentUser.isConnected || UserModel.currentUser.unifiedSearchResultsListModel.currentFetchMoreInProgressProviderId
isSearchInProgress: UserModel.currentUser.unifiedSearchResultsListModel.isSearchInProgress
onTextEdited: { UserModel.currentUser.unifiedSearchResultsListModel.searchTerm = trayWindowUnifiedSearchInputContainer.text }
onClearText: { UserModel.currentUser.unifiedSearchResultsListModel.searchTerm = "" }
Rectangle {
id: bottomUnifiedSearchInputSeparator
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: 1
color: Style.menuBorder
visible: trayWindowMainItem.isUnifiedSearchActive
}
}
ErrorBox {
id: unifiedSearchResultsErrorLabel
visible: UserModel.currentUser.unifiedSearchResultsListModel.errorString && !unifiedSearchResultsListView.visible && ! UserModel.currentUser.unifiedSearchResultsListModel.isSearchInProgress && ! UserModel.currentUser.unifiedSearchResultsListModel.currentFetchMoreInProgressProviderId
text: UserModel.currentUser.unifiedSearchResultsListModel.errorString
anchors.top: trayWindowUnifiedSearchInputContainer.bottom
anchors.left: trayWindowMainItem.left
anchors.right: trayWindowMainItem.right
anchors.margins: Style.trayHorizontalMargin
}
UnifiedSearchResultNothingFound {
id: unifiedSearchResultNothingFound
anchors.top: trayWindowUnifiedSearchInputContainer.bottom
anchors.left: trayWindowMainItem.left
anchors.right: trayWindowMainItem.right
anchors.topMargin: Style.trayHorizontalMargin
text: UserModel.currentUser.unifiedSearchResultsListModel.searchTerm
property bool isSearchRunning: UserModel.currentUser.unifiedSearchResultsListModel.isSearchInProgress
property bool waitingForSearchTermEditEnd: UserModel.currentUser.unifiedSearchResultsListModel.waitingForSearchTermEditEnd
property bool isSearchResultsEmpty: unifiedSearchResultsListView.count === 0
property bool nothingFound: text && isSearchResultsEmpty && !UserModel.currentUser.unifiedSearchResultsListModel.errorString
visible: !isSearchRunning && !waitingForSearchTermEditEnd && nothingFound
}
Loader {
id: unifiedSearchResultsListViewSkeletonLoader
anchors.top: trayWindowUnifiedSearchInputContainer.bottom
anchors.left: trayWindowMainItem.left
anchors.right: trayWindowMainItem.right
anchors.bottom: trayWindowMainItem.bottom
anchors.margins: controlRoot.padding
active: !unifiedSearchResultNothingFound.visible &&
!unifiedSearchResultsListView.visible &&
!UserModel.currentUser.unifiedSearchResultsListModel.errorString &&
UserModel.currentUser.unifiedSearchResultsListModel.searchTerm
sourceComponent: UnifiedSearchResultItemSkeletonContainer {
anchors.fill: parent
spacing: unifiedSearchResultsListView.spacing
animationRectangleWidth: trayWindow.width
}
}
ScrollView {
id: controlRoot
contentWidth: availableWidth
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
data: WheelHandler {
target: controlRoot.contentItem
}
visible: unifiedSearchResultsListView.count > 0
anchors.top: trayWindowUnifiedSearchInputContainer.bottom
anchors.left: trayWindowMainItem.left
anchors.right: trayWindowMainItem.right
anchors.bottom: trayWindowMainItem.bottom
ListView {
id: unifiedSearchResultsListView
spacing: 4
clip: true
keyNavigationEnabled: true
reuseItems: true
Accessible.role: Accessible.List
Accessible.name: qsTr("Unified search results list")
model: UserModel.currentUser.unifiedSearchResultsListModel
delegate: UnifiedSearchResultListItem {
width: unifiedSearchResultsListView.width
isSearchInProgress: unifiedSearchResultsListView.model.isSearchInProgress
currentFetchMoreInProgressProviderId: unifiedSearchResultsListView.model.currentFetchMoreInProgressProviderId
fetchMoreTriggerClicked: unifiedSearchResultsListView.model.fetchMoreTriggerClicked
resultClicked: unifiedSearchResultsListView.model.resultClicked
ListView.onPooled: isPooled = true
ListView.onReused: isPooled = false
}
section.property: "providerName"
section.criteria: ViewSection.FullString
section.delegate: UnifiedSearchResultSectionItem {
width: unifiedSearchResultsListView.width
}
}
}
SyncStatus {
id: syncStatus
visible: !trayWindowMainItem.isUnifiedSearchActive
anchors.top: trayWindowUnifiedSearchInputContainer.bottom
anchors.left: trayWindowMainItem.left
anchors.right: trayWindowMainItem.right
Rectangle {
id: syncStatusSeparator
Layout.fillWidth: true
Layout.preferredHeight: 1
height: 1
color: Style.menuBorder
}
}
Loader {
id: newActivitiesButtonLoader
anchors.top: activityList.top
anchors.topMargin: 5
anchors.horizontalCenter: activityList.horizontalCenter
width: Style.newActivitiesButtonWidth
height: Style.newActivitiesButtonHeight
z: 1
active: false
sourceComponent: CustomButton {
id: newActivitiesButton
hoverEnabled: true
padding: Style.smallSpacing
anchors.fill: parent
text: qsTr("New activities")
icon.source: "image://svgimage-custom-color/expand-less-black.svg" + "/" + Style.currentUserHeaderTextColor
icon.width: Style.activityLabelBaseWidth
icon.height: Style.activityLabelBaseWidth
onClicked: {
activityList.scrollToTop();
newActivitiesButtonLoader.active = false
}
Timer {
id: newActivitiesButtonDisappearTimer
interval: Style.newActivityButtonDisappearTimeout
running: newActivitiesButtonLoader.active && !newActivitiesButton.hovered
repeat: false
onTriggered: fadeoutActivitiesButtonDisappear.running = true
}
OpacityAnimator {
id: fadeoutActivitiesButtonDisappear
target: newActivitiesButton;
from: 1;
to: 0;
duration: Style.newActivityButtonDisappearFadeTimeout
loops: 1
running: false
onFinished: newActivitiesButtonLoader.active = false
}
}
}
ActivityList {
id: activityList
visible: !trayWindowMainItem.isUnifiedSearchActive
anchors.top: syncStatus.bottom
anchors.left: trayWindowMainItem.left
anchors.right: trayWindowMainItem.right
anchors.bottom: trayWindowMainItem.bottom
activeFocusOnTab: true
model: activityModel
onOpenFile: Qt.openUrlExternally(filePath);
onActivityItemClicked: {
model.slotTriggerDefaultAction(index)
}
Connections {
target: activityModel
function onInteractiveActivityReceived() {
if (!activityList.atYBeginning) {
newActivitiesButtonLoader.active = true;
}
}
}
}
} // Item trayWindowMainItem
}