diff --git a/ui/app/AppLayouts/Chat/ContactsColumn/Channel.qml b/ui/app/AppLayouts/Chat/ContactsColumn/Channel.qml index 7c52dbe1bd..0ba0658b09 100644 --- a/ui/app/AppLayouts/Chat/ContactsColumn/Channel.qml +++ b/ui/app/AppLayouts/Chat/ContactsColumn/Channel.qml @@ -44,7 +44,7 @@ Rectangle { anchors.left: parent.left radius: 8 // Hide the box if it is filtered out - property bool isVisible: searchStr == "" || name.includes(searchStr) + property bool isVisible: searchStr === "" || name.includes(searchStr) visible: isVisible ? true : false height: isVisible ? !isCompact ? 64 : contactImage.height + Style.current.smallPadding * 2 : 0 diff --git a/ui/app/AppMain.qml b/ui/app/AppMain.qml index 1048ca1aee..a17bf7bbdd 100644 --- a/ui/app/AppMain.qml +++ b/ui/app/AppMain.qml @@ -69,6 +69,47 @@ RowLayout { shortcut: "Ctrl+4, Ctrl+," onTriggered: changeAppSection(Constants.profile) } + Action { + shortcut: "Ctrl+K" + onTriggered: { + if (channelPicker.opened) { + channelPicker.close() + } else { + channelPicker.open() + } + } + } + Component { + id: statusIdenticonComponent + StatusIdenticon {} + } + + StatusInputListPopup { + id: channelPicker + title: qsTr("Where do you want to go?") + showSearchBox: true + width: 350 + x: parent.width / 2 - width / 2 + y: parent.height / 2 - height / 2 + modelList: chatsModel.chats + getText: function (modelData) { + return modelData.name + } + getImageComponent: function (parent, modelData) { + return statusIdenticonComponent.createObject(parent, { + width: channelPicker.imageWidth, + height: channelPicker.imageHeight, + chatName: modelData.name, + chatType: modelData.chatType, + identicon: modelData.identicon + }); + } + onClicked: function (index) { + chatsModel.setActiveChannelByIndex(index) + appMain.changeAppSection(Constants.chat) + channelPicker.close() + } + } function changeAppSection(section) { let sectionId = -1 diff --git a/ui/shared/Input.qml b/ui/shared/Input.qml index 8cf5cda55e..def07365e9 100644 --- a/ui/shared/Input.qml +++ b/ui/shared/Input.qml @@ -32,7 +32,8 @@ Item { signal textEdited(string inputValue) id: inputBox - height: inputRectangle.height + (hasLabel ? inputLabel.height + labelMargin : 0) + (!!validationError ? (validationErrorText.height + validationErrorTopMargin) : 0) + implicitHeight: inputRectangle.height + (hasLabel ? inputLabel.height + labelMargin : 0) + (!!validationError ? (validationErrorText.height + validationErrorTopMargin) : 0) + height: implicitHeight anchors.right: parent.right anchors.left: parent.left diff --git a/ui/shared/status/StatusInputListPopup.qml b/ui/shared/status/StatusInputListPopup.qml index 0e14e964d9..406fe899c1 100644 --- a/ui/shared/status/StatusInputListPopup.qml +++ b/ui/shared/status/StatusInputListPopup.qml @@ -2,31 +2,53 @@ import QtQuick 2.13 import QtQuick.Controls 2.13 import QtGraphicalEffects 1.12 import QtQuick.Dialogs 1.3 +import QtQuick.Layouts 1.13 import "../../imports" import "../../shared" Popup { property var modelList property alias listView: listView - property var getImageSource: function () {} + property var getImageSource + property var getImageComponent property var getText: function () {} property var onClicked: function () {} property int imageWidth: 22 property int imageHeight: 22 + property string title + property bool showSearchBox: false function openPopup(listParam) { modelList = listParam popup.open() } + onOpened: { + listView.currentIndex = 0 + if (showSearchBox) { + searchBox.textField.forceActiveFocus() + } + } + id: popup + padding: Style.current.smallPadding width: messageInput.width - height: Math.min(400, listView.contentHeight + Style.current.smallPadding) - x : messageInput.x + height: { + let possibleHeight = listView.contentHeight + Style.current.smallPadding * 2 + if (popupTitle.visible) { + possibleHeight += popupTitle.height + Style.current.smallPadding + } + if (searchBox.visible) { + possibleHeight += searchBox.height + Style.current.smallPadding + } + + return Math.min(400, possibleHeight) + } + x: messageInput.x y: -height background: Rectangle { id: bgRectangle - visible: !!popup.modelList && popup.modelList.length > 0 + visible: !!popup.title || (!!popup.modelList && popup.modelList.length > 0) color: Style.current.background border.width: 0 radius: Style.current.radius @@ -46,36 +68,133 @@ Popup { } } + StyledText { + id: popupTitle + visible: !!popup.title + height: visible ? implicitHeight : 0 + text: popup.title + font.pixelSize: 17 + anchors.top: parent.top + } + + SearchBox { + id: searchBox + visible: showSearchBox + height: visible ? implicitHeight : 0 + width: parent.width + anchors.top: popupTitle.bottom + anchors.topMargin: popupTitle.visible ? Style.current.smallPadding : 0 + + function goToNextAvailableIndex(up) { + do { + if (!up && listView.currentIndex === listView.count - 1) { + listView.currentIndex = 0 + return + } else if (up && listView.currentIndex === 0) { + listView.currentIndex = listView.count - 1 + return + } + + if (up) { + listView.decrementCurrentIndex() + } else { + listView.incrementCurrentIndex() + } + } while (!listView.currentItem.visible) + } + + Keys.onReleased: function onKeyPress(event) { + if (event.key === Qt.Key_Down) { + searchBox.goToNextAvailableIndex(false) + } + if (event.key === Qt.Key_Up) { + searchBox.goToNextAvailableIndex(true) + } + if (event.key === Qt.Key_Escape) { + return popup.close() + } + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + return popup.onClicked(listView.currentIndex) + } + } + } + ListView { id: listView model: popup.modelList || [] keyNavigationEnabled: true - anchors.fill: parent + Layout.fillHeight: true + width: parent.width + anchors.top: searchBox.bottom + anchors.topMargin: searchBox.visible ? Style.current.smallPadding : 0 + anchors.bottom: parent.bottom clip: true delegate: Rectangle { + property string myText: { + if (typeof modelData === "undefined") { + return popup.getText(model) + } + return popup.getText(modelData) + } id: rectangle + visible: searchBox.text === "" || myText.includes(searchBox.text) color: listView.currentIndex === index ? Style.current.backgroundHover : Style.current.transparent border.width: 0 width: parent.width - height: 42 + height: visible ? 42 : 0 radius: Style.current.radius - SVGImage { - id: image - source: popup.getImageSource(modelData) + Loader { + id: imageLoader + active: !!popup.getImageComponent || !!popup.getImageSource width: popup.imageWidth height: popup.imageHeight anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: Style.current.smallPadding + sourceComponent: popup.getImageComponent ? customImageComponent : normalImageComponent + } + + Component { + id: customImageComponent + Item { + id: imageComponentContainer + children: { + if (!popup.getImageComponent) { + return "" + } + if (typeof modelData === "undefined") { + return popup.getImageComponent(imageComponentContainer, model) + } + return popup.getImageComponent(imageComponentContainer, modelData) + } + } + } + + Component { + id: normalImageComponent + SVGImage { + visible: !!source + width: popup.imageWidth + height: popup.imageHeight + source: { + if (!popup.getImageSource) { + return "" + } + if (typeof modelData === "undefined") { + return popup.getImageSource(model) + } + return popup.getImageSource(modelData) + } + } } StyledText { - text: popup.getText(modelData) + text: rectangle.myText color: Style.current.textColor anchors.verticalCenter: parent.verticalCenter - anchors.left: image.right + anchors.left: imageLoader.right anchors.leftMargin: Style.current.smallPadding font.pixelSize: 15 }