From 4527caff5226b8d36c38313892de1329ab7d9900 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Sat, 28 Mar 2020 16:14:26 +0100 Subject: [PATCH] Catch up with the nana implementation. Allow users to open and display memory card files --- src/qt/make_qt_animation.cpp | 37 ++++++++++++++ src/qt/make_qt_animation.hpp | 35 +++++++++++++ src/qt/memoserv_win.cpp | 59 +++++++++++++++++++--- src/qt/memoserv_win.hpp | 12 ++++- src/qt/meson.build | 9 +++- src/qt/widgets/animated_icon.cpp | 78 +++++++++++++++++++++++++++++ src/qt/widgets/animated_icon.hpp | 59 ++++++++++++++++++++++ src/qt/widgets/block_grid.cpp | 86 ++++++++++++++++++++++++++++++++ src/qt/widgets/block_grid.hpp | 49 ++++++++++++++++++ 9 files changed, 414 insertions(+), 10 deletions(-) create mode 100644 src/qt/make_qt_animation.cpp create mode 100644 src/qt/make_qt_animation.hpp create mode 100644 src/qt/widgets/animated_icon.cpp create mode 100644 src/qt/widgets/animated_icon.hpp create mode 100644 src/qt/widgets/block_grid.cpp create mode 100644 src/qt/widgets/block_grid.hpp diff --git a/src/qt/make_qt_animation.cpp b/src/qt/make_qt_animation.cpp new file mode 100644 index 0000000..8250908 --- /dev/null +++ b/src/qt/make_qt_animation.cpp @@ -0,0 +1,37 @@ +/* Copyright 2020, Michele Santullo + * This file is part of memoserv. + * + * Memoserv is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Memoserv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Memoserv. If not, see . + */ + +#include "make_qt_animation.hpp" +#include "memcard/block.hpp" +#include "memcard/icon_fetch.hpp" +#include + +namespace duck { + std::unique_ptr make_qt_animation (const mc::psx::ConstBlock& block, int width, int height, float fps) { + using mc::icon_fetch; + + auto retval = std::make_unique(); + + for (const auto& frame : icon_fetch(block, width, height)) { + QPixmap pix(width, height); + pix.loadFromData(reinterpret_cast(frame.data()), frame.size(), "BMP"); + retval->push_back(std::move(pix)); + } + + return retval; + } +} //namespace duck diff --git a/src/qt/make_qt_animation.hpp b/src/qt/make_qt_animation.hpp new file mode 100644 index 0000000..d2e0c89 --- /dev/null +++ b/src/qt/make_qt_animation.hpp @@ -0,0 +1,35 @@ +/* Copyright 2020, Michele Santullo + * This file is part of memoserv. + * + * Memoserv is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Memoserv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Memoserv. If not, see . + */ + +#pragma once + +#include "widgets/animated_icon.hpp" +#include + +namespace mc::psx { +template class BasicBlock; +using ConstBlock = BasicBlock; +} //namespace mc::psx + +namespace duck { + std::unique_ptr make_qt_animation ( + const mc::psx::ConstBlock& block, + int width, + int height, + float fps + ); +} //namespace duck diff --git a/src/qt/memoserv_win.cpp b/src/qt/memoserv_win.cpp index 05d0dbf..f8bb94f 100644 --- a/src/qt/memoserv_win.cpp +++ b/src/qt/memoserv_win.cpp @@ -18,19 +18,36 @@ #include "memoserv_win.hpp" #include "app_config.h" #include "version_info_win.hpp" +#include "widgets/block_grid.hpp" +#include "memcard/memorycard.hpp" +#include "memcard/make_memory_card.hpp" +#include "make_qt_animation.hpp" #include #include #include - -#include +#include +#include +#include +#include +#include class QApplication; namespace duck { +namespace { + const constexpr int g_icon_size = 48; + const constexpr float g_icon_fps = 3.0f; +} //unnamed namespace MemoservWin::MemoservWin(QApplication* app, QWidget *parent) : - QMainWindow(parent) + QMainWindow(parent), + m_main_lay(nullptr), + m_grid_count(0) { + m_main_lay = new QGridLayout; + this->setCentralWidget(new QWidget); + this->centralWidget()->setLayout(m_main_lay); + create_menu(app); } @@ -42,7 +59,7 @@ void MemoservWin::create_menu(QApplication* app) { const QIcon open_icon = QIcon::fromTheme("document-open", QIcon(":/images/open.png")); const auto open_act = new QAction(open_icon, tr("&Open..."), this); open_act->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_O)); - connect(open_act, &QAction::triggered, this, &MemoservWin::open); + connect(open_act, &QAction::triggered, this, &MemoservWin::open_file_from_dialog); file_menu->addAction(open_act); } { @@ -64,12 +81,40 @@ void MemoservWin::create_menu(QApplication* app) { } } -void MemoservWin::open() { - std::cout << "MemoservWin::open()\n"; +void MemoservWin::open_file_from_dialog() { + using std::filesystem::path; + + QStringList files = QFileDialog::getOpenFileNames( + this, + tr("Open memory card"), + QString(), + tr("PSX Memory Card") + " (*.mcr *.sav *.srm);;" + + tr("All files") + " (*.*)" + ); + for (const auto& file : files) { + auto lbl = std::make_unique(); + + lbl->setText(QString::fromStdString(path(file.toStdString()).filename())); + m_main_lay->setColumnStretch(m_grid_count, 0); + m_main_lay->addWidget(lbl.release(), 0, m_grid_count); + + m_memcards.push_back(mc::psx::make_memory_card(file.toStdString())); + auto& mc = m_memcards.back(); + auto grid = std::make_unique(this, 3); + for (const auto& block : mc) { + for (std::size_t n = 0; block.has_magic() and n < block.block_count(); ++n) { + grid->emplace_back(make_qt_animation(block, g_icon_size, g_icon_size, g_icon_fps)); + } + } + m_main_lay->addWidget(grid.release(), 1, m_grid_count); + + ++m_grid_count; + } + + m_main_lay->setColumnStretch(m_grid_count, 1); } void MemoservWin::show_version_info() { - std::cout << "MemoservWin::show_version_info()\n"; VersionInfoWin vinfo(this); vinfo.setWindowModality(Qt::WindowModality::WindowModal); vinfo.exec(); diff --git a/src/qt/memoserv_win.hpp b/src/qt/memoserv_win.hpp index c616d83..ad1c320 100644 --- a/src/qt/memoserv_win.hpp +++ b/src/qt/memoserv_win.hpp @@ -18,8 +18,14 @@ #pragma once #include +#include class QApplication; +class QGridLayout; + +namespace mc::psx { +class MemoryCard; +} //namespace mc::psx namespace duck { @@ -35,7 +41,11 @@ private: void create_menu(QApplication*); void show_version_info(); - void open(); + void open_file_from_dialog(); + + std::list m_memcards; + QGridLayout* m_main_lay; + unsigned int m_grid_count; }; } //namespace duck diff --git a/src/qt/meson.build b/src/qt/meson.build index ac64678..a4cb59e 100644 --- a/src/qt/meson.build +++ b/src/qt/meson.build @@ -3,11 +3,13 @@ fslib_dep = cpp.find_library('stdc++fs', required: false) qt5 = import('qt5') qt5_dep = dependency('qt5', modules: ['Core', 'Gui', 'Widgets'], version: '>=5.14') -inc = include_directories('includes') +inc = include_directories('.') moc_files = qt5.preprocess( moc_headers: [ 'memoserv_win.hpp', 'version_info_win.hpp', + 'widgets/block_grid.hpp', + 'widgets/animated_icon.hpp', ], #ui_files: 'main_window.ui', moc_extra_arguments: ['-DMAKES_MY_MOC_HEADER_COMPILE'], @@ -33,7 +35,10 @@ executable(app_name, config_file, project_config_file, gitrev_config_file, + 'widgets/block_grid.cpp', + 'widgets/animated_icon.cpp', + 'make_qt_animation.cpp', include_directories: inc, - dependencies: [qt5_dep, fslib_dep], + dependencies: [qt5_dep, fslib_dep, memcard_dep], install: true, ) diff --git a/src/qt/widgets/animated_icon.cpp b/src/qt/widgets/animated_icon.cpp new file mode 100644 index 0000000..d374a1e --- /dev/null +++ b/src/qt/widgets/animated_icon.cpp @@ -0,0 +1,78 @@ +/* Copyright 2020, Michele Santullo + * This file is part of memoserv. + * + * Memoserv is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Memoserv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Memoserv. If not, see . + */ + +#include "animated_icon.hpp" +#include +#include +#include + +namespace duck::widget { +namespace { +} //unnamed namespace + +AnimatedIcon::AnimatedIcon (QWidget* parent, float fps, bool loop) : + QWidget(parent), + m_frame(0), + m_fps(fps), + m_loop(loop) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +} + +AnimatedIcon::~AnimatedIcon() noexcept = default; + +QSize AnimatedIcon::minimumSizeHint() const { + return {static_cast(width()), static_cast(height())}; +} + +QSize AnimatedIcon::sizeHint() const { + return {static_cast(width()), static_cast(height())}; +} + +void AnimatedIcon::push_back (QPixmap&& frame) { + m_frames.push_back(std::move(frame)); +} + +std::size_t AnimatedIcon::size() const { + return m_frames.size(); +} + +unsigned int AnimatedIcon::width() const { + return (m_frames.empty() ? EmptyIconWidth : m_frames.front().width()); +} + +unsigned int AnimatedIcon::height() const { + return (m_frames.empty() ? EmptyIconHeight : m_frames.front().height()); +} + +void AnimatedIcon::advance_frame() { + ++m_frame; + if (m_loop) + m_frame = (not m_frames.empty() ? m_frame % m_frames.size() : 0); + else + m_frame = std::min(m_frame, m_frames.size()); + update(); +} + +void AnimatedIcon::paintEvent (QPaintEvent* event) { + if (m_frame < m_frames.size()) { + QPainter painter(this); + painter.drawPixmap(0, 0, m_frames[m_frame]); + } +} + +} //namespace duck::widget diff --git a/src/qt/widgets/animated_icon.hpp b/src/qt/widgets/animated_icon.hpp new file mode 100644 index 0000000..ec54a28 --- /dev/null +++ b/src/qt/widgets/animated_icon.hpp @@ -0,0 +1,59 @@ +/* Copyright 2020, Michele Santullo + * This file is part of memoserv. + * + * Memoserv is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Memoserv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Memoserv. If not, see . + */ + +#pragma once + +#include +#include +#include + +namespace duck::widget { +class AnimatedIcon : public QWidget { + Q_OBJECT +public: + static const constexpr unsigned int EmptyIconWidth = 50; + static const constexpr unsigned int EmptyIconHeight = EmptyIconWidth; + + AnimatedIcon (AnimatedIcon&&) = default; + AnimatedIcon (const AnimatedIcon&) = delete; + explicit AnimatedIcon (QWidget* parent=nullptr, float fps=24.0f, bool loop=true); + ~AnimatedIcon() noexcept; + + AnimatedIcon& operator= (AnimatedIcon&&) = default; + + QSize minimumSizeHint() const override; + QSize sizeHint() const override; + + void push_back (QPixmap&& frame); + std::size_t size() const; + + unsigned int width() const; + unsigned int height() const; + +public slots: + void advance_frame(); + +protected: + void paintEvent (QPaintEvent* event) override; + +private: + std::vector m_frames; + std::size_t m_frame; + float m_fps; + bool m_loop; +}; +} //namespace duck::widget diff --git a/src/qt/widgets/block_grid.cpp b/src/qt/widgets/block_grid.cpp new file mode 100644 index 0000000..db89d6d --- /dev/null +++ b/src/qt/widgets/block_grid.cpp @@ -0,0 +1,86 @@ +/* Copyright 2020, Michele Santullo + * This file is part of memoserv. + * + * Memoserv is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Memoserv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Memoserv. If not, see . + */ + +#include "block_grid.hpp" +#include +#include + +namespace duck::widget { +namespace { + const constexpr int g_icon_spacing = 3; +} //unnamed namespace + +BlockGrid::BlockGrid (QWidget* parent) : + BlockGrid(parent, 3) +{ +} + +BlockGrid::BlockGrid (QWidget* parent, unsigned int columns) : + QWidget(parent), + m_columns(columns), + m_block_lay(nullptr) +{ + auto block_lay = std::make_unique(); + block_lay->setSpacing(g_icon_spacing); + m_block_lay = block_lay.get(); + m_block_lay->setColumnStretch(m_columns, 1); + this->setLayout(block_lay.release()); +} + +BlockGrid::~BlockGrid() noexcept = default; + +QSize BlockGrid::minimumSizeHint() const { + return sizeHint(); +} + +QSize BlockGrid::sizeHint() const { + const auto w = icon_width() * m_columns + g_icon_spacing * (1 + m_columns); + const unsigned int rows = size() / m_columns; + const auto h = icon_height() * rows + g_icon_spacing * (rows + 1); + + return {static_cast(w), static_cast(h)}; +} + +unsigned int BlockGrid::icon_width() const { + return m_icons.empty() ? AnimatedIcon::EmptyIconWidth : m_icons.front()->width(); +} + +unsigned int BlockGrid::icon_height() const { + return m_icons.empty() ? AnimatedIcon::EmptyIconHeight : m_icons.front()->height(); +} + +void BlockGrid::emplace_back (std::unique_ptr&& anim) { + const auto& columns = m_columns; + const auto count = this->size(); + + const int x = count % columns; + const int y = count / columns; + + { + const auto raw_ptr = anim.release(); + m_block_lay->addWidget(raw_ptr, y, x); + m_icons.push_back(raw_ptr); + } + + m_block_lay->setRowStretch(y, 0); + m_block_lay->setRowStretch(y + 1, 1); +} + +std::size_t BlockGrid::size() const { + return m_icons.size(); +} +} //namespace duck::widget diff --git a/src/qt/widgets/block_grid.hpp b/src/qt/widgets/block_grid.hpp new file mode 100644 index 0000000..ebf25ee --- /dev/null +++ b/src/qt/widgets/block_grid.hpp @@ -0,0 +1,49 @@ +/* Copyright 2020, Michele Santullo + * This file is part of memoserv. + * + * Memoserv is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Memoserv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Memoserv. If not, see . + */ + +#pragma once + +#include "animated_icon.hpp" +#include +#include +#include + +class QGridLayout; + +namespace duck::widget { +class BlockGrid : public QWidget { + Q_OBJECT +public: + explicit BlockGrid (QWidget* parent=nullptr); + BlockGrid (QWidget* parent, unsigned int columns); + ~BlockGrid() noexcept; + + QSize minimumSizeHint() const override; + QSize sizeHint() const override; + + unsigned int icon_width() const; + unsigned int icon_height() const; + + void emplace_back (std::unique_ptr&& anim); + std::size_t size() const; + +private: + std::vector m_icons; + unsigned int m_columns; + QGridLayout* m_block_lay; +}; +} //namespace duck::widget