Refactor HInterfaceBox and SignIn page
- Cleaned up code - Sizes are standardized - New HCheckBox component - Replace RememberAccount page by a checkbox on the SignIn page - The page can now be flicked if it's not tall enough to display everything - HNumberAnimation now has an overshoot property that will set the right easing type, overshoot and adapt the animation factor/duration. - HImage can now be colorized
This commit is contained in:
		
							
								
								
									
										13
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								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()`
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										52
									
								
								src/icons/light-thin/check-mark.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/icons/light-thin/check-mark.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="24"
 | 
			
		||||
   height="24"
 | 
			
		||||
   fill-rule="evenodd"
 | 
			
		||||
   clip-rule="evenodd"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   id="svg4"
 | 
			
		||||
   sodipodi:docname="check-mark.svg"
 | 
			
		||||
   inkscape:version="">
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata10">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs8" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1"
 | 
			
		||||
     objecttolerance="10"
 | 
			
		||||
     gridtolerance="10"
 | 
			
		||||
     guidetolerance="10"
 | 
			
		||||
     inkscape:pageopacity="0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:window-width="640"
 | 
			
		||||
     inkscape:window-height="480"
 | 
			
		||||
     id="namedview6"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     inkscape:zoom="9.8333333"
 | 
			
		||||
     inkscape:cx="-7.6779661"
 | 
			
		||||
     inkscape:cy="12"
 | 
			
		||||
     inkscape:current-layer="svg4" />
 | 
			
		||||
  <path
 | 
			
		||||
     d="M21 6.285l-11.16 12.733-6.84-6.018 1.319-1.49 5.341 4.686 9.865-11.196 1.475 1.285z"
 | 
			
		||||
     id="path2"
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:1" />
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 1.5 KiB  | 
							
								
								
									
										48
									
								
								src/qml/Base/HCheckBox.qml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/qml/Base/HCheckBox.qml
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user