diff --git a/TODO.md b/TODO.md index d2053c11..221bfdbd 100644 --- a/TODO.md +++ b/TODO.md @@ -1,10 +1,12 @@ - Refactoring + - `^property type name$` - Use [Animators](https://doc.qt.io/qt-5/qml-qtquick-animator.html) + - Choose a better default easing type for animations + - Use overshoot for the scale login thing - Sendbox - - SignIn/RememberAccount screens - - SignIn must be in a flickable - - Unfinished work in button-refactor branch - - Button can get "hoverEnabled: false" to let HoverHandlers work + - HButton + - Control: hovered, visualFocus, enaled + - Border and pressed color in theme / checkbox theming - Room Sidepane - Hide when window too small - Also save/load its size @@ -88,7 +90,7 @@ - Spinner when loading account, past room events, images or clicking buttons - Show account page as loading until profile initially retrieved - Theming - - Support SVG hue via image provider for icons + - Make all icons white/black, since we can now use ColorOverlay - Don't create additional lines in theme conversion (braces) - Recursively merge default and user theme - Distribute fonts @@ -121,6 +123,7 @@ - Links preview - Client improvements + - Logout previous session if adding an account that's already connected - Image provider: on failed conversion, way to show a "broken image" thumb? - Config file format - Initial sync filter and lazy load, see weechat-matrix `_handle_login()` diff --git a/src/icons/light-thin/check-mark.svg b/src/icons/light-thin/check-mark.svg new file mode 100644 index 00000000..246dd199 --- /dev/null +++ b/src/icons/light-thin/check-mark.svg @@ -0,0 +1,52 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/src/qml/Base/HCheckBox.qml b/src/qml/Base/HCheckBox.qml new file mode 100644 index 00000000..7aac9fe0 --- /dev/null +++ b/src/qml/Base/HCheckBox.qml @@ -0,0 +1,48 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import "../utils.js" as Utils + +CheckBox { + id: box + spacing: theme.spacing + + property bool highlight: enabled && (hovered || visualFocus) + + indicator: Rectangle { + implicitWidth: implicitHeight + implicitHeight: box.contentItem.font.pixelSize * 1.5 + x: box.leftPadding + y: box.topPadding + box.availableHeight / 2 - height / 2 + radius: theme.radius / 1.5 + + color: theme.controls.button.background + border.color: Utils.hsluv( + 180, highlight ? 80 : 0, highlight ? 80 : 40, highlight ? 1 : 0.7 + ) + + Behavior on border.color { HColorAnimation { factor: 0.5 } } + + HIcon { + anchors.centerIn: parent + dimension: parent.width - 2 + svgName: "check-mark" + colorize: theme.colors.accentText + + visible: scale > 0 + scale: box.checked ? 1 : 0 + Behavior on scale { HNumberAnimation { overshoot: 4 } } + } + } + + contentItem: HLabel { + text: box.text + color: box.enabled ? + theme.controls.button.text : + theme.controls.button.disabledText + + // Set a width on CheckBox for wrapping to work, e.g. Layout.fillWidth + wrapMode: Text.Wrap + leftPadding: box.indicator.width + box.spacing + verticalAlignment: Text.AlignVCenter + } +} diff --git a/src/qml/Base/HImage.qml b/src/qml/Base/HImage.qml index 68967ed1..ecb5b186 100644 --- a/src/qml/Base/HImage.qml +++ b/src/qml/Base/HImage.qml @@ -1,8 +1,20 @@ import QtQuick 2.12 +import QtGraphicalEffects 1.12 Image { + id: image asynchronous: true cache: true - mipmap: true fillMode: Image.PreserveAspectFit + + property color colorize: "transparent" + + layer { + enabled: ! Qt.colorEqual(colorize, "transparent") + effect: ColorOverlay { + color: image.colorize + cached: image.cache + } + } + } diff --git a/src/qml/Base/HInterfaceBox.qml b/src/qml/Base/HInterfaceBox.qml index 2043cab1..57ede4fc 100644 --- a/src/qml/Base/HInterfaceBox.qml +++ b/src/qml/Base/HInterfaceBox.qml @@ -1,8 +1,23 @@ import QtQuick 2.12 import QtQuick.Layouts 1.12 -HScalingBox { +HRectangle { id: interfaceBox + color: theme.controls.box.background + implicitWidth: Math.min( + parent.width, theme.minimumSupportedWidthPlusSpacing * multiplyWidth + ) + implicitHeight: childrenRect.height + + property real multiplyWidth: 1.0 + property real multiplyHorizontalSpacing: 1.5 + property real multiplyVerticalSpacing: 1.5 + + property int horizontalSpacing: + Math.min(theme.spacing * width / 400, theme.spacing) * + multiplyHorizontalSpacing + + property int verticalSpacing: theme.spacing * multiplyVerticalSpacing property alias title: interfaceTitle.text property alias buttonModel: interfaceButtonsRepeater.model @@ -18,25 +33,35 @@ HScalingBox { } } + Keys.onReturnPressed: clickEnterButtonTarget() + Keys.onEnterPressed: clickEnterButtonTarget() + HColumnLayout { - anchors.fill: parent id: mainColumn + width: parent.width + spacing: interfaceBox.verticalSpacing - HRowLayout { - Layout.alignment: Qt.AlignHCenter - Layout.margins: interfaceBox.margins + HLabel { + id: interfaceTitle + visible: Boolean(text) + font.pixelSize: theme.fontSize.bigger + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap - HLabel { - id: interfaceTitle - font.pixelSize: theme.fontSize.bigger - } + Layout.fillWidth: true + Layout.topMargin: interfaceBox.verticalSpacing + Layout.leftMargin: interfaceBox.horizontalSpacing + Layout.rightMargin: interfaceBox.horizontalSpacing } - HSpacer {} + HColumnLayout { + id: interfaceBody + spacing: interfaceBox.verticalSpacing - HColumnLayout { id: interfaceBody } - - HSpacer {} + Layout.fillWidth: true + Layout.leftMargin: interfaceBox.horizontalSpacing + Layout.rightMargin: interfaceBox.horizontalSpacing + } HRowLayout { Repeater { @@ -53,7 +78,7 @@ HScalingBox { onClicked: buttonCallbacks[modelData.name](button) Layout.fillWidth: true - Layout.preferredHeight: theme.controls.avatar.size + Layout.preferredHeight: theme.baseElementsHeight } } } diff --git a/src/qml/Base/HNumberAnimation.qml b/src/qml/Base/HNumberAnimation.qml index 2cbafdda..7864f5ff 100644 --- a/src/qml/Base/HNumberAnimation.qml +++ b/src/qml/Base/HNumberAnimation.qml @@ -1,6 +1,10 @@ import QtQuick 2.12 NumberAnimation { - property real factor: 1.0 + property real factor: Math.max(overshoot / 1.75, 1.0) + property real overshoot: 1.0 + duration: theme.animationDuration * factor + easing.type: overshoot > 1 ? Easing.InOutBack : Easing.Linear + easing.overshoot: overshoot } diff --git a/src/qml/Base/HPage.qml b/src/qml/Base/HPage.qml index 7d7dde54..019415bf 100644 --- a/src/qml/Base/HPage.qml +++ b/src/qml/Base/HPage.qml @@ -38,12 +38,13 @@ SwipeView { implicitWidth: parent ? parent.width : 0 color: theme.controls.header.background - height: ! hideHeaderUnderHeight || - window.height >= - hideHeaderUnderHeight + - theme.baseElementsHeight + - currentSpacing * 2 ? - theme.baseElementsHeight : 0 + height: innerHeaderLabel.text && ( + ! hideHeaderUnderHeight || + window.height >= + hideHeaderUnderHeight + + theme.baseElementsHeight + + currentSpacing * 2 + ) ? theme.baseElementsHeight : 0 Behavior on height { HNumberAnimation {} } visible: height > 0 diff --git a/src/qml/Pages/RememberAccount.qml b/src/qml/Pages/RememberAccount.qml deleted file mode 100644 index 6126f225..00000000 --- a/src/qml/Pages/RememberAccount.qml +++ /dev/null @@ -1,46 +0,0 @@ -import QtQuick 2.12 -import QtQuick.Layouts 1.12 -import "../Base" - -Item { - property string loginWith: "username" - property string userId: "" - - HInterfaceBox { - id: rememberBox - title: "Sign in" - anchors.centerIn: parent - - enterButtonTarget: "yes" - - buttonModel: [ - { name: "yes", text: qsTr("Yes") }, - { name: "no", text: qsTr("No") }, - ] - - buttonCallbacks: ({ - yes: button => { - py.callCoro("saved_accounts.add", [userId]) - pageStack.showPage("EditAccount/EditAccount", {userId}) - }, - no: button => { - py.callCoro("saved_accounts.delete", [userId]) - pageStack.showPage("EditAccount/EditAccount", {userId}) - }, - }) - - HLabel { - text: qsTr( - "Do you want to remember this account?\n\n" + - "If yes, the " + loginWith + " and an access token will be " + - "stored to automatically sign in on this device." - ) - wrapMode: Text.Wrap - - Layout.margins: rememberBox.margins - Layout.fillWidth: true - } - - HSpacer {} - } -} diff --git a/src/qml/Pages/SignIn.qml b/src/qml/Pages/SignIn.qml index 96ab22be..14a4488a 100644 --- a/src/qml/Pages/SignIn.qml +++ b/src/qml/Pages/SignIn.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Layouts 1.12 import "../Base" -Item { +HPage { property string loginWith: "username" readonly property bool canLogin: idField.text && passwordField.text @@ -10,15 +10,16 @@ Item { HInterfaceBox { id: signInBox - title: "Sign in" - anchors.centerIn: parent + Layout.alignment: Qt.AlignCenter + multiplyWidth: 0.85 + title: qsTr("Sign in") enterButtonTarget: "login" buttonModel: [ { name: "register", text: qsTr("Register"), enabled: false }, { name: "login", text: qsTr("Login"), enabled: canLogin }, - { name: "forgot", text: qsTr("Forgot?"), enabled: false } + { name: "forgot", text: qsTr("Forgot?"), enabled: false }, ] buttonCallbacks: ({ @@ -29,14 +30,23 @@ Item { let args = [idField.text, passwordField.text] py.callCoro("login_client", args, ([success, data]) => { - if (success) { - // data = userId - errorMessage.text = "" - pageStack.showPage("RememberAccount", {loginWith,data}) - } else { + if (! success) { errorMessage.text = qsTr(data) + button.loading = false + return } - button.loading = false + + py.callCoro( + "saved_accounts." + + (rememberAccount.checked ? "add": "delete"), + [data] + ) + pageStack.showPage( + "EditAccount/EditAccount", {userId: data} + ) + + errorMessage.text = "" + button.loading = false }) }, @@ -44,9 +54,10 @@ Item { }) HRowLayout { - spacing: signInBox.margins * 1.25 - Layout.margins: signInBox.margins + spacing: signInBox.horizontalSpacing * 1.25 Layout.alignment: Qt.AlignHCenter + Layout.topMargin: signInBox.verticalSpacing / 2 + Layout.bottomMargin: Layout.topMargin Repeater { model: ["username", "email", "phone"] @@ -70,20 +81,26 @@ Item { loginWith === "phone" ? "Phone" : "Username" ) - onAccepted: signInBox.clickEnterButtonTarget() Layout.fillWidth: true - Layout.margins: signInBox.margins } HTextField { id: passwordField placeholderText: qsTr("Password") echoMode: HTextField.Password - onAccepted: signInBox.clickEnterButtonTarget() Layout.fillWidth: true - Layout.margins: signInBox.margins + } + + HCheckBox { + id: rememberAccount + text: qsTr("Automatically sign in") + checked: true + spacing: signInBox.horizontalSpacing + + Layout.maximumWidth: parent.width + Layout.alignment: Qt.AlignHCenter } HLabel { @@ -97,7 +114,6 @@ Item { Behavior on Layout.maximumHeight { HNumberAnimation {} } Layout.fillWidth: true - Layout.margins: signInBox.margins } } }