Add basic marker support
This commit is contained in:
parent
11f1cbdb84
commit
1310886609
426
include/jbcoe/polymorphic_value.h
Normal file
426
include/jbcoe/polymorphic_value.h
Normal file
@ -0,0 +1,426 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 2016 Jonathan B. Coe
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef JBCOE_POLYMORPHIC_VALUE_H_INCLUDED
|
||||||
|
#define JBCOE_POLYMORPHIC_VALUE_H_INCLUDED
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <exception>
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
|
namespace jbcoe
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Implementation detail classes
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct default_copy
|
||||||
|
{
|
||||||
|
T* operator()(const T& t) const
|
||||||
|
{
|
||||||
|
return new T(t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct default_delete
|
||||||
|
{
|
||||||
|
void operator()(const T* t) const
|
||||||
|
{
|
||||||
|
delete t;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct control_block
|
||||||
|
{
|
||||||
|
virtual ~control_block() = default;
|
||||||
|
|
||||||
|
virtual std::unique_ptr<control_block> clone() const = 0;
|
||||||
|
|
||||||
|
virtual T* ptr() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class U = T>
|
||||||
|
class direct_control_block : public control_block<T>
|
||||||
|
{
|
||||||
|
static_assert(!std::is_reference<U>::value, "");
|
||||||
|
U u_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <class... Ts>
|
||||||
|
explicit direct_control_block(Ts&&... ts) : u_(U(std::forward<Ts>(ts)...))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<control_block<T>> clone() const override
|
||||||
|
{
|
||||||
|
return std::make_unique<direct_control_block>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
T* ptr() override
|
||||||
|
{
|
||||||
|
return std::addressof(u_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class U, class C = default_copy<U>,
|
||||||
|
class D = default_delete<U>>
|
||||||
|
class pointer_control_block : public control_block<T>, public C
|
||||||
|
{
|
||||||
|
std::unique_ptr<U, D> p_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit pointer_control_block(U* u, C c = C{}, D d = D{})
|
||||||
|
: C(std::move(c)), p_(u, std::move(d))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit pointer_control_block(std::unique_ptr<U, D> p, C c = C{})
|
||||||
|
: C(std::move(c)), p_(std::move(p))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<control_block<T>> clone() const override
|
||||||
|
{
|
||||||
|
assert(p_);
|
||||||
|
return std::make_unique<pointer_control_block>(
|
||||||
|
C::operator()(*p_), static_cast<const C&>(*this), p_.get_deleter());
|
||||||
|
}
|
||||||
|
|
||||||
|
T* ptr() override
|
||||||
|
{
|
||||||
|
return p_.get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class U>
|
||||||
|
class delegating_control_block : public control_block<T>
|
||||||
|
{
|
||||||
|
std::unique_ptr<control_block<U>> delegate_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit delegating_control_block(std::unique_ptr<control_block<U>> b)
|
||||||
|
: delegate_(std::move(b))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<control_block<T>> clone() const override
|
||||||
|
{
|
||||||
|
return std::make_unique<delegating_control_block>(delegate_->clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
T* ptr() override
|
||||||
|
{
|
||||||
|
return delegate_->ptr();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // end namespace detail
|
||||||
|
|
||||||
|
class bad_polymorphic_value_construction : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bad_polymorphic_value_construction() noexcept = default;
|
||||||
|
|
||||||
|
const char* what() const noexcept override
|
||||||
|
{
|
||||||
|
return "Dynamic and static type mismatch in polymorphic_value "
|
||||||
|
"construction";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class polymorphic_value;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct is_polymorphic_value : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct is_polymorphic_value<polymorphic_value<T>> : std::true_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// `polymorphic_value` class definition
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class polymorphic_value
|
||||||
|
{
|
||||||
|
static_assert(!std::is_union<T>::value, "");
|
||||||
|
static_assert(std::is_class<T>::value, "");
|
||||||
|
|
||||||
|
template <class U>
|
||||||
|
friend class polymorphic_value;
|
||||||
|
|
||||||
|
template <class T_, class U, class... Ts>
|
||||||
|
friend polymorphic_value<T_> make_polymorphic_value(Ts&&... ts);
|
||||||
|
template <class T_, class... Ts>
|
||||||
|
friend polymorphic_value<T_> make_polymorphic_value(Ts&&... ts);
|
||||||
|
|
||||||
|
T* ptr_ = nullptr;
|
||||||
|
std::unique_ptr<detail::control_block<T>> cb_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
//
|
||||||
|
// Destructor
|
||||||
|
//
|
||||||
|
|
||||||
|
~polymorphic_value() = default;
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Constructors
|
||||||
|
//
|
||||||
|
|
||||||
|
polymorphic_value()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class U, class C = detail::default_copy<U>,
|
||||||
|
class D = detail::default_delete<U>,
|
||||||
|
class V = std::enable_if_t<std::is_convertible<U*, T*>::value>>
|
||||||
|
explicit polymorphic_value(U* u, C copier = C{}, D deleter = D{})
|
||||||
|
{
|
||||||
|
if (!u)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef POLYMORPHIC_VALUE_NO_RTTI
|
||||||
|
if (std::is_same<D, detail::default_delete<U>>::value &&
|
||||||
|
std::is_same<C, detail::default_copy<U>>::value &&
|
||||||
|
typeid(*u) != typeid(U))
|
||||||
|
throw bad_polymorphic_value_construction();
|
||||||
|
#endif
|
||||||
|
std::unique_ptr<U, D> p(u, std::move(deleter));
|
||||||
|
|
||||||
|
cb_ = std::make_unique<detail::pointer_control_block<T, U, C, D>>(
|
||||||
|
std::move(p), std::move(copier));
|
||||||
|
ptr_ = u;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Copy-constructors
|
||||||
|
//
|
||||||
|
|
||||||
|
polymorphic_value(const polymorphic_value& p)
|
||||||
|
{
|
||||||
|
if (!p)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto tmp_cb = p.cb_->clone();
|
||||||
|
ptr_ = tmp_cb->ptr();
|
||||||
|
cb_ = std::move(tmp_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Move-constructors
|
||||||
|
//
|
||||||
|
|
||||||
|
polymorphic_value(polymorphic_value&& p) noexcept
|
||||||
|
{
|
||||||
|
ptr_ = p.ptr_;
|
||||||
|
cb_ = std::move(p.cb_);
|
||||||
|
p.ptr_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Converting constructors
|
||||||
|
//
|
||||||
|
|
||||||
|
template <class U,
|
||||||
|
class V = std::enable_if_t<!std::is_same<T, U>::value &&
|
||||||
|
std::is_convertible<U*, T*>::value>>
|
||||||
|
explicit polymorphic_value(const polymorphic_value<U>& p)
|
||||||
|
{
|
||||||
|
polymorphic_value<U> tmp(p);
|
||||||
|
ptr_ = tmp.ptr_;
|
||||||
|
cb_ = std::make_unique<detail::delegating_control_block<T, U>>(
|
||||||
|
std::move(tmp.cb_));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class U,
|
||||||
|
class V = std::enable_if_t<!std::is_same<T, U>::value &&
|
||||||
|
std::is_convertible<U*, T*>::value>>
|
||||||
|
explicit polymorphic_value(polymorphic_value<U>&& p)
|
||||||
|
{
|
||||||
|
ptr_ = p.ptr_;
|
||||||
|
cb_ = std::make_unique<detail::delegating_control_block<T, U>>(
|
||||||
|
std::move(p.cb_));
|
||||||
|
p.ptr_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Forwarding constructor
|
||||||
|
//
|
||||||
|
|
||||||
|
template <class U, class V = std::enable_if_t<
|
||||||
|
std::is_convertible<std::decay_t<U>*, T*>::value &&
|
||||||
|
!is_polymorphic_value<std::decay_t<U>>::value>>
|
||||||
|
explicit polymorphic_value(U&& u)
|
||||||
|
: cb_(std::make_unique<
|
||||||
|
detail::direct_control_block<T, std::decay_t<U>>>(
|
||||||
|
std::forward<U>(u)))
|
||||||
|
{
|
||||||
|
ptr_ = cb_->ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Assignment
|
||||||
|
//
|
||||||
|
|
||||||
|
polymorphic_value& operator=(const polymorphic_value& p)
|
||||||
|
{
|
||||||
|
if (std::addressof(p) == this)
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!p)
|
||||||
|
{
|
||||||
|
cb_.reset();
|
||||||
|
ptr_ = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tmp_cb = p.cb_->clone();
|
||||||
|
ptr_ = tmp_cb->ptr();
|
||||||
|
cb_ = std::move(tmp_cb);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Move-assignment
|
||||||
|
//
|
||||||
|
|
||||||
|
polymorphic_value& operator=(polymorphic_value&& p) noexcept
|
||||||
|
{
|
||||||
|
if (std::addressof(p) == this)
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cb_ = std::move(p.cb_);
|
||||||
|
ptr_ = p.ptr_;
|
||||||
|
p.ptr_ = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Modifiers
|
||||||
|
//
|
||||||
|
|
||||||
|
void swap(polymorphic_value& p) noexcept
|
||||||
|
{
|
||||||
|
using std::swap;
|
||||||
|
swap(ptr_, p.ptr_);
|
||||||
|
swap(cb_, p.cb_);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Observers
|
||||||
|
//
|
||||||
|
|
||||||
|
explicit operator bool() const
|
||||||
|
{
|
||||||
|
return bool (cb_);
|
||||||
|
}
|
||||||
|
|
||||||
|
const T* operator->() const
|
||||||
|
{
|
||||||
|
assert(ptr_);
|
||||||
|
return ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& operator*() const
|
||||||
|
{
|
||||||
|
assert(*this);
|
||||||
|
return *ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* operator->()
|
||||||
|
{
|
||||||
|
assert(*this);
|
||||||
|
return ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
T& operator*()
|
||||||
|
{
|
||||||
|
assert(*this);
|
||||||
|
return *ptr_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// polymorphic_value creation
|
||||||
|
//
|
||||||
|
template <class T, class... Ts>
|
||||||
|
polymorphic_value<T> make_polymorphic_value(Ts&&... ts)
|
||||||
|
{
|
||||||
|
polymorphic_value<T> p;
|
||||||
|
p.cb_ = std::make_unique<detail::direct_control_block<T, T>>(
|
||||||
|
std::forward<Ts>(ts)...);
|
||||||
|
p.ptr_ = p.cb_->ptr();
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
template <class T, class U, class... Ts>
|
||||||
|
polymorphic_value<T> make_polymorphic_value(Ts&&... ts)
|
||||||
|
{
|
||||||
|
polymorphic_value<T> p;
|
||||||
|
p.cb_ = std::make_unique<detail::direct_control_block<T, U>>(
|
||||||
|
std::forward<Ts>(ts)...);
|
||||||
|
p.ptr_ = p.cb_->ptr();
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// non-member swap
|
||||||
|
//
|
||||||
|
template <class T>
|
||||||
|
void swap(polymorphic_value<T>& t, polymorphic_value<T>& u) noexcept
|
||||||
|
{
|
||||||
|
t.swap(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace jbcoe
|
||||||
|
|
||||||
|
#endif
|
@ -54,10 +54,22 @@ namespace Data {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Options {
|
||||||
|
std::string marker;
|
||||||
|
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive & archive) {
|
||||||
|
archive(
|
||||||
|
CEREAL_NVP(marker),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// RAII style class which loads preferences from the dedicated file when constructed and saves them when destructed
|
// RAII style class which loads preferences from the dedicated file when constructed and saves them when destructed
|
||||||
struct Preferences {
|
struct Preferences {
|
||||||
Screen screen;
|
Screen screen;
|
||||||
Layout layout;
|
Layout layout;
|
||||||
|
Options options;
|
||||||
KeyMapping key_mapping;
|
KeyMapping key_mapping;
|
||||||
|
|
||||||
Preferences() : screen(), layout() {
|
Preferences() : screen(), layout() {
|
||||||
@ -106,6 +118,7 @@ namespace Data {
|
|||||||
archive(
|
archive(
|
||||||
CEREAL_NVP(screen),
|
CEREAL_NVP(screen),
|
||||||
CEREAL_NVP(layout),
|
CEREAL_NVP(layout),
|
||||||
|
CEREAL_NVP(options),
|
||||||
cereal::make_nvp("key_mapping", key_mapping.m_button_to_key)
|
cereal::make_nvp("key_mapping", key_mapping.m_button_to_key)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
13
src/Main.cpp
13
src/Main.cpp
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "Data/Song.hpp"
|
#include "Data/Song.hpp"
|
||||||
#include "Data/Preferences.hpp"
|
#include "Data/Preferences.hpp"
|
||||||
|
#include "Resources/Marker.hpp"
|
||||||
// #include "Data/Chart.hpp"
|
// #include "Data/Chart.hpp"
|
||||||
// #include "Data/Score.hpp"
|
// #include "Data/Score.hpp"
|
||||||
|
|
||||||
@ -14,7 +15,13 @@
|
|||||||
// #include "Screens/Result.hpp"
|
// #include "Screens/Result.hpp"
|
||||||
|
|
||||||
int main(int argc, char const *argv[]) {
|
int main(int argc, char const *argv[]) {
|
||||||
|
|
||||||
Data::Preferences preferences;
|
Data::Preferences preferences;
|
||||||
|
auto markers = Resources::load_markers();
|
||||||
|
if (markers.find(preferences.options.marker) == markers.end()) {
|
||||||
|
preferences.options.marker = markers.begin()->first;
|
||||||
|
}
|
||||||
|
|
||||||
sf::ContextSettings settings;
|
sf::ContextSettings settings;
|
||||||
settings.antialiasingLevel = 8;
|
settings.antialiasingLevel = 8;
|
||||||
sf::RenderWindow window{
|
sf::RenderWindow window{
|
||||||
@ -27,7 +34,11 @@ int main(int argc, char const *argv[]) {
|
|||||||
settings
|
settings
|
||||||
};
|
};
|
||||||
Data::SongList song_list;
|
Data::SongList song_list;
|
||||||
MusicSelect::Screen music_select{song_list, preferences};
|
MusicSelect::Screen music_select{
|
||||||
|
song_list,
|
||||||
|
preferences,
|
||||||
|
markers,
|
||||||
|
};
|
||||||
|
|
||||||
music_select.select_chart(window);
|
music_select.select_chart(window);
|
||||||
/*
|
/*
|
||||||
|
@ -8,17 +8,7 @@
|
|||||||
|
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
namespace fs = ghc::filesystem;
|
#include "../Toolkit/GHCFilesystemPathHash.hpp"
|
||||||
|
|
||||||
// Define the way we hash fs::path for use in unordered maps
|
|
||||||
namespace std {
|
|
||||||
template <>
|
|
||||||
struct hash<fs::path> {
|
|
||||||
std::size_t operator()(const fs::path& p) const {
|
|
||||||
return std::hash<std::string>()(p.string());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Textures {
|
namespace Textures {
|
||||||
|
|
||||||
|
204
src/Resources/Marker.cpp
Normal file
204
src/Resources/Marker.cpp
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
#include "Marker.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <fstream>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <cereal/archives/json.hpp>
|
||||||
|
|
||||||
|
#include "../Toolkit/GHCFilesystemPathHash.hpp"
|
||||||
|
|
||||||
|
namespace fs = ghc::filesystem;
|
||||||
|
|
||||||
|
namespace Resources {
|
||||||
|
|
||||||
|
Marker::Marker(const fs::path& marker_folder) :
|
||||||
|
m_folder(marker_folder),
|
||||||
|
m_metadata(),
|
||||||
|
m_approach(),
|
||||||
|
m_miss(),
|
||||||
|
m_early(),
|
||||||
|
m_good(),
|
||||||
|
m_great(),
|
||||||
|
m_perfect()
|
||||||
|
{
|
||||||
|
if (not fs::is_directory(m_folder)) {
|
||||||
|
throw std::invalid_argument(m_folder.string()+" is not a folder");
|
||||||
|
}
|
||||||
|
if (not fs::exists(m_folder/"marker.json")) {
|
||||||
|
throw std::invalid_argument("Marker folder ( "+m_folder.string()+" ) has no marker.json file");
|
||||||
|
}
|
||||||
|
std::ifstream marker_json{m_folder/"marker.json"};
|
||||||
|
{
|
||||||
|
cereal::JSONInputArchive archive{marker_json};
|
||||||
|
archive(m_metadata);
|
||||||
|
}
|
||||||
|
load_and_check(m_approach, m_metadata.approach);
|
||||||
|
load_and_check(m_miss, m_metadata.miss);
|
||||||
|
load_and_check(m_early, m_metadata.early);
|
||||||
|
load_and_check(m_good, m_metadata.good);
|
||||||
|
load_and_check(m_great, m_metadata.great);
|
||||||
|
load_and_check(m_perfect, m_metadata.perfect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Marker::load_and_check(sf::Texture& sprite_sheet, const MarkerAnimationMetadata& metadata) {
|
||||||
|
// File Load & Check
|
||||||
|
if (not sprite_sheet.loadFromFile(m_folder/metadata.sprite_sheet)) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Cannot open marker sprite sheet "
|
||||||
|
+(m_folder/metadata.sprite_sheet).string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprite sheet size check
|
||||||
|
// throw if the texture size does not match what's announced by the metadata
|
||||||
|
auto sheet_size = sprite_sheet.getSize();
|
||||||
|
auto expected_size = sf::Vector2u(metadata.columns, metadata.rows) * static_cast<unsigned int>(m_metadata.size);
|
||||||
|
if (sheet_size != expected_size) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Marker sprite sheet ";
|
||||||
|
ss << (m_folder/metadata.sprite_sheet).string();
|
||||||
|
ss << " should be " << expected_size.x << "×" << expected_size.y << " pixels";
|
||||||
|
ss << " but is " << sheet_size.x << "×" << sheet_size.y;
|
||||||
|
throw std::invalid_argument(ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprite count check
|
||||||
|
// throw if the count calls for more sprites than possible according to the 'columns' and 'rows' fields
|
||||||
|
if (metadata.count > metadata.columns * metadata.rows) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Metadata for marker sprite sheet ";
|
||||||
|
ss << (m_folder/metadata.sprite_sheet).string();
|
||||||
|
ss << " indicates that it holds " << metadata.count << " sprites";
|
||||||
|
ss << " when it can only hold a maximum of " << metadata.columns * metadata.rows;
|
||||||
|
ss << " according to the 'columns' and 'rows' fields";
|
||||||
|
throw std::invalid_argument(ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duration check
|
||||||
|
// We do not allow any marker animation to take longer than the jubeat standard of 16 frames at 30 fps
|
||||||
|
// For that we check that :
|
||||||
|
// count/fps <= 16/30
|
||||||
|
// Which is mathematically equivalent to checking that :
|
||||||
|
// count*30 <= 16*fps
|
||||||
|
// Which allows us to avoid having to cast to float
|
||||||
|
if (metadata.count*30 <= 16*m_metadata.fps) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Marker animation for sprite sheet ";
|
||||||
|
ss << (m_folder/metadata.sprite_sheet).string();
|
||||||
|
ss << " lasts " << metadata.count/static_cast<float>(m_metadata.fps)*1000.f << "ms";
|
||||||
|
ss << " (" << metadata.count << "f @ " << m_metadata.fps << "fps)";
|
||||||
|
ss << " which is more than the maximum of " << 16.f/30.f*1000.f << "ms";
|
||||||
|
ss << ' (16f @ 30fps)';
|
||||||
|
throw std::invalid_argument(ss.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sf::Texture& Marker::get_sprite_sheet_from_enum(const MarkerAnimation& state) {
|
||||||
|
switch (state) {
|
||||||
|
case MarkerAnimation::APPROACH:
|
||||||
|
return m_approach;
|
||||||
|
break;
|
||||||
|
case MarkerAnimation::MISS:
|
||||||
|
return m_miss;
|
||||||
|
break;
|
||||||
|
case MarkerAnimation::EARLY:
|
||||||
|
return m_early;
|
||||||
|
break;
|
||||||
|
case MarkerAnimation::GOOD:
|
||||||
|
return m_good;
|
||||||
|
break;
|
||||||
|
case MarkerAnimation::GREAT:
|
||||||
|
return m_great;
|
||||||
|
break;
|
||||||
|
case MarkerAnimation::PERFECT:
|
||||||
|
return m_perfect;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MarkerAnimationMetadata& Marker::get_metadata_from_enum(const MarkerAnimation& state) {
|
||||||
|
switch (state) {
|
||||||
|
case MarkerAnimation::APPROACH:
|
||||||
|
return m_metadata.approach;
|
||||||
|
break;
|
||||||
|
case MarkerAnimation::MISS:
|
||||||
|
return m_metadata.miss;
|
||||||
|
break;
|
||||||
|
case MarkerAnimation::EARLY:
|
||||||
|
return m_metadata.early;
|
||||||
|
break;
|
||||||
|
case MarkerAnimation::GOOD:
|
||||||
|
return m_metadata.good;
|
||||||
|
break;
|
||||||
|
case MarkerAnimation::GREAT:
|
||||||
|
return m_metadata.great;
|
||||||
|
break;
|
||||||
|
case MarkerAnimation::PERFECT:
|
||||||
|
return m_metadata.perfect;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<sf::Sprite> Marker::get_sprite(const MarkerAnimation& state, const float& seconds) {
|
||||||
|
auto raw_frame = static_cast<int>(std::floor(seconds*m_metadata.fps));
|
||||||
|
if (raw_frame >= 0) {
|
||||||
|
if (state == MarkerAnimation::APPROACH) {
|
||||||
|
return get_sprite(MarkerAnimation::MISS, static_cast<std::size_t>(raw_frame));
|
||||||
|
} else {
|
||||||
|
return get_sprite(state, static_cast<std::size_t>(raw_frame));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto approach_frame_count = get_metadata_from_enum(MarkerAnimation::APPROACH).count;
|
||||||
|
return get_sprite(
|
||||||
|
MarkerAnimation::APPROACH,
|
||||||
|
static_cast<std::size_t>(raw_frame+static_cast<int>(approach_frame_count))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<sf::Sprite> Marker::get_sprite(const MarkerAnimation& state, const std::size_t frame) {
|
||||||
|
auto& meta = get_metadata_from_enum(state);
|
||||||
|
if (frame >= meta.count) {
|
||||||
|
return {};
|
||||||
|
} else {
|
||||||
|
auto& tex = get_sprite_sheet_from_enum(state);
|
||||||
|
sf::Sprite sprite{tex};
|
||||||
|
sf::IntRect rect{
|
||||||
|
sf::Vector2i{
|
||||||
|
static_cast<int>(frame % meta.columns),
|
||||||
|
static_cast<int>(frame / meta.columns)
|
||||||
|
} * static_cast<int>(m_metadata.size),
|
||||||
|
sf::Vector2i{
|
||||||
|
static_cast<int>(m_metadata.size),
|
||||||
|
static_cast<int>(m_metadata.size)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
sprite.setTextureRect(rect);
|
||||||
|
return sprite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Markers load_markers() {
|
||||||
|
Markers res;
|
||||||
|
for (auto& p : fs::directory_iterator("markers")) {
|
||||||
|
if (p.is_directory()) {
|
||||||
|
try {
|
||||||
|
Marker m{p.path()};
|
||||||
|
res.emplace(m.m_metadata.name, m);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Unable to load marker folder "
|
||||||
|
<< p.path().string() << " : "
|
||||||
|
<< e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (res.empty()) {
|
||||||
|
throw std::runtime_error("No markers found in marker folder, jujube needs at least one to operate");
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
87
src/Resources/Marker.hpp
Normal file
87
src/Resources/Marker.hpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <cereal/types/string.hpp>
|
||||||
|
#include <ghc/filesystem.hpp>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
#include "../Toolkit/ExtraCerealTypes/GHCFilesystemPath.hpp"
|
||||||
|
|
||||||
|
namespace Resources {
|
||||||
|
enum class MarkerAnimation {
|
||||||
|
APPROACH,
|
||||||
|
MISS,
|
||||||
|
EARLY, // or LATE
|
||||||
|
GOOD,
|
||||||
|
GREAT,
|
||||||
|
PERFECT
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MarkerAnimationMetadata {
|
||||||
|
ghc::filesystem::path sprite_sheet;
|
||||||
|
std::size_t count; // how many sprites total on the sheet
|
||||||
|
std::size_t columns; // how many horizontally
|
||||||
|
std::size_t rows; // how many vertically
|
||||||
|
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive & archive) const {
|
||||||
|
archive(
|
||||||
|
CEREAL_NVP(sprite_sheet),
|
||||||
|
CEREAL_NVP(count),
|
||||||
|
CEREAL_NVP(column),
|
||||||
|
CEREAL_NVP(rows)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Represents what's held in marker.json
|
||||||
|
struct MarkerMetadata {
|
||||||
|
std::string name;
|
||||||
|
std::size_t size; // the side length in pixels
|
||||||
|
std::size_t fps; // classic jubeat markers are 30 fps
|
||||||
|
MarkerAnimationMetadata approach;
|
||||||
|
MarkerAnimationMetadata miss;
|
||||||
|
MarkerAnimationMetadata early;
|
||||||
|
MarkerAnimationMetadata good;
|
||||||
|
MarkerAnimationMetadata great;
|
||||||
|
MarkerAnimationMetadata perfect;
|
||||||
|
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive & archive) const {
|
||||||
|
archive(
|
||||||
|
CEREAL_NVP(name),
|
||||||
|
CEREAL_NVP(size),
|
||||||
|
CEREAL_NVP(fps),
|
||||||
|
CEREAL_NVP(approach),
|
||||||
|
CEREAL_NVP(miss),
|
||||||
|
CEREAL_NVP(early),
|
||||||
|
CEREAL_NVP(good),
|
||||||
|
CEREAL_NVP(great),
|
||||||
|
CEREAL_NVP(perfect)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Marker {
|
||||||
|
explicit Marker(const ghc::filesystem::path& marker_folder);
|
||||||
|
std::optional<sf::Sprite> get_sprite(const MarkerAnimation& state, const float& seconds);
|
||||||
|
std::optional<sf::Sprite> get_sprite(const MarkerAnimation& state, const std::size_t frame);
|
||||||
|
void load_and_check(sf::Texture& spritesheet, const MarkerAnimationMetadata& metadata);
|
||||||
|
sf::Texture& get_sprite_sheet_from_enum(const MarkerAnimation& state);
|
||||||
|
MarkerAnimationMetadata& get_metadata_from_enum(const MarkerAnimation& state);
|
||||||
|
|
||||||
|
ghc::filesystem::path m_folder;
|
||||||
|
MarkerMetadata m_metadata;
|
||||||
|
sf::Texture m_approach;
|
||||||
|
sf::Texture m_miss;
|
||||||
|
sf::Texture m_early;
|
||||||
|
sf::Texture m_good;
|
||||||
|
sf::Texture m_great;
|
||||||
|
sf::Texture m_perfect;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Markers = std::multimap<std::string, Marker>;
|
||||||
|
Markers load_markers();
|
||||||
|
}
|
@ -6,18 +6,7 @@
|
|||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
#include "../Toolkit/Cache.hpp"
|
#include "../Toolkit/Cache.hpp"
|
||||||
|
#include "../Toolkit/GHCFilesystemPathHash.hpp"
|
||||||
namespace fs = ghc::filesystem;
|
|
||||||
|
|
||||||
// Define the way we hash fs::path for use in unordered maps
|
|
||||||
namespace std {
|
|
||||||
template <>
|
|
||||||
struct hash<fs::path> {
|
|
||||||
std::size_t operator()(const fs::path& p) const {
|
|
||||||
return std::hash<std::string>()(p.string());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Textures {
|
namespace Textures {
|
||||||
|
|
||||||
|
@ -7,18 +7,23 @@
|
|||||||
|
|
||||||
#include "../../Data/Buttons.hpp"
|
#include "../../Data/Buttons.hpp"
|
||||||
#include "../../Data/KeyMapping.hpp"
|
#include "../../Data/KeyMapping.hpp"
|
||||||
|
#include "PanelLayout.hpp"
|
||||||
|
|
||||||
MusicSelect::Screen::Screen(const Data::SongList& t_song_list, Data::Preferences& t_preferences) :
|
MusicSelect::Screen::Screen(
|
||||||
|
const Data::SongList& t_song_list,
|
||||||
|
Data::Preferences& t_preferences,
|
||||||
|
const Resources::Markers& t_markers
|
||||||
|
) :
|
||||||
song_list(t_song_list),
|
song_list(t_song_list),
|
||||||
resources(t_preferences),
|
resources(t_preferences),
|
||||||
ribbon(resources),
|
markers(t_markers),
|
||||||
|
ribbon(PanelLayout::title_sort(t_song_list, resources), resources),
|
||||||
song_info(resources),
|
song_info(resources),
|
||||||
selected_panel(),
|
selected_panel(),
|
||||||
button_highlight(resources),
|
button_highlight(resources),
|
||||||
black_frame(t_preferences),
|
black_frame(t_preferences),
|
||||||
key_mapping()
|
key_mapping()
|
||||||
{
|
{
|
||||||
ribbon.title_sort(song_list);
|
|
||||||
std::cout << "loaded MusicSelect::Screen" << std::endl;
|
std::cout << "loaded MusicSelect::Screen" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,8 +117,7 @@ void MusicSelect::Screen::press_button(const Data::Button& button) {
|
|||||||
button_highlight.button_pressed(button);
|
button_highlight.button_pressed(button);
|
||||||
auto button_index = Data::button_to_index(button);
|
auto button_index = Data::button_to_index(button);
|
||||||
if (button_index < 12) {
|
if (button_index < 12) {
|
||||||
ribbon.click_on(button_index);
|
ribbon.click_on(button);
|
||||||
// ribbon.at(button_index)->click(ribbon);
|
|
||||||
} else {
|
} else {
|
||||||
switch (button) {
|
switch (button) {
|
||||||
case Data::Button::B13:
|
case Data::Button::B13:
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
#include <SFML/Window.hpp>
|
#include <SFML/Window.hpp>
|
||||||
|
|
||||||
@ -8,11 +9,13 @@
|
|||||||
#include "../../Data/Chart.hpp"
|
#include "../../Data/Chart.hpp"
|
||||||
#include "../../Data/KeyMapping.hpp"
|
#include "../../Data/KeyMapping.hpp"
|
||||||
#include "../../Drawables/BlackFrame.hpp"
|
#include "../../Drawables/BlackFrame.hpp"
|
||||||
|
#include "../../Resources/Marker.hpp"
|
||||||
#include "../../Toolkit/AffineTransform.hpp"
|
#include "../../Toolkit/AffineTransform.hpp"
|
||||||
#include "Ribbon.hpp"
|
#include "SongListRibbon.hpp"
|
||||||
#include "SongInfo.hpp"
|
#include "SongInfo.hpp"
|
||||||
#include "SharedResources.hpp"
|
#include "SharedResources.hpp"
|
||||||
#include "ButtonHighlight.hpp"
|
#include "ButtonHighlight.hpp"
|
||||||
|
#include "OptionMenu.hpp"
|
||||||
|
|
||||||
namespace MusicSelect {
|
namespace MusicSelect {
|
||||||
|
|
||||||
@ -20,7 +23,11 @@ namespace MusicSelect {
|
|||||||
// it loads a cache of available songs in the song_list attribute
|
// it loads a cache of available songs in the song_list attribute
|
||||||
class Screen {
|
class Screen {
|
||||||
public:
|
public:
|
||||||
Screen(const Data::SongList& t_song_list, Data::Preferences& t_preferences);
|
Screen(
|
||||||
|
const Data::SongList& t_song_list,
|
||||||
|
Data::Preferences& t_preferences,
|
||||||
|
const Resources::Markers& t_markers
|
||||||
|
);
|
||||||
void select_chart(sf::RenderWindow& window);
|
void select_chart(sf::RenderWindow& window);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -29,12 +36,14 @@ namespace MusicSelect {
|
|||||||
|
|
||||||
// Resources
|
// Resources
|
||||||
SharedResources resources;
|
SharedResources resources;
|
||||||
|
Resources::Markers markers;
|
||||||
|
|
||||||
// State
|
// State
|
||||||
Ribbon ribbon;
|
SongListRibbon ribbon;
|
||||||
SongInfo song_info;
|
SongInfo song_info;
|
||||||
std::optional<std::reference_wrapper<SongPanel>> selected_panel;
|
std::optional<std::reference_wrapper<SongPanel>> selected_panel;
|
||||||
ButtonHighlight button_highlight;
|
ButtonHighlight button_highlight;
|
||||||
|
std::stack<OptionMenu> options_state;
|
||||||
|
|
||||||
Drawables::BlackFrame black_frame;
|
Drawables::BlackFrame black_frame;
|
||||||
|
|
||||||
|
22
src/Screens/MusicSelect/OptionMenu.hpp
Normal file
22
src/Screens/MusicSelect/OptionMenu.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
#include "SharedResources.hpp"
|
||||||
|
#include "../../Data/Buttons.hpp"
|
||||||
|
|
||||||
|
namespace MusicSelect {
|
||||||
|
class OptionPage : public sf::Drawable, public sf::Transformable, public HoldsSharedResources {
|
||||||
|
public:
|
||||||
|
virtual void click(const Data::Button& button) = 0;
|
||||||
|
virtual ~OptionPage() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MainOptionPage final : public OptionPage {
|
||||||
|
public:
|
||||||
|
MainOptionPage();
|
||||||
|
void click(const Data::Button& button);
|
||||||
|
private:
|
||||||
|
void draw(sf::RenderTarget& target, sf::RenderStates states);
|
||||||
|
}
|
||||||
|
}
|
@ -26,8 +26,8 @@ namespace MusicSelect {
|
|||||||
target.draw(panel, states);
|
target.draw(panel, states);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CategoryPanel::click(Ribbon& ribbon, std::size_t from_button_index) {
|
void CategoryPanel::click(Ribbon& ribbon, const Data::Button& button) {
|
||||||
ribbon.move_to_next_category(from_button_index);
|
ribbon.move_to_next_category(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CategoryPanel::draw(sf::RenderTarget& target, sf::RenderStates states) const {
|
void CategoryPanel::draw(sf::RenderTarget& target, sf::RenderStates states) const {
|
||||||
@ -66,7 +66,7 @@ namespace MusicSelect {
|
|||||||
target.draw(label_text, states);
|
target.draw(label_text, states);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SongPanel::click(Ribbon& ribbon, std::size_t from_button_index) {
|
void SongPanel::click(MusicSelectRibbon& ribbon, const Data::Button& button) {
|
||||||
if (selected_chart.has_value()) {
|
if (selected_chart.has_value()) {
|
||||||
// The song was already selected : look for the next chart in order
|
// The song was already selected : look for the next chart in order
|
||||||
auto it = m_song->chart_levels.upper_bound(*selected_chart);
|
auto it = m_song->chart_levels.upper_bound(*selected_chart);
|
||||||
|
@ -21,7 +21,7 @@ namespace MusicSelect {
|
|||||||
public:
|
public:
|
||||||
explicit Panel(SharedResources& resources);
|
explicit Panel(SharedResources& resources);
|
||||||
// What happens when you click on the panel
|
// What happens when you click on the panel
|
||||||
virtual void click(Ribbon& ribbon, std::size_t from_button_index) = 0;
|
virtual void click(Ribbon& ribbon, const Data::Button& button) = 0;
|
||||||
virtual ~Panel() = default;
|
virtual ~Panel() = default;
|
||||||
protected:
|
protected:
|
||||||
float get_size() const;
|
float get_size() const;
|
||||||
@ -30,7 +30,7 @@ namespace MusicSelect {
|
|||||||
class EmptyPanel final : public Panel {
|
class EmptyPanel final : public Panel {
|
||||||
public:
|
public:
|
||||||
using Panel::Panel;
|
using Panel::Panel;
|
||||||
void click(Ribbon& ribbon, std::size_t from_button_index) override {return;};
|
void click(Ribbon& ribbon, const Data::Button& button) override {return;};
|
||||||
private:
|
private:
|
||||||
void draw(sf::RenderTarget& target, sf::RenderStates states) const override {return;};
|
void draw(sf::RenderTarget& target, sf::RenderStates states) const override {return;};
|
||||||
};
|
};
|
||||||
@ -38,7 +38,7 @@ namespace MusicSelect {
|
|||||||
class ColoredMessagePanel final : public Panel {
|
class ColoredMessagePanel final : public Panel {
|
||||||
public:
|
public:
|
||||||
ColoredMessagePanel(SharedResources& resources, const sf::Color& color, const std::string& message) : Panel(resources), m_color(color), m_message(message) {};
|
ColoredMessagePanel(SharedResources& resources, const sf::Color& color, const std::string& message) : Panel(resources), m_color(color), m_message(message) {};
|
||||||
void click(Ribbon& ribbon, std::size_t from_button_index) override {return;};
|
void click(Ribbon& ribbon, const Data::Button& button) override {return;};
|
||||||
private:
|
private:
|
||||||
void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
|
void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
|
||||||
const sf::Color m_color;
|
const sf::Color m_color;
|
||||||
@ -48,7 +48,7 @@ namespace MusicSelect {
|
|||||||
class ColorPanel final : public Panel {
|
class ColorPanel final : public Panel {
|
||||||
public:
|
public:
|
||||||
ColorPanel(SharedResources& resources, const sf::Color& t_color) : Panel(resources), m_color(t_color) {};
|
ColorPanel(SharedResources& resources, const sf::Color& t_color) : Panel(resources), m_color(t_color) {};
|
||||||
void click(Ribbon& ribbon, std::size_t from_button_index) override {return;};
|
void click(Ribbon& ribbon, const Data::Button& button) override {return;};
|
||||||
private:
|
private:
|
||||||
void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
|
void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
|
||||||
const sf::Color m_color;
|
const sf::Color m_color;
|
||||||
@ -57,7 +57,7 @@ namespace MusicSelect {
|
|||||||
class CategoryPanel final : public Panel {
|
class CategoryPanel final : public Panel {
|
||||||
public:
|
public:
|
||||||
explicit CategoryPanel(SharedResources& resources, const std::string& t_label) : Panel(resources), m_label(t_label) {};
|
explicit CategoryPanel(SharedResources& resources, const std::string& t_label) : Panel(resources), m_label(t_label) {};
|
||||||
void click(Ribbon& ribbon, std::size_t from_button_index) override;
|
void click(Ribbon& ribbon, const Data::Button& button) override;
|
||||||
private:
|
private:
|
||||||
void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
|
void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
|
||||||
std::string m_label;
|
std::string m_label;
|
||||||
@ -74,7 +74,7 @@ namespace MusicSelect {
|
|||||||
class SongPanel final : public SelectablePanel {
|
class SongPanel final : public SelectablePanel {
|
||||||
public:
|
public:
|
||||||
explicit SongPanel(SharedResources& resources, const std::shared_ptr<const Data::Song>& t_song) : SelectablePanel(resources), m_song(t_song) {};
|
explicit SongPanel(SharedResources& resources, const std::shared_ptr<const Data::Song>& t_song) : SelectablePanel(resources), m_song(t_song) {};
|
||||||
void click(Ribbon& ribbon, std::size_t from_button_index) override;
|
void click(Ribbon& ribbon, const Data::Button& button) override;
|
||||||
void unselect() override;
|
void unselect() override;
|
||||||
std::optional<SongDifficulty> get_selected_difficulty() const override;
|
std::optional<SongDifficulty> get_selected_difficulty() const override;
|
||||||
private:
|
private:
|
||||||
|
102
src/Screens/MusicSelect/PanelLayout.cpp
Normal file
102
src/Screens/MusicSelect/PanelLayout.cpp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#include "PanelLayout.hpp"
|
||||||
|
|
||||||
|
namespace MusicSelect {
|
||||||
|
PanelLayout::PanelLayout(
|
||||||
|
const std::map<std::string,std::vector<jbcoe::polymorphic_value<Panel>>>& categories,
|
||||||
|
SharedResources& resources
|
||||||
|
) {
|
||||||
|
for (auto &&[category, panels] : categories) {
|
||||||
|
if (not panels.empty()) {
|
||||||
|
std::vector<jbcoe::polymorphic_value<Panel>> current_column;
|
||||||
|
current_column.emplace_back(CategoryPanel{resources, category});
|
||||||
|
for (auto &&panel : panels) {
|
||||||
|
if (current_column.size() == 3) {
|
||||||
|
push_back({current_column[0], current_column[1], current_column[2]});
|
||||||
|
current_column.clear();
|
||||||
|
} else {
|
||||||
|
current_column.push_back(std::move(panel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (not current_column.empty()) {
|
||||||
|
while (current_column.size() < 3) {
|
||||||
|
current_column.emplace_back(EmptyPanel{resources});
|
||||||
|
}
|
||||||
|
push_back({current_column[0], current_column[1], current_column[2]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fill_layout(resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
PanelLayout::PanelLayout(
|
||||||
|
const std::vector<jbcoe::polymorphic_value<Panel>> panels,
|
||||||
|
SharedResources& resources
|
||||||
|
) {
|
||||||
|
std::vector<jbcoe::polymorphic_value<Panel>> current_column;
|
||||||
|
for (auto &&panel : panels) {
|
||||||
|
if (current_column.size() == 3) {
|
||||||
|
push_back({current_column[0], current_column[1], current_column[2]});
|
||||||
|
current_column.clear();
|
||||||
|
} else {
|
||||||
|
current_column.push_back(std::move(panel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (not current_column.empty()) {
|
||||||
|
while (current_column.size() < 3) {
|
||||||
|
current_column.emplace_back(EmptyPanel{resources});
|
||||||
|
}
|
||||||
|
push_back({current_column[0], current_column[1], current_column[2]});
|
||||||
|
}
|
||||||
|
fill_layout(resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
PanelLayout PanelLayout::red_empty_layout(SharedResources& resources) {
|
||||||
|
std::vector<jbcoe::polymorphic_value<Panel>> panels;
|
||||||
|
for (size_t i = 0; i < 3*4; i++) {
|
||||||
|
panels.emplace_back(ColoredMessagePanel{resources, sf::Color::Red, "- EMPTY -"});
|
||||||
|
}
|
||||||
|
return PanelLayout{panels, resources};
|
||||||
|
}
|
||||||
|
|
||||||
|
PanelLayout PanelLayout::title_sort(const Data::SongList& song_list, SharedResources& resources) {
|
||||||
|
std::vector<std::shared_ptr<const Data::Song>> songs;
|
||||||
|
for (auto &&song : song_list.songs) {
|
||||||
|
songs.push_back(song);
|
||||||
|
}
|
||||||
|
std::sort(
|
||||||
|
songs.begin(),
|
||||||
|
songs.end(),
|
||||||
|
[](std::shared_ptr<const Data::Song> a, std::shared_ptr<const Data::Song> b){return Data::Song::sort_by_title(*a, *b);}
|
||||||
|
);
|
||||||
|
std::map<std::string, std::vector<jbcoe::polymorphic_value<Panel>>> categories;
|
||||||
|
for (const auto &song : songs) {
|
||||||
|
if (song->title.size() > 0) {
|
||||||
|
char letter = song->title[0];
|
||||||
|
if ('A' <= letter and letter <= 'Z') {
|
||||||
|
categories
|
||||||
|
[std::string(1, letter)]
|
||||||
|
.emplace_back(SongPanel{resources, song});
|
||||||
|
} else if ('a' <= letter and letter <= 'z') {
|
||||||
|
categories
|
||||||
|
[std::string(1, 'A' + (letter - 'a'))]
|
||||||
|
.emplace_back(SongPanel{resources, song});
|
||||||
|
} else {
|
||||||
|
categories["?"].emplace_back(SongPanel{resources, song});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
categories["?"].emplace_back(SongPanel{resources, song});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PanelLayout{categories, resources};
|
||||||
|
}
|
||||||
|
|
||||||
|
void PanelLayout::fill_layout(SharedResources& resources) {
|
||||||
|
while (size() < 4) {
|
||||||
|
push_back({
|
||||||
|
jbcoe::polymorphic_value<Panel>{EmptyPanel{resources}},
|
||||||
|
jbcoe::polymorphic_value<Panel>{EmptyPanel{resources}},
|
||||||
|
jbcoe::polymorphic_value<Panel>{EmptyPanel{resources}}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
src/Screens/MusicSelect/PanelLayout.hpp
Normal file
23
src/Screens/MusicSelect/PanelLayout.hpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <jbcoe/polymorphic_value.h>
|
||||||
|
|
||||||
|
#include "Panel.hpp"
|
||||||
|
#include "SharedResources.hpp"
|
||||||
|
#include "../../Data/Song.hpp"
|
||||||
|
|
||||||
|
namespace MusicSelect {
|
||||||
|
// PanelLayout restricts the ways you can create a scrollable grid of panels
|
||||||
|
class PanelLayout : public std::vector<std::array<jbcoe::polymorphic_value<Panel>,3>> {
|
||||||
|
public:
|
||||||
|
explicit PanelLayout(const std::map<std::string,std::vector<jbcoe::polymorphic_value<Panel>>>& categories, SharedResources& resources);
|
||||||
|
explicit PanelLayout(const std::vector<jbcoe::polymorphic_value<Panel>> panels, SharedResources& resources);
|
||||||
|
static PanelLayout red_empty_layout(SharedResources& resources);
|
||||||
|
static PanelLayout title_sort(const Data::SongList& song_list, SharedResources& resources);
|
||||||
|
private:
|
||||||
|
void fill_layout(SharedResources& resources);
|
||||||
|
};
|
||||||
|
}
|
@ -1,17 +1,17 @@
|
|||||||
#include "Ribbon.hpp"
|
#include "Ribbon.hpp"
|
||||||
|
|
||||||
#include "imgui/imgui.h"
|
|
||||||
#include "imgui-sfml/imgui-SFML.h"
|
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Panel.hpp"
|
#include <imgui/imgui.h>
|
||||||
|
#include <imgui-sfml/imgui-SFML.h>
|
||||||
|
|
||||||
#include "../../Data/Song.hpp"
|
#include "../../Data/Song.hpp"
|
||||||
#include "../../Toolkit/QuickRNG.hpp"
|
#include "../../Toolkit/QuickRNG.hpp"
|
||||||
|
#include "Panel.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace MusicSelect {
|
namespace MusicSelect {
|
||||||
@ -64,126 +64,29 @@ namespace MusicSelect {
|
|||||||
return clock.getElapsedTime() / m_time_factor > sf::milliseconds(300);
|
return clock.getElapsedTime() / m_time_factor > sf::milliseconds(300);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ribbon::Ribbon(SharedResources& t_resources) : HoldsSharedResources(t_resources) {
|
Ribbon::Ribbon(PanelLayout layout, SharedResources& resources) :
|
||||||
|
HoldsSharedResources(resources),
|
||||||
|
m_layout(layout)
|
||||||
|
{
|
||||||
std::cout << "Loaded MusicSelect::Ribbon" << std::endl;
|
std::cout << "Loaded MusicSelect::Ribbon" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ribbon::title_sort(const Data::SongList &song_list) {
|
|
||||||
std::vector<std::shared_ptr<const Data::Song>> songs;
|
std::size_t Ribbon::get_layout_column(const Data::Button& button) const {
|
||||||
for (auto &&song : song_list.songs) {
|
return (m_position + (Data::button_to_index(button) % 4)) % m_layout.size();
|
||||||
songs.push_back(song);
|
|
||||||
}
|
|
||||||
std::sort(
|
|
||||||
songs.begin(),
|
|
||||||
songs.end(),
|
|
||||||
[](std::shared_ptr<const Data::Song> a, std::shared_ptr<const Data::Song> b){return Data::Song::sort_by_title(*a, *b);}
|
|
||||||
);
|
|
||||||
std::map<std::string, std::vector<std::shared_ptr<Panel>>> categories;
|
|
||||||
for (const auto &song : songs) {
|
|
||||||
if (song->title.size() > 0) {
|
|
||||||
char letter = song->title[0];
|
|
||||||
if ('A' <= letter and letter <= 'Z') {
|
|
||||||
categories
|
|
||||||
[std::string(1, letter)]
|
|
||||||
.push_back(
|
|
||||||
std::make_shared<SongPanel>(m_resources, song)
|
|
||||||
);
|
|
||||||
} else if ('a' <= letter and letter <= 'z') {
|
|
||||||
categories
|
|
||||||
[std::string(1, 'A' + (letter - 'a'))]
|
|
||||||
.push_back(
|
|
||||||
std::make_shared<SongPanel>(m_resources, song)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
categories["?"].push_back(std::make_shared<SongPanel>(m_resources, song));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
categories["?"].push_back(std::make_shared<SongPanel>(m_resources, song));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
layout_from_category_map(categories);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ribbon::test_sort() {
|
jbcoe::polymorphic_value<Panel>& Ribbon::get_panel_under_button(const Data::Button& button) const {
|
||||||
m_layout.clear();
|
auto button_index = Data::button_to_index(button);
|
||||||
m_layout.push_back({
|
|
||||||
std::make_shared<EmptyPanel>(m_resources),
|
|
||||||
std::make_shared<CategoryPanel>(m_resources, "A"),
|
|
||||||
std::make_shared<CategoryPanel>(m_resources, "truc")
|
|
||||||
});
|
|
||||||
for (size_t i = 0; i < 3; i++) {
|
|
||||||
m_layout.push_back({
|
|
||||||
std::make_shared<EmptyPanel>(m_resources),
|
|
||||||
std::make_shared<EmptyPanel>(m_resources),
|
|
||||||
std::make_shared<EmptyPanel>(m_resources)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
fill_layout();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Ribbon::test2_sort() {
|
|
||||||
std::string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
||||||
std::map<std::string, std::vector<std::shared_ptr<Panel>>> categories;
|
|
||||||
Toolkit::UniformIntRNG category_size_generator{1, 10};
|
|
||||||
Toolkit::UniformIntRNG panel_hue_generator{0, 255};
|
|
||||||
for (auto &&letter : alphabet) {
|
|
||||||
auto category_size = category_size_generator.generate();
|
|
||||||
for (int i = 0; i < category_size; i++) {
|
|
||||||
categories[std::string(1, letter)].push_back(
|
|
||||||
std::make_shared<ColorPanel>(
|
|
||||||
m_resources,
|
|
||||||
sf::Color(
|
|
||||||
panel_hue_generator.generate(),
|
|
||||||
panel_hue_generator.generate(),
|
|
||||||
panel_hue_generator.generate()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
layout_from_category_map(categories);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::size_t Ribbon::get_layout_column(const std::size_t& button_index) const {
|
|
||||||
return (m_position + (button_index % 4)) % m_layout.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::shared_ptr<Panel> &Ribbon::get_panel_under_button(std::size_t button_index) const {
|
|
||||||
return (
|
return (
|
||||||
m_layout
|
m_layout
|
||||||
.at(this->get_layout_column(button_index))
|
.at(this->get_layout_column(button))
|
||||||
.at(button_index / 4)
|
.at(button_index / 4)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ribbon::click_on(std::size_t button_index) {
|
void Ribbon::click_on(const Data::Button& button) {
|
||||||
this->get_panel_under_button(button_index)->click(*this, button_index);
|
get_panel_under_button(button)->click(*this, button);
|
||||||
}
|
|
||||||
|
|
||||||
void Ribbon::layout_from_category_map(const std::map<std::string, std::vector<std::shared_ptr<Panel>>> &categories) {
|
|
||||||
m_layout.clear();
|
|
||||||
for (auto &&[category, panels] : categories) {
|
|
||||||
if (not panels.empty()) {
|
|
||||||
std::vector<std::shared_ptr<Panel>> current_column;
|
|
||||||
current_column.push_back(std::make_shared<CategoryPanel>(m_resources, category));
|
|
||||||
for (auto &&panel : panels) {
|
|
||||||
if (current_column.size() == 3) {
|
|
||||||
m_layout.push_back({current_column[0], current_column[1], current_column[2]});
|
|
||||||
current_column.clear();
|
|
||||||
} else {
|
|
||||||
current_column.push_back(std::move(panel));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (not current_column.empty()) {
|
|
||||||
while (current_column.size() < 3) {
|
|
||||||
current_column.push_back(std::make_shared<EmptyPanel>(m_resources));
|
|
||||||
}
|
|
||||||
m_layout.push_back({current_column[0], current_column[1], current_column[2]});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fill_layout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ribbon::move_right() {
|
void Ribbon::move_right() {
|
||||||
@ -202,10 +105,8 @@ namespace MusicSelect {
|
|||||||
m_move_animation.emplace(old_position, m_position, m_layout.size(), Direction::Left, m_time_factor);
|
m_move_animation.emplace(old_position, m_position, m_layout.size(), Direction::Left, m_time_factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ribbon::move_to_next_category(const std::size_t& from_button_index) {
|
void Ribbon::move_to_next_category(const Data::Button& button) {
|
||||||
std::size_t old_position = m_position;
|
std::size_t from_column = this->get_layout_column(button);
|
||||||
std::size_t from_column = this->get_layout_column(from_button_index);
|
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
size_t offset = 1;
|
size_t offset = 1;
|
||||||
// Cycle through the whole ribbon once starting on the column next to
|
// Cycle through the whole ribbon once starting on the column next to
|
||||||
@ -226,8 +127,10 @@ namespace MusicSelect {
|
|||||||
}
|
}
|
||||||
if (found) {
|
if (found) {
|
||||||
// we want the next category panel to land on the same column we clicked
|
// we want the next category panel to land on the same column we clicked
|
||||||
|
auto old_position = m_position;
|
||||||
|
auto button_index = Data::button_to_index(button);
|
||||||
auto next_category_column = from_column + offset;
|
auto next_category_column = from_column + offset;
|
||||||
auto onscreen_clicked_column = (from_button_index % 4);
|
auto onscreen_clicked_column = (button_index % 4);
|
||||||
m_position = next_category_column - onscreen_clicked_column;
|
m_position = next_category_column - onscreen_clicked_column;
|
||||||
m_move_animation.emplace(old_position, m_position, m_layout.size(), Direction::Right, m_time_factor);
|
m_move_animation.emplace(old_position, m_position, m_layout.size(), Direction::Right, m_time_factor);
|
||||||
}
|
}
|
||||||
@ -285,50 +188,8 @@ namespace MusicSelect {
|
|||||||
if (debug) {
|
if (debug) {
|
||||||
ImGui::Begin("Ribbon Debug", &debug); {
|
ImGui::Begin("Ribbon Debug", &debug); {
|
||||||
ImGui::SliderFloat("Time Slowdown Factor", &m_time_factor, 1.f, 10.f);
|
ImGui::SliderFloat("Time Slowdown Factor", &m_time_factor, 1.f, 10.f);
|
||||||
/*
|
|
||||||
if (ImGui::CollapsingHeader("Panels")) {
|
|
||||||
auto panel_size = static_cast<int>(m_panel_size);
|
|
||||||
if(ImGui::InputInt("Size", &panel_size)) {
|
|
||||||
if (panel_size < 0) {
|
|
||||||
panel_size = 0;
|
|
||||||
}
|
|
||||||
m_panel_size = static_cast<std::size_t>(panel_size);
|
|
||||||
}
|
|
||||||
auto panel_spacing = static_cast<int>(m_panel_spacing);
|
|
||||||
if(ImGui::InputInt("Spacing", &panel_spacing)) {
|
|
||||||
if (panel_spacing < 0) {
|
|
||||||
panel_spacing = 0;
|
|
||||||
}
|
|
||||||
m_panel_spacing = static_cast<std::size_t>(panel_spacing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obligatory steps before the drawing functions can use the layout without crashing
|
|
||||||
void Ribbon::fill_layout() {
|
|
||||||
if (m_layout.empty()) {
|
|
||||||
m_layout.push_back({
|
|
||||||
std::make_shared<ColoredMessagePanel>(m_resources, sf::Color::Red, "- EMPTY -"),
|
|
||||||
std::make_shared<ColoredMessagePanel>(m_resources, sf::Color::Red, "- EMPTY -"),
|
|
||||||
std::make_shared<ColoredMessagePanel>(m_resources, sf::Color::Red, "- EMPTY -"),
|
|
||||||
});
|
|
||||||
m_layout.push_back({
|
|
||||||
std::make_shared<ColoredMessagePanel>(m_resources, sf::Color::Red, "- EMPTY -"),
|
|
||||||
std::make_shared<ColoredMessagePanel>(m_resources, sf::Color::Red, "- EMPTY -"),
|
|
||||||
std::make_shared<ColoredMessagePanel>(m_resources, sf::Color::Red, "- EMPTY -"),
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while (m_layout.size() < 4) {
|
|
||||||
m_layout.push_back({
|
|
||||||
std::make_shared<EmptyPanel>(m_resources),
|
|
||||||
std::make_shared<EmptyPanel>(m_resources),
|
|
||||||
std::make_shared<EmptyPanel>(m_resources),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,15 @@
|
|||||||
#include <SFML/Graphics/Drawable.hpp>
|
#include <SFML/Graphics/Drawable.hpp>
|
||||||
#include <SFML/Graphics/Transformable.hpp>
|
#include <SFML/Graphics/Transformable.hpp>
|
||||||
|
|
||||||
|
#include "../../Data/Buttons.hpp"
|
||||||
#include "../../Data/Preferences.hpp"
|
#include "../../Data/Preferences.hpp"
|
||||||
#include "../../Data/Song.hpp"
|
#include "../../Data/Song.hpp"
|
||||||
#include "../../Toolkit/AffineTransform.hpp"
|
#include "../../Toolkit/AffineTransform.hpp"
|
||||||
#include "../../Toolkit/Debuggable.hpp"
|
#include "../../Toolkit/Debuggable.hpp"
|
||||||
#include "../../Toolkit/EasingFunctions.hpp"
|
#include "../../Toolkit/EasingFunctions.hpp"
|
||||||
#include "Panel.hpp"
|
|
||||||
#include "SharedResources.hpp"
|
#include "SharedResources.hpp"
|
||||||
|
#include "Panel.hpp"
|
||||||
|
#include "PanelLayout.hpp"
|
||||||
|
|
||||||
namespace MusicSelect {
|
namespace MusicSelect {
|
||||||
|
|
||||||
@ -33,29 +35,23 @@ namespace MusicSelect {
|
|||||||
Toolkit::AffineTransform<float> create_transform(int previous_pos, int next_pos, size_t ribbon_size, Direction direction);
|
Toolkit::AffineTransform<float> create_transform(int previous_pos, int next_pos, size_t ribbon_size, Direction direction);
|
||||||
};
|
};
|
||||||
|
|
||||||
// The Ribbon is the moving part of the Music Select Screen
|
// A Ribbon is a visual representation of a PanelLayout,
|
||||||
// It can be sorted in a number of ways
|
// You can scroll it using the left and right buttons
|
||||||
class Ribbon final : public sf::Drawable, public sf::Transformable, public HoldsSharedResources, public Toolkit::Debuggable {
|
class Ribbon : public sf::Drawable, public sf::Transformable, public HoldsSharedResources, public Toolkit::Debuggable {
|
||||||
public:
|
public:
|
||||||
Ribbon(SharedResources& t_resources);
|
Ribbon(PanelLayout layout, SharedResources& t_resources);
|
||||||
void title_sort(const Data::SongList& song_list);
|
jbcoe::polymorphic_value<Panel>& get_panel_under_button(const Data::Button& button) const;
|
||||||
void test_sort();
|
void click_on(const Data::Button& button);
|
||||||
void test2_sort();
|
|
||||||
void test_song_cover_sort();
|
|
||||||
const std::shared_ptr<MusicSelect::Panel>& get_panel_under_button(std::size_t button_index) const;
|
|
||||||
void click_on(std::size_t button_index);
|
|
||||||
void move_right();
|
void move_right();
|
||||||
void move_left();
|
void move_left();
|
||||||
void move_to_next_category(const std::size_t& from_button_index);
|
void move_to_next_category(const Data::Button& button);
|
||||||
void draw_debug() override;
|
void draw_debug() override;
|
||||||
private:
|
private:
|
||||||
void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
|
void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
|
||||||
void draw_with_animation(sf::RenderTarget& target, sf::RenderStates states) const;
|
void draw_with_animation(sf::RenderTarget& target, sf::RenderStates states) const;
|
||||||
void draw_without_animation(sf::RenderTarget& target, sf::RenderStates states) const;
|
void draw_without_animation(sf::RenderTarget& target, sf::RenderStates states) const;
|
||||||
void layout_from_category_map(const std::map<std::string,std::vector<std::shared_ptr<Panel>>>& categories);
|
std::size_t get_layout_column(const Data::Button& button) const;
|
||||||
void fill_layout();
|
PanelLayout m_layout;
|
||||||
std::size_t get_layout_column(const std::size_t& button_index) const;
|
|
||||||
std::vector<std::array<std::shared_ptr<Panel>,3>> m_layout;
|
|
||||||
std::size_t m_position = 0;
|
std::size_t m_position = 0;
|
||||||
mutable std::optional<MoveAnimation> m_move_animation;
|
mutable std::optional<MoveAnimation> m_move_animation;
|
||||||
float m_time_factor = 1.f;
|
float m_time_factor = 1.f;
|
||||||
|
18
src/Toolkit/ExtraCerealTypes/GHCFilesystemPath.hpp
Normal file
18
src/Toolkit/ExtraCerealTypes/GHCFilesystemPath.hpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <cereal/cereal.hpp>
|
||||||
|
#include <cereal/types/string.hpp>
|
||||||
|
#include <ghc/filesystem.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
template <class Archive>
|
||||||
|
std::string save_minimal(const Archive &, const ghc::filesystem::path & p) {
|
||||||
|
return p.string();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Archive>
|
||||||
|
void load_minimal(const Archive &, ghc::filesystem::path& p, const std::string& value) {
|
||||||
|
p = ghc::filesystem::path{value};
|
||||||
|
}
|
17
src/Toolkit/GHCFilesystemPathHash.hpp
Normal file
17
src/Toolkit/GHCFilesystemPathHash.hpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <ghc/filesystem.hpp>
|
||||||
|
|
||||||
|
namespace fs = ghc::filesystem;
|
||||||
|
|
||||||
|
// Define the way we hash fs::path for use in unordered maps
|
||||||
|
namespace std {
|
||||||
|
template <>
|
||||||
|
struct hash<fs::path> {
|
||||||
|
std::size_t operator()(const fs::path& p) const {
|
||||||
|
return std::hash<std::string>()(p.string());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user