mailmodel 0.1.5

A proof-of-concept mail viewer writting with Qt using QML
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2
import QtQuick.Dialogs 1.2

Item {
    QtObject {
        id: p
        readonly property string no_html_msg: breakIntoLines(qsTr("No HTML Message"))
        readonly property string plain_msg: breakIntoLines(qsTr("Plain Message"))
        readonly property string html_msg: breakIntoLines(qsTr("HTML Message"))
        property string html_toggle: plain_msg
        property bool has_html: mailmodel.email.html
        property bool show_html: has_html && html_toggle === html_msg
        readonly property real html_bar_width: 12
        readonly property real padding: 5
        readonly property real scrollbar_width: 18
        readonly property real view_width: mail.width - scrollbar_width
    }
    Rectangle {
        width: p.html_bar_width
        height: parent.height
        color: p.show_html ? "black" : palette.mid
        Text {
            id: html_status
            anchors.fill: parent
            text: p.has_html ? p.html_toggle : p.no_html_msg
            color: p.show_html ? "white" : "black"
            horizontalAlignment: Text.AlignHCenter
            wrapMode: Text.WrapAnywhere
        }
        MouseArea {
            anchors.fill: parent
            onClicked: {
                if (!p.has_html) {
                    return;
                }
                if (p.html_toggle === p.plain_msg) {
                    p.html_toggle = p.html_msg
                } else {
                    p.html_toggle = p.plain_msg
                }
            }
        }
    }
    ScrollView {
        id: mail
        x: p.html_bar_width
        width: parent.width - p.html_bar_width
        height: parent.height
        Item {
            width: Math.max(p.view_width, plain_text.contentWidth,
                html_text.contentWidth, from.x + from.width + 2 * p.padding,
                date.x + date.width + 2 * p.padding,
                attachments.x + attachments.width + 2 * p.padding)
            height: body.y + body.height
            Text {
                id: subject
                width: p.view_width
                text: mailmodel.email.subject
                textFormat: Text.PlainText
                font.pointSize: 1.5 * from_label.font.pointSize
                wrapMode: Text.WordWrap
                padding: p.padding
            }
            GridLayout {
                id: labels
                columns: 2
                x: p.padding
                y: subject.height
                width: p.view_width
                Text {
                    id: from_label
                    text: qsTr("From:")
                    textFormat: Text.PlainText
                }
                Text {
                    id: from
                    text: mailmodel.email.from
                    textFormat: Text.PlainText
                    wrapMode: Text.WordWrap
                }
                Text {
                    text: qsTr("Date:")
                    textFormat: Text.PlainText
                }
                Text {
                    id: date
                    text: mailmodel.email.date
                    textFormat: Text.PlainText
                    wrapMode: Text.WordWrap
                }
                Text {
                    text: qsTr("Attachments:")
                    textFormat: Text.PlainText
                    visible: attach.count > 0
                }
                Flow {
                    id: attachments
                    visible: attach.count > 0
                    spacing: date.font.pixelSize
                    Repeater {
                        id: attach
                        model: mailmodel.email.attachments
                        Text {
                            text: name
                            visible: text !== "unnamed"
                            MouseArea {
                                anchors.fill: parent
                                cursorShape: Qt.PointingHandCursor
                                onClicked: {
                                    save_dialog.title = qsTr("Save ") + name
                                    save_dialog.visible = true
                                }
                            }
                            FileDialog {
                                id: save_dialog
                                selectFolder: true
                                selectExisting: false
                                selectMultiple: false
                                folder: shortcuts.home
                                onAccepted: {
                                    var r = mailmodel.email.attachments.saveToFolder(index, save_dialog.fileUrl);
                                    if (r) {
                                        save_error.title = r;
                                        save_error_text.text = qsTr("Could not save file ") + mailmodel.email.attachments.name(index) + qsTr(" to ") + save_dialog.fileUrl + ":\n" + r;
                                        save_error.visible = true;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            Rectangle {
                id: line
                y: labels.y + labels.height + p.padding
                width: parent.width
                height: 1
                color: palette.mid
            }
            Item {
                id: body
                y: line.y + line.height + p.padding
                height: Math.max(plain_text.height, html_text.height)
                TextEdit {
                    id: plain_text
                    visible: !p.show_html
                    width: p.view_width
                    text: !p.show_html ?mailmodel.email.body :""
                    textFormat: Text.PlainText
                    wrapMode: Text.WordWrap
                    readOnly: true
                    selectByMouse: true
                    padding: p.padding
                    selectionColor: palette.highlight
                    selectedTextColor: palette.highlightedText
                }
                TextEdit {
                    id: html_text
                    visible: p.show_html
                    width: p.view_width
                    text: p.show_html ?mailmodel.email.html :""
                    textFormat: Text.RichText
                    wrapMode: Text.WordWrap
                    readOnly: true
                    selectByMouse: true
                    padding: p.padding
                    selectionColor: palette.highlight
                    selectedTextColor: palette.highlightedText
                    onLinkActivated: Qt.openUrlExternally(link)
                }
                // Show a Qt.PointingHandCursor when hovering a link
                MouseArea {
                    id: link_mouse
                    visible: html_text.hoveredLink
                    anchors.fill: html_text
                    cursorShape: Qt.PointingHandCursor
                    acceptedButtons: Qt.NoButton
                }
            }
        }
    }
    Rectangle {
        color: "black"
        visible: link_mouse.visible
        x: mail.x
        y: mail.y + mail.height - link_text.height
        width: link_text.width
        height: link_text.height
        Text {
            id: link_text
            color: "white"
            text: html_text.hoveredLink
        }
    }
    function breakIntoLines(msg) {
        return msg.split('').join('\n');
    }

    Dialog {
        id: save_error
        title: "Could not save file"
        standardButtons: StandardButton.Ok
        Text {
            id: save_error_text
            width: 40 * font.pixelSize
            textFormat: Text.PlainText
            wrapMode: Text.WordWrap
        }
    }
}