diff --git a/src/gui/Base/AutoDirectionLayout.qml b/src/gui/Base/AutoDirectionLayout.qml index fd286cda..bc3eb140 100644 --- a/src/gui/Base/AutoDirectionLayout.qml +++ b/src/gui/Base/AutoDirectionLayout.qml @@ -15,6 +15,7 @@ HGridLayout { return sum } + flow: width >= summedImplicitWidth ? HGridLayout.LeftToRight : diff --git a/src/gui/Base/HAvatar.qml b/src/gui/Base/HAvatar.qml index 105930ac..bd6aded4 100644 --- a/src/gui/Base/HAvatar.qml +++ b/src/gui/Base/HAvatar.qml @@ -5,21 +5,6 @@ import QtQuick.Controls 2.12 Rectangle { id: avatar - implicitWidth: implicitHeight - implicitHeight: - compact ? - theme.controls.avatar.compactSize : - theme.controls.avatar.size - - radius: theme.controls.avatar.radius - - 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 - ) - property bool compact: false @@ -37,6 +22,21 @@ Rectangle { readonly property alias circleRadius: avatarImage.circleRadius + implicitWidth: implicitHeight + implicitHeight: + compact ? + theme.controls.avatar.compactSize : + theme.controls.avatar.size + + radius: theme.controls.avatar.radius + + 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 + ) + Behavior on color { HColorAnimation {} } HLabel { @@ -73,13 +73,6 @@ Rectangle { HToolTip { id: avatarToolTip - visible: ! avatarImage.broken && - avatarImage.status !== Image.Error && - avatarImage.width < dimension * 0.75 && - (toolTipSourceOverride || toolTipMxc) && - hoverHandler.hovered - delay: 1000 - backgroundColor: theme.controls.avatar.hoveredImage.background readonly property int dimension: Math.min( mainUI.width / 1.25, @@ -88,6 +81,14 @@ Rectangle { background.border.width * 2, ) + visible: ! avatarImage.broken && + avatarImage.status !== Image.Error && + avatarImage.width < dimension * 0.75 && + (toolTipSourceOverride || toolTipMxc) && + hoverHandler.hovered + delay: 1000 + backgroundColor: theme.controls.avatar.hoveredImage.background + contentItem: HMxcImage { id: avatarToolTipImage fillMode: Image.PreserveAspectCrop diff --git a/src/gui/Base/HBottomFocusLine.qml b/src/gui/Base/HBottomFocusLine.qml index 1184d544..e35621ee 100644 --- a/src/gui/Base/HBottomFocusLine.qml +++ b/src/gui/Base/HBottomFocusLine.qml @@ -6,7 +6,6 @@ import QtQuick 2.12 HRectangleBottomBorder { id: line - property bool show: false diff --git a/src/gui/Base/HBox.qml b/src/gui/Base/HBox.qml index 9e690709..2b76d869 100644 --- a/src/gui/Base/HBox.qml +++ b/src/gui/Base/HBox.qml @@ -12,7 +12,6 @@ HFlickableColumnPage { radius: theme.controls.box.radius } - HNumberAnimation on scale { running: true from: 0 diff --git a/src/gui/Base/HBusyIndicator.qml b/src/gui/Base/HBusyIndicator.qml index 3fe4f063..5a982ee6 100644 --- a/src/gui/Base/HBusyIndicator.qml +++ b/src/gui/Base/HBusyIndicator.qml @@ -9,7 +9,6 @@ HCircleProgressBar { baseCircle.strokeWidth: 2 progressCircle.strokeWidth: 2 - HNumberAnimation on rotation { from: 0 to: 360 diff --git a/src/gui/Base/HButton.qml b/src/gui/Base/HButton.qml index 11d89fc0..0462f3ad 100644 --- a/src/gui/Base/HButton.qml +++ b/src/gui/Base/HButton.qml @@ -6,6 +6,28 @@ import QtQuick.Layouts 1.12 Button { id: button + + readonly property alias iconItem: contentItem.icon + readonly property alias label: contentItem.label + + property color backgroundColor: theme.controls.button.background + property color focusLineColor: + Qt.colorEqual(icon.color, theme.icons.colorize) ? + theme.controls.button.focusedBorder : + icon.color + + property bool disableWhileLoading: true + property bool loading: false + property bool circle: false + property bool padded: true + property bool enableRadius: false + + property HToolTip toolTip: HToolTip { + id: toolTip + visible: text && hovered + } + + enabled: ! button.loading spacing: theme.spacing topPadding: padded ? spacing * (circle ? 1 : 0.5) : 0 @@ -45,28 +67,6 @@ Button { Keys.onEnterPressed: Keys.onReturnPressed(event) activeFocusOnTab: true - - readonly property alias iconItem: contentItem.icon - readonly property alias label: contentItem.label - - property color backgroundColor: theme.controls.button.background - property color focusLineColor: - Qt.colorEqual(icon.color, theme.icons.colorize) ? - theme.controls.button.focusedBorder : - icon.color - - property bool disableWhileLoading: true - property bool loading: false - property bool circle: false - property bool padded: true - property bool enableRadius: false - - property HToolTip toolTip: HToolTip { - id: toolTip - visible: text && hovered - } - - Binding on enabled { when: disableWhileLoading && button.loading value: false diff --git a/src/gui/Base/HButtonBackground.qml b/src/gui/Base/HButtonBackground.qml index 2103647f..42cc5d0f 100644 --- a/src/gui/Base/HButtonBackground.qml +++ b/src/gui/Base/HButtonBackground.qml @@ -15,7 +15,6 @@ Rectangle { enabled ? 1 : theme.disabledElementsOpacity - Behavior on opacity { HNumberAnimation {} } Rectangle { diff --git a/src/gui/Base/HButtonContent.qml b/src/gui/Base/HButtonContent.qml index 363f71f5..7509c231 100644 --- a/src/gui/Base/HButtonContent.qml +++ b/src/gui/Base/HButtonContent.qml @@ -6,10 +6,6 @@ import QtQuick.Layouts 1.12 HRowLayout { id: buttonContent - spacing: button.spacing - opacity: button.loading ? theme.loadingElementsOpacity : - enabled ? 1 : theme.disabledElementsOpacity - property var button property QtObject buttonTheme @@ -18,8 +14,11 @@ HRowLayout { readonly property alias label: label - Behavior on opacity { HNumberAnimation {} } + spacing: button.spacing + opacity: button.loading ? theme.loadingElementsOpacity : + enabled ? 1 : theme.disabledElementsOpacity + Behavior on opacity { HNumberAnimation {} } Item { visible: button.icon.name || button.loading diff --git a/src/gui/Base/HCheckBox.qml b/src/gui/Base/HCheckBox.qml index 1b307c45..a13fb8b4 100644 --- a/src/gui/Base/HCheckBox.qml +++ b/src/gui/Base/HCheckBox.qml @@ -6,6 +6,15 @@ import QtQuick.Layouts 1.12 CheckBox { id: box + + property alias mainText: mainText + property alias subtitle: subtitleText + property bool defaultChecked: false + readonly property bool changed: checked !== defaultChecked + + function reset() { checked = defaultChecked } + + checked: defaultChecked spacing: contentItem.visible ? theme.spacing : 0 padding: 0 @@ -83,15 +92,5 @@ CheckBox { } } - - property alias mainText: mainText - property alias subtitle: subtitleText - property bool defaultChecked: false - readonly property bool changed: checked !== defaultChecked - - - function reset() { checked = defaultChecked } - - Behavior on opacity { HNumberAnimation { factor: 2 } } } diff --git a/src/gui/Base/HCircleProgressBar.qml b/src/gui/Base/HCircleProgressBar.qml index 290be8e3..771c5f18 100644 --- a/src/gui/Base/HCircleProgressBar.qml +++ b/src/gui/Base/HCircleProgressBar.qml @@ -5,14 +5,6 @@ import QtQuick.Shapes 1.12 Item { - implicitWidth: 96 * (theme ? theme.uiScale : 1) - implicitHeight: implicitWidth - - layer.enabled: true - layer.samples: 4 - layer.smooth: true - - property real progress: 0 // 0-1 readonly property alias baseCircle: baseCircle @@ -20,14 +12,22 @@ Item { readonly property alias label: label + implicitWidth: 96 * (theme ? theme.uiScale : 1) + implicitHeight: implicitWidth + + layer.enabled: true + layer.samples: 4 + layer.smooth: true + HLabel { id: label + + property int progressNumber: Math.floor(progress * 100) + anchors.centerIn: parent text: progressNumber + "%" font.pixelSize: theme ? theme.fontSize.big : 22 - property int progressNumber: Math.floor(progress * 100) - Behavior on progressNumber { HNumberAnimation { factor: 2 } } } diff --git a/src/gui/Base/HColorAnimation.qml b/src/gui/Base/HColorAnimation.qml index c5cc8790..7db135d6 100644 --- a/src/gui/Base/HColorAnimation.qml +++ b/src/gui/Base/HColorAnimation.qml @@ -4,5 +4,7 @@ import QtQuick 2.12 ColorAnimation { property real factor: 1.0 + + duration: theme.animationDuration * factor } diff --git a/src/gui/Base/HColumnPage.qml b/src/gui/Base/HColumnPage.qml index 9903acb8..73ceca19 100644 --- a/src/gui/Base/HColumnPage.qml +++ b/src/gui/Base/HColumnPage.qml @@ -5,15 +5,14 @@ import QtQuick 2.12 HPage { id: page - default property alias columnData: column.data + property alias column: column implicitWidth: theme.controls.box.defaultWidth contentHeight: column.childrenRect.height - HColumnLayout { id: column anchors.fill: parent diff --git a/src/gui/Base/HDrawer.qml b/src/gui/Base/HDrawer.qml index b9ee0a8e..8a562bf8 100644 --- a/src/gui/Base/HDrawer.qml +++ b/src/gui/Base/HDrawer.qml @@ -5,29 +5,6 @@ import QtQuick.Controls 2.12 Drawer { id: drawer - implicitWidth: horizontal ? calculatedSize : parent.width - implicitHeight: vertical ? calculatedSize : parent.height - - // Prevents this: open a popup, make the window small enough for the - // drawer to collapse, then make it big again → popup is now behind drawer - z: -1 - - topPadding: 0 - bottomPadding: 0 - leftPadding: 0 - rightPadding: 0 - - // FIXME: https://bugreports.qt.io/browse/QTBUG-59141 - // dragMargin: parent.width / 2 - - interactive: collapse - position: 1 - visible: ! collapse - modal: false - closePolicy: Popup.NoAutoClose - - background: Rectangle { id: bg; color: theme.colors.strongBackground } - property string saveName: "" property var saveId: "ALL" @@ -77,6 +54,29 @@ Drawer { readonly property bool vertical: ! horizontal + implicitWidth: horizontal ? calculatedSize : parent.width + implicitHeight: vertical ? calculatedSize : parent.height + + // Prevents this: open a popup, make the window small enough for the + // drawer to collapse, then make it big again → popup is now behind drawer + z: -1 + + topPadding: 0 + bottomPadding: 0 + leftPadding: 0 + rightPadding: 0 + + // FIXME: https://bugreports.qt.io/browse/QTBUG-59141 + // dragMargin: parent.width / 2 + + interactive: collapse + position: 1 + visible: ! collapse + modal: false + closePolicy: Popup.NoAutoClose + + background: Rectangle { id: bg; color: theme.colors.strongBackground } + Behavior on width { enabled: horizontal && ! resizeMouseHandler.drag.active NumberAnimation { duration: 100 } @@ -97,6 +97,12 @@ Drawer { MouseArea { id: resizeMouseHandler + + function snapSize(num) { + return num < snapAt + snapZone && num > snapAt - snapZone ? + snapAt : num + } + anchors.fill: parent enabled: ! drawer.collapse acceptedButtons: Qt.LeftButton @@ -124,11 +130,6 @@ Drawer { } onReleased: window.saveState(drawer) - - function snapSize(num) { - return num < snapAt + snapZone && num > snapAt - snapZone ? - snapAt : num - } } } } diff --git a/src/gui/Base/HFlickableColumnPage.qml b/src/gui/Base/HFlickableColumnPage.qml index 45234bc9..3730c02f 100644 --- a/src/gui/Base/HFlickableColumnPage.qml +++ b/src/gui/Base/HFlickableColumnPage.qml @@ -6,12 +6,9 @@ import "../ShortcutBundles" HPage { id: page - implicitWidth: theme.controls.box.defaultWidth - contentHeight: - flickable.contentHeight + flickable.topMargin + flickable.bottomMargin - default property alias columnData: column.data + property alias column: column property alias flickable: flickable property alias flickShortcuts: flickShortcuts @@ -20,8 +17,11 @@ HPage { SwipeView ? SwipeView.isCurrentItem : true - padding: 0 + implicitWidth: theme.controls.box.defaultWidth + contentHeight: + flickable.contentHeight + flickable.topMargin + flickable.bottomMargin + padding: 0 HFlickable { id: flickable diff --git a/src/gui/Base/HGridView.qml b/src/gui/Base/HGridView.qml index a7adf366..a915734a 100644 --- a/src/gui/Base/HGridView.qml +++ b/src/gui/Base/HGridView.qml @@ -5,58 +5,6 @@ import QtQuick.Controls 2.12 GridView { id: gridView - currentIndex: -1 - keyNavigationWraps: true - highlightMoveDuration: theme.animationDuration - - // Keep highlighted delegate at the center - highlightRangeMode: GridView.ApplyRange - preferredHighlightBegin: height / 2 - currentItemHeight / 2 - preferredHighlightEnd: height / 2 + currentItemHeight / 2 - - maximumFlickVelocity: window.settings.kineticScrollingMaxSpeed - - - highlight: Rectangle { - color: theme.controls.gridView.highlight - } - - ScrollBar.vertical: HScrollBar { - visible: gridView.interactive - } - - // property bool debug: false - - // https://doc.qt.io/qt-5/qml-qtquick-viewtransition.html - // #handling-interrupted-animations - add: Transition { - // ScriptAction { script: if (gridView.debug) print("add") } - HNumberAnimation { property: "opacity"; from: 0; to: 1 } - HNumberAnimation { property: "scale"; from: 0; to: 1 } - } - - move: Transition { - // ScriptAction { script: if (gridView.debug) print("move") } - HNumberAnimation { property: "opacity"; to: 1 } - HNumberAnimation { property: "scale"; to: 1 } - HNumberAnimation { properties: "x,y" } - } - - remove: Transition { - // ScriptAction { script: if (gridView.debug) print("remove") } - HNumberAnimation { property: "opacity"; to: 0 } - HNumberAnimation { property: "scale"; to: 0 } - } - - displaced: Transition { - // ScriptAction { script: if (gridView.debug) print("displaced") } - HNumberAnimation { property: "opacity"; to: 1 } - HNumberAnimation { property: "scale"; to: 1 } - HNumberAnimation { properties: "x,y" } - } - - onSelectedCountChanged: if (! selectedCount) lastCheckedDelegateIndex = 0 - property alias cursorShape: mouseArea.cursorShape property int currentItemHeight: currentItem ? currentItem.height : 0 @@ -117,6 +65,58 @@ GridView { } + currentIndex: -1 + keyNavigationWraps: true + highlightMoveDuration: theme.animationDuration + + // Keep highlighted delegate at the center + highlightRangeMode: GridView.ApplyRange + preferredHighlightBegin: height / 2 - currentItemHeight / 2 + preferredHighlightEnd: height / 2 + currentItemHeight / 2 + + maximumFlickVelocity: window.settings.kineticScrollingMaxSpeed + + + highlight: Rectangle { + color: theme.controls.gridView.highlight + } + + ScrollBar.vertical: HScrollBar { + visible: gridView.interactive + } + + // property bool debug: false + + // https://doc.qt.io/qt-5/qml-qtquick-viewtransition.html + // #handling-interrupted-animations + add: Transition { + // ScriptAction { script: if (gridView.debug) print("add") } + HNumberAnimation { property: "opacity"; from: 0; to: 1 } + HNumberAnimation { property: "scale"; from: 0; to: 1 } + } + + move: Transition { + // ScriptAction { script: if (gridView.debug) print("move") } + HNumberAnimation { property: "opacity"; to: 1 } + HNumberAnimation { property: "scale"; to: 1 } + HNumberAnimation { properties: "x,y" } + } + + remove: Transition { + // ScriptAction { script: if (gridView.debug) print("remove") } + HNumberAnimation { property: "opacity"; to: 0 } + HNumberAnimation { property: "scale"; to: 0 } + } + + displaced: Transition { + // ScriptAction { script: if (gridView.debug) print("displaced") } + HNumberAnimation { property: "opacity"; to: 1 } + HNumberAnimation { property: "scale"; to: 1 } + HNumberAnimation { properties: "x,y" } + } + + onSelectedCountChanged: if (! selectedCount) lastCheckedDelegateIndex = 0 + HKineticScrollingDisabler { id: mouseArea width: enabled ? parent.width : 0 diff --git a/src/gui/Base/HIcon.qml b/src/gui/Base/HIcon.qml index 9796bbc5..a73bde9c 100644 --- a/src/gui/Base/HIcon.qml +++ b/src/gui/Base/HIcon.qml @@ -5,15 +5,6 @@ import QtGraphicalEffects 1.12 Image { id: icon - cache: true - asynchronous: true - fillMode: Image.PreserveAspectFit - visible: Boolean(svgName) - - source: svgName ? `../../icons/${iconPack}/${svgName}.svg` : "" - sourceSize.width: svgName ? dimension : 0 - sourceSize.height: svgName ? dimension : 0 - property string svgName: "" @@ -27,6 +18,15 @@ Image { property string iconPack: theme ? theme.icons.preferredPack : "thin" + cache: true + asynchronous: true + fillMode: Image.PreserveAspectFit + visible: Boolean(svgName) + source: svgName ? `../../icons/${iconPack}/${svgName}.svg` : "" + + sourceSize.width: svgName ? dimension : 0 + sourceSize.height: svgName ? dimension : 0 + layer.enabled: ! Qt.colorEqual(colorize, "transparent") layer.effect: ColorOverlay { color: icon.colorize diff --git a/src/gui/Base/HImage.qml b/src/gui/Base/HImage.qml index 8f262baa..48b43e4b 100644 --- a/src/gui/Base/HImage.qml +++ b/src/gui/Base/HImage.qml @@ -5,16 +5,6 @@ import QtGraphicalEffects 1.12 Image { id: image - autoTransform: true - asynchronous: true - fillMode: Image.PreserveAspectFit - - cache: ! (animate && animated) && - (sourceSize.width + sourceSize.height) <= 512 - - layer.enabled: radius !== 0 - layer.effect: OpacityMask { maskSource: roundMask } - property bool circle: radius === circleRadius property bool broken: false @@ -31,11 +21,24 @@ Image { Math.ceil(Math.max(image.width, image.height)) + autoTransform: true + asynchronous: true + fillMode: Image.PreserveAspectFit + + cache: ! (animate && animated) && + (sourceSize.width + sourceSize.height) <= 512 + + layer.enabled: radius !== 0 + layer.effect: OpacityMask { maskSource: roundMask } + Component { id: animatedImageComponent AnimatedImage { id: animatedImage + + property bool userPaused: ! window.settings.media.autoPlayGIF + source: image.source autoTransform: image.autoTransform asynchronous: image.asynchronous @@ -46,6 +49,14 @@ Image { horizontalAlignment: image.horizontalAlignment verticalAlignment: image.verticalAlignment + // Online GIFs won't be able to loop if cache is set to false, + // but caching GIFs is expansive. + cache: ! Qt.resolvedUrl(source).startsWith("file://") + paused: ! visible || window.hidden || userPaused + + layer.enabled: image.radius !== 0 + layer.effect: OpacityMask { maskSource: roundMask } + // Hack to make the non-animated image behind this one // basically invisible Binding { @@ -64,16 +75,6 @@ Image { value: 1 } - // Online GIFs won't be able to loop if cache is set to false, - // but caching GIFs is expansive. - cache: ! Qt.resolvedUrl(source).startsWith("file://") - paused: ! visible || window.hidden || userPaused - - layer.enabled: image.radius !== 0 - layer.effect: OpacityMask { maskSource: roundMask } - - property bool userPaused: ! window.settings.media.autoPlayGIF - TapHandler { enabled: image.enabledAnimatedPausing onTapped: parent.userPaused = ! parent.userPaused diff --git a/src/gui/Base/HKineticScrollingDisabler.qml b/src/gui/Base/HKineticScrollingDisabler.qml index 84b23acc..7352104f 100644 --- a/src/gui/Base/HKineticScrollingDisabler.qml +++ b/src/gui/Base/HKineticScrollingDisabler.qml @@ -4,27 +4,12 @@ import QtQuick 2.12 MouseArea { id: mouseArea - enabled: ! window.settings.enableKineticScrolling - propagateComposedEvents: true - acceptedButtons: Qt.NoButton - - - onWheel: { - // Make components below the stack notice the wheel event - wheel.accepted = false - - const pos = getNewPosition(flickable, wheel) - flickable.flick(0, 0) - flickable.contentY = pos - } - property Flickable flickable: parent // Used to get default flickDeceleration value readonly property Flickable dummy: Flickable {} - function getNewPosition(flickable, wheel) { // wheel.pixelDelta will be available on high resolution trackpads. // Otherwise use wheel.angleDelta, which is available from mouses and @@ -53,6 +38,19 @@ MouseArea { } + enabled: ! window.settings.enableKineticScrolling + propagateComposedEvents: true + acceptedButtons: Qt.NoButton + + onWheel: { + // Make components below the stack notice the wheel event + wheel.accepted = false + + const pos = getNewPosition(flickable, wheel) + flickable.flick(0, 0) + flickable.contentY = pos + } + Binding { target: flickable property: "maximumFlickVelocity" diff --git a/src/gui/Base/HLabeledItem.qml b/src/gui/Base/HLabeledItem.qml index b36968d5..17565614 100644 --- a/src/gui/Base/HLabeledItem.qml +++ b/src/gui/Base/HLabeledItem.qml @@ -4,9 +4,6 @@ import QtQuick 2.12 import QtQuick.Layouts 1.12 HColumnLayout { - spacing: theme.spacing / 2 - - default property alias insideData: itemHolder.data property bool loading: false @@ -17,6 +14,8 @@ HColumnLayout { readonly property alias toolTip: toolTip + spacing: theme.spacing / 2 + HRowLayout { spacing: theme.spacing diff --git a/src/gui/Base/HListView.qml b/src/gui/Base/HListView.qml index c838d490..dcac8449 100644 --- a/src/gui/Base/HListView.qml +++ b/src/gui/Base/HListView.qml @@ -5,59 +5,6 @@ import QtQuick.Controls 2.12 ListView { id: listView - currentIndex: -1 - keyNavigationWraps: true - highlightMoveDuration: theme.animationDuration - highlightResizeDuration: theme.animationDuration - - // Keep highlighted delegate at the center - highlightRangeMode: ListView.ApplyRange - preferredHighlightBegin: height / 2 - currentItemHeight / 2 - preferredHighlightEnd: height / 2 + currentItemHeight / 2 - - maximumFlickVelocity: window.settings.kineticScrollingMaxSpeed - - - highlight: Rectangle { - color: theme.controls.listView.highlight - } - - ScrollBar.vertical: HScrollBar { - visible: listView.interactive - } - - // property bool debug: false - - // https://doc.qt.io/qt-5/qml-qtquick-viewtransition.html - // #handling-interrupted-animations - add: Transition { - // ScriptAction { script: if (listView.debug) print("add") } - HNumberAnimation { property: "opacity"; from: 0; to: 1 } - HNumberAnimation { property: "scale"; from: 0; to: 1 } - } - - move: Transition { - // ScriptAction { script: if (listView.debug) print("move") } - HNumberAnimation { property: "opacity"; to: 1 } - HNumberAnimation { property: "scale"; to: 1 } - HNumberAnimation { properties: "x,y" } - } - - remove: Transition { - // ScriptAction { script: if (listView.debug) print("remove") } - HNumberAnimation { property: "opacity"; to: 0 } - HNumberAnimation { property: "scale"; to: 0 } - } - - displaced: Transition { - // ScriptAction { script: if (listView.debug) print("displaced") } - HNumberAnimation { property: "opacity"; to: 1 } - HNumberAnimation { property: "scale"; to: 1 } - HNumberAnimation { properties: "x,y" } - } - - onSelectedCountChanged: if (! selectedCount) lastCheckedDelegateIndex = 0 - property alias cursorShape: cursorShapeArea.cursorShape property int currentItemHeight: currentItem ? currentItem.height : 0 @@ -67,7 +14,6 @@ ListView { property int lastCheckedDelegateIndex: 0 property int selectedCount: Object.keys(checked).length - function check(...indices) { for (const i of indices) { const model = listView.model.get(i) @@ -131,6 +77,59 @@ ListView { } + currentIndex: -1 + keyNavigationWraps: true + highlightMoveDuration: theme.animationDuration + highlightResizeDuration: theme.animationDuration + + // Keep highlighted delegate at the center + highlightRangeMode: ListView.ApplyRange + preferredHighlightBegin: height / 2 - currentItemHeight / 2 + preferredHighlightEnd: height / 2 + currentItemHeight / 2 + + maximumFlickVelocity: window.settings.kineticScrollingMaxSpeed + + + highlight: Rectangle { + color: theme.controls.listView.highlight + } + + ScrollBar.vertical: HScrollBar { + visible: listView.interactive + } + + // property bool debug: false + + // https://doc.qt.io/qt-5/qml-qtquick-viewtransition.html + // #handling-interrupted-animations + add: Transition { + // ScriptAction { script: if (listView.debug) print("add") } + HNumberAnimation { property: "opacity"; from: 0; to: 1 } + HNumberAnimation { property: "scale"; from: 0; to: 1 } + } + + move: Transition { + // ScriptAction { script: if (listView.debug) print("move") } + HNumberAnimation { property: "opacity"; to: 1 } + HNumberAnimation { property: "scale"; to: 1 } + HNumberAnimation { properties: "x,y" } + } + + remove: Transition { + // ScriptAction { script: if (listView.debug) print("remove") } + HNumberAnimation { property: "opacity"; to: 0 } + HNumberAnimation { property: "scale"; to: 0 } + } + + displaced: Transition { + // ScriptAction { script: if (listView.debug) print("displaced") } + HNumberAnimation { property: "opacity"; to: 1 } + HNumberAnimation { property: "scale"; to: 1 } + HNumberAnimation { properties: "x,y" } + } + + onSelectedCountChanged: if (! selectedCount) lastCheckedDelegateIndex = 0 + MouseArea { id: cursorShapeArea anchors.fill: parent diff --git a/src/gui/Base/HMenu.qml b/src/gui/Base/HMenu.qml index 55b5532b..d85c7770 100644 --- a/src/gui/Base/HMenu.qml +++ b/src/gui/Base/HMenu.qml @@ -6,6 +6,17 @@ import CppUtils 0.1 Menu { id: menu + + property var previouslyFocused: null + + // MenuItems that open popups (or other elements taking focus when opened) + // should set this to null. It will be reset to previouslyFocus when + // the Menu is closed and opened again. + property Item focusOnClosed: previouslyFocused + + readonly property string uuid: CppUtils.uuid() + + modal: true dim: false padding: theme.controls.menu.borderWidth @@ -48,24 +59,17 @@ Menu { previouslyFocused = window.activeFocusItem focusOnClosed = Qt.binding(() => previouslyFocused) } + onOpened: { window.visibleMenus[uuid] = this window.visibleMenusChanged() } + onClosed: { if (focusOnClosed) focusOnClosed.forceActiveFocus() delete window.visibleMenus[uuid] window.visibleMenusChanged() } + Component.onDestruction: closed() - - - property var previouslyFocused: null - - // MenuItems that open popups (or other elements taking focus when opened) - // should set this to null. It will be reset to previouslyFocus when - // the Menu is closed and opened again. - property Item focusOnClosed: previouslyFocused - - readonly property string uuid: CppUtils.uuid() } diff --git a/src/gui/Base/HMenuItem.qml b/src/gui/Base/HMenuItem.qml index 7dfc3822..3181df09 100644 --- a/src/gui/Base/HMenuItem.qml +++ b/src/gui/Base/HMenuItem.qml @@ -5,6 +5,11 @@ import QtQuick.Controls 2.12 MenuItem { id: menuItem + + readonly property alias iconItem: contentItem.icon + readonly property alias label: contentItem.label + + spacing: theme.spacing leftPadding: spacing rightPadding: leftPadding @@ -27,8 +32,4 @@ MenuItem { buttonTheme: theme.controls.menuItem label.horizontalAlignment: Label.AlignLeft } - - - readonly property alias iconItem: contentItem.icon - readonly property alias label: contentItem.label } diff --git a/src/gui/Base/HMenuItemPopupSpawner.qml b/src/gui/Base/HMenuItemPopupSpawner.qml index bc052540..d6f466f2 100644 --- a/src/gui/Base/HMenuItemPopupSpawner.qml +++ b/src/gui/Base/HMenuItemPopupSpawner.qml @@ -4,6 +4,11 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 HMenuItem { + property var popup // url or HPopup Component + property bool autoDestruct: true + property var properties: ({}) + + onTriggered: { menu.focusOnClosed = null @@ -16,9 +21,4 @@ HMenuItem { autoDestruct, ) } - - - property var popup // url or HPopup Component - property bool autoDestruct: true - property var properties: ({}) } diff --git a/src/gui/Base/HMxcImage.qml b/src/gui/Base/HMxcImage.qml index f246e184..b28e516d 100644 --- a/src/gui/Base/HMxcImage.qml +++ b/src/gui/Base/HMxcImage.qml @@ -5,15 +5,6 @@ import "../PythonBridge" HImage { id: image - inderterminateProgressBar: isMxc - source: sourceOverride || (show ? cachedPath : "") - - onWidthChanged: Qt.callLater(update) - onHeightChanged: Qt.callLater(update) - onVisibleChanged: Qt.callLater(update) - onMxcChanged: Qt.callLater(update) - Component.onDestruction: if (getFuture) getFuture.cancel() - property string mxc property string title @@ -28,7 +19,6 @@ HImage { readonly property bool isMxc: mxc.startsWith("mxc://") - function update() { if (! py) return // component was destroyed @@ -64,4 +54,14 @@ HImage { }, ) } + + + inderterminateProgressBar: isMxc + source: sourceOverride || (show ? cachedPath : "") + + onWidthChanged: Qt.callLater(update) + onHeightChanged: Qt.callLater(update) + onVisibleChanged: Qt.callLater(update) + onMxcChanged: Qt.callLater(update) + Component.onDestruction: if (getFuture) getFuture.cancel() } diff --git a/src/gui/Base/HNoticePage.qml b/src/gui/Base/HNoticePage.qml index b407eda7..6b62a0b1 100644 --- a/src/gui/Base/HNoticePage.qml +++ b/src/gui/Base/HNoticePage.qml @@ -11,6 +11,7 @@ HRowLayout { property alias backgroundColor: noticeLabelBackground.color property alias radius: noticeLabelBackground.radius + HLabel { id: noticeLabel horizontalAlignment: Text.AlignHCenter diff --git a/src/gui/Base/HNumberAnimation.qml b/src/gui/Base/HNumberAnimation.qml index e5750df0..b444f19c 100644 --- a/src/gui/Base/HNumberAnimation.qml +++ b/src/gui/Base/HNumberAnimation.qml @@ -6,6 +6,7 @@ NumberAnimation { property real factor: 1.0 property real overshoot: 1.0 + duration: theme.animationDuration * Math.max(overshoot / 1.7, 1.0) * factor easing.type: overshoot > 1 ? Easing.OutBack : Easing.Linear easing.overshoot: overshoot diff --git a/src/gui/Base/HPage.qml b/src/gui/Base/HPage.qml index 9026957d..b8ae94af 100644 --- a/src/gui/Base/HPage.qml +++ b/src/gui/Base/HPage.qml @@ -4,14 +4,6 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 Page { - padding: currentSpacing < theme.spacing ? 0 : currentSpacing - background: null - - Keys.onReturnPressed: keyboardAccept() - Keys.onEnterPressed: keyboardAccept() - Keys.onEscapePressed: keyboardCancel() - - property bool useVariableSpacing: true property int currentSpacing: @@ -27,5 +19,12 @@ Page { signal keyboardCancel() + padding: currentSpacing < theme.spacing ? 0 : currentSpacing + background: null + + Keys.onReturnPressed: keyboardAccept() + Keys.onEnterPressed: keyboardAccept() + Keys.onEscapePressed: keyboardCancel() + Behavior on padding { HNumberAnimation {} } } diff --git a/src/gui/Base/HPopup.qml b/src/gui/Base/HPopup.qml index 855a1a48..306f9d3e 100644 --- a/src/gui/Base/HPopup.qml +++ b/src/gui/Base/HPopup.qml @@ -6,6 +6,19 @@ import CppUtils 0.1 Popup { id: popup + + property var previouslyFocused: null + property Item focusOnClosed: previouslyFocused + + readonly property int maximumPreferredWidth: + window.width - leftMargin - rightMargin - leftInset - rightInset + + readonly property int maximumPreferredHeight: + window.height - topMargin - bottomMargin - topInset - bottomInset + + readonly property string uuid: CppUtils.uuid() + + modal: true focus: true padding: 0 @@ -43,17 +56,6 @@ Popup { delete window.visiblePopups[uuid] window.visibleMenusChanged() } + Component.onDestruction: closed() - - - property var previouslyFocused: null - property Item focusOnClosed: previouslyFocused - - readonly property int maximumPreferredWidth: - window.width - leftMargin - rightMargin - leftInset - rightInset - - readonly property int maximumPreferredHeight: - window.height - topMargin - bottomMargin - topInset - bottomInset - - readonly property string uuid: CppUtils.uuid() } diff --git a/src/gui/Base/HProgressBar.qml b/src/gui/Base/HProgressBar.qml index 3e41eb1f..ff98d378 100644 --- a/src/gui/Base/HProgressBar.qml +++ b/src/gui/Base/HProgressBar.qml @@ -9,6 +9,7 @@ ProgressBar { property color backgroundColor: theme.controls.progressBar.background property color foregroundColor: theme.controls.progressBar.foreground + background: Rectangle { implicitWidth: 200 implicitHeight: theme.controls.progressBar.height diff --git a/src/gui/Base/HRectangleBottomBorder.qml b/src/gui/Base/HRectangleBottomBorder.qml index 9fcb9d06..0f45ee76 100644 --- a/src/gui/Base/HRectangleBottomBorder.qml +++ b/src/gui/Base/HRectangleBottomBorder.qml @@ -7,6 +7,7 @@ Item { property alias borderHeight: clipArea.height property alias color: borderRectangle.color + implicitWidth: rectangle.width implicitHeight: rectangle.height diff --git a/src/gui/Base/HRepeater.qml b/src/gui/Base/HRepeater.qml index 49d63390..e59f222b 100644 --- a/src/gui/Base/HRepeater.qml +++ b/src/gui/Base/HRepeater.qml @@ -7,7 +7,6 @@ import QtQuick 2.12 Repeater { id: repeater - readonly property var childrenImplicitWidth: { const widths = [] diff --git a/src/gui/Base/HRoomAvatar.qml b/src/gui/Base/HRoomAvatar.qml index d0821fe9..9efa33bf 100644 --- a/src/gui/Base/HRoomAvatar.qml +++ b/src/gui/Base/HRoomAvatar.qml @@ -3,13 +3,13 @@ import QtQuick 2.12 HAvatar { + property string roomId + property string displayName + + name: displayName[0] === "#" && displayName.length > 1 ? displayName.substring(1) : displayName title: "room_" + roomId + ".avatar" - - - property string roomId - property string displayName } diff --git a/src/gui/Base/HScrollBar.qml b/src/gui/Base/HScrollBar.qml index 85d643bc..51815fd4 100644 --- a/src/gui/Base/HScrollBar.qml +++ b/src/gui/Base/HScrollBar.qml @@ -5,6 +5,7 @@ import QtQuick.Controls 2.12 ScrollBar { id: scrollBar + minimumSize: (Math.min(height / 1.5, 48) * theme.uiScale) / height opacity: size < 1 && (active || hovered) ? 1 : 0 padding: 0 diff --git a/src/gui/Base/HSelectableLabel.qml b/src/gui/Base/HSelectableLabel.qml index 40c23cc8..602b18c1 100644 --- a/src/gui/Base/HSelectableLabel.qml +++ b/src/gui/Base/HSelectableLabel.qml @@ -5,6 +5,19 @@ import QtQuick.Controls 2.12 TextEdit { id: label + + property bool enableLinkActivation: true + + function selectWordAt(position) { + label.cursorPosition = positionAt(position.x, position.y) + label.selectWord() + } + + function selectAllText() { + label.selectAll() + } + + font.family: theme.fontFamily.sans font.pixelSize: theme.fontSize.normal color: theme.colors.text @@ -18,20 +31,6 @@ TextEdit { selectByMouse: true onLinkActivated: if (enableLinkActivation) Qt.openUrlExternally(link) - - property bool enableLinkActivation: true - - - function selectWordAt(position) { - label.cursorPosition = positionAt(position.x, position.y) - label.selectWord() - } - - function selectAllText() { - label.selectAll() - } - - MouseArea { anchors.fill: label acceptedButtons: Qt.NoButton diff --git a/src/gui/Base/HShortcut.qml b/src/gui/Base/HShortcut.qml index 040db6f7..6da39fd7 100644 --- a/src/gui/Base/HShortcut.qml +++ b/src/gui/Base/HShortcut.qml @@ -3,10 +3,10 @@ import QtQuick 2.12 Shortcut { - enabled: ! window.anyPopupOrMenu && active - context: Qt.ApplicationShortcut - - // TODO: use enabled + a Binding with restoreValue when switch to Qt 5.15 property bool active: true + + + enabled: ! window.anyPopupOrMenu && active + context: Qt.ApplicationShortcut } diff --git a/src/gui/Base/HSlider.qml b/src/gui/Base/HSlider.qml index ea9d9f74..edf73717 100644 --- a/src/gui/Base/HSlider.qml +++ b/src/gui/Base/HSlider.qml @@ -5,10 +5,6 @@ import QtQuick.Controls 2.12 Slider { id: slider - leftPadding: 0 - rightPadding: leftPadding - topPadding: 0 - bottomPadding: topPadding property bool enableRadius: true property bool fullHeight: false @@ -18,6 +14,12 @@ Slider { property alias toolTip: toolTip property alias mouseArea: mouseArea + + leftPadding: 0 + rightPadding: leftPadding + topPadding: 0 + bottomPadding: topPadding + background: Rectangle { color: backgroundColor x: slider.leftPadding diff --git a/src/gui/Base/HSwipeView.qml b/src/gui/Base/HSwipeView.qml index 1b80084e..f02511f6 100644 --- a/src/gui/Base/HSwipeView.qml +++ b/src/gui/Base/HSwipeView.qml @@ -7,21 +7,6 @@ import "../ShortcutBundles" SwipeView { id: swipeView - Component.onCompleted: if (! changed) { - setCurrentIndex(window.getState(this, "currentIndex", defaultIndex)) - saveEnabled = true - } - - onCurrentIndexChanged: { - if (saveEnabled) window.saveState(this) - - if (currentIndex < previousIndex) lastMove = HSwipeView.Move.ToPrevious - if (currentIndex > previousIndex) lastMove = HSwipeView.Move.ToNext - - previousIndex = currentIndex - } - - enum Move { ToPrevious, ToNext } property string saveName: "" @@ -52,6 +37,20 @@ SwipeView { } + Component.onCompleted: if (! changed) { + setCurrentIndex(window.getState(this, "currentIndex", defaultIndex)) + saveEnabled = true + } + + onCurrentIndexChanged: { + if (saveEnabled) window.saveState(this) + + if (currentIndex < previousIndex) lastMove = HSwipeView.Move.ToPrevious + if (currentIndex > previousIndex) lastMove = HSwipeView.Move.ToNext + + previousIndex = currentIndex + } + TabShortcuts { container: swipeView } diff --git a/src/gui/Base/HTabBar.qml b/src/gui/Base/HTabBar.qml index cb851b30..a1a75fba 100644 --- a/src/gui/Base/HTabBar.qml +++ b/src/gui/Base/HTabBar.qml @@ -6,6 +6,7 @@ import "../ShortcutBundles" TabBar { id: tabBar + spacing: 0 position: TabBar.Header @@ -18,7 +19,6 @@ TabBar { } } - TabShortcuts { container: tabBar } diff --git a/src/gui/Base/HTabButton.qml b/src/gui/Base/HTabButton.qml index ab545082..63cb02c4 100644 --- a/src/gui/Base/HTabButton.qml +++ b/src/gui/Base/HTabButton.qml @@ -6,27 +6,6 @@ import QtQuick.Layouts 1.12 TabButton { id: button - spacing: theme.spacing - topPadding: spacing / 1.5 - bottomPadding: topPadding - leftPadding: spacing - rightPadding: leftPadding - - icon.color: theme.icons.colorize - - implicitWidth: Math.max( - implicitBackgroundWidth + leftInset + rightInset, - // FIXME: why is *2 needed to not get ellided text in AddAccount page? - implicitContentWidth + leftPadding * 2 + rightPadding * 2, - ) - implicitHeight: Math.max( - implicitBackgroundHeight + topInset + bottomInset, - implicitContentHeight + topPadding + bottomPadding, - ) - - // Prevent button from gaining focus and being highlighted on click - focusPolicy: Qt.TabFocus - readonly property alias iconItem: contentItem.icon readonly property alias label: contentItem.label @@ -48,6 +27,27 @@ TabButton { } + spacing: theme.spacing + topPadding: spacing / 1.5 + bottomPadding: topPadding + leftPadding: spacing + rightPadding: leftPadding + + icon.color: theme.icons.colorize + + implicitWidth: Math.max( + implicitBackgroundWidth + leftInset + rightInset, + // FIXME: why is *2 needed to not get ellided text in AddAccount page? + implicitContentWidth + leftPadding * 2 + rightPadding * 2, + ) + implicitHeight: Math.max( + implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding, + ) + + // Prevent button from gaining focus and being highlighted on click + focusPolicy: Qt.TabFocus + background: HButtonBackground { button: button buttonTheme: theme.controls.tab diff --git a/src/gui/Base/HTextArea.qml b/src/gui/Base/HTextArea.qml index 6a172e95..daac70e1 100644 --- a/src/gui/Base/HTextArea.qml +++ b/src/gui/Base/HTextArea.qml @@ -6,7 +6,6 @@ import QtQuick.Controls 2.12 TextArea { id: textArea - property string saveName: "" property var saveId: "ALL" property var saveProperties: ["text"] @@ -27,7 +26,6 @@ TextArea { property string previousDefaultText: "" // private - function reset() { clear(); text = Qt.binding(() => defaultText || "") } function insertAtCursor(text) { insert(cursorPosition, text) } diff --git a/src/gui/Base/HTextContextMenu.qml b/src/gui/Base/HTextContextMenu.qml index 8d7595c3..3e378bb7 100644 --- a/src/gui/Base/HTextContextMenu.qml +++ b/src/gui/Base/HTextContextMenu.qml @@ -7,7 +7,6 @@ HMenu { property bool hadPersistentSelection: false // TODO: use a Qt 5.15 Binding - function spawn(atMousePosition=true) { hadPersistentSelection = control.persistentSelection control.persistentSelection = true @@ -25,7 +24,6 @@ HMenu { Component.onDestruction: control.persistentSelection = hadPersistentSelection - HMenuItem { icon.name: "undo" text: qsTr("Undo") diff --git a/src/gui/Base/HTextField.qml b/src/gui/Base/HTextField.qml index 8c99c1e0..5020e91b 100644 --- a/src/gui/Base/HTextField.qml +++ b/src/gui/Base/HTextField.qml @@ -5,6 +5,33 @@ import QtQuick.Controls 2.12 TextField { id: field + + property string saveName: "" + property var saveId: "ALL" + property var saveProperties: ["text"] + + property bool error: false + + property alias radius: textFieldBackground.radius + property bool bordered: true + + property color backgroundColor: theme.controls.textField.background + property color borderColor: theme.controls.textField.border + property color errorBorder: theme.controls.textField.errorBorder + + property color focusedBackgroundColor: + theme.controls.textField.focusedBackground + property color focusedBorderColor: theme.controls.textField.focusedBorder + + property var disabledText: null + property var defaultText: null + readonly property bool changed: text !== (defaultText || "") + + property string previousDefaultText: "" // private + + function reset() { clear(); text = Qt.binding(() => defaultText || "")} + + text: defaultText || "" opacity: enabled ? 1 : theme.disabledElementsOpacity selectByMouse: true @@ -75,33 +102,6 @@ TextField { event.accepted = cursorPosition === length && ! selectedText - property string saveName: "" - property var saveId: "ALL" - property var saveProperties: ["text"] - - property bool error: false - - property alias radius: textFieldBackground.radius - property bool bordered: true - - property color backgroundColor: theme.controls.textField.background - property color borderColor: theme.controls.textField.border - property color errorBorder: theme.controls.textField.errorBorder - - property color focusedBackgroundColor: - theme.controls.textField.focusedBackground - property color focusedBorderColor: theme.controls.textField.focusedBorder - - property var disabledText: null - property var defaultText: null - readonly property bool changed: text !== (defaultText || "") - - property string previousDefaultText: "" // private - - - function reset() { clear(); text = Qt.binding(() => defaultText || "")} - - Binding on color { value: "transparent" when: disabledText !== null && ! field.enabled diff --git a/src/gui/Base/HTile/ContentRow.qml b/src/gui/Base/HTile/ContentRow.qml index 08c63248..e0dea128 100644 --- a/src/gui/Base/HTile/ContentRow.qml +++ b/src/gui/Base/HTile/ContentRow.qml @@ -4,9 +4,9 @@ import QtQuick 2.12 import ".." HRowLayout { + property HTile tile + + spacing: tile.spacing opacity: tile.contentOpacity - - - property HTile tile } diff --git a/src/gui/Base/HTile/HTile.qml b/src/gui/Base/HTile/HTile.qml index 04eabc4f..86c3ce4c 100644 --- a/src/gui/Base/HTile/HTile.qml +++ b/src/gui/Base/HTile/HTile.qml @@ -5,26 +5,15 @@ import ".." HButton { id: tile - topPadding: padded ? spacing / (compact ? 4 : 2) : 0 - bottomPadding: topPadding - - Keys.onEnterPressed: leftClicked() - Keys.onReturnPressed: leftClicked() - Keys.onSpacePressed: leftClicked() - Keys.onMenuPressed: doRightClick(false) + property bool compact: window.settings.compactMode + property real contentOpacity: 1 + property Component contextMenu: null signal leftClicked() signal rightClicked() signal longPressed() - - property bool compact: window.settings.compactMode - property real contentOpacity: 1 - - property Component contextMenu: null - - function openMenu(atCursor=true) { if (! contextMenu) return const menu = contextMenu.createObject(tile) @@ -38,6 +27,15 @@ HButton { } + topPadding: padded ? spacing / (compact ? 4 : 2) : 0 + bottomPadding: topPadding + + Keys.onEnterPressed: leftClicked() + Keys.onReturnPressed: leftClicked() + Keys.onSpacePressed: leftClicked() + Keys.onMenuPressed: doRightClick(false) + + Behavior on topPadding { HNumberAnimation {} } Behavior on bottomPadding { HNumberAnimation {} } diff --git a/src/gui/Base/HTile/SubtitleLabel.qml b/src/gui/Base/HTile/SubtitleLabel.qml index 5cca552c..0d5af75c 100644 --- a/src/gui/Base/HTile/SubtitleLabel.qml +++ b/src/gui/Base/HTile/SubtitleLabel.qml @@ -7,6 +7,7 @@ import ".." HLabel { property HTile tile + textFormat: Text.StyledText font.pixelSize: theme.fontSize.small verticalAlignment: Qt.AlignVCenter diff --git a/src/gui/Base/HTile/TitleRightInfoLabel.qml b/src/gui/Base/HTile/TitleRightInfoLabel.qml index bdd1a6ca..2fb623a4 100644 --- a/src/gui/Base/HTile/TitleRightInfoLabel.qml +++ b/src/gui/Base/HTile/TitleRightInfoLabel.qml @@ -5,6 +5,9 @@ import QtQuick.Layouts 1.12 import ".." HLabel { + property HTile tile + + font.pixelSize: theme.fontSize.small verticalAlignment: Qt.AlignVCenter color: theme.colors.halfDimText @@ -15,9 +18,5 @@ HLabel { text && tile.width >= 200 * theme.uiScale ? implicitWidth : 0 - - property HTile tile - - Behavior on Layout.maximumWidth { HNumberAnimation {} } } diff --git a/src/gui/Base/HToolTip.qml b/src/gui/Base/HToolTip.qml index edc4947c..8056de3c 100644 --- a/src/gui/Base/HToolTip.qml +++ b/src/gui/Base/HToolTip.qml @@ -6,6 +6,23 @@ import QtQuick.Layouts 1.12 ToolTip { id: toolTip + + property bool instant: false + + property alias label: label + property alias backgroundColor: background.color + + readonly property bool hideNow: ! window.hovered + + + function instantShow() { + if (visible) return + instant = true + open() + instant = false + } + + delay: instant ? 0 : theme.controls.toolTip.delay padding: background.border.width @@ -43,23 +60,6 @@ ToolTip { onHideNowChanged: if (visible && hideNow) toolTip.hide() - - property bool instant: false - - property alias label: label - property alias backgroundColor: background.color - - readonly property bool hideNow: ! window.hovered - - - function instantShow() { - if (visible) return - instant = true - open() - instant = false - } - - TapHandler { onTapped: toolTip.hide() } diff --git a/src/gui/Base/HUserAvatar.qml b/src/gui/Base/HUserAvatar.qml index 8fb91f31..7f9c0ebe 100644 --- a/src/gui/Base/HUserAvatar.qml +++ b/src/gui/Base/HUserAvatar.qml @@ -3,10 +3,6 @@ import QtQuick 2.12 HAvatar { - name: displayName || userId.substring(1) // no leading @ - title: "user_" + userId + ".avatar" - - property string userId property string displayName property string presence: "" @@ -18,6 +14,9 @@ HAvatar { readonly property bool moderator: powerLevel >= 50 && ! admin + name: displayName || userId.substring(1) // no leading @ + title: "user_" + userId + ".avatar" + HLoader { active: admin || moderator || invited anchors.top: parent.top @@ -53,6 +52,11 @@ HAvatar { } HLoader { + property int diameter: + window.settings.compactMode ? + theme.controls.presence.radius * 2 : + theme.controls.presence.radius * 2.5 + active: presence && presence !== "offline" anchors.bottom: parent.bottom anchors.right: parent.right @@ -61,11 +65,6 @@ HAvatar { opacity: theme.controls.presence.opacity z: 300 - property int diameter: - window.settings.compactMode ? - theme.controls.presence.radius * 2 : - theme.controls.presence.radius * 2.5 - sourceComponent: Rectangle { width: diameter height: diameter diff --git a/src/gui/Base/MediaPlayer/AudioPlayer.qml b/src/gui/Base/MediaPlayer/AudioPlayer.qml index d81f18e2..2c7692ab 100644 --- a/src/gui/Base/MediaPlayer/AudioPlayer.qml +++ b/src/gui/Base/MediaPlayer/AudioPlayer.qml @@ -5,16 +5,16 @@ import QtAV 1.7 OSD { id: osd + + property alias source: audioPlayer.source + + audioOnly: true media: audioPlayer implicitWidth: osd.width implicitHeight: osd.height - - property alias source: audioPlayer.source - - MediaPlayer { id: audioPlayer autoLoad: window.settings.media.autoLoad diff --git a/src/gui/Base/MediaPlayer/OSD.qml b/src/gui/Base/MediaPlayer/OSD.qml index baef2f90..cf015a4f 100644 --- a/src/gui/Base/MediaPlayer/OSD.qml +++ b/src/gui/Base/MediaPlayer/OSD.qml @@ -7,20 +7,6 @@ import "../../Base" HColumnLayout { id: osd - visible: osdScaleTransform.yScale > 0 - - transform: Scale { - id: osdScaleTransform - yScale: audioOnly || - osdHover.hovered || - media.playbackState !== MediaPlayer.PlayingState || - osd.showup ? - 1 : 0 - origin.y: osd.height - - Behavior on yScale { HNumberAnimation {} } - } - property QtObject media: parent // QtAV.Video or QtAV.MediaPlayer property bool audioOnly: false @@ -35,12 +21,6 @@ HColumnLayout { savedDuration ? Math.min(media.position, savedDuration) : media.position - - onShowupChanged: if (showup) osdHideTimer.restart() - onDurationChanged: if (duration) savedDuration = duration - onAspectRatioChanged: if (aspectRatio) savedAspectRatio = aspectRatio - - function togglePlay() { media.playbackState === MediaPlayer.PlayingState ? media.pause() : media.play() @@ -52,6 +32,24 @@ HColumnLayout { } + visible: osdScaleTransform.yScale > 0 + + transform: Scale { + id: osdScaleTransform + yScale: audioOnly || + osdHover.hovered || + media.playbackState !== MediaPlayer.PlayingState || + osd.showup ? + 1 : 0 + origin.y: osd.height + + Behavior on yScale { HNumberAnimation {} } + } + + onShowupChanged: if (showup) osdHideTimer.restart() + onDurationChanged: if (duration) savedDuration = duration + onAspectRatioChanged: if (aspectRatio) savedAspectRatio = aspectRatio + HoverHandler { id: osdHover } Timer { @@ -77,6 +75,13 @@ HColumnLayout { HToolTip { id: previewToolTip + + readonly property int wantTimestamp: + visible ? + savedDuration * + (timeSlider.mouseArea.mouseX / timeSlider.mouseArea.width) : + -1 + x: timeSlider.mouseArea.mouseX - width / 2 visible: ! audioOnly && @@ -88,20 +93,6 @@ HColumnLayout { ! timeSlider.pressed && timeSlider.mouseArea.containsMouse - readonly property int wantTimestamp: - visible ? - savedDuration * - (timeSlider.mouseArea.mouseX / timeSlider.mouseArea.width) : - -1 - - Timer { - interval: 300 - running: previewToolTip.visible - repeat: true - triggeredOnStart: true - onTriggered: preview.timestamp = previewToolTip.wantTimestamp - } - contentItem: VideoPreview { id: preview implicitHeight: Math.min( @@ -129,11 +120,19 @@ HColumnLayout { } } } - } - Binding on value { - value: boundPosition - when: ! timeSlider.pressed + Binding on value { + value: boundPosition + when: ! timeSlider.pressed + } + + Timer { + interval: 300 + running: previewToolTip.visible + repeat: true + triggeredOnStart: true + onTriggered: preview.timestamp = previewToolTip.wantTimestamp + } } } diff --git a/src/gui/Base/MediaPlayer/OSDButton.qml b/src/gui/Base/MediaPlayer/OSDButton.qml index 3fed282a..a3eb7f2c 100644 --- a/src/gui/Base/MediaPlayer/OSDButton.qml +++ b/src/gui/Base/MediaPlayer/OSDButton.qml @@ -3,7 +3,6 @@ import QtQuick 2.12 import "../../Base" - HButton { backgroundColor: "transparent" iconItem.dimension: theme.mediaPlayer.controls.iconSize diff --git a/src/gui/Base/MediaPlayer/OSDLabel.qml b/src/gui/Base/MediaPlayer/OSDLabel.qml index 1c847b63..2eef077f 100644 --- a/src/gui/Base/MediaPlayer/OSDLabel.qml +++ b/src/gui/Base/MediaPlayer/OSDLabel.qml @@ -4,7 +4,6 @@ import QtQuick 2.12 import QtQuick.Layouts 1.12 import "../../Base" - HLabel { Layout.leftMargin: theme.spacing / 2 Layout.rightMargin: Layout.leftMargin diff --git a/src/gui/Base/MultiviewPane.qml b/src/gui/Base/MultiviewPane.qml index 3bc75ec1..f3905e87 100644 --- a/src/gui/Base/MultiviewPane.qml +++ b/src/gui/Base/MultiviewPane.qml @@ -6,9 +6,8 @@ import QtQuick.Layouts 1.12 HDrawer { id: pane - defaultSize: buttonRepeater.count * buttonWidth - minimumSize: buttonWidth + default property alias swipeViewData: swipeView.contentData property color buttonsBackgroundColor @@ -18,8 +17,9 @@ HDrawer { readonly property alias buttonRepeater: buttonRepeater readonly property alias swipeView: swipeView - default property alias swipeViewData: swipeView.contentData + defaultSize: buttonRepeater.count * buttonWidth + minimumSize: buttonWidth HColumnLayout { anchors.fill: parent diff --git a/src/gui/DebugConsole.qml b/src/gui/DebugConsole.qml index d2400a6d..3f89a316 100644 --- a/src/gui/DebugConsole.qml +++ b/src/gui/DebugConsole.qml @@ -8,37 +8,6 @@ import "ShortcutBundles" HDrawer { id: debugConsole - objectName: "debugConsole" - edge: Qt.TopEdge - x: horizontal ? 0 : referenceSizeParent.width / 2 - width / 2 - y: vertical ? 0 : referenceSizeParent.height / 2 - height / 2 - width: horizontal ? calculatedSize : Math.min(window.width, 720) - height: vertical ? calculatedSize : Math.min(window.height, 720) - defaultSize: 400 - z: 9999 - position: 0 - - onTargetChanged: { - commandsView.model.insert(0, { - input: "t = " + String(target), - output: "", - error: false, - }) - } - - onVisibleChanged: { - if (visible) { - previouslyFocused = window.activeFocusItem - forceActiveFocus() - } else if (previouslyFocused) { - previouslyFocused.forceActiveFocus() - } - } - - onHistoryEntryChanged: - inputField.text = - historyEntry === -1 ? "" : history.slice(-historyEntry - 1)[0] - property Item previouslyFocused: null @@ -74,7 +43,6 @@ HDrawer { readonly property alias commandsView: commandsView - function toggle(targetItem=null, js="", addToHistory=false) { if (debugConsole.visible) { debugConsole.visible = false @@ -90,7 +58,6 @@ HDrawer { if (js) debugConsole.runJS(js, addToHistory) } - function runJS(input, addToHistory=true) { if (addToHistory && history.slice(-1)[0] !== input) { history.push(input) @@ -135,6 +102,37 @@ HDrawer { } + objectName: "debugConsole" + edge: Qt.TopEdge + x: horizontal ? 0 : referenceSizeParent.width / 2 - width / 2 + y: vertical ? 0 : referenceSizeParent.height / 2 - height / 2 + width: horizontal ? calculatedSize : Math.min(window.width, 720) + height: vertical ? calculatedSize : Math.min(window.height, 720) + defaultSize: 400 + z: 9999 + position: 0 + + onTargetChanged: { + commandsView.model.insert(0, { + input: "t = " + String(target), + output: "", + error: false, + }) + } + + onVisibleChanged: { + if (visible) { + previouslyFocused = window.activeFocusItem + forceActiveFocus() + } else if (previouslyFocused) { + previouslyFocused.forceActiveFocus() + } + } + + onHistoryEntryChanged: + inputField.text = + historyEntry === -1 ? "" : history.slice(-historyEntry - 1)[0] + HShortcut { sequences: settings.keys.toggleDebugConsole onActivated: debugConsole.toggle() diff --git a/src/gui/Dialogs/ExportKeys.qml b/src/gui/Dialogs/ExportKeys.qml index a8fbeb99..a66ed74f 100644 --- a/src/gui/Dialogs/ExportKeys.qml +++ b/src/gui/Dialogs/ExportKeys.qml @@ -5,24 +5,13 @@ import Qt.labs.platform 1.1 import "../Popups" HFileDialogOpener { - fill: false - dialog.title: qsTr("Save decryption keys file as...") - dialog.fileMode: FileDialog.SaveFile - onFilePicked: { - exportPasswordPopup.file = file - exportPasswordPopup.open() - } - - // This is used for the SignOutPopup to know when the export is done // so it can close signal done() - property string userId: "" property bool exporting: false - function exportKeys(file, passphrase) { exporting = true @@ -35,14 +24,23 @@ HFileDialogOpener { } + fill: false + dialog.title: qsTr("Save decryption keys file as...") + dialog.fileMode: FileDialog.SaveFile + onFilePicked: { + exportPasswordPopup.file = file + exportPasswordPopup.open() + } + PasswordPopup { id: exportPasswordPopup + + property url file: "" + summary.text: qsTr("Passphrase to protect this file:") validateButton.text: qsTr("Export") validateButton.icon.name: "export-keys" onAcceptedPasswordChanged: exportKeys(file, acceptedPassword) - - property url file: "" } } diff --git a/src/gui/Dialogs/HFileDialogOpener.qml b/src/gui/Dialogs/HFileDialogOpener.qml index c978b53f..89d95e05 100644 --- a/src/gui/Dialogs/HFileDialogOpener.qml +++ b/src/gui/Dialogs/HFileDialogOpener.qml @@ -5,13 +5,8 @@ import Qt.labs.platform 1.1 Item { id: opener - anchors.fill: fill ? parent : undefined - - - signal filePicked(string file) - signal filesPicked(var files) - signal cancelled() + enum FileType { All, Images } property bool fill: true @@ -24,9 +19,14 @@ Item { property string selectSubject: dialog.fileMode === FileDialog.SaveFile ? qsTr("file") : qsTr("open") - enum FileType { All, Images } property int fileType: HFileDialogOpener.FileType.All + signal filePicked(string file) + signal filesPicked(var files) + signal cancelled() + + + anchors.fill: fill ? parent : undefined TapHandler { enabled: opener.enabled && fill; onTapped: fileDialog.open() } diff --git a/src/gui/Dialogs/ImportKeys.qml b/src/gui/Dialogs/ImportKeys.qml index 0d008866..f84fa0ee 100644 --- a/src/gui/Dialogs/ImportKeys.qml +++ b/src/gui/Dialogs/ImportKeys.qml @@ -7,6 +7,10 @@ import "../Popups" import "../PythonBridge" HFileDialogOpener { + property string userId: "" + property Future importFuture: null + + fill: false dialog.title: qsTr("Select a decryption keys file to import") onFilePicked: { @@ -14,26 +18,11 @@ HFileDialogOpener { importPasswordPopup.open() } - - property string userId: "" - property Future importFuture: null - - PasswordPopup { id: importPasswordPopup - summary.text: - importFuture ? - qsTr("This might take a while...") : - qsTr("Passphrase used to protect this file:") - validateButton.text: qsTr("Import") - validateButton.icon.name: "import-keys" - - onClosed: if (importFuture) importFuture.cancel() - property url file: "" - function verifyPassword(pass, callback) { const call = py.callClientCoro const path = file.toString().replace(/^file:\/\//, "") @@ -72,6 +61,14 @@ HFileDialogOpener { }) } + summary.text: + importFuture ? + qsTr("This might take a while...") : + qsTr("Passphrase used to protect this file:") + validateButton.text: qsTr("Import") + validateButton.icon.name: "import-keys" + + onClosed: if (importFuture) importFuture.cancel() Binding on closePolicy { value: Popup.CloseOnEscape diff --git a/src/gui/Dialogs/SendFilePicker.qml b/src/gui/Dialogs/SendFilePicker.qml index 56a3987c..6a0b1a77 100644 --- a/src/gui/Dialogs/SendFilePicker.qml +++ b/src/gui/Dialogs/SendFilePicker.qml @@ -4,6 +4,11 @@ import QtQuick 2.12 import Qt.labs.platform 1.1 HFileDialogOpener { + property string userId + property string roomId + property bool destroyWhenDone: false + + fill: false dialog.title: qsTr("Select a file to send") dialog.fileMode: FileDialog.OpenFiles @@ -23,9 +28,4 @@ HFileDialogOpener { } onCancelled: if (destroyWhenDone) destroy() - - - property string userId - property string roomId - property bool destroyWhenDone: false } diff --git a/src/gui/IdleManager.qml b/src/gui/IdleManager.qml index 4fd2c002..046df980 100644 --- a/src/gui/IdleManager.qml +++ b/src/gui/IdleManager.qml @@ -8,7 +8,6 @@ Timer { readonly property ListModel accounts: ModelStore.get("accounts") readonly property var accountsSet: new Set() - function setPresence(userId, presence) { py.callClientCoro(userId, "set_presence", [presence, undefined, false]) } diff --git a/src/gui/MainPane/AccountBar.qml b/src/gui/MainPane/AccountBar.qml index d1a4c391..0815ba92 100644 --- a/src/gui/MainPane/AccountBar.qml +++ b/src/gui/MainPane/AccountBar.qml @@ -7,14 +7,13 @@ import "../Base" import "../Base/HTile" Rectangle { - implicitHeight: accountList.count >= 2 ? accountList.contentHeight : 0 - color: theme.mainPane.accountBar.background - - property RoomList roomList readonly property alias accountList: accountList + implicitHeight: accountList.count >= 2 ? accountList.contentHeight : 0 + color: theme.mainPane.accountBar.background + Behavior on implicitHeight { HNumberAnimation {} } HGridView { diff --git a/src/gui/MainPane/AccountContextMenu.qml b/src/gui/MainPane/AccountContextMenu.qml index bb6755ec..f910fdbb 100644 --- a/src/gui/MainPane/AccountContextMenu.qml +++ b/src/gui/MainPane/AccountContextMenu.qml @@ -13,10 +13,8 @@ HMenu { property string presence property string statusMsg - signal wentToAccountPage() - function setPresence(presence, statusMsg=undefined) { py.callClientCoro(userId, "set_presence", [presence, statusMsg]) } @@ -24,7 +22,6 @@ HMenu { onOpened: statusText.forceActiveFocus() - HLabeledItem { id: statusMsgLabel enabled: presence && presence !== "offline" diff --git a/src/gui/MainPane/AccountDelegate.qml b/src/gui/MainPane/AccountDelegate.qml index 6f55fb39..c88a51ef 100644 --- a/src/gui/MainPane/AccountDelegate.qml +++ b/src/gui/MainPane/AccountDelegate.qml @@ -7,6 +7,39 @@ import "../Base/HTile" HTile { id: account + + property bool enableKeybinds: false + property bool filterActive: false + + readonly property bool collapsed: + (window.uiState.collapseAccounts[model.id] || false) && + ! filterActive + + readonly property alias avatar: title + readonly property alias totalMessageIndicator: totalMessageIndicator + readonly property alias title: title + readonly property alias addChat: addChat + readonly property alias expand: expand + + signal wentToAccountPage() + + function setCollapse(collapse) { + window.uiState.collapseAccounts[model.id] = collapse + window.uiStateChanged() + + py.callCoro("set_account_collapse", [model.id, collapse]) + } + + function toggleCollapse() { + setCollapse(! collapsed) + } + + function togglePresence(presence) { + if (model.presence === presence) presence = "online" + py.callClientCoro(model.id, "set_presence", [presence]) + } + + backgroundColor: theme.mainPane.listView.account.background contentItem: ContentRow { @@ -171,40 +204,6 @@ HTile { onWentToAccountPage: account.wentToAccountPage() } - - property bool enableKeybinds: false - property bool filterActive: false - - readonly property bool collapsed: - (window.uiState.collapseAccounts[model.id] || false) && - ! filterActive - - readonly property alias avatar: title - readonly property alias totalMessageIndicator: totalMessageIndicator - readonly property alias title: title - readonly property alias addChat: addChat - readonly property alias expand: expand - - signal wentToAccountPage() - - - function setCollapse(collapse) { - window.uiState.collapseAccounts[model.id] = collapse - window.uiStateChanged() - - py.callCoro("set_account_collapse", [model.id, collapse]) - } - - function toggleCollapse() { - setCollapse(! collapsed) - } - - function togglePresence(presence) { - if (model.presence === presence) presence = "online" - py.callClientCoro(model.id, "set_presence", [presence]) - } - - HShortcut { enabled: enableKeybinds sequences: window.settings.keys.accountSettings diff --git a/src/gui/MainPane/BottomBar.qml b/src/gui/MainPane/BottomBar.qml index 9de82fee..af5cdc97 100644 --- a/src/gui/MainPane/BottomBar.qml +++ b/src/gui/MainPane/BottomBar.qml @@ -5,17 +5,16 @@ import QtQuick.Layouts 1.12 import "../Base" Rectangle { - // Hide filter field overflowing for a sec on size changes - clip: true - implicitHeight: theme.baseElementsHeight - color: theme.mainPane.bottomBar.background - - property RoomList roomList readonly property alias addAccountButton: addAccountButton readonly property alias filterField: filterField + // Hide filter field overflowing for a sec on size changes + clip: true + implicitHeight: theme.baseElementsHeight + color: theme.mainPane.bottomBar.background + HRowLayout { anchors.fill: parent diff --git a/src/gui/MainPane/MainPane.qml b/src/gui/MainPane/MainPane.qml index cda68341..fbd3c586 100644 --- a/src/gui/MainPane/MainPane.qml +++ b/src/gui/MainPane/MainPane.qml @@ -6,15 +6,11 @@ import "../Base" HDrawer { id: mainPane - saveName: "mainPane" - background: Rectangle { color: theme.mainPane.background } - minimumSize: theme.mainPane.minimumSize readonly property alias accountBar: accountBar readonly property alias roomList: roomList readonly property alias bottomBar: bottomBar - function toggleFocus() { if (bottomBar.filterField.activeFocus) { pageLoader.takeFocus() @@ -26,6 +22,10 @@ HDrawer { } + saveName: "mainPane" + background: Rectangle { color: theme.mainPane.background } + minimumSize: theme.mainPane.minimumSize + Behavior on opacity { HNumberAnimation {} } Binding on visible { diff --git a/src/gui/MainPane/MessageIndicator.qml b/src/gui/MainPane/MessageIndicator.qml index 63b1dc27..dc137f3c 100644 --- a/src/gui/MainPane/MessageIndicator.qml +++ b/src/gui/MainPane/MessageIndicator.qml @@ -4,6 +4,13 @@ import QtQuick 2.12 import "../Base" HLabel { + property QtObject indicatorTheme + property int unreads: 0 + property int highlights: 0 + property bool localUnreads: false + property bool localHighlights: false + + text: unreads >= 1000000 ? Math.floor(unreads / 1000000) + "M" : unreads >= 1000 ? Math.floor(unreads / 1000) + "K" : @@ -31,13 +38,5 @@ HLabel { Behavior on color { HColorAnimation {} } } - - property QtObject indicatorTheme - property int unreads: 0 - property int highlights: 0 - property bool localUnreads: false - property bool localHighlights: false - - Behavior on scale { HNumberAnimation {} } } diff --git a/src/gui/MainPane/RoomDelegate.qml b/src/gui/MainPane/RoomDelegate.qml index 705b1cc2..a0b5237d 100644 --- a/src/gui/MainPane/RoomDelegate.qml +++ b/src/gui/MainPane/RoomDelegate.qml @@ -9,6 +9,22 @@ import "../Base/HTile" HTile { id: room + + readonly property bool joined: ! invited && ! parted + readonly property bool invited: model.inviter_id && ! parted + readonly property bool parted: model.left + + readonly property ListModel eventModel: + ModelStore.get(model.for_account, model.id, "events") + + // FIXME: binding loop + readonly property QtObject accountModel: + ModelStore.get("accounts").find(model.for_account) + + readonly property QtObject lastEvent: + eventModel.count > 0 ? eventModel.get(0) : null + + backgroundColor: theme.mainPane.listView.room.background leftPadding: theme.spacing * 2 rightPadding: theme.spacing @@ -179,19 +195,4 @@ HTile { }) } } - - - readonly property bool joined: ! invited && ! parted - readonly property bool invited: model.inviter_id && ! parted - readonly property bool parted: model.left - - readonly property ListModel eventModel: - ModelStore.get(model.for_account, model.id, "events") - - // FIXME: binding loop - readonly property QtObject accountModel: - ModelStore.get("accounts").find(model.for_account) - - readonly property QtObject lastEvent: - eventModel.count > 0 ? eventModel.get(0) : null } diff --git a/src/gui/MainPane/RoomList.qml b/src/gui/MainPane/RoomList.qml index 1113660c..5f97dc69 100644 --- a/src/gui/MainPane/RoomList.qml +++ b/src/gui/MainPane/RoomList.qml @@ -8,83 +8,19 @@ import "../Base" HListView { id: roomList - model: ModelStore.get("all_rooms") - - delegate: DelegateChooser { - role: "type" - - DelegateChoice { - roleValue: "Account" - AccountDelegate { - width: roomList.width - leftPadding: theme.spacing - rightPadding: 0 // the right buttons have padding - - filterActive: Boolean(filter) - enableKeybinds: Boolean( - roomList.model.get(currentIndex) && ( - roomList.model.get(currentIndex).for_account || - roomList.model.get(currentIndex).id - ) === model.id - ) - - totalMessageIndicator.visible: false - - onLeftClicked: showItemAtIndex(model.index) - onCollapsedChanged: - if (wantedUserId === model.id) startCorrectItemSearch() - - onWentToAccountPage: roomList.currentIndex = model.index - } - } - - DelegateChoice { - roleValue: "Room" - RoomDelegate { - width: roomList.width - onLeftClicked: showItemAtIndex(model.index) - } - } - } - - onFilterChanged: { - py.callCoro("set_substring_filter", ["all_rooms", filter], () => { - if (filter) { - currentIndex = 1 // highlight the first matching room - return - } - - const item = model.get(currentIndex) - - if ( - ! filter && - item && ( - currentIndex === 1 || // required, related to the if above - ( - currentShouldBeAccount && - wantedUserId !== item.id - ) || ( - currentShouldBeRoom && ( - wantedUserId !== item.for_account || - wantedRoomId !== item.id - ) - ) - ) - ) - startCorrectItemSearch() - }) - } - property string filter: "" readonly property bool currentShouldBeAccount: window.uiState.page === "Pages/AccountSettings/AccountSettings.qml" || window.uiState.page === "Pages/AddChat/AddChat.qml" + readonly property bool currentShouldBeRoom: window.uiState.page === "Pages/Chat/Chat.qml" + readonly property string wantedUserId: window.uiState.pageProperties.userId || "" + readonly property string wantedRoomId: window.uiState.pageProperties.roomId || "" @@ -99,7 +35,6 @@ HListView { return accounts } - function goToAccount(userId) { accountIndice[userId] + 1 <= model.count -1 && model.get(accountIndice[userId] + 1).type === "Room" ? @@ -200,6 +135,73 @@ HListView { } + model: ModelStore.get("all_rooms") + + delegate: DelegateChooser { + role: "type" + + DelegateChoice { + roleValue: "Account" + AccountDelegate { + width: roomList.width + leftPadding: theme.spacing + rightPadding: 0 // the right buttons have padding + + filterActive: Boolean(filter) + enableKeybinds: Boolean( + roomList.model.get(currentIndex) && ( + roomList.model.get(currentIndex).for_account || + roomList.model.get(currentIndex).id + ) === model.id + ) + + totalMessageIndicator.visible: false + + onLeftClicked: showItemAtIndex(model.index) + onCollapsedChanged: + if (wantedUserId === model.id) startCorrectItemSearch() + + onWentToAccountPage: roomList.currentIndex = model.index + } + } + + DelegateChoice { + roleValue: "Room" + RoomDelegate { + width: roomList.width + onLeftClicked: showItemAtIndex(model.index) + } + } + } + + onFilterChanged: { + py.callCoro("set_substring_filter", ["all_rooms", filter], () => { + if (filter) { + currentIndex = 1 // highlight the first matching room + return + } + + const item = model.get(currentIndex) + + if ( + ! filter && + item && ( + currentIndex === 1 || // required, related to the if above + ( + currentShouldBeAccount && + wantedUserId !== item.id + ) || ( + currentShouldBeRoom && ( + wantedUserId !== item.for_account || + wantedRoomId !== item.id + ) + ) + ) + ) + startCorrectItemSearch() + }) + } + Connections { target: pageLoader diff --git a/src/gui/ModelStore.qml b/src/gui/ModelStore.qml index 14884197..773ead26 100644 --- a/src/gui/ModelStore.qml +++ b/src/gui/ModelStore.qml @@ -6,22 +6,17 @@ import "PythonBridge" QtObject { property QtObject privates: QtObject { - onEnsureModelExists: - py.callCoro("models.ensure_exists_from_qml", [modelId]) - - signal ensureModelExists(var modelId) - readonly property var store: ({}) readonly property PythonBridge py: PythonBridge {} readonly property Component model: Component { ListModel { + property var modelId + // Used by HFilterModel signal fieldsChanged(int index, var changes) - property var modelId - function findIndex(id, default_=null) { for (let i = 0; i < count; i++) if (get(i).id === id) return i @@ -37,6 +32,11 @@ QtObject { } } } + + signal ensureModelExists(var modelId) + + onEnsureModelExists: + py.callCoro("models.ensure_exists_from_qml", [modelId]) } diff --git a/src/gui/PageLoader.qml b/src/gui/PageLoader.qml index cf175c70..2c72fdfa 100644 --- a/src/gui/PageLoader.qml +++ b/src/gui/PageLoader.qml @@ -10,31 +10,6 @@ import "MainPane" HLoader { id: pageLoader - clip: appearAnimation.running - - onLoaded: { takeFocus(); appearAnimation.start() } - - Component.onCompleted: { - if (! py.startupAnyAccountsSaved) { - pageLoader.showPage( - "AddAccount/AddAccount", {"header.show": false}, - ) - return - } - - const page = window.uiState.page - const props = window.uiState.pageProperties - - if (page === "Pages/Chat/Chat.qml") { - pageLoader.showRoom(props.userId, props.roomId) - } else { - pageLoader._show(page, props) - } - } - - - signal previousShown(string componentUrl, var properties) - property bool isWide: width > theme.contentIsWideAbove @@ -44,6 +19,7 @@ HLoader { readonly property alias appearAnimation: appearAnimation + signal previousShown(string componentUrl, var properties) function _show(componentUrl, properties={}) { history.unshift([componentUrl, properties]) @@ -90,6 +66,28 @@ HLoader { } + clip: appearAnimation.running + + onLoaded: { takeFocus(); appearAnimation.start() } + + Component.onCompleted: { + if (! py.startupAnyAccountsSaved) { + pageLoader.showPage( + "AddAccount/AddAccount", {"header.show": false}, + ) + return + } + + const page = window.uiState.page + const props = window.uiState.pageProperties + + if (page === "Pages/Chat/Chat.qml") { + pageLoader.showRoom(props.userId, props.roomId) + } else { + pageLoader._show(page, props) + } + } + HNumberAnimation { id: appearAnimation target: pageLoader.item diff --git a/src/gui/Pages/AccountSettings/Account.qml b/src/gui/Pages/AccountSettings/Account.qml index ae51de37..7fdc02f5 100644 --- a/src/gui/Pages/AccountSettings/Account.qml +++ b/src/gui/Pages/AccountSettings/Account.qml @@ -11,12 +11,10 @@ import "../../Dialogs" HFlickableColumnPage { id: page - property string userId readonly property QtObject account: ModelStore.get("accounts").find(userId) readonly property bool ready: account && account.profile_updated >= new Date(1) - function takeFocus() { nameField.item.forceActiveFocus() } @@ -90,11 +88,11 @@ HFlickableColumnPage { onKeyboardCancel: cancel() onKeyboardAccept: applyChanges() - HUserAvatar { + id: avatar + property bool changed: Boolean(sourceOverride) - id: avatar userId: page.userId displayName: nameField.item.text mxc: account ? account.avatar_url : "" @@ -231,6 +229,8 @@ HFlickableColumnPage { } HLabeledItem { + id: aliasField + readonly property var aliases: window.settings.writeAliases readonly property string currentAlias: aliases[userId] || "" @@ -243,9 +243,6 @@ HFlickableColumnPage { return "" } - - id: aliasField - label.text: qsTr("Composer alias:") errorLabel.text: diff --git a/src/gui/Pages/AccountSettings/AccountSettings.qml b/src/gui/Pages/AccountSettings/AccountSettings.qml index ce7a0cea..eb05844f 100644 --- a/src/gui/Pages/AccountSettings/AccountSettings.qml +++ b/src/gui/Pages/AccountSettings/AccountSettings.qml @@ -9,7 +9,6 @@ import "../../Base" HPage { id: page - property string userId diff --git a/src/gui/Pages/AccountSettings/DeviceDelegate.qml b/src/gui/Pages/AccountSettings/DeviceDelegate.qml index 00b96d17..cefc141c 100644 --- a/src/gui/Pages/AccountSettings/DeviceDelegate.qml +++ b/src/gui/Pages/AccountSettings/DeviceDelegate.qml @@ -9,7 +9,6 @@ import "../../Base/HTile" HTile { id: deviceTile - property HListView view property string userId diff --git a/src/gui/Pages/AccountSettings/DeviceSection.qml b/src/gui/Pages/AccountSettings/DeviceSection.qml index 4ba77009..9ff79c3a 100644 --- a/src/gui/Pages/AccountSettings/DeviceSection.qml +++ b/src/gui/Pages/AccountSettings/DeviceSection.qml @@ -15,6 +15,7 @@ HRowLayout { readonly property int sectionTotalCount: deviceList.sectionItemCounts[section] || 0 + HCheckBox { id: checkBox padding: theme.spacing diff --git a/src/gui/Pages/AccountSettings/Encryption.qml b/src/gui/Pages/AccountSettings/Encryption.qml index 032aa81c..01e35f6b 100644 --- a/src/gui/Pages/AccountSettings/Encryption.qml +++ b/src/gui/Pages/AccountSettings/Encryption.qml @@ -8,10 +8,8 @@ import "../../Base/Buttons" HFlickableColumnPage { id: page - property string userId - function takeFocus() { exportButton.forceActiveFocus() } diff --git a/src/gui/Pages/AccountSettings/Sessions.qml b/src/gui/Pages/AccountSettings/Sessions.qml index a06702c6..980e1d67 100644 --- a/src/gui/Pages/AccountSettings/Sessions.qml +++ b/src/gui/Pages/AccountSettings/Sessions.qml @@ -11,15 +11,6 @@ import "../../ShortcutBundles" HColumnPage { id: page - enabled: ModelStore.get("accounts").find(userId).presence !== "offline" - contentHeight: Math.min( - window.height, - Math.max( - deviceList.contentHeight + deviceList.bottomMargin, - busyIndicatorLoader.height + theme.spacing * 2, - ) - ) - property string userId @@ -28,7 +19,6 @@ HColumnPage { property Future loadFuture: null - function takeFocus() {} // TODO function loadDevices() { @@ -100,6 +90,15 @@ HColumnPage { } + enabled: ModelStore.get("accounts").find(userId).presence !== "offline" + contentHeight: Math.min( + window.height, + Math.max( + deviceList.contentHeight + deviceList.bottomMargin, + busyIndicatorLoader.height + theme.spacing * 2, + ) + ) + footer: AutoDirectionLayout { GroupButton { id: refreshButton @@ -126,7 +125,6 @@ HColumnPage { Keys.forwardTo: [deviceList] - HListView { id: deviceList diff --git a/src/gui/Pages/AddAccount/Register.qml b/src/gui/Pages/AddAccount/Register.qml index c5c799ab..ac8c993e 100644 --- a/src/gui/Pages/AddAccount/Register.qml +++ b/src/gui/Pages/AddAccount/Register.qml @@ -20,7 +20,6 @@ HFlickableColumnPage { } } - HLabel { wrapMode: Text.Wrap horizontalAlignment: Qt.AlignHCenter diff --git a/src/gui/Pages/AddAccount/Reset.qml b/src/gui/Pages/AddAccount/Reset.qml index 9e4cb56d..42675c68 100644 --- a/src/gui/Pages/AddAccount/Reset.qml +++ b/src/gui/Pages/AddAccount/Reset.qml @@ -21,7 +21,6 @@ HFlickableColumnPage { } } - HLabel { wrapMode: Text.Wrap horizontalAlignment: Qt.AlignHCenter diff --git a/src/gui/Pages/AddAccount/SignIn.qml b/src/gui/Pages/AddAccount/SignIn.qml index f8d32a26..88cdce98 100644 --- a/src/gui/Pages/AddAccount/SignIn.qml +++ b/src/gui/Pages/AddAccount/SignIn.qml @@ -8,7 +8,6 @@ import "../../Base/Buttons" HFlickableColumnPage { id: page - property var loginFuture: null property string signInWith: "username" @@ -17,7 +16,6 @@ HFlickableColumnPage { serverField.item.text.trim() && idField.item.text.trim() && passwordField.item.text && ! serverField.item.error - function takeFocus() { idField.item.forceActiveFocus() } function signIn() { @@ -95,7 +93,6 @@ HFlickableColumnPage { onKeyboardAccept: page.signIn() onKeyboardCancel: page.cancel() - Timer { id: signInTimeout interval: 30 * 1000 @@ -190,11 +187,11 @@ HFlickableColumnPage { Layout.fillWidth: true HTextField { + readonly property string cleanText: text.toLowerCase().trim() + width: parent.width text: "https://matrix.org" error: ! /.+:\/\/.+/.test(cleanText) - - readonly property string cleanText: text.toLowerCase().trim() } } diff --git a/src/gui/Pages/AddChat/AddChat.qml b/src/gui/Pages/AddChat/AddChat.qml index 4ad4a259..955dcbf8 100644 --- a/src/gui/Pages/AddChat/AddChat.qml +++ b/src/gui/Pages/AddChat/AddChat.qml @@ -7,7 +7,6 @@ import "../../Base" HPage { id: page - property string userId diff --git a/src/gui/Pages/AddChat/CreateRoom.qml b/src/gui/Pages/AddChat/CreateRoom.qml index ef7d5112..15254699 100644 --- a/src/gui/Pages/AddChat/CreateRoom.qml +++ b/src/gui/Pages/AddChat/CreateRoom.qml @@ -8,13 +8,10 @@ import "../../Base/Buttons" HFlickableColumnPage { id: page - enabled: account && account.presence !== "offline" - property string userId readonly property QtObject account: ModelStore.get("accounts").find(userId) - function takeFocus() { nameField.item.forceActiveFocus() } function create() { @@ -52,6 +49,8 @@ HFlickableColumnPage { } + enabled: account && account.presence !== "offline" + footer: AutoDirectionLayout { ApplyButton { id: applyButton @@ -68,7 +67,6 @@ HFlickableColumnPage { onKeyboardAccept: create() onKeyboardCancel: cancel() - HRoomAvatar { id: avatar roomId: "" diff --git a/src/gui/Pages/AddChat/CurrentUserAvatar.qml b/src/gui/Pages/AddChat/CurrentUserAvatar.qml index 2593ff90..39d7a74a 100644 --- a/src/gui/Pages/AddChat/CurrentUserAvatar.qml +++ b/src/gui/Pages/AddChat/CurrentUserAvatar.qml @@ -7,6 +7,7 @@ import "../../Base" HUserAvatar { property QtObject account + // userId: (set me) displayName: account ? account.display_name : "" mxc: account ? account.avatar_url : "" diff --git a/src/gui/Pages/AddChat/DirectChat.qml b/src/gui/Pages/AddChat/DirectChat.qml index 8662d105..6bce4d20 100644 --- a/src/gui/Pages/AddChat/DirectChat.qml +++ b/src/gui/Pages/AddChat/DirectChat.qml @@ -8,13 +8,10 @@ import "../../Base/Buttons" HFlickableColumnPage { id: page - enabled: account && account.presence !== "offline" - property string userId readonly property QtObject account: ModelStore.get("accounts").find(userId) - function takeFocus() { userField.item.forceActiveFocus() } @@ -64,6 +61,8 @@ HFlickableColumnPage { } + enabled: account && account.presence !== "offline" + footer: AutoDirectionLayout { ApplyButton { id: applyButton @@ -85,7 +84,6 @@ HFlickableColumnPage { onKeyboardAccept: startChat() onKeyboardCancel: cancel() - CurrentUserAvatar { userId: page.userId account: page.account diff --git a/src/gui/Pages/AddChat/EncryptCheckBox.qml b/src/gui/Pages/AddChat/EncryptCheckBox.qml index 986ff4f4..91b6fcaa 100644 --- a/src/gui/Pages/AddChat/EncryptCheckBox.qml +++ b/src/gui/Pages/AddChat/EncryptCheckBox.qml @@ -5,11 +5,11 @@ import "../../Base" HCheckBox { text: qsTr("Encrypt messages") + subtitle.textFormat: Text.StyledText subtitle.text: qsTr("Only you and those you trust will be able to read the " + "conversation") + `
` + qsTr("Cannot be disabled later!") + "" - subtitle.textFormat: Text.StyledText } diff --git a/src/gui/Pages/AddChat/JoinRoom.qml b/src/gui/Pages/AddChat/JoinRoom.qml index 28e723da..08e563a5 100644 --- a/src/gui/Pages/AddChat/JoinRoom.qml +++ b/src/gui/Pages/AddChat/JoinRoom.qml @@ -8,13 +8,10 @@ import "../../Base/Buttons" HFlickableColumnPage { id: page - enabled: account && account.presence !== "offline" - property string userId readonly property QtObject account: ModelStore.get("accounts").find(userId) - function takeFocus() { roomField.item.forceActiveFocus() } @@ -57,6 +54,8 @@ HFlickableColumnPage { } + enabled: account && account.presence !== "offline" + footer: AutoDirectionLayout { ApplyButton { id: joinButton @@ -74,7 +73,6 @@ HFlickableColumnPage { onKeyboardAccept: join() onKeyboardCancel: cancel() - CurrentUserAvatar { userId: page.userId account: page.account diff --git a/src/gui/Pages/Chat/Banners/Banner.qml b/src/gui/Pages/Chat/Banners/Banner.qml index 477e8360..bb51d0db 100644 --- a/src/gui/Pages/Chat/Banners/Banner.qml +++ b/src/gui/Pages/Chat/Banners/Banner.qml @@ -6,8 +6,6 @@ import "../../../Base" Rectangle { id: banner - implicitHeight: childrenRect.height - color: theme.controls.box.background property alias avatar: bannerAvatar property alias icon: bannerIcon @@ -15,6 +13,10 @@ Rectangle { property alias buttonModel: bannerRepeater.model property var buttonCallbacks: [] + + implicitHeight: childrenRect.height + color: theme.controls.box.background + HGridLayout { id: bannerGrid width: parent.width diff --git a/src/gui/Pages/Chat/Banners/InviteBanner.qml b/src/gui/Pages/Chat/Banners/InviteBanner.qml index 8313f1eb..1ee9606b 100644 --- a/src/gui/Pages/Chat/Banners/InviteBanner.qml +++ b/src/gui/Pages/Chat/Banners/InviteBanner.qml @@ -8,6 +8,7 @@ Banner { property string inviterName: chat.roomInfo.inviter_name property string inviterAvatar: chat.roomInfo.inviter_avatar + color: theme.chat.inviteBanner.background avatar.userId: inviterId diff --git a/src/gui/Pages/Chat/Chat.qml b/src/gui/Pages/Chat/Chat.qml index 6b935216..3dc4c490 100644 --- a/src/gui/Pages/Chat/Chat.qml +++ b/src/gui/Pages/Chat/Chat.qml @@ -9,10 +9,6 @@ import "RoomPane" Item { id: chat - onFocusChanged: if (focus && loader.item) loader.item.composer.takeFocus() - onReadyChanged: longLoading = false - - property string userId property string roomId @@ -33,6 +29,9 @@ Item { Boolean(loader.item && loader.item.composer.hasFocus) + onFocusChanged: if (focus && loader.item) loader.item.composer.takeFocus() + onReadyChanged: longLoading = false + HShortcut { sequences: window.settings.keys.leaveRoom active: userInfo && userInfo.presence !== "offline" diff --git a/src/gui/Pages/Chat/ChatPage.qml b/src/gui/Pages/Chat/ChatPage.qml index e2fea337..3a4347bd 100644 --- a/src/gui/Pages/Chat/ChatPage.qml +++ b/src/gui/Pages/Chat/ChatPage.qml @@ -10,12 +10,6 @@ import "Timeline" HColumnPage { id: chatPage - padding: 0 - column.spacing: 0 - - onLoadEventListChanged: if (loadEventList) loadedOnce = true - Component.onDestruction: if (loadMembersFuture) loadMembersFuture.cancel() - property bool loadedOnce: false property var loadMembersFuture: null @@ -26,6 +20,12 @@ HColumnPage { ! mainUI.mainPane.visible : ! pageLoader.appearAnimation.running + padding: 0 + column.spacing: 0 + + onLoadEventListChanged: if (loadEventList) loadedOnce = true + Component.onDestruction: if (loadMembersFuture) loadMembersFuture.cancel() + Timer { interval: 200 running: true diff --git a/src/gui/Pages/Chat/Composer/Composer.qml b/src/gui/Pages/Chat/Composer/Composer.qml index 44f8d8e1..4fb5b52a 100644 --- a/src/gui/Pages/Chat/Composer/Composer.qml +++ b/src/gui/Pages/Chat/Composer/Composer.qml @@ -8,7 +8,6 @@ Rectangle { property alias eventList: messageArea.eventList readonly property bool hasFocus: messageArea.activeFocus - function takeFocus() { messageArea.forceActiveFocus() } @@ -17,7 +16,6 @@ Rectangle { color: theme.chat.composer.background - HRowLayout { anchors.fill: parent diff --git a/src/gui/Pages/Chat/Composer/MessageArea.qml b/src/gui/Pages/Chat/Composer/MessageArea.qml index 13587d2b..6975ee8e 100644 --- a/src/gui/Pages/Chat/Composer/MessageArea.qml +++ b/src/gui/Pages/Chat/Composer/MessageArea.qml @@ -7,7 +7,6 @@ import "../../../Base" HTextArea { id: textArea - property HListView eventList property string indent: " " @@ -50,7 +49,6 @@ HTextArea { return obj } - function setTyping(typing) { py.callClientCoro( writingUserId, "room_typing", [chat.roomId, typing, 5000], diff --git a/src/gui/Pages/Chat/Composer/UploadButton.qml b/src/gui/Pages/Chat/Composer/UploadButton.qml index 263de22c..00390b66 100644 --- a/src/gui/Pages/Chat/Composer/UploadButton.qml +++ b/src/gui/Pages/Chat/Composer/UploadButton.qml @@ -19,7 +19,6 @@ HButton { onClicked: sendFilePicker.dialog.open() - HShortcut { sequences: window.settings.keys.sendFileFromPathInClipboard onActivated: utils.sendFile( diff --git a/src/gui/Pages/Chat/FileTransfer/Transfer.qml b/src/gui/Pages/Chat/FileTransfer/Transfer.qml index 929a2ab6..d9dbd262 100644 --- a/src/gui/Pages/Chat/FileTransfer/Transfer.qml +++ b/src/gui/Pages/Chat/FileTransfer/Transfer.qml @@ -8,7 +8,6 @@ import "../../../Base" HColumnLayout { id: transfer - property bool cancelPending: false property int msLeft: model.time_left @@ -18,7 +17,6 @@ HColumnLayout { readonly property string status: model.status readonly property bool paused: model.paused - function cancel() { cancelPending = true // Python will delete this model item on cancel @@ -51,6 +49,15 @@ HColumnLayout { HLabel { id: statusLabel + + property bool expand: status === "Error" + + readonly property string fileName: + model.filepath.split("/").slice(-1)[0] + + readonly property string filePath: + model.filepath.replace(/^file:\/\//, "") + elide: expand ? Text.ElideNone : Text.ElideRight wrapMode: expand ? Text.Wrap : Text.NoWrap @@ -92,16 +99,6 @@ HColumnLayout { Layout.fillWidth: true - - property bool expand: status === "Error" - - readonly property string fileName: - model.filepath.split("/").slice(-1)[0] - - readonly property string filePath: - model.filepath.replace(/^file:\/\//, "") - - HoverHandler { id: statusLabelHover } HToolTip { diff --git a/src/gui/Pages/Chat/FileTransfer/TransferList.qml b/src/gui/Pages/Chat/FileTransfer/TransferList.qml index ec3116ad..0a6fe431 100644 --- a/src/gui/Pages/Chat/FileTransfer/TransferList.qml +++ b/src/gui/Pages/Chat/FileTransfer/TransferList.qml @@ -5,13 +5,6 @@ import "../../.." import "../../../Base" Rectangle { - implicitWidth: 800 - implicitHeight: firstDelegate ? firstDelegate.height : 0 - color: theme.chat.fileTransfer.background - opacity: implicitHeight ? 1 : 0 - clip: true - - property int delegateHeight: 0 readonly property var firstDelegate: @@ -20,6 +13,12 @@ Rectangle { readonly property alias transferCount: transferList.count + implicitWidth: 800 + implicitHeight: firstDelegate ? firstDelegate.height : 0 + color: theme.chat.fileTransfer.background + opacity: implicitHeight ? 1 : 0 + clip: true + Behavior on implicitHeight { HNumberAnimation {} } HListView { diff --git a/src/gui/Pages/Chat/InfoBar.qml b/src/gui/Pages/Chat/InfoBar.qml index 7e117c04..a8681363 100644 --- a/src/gui/Pages/Chat/InfoBar.qml +++ b/src/gui/Pages/Chat/InfoBar.qml @@ -5,15 +5,15 @@ import QtQuick.Layouts 1.12 import "../../Base" Rectangle { - implicitHeight: label.text ? rowLayout.height : 0 - opacity: implicitHeight ? 1 : 0 - + default property alias rowLayoutData: rowLayout.data readonly property alias icon: icon readonly property alias label: label - default property alias rowLayoutData: rowLayout.data + implicitHeight: label.text ? rowLayout.height : 0 + opacity: implicitHeight ? 1 : 0 + Behavior on implicitHeight { HNumberAnimation {} } HRowLayout { diff --git a/src/gui/Pages/Chat/ReplyBar.qml b/src/gui/Pages/Chat/ReplyBar.qml index 30cedfde..74aa280b 100644 --- a/src/gui/Pages/Chat/ReplyBar.qml +++ b/src/gui/Pages/Chat/ReplyBar.qml @@ -5,6 +5,13 @@ import QtQuick.Layouts 1.12 import "../../Base" InfoBar { + property string replyToEventId: "" + property string replyToUserId: "" + property string replyToDisplayName: "" + + signal cancel() + + color: theme.chat.replyBar.background icon.svgName: "reply-to" label.textFormat: Text.StyledText @@ -13,15 +20,6 @@ InfoBar { utils.coloredNameHtml(replyToDisplayName, replyToUserId) : "" - - signal cancel() - - - property string replyToEventId: "" - property string replyToUserId: "" - property string replyToDisplayName: "" - - HButton { backgroundColor: "transparent" icon.name: "reply-cancel" diff --git a/src/gui/Pages/Chat/RoomHeader.qml b/src/gui/Pages/Chat/RoomHeader.qml index 678d3564..e6f76c34 100644 --- a/src/gui/Pages/Chat/RoomHeader.qml +++ b/src/gui/Pages/Chat/RoomHeader.qml @@ -5,16 +5,15 @@ import QtQuick.Layouts 1.12 import "../../Base" Rectangle { - implicitHeight: theme.baseElementsHeight - color: theme.chat.roomHeader.background - - readonly property bool showPaneButtons: mainUI.mainPane.collapse readonly property bool center: showPaneButtons || window.settings.alwaysCenterRoomHeader + implicitHeight: theme.baseElementsHeight + color: theme.chat.roomHeader.background + HRowLayout { id: row anchors.fill: parent @@ -113,16 +112,16 @@ Rectangle { } HToolTip { - visible: text && (nameHover.hovered || topicHover.hovered) - label.textFormat: Text.StyledText - text: name && topic ? (`${name}
${topic}`) : (name || topic) - readonly property string name: nameLabel.truncated ? (`${chat.roomInfo.display_name}`) : "" readonly property string topic: topicLabel.truncated ? chat.roomInfo.topic : "" + + visible: text && (nameHover.hovered || topicHover.hovered) + label.textFormat: Text.StyledText + text: name && topic ? (`${name}
${topic}`) : (name || topic) } HSpacer { diff --git a/src/gui/Pages/Chat/RoomPane/MemberView/DeviceVerification.qml b/src/gui/Pages/Chat/RoomPane/MemberView/DeviceVerification.qml index 1de8d4b5..7375c89e 100644 --- a/src/gui/Pages/Chat/RoomPane/MemberView/DeviceVerification.qml +++ b/src/gui/Pages/Chat/RoomPane/MemberView/DeviceVerification.qml @@ -8,7 +8,6 @@ import "../../../../Base/Buttons" HFlickableColumnPage { id: page - property string deviceOwner property string deviceOwnerDisplayName property string deviceId @@ -20,7 +19,6 @@ HFlickableColumnPage { signal trustSet(bool trust) - function close() { if (previouslyFocused) previouslyFocused.forceActiveFocus() stackView.pop() @@ -72,7 +70,6 @@ HFlickableColumnPage { onKeyboardCancel: page.close() - HRowLayout { HButton { id: closeButton diff --git a/src/gui/Pages/Chat/RoomPane/MemberView/MemberDelegate.qml b/src/gui/Pages/Chat/RoomPane/MemberView/MemberDelegate.qml index f2cdfec4..6430332d 100644 --- a/src/gui/Pages/Chat/RoomPane/MemberView/MemberDelegate.qml +++ b/src/gui/Pages/Chat/RoomPane/MemberView/MemberDelegate.qml @@ -9,6 +9,7 @@ import "../../../../Popups" HTile { id: member + backgroundColor: theme.chat.roomPane.listView.member.background contentOpacity: model.invited ? theme.chat.roomPane.listView.member.invitedOpacity : 1 @@ -142,7 +143,6 @@ HTile { } } - Behavior on contentOpacity { HNumberAnimation {} } Behavior on spacing { HNumberAnimation {} } diff --git a/src/gui/Pages/Chat/RoomPane/MemberView/MemberProfile.qml b/src/gui/Pages/Chat/RoomPane/MemberView/MemberProfile.qml index 93614ee7..b4dbb2e3 100644 --- a/src/gui/Pages/Chat/RoomPane/MemberView/MemberProfile.qml +++ b/src/gui/Pages/Chat/RoomPane/MemberView/MemberProfile.qml @@ -8,13 +8,11 @@ import "../../../../Base" HListView { id: profile - property string userId property string roomId property QtObject member // RoomMember model item property HStackView stackView - function loadDevices() { py.callClientCoro(userId, "member_devices", [member.id], devices => { profile.model.clear() @@ -211,7 +209,6 @@ HListView { } Keys.onEscapePressed: stackView.pop() - Connections { target: py.eventHandlers diff --git a/src/gui/Pages/Chat/RoomPane/MemberView/MemberView.qml b/src/gui/Pages/Chat/RoomPane/MemberView/MemberView.qml index 2cb88bc2..1f4311b4 100644 --- a/src/gui/Pages/Chat/RoomPane/MemberView/MemberView.qml +++ b/src/gui/Pages/Chat/RoomPane/MemberView/MemberView.qml @@ -10,6 +10,7 @@ HColumnLayout { readonly property var modelSyncId: [chat.userId, chat.roomId, "filtered_members"] + HStackView { id: stackView diff --git a/src/gui/Pages/Chat/RoomPane/RoomPane.qml b/src/gui/Pages/Chat/RoomPane/RoomPane.qml index 1c95a15a..a24908f2 100644 --- a/src/gui/Pages/Chat/RoomPane/RoomPane.qml +++ b/src/gui/Pages/Chat/RoomPane/RoomPane.qml @@ -7,6 +7,22 @@ import "MemberView" MultiviewPane { id: roomPane + + readonly property QtObject accountModel: + ModelStore.get("accounts").find(chat.roomInfo.for_account) + + function toggleFocus() { + if (roomPane.activeFocus) { + if (roomPane.collapse) roomPane.close() + pageLoader.takeFocus() + return + } + + roomPane.open() + swipeView.currentItem.keybindFocusItem.forceActiveFocus() + } + + saveName: "roomPane" edge: Qt.RightEdge @@ -56,23 +72,6 @@ MultiviewPane { } } - - readonly property QtObject accountModel: - ModelStore.get("accounts").find(chat.roomInfo.for_account) - - - function toggleFocus() { - if (roomPane.activeFocus) { - if (roomPane.collapse) roomPane.close() - pageLoader.takeFocus() - return - } - - roomPane.open() - swipeView.currentItem.keybindFocusItem.forceActiveFocus() - } - - Connections { target: swipeView diff --git a/src/gui/Pages/Chat/RoomPane/SettingsView.qml b/src/gui/Pages/Chat/RoomPane/SettingsView.qml index f551f82e..1eda1b2a 100644 --- a/src/gui/Pages/Chat/RoomPane/SettingsView.qml +++ b/src/gui/Pages/Chat/RoomPane/SettingsView.qml @@ -9,7 +9,6 @@ import "../../../Base/Buttons" HFlickableColumnPage { id: settingsView - property var saveFuture: null readonly property bool anyChange: @@ -19,7 +18,6 @@ HFlickableColumnPage { readonly property Item keybindFocusItem: nameField.item - function save() { if (saveFuture) saveFuture.cancel() @@ -81,7 +79,6 @@ HFlickableColumnPage { onKeyboardAccept: save() onKeyboardCancel: cancel() - HRoomAvatar { id: avatar roomId: chat.roomId @@ -117,13 +114,13 @@ HFlickableColumnPage { Layout.fillWidth: true HScrollView { + readonly property alias area: topicAreaIn + clip: true width: parent.width height: Math.min(topicAreaIn.implicitHeight, settingsView.height / 2) - readonly property alias area: topicAreaIn - HTextArea { id: topicAreaIn placeholderText: qsTr("This room is about...") diff --git a/src/gui/Pages/Chat/Timeline/EventContent.qml b/src/gui/Pages/Chat/Timeline/EventContent.qml index b11edac1..ff8ad099 100644 --- a/src/gui/Pages/Chat/Timeline/EventContent.qml +++ b/src/gui/Pages/Chat/Timeline/EventContent.qml @@ -7,9 +7,6 @@ import "../../.." HRowLayout { id: eventContent - spacing: theme.chat.message.horizontalSpacing - layoutDirection: onRight ? Qt.RightToLeft: Qt.LeftToRight - readonly property var mentions: JSON.parse(model.mentions) @@ -76,6 +73,9 @@ HRowLayout { readonly property alias selectedText: contentLabel.selectedText + spacing: theme.chat.message.horizontalSpacing + layoutDirection: onRight ? Qt.RightToLeft: Qt.LeftToRight + Item { id: avatarWrapper opacity: collapseAvatar ? 0 : 1 @@ -187,6 +187,9 @@ HRowLayout { PointHandler { id: mousePointHandler + + property bool checkedNow: false + acceptedButtons: Qt.LeftButton acceptedModifiers: Qt.NoModifier acceptedPointerTypes: @@ -208,8 +211,6 @@ HRowLayout { eventList.uncheck(model.index) } } - - property bool checkedNow: false } PointHandler { diff --git a/src/gui/Pages/Chat/Timeline/EventDelegate.qml b/src/gui/Pages/Chat/Timeline/EventDelegate.qml index 20ba1ead..dafaf02c 100644 --- a/src/gui/Pages/Chat/Timeline/EventDelegate.qml +++ b/src/gui/Pages/Chat/Timeline/EventDelegate.qml @@ -8,10 +8,6 @@ import "../../../Base" HColumnLayout { id: eventDelegate - width: eventList.width - eventList.leftMargin - eventList.rightMargin - - ListView.onRemove: eventList.uncheck(model.id) - enum Media { Page, File, Image, Video, Audio } @@ -62,18 +58,6 @@ HColumnLayout { readonly property alias eventContent: eventContent - // Needed because of eventList's MouseArea which steals the - // HSelectableLabel's MouseArea hover events - onCursorShapeChanged: eventList.cursorShape = cursorShape - - Component.onCompleted: if (model.fetch_profile) py.callClientCoro( - chat.userId, "get_event_profiles", [chat.roomId, model.id], - ) - - Component.onDestruction: - if (fetchProfilesFuture) fetchProfilesFuture.cancel() - - function json() { let event = ModelStore.get(chat.userId, chat.roomId, "events") .get(model.index) @@ -93,6 +77,21 @@ HColumnLayout { } + width: eventList.width - eventList.leftMargin - eventList.rightMargin + + // Needed because of eventList's MouseArea which steals the + // HSelectableLabel's MouseArea hover events + onCursorShapeChanged: eventList.cursorShape = cursorShape + + Component.onCompleted: if (model.fetch_profile) py.callClientCoro( + chat.userId, "get_event_profiles", [chat.roomId, model.id], + ) + + Component.onDestruction: + if (fetchProfilesFuture) fetchProfilesFuture.cancel() + + ListView.onRemove: eventList.uncheck(model.id) + Item { Layout.fillWidth: true Layout.preferredHeight: @@ -240,6 +239,16 @@ HColumnLayout { } HMenuItemPopupSpawner { + readonly property var events: { + eventList.selectedCount ? + eventList.redactableCheckedEvents : + + eventList.canRedact(currentModel) ? + [model] : + + [] + } + icon.name: "remove-message" text: qsTr("Remove") enabled: properties.eventSenderAndIds.length @@ -254,16 +263,6 @@ HColumnLayout { ! chat.roomInfo.can_redact_all && events.length < eventList.selectedCount }) - - readonly property var events: { - eventList.selectedCount ? - eventList.redactableCheckedEvents : - - eventList.canRedact(currentModel) ? - [model] : - - [] - } } HMenuItem { diff --git a/src/gui/Pages/Chat/Timeline/EventFile.qml b/src/gui/Pages/Chat/Timeline/EventFile.qml index 950b15f2..6cb7b8cd 100644 --- a/src/gui/Pages/Chat/Timeline/EventFile.qml +++ b/src/gui/Pages/Chat/Timeline/EventFile.qml @@ -8,6 +8,15 @@ import "../../../Base/HTile" HTile { id: file + + property EventMediaLoader loader + + readonly property bool cryptDict: + JSON.parse(loader.singleMediaInfo.media_crypt_dict) + + readonly property bool isEncrypted: ! utils.isEmptyObject(cryptDict) + + width: Math.min( eventDelegate.width, eventContent.maxMessageWidth, @@ -55,15 +64,6 @@ HTile { ] } - - property EventMediaLoader loader - - readonly property bool cryptDict: - JSON.parse(loader.singleMediaInfo.media_crypt_dict) - - readonly property bool isEncrypted: ! utils.isEmptyObject(cryptDict) - - Binding on backgroundColor { value: theme.chat.message.checkedBackground when: eventDelegate.checked diff --git a/src/gui/Pages/Chat/Timeline/EventImage.qml b/src/gui/Pages/Chat/Timeline/EventImage.qml index 248fedfd..2269f249 100644 --- a/src/gui/Pages/Chat/Timeline/EventImage.qml +++ b/src/gui/Pages/Chat/Timeline/EventImage.qml @@ -5,24 +5,6 @@ import "../../../Base" HMxcImage { id: image - width: fitSize.width - height: fitSize.height - horizontalAlignment: Image.AlignLeft - enabledAnimatedPausing: ! eventList.selectedCount - - title: thumbnail ? loader.thumbnailTitle : loader.title - animated: loader.singleMediaInfo.media_mime === "image/gif" || - utils.urlExtension(loader.mediaUrl).toLowerCase() === "gif" - thumbnail: ! animated && loader.thumbnailMxc - mxc: thumbnail ? - (loader.thumbnailMxc || loader.mediaUrl) : - (loader.mediaUrl || loader.thumbnailMxc) - cryptDict: JSON.parse( - thumbnail && loader.thumbnailMxc ? - loader.singleMediaInfo.thumbnail_crypt_dict : - loader.singleMediaInfo.media_crypt_dict - ) - property EventMediaLoader loader @@ -61,7 +43,6 @@ HMxcImage { Math.max(maxHeight, theme.chat.message.thumbnailMinSize.height), ) - function getOpenUrl(callback) { if (image.isEncrypted && loader.mediaUrl) { loader.download(callback) @@ -82,6 +63,24 @@ HMxcImage { } + width: fitSize.width + height: fitSize.height + horizontalAlignment: Image.AlignLeft + enabledAnimatedPausing: ! eventList.selectedCount + + title: thumbnail ? loader.thumbnailTitle : loader.title + animated: loader.singleMediaInfo.media_mime === "image/gif" || + utils.urlExtension(loader.mediaUrl).toLowerCase() === "gif" + thumbnail: ! animated && loader.thumbnailMxc + mxc: thumbnail ? + (loader.thumbnailMxc || loader.mediaUrl) : + (loader.mediaUrl || loader.thumbnailMxc) + cryptDict: JSON.parse( + thumbnail && loader.thumbnailMxc ? + loader.singleMediaInfo.thumbnail_crypt_dict : + loader.singleMediaInfo.media_crypt_dict + ) + TapHandler { acceptedModifiers: Qt.NoModifier onTapped: diff --git a/src/gui/Pages/Chat/Timeline/EventImageTextBubble.qml b/src/gui/Pages/Chat/Timeline/EventImageTextBubble.qml index 196506b2..98d685e7 100644 --- a/src/gui/Pages/Chat/Timeline/EventImageTextBubble.qml +++ b/src/gui/Pages/Chat/Timeline/EventImageTextBubble.qml @@ -5,6 +5,7 @@ import "../../../Base" HLabel { id: bubble + anchors.margins: theme.spacing / 4 topPadding: theme.spacing / 2 diff --git a/src/gui/Pages/Chat/Timeline/EventList.qml b/src/gui/Pages/Chat/Timeline/EventList.qml index 92c00c09..75c1c902 100644 --- a/src/gui/Pages/Chat/Timeline/EventList.qml +++ b/src/gui/Pages/Chat/Timeline/EventList.qml @@ -10,12 +10,11 @@ import "../../../PythonBridge" import "../../../ShortcutBundles" Rectangle { - color: theme.chat.eventList.background - - property alias eventList: eventList + color: theme.chat.eventList.background + HShortcut { sequences: window.settings.keys.unfocusOrDeselectAllMessages onActivated: { @@ -52,6 +51,27 @@ Rectangle { } HShortcut { + readonly property var events: + eventList.selectedCount ? + eventList.redactableCheckedEvents : + + eventList.currentItem && + eventList.canRedact(eventList.currentItem.currentModel) ? + [eventList.currentItem.currentModel] : + + eventList.currentItem ? + [] : + null + + function findLastRemovableDelegate() { + for (let i = 0; i < eventList.model.count && i <= 1000; i++) { + const event = eventList.model.get(i) + if (eventList.canRedact(event) && + mainUI.accountIds.includes(event.sender_id)) return [event] + } + return [] + } + enabled: (events && events.length > 0) || events === null sequences: window.settings.keys.removeFocusedOrSelectedMessages onActivated: utils.makePopup( @@ -73,27 +93,6 @@ Rectangle { events.length < eventList.selectedCount } ) - - readonly property var events: - eventList.selectedCount ? - eventList.redactableCheckedEvents : - - eventList.currentItem && - eventList.canRedact(eventList.currentItem.currentModel) ? - [eventList.currentItem.currentModel] : - - eventList.currentItem ? - [] : - null - - function findLastRemovableDelegate() { - for (let i = 0; i < eventList.model.count && i <= 1000; i++) { - const event = eventList.model.get(i) - if (eventList.canRedact(event) && - mainUI.accountIds.includes(event.sender_id)) return [event] - } - return [] - } } HShortcut { @@ -181,67 +180,6 @@ Rectangle { HListView { id: eventList - anchors.fill: parent - clip: true - keyNavigationWraps: false - leftMargin: theme.spacing - rightMargin: theme.spacing - topMargin: theme.spacing - bottomMargin: theme.spacing - verticalLayoutDirection: ListView.BottomToTop - - // Keep x scroll pages cached, to limit images having to be - // reloaded from network. - cacheBuffer: Screen.desktopAvailableHeight * 2 - - model: ModelStore.get(chat.userId, chat.roomId, "events") - delegate: EventDelegate {} - - highlight: Rectangle { - color: theme.chat.message.focusedHighlight - opacity: theme.chat.message.focusedHighlightOpacity - } - - // Since the list is BottomToTop, this is actually a header - footer: Item { - width: eventList.width - height: (button.height + theme.spacing * 2) * opacity - opacity: eventList.loading ? 1 : 0 - visible: opacity > 0 - - Behavior on opacity { HNumberAnimation {} } - - HButton { - readonly property bool offline: - chat.userInfo.presence === "offline" - - id: button - width: Math.min(parent.width, implicitWidth) - anchors.centerIn: parent - - loading: parent.visible && ! offline - icon.name: offline ? "feature-unavailable-offline" : "" - icon.color: - offline ? - theme.colors.negativeBackground : - theme.icons.colorize - text: - offline ? - qsTr("Cannot load history offline") : - qsTr("Loading previous messages...") - - enableRadius: true - iconItem.small: true - } - } - - onYPosChanged: - if (canLoad && yPos < 0.1) Qt.callLater(loadPastEvents) - - // When an invited room becomes joined, we should now be able to - // fetch past events. - onInviterChanged: canLoad = true - property string inviter: chat.roomInfo.inviter || "" property real yPos: visibleArea.yPosition @@ -261,7 +199,6 @@ Rectangle { readonly property var redactableCheckedEvents: getSortedChecked().filter(ev => eventList.canRedact(ev)) - function copySelectedDelegates() { if (eventList.selectedText) { Clipboard.text = eventList.selectedText @@ -362,6 +299,67 @@ Rectangle { return } } + + anchors.fill: parent + clip: true + keyNavigationWraps: false + leftMargin: theme.spacing + rightMargin: theme.spacing + topMargin: theme.spacing + bottomMargin: theme.spacing + verticalLayoutDirection: ListView.BottomToTop + + // Keep x scroll pages cached, to limit images having to be + // reloaded from network. + cacheBuffer: Screen.desktopAvailableHeight * 2 + + model: ModelStore.get(chat.userId, chat.roomId, "events") + delegate: EventDelegate {} + + highlight: Rectangle { + color: theme.chat.message.focusedHighlight + opacity: theme.chat.message.focusedHighlightOpacity + } + + // Since the list is BottomToTop, this is actually a header + footer: Item { + width: eventList.width + height: (button.height + theme.spacing * 2) * opacity + opacity: eventList.loading ? 1 : 0 + visible: opacity > 0 + + Behavior on opacity { HNumberAnimation {} } + + HButton { + readonly property bool offline: + chat.userInfo.presence === "offline" + + id: button + width: Math.min(parent.width, implicitWidth) + anchors.centerIn: parent + + loading: parent.visible && ! offline + icon.name: offline ? "feature-unavailable-offline" : "" + icon.color: + offline ? + theme.colors.negativeBackground : + theme.icons.colorize + text: + offline ? + qsTr("Cannot load history offline") : + qsTr("Loading previous messages...") + + enableRadius: true + iconItem.small: true + } + } + + onYPosChanged: + if (canLoad && yPos < 0.1) Qt.callLater(loadPastEvents) + + // When an invited room becomes joined, we should now be able to + // fetch past events. + onInviterChanged: canLoad = true } Timer { diff --git a/src/gui/Pages/Chat/Timeline/EventMediaLoader.qml b/src/gui/Pages/Chat/Timeline/EventMediaLoader.qml index 472e86e0..bbbf5ca4 100644 --- a/src/gui/Pages/Chat/Timeline/EventMediaLoader.qml +++ b/src/gui/Pages/Chat/Timeline/EventMediaLoader.qml @@ -5,21 +5,6 @@ import "../../../Base" HLoader { id: loader - visible: Boolean(item) - x: eventContent.spacing - - onTypeChanged: { - if (type === EventDelegate.Media.Image) { - var file = "EventImage.qml" - - } else if (type !== EventDelegate.Media.Page) { - var file = "EventFile.qml" - - } else { return } - - loader.setSource(file, {loader}) - } - property QtObject singleMediaInfo property string mediaUrl @@ -87,7 +72,6 @@ HLoader { readonly property string thumbnailMxc: singleMediaInfo.thumbnail_url - function download(callback) { if (! loader.mediaUrl.startsWith("mxc://")) { downloadedPath = loader.mediaUrl @@ -109,4 +93,20 @@ HLoader { callback(path) }) } + + + visible: Boolean(item) + x: eventContent.spacing + + onTypeChanged: { + if (type === EventDelegate.Media.Image) { + var file = "EventImage.qml" + + } else if (type !== EventDelegate.Media.Page) { + var file = "EventFile.qml" + + } else { return } + + loader.setSource(file, {loader}) + } } diff --git a/src/gui/Pages/Chat/TypingMembersBar.qml b/src/gui/Pages/Chat/TypingMembersBar.qml index 2173bde6..47436b3d 100644 --- a/src/gui/Pages/Chat/TypingMembersBar.qml +++ b/src/gui/Pages/Chat/TypingMembersBar.qml @@ -5,6 +5,9 @@ import QtQuick.Layouts 1.12 import "../../Base" InfoBar { + property var typingMembers: [] + + color: theme.chat.typingMembers.background icon.svgName: "typing" // TODO: animate label.textFormat: Text.StyledText @@ -17,7 +20,4 @@ InfoBar { return qsTr("%1 and %2 are typing...") .arg(tm.slice(0, -1).join(", ")).arg(tm.slice(-1)[0]) } - - - property var typingMembers: [] } diff --git a/src/gui/Popups/ClearMessagesPopup.qml b/src/gui/Popups/ClearMessagesPopup.qml index e982c1d8..14b890ce 100644 --- a/src/gui/Popups/ClearMessagesPopup.qml +++ b/src/gui/Popups/ClearMessagesPopup.qml @@ -7,7 +7,6 @@ import "../Base/Buttons" HFlickableColumnPopup { id: popup - property string userId: "" property string roomId: "" property var preClearCallback: null diff --git a/src/gui/Popups/DeleteDevicesPopup.qml b/src/gui/Popups/DeleteDevicesPopup.qml index 0983afd0..b226c619 100644 --- a/src/gui/Popups/DeleteDevicesPopup.qml +++ b/src/gui/Popups/DeleteDevicesPopup.qml @@ -8,14 +8,12 @@ import "../PythonBridge" PasswordPopup { id: popup - property string userId property var deviceIds // array property var deletedCallback: null property Future deleteFuture: null - function verifyPassword(pass, callback) { deleteFuture = py.callClientCoro( userId, @@ -35,6 +33,7 @@ PasswordPopup { ) } + summary.text: qsTr("Enter your account's password to continue:") diff --git a/src/gui/Popups/ForgetRoomPopup.qml b/src/gui/Popups/ForgetRoomPopup.qml index 503bc3b0..a6656308 100644 --- a/src/gui/Popups/ForgetRoomPopup.qml +++ b/src/gui/Popups/ForgetRoomPopup.qml @@ -7,14 +7,12 @@ import "../Base/Buttons" HFlickableColumnPopup { id: popup - property string userId: "" property string roomId: "" property string roomName: "" property bool canDestroy: false - function forget() { py.callClientCoro(userId, "room_forget", [roomId], () => { if (window.uiState.page === "Pages/Chat/Chat.qml" && @@ -49,7 +47,6 @@ HFlickableColumnPopup { onOpened: forgetButton.forceActiveFocus() onClosed: if (canDestroy) Qt.callLater(popup.destroy) - SummaryLabel { text: qsTr("Leave %1 and lose the history?").arg(roomName) textFormat: Text.StyledText diff --git a/src/gui/Popups/HColumnPopup.qml b/src/gui/Popups/HColumnPopup.qml index a800a781..ddfd4f45 100644 --- a/src/gui/Popups/HColumnPopup.qml +++ b/src/gui/Popups/HColumnPopup.qml @@ -6,8 +6,8 @@ import "../Base" HPopup { id: popup - default property alias pageData: page.columnData + readonly property alias page: page signal keyboardAccept() diff --git a/src/gui/Popups/HFlickableColumnPopup.qml b/src/gui/Popups/HFlickableColumnPopup.qml index 970289f9..0cfef004 100644 --- a/src/gui/Popups/HFlickableColumnPopup.qml +++ b/src/gui/Popups/HFlickableColumnPopup.qml @@ -6,8 +6,8 @@ import "../Base" HPopup { id: popup - default property alias pageData: page.columnData + readonly property alias page: page signal keyboardAccept() diff --git a/src/gui/Popups/InviteToRoomPopup.qml b/src/gui/Popups/InviteToRoomPopup.qml index a9ae7f36..56e9adbe 100644 --- a/src/gui/Popups/InviteToRoomPopup.qml +++ b/src/gui/Popups/InviteToRoomPopup.qml @@ -9,7 +9,6 @@ import "../Base/Buttons" HColumnPopup { id: popup - property string userId property string roomId property string roomName @@ -19,7 +18,6 @@ HColumnPopup { property var successfulInvites: [] property var failedInvites: [] - function invite() { inviteButton.loading = true @@ -67,7 +65,6 @@ HColumnPopup { onInvitingAllowedChanged: if (! invitingAllowed && inviteFuture) inviteFuture.cancel() - SummaryLabel { text: qsTr("Invite members to %1").arg(roomName) textFormat: Text.StyledText @@ -89,16 +86,6 @@ HColumnPopup { HLabel { id: errorMessage - visible: Layout.maximumHeight > 0 - wrapMode: Text.Wrap - color: theme.colors.errorText - text: - invitingAllowed ? - allErrors : - qsTr("You do not have permission to invite members to this room") - - Layout.maximumHeight: text ? implicitHeight : 0 - Layout.fillWidth: true readonly property string allErrors: { // TODO: handle these: real user not found @@ -142,6 +129,17 @@ HColumnPopup { return lines.join("\n\n") } + visible: Layout.maximumHeight > 0 + wrapMode: Text.Wrap + color: theme.colors.errorText + text: + invitingAllowed ? + allErrors : + qsTr("You do not have permission to invite members to this room") + + Layout.maximumHeight: text ? implicitHeight : 0 + Layout.fillWidth: true + Behavior on Layout.maximumHeight { HNumberAnimation {} } } } diff --git a/src/gui/Popups/KeyVerificationPopup.qml b/src/gui/Popups/KeyVerificationPopup.qml index c3c59fed..6cab3e6c 100644 --- a/src/gui/Popups/KeyVerificationPopup.qml +++ b/src/gui/Popups/KeyVerificationPopup.qml @@ -8,7 +8,6 @@ import "../Base/Buttons" HFlickableColumnPopup { id: popup - property string deviceOwner property string deviceId property string deviceName @@ -76,6 +75,8 @@ HFlickableColumnPopup { } HTextArea { + id: infoArea + function formatInfo(info, value) { return ( `

` + @@ -86,7 +87,6 @@ HFlickableColumnPopup { ) } - id: infoArea readOnly: true wrapMode: HSelectableLabel.Wrap textFormat: Qt.RichText diff --git a/src/gui/Popups/LeaveRoomPopup.qml b/src/gui/Popups/LeaveRoomPopup.qml index 2891813c..5d4fa442 100644 --- a/src/gui/Popups/LeaveRoomPopup.qml +++ b/src/gui/Popups/LeaveRoomPopup.qml @@ -7,7 +7,6 @@ import "../Base/Buttons" HFlickableColumnPopup { id: popup - property string userId: "" property string roomId: "" property string roomName: "" @@ -33,7 +32,6 @@ HFlickableColumnPopup { onOpened: leaveButton.forceActiveFocus() - SummaryLabel { text: qsTr("Leave %1?").arg(roomName) textFormat: Text.StyledText diff --git a/src/gui/Popups/PasswordPopup.qml b/src/gui/Popups/PasswordPopup.qml index b064fecd..35e1e419 100644 --- a/src/gui/Popups/PasswordPopup.qml +++ b/src/gui/Popups/PasswordPopup.qml @@ -8,7 +8,6 @@ import "../Base/Buttons" HFlickableColumnPopup { id: popup - property bool validateWhileTyping: false property string acceptedPassword: "" @@ -21,7 +20,6 @@ HFlickableColumnPopup { signal cancelled() - function verifyPassword(pass, callback) { // Can be reimplemented when using this component. // Pass to the callback true on success, false on invalid password, @@ -77,7 +75,6 @@ HFlickableColumnPopup { onOpened: passwordField.forceActiveFocus() onKeyboardAccept: popup.validate() - SummaryLabel { id: summary } DetailsLabel { id: details } diff --git a/src/gui/Popups/RedactPopup.qml b/src/gui/Popups/RedactPopup.qml index 0bc8d8c1..87be185b 100644 --- a/src/gui/Popups/RedactPopup.qml +++ b/src/gui/Popups/RedactPopup.qml @@ -8,7 +8,6 @@ import "../Base/Buttons" HFlickableColumnPopup { id: popup - property string preferUserId: "" property string roomId: "" @@ -16,7 +15,6 @@ HFlickableColumnPopup { property bool onlyOwnMessageWarning: false property bool isLast: false - function remove() { const idsForSender = {} // {senderId: [event.id, ...]} @@ -53,7 +51,6 @@ HFlickableColumnPopup { onOpened: reasonField.item.forceActiveFocus() onKeyboardAccept: popup.remove() - SummaryLabel { text: isLast ? diff --git a/src/gui/Popups/RemoveMemberPopup.qml b/src/gui/Popups/RemoveMemberPopup.qml index a4f94936..deb2cba4 100644 --- a/src/gui/Popups/RemoveMemberPopup.qml +++ b/src/gui/Popups/RemoveMemberPopup.qml @@ -8,7 +8,6 @@ import "../Base/Buttons" HFlickableColumnPopup { id: popup - property string userId property string roomId property string targetUserId @@ -50,7 +49,6 @@ HFlickableColumnPopup { onOpened: reasonField.item.forceActiveFocus() onKeyboardAccept: popup.remove() - SummaryLabel { textFormat: Text.StyledText text: diff --git a/src/gui/Popups/SignOutPopup.qml b/src/gui/Popups/SignOutPopup.qml index b3f7e08a..0f31faab 100644 --- a/src/gui/Popups/SignOutPopup.qml +++ b/src/gui/Popups/SignOutPopup.qml @@ -8,7 +8,6 @@ import "../Base/Buttons" HFlickableColumnPopup { id: popup - property string userId: "" @@ -54,7 +53,6 @@ HFlickableColumnPopup { onOpened: exportButton.forceActiveFocus() - SummaryLabel { text: qsTr("Backup your decryption keys before signing out?") } diff --git a/src/gui/Popups/UnexpectedErrorPopup.qml b/src/gui/Popups/UnexpectedErrorPopup.qml index da49a087..71c9d043 100644 --- a/src/gui/Popups/UnexpectedErrorPopup.qml +++ b/src/gui/Popups/UnexpectedErrorPopup.qml @@ -9,7 +9,6 @@ import "../Base/Buttons" HColumnPopup { id: popup - property string errorType property string message: "" property string traceback: "" @@ -31,7 +30,6 @@ HColumnPopup { onOpened: cancelButton.forceActiveFocus() - SummaryLabel { text: qsTr("Unexpected error occured: %1").arg(errorType) textFormat: Text.StyledText diff --git a/src/gui/PythonBridge/EventHandlers.qml b/src/gui/PythonBridge/EventHandlers.qml index 162d9e18..4a118c4b 100644 --- a/src/gui/PythonBridge/EventHandlers.qml +++ b/src/gui/PythonBridge/EventHandlers.qml @@ -7,12 +7,10 @@ import "../.." QtObject { signal deviceUpdateSignal(string forAccount) - function onExitRequested(exitCode) { Qt.exit(exitCode) } - function onAlertRequested(highImportance) { const msec = highImportance ? @@ -24,7 +22,6 @@ QtObject { } } - function onCoroutineDone(uuid, result, error, traceback) { const onSuccess = Globals.pendingCoroutines[uuid].onSuccess const onError = Globals.pendingCoroutines[uuid].onError @@ -48,14 +45,12 @@ QtObject { if (onSuccess) onSuccess(result) } - function onLoopException(message, error, traceback) { // No need to log these here, the asyncio exception handler does it const type = py.getattr(py.getattr(error, "__class__"), "__name__") utils.showError(type, traceback, message) } - function onModelItemSet(syncId, indexThen, indexNow, changedFields){ if (indexThen === undefined) { // print("insert", syncId, indexThen, indexNow, @@ -74,19 +69,16 @@ QtObject { } } - function onModelItemDeleted(syncId, index, count=1) { // print("delete", syncId, index, count) ModelStore.get(syncId).remove(index, count) } - function onModelCleared(syncId) { // print("clear", syncId) ModelStore.get(syncId).clear() } - function onDevicesUpdated(forAccount) { deviceUpdateSignal(forAccount) } diff --git a/src/gui/PythonBridge/Future.qml b/src/gui/PythonBridge/Future.qml index d9a6a102..c6029a53 100644 --- a/src/gui/PythonBridge/Future.qml +++ b/src/gui/PythonBridge/Future.qml @@ -5,16 +5,14 @@ import QtQuick 2.12 QtObject { id: future - property PythonBridge bridge readonly property QtObject privates: QtObject { - onPythonFutureChanged: if (cancelPending) future.cancel() - property var pythonFuture: null property bool cancelPending: false - } + onPythonFutureChanged: if (cancelPending) future.cancel() + } function cancel() { if (! privates.pythonFuture) { diff --git a/src/gui/PythonBridge/PythonBridge.qml b/src/gui/PythonBridge/PythonBridge.qml index 9a09fd4a..71aa974d 100644 --- a/src/gui/PythonBridge/PythonBridge.qml +++ b/src/gui/PythonBridge/PythonBridge.qml @@ -8,10 +8,8 @@ import "." Python { id: py - readonly property var pendingCoroutines: Globals.pendingCoroutines - function makeFuture(callback) { return Qt.createComponent("Future.qml").createObject(py, {bridge: py}) } diff --git a/src/gui/PythonBridge/PythonRootBridge.qml b/src/gui/PythonBridge/PythonRootBridge.qml index 1b233e34..5c17eecb 100644 --- a/src/gui/PythonBridge/PythonRootBridge.qml +++ b/src/gui/PythonBridge/PythonRootBridge.qml @@ -4,6 +4,12 @@ import QtQuick 2.12 import "." PythonBridge { + property bool ready: false + property bool startupAnyAccountsSaved: false + + readonly property EventHandlers eventHandlers: EventHandlers {} + + Component.onCompleted: { for (var func in eventHandlers) { if (! eventHandlers.hasOwnProperty(func)) continue @@ -25,10 +31,4 @@ PythonBridge { }) }) } - - - property bool ready: false - property bool startupAnyAccountsSaved: false - - readonly property EventHandlers eventHandlers: EventHandlers {} } diff --git a/src/gui/ShortcutBundles/FlickShortcuts.qml b/src/gui/ShortcutBundles/FlickShortcuts.qml index e64ecdab..239007bf 100644 --- a/src/gui/ShortcutBundles/FlickShortcuts.qml +++ b/src/gui/ShortcutBundles/FlickShortcuts.qml @@ -6,7 +6,6 @@ import "../Base" HQtObject { id: root - property Item flickable: parent property bool active: true diff --git a/src/gui/ShortcutBundles/TabShortcuts.qml b/src/gui/ShortcutBundles/TabShortcuts.qml index 52759de8..791cd9a9 100644 --- a/src/gui/ShortcutBundles/TabShortcuts.qml +++ b/src/gui/ShortcutBundles/TabShortcuts.qml @@ -6,7 +6,6 @@ import "../Base" HQtObject { id: root - property Item container: parent property bool active: container.count > 1 diff --git a/src/gui/UI.qml b/src/gui/UI.qml index fe4c9ec7..73e670ea 100644 --- a/src/gui/UI.qml +++ b/src/gui/UI.qml @@ -10,10 +10,6 @@ import "MainPane" Item { id: mainUI - focus: true - - Component.onCompleted: window.mainUI = mainUI - property bool accountsPresent: ModelStore.get("accounts").count > 0 || py.startupAnyAccountsSaved @@ -35,12 +31,14 @@ Item { readonly property alias fontMetrics: fontMetrics readonly property alias idleManager: idleManager - function reloadSettings() { py.loadSettings(() => { mainUI.pressAnimation.start() }) } + focus: true + Component.onCompleted: window.mainUI = mainUI + SequentialAnimation { id: pressAnimation HNumberAnimation { diff --git a/src/gui/Window.qml b/src/gui/Window.qml index f05cd85d..f3022f25 100644 --- a/src/gui/Window.qml +++ b/src/gui/Window.qml @@ -7,14 +7,6 @@ import "PythonBridge" ApplicationWindow { id: window - flags: Qt.WA_TranslucentBackground - minimumWidth: theme ? theme.minimumSupportedWidth : 240 - minimumHeight: theme ? theme.minimumSupportedHeight : 120 - width: Math.min(screen.width, 1152) - height: Math.min(screen.height, 768) - visible: true - color: "transparent" - // FIXME: Qt 5.13.1 bug, this randomly stops updating after the cursor // leaves the window until it's clicked again. @@ -26,20 +18,10 @@ ApplicationWindow { window.visibility === window.Minimized || window.visibility === window.Hidden - // NOTE: For JS object variables, the corresponding method to notify - // key/value changes must be called manually, e.g. settingsChanged(). - property var mainUI: null - property var settings: ({}) - onSettingsChanged: py.saveConfig("ui_settings", settings) - property var uiState: ({}) - onUiStateChanged: py.saveConfig("ui_state", uiState) - property var history: ({}) - onHistoryChanged: py.saveConfig("history", history) - property var theme: null property var hideErrorTypes: new Set(["gaierror", "SSLError"]) @@ -77,6 +59,20 @@ ApplicationWindow { } + flags: Qt.WA_TranslucentBackground + minimumWidth: theme ? theme.minimumSupportedWidth : 240 + minimumHeight: theme ? theme.minimumSupportedHeight : 120 + width: Math.min(screen.width, 1152) + height: Math.min(screen.height, 768) + visible: true + color: "transparent" + + // NOTE: For JS object variables, the corresponding method to notify + // key/value changes must be called manually, e.g. settingsChanged(). + onSettingsChanged: py.saveConfig("ui_settings", settings) + onUiStateChanged: py.saveConfig("ui_state", uiState) + onHistoryChanged: py.saveConfig("history", history) + PythonRootBridge { id: py } Utils { id: utils }