Implement basic UI with QML (MailListPage) and batching DbChangeProcessor (Step 3-4 of transition plan)

This commit is contained in:
Padrino
2026-05-17 01:09:01 +02:00
parent e3071a23e0
commit acec320222
20 changed files with 829 additions and 82 deletions
+136
View File
@@ -0,0 +1,136 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Item {
id: mailListPage
anchors.fill: parent
// Layout: SplitView for resizable panes
SplitView {
anchors.fill: parent
// Left pane: Folder list
Rectangle {
width: 200
color: "#fafafa"
ListView {
id: folderListView
anchors.fill: parent
clip: true
model: FolderModel {}
delegate: Item {
height: 40
Layout.fillWidth: true
Rectangle {
anchors.fill: parent
color: ListView.isCurrentItem ? "#e3f2fd" : "transparent"
Text {
text: folderName
anchors.left: parent.left
anchors.leftMargin: 16
anchors.verticalCenter: parent.verticalCenter
font.pointSize: 14
color: ListView.isCurrentItem ? "#1976d2" : "#666"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
folderListView.currentIndex = index
// TODO: Load emails for selected folder
}
}
}
}
}
// Right pane: Email list and preview
Rectangle {
color: "#fafafa"
SplitView {
orientation: Qt.Vertical
anchors.fill: parent
// Email list (top)
Rectangle {
Layout.minimumHeight: 200
color: "#fafafa"
ListView {
id: emailListView
anchors.fill: parent
clip: true
model: EmailModel {}
delegate: Item {
height: 60
Layout.fillWidth: true
Rectangle {
anchors.fill: parent
color: ListView.isCurrentItem ? "#e3f2fd" : "transparent"
RowLayout {
anchors.fill: parent
anchors.margins: 8
// Sender avatar/initial
Rectangle {
width: 40
height: 40
color: "#cccccc"
Text {
text: senderInitial
anchors.centerIn: parent
font.pointSize: 16
color: "#666"
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: 4
Text {
text: senderName
font.pointSize: 14
color: "#333"
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: subject
font.pointSize: 13
color: "#666"
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: time
font.pointSize: 12
color: "#999"
}
}
}
}
MouseArea {
anchors.fill: parent
onClicked: {
emailListView.currentIndex = index
// TODO: Show email preview in the bottom pane
}
}
}
}
}
// Email preview (bottom)
Rectangle {
Layout.minimumHeight: 200
color: "#ffffff"
Text {
id: emailPreview
text: "Select an email to preview"
anchors.centerIn: parent
font.pointSize: 14
color: "#666"
}
}
}
}
}
}
+174
View File
@@ -0,0 +1,174 @@
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
ApplicationWindow {
id: shell
visible: true
width: 1024
height: 768
title: qsTr("Wino Mail Qt")
// Set the title from translator in Component.onCompleted
Component.onCompleted: {
title = translator.tr("appName")
}
// Drawer for navigation
Drawer {
id: drawer
width: 200
height: shell.height
Container {
anchors.fill: parent
Layout.alignment: Qt.AlignTop
ScrollView {
anchors.fill: parent
ColumnLayout {
spacing: 0
// App logo/name
Rectangle {
Layout.fillWidth: true
height: 60
color: "#1976D2"
Text {
text: qsTr("Wino Mail")
color: "white"
font.pointSize: 18
anchors.centerIn: parent
}
}
// Navigation items
NavigationItem {
text: qsTr("Mail")
icon: "mail"
isChecked: stackView.currentIndex === 0
onClicked: {
drawer.close()
stackView.currentIndex = 0
}
}
NavigationItem {
text: qsTr("Calendar")
icon: "calendar"
isChecked: stackView.currentIndex === 1
onClicked: {
drawer.close()
stackView.currentIndex = 1
}
}
NavigationItem {
text: qsTr("Contacts")
icon: "contacts"
isChecked: stackView.currentIndex === 2
onClicked: {
drawer.close()
stackView.currentIndex = 2
}
}
NavigationItem {
text: qsTr("Settings")
icon: "settings"
isChecked: stackView.currentIndex === 3
onClicked: {
drawer.close()
stackView.currentIndex = 3
}
}
}
}
}
}
// Main content area with StackView for pages
StackView {
id: stackView
anchors.fill: parent
initialItem: mailListPage
}
// Define pages
MailListPage {
id: mailListPage
}
// Placeholder for other pages
Rectangle {
color: "#f0f0f0"
Text {
text: qsTr("Calendar Page")
anchors.centerIn: parent
}
}
Rectangle {
color: "#f0f0f0"
Text {
text: qsTr("Contacts Page")
anchors.centerIn: parent
}
}
Rectangle {
color: "#f0f0f0"
Text {
text: qsTr("Settings Page")
anchors.centerIn: parent
}
}
// Hamburger button to open drawer
MenuButton {
icon: "menu"
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: 10
onClicked: drawer.open()
}
}
// Helper components
component NavigationItem: Button {
Layout.fillWidth: true
height: 48
padding: 0
contentItem: RowLayout {
spacing: 16
anchors.fill: parent
anchors.margins: 24
Icon {
source: icon
width: 24
height: 24
color: isChecked ? "#1976D2" : "#666"
}
Text {
text: text
font.pointSize: 14
verticalAlignment: Text.AlignVCenter
color: isChecked ? "#1976D2" : "#666"
}
}
background: Rectangle {
color: isChecked ? "#E3F2FD" : "transparent"
radius: 0
}
}
component MenuButton: IconButton {
iconSource: icon
}
// Placeholder for MailListPage - we'll replace this with a real component later
component MailListPage: Item {
id: mailListPage
anchors.fill: parent
Rectangle {
anchors.fill: parent
color: "#fafafa"
Text {
text: qsTr("Mail List Placeholder")
anchors.centerIn: parent
font.pointSize: 16
color: "#666"
}
}
}
+2 -24
View File
@@ -2,28 +2,6 @@ import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 1024
height: 768
visible: true
title: qsTr("Wino Mail Qt")
// Use the translator to set the title
// Note: we can't directly call translator.tr here because it's a C++ object.
// We'll set the title in Component.onCompleted by accessing the context property.
Component.onCompleted: {
title = translator.tr("appName")
}
Rectangle {
anchors.fill: parent
color: "#f0f0f0"
Text {
text: qsTr("Welcome to Wino Mail Qt!")
anchors.centerIn: parent
font.pointSize: 24
color: "#333"
}
}
// We'll load the Shell component from Shell.qml
Shell {
}
+24
View File
@@ -0,0 +1,24 @@
import QtQuick 2.15
ListModel {
ListElement {
folderName: "Inbox"
unreadCount: 5
}
ListElement {
folderName: "Sent"
unreadCount: 0
}
ListElement {
folderName: "Drafts"
unreadCount: 0
}
ListElement {
folderName: "Trash"
unreadCount: 0
}
ListElement {
folderName: "Spam"
unreadCount: 0
}
}