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

377 lines
13 KiB
QML

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 int defaultVMargin: 9
readonly property int maxHeightCountNo: 5
readonly property int itemStandardHeight: 44
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)
}
}
contentItem: ColumnLayout {
spacing: 0
SearchBox {
id: searcher
Layout.fillWidth: true
topPadding: 0
bottomPadding: 0
minimumHeight: 36
maximumHeight: 36
}
StatusListItem {
Layout.fillWidth: true
Layout.topMargin: d.defaultVMargin
Layout.preferredHeight: d.itemStandardHeight
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.minimumHeight: Math.min(d.maxHeightCountNo, topRepeater.count) * d.itemStandardHeight
Layout.maximumHeight: Layout.minimumHeight
contentHeight: scrollableColumn.implicitHeight
Layout.bottomMargin: d.defaultVMargin
Layout.topMargin:
!root.allowChoosingEntireCommunity && !root.allowChoosingEntireCommunity ? d.defaultVMargin : 0
padding: 0
ColumnLayout {
id: scrollableColumn
width: scrollView.width
spacing: 0
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.preferredHeight: d.itemStandardHeight
Layout.topMargin: model.isCategory ? d.defaultVMargin : 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()))
}
}
}
}