From 6b835cd1d9cf79024499e55dc8369e542eb8d3f6 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Mon, 24 Feb 2025 18:58:15 +0000 Subject: [PATCH] Drag and drop work in progress implementation --- meson.build | 2 +- src/qt/memoserv_win.cpp | 3 +- src/qt/meson.build | 1 + src/qt/savegame_db.cpp | 56 ++++++--- src/qt/savegame_db.hpp | 112 ++++++++++++++++-- src/qt/savegame_model.cpp | 105 ++++++++++++++++ src/qt/savegame_model.hpp | 89 ++++++++++++++ src/qt/widgets/block_grid.cpp | 5 +- src/qt/widgets/block_grid.hpp | 7 +- src/qt/widgets/detail/block_drag_and_drop.cpp | 21 +++- subprojects/memcard/meson.build | 2 +- 11 files changed, 370 insertions(+), 33 deletions(-) create mode 100644 src/qt/savegame_model.cpp create mode 100644 src/qt/savegame_model.hpp diff --git a/meson.build b/meson.build index 4a14ec9..20dda0f 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project('memoserv', 'cpp', version: '0.1.0', meson_version: '>=0.63.0', - default_options: ['debug=true', 'cpp_std=c++17', 'b_ndebug=if-release'] + default_options: ['debug=true', 'cpp_std=c++20', 'b_ndebug=if-release'] ) is_debug_build = 0 diff --git a/src/qt/memoserv_win.cpp b/src/qt/memoserv_win.cpp index 351b9c2..3ffc9fb 100644 --- a/src/qt/memoserv_win.cpp +++ b/src/qt/memoserv_win.cpp @@ -135,8 +135,7 @@ void MemoservWin::load_memory_cards (const QStringList& paths) { } for (std::size_t n = 0; block.has_magic() and n < block.block_count(); ++n) { - grid->push_back(make_qt_animation(block, g_icon_size, g_icon_size)); - m_lower_grid->push_back(make_qt_animation(block, g_icon_size, g_icon_size)); + //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 59a3672..182a729 100644 --- a/src/qt/meson.build +++ b/src/qt/meson.build @@ -39,6 +39,7 @@ executable(app_name, 'make_qt_animation.cpp', 'animated_pixmap.cpp', 'savegame_db.cpp', + 'savegame_model.cpp', include_directories: inc, dependencies: [ qt6_dep, diff --git a/src/qt/savegame_db.cpp b/src/qt/savegame_db.cpp index b0ce17c..994ae08 100644 --- a/src/qt/savegame_db.cpp +++ b/src/qt/savegame_db.cpp @@ -16,7 +16,7 @@ */ #include "savegame_db.hpp" -#include "memcard/block.hpp" +#include "memcard/alignment.hpp" #include #include #include @@ -24,21 +24,24 @@ #include namespace duck { +namespace { +} //unnamed namespace + BlockEntry::BlockEntry ( - std::vector>&& blocks, - std::unique_ptr&& data + std::unique_ptr&& data, + std::size_t count ) : - blocks(std::move(blocks)), - data(std::move(data)) + data(std::move(data)), + block_count(count) { } std::string_view BlockEntry::identifier() const { - return blocks.front().identifier(); + return make_block(0).identifier(); } std::string BlockEntry::title() const { - return blocks.front().title(); + return make_block(0).title(); } SavegameDb::SavegameDb() = default; @@ -52,31 +55,52 @@ unsigned int SavegameDb::add (const std::vector >& bl const auto block_count = blocks.size(); assert(block_count == blocks.front().block_count()); - auto mem = std::make_unique(frame_size + block_size * block_count); - uint8_t* dest = std::copy(blocks.front().toc().cbegin(), blocks.front().toc().cend(), mem.get()); - assert(std::distance(mem.get(), dest) == frame_size); - mc::psx::Frame toc(mem.get(), 0); + static_assert( + frame_size % mc::psx::Block::DataAlignment == 0, + "Unexpected alignment, alignment in this class will be wrong if you ignore this error" + ); + + auto mem = mc::psx::make_unique_for_block_aligned(frame_size + block_size * block_count); + + std::uint8_t* const ptr = mc::psx::to_block_aligned(mem.get()); + uint8_t* dest = std::copy(blocks.front().toc().cbegin(), blocks.front().toc().cend(), ptr); + assert(mc::psx::is_block_aligned(dest)); + assert(std::distance(ptr, dest) == frame_size); + mc::psx::Frame toc(ptr, 0); - std::vector> new_blocks; - new_blocks.reserve(block_count); - std::size_t index = 0; for (const auto& block : blocks) { assert(block_count == block.block_count() or block.block_count() == 0); uint8_t* const curr_block_ptr = dest; dest = std::copy(block.data(), block.data() + block_size, dest); - new_blocks.emplace_back(curr_block_ptr, toc, index++); } const std::size_t new_id = m_next_id++; m_saves.emplace( std::piecewise_construct, std::forward_as_tuple(new_id), - std::forward_as_tuple(std::move(new_blocks), std::move(mem)) + std::forward_as_tuple(std::move(mem), block_count) ); + m_block_count += block_count; return static_cast(m_saves.size() - 1u); } +std::size_t SavegameDb::savegame_count() const { + return m_saves.size(); +} + +std::size_t SavegameDb::block_count() const { + return m_block_count; +} + +auto SavegameDb::begin() -> iterator { + return {m_saves.begin()}; +} + +auto SavegameDb::end() -> iterator { + return {m_saves.end()}; +} + template unsigned int SavegameDb::add (const std::vector >&); template unsigned int SavegameDb::add (const std::vector >&); } //namespace duck diff --git a/src/qt/savegame_db.hpp b/src/qt/savegame_db.hpp index 66b42ec..6a01508 100644 --- a/src/qt/savegame_db.hpp +++ b/src/qt/savegame_db.hpp @@ -17,41 +17,135 @@ #pragma once +#include "memcard/alignment.hpp" +#include "memcard/block.hpp" #include #include #include #include #include #include - -namespace mc::psx { -template class BasicBlock; -} //namespace mc::psx +#include +#include +#include +#include +#include namespace duck { struct BlockEntry { BlockEntry ( - std::vector >&& blocks, - std::unique_ptr&& data + std::unique_ptr&& data, + std::size_t count ); + template + mc::psx::BasicBlock make_block (std::size_t index) const; std::string_view identifier() const; std::string title() const; + std::uint8_t* data_ptr() const; - std::vector > blocks; - std::unique_ptr data; + std::unique_ptr data; + std::size_t block_count; }; class SavegameDb { + typedef std::map SavegameMap; public: + template + class BlockIterator { + It m_it; + std::size_t m_pos_in_it {0}; + + public: + typedef typename std::iterator_traits::difference_type difference_type; + typedef mc::psx::BasicBlock::value> value_type; + typedef std::add_pointer::type pointer; + typedef value_type reference; + typedef mc::psx::ConstBlock const_reference; + typedef typename std::iterator_traits::iterator_category iterator_category; + + BlockIterator() = default; + BlockIterator(const BlockIterator&) = default; + BlockIterator(BlockIterator&&) = default; + BlockIterator (It&& it) : m_it(std::move(it)) {} + BlockIterator (const It& it) : m_it(it) {} + + bool operator!=(const BlockIterator& other) const + {return m_it!=other.m_it or m_pos_in_it!=other.m_pos_in_it;} + bool operator==(const BlockIterator& other) const + {return m_it==other.m_it and m_pos_in_it==other.m_pos_in_it;} + + BlockIterator& operator++(); + BlockIterator operator++(int); + reference operator*(); + const_reference operator*() const; + }; + + typedef BlockIterator iterator; + typedef BlockIterator const_iterator; + SavegameDb(); ~SavegameDb() noexcept; template unsigned int add (const std::vector >& blocks); + std::size_t savegame_count() const; + std::size_t block_count() const; + + std::size_t size() const { return block_count(); } + iterator begin(); + iterator end(); private: - std::map m_saves; + SavegameMap m_saves; std::size_t m_next_id{0}; + std::size_t m_block_count{0}; }; + +template +mc::psx::BasicBlock BlockEntry::make_block (std::size_t index) const { + using mc::psx::to_block_aligned; + using mc::psx::Frame; + using FrameType = typename mc::psx::BasicBlock::FrameType; + + assert(this->block_count > 0); + assert(this->block_count > index); + + constexpr std::size_t frame_index = 0; + return { + to_block_aligned(this->data.get(), index, Frame::Size), + FrameType{to_block_aligned(this->data.get()), frame_index}, + index + }; +} + +template +SavegameDb::BlockIterator& SavegameDb::BlockIterator::operator++() { + const BlockEntry& entry = m_it->second; + ++m_pos_in_it; + if (m_pos_in_it >= entry.block_count) { + ++m_it; + m_pos_in_it = 0; + } + return *this; +} + +template +SavegameDb::BlockIterator SavegameDb::BlockIterator::operator++(int) { + auto retval = *this; + this->operator++(); + return retval; +} + +template +auto SavegameDb::BlockIterator::operator*() -> reference { + BlockEntry& entry = m_it->second; + return entry.make_block(m_pos_in_it); +} + +template +auto SavegameDb::BlockIterator::operator*() const -> const_reference { + BlockEntry& entry = m_it->second; + return entry.make_block(m_pos_in_it); +} } //namespace duck diff --git a/src/qt/savegame_model.cpp b/src/qt/savegame_model.cpp new file mode 100644 index 0000000..71fb38f --- /dev/null +++ b/src/qt/savegame_model.cpp @@ -0,0 +1,105 @@ +/* Copyright 2020-2025, 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 "savegame_model.hpp" +#include "savegame_db.hpp" +#include +#include +#include + +namespace duck { +SavegameModel::SavegameModel() = default; +SavegameModel::~SavegameModel() noexcept = default; + +MemoryCardSavegameModel::MemoryCardSavegameModel (mc::psx::MemoryCard* memcard) : + m_mc(memcard) +{ + assert(m_mc); +} + +SavegameModel::iterator MemoryCardSavegameModel::begin() { + return {m_mc->begin()}; +} + +SavegameModel::iterator MemoryCardSavegameModel::end() { + return {m_mc->end()}; +} + +std::size_t MemoryCardSavegameModel::block_count() const { + return m_mc->size(); +} + +bool MemoryCardSavegameModel::has_toc() const { + return true; +} + +mc::psx::Block MemoryCardSavegameModel::toc() { + return m_mc->toc_block(); +} + +mc::psx::ConstBlock MemoryCardSavegameModel::toc() const { + return m_mc->toc_block(); +} + +mc::psx::Block MemoryCardSavegameModel::block (std::size_t index) { + return (*m_mc)[index]; +} + +mc::psx::ConstBlock MemoryCardSavegameModel::block (std::size_t index) const { + return (*m_mc)[index]; +} + +DbSavegameModel::DbSavegameModel (SavegameDb* db) : + m_db(db) +{ + assert(m_db); +} + +SavegameModel::iterator DbSavegameModel::begin() { + return {m_db->begin()}; +} + +SavegameModel::iterator DbSavegameModel::end() { + return {m_db->end()}; +} + +std::size_t DbSavegameModel::block_count() const { + return m_db->block_count(); +} + +bool DbSavegameModel::has_toc() const { + return false; +} + +mc::psx::Block DbSavegameModel::toc() { + throw std::runtime_error("TOC not available in SavegameDb"); +} + +mc::psx::ConstBlock DbSavegameModel::toc() const { + throw std::runtime_error("TOC not available in SavegameDb"); +} + +mc::psx::Block DbSavegameModel::block (std::size_t index) { + auto it = begin(); + std::advance(it, index); + return *it; +} + +mc::psx::ConstBlock DbSavegameModel::block (std::size_t index) const { +} + +} //namespace duck diff --git a/src/qt/savegame_model.hpp b/src/qt/savegame_model.hpp new file mode 100644 index 0000000..4aae7aa --- /dev/null +++ b/src/qt/savegame_model.hpp @@ -0,0 +1,89 @@ +/* Copyright 2020-2025, 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 "memcard/memorycard.hpp" +#include "any_iterator.hpp" +#include + +namespace mc::psx { +template class BasicBlock; +}; + +namespace duck { +class SavegameDb; + +class SavegameModel { +public: + typedef liph::any_forward_iterator< + mc::psx::BasicBlock, + mc::psx::BasicBlock + > iterator; + + SavegameModel(); + virtual ~SavegameModel() noexcept; + + std::size_t size() const { return this->block_count(); } + + virtual iterator begin() = 0; + virtual iterator end() = 0; + virtual std::size_t block_count() const = 0; + virtual bool has_toc() const = 0; + virtual mc::psx::Block toc() = 0; + virtual mc::psx::ConstBlock toc() const = 0; + virtual mc::psx::Block block (std::size_t index) = 0; + virtual mc::psx::ConstBlock block (std::size_t index) const = 0; +private: +}; + +class MemoryCardSavegameModel : public SavegameModel { +public: + explicit MemoryCardSavegameModel (mc::psx::MemoryCard* memcard); + virtual ~MemoryCardSavegameModel() noexcept = default; + + SavegameModel::iterator begin() override; + SavegameModel::iterator end() override; + std::size_t block_count() const override; + bool has_toc() const override; + mc::psx::Block toc() override; + mc::psx::ConstBlock toc() const override; + mc::psx::Block block (std::size_t index) override; + mc::psx::ConstBlock block (std::size_t index) const override; + +private: + mc::psx::MemoryCard* m_mc; +}; + +class DbSavegameModel : public SavegameModel { +public: + explicit DbSavegameModel (SavegameDb* db); + virtual ~DbSavegameModel() noexcept = default; + + SavegameModel::iterator begin() override; + SavegameModel::iterator end() override; + std::size_t block_count() const override; + bool has_toc() const override; + mc::psx::Block toc() override; + mc::psx::ConstBlock toc() const override; + mc::psx::Block block (std::size_t index) override; + mc::psx::ConstBlock block (std::size_t index) const override; + +private: + SavegameDb* m_db; +}; +} //namespace duck diff --git a/src/qt/widgets/block_grid.cpp b/src/qt/widgets/block_grid.cpp index 9723e0a..cbbe98a 100644 --- a/src/qt/widgets/block_grid.cpp +++ b/src/qt/widgets/block_grid.cpp @@ -17,6 +17,7 @@ #include "block_grid.hpp" #include "vec.hpp" +#include "savegame_model.hpp" #include #include #include @@ -97,8 +98,8 @@ 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) { - m_icons.push_back(std::move(anim)); +void BlockGrid::set_savegames (std::unique_ptr&& savegames) { + m_savegames = std::move(savegames); } std::size_t BlockGrid::size() const { diff --git a/src/qt/widgets/block_grid.hpp b/src/qt/widgets/block_grid.hpp index 613a92d..cc94aaa 100644 --- a/src/qt/widgets/block_grid.hpp +++ b/src/qt/widgets/block_grid.hpp @@ -27,6 +27,10 @@ class QTimer; +namespace duck { +class SavegameModel; +} //namespace duck + namespace duck::widget { class BlockGrid : public QWidget { Q_OBJECT @@ -41,7 +45,7 @@ public: void set_icon_size (int sz); QSize icon_size() const; - void push_back (AnimatedPixmap&& anim); + void set_savegames (std::unique_ptr&& savegames); std::size_t size() const; protected: @@ -65,6 +69,7 @@ private: QSize m_icon_size; QTimer* m_anim_timer; std::unique_ptr m_empty_icon; + std::unique_ptr m_savegames; unsigned int m_rows; unsigned int m_columns; }; diff --git a/src/qt/widgets/detail/block_drag_and_drop.cpp b/src/qt/widgets/detail/block_drag_and_drop.cpp index ae73cf0..f297380 100644 --- a/src/qt/widgets/detail/block_drag_and_drop.cpp +++ b/src/qt/widgets/detail/block_drag_and_drop.cpp @@ -117,10 +117,29 @@ void BlockDragAndDrop::mouse_move_event (const QMouseEvent* event) { } void BlockDragAndDrop::drag_enter_event(QDragEnterEvent* event) { - if (not m_dragging and event->mimeData()->hasFormat(g_drag_mime)) + const QMimeData* const mime = event->mimeData(); + if (not m_dragging and mime->hasFormat(g_drag_mime)) event->acceptProposedAction(); + else if (mime->hasUrls()) + event->acceptProposedAction(); + else { + std::cout << "drag_enter rejected:"; + for (const auto& str : mime->formats()) { + std::cout << ' ' << str.toStdString(); + } + std::cout << '\n'; + } } void BlockDragAndDrop::drop_event(QDropEvent* event) { + if (event->mimeData()->hasUrls()) { + for (const auto& url : event->mimeData()->urls()) { + if (url.isLocalFile()) + std::cout << "would accept " << url.toLocalFile().toStdString(); + else + std::cout << "rejecting " << url.toString().toStdString(); + std::cout << '\n'; + } + } } } //namespace duck::widget::detail diff --git a/subprojects/memcard/meson.build b/subprojects/memcard/meson.build index 1e630b7..248793b 100644 --- a/subprojects/memcard/meson.build +++ b/subprojects/memcard/meson.build @@ -1,4 +1,4 @@ -project('memcard', 'cpp', 'c', default_options:['debug=true', 'cpp_std=c++17', 'b_ndebug=if-release']) +project('memcard', 'cpp', 'c', default_options:['debug=true', 'cpp_std=c++20', 'b_ndebug=if-release']) add_project_link_arguments(['-lstdc++fs'], language: 'cpp') private_incl = include_directories('src')