Merge 9a3825c622
into 53b54406bd
This commit is contained in:
commit
dbf9cd4947
|
@ -195,7 +195,8 @@ JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getBannerHe
|
|||
JNIEXPORT jlong JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_model_GameFile_getTimePlayedMsInternal(JNIEnv* env, jobject obj)
|
||||
{
|
||||
const std::chrono::milliseconds time = TimePlayed().GetTimePlayed(GetRef(env, obj)->GetGameID());
|
||||
const std::chrono::milliseconds time =
|
||||
TimePlayedManager::GetInstance().GetTimePlayed(GetRef(env, obj)->GetGameID());
|
||||
return time.count();
|
||||
}
|
||||
|
||||
|
|
|
@ -75,33 +75,26 @@ void CPUManager::StartTimePlayedTimer()
|
|||
// Steady clock for greater accuracy of timing
|
||||
std::chrono::steady_clock timer;
|
||||
auto prev_time = timer.now();
|
||||
auto& time_played_manager = TimePlayedManager::GetInstance();
|
||||
|
||||
while (true)
|
||||
while (m_state != State::PowerDown)
|
||||
{
|
||||
TimePlayed time_played;
|
||||
m_time_played_finish_sync.WaitFor(std::chrono::seconds(10));
|
||||
auto curr_time = timer.now();
|
||||
|
||||
// Check that emulation is not paused
|
||||
// If the emulation is paused, wait for SetStepping() to reactivate
|
||||
if (m_state == State::Running)
|
||||
{
|
||||
const std::string game_id = SConfig::GetInstance().GetGameID();
|
||||
const auto diff_time =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(curr_time - prev_time);
|
||||
time_played.AddTime(game_id, diff_time);
|
||||
}
|
||||
else if (m_state == State::Stepping)
|
||||
const std::string game_id = SConfig::GetInstance().GetGameID();
|
||||
const auto diff_time =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(curr_time - prev_time);
|
||||
time_played_manager.AddTime(game_id, diff_time);
|
||||
|
||||
// If the emulation is paused, wait for SetStateLocked() to reactivate
|
||||
if (m_state == State::Stepping)
|
||||
{
|
||||
m_time_played_finish_sync.Wait();
|
||||
curr_time = timer.now();
|
||||
}
|
||||
|
||||
prev_time = curr_time;
|
||||
|
||||
if (m_state == State::PowerDown)
|
||||
return;
|
||||
|
||||
m_time_played_finish_sync.WaitFor(std::chrono::seconds(30));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,17 +286,27 @@ void CPUManager::StepOpcode(Common::Event* event)
|
|||
}
|
||||
|
||||
// Requires m_state_change_lock
|
||||
bool CPUManager::SetStateLocked(State s)
|
||||
bool CPUManager::SetStateLocked(const State s)
|
||||
{
|
||||
if (m_state == State::PowerDown)
|
||||
return false;
|
||||
if (s == State::Stepping)
|
||||
m_system.GetPowerPC().GetBreakPoints().ClearTemporary();
|
||||
m_state = s;
|
||||
|
||||
// CPUThreadGuard is used in various places to avoid racing with the CPU thread. CPUThreadGuard
|
||||
// can indirectly call SetStateLocked, which can result in it getting called with the same state
|
||||
// that m_state already had. Since m_time_played_finish_sync only needs to be Set when m_state
|
||||
// changes, avoid doing so when it hasn't.
|
||||
if (m_state != s)
|
||||
{
|
||||
m_state = s;
|
||||
m_time_played_finish_sync.Set();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPUManager::SetStepping(bool stepping)
|
||||
void CPUManager::SetStepping(const bool stepping)
|
||||
{
|
||||
std::lock_guard stepping_lock(m_stepping_lock);
|
||||
std::unique_lock state_lock(m_state_change_lock);
|
||||
|
@ -322,7 +325,6 @@ void CPUManager::SetStepping(bool stepping)
|
|||
else if (SetStateLocked(State::Running))
|
||||
{
|
||||
m_state_cpu_cvar.notify_one();
|
||||
m_time_played_finish_sync.Set();
|
||||
RunAdjacentSystems(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,39 +4,74 @@
|
|||
#include "Core/TimePlayed.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/HookableEvent.h"
|
||||
#include "Common/IniFile.h"
|
||||
#include "Common/NandPaths.h"
|
||||
|
||||
TimePlayed::TimePlayed() : m_ini_path(File::GetUserPath(D_CONFIG_IDX) + "TimePlayed.ini")
|
||||
{
|
||||
Reload();
|
||||
}
|
||||
static constexpr std::chrono::milliseconds MAX_TIME_PLAYED =
|
||||
std::chrono::hours(TimePlayedManager::MAX_HOURS) + std::chrono::minutes(59);
|
||||
|
||||
TimePlayed::~TimePlayed() = default;
|
||||
|
||||
void TimePlayed::AddTime(const std::string& game_id, std::chrono::milliseconds time_emulated)
|
||||
{
|
||||
std::string filtered_game_id = Common::EscapeFileName(game_id);
|
||||
u64 previous_time;
|
||||
m_time_list->Get(filtered_game_id, &previous_time);
|
||||
m_time_list->Set(filtered_game_id, previous_time + static_cast<u64>(time_emulated.count()));
|
||||
m_ini.Save(m_ini_path);
|
||||
}
|
||||
|
||||
std::chrono::milliseconds TimePlayed::GetTimePlayed(const std::string& game_id) const
|
||||
{
|
||||
std::string filtered_game_id = Common::EscapeFileName(game_id);
|
||||
u64 previous_time;
|
||||
m_time_list->Get(filtered_game_id, &previous_time);
|
||||
return std::chrono::milliseconds(previous_time);
|
||||
}
|
||||
|
||||
void TimePlayed::Reload()
|
||||
TimePlayedManager::TimePlayedManager()
|
||||
: m_ini_path(File::GetUserPath(D_CONFIG_IDX) + "TimePlayed.ini")
|
||||
{
|
||||
m_ini.Load(m_ini_path);
|
||||
m_time_list = m_ini.GetOrCreateSection("TimePlayed");
|
||||
}
|
||||
|
||||
TimePlayedManager::~TimePlayedManager() = default;
|
||||
|
||||
TimePlayedManager& TimePlayedManager::GetInstance()
|
||||
{
|
||||
static TimePlayedManager time_played_manager;
|
||||
return time_played_manager;
|
||||
}
|
||||
|
||||
void TimePlayedManager::AddTime(const std::string& game_id,
|
||||
const std::chrono::milliseconds time_emulated)
|
||||
{
|
||||
const std::string filtered_game_id = Common::EscapeFileName(game_id);
|
||||
u64 previous_time;
|
||||
std::chrono::milliseconds capped_new_time;
|
||||
|
||||
{
|
||||
std::lock_guard guard(m_mutex);
|
||||
|
||||
m_time_list->Get(filtered_game_id, &previous_time);
|
||||
const auto new_time = std::chrono::milliseconds(previous_time) + time_emulated;
|
||||
capped_new_time = std::min(MAX_TIME_PLAYED, new_time);
|
||||
m_time_list->Set(filtered_game_id, static_cast<u64>(capped_new_time.count()));
|
||||
m_ini.Save(m_ini_path);
|
||||
}
|
||||
|
||||
UpdateEvent::Trigger(filtered_game_id, capped_new_time);
|
||||
}
|
||||
|
||||
void TimePlayedManager::SetTimePlayed(const std::string& game_id,
|
||||
const std::chrono::milliseconds time_played)
|
||||
{
|
||||
const std::string filtered_game_id = Common::EscapeFileName(game_id);
|
||||
const std::chrono::milliseconds capped_time_played = std::min(MAX_TIME_PLAYED, time_played);
|
||||
|
||||
{
|
||||
std::lock_guard guard(m_mutex);
|
||||
m_time_list->Set(filtered_game_id, static_cast<u64>(capped_time_played.count()));
|
||||
m_ini.Save(m_ini_path);
|
||||
}
|
||||
|
||||
UpdateEvent::Trigger(filtered_game_id, capped_time_played);
|
||||
}
|
||||
|
||||
std::chrono::milliseconds TimePlayedManager::GetTimePlayed(const std::string& game_id) const
|
||||
{
|
||||
const std::string filtered_game_id = Common::EscapeFileName(game_id);
|
||||
u64 previous_time;
|
||||
|
||||
std::lock_guard guard(m_mutex);
|
||||
m_time_list->Get(filtered_game_id, &previous_time);
|
||||
return std::chrono::milliseconds(previous_time);
|
||||
}
|
||||
|
|
|
@ -4,32 +4,41 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/HookableEvent.h"
|
||||
#include "Common/IniFile.h"
|
||||
|
||||
class TimePlayed
|
||||
class TimePlayedManager
|
||||
{
|
||||
public:
|
||||
TimePlayed();
|
||||
TimePlayedManager(const TimePlayedManager& other) = delete;
|
||||
TimePlayedManager(TimePlayedManager&& other) = delete;
|
||||
TimePlayedManager& operator=(const TimePlayedManager& other) = delete;
|
||||
TimePlayedManager& operator=(TimePlayedManager&& other) = delete;
|
||||
|
||||
// not copyable due to the stored section pointer
|
||||
TimePlayed(const TimePlayed& other) = delete;
|
||||
TimePlayed(TimePlayed&& other) = delete;
|
||||
TimePlayed& operator=(const TimePlayed& other) = delete;
|
||||
TimePlayed& operator=(TimePlayed&& other) = delete;
|
||||
~TimePlayedManager();
|
||||
|
||||
~TimePlayed();
|
||||
static TimePlayedManager& GetInstance();
|
||||
|
||||
void AddTime(const std::string& game_id, std::chrono::milliseconds time_emulated);
|
||||
|
||||
void SetTimePlayed(const std::string& game_id, std::chrono::milliseconds time_played);
|
||||
|
||||
std::chrono::milliseconds GetTimePlayed(const std::string& game_id) const;
|
||||
|
||||
void Reload();
|
||||
using UpdateEvent =
|
||||
Common::HookableEvent<"Time Played Update", const std::string&, std::chrono::milliseconds>;
|
||||
|
||||
static constexpr int MAX_HOURS = 999'999;
|
||||
|
||||
private:
|
||||
TimePlayedManager();
|
||||
|
||||
std::string m_ini_path;
|
||||
mutable std::mutex m_mutex;
|
||||
Common::IniFile m_ini;
|
||||
Common::IniFile::Section* m_time_list;
|
||||
};
|
||||
|
|
|
@ -3,17 +3,25 @@
|
|||
|
||||
#include "DolphinQt/Config/InfoWidget.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDir>
|
||||
#include <QFormLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QSpinBox>
|
||||
#include <QString>
|
||||
#include <QTextEdit>
|
||||
|
||||
#include "Common/HookableEvent.h"
|
||||
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/TimePlayed.h"
|
||||
|
||||
#include "DiscIO/Blob.h"
|
||||
#include "DiscIO/Enums.h"
|
||||
|
@ -22,6 +30,7 @@
|
|||
|
||||
#include "DolphinQt/QtUtils/DolphinFileDialog.h"
|
||||
#include "DolphinQt/QtUtils/ImageConverter.h"
|
||||
#include "DolphinQt/QtUtils/QueueOnObject.h"
|
||||
|
||||
#include "UICommon/UICommon.h"
|
||||
|
||||
|
@ -37,10 +46,17 @@ InfoWidget::InfoWidget(const UICommon::GameFile& game) : m_game(game)
|
|||
if (!game.GetLanguages().empty())
|
||||
layout->addWidget(CreateBannerDetails());
|
||||
|
||||
layout->addWidget(CreateTimePlayedDetails());
|
||||
|
||||
layout->addStretch(1);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
InfoWidget::~InfoWidget() = default;
|
||||
InfoWidget::~InfoWidget()
|
||||
{
|
||||
m_time_played_update_event.reset();
|
||||
}
|
||||
|
||||
QGroupBox* InfoWidget::CreateFileDetails()
|
||||
{
|
||||
|
@ -181,6 +197,78 @@ QGroupBox* InfoWidget::CreateBannerDetails()
|
|||
return group;
|
||||
}
|
||||
|
||||
QGroupBox* InfoWidget::CreateTimePlayedDetails()
|
||||
{
|
||||
const auto set_time_played = [this]() {
|
||||
const int hours = m_hours_played->text().toInt();
|
||||
const int minutes = m_minutes_played->text().toInt();
|
||||
const std::chrono::milliseconds time_played =
|
||||
std::chrono::hours(hours) + std::chrono::minutes(minutes);
|
||||
TimePlayedManager::GetInstance().SetTimePlayed(m_game.GetGameID(), time_played);
|
||||
};
|
||||
|
||||
auto* const time_played_label = new QLabel(tr("Time Played:"));
|
||||
|
||||
m_hours_played = new QSpinBox;
|
||||
m_hours_played->setRange(0, TimePlayedManager::MAX_HOURS);
|
||||
connect(m_hours_played, &QSpinBox::valueChanged, this, set_time_played);
|
||||
|
||||
m_minutes_played = new QSpinBox;
|
||||
m_minutes_played->setRange(0, 59);
|
||||
connect(m_minutes_played, &QSpinBox::valueChanged, this, set_time_played);
|
||||
|
||||
auto* const hours_label = new QLabel(tr("Hours"));
|
||||
hours_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
auto* const minutes_label = new QLabel(tr("Minutes"));
|
||||
minutes_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
auto* const time_played_layout = new QHBoxLayout;
|
||||
time_played_layout->addWidget(m_hours_played);
|
||||
time_played_layout->addWidget(hours_label);
|
||||
time_played_layout->addWidget(m_minutes_played);
|
||||
time_played_layout->addWidget(minutes_label);
|
||||
|
||||
auto* const layout = new QFormLayout;
|
||||
layout->addRow(time_played_label, time_played_layout);
|
||||
|
||||
const auto update_time_played = [this](const std::string& game_id,
|
||||
const std::chrono::milliseconds time_played) {
|
||||
if (game_id != m_game.GetGameID())
|
||||
return;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
const auto hours_played = time_played / 1h;
|
||||
const auto minutes_played = (time_played % 60min) / 1min;
|
||||
|
||||
QSignalBlocker hours_blocker(m_hours_played);
|
||||
QSignalBlocker minutes_blocker(m_minutes_played);
|
||||
|
||||
m_hours_played->setValue(hours_played);
|
||||
m_minutes_played->setValue(minutes_played);
|
||||
};
|
||||
|
||||
const std::chrono::milliseconds time_played =
|
||||
TimePlayedManager::GetInstance().GetTimePlayed(m_game.GetGameID());
|
||||
update_time_played(m_game.GetGameID(), time_played);
|
||||
|
||||
const auto update_time_played_on_host_thread =
|
||||
[this, update_time_played](const std::string& game_id,
|
||||
const std::chrono::milliseconds time_played) {
|
||||
QueueOnObject(this, [update_time_played, game_id, time_played]() {
|
||||
update_time_played(game_id, time_played);
|
||||
});
|
||||
};
|
||||
m_time_played_update_event =
|
||||
TimePlayedManager::UpdateEvent::Register(update_time_played_on_host_thread, "InfoWidget");
|
||||
|
||||
auto* const group_box = new QGroupBox(tr("Time Played Details"));
|
||||
group_box->setLayout(layout);
|
||||
layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
|
||||
return group_box;
|
||||
}
|
||||
|
||||
QWidget* InfoWidget::CreateBannerGraphic(const QPixmap& image)
|
||||
{
|
||||
QWidget* widget = new QWidget();
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include <QWidget>
|
||||
|
||||
#include "Common/HookableEvent.h"
|
||||
|
||||
#include "UICommon/GameFile.h"
|
||||
|
||||
namespace DiscIO
|
||||
|
@ -19,6 +21,7 @@ class QComboBox;
|
|||
class QGroupBox;
|
||||
class QLineEdit;
|
||||
class QPixmap;
|
||||
class QSpinBox;
|
||||
class QTextEdit;
|
||||
|
||||
class InfoWidget final : public QWidget
|
||||
|
@ -35,6 +38,7 @@ private:
|
|||
QGroupBox* CreateFileDetails();
|
||||
QGroupBox* CreateGameDetails();
|
||||
QGroupBox* CreateBannerDetails();
|
||||
QGroupBox* CreateTimePlayedDetails();
|
||||
QLineEdit* CreateValueDisplay(const QString& value);
|
||||
QLineEdit* CreateValueDisplay(const std::string& value = "");
|
||||
void CreateLanguageSelector();
|
||||
|
@ -46,4 +50,8 @@ private:
|
|||
QLineEdit* m_name = {};
|
||||
QLineEdit* m_maker = {};
|
||||
QTextEdit* m_description = {};
|
||||
QSpinBox* m_hours_played = {};
|
||||
QSpinBox* m_minutes_played = {};
|
||||
|
||||
Common::EventHook m_time_played_update_event;
|
||||
};
|
||||
|
|
|
@ -3,18 +3,21 @@
|
|||
|
||||
#include "DolphinQt/GameList/GameListModel.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QPixmap>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/TimePlayed.h"
|
||||
|
||||
#include "DiscIO/Enums.h"
|
||||
|
||||
#include "DolphinQt/QtUtils/ImageConverter.h"
|
||||
#include "DolphinQt/QtUtils/QueueOnObject.h"
|
||||
#include "DolphinQt/Resources.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
||||
|
@ -23,7 +26,8 @@
|
|||
|
||||
const QSize GAMECUBE_BANNER_SIZE(96, 32);
|
||||
|
||||
GameListModel::GameListModel(QObject* parent) : QAbstractTableModel(parent)
|
||||
GameListModel::GameListModel(QObject* parent)
|
||||
: QAbstractTableModel(parent), m_time_played_manager(TimePlayedManager::GetInstance())
|
||||
{
|
||||
connect(&m_tracker, &GameTracker::GameLoaded, this, &GameListModel::AddGame);
|
||||
connect(&m_tracker, &GameTracker::GameUpdated, this, &GameListModel::UpdateGame);
|
||||
|
@ -34,8 +38,6 @@ GameListModel::GameListModel(QObject* parent) : QAbstractTableModel(parent)
|
|||
&GameTracker::RefreshAll);
|
||||
connect(&Settings::Instance(), &Settings::TitleDBReloadRequested,
|
||||
[this] { m_title_database = Core::TitleDatabase(); });
|
||||
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
|
||||
&GameListModel::OnEmulationStateChanged);
|
||||
|
||||
for (const QString& dir : Settings::Instance().GetPaths())
|
||||
m_tracker.AddDirectory(dir);
|
||||
|
@ -49,6 +51,28 @@ GameListModel::GameListModel(QObject* parent) : QAbstractTableModel(parent)
|
|||
emit layoutChanged();
|
||||
});
|
||||
|
||||
const auto on_time_played_update = [this](const std::string& game_id,
|
||||
const std::chrono::milliseconds) {
|
||||
const auto update_cell = [this, game_id]() {
|
||||
for (int model_row = 0; model_row < m_games.size(); ++model_row)
|
||||
{
|
||||
if (game_id != m_games[model_row]->GetGameID())
|
||||
continue;
|
||||
|
||||
const QModelIndex time_played_index =
|
||||
index(model_row, static_cast<int>(Column::TimePlayed));
|
||||
emit dataChanged(time_played_index, time_played_index);
|
||||
|
||||
// Multiple entries in the GameList can have the same GameID, so don't break out of the
|
||||
// loop when a match is found.
|
||||
}
|
||||
};
|
||||
QueueOnObject(this, update_cell);
|
||||
};
|
||||
|
||||
m_time_played_update_event =
|
||||
TimePlayedManager::UpdateEvent::Register(on_time_played_update, "GameListModel");
|
||||
|
||||
auto& settings = Settings::GetQSettings();
|
||||
|
||||
m_tag_list = settings.value(QStringLiteral("gamelist/tags")).toStringList();
|
||||
|
@ -195,19 +219,22 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const
|
|||
if (role == Qt::DisplayRole)
|
||||
{
|
||||
const std::string game_id = game.GetGameID();
|
||||
const std::chrono::milliseconds total_time = m_timer.GetTimePlayed(game_id);
|
||||
const std::chrono::milliseconds total_time = m_time_played_manager.GetTimePlayed(game_id);
|
||||
const auto total_minutes = std::chrono::duration_cast<std::chrono::minutes>(total_time);
|
||||
const auto total_hours = std::chrono::duration_cast<std::chrono::hours>(total_time);
|
||||
const auto total_seconds = std::chrono::duration_cast<std::chrono::seconds>(total_time);
|
||||
|
||||
// i18n: A time displayed as hours and minutes
|
||||
QString formatted_time =
|
||||
tr("%1h %2m").arg(total_hours.count()).arg(total_minutes.count() % 60);
|
||||
QString formatted_time = tr("%1h %2m %3s")
|
||||
.arg(total_hours.count())
|
||||
.arg(total_minutes.count() % 60)
|
||||
.arg(total_seconds.count() % 60);
|
||||
return formatted_time;
|
||||
}
|
||||
if (role == SORT_ROLE)
|
||||
{
|
||||
const std::string game_id = game.GetGameID();
|
||||
return static_cast<qlonglong>(m_timer.GetTimePlayed(game_id).count());
|
||||
return static_cast<qlonglong>(m_time_played_manager.GetTimePlayed(game_id).count());
|
||||
}
|
||||
break;
|
||||
case Column::Tags:
|
||||
|
@ -507,11 +534,3 @@ void GameListModel::PurgeCache()
|
|||
{
|
||||
m_tracker.PurgeCache();
|
||||
}
|
||||
|
||||
void GameListModel::OnEmulationStateChanged(Core::State state)
|
||||
{
|
||||
if (state == Core::State::Uninitialized)
|
||||
{
|
||||
m_timer.Reload();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
|
||||
#include "Core/Core.h"
|
||||
#include "Core/TimePlayed.h"
|
||||
#include "Core/TitleDatabase.h"
|
||||
|
||||
|
@ -90,15 +89,14 @@ private:
|
|||
// Index in m_games, or -1 if it isn't found
|
||||
int FindGameIndex(const std::string& path) const;
|
||||
|
||||
void OnEmulationStateChanged(Core::State state);
|
||||
|
||||
QStringList m_tag_list;
|
||||
QMap<QString, QVariant> m_game_tags;
|
||||
|
||||
GameTracker m_tracker;
|
||||
QList<std::shared_ptr<const UICommon::GameFile>> m_games;
|
||||
Core::TitleDatabase m_title_database;
|
||||
TimePlayed m_timer;
|
||||
TimePlayedManager& m_time_played_manager;
|
||||
Common::EventHook m_time_played_update_event;
|
||||
QString m_term;
|
||||
float m_scale = 1.0;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue