Mercurial > minori
comparison src/gui/pages/anime_list.cc @ 374:f7bb2978de48
gui/pages/anime_list: add Search right-click menu, don't create menu items that do nothing
| author | Paper <paper@tflc.us> |
|---|---|
| date | Fri, 25 Jul 2025 11:03:34 -0400 |
| parents | ea3a74ed2ef9 |
| children | a0bc3ae5164a |
comparison
equal
deleted
inserted
replaced
| 373:fbc8c617de80 | 374:f7bb2978de48 |
|---|---|
| 53 queue_mutex_.lock(); | 53 queue_mutex_.lock(); |
| 54 while (!queue_.empty() && !isInterruptionRequested()) { | 54 while (!queue_.empty() && !isInterruptionRequested()) { |
| 55 int id = queue_.front(); | 55 int id = queue_.front(); |
| 56 | 56 |
| 57 /* unlock the mutex for a long blocking operation, so items | 57 /* unlock the mutex for a long blocking operation, so items |
| 58 * can be added without worry */ | 58 * can be added without worry |
| 59 * | |
| 60 * NOTE: this code is duplicated elsewhere; is it better to | |
| 61 * have lots of threads, or just one main "worker" thread? */ | |
| 59 queue_mutex_.unlock(); | 62 queue_mutex_.unlock(); |
| 60 Services::UpdateAnimeEntry(id); | 63 Services::UpdateAnimeEntry(id); |
| 61 queue_mutex_.lock(); | 64 queue_mutex_.lock(); |
| 62 | 65 |
| 63 queue_.pop(); | 66 queue_.pop(); |
| 359 // ---------------- | 362 // ---------------- |
| 360 // Invert selection | 363 // Invert selection |
| 361 // ---------------- | 364 // ---------------- |
| 362 // Delete from list... <Del> | 365 // Delete from list... <Del> |
| 363 } else if (animes.size() > 0) { | 366 } else if (animes.size() > 0) { |
| 364 // menu in Taiga: | |
| 365 // | |
| 366 // Information | |
| 367 // Search -> | |
| 368 // AniDB | |
| 369 // AniList | |
| 370 // Anime News Network | |
| 371 // Kitsu | |
| 372 // MyAnimeList | |
| 373 // Reddit | |
| 374 // Wikipedia | |
| 375 // YouTube | |
| 376 // ---------------- | |
| 377 // Custom RSS feed | |
| 378 // Nyaa.si | |
| 379 // ---------------- | |
| 380 // Edit | |
| 381 // Delete from list... <Del> | |
| 382 // ---------------- | |
| 383 // Open folder <Ctrl+O> | |
| 384 // Scan available episodes <F5> | |
| 385 // ---------------- | |
| 386 // Play episode -> | |
| 387 // grid of episodes (dunno how to implement this) | |
| 388 // Play last episode (#<episode>) | |
| 389 // Play next episode (#<episode>) <Ctrl+N> | |
| 390 // Play random episode <Ctrl+R> (why?) | |
| 391 | |
| 392 Anime::Anime *anime = *animes.begin(); | 367 Anime::Anime *anime = *animes.begin(); |
| 368 std::optional<std::filesystem::path> path = Library::db.GetAnimeFolder(anime->GetId()); | |
| 393 | 369 |
| 394 menu->addAction(tr("Information"), [this, anime] { | 370 menu->addAction(tr("Information"), [this, anime] { |
| 395 InformationDialog *dialog = new InformationDialog( | 371 InformationDialog *dialog = new InformationDialog( |
| 396 anime, [this](Anime::Anime *anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO, | 372 anime, [this](Anime::Anime *anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO, |
| 397 this); | 373 this); |
| 399 dialog->show(); | 375 dialog->show(); |
| 400 dialog->raise(); | 376 dialog->raise(); |
| 401 dialog->activateWindow(); | 377 dialog->activateWindow(); |
| 402 connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater); | 378 connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater); |
| 403 }); | 379 }); |
| 380 | |
| 381 { | |
| 382 struct { | |
| 383 bool sep; | |
| 384 QString name; | |
| 385 QString format; | |
| 386 } net[] = { | |
| 387 /* note: the format should not be percent formatted; Qt does | |
| 388 * that automatically */ | |
| 389 | |
| 390 {0, tr("AniDB"), "" /* ??? */}, | |
| 391 {0, tr("AniList"), "" /* ??? */}, | |
| 392 {0, tr("Anime News Network"), "https://www.animenewsnetwork.com/search?q=%1"}, | |
| 393 {0, tr("Kitsu"), "" /* ??? */}, | |
| 394 {0, tr("MyAnimeList"), "https://myanimelist.net/search/all?q=%1&cat=all"}, | |
| 395 {0, tr("Reddit"), "https://www.reddit.com/search?q=%1"}, | |
| 396 {0, tr("Wikipedia"), "https://en.wikipedia.org/w/index.php?search=%1&title=Special:Search&ns0=1"}, | |
| 397 {0, tr("YouTube"), "https://www.youtube.com/results?search_query=%1"}, | |
| 398 {1, /* ------------------- */}, | |
| 399 {0, tr("Custom RSS feed"), "" /* ??? */}, | |
| 400 {0, tr("Nyaa.si"), "https://nyaa.si/?f=0&c=0_0&q=%1"}, | |
| 401 }; | |
| 402 size_t i; | |
| 403 QMenu *msearch = menu->addMenu(tr("Search")); | |
| 404 | |
| 405 for (i = 0; i < (sizeof(net)/sizeof(net[0])); i++) { | |
| 406 if (net[i].sep) { | |
| 407 msearch->addSeparator(); | |
| 408 } else { | |
| 409 if (net[i].format.isEmpty()) | |
| 410 continue; | |
| 411 | |
| 412 msearch->addAction(net[i].name, [anime, net, i] { | |
| 413 /* I suppose romaji is probably the safest */ | |
| 414 std::optional<std::string> title = anime->GetTitle(Anime::TitleLanguage::Romaji); | |
| 415 if (!title) | |
| 416 return; /* wat */ | |
| 417 | |
| 418 QString str = net[i].format.arg(Strings::ToQString(title.value())); | |
| 419 QUrl url(str); | |
| 420 | |
| 421 QDesktopServices::openUrl(url); | |
| 422 }); | |
| 423 } | |
| 424 } | |
| 425 } | |
| 404 | 426 |
| 405 menu->addSeparator(); | 427 menu->addSeparator(); |
| 406 | 428 |
| 407 menu->addAction(tr("Edit"), [this, anime] { | 429 menu->addAction(tr("Edit"), [this, anime] { |
| 408 InformationDialog *dialog = new InformationDialog( | 430 InformationDialog *dialog = new InformationDialog( |
| 418 tr("Delete from list..."), [this, anime] { RemoveAnime(anime->GetId()); }, | 440 tr("Delete from list..."), [this, anime] { RemoveAnime(anime->GetId()); }, |
| 419 QKeySequence(QKeySequence::Delete)); | 441 QKeySequence(QKeySequence::Delete)); |
| 420 | 442 |
| 421 menu->addSeparator(); | 443 menu->addSeparator(); |
| 422 | 444 |
| 423 menu->addAction(tr("Open folder"), [this, anime] { | 445 if (path) { |
| 424 std::optional<std::filesystem::path> path = Library::db.GetAnimeFolder(anime->GetId()); | 446 menu->addAction(tr("Open folder"), [path] { |
| 425 if (!path) // ... | 447 QDesktopServices::openUrl(QUrl::fromLocalFile(Strings::ToQString(path.value().u8string()))); |
| 426 return; | 448 }, QKeySequence(QKeySequence::Open)); |
| 427 | |
| 428 QDesktopServices::openUrl(QUrl::fromLocalFile(Strings::ToQString(path.value().u8string()))); | |
| 429 }); | |
| 430 menu->addAction(tr("Scan available episodes"), [this, anime] { Library::db.Refresh(anime->GetId()); }); | |
| 431 | |
| 432 menu->addSeparator(); | |
| 433 | |
| 434 { | |
| 435 QMenu *submenu = menu->addMenu(tr("Play episode")); | |
| 436 | |
| 437 // this submenu actually uses win32 API magic to | |
| 438 // make a *grid* of episodes (weird!) | |
| 439 | |
| 440 (void)submenu; | |
| 441 } | 449 } |
| 442 | 450 |
| 443 const int progress = anime->GetUserProgress(); | 451 menu->addAction(tr("Scan available episodes"), |
| 444 const int episodes = anime->GetEpisodes(); | 452 [this, anime] { Library::db.Refresh(anime->GetId()); }, |
| 445 | 453 QKeySequence(QKeySequence::Refresh)); |
| 446 // I think this is right? | 454 |
| 447 if (progress > 0) { | 455 if (path) { |
| 448 menu->addAction(tr("Play last episode (#%1)").arg(progress), [this, anime, progress] { | 456 menu->addSeparator(); |
| 449 const int id = anime->GetId(); | 457 |
| 450 | 458 { |
| 451 if (Library::db.items.find(id) == Library::db.items.end() || | 459 QMenu *submenu = menu->addMenu(tr("Play episode")); |
| 452 Library::db.items[id].find(progress) == Library::db.items[id].end()) | 460 |
| 453 return; | 461 // this submenu actually uses win32 API magic to |
| 454 | 462 // make a *grid* of episodes (weird!) |
| 455 QDesktopServices::openUrl( | 463 |
| 456 QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][progress].u8string()))); | 464 (void)submenu; |
| 457 }); | 465 } |
| 466 | |
| 467 const int progress = anime->GetUserProgress(); | |
| 468 const int episodes = anime->GetEpisodes(); | |
| 469 | |
| 470 // I think this is right? | |
| 471 if (progress > 0) { | |
| 472 menu->addAction(tr("Play last episode (#%1)").arg(progress), [this, anime, progress] { | |
| 473 const int id = anime->GetId(); | |
| 474 | |
| 475 if (Library::db.items.find(id) == Library::db.items.end() || | |
| 476 Library::db.items[id].find(progress) == Library::db.items[id].end()) | |
| 477 return; | |
| 478 | |
| 479 QDesktopServices::openUrl( | |
| 480 QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][progress].u8string()))); | |
| 481 }); | |
| 482 } | |
| 483 | |
| 484 if (progress < episodes) { | |
| 485 menu->addAction( | |
| 486 tr("Play next episode (#%1)").arg(progress + 1), | |
| 487 [this, anime, progress] { | |
| 488 const int id = anime->GetId(); | |
| 489 | |
| 490 if (Library::db.items.find(id) == Library::db.items.end() || | |
| 491 Library::db.items[id].find(progress + 1) == Library::db.items[id].end()) | |
| 492 return; | |
| 493 | |
| 494 QDesktopServices::openUrl( | |
| 495 QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][progress + 1].u8string()))); | |
| 496 }, | |
| 497 QKeySequence(Qt::CTRL | Qt::Key_N)); | |
| 498 } | |
| 499 | |
| 500 menu->addAction( | |
| 501 tr("Play random episode"), | |
| 502 [this, anime, episodes] { | |
| 503 const int id = anime->GetId(); | |
| 504 | |
| 505 std::uniform_int_distribution<int> distrib(1, episodes); | |
| 506 const int episode = distrib(session.gen); | |
| 507 | |
| 508 if (Library::db.items.find(id) == Library::db.items.end() || | |
| 509 Library::db.items[id].find(episode) == Library::db.items[id].end()) | |
| 510 return; | |
| 511 | |
| 512 QDesktopServices::openUrl( | |
| 513 QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][episode].u8string()))); | |
| 514 }, | |
| 515 QKeySequence(Qt::CTRL | Qt::Key_R)); | |
| 458 } | 516 } |
| 459 | |
| 460 if (progress < episodes) { | |
| 461 menu->addAction( | |
| 462 tr("Play next episode (#%1)").arg(progress + 1), | |
| 463 [this, anime, progress] { | |
| 464 const int id = anime->GetId(); | |
| 465 | |
| 466 if (Library::db.items.find(id) == Library::db.items.end() || | |
| 467 Library::db.items[id].find(progress + 1) == Library::db.items[id].end()) | |
| 468 return; | |
| 469 | |
| 470 QDesktopServices::openUrl( | |
| 471 QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][progress + 1].u8string()))); | |
| 472 }, | |
| 473 QKeySequence(Qt::CTRL | Qt::Key_N)); | |
| 474 } | |
| 475 | |
| 476 menu->addAction( | |
| 477 tr("Play random episode"), | |
| 478 [this, anime, episodes] { | |
| 479 const int id = anime->GetId(); | |
| 480 | |
| 481 std::uniform_int_distribution<int> distrib(1, episodes); | |
| 482 const int episode = distrib(session.gen); | |
| 483 | |
| 484 if (Library::db.items.find(id) == Library::db.items.end() || | |
| 485 Library::db.items[id].find(episode) == Library::db.items[id].end()) | |
| 486 return; | |
| 487 | |
| 488 QDesktopServices::openUrl( | |
| 489 QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][episode].u8string()))); | |
| 490 }, | |
| 491 QKeySequence(Qt::CTRL | Qt::Key_R)); | |
| 492 | 517 |
| 493 menu->popup(QCursor::pos()); | 518 menu->popup(QCursor::pos()); |
| 494 } else { | 519 } else { |
| 495 // Where are we now? | 520 // Where are we now? |
| 496 } | 521 } |
