comparison src/gui/pages/anime_list.cpp @ 10:4b198a111713

Update things actually compile now btw qttest wants to fuck over the model but that might be my fault so /shrug
author Paper <mrpapersonic@gmail.com>
date Sat, 16 Sep 2023 02:06:01 -0400
parents 5c0397762b53
children fc1bf97c528b
comparison
equal deleted inserted replaced
9:5c0397762b53 10:4b198a111713
18 #include "services/anilist.h" 18 #include "services/anilist.h"
19 #include <QHBoxLayout> 19 #include <QHBoxLayout>
20 #include <QHeaderView> 20 #include <QHeaderView>
21 #include <QMenu> 21 #include <QMenu>
22 #include <QProgressBar> 22 #include <QProgressBar>
23 #include <QDebug>
23 #include <QShortcut> 24 #include <QShortcut>
24 #include <QStylePainter> 25 #include <QStylePainter>
25 #include <QStyledItemDelegate> 26 #include <QStyledItemDelegate>
27 #include <QAbstractItemModelTester>
26 #include <cmath> 28 #include <cmath>
27 29
28 #if 0
29 AnimeListWidgetDelegate::AnimeListWidgetDelegate(QObject* parent) : QStyledItemDelegate(parent) { 30 AnimeListWidgetDelegate::AnimeListWidgetDelegate(QObject* parent) : QStyledItemDelegate(parent) {
30 } 31 }
31 32
32 QWidget* AnimeListWidgetDelegate::createEditor(QWidget*, const QStyleOptionViewItem&, const QModelIndex&) const { 33 QWidget* AnimeListWidgetDelegate::createEditor(QWidget*, const QStyleOptionViewItem&, const QModelIndex&) const {
33 // no edit 4 u 34 // no edit 4 u
78 case QMetaType::QString: 79 case QMetaType::QString:
79 default: return QString::compare(left.toString(), right.toString(), Qt::CaseInsensitive) < 0; 80 default: return QString::compare(left.toString(), right.toString(), Qt::CaseInsensitive) < 0;
80 } 81 }
81 } 82 }
82 83
83 AnimeListWidgetModel::AnimeListWidgetModel(QWidget* parent) : QAbstractListModel(parent) { 84 AnimeListWidgetModel::AnimeListWidgetModel(QWidget* parent, Anime::ListStatus _status) : QAbstractListModel(parent) {
85 status = _status;
84 return; 86 return;
85 } 87 }
86 88
87 int AnimeListWidgetModel::rowCount(const QModelIndex& parent) const { 89 int AnimeListWidgetModel::rowCount(const QModelIndex& parent) const {
88 int count = 0; 90 return list.size();
89 for (const auto& [id, anime] : Anime::db.items) {
90 if (anime.IsInUserList())
91 count++;
92 }
93 return count;
94 (void)(parent); 91 (void)(parent);
95 } 92 }
96 93
97 int AnimeListWidgetModel::columnCount(const QModelIndex& parent) const { 94 int AnimeListWidgetModel::columnCount(const QModelIndex& parent) const {
98 return NB_COLUMNS; 95 return NB_COLUMNS;
140 switch (role) { 137 switch (role) {
141 case Qt::DisplayRole: 138 case Qt::DisplayRole:
142 switch (index.column()) { 139 switch (index.column()) {
143 case AL_TITLE: return QString::fromUtf8(list[index.row()].GetUserPreferredTitle().c_str()); 140 case AL_TITLE: return QString::fromUtf8(list[index.row()].GetUserPreferredTitle().c_str());
144 case AL_PROGRESS: 141 case AL_PROGRESS:
145 return QString::number(list[index.row()].progress) + "/" + QString::number(list[index.row()].episodes); 142 return QString::number(list[index.row()].GetUserProgress()) + "/" + QString::number(list[index.row()].GetEpisodes());
146 case AL_EPISODES: return list[index.row()].episodes; 143 case AL_EPISODES: return list[index.row()].GetEpisodes();
147 case AL_SCORE: return list[index.row()].score; 144 case AL_SCORE: return list[index.row()].GetUserScore();
148 case AL_TYPE: return QString::fromStdString(Translate::TranslateSeriesFormat(list[index.row()].type)); 145 case AL_TYPE: return QString::fromStdString(Translate::TranslateSeriesFormat(list[index.row()].GetFormat()));
149 case AL_SEASON: 146 case AL_SEASON:
150 return QString::fromStdString(Translate::TranslateSeriesSeason(list[index.row()].season)) + " " + 147 return QString::fromStdString(Translate::TranslateSeriesSeason(list[index.row()].GetSeason())) + " " +
151 QString::number(list[index.row()].air_date.GetYear()); 148 QString::number(list[index.row()].GetAirDate().GetYear());
152 case AL_AVG_SCORE: return QString::number(list[index.row()].audience_score) + "%"; 149 case AL_AVG_SCORE: return QString::number(list[index.row()].GetAudienceScore()) + "%";
153 case AL_STARTED: return list[index.row()].started.GetAsQDate(); 150 case AL_STARTED: return list[index.row()].GetUserDateStarted().GetAsQDate();
154 case AL_COMPLETED: return list[index.row()].completed.GetAsQDate(); 151 case AL_COMPLETED: return list[index.row()].GetUserDateCompleted().GetAsQDate();
155 case AL_UPDATED: { 152 case AL_UPDATED: {
156 if (list[index.row()].updated == 0) 153 if (list[index.row()].GetUserTimeUpdated() == 0)
157 return QString("-"); 154 return QString("-");
158 Time::Duration duration(Time::GetSystemTime() - list[index.row()].updated); 155 Time::Duration duration(Time::GetSystemTime() - list[index.row()].GetUserTimeUpdated());
159 return QString::fromUtf8(duration.AsRelativeString().c_str()); 156 return QString::fromUtf8(duration.AsRelativeString().c_str());
160 } 157 }
161 case AL_NOTES: return QString::fromUtf8(list[index.row()].notes.c_str()); 158 case AL_NOTES: return QString::fromUtf8(list[index.row()].GetUserNotes().c_str());
162 default: return ""; 159 default: return "";
163 } 160 }
164 break; 161 break;
165 case Qt::UserRole: 162 case Qt::UserRole:
166 switch (index.column()) { 163 switch (index.column()) {
167 case AL_ID: return 164 case AL_ID: return list[index.row()].GetId();
168 case AL_PROGRESS: return list[index.row()].progress; 165 case AL_PROGRESS: return list[index.row()].GetUserProgress();
169 case AL_TYPE: return list[index.row()].type; 166 case AL_TYPE: return static_cast<int>(list[index.row()].GetFormat());
170 case AL_SEASON: return list[index.row()].air_date.GetAsQDate(); 167 case AL_SEASON: return list[index.row()].GetAirDate().GetAsQDate();
171 case AL_AVG_SCORE: return list[index.row()].audience_score; 168 case AL_AVG_SCORE: return list[index.row()].GetAudienceScore();
172 case AL_UPDATED: return list[index.row()].updated; 169 case AL_UPDATED: return list[index.row()].GetUserTimeUpdated();
173 default: return data(index, Qt::DisplayRole); 170 default: return data(index, Qt::DisplayRole);
174 } 171 }
175 break; 172 break;
176 case Qt::TextAlignmentRole: 173 case Qt::TextAlignmentRole:
177 switch (index.column()) { 174 switch (index.column()) {
192 } 189 }
193 return QVariant(); 190 return QVariant();
194 } 191 }
195 192
196 void AnimeListWidgetModel::UpdateAnime(int id) { 193 void AnimeListWidgetModel::UpdateAnime(int id) {
197 /* meh... it might be better to just redraw the entire list */ 194 /* meh... it might be better to just reinit the entire list */
198 int i = 0; 195 int i = 0;
199 for (const auto& [a_id, anime] : Anime:db.items) { 196 for (const auto& [a_id, anime] : Anime::db.items) {
200 if (anime.IsInUserList() && a_id == id && anime.GetUserStatus() == Anime::ListStatus::WATCHING) { 197 if (anime.IsInUserList() && a_id == id && anime.GetUserStatus() == status) {
201 emit dataChanged(index(i), index(i)); 198 emit dataChanged(index(i), index(i));
202 } 199 }
203 i++; 200 i++;
204 } 201 }
205 } 202 }
206 #endif 203
204 Anime::Anime* AnimeListWidgetModel::GetAnimeFromIndex(QModelIndex index) {
205 return &list.at(index.row());
206 }
207
208 void AnimeListWidgetModel::RefreshList() {
209 bool has_children = !!rowCount(index(0));
210 if (has_children) beginResetModel();
211 list.clear();
212
213 for (const auto& [id, anime] : Anime::db.items) {
214 if (anime.IsInUserList() && anime.GetUserStatus() == status) {
215 list.push_back(anime);
216 }
217 }
218 if (has_children) endResetModel();
219 }
207 220
208 int AnimeListWidget::VisibleColumnsCount() const { 221 int AnimeListWidget::VisibleColumnsCount() const {
209 int count = 0; 222 int count = 0;
210 223
211 for (int i = 0, end = tree_view->header()->count(); i < end; i++) { 224 for (int i = 0, end = tree_view->header()->count(); i < end; i++) {
227 tree_view->setColumnHidden(AnimeListWidgetModel::AL_AVG_SCORE, true); 240 tree_view->setColumnHidden(AnimeListWidgetModel::AL_AVG_SCORE, true);
228 tree_view->setColumnHidden(AnimeListWidgetModel::AL_STARTED, true); 241 tree_view->setColumnHidden(AnimeListWidgetModel::AL_STARTED, true);
229 tree_view->setColumnHidden(AnimeListWidgetModel::AL_COMPLETED, true); 242 tree_view->setColumnHidden(AnimeListWidgetModel::AL_COMPLETED, true);
230 tree_view->setColumnHidden(AnimeListWidgetModel::AL_UPDATED, true); 243 tree_view->setColumnHidden(AnimeListWidgetModel::AL_UPDATED, true);
231 tree_view->setColumnHidden(AnimeListWidgetModel::AL_NOTES, true); 244 tree_view->setColumnHidden(AnimeListWidgetModel::AL_NOTES, true);
245 tree_view->setColumnHidden(AnimeListWidgetModel::AL_ID, true);
232 } 246 }
233 247
234 void AnimeListWidget::DisplayColumnHeaderMenu() { 248 void AnimeListWidget::DisplayColumnHeaderMenu() {
235 QMenu* menu = new QMenu(this); 249 QMenu* menu = new QMenu(this);
236 menu->setAttribute(Qt::WA_DeleteOnClose); 250 menu->setAttribute(Qt::WA_DeleteOnClose);
237 menu->setTitle(tr("Column visibility")); 251 menu->setTitle(tr("Column visibility"));
238 menu->setToolTipsVisible(true); 252 menu->setToolTipsVisible(true);
239 253
240 for (int i = 0; i < AnimeListWidgetModel::NB_COLUMNS; i++) { 254 for (int i = 0; i < AnimeListWidgetModel::NB_COLUMNS; i++) {
241 if (i == AnimeListWidgetModel::AL_TITLE) 255 if (i == AnimeListWidgetModel::AL_TITLE || i == AnimeListWidgetModel::AL_ID)
242 continue; 256 continue;
243 const auto column_name = 257 const auto column_name =
244 sort_models[tab_bar->currentIndex()]->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(); 258 sort_models[tab_bar->currentIndex()]->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
245 QAction* action = menu->addAction(column_name, this, [this, i](const bool checked) { 259 QAction* action = menu->addAction(column_name, this, [this, i](const bool checked) {
246 if (!checked && (VisibleColumnsCount() <= 1)) 260 if (!checked && (VisibleColumnsCount() <= 1))
277 const QItemSelection selection = sort_models[tab_bar->currentIndex()]->mapSelectionToSource(tree_view->selectionModel()->selection()); 291 const QItemSelection selection = sort_models[tab_bar->currentIndex()]->mapSelectionToSource(tree_view->selectionModel()->selection());
278 if (!selection.indexes().first().isValid()) { 292 if (!selection.indexes().first().isValid()) {
279 return; 293 return;
280 } 294 }
281 295
282 /*
283 QAction* action = menu->addAction("Information", [this, selection] { 296 QAction* action = menu->addAction("Information", [this, selection] {
284 const QModelIndex index = ((AnimeListWidgetModel*)sort_models[tab_bar->currentIndex()]->sourceModel()) 297 const QModelIndex index = ((AnimeListWidgetModel*)sort_models[tab_bar->currentIndex()]->sourceModel())
285 ->index(selection.indexes().first().row()); 298 ->index(selection.indexes().first().row());
286 Anime::Anime* anime = 299 Anime::Anime* anime =
287 ((AnimeListWidgetModel*)sort_models[tab_bar->currentIndex()]->sourceModel())->GetAnimeFromIndex(index); 300 ((AnimeListWidgetModel*)sort_models[tab_bar->currentIndex()]->sourceModel())->GetAnimeFromIndex(index);
290 } 303 }
291 304
292 InformationDialog* dialog = new InformationDialog( 305 InformationDialog* dialog = new InformationDialog(
293 *anime, 306 *anime,
294 [this, anime] { 307 [this, anime] {
295 ((AnimeListWidgetModel*)sort_models[tab_bar->currentIndex()]->sourceModel())->UpdateAnime(*anime); 308 ((AnimeListWidgetModel*)sort_models[tab_bar->currentIndex()]->sourceModel())->UpdateAnime(anime->GetId());
296 }, 309 },
297 this); 310 this);
298 311
299 dialog->show(); 312 dialog->show();
300 dialog->raise(); 313 dialog->raise();
301 dialog->activateWindow(); 314 dialog->activateWindow();
302 }); 315 });
303 */
304 menu->popup(QCursor::pos()); 316 menu->popup(QCursor::pos());
305 } 317 }
306 318
307 void AnimeListWidget::ItemDoubleClicked() { 319 void AnimeListWidget::ItemDoubleClicked() {
308 /* throw out any other garbage */ 320 /* throw out any other garbage */
310 sort_models[tab_bar->currentIndex()]->mapSelectionToSource(tree_view->selectionModel()->selection()); 322 sort_models[tab_bar->currentIndex()]->mapSelectionToSource(tree_view->selectionModel()->selection());
311 if (!selection.indexes().first().isValid()) { 323 if (!selection.indexes().first().isValid()) {
312 return; 324 return;
313 } 325 }
314 326
315 /*
316 const QModelIndex index = ((AnimeListWidgetModel*)sort_models[tab_bar->currentIndex()]->sourceModel()) 327 const QModelIndex index = ((AnimeListWidgetModel*)sort_models[tab_bar->currentIndex()]->sourceModel())
317 ->index(selection.indexes().first().row()); 328 ->index(selection.indexes().first().row());
318 Anime::Anime* anime = 329 Anime::Anime* anime =
319 ((AnimeListWidgetModel*)sort_models[tab_bar->currentIndex()]->sourceModel())->GetAnimeFromIndex(index); 330 ((AnimeListWidgetModel*)sort_models[tab_bar->currentIndex()]->sourceModel())->GetAnimeFromIndex(index);
320 if (!anime) { 331
321 return; 332 InformationDialog* dialog = new InformationDialog(
322 } 333 *anime,
323 334 [this, anime] {
324 InformationDialog* dialog = new InformationDialog(*anime, [this, anime] { 335 ((AnimeListWidgetModel*)sort_models[tab_bar->currentIndex()]->sourceModel())->UpdateAnime(anime->GetId());
325 ((AnimeListWidgetModel*)sort_models[tab_bar->currentIndex()]->sourceModel())->UpdateAnime(*anime); 336 },
326 }, this); 337 this);
327 338
328 dialog->show(); 339 dialog->show();
329 dialog->raise(); 340 dialog->raise();
330 dialog->activateWindow(); 341 dialog->activateWindow();
331 */
332 } 342 }
333 343
334 void AnimeListWidget::paintEvent(QPaintEvent*) { 344 void AnimeListWidget::paintEvent(QPaintEvent*) {
335 QStylePainter p(this); 345 QStylePainter p(this);
336 346
396 AnimeListWidget::AnimeListWidget(QWidget* parent) : QWidget(parent) { 406 AnimeListWidget::AnimeListWidget(QWidget* parent) : QWidget(parent) {
397 /* Tab bar */ 407 /* Tab bar */
398 tab_bar = new QTabBar(this); 408 tab_bar = new QTabBar(this);
399 tab_bar->setExpanding(false); 409 tab_bar->setExpanding(false);
400 tab_bar->setDrawBase(false); 410 tab_bar->setDrawBase(false);
401 for (int i = 0; i < ARRAYSIZE(sort_models); i++) { 411
402 tab_bar->addTab(QString::fromStdString(Translate::TranslateListStatus(Anime::ListStatuses[i]))); 412 /* Tree view... */
403 413 QWidget* tree_widget = new QWidget(this);
404 /* Tree view... */ 414 tree_view = new QTreeView(tree_widget);
405 QWidget* tree_widget = new QWidget(this); 415 tree_view->setItemDelegate(new AnimeListWidgetDelegate(tree_view));
406 tree_view = new QTreeView(tree_widget); 416 tree_view->setUniformRowHeights(true);
407 tree_view->setItemDelegate(new AnimeListWidgetDelegate(tree_view)); 417 tree_view->setAllColumnsShowFocus(false);
408 tree_view->setUniformRowHeights(true); 418 tree_view->setAlternatingRowColors(true);
409 tree_view->setAllColumnsShowFocus(false); 419 tree_view->setSortingEnabled(true);
410 tree_view->setAlternatingRowColors(true); 420 tree_view->setSelectionMode(QAbstractItemView::ExtendedSelection);
411 tree_view->setSortingEnabled(true); 421 tree_view->setItemsExpandable(false);
412 tree_view->setSelectionMode(QAbstractItemView::ExtendedSelection); 422 tree_view->setRootIsDecorated(false);
413 tree_view->setItemsExpandable(false); 423 tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
414 tree_view->setRootIsDecorated(false); 424 tree_view->setFrameShape(QFrame::NoFrame);
415 tree_view->setContextMenuPolicy(Qt::CustomContextMenu); 425
416 tree_view->setFrameShape(QFrame::NoFrame); 426 for (unsigned int i = 0; i < ARRAYSIZE(sort_models); i++) {
417 427 tab_bar->addTab(QString::fromStdString(Translate::TranslateListStatus(Anime::ListStatuses[i])) + " (" + QString::number(Anime::db.GetListsAnimeAmount(Anime::ListStatuses[i])) + ")");
418 QHBoxLayout* layout = new QHBoxLayout; 428 sort_models[i] = new AnimeListWidgetSortFilter(tree_view);
419 layout->addWidget(tree_view); 429 AnimeListWidgetModel* model = new AnimeListWidgetModel(this, Anime::ListStatuses[i]);
420 layout->setMargin(0); 430 new QAbstractItemModelTester(model, QAbstractItemModelTester::FailureReportingMode::Fatal, this);
421 tree_widget->setLayout(layout); 431 sort_models[i]->setSourceModel(model);
422 432 sort_models[i]->setSortRole(Qt::UserRole);
423 /* Double click stuff */ 433 sort_models[i]->setSortCaseSensitivity(Qt::CaseInsensitive);
424 connect(tree_view, &QAbstractItemView::doubleClicked, this, &AnimeListWidget::ItemDoubleClicked); 434 }
425 connect(tree_view, &QWidget::customContextMenuRequested, this, &AnimeListWidget::DisplayListMenu); 435
426 436 QHBoxLayout* layout = new QHBoxLayout;
427 /* Enter & return keys */ 437 layout->addWidget(tree_view);
428 connect(new QShortcut(Qt::Key_Return, tree_view, nullptr, nullptr, Qt::WidgetShortcut), &QShortcut::activated, 438 layout->setMargin(0);
429 this, &AnimeListWidget::ItemDoubleClicked); 439 tree_widget->setLayout(layout);
430 440
431 connect(new QShortcut(Qt::Key_Enter, tree_view, nullptr, nullptr, Qt::WidgetShortcut), &QShortcut::activated, 441 /* Double click stuff */
432 this, &AnimeListWidget::ItemDoubleClicked); 442 connect(tree_view, &QAbstractItemView::doubleClicked, this, &AnimeListWidget::ItemDoubleClicked);
433 443 connect(tree_view, &QWidget::customContextMenuRequested, this, &AnimeListWidget::DisplayListMenu);
434 tree_view->header()->setStretchLastSection(false); 444
435 tree_view->header()->setContextMenuPolicy(Qt::CustomContextMenu); 445 /* Enter & return keys */
436 connect(tree_view->header(), &QWidget::customContextMenuRequested, this, 446 connect(new QShortcut(Qt::Key_Return, tree_view, nullptr, nullptr, Qt::WidgetShortcut), &QShortcut::activated,
437 &AnimeListWidget::DisplayColumnHeaderMenu); 447 this, &AnimeListWidget::ItemDoubleClicked);
438 448
439 connect(tab_bar, &QTabBar::currentChanged, this, [this](int index) { 449 connect(new QShortcut(Qt::Key_Enter, tree_view, nullptr, nullptr, Qt::WidgetShortcut), &QShortcut::activated,
440 if (sort_models[index]) 450 this, &AnimeListWidget::ItemDoubleClicked);
441 tree_view->setModel(sort_models[index]); 451
442 }); 452 tree_view->header()->setStretchLastSection(false);
443 453 tree_view->header()->setContextMenuPolicy(Qt::CustomContextMenu);
444 setFocusPolicy(Qt::TabFocus); 454 connect(tree_view->header(), &QWidget::customContextMenuRequested, this,
445 setFocusProxy(tab_bar); 455 &AnimeListWidget::DisplayColumnHeaderMenu);
446 } 456
447 457 connect(tab_bar, &QTabBar::currentChanged, this, [this](int index) {
448 void AnimeListWidget::UpdateAnimeList() { 458 if (sort_models[index])
449 for (unsigned int i = 0; i < ARRAYSIZE(sort_models); i++) { 459 tree_view->setModel(sort_models[index]);
450 sort_models[i] = new AnimeListWidgetSortFilter(tree_view); 460 });
451 sort_models[i]->setSourceModel(new AnimeListWidgetModel(this, &anime_lists[i])); 461
452 sort_models[i]->setSortRole(Qt::UserRole); 462 setFocusPolicy(Qt::TabFocus);
453 sort_models[i]->setSortCaseSensitivity(Qt::CaseInsensitive); 463 setFocusProxy(tab_bar);
454 } 464 }
455 if (ARRAYSIZE(sort_models) > 0) 465
456 tree_view->setModel(sort_models[0]); 466 void AnimeListWidget::RefreshList() {
457 SetColumnDefaults(); 467 for (unsigned int i = 0; i < ARRAYSIZE(sort_models); i++) {
458 SetupLayout(); 468 ((AnimeListWidgetModel*)sort_models[i]->sourceModel())->RefreshList();
459 } 469 }
460 470 }
461 void AnimeListWidget::Reset() { 471
462 while (tab_bar->count()) 472 void AnimeListWidget::Reset() {
463 tab_bar->removeTab(0); 473 while (tab_bar->count())
464 for (int i = 0; i < ARRAYSIZE(sort_models); i++) 474 tab_bar->removeTab(0);
465 delete sort_models[i]; 475 for (unsigned int i = 0; i < ARRAYSIZE(sort_models); i++)
466 } 476 delete sort_models[i];
477 }
467 478
468 #include "gui/pages/moc_anime_list.cpp" 479 #include "gui/pages/moc_anime_list.cpp"