comparison src/gui/window.cc @ 369:47c9f8502269

*: clang-format all the things I've edited the formatting a bit. Now pointer asterisks (and reference ampersands) are on the variable instead of the type, as well as having newlines for function braces (but nothing else)
author Paper <paper@tflc.us>
date Fri, 25 Jul 2025 10:16:02 -0400
parents 886f66775f31
children
comparison
equal deleted inserted replaced
368:6d37a998cf91 369:47c9f8502269
41 #include <QToolButton> 41 #include <QToolButton>
42 42
43 #include <iostream> 43 #include <iostream>
44 44
45 #ifdef MACOSX 45 #ifdef MACOSX
46 # include "sys/osx/dark_theme.h" 46 # include "sys/osx/dark_theme.h"
47 # include "sys/osx/permissions.h" 47 # include "sys/osx/permissions.h"
48 #elif defined(WIN32) 48 #elif defined(WIN32)
49 # include "sys/win32/dark_theme.h" 49 # include "sys/win32/dark_theme.h"
50 #endif 50 #endif
51 51
52 void MainWindowPlayingThread::run() { 52 void MainWindowPlayingThread::run()
53 {
53 std::vector<std::string> files; 54 std::vector<std::string> files;
54 Track::Media::GetCurrentlyPlaying(files); 55 Track::Media::GetCurrentlyPlaying(files);
55 emit Done(files); 56 emit Done(files);
56 } 57 }
57 58
58 MainWindowAsyncSynchronizeThread::MainWindowAsyncSynchronizeThread(QAction* action, AnimeListPage* page, QObject* parent) : QThread(parent) { 59 MainWindowAsyncSynchronizeThread::MainWindowAsyncSynchronizeThread(QAction *action, AnimeListPage *page,
60 QObject *parent)
61 : QThread(parent)
62 {
59 SetAction(action); 63 SetAction(action);
60 SetPage(page); 64 SetPage(page);
61 } 65 }
62 66
63 void MainWindowAsyncSynchronizeThread::SetAction(QAction* action) { 67 void MainWindowAsyncSynchronizeThread::SetAction(QAction *action)
68 {
64 action_ = action; 69 action_ = action;
65 } 70 }
66 71
67 void MainWindowAsyncSynchronizeThread::SetPage(AnimeListPage* page) { 72 void MainWindowAsyncSynchronizeThread::SetPage(AnimeListPage *page)
73 {
68 page_ = page; 74 page_ = page;
69 } 75 }
70 76
71 void MainWindowAsyncSynchronizeThread::run() { 77 void MainWindowAsyncSynchronizeThread::run()
78 {
72 action_->setEnabled(false); 79 action_->setEnabled(false);
73 Services::Synchronize(); 80 Services::Synchronize();
74 page_->Refresh(); 81 page_->Refresh();
75 action_->setEnabled(true); 82 action_->setEnabled(true);
76 } 83 }
77 84
78 MainWindow::MainWindow(QWidget* parent) 85 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), async_synchronize_thread_(nullptr, nullptr)
79 : QMainWindow(parent) 86 {
80 , async_synchronize_thread_(nullptr, nullptr) {
81 sidebar_.setFixedWidth(128); 87 sidebar_.setFixedWidth(128);
82 sidebar_.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); 88 sidebar_.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
83 89
84 statusBar(); 90 statusBar();
85 91
99 105
100 AddMainWidgets(); 106 AddMainWidgets();
101 sidebar_.SetCurrentItem(static_cast<int>(Pages::ANIME_LIST)); 107 sidebar_.SetCurrentItem(static_cast<int>(Pages::ANIME_LIST));
102 setCentralWidget(&main_widget_); 108 setCentralWidget(&main_widget_);
103 109
104 NowPlayingPage* page = reinterpret_cast<NowPlayingPage*>(stack_.widget(static_cast<int>(Pages::NOW_PLAYING))); 110 NowPlayingPage *page = reinterpret_cast<NowPlayingPage *>(stack_.widget(static_cast<int>(Pages::NOW_PLAYING)));
105 111
106 connect(&playing_thread_, &MainWindowPlayingThread::Done, this, [page](const std::vector<std::string>& files) { 112 connect(&playing_thread_, &MainWindowPlayingThread::Done, this, [page](const std::vector<std::string> &files) {
107 for (const auto& file : files) { 113 for (const auto &file : files) {
108 anitomy::Anitomy anitomy; 114 anitomy::Anitomy anitomy;
109 anitomy.Parse(file); 115 anitomy.Parse(file);
110 116
111 const auto& elements = anitomy.elements(); 117 const auto &elements = anitomy.elements();
112 118
113 const std::string title = Strings::ToUtf8String(elements.get(anitomy::kElementAnimeTitle)); 119 const std::string title = Strings::ToUtf8String(elements.get(anitomy::kElementAnimeTitle));
114 120
115 int id = Anime::db.LookupAnimeTitle(title); 121 int id = Anime::db.LookupAnimeTitle(title);
116 if (id <= 0) 122 if (id <= 0)
134 #endif 140 #endif
135 141
136 playing_thread_timer_.start(5000); 142 playing_thread_timer_.start(5000);
137 } 143 }
138 144
139 void MainWindow::SetStatusMessage(const std::string& message) { 145 void MainWindow::SetStatusMessage(const std::string &message)
146 {
140 statusBar()->showMessage(Strings::ToQString(message), 2000); 147 statusBar()->showMessage(Strings::ToQString(message), 2000);
141 } 148 }
142 149
143 /* FIXME: 150 /* FIXME:
144 * ALL of the pages need to have a retranslate function. This would require 151 * ALL of the pages need to have a retranslate function. This would require
145 * huge amounts of refactoring hence why it hasn't been done yet. */ 152 * huge amounts of refactoring hence why it hasn't been done yet. */
146 void MainWindow::AddMainWidgets() { 153 void MainWindow::AddMainWidgets()
154 {
147 int page = sidebar_.GetCurrentItem(); 155 int page = sidebar_.GetCurrentItem();
148 156
149 sidebar_.clear(); 157 sidebar_.clear();
150 158
151 sidebar_.AddItem(tr("Now Playing"), SideBar::CreateIcon(":/icons/16x16/film.png")); 159 sidebar_.AddItem(tr("Now Playing"), SideBar::CreateIcon(":/icons/16x16/film.png"));
162 170
163 main_widget_.layout()->addWidget(&sidebar_); 171 main_widget_.layout()->addWidget(&sidebar_);
164 main_widget_.layout()->addWidget(&stack_); 172 main_widget_.layout()->addWidget(&stack_);
165 } 173 }
166 174
167 void MainWindow::CreateBars() { 175 void MainWindow::CreateBars()
168 QMenuBar* menubar = new QMenuBar(this); 176 {
169 QAction* sync_action; 177 QMenuBar *menubar = new QMenuBar(this);
178 QAction *sync_action;
170 179
171 { 180 {
172 /* File */ 181 /* File */
173 QMenu* menu = menubar->addMenu(tr("&File")); 182 QMenu *menu = menubar->addMenu(tr("&File"));
174 183
175 { 184 {
176 folder_menu = menu->addMenu(tr("&Library folders")); 185 folder_menu = menu->addMenu(tr("&Library folders"));
177 186
178 UpdateFolderMenu(); 187 UpdateFolderMenu();
195 // } 204 // }
196 205
197 menu->addSeparator(); 206 menu->addSeparator();
198 207
199 { 208 {
200 QAction* action = menu->addAction(tr("E&xit"), this, &MainWindow::close); 209 QAction *action = menu->addAction(tr("E&xit"), this, &MainWindow::close);
201 action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q)); 210 action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q));
202 } 211 }
203 } 212 }
204 213
205 { 214 {
206 /* Services */ 215 /* Services */
207 QMenu* menu = menubar->addMenu(tr("&Services")); 216 QMenu *menu = menubar->addMenu(tr("&Services"));
208 { 217 {
209 { 218 {
210 sync_action = menu->addAction(tr("Synchronize &list")); 219 sync_action = menu->addAction(tr("Synchronize &list"));
211 220
212 connect(sync_action, &QAction::triggered, this, 221 connect(sync_action, &QAction::triggered, this,
241 } 250 }
242 } 251 }
243 252
244 { 253 {
245 /* Tools */ 254 /* Tools */
246 QMenu* menu = menubar->addMenu(tr("&Tools")); 255 QMenu *menu = menubar->addMenu(tr("&Tools"));
247 // { 256 // {
248 // /* Export anime list */ 257 // /* Export anime list */
249 // QMenu* submenu = menu->addMenu(tr("&Export anime list")); 258 // QMenu* submenu = menu->addMenu(tr("&Export anime list"));
250 // 259 //
251 // { 260 // {
276 // } 285 // }
277 // 286 //
278 // menu->addSeparator(); 287 // menu->addSeparator();
279 288
280 { 289 {
281 QAction* action = menu->addAction(tr("&Settings"), [this] { 290 QAction *action = menu->addAction(tr("&Settings"), [this] {
282 SettingsDialog dialog(this); 291 SettingsDialog dialog(this);
283 dialog.exec(); 292 dialog.exec();
284 UpdateFolderMenu(); 293 UpdateFolderMenu();
285 }); 294 });
286 action->setMenuRole(QAction::PreferencesRole); 295 action->setMenuRole(QAction::PreferencesRole);
287 } 296 }
288 } 297 }
289 298
290 { 299 {
291 /* View */ 300 /* View */
292 QMenu* menu = menubar->addMenu(tr("&View")); 301 QMenu *menu = menubar->addMenu(tr("&View"));
293 302
294 { 303 {
295 /* Pages... */ 304 /* Pages... */
296 QActionGroup* pages_group = new QActionGroup(menu); 305 QActionGroup *pages_group = new QActionGroup(menu);
297 pages_group->setExclusive(true); 306 pages_group->setExclusive(true);
298 307
299 { 308 {
300 QAction* action = pages_group->addAction(menu->addAction(tr("&Now Playing"))); 309 QAction *action = pages_group->addAction(menu->addAction(tr("&Now Playing")));
301 action->setCheckable(true); 310 action->setCheckable(true);
302 connect(action, &QAction::toggled, this, [this] { sidebar_.SetCurrentItem(0); }); 311 connect(action, &QAction::toggled, this, [this] { sidebar_.SetCurrentItem(0); });
303 } 312 }
304 313
305 { 314 {
306 QAction* action = pages_group->addAction(menu->addAction(tr("&Anime List"))); 315 QAction *action = pages_group->addAction(menu->addAction(tr("&Anime List")));
307 action->setCheckable(true); 316 action->setCheckable(true);
308 action->setChecked(true); 317 action->setChecked(true);
309 connect(action, &QAction::toggled, this, [this] { sidebar_.SetCurrentItem(1); }); 318 connect(action, &QAction::toggled, this, [this] { sidebar_.SetCurrentItem(1); });
310 } 319 }
311 320
312 { 321 {
313 QAction* action = pages_group->addAction(menu->addAction(tr("&History"))); 322 QAction *action = pages_group->addAction(menu->addAction(tr("&History")));
314 action->setCheckable(true); 323 action->setCheckable(true);
315 connect(action, &QAction::toggled, this, [this] { sidebar_.SetCurrentItem(2); }); 324 connect(action, &QAction::toggled, this, [this] { sidebar_.SetCurrentItem(2); });
316 } 325 }
317 326
318 { 327 {
319 QAction* action = pages_group->addAction(menu->addAction(tr("&Statistics"))); 328 QAction *action = pages_group->addAction(menu->addAction(tr("&Statistics")));
320 action->setCheckable(true); 329 action->setCheckable(true);
321 connect(action, &QAction::toggled, this, [this] { sidebar_.SetCurrentItem(3); }); 330 connect(action, &QAction::toggled, this, [this] { sidebar_.SetCurrentItem(3); });
322 } 331 }
323 332
324 { 333 {
325 QAction* action = pages_group->addAction(menu->addAction(tr("S&earch"))); 334 QAction *action = pages_group->addAction(menu->addAction(tr("S&earch")));
326 action->setCheckable(true); 335 action->setCheckable(true);
327 connect(action, &QAction::toggled, this, [this] { sidebar_.SetCurrentItem(4); }); 336 connect(action, &QAction::toggled, this, [this] { sidebar_.SetCurrentItem(4); });
328 } 337 }
329 338
330 { 339 {
331 QAction* action = pages_group->addAction(menu->addAction(tr("Se&asons"))); 340 QAction *action = pages_group->addAction(menu->addAction(tr("Se&asons")));
332 action->setCheckable(true); 341 action->setCheckable(true);
333 connect(action, &QAction::toggled, this, [this] { sidebar_.SetCurrentItem(5); }); 342 connect(action, &QAction::toggled, this, [this] { sidebar_.SetCurrentItem(5); });
334 } 343 }
335 344
336 { 345 {
337 QAction* action = pages_group->addAction(menu->addAction(tr("&Torrents"))); 346 QAction *action = pages_group->addAction(menu->addAction(tr("&Torrents")));
338 action->setCheckable(true); 347 action->setCheckable(true);
339 connect(action, &QAction::toggled, this, [this] { sidebar_.SetCurrentItem(6); }); 348 connect(action, &QAction::toggled, this, [this] { sidebar_.SetCurrentItem(6); });
340 } 349 }
341 350
342 /* pain in the ass */ 351 /* pain in the ass */
343 disconnect(&sidebar_, &SideBar::CurrentItemChanged, nullptr, nullptr); 352 disconnect(&sidebar_, &SideBar::CurrentItemChanged, nullptr, nullptr);
344 connect(&sidebar_, &SideBar::CurrentItemChanged, &stack_, &QStackedWidget::setCurrentIndex); 353 connect(&sidebar_, &SideBar::CurrentItemChanged, &stack_, &QStackedWidget::setCurrentIndex);
345 connect(&sidebar_, &SideBar::CurrentItemChanged, this, [pages_group](int index) { 354 connect(&sidebar_, &SideBar::CurrentItemChanged, this, [pages_group](int index) {
346 QAction* checked = pages_group->checkedAction(); 355 QAction *checked = pages_group->checkedAction();
347 356
348 const QList<QAction*>& actions = pages_group->actions(); 357 const QList<QAction *> &actions = pages_group->actions();
349 if (index > actions.size()) 358 if (index > actions.size())
350 return; 359 return;
351 360
352 if (checked) 361 if (checked)
353 checked->setChecked(false); 362 checked->setChecked(false);
362 // } 371 // }
363 } 372 }
364 373
365 { 374 {
366 /* Help */ 375 /* Help */
367 QMenu* menu = menubar->addMenu(tr("&Help")); 376 QMenu *menu = menubar->addMenu(tr("&Help"));
368 377
369 { 378 {
370 /* About Minori */ 379 /* About Minori */
371 menu->addAction(tr("&About Minori"), this, [this] { 380 menu->addAction(tr("&About Minori"), this, [this] {
372 AboutWindow dialog(this); 381 AboutWindow dialog(this);
374 }); 383 });
375 } 384 }
376 385
377 { 386 {
378 /* About Qt */ 387 /* About Qt */
379 QAction* action = menu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt); 388 QAction *action = menu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
380 action->setMenuRole(QAction::AboutQtRole); 389 action->setMenuRole(QAction::AboutQtRole);
381 } 390 }
382 } 391 }
383 /* QMainWindow will delete the old one for us, 392 /* QMainWindow will delete the old one for us,
384 * according to the docs 393 * according to the docs
387 396
388 /* Toolbar */ 397 /* Toolbar */
389 398
390 /* remove old toolbar(s) */ 399 /* remove old toolbar(s) */
391 /* the empty QString() is a Qt 5 wart... */ 400 /* the empty QString() is a Qt 5 wart... */
392 for (QToolBar*& t : findChildren<QToolBar*>(QString(), Qt::FindDirectChildrenOnly)) { 401 for (QToolBar *&t : findChildren<QToolBar *>(QString(), Qt::FindDirectChildrenOnly)) {
393 removeToolBar(t); 402 removeToolBar(t);
394 delete t; 403 delete t;
395 } 404 }
396 405
397 { 406 {
398 /* Toolbar */ 407 /* Toolbar */
399 QToolBar* toolbar = new QToolBar(this); 408 QToolBar *toolbar = new QToolBar(this);
400 toolbar->addAction(sync_action); 409 toolbar->addAction(sync_action);
401 410
402 toolbar->addSeparator(); 411 toolbar->addSeparator();
403 412
404 { 413 {
405 QToolButton* button = new QToolButton(toolbar); 414 QToolButton *button = new QToolButton(toolbar);
406 { button->setMenu(folder_menu); } 415 {
416 button->setMenu(folder_menu);
417 }
407 button->setIcon(QIcon(":/icons/24x24/folder-open.png")); 418 button->setIcon(QIcon(":/icons/24x24/folder-open.png"));
408 button->setPopupMode(QToolButton::InstantPopup); 419 button->setPopupMode(QToolButton::InstantPopup);
409 toolbar->addWidget(button); 420 toolbar->addWidget(button);
410 } 421 }
411 422
412 { 423 {
413 QToolButton* button = new QToolButton(toolbar); 424 QToolButton *button = new QToolButton(toolbar);
414 425
415 { 426 {
416 /* links */ 427 /* links */
417 QMenu* menu = new QMenu(button); 428 QMenu *menu = new QMenu(button);
418 menu->addAction("Hibari", [] { QDesktopServices::openUrl(QUrl("https://hb.wopian.me/")); }); 429 menu->addAction("Hibari", [] { QDesktopServices::openUrl(QUrl("https://hb.wopian.me/")); });
419 menu->addAction("MALgraph", [] { QDesktopServices::openUrl(QUrl("https://graph.anime.plus/")); }); 430 menu->addAction("MALgraph", [] { QDesktopServices::openUrl(QUrl("https://graph.anime.plus/")); });
420 menu->addSeparator(); 431 menu->addSeparator();
421 menu->addAction("AniChart", [] { QDesktopServices::openUrl(QUrl("https://anichart.net/airing")); }); 432 menu->addAction("AniChart", [] { QDesktopServices::openUrl(QUrl("https://anichart.net/airing")); });
422 menu->addAction("Monthly.moe", 433 menu->addAction("Monthly.moe",
445 }); 456 });
446 addToolBar(toolbar); 457 addToolBar(toolbar);
447 } 458 }
448 } 459 }
449 460
450 void MainWindow::UpdateFolderMenu() { 461 void MainWindow::UpdateFolderMenu()
462 {
451 if (!folder_menu) 463 if (!folder_menu)
452 return; 464 return;
453 465
454 folder_menu->clear(); 466 folder_menu->clear();
455 467
456 /* add in all of our existing folders... */ 468 /* add in all of our existing folders... */
457 std::size_t i = 0; 469 std::size_t i = 0;
458 for (const auto& path : session.config.library.paths) { 470 for (const auto &path : session.config.library.paths) {
459 const QString folder = Strings::ToQString(path); 471 const QString folder = Strings::ToQString(path);
460 QAction* action = 472 QAction *action =
461 folder_menu->addAction(folder, [folder] { QDesktopServices::openUrl(QUrl::fromLocalFile(folder)); }); 473 folder_menu->addAction(folder, [folder] { QDesktopServices::openUrl(QUrl::fromLocalFile(folder)); });
462 474
463 if (i < 9) { 475 if (i < 9) {
464 /* Qt::Key_1 is equivalent to 1 in ASCII, so we can use the same 476 /* Qt::Key_1 is equivalent to 1 in ASCII, so we can use the same
465 * stupid `'0' + i` trick here 477 * stupid `'0' + i` trick here
485 UpdateFolderMenu(); 497 UpdateFolderMenu();
486 }); 498 });
487 } 499 }
488 } 500 }
489 501
490 void MainWindow::SetActivePage(QWidget* page) { 502 void MainWindow::SetActivePage(QWidget *page)
503 {
491 this->setCentralWidget(page); 504 this->setCentralWidget(page);
492 } 505 }
493 506
494 void MainWindow::AsyncSynchronize(QAction* action, QStackedWidget* stack) { 507 void MainWindow::AsyncSynchronize(QAction *action, QStackedWidget *stack)
508 {
495 if (session.config.service == Anime::Service::None) { 509 if (session.config.service == Anime::Service::None) {
496 QMessageBox msg; 510 QMessageBox msg;
497 msg.setWindowTitle(tr("Error synchronizing with service!")); 511 msg.setWindowTitle(tr("Error synchronizing with service!"));
498 msg.setText(tr("It seems you haven't yet selected a service to use.")); 512 msg.setText(tr("It seems you haven't yet selected a service to use."));
499 msg.setInformativeText(tr("Would you like to select one now?")); 513 msg.setInformativeText(tr("Would you like to select one now?"));
505 dialog.exec(); 519 dialog.exec();
506 } 520 }
507 } 521 }
508 522
509 /* FIXME: make this use a QThread; this is *very* unsafe */ 523 /* FIXME: make this use a QThread; this is *very* unsafe */
510 AnimeListPage* page = reinterpret_cast<AnimeListPage*>(stack->widget(static_cast<int>(Pages::ANIME_LIST))); 524 AnimeListPage *page = reinterpret_cast<AnimeListPage *>(stack->widget(static_cast<int>(Pages::ANIME_LIST)));
511 if (!async_synchronize_thread_.isRunning()) { 525 if (!async_synchronize_thread_.isRunning()) {
512 async_synchronize_thread_.SetAction(action); 526 async_synchronize_thread_.SetAction(action);
513 async_synchronize_thread_.SetPage(page); 527 async_synchronize_thread_.SetPage(page);
514 async_synchronize_thread_.start(); 528 async_synchronize_thread_.start();
515 } 529 }
516 } 530 }
517 531
518 void MainWindow::RetranslateUI() { 532 void MainWindow::RetranslateUI()
533 {
519 /* This sucks a LOT */ 534 /* This sucks a LOT */
520 setUpdatesEnabled(false); 535 setUpdatesEnabled(false);
521 AddMainWidgets(); 536 AddMainWidgets();
522 CreateBars(); 537 CreateBars();
523 setUpdatesEnabled(true); 538 setUpdatesEnabled(true);
524 } 539 }
525 540
526 void MainWindow::changeEvent(QEvent* event) { 541 void MainWindow::changeEvent(QEvent *event)
542 {
527 if (event) { /* is this really necessary */ 543 if (event) { /* is this really necessary */
528 switch (event->type()) { 544 switch (event->type()) {
529 // this event is send if a translator is loaded 545 // this event is send if a translator is loaded
530 case QEvent::LanguageChange: RetranslateUI(); break; 546 case QEvent::LanguageChange: RetranslateUI(); break;
531 547
533 } 549 }
534 } 550 }
535 QMainWindow::changeEvent(event); 551 QMainWindow::changeEvent(event);
536 } 552 }
537 553
538 void MainWindow::showEvent(QShowEvent* event) { 554 void MainWindow::showEvent(QShowEvent *event)
555 {
539 QMainWindow::showEvent(event); 556 QMainWindow::showEvent(event);
540 #ifdef WIN32 557 #ifdef WIN32
541 /* Technically this *should* be 558 /* Technically this *should* be
542 * session.config.theme.IsInDarkTheme() && win32::IsInDarkTheme() 559 * session.config.theme.IsInDarkTheme() && win32::IsInDarkTheme()
543 * but I prefer the title bar being black even when light mode 560 * but I prefer the title bar being black even when light mode
544 * is enabled :/ */ 561 * is enabled :/ */
545 win32::SetTitleBarsToBlack(session.config.theme.IsInDarkTheme()); 562 win32::SetTitleBarsToBlack(session.config.theme.IsInDarkTheme());
546 #endif 563 #endif
547 } 564 }
548 565
549 void MainWindow::closeEvent(QCloseEvent* event) { 566 void MainWindow::closeEvent(QCloseEvent *event)
567 {
550 playing_thread_timer_.stop(); 568 playing_thread_timer_.stop();
551 playing_thread_.wait(); 569 playing_thread_.wait();
552 async_synchronize_thread_.wait(); 570 async_synchronize_thread_.wait();
553 571
554 session.config.Save(); 572 session.config.Save();