#include "grid_comics_view.h" #include #include #include #include "comic.h" #include "comic_files_manager.h" #include "QsLog.h" #include "yacreader_global.h" #include "yacreader_tool_bar_stretch.h" #include "comic_db.h" #include "yacreader_comics_selection_helper.h" #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) { settings = new QSettings(YACReader::getSettingsPath() + "/YACReaderLibrary.ini", QSettings::IniFormat, this); settings->beginGroup("libraryConfig"); qmlRegisterType("com.yacreader.ComicModel", 1, 0, "ComicModel"); qmlRegisterType("com.yacreader.ComicDB", 1, 0, "ComicDB"); qmlRegisterType("com.yacreader.ComicInfo", 1, 0, "ComicInfo"); view = new QQuickWidget(this); view->setResizeMode(QQuickWidget::SizeRootObjectToView); connect( view, &QQuickWidget::statusChanged, [=](QQuickWidget::Status status) { if (status == QQuickWidget::Error) { QLOG_ERROR() << view->errors(); } }); //view->setFocusPolicy(Qt::TabFocus); selectionHelper = new YACReaderComicsSelectionHelper(this); connect(selectionHelper, &YACReaderComicsSelectionHelper::selectionChanged, this, &GridComicsView::dummyUpdater); comicInfoHelper = new YACReaderComicInfoHelper(this); QQmlContext *ctxt = view->rootContext(); LibraryUITheme theme; #ifdef Q_OS_MAC theme = Light; #else theme = Dark; #endif if (theme == Light) { 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("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", 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"); } #ifdef Q_OS_MAC #else #endif ctxt->setContextProperty("backgroundImage", QUrl()); ctxt->setContextProperty("backgroundBlurOpacity", 0.0); ctxt->setContextProperty("backgroundBlurRadius", 0.0); ctxt->setContextProperty("backgroundBlurVisible", false); auto model = new ComicModel(); selectionHelper->setModel(model); ctxt->setContextProperty("comicsList", model); ctxt->setContextProperty("comicsSelection", selectionHelper->selectionModel()); ctxt->setContextProperty("contextMenuHelper", this); ctxt->setContextProperty("comicsSelectionHelper", selectionHelper); ctxt->setContextProperty("currentIndexHelper", this); ctxt->setContextProperty("comicRatingHelper", this); ctxt->setContextProperty("dummyValue", true); ctxt->setContextProperty("dragManager", this); ctxt->setContextProperty("dropManager", this); ctxt->setContextProperty("comicOpener", this); bool showInfo = settings->value(COMICS_GRID_SHOW_INFO, false).toBool(); ctxt->setContextProperty("showInfo", showInfo); view->setSource(QUrl("qrc:/qml/GridComicsView.qml")); auto rootObject = dynamic_cast(view->rootObject()); auto infoContainer = rootObject->findChild("infoContainer"); QQmlProperty(infoContainer, "width").write(settings->value(COMICS_GRID_INFO_WIDTH, 350)); showInfoAction = new QAction(tr("Show info"), this); showInfoAction->setIcon(QIcon(":/images/comics_view_toolbar/show_comic_info.png")); showInfoAction->setCheckable(true); showInfoAction->setChecked(showInfo); connect(showInfoAction, &QAction::toggled, this, &GridComicsView::showInfo); setShowMarks(true); //TODO save this in settings auto l = new QVBoxLayout; l->addWidget(view); this->setLayout(l); setContentsMargins(0, 0, 0, 0); l->setContentsMargins(0, 0, 0, 0); l->setSpacing(0); QLOG_TRACE() << "GridComicsView"; } GridComicsView::~GridComicsView() { delete view; } void GridComicsView::createCoverSizeSliderWidget() { toolBarStretch = new YACReaderToolBarStretch(this); coverSizeSliderWidget = new QWidget(this); coverSizeSliderWidget->setFixedWidth(200); coverSizeSlider = new QSlider(); 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->setMargin(0); coverSizeSliderWidget->setLayout(horizontalLayout); //TODO add shortcuts (ctrl-+ and ctrl-- for zooming in out, + ctrl-0 for reseting the zoom) connect(coverSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(setCoversSize(int))); int coverSize = settings->value(COMICS_GRID_COVER_SIZES, YACREADER_MIN_COVER_WIDTH).toInt(); coverSizeSlider->setValue(coverSize); setCoversSize(coverSize); } void GridComicsView::setToolBar(QToolBar *toolBar) { static_cast(this->layout())->insertWidget(1, toolBar); this->toolbar = toolBar; createCoverSizeSliderWidget(); toolBarStretchAction = toolBar->addWidget(toolBarStretch); toolBar->addAction(showInfoAction); showInfoSeparatorAction = toolBar->addSeparator(); coverSizeSliderAction = toolBar->addWidget(coverSizeSliderWidget); } void GridComicsView::setModel(ComicModel *model) { if (model == NULL) return; ComicsView::setModel(model); setCurrentComicIfNeeded(); selectionHelper->setModel(model); comicInfoHelper->setModel(model); QQmlContext *ctxt = view->rootContext(); ctxt->setContextProperty("comicsList", model); ctxt->setContextProperty("comicsSelection", selectionHelper->selectionModel()); ctxt->setContextProperty("contextMenuHelper", this); ctxt->setContextProperty("comicsSelectionHelper", selectionHelper); ctxt->setContextProperty("currentIndexHelper", this); ctxt->setContextProperty("comicRatingHelper", this); ctxt->setContextProperty("dummyValue", true); ctxt->setContextProperty("dragManager", this); ctxt->setContextProperty("dropManager", this); ctxt->setContextProperty("comicInfoHelper", comicInfoHelper); updateBackgroundConfig(); selectionHelper->clear(); if (model->rowCount() > 0) { setCurrentIndex(model->index(0, 0)); if (showInfoAction->isChecked()) updateInfoForIndex(0); } //If the currentComicView was hidden before showing it sometimes the scroll view doesn't show it //this is a hacky solution... QTimer::singleShot(0, this, SLOT(resetScroll())); } void GridComicsView::updateBackgroundConfig() { if (this->model == NULL) return; QQmlContext *ctxt = view->rootContext(); //backgroun image configuration bool useBackgroundImage = settings->value(USE_BACKGROUND_IMAGE_IN_GRID_VIEW, true).toBool(); if (useBackgroundImage && this->model->rowCount() > 0) { float opacity = settings->value(OPACITY_BACKGROUND_IMAGE_IN_GRID_VIEW, 0.2).toFloat(); float blurRadius = settings->value(BLUR_RADIUS_BACKGROUND_IMAGE_IN_GRID_VIEW, 75).toInt(); int row = settings->value(USE_SELECTED_COMIC_COVER_AS_BACKGROUND_IMAGE_IN_GRID_VIEW, false).toBool() ? currentIndex().row() : 0; ctxt->setContextProperty("backgroundImage", this->model->data(this->model->index(row, 0), ComicModel::CoverPathRole)); ctxt->setContextProperty("backgroundBlurOpacity", opacity); ctxt->setContextProperty("backgroundBlurRadius", blurRadius); ctxt->setContextProperty("backgroundBlurVisible", true); } else { ctxt->setContextProperty("backgroundImage", QUrl()); ctxt->setContextProperty("backgroundBlurOpacity", 0.0); ctxt->setContextProperty("backgroundBlurRadius", 0.0); ctxt->setContextProperty("backgroundBlurVisible", false); } #ifdef Q_OS_MAC ctxt->setContextProperty("cellColor", useBackgroundImage ? "#99FFFFFF" : "#FFFFFF"); ctxt->setContextProperty("selectedColor", "#FFFFFF"); #else ctxt->setContextProperty("cellColor", useBackgroundImage ? "#99212121" : "#212121"); ctxt->setContextProperty("selectedColor", "#121212"); #endif } void GridComicsView::showInfo() { QQmlContext *ctxt = view->rootContext(); ctxt->setContextProperty("showInfo", showInfoAction->isChecked()); updateInfoForIndex(currentIndex().row()); } void GridComicsView::setCurrentIndex(const QModelIndex &index) { selectionHelper->clear(); selectionHelper->selectIndex(index.row()); if (settings->value(USE_SELECTED_COMIC_COVER_AS_BACKGROUND_IMAGE_IN_GRID_VIEW, false).toBool()) updateBackgroundConfig(); if (showInfoAction->isChecked()) updateInfoForIndex(index.row()); } void GridComicsView::setCurrentIndex(int index) { setCurrentIndex(model->index(index, 0)); } QModelIndex GridComicsView::currentIndex() { return selectionHelper->currentIndex(); } QItemSelectionModel *GridComicsView::selectionModel() { return selectionHelper->selectionModel(); } void GridComicsView::scrollTo(const QModelIndex &mi, QAbstractItemView::ScrollHint hint) { Q_UNUSED(mi); Q_UNUSED(hint); } void GridComicsView::toFullScreen() { toolbar->hide(); } void GridComicsView::toNormal() { toolbar->show(); } void GridComicsView::updateConfig(QSettings *settings) { Q_UNUSED(settings); } void GridComicsView::enableFilterMode(bool enabled) { filterEnabled = enabled; QQmlContext *ctxt = view->rootContext(); if (enabled) { ctxt->setContextProperty("showCurrentComic", false); ctxt->setContextProperty("currentComic", nullptr); } else { setCurrentComicIfNeeded(); } } void GridComicsView::selectAll() { selectionHelper->selectAll(); } void GridComicsView::selectIndex(int index) { selectionHelper->selectIndex(index); } void GridComicsView::triggerOpenCurrentComic() { emit openComic(currentComic); } void GridComicsView::rate(int index, int rating) { model->updateRating(rating, model->index(index, 0)); } void GridComicsView::requestedContextMenu(const QPoint &point) { emit customContextMenuViewRequested(point); } void GridComicsView::setCoversSize(int width) { QQmlContext *ctxt = view->rootContext(); auto grid = view->rootObject()->findChild(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)); } 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 GridComicsView::dummyUpdater() { QQmlContext *ctxt = view->rootContext(); ctxt->setContextProperty("dummyValue", true); } void GridComicsView::setCurrentComicIfNeeded() { bool found; currentComic = currentComicFromModel(model, found); QQmlContext *ctxt = view->rootContext(); ComicModel::Mode mode = model->getMode(); bool showCurrentComic = found && filterEnabled == false && (mode == ComicModel::Mode::Folder || mode == ComicModel::Mode::ReadingList) && settings->value(DISPLAY_CONTINUE_READING_IN_GRID_VIEW, true).toBool(); if (showCurrentComic) { ctxt->setContextProperty("currentComic", ¤tComic); ctxt->setContextProperty("currentComicInfo", &(currentComic.info)); ctxt->setContextProperty("showCurrentComic", true); } else { ctxt->setContextProperty("currentComic", ¤tComic); ctxt->setContextProperty("currentComicInfo", &(currentComic.info)); ctxt->setContextProperty("showCurrentComic", false); //ctxt->setContextProperty("currentComic", nullptr); } } void GridComicsView::resetScroll() { auto rootObject = dynamic_cast(view->rootObject()); auto scrollView = rootObject->findChild("topScrollView", Qt::FindChildrenRecursively); QMetaObject::invokeMethod(scrollView, "scrollToOrigin"); } QSize GridComicsView::sizeHint() { return QSize(1280, 768); } QByteArray GridComicsView::getMimeDataFromSelection() { QByteArray data; QMimeData *mimeData = model->mimeData(selectionHelper->selectedIndexes()); data = mimeData->data(YACReader::YACReaderLibrarComiscSelectionMimeDataFormat); delete mimeData; return data; } void GridComicsView::updateCurrentComicView() { setCurrentComicIfNeeded(); } void GridComicsView::startDrag() { auto drag = new QDrag(this); drag->setMimeData(model->mimeData(selectionHelper->selectedRows())); drag->setPixmap(QPixmap(":/images/comics_view_toolbar/openInYACReader.png")); //TODO add better image /*Qt::DropAction dropAction =*/drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction); } bool GridComicsView::canDropUrls(const QList &urls, Qt::DropAction action) { if (action == Qt::CopyAction) { QString currentPath; foreach (QUrl url, urls) { //comics or folders are accepted, folders' content is validate in dropEvent (avoid any lag before droping) currentPath = url.toLocalFile(); if (Comic::fileIsComic(currentPath) || QFileInfo(currentPath).isDir()) return true; } } return false; } bool GridComicsView::canDropFormats(const QString &formats) { return (formats.contains(YACReader::YACReaderLibrarComiscSelectionMimeDataFormat) && model->canBeResorted()); } void GridComicsView::droppedFiles(const QList &urls, Qt::DropAction action) { bool validAction = action == Qt::CopyAction; //TODO add move if (validAction) { QList> droppedFiles = ComicFilesManager::getDroppedFiles(urls); emit copyComicsToCurrentFolder(droppedFiles); } } void GridComicsView::droppedComicsForResortingAt(const QString &data, int index) { Q_UNUSED(data); model->dropMimeData(model->mimeData(selectionHelper->selectedRows()), Qt::MoveAction, index, 0, QModelIndex()); } void GridComicsView::selectedItem(int index) { emit selected(index); } void GridComicsView::setShowMarks(bool show) { QQmlContext *ctxt = view->rootContext(); ctxt->setContextProperty("show_marks", show); } void GridComicsView::closeEvent(QCloseEvent *event) { toolbar->removeAction(toolBarStretchAction); toolbar->removeAction(showInfoAction); toolbar->removeAction(showInfoSeparatorAction); toolbar->removeAction(coverSizeSliderAction); auto rootObject = dynamic_cast(view->rootObject()); auto infoContainer = rootObject->findChild("infoContainer", Qt::FindChildrenRecursively); int infoWidth = QQmlProperty(infoContainer, "width").read().toInt(); /*QObject *object = view->rootObject(); QMetaObject::invokeMethod(object, "exit"); container->close(); view->close();*/ event->accept(); ComicsView::closeEvent(event); //save settings settings->setValue(COMICS_GRID_COVER_SIZES, coverSizeSlider->value()); settings->setValue(COMICS_GRID_SHOW_INFO, showInfoAction->isChecked()); settings->setValue(COMICS_GRID_INFO_WIDTH, infoWidth); }