diff options
author | Chris Xiong <chirs241097@gmail.com> | 2022-09-06 01:13:42 -0400 |
---|---|---|
committer | Chris Xiong <chirs241097@gmail.com> | 2022-09-06 01:13:42 -0400 |
commit | 532ea00ea6ec0e20898ef009e432ea1f55dc8db1 (patch) | |
tree | 53bd9a8fba8a8bcca02ad6b84f3863aa3120fb7c | |
parent | ad93efafe98029ff72dec710949ea168d648dbd7 (diff) | |
download | deduper-532ea00ea6ec0e20898ef009e432ea1f55dc8db1.tar.xz |
Transitioning to using ListView to show images.
The old widgets are not yet removed. A LOT of old sh*t needs rewriting
for removing them.
(Note to self) Other TODOs:
- Convert all keyevents to qactions.
- Convert main window to an actual main window.
- Move file loading logic out of main.cpp (or not, doesn't even matter).
- Move subsliced signatures into library.
-rw-r--r-- | mingui/CMakeLists.txt | 1 | ||||
-rw-r--r-- | mingui/imageitem.cpp | 129 | ||||
-rw-r--r-- | mingui/imageitem.hpp | 39 | ||||
-rw-r--r-- | mingui/mingui.cpp | 47 | ||||
-rw-r--r-- | mingui/mingui.hpp | 7 |
5 files changed, 223 insertions, 0 deletions
diff --git a/mingui/CMakeLists.txt b/mingui/CMakeLists.txt index 3a53455..3503f98 100644 --- a/mingui/CMakeLists.txt +++ b/mingui/CMakeLists.txt @@ -17,6 +17,7 @@ set(CMAKE_AUTOUIC ON) add_executable(mingui main.cpp mingui.cpp + imageitem.cpp ) target_link_libraries(mingui diff --git a/mingui/imageitem.cpp b/mingui/imageitem.cpp new file mode 100644 index 0000000..ec7dc94 --- /dev/null +++ b/mingui/imageitem.cpp @@ -0,0 +1,129 @@ +#include "imageitem.hpp" + +#include <algorithm> + +#include <QDebug> +#include <QFileInfo> +#include <QPixmap> +#include <QListView> +#include <QScrollBar> +#include <QFontMetrics> +#include <QPainter> +#include <QLocale> + +ImageItem::ImageItem(QString fn, QString dispn, QKeySequence hotkey, double pxratio) +{ + this->setText(dispn); + this->setData(fn, ImageItemRoles::path_role); + this->setData(QFileInfo(fn).size(), ImageItemRoles::file_size_role); + this->setCheckable(true); + QPixmap pm(fn); + pm.setDevicePixelRatio(pxratio); + this->setData(pm.size(), ImageItemRoles::dimension_role); + this->setData(hotkey, ImageItemRoles::hotkey_role); + this->setData(pm, Qt::ItemDataRole::DecorationRole); +} + +void ImageItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QRect imr = option.rect; + imr.adjust(MARGIN + BORDER, MARGIN + BORDER, -MARGIN - BORDER, -MARGIN - BORDER); + QFont bfnt(option.font); + bfnt.setBold(true); + QFontMetrics bfm(bfnt); + imr.adjust(0, 0, 0, -2 * HKPADD - bfm.height() - LINESP); + + QRect selr = option.rect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN); + QStyleOptionViewItem so(option); + so.rect = selr; + if (index.data(Qt::ItemDataRole::CheckStateRole).value<Qt::CheckState>() == Qt::CheckState::Checked) + so.state |= QStyle::StateFlag::State_Selected; + so.state |= QStyle::StateFlag::State_Active; + option.widget->style()->drawPrimitive(QStyle::PrimitiveElement::PE_PanelItemViewItem, &so, painter, option.widget); + + + QPixmap pm = index.data(Qt::ItemDataRole::DecorationRole).value<QPixmap>(); + QSize imd = pm.size().scaled(imr.size(), Qt::AspectRatioMode::KeepAspectRatio); + painter->setRenderHint(QPainter::RenderHint::SmoothPixmapTransform); + painter->drawPixmap(QRect(imr.topLeft(), imd), pm); + QPoint dtopright = QRect(imr.topLeft(),imd).bottomLeft(); + + QPoint hko = dtopright + QPoint(HKPADD, HKPADD + LINESP); + QKeySequence ks = index.data(ImageItem::ImageItemRoles::hotkey_role).value<QKeySequence>(); + QString kss = ks.toString(); + QRect r = bfm.boundingRect(kss); + r.moveTopLeft(hko); + QRect hkbg = r.adjusted(-HKPADD, -HKPADD, HKPADD, HKPADD); + if (hkbg.width() < hkbg.height()) + { + int shift = (hkbg.height() - hkbg.width()) / 2; + r.adjust(shift, 0, shift, 0); + hkbg.setWidth(hkbg.height()); + } + painter->setPen(QPen(QBrush(), 0)); + painter->setBrush(option.widget->palette().color(QPalette::ColorGroup::Normal, QPalette::ColorRole::Light)); + painter->drawRoundedRect(hkbg.adjusted(HKSHDS, HKSHDS, HKSHDS, HKSHDS), 4, 4); + painter->setBrush(option.widget->palette().color(QPalette::ColorGroup::Normal, QPalette::ColorRole::WindowText)); + painter->drawRoundedRect(hkbg, 4, 4); + painter->setPen(option.widget->palette().color(QPalette::ColorGroup::Normal, QPalette::ColorRole::Window)); + painter->setBrush(QBrush()); + painter->setFont(bfnt); + painter->drawText(r, kss); + + QPoint ftopright = hkbg.topRight() + QPoint(LINESP + HKSHDS, 0); + QSize dim = index.data(ImageItem::ImageItemRoles::dimension_role).value<QSize>(); + qint64 fsz = index.data(ImageItem::ImageItemRoles::file_size_role).value<qint64>(); + QString infos = QString("%1 x %2, %3") + .arg(dim.width()).arg(dim.height()) + .arg(QLocale::system().formattedDataSize(fsz, 3)); + QString fns = index.data(Qt::ItemDataRole::DisplayRole).toString(); + r = option.fontMetrics.boundingRect(infos); + r.moveTopLeft(ftopright + QPoint(0, (hkbg.height() - r.height()) / 2)); + painter->setFont(option.font); + painter->setPen(option.widget->palette().color(QPalette::ColorGroup::Normal, QPalette::ColorRole::Text)); + painter->drawText(r, infos); + r = option.fontMetrics.boundingRect(fns); + r.moveTopRight(QPoint(option.rect.right() - MARGIN - BORDER, ftopright.y() + (hkbg.height() - r.height()) / 2)); + painter->drawText(r, fns); + /* + painter->setPen(QColor(Qt::GlobalColor::red)); + painter->drawRect(QRect(imr.topLeft(), imd)); + painter->drawRect(txt); + painter->setPen(option.widget->palette().color(QPalette::ColorRole::Text)); + */ +} + +QSize ImageItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + const QListView *lw = qobject_cast<const QListView*>(option.widget); + QSize vpsz = lw->maximumViewportSize(); + QScrollBar *vsc = lw->verticalScrollBar(); + if (vsc->isVisible()) + vpsz.setWidth(vpsz.width() - vsc->size().width()); + QScrollBar *hsc = lw->horizontalScrollBar(); + if (hsc->isVisible()) + vpsz.setHeight(vpsz.height() - vsc->size().height()); + QPixmap pm = index.data(Qt::ItemDataRole::DecorationRole).value<QPixmap>(); + QSize onscsz = pm.size() / pm.devicePixelRatioF(); + int imh = onscsz.height(); + if (onscsz.width() > vpsz.width() - 2 * MARGIN - 2 * BORDER) + imh = (vpsz.width() - 2 * MARGIN - 2 * BORDER) / (double)onscsz.width() * onscsz.height(); + + QFont fnt(option.font); + fnt.setBold(true); + QFontMetrics fm(fnt); + int extra_height = 2 * MARGIN + 2 * BORDER + LINESP + fm.height() + 2 * HKPADD + HKSHDS; + int min_height = 64; + int max_height = imh; + + QSize ret(vpsz); + ret.setHeight(vpsz.height() / index.model()->rowCount() - lw->spacing()); + ret.setHeight(std::max(min_height + extra_height, ret.height())); + ret.setHeight(std::min(max_height + extra_height, ret.height())); + return ret; +} + +void ImageItemDelegate::resize(const QModelIndex &index) +{ + Q_EMIT sizeHintChanged(index); +} diff --git a/mingui/imageitem.hpp b/mingui/imageitem.hpp new file mode 100644 index 0000000..8c9397e --- /dev/null +++ b/mingui/imageitem.hpp @@ -0,0 +1,39 @@ +#ifndef IMAGEITEM_HPP +#define IMAGEITEM_HPP + +#include <QStandardItem> +#include <QAbstractItemDelegate> +#include <QStyleOptionViewItem> +#include <QModelIndex> + +class ImageItem : public QStandardItem +{ +public: + enum ImageItemRoles + { + path_role = Qt::ItemDataRole::UserRole + 0x1000, + dimension_role, + file_size_role, + hotkey_role + }; + ImageItem(QString fn, QString dispn, QKeySequence hotkey, double pxratio = 1.0); +}; + +class ImageItemDelegate : public QAbstractItemDelegate +{ + Q_OBJECT +private: + const static int MARGIN = 3; + const static int BORDER = 3; + const static int HKPADD = 4; + const static int LINESP = 4; + const static int HKSHDS = 2; +public: + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + void resize(const QModelIndex &index); +Q_SIGNALS: + void sizeHintChanged(const QModelIndex &index); +}; + +#endif diff --git a/mingui/mingui.cpp b/mingui/mingui.cpp index ab1a083..94f7e81 100644 --- a/mingui/mingui.cpp +++ b/mingui/mingui.cpp @@ -1,4 +1,5 @@ #include "mingui.hpp" +#include "imageitem.hpp" #include <cstdio> #include <cwchar> @@ -8,6 +9,8 @@ #include <QKeyEvent> #include <QString> #include <QScrollArea> +#include <QListView> +#include <QStandardItemModel> #include <QLabel> #include <QHBoxLayout> #include <QVBoxLayout> @@ -59,9 +62,39 @@ MinGuiWidget::MinGuiWidget() infopanel = new QTextEdit(this); infopanel->setReadOnly(true); imgcontainer = new QWidget(this); + lw = new QListView(this); + im = new QStandardItemModel(this); + lw->setModel(im); + id = new ImageItemDelegate(); + lw->setItemDelegate(id); + lw->setSelectionMode(QAbstractItemView::SelectionMode::NoSelection); + lw->setResizeMode(QListView::ResizeMode::Adjust); + QObject::connect(lw, &QListView::clicked, [this](const QModelIndex &i) { + auto cs = i.data(Qt::ItemDataRole::CheckStateRole).value<Qt::CheckState>(); + if (cs == Qt::CheckState::Checked) + cs = Qt::CheckState::Unchecked; + else cs = Qt::CheckState::Checked; + this->im->setData(i, cs, Qt::ItemDataRole::CheckStateRole); + }); + QObject::connect(lw, &QListView::doubleClicked, [this](const QModelIndex &i) { + auto cs = i.data(Qt::ItemDataRole::CheckStateRole).value<Qt::CheckState>(); + if (cs == Qt::CheckState::Checked) + cs = Qt::CheckState::Unchecked; + else cs = Qt::CheckState::Checked; + this->im->setData(i, cs, Qt::ItemDataRole::CheckStateRole); + QDesktopServices::openUrl(QUrl::fromLocalFile(i.data(ImageItem::ImageItemRoles::path_role).toString())); + }); + QObject::connect(im, &QStandardItemModel::itemChanged, [this](QStandardItem *i) { + ImageItem *itm = static_cast<ImageItem*>(i); + QModelIndex idx = itm->index(); + bool checked = itm->data(Qt::ItemDataRole::CheckStateRole) == Qt::CheckState::Checked; + if (checked != marks[idx.row()]) + this->mark_toggle(idx.row()); + }); sa = new QScrollArea(this); sa->setFrameStyle(QFrame::Shape::NoFrame); l->addWidget(sa); + l->addWidget(lw); l->addWidget(infopanel); marked.clear(); infopanel->setText("bleh"); @@ -74,6 +107,7 @@ void MinGuiWidget::show_images(const std::vector<fs::path> &fns) current_set = fns; marks.clear(); imgw.clear(); + im->clear(); qDeleteAll(imgcontainer->children()); sa->takeWidget(); imgcontainer->setLayout(new QVBoxLayout(imgcontainer)); @@ -94,6 +128,7 @@ void MinGuiWidget::show_images(const std::vector<fs::path> &fns) { marks.push_back(marked.find(f) != marked.end()); ImageWidget *tw = new ImageWidget(f, f.native().substr(common_pfx.length()), idx, max_width, max_height, this); + im->appendRow(new ImageItem(fsstr_to_qstring(f.native()), fsstr_to_qstring(f.native().substr(common_pfx.length())), keys[idx], lw->devicePixelRatioF())); QObject::connect(tw, &ImageWidget::clicked, [this, idx] { this->mark_toggle(idx); }); imgw.push_back(tw); imgcontainer->layout()->addWidget(tw); @@ -230,10 +265,14 @@ void MinGuiWidget::mark_view_update(bool update_msg) if (marks[i]) { p.setColor(QPalette::ColorRole::Window, Qt::GlobalColor::red); + im->item(i)->setCheckState(Qt::CheckState::Checked); ++m; } else + { p.setColor(QPalette::ColorRole::Window, this->palette().color(QPalette::ColorRole::Window)); + im->item(i)->setCheckState(Qt::CheckState::Unchecked); + } imgw[i]->setBackgroundRole(QPalette::ColorRole::Window); imgw[i]->setAutoFillBackground(true); imgw[i]->setPalette(p); @@ -301,6 +340,14 @@ void MinGuiWidget::keyReleaseEvent(QKeyEvent *e) } } +void MinGuiWidget::resizeEvent(QResizeEvent *e) +{ + QWidget::resizeEvent(e); + if (!id || !im) return; + for (int i = 0; i < im->rowCount(); ++i) + id->resize(im->indexFromItem(im->item(i))); +} + void MinGuiWidget::closeEvent(QCloseEvent *e) { if (QMessageBox::StandardButton::Yes == diff --git a/mingui/mingui.hpp b/mingui/mingui.hpp index 755b6ad..aa71596 100644 --- a/mingui/mingui.hpp +++ b/mingui/mingui.hpp @@ -13,6 +13,9 @@ class QLabel; class QStatusBar; class QScrollArea; class QTextEdit; +class QListView; +class QStandardItemModel; +class ImageItemDelegate; namespace fs = std::filesystem; @@ -26,6 +29,9 @@ private: QWidget *imgcontainer; QStatusBar *sb; QScrollArea *sa; + QListView *lw; + QStandardItemModel *im = nullptr; + ImageItemDelegate *id = nullptr; std::size_t ngroups, curgroup; bool nohotkeywarn; void mark_toggle(std::size_t x); @@ -41,6 +47,7 @@ private: protected: void keyPressEvent(QKeyEvent *e) override; void keyReleaseEvent(QKeyEvent *e) override; + void resizeEvent(QResizeEvent *e) override; void closeEvent(QCloseEvent *e) override; public: MinGuiWidget(); |