Generic Search Page
Pull the generic aspects from Room search and join room pages into it's own component. This is done in anticipation of using the new generic search page for a user search functionality. - `SearchPage` is now used for the generic version with the old one being renamed `RoomSearchPage` - `JoinRoomPage` is renamed to `ExploreRoomsPage` inline with everywhere else in NeoChat There is also some cleanup of the code for both search pages in here.
This commit is contained in:
@@ -165,7 +165,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/UserInfoDesktop.qml
|
||||
qml/RoomPage.qml
|
||||
qml/RoomWindow.qml
|
||||
qml/JoinRoomPage.qml
|
||||
qml/ExploreRoomsPage.qml
|
||||
qml/ManualRoomDialog.qml
|
||||
qml/ExplorerDelegate.qml
|
||||
qml/InviteUserPage.qml
|
||||
@@ -271,7 +271,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/EmojiTonesPicker.qml
|
||||
qml/EmojiDelegate.qml
|
||||
qml/EmojiGrid.qml
|
||||
qml/SearchPage.qml
|
||||
qml/RoomSearchPage.qml
|
||||
qml/LocationDelegate.qml
|
||||
qml/LocationChooser.qml
|
||||
qml/TimelineView.qml
|
||||
@@ -302,6 +302,8 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/NotificationsView.qml
|
||||
qml/LoadingDelegate.qml
|
||||
qml/TimelineEndDelegate.qml
|
||||
qml/SearchPage.qml
|
||||
qml/ServerComboBox.qml
|
||||
RESOURCES
|
||||
qml/confetti.png
|
||||
qml/glowdot.png
|
||||
|
||||
@@ -41,7 +41,7 @@ void PublicRoomListModel::setConnection(Connection *conn)
|
||||
if (job) {
|
||||
job->abandon();
|
||||
job = nullptr;
|
||||
Q_EMIT loadingChanged();
|
||||
Q_EMIT searchingChanged();
|
||||
}
|
||||
|
||||
if (m_connection) {
|
||||
@@ -50,7 +50,6 @@ void PublicRoomListModel::setConnection(Connection *conn)
|
||||
|
||||
Q_EMIT connectionChanged();
|
||||
Q_EMIT serverChanged();
|
||||
Q_EMIT hasMoreChanged();
|
||||
}
|
||||
|
||||
QString PublicRoomListModel::server() const
|
||||
@@ -71,14 +70,14 @@ void PublicRoomListModel::setServer(const QString &value)
|
||||
nextBatch = QString();
|
||||
attempted = false;
|
||||
rooms.clear();
|
||||
Q_EMIT loadingChanged();
|
||||
Q_EMIT searchingChanged();
|
||||
|
||||
endResetModel();
|
||||
|
||||
if (job) {
|
||||
job->abandon();
|
||||
job = nullptr;
|
||||
Q_EMIT loadingChanged();
|
||||
Q_EMIT searchingChanged();
|
||||
}
|
||||
|
||||
if (m_connection) {
|
||||
@@ -86,21 +85,20 @@ void PublicRoomListModel::setServer(const QString &value)
|
||||
}
|
||||
|
||||
Q_EMIT serverChanged();
|
||||
Q_EMIT hasMoreChanged();
|
||||
}
|
||||
|
||||
QString PublicRoomListModel::keyword() const
|
||||
QString PublicRoomListModel::searchText() const
|
||||
{
|
||||
return m_keyword;
|
||||
return m_searchText;
|
||||
}
|
||||
|
||||
void PublicRoomListModel::setKeyword(const QString &value)
|
||||
void PublicRoomListModel::setSearchText(const QString &value)
|
||||
{
|
||||
if (m_keyword == value) {
|
||||
if (m_searchText == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_keyword = value;
|
||||
m_searchText = value;
|
||||
|
||||
beginResetModel();
|
||||
|
||||
@@ -113,15 +111,14 @@ void PublicRoomListModel::setKeyword(const QString &value)
|
||||
if (job) {
|
||||
job->abandon();
|
||||
job = nullptr;
|
||||
Q_EMIT loadingChanged();
|
||||
Q_EMIT searchingChanged();
|
||||
}
|
||||
|
||||
if (m_connection) {
|
||||
next();
|
||||
}
|
||||
|
||||
Q_EMIT keywordChanged();
|
||||
Q_EMIT hasMoreChanged();
|
||||
Q_EMIT searchTextChanged();
|
||||
}
|
||||
|
||||
bool PublicRoomListModel::showOnlySpaces() const
|
||||
@@ -154,8 +151,8 @@ void PublicRoomListModel::next(int count)
|
||||
if (m_showOnlySpaces) {
|
||||
roomTypes += QLatin1String("m.space");
|
||||
}
|
||||
job = m_connection->callApi<QueryPublicRoomsJob>(m_server, count, nextBatch, QueryPublicRoomsJob::Filter{m_keyword, roomTypes});
|
||||
Q_EMIT loadingChanged();
|
||||
job = m_connection->callApi<QueryPublicRoomsJob>(m_server, count, nextBatch, QueryPublicRoomsJob::Filter{m_searchText, roomTypes});
|
||||
Q_EMIT searchingChanged();
|
||||
|
||||
connect(job, &BaseJob::finished, this, [this] {
|
||||
attempted = true;
|
||||
@@ -166,14 +163,10 @@ void PublicRoomListModel::next(int count)
|
||||
this->beginInsertRows({}, rooms.count(), rooms.count() + job->chunk().count() - 1);
|
||||
rooms.append(job->chunk());
|
||||
this->endInsertRows();
|
||||
|
||||
if (job->nextBatch().isEmpty()) {
|
||||
Q_EMIT hasMoreChanged();
|
||||
}
|
||||
}
|
||||
|
||||
this->job = nullptr;
|
||||
Q_EMIT loadingChanged();
|
||||
Q_EMIT searchingChanged();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -271,12 +264,19 @@ int PublicRoomListModel::rowCount(const QModelIndex &parent) const
|
||||
return rooms.count();
|
||||
}
|
||||
|
||||
bool PublicRoomListModel::hasMore() const
|
||||
bool PublicRoomListModel::canFetchMore(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return !(attempted && nextBatch.isEmpty());
|
||||
}
|
||||
|
||||
bool PublicRoomListModel::loading() const
|
||||
void PublicRoomListModel::fetchMore(const QModelIndex &parent)
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
next();
|
||||
}
|
||||
|
||||
bool PublicRoomListModel::searching() const
|
||||
{
|
||||
return job != nullptr;
|
||||
}
|
||||
|
||||
@@ -41,9 +41,9 @@ class PublicRoomListModel : public QAbstractListModel
|
||||
Q_PROPERTY(QString server READ server WRITE setServer NOTIFY serverChanged)
|
||||
|
||||
/**
|
||||
* @brief The filter keyword for the list of public rooms.
|
||||
* @brief The text to search the public room list for.
|
||||
*/
|
||||
Q_PROPERTY(QString keyword READ keyword WRITE setKeyword NOTIFY keywordChanged)
|
||||
Q_PROPERTY(QString searchText READ searchText WRITE setSearchText NOTIFY searchTextChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether only space rooms should be shown.
|
||||
@@ -51,14 +51,9 @@ class PublicRoomListModel : public QAbstractListModel
|
||||
Q_PROPERTY(bool showOnlySpaces READ showOnlySpaces WRITE setShowOnlySpaces NOTIFY showOnlySpacesChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the model has more items to load.
|
||||
* @brief Whether the model is searching.
|
||||
*/
|
||||
Q_PROPERTY(bool hasMore READ hasMore NOTIFY hasMoreChanged)
|
||||
|
||||
/**
|
||||
* @biref Whether the model is still loading.
|
||||
*/
|
||||
Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
|
||||
Q_PROPERTY(bool searching READ searching NOTIFY searchingChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -105,31 +100,31 @@ public:
|
||||
[[nodiscard]] QString server() const;
|
||||
void setServer(const QString &value);
|
||||
|
||||
[[nodiscard]] QString keyword() const;
|
||||
void setKeyword(const QString &value);
|
||||
[[nodiscard]] QString searchText() const;
|
||||
void setSearchText(const QString &searchText);
|
||||
|
||||
[[nodiscard]] bool showOnlySpaces() const;
|
||||
void setShowOnlySpaces(bool showOnlySpaces);
|
||||
|
||||
[[nodiscard]] bool hasMore() const;
|
||||
[[nodiscard]] bool searching() const;
|
||||
|
||||
[[nodiscard]] bool loading() const;
|
||||
private:
|
||||
QPointer<Quotient::Connection> m_connection = nullptr;
|
||||
QString m_server;
|
||||
QString m_searchText;
|
||||
bool m_showOnlySpaces = false;
|
||||
|
||||
/**
|
||||
* @brief Load the next set of rooms.
|
||||
*
|
||||
* @param count the maximum number of rooms to load.
|
||||
*/
|
||||
Q_INVOKABLE void next(int count = 50);
|
||||
|
||||
private:
|
||||
Quotient::Connection *m_connection = nullptr;
|
||||
QString m_server;
|
||||
QString m_keyword;
|
||||
bool m_showOnlySpaces = false;
|
||||
void next(int count = 50);
|
||||
bool canFetchMore(const QModelIndex &parent) const override;
|
||||
void fetchMore(const QModelIndex &parent) override;
|
||||
|
||||
bool attempted = false;
|
||||
bool m_loading = false;
|
||||
bool m_searching = false;
|
||||
QString nextBatch;
|
||||
|
||||
QList<Quotient::PublicRoomsChunk> rooms;
|
||||
@@ -139,8 +134,7 @@ private:
|
||||
Q_SIGNALS:
|
||||
void connectionChanged();
|
||||
void serverChanged();
|
||||
void keywordChanged();
|
||||
void searchTextChanged();
|
||||
void showOnlySpacesChanged();
|
||||
void hasMoreChanged();
|
||||
void loadingChanged();
|
||||
void searchingChanged();
|
||||
};
|
||||
|
||||
@@ -36,7 +36,7 @@ void SearchModel::setSearchText(const QString &searchText)
|
||||
|
||||
void SearchModel::search()
|
||||
{
|
||||
Q_ASSERT(m_connection);
|
||||
Q_ASSERT(m_room);
|
||||
setSearching(true);
|
||||
if (m_job) {
|
||||
m_job->abandon();
|
||||
@@ -62,7 +62,7 @@ void SearchModel::search()
|
||||
|
||||
};
|
||||
|
||||
auto job = m_connection->callApi<SearchJob>(SearchJob::Categories{criteria});
|
||||
auto job = m_room->connection()->callApi<SearchJob>(SearchJob::Categories{criteria});
|
||||
m_job = job;
|
||||
connect(job, &BaseJob::finished, this, [this, job] {
|
||||
beginResetModel();
|
||||
@@ -74,17 +74,6 @@ void SearchModel::search()
|
||||
});
|
||||
}
|
||||
|
||||
Connection *SearchModel::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
void SearchModel::setConnection(Connection *connection)
|
||||
{
|
||||
m_connection = connection;
|
||||
Q_EMIT connectionChanged();
|
||||
}
|
||||
|
||||
QVariant SearchModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
auto row = index.row();
|
||||
|
||||
@@ -31,11 +31,6 @@ class SearchModel : public QAbstractListModel
|
||||
*/
|
||||
Q_PROPERTY(QString searchText READ searchText WRITE setSearchText NOTIFY searchTextChanged)
|
||||
|
||||
/**
|
||||
* @brief The current connection that the model is using to search for messages.
|
||||
*/
|
||||
Q_PROPERTY(Quotient::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
|
||||
/**
|
||||
* @brief The current room that the search is being done from.
|
||||
*/
|
||||
@@ -94,9 +89,6 @@ public:
|
||||
QString searchText() const;
|
||||
void setSearchText(const QString &searchText);
|
||||
|
||||
Quotient::Connection *connection() const;
|
||||
void setConnection(Quotient::Connection *connection);
|
||||
|
||||
NeoChatRoom *room() const;
|
||||
void setRoom(NeoChatRoom *room);
|
||||
|
||||
@@ -130,7 +122,6 @@ public:
|
||||
|
||||
Q_SIGNALS:
|
||||
void searchTextChanged();
|
||||
void connectionChanged();
|
||||
void roomChanged();
|
||||
void searchingChanged();
|
||||
|
||||
@@ -141,7 +132,6 @@ private:
|
||||
void setSearching(bool searching);
|
||||
|
||||
QString m_searchText;
|
||||
Quotient::Connection *m_connection = nullptr;
|
||||
NeoChatRoom *m_room = nullptr;
|
||||
Quotient::Omittable<Quotient::SearchJob::ResultRoomEvents> m_result = Quotient::none;
|
||||
Quotient::SearchJob *m_job = nullptr;
|
||||
|
||||
@@ -103,7 +103,7 @@ FormCard.FormCardPage {
|
||||
visible: !chosenRoomDelegate.visible
|
||||
text: i18nc("@action:button", "Pick room")
|
||||
onClicked: {
|
||||
let dialog = pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/JoinRoomPage.qml", {connection: root.connection}, {title: i18nc("@title", "Explore Rooms")})
|
||||
let dialog = pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/ExploreRoomsPage.qml", {connection: root.connection}, {title: i18nc("@title", "Explore Rooms")})
|
||||
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||
chosenRoomDelegate.roomId = roomId;
|
||||
chosenRoomDelegate.displayName = displayName;
|
||||
@@ -182,7 +182,7 @@ FormCard.FormCardPage {
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
let dialog = pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/JoinRoomPage.qml", {connection: root.connection}, {title: i18nc("@title", "Explore Rooms")})
|
||||
let dialog = pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/ExploreRoomsPage.qml", {connection: root.connection}, {title: i18nc("@title", "Explore Rooms")})
|
||||
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||
chosenRoomDelegate.roomId = roomId;
|
||||
chosenRoomDelegate.displayName = displayName;
|
||||
|
||||
@@ -21,7 +21,7 @@ RowLayout {
|
||||
text: i18n("Explore rooms")
|
||||
icon.name: "compass"
|
||||
onTriggered: {
|
||||
let dialog = pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/JoinRoomPage.qml", {connection: root.connection}, {title: i18nc("@title", "Explore Rooms")})
|
||||
let dialog = pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/ExploreRoomsPage.qml", {connection: root.connection}, {title: i18nc("@title", "Explore Rooms")})
|
||||
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||
if (isJoined) {
|
||||
RoomManager.enterRoom(root.connection.room(roomId))
|
||||
|
||||
@@ -52,7 +52,7 @@ ColumnLayout {
|
||||
text: i18n("Explore rooms")
|
||||
icon.name: "compass"
|
||||
onTriggered: {
|
||||
let dialog = pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/JoinRoomPage.qml", {connection: root.connection}, {title: i18nc("@title", "Explore Rooms")})
|
||||
let dialog = pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/ExploreRoomsPage.qml", {connection: root.connection}, {title: i18nc("@title", "Explore Rooms")})
|
||||
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||
if (isJoined) {
|
||||
RoomManager.enterRoom(root.connection.room(roomId));
|
||||
|
||||
112
src/qml/ExploreRoomsPage.qml
Normal file
112
src/qml/ExploreRoomsPage.qml
Normal file
@@ -0,0 +1,112 @@
|
||||
// SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
|
||||
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import Qt.labs.qmlmodels
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.delegates as Delegates
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
/**
|
||||
* @brief Component for finding rooms for the public list.
|
||||
*
|
||||
* This component is based on a SearchPage, adding the functionality to select or
|
||||
* enter a server in the header, as well as the ability to manually type a room in
|
||||
* if the public room search cannot find it.
|
||||
*
|
||||
* @sa SearchPage
|
||||
*/
|
||||
SearchPage {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* @brief The connection for the current local user.
|
||||
*/
|
||||
required property NeoChatConnection connection
|
||||
|
||||
/**
|
||||
* @brief Whether results should only includes spaces.
|
||||
*/
|
||||
property bool showOnlySpaces: false
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when a room is selected.
|
||||
*
|
||||
* The signal contains all the room's info so that it can be acted
|
||||
* upon as required, e.g. joining or entering the room or adding the room as
|
||||
* the child of a space.
|
||||
*/
|
||||
signal roomSelected(string roomId,
|
||||
string displayName,
|
||||
url avatarUrl,
|
||||
string alias,
|
||||
string topic,
|
||||
int memberCount,
|
||||
bool isJoined)
|
||||
|
||||
title: i18nc("@action:title", "Explore Rooms")
|
||||
|
||||
Component.onCompleted: focusSearch()
|
||||
|
||||
headerTrailing: ServerComboBox {
|
||||
id: serverComboBox
|
||||
connection: root.connection
|
||||
}
|
||||
|
||||
model: PublicRoomListModel {
|
||||
id: publicRoomListModel
|
||||
|
||||
connection: root.connection
|
||||
server: serverComboBox.server
|
||||
showOnlySpaces: root.showOnlySpaces
|
||||
}
|
||||
|
||||
modelDelegate: ExplorerDelegate {
|
||||
onRoomSelected: (roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||
root.roomSelected(roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined);
|
||||
root.closeDialog();
|
||||
}
|
||||
}
|
||||
|
||||
listHeaderDelegate: Delegates.RoundedItemDelegate {
|
||||
onClicked: _private.openManualRoomDialog()
|
||||
|
||||
text: i18n("Enter a room address")
|
||||
icon.name: "compass"
|
||||
icon.width: Kirigami.Units.gridUnit * 2
|
||||
icon.height: Kirigami.Units.gridUnit * 2
|
||||
}
|
||||
|
||||
listFooterDelegate: QQC2.ProgressBar {
|
||||
width: ListView.view.width
|
||||
leftInset: Kirigami.Units.largeSpacing
|
||||
rightInset: Kirigami.Units.largeSpacing
|
||||
visible: root.count !== 0 && publicRoomListModel.searching
|
||||
indeterminate: true
|
||||
}
|
||||
|
||||
searchFieldPlaceholder: i18n("Find a room...")
|
||||
noResultPlaceholderMessage: i18nc("@info:label", "No public rooms found")
|
||||
|
||||
Component {
|
||||
id: manualRoomDialog
|
||||
ManualRoomDialog {}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: _private
|
||||
function openManualRoomDialog() {
|
||||
let dialog = manualRoomDialog.createObject(applicationWindow().overlay, {connection: root.connection});
|
||||
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||
root.roomSelected(roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined);
|
||||
root.closeDialog();
|
||||
});
|
||||
dialog.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ Labs.MenuBar {
|
||||
Labs.MenuItem {
|
||||
text: i18nc("menu", "Browse Chats…")
|
||||
onTriggered: {
|
||||
let dialog = pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/JoinRoomPage.qml", {connection: root.connection}, {title: i18nc("@title", "Explore Rooms")})
|
||||
let dialog = pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/ExploreRoomsPage.qml", {connection: root.connection}, {title: i18nc("@title", "Explore Rooms")})
|
||||
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||
if (isJoined) {
|
||||
RoomManager.enterRoom(root.connection.room(roomId))
|
||||
|
||||
@@ -1,285 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
|
||||
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import Qt.labs.qmlmodels
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.delegates as Delegates
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
Kirigami.ScrollablePage {
|
||||
id: root
|
||||
|
||||
required property NeoChatConnection connection
|
||||
|
||||
property bool showOnlySpaces: false
|
||||
|
||||
property alias keyword: identifierField.text
|
||||
property string server
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when a room is selected.
|
||||
*
|
||||
* The signal contains all the room's info so that it can be acted
|
||||
* upon as required, e.g. joinng or entering the room or adding the room as
|
||||
* the child of a space.
|
||||
*/
|
||||
signal roomSelected(string roomId,
|
||||
string displayName,
|
||||
url avatarUrl,
|
||||
string alias,
|
||||
string topic,
|
||||
int memberCount,
|
||||
bool isJoined)
|
||||
|
||||
title: i18n("Explore Rooms")
|
||||
|
||||
Component.onCompleted: identifierField.forceActiveFocus()
|
||||
|
||||
header: QQC2.Control {
|
||||
padding: Kirigami.Units.largeSpacing
|
||||
|
||||
background: Rectangle {
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
Kirigami.SearchField {
|
||||
id: identifierField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: i18n("Find a room...")
|
||||
}
|
||||
QQC2.ComboBox {
|
||||
id: serverField
|
||||
|
||||
// TODO: in KF6 we should be able to switch to using implicitContentWidthPolicy
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 10
|
||||
|
||||
Component.onCompleted: currentIndex = 0
|
||||
|
||||
textRole: "url"
|
||||
valueRole: "url"
|
||||
model: ServerListModel {
|
||||
id: serverListModel
|
||||
connection: root.connection
|
||||
}
|
||||
|
||||
delegate: Delegates.RoundedItemDelegate {
|
||||
id: serverItem
|
||||
|
||||
required property int index
|
||||
required property string url
|
||||
required property bool isAddServerDelegate
|
||||
required property bool isHomeServer
|
||||
required property bool isDeletable
|
||||
|
||||
text: isAddServerDelegate ? i18n("Add New Server") : url
|
||||
highlighted: false
|
||||
|
||||
topInset: index === 0 ? Kirigami.Units.smallSpacing : Math.round(Kirigami.Units.smallSpacing / 2)
|
||||
bottomInset: index === ListView.view.count - 1 ? Kirigami.Units.smallSpacing : Math.round(Kirigami.Units.smallSpacing / 2)
|
||||
|
||||
onClicked: if (isAddServerDelegate) {
|
||||
addServerSheet.open()
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Delegates.SubtitleContentItem {
|
||||
itemDelegate: serverItem
|
||||
subtitle: serverItem.isHomeServer ? i18n("Home Server") : ""
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
QQC2.ToolButton {
|
||||
visible: serverItem.isAddServerDelegate || serverItem.isDeletable
|
||||
icon.name: serverItem.isAddServerDelegate ? "list-add" : "dialog-close"
|
||||
text: i18nc("@action:button", "Add new server")
|
||||
Accessible.name: text
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: {
|
||||
if (serverField.currentIndex === serverItem.index && serverItem.isDeletable) {
|
||||
serverField.currentIndex = 0;
|
||||
server = serverField.currentValue;
|
||||
serverField.popup.close();
|
||||
}
|
||||
if (serverItem.isAddServerDelegate) {
|
||||
addServerSheet.open();
|
||||
serverItem.clicked();
|
||||
} else {
|
||||
serverListModel.removeServerAtIndex(serverItem.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onActivated: {
|
||||
if (currentIndex !== count - 1) {
|
||||
server = currentValue
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.OverlaySheet {
|
||||
id: addServerSheet
|
||||
|
||||
parent: applicationWindow().overlay
|
||||
|
||||
title: i18nc("@title:window", "Add server")
|
||||
|
||||
onOpened: if (!serverUrlField.isValidServer && !addServerSheet.opened) {
|
||||
serverField.currentIndex = 0
|
||||
server = serverField.currentValue
|
||||
} else if (addServerSheet.opened) {
|
||||
serverUrlField.forceActiveFocus()
|
||||
}
|
||||
|
||||
contentItem: Kirigami.FormLayout {
|
||||
QQC2.Label {
|
||||
Layout.minimumWidth: Kirigami.Units.gridUnit * 20
|
||||
|
||||
text: serverUrlField.length > 0 ? (serverUrlField.acceptableInput ? (serverUrlField.isValidServer ? i18n("Valid server entered") : i18n("This server cannot be resolved or has already been added")) : i18n("The entered text is not a valid url")) : i18n("Enter server url e.g. kde.org")
|
||||
color: serverUrlField.length > 0 ? (serverUrlField.acceptableInput ? (serverUrlField.isValidServer ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.negativeTextColor) : Kirigami.Theme.negativeTextColor) : Kirigami.Theme.textColor
|
||||
}
|
||||
QQC2.TextField {
|
||||
id: serverUrlField
|
||||
|
||||
property bool isValidServer: false
|
||||
|
||||
Kirigami.FormData.label: i18n("Server URL")
|
||||
onTextChanged: {
|
||||
if(acceptableInput) {
|
||||
serverListModel.checkServer(text)
|
||||
}
|
||||
}
|
||||
|
||||
validator: RegularExpressionValidator {
|
||||
regularExpression: /^[a-zA-Z0-9-]{1,61}\.([a-zA-Z]{2,}|[a-zA-Z0-9-]{2,}\.[a-zA-Z]{2,3})$/
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: serverListModel
|
||||
function onServerCheckComplete(url, valid) {
|
||||
if (url == serverUrlField.text && valid) {
|
||||
serverUrlField.isValidServer = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Button {
|
||||
id: okButton
|
||||
|
||||
text: i18nc("@action:button", "Ok")
|
||||
enabled: serverUrlField.acceptableInput && serverUrlField.isValidServer
|
||||
onClicked: {
|
||||
serverListModel.addServer(serverUrlField.text)
|
||||
serverField.currentIndex = serverField.indexOfValue(serverUrlField.text)
|
||||
server = serverField.currentValue
|
||||
serverUrlField.text = ""
|
||||
addServerSheet.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
z: 999
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: publicRoomsListView
|
||||
|
||||
topMargin: Math.round(Kirigami.Units.smallSpacing / 2)
|
||||
bottomMargin: Math.round(Kirigami.Units.smallSpacing / 2)
|
||||
|
||||
model: PublicRoomListModel {
|
||||
id: publicRoomListModel
|
||||
|
||||
connection: root.connection
|
||||
server: root.server
|
||||
keyword: root.keyword
|
||||
showOnlySpaces: root.showOnlySpaces
|
||||
}
|
||||
|
||||
onContentYChanged: {
|
||||
if(publicRoomListModel.hasMore && contentHeight - contentY < publicRoomsListView.height + 200)
|
||||
publicRoomListModel.next();
|
||||
}
|
||||
delegate: ExplorerDelegate {
|
||||
onRoomSelected: (roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||
root.roomSelected(roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined);
|
||||
root.closeDialog();
|
||||
}
|
||||
}
|
||||
|
||||
header: Delegates.RoundedItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
onClicked: _private.openManualRoomDialog()
|
||||
|
||||
text: i18n("Enter a room address")
|
||||
icon.name: "compass"
|
||||
icon.width: Kirigami.Units.gridUnit * 2
|
||||
icon.height: Kirigami.Units.gridUnit * 2
|
||||
}
|
||||
|
||||
footer: QQC2.ProgressBar {
|
||||
width: parent.width
|
||||
visible: publicRoomsListView.count !== 0 && publicRoomsListView.model.loading
|
||||
indeterminate: true
|
||||
padding: Kirigami.Units.largeSpacing * 2
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
}
|
||||
|
||||
Kirigami.LoadingPlaceholder {
|
||||
anchors.centerIn: parent
|
||||
visible: publicRoomsListView.model.loading && publicRoomsListView.count === 0
|
||||
}
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
anchors.centerIn: parent
|
||||
visible: !publicRoomsListView.model.loading && publicRoomsListView.count === 0
|
||||
text: i18nc("@info:label", "No public rooms found")
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: manualRoomDialog
|
||||
ManualRoomDialog {}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: _private
|
||||
function openManualRoomDialog() {
|
||||
let dialog = manualRoomDialog.createObject(applicationWindow().overlay, {connection: root.connection});
|
||||
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||
root.roomSelected(roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined);
|
||||
root.closeDialog();
|
||||
});
|
||||
dialog.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,9 +95,8 @@ QQC2.ScrollView {
|
||||
Layout.fillWidth: true
|
||||
|
||||
onClicked: {
|
||||
pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/SearchPage.qml", {
|
||||
currentRoom: root.room,
|
||||
connection: root.connection
|
||||
pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/RoomSearchPage.qml", {
|
||||
room: root.room
|
||||
}, {
|
||||
title: i18nc("@action:title", "Search")
|
||||
})
|
||||
|
||||
@@ -164,7 +164,7 @@ Kirigami.Page {
|
||||
icon.name: sortFilterRoomListModel.filterText.length > 0 ? "search" : "list-add"
|
||||
text: sortFilterRoomListModel.filterText.length > 0 ? i18n("Search in room directory") : i18n("Explore rooms")
|
||||
onTriggered: {
|
||||
let dialog = pageStack.layers.push("qrc:/org/kde/neochat/qml/JoinRoomPage.qml", {
|
||||
let dialog = pageStack.layers.push("qrc:/org/kde/neochat/qml/ExploreRoomsPage.qml", {
|
||||
connection: root.connection,
|
||||
keyword: sortFilterRoomListModel.filterText
|
||||
}, {
|
||||
|
||||
41
src/qml/RoomSearchPage.qml
Normal file
41
src/qml/RoomSearchPage.qml
Normal file
@@ -0,0 +1,41 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
/**
|
||||
* @brief Component for finding messages in a room.
|
||||
*
|
||||
* This component is based on a SearchPage and allows the user to enter a search
|
||||
* term into the input field and then search the room for messages with text that
|
||||
* matches the input.
|
||||
*
|
||||
* @sa SearchPage
|
||||
*/
|
||||
SearchPage {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* @brief The room the search is being performed in.
|
||||
*/
|
||||
required property NeoChatRoom room
|
||||
|
||||
title: i18nc("@action:title", "Search Messages")
|
||||
|
||||
model: SearchModel {
|
||||
id: searchModel
|
||||
room: root.room
|
||||
}
|
||||
|
||||
modelDelegate: EventDelegate {
|
||||
room: root.room
|
||||
}
|
||||
|
||||
searchFieldPlaceholder: i18n("Find messages…")
|
||||
noSearchPlaceholderMessage: i18n("Enter text to start searching")
|
||||
noResultPlaceholderMessage: i18n("No messages found")
|
||||
|
||||
listVerticalLayoutDirection: ListView.BottomToTop
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
@@ -7,29 +7,87 @@ import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
/**
|
||||
* @brief Component for a generic search page.
|
||||
*
|
||||
* This component provides a header with the search field and a ListView to visualise
|
||||
* search results from the given model.
|
||||
*/
|
||||
Kirigami.ScrollablePage {
|
||||
id: root
|
||||
|
||||
property NeoChatRoom currentRoom
|
||||
required property NeoChatConnection connection
|
||||
/**
|
||||
* @brief Any additional controls after the search button.
|
||||
*/
|
||||
property alias headerTrailing: headerContent.children
|
||||
|
||||
title: i18nc("@action:title", "Search Messages")
|
||||
/**
|
||||
* @brief The model that provides the search results.
|
||||
*
|
||||
* The model needs to provide the following properties:
|
||||
* - searchText
|
||||
* - searching
|
||||
* Where searchText is the text from the searchField and is used to match results
|
||||
* and searching is true while the model is finding results.
|
||||
*
|
||||
* The model must also provide a search() function to start the search if
|
||||
* it doesn't do so when the searchText is changed.
|
||||
*/
|
||||
property alias model: listView.model
|
||||
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||
/**
|
||||
* @brief The number of delegates currently in the view.
|
||||
*/
|
||||
property alias count: listView.count
|
||||
|
||||
SearchModel {
|
||||
id: searchModel
|
||||
connection: root.connection
|
||||
searchText: searchField.text
|
||||
room: root.currentRoom
|
||||
/**
|
||||
* @brief The delegate to use to visualize the model data.
|
||||
*/
|
||||
property alias modelDelegate: listView.delegate
|
||||
|
||||
/**
|
||||
* @brief The delegate to appear as the header of the list.
|
||||
*/
|
||||
property alias listHeaderDelegate: listView.header
|
||||
|
||||
/**
|
||||
* @brief The delegate to appear as the footer of the list.
|
||||
*/
|
||||
property alias listFooterDelegate: listView.footer
|
||||
|
||||
/**
|
||||
* @brief The placeholder text in the search field.
|
||||
*/
|
||||
property alias searchFieldPlaceholder: searchField.placeholderText
|
||||
|
||||
/**
|
||||
* @brief The text to show when no search term has been entered.
|
||||
*/
|
||||
property alias noSearchPlaceholderMessage: noSearchMessage.text
|
||||
|
||||
/**
|
||||
* @brief The text to show when no results have been found.
|
||||
*/
|
||||
property alias noResultPlaceholderMessage: noResultMessage.text
|
||||
|
||||
/**
|
||||
* @brief The verticalLayoutDirection property of the internal ListView.
|
||||
*/
|
||||
property alias listVerticalLayoutDirection: listView.verticalLayoutDirection
|
||||
|
||||
/**
|
||||
* @brief Force the search field to be focussed.
|
||||
*/
|
||||
function focusSearch() {
|
||||
searchField.forceActiveFocus();
|
||||
}
|
||||
|
||||
header: QQC2.Control {
|
||||
padding: Kirigami.Units.largeSpacing
|
||||
|
||||
background: Rectangle {
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||
Kirigami.Theme.inherit: false
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
|
||||
Kirigami.Separator {
|
||||
@@ -42,6 +100,7 @@ Kirigami.ScrollablePage {
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
id: headerContent
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
Kirigami.SearchField {
|
||||
@@ -50,44 +109,48 @@ Kirigami.ScrollablePage {
|
||||
Layout.fillWidth: true
|
||||
Keys.onEnterPressed: searchButton.clicked()
|
||||
Keys.onReturnPressed: searchButton.clicked()
|
||||
onTextChanged: {
|
||||
if (model) {
|
||||
model.searchText = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
QQC2.Button {
|
||||
id: searchButton
|
||||
onClicked: searchModel.search()
|
||||
icon.name: "search"
|
||||
onClicked: {
|
||||
if (typeof model.search === 'function') {
|
||||
model.search()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: messageListView
|
||||
id: listView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: 0
|
||||
verticalLayoutDirection: ListView.BottomToTop
|
||||
|
||||
section.property: "section"
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
id: noSearchMessage
|
||||
anchors.centerIn: parent
|
||||
visible: searchField.text.length === 0 && messageListView.count === 0
|
||||
text: i18n("Enter a text to start searching")
|
||||
visible: searchField.text.length === 0 && listView.count === 0
|
||||
}
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
id: noResultMessage
|
||||
anchors.centerIn: parent
|
||||
visible: searchField.text.length > 0 && messageListView.count === 0 && !searchModel.searching
|
||||
text: i18n("No results found")
|
||||
visible: searchField.text.length > 0 && listView.count === 0 && !root.model.searching
|
||||
}
|
||||
|
||||
Kirigami.LoadingPlaceholder {
|
||||
anchors.centerIn: parent
|
||||
visible: searchModel.searching
|
||||
}
|
||||
|
||||
model: searchModel
|
||||
delegate: EventDelegate {
|
||||
room: root.currentRoom
|
||||
visible: root.model.searching
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ Kirigami.Dialog {
|
||||
visible: !chosenRoomDelegate.visible
|
||||
text: i18nc("@action:button", "Pick room")
|
||||
onClicked: {
|
||||
let dialog = pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/JoinRoomPage.qml", {connection: root.room.connection, showOnlySpaces: true}, {title: i18nc("@title", "Choose Parent Space")})
|
||||
let dialog = pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/ExploreRoomsPage.qml", {connection: root.room.connection, showOnlySpaces: true}, {title: i18nc("@title", "Choose Parent Space")})
|
||||
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||
chosenRoomDelegate.roomId = roomId;
|
||||
chosenRoomDelegate.displayName = displayName;
|
||||
@@ -128,7 +128,7 @@ Kirigami.Dialog {
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
let dialog = pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/JoinRoomPage.qml", {connection: root.room.connection, showOnlySpaces: true}, {title: i18nc("@title", "Explore Rooms")})
|
||||
let dialog = pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/ExploreRoomsPage.qml", {connection: root.room.connection, showOnlySpaces: true}, {title: i18nc("@title", "Explore Rooms")})
|
||||
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||
chosenRoomDelegate.roomId = roomId;
|
||||
chosenRoomDelegate.displayName = displayName;
|
||||
|
||||
161
src/qml/ServerComboBox.qml
Normal file
161
src/qml/ServerComboBox.qml
Normal file
@@ -0,0 +1,161 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.delegates as Delegates
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
QQC2.ComboBox {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* @brief The connection for the current local user.
|
||||
*/
|
||||
required property NeoChatConnection connection
|
||||
|
||||
/**
|
||||
* @brief The server to get the search results from.
|
||||
*/
|
||||
property string server
|
||||
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 10
|
||||
Component.onCompleted: currentIndex = 0
|
||||
|
||||
textRole: "url"
|
||||
valueRole: "url"
|
||||
model: ServerListModel {
|
||||
id: serverListModel
|
||||
connection: root.connection
|
||||
}
|
||||
|
||||
delegate: Delegates.RoundedItemDelegate {
|
||||
id: serverItem
|
||||
|
||||
required property int index
|
||||
required property string url
|
||||
required property bool isAddServerDelegate
|
||||
required property bool isHomeServer
|
||||
required property bool isDeletable
|
||||
|
||||
text: isAddServerDelegate ? i18n("Add New Server") : url
|
||||
highlighted: false
|
||||
|
||||
topInset: index === 0 ? Kirigami.Units.smallSpacing : Math.round(Kirigami.Units.smallSpacing / 2)
|
||||
bottomInset: index === ListView.view.count - 1 ? Kirigami.Units.smallSpacing : Math.round(Kirigami.Units.smallSpacing / 2)
|
||||
|
||||
onClicked: if (isAddServerDelegate) {
|
||||
addServerSheet.open()
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Delegates.SubtitleContentItem {
|
||||
itemDelegate: serverItem
|
||||
subtitle: serverItem.isHomeServer ? i18n("Home Server") : ""
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
QQC2.ToolButton {
|
||||
visible: serverItem.isAddServerDelegate || serverItem.isDeletable
|
||||
icon.name: serverItem.isAddServerDelegate ? "list-add" : "dialog-close"
|
||||
text: i18nc("@action:button", "Add new server")
|
||||
Accessible.name: text
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: {
|
||||
if (root.currentIndex === serverItem.index && serverItem.isDeletable) {
|
||||
root.currentIndex = 0;
|
||||
root.server = root.currentValue
|
||||
root.popup.close();
|
||||
}
|
||||
if (serverItem.isAddServerDelegate) {
|
||||
addServerSheet.open();
|
||||
serverItem.clicked();
|
||||
} else {
|
||||
serverListModel.removeServerAtIndex(serverItem.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onActivated: {
|
||||
if (currentIndex !== count - 1) {
|
||||
root.server = root.currentValue
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.OverlaySheet {
|
||||
id: addServerSheet
|
||||
|
||||
parent: applicationWindow().overlay
|
||||
|
||||
title: i18nc("@title:window", "Add server")
|
||||
|
||||
onOpened: if (!serverUrlField.isValidServer && !addServerSheet.opened) {
|
||||
root.currentIndex = 0
|
||||
root.server = root.currentValue
|
||||
} else if (addServerSheet.opened) {
|
||||
serverUrlField.forceActiveFocus()
|
||||
}
|
||||
|
||||
onClosed: if (serverUrlField.length <= 0) {
|
||||
root.currentIndex = root.indexOfValue(root.server)
|
||||
}
|
||||
|
||||
contentItem: Kirigami.FormLayout {
|
||||
QQC2.Label {
|
||||
Layout.minimumWidth: Kirigami.Units.gridUnit * 20
|
||||
|
||||
text: serverUrlField.length > 0 ? (serverUrlField.acceptableInput ? (serverUrlField.isValidServer ? i18n("Valid server entered") : i18n("This server cannot be resolved or has already been added")) : i18n("The entered text is not a valid url")) : i18n("Enter server url e.g. kde.org")
|
||||
color: serverUrlField.length > 0 ? (serverUrlField.acceptableInput ? (serverUrlField.isValidServer ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.negativeTextColor) : Kirigami.Theme.negativeTextColor) : Kirigami.Theme.textColor
|
||||
}
|
||||
QQC2.TextField {
|
||||
id: serverUrlField
|
||||
|
||||
property bool isValidServer: false
|
||||
|
||||
Kirigami.FormData.label: i18n("Server URL")
|
||||
onTextChanged: {
|
||||
if(acceptableInput) {
|
||||
serverListModel.checkServer(text)
|
||||
}
|
||||
}
|
||||
|
||||
validator: RegularExpressionValidator {
|
||||
regularExpression: /^[a-zA-Z0-9-]{1,61}\.([a-zA-Z]{2,}|[a-zA-Z0-9-]{2,}\.[a-zA-Z]{2,3})$/
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: serverListModel
|
||||
function onServerCheckComplete(url, valid) {
|
||||
if (url == serverUrlField.text && valid) {
|
||||
serverUrlField.isValidServer = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Button {
|
||||
id: okButton
|
||||
|
||||
text: i18nc("@action:button", "Ok")
|
||||
enabled: serverUrlField.acceptableInput && serverUrlField.isValidServer
|
||||
onClicked: {
|
||||
serverListModel.addServer(serverUrlField.text)
|
||||
root.currentIndex = root.indexOfValue(serverUrlField.text)
|
||||
root.server = root.currentValue
|
||||
serverUrlField.text = ""
|
||||
addServerSheet.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user