From c2df11298b3be4919a660206e5100ee6dbdecd1c Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 27 Jun 2019 08:43:18 -0400
Subject: refactored stuff into PaintContext and Files keep selection when
switching back to no filtering up/down keyboard navigation
---
src/modinfodialog.ui | 6 +-
src/modinfodialogimages.cpp | 445 ++++++++++++++++++++++++++++++--------------
src/modinfodialogimages.h | 98 +++++++---
3 files changed, 387 insertions(+), 162 deletions(-)
diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui
index a9af2593..e745967d 100644
--- a/src/modinfodialog.ui
+++ b/src/modinfodialog.ui
@@ -207,7 +207,11 @@
0
-
-
+
+
+ Qt::StrongFocus
+
+
-
diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp
index 7e021f97..4179c62a 100644
--- a/src/modinfodialogimages.cpp
+++ b/src/modinfodialogimages.cpp
@@ -33,7 +33,7 @@ ImagesTab::ImagesTab(
ModInfoDialogTab(oc, plugin, parent, ui, id),
m_image(new ScalableImage),
m_margins(3), m_border(1), m_padding(0), m_spacing(5),
- m_selection(nullptr), m_ddsAvailable(false), m_ddsEnabled(false)
+ m_ddsAvailable(false), m_ddsEnabled(false)
{
getSupportedFormats();
@@ -80,11 +80,8 @@ ImagesTab::ImagesTab(
void ImagesTab::clear()
{
- m_files.clear();
- m_filteredFiles.clear();
ui->imagesScrollerVBar->setValue(0);
-
- select(nullptr);
+ select(BadIndex);
setHasData(false);
}
@@ -92,7 +89,7 @@ bool ImagesTab::feedFile(const QString& rootPath, const QString& fullPath)
{
for (const auto& ext : m_supportedFormats) {
if (fullPath.endsWith(ext, Qt::CaseInsensitive)) {
- m_files.push_back({fullPath});
+ m_files.add({fullPath});
return true;
}
}
@@ -102,11 +99,11 @@ bool ImagesTab::feedFile(const QString& rootPath, const QString& fullPath)
void ImagesTab::update()
{
- filterImages();
+ checkFiltering();
updateScrollbar();
ui->imagesThumbnails->update();
- setHasData(fileCount() > 0);
+ setHasData(m_files.size() > 0);
}
void ImagesTab::saveState(Settings& s)
@@ -121,88 +118,79 @@ void ImagesTab::restoreState(const Settings& s)
.value("mod_info_dialog_images_show_dds", false).toBool());
}
-void ImagesTab::filterImages()
+void ImagesTab::checkFiltering()
{
- if (!needsFiltering()) {
- return;
+ if (m_filter.empty() && m_ddsEnabled) {
+ // no filtering needed
+
+ if (m_files.isFiltered()) {
+ // was filtered, needs switch
+ switchToAll();
+ }
+ } else {
+ // filtering is needed
+ switchToFiltered();
}
+}
- m_filteredFiles.clear();
+void ImagesTab::switchToAll()
+{
+ // remember selection
+ const auto* oldSelection = m_files.selectedFile();
+
+ // switch
+ m_files.switchToAll();
+
+ // reselect old
+ if (oldSelection) {
+ m_files.select(m_files.indexOf(oldSelection));
+ } else {
+ m_files.select(BadIndex);
+ }
+}
+
+void ImagesTab::switchToFiltered()
+{
+ // remember old selection, will be checked when building the filtered list
+ // below
+ const auto* oldSelection = m_files.selectedFile();
+ std::size_t newSelection = BadIndex;
+
+ // switch, also clears list
+ m_files.switchToFiltered();
- bool hasTextFilter = !m_filter.empty();
- bool sawSelection = false;
+ const bool hasTextFilter = !m_filter.empty();
- for (auto& f : m_files) {
+ for (auto& f : m_files.allFiles()) {
if (hasTextFilter) {
+ // check filter widget
const auto m = m_filter.matches([&](auto&& what) {
return f.path.contains(what, Qt::CaseInsensitive);
- });
+ });
if (!m) {
+ // no match, skip
continue;
}
}
if (!m_ddsEnabled) {
+ // skip .dds files
if (f.path.endsWith(".dds", Qt::CaseInsensitive)) {
continue;
}
}
- if (&f == m_selection) {
- sawSelection = true;
- }
-
- m_filteredFiles.push_back(&f);
- }
-
- if (!sawSelection) {
- select(nullptr);
- }
-}
-
-std::size_t ImagesTab::fileCount() const
-{
- if (needsFiltering()) {
- return m_filteredFiles.size();
- } else {
- return m_files.size();
- }
-}
-
-const ImagesTab::File* ImagesTab::getFile(std::size_t i) const
-{
- if (needsFiltering()) {
- if (i >= m_filteredFiles.size()) {
- return nullptr;
- }
-
- return m_filteredFiles[i];
- } else {
- if (i >= m_files.size()) {
- return nullptr;
+ if (&f == oldSelection) {
+ // found the old selection, remember its index
+ newSelection = m_files.size();
}
- return &m_files[i];
- }
-}
-
-ImagesTab::File* ImagesTab::getFile(std::size_t i)
-{
- return const_cast(std::as_const(*this).getFile(i));
-}
-
-bool ImagesTab::needsFiltering() const
-{
- if (!m_filter.empty()) {
- return true;
+ m_files.addFiltered(&f);
}
- if (!m_ddsEnabled) {
- return true;
- }
-
- return false;
+ // reselect old, or clear if it wasn't found
+ select(newSelection);
}
void ImagesTab::getSupportedFormats()
@@ -229,22 +217,101 @@ void ImagesTab::getSupportedFormats()
}
}
-void ImagesTab::select(const File* f)
+void ImagesTab::select(std::size_t i)
{
- if (f) {
+ m_files.select(i);
+
+ if (const auto* f=m_files.selectedFile()) {
ui->imagesPath->setText(QDir::toNativeSeparators(f->path));
ui->imagesExplore->setEnabled(true);
m_image->setImage(f->original);
+ ensureVisible(i);
} else {
ui->imagesPath->clear();
ui->imagesExplore->setEnabled(false);
m_image->clear();
}
- m_selection = f;
ui->imagesThumbnails->update();
}
+void ImagesTab::moveSelection(int direction)
+{
+ const auto i = m_files.selectedIndex();
+
+ if (i == BadIndex) {
+ if (m_files.size() > 0) {
+ select(0);
+ }
+
+ return;
+ }
+
+ if (direction > 0) {
+ // moving down
+ if (i < (m_files.size() - 1)) {
+ select(i + 1);
+ }
+ } else {
+ // moving up
+ if (i > 0) {
+ select(i - 1);
+ }
+ }
+}
+
+void ImagesTab::ensureVisible(std::size_t i)
+{
+ const auto geo = makeGeometry();
+
+ const auto fullyVisible = geo.fullyVisibleCount();
+ const auto first = ui->imagesScrollerVBar->value();
+
+ if (i < first) {
+ // go up
+ ui->imagesScrollerVBar->setValue(static_cast(i));
+ } else if (i >= first + fullyVisible) {
+ // go down
+
+ if (i >= fullyVisible) {
+ ui->imagesScrollerVBar->setValue(static_cast(i - fullyVisible + 1));
+ }
+ }
+}
+
+std::size_t ImagesTab::fileIndexAtPos(const QPoint& p) const
+{
+ const auto geo = makeGeometry();
+
+ // this is the index relative to the top
+ const auto offset = geo.indexAt(p);
+ if (offset == ImagesGeometry::BadIndex) {
+ return BadIndex;
+ }
+
+ const auto first = ui->imagesScrollerVBar->value();
+ if (first < 0) {
+ return BadIndex;
+ }
+
+ const auto i = static_cast(first) + offset;
+ if (i >= m_files.size()) {
+ return BadIndex;
+ }
+
+ return i;
+}
+
+const ImagesTab::File* ImagesTab::fileAtPos(const QPoint& p) const
+{
+ const auto i = fileIndexAtPos(p);
+ if (i >= m_files.size()) {
+ return nullptr;
+ }
+
+ return m_files.get(i);
+}
+
ImagesGeometry ImagesTab::makeGeometry() const
{
return ImagesGeometry(
@@ -254,105 +321,78 @@ ImagesGeometry ImagesTab::makeGeometry() const
void ImagesTab::paintThumbnailsArea(QPaintEvent* e)
{
- const auto geo = makeGeometry();
-
- QPainter painter(ui->imagesThumbnails);
+ PaintContext cx(ui->imagesThumbnails, makeGeometry());
- painter.fillRect(
+ cx.painter.fillRect(
ui->imagesThumbnails->rect(),
ui->imagesThumbnails->palette().color(QPalette::Window));
- const auto visible = geo.fullyVisibleCount() + 1;
+ const auto visible = cx.geo.fullyVisibleCount() + 1;
const auto first = ui->imagesScrollerVBar->value();
for (std::size_t i=0; ifailed) {
return;
}
- if (needsReload(geo, file)) {
- reload(geo, file);
+ if (needsReload(cx.geo, *cx.file)) {
+ reload(cx.geo, *cx.file);
}
- if (file.thumbnail.isNull()) {
+ if (cx.file->thumbnail.isNull()) {
return;
}
- const auto imageRect = geo.imageRect(i);
- const auto scaledThumbRect = centeredRect(imageRect, file.thumbnail.size());
-
- painter.fillRect(scaledThumbRect, m_colors.background);
- painter.drawImage(scaledThumbRect, file.thumbnail);
-}
-
-const ImagesTab::File* ImagesTab::fileAtPos(const QPoint& p) const
-{
- const auto geo = makeGeometry();
-
- // this is the index relative to the top
- const auto offset = geo.indexAt(p);
- if (offset == ImagesGeometry::BadIndex) {
- return nullptr;
- }
-
- const auto first = ui->imagesScrollerVBar->value();
- if (first < 0) {
- return nullptr;
- }
-
- const auto i = static_cast(first) + offset;
- if (i >= fileCount()) {
- return nullptr;
- }
+ const auto imageRect = cx.geo.imageRect(cx.thumbIndex);
+ const auto scaledThumbRect = centeredRect(
+ imageRect, cx.file->thumbnail.size());
- return getFile(i);
+ cx.painter.fillRect(scaledThumbRect, m_colors.background);
+ cx.painter.drawImage(scaledThumbRect, cx.file->thumbnail);
}
void ImagesTab::scrollAreaResized(const QSize&)
@@ -366,7 +406,7 @@ void ImagesTab::thumbnailAreaMouseEvent(QMouseEvent* e)
return;
}
- select(fileAtPos(e->pos()));
+ select(fileIndexAtPos(e->pos()));
}
void ImagesTab::thumbnailAreaWheelEvent(QWheelEvent* e)
@@ -377,6 +417,19 @@ void ImagesTab::thumbnailAreaWheelEvent(QWheelEvent* e)
ui->imagesScrollerVBar->value() + (d > 0 ? -1 : 1));
}
+bool ImagesTab::thumbnailAreaKeyPressEvent(QKeyEvent* e)
+{
+ if (e->key() == Qt::Key_Down) {
+ moveSelection(+1);
+ return true;
+ } else if (e->key() == Qt::Key_Up) {
+ moveSelection(-1);
+ return true;
+ }
+
+ return false;
+}
+
void ImagesTab::onScrolled()
{
ui->imagesThumbnails->update();
@@ -398,11 +451,9 @@ void ImagesTab::showTooltip(QHelpEvent* e)
void ImagesTab::onExplore()
{
- if (!m_selection) {
- return;
+ if (auto* f=m_files.selectedFile()) {
+ MOBase::shell::ExploreFile(f->path);
}
-
- MOBase::shell::ExploreFile(m_selection->path);
}
void ImagesTab::onShowDDS()
@@ -452,7 +503,7 @@ void ImagesTab::reload(const ImagesGeometry& geo, File& file)
void ImagesTab::updateScrollbar()
{
- if (fileCount() == 0) {
+ if (m_files.size() == 0) {
ui->imagesScrollerVBar->setRange(0, 0);
ui->imagesScrollerVBar->setEnabled(false);
return;
@@ -462,11 +513,11 @@ void ImagesTab::updateScrollbar()
const auto availableSize = ui->imagesThumbnails->size();
const auto fullyVisible = geo.fullyVisibleCount();
- if (fullyVisible >= fileCount()) {
+ if (fullyVisible >= m_files.size()) {
ui->imagesScrollerVBar->setRange(0, 0);
ui->imagesScrollerVBar->setEnabled(false);
} else {
- const auto d = fileCount() - fullyVisible;
+ const auto d = m_files.size() - fullyVisible;
ui->imagesScrollerVBar->setRange(0, static_cast(d));
ui->imagesScrollerVBar->setEnabled(true);
}
@@ -506,6 +557,17 @@ void ImagesThumbnails::resizeEvent(QResizeEvent* e)
}
}
+void ImagesThumbnails::keyPressEvent(QKeyEvent* e)
+{
+ if (m_tab) {
+ if (m_tab->thumbnailAreaKeyPressEvent(e)) {
+ return;
+ }
+ }
+
+ QWidget::keyPressEvent(e);
+}
+
bool ImagesThumbnails::event(QEvent* e)
{
if (e->type() == QEvent::ToolTip) {
@@ -711,3 +773,116 @@ void ImagesGeometry::dump() const
<< " . spacing: " << m_spacing << "\n"
<< " . top rect: " << m_topRect;
}
+
+
+ImagesTab::Files::Files()
+ : m_selection(BadIndex), m_filtered(false)
+{
+}
+
+void ImagesTab::Files::clear()
+{
+ m_allFiles.clear();
+ m_filteredFiles.clear();
+ m_filtered = false;
+}
+
+void ImagesTab::Files::add(File f)
+{
+ m_allFiles.emplace_back(std::move(f));
+}
+
+void ImagesTab::Files::addFiltered(File* f)
+{
+ m_filteredFiles.push_back(f);
+}
+
+std::size_t ImagesTab::Files::size() const
+{
+ if (m_filtered) {
+ return m_filteredFiles.size();
+ } else {
+ return m_allFiles.size();
+ }
+}
+
+void ImagesTab::Files::switchToAll()
+{
+ m_filtered = false;
+ m_filteredFiles.clear();
+}
+
+void ImagesTab::Files::switchToFiltered()
+{
+ m_filtered = true;
+ m_filteredFiles.clear();
+}
+
+const ImagesTab::File* ImagesTab::Files::get(std::size_t i) const
+{
+ if (m_filtered) {
+ if (i < m_filteredFiles.size()) {
+ return m_filteredFiles[i];
+ }
+ } else {
+ if (i < m_allFiles.size()) {
+ return &m_allFiles[i];
+ }
+ }
+
+ return nullptr;
+}
+
+ImagesTab::File* ImagesTab::Files::get(std::size_t i)
+{
+ return const_cast(std::as_const(*this).get(i));
+}
+
+std::size_t ImagesTab::Files::indexOf(const File* f) const
+{
+ if (m_filtered) {
+ for (std::size_t i=0; i& ImagesTab::Files::allFiles()
+{
+ return m_allFiles;
+}
+
+bool ImagesTab::Files::isFiltered() const
+{
+ return m_filtered;
+}
diff --git a/src/modinfodialogimages.h b/src/modinfodialogimages.h
index db541ed8..c7f46975 100644
--- a/src/modinfodialogimages.h
+++ b/src/modinfodialogimages.h
@@ -54,6 +54,10 @@ protected:
//
void resizeEvent(QResizeEvent* e) override;
+ // forwards to ImagesTab::thumbnailAreaKeyPressEvent()
+ //
+ void keyPressEvent(QKeyEvent* e) override;
+
// forwards to ImagesTab::showTooltip for tooltip events
//
bool event(QEvent* e) override;
@@ -210,6 +214,9 @@ public:
void restoreState(const Settings& s) override;
private:
+ static constexpr std::size_t BadIndex =
+ std::numeric_limits::max();
+
struct File
{
QString path;
@@ -227,25 +234,72 @@ private:
QColor border, background, selection;
};
+ struct PaintContext
+ {
+ mutable QPainter painter;
+ ImagesGeometry geo;
+ File* file;
+ std::size_t thumbIndex;
+ std::size_t fileIndex;
+
+ PaintContext(QWidget* w, ImagesGeometry geo)
+ : painter(w), geo(geo), file(nullptr), thumbIndex(0), fileIndex(0)
+ {
+ }
+ };
+
+ class Files
+ {
+ public:
+ Files();
+
+ void clear();
+
+ void add(File f);
+ void addFiltered(File* f);
+
+ std::size_t size() const;
+
+ void switchToAll();
+ void switchToFiltered();
+
+ const File* get(std::size_t i) const;
+ File* get(std::size_t i);
+ std::size_t indexOf(const File* f) const;
+
+ const File* selectedFile() const;
+ File* selectedFile();
+ std::size_t selectedIndex() const;
+ void select(std::size_t i);
+
+ std::vector& allFiles();
+
+ bool isFiltered() const;
+
+ private:
+ std::vector m_allFiles;
+ std::vector m_filteredFiles;
+ std::size_t m_selection;
+ bool m_filtered;
+ };
+
+
ScalableImage* m_image;
- std::vector m_files;
- std::vector m_filteredFiles;
std::vector m_supportedFormats;
+ Files m_files;
int m_margins, m_border, m_padding, m_spacing;
- const File* m_selection;
FilterWidget m_filter;
bool m_ddsAvailable, m_ddsEnabled;
Colors m_colors;
void getSupportedFormats();
void enableDDS(bool b);
- void select(const File* file);
- bool needsFiltering() const;
void scrollAreaResized(const QSize& s);
void paintThumbnailsArea(QPaintEvent* e);
void thumbnailAreaMouseEvent(QMouseEvent* e);
void thumbnailAreaWheelEvent(QWheelEvent* e);
+ bool thumbnailAreaKeyPressEvent(QKeyEvent* e);
void onScrolled();
void showTooltip(QHelpEvent* e);
@@ -253,31 +307,23 @@ private:
void onShowDDS();
void onFilterChanged();
- ImagesGeometry makeGeometry() const;
-
- void paintThumbnail(
- QPainter& painter, const ImagesGeometry& geo,
- File& file, std::size_t i);
-
- void paintThumbnailBackground(
- QPainter& painter, const ImagesGeometry& geo,
- File& file, std::size_t i);
-
- void paintThumbnailBorder(
- QPainter& painter, const ImagesGeometry& geo,
- std::size_t i);
-
- void paintThumbnailImage(
- QPainter& painter, const ImagesGeometry& geo,
- File& file, std::size_t i);
+ void select(std::size_t i);
+ void moveSelection(int direction);
+ void ensureVisible(std::size_t i);
+ std::size_t fileIndexAtPos(const QPoint& p) const;
const File* fileAtPos(const QPoint& p) const;
- std::size_t fileCount() const;
- const File* getFile(std::size_t i) const;
- File* getFile(std::size_t i);
+ ImagesGeometry makeGeometry() const;
+
+ void paintThumbnail(const PaintContext& cx);
+ void paintThumbnailBackground(const PaintContext& cx);
+ void paintThumbnailBorder(const PaintContext& cx);
+ void paintThumbnailImage(const PaintContext& cx);
- void filterImages();
+ void checkFiltering();
+ void switchToAll();
+ void switchToFiltered();
bool needsReload(const ImagesGeometry& geo, const File& file) const;
void reload(const ImagesGeometry& geo, File& file);
void updateScrollbar();
--
cgit v1.3.1