From 0d287a60f12b5eca11be699debe4f90db4f4c2b2 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Sat, 28 Mar 2020 21:33:46 +0100 Subject: [PATCH] Draw blank squares for empty blocks in the grid. Import vectorwrapper, it's just too convenient. It's only used in one place for now but I will probably use it more from now on. --- .gitmodules | 3 ++ meson.build | 3 ++ src/qt/memoserv_win.cpp | 5 ++- src/qt/meson.build | 7 +++- src/qt/vec.hpp | 45 ++++++++++++++++++++++ src/qt/widgets/block_grid.cpp | 71 ++++++++++++++++++++++++++++------- src/qt/widgets/block_grid.hpp | 12 ++++-- subprojects/vectorwrapper | 1 + 8 files changed, 127 insertions(+), 20 deletions(-) create mode 100644 src/qt/vec.hpp create mode 160000 subprojects/vectorwrapper diff --git a/.gitmodules b/.gitmodules index 4251c0f..7b156e0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "subprojects/cxxopts"] path = subprojects/cxxopts url = https://github.com/jarro2783/cxxopts.git +[submodule "subprojects/vectorwrapper"] + path = subprojects/vectorwrapper + url = http://alarmpi.no-ip.org/gitan/King_DuckZ/vectorwrapper.git diff --git a/meson.build b/meson.build index 565b0a1..477cf9d 100644 --- a/meson.build +++ b/meson.build @@ -50,4 +50,7 @@ cxxopts_incl = include_directories('subprojects/cxxopts/include', '.') memcard_proj = subproject('memcard') memcard_dep = memcard_proj.get_variable('memcard_dep') +vectorwrapper_proj = subproject('vectorwrapper') +vectorwrapper_dep = vectorwrapper_proj.get_variable('vectorwrapper_dep') + subdir('src') diff --git a/src/qt/memoserv_win.cpp b/src/qt/memoserv_win.cpp index 17373e3..310f23d 100644 --- a/src/qt/memoserv_win.cpp +++ b/src/qt/memoserv_win.cpp @@ -100,10 +100,11 @@ void MemoservWin::open_file_from_dialog() { m_memcards.push_back(mc::psx::make_memory_card(file.toStdString())); auto& mc = m_memcards.back(); - auto grid = std::make_unique(this, 3, g_icon_fps); + auto grid = std::make_unique(this, 5, 3, g_icon_fps); + grid->set_icon_size(g_icon_size); 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)); + grid->push_back(make_qt_animation(block, g_icon_size, g_icon_size)); } } m_main_lay->addWidget(grid.release(), 1, m_grid_count); diff --git a/src/qt/meson.build b/src/qt/meson.build index 8fa4df4..b497834 100644 --- a/src/qt/meson.build +++ b/src/qt/meson.build @@ -38,6 +38,11 @@ executable(app_name, 'make_qt_animation.cpp', 'animated_pixmap.cpp', include_directories: inc, - dependencies: [qt5_dep, fslib_dep, memcard_dep], + dependencies: [ + qt5_dep, + fslib_dep, + memcard_dep, + vectorwrapper_dep + ], install: true, ) diff --git a/src/qt/vec.hpp b/src/qt/vec.hpp new file mode 100644 index 0000000..bd5924f --- /dev/null +++ b/src/qt/vec.hpp @@ -0,0 +1,45 @@ +/* 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::detail { +struct VecSimpleXYPoint { int x, y; }; +} //namespace duck::detail + +namespace vwr { +template <> +struct VectorWrapperInfo { + enum { dimensions = 2 }; + typedef int scalar_type; + typedef QPoint vector_type; + + static_assert(sizeof(duck::detail::VecSimpleXYPoint) == sizeof(QPoint), "Unexpected QPoint size, maybe you will have to provide get_at()"); + enum { + offset_x = offsetof(duck::detail::VecSimpleXYPoint, x), + offset_y = offsetof(duck::detail::VecSimpleXYPoint, y) + }; +}; +} //namespace vwr + +namespace duck { +typedef vwr::Vec qtint2; +} //namespace duck diff --git a/src/qt/widgets/block_grid.cpp b/src/qt/widgets/block_grid.cpp index cf71883..7513538 100644 --- a/src/qt/widgets/block_grid.cpp +++ b/src/qt/widgets/block_grid.cpp @@ -16,18 +16,36 @@ */ #include "block_grid.hpp" +#include "vec.hpp" #include #include +#include +#include #include namespace duck::widget { namespace { const constexpr int g_icon_spacing = 3; + + [[gnu::const]] + qtint2 scalar_to_point (int scalar, int max) { + return { + scalar % max, + scalar / max + }; + } + + [[gnu::const]] + qtint2 to_qtint2 (const QSize& sz) { + return {sz.width(), sz.height()}; + } } //unnamed namespace -BlockGrid::BlockGrid (QWidget* parent, unsigned int columns, float fps) : +BlockGrid::BlockGrid (QWidget* parent, unsigned int rows, unsigned int columns, float fps) : QWidget(parent), m_anim_timer(new QTimer(this)), + m_icon_size(-1, -1), + m_rows(rows), m_columns(columns) { fps = std::max(fps, 1.0f); @@ -43,9 +61,9 @@ QSize BlockGrid::minimumSizeHint() const { QSize BlockGrid::sizeHint() const { if (m_columns) { - 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); + const auto w = icon_size().width() * m_columns + g_icon_spacing * (1 + m_columns); + const unsigned int rows = (m_rows ? m_rows : size() / m_columns); + const auto h = icon_size().height() * rows + g_icon_spacing * (rows + 1); return {static_cast(w), static_cast(h)}; } @@ -54,12 +72,23 @@ QSize BlockGrid::sizeHint() const { } } -unsigned int BlockGrid::icon_width() const { - return m_icons.empty() ? static_cast(g_icon_spacing) : m_icons.front().size().width(); +void BlockGrid::set_icon_size (const QSize& sz) { + if (sz != m_icon_size) { + m_icon_size = sz; + m_empty_icon.reset(); + if (m_icon_size.isValid()) { + m_empty_icon = std::make_unique(m_icon_size); + m_empty_icon->fill(QColor(115, 115, 115, 255)); + } + } } -unsigned int BlockGrid::icon_height() const { - return m_icons.empty() ? static_cast(g_icon_spacing) : m_icons.front().size().height(); +void BlockGrid::set_icon_size (int sz) { + set_icon_size(QSize{sz, sz}); +} + +QSize BlockGrid::icon_size() const { + return m_icon_size.isValid() ? m_icon_size : QSize(g_icon_spacing, g_icon_spacing); } void BlockGrid::push_back (AnimatedPixmap&& anim) { @@ -70,6 +99,17 @@ std::size_t BlockGrid::size() const { return m_icons.size(); } +unsigned int BlockGrid::rows() const { + if (m_rows) { + return m_rows; + } + else { + const auto widget_h = static_cast(std::max(this->height(), g_icon_spacing)); + const auto ico_spacing_uns = static_cast(g_icon_spacing); + return (widget_h - ico_spacing_uns) / (icon_size().height() + ico_spacing_uns); + } +} + unsigned int BlockGrid::columns() const { if (m_columns) { return m_columns; @@ -78,26 +118,29 @@ unsigned int BlockGrid::columns() const { const auto widget_w = static_cast(std::max(this->width(), g_icon_spacing)); const auto ico_spacing_uns = static_cast(g_icon_spacing); - return (widget_w - ico_spacing_uns) / (icon_width() + ico_spacing_uns); + return (widget_w - ico_spacing_uns) / (icon_size().width() + ico_spacing_uns); } } void BlockGrid::paintEvent (QPaintEvent* event) { QPainter painter(this); const int max_col = static_cast(this->columns()); - int x = 0; - int y = 0; int count = 0; for (auto& ico : m_icons) { - x = g_icon_spacing + (count % max_col) * (g_icon_spacing + ico.size().width()); - y = g_icon_spacing + (count / max_col) * (g_icon_spacing + ico.size().height()); + auto point = g_icon_spacing + scalar_to_point(count, max_col) * (g_icon_spacing + to_qtint2(ico.size())); if (ico.frame_count()) { - painter.drawPixmap(x, y, ico.current_frame()); + painter.drawPixmap(point.x(), point.y(), ico.current_frame()); } ++count; } + + const int total_squares = static_cast(rows() * columns()); + for (int z = count; z < total_squares && m_empty_icon; ++z) { + auto point = g_icon_spacing + scalar_to_point(z, max_col) * (g_icon_spacing + to_qtint2(icon_size())); + painter.drawPixmap(point.x(), point.y(), *m_empty_icon); + } } void BlockGrid::advance_frame() { diff --git a/src/qt/widgets/block_grid.hpp b/src/qt/widgets/block_grid.hpp index f9c4649..cff29f6 100644 --- a/src/qt/widgets/block_grid.hpp +++ b/src/qt/widgets/block_grid.hpp @@ -19,6 +19,7 @@ #include "../animated_pixmap.hpp" #include +#include #include #include @@ -28,14 +29,15 @@ namespace duck::widget { class BlockGrid : public QWidget { Q_OBJECT public: - explicit BlockGrid (QWidget* parent=nullptr, unsigned int columns=0, float fps=24.0f); + explicit BlockGrid (QWidget* parent=nullptr, unsigned int rows=0, unsigned int columns=0, float fps=24.0f); ~BlockGrid() noexcept; QSize minimumSizeHint() const override; QSize sizeHint() const override; - unsigned int icon_width() const; - unsigned int icon_height() const; + void set_icon_size (const QSize& sz); + void set_icon_size (int sz); + QSize icon_size() const; void push_back (AnimatedPixmap&& anim); std::size_t size() const; @@ -47,10 +49,14 @@ private slots: void advance_frame(); private: + unsigned int rows() const; unsigned int columns() const; std::vector m_icons; QTimer* m_anim_timer; + std::unique_ptr m_empty_icon; + QSize m_icon_size; + unsigned int m_rows; unsigned int m_columns; }; } //namespace duck::widget diff --git a/subprojects/vectorwrapper b/subprojects/vectorwrapper new file mode 160000 index 0000000..0a90a28 --- /dev/null +++ b/subprojects/vectorwrapper @@ -0,0 +1 @@ +Subproject commit 0a90a289a2c354c88130dbf04a4c15c696e0a9de