mirror of
https://github.com/YACReader/yacreader
synced 2025-05-28 03:10:27 -04:00
Merge pull request #331 from YACReader/folder_content_view
feature: Folder content view
This commit is contained in:
commit
551726d522
@ -83,6 +83,7 @@ HEADERS += comic_flow.h \
|
||||
db/comic_query_result_processor.h \
|
||||
db/folder_query_result_processor.h \
|
||||
db/query_lexer.h \
|
||||
folder_content_view.h \
|
||||
initial_comic_info_extractor.h \
|
||||
library_comic_opener.h \
|
||||
library_creator.h \
|
||||
@ -165,6 +166,7 @@ SOURCES += comic_flow.cpp \
|
||||
db/comic_query_result_processor.cpp \
|
||||
db/folder_query_result_processor.cpp \
|
||||
db/query_lexer.cpp \
|
||||
folder_content_view.cpp \
|
||||
initial_comic_info_extractor.cpp \
|
||||
library_comic_opener.cpp \
|
||||
library_creator.cpp \
|
||||
|
@ -1,6 +1,7 @@
|
||||
|
||||
#include <QtGui>
|
||||
#include <QtDebug>
|
||||
#include <QStringBuilder>
|
||||
#include <limits>
|
||||
|
||||
#include "comic_item.h"
|
||||
@ -237,6 +238,7 @@ QHash<int, QByteArray> ComicModel::roleNames() const
|
||||
roles[HasBeenOpenedRole] = "has_been_opened";
|
||||
roles[CoverPathRole] = "cover_path";
|
||||
roles[PublicationDate] = "date";
|
||||
roles[ReadableTitle] = "readable_title";
|
||||
|
||||
return roles;
|
||||
}
|
||||
@ -275,7 +277,13 @@ QVariant ComicModel::data(const QModelIndex &index, int role) const
|
||||
return item->data(Number);
|
||||
else if (role == TitleRole)
|
||||
return item->data(Title).isNull() ? item->data(FileName) : item->data(Title);
|
||||
else if (role == FileNameRole)
|
||||
else if (role == ReadableTitle) {
|
||||
QString title;
|
||||
if (!item->data(Number).isNull()) {
|
||||
title = title % "#" % item->data(Number).toString() % " ";
|
||||
}
|
||||
return QVariant(title % (item->data(Title).isNull() ? item->data(FileName).toString() : item->data(Title).toString()));
|
||||
} else if (role == FileNameRole)
|
||||
return item->data(FileName);
|
||||
else if (role == RatingRole)
|
||||
return item->data(Rating);
|
||||
|
@ -54,6 +54,7 @@ public:
|
||||
HasBeenOpenedRole,
|
||||
CoverPathRole,
|
||||
PublicationDateRole,
|
||||
ReadableTitle,
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
|
@ -54,26 +54,26 @@ void drawMacOSXFinishedFolderIcon()
|
||||
#define ROOT 1
|
||||
|
||||
FolderModel::FolderModel(QObject *parent)
|
||||
: QAbstractItemModel(parent), rootItem(0)
|
||||
: QAbstractItemModel(parent), isSubfolder(false), rootItem(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
FolderModel::FolderModel(QSqlQuery &sqlquery, QObject *parent)
|
||||
: QAbstractItemModel(parent), rootItem(0)
|
||||
: QAbstractItemModel(parent), isSubfolder(false), rootItem(nullptr)
|
||||
{
|
||||
// lo m<>s probable es que el nodo ra<72>z no necesite tener informaci<63>n
|
||||
QList<QVariant> rootData;
|
||||
rootData << "root"; // id 0, padre 0, title "root" (el id, y el id del padre van a ir en la clase TreeItem)
|
||||
rootItem = new FolderItem(rootData);
|
||||
rootItem->id = ROOT;
|
||||
rootItem->parentItem = 0;
|
||||
rootItem->parentItem = nullptr;
|
||||
setupModelData(sqlquery, rootItem);
|
||||
// sqlquery.finish();
|
||||
}
|
||||
|
||||
FolderModel::~FolderModel()
|
||||
{
|
||||
if (rootItem != 0)
|
||||
if (rootItem != nullptr)
|
||||
delete rootItem;
|
||||
}
|
||||
|
||||
@ -81,8 +81,31 @@ int FolderModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
return static_cast<FolderItem *>(parent.internalPointer())->columnCount();
|
||||
else
|
||||
else {
|
||||
if (rootItem == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return rootItem->columnCount();
|
||||
}
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> FolderModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
|
||||
roles[FinishedRole] = "is_finished";
|
||||
roles[CompletedRole] = "is_completed";
|
||||
roles[IdRole] = "id";
|
||||
roles[MangaRole] = "is_manga";
|
||||
roles[CoverPathRole] = "cover_path";
|
||||
roles[FolderName] = "name";
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
void FolderModel::reload()
|
||||
{
|
||||
setupModelData(_databasePath);
|
||||
}
|
||||
|
||||
QVariant FolderModel::data(const QModelIndex &index, int role) const
|
||||
@ -121,6 +144,10 @@ QVariant FolderModel::data(const QModelIndex &index, int role) const
|
||||
#endif
|
||||
}
|
||||
|
||||
if (role == FolderModel::FolderName) {
|
||||
return item->data(FolderModel::Name);
|
||||
}
|
||||
|
||||
if (role == FolderModel::CompletedRole)
|
||||
return item->data(FolderModel::Completed);
|
||||
|
||||
@ -133,6 +160,9 @@ QVariant FolderModel::data(const QModelIndex &index, int role) const
|
||||
if (role == FolderModel::IdRole)
|
||||
return item->id;
|
||||
|
||||
if (role == FolderModel::CoverPathRole)
|
||||
return getCoverUrlPathForComicHash(item->data(FirstChildHash).toString());
|
||||
|
||||
if (role != Qt::DisplayRole)
|
||||
return QVariant();
|
||||
|
||||
@ -150,6 +180,10 @@ Qt::ItemFlags FolderModel::flags(const QModelIndex &index) const
|
||||
QVariant FolderModel::headerData(int section, Qt::Orientation orientation,
|
||||
int role) const
|
||||
{
|
||||
if (rootItem == nullptr) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
|
||||
return rootItem->data(section);
|
||||
|
||||
@ -196,10 +230,14 @@ int FolderModel::rowCount(const QModelIndex &parent) const
|
||||
if (parent.column() > 0)
|
||||
return 0;
|
||||
|
||||
if (!parent.isValid())
|
||||
if (!parent.isValid()) {
|
||||
if (rootItem == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
parentItem = rootItem;
|
||||
else
|
||||
} else {
|
||||
parentItem = static_cast<FolderItem *>(parent.internalPointer());
|
||||
}
|
||||
|
||||
return parentItem->childCount();
|
||||
}
|
||||
@ -207,17 +245,17 @@ int FolderModel::rowCount(const QModelIndex &parent) const
|
||||
void FolderModel::setupModelData(QString path)
|
||||
{
|
||||
beginResetModel();
|
||||
if (rootItem != 0)
|
||||
if (rootItem != nullptr)
|
||||
delete rootItem; // TODO comprobar que se libera bien la memoria
|
||||
|
||||
rootItem = 0;
|
||||
rootItem = nullptr;
|
||||
|
||||
// inicializar el nodo ra<72>z
|
||||
QList<QVariant> rootData;
|
||||
rootData << "root"; // id 0, padre 0, title "root" (el id, y el id del padre van a ir en la clase TreeItem)
|
||||
rootItem = new FolderItem(rootData);
|
||||
rootItem->id = ROOT;
|
||||
rootItem->parentItem = 0;
|
||||
rootItem->parentItem = nullptr;
|
||||
|
||||
// cargar la base de datos
|
||||
_databasePath = path;
|
||||
@ -235,6 +273,13 @@ void FolderModel::setupModelData(QString path)
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void FolderModel::fullSetup(QSqlQuery &sqlquery, FolderItem *parent)
|
||||
{
|
||||
rootItem = parent;
|
||||
|
||||
setupModelData(sqlquery, parent);
|
||||
}
|
||||
|
||||
void FolderModel::setupModelData(QSqlQuery &sqlquery, FolderItem *parent)
|
||||
{
|
||||
// 64 bits para la primary key, es decir la misma precisi<73>n que soporta sqlit 2^64
|
||||
@ -252,6 +297,7 @@ void FolderModel::setupModelData(QSqlQuery &sqlquery, FolderItem *parent)
|
||||
int manga = record.indexOf("manga");
|
||||
int id = record.indexOf("id");
|
||||
int parentId = record.indexOf("parentId");
|
||||
int firstChildHash = record.indexOf("firstChildHash");
|
||||
|
||||
while (sqlquery.next()) {
|
||||
QList<QVariant> data;
|
||||
@ -261,6 +307,7 @@ void FolderModel::setupModelData(QSqlQuery &sqlquery, FolderItem *parent)
|
||||
data << sqlquery.value(finished).toBool();
|
||||
data << sqlquery.value(completed).toBool();
|
||||
data << sqlquery.value(manga).toBool();
|
||||
data << sqlquery.value(firstChildHash).toString();
|
||||
auto item = new FolderItem(data);
|
||||
|
||||
item->id = sqlquery.value(id).toULongLong();
|
||||
@ -286,6 +333,7 @@ void FolderModel::updateFolderModelData(QSqlQuery &sqlquery, FolderItem *parent)
|
||||
int manga = record.indexOf("manga");
|
||||
int id = record.indexOf("id");
|
||||
int parentId = record.indexOf("parentId");
|
||||
int firstChildHash = record.indexOf("firstChildHash");
|
||||
|
||||
while (sqlquery.next()) {
|
||||
QList<QVariant> data;
|
||||
@ -295,6 +343,7 @@ void FolderModel::updateFolderModelData(QSqlQuery &sqlquery, FolderItem *parent)
|
||||
data << sqlquery.value(finished).toBool();
|
||||
data << sqlquery.value(completed).toBool();
|
||||
data << sqlquery.value(manga).toBool();
|
||||
data << sqlquery.value(firstChildHash).toString();
|
||||
auto item = new FolderItem(data);
|
||||
|
||||
item->id = sqlquery.value(id).toULongLong();
|
||||
@ -338,7 +387,7 @@ void FolderModel::updateFolderCompletedStatus(const QModelIndexList &list, bool
|
||||
}
|
||||
QSqlDatabase::removeDatabase(connectionName);
|
||||
|
||||
emit dataChanged(index(list.first().row(), FolderModel::Name), index(list.last().row(), FolderModel::Completed));
|
||||
emit dataChanged(index(list.first().row(), FolderModel::Name), index(list.last().row(), FolderModel::FirstChildHash));
|
||||
}
|
||||
|
||||
void FolderModel::updateFolderFinishedStatus(const QModelIndexList &list, bool status)
|
||||
@ -360,7 +409,7 @@ void FolderModel::updateFolderFinishedStatus(const QModelIndexList &list, bool s
|
||||
}
|
||||
QSqlDatabase::removeDatabase(connectionName);
|
||||
|
||||
emit dataChanged(index(list.first().row(), FolderModel::Name), index(list.last().row(), FolderModel::Completed));
|
||||
emit dataChanged(index(list.first().row(), FolderModel::Name), index(list.last().row(), FolderModel::FirstChildHash));
|
||||
}
|
||||
|
||||
void FolderModel::updateFolderManga(const QModelIndexList &list, bool manga)
|
||||
@ -390,7 +439,7 @@ void FolderModel::updateFolderManga(const QModelIndexList &list, bool manga)
|
||||
}
|
||||
QSqlDatabase::removeDatabase(connectionName);
|
||||
|
||||
emit dataChanged(index(list.first().row(), FolderModel::Name), index(list.last().row(), FolderModel::Manga));
|
||||
emit dataChanged(index(list.first().row(), FolderModel::Name), index(list.last().row(), FolderModel::FirstChildHash));
|
||||
}
|
||||
|
||||
QStringList FolderModel::getSubfoldersNames(const QModelIndex &mi)
|
||||
@ -417,6 +466,97 @@ QStringList FolderModel::getSubfoldersNames(const QModelIndex &mi)
|
||||
return result;
|
||||
}
|
||||
|
||||
FolderModel *FolderModel::getSubfoldersModel(const QModelIndex &mi)
|
||||
{
|
||||
qulonglong id = 1;
|
||||
FolderItem *parent = nullptr;
|
||||
if (mi.isValid()) {
|
||||
auto item = static_cast<FolderItem *>(mi.internalPointer());
|
||||
parent = new FolderItem(item->getData(), item->parent());
|
||||
id = parent->id = item->id;
|
||||
}
|
||||
|
||||
if (id == 1) {
|
||||
if (parent != nullptr) {
|
||||
delete parent;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
auto model = new FolderModel();
|
||||
|
||||
QString connectionName = "";
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
|
||||
|
||||
QSqlQuery selectQuery(db); // TODO check
|
||||
selectQuery.prepare("SELECT * FROM folder WHERE parentId = :parentId and id <> 1");
|
||||
selectQuery.bindValue(":parentId", id);
|
||||
selectQuery.exec();
|
||||
|
||||
if (parent != nullptr) {
|
||||
model->fullSetup(selectQuery, parent);
|
||||
}
|
||||
|
||||
connectionName = db.connectionName();
|
||||
}
|
||||
QSqlDatabase::removeDatabase(connectionName);
|
||||
|
||||
model->_databasePath = _databasePath;
|
||||
|
||||
model->isSubfolder = true;
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
Folder FolderModel::getFolder(const QModelIndex &mi)
|
||||
{
|
||||
auto folderItem = static_cast<FolderItem *>(mi.internalPointer());
|
||||
auto name = folderItem->data(FolderModel::Name).toString();
|
||||
auto parentItem = folderItem->parent();
|
||||
auto folder = Folder(folderItem->id,
|
||||
parentItem->id,
|
||||
name,
|
||||
folderItem->parent()->data(Columns::Path).toString() + "/" + name,
|
||||
folderItem->data(Columns::Completed).toBool(),
|
||||
folderItem->data(Columns::Finished).toBool(),
|
||||
folderItem->data(Columns::Manga).toBool());
|
||||
|
||||
return folder;
|
||||
}
|
||||
|
||||
QModelIndex FolderModel::getIndexFromFolder(const Folder &folder, const QModelIndex &parent)
|
||||
{
|
||||
if (rootItem == nullptr) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
auto numRows = rowCount(parent);
|
||||
for (auto i = 0; i < numRows; i++) {
|
||||
auto modelIndex = index(i, 0, parent);
|
||||
|
||||
if (modelIndex.isValid()) {
|
||||
auto folderItem = static_cast<FolderItem *>(modelIndex.internalPointer());
|
||||
|
||||
if (folderItem->id == folder.id) {
|
||||
return modelIndex;
|
||||
}
|
||||
|
||||
auto childModelIndex = getIndexFromFolder(folder, modelIndex);
|
||||
|
||||
if (childModelIndex.isValid()) {
|
||||
auto folderItem = static_cast<FolderItem *>(childModelIndex.internalPointer());
|
||||
|
||||
if (folderItem->id == folder.id) {
|
||||
return childModelIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
void FolderModel::fetchMoreFromDB(const QModelIndex &parent)
|
||||
{
|
||||
FolderItem *item;
|
||||
@ -531,6 +671,11 @@ QModelIndex FolderModel::addFolderAtParent(const QString &folderName, const QMod
|
||||
return index(destRow, 0, parent);
|
||||
}
|
||||
|
||||
QUrl FolderModel::getCoverUrlPathForComicHash(const QString &hash) const
|
||||
{
|
||||
return QUrl("file:" + _databasePath + "/covers/" + hash + ".jpg");
|
||||
}
|
||||
|
||||
void FolderModel::deleteFolder(const QModelIndex &mi)
|
||||
{
|
||||
beginRemoveRows(mi.parent(), mi.row(), mi.row());
|
||||
|
@ -7,9 +7,11 @@
|
||||
#include <QVariant>
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlDatabase>
|
||||
#include <QUrl>
|
||||
|
||||
#include "yacreader_global.h"
|
||||
#include "folder.h"
|
||||
#include "folder_query_result_processor.h"
|
||||
#include "yacreader_global.h"
|
||||
|
||||
class FolderItem;
|
||||
|
||||
@ -54,8 +56,10 @@ public:
|
||||
QModelIndex parent(const QModelIndex &index) const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
// Convenience methods
|
||||
void reload();
|
||||
void setupModelData(QString path);
|
||||
QString getDatabase();
|
||||
QString getFolderPath(const QModelIndex &folder);
|
||||
@ -68,31 +72,44 @@ public:
|
||||
void updateFolderManga(const QModelIndexList &list, bool manga);
|
||||
|
||||
QStringList getSubfoldersNames(const QModelIndex &mi);
|
||||
FolderModel *getSubfoldersModel(const QModelIndex &mi);
|
||||
|
||||
Folder getFolder(const QModelIndex &mi);
|
||||
QModelIndex getIndexFromFolder(const Folder &folder, const QModelIndex &parent = QModelIndex());
|
||||
|
||||
void fetchMoreFromDB(const QModelIndex &parent);
|
||||
|
||||
QModelIndex addFolderAtParent(const QString &folderName, const QModelIndex &parent);
|
||||
|
||||
Q_INVOKABLE QUrl getCoverUrlPathForComicHash(const QString &hash) const;
|
||||
|
||||
enum Columns {
|
||||
Name = 0,
|
||||
Path = 1,
|
||||
Finished = 2,
|
||||
Completed = 3,
|
||||
Manga = 4
|
||||
Manga = 4,
|
||||
FirstChildHash = 5
|
||||
}; // id INTEGER PRIMARY KEY, parentId INTEGER NOT NULL, name TEXT NOT NULL, path TEXT NOT NULL
|
||||
|
||||
enum Roles {
|
||||
FinishedRole = Qt::UserRole + 1,
|
||||
CompletedRole,
|
||||
IdRole,
|
||||
MangaRole
|
||||
MangaRole,
|
||||
CoverPathRole,
|
||||
FolderName,
|
||||
};
|
||||
|
||||
bool isSubfolder;
|
||||
|
||||
public slots:
|
||||
void deleteFolder(const QModelIndex &mi);
|
||||
void updateFolderChildrenInfo(qulonglong folderId);
|
||||
|
||||
private:
|
||||
void fullSetup(QSqlQuery &sqlquery, FolderItem *parent);
|
||||
|
||||
void setupModelData(QSqlQuery &sqlquery, FolderItem *parent);
|
||||
void updateFolderModelData(QSqlQuery &sqlquery, FolderItem *parent);
|
||||
|
||||
|
271
YACReaderLibrary/folder_content_view.cpp
Normal file
271
YACReaderLibrary/folder_content_view.cpp
Normal file
@ -0,0 +1,271 @@
|
||||
#include "folder_content_view.h"
|
||||
|
||||
#include "folder_model.h"
|
||||
#include "grid_comics_view.h"
|
||||
#include "yacreader_global_gui.h"
|
||||
#include "yacreader_tool_bar_stretch.h"
|
||||
|
||||
#include "QsLog.h"
|
||||
|
||||
#include <QQmlContext>
|
||||
#include <QQuickItem>
|
||||
#include <QQuickWidget>
|
||||
|
||||
using namespace YACReader;
|
||||
|
||||
FolderContentView::FolderContentView(QWidget *parent)
|
||||
: QWidget { parent }, parent(QModelIndex()), comicModel(new ComicModel()), folderModel(new FolderModel())
|
||||
{
|
||||
qmlRegisterType<FolderModel>("com.yacreader.FolderModel", 1, 0, "FolderModel");
|
||||
|
||||
settings = new QSettings(YACReader::getSettingsPath() + "/YACReaderLibrary.ini", QSettings::IniFormat, this);
|
||||
settings->beginGroup("libraryConfig");
|
||||
|
||||
view = new QQuickWidget();
|
||||
|
||||
// QQuickWidget requires rendering into OpenGL framebuffer objects
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
view->quickWindow()->setGraphicsApi(QSGRendererInterface::OpenGL);
|
||||
#endif
|
||||
|
||||
view->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||
connect(
|
||||
view, &QQuickWidget::statusChanged,
|
||||
[=](QQuickWidget::Status status) {
|
||||
if (status == QQuickWidget::Error) {
|
||||
QLOG_ERROR() << view->errors();
|
||||
}
|
||||
});
|
||||
|
||||
coverSizeSliderWidget = new QWidget(this);
|
||||
coverSizeSliderWidget->setFixedWidth(200);
|
||||
coverSizeSlider = new QSlider(coverSizeSliderWidget);
|
||||
coverSizeSlider->setOrientation(Qt::Horizontal);
|
||||
coverSizeSlider->setRange(YACREADER_MIN_GRID_ZOOM_WIDTH, YACREADER_MAX_GRID_ZOOM_WIDTH);
|
||||
|
||||
auto horizontalLayout = new QHBoxLayout();
|
||||
QLabel *smallLabel = new QLabel();
|
||||
smallLabel->setPixmap(QPixmap(":/images/comics_view_toolbar/small_size_grid_zoom.png"));
|
||||
horizontalLayout->addWidget(smallLabel);
|
||||
horizontalLayout->addWidget(coverSizeSlider, 0, Qt::AlignVCenter);
|
||||
QLabel *bigLabel = new QLabel();
|
||||
bigLabel->setPixmap(QPixmap(":/images/comics_view_toolbar/big_size_grid_zoom.png"));
|
||||
horizontalLayout->addWidget(bigLabel);
|
||||
horizontalLayout->addSpacing(10);
|
||||
horizontalLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
coverSizeSliderWidget->setLayout(horizontalLayout);
|
||||
|
||||
connect(coverSizeSlider, &QAbstractSlider::valueChanged, this, &FolderContentView::setCoversSize);
|
||||
|
||||
toolbar = new QToolBar();
|
||||
toolbar->addWidget(new YACReaderToolBarStretch);
|
||||
toolbar->addWidget(coverSizeSliderWidget);
|
||||
|
||||
auto l = new QVBoxLayout;
|
||||
setContentsMargins(0, 0, 0, 0);
|
||||
l->setContentsMargins(0, 0, 0, 0);
|
||||
l->setSpacing(0);
|
||||
l->addWidget(view);
|
||||
l->addWidget(toolbar);
|
||||
this->setLayout(l);
|
||||
|
||||
QQmlContext *ctxt = view->rootContext();
|
||||
|
||||
LibraryUITheme theme;
|
||||
#ifdef Q_OS_MAC
|
||||
theme = Light;
|
||||
#else
|
||||
theme = Dark;
|
||||
#endif
|
||||
|
||||
if (theme == Light) {
|
||||
ctxt->setContextProperty("continueReadingBackgroundColor", "#DDDDDD");
|
||||
ctxt->setContextProperty("continueReadingColor", "#000000");
|
||||
|
||||
ctxt->setContextProperty("backgroundColor", "#F6F6F6");
|
||||
ctxt->setContextProperty("cellColor", "#FFFFFF");
|
||||
ctxt->setContextProperty("selectedColor", "#FFFFFF");
|
||||
ctxt->setContextProperty("selectedBorderColor", "#007AFF");
|
||||
ctxt->setContextProperty("borderColor", "#DBDBDB");
|
||||
ctxt->setContextProperty("titleColor", "#121212");
|
||||
ctxt->setContextProperty("textColor", "#636363");
|
||||
// fonts settings
|
||||
ctxt->setContextProperty("fontSize", 11);
|
||||
ctxt->setContextProperty("fontFamily", QApplication::font().family());
|
||||
ctxt->setContextProperty("fontSpacing", 0.5);
|
||||
|
||||
// info - copy/pasted from info_comics_view TODO create helpers for setting the UI config
|
||||
ctxt->setContextProperty("infoBackgroundColor", "#FFFFFF");
|
||||
ctxt->setContextProperty("topShadow", QUrl());
|
||||
ctxt->setContextProperty("infoShadow", "info-shadow-light.png");
|
||||
ctxt->setContextProperty("infoIndicator", "info-indicator-light.png");
|
||||
|
||||
ctxt->setContextProperty("infoTextColor", "#404040");
|
||||
ctxt->setContextProperty("infoTitleColor", "#2E2E2E");
|
||||
|
||||
ctxt->setContextProperty("ratingUnselectedColor", "#DEDEDE");
|
||||
ctxt->setContextProperty("ratingSelectedColor", "#2B2B2B");
|
||||
|
||||
ctxt->setContextProperty("favUncheckedColor", "#DEDEDE");
|
||||
ctxt->setContextProperty("favCheckedColor", "#E84852");
|
||||
|
||||
ctxt->setContextProperty("readTickUncheckedColor", "#DEDEDE");
|
||||
ctxt->setContextProperty("readTickCheckedColor", "#E84852");
|
||||
} else {
|
||||
ctxt->setContextProperty("continueReadingBackgroundColor", "#88000000");
|
||||
ctxt->setContextProperty("continueReadingColor", "#FFFFFF");
|
||||
|
||||
ctxt->setContextProperty("backgroundColor", "#2A2A2A");
|
||||
ctxt->setContextProperty("cellColor", "#212121");
|
||||
ctxt->setContextProperty("selectedColor", "#121212");
|
||||
ctxt->setContextProperty("selectedBorderColor", "#121212");
|
||||
ctxt->setContextProperty("borderColor", "#121212");
|
||||
ctxt->setContextProperty("titleColor", "#FFFFFF");
|
||||
ctxt->setContextProperty("textColor", "#A8A8A8");
|
||||
ctxt->setContextProperty("dropShadow", QVariant(false));
|
||||
// fonts settings
|
||||
int fontSize = QApplication::font().pointSize();
|
||||
if (fontSize == -1)
|
||||
fontSize = QApplication::font().pixelSize();
|
||||
ctxt->setContextProperty("fontSize", fontSize);
|
||||
ctxt->setContextProperty("fontFamily", QApplication::font().family());
|
||||
ctxt->setContextProperty("fontSpacing", 0.5);
|
||||
|
||||
// info - copy/pasted from info_comics_view TODO create helpers for setting the UI config
|
||||
ctxt->setContextProperty("infoBackgroundColor", "#2E2E2E");
|
||||
ctxt->setContextProperty("topShadow", "info-top-shadow.png");
|
||||
ctxt->setContextProperty("infoShadow", "info-shadow.png");
|
||||
ctxt->setContextProperty("infoIndicator", "info-indicator.png");
|
||||
|
||||
ctxt->setContextProperty("infoTextColor", "#B0B0B0");
|
||||
ctxt->setContextProperty("infoTitleColor", "#FFFFFF");
|
||||
|
||||
ctxt->setContextProperty("ratingUnselectedColor", "#1C1C1C");
|
||||
ctxt->setContextProperty("ratingSelectedColor", "#FFFFFF");
|
||||
|
||||
ctxt->setContextProperty("favUncheckedColor", "#1C1C1C");
|
||||
ctxt->setContextProperty("favCheckedColor", "#E84852");
|
||||
|
||||
ctxt->setContextProperty("readTickUncheckedColor", "#1C1C1C");
|
||||
ctxt->setContextProperty("readTickCheckedColor", "#E84852");
|
||||
}
|
||||
|
||||
updateCoversSizeInContext(YACREADER_MIN_COVER_WIDTH, ctxt);
|
||||
|
||||
ctxt->setContextProperty("comicsList", comicModel.get());
|
||||
ctxt->setContextProperty("foldersList", folderModel);
|
||||
|
||||
ctxt->setContextProperty("showCurrentComic", QVariant(false));
|
||||
|
||||
ctxt->setContextProperty("openHelper", this);
|
||||
ctxt->setContextProperty("contextMenuHelper", this);
|
||||
|
||||
view->setSource(QUrl("qrc:/qml/FolderContentView.qml"));
|
||||
}
|
||||
|
||||
void FolderContentView::setModel(const QModelIndex &parent, FolderModel *model)
|
||||
{
|
||||
this->parent = parent;
|
||||
QQmlContext *ctxt = view->rootContext();
|
||||
|
||||
ctxt->setContextProperty("foldersList", model);
|
||||
|
||||
// when the root folder is set, FolderModel just returns itself in `getSubfoldersModel`, I need to measure the performance of create a deep copy...
|
||||
if (folderModel->isSubfolder) {
|
||||
delete folderModel;
|
||||
}
|
||||
folderModel = model;
|
||||
|
||||
auto grid = view->rootObject()->findChild<QQuickItem *>(QStringLiteral("grid"));
|
||||
|
||||
if (grid != nullptr) {
|
||||
grid->setProperty("currentIndex", 0);
|
||||
}
|
||||
}
|
||||
|
||||
void FolderContentView::setContinueReadingModel(ComicModel *model)
|
||||
{
|
||||
QQmlContext *ctxt = view->rootContext();
|
||||
|
||||
ctxt->setContextProperty("comicsList", model);
|
||||
this->comicModel.reset(model);
|
||||
|
||||
auto list = view->rootObject()->findChild<QQuickItem *>(QStringLiteral("list"));
|
||||
|
||||
if (list != nullptr) {
|
||||
list->setProperty("currentIndex", 0);
|
||||
}
|
||||
}
|
||||
|
||||
void FolderContentView::openFolder(int index)
|
||||
{
|
||||
emit subfolderSelected(this->parent, index);
|
||||
}
|
||||
|
||||
void FolderContentView::openComicFromContinueReadingList(int index)
|
||||
{
|
||||
auto comic = comicModel->getComic(comicModel->index(index, 0));
|
||||
emit openComic(comic, ComicModel::Folder);
|
||||
}
|
||||
|
||||
void FolderContentView::requestedFolderContextMenu(QPoint point, int index)
|
||||
{
|
||||
auto folder = folderModel->getFolder(folderModel->index(index, 0));
|
||||
emit openFolderContextMenu(point, folder);
|
||||
}
|
||||
|
||||
void FolderContentView::requestedContinueReadingComicContextMenu(QPoint point, int index)
|
||||
{
|
||||
auto comic = comicModel->getComic(comicModel->index(index, 0));
|
||||
emit openContinueReadingComicContextMenu(point, comic);
|
||||
}
|
||||
|
||||
void FolderContentView::updateCoversSizeInContext(int width, QQmlContext *ctxt)
|
||||
{
|
||||
int cellBottomMarging = 8 * (1 + 2 * (1 - (float(YACREADER_MAX_GRID_ZOOM_WIDTH - width) / (YACREADER_MAX_GRID_ZOOM_WIDTH - YACREADER_MIN_GRID_ZOOM_WIDTH))));
|
||||
|
||||
ctxt->setContextProperty("cellCustomHeight", ((width * YACREADER_MAX_COVER_HEIGHT) / YACREADER_MIN_COVER_WIDTH) + 51 + cellBottomMarging);
|
||||
ctxt->setContextProperty("cellCustomWidth", (width * YACREADER_MIN_CELL_CUSTOM_WIDTH) / YACREADER_MIN_COVER_WIDTH);
|
||||
|
||||
ctxt->setContextProperty("itemWidth", width);
|
||||
ctxt->setContextProperty("itemHeight", ((width * YACREADER_MAX_COVER_HEIGHT) / YACREADER_MIN_COVER_WIDTH) + 51);
|
||||
|
||||
ctxt->setContextProperty("coverWidth", width);
|
||||
ctxt->setContextProperty("coverHeight", (width * YACREADER_MAX_COVER_HEIGHT) / YACREADER_MIN_COVER_WIDTH);
|
||||
}
|
||||
|
||||
void FolderContentView::setCoversSize(int width)
|
||||
{
|
||||
QQmlContext *ctxt = view->rootContext();
|
||||
|
||||
auto grid = view->rootObject()->findChild<QQuickItem *>(QStringLiteral("grid"));
|
||||
|
||||
if (grid != 0) {
|
||||
QVariant cellCustomWidth = (width * YACREADER_MIN_CELL_CUSTOM_WIDTH) / YACREADER_MIN_GRID_ZOOM_WIDTH;
|
||||
QMetaObject::invokeMethod(grid, "calculateCellWidths",
|
||||
Q_ARG(QVariant, cellCustomWidth));
|
||||
}
|
||||
|
||||
updateCoversSizeInContext(width, ctxt);
|
||||
|
||||
settings->setValue(COMICS_GRID_COVER_SIZES, coverSizeSlider->value());
|
||||
}
|
||||
|
||||
void FolderContentView::showEvent(QShowEvent *event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
|
||||
int coverSize = settings->value(COMICS_GRID_COVER_SIZES, YACREADER_MIN_COVER_WIDTH).toInt();
|
||||
|
||||
coverSizeSlider->setValue(coverSize);
|
||||
setCoversSize(coverSize);
|
||||
}
|
||||
|
||||
void FolderContentView::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
}
|
||||
|
||||
void FolderContentView::dropEvent(QDropEvent *event)
|
||||
{
|
||||
}
|
70
YACReaderLibrary/folder_content_view.h
Normal file
70
YACReaderLibrary/folder_content_view.h
Normal file
@ -0,0 +1,70 @@
|
||||
#ifndef FOLDERCONTENTVIEW_H
|
||||
#define FOLDERCONTENTVIEW_H
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "comic_model.h"
|
||||
|
||||
#include "folder.h"
|
||||
#include "comic_db.h"
|
||||
|
||||
class FolderModel;
|
||||
class ComicModel;
|
||||
class YACReaderToolBarStretch;
|
||||
|
||||
class QQuickWidget;
|
||||
class QQmlContext;
|
||||
|
||||
class FolderContentView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FolderContentView(QWidget *parent = nullptr);
|
||||
void setModel(const QModelIndex &parent, FolderModel *model);
|
||||
void setContinueReadingModel(ComicModel *model);
|
||||
|
||||
signals:
|
||||
void subfolderSelected(QModelIndex, int);
|
||||
void openComic(const ComicDB &comic, const ComicModel::Mode mode);
|
||||
|
||||
// Drops
|
||||
void copyComicsToCurrentFolder(QList<QPair<QString, QString>>);
|
||||
void moveComicsToCurrentFolder(QList<QPair<QString, QString>>);
|
||||
|
||||
void openFolderContextMenu(QPoint point, Folder folder);
|
||||
void openContinueReadingComicContextMenu(QPoint point, ComicDB comic);
|
||||
|
||||
protected slots:
|
||||
// void onItemClicked(const QModelIndex &mi);
|
||||
void updateCoversSizeInContext(int width, QQmlContext *ctxt);
|
||||
void setCoversSize(int width);
|
||||
virtual void showEvent(QShowEvent *event) override;
|
||||
void openFolder(int index);
|
||||
void openComicFromContinueReadingList(int index);
|
||||
void requestedFolderContextMenu(QPoint point, int index);
|
||||
void requestedContinueReadingComicContextMenu(QPoint point, int index);
|
||||
|
||||
protected:
|
||||
QQuickWidget *view;
|
||||
QModelIndex parent;
|
||||
|
||||
std::unique_ptr<ComicModel> comicModel;
|
||||
FolderModel *folderModel;
|
||||
|
||||
// Drop to import
|
||||
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||
void dropEvent(QDropEvent *event) override;
|
||||
|
||||
private:
|
||||
QSettings *settings;
|
||||
QToolBar *toolbar;
|
||||
YACReaderToolBarStretch *toolBarStretch;
|
||||
QAction *toolBarStretchAction;
|
||||
QWidget *coverSizeSliderWidget;
|
||||
QSlider *coverSizeSlider;
|
||||
QAction *coverSizeSliderAction;
|
||||
QAction *showInfoAction;
|
||||
QAction *showInfoSeparatorAction;
|
||||
};
|
||||
|
||||
#endif // FOLDERCONTENTVIEW_H
|
@ -14,22 +14,6 @@
|
||||
#include "yacreader_comic_info_helper.h"
|
||||
#include "current_comic_view_helper.h"
|
||||
|
||||
// values relative to visible cells
|
||||
const unsigned int YACREADER_MIN_GRID_ZOOM_WIDTH = 156;
|
||||
const unsigned int YACREADER_MAX_GRID_ZOOM_WIDTH = 312;
|
||||
|
||||
// GridView cells
|
||||
const unsigned int YACREADER_MIN_CELL_CUSTOM_HEIGHT = 295;
|
||||
const unsigned int YACREADER_MIN_CELL_CUSTOM_WIDTH = 185;
|
||||
|
||||
// Covers
|
||||
const unsigned int YACREADER_MAX_COVER_HEIGHT = 236;
|
||||
const unsigned int YACREADER_MIN_COVER_WIDTH = YACREADER_MIN_GRID_ZOOM_WIDTH;
|
||||
|
||||
// visible cells (realCell in qml), grid cells size is used to create faux inner margings
|
||||
const unsigned int YACREADER_MIN_ITEM_HEIGHT = YACREADER_MAX_COVER_HEIGHT + 51; // 51 is the height of the bottom rectangle used for title and other info
|
||||
const unsigned int YACREADER_MIN_ITEM_WIDTH = YACREADER_MIN_COVER_WIDTH;
|
||||
|
||||
GridComicsView::GridComicsView(QWidget *parent)
|
||||
: ComicsView(parent), filterEnabled(false)
|
||||
{
|
||||
@ -422,6 +406,8 @@ void GridComicsView::setCoversSize(int width)
|
||||
}
|
||||
|
||||
updateCoversSizeInContext(width, ctxt);
|
||||
|
||||
settings->setValue(COMICS_GRID_COVER_SIZES, coverSizeSlider->value());
|
||||
}
|
||||
|
||||
void GridComicsView::updateCoversSizeInContext(int width, QQmlContext *ctxt)
|
||||
@ -478,6 +464,15 @@ void GridComicsView::resetScroll()
|
||||
QMetaObject::invokeMethod(scrollView, "scrollToOrigin");
|
||||
}
|
||||
|
||||
void GridComicsView::showEvent(QShowEvent *event)
|
||||
{
|
||||
ComicsView::showEvent(event);
|
||||
int coverSize = settings->value(COMICS_GRID_COVER_SIZES, YACREADER_MIN_COVER_WIDTH).toInt();
|
||||
|
||||
coverSizeSlider->setValue(coverSize);
|
||||
setCoversSize(coverSize);
|
||||
}
|
||||
|
||||
QByteArray GridComicsView::getMimeDataFromSelection()
|
||||
{
|
||||
QByteArray data;
|
||||
|
@ -16,6 +16,22 @@ class YACReaderToolBarStretch;
|
||||
class YACReaderComicsSelectionHelper;
|
||||
class YACReaderComicInfoHelper;
|
||||
|
||||
// values relative to visible cells
|
||||
const unsigned int YACREADER_MIN_GRID_ZOOM_WIDTH = 156;
|
||||
const unsigned int YACREADER_MAX_GRID_ZOOM_WIDTH = 312;
|
||||
|
||||
// GridView cells
|
||||
const unsigned int YACREADER_MIN_CELL_CUSTOM_HEIGHT = 295;
|
||||
const unsigned int YACREADER_MIN_CELL_CUSTOM_WIDTH = 185;
|
||||
|
||||
// Covers
|
||||
const unsigned int YACREADER_MAX_COVER_HEIGHT = 236;
|
||||
const unsigned int YACREADER_MIN_COVER_WIDTH = YACREADER_MIN_GRID_ZOOM_WIDTH;
|
||||
|
||||
// visible cells (realCell in qml), grid cells size is used to create faux inner margings
|
||||
const unsigned int YACREADER_MIN_ITEM_HEIGHT = YACREADER_MAX_COVER_HEIGHT + 51; // 51 is the height of the bottom rectangle used for title and other info
|
||||
const unsigned int YACREADER_MIN_ITEM_WIDTH = YACREADER_MIN_COVER_WIDTH;
|
||||
|
||||
class GridComicsView : public ComicsView
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -73,6 +89,8 @@ protected slots:
|
||||
|
||||
void resetScroll();
|
||||
|
||||
virtual void showEvent(QShowEvent *event) override;
|
||||
|
||||
signals:
|
||||
void onScrollToOrigin();
|
||||
|
||||
|
@ -79,6 +79,7 @@
|
||||
#include "opengl_checker.h"
|
||||
|
||||
#include "yacreader_comics_views_manager.h"
|
||||
#include "folder_content_view.h"
|
||||
|
||||
#include "trayicon_controller.h"
|
||||
|
||||
@ -1544,7 +1545,7 @@ void LibraryWindow::reloadAfterCopyMove(const QModelIndex &mi)
|
||||
navigationController->loadFolderInfo(mi);
|
||||
}
|
||||
|
||||
foldersModel->fetchMoreFromDB(mi);
|
||||
foldersModel->reload();
|
||||
|
||||
enableNeededActions();
|
||||
}
|
||||
@ -1783,6 +1784,87 @@ void LibraryWindow::showComicsItemContextMenu(const QPoint &point)
|
||||
menu.exec(comicsViewsManager->comicsView->mapToGlobal(point));
|
||||
}
|
||||
|
||||
void LibraryWindow::showGridFoldersContextMenu(QPoint point, Folder folder)
|
||||
{
|
||||
QMenu menu;
|
||||
|
||||
auto openContainingFolderAction = new QAction();
|
||||
openContainingFolderAction->setText(tr("Open folder..."));
|
||||
openContainingFolderAction->setIcon(QIcon(":/images/menus_icons/open.png"));
|
||||
|
||||
auto updateFolderAction = new QAction(tr("Update folder"), this);
|
||||
updateFolderAction->setIcon(QIcon(":/images/menus_icons/updateLibraryIcon.png"));
|
||||
|
||||
auto setFolderAsNotCompletedAction = new QAction();
|
||||
setFolderAsNotCompletedAction->setText(tr("Set as uncompleted"));
|
||||
|
||||
auto setFolderAsCompletedAction = new QAction();
|
||||
setFolderAsCompletedAction->setText(tr("Set as completed"));
|
||||
|
||||
auto setFolderAsReadAction = new QAction();
|
||||
setFolderAsReadAction->setText(tr("Set as read"));
|
||||
|
||||
auto setFolderAsUnreadAction = new QAction();
|
||||
setFolderAsUnreadAction->setText(tr("Set as unread"));
|
||||
|
||||
auto setFolderAsMangaAction = new QAction();
|
||||
setFolderAsMangaAction->setText(tr("Set as manga"));
|
||||
|
||||
auto setFolderAsNormalAction = new QAction();
|
||||
setFolderAsNormalAction->setText(tr("Set as comic"));
|
||||
|
||||
menu.addAction(openContainingFolderAction);
|
||||
menu.addAction(updateFolderAction);
|
||||
menu.addSeparator();
|
||||
if (folder.isCompleted())
|
||||
menu.addAction(setFolderAsNotCompletedAction);
|
||||
else
|
||||
menu.addAction(setFolderAsCompletedAction);
|
||||
menu.addSeparator();
|
||||
if (folder.isFinished())
|
||||
menu.addAction(setFolderAsUnreadAction);
|
||||
else
|
||||
menu.addAction(setFolderAsReadAction);
|
||||
menu.addSeparator();
|
||||
if (folder.isManga())
|
||||
menu.addAction(setFolderAsNormalAction);
|
||||
else
|
||||
menu.addAction(setFolderAsMangaAction);
|
||||
|
||||
// TODO update the subfolder model loaded in folderContentView
|
||||
connect(openContainingFolderAction, &QAction::triggered, this, [=]() {
|
||||
QDesktopServices::openUrl(QUrl("file:///" + QDir::cleanPath(currentPath() + "/" + folder.path), QUrl::TolerantMode));
|
||||
});
|
||||
connect(updateFolderAction, &QAction::triggered, this, [=]() {
|
||||
updateFolder(foldersModel->getIndexFromFolder(folder));
|
||||
});
|
||||
connect(setFolderAsNotCompletedAction, &QAction::triggered, this, [=]() {
|
||||
foldersModel->updateFolderCompletedStatus(QModelIndexList() << foldersModel->getIndexFromFolder(folder), false);
|
||||
});
|
||||
connect(setFolderAsCompletedAction, &QAction::triggered, this, [=]() {
|
||||
foldersModel->updateFolderCompletedStatus(QModelIndexList() << foldersModel->getIndexFromFolder(folder), true);
|
||||
});
|
||||
connect(setFolderAsReadAction, &QAction::triggered, this, [=]() {
|
||||
foldersModel->updateFolderFinishedStatus(QModelIndexList() << foldersModel->getIndexFromFolder(folder), true);
|
||||
});
|
||||
connect(setFolderAsUnreadAction, &QAction::triggered, this, [=]() {
|
||||
foldersModel->updateFolderFinishedStatus(QModelIndexList() << foldersModel->getIndexFromFolder(folder), false);
|
||||
});
|
||||
connect(setFolderAsMangaAction, &QAction::triggered, this, [=]() {
|
||||
foldersModel->updateFolderManga(QModelIndexList() << foldersModel->getIndexFromFolder(folder), true);
|
||||
});
|
||||
connect(setFolderAsNormalAction, &QAction::triggered, this, [=]() {
|
||||
foldersModel->updateFolderManga(QModelIndexList() << foldersModel->getIndexFromFolder(folder), false);
|
||||
});
|
||||
|
||||
menu.exec(comicsViewsManager->folderContentView->mapToGlobal(point));
|
||||
}
|
||||
|
||||
void LibraryWindow::showContinueReadingContextMenu(QPoint point, ComicDB comic)
|
||||
{
|
||||
qDebug() << "openContinueReadingComicContextMenu" << comic.name;
|
||||
}
|
||||
|
||||
void LibraryWindow::setupAddToSubmenu(QMenu &menu)
|
||||
{
|
||||
menu.addAction(addToFavoritesAction);
|
||||
@ -1832,7 +1914,7 @@ void LibraryWindow::saveSelectedCoversTo()
|
||||
if (!folderPath.isEmpty()) {
|
||||
QModelIndexList comics = getSelectedComics();
|
||||
foreach (QModelIndex comic, comics) {
|
||||
QString origin = comic.data(ComicModel::CoverPathRole).toString().remove("file:///");
|
||||
QString origin = comic.data(ComicModel::CoverPathRole).toString().remove("file:///").remove("file:");
|
||||
QString destination = QDir(folderPath).filePath(comic.data(ComicModel::FileNameRole).toString() + ".jpg");
|
||||
|
||||
QLOG_DEBUG() << "From : " << origin;
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include "folder_query_result_processor.h"
|
||||
|
||||
#include "comic_model.h"
|
||||
#include "comic_db.h"
|
||||
#include "folder.h"
|
||||
|
||||
#include <future>
|
||||
#include <memory>
|
||||
@ -379,6 +381,8 @@ public slots:
|
||||
void deleteComicsFromList();
|
||||
// void showSocial();
|
||||
void showFoldersContextMenu(const QPoint &point);
|
||||
void showGridFoldersContextMenu(QPoint point, Folder folder);
|
||||
void showContinueReadingContextMenu(QPoint point, ComicDB comic);
|
||||
void libraryAlreadyExists(const QString &name);
|
||||
void importLibraryPackage();
|
||||
void updateComicsView(quint64 libraryId, const ComicDB &comic);
|
||||
|
@ -1,6 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>qml/GridComicsView.qml</file>
|
||||
<file>qml/FolderContentView.qml</file>
|
||||
<file>qml/tick.png</file>
|
||||
<file>qml/reading.png</file>
|
||||
<file>qml/star_menu.png</file>
|
||||
|
457
YACReaderLibrary/qml/FolderContentView.qml
Normal file
457
YACReaderLibrary/qml/FolderContentView.qml
Normal file
@ -0,0 +1,457 @@
|
||||
import QtQuick 2.15
|
||||
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.12
|
||||
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
import com.yacreader.ComicModel 1.0
|
||||
|
||||
import com.yacreader.ComicInfo 1.0
|
||||
import com.yacreader.ComicDB 1.0
|
||||
|
||||
Rectangle {
|
||||
id: main
|
||||
|
||||
property int continuReadingHeight: 430;
|
||||
property int topContentMargin: 20;
|
||||
|
||||
color: backgroundColor
|
||||
anchors.margins: 0
|
||||
|
||||
Component {
|
||||
id: appDelegate
|
||||
Rectangle
|
||||
{
|
||||
id: cell
|
||||
width: grid.cellWidth
|
||||
height: grid.cellHeight
|
||||
color: "#00000000"
|
||||
|
||||
scale: mouseArea.containsMouse ? 1.025 : 1
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation { duration: 90 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: realCell
|
||||
|
||||
property int position : 0
|
||||
|
||||
width: itemWidth
|
||||
height: itemHeight
|
||||
|
||||
color: "transparent"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
onDoubleClicked: {
|
||||
openHelper.openFolder(index);
|
||||
}
|
||||
|
||||
onPressed: mouse => {
|
||||
var ci = grid.currentIndex; //save current index
|
||||
|
||||
mouse.accepted = true;
|
||||
|
||||
if(mouse.button === Qt.RightButton) // context menu is requested
|
||||
{
|
||||
var coordinates = main.mapFromItem(realCell,mouseX,mouseY)
|
||||
contextMenuHelper.requestedFolderContextMenu(Qt.point(coordinates.x,coordinates.y), index);
|
||||
mouse.accepted = false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**/
|
||||
|
||||
Rectangle {
|
||||
transform: Rotation { origin.x: coverWidth / 2; origin.y: coverHeight / 2; angle: -4}
|
||||
width: coverElement.width
|
||||
height: coverElement.height
|
||||
radius: 10
|
||||
anchors {horizontalCenter: parent.horizontalCenter; top: realCell.top; topMargin: 0}
|
||||
color: "#20000000"
|
||||
border {
|
||||
color: "#20FFFFFF"
|
||||
width: 1
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
transform: Rotation { origin.x: coverWidth / 2; origin.y: coverHeight / 2; angle: 3}
|
||||
width: coverElement.width
|
||||
height: coverElement.height
|
||||
radius: 10
|
||||
anchors {horizontalCenter: parent.horizontalCenter; top: realCell.top; topMargin: 0}
|
||||
color: "#88000000"
|
||||
border {
|
||||
color: "#20FFFFFF"
|
||||
width: 1
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: coverWidth
|
||||
height: coverHeight
|
||||
anchors {horizontalCenter: parent.horizontalCenter; top: realCell.top; topMargin: 0}
|
||||
id: coverElement
|
||||
|
||||
Image {
|
||||
id: coverImage
|
||||
anchors.fill: parent
|
||||
source: cover_path
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
smooth: true
|
||||
mipmap: true
|
||||
asynchronous : true
|
||||
cache: true
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
anchors.fill: parent
|
||||
cached: true
|
||||
maskSource: Rectangle {
|
||||
width: coverElement.width
|
||||
height: coverElement.height
|
||||
radius: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//border
|
||||
Rectangle {
|
||||
width: coverElement.width
|
||||
height: coverElement.height
|
||||
radius: 10
|
||||
anchors {horizontalCenter: parent.horizontalCenter; top: realCell.top; topMargin: 0}
|
||||
color: "transparent"
|
||||
border {
|
||||
color: "#20FFFFFF"
|
||||
width: 1
|
||||
}
|
||||
}
|
||||
|
||||
//folder name
|
||||
Text {
|
||||
id : titleText
|
||||
anchors { top: coverElement.bottom; left: realCell.left; leftMargin: 4; rightMargin: 4; topMargin: 10; }
|
||||
width: itemWidth - 8
|
||||
maximumLineCount: 2
|
||||
wrapMode: Text.WordWrap
|
||||
text: name
|
||||
elide: Text.ElideRight
|
||||
color: titleColor
|
||||
font.letterSpacing: fontSpacing
|
||||
font.pointSize: fontSize
|
||||
font.family: fontFamily
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: scrollView
|
||||
objectName: "topScrollView"
|
||||
anchors.fill: parent
|
||||
anchors.margins: 0
|
||||
children: grid
|
||||
|
||||
color: "transparent"
|
||||
|
||||
function scrollToOrigin() {
|
||||
grid.contentY = grid.originY
|
||||
grid.contentX = grid.originX
|
||||
}
|
||||
|
||||
DropArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onEntered: {
|
||||
if(drag.hasUrls)
|
||||
{
|
||||
if(dropManager.canDropUrls(drag.urls, drag.action))
|
||||
{
|
||||
drag.accepted = true;
|
||||
}else
|
||||
drag.accepted = false;
|
||||
}
|
||||
else if (dropManager.canDropFormats(drag.formats)) {
|
||||
drag.accepted = true;
|
||||
} else
|
||||
drag.accepted = false;
|
||||
}
|
||||
|
||||
onDropped: {
|
||||
if(drop.hasUrls && dropManager.canDropUrls(drop.urls, drop.action))
|
||||
{
|
||||
dropManager.droppedFiles(drop.urls, drop.action);
|
||||
}
|
||||
else{
|
||||
if (dropManager.canDropFormats(drop.formats))
|
||||
{
|
||||
var destItem = grid.itemAt(drop.x,drop.y + grid.contentY);
|
||||
var destLocalX = grid.mapToItem(destItem,drop.x,drop.y + grid.contentY).x
|
||||
var realIndex = grid.indexAt(drop.x,drop.y + grid.contentY);
|
||||
|
||||
if(realIndex === -1)
|
||||
realIndex = grid.count - 1;
|
||||
|
||||
var destIndex = destLocalX < (grid.cellWidth / 2) ? realIndex : realIndex + 1;
|
||||
dropManager.droppedComicsForResortingAt(drop.getDataAsString(), destIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property Component continueReadingView: Component {
|
||||
id: continueReadingView
|
||||
Rectangle {
|
||||
id: continueReadingTopView
|
||||
color: "#00000000"
|
||||
|
||||
height: list.count > 0 ? main.continuReadingHeight : main.topContentMargin
|
||||
|
||||
Rectangle {
|
||||
color: continueReadingBackgroundColor
|
||||
|
||||
id: continueReadingBackground
|
||||
|
||||
width: main.width
|
||||
height: main.continuReadingHeight - main.topContentMargin
|
||||
|
||||
visible: list.count > 0
|
||||
|
||||
Text {
|
||||
id: continueReadingText
|
||||
text: qsTr("Continue Reading...")
|
||||
color: continueReadingColor
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 15
|
||||
anchors.bottomMargin: 20
|
||||
anchors.leftMargin: 25
|
||||
anchors.rightMargin: 0
|
||||
font.pointSize: 18
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: list
|
||||
objectName: "list"
|
||||
anchors { top: continueReadingText.bottom; left: parent.left; right: parent.right; bottom: parent.bottom; }
|
||||
|
||||
property int previousIndex;
|
||||
property int verticalPadding: 20
|
||||
|
||||
orientation: Qt.Horizontal
|
||||
pixelAligned: true
|
||||
|
||||
model: comicsList
|
||||
|
||||
spacing: 20
|
||||
anchors.topMargin: 15
|
||||
anchors.bottomMargin: 20
|
||||
anchors.leftMargin: 25
|
||||
anchors.rightMargin: 20
|
||||
|
||||
WheelHandler {
|
||||
onWheel: event => {
|
||||
if (list.contentWidth <= list.width) {
|
||||
return;
|
||||
}
|
||||
|
||||
var newValue = Math.min(list.contentWidth - list.width - anchors.leftMargin, (Math.max(list.originX , list.contentX - event.angleDelta.y)));
|
||||
list.contentX = newValue
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Component {
|
||||
|
||||
//cover
|
||||
Rectangle {
|
||||
width: Math.floor((list.height - (list.verticalPadding * 2)) * 0.65);
|
||||
height: list.height - (list.verticalPadding * 2);
|
||||
|
||||
color:"transparent"
|
||||
|
||||
scale: mouseArea.containsMouse ? 1.025 : 1
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation { duration: 90 }
|
||||
}
|
||||
|
||||
Image {
|
||||
id: coverElement
|
||||
anchors.fill: parent
|
||||
source: cover_path
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
smooth: true
|
||||
mipmap: true
|
||||
asynchronous : true
|
||||
cache: true
|
||||
}
|
||||
|
||||
//title
|
||||
Text {
|
||||
id : comicTitleText
|
||||
anchors { top: coverElement.bottom; left: coverElement.left; right: coverElement.right; leftMargin: 4; rightMargin: 4; topMargin: 4; }
|
||||
width: itemWidth - 8
|
||||
maximumLineCount: 2
|
||||
wrapMode: Text.WordWrap
|
||||
text: readable_title
|
||||
elide: Text.ElideRight
|
||||
color: titleColor
|
||||
font.letterSpacing: fontSpacing
|
||||
font.pointSize: fontSize
|
||||
font.family: fontFamily
|
||||
}
|
||||
|
||||
//border
|
||||
Rectangle {
|
||||
width: coverElement.width
|
||||
height: coverElement.height
|
||||
anchors.centerIn: coverElement
|
||||
color: "transparent"
|
||||
border {
|
||||
color: "#30FFFFFF"
|
||||
width: 1
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
onDoubleClicked: {
|
||||
list.currentIndex = index;
|
||||
openHelper.openComicFromContinueReadingList(index);
|
||||
}
|
||||
|
||||
onReleased: mouse => {
|
||||
list.currentIndex = index;
|
||||
|
||||
if(mouse.button === Qt.RightButton) // context menu is requested
|
||||
{
|
||||
var coordinates = main.mapFromItem(coverElement,mouseX,mouseY)
|
||||
contextMenuHelper.requestedContinueReadingComicContextMenu(Qt.point(coordinates.x,coordinates.y), index);
|
||||
}
|
||||
|
||||
mouse.accepted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
focus: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GridView {
|
||||
id:grid
|
||||
objectName: "grid"
|
||||
anchors.fill: parent
|
||||
cellHeight: cellCustomHeight
|
||||
header: continueReadingView
|
||||
focus: true
|
||||
model: foldersList
|
||||
delegate: appDelegate
|
||||
anchors.topMargin: 0
|
||||
anchors.bottomMargin: 10
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: 0
|
||||
pixelAligned: true
|
||||
highlightFollowsCurrentItem: true
|
||||
|
||||
currentIndex: 0
|
||||
cacheBuffer: 0
|
||||
|
||||
interactive: true
|
||||
|
||||
move: Transition {
|
||||
NumberAnimation { properties: "x,y"; duration: 250 }
|
||||
}
|
||||
|
||||
moveDisplaced: Transition {
|
||||
NumberAnimation { properties: "x,y"; duration: 250 }
|
||||
}
|
||||
|
||||
remove: Transition {
|
||||
ParallelAnimation {
|
||||
NumberAnimation { property: "opacity"; to: 0; duration: 250 }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
removeDisplaced: Transition {
|
||||
NumberAnimation { properties: "x,y"; duration: 250 }
|
||||
}
|
||||
|
||||
displaced: Transition {
|
||||
NumberAnimation { properties: "x,y"; duration: 250 }
|
||||
}
|
||||
|
||||
function numCellsPerRow() {
|
||||
return Math.floor(width / cellCustomWidth);
|
||||
}
|
||||
|
||||
onWidthChanged: {
|
||||
calculateCellWidths(cellCustomWidth);
|
||||
}
|
||||
|
||||
function calculateCellWidths(cWidth) {
|
||||
var wholeCells = Math.floor(width / cWidth);
|
||||
var rest = width - (cWidth * wholeCells)
|
||||
|
||||
grid.cellWidth = cWidth + Math.floor(rest / wholeCells);
|
||||
}
|
||||
|
||||
WheelHandler {
|
||||
onWheel: event => {
|
||||
if (grid.contentHeight <= grid.height) {
|
||||
return;
|
||||
}
|
||||
|
||||
var newValue = Math.min((grid.contentHeight - grid.height - (true ? main.continuReadingHeight : main.topContentMargin)), (Math.max(grid.originY , grid.contentY - event.angleDelta.y)));
|
||||
grid.contentY = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
visible: grid.contentHeight > grid.height
|
||||
|
||||
contentItem: Item {
|
||||
implicitWidth: 12
|
||||
implicitHeight: 26
|
||||
Rectangle {
|
||||
color: "#88424242"
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 6
|
||||
anchors.leftMargin: 3
|
||||
anchors.rightMargin: 2
|
||||
anchors.bottomMargin: 6
|
||||
border.color: "#AA313131"
|
||||
border.width: 1
|
||||
radius: 3.5
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
459
YACReaderLibrary/qml/FolderContentView6.qml
Normal file
459
YACReaderLibrary/qml/FolderContentView6.qml
Normal file
@ -0,0 +1,459 @@
|
||||
import QtQuick 2.15
|
||||
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.12
|
||||
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import com.yacreader.ComicModel 1.0
|
||||
|
||||
import com.yacreader.ComicInfo 1.0
|
||||
import com.yacreader.ComicDB 1.0
|
||||
|
||||
import QtQuick.Controls.Basic
|
||||
|
||||
Rectangle {
|
||||
id: main
|
||||
|
||||
property int continuReadingHeight: 430;
|
||||
property int topContentMargin: 20;
|
||||
|
||||
color: backgroundColor
|
||||
anchors.margins: 0
|
||||
|
||||
Component {
|
||||
id: appDelegate
|
||||
Rectangle
|
||||
{
|
||||
id: cell
|
||||
width: grid.cellWidth
|
||||
height: grid.cellHeight
|
||||
color: "#00000000"
|
||||
|
||||
scale: mouseArea.containsMouse ? 1.025 : 1
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation { duration: 90 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: realCell
|
||||
|
||||
property int position : 0
|
||||
|
||||
width: itemWidth
|
||||
height: itemHeight
|
||||
|
||||
color: "transparent"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
onDoubleClicked: {
|
||||
openHelper.openFolder(index);
|
||||
}
|
||||
|
||||
onPressed: mouse => {
|
||||
var ci = grid.currentIndex; //save current index
|
||||
|
||||
mouse.accepted = true;
|
||||
|
||||
if(mouse.button === Qt.RightButton) // context menu is requested
|
||||
{
|
||||
var coordinates = main.mapFromItem(realCell,mouseX,mouseY)
|
||||
contextMenuHelper.requestedFolderContextMenu(Qt.point(coordinates.x,coordinates.y), index);
|
||||
mouse.accepted = false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**/
|
||||
|
||||
Rectangle {
|
||||
transform: Rotation { origin.x: coverWidth / 2; origin.y: coverHeight / 2; angle: -4}
|
||||
width: coverElement.width
|
||||
height: coverElement.height
|
||||
radius: 10
|
||||
anchors {horizontalCenter: parent.horizontalCenter; top: realCell.top; topMargin: 0}
|
||||
color: "#20000000"
|
||||
border {
|
||||
color: "#20FFFFFF"
|
||||
width: 1
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
transform: Rotation { origin.x: coverWidth / 2; origin.y: coverHeight / 2; angle: 3}
|
||||
width: coverElement.width
|
||||
height: coverElement.height
|
||||
radius: 10
|
||||
anchors {horizontalCenter: parent.horizontalCenter; top: realCell.top; topMargin: 0}
|
||||
color: "#88000000"
|
||||
border {
|
||||
color: "#20FFFFFF"
|
||||
width: 1
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: coverWidth
|
||||
height: coverHeight
|
||||
anchors {horizontalCenter: parent.horizontalCenter; top: realCell.top; topMargin: 0}
|
||||
id: coverElement
|
||||
|
||||
Image {
|
||||
id: coverImage
|
||||
anchors.fill: parent
|
||||
source: cover_path
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
smooth: true
|
||||
mipmap: true
|
||||
asynchronous : true
|
||||
cache: true
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
anchors.fill: parent
|
||||
cached: true
|
||||
maskSource: Rectangle {
|
||||
width: coverElement.width
|
||||
height: coverElement.height
|
||||
radius: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//border
|
||||
Rectangle {
|
||||
width: coverElement.width
|
||||
height: coverElement.height
|
||||
radius: 10
|
||||
anchors {horizontalCenter: parent.horizontalCenter; top: realCell.top; topMargin: 0}
|
||||
color: "transparent"
|
||||
border {
|
||||
color: "#20FFFFFF"
|
||||
width: 1
|
||||
}
|
||||
}
|
||||
|
||||
//folder name
|
||||
Text {
|
||||
id : titleText
|
||||
anchors { top: coverElement.bottom; left: realCell.left; leftMargin: 4; rightMargin: 4; topMargin: 10; }
|
||||
width: itemWidth - 8
|
||||
maximumLineCount: 2
|
||||
wrapMode: Text.WordWrap
|
||||
text: name
|
||||
elide: Text.ElideRight
|
||||
color: titleColor
|
||||
font.letterSpacing: fontSpacing
|
||||
font.pointSize: fontSize
|
||||
font.family: fontFamily
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: scrollView
|
||||
objectName: "topScrollView"
|
||||
anchors.fill: parent
|
||||
anchors.margins: 0
|
||||
children: grid
|
||||
|
||||
color: "transparent"
|
||||
|
||||
function scrollToOrigin() {
|
||||
grid.contentY = grid.originY
|
||||
grid.contentX = grid.originX
|
||||
}
|
||||
|
||||
DropArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onEntered: {
|
||||
if(drag.hasUrls)
|
||||
{
|
||||
if(dropManager.canDropUrls(drag.urls, drag.action))
|
||||
{
|
||||
drag.accepted = true;
|
||||
}else
|
||||
drag.accepted = false;
|
||||
}
|
||||
else if (dropManager.canDropFormats(drag.formats)) {
|
||||
drag.accepted = true;
|
||||
} else
|
||||
drag.accepted = false;
|
||||
}
|
||||
|
||||
onDropped: {
|
||||
if(drop.hasUrls && dropManager.canDropUrls(drop.urls, drop.action))
|
||||
{
|
||||
dropManager.droppedFiles(drop.urls, drop.action);
|
||||
}
|
||||
else{
|
||||
if (dropManager.canDropFormats(drop.formats))
|
||||
{
|
||||
var destItem = grid.itemAt(drop.x,drop.y + grid.contentY);
|
||||
var destLocalX = grid.mapToItem(destItem,drop.x,drop.y + grid.contentY).x
|
||||
var realIndex = grid.indexAt(drop.x,drop.y + grid.contentY);
|
||||
|
||||
if(realIndex === -1)
|
||||
realIndex = grid.count - 1;
|
||||
|
||||
var destIndex = destLocalX < (grid.cellWidth / 2) ? realIndex : realIndex + 1;
|
||||
dropManager.droppedComicsForResortingAt(drop.getDataAsString(), destIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property Component continueReadingView: Component {
|
||||
id: continueReadingView
|
||||
Rectangle {
|
||||
id: continueReadingTopView
|
||||
color: "#00000000"
|
||||
|
||||
height: list.count > 0 ? main.continuReadingHeight : main.topContentMargin
|
||||
|
||||
Rectangle {
|
||||
color: continueReadingBackgroundColor
|
||||
|
||||
id: continueReadingBackground
|
||||
|
||||
width: main.width
|
||||
height: main.continuReadingHeight - main.topContentMargin
|
||||
|
||||
visible: list.count > 0
|
||||
|
||||
Text {
|
||||
id: continueReadingText
|
||||
text: qsTr("Continue Reading...")
|
||||
color: continueReadingColor
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 15
|
||||
anchors.bottomMargin: 20
|
||||
anchors.leftMargin: 25
|
||||
anchors.rightMargin: 0
|
||||
font.pointSize: 18
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: list
|
||||
objectName: "list"
|
||||
anchors { top: continueReadingText.bottom; left: parent.left; right: parent.right; bottom: parent.bottom; }
|
||||
|
||||
property int previousIndex;
|
||||
property int verticalPadding: 20
|
||||
|
||||
orientation: Qt.Horizontal
|
||||
pixelAligned: true
|
||||
|
||||
model: comicsList
|
||||
|
||||
spacing: 20
|
||||
anchors.topMargin: 15
|
||||
anchors.bottomMargin: 20
|
||||
anchors.leftMargin: 25
|
||||
anchors.rightMargin: 20
|
||||
|
||||
WheelHandler {
|
||||
onWheel: event => {
|
||||
if (list.contentWidth <= list.width) {
|
||||
return;
|
||||
}
|
||||
|
||||
var newValue = Math.min(list.contentWidth - list.width - anchors.leftMargin, (Math.max(list.originX , list.contentX - event.angleDelta.y)));
|
||||
list.contentX = newValue
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Component {
|
||||
|
||||
//cover
|
||||
Rectangle {
|
||||
width: Math.floor((list.height - (list.verticalPadding * 2)) * 0.65);
|
||||
height: list.height - (list.verticalPadding * 2);
|
||||
|
||||
color:"transparent"
|
||||
|
||||
scale: mouseArea.containsMouse ? 1.025 : 1
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation { duration: 90 }
|
||||
}
|
||||
|
||||
Image {
|
||||
id: coverElement
|
||||
anchors.fill: parent
|
||||
source: cover_path
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
smooth: true
|
||||
mipmap: true
|
||||
asynchronous : true
|
||||
cache: true
|
||||
}
|
||||
|
||||
//title
|
||||
Text {
|
||||
id : comicTitleText
|
||||
anchors { top: coverElement.bottom; left: coverElement.left; right: coverElement.right; leftMargin: 4; rightMargin: 4; topMargin: 4; }
|
||||
width: itemWidth - 8
|
||||
maximumLineCount: 2
|
||||
wrapMode: Text.WordWrap
|
||||
text: readable_title
|
||||
elide: Text.ElideRight
|
||||
color: titleColor
|
||||
font.letterSpacing: fontSpacing
|
||||
font.pointSize: fontSize
|
||||
font.family: fontFamily
|
||||
}
|
||||
|
||||
//border
|
||||
Rectangle {
|
||||
width: coverElement.width
|
||||
height: coverElement.height
|
||||
anchors.centerIn: coverElement
|
||||
color: "transparent"
|
||||
border {
|
||||
color: "#30FFFFFF"
|
||||
width: 1
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
onDoubleClicked: {
|
||||
list.currentIndex = index;
|
||||
openHelper.openComicFromContinueReadingList(index);
|
||||
}
|
||||
|
||||
onReleased: mouse => {
|
||||
list.currentIndex = index;
|
||||
|
||||
if(mouse.button === Qt.RightButton) // context menu is requested
|
||||
{
|
||||
var coordinates = main.mapFromItem(coverElement,mouseX,mouseY)
|
||||
contextMenuHelper.requestedContinueReadingComicContextMenu(Qt.point(coordinates.x,coordinates.y), index);
|
||||
}
|
||||
|
||||
mouse.accepted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
focus: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GridView {
|
||||
id:grid
|
||||
objectName: "grid"
|
||||
anchors.fill: parent
|
||||
cellHeight: cellCustomHeight
|
||||
header: continueReadingView
|
||||
focus: true
|
||||
model: foldersList
|
||||
delegate: appDelegate
|
||||
anchors.topMargin: 0
|
||||
anchors.bottomMargin: 10
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: 0
|
||||
pixelAligned: true
|
||||
highlightFollowsCurrentItem: true
|
||||
|
||||
currentIndex: 0
|
||||
cacheBuffer: 0
|
||||
|
||||
interactive: true
|
||||
|
||||
move: Transition {
|
||||
NumberAnimation { properties: "x,y"; duration: 250 }
|
||||
}
|
||||
|
||||
moveDisplaced: Transition {
|
||||
NumberAnimation { properties: "x,y"; duration: 250 }
|
||||
}
|
||||
|
||||
remove: Transition {
|
||||
ParallelAnimation {
|
||||
NumberAnimation { property: "opacity"; to: 0; duration: 250 }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
removeDisplaced: Transition {
|
||||
NumberAnimation { properties: "x,y"; duration: 250 }
|
||||
}
|
||||
|
||||
displaced: Transition {
|
||||
NumberAnimation { properties: "x,y"; duration: 250 }
|
||||
}
|
||||
|
||||
function numCellsPerRow() {
|
||||
return Math.floor(width / cellCustomWidth);
|
||||
}
|
||||
|
||||
onWidthChanged: {
|
||||
calculateCellWidths(cellCustomWidth);
|
||||
}
|
||||
|
||||
function calculateCellWidths(cWidth) {
|
||||
var wholeCells = Math.floor(width / cWidth);
|
||||
var rest = width - (cWidth * wholeCells)
|
||||
|
||||
grid.cellWidth = cWidth + Math.floor(rest / wholeCells);
|
||||
}
|
||||
|
||||
WheelHandler {
|
||||
onWheel: event => {
|
||||
if (grid.contentHeight <= grid.height) {
|
||||
return;
|
||||
}
|
||||
|
||||
var newValue = Math.min((grid.contentHeight - grid.height - (true ? main.continuReadingHeight : main.topContentMargin)), (Math.max(grid.originY , grid.contentY - event.angleDelta.y)));
|
||||
grid.contentY = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
visible: grid.contentHeight > grid.height
|
||||
|
||||
contentItem: Item {
|
||||
implicitWidth: 12
|
||||
implicitHeight: 26
|
||||
Rectangle {
|
||||
color: "#88424242"
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 6
|
||||
anchors.leftMargin: 3
|
||||
anchors.rightMargin: 2
|
||||
anchors.bottomMargin: 6
|
||||
border.color: "#AA313131"
|
||||
border.width: 1
|
||||
radius: 3.5
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file alias="qml/GridComicsView.qml">qml/GridComicsView6.qml</file>
|
||||
<file alias="qml/FolderContentView.qml">qml/FolderContentView6.qml</file>
|
||||
<file alias="qml/FlowView.qml">qml/FlowView6.qml</file>
|
||||
<file alias="qml/InfoTick.qml">qml/InfoTick6.qml</file>
|
||||
<file alias="qml/InfoFavorites.qml">qml/InfoFavorites6.qml</file>
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "grid_comics_view.h"
|
||||
#include "info_comics_view.h"
|
||||
#include "comics_view_transition.h"
|
||||
#include "empty_folder_widget.h"
|
||||
#include "folder_content_view.h"
|
||||
#include "empty_label_widget.h"
|
||||
#include "empty_special_list.h"
|
||||
#include "empty_reading_list_widget.h"
|
||||
@ -48,7 +48,7 @@ YACReaderComicsViewsManager::YACReaderComicsViewsManager(QSettings *settings, Li
|
||||
doComicsViewConnections();
|
||||
|
||||
comicsViewStack->addWidget(comicsViewTransition = new ComicsViewTransition());
|
||||
comicsViewStack->addWidget(emptyFolderWidget = new EmptyFolderWidget());
|
||||
comicsViewStack->addWidget(folderContentView = new FolderContentView());
|
||||
comicsViewStack->addWidget(emptyLabelWidget = new EmptyLabelWidget());
|
||||
comicsViewStack->addWidget(emptySpecialList = new EmptySpecialListWidget());
|
||||
comicsViewStack->addWidget(emptyReadingList = new EmptyReadingListWidget());
|
||||
@ -59,8 +59,8 @@ YACReaderComicsViewsManager::YACReaderComicsViewsManager(QSettings *settings, Li
|
||||
comicsViewStack->setCurrentWidget(comicsView);
|
||||
|
||||
// connections
|
||||
connect(emptyFolderWidget, &EmptyFolderWidget::copyComicsToCurrentFolder, libraryWindow, &LibraryWindow::copyAndImportComicsToCurrentFolder);
|
||||
connect(emptyFolderWidget, &EmptyFolderWidget::moveComicsToCurrentFolder, libraryWindow, &LibraryWindow::moveAndImportComicsToCurrentFolder);
|
||||
connect(folderContentView, &FolderContentView::copyComicsToCurrentFolder, libraryWindow, &LibraryWindow::copyAndImportComicsToCurrentFolder);
|
||||
connect(folderContentView, &FolderContentView::moveComicsToCurrentFolder, libraryWindow, &LibraryWindow::moveAndImportComicsToCurrentFolder);
|
||||
}
|
||||
|
||||
QWidget *YACReaderComicsViewsManager::containerWidget()
|
||||
@ -86,7 +86,7 @@ void YACReaderComicsViewsManager::showComicsView()
|
||||
|
||||
void YACReaderComicsViewsManager::showEmptyFolderView()
|
||||
{
|
||||
comicsViewStack->setCurrentWidget(emptyFolderWidget);
|
||||
comicsViewStack->setCurrentWidget(folderContentView);
|
||||
}
|
||||
|
||||
void YACReaderComicsViewsManager::showEmptyLabelView()
|
||||
|
@ -12,7 +12,7 @@ class ClassicComicsView;
|
||||
class GridComicsView;
|
||||
class InfoComicsView;
|
||||
class ComicsViewTransition;
|
||||
class EmptyFolderWidget;
|
||||
class FolderContentView;
|
||||
class EmptyLabelWidget;
|
||||
class EmptySpecialListWidget;
|
||||
class EmptyReadingListWidget;
|
||||
@ -20,6 +20,7 @@ class NoSearchResultsWidget;
|
||||
|
||||
using namespace YACReader;
|
||||
|
||||
// TODO rename to YACReaderContentViewsManager
|
||||
class YACReaderComicsViewsManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -32,7 +33,7 @@ public:
|
||||
|
||||
ComicsViewTransition *comicsViewTransition;
|
||||
|
||||
EmptyFolderWidget *emptyFolderWidget;
|
||||
FolderContentView *folderContentView;
|
||||
EmptyLabelWidget *emptyLabelWidget;
|
||||
EmptySpecialListWidget *emptySpecialList;
|
||||
EmptyReadingListWidget *emptyReadingList;
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "folder_model.h"
|
||||
#include "reading_list_model.h"
|
||||
#include "comics_view.h"
|
||||
#include "empty_folder_widget.h"
|
||||
#include "folder_content_view.h"
|
||||
#include "yacreader_search_line_edit.h"
|
||||
#include "yacreader_global.h"
|
||||
#include "empty_label_widget.h"
|
||||
@ -249,9 +249,18 @@ void YACReaderNavigationController::selectSubfolder(const QModelIndex &sourceMIP
|
||||
|
||||
void YACReaderNavigationController::loadEmptyFolderInfo(const QModelIndex &modelIndex)
|
||||
{
|
||||
QStringList subfolders;
|
||||
subfolders = libraryWindow->foldersModel->getSubfoldersNames(modelIndex);
|
||||
comicsViewsManager->emptyFolderWidget->setSubfolders(modelIndex, subfolders);
|
||||
auto readingComicsModel = new ComicModel();
|
||||
|
||||
auto isRoot = !modelIndex.isValid();
|
||||
|
||||
if (isRoot) {
|
||||
readingComicsModel->setupReadingModelData(libraryWindow->foldersModel->getDatabase());
|
||||
}
|
||||
|
||||
comicsViewsManager->folderContentView->setContinueReadingModel(readingComicsModel);
|
||||
|
||||
auto subFolderModel = libraryWindow->foldersModel->getSubfoldersModel(modelIndex);
|
||||
comicsViewsManager->folderContentView->setModel(modelIndex, subFolderModel);
|
||||
}
|
||||
|
||||
void YACReaderNavigationController::loadPreviousStatus()
|
||||
@ -266,7 +275,10 @@ void YACReaderNavigationController::setupConnections()
|
||||
connect(libraryWindow->foldersView, &YACReaderTreeView::clicked, this, &YACReaderNavigationController::selectedFolder);
|
||||
connect(libraryWindow->listsView, &QAbstractItemView::clicked, this, &YACReaderNavigationController::selectedList);
|
||||
connect(libraryWindow->historyController, &YACReaderHistoryController::modelIndexSelected, this, &YACReaderNavigationController::selectedIndexFromHistory);
|
||||
connect(comicsViewsManager->emptyFolderWidget, &EmptyFolderWidget::subfolderSelected, this, &YACReaderNavigationController::selectSubfolder);
|
||||
connect(comicsViewsManager->folderContentView, &FolderContentView::subfolderSelected, this, &YACReaderNavigationController::selectSubfolder);
|
||||
connect(comicsViewsManager->folderContentView, &FolderContentView::openComic, libraryWindow, QOverload<const ComicDB &, const ComicModel::Mode>::of(&LibraryWindow::openComic));
|
||||
connect(comicsViewsManager->folderContentView, &FolderContentView::openFolderContextMenu, libraryWindow, &LibraryWindow::showGridFoldersContextMenu);
|
||||
connect(comicsViewsManager->folderContentView, &FolderContentView::openContinueReadingComicContextMenu, libraryWindow, &LibraryWindow::showContinueReadingContextMenu);
|
||||
connect(libraryWindow->comicsModel, &ComicModel::isEmpty, this, &YACReaderNavigationController::reselectCurrentSource);
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,20 @@ Folder::Folder(qulonglong folderId, qulonglong parentId, const QString &folderNa
|
||||
this->path = folderPath;
|
||||
}
|
||||
|
||||
Folder::Folder(qulonglong folderId, qulonglong parentId, const QString &folderName, const QString &folderPath, bool completed, bool finished, bool manga)
|
||||
: knownParent(true),
|
||||
knownId(true),
|
||||
numChildren(-1)
|
||||
{
|
||||
this->id = folderId;
|
||||
this->parentId = parentId;
|
||||
this->name = folderName;
|
||||
this->path = folderPath;
|
||||
this->completed = completed;
|
||||
this->finished = finished;
|
||||
this->manga = manga;
|
||||
}
|
||||
|
||||
Folder::Folder(const Folder &folder)
|
||||
{
|
||||
operator=(folder);
|
||||
|
@ -13,6 +13,7 @@ public:
|
||||
|
||||
Folder();
|
||||
Folder(qulonglong folderId, qulonglong parentId, const QString &folderName, const QString &folderPath);
|
||||
Folder(qulonglong folderId, qulonglong parentId, const QString &folderName, const QString &folderPath, bool completed, bool finished, bool manga);
|
||||
Folder(const QString &folderName, const QString &folderPath);
|
||||
Folder(const Folder &folder);
|
||||
Folder &operator=(const Folder &other);
|
||||
@ -94,8 +95,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool finished;
|
||||
bool completed;
|
||||
bool finished; // finished means read, the user has read all the content in this folder
|
||||
bool completed; // completed means the folder has all the content, e.g. a series got its final issue and the user has collected all of them
|
||||
bool manga;
|
||||
|
||||
qint32 numChildren; //-1 for unknown number of children
|
||||
|
Loading…
Reference in New Issue
Block a user