feat(misc): a bunch of crude UI/UX tweaks

- display add time for later use
- basic keyboard navigation support
- remember last-used server address
- show connection error when attempt to connect to server
This commit is contained in:
Gary Wang 2024-11-15 20:58:03 +08:00
parent 1f7adc2a3a
commit edb3338bc4
No known key found for this signature in database
GPG Key ID: 5D30A4F15EA78760
8 changed files with 59 additions and 8 deletions

View File

@ -11,12 +11,14 @@ Control {
property var curComicIndex: AppController.currentComicModelIndex() property var curComicIndex: AppController.currentComicModelIndex()
property bool startReading: AppController.selectedComicOpened property bool startReading: AppController.selectedComicOpened
readonly property int comicPageCount: dataByRole(ComicItem.PageCountRole)
function dataByRole(role) { function dataByRole(role) {
return AppController.comicsModel.data(curComicIndex, role) return AppController.comicsModel.data(curComicIndex, role)
} }
contentItem: ColumnLayout { contentItem: ColumnLayout {
enabled: !root.startReading
Image { Image {
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumHeight: Math.min(root.width / 2, root.height / 2) Layout.maximumHeight: Math.min(root.width / 2, root.height / 2)
@ -32,7 +34,12 @@ Control {
} }
Label { Label {
Layout.preferredWidth: root.width - 10 Layout.preferredWidth: root.width - 10
text: `${dataByRole(ComicItem.PageCountRole)} pages` text: "Added: " + new Date(Number(`${dataByRole(ComicItem.AddedTimeRole)}000`)).toLocaleString(Qt.locale())
wrapMode: Text.Wrap
}
Label {
Layout.preferredWidth: root.width - 10
text: `${comicPageCount} pages`
wrapMode: Text.Wrap wrapMode: Text.Wrap
} }
Label { Label {
@ -43,6 +50,7 @@ Control {
Button { Button {
Layout.fillWidth: true Layout.fillWidth: true
text: "Read" text: "Read"
enabled: root.comicPageCount > 0
onClicked: function() { onClicked: function() {
AppController.openComic() AppController.openComic()
} }
@ -60,7 +68,7 @@ Control {
} }
ComicViewer { ComicViewer {
z: 2 parent: Overlay.overlay
visible: root.startReading visible: root.startReading
pageCount: dataByRole(ComicItem.PageCountRole) pageCount: dataByRole(ComicItem.PageCountRole)
} }

View File

@ -15,6 +15,12 @@ Pane {
id: view id: view
anchors.fill: parent anchors.fill: parent
onVisibleChanged: function() {
if (visible) {
forceActiveFocus()
}
}
Repeater { Repeater {
model: root.pageCount model: root.pageCount
Loader { Loader {
@ -46,11 +52,12 @@ Pane {
Pane { Pane {
visible: root.osdVisible visible: root.osdVisible
anchors.fill: parent anchors.fill: parent
opacity: 0.6 opacity: 0.65
} }
ColumnLayout { ColumnLayout {
visible: root.osdVisible visible: root.osdVisible
enabled: root.osdVisible
anchors.fill: parent anchors.fill: parent
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
@ -65,6 +72,7 @@ Pane {
text: "Close Comic" text: "Close Comic"
Layout.fillWidth: true Layout.fillWidth: true
onClicked: function() { onClicked: function() {
root.osdVisible = false
AppController.closeComic() AppController.closeComic()
} }
} }
@ -86,4 +94,8 @@ Pane {
} }
} }
} }
Keys.onEscapePressed: function(event) {
root.osdVisible = !root.osdVisible
}
} }

View File

@ -1,3 +1,4 @@
import QtCore
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
@ -21,17 +22,19 @@ Control {
TextField { TextField {
id: baseUrlEdit id: baseUrlEdit
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: "e.g. http://192.168.123.123:8080"
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhUrlCharactersOnly
enabled: AppController.connectionState === AppController.NotConnected enabled: AppController.connectionState === AppController.NotConnected
} }
Item { Label {
Layout.fillHeight: true text: AppController.lastErrorMessage
Layout.verticalStretchFactor: 1
} }
Button { Button {
Layout.fillWidth: true Layout.fillWidth: true
text: AppController.connectionState === AppController.NotConnected ? "Connect" : "Connecting" text: AppController.connectionState === AppController.NotConnected ? "Connect" : "Connecting"
enabled: baseUrlEdit.enabled enabled: baseUrlEdit.enabled
onClicked: function() { onClicked: function() {
settings.setValue("server_url", baseUrlEdit.text)
AppController.connectServer(baseUrlEdit.text) AppController.connectServer(baseUrlEdit.text)
} }
} }
@ -40,4 +43,14 @@ Control {
Layout.verticalStretchFactor: 3 Layout.verticalStretchFactor: 3
} }
} }
Settings {
id: settings
Component.onCompleted: function() {
let srvUrl = settings.value("server_url", "")
if (srvUrl !== "") {
baseUrlEdit.text = srvUrl
}
}
}
} }

View File

@ -4,6 +4,8 @@
#include "dataitems/comicitem.h" #include "dataitems/comicitem.h"
#include "dataitems/folderitem.h" #include "dataitems/folderitem.h"
#include <chrono>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
@ -12,6 +14,8 @@
#include <QRestReply> #include <QRestReply>
#include <QSettings> #include <QSettings>
using namespace std::chrono_literals;
AppController::AppController(QObject *parent) AppController::AppController(QObject *parent)
: QObject(parent) : QObject(parent)
, m_networkAccessManager(new QNetworkAccessManager(this)) , m_networkAccessManager(new QNetworkAccessManager(this))
@ -30,17 +34,22 @@ void AppController::connectServer(QUrl serverBaseUrl)
setProperty("connectionState", Connecting); setProperty("connectionState", Connecting);
serverBaseUrl.setPath("/v2/"); serverBaseUrl.setPath("/v2/");
QNetworkRequestFactory api(serverBaseUrl); QNetworkRequestFactory api(serverBaseUrl);
m_restAccessManager->get(api.createRequest("version"), this, [=](QRestReply &reply){ QNetworkRequest req(api.createRequest("version"));
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy);
req.setTransferTimeout(5s);
m_restAccessManager->get(req, this, [=](QRestReply &reply){
qDebug() << reply.httpStatus() << reply << reply.isHttpStatusSuccess();
if (reply.isSuccess()) { if (reply.isSuccess()) {
qDebug() << reply.readText();
m_requestFactory.setBaseUrl(serverBaseUrl); m_requestFactory.setBaseUrl(serverBaseUrl);
m_requestFactory.clearCommonHeaders(); m_requestFactory.clearCommonHeaders();
QHttpHeaders commonHeaders; QHttpHeaders commonHeaders;
commonHeaders.append("X-Request-Id", "114514"); commonHeaders.append("X-Request-Id", "114514");
m_requestFactory.setCommonHeaders(commonHeaders); m_requestFactory.setCommonHeaders(commonHeaders);
setProperty("connectionState", Connected); setProperty("connectionState", Connected);
setProperty("lastErrorMessage", "");
} else { } else {
setProperty("connectionState", NotConnected); setProperty("connectionState", NotConnected);
setProperty("lastErrorMessage", QStringLiteral("%1: %2").arg(reply.httpStatus()).arg(reply.errorString()));
} }
}); });
} }

View File

@ -22,6 +22,7 @@ public:
Q_PROPERTY(int selectedLibraryId MEMBER m_currentLibraryId NOTIFY currentLibraryIdChanged FINAL) Q_PROPERTY(int selectedLibraryId MEMBER m_currentLibraryId NOTIFY currentLibraryIdChanged FINAL)
Q_PROPERTY(QString selectedComicId MEMBER m_currentComicId NOTIFY currentComicIdChanged FINAL) Q_PROPERTY(QString selectedComicId MEMBER m_currentComicId NOTIFY currentComicIdChanged FINAL)
Q_PROPERTY(bool selectedComicOpened MEMBER m_currentComicOpened NOTIFY currentComicOpenedChanged FINAL) Q_PROPERTY(bool selectedComicOpened MEMBER m_currentComicOpened NOTIFY currentComicOpenedChanged FINAL)
Q_PROPERTY(QString lastErrorMessage MEMBER m_lastErrorMessage NOTIFY lastErrorMessageChanged FINAL)
Q_PROPERTY(QStandardItemModel * librariesModel MEMBER m_librariesModel CONSTANT FINAL) Q_PROPERTY(QStandardItemModel * librariesModel MEMBER m_librariesModel CONSTANT FINAL)
Q_PROPERTY(QStandardItemModel * comicsModel MEMBER m_comicsModel CONSTANT FINAL) Q_PROPERTY(QStandardItemModel * comicsModel MEMBER m_comicsModel CONSTANT FINAL)
Q_PROPERTY(QStandardItemModel * foldersModel MEMBER m_foldersModel CONSTANT FINAL) Q_PROPERTY(QStandardItemModel * foldersModel MEMBER m_foldersModel CONSTANT FINAL)
@ -42,6 +43,7 @@ signals:
void currentLibraryIdChanged(int newLibraryId); void currentLibraryIdChanged(int newLibraryId);
void currentComicIdChanged(QString newComicId); void currentComicIdChanged(QString newComicId);
void currentComicOpenedChanged(bool opened); void currentComicOpenedChanged(bool opened);
void lastErrorMessageChanged(QString errorMessage);
private: private:
QNetworkRequestFactory apiFactory() const; QNetworkRequestFactory apiFactory() const;
@ -50,6 +52,7 @@ private:
int m_currentLibraryId = -1; int m_currentLibraryId = -1;
QString m_currentComicId; QString m_currentComicId;
bool m_currentComicOpened = false; bool m_currentComicOpened = false;
QString m_lastErrorMessage;
QNetworkRequestFactory m_requestFactory; QNetworkRequestFactory m_requestFactory;
QNetworkAccessManager * m_networkAccessManager; QNetworkAccessManager * m_networkAccessManager;
QRestAccessManager * m_restAccessManager; QRestAccessManager * m_restAccessManager;

View File

@ -8,6 +8,7 @@ ComicItem::ComicItem(QJsonObject jsonObj, const QString &name)
setData(jsonObj["num_pages"].toInt(), PageCountRole); setData(jsonObj["num_pages"].toInt(), PageCountRole);
setData(jsonObj["current_page"].toInt(), CurrentPageRole); setData(jsonObj["current_page"].toInt(), CurrentPageRole);
setData(jsonObj["type"].toInt(), TypeRole); setData(jsonObj["type"].toInt(), TypeRole);
setData(jsonObj["added"].toInteger(), AddedTimeRole);
} }
QHash<int, QByteArray> ComicItem::roleNames() QHash<int, QByteArray> ComicItem::roleNames()
@ -19,5 +20,6 @@ QHash<int, QByteArray> ComicItem::roleNames()
{ComicItem::PageCountRole, "pageCount"}, {ComicItem::PageCountRole, "pageCount"},
{ComicItem::CurrentPageRole, "currentPage"}, {ComicItem::CurrentPageRole, "currentPage"},
{ComicItem::TypeRole, "type"}, {ComicItem::TypeRole, "type"},
{ComicItem::AddedTimeRole, "addedTime"},
}; };
} }

View File

@ -14,6 +14,7 @@ public:
PageCountRole, PageCountRole,
CurrentPageRole, CurrentPageRole,
TypeRole, TypeRole,
AddedTimeRole,
}; };
Q_ENUM(Roles) Q_ENUM(Roles)

View File

@ -33,7 +33,10 @@ public:
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QCoreApplication::setApplicationName("Pineapple Comic Reader");
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
app.setOrganizationName("Chestnut Software");
app.setOrganizationDomain("blumia.net");
qputenv("QT_QUICK_CONTROLS_STYLE", QByteArray("FluentWinUI3")); qputenv("QT_QUICK_CONTROLS_STYLE", QByteArray("FluentWinUI3"));