Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Screenshot manager #7851

Merged
merged 4 commits into from Mar 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 4 additions & 28 deletions rpcs3/rpcs3qt/game_list_frame.cpp
Expand Up @@ -780,30 +780,6 @@ void game_list_frame::SaveSettings()
m_gui_settings->SetValue(gui::gl_state, m_game_list->horizontalHeader()->saveState());
}

static void open_dir(const std::string& spath)
{
fs::create_dir(spath);
const QString path = qstr(spath);

if (fs::is_file(spath))
{
// open directory and select file
// https://stackoverflow.com/questions/3490336/how-to-reveal-in-finder-or-show-in-explorer-with-qt
#ifdef _WIN32
QProcess::startDetached("explorer.exe", { "/select,", QDir::toNativeSeparators(path) });
#elif defined(__APPLE__)
QProcess::execute("/usr/bin/osascript", { "-e", "tell application \"Finder\" to reveal POSIX file \"" + path + "\"" });
QProcess::execute("/usr/bin/osascript", { "-e", "tell application \"Finder\" to activate" });
#else
// open parent directory
QDesktopServices::openUrl(QUrl("file:///" + qstr(fs::get_parent_dir(spath))));
#endif
return;
}

QDesktopServices::openUrl(QUrl("file:///" + path));
}

