status-desktop/ui/app/AppLayouts/Chat/controls/community/InDropdown.qml

388 lines
12 KiB
QML
Raw Normal View History

import QtQuick 2.14
import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Popups 0.1
import shared.controls 1.0
import SortFilterProxyModel 0.2
StatusDropdown {
id: root
property bool allowChoosingEntireCommunity: false
property bool showAddChannelButton: false
property string communityName
property string communityImage
property color communityColor
property var model
property int acceptMode: InDropdown.AcceptMode.Add
enum AcceptMode {
Add, Update
}
width: 289
padding: 8
// force keeping within the bounds of the enclosing window
margins: 0
signal addChannelClicked
signal communitySelected
signal channelsSelected(var channels)
function setSelectedChannels(channels) {
d.setSelectedChannels(channels)
}
onAboutToHide: searcher.text = ""
onAboutToShow: scrollView.Layout.preferredHeight = Math.min(
scrollView.implicitHeight, 420)
QtObject {
id: d
readonly property var selectedChannels: new Map()
signal setSelectedChannels(var channels)
function search(text, searcherText) {
return text.toLowerCase().includes(searcherText.toLowerCase())
}
function resolveEmoji(emoji) {
return !!emoji ? emoji : ""
}
function resolveColor(color, colorId) {
return !!color ? color : Theme.palette.userCustomizationColors[colorId]
}
function addToSelectedChannels(model) {
selectedChannels.set(model.itemId, {
itemId: model.itemId,
name: model.name,
color: d.resolveColor(model.color, model.colorId),
emoji: d.resolveEmoji(model.emoji)
})
}
function removeFromSelectedChannels(model) {
selectedChannels.delete(model.itemId)
}
}
ColumnLayout {
anchors.top: parent.top
anchors.right: parent.right
anchors.left: parent.left
spacing: 0
SearchBox {
id: searcher
Layout.fillWidth: true
topPadding: 0
bottomPadding: 0
minimumHeight: 36
maximumHeight: 36
}
StatusListItem {
Layout.fillWidth: true
Layout.topMargin: 9
Layout.preferredHeight: 44
visible: root.allowChoosingEntireCommunity
title: root.communityName
subTitle: qsTr("Community")
asset.name: root.communityImage
asset.color: root.communityColor
asset.isImage: true
asset.width: 32
asset.height: 32
asset.isLetterIdenticon: !asset.name
asset.charactersLen: 2
asset.letterSize: 15
leftPadding: 8
rightPadding: 6
statusListItemTitleArea.anchors.leftMargin: 8
statusListItemTitle.font.pixelSize: 13
statusListItemTitle.font.weight: Font.Medium
statusListItemSubTitle.font.pixelSize: 12
components: [
StatusRadioButton {
id: radioButton
size: StatusRadioButton.Size.Small
rightPadding: 0
}
]
// using MouseArea instead of build-in 'clicked' signal to avoid
// intercepting event by the StatusRadioButton
MouseArea {
anchors.fill: parent
onClicked: {
radioButton.toggle()
radioButton.toggled()
}
cursorShape: Qt.PointingHandCursor
}
}
StatusMenuSeparator {
Layout.fillWidth: true
Layout.topMargin: 4 - implicitHeight / 2
Layout.bottomMargin: 4 - implicitHeight / 2
visible: root.allowChoosingEntireCommunity
}
StatusScrollView {
id: scrollView
Layout.fillWidth: true
Layout.fillHeight: true
Layout.bottomMargin: 9
Layout.topMargin:
!root.allowChoosingEntireCommunity && !root.allowChoosingEntireCommunity ? 9 : 0
padding: 0
ColumnLayout {
id: scollableColumn
spacing: 0
width: scrollView.width
StatusIconTextButton {
Layout.preferredHeight: 36
visible: root.showAddChannelButton
leftPadding: 8
spacing: 8
statusIcon: "add"
icon.width: 16
icon.height: 16
iconRotation: 0
text: qsTr("Add channel")
onClicked: root.addChannelClicked()
}
Repeater {
id: topRepeater
model: SortFilterProxyModel {
id: topLevelModel
sourceModel: root.model
sorters: [
RoleSorter { roleName: "isCategory" },
RoleSorter { roleName: "position" }
]
}
ColumnLayout {
id: column
Layout.fillWidth: true
spacing: 0
readonly property var topModel: model
readonly property alias checkBox: loader.item
property int checkedCount: 0
visible: {
if (!model.isCategory)
return d.search(model.name, searcher.text)
|| checkBox.checked
if (checkedCount > 0)
return true
const subItemsCount = subItemsRepeater.count
for (let i = 0; i < subItemsCount; i++)
if (subItemsRepeater.itemAt(i).show)
return true
return false
}
Loader {
id: loader
Layout.fillWidth: true
Layout.topMargin: model.isCategory ? 9 : 0
sourceComponent: model.isCategory
? communityCategoryDelegate
: communityDelegate
Connections {
target: radioButton
function onToggled() {
const checkBox = loader.item.checkBox
checkBox.checked = false
checkBox.onToggled()
}
}
Component {
id: communityDelegate
CommunityListItem {
id: communityItem
title: "#" + model.name
asset.name: model.icon
asset.emoji: d.resolveEmoji(model.emoji)
asset.color: d.resolveColor(model.color,
model.colorId)
checkBox.onToggled: {
if (checked)
radioButton.checked = false
}
checkBox.onCheckedChanged: {
if (checkBox.checked)
d.addToSelectedChannels(model)
else
d.removeFromSelectedChannels(model)
}
Connections {
target: d
function onSetSelectedChannels(channels) {
communityItem.checked = channels.includes(
model.itemId)
}
}
}
}
Component {
id: communityCategoryDelegate
CommunityCategoryListItem {
title: model.name
checkState: {
if (checkedCount === model.subItems.count)
return Qt.Checked
else if (checkedCount === 0)
return Qt.Unchecked
return Qt.PartiallyChecked
}
checkBox.onToggled: {
if (checked)
radioButton.checked = false
subItemsRepeater.setAll(checkState)
}
}
}
}
Repeater {
id: subItemsRepeater
model: SortFilterProxyModel {
sourceModel: topModel.isCategory ? topModel.subItems : null
sorters: RoleSorter { roleName: "position" }
}
function setAll(checkState) {
const subItemsCount = count
for (let i = 0; i < subItemsCount; i++) {
itemAt(i).checkState = checkState
}
}
CommunityListItem {
id: communitySubItem
Layout.fillWidth: true
readonly property bool show: d.search(model.name, searcher.text)
|| checked
visible: show
title: "#" + model.name
asset.name: model.icon
asset.emoji: d.resolveEmoji(model.emoji)
asset.color: d.resolveColor(model.color,
model.colorId)
onCheckedChanged: {
if (checked) {
radioButton.checked = false
d.addToSelectedChannels(model)
} else {
d.removeFromSelectedChannels(model)
}
Qt.callLater(() => checkedCount += checked ? 1 : -1)
}
Connections {
target: d
function onSetSelectedChannels(channels) {
communitySubItem.checked = channels.includes(
model.itemId)
}
}
}
}
}
}
}
}
StatusButton {
Layout.fillWidth: true
text: root.acceptMode === InDropdown.AcceptMode.Add
? qsTr("Add") : qsTr("Update")
onClicked: {
if (radioButton.checked) {
root.communitySelected()
return
}
root.channelsSelected(Array.from(d.selectedChannels.values()))
}
}
}
}