Drag and drop work in progress implementation

This commit is contained in:
King_DuckZ 2025-02-24 18:58:15 +00:00
parent 7af7ce540d
commit 6b835cd1d9
11 changed files with 370 additions and 33 deletions

View file

@ -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

View file

@ -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);

View file

@ -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,

View file

@ -16,7 +16,7 @@
*/
#include "savegame_db.hpp"
#include "memcard/block.hpp"
#include "memcard/alignment.hpp"
#include <algorithm>
#include <ciso646>
#include <iterator>
@ -24,21 +24,24 @@
#include <tuple>
namespace duck {
namespace {
} //unnamed namespace
BlockEntry::BlockEntry (
std::vector<mc::psx::BasicBlock<false>>&& blocks,
std::unique_ptr<uint8_t[]>&& data
std::unique_ptr<uint8_t[]>&& 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<true>(0).identifier();
}
std::string BlockEntry::title() const {
return blocks.front().title();
return make_block<true>(0).title();
}
SavegameDb::SavegameDb() = default;
@ -52,31 +55,52 @@ unsigned int SavegameDb::add (const std::vector<mc::psx::BasicBlock<Const> >& bl
const auto block_count = blocks.size();
assert(block_count == blocks.front().block_count());
auto mem = std::make_unique<uint8_t[]>(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<mc::psx::BasicBlock<false>> 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<unsigned int>(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<true> (const std::vector<mc::psx::BasicBlock<true> >&);
template unsigned int SavegameDb::add<false> (const std::vector<mc::psx::BasicBlock<false> >&);
} //namespace duck

View file

@ -17,41 +17,135 @@
#pragma once
#include "memcard/alignment.hpp"
#include "memcard/block.hpp"
#include <cstdint>
#include <vector>
#include <memory>
#include <string_view>
#include <string>
#include <map>
namespace mc::psx {
template <bool C> class BasicBlock;
} //namespace mc::psx
#include <utility>
#include <iterator>
#include <cassert>
#include <ciso646>
#include <type_traits>
namespace duck {
struct BlockEntry {
BlockEntry (
std::vector<mc::psx::BasicBlock<false> >&& blocks,
std::unique_ptr<uint8_t[]>&& data
std::unique_ptr<uint8_t[]>&& data,
std::size_t count
);
template <bool C>
mc::psx::BasicBlock<C> make_block (std::size_t index) const;
std::string_view identifier() const;
std::string title() const;
std::uint8_t* data_ptr() const;
std::vector<mc::psx::BasicBlock<false> > blocks;
std::unique_ptr<uint8_t[]> data;
std::unique_ptr<std::uint8_t[]> data;
std::size_t block_count;
};
class SavegameDb {
typedef std::map<unsigned int, BlockEntry> SavegameMap;
public:
template <typename It>
class BlockIterator {
It m_it;
std::size_t m_pos_in_it {0};
public:
typedef typename std::iterator_traits<It>::difference_type difference_type;
typedef mc::psx::BasicBlock<std::is_const<SavegameMap::mapped_type>::value> value_type;
typedef std::add_pointer<value_type>::type pointer;
typedef value_type reference;
typedef mc::psx::ConstBlock const_reference;
typedef typename std::iterator_traits<It>::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<SavegameMap::iterator> iterator;
typedef BlockIterator<SavegameMap::const_iterator> const_iterator;
SavegameDb();
~SavegameDb() noexcept;
template <bool Const>
unsigned int add (const std::vector<mc::psx::BasicBlock<Const> >& 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<unsigned int, BlockEntry> m_saves;
SavegameMap m_saves;
std::size_t m_next_id{0};
std::size_t m_block_count{0};
};
template <bool C>
mc::psx::BasicBlock<C> BlockEntry::make_block (std::size_t index) const {
using mc::psx::to_block_aligned;
using mc::psx::Frame;
using FrameType = typename mc::psx::BasicBlock<C>::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 <typename It>
SavegameDb::BlockIterator<It>& SavegameDb::BlockIterator<It>::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 <typename It>
SavegameDb::BlockIterator<It> SavegameDb::BlockIterator<It>::operator++(int) {
auto retval = *this;
this->operator++();
return retval;
}
template <typename It>
auto SavegameDb::BlockIterator<It>::operator*() -> reference {
BlockEntry& entry = m_it->second;
return entry.make_block<value_type::IsConst>(m_pos_in_it);
}
template <typename It>
auto SavegameDb::BlockIterator<It>::operator*() const -> const_reference {
BlockEntry& entry = m_it->second;
return entry.make_block<value_type::IsConst>(m_pos_in_it);
}
} //namespace duck

105
src/qt/savegame_model.cpp Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "savegame_model.hpp"
#include "savegame_db.hpp"
#include <cassert>
#include <stdexcept>
#include <ciso646>
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

89
src/qt/savegame_model.hpp Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "memcard/memorycard.hpp"
#include "any_iterator.hpp"
#include <cstddef>
namespace mc::psx {
template <bool> class BasicBlock;
};
namespace duck {
class SavegameDb;
class SavegameModel {
public:
typedef liph::any_forward_iterator<
mc::psx::BasicBlock<false>,
mc::psx::BasicBlock<false>
> 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

View file

@ -17,6 +17,7 @@
#include "block_grid.hpp"
#include "vec.hpp"
#include "savegame_model.hpp"
#include <QPainter>
#include <QTimer>
#include <QPixmap>
@ -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<duck::SavegameModel>&& savegames) {
m_savegames = std::move(savegames);
}
std::size_t BlockGrid::size() const {

View file

@ -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<duck::SavegameModel>&& savegames);
std::size_t size() const;
protected:
@ -65,6 +69,7 @@ private:
QSize m_icon_size;
QTimer* m_anim_timer;
std::unique_ptr<QPixmap> m_empty_icon;
std::unique_ptr<duck::SavegameModel> m_savegames;
unsigned int m_rows;
unsigned int m_columns;
};

View file

@ -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

View file

@ -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')