void game_list_frame::doubleClickedSlot(QTableWidgetItem *item)
{
if (!item)
Expand Down Expand Up @@ -983,20 +959,20 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
const std::string new_config_path = Emulator::GetCustomConfigPath(current_game.serial);

if (fs::is_file(new_config_path))
open_dir(new_config_path);
gui::utils::open_dir(new_config_path);

const std::string old_config_path = Emulator::GetCustomConfigPath(current_game.serial, true);

if (fs::is_file(old_config_path))
open_dir(old_config_path);
gui::utils::open_dir(old_config_path);
});
}
if (fs::is_dir(data_base_dir))
{
QAction* open_data_dir = menu.addAction(tr("&Open Data Folder"));
connect(open_data_dir, &QAction::triggered, [=, this]()
{
open_dir(data_base_dir);
gui::utils::open_dir(data_base_dir);
});
}
menu.addSeparator();
Expand Down Expand Up @@ -1106,7 +1082,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
});
connect(open_game_folder, &QAction::triggered, [=, this]()
{
open_dir(current_game.path);
gui::utils::open_dir(current_game.path);
});
connect(check_compat, &QAction::triggered, [=, this]
{
Expand Down
2 changes: 1 addition & 1 deletion rpcs3/rpcs3qt/gs_frame.cpp
Expand Up @@ -301,7 +301,7 @@ void gs_frame::take_screenshot(const std::vector<u8> sshot_data, const u32 sshot
std::thread(
[sshot_width, sshot_height](const std::vector<u8> sshot_data)
{
std::string screen_path = fs::get_config_dir() + "/screenshots/";
std::string screen_path = fs::get_config_dir() + "screenshots/";

if (!fs::create_dir(screen_path) && fs::g_tls_error != fs::error::exist)
{
Expand Down
32 changes: 32 additions & 0 deletions rpcs3/rpcs3qt/qt_utils.cpp
@@ -1,9 +1,12 @@
#include "qt_utils.h"
#include <QApplication>
#include <QBitmap>
#include <QDesktopServices>
#include <QFontMetrics>
#include <QPainter>
#include <QProcess>
#include <QScreen>
#include <QUrl>

#include "Emu/System.h"

Expand Down Expand Up @@ -276,5 +279,34 @@ namespace gui
// if nothing was found reset the icon to default
return QApplication::windowIcon();
}

void open_dir(const std::string& spath)
{
fs::create_dir(spath);
const QString path = qstr(spath);

if (fs::is_file(spath))
{
// open directory and select file
// https://stackoverflow.com/questions/3490336/how-to-reveal-in-finder-or-show-in-explorer-with-qt
#ifdef _WIN32
QProcess::startDetached("explorer.exe", { "/select,", QDir::toNativeSeparators(path) });
#elif defined(__APPLE__)
QProcess::execute("/usr/bin/osascript", { "-e", "tell application \"Finder\" to reveal POSIX file \"" + path + "\"" });
QProcess::execute("/usr/bin/osascript", { "-e", "tell application \"Finder\" to activate" });
#else
// open parent directory
QDesktopServices::openUrl(QUrl("file:///" + qstr(fs::get_parent_dir(spath))));
#endif
return;
}

QDesktopServices::openUrl(QUrl("file:///" + path));
}

void open_dir(const QString& path)
{
open_dir(sstr(path));
}
} // utils
} // gui
6 changes: 6 additions & 0 deletions rpcs3/rpcs3qt/qt_utils.h
Expand Up @@ -56,5 +56,11 @@ namespace gui

// Loads the app icon from path and embeds it centered into an empty square icon
QIcon get_app_icon_from_path(const std::string& path, const std::string& title_id);

// Open a path in the explorer and mark the file
void open_dir(const std::string& spath);

// Open a path in the explorer and mark the file
void open_dir(const QString& path);
} // utils
} // gui
1 change: 1 addition & 0 deletions rpcs3/rpcs3qt/save_manager_dialog.cpp
Expand Up @@ -98,6 +98,7 @@ save_manager_dialog::save_manager_dialog(std::shared_ptr<gui_settings> gui_setti
{
setWindowTitle(tr("Save Manager"));
setMinimumSize(QSize(400, 400));
setAttribute(Qt::WA_DeleteOnClose);

Init(dir);
}
Expand Down
121 changes: 105 additions & 16 deletions rpcs3/rpcs3qt/screenshot_manager_dialog.cpp
Expand Up @@ -8,47 +8,50 @@
#include <QDirIterator>
#include <QListWidget>
#include <QScreen>
#include <QScrollBar>
#include <QVBoxLayout>
#include <QtConcurrent>

screenshot_manager_dialog::screenshot_manager_dialog(QWidget* parent) : QDialog(parent)
{
setWindowTitle(tr("Screenshots"));
setAttribute(Qt::WA_DeleteOnClose);

m_icon_size = QSize(160, 90);

m_grid = new QListWidget(this);
m_grid->setViewMode(QListWidget::IconMode);
m_grid->setMovement(QListWidget::Static);
m_grid->setResizeMode(QListWidget::Adjust);
m_grid->setIconSize(QSize(160, 90));
m_grid->setGridSize(m_grid->iconSize() + QSize(10, 10));
m_grid->setIconSize(m_icon_size);
m_grid->setGridSize(m_icon_size + QSize(10, 10));

const std::string screen_path = fs::get_config_dir() + "/screenshots/";
const std::string screen_path = fs::get_config_dir() + "screenshots/";
const QStringList filter{ QStringLiteral("*.png") };
QDirIterator dir_iter(QString::fromStdString(screen_path), filter, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);

QPixmap placeholder(m_icon_size);
placeholder.fill(Qt::gray);
m_placeholder = QIcon(placeholder);

while (dir_iter.hasNext())
{
const QString filepath = dir_iter.next();

QListWidgetItem* item = new QListWidgetItem;
item->setData(Qt::UserRole, filepath);
item->setIcon(QIcon(filepath));
item->setData(item_role::source, filepath);
item->setData(item_role::loaded, false);
item->setIcon(m_placeholder);
item->setToolTip(filepath);

m_grid->addItem(item);
}

connect(m_grid, &QListWidget::itemDoubleClicked, [this](QListWidgetItem* item)
{
if (!item)
{
return;
}

const QString filepath = item->data(Qt::UserRole).toString();
m_icon_loader = new QFutureWatcher<thumbnail>(this);
connect(m_icon_loader, &QFutureWatcher<QIcon>::resultReadyAt, this, &screenshot_manager_dialog::update_icon);

screenshot_preview* preview = new screenshot_preview(filepath);
preview->show();
});
connect(m_grid, &QListWidget::itemDoubleClicked, this, &screenshot_manager_dialog::show_preview);
connect(m_grid->verticalScrollBar(), &QScrollBar::valueChanged, this, &screenshot_manager_dialog::update_icons);

QVBoxLayout* layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
Expand All @@ -57,3 +60,89 @@ screenshot_manager_dialog::screenshot_manager_dialog(QWidget* parent) : QDialog(

resize(QGuiApplication::primaryScreen()->availableSize() * 3 / 5);
}

screenshot_manager_dialog::~screenshot_manager_dialog()
{
m_icon_loader->cancel();
m_icon_loader->waitForFinished();
}

void screenshot_manager_dialog::show_preview(QListWidgetItem* item)
{
if (!item)
{
return;
}

const QString filepath = item->data(Qt::UserRole).toString();

screenshot_preview* preview = new screenshot_preview(filepath);
preview->show();
}

void screenshot_manager_dialog::update_icon(int index)
{
const thumbnail tn = m_icon_loader->resultAt(index);

if (QListWidgetItem* item = m_grid->item(tn.index))
{
item->setIcon(tn.icon);
item->setData(item_role::loaded, true);
}
}

void screenshot_manager_dialog::update_icons(int value)
{
const QRect visible_rect = rect();

QList<thumbnail> thumbnails_to_load;

const bool forward = value >= m_scrollbar_value;
m_scrollbar_value = value;

const int first = forward ? 0 : (m_grid->count() - 1);
const int last = forward ? (m_grid->count() - 1) : 0;

for (int i = first; forward ? i <= last : i >= last; forward ? ++i : --i)
{
if (QListWidgetItem* item = m_grid->item(i))
{
const bool is_loaded = item->data(item_role::loaded).toBool();
const bool is_visible = visible_rect.intersects(m_grid->visualItemRect(item));

if (is_visible)
{
if (!is_loaded)
{
thumbnails_to_load.push_back({ QIcon(), item->data(item_role::source).toString() , i });
}
}
else if (is_loaded)
{
item->setIcon(m_placeholder);
item->setData(item_role::loaded, false);
}
}
}

if (m_icon_loader->isRunning())
{
m_icon_loader->cancel();
m_icon_loader->waitForFinished();
}

std::function<thumbnail(thumbnail)> load = [icon_size = m_icon_size](thumbnail tn) -> thumbnail
{
QPixmap pixmap(tn.path);
tn.icon = QIcon(pixmap.scaled(icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
return tn;
};

m_icon_loader->setFuture(QtConcurrent::mapped(thumbnails_to_load, load));
}

void screenshot_manager_dialog::resizeEvent(QResizeEvent* event)
{
QDialog::resizeEvent(event);
update_icons(m_scrollbar_value);
}
37 changes: 37 additions & 0 deletions rpcs3/rpcs3qt/screenshot_manager_dialog.h
@@ -1,16 +1,53 @@
#pragma once

#include <QDialog>
#include <QFutureWatcher>
#include <QIcon>
#include <QSize>

class QListWidget;
class QListWidgetItem;

class screenshot_manager_dialog : public QDialog
{
Q_OBJECT

public:
screenshot_manager_dialog(QWidget* parent = nullptr);
~screenshot_manager_dialog();

protected:
void resizeEvent(QResizeEvent* event) override;

private Q_SLOTS:
void update_icon(int index);

Q_SIGNALS:
void signal_icon_change(int index, const QString& path);

private:
void show_preview(QListWidgetItem* item);
void update_icons(int value);

enum item_role
{
source = Qt::UserRole,
loaded = Qt::UserRole + 1,
};

struct thumbnail
{
QIcon icon;
QString path;
int index = 0;
};

QListWidget* m_grid = nullptr;

QFutureWatcher<thumbnail>* m_icon_loader;

QSize m_icon_size;
QIcon m_placeholder;

int m_scrollbar_value = 0;
};
3 changes: 2 additions & 1 deletion rpcs3/rpcs3qt/screenshot_preview.cpp
Expand Up @@ -27,13 +27,14 @@ screenshot_preview::screenshot_preview(const QString& filepath, QWidget* parent)

connect(this, &screenshot_preview::customContextMenuRequested, this, &screenshot_preview::show_context_menu);
}
#include <QDebug>

void screenshot_preview::show_context_menu(const QPoint & pos)
{
QMenu* menu = new QMenu();
menu->addAction(tr("&Copy"), [this]() { QGuiApplication::clipboard()->setImage(m_image); });
menu->addSeparator();
menu->addAction(tr("&Open file location"), [this]() { gui::utils::open_dir(m_filepath); });
menu->addSeparator();

QAction* reset_act = menu->addAction(tr("To &Normal Size"), [this]() { scale(m_image.size()); });
reset_act->setEnabled(pixmap()->size() != m_image.size());
Expand Down
2 changes: 1 addition & 1 deletion rpcs3/rpcs3qt/screenshot_preview.h
Expand Up @@ -11,7 +11,7 @@ class screenshot_preview : public QLabel
screenshot_preview(const QString& filepath, QWidget* parent = nullptr);

protected:
void resizeEvent(QResizeEvent* event);
void resizeEvent(QResizeEvent* event) override;

private Q_SLOTS:
void show_context_menu(const QPoint& pos);
Expand Down