Move to automatic type registration

This commit is contained in:
Nicolas Werner 2023-06-19 01:38:40 +02:00
parent 2cb04fd741
commit ce1a64bc19
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
73 changed files with 570 additions and 472 deletions

View file

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.13)
cmake_minimum_required(VERSION 3.13..3.21)
option(APPVEYOR_BUILD "Build on appveyor" OFF)
option(CI_BUILD "Set when building in CI. Enables -Werror where possible" OFF)
@ -395,8 +395,6 @@ set(SRC_FILES
src/ui/NhekoCursorShape.h
src/ui/NhekoDropArea.cpp
src/ui/NhekoDropArea.h
src/ui/NhekoEventObserver.cpp
src/ui/NhekoEventObserver.h
src/ui/NhekoGlobalObject.cpp
src/ui/NhekoGlobalObject.h
src/ui/RoomSettings.cpp
@ -499,8 +497,6 @@ set(SRC_FILES
src/SingleImagePackModel.h
src/TrayIcon.cpp
src/TrayIcon.h
src/UserDirectoryModel.cpp
src/UserDirectoryModel.h
src/UserSettingsPage.cpp
src/UserSettingsPage.h
src/UsersModel.cpp
@ -679,10 +675,10 @@ if(ASAN)
endif()
if(WIN32)
add_executable (nheko WIN32 ${OS_BUNDLE} ${NHEKO_DEPS})
qt_add_executable (nheko WIN32 ${OS_BUNDLE} ${NHEKO_DEPS})
target_compile_definitions(nheko PRIVATE _WIN32_WINNT=0x0601 NOMINMAX WIN32_LEAN_AND_MEAN STRICT)
else()
add_executable (nheko ${OS_BUNDLE} ${NHEKO_DEPS})
qt_add_executable (nheko ${OS_BUNDLE} ${NHEKO_DEPS})
if (HAVE_BACKTRACE_SYMBOLS_FD AND NOT CMAKE_BUILD_TYPE STREQUAL "Release")
set_target_properties(nheko PROPERTIES ENABLE_EXPORTS ON)
@ -700,6 +696,130 @@ set_target_properties(nheko
file(GLOB LANG_TS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/resources/langs/*.ts")
qt_add_translations(nheko RESOURCE_PREFIX "/translations" TS_FILES ${LANG_TS_SRC})
#
# Add qml files
#
set(QML_SOURCES
resources/qml/Root.qml
resources/qml/ChatPage.qml
resources/qml/CommunitiesList.qml
resources/qml/RoomList.qml
resources/qml/TimelineView.qml
resources/qml/Avatar.qml
resources/qml/Completer.qml
resources/qml/EncryptionIndicator.qml
resources/qml/ImageButton.qml
resources/qml/ElidedLabel.qml
resources/qml/MatrixText.qml
resources/qml/MatrixTextField.qml
resources/qml/ToggleButton.qml
resources/qml/UploadBox.qml
resources/qml/MessageInput.qml
resources/qml/MessageView.qml
resources/qml/PrivacyScreen.qml
resources/qml/Reactions.qml
resources/qml/ReplyPopup.qml
resources/qml/StatusIndicator.qml
resources/qml/TimelineRow.qml
resources/qml/TopBar.qml
resources/qml/QuickSwitcher.qml
resources/qml/ForwardCompleter.qml
resources/qml/SelfVerificationCheck.qml
resources/qml/TypingIndicator.qml
resources/qml/MessageInputWarning.qml
resources/qml/components/AdaptiveLayout.qml
resources/qml/components/AdaptiveLayoutElement.qml
resources/qml/components/AvatarListTile.qml
resources/qml/components/FlatButton.qml
resources/qml/components/MainWindowDialog.qml
resources/qml/components/NhekoTabButton.qml
resources/qml/components/NotificationBubble.qml
resources/qml/components/ReorderableListview.qml
resources/qml/components/SpaceMenuLevel.qml
resources/qml/components/TextButton.qml
resources/qml/components/UserListRow.qml
resources/qml/delegates/Encrypted.qml
resources/qml/delegates/FileMessage.qml
resources/qml/delegates/ImageMessage.qml
resources/qml/delegates/MessageDelegate.qml
resources/qml/delegates/NoticeMessage.qml
resources/qml/delegates/Pill.qml
resources/qml/delegates/Placeholder.qml
resources/qml/delegates/PlayableMediaMessage.qml
resources/qml/delegates/Redacted.qml
resources/qml/delegates/Reply.qml
resources/qml/delegates/TextMessage.qml
resources/qml/device-verification/DeviceVerification.qml
resources/qml/device-verification/DigitVerification.qml
resources/qml/device-verification/EmojiVerification.qml
resources/qml/device-verification/Failed.qml
resources/qml/device-verification/NewVerificationRequest.qml
resources/qml/device-verification/Success.qml
resources/qml/device-verification/Waiting.qml
resources/qml/dialogs/AliasEditor.qml
resources/qml/dialogs/ConfirmJoinRoomDialog.qml
resources/qml/dialogs/CreateDirect.qml
resources/qml/dialogs/CreateRoom.qml
resources/qml/dialogs/HiddenEventsDialog.qml
resources/qml/dialogs/ImageOverlay.qml
resources/qml/dialogs/ImagePackEditorDialog.qml
resources/qml/dialogs/ImagePackSettingsDialog.qml
resources/qml/dialogs/InputDialog.qml
resources/qml/dialogs/InviteDialog.qml
resources/qml/dialogs/JoinRoomDialog.qml
resources/qml/dialogs/LeaveRoomDialog.qml
resources/qml/dialogs/LogoutDialog.qml
resources/qml/dialogs/PhoneNumberInputDialog.qml
resources/qml/dialogs/PowerLevelEditor.qml
resources/qml/dialogs/PowerLevelSpacesApplyDialog.qml
resources/qml/dialogs/RawMessageDialog.qml
resources/qml/dialogs/ReadReceipts.qml
resources/qml/dialogs/RoomDirectory.qml
resources/qml/dialogs/RoomMembers.qml
resources/qml/dialogs/AllowedRoomsSettingsDialog.qml
resources/qml/dialogs/RoomSettings.qml
resources/qml/dialogs/UserProfile.qml
resources/qml/emoji/StickerPicker.qml
resources/qml/pages/LoginPage.qml
resources/qml/pages/RegisterPage.qml
resources/qml/pages/UserSettingsPage.qml
resources/qml/pages/WelcomePage.qml
resources/qml/ui/NhekoSlider.qml
resources/qml/ui/Ripple.qml
resources/qml/ui/Snackbar.qml
resources/qml/ui/Spinner.qml
resources/qml/ui/animations/BlinkAnimation.qml
resources/qml/ui/media/MediaControls.qml
resources/qml/voip/ActiveCallBar.qml
resources/qml/voip/CallDevices.qml
resources/qml/voip/CallInvite.qml
resources/qml/voip/CallInviteBar.qml
resources/qml/voip/DeviceError.qml
resources/qml/voip/PlaceCall.qml
resources/qml/voip/ScreenShare.qml
resources/qml/voip/VideoCall.qml
resources/qml/delegates/EncryptionEnabled.qml
resources/qml/ui/TimelineEffects.qml
)
qt_add_qml_module(nheko
URI im.nheko
NO_RESOURCE_TARGET_PATH
RESOURCE_PREFIX "/"
VERSION 1.1
DEPENDENCIES QtQml QtQuick # https://bugreports.qt.io/browse/QTBUG-102554
QML_FILES
${QML_SOURCES}
SOURCES
src/UserDirectoryModel.cpp
src/UserDirectoryModel.h
)
#qt_target_qml_sources(nheko
# #PREFIX "/"
#)
if(WIN32)
target_compile_definitions(nheko PRIVATE WIN32_LEAN_AND_MEAN)
if(MSVC)
@ -712,7 +832,7 @@ else()
endif()
endif()
target_include_directories(nheko PRIVATE src includes)
target_include_directories(nheko PRIVATE src includes src/timeline/ src/ui/ src/encryption/ src/voip/)
if (USE_BUNDLED_CPPHTTPLIB)
target_include_directories(nheko PRIVATE third_party/cpp-httplib-0.5.12)

View file

@ -102,7 +102,7 @@ AbstractButton {
target: Presence
}
}
CursorShape {
NhekoCursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}

View file

@ -31,7 +31,7 @@ AbstractButton {
sourceSize.height: button.height
sourceSize.width: button.width
}
CursorShape {
NhekoCursorShape {
id: mouseArea
anchors.fill: parent

View file

@ -44,7 +44,7 @@ TextArea {
onPressAndHold: (event) => event.accepted = false
onPressed: (event) => event.accepted = (event.button == Qt.LeftButton)
CursorShape {
NhekoCursorShape {
id: cs
anchors.fill: parent

View file

@ -332,7 +332,7 @@ Item {
sourceSize.height: button.height
sourceSize.width: button.width
}
CursorShape {
NhekoCursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
@ -590,7 +590,7 @@ Item {
elideWidth: userInfo.remainingWidth - Math.min(statusMsg.implicitWidth, userInfo.remainingWidth / 3)
text: userName
}
CursorShape {
NhekoCursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}

View file

@ -15,7 +15,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Window
import im.nheko
import im.nheko.EmojiModel
Pane {
id: timelineRoot
@ -92,15 +91,14 @@ Pane {
FontMetrics {
id: fontMetrics
}
RoomDirectoryModel {
id: publicRooms
}
UserDirectoryModel {
id: userDirectory
}
RoomDirectoryModel {
id: publicRooms
}
Component {
id: readReceiptsDialog

View file

@ -173,7 +173,7 @@ AbstractButton {
Layout.row: 0
blurhash: r.relatedEventCacheBuster, fromModel(Room.Blurhash) ?? ""
body: r.relatedEventCacheBuster, fromModel(Room.Body) ?? ""
callType: r.relatedEventCacheBuster, fromModel(Room.CallType) ?? ""
callType: r.relatedEventCacheBuster, fromModel(Room.Voip) ?? ""
duration: r.relatedEventCacheBuster, fromModel(Room.Duration) ?? 0
encryptionError: r.relatedEventCacheBuster, fromModel(Room.EncryptionError) ?? 0
eventId: fromModel(Room.EventId) ?? ""

View file

@ -8,14 +8,13 @@ import "./device-verification"
import "./emoji"
import "./ui"
import "./voip"
import Qt.labs.platform 1.1 as Platform
import QtQuick 2.15
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
import QtQuick.Particles 2.15
import QtQuick.Window 2.13
import im.nheko 1.0
import im.nheko.EmojiModel 1.0
import Qt.labs.platform as Platform
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Particles
import QtQuick.Window
import im.nheko
Item {
id: timelineView
@ -123,7 +122,7 @@ Item {
searchString: topBar.searchString
}
Loader {
source: CallManager.isOnCall && CallManager.callType != CallType.VOICE ? "voip/VideoCall.qml" : ""
source: CallManager.isOnCall && CallManager.callType != Voip.VOICE ? "voip/VideoCall.qml" : ""
onLoaded: TimelineManager.setVideoCallItem()
}
@ -249,7 +248,7 @@ Item {
onLinkActivated: Nheko.openLink(link)
CursorShape {
NhekoCursorShape {
anchors.fill: parent
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}

View file

@ -369,7 +369,7 @@ Pane {
onAccepted: topBar.searchString = text
}
}
CursorShape {
NhekoCursorShape {
anchors.bottomMargin: (pinnedMessages.visible ? pinnedMessages.height : 0) + (widgets.visible ? widgets.height : 0)
anchors.fill: parent
cursorShape: Qt.PointingHandCursor

View file

@ -87,7 +87,7 @@ Container {
x: parent.preferredWidth
z: 3
CursorShape {
NhekoCursorShape {
height: parent.height
width: container.splitterGrabMargin * 2
x: -container.splitterGrabMargin

View file

@ -32,7 +32,7 @@ AbstractButton {
horizontalAlignment: Text.AlignHCenter
}
CursorShape {
NhekoCursorShape {
id: mouseArea
anchors.fill: parent

View file

@ -49,7 +49,7 @@ Item {
gesturePolicy: TapHandler.ReleaseWithinBounds
}
CursorShape {
NhekoCursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}

View file

@ -43,7 +43,7 @@ AbstractButton {
implicitHeight: replyContainer.height
implicitWidth: visible? colorLine.width+Math.max(replyContainer.implicitWidth,userName_.fullTextWidth) : 0 // visible? seems to be causing issues
CursorShape {
NhekoCursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}

View file

@ -46,7 +46,7 @@ MatrixText {
enabled: !Settings.mobileMode
font.pointSize: (Settings.enlargeEmojiOnlyMessages && isOnlyEmoji > 0 && isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
CursorShape {
NhekoCursorShape {
enabled: isReply
anchors.fill: parent
cursorShape: Qt.PointingHandCursor

View file

@ -212,7 +212,7 @@ ApplicationWindow {
onClicked: invitees.removeUser(model.mxid)
}
CursorShape {
NhekoCursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}

View file

@ -110,7 +110,7 @@ ApplicationWindow {
}
CursorShape {
NhekoCursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}

View file

@ -220,7 +220,7 @@ ApplicationWindow {
}
CursorShape {
NhekoCursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}

View file

@ -213,7 +213,7 @@ ApplicationWindow {
horizontalAlignment: TextEdit.AlignHCenter
onLinkActivated: Nheko.openLink(link)
CursorShape {
NhekoCursorShape {
anchors.fill: parent
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}

View file

@ -7,7 +7,6 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import im.nheko
import im.nheko.EmojiModel
Menu {
id: stickerPopup

View file

@ -1,2 +0,0 @@
module im.nheko.UI.Animations
BlinkAnimation 1.0 BlinkAnimation.qml

View file

@ -1,3 +0,0 @@
module im.nheko.UI.Media
VolumeSlider 1.0 VolumeSlider.qml
MediaControls 1.0 MediaControls.qml

View file

@ -1,4 +0,0 @@
module im.nheko.UI
NhekoSlider 1.0 NhekoSlider.qml
Ripple 1.0 Ripple.qml
Spinner 1.0 Spinner.qml

View file

@ -16,7 +16,7 @@ Rectangle {
MouseArea {
anchors.fill: parent
onClicked: {
if (CallManager.callType != CallType.VOICE)
if (CallManager.callType != Voip.VOICE)
stackLayout.currentIndex = stackLayout.currentIndex ? 0 : 1;
}
@ -58,7 +58,7 @@ Rectangle {
states: [
State {
name: "VOICE"
when: CallManager.callType == CallType.VOICE
when: CallManager.callType == Voip.VOICE
PropertyChanges {
target: callTypeIcon
@ -68,7 +68,7 @@ Rectangle {
},
State {
name: "VIDEO"
when: CallManager.callType == CallType.VIDEO
when: CallManager.callType == Voip.VIDEO
PropertyChanges {
target: callTypeIcon
@ -78,7 +78,7 @@ Rectangle {
},
State {
name: "SCREEN"
when: CallManager.callType == CallType.SCREEN
when: CallManager.callType == Voip.SCREEN
PropertyChanges {
target: callTypeIcon
@ -100,7 +100,7 @@ Rectangle {
states: [
State {
name: "OFFERSENT"
when: CallManager.callState == WebRTCState.OFFERSENT
when: CallManager.callState == Voip.OFFERSENT
PropertyChanges {
target: callStateLabel
@ -110,7 +110,7 @@ Rectangle {
},
State {
name: "CONNECTING"
when: CallManager.callState == WebRTCState.CONNECTING
when: CallManager.callState == Voip.CONNECTING
PropertyChanges {
target: callStateLabel
@ -120,7 +120,7 @@ Rectangle {
},
State {
name: "ANSWERSENT"
when: CallManager.callState == WebRTCState.ANSWERSENT
when: CallManager.callState == Voip.ANSWERSENT
PropertyChanges {
target: callStateLabel
@ -130,7 +130,7 @@ Rectangle {
},
State {
name: "CONNECTED"
when: CallManager.callState == WebRTCState.CONNECTED
when: CallManager.callState == Voip.CONNECTED
PropertyChanges {
target: callStateLabel
@ -144,13 +144,13 @@ Rectangle {
PropertyChanges {
target: stackLayout
currentIndex: CallManager.callType != CallType.VOICE ? 1 : 0
currentIndex: CallManager.callType != Voip.VOICE ? 1 : 0
}
},
State {
name: "DISCONNECTED"
when: CallManager.callState == WebRTCState.DISCONNECTED
when: CallManager.callState == Voip.DISCONNECTED
PropertyChanges {
target: callStateLabel
@ -176,7 +176,7 @@ Rectangle {
}
interval: 1000
running: CallManager.callState == WebRTCState.CONNECTED
running: CallManager.callState == Voip.CONNECTED
repeat: true
onTriggered: {
var d = new Date();
@ -190,7 +190,7 @@ Rectangle {
Label {
Layout.leftMargin: 16
visible: CallManager.callType == CallType.SCREEN && CallManager.callState == WebRTCState.CONNECTED
visible: CallManager.callType == Voip.SCREEN && CallManager.callState == Voip.CONNECTED
text: qsTr("You are screen sharing")
font.pointSize: fontMetrics.font.pointSize * 1.1
color: "#000000"

View file

@ -43,7 +43,7 @@ Popup {
}
RowLayout {
visible: CallManager.callType == CallType.VIDEO && CallManager.cameras.length > 0
visible: CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0
Image {
Layout.preferredWidth: 22

View file

@ -62,7 +62,7 @@ Popup {
Layout.bottomMargin: callInv.height / 25
Image {
property string image: CallManager.callType == CallType.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
property string image: CallManager.callType == Voip.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: callInv.height / 10
@ -72,7 +72,7 @@ Popup {
Label {
Layout.alignment: Qt.AlignCenter
text: CallManager.callType == CallType.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
text: CallManager.callType == Voip.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
font.pointSize: fontMetrics.font.pointSize * 2
color: palette.windowText
}
@ -106,7 +106,7 @@ Popup {
}
RowLayout {
visible: CallManager.callType == CallType.VIDEO && CallManager.cameras.length > 0
visible: CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0
Layout.alignment: Qt.AlignCenter
Image {
@ -169,7 +169,7 @@ Popup {
RoundButton {
id: acceptButton
property string image: CallManager.callType == CallType.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
property string image: CallManager.callType == Voip.VIDEO ? ":/icons/icons/ui/video.svg" : ":/icons/icons/ui/place-call.svg"
implicitWidth: buttonLayout.buttonSize
implicitHeight: buttonLayout.buttonSize

View file

@ -57,12 +57,12 @@ Rectangle {
Layout.leftMargin: 4
Layout.preferredWidth: 24
Layout.preferredHeight: 24
source: CallManager.callType == CallType.VIDEO ? "qrc:/icons/icons/ui/video.svg" : "qrc:/icons/icons/ui/place-call.svg"
source: CallManager.callType == Voip.VIDEO ? "qrc:/icons/icons/ui/video.svg" : "qrc:/icons/icons/ui/place-call.svg"
}
Label {
font.pointSize: fontMetrics.font.pointSize * 1.1
text: CallManager.callType == CallType.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
text: CallManager.callType == Voip.VIDEO ? qsTr("Video Call") : qsTr("Voice Call")
color: "#000000"
}
@ -88,7 +88,7 @@ Rectangle {
Button {
Layout.rightMargin: 4
icon.source: CallManager.callType == CallType.VIDEO ? "qrc:/icons/icons/ui/video.svg" : "qrc:/icons/icons/ui/place-call.svg"
icon.source: CallManager.callType == Voip.VIDEO ? "qrc:/icons/icons/ui/video.svg" : "qrc:/icons/icons/ui/place-call.svg"
text: qsTr("Accept")
onClicked: {
if (CallManager.mics.length == 0) {
@ -108,7 +108,7 @@ Rectangle {
timelineRoot.destroyOnClose(dialog);
return ;
}
if (CallManager.callType == CallType.VIDEO && CallManager.cameras.length > 0 && !CallManager.cameras.includes(Settings.camera)) {
if (CallManager.callType == Voip.VIDEO && CallManager.cameras.length > 0 && !CallManager.cameras.includes(Settings.camera)) {
var dialog = deviceError.createObject(timelineRoot, {
"errorString": qsTr("Unknown camera: %1").arg(Settings.camera),
"image": ":/icons/icons/ui/video.svg"

View file

@ -81,7 +81,7 @@ Popup {
onClicked: {
if (buttonLayout.validateMic()) {
Settings.microphone = micCombo.currentText;
CallManager.sendInvite(room.roomId, CallType.VOICE);
CallManager.sendInvite(room.roomId, Voip.VOICE);
close();
}
}
@ -95,7 +95,7 @@ Popup {
if (buttonLayout.validateMic()) {
Settings.microphone = micCombo.currentText;
Settings.camera = cameraCombo.currentText;
CallManager.sendInvite(room.roomId, CallType.VIDEO);
CallManager.sendInvite(room.roomId, Voip.VIDEO);
close();
}
}

View file

@ -47,7 +47,7 @@ Popup {
Layout.fillWidth: true
model: CallManager.screenShareTypeList()
onCurrentIndexChanged: CallManager.setScreenShareType(currentIndex);
onCurrentIndexChanged: CallManager.setVoip(currentIndex);
}
}
@ -63,7 +63,7 @@ Popup {
}
ComboBox {
visible: CallManager.screenShareType == ScreenShareType.X11
visible: CallManager.screenShareType == Voip.X11
id: windowCombo
Layout.fillWidth: true
@ -71,7 +71,7 @@ Popup {
}
Button {
visible: CallManager.screenShareType == ScreenShareType.XDP
visible: CallManager.screenShareType == Voip.XDP
highlighted: !CallManager.screenShareReady
text: qsTr("Request screencast")
onClicked: {
@ -165,7 +165,7 @@ Popup {
Settings.screenShareRemoteVideo = remoteVideoCheckBox.checked;
Settings.screenShareHideCursor = hideCursorCheckBox.checked;
CallManager.sendInvite(room.roomId, CallType.SCREEN, windowCombo.currentIndex);
CallManager.sendInvite(room.roomId, Voip.SCREEN, windowCombo.currentIndex);
close();
}
}

View file

@ -92,107 +92,7 @@
</qresource>
<qresource prefix="/">
<file>qtquickcontrols2.conf</file>
<file>qml/Root.qml</file>
<file>qml/ChatPage.qml</file>
<file>qml/CommunitiesList.qml</file>
<file>qml/RoomList.qml</file>
<file>qml/TimelineView.qml</file>
<file>qml/Avatar.qml</file>
<file>qml/Completer.qml</file>
<file>qml/EncryptionIndicator.qml</file>
<file>qml/ImageButton.qml</file>
<file>qml/ElidedLabel.qml</file>
<file>qml/MatrixText.qml</file>
<file>qml/MatrixTextField.qml</file>
<file>qml/ToggleButton.qml</file>
<file>qml/UploadBox.qml</file>
<file>qml/MessageInput.qml</file>
<file>qml/MessageView.qml</file>
<file>qml/PrivacyScreen.qml</file>
<file>qml/Reactions.qml</file>
<file>qml/ReplyPopup.qml</file>
<file>qml/StatusIndicator.qml</file>
<file>qml/TimelineRow.qml</file>
<file>qml/TopBar.qml</file>
<file>qml/QuickSwitcher.qml</file>
<file>qml/ForwardCompleter.qml</file>
<file>qml/SelfVerificationCheck.qml</file>
<file>qml/TypingIndicator.qml</file>
<file>qml/MessageInputWarning.qml</file>
<file>qml/components/AdaptiveLayout.qml</file>
<file>qml/components/AdaptiveLayoutElement.qml</file>
<file>qml/components/AvatarListTile.qml</file>
<file>qml/components/FlatButton.qml</file>
<file>qml/components/MainWindowDialog.qml</file>
<file>qml/components/NhekoTabButton.qml</file>
<file>qml/components/NotificationBubble.qml</file>
<file>qml/components/ReorderableListview.qml</file>
<file>qml/components/SpaceMenuLevel.qml</file>
<file>qml/components/TextButton.qml</file>
<file>qml/components/UserListRow.qml</file>
<file>qml/delegates/Encrypted.qml</file>
<file>qml/delegates/FileMessage.qml</file>
<file>qml/delegates/ImageMessage.qml</file>
<file>qml/delegates/MessageDelegate.qml</file>
<file>qml/delegates/NoticeMessage.qml</file>
<file>qml/delegates/Pill.qml</file>
<file>qml/delegates/Placeholder.qml</file>
<file>qml/delegates/PlayableMediaMessage.qml</file>
<file>qml/delegates/Redacted.qml</file>
<file>qml/delegates/Reply.qml</file>
<file>qml/delegates/TextMessage.qml</file>
<file>qml/device-verification/DeviceVerification.qml</file>
<file>qml/device-verification/DigitVerification.qml</file>
<file>qml/device-verification/EmojiVerification.qml</file>
<file>qml/device-verification/Failed.qml</file>
<file>qml/device-verification/NewVerificationRequest.qml</file>
<file>qml/device-verification/Success.qml</file>
<file>qml/device-verification/Waiting.qml</file>
<file>qml/dialogs/AliasEditor.qml</file>
<file>qml/dialogs/ConfirmJoinRoomDialog.qml</file>
<file>qml/dialogs/CreateDirect.qml</file>
<file>qml/dialogs/CreateRoom.qml</file>
<file>qml/dialogs/HiddenEventsDialog.qml</file>
<file>qml/dialogs/ImageOverlay.qml</file>
<file>qml/dialogs/ImagePackEditorDialog.qml</file>
<file>qml/dialogs/ImagePackSettingsDialog.qml</file>
<file>qml/dialogs/InputDialog.qml</file>
<file>qml/dialogs/InviteDialog.qml</file>
<file>qml/dialogs/JoinRoomDialog.qml</file>
<file>qml/dialogs/LeaveRoomDialog.qml</file>
<file>qml/dialogs/LogoutDialog.qml</file>
<file>qml/dialogs/PhoneNumberInputDialog.qml</file>
<file>qml/dialogs/PowerLevelEditor.qml</file>
<file>qml/dialogs/PowerLevelSpacesApplyDialog.qml</file>
<file>qml/dialogs/RawMessageDialog.qml</file>
<file>qml/dialogs/ReadReceipts.qml</file>
<file>qml/dialogs/RoomDirectory.qml</file>
<file>qml/dialogs/RoomMembers.qml</file>
<file>qml/dialogs/AllowedRoomsSettingsDialog.qml</file>
<file>qml/dialogs/RoomSettings.qml</file>
<file>qml/dialogs/UserProfile.qml</file>
<file>qml/emoji/StickerPicker.qml</file>
<file>qml/pages/LoginPage.qml</file>
<file>qml/pages/RegisterPage.qml</file>
<file>qml/pages/UserSettingsPage.qml</file>
<file>qml/pages/WelcomePage.qml</file>
<file>qml/ui/NhekoSlider.qml</file>
<file>qml/ui/Ripple.qml</file>
<file>qml/ui/Snackbar.qml</file>
<file>qml/ui/Spinner.qml</file>
<file>qml/ui/animations/BlinkAnimation.qml</file>
<file>qml/ui/media/MediaControls.qml</file>
<file>qml/voip/ActiveCallBar.qml</file>
<file>qml/voip/CallDevices.qml</file>
<file>qml/voip/CallInvite.qml</file>
<file>qml/voip/CallInviteBar.qml</file>
<file>qml/voip/DeviceError.qml</file>
<file>qml/voip/PlaceCall.qml</file>
<file>qml/voip/ScreenShare.qml</file>
<file>qml/voip/VideoCall.qml</file>
<file>confettiparticle.svg</file>
<file>qml/delegates/EncryptionEnabled.qml</file>
<file>qml/ui/TimelineEffects.qml</file>
</qresource>
<qresource prefix="/media">
<file>media/ring.ogg</file>

View file

@ -5,6 +5,7 @@
#pragma once
#include <QAbstractListModel>
#include <QQmlEngine>
#include <QVector>
#include <mtx/events/canonical_alias.hpp>
@ -29,6 +30,9 @@ signals:
class AliasEditingModel final : public QAbstractListModel
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("Please use editAliases to create the models")
Q_PROPERTY(bool canAdvertize READ canAdvertize CONSTANT)
public:

View file

@ -5,6 +5,7 @@
#pragma once
#include <QObject>
#include <QQmlEngine>
#include <map>
#include <mutex>
@ -16,6 +17,8 @@
namespace crypto {
Q_NAMESPACE
QML_NAMED_ELEMENT(Crypto)
//! How much a participant is trusted.
enum Trust
{

View file

@ -5,11 +5,15 @@
#pragma once
#include <QObject>
#include <QQmlEngine>
#include <QString>
class Clipboard final : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
public:

View file

@ -5,13 +5,10 @@
#pragma once
#include <QObject>
#include <QQmlEngine>
#include <QVariantList>
namespace mtx {
namespace responses {
struct Login;
}
}
#include <mtx/responses/login.hpp>
struct SSOProvider
{
@ -33,6 +30,7 @@ public:
class LoginPage : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString mxid READ mxid WRITE setMxid NOTIFY matrixIdChanged)
Q_PROPERTY(QString homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged)

View file

@ -25,21 +25,15 @@
#include "InviteesModel.h"
#include "JdenticonProvider.h"
#include "Logging.h"
#include "LoginPage.h"
#include "MainWindow.h"
#include "MatrixClient.h"
#include "MemberList.h"
#include "MxcImageProvider.h"
#include "PowerlevelsEditModels.h"
#include "ReadReceiptsModel.h"
#include "RegisterPage.h"
#include "RoomDirectoryModel.h"
#include "RoomsModel.h"
#include "SingleImagePackModel.h"
#include "TrayIcon.h"
#include "UserDirectoryModel.h"
#include "UserSettingsPage.h"
#include "UsersModel.h"
#include "Utils.h"
#include "dock/Dock.h"
#include "emoji/Provider.h"
@ -48,12 +42,6 @@
#include "timeline/DelegateChooser.h"
#include "timeline/TimelineFilter.h"
#include "timeline/TimelineViewManager.h"
#include "ui/HiddenEvents.h"
#include "ui/MxcAnimatedImage.h"
#include "ui/MxcMediaProxy.h"
#include "ui/NhekoCursorShape.h"
#include "ui/NhekoDropArea.h"
#include "ui/NhekoEventObserver.h"
#include "ui/NhekoGlobalObject.h"
#include "ui/RoomSummary.h"
#include "ui/UIA.h"
@ -83,7 +71,7 @@ MainWindow::MainWindow(QWindow *parent)
registerQmlTypes();
setColor(Theme::paletteFromTheme(userSettings_->theme()).window().color());
setSource(QUrl(QStringLiteral("qrc:///qml/Root.qml")));
setSource(QUrl(QStringLiteral("qrc:///resources/qml/Root.qml")));
trayIcon_ = new TrayIcon(QStringLiteral(":/logos/nheko.svg"), this);
@ -132,156 +120,57 @@ MainWindow::MainWindow(QWindow *parent)
void
MainWindow::registerQmlTypes()
{
qmlRegisterUncreatableMetaObject(qml_mtx_events::staticMetaObject,
"im.nheko",
1,
0,
"MtxEvent",
QStringLiteral("Can't instantiate enum!"));
qmlRegisterUncreatableMetaObject(
olm::staticMetaObject, "im.nheko", 1, 0, "Olm", QStringLiteral("Can't instantiate enum!"));
qmlRegisterUncreatableMetaObject(crypto::staticMetaObject,
"im.nheko",
1,
0,
"Crypto",
QStringLiteral("Can't instantiate enum!"));
qmlRegisterUncreatableMetaObject(verification::staticMetaObject,
"im.nheko",
1,
0,
"VerificationStatus",
QStringLiteral("Can't instantiate enum!"));
// qmlRegisterUncreatableType<DeviceVerificationFlow>(
// "im.nheko",
// 1,
// 0,
// "DeviceVerificationFlow",
// QStringLiteral("Can't create verification flow from QML!"));
// qmlRegisterUncreatableType<UserProfile>(
// "im.nheko",
// 1,
// 0,
// "UserProfileModel",
// QStringLiteral("UserProfile needs to be instantiated on the C++ side"));
// qmlRegisterUncreatableType<MemberList>(
// "im.nheko",
// 1,
// 0,
// "MemberList",
// QStringLiteral("MemberList needs to be instantiated on the C++ side"));
// qmlRegisterUncreatableType<RoomSettings>(
// "im.nheko",
// 1,
// 0,
// "RoomSettingsModel",
// QStringLiteral("Room Settings needs to be instantiated on the C++ side"));
// qmlRegisterUncreatableType<TimelineModel>(
// "im.nheko", 1, 0, "Room", QStringLiteral("Room needs to be instantiated on the C++ side"));
// qmlRegisterUncreatableType<ImagePackListModel>(
// "im.nheko",
// 1,
// 0,
// "ImagePackListModel",
// QStringLiteral("ImagePackListModel needs to be instantiated on the C++ side"));
// qmlRegisterUncreatableType<SingleImagePackModel>(
// "im.nheko",
// 1,
// 0,
// "SingleImagePackModel",
// QStringLiteral("SingleImagePackModel needs to be instantiated on the C++ side"));
// qmlRegisterUncreatableType<InviteesModel>(
// "im.nheko",
// 1,
// 0,
// "InviteesModel",
// QStringLiteral("InviteesModel needs to be instantiated on the C++ side"));
qmlRegisterType<DelegateChoice>("im.nheko", 1, 0, "DelegateChoice");
qmlRegisterType<DelegateChooser>("im.nheko", 1, 0, "DelegateChooser");
qmlRegisterType<NhekoDropArea>("im.nheko", 1, 0, "NhekoDropArea");
qmlRegisterType<NhekoCursorShape>("im.nheko", 1, 0, "CursorShape");
qmlRegisterType<NhekoEventObserver>("im.nheko", 1, 0, "EventObserver");
qmlRegisterType<MxcAnimatedImage>("im.nheko", 1, 0, "MxcAnimatedImage");
qmlRegisterType<MxcMediaProxy>("im.nheko", 1, 0, "MxcMedia");
qmlRegisterType<RoomDirectoryModel>("im.nheko", 1, 0, "RoomDirectoryModel");
qmlRegisterType<UserDirectoryModel>("im.nheko", 1, 0, "UserDirectoryModel");
qmlRegisterType<LoginPage>("im.nheko", 1, 0, "Login");
qmlRegisterType<RegisterPage>("im.nheko", 1, 0, "Registration");
qmlRegisterType<HiddenEvents>("im.nheko", 1, 0, "HiddenEvents");
qmlRegisterType<TimelineFilter>("im.nheko", 1, 0, "TimelineFilter");
qmlRegisterUncreatableType<RoomSummary>(
"im.nheko",
1,
0,
"RoomSummary",
QStringLiteral("Please use joinRoom to create a room summary."));
qmlRegisterUncreatableType<AliasEditingModel>(
"im.nheko",
1,
0,
"AliasEditingModel",
QStringLiteral("Please use editAliases to create the models"));
qmlRegisterUncreatableType<PowerlevelEditingModels>(
"im.nheko",
1,
0,
"PowerlevelEditingModels",
QStringLiteral("Please use editPowerlevels to create the models"));
qmlRegisterUncreatableType<DeviceVerificationFlow>(
"im.nheko",
1,
0,
"DeviceVerificationFlow",
QStringLiteral("Can't create verification flow from QML!"));
qmlRegisterUncreatableType<UserProfile>(
"im.nheko",
1,
0,
"UserProfileModel",
QStringLiteral("UserProfile needs to be instantiated on the C++ side"));
qmlRegisterUncreatableType<MemberList>(
"im.nheko",
1,
0,
"MemberList",
QStringLiteral("MemberList needs to be instantiated on the C++ side"));
qmlRegisterUncreatableType<RoomSettings>(
"im.nheko",
1,
0,
"RoomSettingsModel",
QStringLiteral("Room Settings needs to be instantiated on the C++ side"));
qmlRegisterUncreatableType<TimelineModel>(
"im.nheko", 1, 0, "Room", QStringLiteral("Room needs to be instantiated on the C++ side"));
qmlRegisterUncreatableType<ImagePackListModel>(
"im.nheko",
1,
0,
"ImagePackListModel",
QStringLiteral("ImagePackListModel needs to be instantiated on the C++ side"));
qmlRegisterUncreatableType<SingleImagePackModel>(
"im.nheko",
1,
0,
"SingleImagePackModel",
QStringLiteral("SingleImagePackModel needs to be instantiated on the C++ side"));
qmlRegisterUncreatableType<InviteesModel>(
"im.nheko",
1,
0,
"InviteesModel",
QStringLiteral("InviteesModel needs to be instantiated on the C++ side"));
qmlRegisterUncreatableType<ReadReceiptsProxy>(
"im.nheko",
1,
0,
"ReadReceiptsProxy",
QStringLiteral("ReadReceiptsProxy needs to be instantiated on the C++ side"));
qmlRegisterSingletonType<Clipboard>(
"im.nheko", 1, 0, "Clipboard", [](QQmlEngine *, QJSEngine *) -> QObject * {
return new Clipboard();
});
qmlRegisterSingletonType<Nheko>(
"im.nheko", 1, 0, "Nheko", [](QQmlEngine *, QJSEngine *) -> QObject * {
return new Nheko();
});
qmlRegisterSingletonType<UserSettingsModel>(
"im.nheko", 1, 0, "UserSettingsModel", [](QQmlEngine *, QJSEngine *) -> QObject * {
return new UserSettingsModel();
});
qmlRegisterSingletonInstance("im.nheko", 1, 0, "Settings", userSettings_.data());
qmlRegisterUncreatableType<FilteredCommunitiesModel>(
"im.nheko",
1,
0,
"FilteredCommunitiesModel",
QStringLiteral("Use Communities.filtered() to create a FilteredCommunitiesModel"));
qmlRegisterUncreatableType<MediaUpload>(
"im.nheko", 1, 0, "MediaUpload", QStringLiteral("MediaUploads can not be created in Qml"));
qmlRegisterUncreatableMetaObject(emoji::staticMetaObject,
"im.nheko.EmojiModel",
1,
0,
"EmojiCategory",
QStringLiteral("Error: Only enums"));
qmlRegisterType<RoomDirectoryModel>("im.nheko", 1, 0, "RoomDirectoryModel");
qmlRegisterSingletonType<SelfVerificationStatus>(
"im.nheko", 1, 0, "SelfVerificationStatus", [](QQmlEngine *, QJSEngine *) -> QObject * {
auto ptr = new SelfVerificationStatus();
QObject::connect(ChatPage::instance(),
&ChatPage::initializeEmptyViews,
ptr,
&SelfVerificationStatus::invalidate);
return ptr;
});
qmlRegisterSingletonInstance("im.nheko", 1, 0, "MainWindow", this);
qmlRegisterSingletonInstance("im.nheko", 1, 0, "UIA", UIA::instance());
qmlRegisterSingletonInstance(
"im.nheko", 1, 0, "CallManager", ChatPage::instance()->callManager());
// qmlRegisterUncreatableMetaObject(emoji::staticMetaObject,
// "im.nheko.EmojiModel",
// 1,
// 0,
// "EmojiCategory",
// QStringLiteral("Error: Only enums"));
imgProvider = new MxcImageProvider();
engine()->addImageProvider(QStringLiteral("MxcImage"), imgProvider);

View file

@ -50,14 +50,35 @@ public:
bool eventFilter(QObject *obj, QEvent *event) override;
};
class MainWindow final : public QQuickView
class MainWindow : public QQuickView
{
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
explicit MainWindow(QWindow *parent = nullptr);
explicit MainWindow(QWindow *parent);
static MainWindow *instance() { return instance_; }
static MainWindow *create(QQmlEngine *qmlEngine, QJSEngine *)
{
// The instance has to exist before it is used. We cannot replace it.
Q_ASSERT(instance_);
// The engine has to have the same thread affinity as the singleton.
Q_ASSERT(qmlEngine->thread() == instance_->thread());
// There can only be one engine accessing the singleton.
static QJSEngine *s_engine = nullptr;
if (s_engine)
Q_ASSERT(qmlEngine == s_engine);
else
s_engine = qmlEngine;
QJSEngine::setObjectOwnership(instance_, QJSEngine::CppOwnership);
return instance_;
}
void saveCurrentWindowSize();
void openJoinRoomDialog(std::function<void(const QString &room_id)> callback);

View file

@ -5,6 +5,7 @@
#pragma once
#include <QAbstractListModel>
#include <QQmlEngine>
#include <QSortFilterProxyModel>
#include <mtx/events/power_levels.hpp>
@ -196,6 +197,9 @@ class PowerlevelEditingModels final : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("Please use editPowerlevels to create the models")
Q_PROPERTY(PowerlevelsUserListModel *users READ users CONSTANT)
Q_PROPERTY(PowerlevelsTypeListModel *types READ types CONSTANT)
Q_PROPERTY(PowerlevelsSpacesListModel *spaces READ spaces CONSTANT)

View file

@ -8,6 +8,7 @@
#include <QAbstractListModel>
#include <QDateTime>
#include <QObject>
#include <QQmlEngine>
#include <QSortFilterProxyModel>
#include <QString>
@ -54,6 +55,9 @@ class ReadReceiptsProxy final : public QSortFilterProxyModel
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
Q_PROPERTY(QString eventId READ eventId CONSTANT)
Q_PROPERTY(QString roomId READ roomId CONSTANT)

View file

@ -5,6 +5,7 @@
#pragma once
#include <QObject>
#include <QQmlEngine>
#include <QString>
#include <mtx/user_interactive.hpp>
@ -13,6 +14,7 @@
class RegisterPage : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString error READ error NOTIFY errorChanged)
Q_PROPERTY(QString hsError READ hsError NOTIFY hsErrorChanged)

View file

@ -5,6 +5,7 @@
#pragma once
#include <QAbstractListModel>
#include <QQmlEngine>
#include <QString>
#include <string>
#include <vector>
@ -32,6 +33,7 @@ signals:
class RoomDirectoryModel : public QAbstractListModel
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(bool loadingMoreRooms READ loadingMoreRooms NOTIFY loadingMoreRoomsChanged)
Q_PROPERTY(

View file

@ -5,6 +5,7 @@
#pragma once
#include <QAbstractListModel>
#include <QQmlEngine>
#include <QString>
#include <string>
#include <vector>
@ -26,6 +27,7 @@ signals:
class UserDirectoryModel : public QAbstractListModel
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(bool searchingUsers READ searchingUsers NOTIFY searchingUsersChanged)

View file

@ -6,6 +6,7 @@
#include <QAbstractListModel>
#include <QProcessEnvironment>
#include <QQmlEngine>
#include <QSettings>
#include <QSharedPointer>
@ -23,6 +24,8 @@ class QVBoxLayout;
class UserSettings final : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(Settings)
QML_SINGLETON
Q_PROPERTY(QString theme READ theme WRITE setTheme NOTIFY themeChanged)
Q_PROPERTY(bool messageHoverHighlight READ messageHoverHighlight WRITE setMessageHoverHighlight
@ -131,6 +134,24 @@ class UserSettings final : public QObject
public:
static QSharedPointer<UserSettings> instance();
static void initialize(std::optional<QString> profile);
static UserSettings *create(QQmlEngine *qmlEngine, QJSEngine *)
{
// The instance has to exist before it is used. We cannot replace it.
Q_ASSERT(instance());
// The engine has to have the same thread affinity as the singleton.
Q_ASSERT(qmlEngine->thread() == instance()->thread());
// There can only be one engine accessing the singleton.
static QJSEngine *s_engine = nullptr;
if (s_engine)
Q_ASSERT(qmlEngine == s_engine);
else
s_engine = qmlEngine;
QJSEngine::setObjectOwnership(instance().get(), QJSEngine::CppOwnership);
return instance().get();
}
QSettings *qsettings() { return &settings; }
@ -431,9 +452,10 @@ private:
static QSharedPointer<UserSettings> instance_;
};
class UserSettingsModel final : public QAbstractListModel
class UserSettingsModel : public QAbstractListModel
{
Q_OBJECT
QML_ELEMENT
enum Indices
{

View file

@ -9,10 +9,13 @@
#include <mtx/events/encrypted.hpp>
#include <mtxclient/crypto/client.hpp>
#include <QQmlEngine>
#include <CacheCryptoStructs.h>
namespace olm {
Q_NAMESPACE
QML_NAMED_ELEMENT(Olm)
enum DecryptionErrorCode
{

View file

@ -29,6 +29,11 @@ SelfVerificationStatus::SelfVerificationStatus(QObject *o)
Qt::UniqueConnection);
cache::client()->markUserKeysOutOfDate({http::client()->user_id().to_string()});
});
connect(ChatPage::instance(),
&ChatPage::initializeEmptyViews,
this,
&SelfVerificationStatus::invalidate);
}
void

View file

@ -5,11 +5,15 @@
#pragma once
#include <QObject>
#include <QQmlEngine>
class SelfVerificationStatus final : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
Q_PROPERTY(bool hasSSSS READ hasSSSS NOTIFY hasSSSSChanged)

View file

@ -15,6 +15,7 @@ VerificationManager::VerificationManager(TimelineViewManager *o)
: QObject(o)
, rooms_(o->rooms())
{
instance_ = this;
}
static bool

View file

@ -6,6 +6,7 @@
#include <QHash>
#include <QObject>
#include <QQmlEngine>
#include <QSharedPointer>
#include <mtx/events.hpp>
@ -21,8 +22,30 @@ class VerificationManager final : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
VerificationManager(TimelineViewManager *o = nullptr);
VerificationManager(TimelineViewManager *o);
static VerificationManager *create(QQmlEngine *qmlEngine, QJSEngine *)
{
// The instance has to exist before it is used. We cannot replace it.
Q_ASSERT(instance_);
// The engine has to have the same thread affinity as the singleton.
Q_ASSERT(qmlEngine->thread() == instance_->thread());
// There can only be one engine accessing the singleton.
static QJSEngine *s_engine = nullptr;
if (s_engine)
Q_ASSERT(qmlEngine == s_engine);
else
s_engine = qmlEngine;
QJSEngine::setObjectOwnership(instance_, QJSEngine::CppOwnership);
return instance_;
}
Q_INVOKABLE void removeVerificationFlow(DeviceVerificationFlow *flow);
void verifyUser(QString userid);
@ -45,4 +68,6 @@ private:
QHash<QString, QSharedPointer<DeviceVerificationFlow>> dvList;
bool isInitialSync_ = false;
RoomlistModel *rooms_;
inline static VerificationManager *instance_ = nullptr;
};

View file

@ -346,7 +346,7 @@ main(int argc, char *argv[])
QStringLiteral(":/translations")))
app.installTranslator(&appTranslator);
MainWindow w;
MainWindow w(nullptr);
// QQuickView w;
// Move the MainWindow to the center

View file

@ -22,6 +22,7 @@ CommunitiesModel::CommunitiesModel(QObject *parent)
, hiddenTagIds_{UserSettings::instance()->hiddenTags()}
, mutedTagIds_{UserSettings::instance()->mutedTags()}
{
instance_ = this;
}
QHash<int, QByteArray>

View file

@ -6,6 +6,7 @@
#include <QAbstractListModel>
#include <QHash>
#include <QQmlEngine>
#include <QSortFilterProxyModel>
#include <QString>
#include <QStringList>
@ -21,6 +22,8 @@ class CommunitiesModel;
class FilteredCommunitiesModel final : public QSortFilterProxyModel
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("Use Communities.filtered() to create a FilteredCommunitiesModel")
public:
explicit FilteredCommunitiesModel(CommunitiesModel *model, QObject *parent = nullptr);
@ -73,6 +76,9 @@ public:
class CommunitiesModel final : public QAbstractListModel
{
Q_OBJECT
QML_NAMED_ELEMENT(Communities)
QML_SINGLETON
Q_PROPERTY(QString currentTagId READ currentTagId WRITE setCurrentTagId NOTIFY
currentTagIdChanged RESET resetCurrentTagId)
Q_PROPERTY(QStringList tags READ tags NOTIFY tagsChanged)
@ -149,6 +155,26 @@ public:
};
CommunitiesModel(QObject *parent = nullptr);
static CommunitiesModel *create(QQmlEngine *qmlEngine, QJSEngine *)
{
// The instance has to exist before it is used. We cannot replace it.
Q_ASSERT(instance_);
// The engine has to have the same thread affinity as the singleton.
Q_ASSERT(qmlEngine->thread() == instance_->thread());
// There can only be one engine accessing the singleton.
static QJSEngine *s_engine = nullptr;
if (s_engine)
Q_ASSERT(qmlEngine == s_engine);
else
s_engine = qmlEngine;
QJSEngine::setObjectOwnership(instance_, QJSEngine::CppOwnership);
return instance_;
}
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
@ -221,4 +247,6 @@ private:
mtx::responses::UnreadNotifications dmUnreads{};
friend class FilteredCommunitiesModel;
inline static CommunitiesModel *instance_ = nullptr;
};

View file

@ -19,6 +19,7 @@ class QQmlAdaptorModel;
class DelegateChoice : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_CLASSINFO("DefaultProperty", "delegate")
public:
@ -45,6 +46,7 @@ private:
class DelegateChooser : public QQuickItem
{
Q_OBJECT
QML_ELEMENT
Q_CLASSINFO("DefaultProperty", "choices")
public:

View file

@ -7,11 +7,13 @@
#include <QIODevice>
#include <QImage>
#include <QObject>
#include <QQmlEngine>
#include <QSize>
#include <QStringList>
#include <QTimer>
#include <QUrl>
#include <QVariantList>
#include <deque>
#include <memory>
@ -43,6 +45,10 @@ enum class MarkdownOverride
class MediaUpload final : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
Q_PROPERTY(int mediaType READ type NOTIFY mediaTypeChanged)
// https://stackoverflow.com/questions/33422265/pass-qimage-to-qml/68554646#68554646
Q_PROPERTY(QUrl thumbnail READ thumbnailDataUrl NOTIFY thumbnailChanged)

View file

@ -5,6 +5,7 @@
#pragma once
#include <QObject>
#include <QQmlEngine>
#include <vector>
@ -15,10 +16,33 @@ class PresenceEmitter final : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(Presence)
QML_SINGLETON
public:
PresenceEmitter(QObject *p = nullptr)
: QObject(p)
{
instance_ = this;
}
static PresenceEmitter *create(QQmlEngine *qmlEngine, QJSEngine *)
{
// The instance has to exist before it is used. We cannot replace it.
Q_ASSERT(instance_);
// The engine has to have the same thread affinity as the singleton.
Q_ASSERT(qmlEngine->thread() == instance_->thread());
// There can only be one engine accessing the singleton.
static QJSEngine *s_engine = nullptr;
if (s_engine)
Q_ASSERT(qmlEngine == s_engine);
else
s_engine = qmlEngine;
QJSEngine::setObjectOwnership(instance_, QJSEngine::CppOwnership);
return instance_;
}
void sync(const std::vector<mtx::events::Event<mtx::events::presence::Presence>> &presences);
@ -28,4 +52,7 @@ public:
signals:
void presenceChanged(QString userid);
private:
inline static PresenceEmitter *instance_ = nullptr;
};

View file

@ -909,6 +909,8 @@ FilteredRoomlistModel::FilteredRoomlistModel(RoomlistModel *model, QObject *pare
: QSortFilterProxyModel(parent)
, roomlistmodel(model)
{
instance_ = this;
this->sortByImportance = UserSettings::instance()->sortByImportance();
this->sortByAlphabet = UserSettings::instance()->sortByAlphabet();
setSourceModel(model);

View file

@ -167,12 +167,36 @@ private:
class FilteredRoomlistModel final : public QSortFilterProxyModel
{
Q_OBJECT
QML_NAMED_ELEMENT(Rooms)
QML_SINGLETON
Q_PROPERTY(
TimelineModel *currentRoom READ currentRoom NOTIFY currentRoomChanged RESET resetCurrentRoom)
Q_PROPERTY(RoomPreview currentRoomPreview READ currentRoomPreview NOTIFY currentRoomChanged
RESET resetCurrentRoom)
public:
FilteredRoomlistModel(RoomlistModel *model, QObject *parent = nullptr);
static FilteredRoomlistModel *create(QQmlEngine *qmlEngine, QJSEngine *)
{
// The instance has to exist before it is used. We cannot replace it.
Q_ASSERT(instance_);
// The engine has to have the same thread affinity as the singleton.
Q_ASSERT(qmlEngine->thread() == instance_->thread());
// There can only be one engine accessing the singleton.
static QJSEngine *s_engine = nullptr;
if (s_engine)
Q_ASSERT(qmlEngine == s_engine);
else
s_engine = qmlEngine;
QJSEngine::setObjectOwnership(instance_, QJSEngine::CppOwnership);
return instance_;
}
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
bool filterAcceptsRow(int sourceRow, const QModelIndex &) const override;
@ -249,4 +273,6 @@ private:
FilterBy filterType = FilterBy::Nothing;
QStringList hiddenTags, hiddenSpaces;
bool hideDMs = false;
inline static FilteredRoomlistModel *instance_ = nullptr;
};

View file

@ -4,6 +4,7 @@
#pragma once
#include <QQmlEngine>
#include <QSortFilterProxyModel>
#include <QString>
@ -14,6 +15,7 @@
class TimelineFilter : public QSortFilterProxyModel
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString filterByThread READ filterByThread WRITE setThreadId NOTIFY threadIdChanged)
Q_PROPERTY(QString filterByContent READ filterByContent WRITE setContentFilter NOTIFY

View file

@ -11,6 +11,7 @@
#include <QTimer>
#include <QVariant>
#include <mtx/responses/common.hpp>
#include <mtxclient/http/errors.hpp>
#include "CacheCryptoStructs.h"
@ -36,6 +37,7 @@ struct RelatedInfo;
namespace qml_mtx_events {
Q_NAMESPACE
QML_NAMED_ELEMENT(MtxEvent)
enum EventType
{
@ -193,6 +195,9 @@ class TimelineViewManager;
class TimelineModel final : public QAbstractListModel
{
Q_OBJECT
QML_NAMED_ELEMENT(Room)
QML_UNCREATABLE("")
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
Q_PROPERTY(std::vector<QString> typingUsers READ typingUsers WRITE updateTypingUsers NOTIFY
typingUsersChanged)

View file

@ -96,29 +96,21 @@ TimelineViewManager::userColor(QString id, QColor background)
TimelineViewManager::TimelineViewManager(CallManager *, ChatPage *parent)
: QObject(parent)
, rooms_(new RoomlistModel(this))
, frooms_(new FilteredRoomlistModel(this->rooms_))
, communities_(new CommunitiesModel(this))
, verificationManager_(new VerificationManager(this))
, presenceEmitter(new PresenceEmitter(this))
{
static auto self = this;
qmlRegisterSingletonInstance("im.nheko", 1, 0, "TimelineManager", self);
qmlRegisterSingletonType<RoomlistModel>(
"im.nheko", 1, 0, "Rooms", [](QQmlEngine *, QJSEngine *) -> QObject * {
auto ptr = new FilteredRoomlistModel(self->rooms_);
instance_ = this;
connect(self->communities_,
connect(this->communities_,
&CommunitiesModel::currentTagIdChanged,
ptr,
frooms_,
&FilteredRoomlistModel::updateFilterTag);
connect(self->communities_,
connect(this->communities_,
&CommunitiesModel::hiddenTagsChanged,
ptr,
frooms_,
&FilteredRoomlistModel::updateHiddenTagsAndSpaces);
return ptr;
});
qmlRegisterSingletonInstance("im.nheko", 1, 0, "Communities", self->communities_);
qmlRegisterSingletonInstance("im.nheko", 1, 0, "VerificationManager", verificationManager_);
qmlRegisterSingletonInstance("im.nheko", 1, 0, "Presence", presenceEmitter);
updateColorPalette();

View file

@ -34,6 +34,9 @@ class TimelineViewManager final : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(TimelineManager)
QML_SINGLETON
Q_PROPERTY(
bool isInitialSync MEMBER isInitialSync_ READ isInitialSync NOTIFY initialSyncChanged)
Q_PROPERTY(bool isConnected READ isConnected NOTIFY isConnectedChanged)
@ -41,6 +44,25 @@ class TimelineViewManager final : public QObject
public:
TimelineViewManager(CallManager *callManager, ChatPage *parent = nullptr);
static TimelineViewManager *create(QQmlEngine *qmlEngine, QJSEngine *)
{
// The instance has to exist before it is used. We cannot replace it.
Q_ASSERT(instance_);
// The engine has to have the same thread affinity as the singleton.
Q_ASSERT(qmlEngine->thread() == instance_->thread());
// There can only be one engine accessing the singleton.
static QJSEngine *s_engine = nullptr;
if (s_engine)
Q_ASSERT(qmlEngine == s_engine);
else
s_engine = qmlEngine;
QJSEngine::setObjectOwnership(instance_, QJSEngine::CppOwnership);
return instance_;
}
void sync(const mtx::responses::Sync &sync_);
VerificationManager *verificationManager() { return verificationManager_; }
@ -123,6 +145,7 @@ private:
bool isConnected_ = true;
RoomlistModel *rooms_ = nullptr;
FilteredRoomlistModel *frooms_ = nullptr;
CommunitiesModel *communities_ = nullptr;
// don't move this above the rooms_
@ -130,4 +153,6 @@ private:
PresenceEmitter *presenceEmitter = nullptr;
QHash<QPair<QString, quint64>, QColor> userColors;
inline static TimelineViewManager *instance_ = nullptr;
};

View file

@ -5,6 +5,7 @@
#pragma once
#include <QObject>
#include <QQmlEngine>
#include <QString>
#include <QVariantList>
@ -13,6 +14,7 @@
class HiddenEvents : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString roomid READ roomid WRITE setRoomid NOTIFY roomidChanged REQUIRED)
Q_PROPERTY(QVariantList hiddenEvents READ hiddenEvents NOTIFY hiddenEventsChanged)
public:

View file

@ -15,6 +15,7 @@
class MxcAnimatedImage : public QQuickItem
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(TimelineModel *roomm READ room WRITE setRoom NOTIFY roomChanged REQUIRED)
Q_PROPERTY(QString eventId READ eventId WRITE setEventId NOTIFY eventIdChanged)
Q_PROPERTY(bool animatable READ animatable NOTIFY animatableChanged)

View file

@ -8,6 +8,7 @@
#include <QMediaPlayer>
#include <QObject>
#include <QPointer>
#include <QQuickItem>
#include <QString>
#include <QUrl>
#include <QVideoSink>
@ -21,6 +22,8 @@ class TimelineModel;
class MxcMediaProxy : public QMediaPlayer
{
Q_OBJECT
QML_NAMED_ELEMENT(MxcMedia)
Q_PROPERTY(TimelineModel *roomm READ room WRITE setRoom NOTIFY roomChanged REQUIRED)
Q_PROPERTY(QString eventId READ eventId WRITE setEventId NOTIFY eventIdChanged)
Q_PROPERTY(bool loaded READ loaded NOTIFY loadedChanged)

View file

@ -12,7 +12,7 @@
class NhekoCursorShape : public QQuickItem
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(
Qt::CursorShape cursorShape READ cursorShape WRITE setCursorShape NOTIFY cursorShapeChanged)

View file

@ -7,6 +7,7 @@
class NhekoDropArea : public QQuickItem
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString roomid READ roomid WRITE setRoomid NOTIFY roomidChanged)
public:
NhekoDropArea(QQuickItem *parent = nullptr);

View file

@ -1,60 +0,0 @@
// SPDX-FileCopyrightText: Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include "NhekoEventObserver.h"
#include <QMouseEvent>
#include "Logging.h"
NhekoEventObserver::NhekoEventObserver(QQuickItem *parent)
: QQuickItem(parent)
{
setFiltersChildMouseEvents(true);
}
bool
NhekoEventObserver::childMouseEventFilter(QQuickItem * /*item*/, QEvent *event)
{
// nhlog::ui()->debug("Touched {}", item->metaObject()->className());
auto setTouched = [this](bool touched) {
if (touched != this->wasTouched_) {
this->wasTouched_ = touched;
emit wasTouchedChanged();
}
};
// see
// https://code.qt.io/cgit/qt/qtdeclarative.git/tree/src/quicktemplates2/qquickscrollview.cpp?id=7f29e89c26ae2babc358b1c4e6f965af6ec759f4#n471
switch (event->type()) {
case QEvent::TouchBegin:
case QEvent::TouchEnd:
setTouched(true);
break;
case QEvent::MouseButtonPress:
if (static_cast<QMouseEvent *>(event)->source() == Qt::MouseEventNotSynthesized) {
setTouched(false);
}
break;
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
if (static_cast<QMouseEvent *>(event)->source() == Qt::MouseEventNotSynthesized)
setTouched(false);
break;
case QEvent::HoverEnter:
case QEvent::HoverMove:
case QEvent::Wheel:
setTouched(false);
break;
default:
break;
}
return false;
}

View file

@ -1,27 +0,0 @@
// SPDX-FileCopyrightText: Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QQuickItem>
class NhekoEventObserver : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(bool wasTouched READ wasTouched NOTIFY wasTouchedChanged)
public:
explicit NhekoEventObserver(QQuickItem *parent = 0);
bool childMouseEventFilter(QQuickItem *item, QEvent *event) override;
private:
bool wasTouched() { return wasTouched_; }
bool wasTouched_ = false;
signals:
void wasTouchedChanged();
};

View file

@ -7,6 +7,7 @@
#include <QFontDatabase>
#include <QObject>
#include <QPalette>
#include <QQmlEngine>
#include <QWindow>
#include "AliasEditModel.h"
@ -19,6 +20,9 @@ class Nheko final : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_PROPERTY(QPalette colors READ colors NOTIFY colorsChanged)
Q_PROPERTY(QPalette inactiveColors READ inactiveColors NOTIFY colorsChanged)
Q_PROPERTY(Theme theme READ theme NOTIFY colorsChanged)

View file

@ -7,6 +7,7 @@
#include <optional>
#include <QObject>
#include <QQmlEngine>
#include <mtx/responses/public_rooms.hpp>
@ -25,6 +26,9 @@ class RoomSummary final : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("Please use joinRoom to create a room summary.")
Q_PROPERTY(QString reason READ reason WRITE setReason NOTIFY reasonChanged)
Q_PROPERTY(QString roomid READ roomid NOTIFY loaded)

View file

@ -5,6 +5,7 @@
#pragma once
#include <QObject>
#include <QQmlEngine>
#include <mtxclient/http/client.hpp>
@ -12,10 +13,31 @@ class UIA final : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_PROPERTY(QString title READ title NOTIFY titleChanged)
public:
static UIA *instance();
static UIA *create(QQmlEngine *qmlEngine, QJSEngine *)
{
// The instance has to exist before it is used. We cannot replace it.
Q_ASSERT(instance());
// The engine has to have the same thread affinity as the singleton.
Q_ASSERT(qmlEngine->thread() == instance()->thread());
// There can only be one engine accessing the singleton.
static QJSEngine *s_engine = nullptr;
if (s_engine)
Q_ASSERT(qmlEngine == s_engine);
else
s_engine = qmlEngine;
QJSEngine::setObjectOwnership(instance(), QJSEngine::CppOwnership);
return instance();
}
UIA(QObject *parent = nullptr)
: QObject(parent)

View file

@ -6,6 +6,7 @@
#include <QAbstractListModel>
#include <QObject>
#include <QQmlEngine>
#include <QString>
#include <QVector>
#include <mtx/responses.hpp>
@ -16,6 +17,7 @@
namespace verification {
Q_NAMESPACE
QML_NAMED_ELEMENT(VerificationStatus)
enum Status
{

View file

@ -54,6 +54,27 @@ std::vector<std::string>
getTurnURIs(const mtx::responses::TurnServer &turnServer);
}
CallManager *
CallManager::create(QQmlEngine *qmlEngine, QJSEngine *)
{
// The instance has to exist before it is used. We cannot replace it.
auto instance = ChatPage::instance()->callManager();
Q_ASSERT(instance);
// The engine has to have the same thread affinity as the singleton.
Q_ASSERT(qmlEngine->thread() == instance->thread());
// There can only be one engine accessing the singleton.
static QJSEngine *s_engine = nullptr;
if (s_engine)
Q_ASSERT(qmlEngine == s_engine);
else
s_engine = qmlEngine;
QJSEngine::setObjectOwnership(instance, QJSEngine::CppOwnership);
return instance;
}
CallManager::CallManager(QObject *parent)
: QObject(parent)
, session_(WebRTCSession::instance())

View file

@ -9,6 +9,7 @@
#include <QMediaPlayer>
#include <QObject>
#include <QQmlEngine>
#include <QString>
#include <QStringList>
#include <QTimer>
@ -29,6 +30,10 @@ class QUrl;
class CallManager final : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_PROPERTY(bool haveCallInvite READ haveCallInvite NOTIFY newInviteState)
Q_PROPERTY(bool isOnCall READ isOnCall NOTIFY newCallState)
Q_PROPERTY(bool isOnCallOnOtherDevice READ isOnCallOnOtherDevice NOTIFY newCallDeviceState)
@ -49,6 +54,8 @@ class CallManager final : public QObject
public:
CallManager(QObject *);
static CallManager *create(QQmlEngine *qmlEngine, QJSEngine *);
bool haveCallInvite() const { return haveCallInvite_; }
bool isOnCall() const { return (session_.state() != webrtc::State::DISCONNECTED); }
bool isOnCallOnOtherDevice() const { return (isOnCallOnOtherDevice_ != ""); }

View file

@ -48,26 +48,26 @@ using webrtc::State;
WebRTCSession::WebRTCSession()
: devices_(CallDevices::instance())
{
qmlRegisterUncreatableMetaObject(webrtc::staticMetaObject,
"im.nheko",
1,
0,
"CallType",
QStringLiteral("Can't instantiate enum"));
// qmlRegisterUncreatableMetaObject(webrtc::staticMetaObject,
// "im.nheko",
// 1,
// 0,
// "CallType",
// QStringLiteral("Can't instantiate enum"));
qmlRegisterUncreatableMetaObject(webrtc::staticMetaObject,
"im.nheko",
1,
0,
"ScreenShareType",
QStringLiteral("Can't instantiate enum"));
// qmlRegisterUncreatableMetaObject(webrtc::staticMetaObject,
// "im.nheko",
// 1,
// 0,
// "ScreenShareType",
// QStringLiteral("Can't instantiate enum"));
qmlRegisterUncreatableMetaObject(webrtc::staticMetaObject,
"im.nheko",
1,
0,
"WebRTCState",
QStringLiteral("Can't instantiate enum"));
// qmlRegisterUncreatableMetaObject(webrtc::staticMetaObject,
// "im.nheko",
// 1,
// 0,
// "WebRTCState",
// QStringLiteral("Can't instantiate enum"));
connect(this, &WebRTCSession::stateChanged, this, &WebRTCSession::setState);
init();

View file

@ -8,6 +8,7 @@
#include <vector>
#include <QObject>
#include <QQmlEngine>
#include "mtx/events/voip.hpp"
@ -17,6 +18,7 @@ class QQuickItem;
namespace webrtc {
Q_NAMESPACE
QML_NAMED_ELEMENT(Voip)
enum class CallType
{