diff --git a/src/python/theme_parser.py b/src/python/theme_parser.py
index 019c799d..25e0fe46 100644
--- a/src/python/theme_parser.py
+++ b/src/python/theme_parser.py
@@ -58,11 +58,10 @@ def convert_to_qml(theme_content: str) -> str:
lines = [
"import QtQuick 2.12",
'import "Base"',
- 'import "utils.js" as Utils',
"QtObject {",
- " function hsluv(h, s, l, a) { return Utils.hsluv(h, s, l, a) }",
- " function hsl(h, s, l) { return Utils.hsl(h, s, l) }",
- " function hsla(h, s, l, a) { return Utils.hsla(h, s, l, a) }",
+ " function hsluv(h, s, l, a) { return utils.hsluv(h, s, l, a) }",
+ " function hsl(h, s, l) { return utils.hsl(h, s, l) }",
+ " function hsla(h, s, l, a) { return utils.hsla(h, s, l, a) }",
" id: theme",
]
lines += [f" {line}" for line in _process_lines(theme_content)]
diff --git a/src/qml/Base/HAvatar.qml b/src/qml/Base/HAvatar.qml
index 9c36e501..ee06426c 100644
--- a/src/qml/Base/HAvatar.qml
+++ b/src/qml/Base/HAvatar.qml
@@ -1,15 +1,14 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import "../Base"
-import "../utils.js" as Utils
Rectangle {
id: avatar
implicitWidth: theme.controls.avatar.size
implicitHeight: theme.controls.avatar.size
- color: avatarImage.visible ? "transparent" : Utils.hsluv(
- name ? Utils.hueFrom(name) : 0,
+ color: avatarImage.visible ? "transparent" : utils.hsluv(
+ name ? utils.hueFrom(name) : 0,
name ? theme.controls.avatar.background.saturation : 0,
theme.controls.avatar.background.lightness,
theme.controls.avatar.background.opacity
@@ -34,8 +33,8 @@ Rectangle {
text: name ? name.charAt(0) : "?"
font.pixelSize: parent.height / 1.4
- color: Utils.hsluv(
- name ? Utils.hueFrom(name) : 0,
+ color: utils.hsluv(
+ name ? utils.hueFrom(name) : 0,
name ? theme.controls.avatar.letter.saturation : 0,
theme.controls.avatar.letter.lightness,
theme.controls.avatar.letter.opacity
diff --git a/src/qml/Base/HBox.qml b/src/qml/Base/HBox.qml
index 2016c280..41e3dca7 100644
--- a/src/qml/Base/HBox.qml
+++ b/src/qml/Base/HBox.qml
@@ -1,6 +1,5 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
-import "../utils.js" as Utils
Rectangle {
id: box
@@ -114,10 +113,10 @@ Rectangle {
property string name: modelData.name
property Item next: buttonRepeater.itemAt(
- Utils.numberWrapAt(index + 1, buttonRepeater.count),
+ utils.numberWrapAt(index + 1, buttonRepeater.count),
)
property Item previous: buttonRepeater.itemAt(
- Utils.numberWrapAt(index - 1, buttonRepeater.count),
+ utils.numberWrapAt(index - 1, buttonRepeater.count),
)
}
}
diff --git a/src/qml/Base/HCheckBox.qml b/src/qml/Base/HCheckBox.qml
index d1a2cd99..45803fa3 100644
--- a/src/qml/Base/HCheckBox.qml
+++ b/src/qml/Base/HCheckBox.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
-import "../utils.js" as Utils
CheckBox {
id: box
diff --git a/src/qml/Base/HDrawer.qml b/src/qml/Base/HDrawer.qml
index 9d8a8364..156c7519 100644
--- a/src/qml/Base/HDrawer.qml
+++ b/src/qml/Base/HDrawer.qml
@@ -1,6 +1,5 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
-import "../utils.js" as Utils
Drawer {
id: drawer
diff --git a/src/qml/Base/HImage.qml b/src/qml/Base/HImage.qml
index 8a499376..d75d7528 100644
--- a/src/qml/Base/HImage.qml
+++ b/src/qml/Base/HImage.qml
@@ -1,5 +1,4 @@
import QtQuick 2.12
-import "../utils.js" as Utils
Image {
id: image
@@ -13,7 +12,7 @@ Image {
property bool broken: false
property bool animate: true
- property bool animated: Utils.urlExtension(image.source) === "gif"
+ property bool animated: utils.urlExtension(image.source) === "gif"
property alias showProgressBar: progressBarLoader.active
property bool inderterminateProgressBar: false
diff --git a/src/qml/Base/HSelectableLabel.qml b/src/qml/Base/HSelectableLabel.qml
index 6d4965b3..0ac03783 100644
--- a/src/qml/Base/HSelectableLabel.qml
+++ b/src/qml/Base/HSelectableLabel.qml
@@ -1,6 +1,5 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
-import "../utils.js" as Utils
TextEdit {
id: label
diff --git a/src/qml/Base/HSelectableLabelContainer.qml b/src/qml/Base/HSelectableLabelContainer.qml
index be859852..5e5410d8 100644
--- a/src/qml/Base/HSelectableLabelContainer.qml
+++ b/src/qml/Base/HSelectableLabelContainer.qml
@@ -1,5 +1,4 @@
import QtQuick 2.12
-import "../utils.js" as Utils
FocusScope {
signal deselectAll()
diff --git a/src/qml/Base/HTile.qml b/src/qml/Base/HTile.qml
index fcaa19a1..a9f2f112 100644
--- a/src/qml/Base/HTile.qml
+++ b/src/qml/Base/HTile.qml
@@ -1,6 +1,5 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
-import "../utils.js" as Utils
HButton {
id: tile
diff --git a/src/qml/Base/MediaPlayer/OSD.qml b/src/qml/Base/MediaPlayer/OSD.qml
index 157432ce..0977421a 100644
--- a/src/qml/Base/MediaPlayer/OSD.qml
+++ b/src/qml/Base/MediaPlayer/OSD.qml
@@ -2,7 +2,6 @@ import QtQuick 2.12
import QtQuick.Layouts 1.12
import QtAV 1.7
import "../../Base"
-import "../../utils.js" as Utils
HColumnLayout {
id: osd
@@ -118,7 +117,7 @@ HColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.margins: padding / 4
- text: Utils.formatDuration(previewToolTip.wantTimestamp)
+ text: utils.formatDuration(previewToolTip.wantTimestamp)
padding: theme.spacing / 2
opacity: previewToolTip.wantTimestamp === -1 ? 0 : 1
@@ -197,7 +196,7 @@ HColumnLayout {
OSDButton {
id: speedButton
icon.name: "player-speed"
- text: qsTr("%1x").arg(Utils.round(media.playbackRate))
+ text: qsTr("%1x").arg(utils.round(media.playbackRate))
toolTip.text: qsTr("Reset speed")
onClicked: media.playbackRate = 1
}
@@ -225,11 +224,11 @@ HColumnLayout {
text: boundPosition && savedDuration ?
qsTr("%1 / %2")
- .arg(Utils.formatDuration(boundPosition))
- .arg(Utils.formatDuration(savedDuration)) :
+ .arg(utils.formatDuration(boundPosition))
+ .arg(utils.formatDuration(savedDuration)) :
boundPosition || savedDuration ?
- Utils.formatDuration(boundPosition || savedDuration) :
+ utils.formatDuration(boundPosition || savedDuration) :
""
}
@@ -239,7 +238,7 @@ HColumnLayout {
OSDLabel {
text: boundPosition && savedDuration ?
qsTr("-%1").arg(
- Utils.formatDuration(savedDuration - boundPosition)
+ utils.formatDuration(savedDuration - boundPosition)
) : ""
}
diff --git a/src/qml/Chat/Banners/InviteBanner.qml b/src/qml/Chat/Banners/InviteBanner.qml
index 5ec13242..d824b252 100644
--- a/src/qml/Chat/Banners/InviteBanner.qml
+++ b/src/qml/Chat/Banners/InviteBanner.qml
@@ -1,6 +1,5 @@
import QtQuick 2.12
import "../../Base"
-import "../../utils.js" as Utils
Banner {
property string inviterId: chat.roomInfo.inviter
@@ -14,7 +13,7 @@ Banner {
avatar.mxc: inviterAvatar
labelText: qsTr("%1 invited you to this room").arg(
- Utils.coloredNameHtml(inviterName, inviterId)
+ utils.coloredNameHtml(inviterName, inviterId)
)
buttonModel: [
diff --git a/src/qml/Chat/Banners/LeftBanner.qml b/src/qml/Chat/Banners/LeftBanner.qml
index e956458c..9e9d2e45 100644
--- a/src/qml/Chat/Banners/LeftBanner.qml
+++ b/src/qml/Chat/Banners/LeftBanner.qml
@@ -1,6 +1,5 @@
import QtQuick 2.12
import "../../Base"
-import "../../utils.js" as Utils
Banner {
color: theme.chat.leftBanner.background
@@ -22,7 +21,7 @@ Banner {
buttonCallbacks: ({
forget: button => {
- Utils.makePopup(
+ utils.makePopup(
"Popups/ForgetRoomPopup.qml",
mainUI, // Must not be destroyed with chat
{
diff --git a/src/qml/Chat/Banners/UnknownDevicesBanner.qml b/src/qml/Chat/Banners/UnknownDevicesBanner.qml
index b5ea400f..d4eaa74b 100644
--- a/src/qml/Chat/Banners/UnknownDevicesBanner.qml
+++ b/src/qml/Chat/Banners/UnknownDevicesBanner.qml
@@ -1,6 +1,5 @@
import QtQuick 2.12
import "../../Base"
-import "../utils.js" as ChatJS
Banner {
color: theme.chat.unknownDevices.background
diff --git a/src/qml/Chat/Chat.qml b/src/qml/Chat/Chat.qml
index a2f727ec..365e746c 100644
--- a/src/qml/Chat/Chat.qml
+++ b/src/qml/Chat/Chat.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../Base"
-import "../utils.js" as Utils
import "RoomPane"
Item {
@@ -16,10 +15,10 @@ Item {
property bool ready: userInfo !== "waiting" && roomInfo !== "waiting"
readonly property var userInfo:
- Utils.getItem(modelSources["Account"] || [], "user_id", userId) ||
+ utils.getItem(modelSources["Account"] || [], "user_id", userId) ||
"waiting"
- readonly property var roomInfo: Utils.getItem(
+ readonly property var roomInfo: utils.getItem(
modelSources[["Room", userId]] || [], "room_id", roomId
) || "waiting"
diff --git a/src/qml/Chat/ChatPage.qml b/src/qml/Chat/ChatPage.qml
index 0871c248..1d41061a 100644
--- a/src/qml/Chat/ChatPage.qml
+++ b/src/qml/Chat/ChatPage.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../Base"
-import "../utils.js" as Utils
import "Banners"
import "Timeline"
import "FileTransfer"
diff --git a/src/qml/Chat/Composer.qml b/src/qml/Chat/Composer.qml
index 3a696a60..70e6726c 100644
--- a/src/qml/Chat/Composer.qml
+++ b/src/qml/Chat/Composer.qml
@@ -2,7 +2,6 @@ import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../Base"
import "../Dialogs"
-import "../utils.js" as Utils
Rectangle {
property string indent: " "
@@ -12,7 +11,7 @@ Rectangle {
property string writingUserId: chat.userId
readonly property var writingUserInfo:
- Utils.getItem(modelSources["Account"] || [], "user_id", writingUserId)
+ utils.getItem(modelSources["Account"] || [], "user_id", writingUserId)
property bool textChangedSinceLostFocus: false
@@ -90,7 +89,7 @@ Rectangle {
}
onTextChanged: {
- if (Utils.isEmptyObject(aliases)) {
+ if (utils.isEmptyObject(aliases)) {
writingUserId = Qt.binding(() => chat.userId)
toSend = text
setTyping(Boolean(text))
diff --git a/src/qml/Chat/FileTransfer/Transfer.qml b/src/qml/Chat/FileTransfer/Transfer.qml
index 44192d7c..7cb2e5d0 100644
--- a/src/qml/Chat/FileTransfer/Transfer.qml
+++ b/src/qml/Chat/FileTransfer/Transfer.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../../Base"
-import "../../utils.js" as Utils
HColumnLayout {
id: transfer
@@ -102,7 +101,7 @@ HColumnLayout {
Repeater {
model: [
- msLeft ? qsTr("-%1").arg(Utils.formatDuration(msLeft)) : "",
+ msLeft ? qsTr("-%1").arg(utils.formatDuration(msLeft)) : "",
speed ? qsTr("%1/s").arg(CppUtils.formattedBytes(speed)) : "",
diff --git a/src/qml/Chat/RoomPane/MemberDelegate.qml b/src/qml/Chat/RoomPane/MemberDelegate.qml
index ce33816a..993b2f65 100644
--- a/src/qml/Chat/RoomPane/MemberDelegate.qml
+++ b/src/qml/Chat/RoomPane/MemberDelegate.qml
@@ -1,6 +1,5 @@
import QtQuick 2.12
import "../../Base"
-import "../../utils.js" as Utils
HTileDelegate {
id: memberDelegate
@@ -20,7 +19,7 @@ HTileDelegate {
title.text: model.display_name || model.user_id
title.color:
memberDelegate.hovered ?
- Utils.nameColor(model.display_name || model.user_id.substring(1)) :
+ utils.nameColor(model.display_name || model.user_id.substring(1)) :
theme.chat.roomPane.member.name
subtitle.text: model.display_name ? model.user_id : ""
diff --git a/src/qml/Chat/RoomPane/MemberView.qml b/src/qml/Chat/RoomPane/MemberView.qml
index 2a5f00a9..f5aa3cf4 100644
--- a/src/qml/Chat/RoomPane/MemberView.qml
+++ b/src/qml/Chat/RoomPane/MemberView.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../../Base"
-import "../../utils.js" as Utils
HColumnLayout {
HListView {
@@ -21,7 +20,7 @@ HColumnLayout {
function filterSource() {
model.source =
- Utils.filterModelSource(originSource, filterField.text)
+ utils.filterModelSource(originSource, filterField.text)
}
@@ -77,7 +76,7 @@ HColumnLayout {
topPadding: 0 // XXX
bottomPadding: 0
- onClicked: Utils.makePopup(
+ onClicked: utils.makePopup(
"Popups/InviteToRoomPopup.qml",
chat,
{
diff --git a/src/qml/Chat/RoomPane/SettingsView.qml b/src/qml/Chat/RoomPane/SettingsView.qml
index 388759c3..45fbb5fb 100644
--- a/src/qml/Chat/RoomPane/SettingsView.qml
+++ b/src/qml/Chat/RoomPane/SettingsView.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../../Base"
-import "../../utils.js" as Utils
HBox {
color: "transparent"
diff --git a/src/qml/Chat/Timeline/EventAudio.qml b/src/qml/Chat/Timeline/EventAudio.qml
index 94d394c7..5d6e62c2 100644
--- a/src/qml/Chat/Timeline/EventAudio.qml
+++ b/src/qml/Chat/Timeline/EventAudio.qml
@@ -3,7 +3,6 @@ import QtQuick.Layouts 1.12
import QtAV 1.7
import "../../Base"
import "../../Base/MediaPlayer"
-import "../../utils.js" as Utils
AudioPlayer {
id: audio
diff --git a/src/qml/Chat/Timeline/EventContent.qml b/src/qml/Chat/Timeline/EventContent.qml
index 1eff8143..f0b1b0f4 100644
--- a/src/qml/Chat/Timeline/EventContent.qml
+++ b/src/qml/Chat/Timeline/EventContent.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../../Base"
-import "../../utils.js" as Utils
HRowLayout {
id: eventContent
@@ -12,11 +11,11 @@ HRowLayout {
readonly property string senderText:
hideNameLine ? "" : (
"
" +
- Utils.coloredNameHtml(model.sender_name, model.sender_id) +
+ utils.coloredNameHtml(model.sender_name, model.sender_id) +
"
"
)
- readonly property string contentText: Utils.processedEventText(model)
- readonly property string timeText: Utils.formatTime(model.date, false)
+ readonly property string contentText: utils.processedEventText(model)
+ readonly property string timeText: utils.formatTime(model.date, false)
readonly property string localEchoText:
model.is_local_echo ?
` ⏳` :
@@ -40,7 +39,7 @@ HRowLayout {
TapHandler {
enabled: debugMode
onDoubleTapped:
- Utils.debug(eventContent, null, con => { con.runJS("json()") })
+ utils.debug(eventContent, null, con => { con.runJS("json()") })
}
Item {
@@ -149,7 +148,7 @@ HRowLayout {
visible: model.event_type === "RoomMessageNotice"
width: theme.chat.message.noticeLineWidth
height: parent.height
- color: Utils.nameColor(
+ color: utils.nameColor(
model.sender_name || model.sender_id.substring(1),
)
}
diff --git a/src/qml/Chat/Timeline/EventDelegate.qml b/src/qml/Chat/Timeline/EventDelegate.qml
index 201244b6..ab205ed0 100644
--- a/src/qml/Chat/Timeline/EventDelegate.qml
+++ b/src/qml/Chat/Timeline/EventDelegate.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../../Base"
-import "../../utils.js" as Utils
HColumnLayout {
id: eventDelegate
@@ -63,7 +62,7 @@ HColumnLayout {
function json() {
return JSON.stringify(
{
- "model": Utils.getItem(
+ "model": utils.getItem(
modelSources[[
"Event", chat.userId, chat.roomId
]],
@@ -172,7 +171,7 @@ HColumnLayout {
HMenuItem {
icon.name: "clear-messages"
text: qsTr("Clear messages")
- onTriggered: Utils.makePopup(
+ onTriggered: utils.makePopup(
"Popups/ClearMessagesPopup.qml",
chat,
{userId: chat.userId, roomId: chat.roomId},
diff --git a/src/qml/Chat/Timeline/EventFile.qml b/src/qml/Chat/Timeline/EventFile.qml
index 5a4ffafe..ec751b87 100644
--- a/src/qml/Chat/Timeline/EventFile.qml
+++ b/src/qml/Chat/Timeline/EventFile.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../../Base"
-import "../../utils.js" as Utils
HTile {
id: file
@@ -39,5 +38,5 @@ HTile {
property EventMediaLoader loader
readonly property bool cryptDict: loader.singleMediaInfo.media_crypt_dict
- readonly property bool isEncrypted: ! Utils.isEmptyObject(cryptDict)
+ readonly property bool isEncrypted: ! utils.isEmptyObject(cryptDict)
}
diff --git a/src/qml/Chat/Timeline/EventImage.qml b/src/qml/Chat/Timeline/EventImage.qml
index 354b800f..2c57778a 100644
--- a/src/qml/Chat/Timeline/EventImage.qml
+++ b/src/qml/Chat/Timeline/EventImage.qml
@@ -1,6 +1,5 @@
import QtQuick 2.12
import "../../Base"
-import "../../utils.js" as Utils
HMxcImage {
id: image
@@ -9,7 +8,7 @@ HMxcImage {
horizontalAlignment: Image.AlignLeft
animated: loader.singleMediaInfo.media_mime === "image/gif" ||
- Utils.urlExtension(loader.mediaUrl) === "gif"
+ utils.urlExtension(loader.mediaUrl) === "gif"
thumbnail: ! animated && loader.thumbnailMxc
mxc: thumbnail ?
(loader.thumbnailMxc || loader.mediaUrl) :
@@ -21,12 +20,12 @@ HMxcImage {
property EventMediaLoader loader
- readonly property bool isEncrypted: ! Utils.isEmptyObject(cryptDict)
+ readonly property bool isEncrypted: ! utils.isEmptyObject(cryptDict)
readonly property real maxHeight:
theme.chat.message.thumbnailMaxHeightRatio
- readonly property size fitSize: Utils.fitSize(
+ readonly property size fitSize: utils.fitSize(
// Minimum display size
theme.chat.message.thumbnailMinSize.width,
theme.chat.message.thumbnailMinSize.height,
diff --git a/src/qml/Chat/Timeline/EventList.qml b/src/qml/Chat/Timeline/EventList.qml
index 4e142e6b..aaaedef9 100644
--- a/src/qml/Chat/Timeline/EventList.qml
+++ b/src/qml/Chat/Timeline/EventList.qml
@@ -1,6 +1,5 @@
import QtQuick 2.12
import "../../Base"
-import "../../utils.js" as Utils
Rectangle {
property alias selectableLabelContainer: selectableLabelContainer
@@ -100,7 +99,7 @@ Rectangle {
! canTalkBreak(item, itemAfter) &&
! canDayBreak(item, itemAfter) &&
item.sender_id === itemAfter.sender_id &&
- Utils.minutesBetween(item.date, itemAfter.date) <= 5
+ utils.minutesBetween(item.date, itemAfter.date) <= 5
)
}
@@ -109,7 +108,7 @@ Rectangle {
return Boolean(
! canDayBreak(item, itemAfter) &&
- Utils.minutesBetween(item.date, itemAfter.date) >= 20
+ utils.minutesBetween(item.date, itemAfter.date) >= 20
)
}
diff --git a/src/qml/Chat/Timeline/EventMediaLoader.qml b/src/qml/Chat/Timeline/EventMediaLoader.qml
index b66df087..5ef1423c 100644
--- a/src/qml/Chat/Timeline/EventMediaLoader.qml
+++ b/src/qml/Chat/Timeline/EventMediaLoader.qml
@@ -1,6 +1,5 @@
import QtQuick 2.12
import "../../Base"
-import "../../utils.js" as Utils
HLoader {
id: loader
@@ -58,7 +57,7 @@ HLoader {
return EventDelegate.Media.File
// If this is a preview for a link in a normal message
- let ext = Utils.urlExtension(mediaUrl)
+ let ext = utils.urlExtension(mediaUrl)
if (imageExtensions.includes(ext)) return EventDelegate.Media.Image
if (videoExtensions.includes(ext)) return EventDelegate.Media.Video
diff --git a/src/qml/Chat/Timeline/EventVideo.qml b/src/qml/Chat/Timeline/EventVideo.qml
index 27d7fa6c..aafa325d 100644
--- a/src/qml/Chat/Timeline/EventVideo.qml
+++ b/src/qml/Chat/Timeline/EventVideo.qml
@@ -3,7 +3,6 @@ import QtQuick.Layouts 1.12
import QtAV 1.7
import "../../Base"
import "../../Base/MediaPlayer"
-import "../../utils.js" as Utils
VideoPlayer {
id: video
diff --git a/src/qml/DebugConsole.qml b/src/qml/DebugConsole.qml
index 7f9c383a..703e88f9 100644
--- a/src/qml/DebugConsole.qml
+++ b/src/qml/DebugConsole.qml
@@ -2,8 +2,6 @@ import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.12
import "Base"
-import "utils.js" as Utils
-import "utils.js" as U
HDrawer {
id: debugConsole
@@ -31,9 +29,8 @@ HDrawer {
`Javascript debugging console
Useful variables:
- window, theme, settings, shortcuts, mainUI, pageLoader
+ window, theme, settings, shortcuts, utils, mainUI, pageLoader
py Python interpreter
- U Utils/utils.js module
this The console itself
t Target item to debug for which this console was opened
his History, list of commands entered
diff --git a/src/qml/Dialogs/SendFilePicker.qml b/src/qml/Dialogs/SendFilePicker.qml
index 4b1e6b06..8d8341fd 100644
--- a/src/qml/Dialogs/SendFilePicker.qml
+++ b/src/qml/Dialogs/SendFilePicker.qml
@@ -1,6 +1,5 @@
import QtQuick 2.12
import Qt.labs.platform 1.1
-import "../utils.js" as Utils
HFileDialogOpener {
fill: false
@@ -11,7 +10,7 @@ HFileDialogOpener {
for (let file of files) {
let path = Qt.resolvedUrl(file).replace(/^file:/, "")
- Utils.sendFile(userId, roomId, path, () => {
+ utils.sendFile(userId, roomId, path, () => {
if (destroyWhenDone) destroy()
},
(type, args, error, traceback) => {
diff --git a/src/qml/MainPane/AccountDelegate.qml b/src/qml/MainPane/AccountDelegate.qml
index 78a936d6..4d1d1aff 100644
--- a/src/qml/MainPane/AccountDelegate.qml
+++ b/src/qml/MainPane/AccountDelegate.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../Base"
-import "../utils.js" as Utils
HTileDelegate {
id: accountDelegate
@@ -113,7 +112,7 @@ HTileDelegate {
icon.name: "sign-out"
icon.color: theme.colors.negativeBackground
text: qsTr("Sign out")
- onTriggered: Utils.makePopup(
+ onTriggered: utils.makePopup(
"Popups/SignOutPopup.qml",
window,
{ "userId": model.data.user_id },
diff --git a/src/qml/MainPane/AccountRoomList.qml b/src/qml/MainPane/AccountRoomList.qml
index d445bf41..99a3cf85 100644
--- a/src/qml/MainPane/AccountRoomList.qml
+++ b/src/qml/MainPane/AccountRoomList.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../Base"
-import "../utils.js" as Utils
HListView {
id: mainPaneList
@@ -28,12 +27,12 @@ HListView {
if (item.type === "Account" ||
(filter ?
- Utils.filterMatches(filter, item.data.filter_string) :
+ utils.filterMatches(filter, item.data.filter_string) :
! window.uiState.collapseAccounts[item.user_id]))
{
if (filter && show.length && item.type === "Account" &&
show[show.length - 1].type === "Account" &&
- ! Utils.filterMatches(
+ ! utils.filterMatches(
filter, show[show.length - 1].data.filter_string)
) {
// If filter active, current and previous items are
@@ -48,7 +47,7 @@ HListView {
let last = show[show.length - 1]
if (show.length && filter && last.type === "Account" &&
- ! Utils.filterMatches(filter, last.data.filter_string))
+ ! utils.filterMatches(filter, last.data.filter_string))
{
// If filter active, last item is an account and last item
// doesn't match filter, that account had no matching rooms.
diff --git a/src/qml/MainPane/MainPane.qml b/src/qml/MainPane/MainPane.qml
index 1541aa9b..7b09efe4 100644
--- a/src/qml/MainPane/MainPane.qml
+++ b/src/qml/MainPane/MainPane.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../Base"
-import "../utils.js" as Utils
HDrawer {
id: mainPane
diff --git a/src/qml/MainPane/RoomDelegate.qml b/src/qml/MainPane/RoomDelegate.qml
index c491a4dd..80725971 100644
--- a/src/qml/MainPane/RoomDelegate.qml
+++ b/src/qml/MainPane/RoomDelegate.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../Base"
-import "../utils.js" as Utils
HTileDelegate {
id: roomDelegate
@@ -53,8 +52,8 @@ HTileDelegate {
! lastEvent || ! lastEvent.date ?
"" :
- Utils.dateIsToday(lastEvent.date) ?
- Utils.formatTime(lastEvent.date, false) : // no seconds
+ utils.dateIsToday(lastEvent.date) ?
+ utils.formatTime(lastEvent.date, false) : // no seconds
lastEvent.date.getFullYear() === new Date().getFullYear() ?
Qt.formatDate(lastEvent.date, "d MMM") : // e.g. "5 Dec"
@@ -76,10 +75,10 @@ HTileDelegate {
// If it's a general event
if (isEmote || isUnknownMsg || (! isMsg && ! isCryptMedia)) {
- return Utils.processedEventText(lastEvent)
+ return utils.processedEventText(lastEvent)
}
- let text = Utils.coloredNameHtml(
+ let text = utils.coloredNameHtml(
lastEvent.sender_name, lastEvent.sender_id
) + ": " + lastEvent.inline_content
@@ -96,7 +95,7 @@ HTileDelegate {
icon.name: "room-send-invite"
text: qsTr("Invite members")
- onTriggered: Utils.makePopup(
+ onTriggered: utils.makePopup(
"Popups/InviteToRoomPopup.qml",
window,
{
@@ -118,7 +117,7 @@ HTileDelegate {
visible: invited
icon.name: "invite-accept"
icon.color: theme.colors.positiveBackground
- text: qsTr("Accept %1's invite").arg(Utils.coloredNameHtml(
+ text: qsTr("Accept %1's invite").arg(utils.coloredNameHtml(
model.data.inviter_name, model.data.inviter_id
))
label.textFormat: Text.StyledText
@@ -134,7 +133,7 @@ HTileDelegate {
icon.color: theme.colors.negativeBackground
text: invited ? qsTr("Decline invite") : qsTr("Leave")
- onTriggered: Utils.makePopup(
+ onTriggered: utils.makePopup(
"Popups/LeaveRoomPopup.qml",
window,
{
@@ -150,7 +149,7 @@ HTileDelegate {
icon.color: theme.colors.negativeBackground
text: qsTr("Forget")
- onTriggered: Utils.makePopup(
+ onTriggered: utils.makePopup(
"Popups/ForgetRoomPopup.qml",
window,
{
diff --git a/src/qml/Pages/AccountSettings/AccountSettings.qml b/src/qml/Pages/AccountSettings/AccountSettings.qml
index 5a9c8354..11fd8f4f 100644
--- a/src/qml/Pages/AccountSettings/AccountSettings.qml
+++ b/src/qml/Pages/AccountSettings/AccountSettings.qml
@@ -2,7 +2,6 @@ import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import "../../Base"
-import "../../utils.js" as Utils
HPage {
id: accountSettings
@@ -14,7 +13,7 @@ HPage {
readonly property bool ready:
accountInfo !== "waiting" && Boolean(accountInfo.profile_updated)
- readonly property var accountInfo: Utils.getItem(
+ readonly property var accountInfo: utils.getItem(
modelSources["Account"] || [], "user_id", userId
) || "waiting"
@@ -22,7 +21,7 @@ HPage {
hideHeaderUnderHeight: avatarPreferredSize
headerLabel.text: qsTr("Account settings for %1").arg(
- Utils.coloredNameHtml(headerName, userId)
+ utils.coloredNameHtml(headerName, userId)
)
diff --git a/src/qml/Pages/AccountSettings/ImportExportKeys.qml b/src/qml/Pages/AccountSettings/ImportExportKeys.qml
index 7905a073..9be1c394 100644
--- a/src/qml/Pages/AccountSettings/ImportExportKeys.qml
+++ b/src/qml/Pages/AccountSettings/ImportExportKeys.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../../Base"
-import "../../utils.js" as Utils
HBox {
buttonModel: [
@@ -11,7 +10,7 @@ HBox {
buttonCallbacks: ({
export: button => {
- Utils.makeObject(
+ utils.makeObject(
"Dialogs/ExportKeys.qml",
accountSettings,
{ userId: accountSettings.userId },
@@ -22,7 +21,7 @@ HBox {
)
},
import: button => {
- Utils.makeObject(
+ utils.makeObject(
"Dialogs/ImportKeys.qml",
accountSettings,
{ userId: accountSettings.userId },
diff --git a/src/qml/Pages/AccountSettings/Profile.qml b/src/qml/Pages/AccountSettings/Profile.qml
index 02b0aedf..2f1da836 100644
--- a/src/qml/Pages/AccountSettings/Profile.qml
+++ b/src/qml/Pages/AccountSettings/Profile.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import "../../Base"
import "../../Dialogs"
-import "../../utils.js" as Utils
HGridLayout {
function applyChanges() {
@@ -77,7 +76,7 @@ HGridLayout {
1 : 0
anchors.fill: parent
- color: Utils.hsluv(0, 0, 0,
+ color: utils.hsluv(0, 0, 0,
(! avatar.mxc && overlayHover.hovered) ? 0.8 : 0.7
)
@@ -139,7 +138,7 @@ HGridLayout {
HLabel {
text: qsTr("User ID:
%1")
- .arg(Utils.coloredNameHtml(userId, userId, userId))
+ .arg(utils.coloredNameHtml(userId, userId, userId))
textFormat: Text.StyledText
wrapMode: Text.Wrap
diff --git a/src/qml/Pages/AddChat/AddChat.qml b/src/qml/Pages/AddChat/AddChat.qml
index f6a5a9aa..2f7cd3d4 100644
--- a/src/qml/Pages/AddChat/AddChat.qml
+++ b/src/qml/Pages/AddChat/AddChat.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../../Base"
-import "../../utils.js" as Utils
HPage {
id: addChatPage
@@ -10,7 +9,7 @@ HPage {
property string userId
readonly property var account:
- Utils.getItem(modelSources["Account"] || [], "user_id", userId)
+ utils.getItem(modelSources["Account"] || [], "user_id", userId)
HTabContainer {
diff --git a/src/qml/Pages/AddChat/CreateRoom.qml b/src/qml/Pages/AddChat/CreateRoom.qml
index 991e31c5..7533def3 100644
--- a/src/qml/Pages/AddChat/CreateRoom.qml
+++ b/src/qml/Pages/AddChat/CreateRoom.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../../Base"
-import "../../utils.js" as Utils
HBox {
id: addChatBox
diff --git a/src/qml/Pages/AddChat/DirectChat.qml b/src/qml/Pages/AddChat/DirectChat.qml
index bcfe419b..f149ff8a 100644
--- a/src/qml/Pages/AddChat/DirectChat.qml
+++ b/src/qml/Pages/AddChat/DirectChat.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../../Base"
-import "../../utils.js" as Utils
HBox {
id: addChatBox
diff --git a/src/qml/Pages/AddChat/JoinRoom.qml b/src/qml/Pages/AddChat/JoinRoom.qml
index e9ed4207..10c9a997 100644
--- a/src/qml/Pages/AddChat/JoinRoom.qml
+++ b/src/qml/Pages/AddChat/JoinRoom.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../../Base"
-import "../../utils.js" as Utils
HBox {
id: addChatBox
diff --git a/src/qml/Popups/BoxPopup.qml b/src/qml/Popups/BoxPopup.qml
index 4b8bfffc..74656bb4 100644
--- a/src/qml/Popups/BoxPopup.qml
+++ b/src/qml/Popups/BoxPopup.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../Base"
-import "../utils.js" as Utils
HPopup {
id: popup
diff --git a/src/qml/Popups/SignOutPopup.qml b/src/qml/Popups/SignOutPopup.qml
index 54153149..b54ccaf1 100644
--- a/src/qml/Popups/SignOutPopup.qml
+++ b/src/qml/Popups/SignOutPopup.qml
@@ -1,5 +1,4 @@
import QtQuick 2.12
-import "../utils.js" as Utils
BoxPopup {
id: popup
@@ -25,7 +24,7 @@ BoxPopup {
box.buttonCallbacks: ({
ok: button => {
- Utils.makeObject(
+ utils.makeObject(
"Dialogs/ExportKeys.qml",
mainUI,
{ userId },
diff --git a/src/qml/Shortcuts.qml b/src/qml/Shortcuts.qml
index 8c99ee4e..4f20c038 100644
--- a/src/qml/Shortcuts.qml
+++ b/src/qml/Shortcuts.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import "Base"
-import "utils.js" as Utils
Item {
visible: false
@@ -35,7 +34,7 @@ Item {
if (debugConsole) {
debugConsole.visible = ! debugConsole.visible
} else {
- Utils.debug(mainUI || window)
+ utils.debug(mainUI || window)
}
}
}
@@ -72,37 +71,37 @@ Item {
HShortcut {
enabled: toFlick
sequences: settings.keys.scrollUp
- onActivated: Utils.flickPages(toFlick, -1 / 10)
+ onActivated: utils.flickPages(toFlick, -1 / 10)
}
HShortcut {
enabled: toFlick
sequences: settings.keys.scrollDown
- onActivated: Utils.flickPages(toFlick, 1 / 10)
+ onActivated: utils.flickPages(toFlick, 1 / 10)
}
HShortcut {
enabled: toFlick
sequences: settings.keys.scrollPageUp
- onActivated: Utils.flickPages(toFlick, -1)
+ onActivated: utils.flickPages(toFlick, -1)
}
HShortcut {
enabled: toFlick
sequences: settings.keys.scrollPageDown
- onActivated: Utils.flickPages(toFlick, 1)
+ onActivated: utils.flickPages(toFlick, 1)
}
HShortcut {
enabled: toFlick
sequences: settings.keys.scrollToTop
- onActivated: Utils.flickToTop(toFlick)
+ onActivated: utils.flickToTop(toFlick)
}
HShortcut {
enabled: toFlick
sequences: settings.keys.scrollToBottom
- onActivated: Utils.flickToBottom(toFlick)
+ onActivated: utils.flickToBottom(toFlick)
}
@@ -112,7 +111,7 @@ Item {
enabled: tabsTarget
sequences: settings.keys.previousTab
onActivated: tabsTarget.setCurrentIndex(
- Utils.numberWrapAt(tabsTarget.currentIndex - 1, tabsTarget.count),
+ utils.numberWrapAt(tabsTarget.currentIndex - 1, tabsTarget.count),
)
}
@@ -120,7 +119,7 @@ Item {
enabled: tabsTarget
sequences: settings.keys.nextTab
onActivated: tabsTarget.setCurrentIndex(
- Utils.numberWrapAt(tabsTarget.currentIndex + 1, tabsTarget.count),
+ utils.numberWrapAt(tabsTarget.currentIndex + 1, tabsTarget.count),
)
}
@@ -185,7 +184,7 @@ Item {
HShortcut {
enabled: window.uiState.page === "Chat/Chat.qml"
sequences: settings.keys.clearRoomMessages
- onActivated: Utils.makePopup(
+ onActivated: utils.makePopup(
"Popups/ClearMessagesPopup.qml",
mainUI,
{
@@ -198,7 +197,7 @@ Item {
HShortcut {
enabled: window.uiState.page === "Chat/Chat.qml"
sequences: settings.keys.sendFile
- onActivated: Utils.makeObject(
+ onActivated: utils.makeObject(
"Dialogs/SendFilePicker.qml",
mainUI,
{
@@ -213,7 +212,7 @@ Item {
HShortcut {
enabled: window.uiState.page === "Chat/Chat.qml"
sequences: settings.keys.sendFileFromPathInClipboard
- onActivated: Utils.sendFile(
+ onActivated: utils.sendFile(
window.uiState.pageProperties.userId,
window.uiState.pageProperties.roomId,
Clipboard.text.trim(),
diff --git a/src/qml/UI.qml b/src/qml/UI.qml
index 3d87943e..a9f4fc9a 100644
--- a/src/qml/UI.qml
+++ b/src/qml/UI.qml
@@ -5,7 +5,6 @@ import QtQuick.Window 2.7
import QtGraphicalEffects 1.12
import "Base"
import "MainPane"
-import "utils.js" as Utils
Item {
id: mainUI
diff --git a/src/qml/Utils.qml b/src/qml/Utils.qml
new file mode 100644
index 00000000..1492e2ed
--- /dev/null
+++ b/src/qml/Utils.qml
@@ -0,0 +1,323 @@
+import QtQuick 2.12
+
+QtObject {
+ function makeObject(urlComponent, parent=null, properties={},
+ callback=null) {
+ let comp = urlComponent
+
+ if (! Qt.isQtObject(urlComponent)) {
+ // It's an url or path string to a component
+ comp = Qt.createComponent(urlComponent, Component.Asynchronous)
+ }
+
+ let ready = false
+
+ comp.statusChanged.connect(status => {
+ if ([Component.Null, Component.Error].includes(status)) {
+ console.error("Failed creating component: ", comp.errorString())
+
+ } else if (! ready && status === Component.Ready) {
+ let incu = comp.incubateObject(
+ parent, properties, Qt.Asynchronous,
+ )
+
+ if (incu.status === Component.Ready) {
+ if (callback) callback(incu.object)
+ ready = true
+ return
+ }
+
+ incu.onStatusChanged = (istatus) => {
+ if (incu.status === Component.Error) {
+ console.error("Failed incubating object: ",
+ incu.errorString())
+
+ } else if (istatus === Component.Ready &&
+ callback && ! ready) {
+ if (callback) callback(incu.object)
+ ready = true
+ }
+ }
+ }
+ })
+
+ if (comp.status === Component.Ready) comp.statusChanged(comp.status)
+ }
+
+
+ function makePopup(urlComponent, parent=null, properties={}, callback=null,
+ autoDestruct=true) {
+ makeObject(urlComponent, parent, properties, (popup) => {
+ popup.open()
+ if (autoDestruct) popup.closed.connect(() => { popup.destroy() })
+ if (callback) callback(popup)
+ })
+ }
+
+
+ function debug(target, parent=null, callback=null) {
+ parent = parent || target
+ return utils.makeObject(
+ "DebugConsole.qml", parent, { target }, callback,
+ )
+ }
+
+
+ function isEmptyObject(obj) {
+ return Object.entries(obj).length === 0 && obj.constructor === Object
+ }
+
+
+ function objectUpdateRecursive(current, update) {
+ for (const key of Object.keys(update)) {
+ if ((key in current) && typeof(current[key]) === "object" &&
+ typeof(update[key]) === "object") {
+ objectUpdateRecursive(current[key], update[key])
+ } else {
+ current[key] = update[key]
+ }
+ }
+ }
+
+
+ function numberWrapAt(num, max) {
+ return num < 0 ? max + (num % max) : (num % max)
+ }
+
+
+ function hsluv(hue, saturation, lightness, alpha=1.0) {
+ hue = numberWrapAt(hue, 360)
+ let rgb = py.callSync("hsluv", [hue, saturation, lightness])
+ return Qt.rgba(rgb[0], rgb[1], rgb[2], alpha)
+ }
+
+
+ function hueFrom(string) {
+ // Calculate and return a unique hue between 0 and 360 for the string
+ let hue = 0
+ for (let i = 0; i < string.length; i++) {
+ hue += string.charCodeAt(i) * 99
+ }
+ return hue % 360
+ }
+
+
+ function nameColor(name) {
+ return hsluv(
+ hueFrom(name),
+ theme.controls.displayName.saturation,
+ theme.controls.displayName.lightness,
+ )
+ }
+
+
+ function coloredNameHtml(name, userId, displayText=null,
+ disambiguate=false) {
+ // substring: remove leading @
+ return `` +
+ escapeHtml(displayText || name || userId) +
+ ""
+ }
+
+
+ function escapeHtml(string) {
+ // Replace special HTML characters by encoded alternatives
+ return string.replace("&", "&")
+ .replace("<", "<")
+ .replace(">", ">")
+ .replace('"', """)
+ .replace("'", "'")
+ }
+
+
+ function processedEventText(ev) {
+ if (ev.event_type === "RoomMessageEmote")
+ return coloredNameHtml(ev.sender_name, ev.sender_id) + " " +
+ ev.content
+
+ let unknown = ev.event_type === "RoomMessageUnknown"
+
+ if (ev.event_type.startsWith("RoomMessage") && ! unknown)
+ return ev.content
+
+ if (ev.event_type.startsWith("RoomEncrypted")) return ev.content
+
+ let text = qsTr(ev.content).arg(
+ coloredNameHtml(ev.sender_name, ev.sender_id)
+ )
+
+ if (text.includes("%2") && ev.target_id)
+ text = text.arg(coloredNameHtml(ev.target_name, ev.target_id))
+
+ return text
+ }
+
+
+ function filterMatches(filter, text) {
+ let filter_lower = filter.toLowerCase()
+
+ if (filter_lower === filter) {
+ // Consider case only if filter isn't all lowercase (smart case)
+ filter = filter_lower
+ text = text.toLowerCase()
+ }
+
+ for (let word of filter.split(" ")) {
+ if (word && ! text.includes(word)) {
+ return false
+ }
+ }
+ return true
+ }
+
+
+ function filterModelSource(source, filter_text, property="filter_string") {
+ if (! filter_text) return source
+ let results = []
+
+ for (let i = 0; i < source.length; i++) {
+ if (filterMatches(filter_text, source[i][property])) {
+ results.push(source[i])
+ }
+ }
+
+ return results
+ }
+
+
+ function fitSize(minWidth, minHeight, width, height, maxWidth, maxHeight) {
+ if (width >= height) {
+ let new_width = Math.max(Math.min(width, maxWidth), minWidth)
+ return Qt.size(new_width, height / (width / new_width))
+ }
+
+ let new_height = Math.max(Math.min(height, maxHeight), minHeight)
+ return Qt.size(width / (height / new_height), new_height)
+ }
+
+
+ function minutesBetween(date1, date2) {
+ return ((date2 - date1) / 1000) / 60
+ }
+
+
+ function dateIsDay(date, dayDate) {
+ return date.getDate() === dayDate.getDate() &&
+ date.getMonth() === dayDate.getMonth() &&
+ date.getFullYear() === dayDate.getFullYear()
+ }
+
+
+ function dateIsToday(date) {
+ return dateIsDay(date, new Date())
+ }
+
+
+ function dateIsYesterday(date) {
+ const yesterday = new Date()
+ yesterday.setDate(yesterday.getDate() - 1)
+ return dateIsDay(date, yesterday)
+ }
+
+
+ function formatTime(time, seconds=true) {
+ return Qt.formatTime(
+ time,
+
+ Qt.locale().timeFormat(
+ seconds ? Locale.LongFormat : Locale.NarrowFormat
+ ).replace(/\./g, ":").replace(/ t$/, "")
+ // en_DK.UTF-8 locale wrongfully gives "." separators;
+ // also remove the timezone at the end
+ )
+ }
+
+
+ function formatDuration(milliseconds) {
+ let totalSeconds = milliseconds / 1000
+
+ let hours = Math.floor(totalSeconds / 3600)
+ let minutes = Math.floor((totalSeconds % 3600) / 60)
+ let seconds = Math.floor(totalSeconds % 60)
+
+ if (seconds < 10) seconds = `0${seconds}`
+ if (hours < 1) return `${minutes}:${seconds}`
+
+ if (minutes < 10) minutes = `0${minutes}`
+ return `${hours}:${minutes}:${seconds}`
+ }
+
+
+ function round(floatNumber) {
+ return parseFloat(floatNumber.toFixed(2))
+ }
+
+
+ function getItem(array, mainKey, value) {
+ for (let i = 0; i < array.length; i++) {
+ if (array[i][mainKey] === value) { return array[i] }
+ }
+ return undefined
+ }
+
+
+ function flickPages(flickable, pages) {
+ // Adapt velocity and deceleration for the number of pages to flick.
+ // If this is a repeated flicking, flick faster than a single flick.
+ if (! flickable.interactive && flickable.allowDragging) return
+
+ const futureVelocity = -flickable.height * pages
+ const currentVelocity = -flickable.verticalVelocity
+ const goFaster =
+ (futureVelocity < 0 && currentVelocity < futureVelocity / 2) ||
+ (futureVelocity > 0 && currentVelocity > futureVelocity / 2)
+
+ const normalDecel = flickable.flickDeceleration
+ const fastMultiply = pages && 8 / (1 - Math.log10(Math.abs(pages)))
+ const magicNumber = 2.5
+
+ flickable.flickDeceleration = Math.max(
+ goFaster ? normalDecel : -Infinity,
+ Math.abs(normalDecel * magicNumber * pages),
+ )
+
+ flickable.flick(
+ 0, futureVelocity * magicNumber * (goFaster ? fastMultiply : 1),
+ )
+
+ flickable.flickDeceleration = normalDecel
+ }
+
+
+ function flickToTop(flickable) {
+ if (! flickable.interactive && flickable.allowDragging) return
+ if (flickable.visibleArea.yPosition < 0) return
+
+ flickable.contentY -= flickable.contentHeight
+ flickable.returnToBounds()
+ flickable.flick(0, -100) // Force the delegates to load
+ }
+
+
+ function flickToBottom(flickable) {
+ if (! flickable.interactive && flickable.allowDragging) return
+ if (flickable.visibleArea.yPosition < 0) return
+
+ flickable.contentY = flickTarget.contentHeight - flickTarget.height
+ flickable.returnToBounds()
+ flickable.flick(0, 100)
+ }
+
+
+ function urlExtension(url) {
+ return url.toString().split("/").slice(-1)[0].split("?")[0].split(".")
+ .slice(-1)[0].toLowerCase()
+ }
+
+
+ function sendFile(userId, roomId, path, onSuccess, onError) {
+ py.callClientCoro(
+ userId, "send_file", [roomId, path], onSuccess, onError,
+ )
+ }
+}
diff --git a/src/qml/Window.qml b/src/qml/Window.qml
index 26e54c57..3e403d98 100644
--- a/src/qml/Window.qml
+++ b/src/qml/Window.qml
@@ -1,7 +1,6 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import "Base"
-import "utils.js" as Utils
ApplicationWindow {
id: window
@@ -55,7 +54,7 @@ ApplicationWindow {
propertyValues[prop] = obj[prop]
}
- Utils.objectUpdateRecursive(uiState, {
+ utils.objectUpdateRecursive(uiState, {
[obj.saveName]: { [obj.saveId || "ALL"]: propertyValues },
})
@@ -73,6 +72,8 @@ ApplicationWindow {
Python { id: py }
+ Utils { id: utils }
+
HoverHandler { id: windowHover }
HLoader {
diff --git a/src/qml/utils.js b/src/qml/utils.js
deleted file mode 100644
index 4a12c2ca..00000000
--- a/src/qml/utils.js
+++ /dev/null
@@ -1,307 +0,0 @@
-function makeObject(urlComponent, parent=null, properties={}, callback=null) {
- let comp = urlComponent
-
- if (! Qt.isQtObject(urlComponent)) {
- // It's an url or path string to a component
- comp = Qt.createComponent(urlComponent, Component.Asynchronous)
- }
-
- let ready = false
-
- comp.statusChanged.connect(status => {
- if ([Component.Null, Component.Error].includes(status)) {
- console.error("Failed creating component: ", comp.errorString())
-
- } else if (! ready && status === Component.Ready) {
- let incu = comp.incubateObject(parent, properties, Qt.Asynchronous)
-
- if (incu.status === Component.Ready) {
- if (callback) callback(incu.object)
- ready = true
- return
- }
-
- incu.onStatusChanged = (istatus) => {
- if (incu.status === Component.Error) {
- console.error("Failed incubating object: ",
- incu.errorString())
-
- } else if (istatus === Component.Ready && callback && ! ready) {
- if (callback) callback(incu.object)
- ready = true
- }
- }
- }
- })
-
- if (comp.status === Component.Ready) comp.statusChanged(comp.status)
-}
-
-
-function makePopup(urlComponent, parent=null, properties={}, callback=null,
- autoDestruct=true) {
- makeObject(urlComponent, parent, properties, (popup) => {
- popup.open()
- if (autoDestruct) popup.closed.connect(() => { popup.destroy() })
- if (callback) callback(popup)
- })
-}
-
-
-function debug(target, parent=null, callback=null) {
- parent = parent || target
- return Utils.makeObject("DebugConsole.qml", parent, { target }, callback)
-}
-
-
-function isEmptyObject(obj) {
- return Object.entries(obj).length === 0 && obj.constructor === Object
-}
-
-
-function objectUpdateRecursive(current, update) {
- for (const key of Object.keys(update)) {
- if ((key in current) && typeof(current[key]) === "object" &&
- typeof(update[key]) === "object") {
- objectUpdateRecursive(current[key], update[key])
- } else {
- current[key] = update[key]
- }
- }
-}
-
-
-function numberWrapAt(num, max) {
- return num < 0 ? max + (num % max) : (num % max)
-}
-
-
-function hsluv(hue, saturation, lightness, alpha=1.0) {
- hue = numberWrapAt(hue, 360)
- let rgb = py.callSync("hsluv", [hue, saturation, lightness])
- return Qt.rgba(rgb[0], rgb[1], rgb[2], alpha)
-}
-
-
-function hueFrom(string) {
- // Calculate and return a unique hue between 0 and 360 for the string
- let hue = 0
- for (let i = 0; i < string.length; i++) {
- hue += string.charCodeAt(i) * 99
- }
- return hue % 360
-}
-
-
-function nameColor(name) {
- return hsluv(
- hueFrom(name),
- theme.controls.displayName.saturation,
- theme.controls.displayName.lightness,
- )
-}
-
-
-function coloredNameHtml(name, userId, displayText=null, disambiguate=false) {
- // substring: remove leading @
- return `` +
- escapeHtml(displayText || name || userId) +
- ""
-}
-
-
-function escapeHtml(string) {
- // Replace special HTML characters by encoded alternatives
- return string.replace("&", "&")
- .replace("<", "<")
- .replace(">", ">")
- .replace('"', """)
- .replace("'", "'")
-}
-
-
-function processedEventText(ev) {
- if (ev.event_type === "RoomMessageEmote")
- return coloredNameHtml(ev.sender_name, ev.sender_id) + " " + ev.content
-
- let unknown = ev.event_type === "RoomMessageUnknown"
-
- if (ev.event_type.startsWith("RoomMessage") && ! unknown) return ev.content
- if (ev.event_type.startsWith("RoomEncrypted")) return ev.content
-
- let text = qsTr(ev.content).arg(
- coloredNameHtml(ev.sender_name, ev.sender_id)
- )
-
- if (text.includes("%2") && ev.target_id)
- text = text.arg(coloredNameHtml(ev.target_name, ev.target_id))
-
- return text
-}
-
-
-function filterMatches(filter, text) {
- let filter_lower = filter.toLowerCase()
-
- if (filter_lower === filter) {
- // Consider case only if filter isn't all lowercase (smart case)
- filter = filter_lower
- text = text.toLowerCase()
- }
-
- for (let word of filter.split(" ")) {
- if (word && ! text.includes(word)) {
- return false
- }
- }
- return true
-}
-
-
-function filterModelSource(source, filter_text, property="filter_string") {
- if (! filter_text) return source
- let results = []
-
- for (let i = 0; i < source.length; i++) {
- if (filterMatches(filter_text, source[i][property])) {
- results.push(source[i])
- }
- }
-
- return results
-}
-
-
-function fitSize(minWidth, minHeight, width, height, maxWidth, maxHeight) {
- if (width >= height) {
- let new_width = Math.max(Math.min(width, maxWidth), minWidth)
- return Qt.size(new_width, height / (width / new_width))
- }
-
- let new_height = Math.max(Math.min(height, maxHeight), minHeight)
- return Qt.size(width / (height / new_height), new_height)
-}
-
-
-function minutesBetween(date1, date2) {
- return ((date2 - date1) / 1000) / 60
-}
-
-
-function dateIsDay(date, dayDate) {
- return date.getDate() === dayDate.getDate() &&
- date.getMonth() === dayDate.getMonth() &&
- date.getFullYear() === dayDate.getFullYear()
-}
-
-
-function dateIsToday(date) {
- return dateIsDay(date, new Date())
-}
-
-
-function dateIsYesterday(date) {
- const yesterday = new Date()
- yesterday.setDate(yesterday.getDate() - 1)
- return dateIsDay(date, yesterday)
-}
-
-
-function formatTime(time, seconds=true) {
- return Qt.formatTime(
- time,
-
- Qt.locale().timeFormat(
- seconds ? Locale.LongFormat : Locale.NarrowFormat
- ).replace(/\./g, ":").replace(/ t$/, "")
- // en_DK.UTF-8 locale wrongfully gives "." separators;
- // also remove the timezone at the end
- )
-}
-
-
-function formatDuration(milliseconds) {
- let totalSeconds = milliseconds / 1000
-
- let hours = Math.floor(totalSeconds / 3600)
- let minutes = Math.floor((totalSeconds % 3600) / 60)
- let seconds = Math.floor(totalSeconds % 60)
-
- if (seconds < 10) seconds = `0${seconds}`
- if (hours < 1) return `${minutes}:${seconds}`
-
- if (minutes < 10) minutes = `0${minutes}`
- return `${hours}:${minutes}:${seconds}`
-}
-
-
-function round(float) {
- return parseFloat(float.toFixed(2))
-}
-
-
-function getItem(array, mainKey, value) {
- for (let i = 0; i < array.length; i++) {
- if (array[i][mainKey] === value) { return array[i] }
- }
- return undefined
-}
-
-
-function flickPages(flickable, pages) {
- // Adapt velocity and deceleration for the number of pages to flick.
- // If this is a repeated flicking, flick faster than a single flick.
- if (! flickable.interactive && flickable.allowDragging) return
-
- const futureVelocity = -flickable.height * pages
- const currentVelocity = -flickable.verticalVelocity
- const goFaster =
- (futureVelocity < 0 && currentVelocity < futureVelocity / 2) ||
- (futureVelocity > 0 && currentVelocity > futureVelocity / 2)
-
- const normalDecel = flickable.flickDeceleration
- const fastMultiply = pages && 8 / (1 - Math.log10(Math.abs(pages)))
- const magicNumber = 2.5
-
- flickable.flickDeceleration = Math.max(
- goFaster ? normalDecel : -Infinity,
- Math.abs(normalDecel * magicNumber * pages),
- )
-
- flickable.flick(
- 0, futureVelocity * magicNumber * (goFaster ? fastMultiply : 1),
- )
-
- flickable.flickDeceleration = normalDecel
-}
-
-
-function flickToTop(flickable) {
- if (! flickable.interactive && flickable.allowDragging) return
- if (flickable.visibleArea.yPosition < 0) return
-
- flickable.contentY -= flickable.contentHeight
- flickable.returnToBounds()
- flickable.flick(0, -100) // Force the delegates to load
-}
-
-
-function flickToBottom(flickable) {
- if (! flickable.interactive && flickable.allowDragging) return
- if (flickable.visibleArea.yPosition < 0) return
-
- flickable.contentY = flickTarget.contentHeight - flickTarget.height
- flickable.returnToBounds()
- flickable.flick(0, 100)
-}
-
-
-function urlExtension(url) {
- return url.toString().split("/").slice(-1)[0].split("?")[0].split(".")
- .slice(-1)[0].toLowerCase()
-}
-
-
-function sendFile(userId, roomId, path, onSuccess, onError) {
- py.callClientCoro(userId, "send_file", [roomId, path], onSuccess, onError)
-}