# HG changeset patch # User Paper # Date 1699245109 18000 # Node ID 2004b41d4a59a60ce36019762428cb2631420614 # Parent 49c8d197686946116e66997bd531a4a6f68a9398 *: huge commit 1. WORKING LOCALIZATION + translation for Spanish and British English 2. idk like 2 changes for the dark theme :) diff -r 49c8d1976869 -r 2004b41d4a59 CMakeLists.txt --- a/CMakeLists.txt Sun Nov 05 17:44:49 2023 -0500 +++ b/CMakeLists.txt Sun Nov 05 23:31:49 2023 -0500 @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.18) project(minori LANGUAGES CXX VERSION 0.1.0) if(APPLE) @@ -10,7 +10,8 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") option(BUILD_SHARED_LIBS "Build using shared libraries" ON) -option(USE_QT6 "Build with Qt 6 instead of Qt 5" OFF) +option(USE_QT6 "Force build with Qt 6" OFF) +option(USE_QT5 "Force build with Qt 5" OFF) add_subdirectory(dep/anitomy) add_subdirectory(dep/animia) @@ -19,25 +20,25 @@ # Fix for mingw64 list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES ".dll.a" ".a") -if(USE_QT6) - find_package(Qt6 COMPONENTS Widgets REQUIRED) +if (USE_QT6) + set(QT_VERSION_MAJOR 6) +elseif(USE_Qt5) + set(QT_VERSION_MAJOR 5) else() - find_package(Qt5 COMPONENTS Widgets REQUIRED) + find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core) endif() + +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets LinguistTools) + find_package(CURL REQUIRED) set(LIBRARIES ${CURL_LIBRARIES} + ${Qt${QT_VERSION_MAJOR}Widgets_LIBRARIES} anitomy animia ) -if(USE_QT6) - list(APPEND LIBRARIES ${Qt6Widgets_LIBRARIES}) -else() - list(APPEND LIBRARIES ${Qt5Widgets_LIBRARIES}) -endif() - # We need Cocoa for some OS X stuff if(APPLE) find_library(COCOA_LIBRARY Cocoa) @@ -62,6 +63,7 @@ # Main window src/gui/window.cc src/gui/theme.cc + src/gui/locale.cc # Main window pages src/gui/pages/anime_list.cc @@ -105,6 +107,41 @@ rc/dark.qrc ) +set(INCLUDE + include + dep/pugixml/src + dep/animia/include + dep/anitomy + dep +) + +set(TS_FILES + rc/locale/en_GB.ts + rc/locale/es.ts +) + +set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/rc/locale") + +# dumb little hack to get this working on Qt5 and Qt6 +cmake_language(CALL qt${QT_VERSION_MAJOR}_create_translation ${SRC_FILES} ${TS_FILES} OPTIONS "-I${CMAKE_CURRENT_SOURCE_DIR}/include") +cmake_language(CALL qt${QT_VERSION_MAJOR}_add_translation QM_FILES ${TS_FILES}) +list(APPEND SRC_FILES ${QM_FILES}) + +function(qt_create_resource_file outfile) + set(QRC "\n\t\n") + get_filename_component(DIR ${outfile} DIRECTORY) + foreach (qm ${ARGN}) + file(RELATIVE_PATH name ${DIR} ${qm}) + string(APPEND QRC "\t\t${name}\n") + endforeach() + string(APPEND QRC "\t\n\n") + file(WRITE ${outfile} ${QRC}) +endfunction() + +qt_create_resource_file("${CMAKE_CURRENT_BINARY_DIR}/rc/locale.qrc" ${QM_FILES}) +list(APPEND SRC_FILES "${CMAKE_CURRENT_BINARY_DIR}/rc/locale.qrc") + +# This is also used in the Win32 rc file set(RC_INFO_STRING "A lightweight anime tracker built with Qt.") if(APPLE) # Mac OS X (or OS X (or macOS)) @@ -140,12 +177,8 @@ set_property(TARGET minori PROPERTY AUTOMOC ON) set_property(TARGET minori PROPERTY AUTORCC ON) -target_include_directories(minori PUBLIC ${CURL_INCLUDE_DIRS} PRIVATE include dep/pugixml/src dep/animia/include dep/anitomy dep) -if(USE_QT6) - target_include_directories(minori PUBLIC ${Qt6Widgets_INCLUDE_DIRS}) -else() - target_include_directories(minori PUBLIC ${Qt5Widgets_INCLUDE_DIRS}) -endif() +target_include_directories(minori PUBLIC ${CURL_INCLUDE_DIRS} PRIVATE ${INCLUDE}) +target_include_directories(minori PUBLIC ${Qt${QT_VERSION_MAJOR}Widgets_INCLUDE_DIRS}) target_compile_options(minori PRIVATE -Wall -Wpedantic -Wextra -Wsuggest-override -Wold-style-cast) if(APPLE) target_compile_definitions(minori PUBLIC MACOSX) diff -r 49c8d1976869 -r 2004b41d4a59 include/core/config.h --- a/include/core/config.h Sun Nov 05 17:44:49 2023 -0500 +++ b/include/core/config.h Sun Nov 05 23:31:49 2023 -0500 @@ -3,6 +3,7 @@ #include "core/anime.h" #include "gui/theme.h" +#include "gui/locale.h" class Config { public: @@ -11,6 +12,7 @@ Anime::Services service; Theme::Theme theme; + Locale::Locale locale; struct { public: diff -r 49c8d1976869 -r 2004b41d4a59 include/core/session.h --- a/include/core/session.h Sun Nov 05 17:44:49 2023 -0500 +++ b/include/core/session.h Sun Nov 05 23:31:49 2023 -0500 @@ -2,6 +2,7 @@ #define __core__session_h #include "core/config.h" +#include "gui/locale.h" #include struct Session { diff -r 49c8d1976869 -r 2004b41d4a59 include/gui/dialog/about.h --- a/include/gui/dialog/about.h Sun Nov 05 17:44:49 2023 -0500 +++ b/include/gui/dialog/about.h Sun Nov 05 23:31:49 2023 -0500 @@ -4,8 +4,13 @@ #include class AboutWindow final : public QDialog { + Q_OBJECT + public: AboutWindow(QWidget* parent = nullptr); + + protected: + void showEvent(QShowEvent* event) override; }; #endif // __gui__dialog__about_h diff -r 49c8d1976869 -r 2004b41d4a59 include/gui/dialog/information.h --- a/include/gui/dialog/information.h Sun Nov 05 17:44:49 2023 -0500 +++ b/include/gui/dialog/information.h Sun Nov 05 23:31:49 2023 -0500 @@ -12,6 +12,9 @@ public: InformationDialog(Anime::Anime& anime, std::function accept, QWidget* parent = nullptr); + protected: + void showEvent(QShowEvent* event) override; + private: void SaveData(Anime::Anime& anime); unsigned int _progress; diff -r 49c8d1976869 -r 2004b41d4a59 include/gui/dialog/settings.h --- a/include/gui/dialog/settings.h Sun Nov 05 17:44:49 2023 -0500 +++ b/include/gui/dialog/settings.h Sun Nov 05 23:31:49 2023 -0500 @@ -5,6 +5,7 @@ #include "core/config.h" #include #include +#include class QLabel; class QTabWidget; @@ -26,6 +27,8 @@ }; class SettingsPageServices final : public SettingsPage { + Q_OBJECT + public: SettingsPageServices(QWidget* parent = nullptr); void SaveInfo() override; @@ -38,6 +41,8 @@ }; class SettingsPageApplication final : public SettingsPage { + Q_OBJECT + public: SettingsPageApplication(QWidget* parent = nullptr); void SaveInfo() override; @@ -45,6 +50,7 @@ private: QWidget* CreateAnimeListWidget(); Themes theme; + QLocale locale; Anime::TitleLanguage language; bool display_aired_episodes; bool display_available_episodes; @@ -60,6 +66,9 @@ QWidget* CreateServicesMainPage(QWidget* parent); void OnOK(); + protected: + void showEvent(QShowEvent* event) override; + private: SideBar* sidebar; QStackedWidget* stacked; diff -r 49c8d1976869 -r 2004b41d4a59 include/gui/pages/anime_list.h --- a/include/gui/pages/anime_list.h Sun Nov 05 17:44:49 2023 -0500 +++ b/include/gui/pages/anime_list.h Sun Nov 05 23:31:49 2023 -0500 @@ -11,16 +11,6 @@ class QTreeView; class QTabBar; -class AnimeListPageDelegate final : public QStyledItemDelegate { - Q_OBJECT - - public: - explicit AnimeListPageDelegate(QObject* parent); - - QWidget* createEditor(QWidget*, const QStyleOptionViewItem&, const QModelIndex&) const override; - void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; -}; - class AnimeListPageSortFilter final : public QSortFilterProxyModel { Q_OBJECT diff -r 49c8d1976869 -r 2004b41d4a59 include/gui/theme.h --- a/include/gui/theme.h Sun Nov 05 17:44:49 2023 -0500 +++ b/include/gui/theme.h Sun Nov 05 23:31:49 2023 -0500 @@ -16,7 +16,7 @@ Theme(Themes theme = Themes::OS); void SetTheme(Themes theme); Themes GetTheme(); - bool IsInDarkMode(); + bool IsInDarkTheme(); void RepaintCurrentTheme(); private: diff -r 49c8d1976869 -r 2004b41d4a59 rc/win32/version.rc.in --- a/rc/win32/version.rc.in Sun Nov 05 17:44:49 2023 -0500 +++ b/rc/win32/version.rc.in Sun Nov 05 23:31:49 2023 -0500 @@ -6,7 +6,7 @@ #define VER_PRODUCTVERSION @minori_VERSION_MAJOR@,@minori_VERSION_MINOR@,@minori_VERSION_PATCH@,0 #define VER_PRODUCTVERSION_STR "@minori_VERSION_MAJOR@.@minori_VERSION_MINOR@.@minori_VERSION_PATCH@\0" -#define RC_INFO_STRING "@RC_INFO_STRING@" +#define RC_INFO_STRING "@RC_INFO_STRING@\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION diff -r 49c8d1976869 -r 2004b41d4a59 src/core/config.cc --- a/src/core/config.cc Sun Nov 05 17:44:49 2023 -0500 +++ b/src/core/config.cc Sun Nov 05 23:31:49 2023 -0500 @@ -41,6 +41,7 @@ file.read(ini); service = Translate::ToService(ini.get("General").get("Service")); + locale.SetActiveLocale(QLocale(Strings::ToQString(ini.get("General").get("Locale")))); anime_list.language = Translate::ToLanguage(ini.get("Anime List").get("Title language")); anime_list.display_aired_episodes = string_to_bool(ini.get("Anime List").get("Display only aired episodes"), true); anime_list.display_available_episodes = string_to_bool(ini.get("Anime List").get("Display only available episodes in library"), true); @@ -62,6 +63,7 @@ mINI::INIStructure ini; ini["General"]["Service"] = Translate::ToString(service); + ini["General"]["Locale"] = Strings::ToUtf8String(locale.GetLocale().name()); ini["Anime List"]["Title language"] = Translate::ToString(anime_list.language); ini["Anime List"]["Display only aired episodes"] = bool_to_string(anime_list.display_aired_episodes); ini["Anime List"]["Display only available episodes in library"] = bool_to_string(anime_list.display_available_episodes); diff -r 49c8d1976869 -r 2004b41d4a59 src/gui/dialog/about.cc --- a/src/gui/dialog/about.cc Sun Nov 05 17:44:49 2023 -0500 +++ b/src/gui/dialog/about.cc Sun Nov 05 23:31:49 2023 -0500 @@ -1,6 +1,7 @@ #include "gui/dialog/about.h" #include "core/json.h" #include "core/version.h" +#include "core/session.h" #include "core/strings.h" #include "gui/widgets/text.h" #include "pugixml.hpp" @@ -9,13 +10,18 @@ #include #include #include +#include #include +#ifdef WIN32 +#include "sys/win32/dark_theme.h" +#endif template constexpr size_t array_size(T (&)[N]) { return N; } +/* used for JSON for Modern C++ */ #define CONCAT_VERSION_NX(major, minor, patch) "v" #major "." #minor "." #patch #define CONCAT_VERSION(major, minor, patch) CONCAT_VERSION_NX(major, minor, patch) @@ -35,59 +41,76 @@ return data->version; } -/* I hate HTML so much... */ -static const QString html = QString( - "" - "

Minori " MINORI_VERSION "

" - "

" - " Author:
" - " Paper (@mrpapersonic)" - "

" - "

" - " Third party components:
" - "libcurl v") + get_curl_version() + "" - ", " - "Fugue Icons v3.5.6" - ", " - "Anitomy" - ", " - "JSON for Modern C++ " CONCAT_VERSION(NLOHMANN_JSON_VERSION_MAJOR, - NLOHMANN_JSON_VERSION_MINOR, - NLOHMANN_JSON_VERSION_PATCH) "" - ", " - "pugixml v" + pugixml_version + "" - ", " - "mINI v0.9.14" - "

" - "" - "Special thanks:" - "" - "
    " - "
  • Eren Okka for creating Taiga
  • " - "
  • Alex Huszagh and Colin Duquesnoy for " - "creating BreezeStyleSheets, on which the dark theme in this program is " - "based off of
  • " - "
  • Andy Brice for making " - "
" - ""; - AboutWindow::AboutWindow(QWidget* parent) : QDialog(parent) { setMinimumSize(641, 325); setWindowTitle(tr("About Minori")); setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint); - QHBoxLayout* layout = new QHBoxLayout(this); - - QPalette pal = QPalette(); - pal.setColor(QPalette::Window, pal.color(QPalette::Base)); - setPalette(pal); setAutoFillBackground(true); - QTextBrowser* paragraph = new QTextBrowser(this); - paragraph->setOpenExternalLinks(true); - paragraph->setFrameShape(QFrame::NoFrame); - paragraph->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - paragraph->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - paragraph->setHtml(html); + QHBoxLayout* layout = new QHBoxLayout(this); - layout->addWidget(paragraph); + /* we have to generate this on-the-fly for localization purposes */ + static const QString html = QString( + "" + "

Minori " MINORI_VERSION "

" + "

" + " " + QCoreApplication::tr("Author:") + "
" + " Paper (@mrpapersonic)" + "

" + "

" + " " + QCoreApplication::tr("Third party components:") + "
" + "libcurl v") + get_curl_version() + "" + ", " + "Fugue Icons v3.5.6" + ", " + "Anitomy" + ", " + "JSON for Modern C++ " CONCAT_VERSION(NLOHMANN_JSON_VERSION_MAJOR, + NLOHMANN_JSON_VERSION_MINOR, + NLOHMANN_JSON_VERSION_PATCH) "" + ", " + "pugixml v" + pugixml_version + "" + ", " + "mINI v0.9.14" + "

" + "" + "" + QCoreApplication::tr("Special thanks:") + "" + "" + "
    " + "
  • Eren Okka " + QCoreApplication::tr("for creating Taiga") + "
  • " + "
  • Alex Huszagh " + QCoreApplication::tr("and") + " Colin Duquesnoy " + + QCoreApplication::tr("for creating BreezeStyleSheets, on which the dark theme in this program is " + "based off of") + "
  • " + "
  • Andy Brice " + QCoreApplication::tr("for providing some sample code for " + "detecting dark mode on Windows and macOS") + "
  • " + "
  • Manuel Wudka-Robles " + QCoreApplication::tr("for providing information on " + "getting open file descriptors on macOS") + "
  • " + "
" + ""; + + { + QPalette pal = QPalette(); + pal.setColor(QPalette::Window, pal.color(QPalette::Base)); + setPalette(pal); + } + + { + QTextBrowser* paragraph = new QTextBrowser(this); + paragraph->setOpenExternalLinks(true); + paragraph->setFrameShape(QFrame::NoFrame); + paragraph->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + paragraph->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + paragraph->setHtml(html); + + layout->addWidget(paragraph); + } } + +void AboutWindow::showEvent(QShowEvent* event) { + QDialog::showEvent(event); +#ifdef WIN32 + win32::SetTitleBarsToBlack(session.config.theme.IsInDarkTheme()); +#endif +} + +#include "gui/dialog/moc_about.cpp" diff -r 49c8d1976869 -r 2004b41d4a59 src/gui/dialog/information.cc --- a/src/gui/dialog/information.cc Sun Nov 05 17:44:49 2023 -0500 +++ b/src/gui/dialog/information.cc Sun Nov 05 23:31:49 2023 -0500 @@ -2,6 +2,7 @@ #include "core/anime.h" #include "core/anime_db.h" #include "core/strings.h" +#include "core/session.h" #include "gui/pages/anime_list.h" #include "gui/translate/anime.h" #include "gui/widgets/anime_info.h" @@ -22,6 +23,9 @@ #include #include #include +#ifdef WIN32 +#include "sys/win32/dark_theme.h" +#endif /* TODO: Taiga disables rendering of the tab widget entirely when the anime is not part of a list, which sucks. Think of a better way to implement this later. */ @@ -37,6 +41,7 @@ InformationDialog::InformationDialog(Anime::Anime& anime, std::function accept, QWidget* parent) : QDialog(parent) { + /* ack. lots of brackets here, but MUCH, MUCH MUCH better than what it used to be */ setFixedSize(842, 613); setWindowTitle(tr("Anime Information")); setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint); @@ -47,214 +52,259 @@ setPalette(pal); } - QWidget* widget = new QWidget(this); + QVBoxLayout* full_layout = new QVBoxLayout(this); + + { + /* this handles the actual page. */ + QWidget* widget = new QWidget(this); + QHBoxLayout* layout = new QHBoxLayout(widget); + + { + /* Sidebar */ + QWidget* sidebar = new QWidget(widget); + QVBoxLayout* sidebar_layout = new QVBoxLayout(sidebar); + { + /* Poster */ + Poster* poster = new Poster(anime, sidebar); + sidebar_layout->addWidget(poster); + } + sidebar_layout->setContentsMargins(0, 0, 0, 0); + sidebar_layout->addStretch(); + layout->addWidget(sidebar); + } + + { + /* ... everything else. */ + QWidget* main_widget = new QWidget(widget); + QVBoxLayout* main_layout = new QVBoxLayout(main_widget); + + main_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - /* "sidebar", includes... just the anime image :) */ - QWidget* sidebar = new QWidget(widget); - QVBoxLayout* sidebar_layout = new QVBoxLayout(sidebar); - Poster* poster = new Poster(anime, sidebar); - sidebar_layout->addWidget(poster); - sidebar_layout->setContentsMargins(0, 0, 0, 0); - sidebar_layout->addStretch(); + { + /* Anime title */ + TextWidgets::Title* anime_title = + new TextWidgets::Title(Strings::ToQString(anime.GetUserPreferredTitle()), main_widget); + main_layout->addWidget(anime_title); + } + + { + /* Tab widget, contains main info and settings */ + QTabWidget* tabbed_widget = new QTabWidget(main_widget); + tabbed_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - /* main widget */ - QWidget* main_widget = new QWidget(widget); + { + /* Main information */ + AnimeInfoWidget* main_information_widget = new AnimeInfoWidget(anime, tabbed_widget); + tabbed_widget->addTab(main_information_widget, tr("Main information")); + } - main_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + { + /* My list and settings */ + QWidget* settings_widget = new QWidget(tabbed_widget); + settings_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); - /* anime title header text */ - TextWidgets::Title* anime_title = - new TextWidgets::Title(Strings::ToQString(anime.GetUserPreferredTitle()), main_widget); + QVBoxLayout* settings_layout = new QVBoxLayout(settings_widget); + settings_layout->addWidget(new TextWidgets::Header(tr("Anime list"), settings_widget)); + + { + /* Anime List */ + QWidget* sg_anime_list_content = new QWidget(settings_widget); - /* tabbed widget */ - QTabWidget* tabbed_widget = new QTabWidget(main_widget); - tabbed_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + constexpr int LAYOUT_HORIZ_SPACING = 25; + constexpr int LAYOUT_VERT_SPACING = 5; + constexpr int LAYOUT_ITEM_WIDTH = 175; + + QVBoxLayout* al_layout = new QVBoxLayout(sg_anime_list_content); + al_layout->setSpacing(LAYOUT_VERT_SPACING); + al_layout->setContentsMargins(12, 0, 0, 0); - /* main info tab */ - AnimeInfoWidget* main_information_widget = new AnimeInfoWidget(anime, tabbed_widget); + /* Helper function for creating sections, reduces clutter. */ + const auto CREATE_SECTION = [](QWidget* parent, std::function x) { + QWidget* section = new QWidget(parent); + QGridLayout* layout = new QGridLayout(section); + layout->setHorizontalSpacing(LAYOUT_HORIZ_SPACING); + layout->setVerticalSpacing(LAYOUT_VERT_SPACING); + layout->setContentsMargins(0, 0, 0, 0); + x(section, layout); + parent->layout()->addWidget(section); + }; - QWidget* settings_widget = new QWidget(tabbed_widget); - settings_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){ + { + /* Episodes watched... */ + layout->addWidget(new QLabel(tr("Episodes watched:"), section), 0, 0); - QVBoxLayout* settings_layout = new QVBoxLayout(settings_widget); - settings_layout->addWidget(new TextWidgets::Header(tr("Anime list"), settings_widget)); - - QWidget* sg_anime_list_content = new QWidget(settings_widget); + QSpinBox* spin_box = new QSpinBox(section); + connect(spin_box, QOverload::of(&QSpinBox::valueChanged), this, [this](int i) { _progress = i; }); + spin_box->setRange(0, anime.GetEpisodes()); + spin_box->setSingleStep(1); + spin_box->setValue(_progress = anime.GetUserProgress()); + spin_box->setFixedWidth(LAYOUT_ITEM_WIDTH); + layout->addWidget(spin_box, 1, 0); + } - constexpr int LAYOUT_HORIZ_SPACING = 25; - constexpr int LAYOUT_VERT_SPACING = 5; - constexpr int LAYOUT_ITEM_WIDTH = 175; + { + /* Rewatching? */ + QCheckBox* checkbox = new QCheckBox(tr("Rewatching")); + connect(checkbox, QOverload::of(&QCheckBox::stateChanged), this, + [this](int state) { _rewatching = (state == Qt::Checked); }); + checkbox->setCheckState((_rewatching = anime.GetUserIsRewatching()) ? Qt::Checked : Qt::Unchecked); + checkbox->setFixedWidth(LAYOUT_ITEM_WIDTH); + layout->addWidget(checkbox, 1, 1); + } + layout->setColumnStretch(layout->columnCount(), 1); + }); - QVBoxLayout* al_layout = new QVBoxLayout(sg_anime_list_content); - al_layout->setSpacing(LAYOUT_VERT_SPACING); - al_layout->setContentsMargins(12, 0, 0, 0); + CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){ + { + /* Status */ + layout->addWidget(new QLabel(tr("Status:"), section), 0, 0); + + QComboBox* combo_box = new QComboBox(section); + + for (unsigned int i = 0; i < Anime::ListStatuses.size(); i++) + combo_box->addItem(Strings::ToQString(Translate::ToString(Anime::ListStatuses[i])), static_cast(Anime::ListStatuses[i])); + + connect(combo_box, QOverload::of(&QComboBox::currentIndexChanged), this, [this, combo_box](int) { + _status = static_cast(combo_box->currentData().toInt()); + }); - const auto CREATE_SECTION = [](QWidget* parent, std::function x) { - QWidget* section = new QWidget(parent); - QGridLayout* layout = new QGridLayout(section); - layout->setHorizontalSpacing(LAYOUT_HORIZ_SPACING); - layout->setVerticalSpacing(LAYOUT_VERT_SPACING); - layout->setContentsMargins(0, 0, 0, 0); - x(section, layout); - parent->layout()->addWidget(section); - }; + /* this should NEVER, EVER, be NOT_IN_LIST */ + combo_box->setCurrentIndex(static_cast(_status = anime.GetUserStatus()) - 1); + combo_box->setFixedWidth(LAYOUT_ITEM_WIDTH); + layout->addWidget(combo_box, 1, 0); + } + + { + /* Score */ + layout->addWidget(new QLabel(tr("Score:"), section), 0, 1); + + QSpinBox* spin_box = new QSpinBox(section); + connect(spin_box, QOverload::of(&QSpinBox::valueChanged), this, [this](int i) { + _score = i; + }); + spin_box->setRange(0, 100); + spin_box->setSingleStep(5); + spin_box->setValue(_score = anime.GetUserScore()); + spin_box->setFixedWidth(LAYOUT_ITEM_WIDTH); + layout->addWidget(spin_box, 1, 1); + } + layout->setColumnStretch(layout->columnCount(), 1); + }); + + CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){ + layout->addWidget(new QLabel(tr("Notes:"), section), 0, 0); - CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){ - /* Episodes watched... */ - layout->addWidget(new QLabel(tr("Episodes watched:"), section), 0, 0); + QLineEdit* line_edit = new QLineEdit(section); + connect(line_edit, &QLineEdit::textChanged, this, [this](const QString& text) { + /* this sucks but I don't really want to implement anything smarter :) */ + _notes = Strings::ToUtf8String(text); + }); + line_edit->setText(Strings::ToQString(_notes = anime.GetUserNotes())); + line_edit->setPlaceholderText(tr("Enter your notes about this anime")); + layout->addWidget(line_edit, 1, 0); + }); + + CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){ + /* Started */ + { + layout->addWidget(new QLabel(tr("Date started:"), section), 0, 0); - QSpinBox* spin_box = new QSpinBox(section); - connect(spin_box, QOverload::of(&QSpinBox::valueChanged), this, [this](int i) { _progress = i; }); - spin_box->setRange(0, anime.GetEpisodes()); - spin_box->setSingleStep(1); - spin_box->setValue(_progress = anime.GetUserProgress()); - spin_box->setFixedWidth(LAYOUT_ITEM_WIDTH); - layout->addWidget(spin_box, 1, 0); + OptionalDate* date = new OptionalDate(true, section); + connect(date, &OptionalDate::DataChanged, this, + [this](bool enabled, Date date) { _started = enabled ? date : Date(); }); + date->setFixedWidth(LAYOUT_ITEM_WIDTH); + _started = anime.GetUserDateStarted(); + if (!_started.IsValid()) { + date->SetEnabled(false); + _started = anime.GetAirDate(); + } + date->SetDate(_started); + layout->addWidget(date, 1, 0); + } + + /* Completed */ + { + layout->addWidget(new QLabel(tr("Date completed:"), section), 0, 1); - /* Rewatching? */ - QCheckBox* checkbox = new QCheckBox(tr("Rewatching")); - connect(checkbox, QOverload::of(&QCheckBox::stateChanged), this, - [this](int state) { _rewatching = (state == Qt::Checked); }); - checkbox->setCheckState((_rewatching = anime.GetUserIsRewatching()) ? Qt::Checked : Qt::Unchecked); - checkbox->setFixedWidth(LAYOUT_ITEM_WIDTH); - layout->addWidget(checkbox, 1, 1); - layout->setColumnStretch(layout->columnCount(), 1); - }); + OptionalDate* date = new OptionalDate(true, section); + connect(date, &OptionalDate::DataChanged, this, + [this](bool enabled, Date date) { _completed = enabled ? date : Date(); }); + date->setFixedWidth(LAYOUT_ITEM_WIDTH); + _completed = anime.GetUserDateCompleted(); + if (!_completed.IsValid()) { + date->SetEnabled(false); + _completed = anime.GetAirDate(); + } + date->SetDate(_completed); + layout->addWidget(date, 1, 1); + } + layout->setColumnStretch(layout->columnCount(), 1); + }); - CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){ - /* Status */ - layout->addWidget(new QLabel(tr("Status:"), section), 0, 0); + settings_layout->addWidget(sg_anime_list_content); + } + + /* + { + // commenting this out until it actually gets implemented :) - QComboBox* combo_box = new QComboBox(section); + settings_layout->addWidget(new TextWidgets::Header(tr("Local settings"), settings_widget)); + + QWidget* sg_local_content = new QWidget(settings_widget); + QVBoxLayout* sg_local_layout = new QVBoxLayout(sg_local_content); + sg_local_layout->setSpacing(5); + sg_local_layout->setContentsMargins(12, 0, 0, 0); + + CREATE_SECTION(sg_local_content, [this, &anime](QWidget* section, QGridLayout* layout){ + layout->addWidget(new QLabel(tr("Alternative titles:"), section), 0, 0); - for (unsigned int i = 0; i < Anime::ListStatuses.size(); i++) - combo_box->addItem(Strings::ToQString(Translate::ToString(Anime::ListStatuses[i])), static_cast(Anime::ListStatuses[i])); + QLineEdit* line_edit = new QLineEdit("", section); + line_edit->setPlaceholderText( + tr("Enter alternative titles here, separated by a semicolon (i.e. Title 1; Title 2)")); + layout->addWidget(line_edit, 1, 0); + + QCheckBox* checkbox = new QCheckBox(tr("Use the first alternative title to search for torrents")); + layout->addWidget(checkbox, 2, 0); + }); + + settings_layout->addWidget(sg_local_content); + } + */ + + settings_layout->addStretch(); - connect(combo_box, QOverload::of(&QComboBox::currentIndexChanged), this, [this, combo_box](int) { - _status = static_cast(combo_box->currentData().toInt()); - }); - /* this should NEVER, EVER, be NOT_IN_LIST */ - combo_box->setCurrentIndex(static_cast(_status = anime.GetUserStatus()) - 1); - combo_box->setFixedWidth(LAYOUT_ITEM_WIDTH); - layout->addWidget(combo_box, 1, 0); + tabbed_widget->addTab(settings_widget, tr("My list and settings")); + } + main_layout->addWidget(tabbed_widget); + main_layout->setContentsMargins(0, 0, 0, 0); + main_layout->setSpacing(12); + } - /* Score */ - layout->addWidget(new QLabel(tr("Score:"), section), 0, 1); + layout->addWidget(main_widget); + } + layout->setSpacing(12); + full_layout->addWidget(widget); + } - QSpinBox* spin_box = new QSpinBox(section); - connect(spin_box, QOverload::of(&QSpinBox::valueChanged), this, [this](int i) { - _score = i; + { + /* Dialog box buttons */ + QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + connect(button_box, &QDialogButtonBox::accepted, this, [this, accept, &anime] { + SaveData(anime); + accept(); + QDialog::accept(); }); - spin_box->setRange(0, 100); - spin_box->setSingleStep(5); - spin_box->setValue(_score = anime.GetUserScore()); - spin_box->setFixedWidth(LAYOUT_ITEM_WIDTH); - layout->addWidget(spin_box, 1, 1); - layout->setColumnStretch(layout->columnCount(), 1); - }); - - CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){ - layout->addWidget(new QLabel(tr("Notes:"), section), 0, 0); - - QLineEdit* line_edit = new QLineEdit(section); - connect(line_edit, &QLineEdit::textChanged, this, [this](const QString& text) { - /* this sucks but I don't really want to implement anything smarter :) */ - _notes = Strings::ToUtf8String(text); - }); - line_edit->setText(Strings::ToQString(_notes = anime.GetUserNotes())); - line_edit->setPlaceholderText(tr("Enter your notes about this anime")); - layout->addWidget(line_edit, 1, 0); - }); - - CREATE_SECTION(sg_anime_list_content, [this, &anime](QWidget* section, QGridLayout* layout){ - /* Started */ - layout->addWidget(new QLabel(tr("Date started:"), section), 0, 0); - - OptionalDate* date = new OptionalDate(true, section); - connect(date, &OptionalDate::DataChanged, this, - [this](bool enabled, Date date) { _started = enabled ? date : Date(); }); - date->setFixedWidth(LAYOUT_ITEM_WIDTH); - _started = anime.GetUserDateStarted(); - if (!_started.IsValid()) { - date->SetEnabled(false); - _started = anime.GetAirDate(); - } - date->SetDate(_started); - layout->addWidget(date, 1, 0); - - /* Completed */ - layout->addWidget(new QLabel(tr("Date completed:"), section), 0, 1); - - date = new OptionalDate(true, section); - connect(date, &OptionalDate::DataChanged, this, - [this](bool enabled, Date date) { _completed = enabled ? date : Date(); }); - date->setFixedWidth(LAYOUT_ITEM_WIDTH); - _completed = anime.GetUserDateCompleted(); - if (!_completed.IsValid()) { - date->SetEnabled(false); - _completed = anime.GetAirDate(); - } - date->SetDate(_completed); - layout->addWidget(date, 1, 1); - layout->setColumnStretch(layout->columnCount(), 1); - }); + connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); + full_layout->addWidget(button_box, 0, Qt::AlignBottom); + } +} - settings_layout->addWidget(sg_anime_list_content); - -/* - // commenting this out until it actually gets implemented :) - - settings_layout->addWidget(new TextWidgets::Header(tr("Local settings"), settings_widget)); - - QWidget* sg_local_content = new QWidget(settings_widget); - QVBoxLayout* sg_local_layout = new QVBoxLayout(sg_local_content); - sg_local_layout->setSpacing(5); - sg_local_layout->setContentsMargins(12, 0, 0, 0); - - CREATE_SECTION(sg_local_content, [this, &anime](QWidget* section, QGridLayout* layout){ - layout->addWidget(new QLabel(tr("Alternative titles:"), section), 0, 0); - - QLineEdit* line_edit = new QLineEdit("", section); - line_edit->setPlaceholderText( - tr("Enter alternative titles here, separated by a semicolon (i.e. Title 1; Title 2)")); - layout->addWidget(line_edit, 1, 0); - - QCheckBox* checkbox = new QCheckBox(tr("Use the first alternative title to search for torrents")); - layout->addWidget(checkbox, 2, 0); - }); - - settings_layout->addWidget(sg_local_content); -*/ -#undef CREATE_SECTION -#undef CREATE_FULL_WIDTH_SECTION - - settings_layout->addStretch(); - - tabbed_widget->addTab(main_information_widget, tr("Main information")); - tabbed_widget->addTab(settings_widget, tr("My list and settings")); - - QVBoxLayout* main_layout = new QVBoxLayout(main_widget); - main_layout->addWidget(anime_title); - main_layout->addWidget(tabbed_widget); - main_layout->setContentsMargins(0, 0, 0, 0); - main_layout->setSpacing(12); - - QHBoxLayout* layout = new QHBoxLayout(widget); - layout->addWidget(sidebar); - layout->addWidget(main_widget); - layout->setSpacing(12); - - QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); - connect(button_box, &QDialogButtonBox::accepted, this, [this, accept, &anime] { - SaveData(anime); - accept(); - QDialog::accept(); - }); - connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); - - QVBoxLayout* buttons_layout = new QVBoxLayout(this); - buttons_layout->addWidget(widget); - buttons_layout->addWidget(button_box, 0, Qt::AlignBottom); +void InformationDialog::showEvent(QShowEvent* event) { + QDialog::showEvent(event); +#ifdef WIN32 + win32::SetTitleBarsToBlack(session.config.theme.IsInDarkTheme()); +#endif } #include "gui/dialog/moc_information.cpp" diff -r 49c8d1976869 -r 2004b41d4a59 src/gui/dialog/settings.cc --- a/src/gui/dialog/settings.cc Sun Nov 05 17:44:49 2023 -0500 +++ b/src/gui/dialog/settings.cc Sun Nov 05 23:31:49 2023 -0500 @@ -1,4 +1,5 @@ #include "gui/dialog/settings.h" +#include "core/session.h" #include "gui/widgets/sidebar.h" #include "gui/widgets/text.h" #include @@ -7,6 +8,9 @@ #include #include #include +#ifdef WIN32 +#include "sys/win32/dark_theme.h" +#endif SettingsPage::SettingsPage(QWidget* parent, QString title) : QWidget(parent) { setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); @@ -60,48 +64,68 @@ QDialog::accept(); } +void SettingsDialog::showEvent(QShowEvent* event) { + QDialog::showEvent(event); +#ifdef WIN32 + win32::SetTitleBarsToBlack(session.config.theme.IsInDarkTheme()); +#endif +} + SettingsDialog::SettingsDialog(QWidget* parent) : QDialog(parent) { setFixedSize(755, 566); setWindowTitle(tr("Settings")); setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint); - QWidget* widget = new QWidget(this); - widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - sidebar = new SideBar(widget); - sidebar->setCurrentItem(sidebar->AddItem(tr("Services"), SideBar::CreateIcon(":/icons/24x24/globe.png"))); - // sidebar->AddItem(tr("Library"), SideBar::CreateIcon(":/icons/24x24/inbox-film.png")); - sidebar->AddItem(tr("Application"), SideBar::CreateIcon(":/icons/24x24/application-sidebar-list.png")); - // sidebar->AddItem(tr("Recognition"), SideBar::CreateIcon(":/icons/24x24/question.png")); - // sidebar->AddItem(tr("Sharing"), SideBar::CreateIcon(":/icons/24x24/megaphone.png")); - // sidebar->AddItem(tr("Torrents"), SideBar::CreateIcon(":/icons/24x24/feed.png")); - // sidebar->AddItem(tr("Advanced"), SideBar::CreateIcon(":/icons/24x24/gear.png")); - sidebar->setIconSize(QSize(24, 24)); - sidebar->setFrameShape(QFrame::Box); + + QVBoxLayout* full_layout = new QVBoxLayout(this); + + { + QWidget* widget = new QWidget(this); + widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + QHBoxLayout* layout = new QHBoxLayout(widget); + + { + sidebar = new SideBar(widget); - QPalette pal(sidebar->palette()); - sidebar->SetBackgroundColor(pal.color(QPalette::Base)); + sidebar->setCurrentItem(sidebar->AddItem(tr("Services"), SideBar::CreateIcon(":/icons/24x24/globe.png"))); + // sidebar->AddItem(tr("Library"), SideBar::CreateIcon(":/icons/24x24/inbox-film.png")); + sidebar->AddItem(tr("Application"), SideBar::CreateIcon(":/icons/24x24/application-sidebar-list.png")); + // sidebar->AddItem(tr("Recognition"), SideBar::CreateIcon(":/icons/24x24/question.png")); + // sidebar->AddItem(tr("Sharing"), SideBar::CreateIcon(":/icons/24x24/megaphone.png")); + // sidebar->AddItem(tr("Torrents"), SideBar::CreateIcon(":/icons/24x24/feed.png")); + // sidebar->AddItem(tr("Advanced"), SideBar::CreateIcon(":/icons/24x24/gear.png")); - sidebar->setFixedWidth(158); - sidebar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + sidebar->setIconSize(QSize(24, 24)); + sidebar->setFrameShape(QFrame::Box); + + QPalette pal(sidebar->palette()); + sidebar->SetBackgroundColor(pal.color(QPalette::Base)); - stacked = new QStackedWidget(this); - stacked->addWidget(new SettingsPageServices(stacked)); - stacked->addWidget(new SettingsPageApplication(stacked)); - stacked->setCurrentIndex(0); + sidebar->setFixedWidth(158); + sidebar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + layout->addWidget(sidebar); + } - connect(sidebar, &QListWidget::currentRowChanged, stacked, &QStackedWidget::setCurrentIndex); + { + stacked = new QStackedWidget(widget); + stacked->addWidget(new SettingsPageServices(stacked)); + stacked->addWidget(new SettingsPageApplication(stacked)); + stacked->setCurrentIndex(0); + + connect(sidebar, &QListWidget::currentRowChanged, stacked, &QStackedWidget::setCurrentIndex); - QHBoxLayout* layout = new QHBoxLayout(widget); - layout->addWidget(sidebar); - layout->addWidget(stacked); - layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(stacked); + } + + layout->setContentsMargins(0, 0, 0, 0); + full_layout->addWidget(widget); + } - QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); - connect(button_box, &QDialogButtonBox::accepted, this, &SettingsDialog::OnOK); - connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); - - QVBoxLayout* buttons_layout = new QVBoxLayout(this); - buttons_layout->addWidget(widget); - buttons_layout->addWidget(button_box); + { + QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + connect(button_box, &QDialogButtonBox::accepted, this, &SettingsDialog::OnOK); + connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); + full_layout->addWidget(button_box); + } } #include "gui/dialog/moc_settings.cpp" diff -r 49c8d1976869 -r 2004b41d4a59 src/gui/dialog/settings/application.cc --- a/src/gui/dialog/settings/application.cc Sun Nov 05 17:44:49 2023 -0500 +++ b/src/gui/dialog/settings/application.cc Sun Nov 05 23:31:49 2023 -0500 @@ -1,6 +1,8 @@ #include "core/session.h" +#include "core/strings.h" #include "gui/dialog/settings.h" #include "gui/theme.h" +#include "gui/locale.h" #include #include #include @@ -9,109 +11,178 @@ #include #include #include +#include QWidget* SettingsPageApplication::CreateAnimeListWidget() { QWidget* result = new QWidget(this); result->setAutoFillBackground(true); result->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); - QGroupBox* actions_group_box = new QGroupBox(tr("Actions"), result); - actions_group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + QVBoxLayout* full_layout = new QVBoxLayout(result); - /* Actions/Double click */ - QWidget* double_click_widget = new QWidget(actions_group_box); - QLabel* dc_combo_box_label = new QLabel(tr("Double click:"), double_click_widget); - QComboBox* dc_combo_box = new QComboBox(double_click_widget); - dc_combo_box->addItem(tr("View anime info")); + { + /* Actions */ + QGroupBox* actions_group_box = new QGroupBox(tr("Actions"), result); + actions_group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + + QHBoxLayout* actions_layout = new QHBoxLayout(actions_group_box); - QVBoxLayout* double_click_layout = new QVBoxLayout(double_click_widget); - double_click_layout->addWidget(dc_combo_box_label); - double_click_layout->addWidget(dc_combo_box); - double_click_layout->setContentsMargins(0, 0, 0, 0); + { + /* Actions/Double click */ + QWidget* double_click_widget = new QWidget(actions_group_box); + QLabel* dc_combo_box_label = new QLabel(tr("Double click:"), double_click_widget); + QComboBox* dc_combo_box = new QComboBox(double_click_widget); + dc_combo_box->addItem(tr("View anime info")); - /* Actions/Middle click */ - QWidget* middle_click_widget = new QWidget(actions_group_box); - QLabel* mc_combo_box_label = new QLabel(tr("Middle click:"), middle_click_widget); - QComboBox* mc_combo_box = new QComboBox(middle_click_widget); - mc_combo_box->addItem(tr("Play next episode")); + QVBoxLayout* double_click_layout = new QVBoxLayout(double_click_widget); + double_click_layout->addWidget(dc_combo_box_label); + double_click_layout->addWidget(dc_combo_box); + double_click_layout->setContentsMargins(0, 0, 0, 0); + + actions_layout->addWidget(double_click_widget); + } + + { + /* Actions/Middle click */ + QWidget* middle_click_widget = new QWidget(actions_group_box); + QLabel* mc_combo_box_label = new QLabel(tr("Middle click:"), middle_click_widget); + QComboBox* mc_combo_box = new QComboBox(middle_click_widget); + mc_combo_box->addItem(tr("Play next episode")); - QVBoxLayout* middle_click_layout = new QVBoxLayout(middle_click_widget); - middle_click_layout->addWidget(mc_combo_box_label); - middle_click_layout->addWidget(mc_combo_box); - middle_click_layout->setContentsMargins(0, 0, 0, 0); + QVBoxLayout* middle_click_layout = new QVBoxLayout(middle_click_widget); + middle_click_layout->addWidget(mc_combo_box_label); + middle_click_layout->addWidget(mc_combo_box); + middle_click_layout->setContentsMargins(0, 0, 0, 0); + + actions_layout->addWidget(middle_click_widget); + } - /* Actions */ - QHBoxLayout* actions_layout = new QHBoxLayout(actions_group_box); - actions_layout->addWidget(double_click_widget); - actions_layout->addWidget(middle_click_widget); + full_layout->addWidget(actions_group_box); + } + + { + /* Appearance */ + QGroupBox* appearance_group_box = new QGroupBox(tr("Appearance"), result); + appearance_group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + + QVBoxLayout* appearance_layout = new QVBoxLayout(appearance_group_box); - QGroupBox* appearance_group_box = new QGroupBox(tr("Appearance"), result); - appearance_group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + { + /* Title language */ + { + QLabel* lang_combo_box_label = new QLabel(tr("Title language preference:"), appearance_group_box); + appearance_layout->addWidget(lang_combo_box_label); + } + { + QComboBox* lang_combo_box = new QComboBox(appearance_group_box); + lang_combo_box->addItem(tr("Romaji")); + lang_combo_box->addItem(tr("Native")); + lang_combo_box->addItem(tr("English")); + connect(lang_combo_box, QOverload::of(&QComboBox::currentIndexChanged), this, + [this](int index) { language = static_cast(index); }); + lang_combo_box->setCurrentIndex(static_cast(language)); + appearance_layout->addWidget(lang_combo_box); + } + } - QLabel* lang_combo_box_label = new QLabel(tr("Title language preference:"), appearance_group_box); - QComboBox* lang_combo_box = new QComboBox(appearance_group_box); - lang_combo_box->addItem(tr("Romaji")); - lang_combo_box->addItem(tr("Native")); - lang_combo_box->addItem(tr("English")); - connect(lang_combo_box, QOverload::of(&QComboBox::currentIndexChanged), this, - [this](int index) { language = static_cast(index); }); - lang_combo_box->setCurrentIndex(static_cast(language)); + { + /* Application theme */ + { + QLabel* theme_combo_box_label = new QLabel(tr("Application theme:"), appearance_group_box); + appearance_layout->addWidget(theme_combo_box_label); + } - QLabel* theme_combo_box_label = new QLabel(tr("Application theme:"), appearance_group_box); - QComboBox* theme_combo_box = new QComboBox(appearance_group_box); - theme_combo_box->addItem(tr("Default")); - theme_combo_box->addItem(tr("Light")); - theme_combo_box->addItem(tr("Dark")); - connect(theme_combo_box, QOverload::of(&QComboBox::currentIndexChanged), this, - [this](int index) { theme = static_cast(index); }); - theme_combo_box->setCurrentIndex(static_cast(theme)); + { + QComboBox* theme_combo_box = new QComboBox(appearance_group_box); + theme_combo_box->addItem(tr("Default")); + theme_combo_box->addItem(tr("Light")); + theme_combo_box->addItem(tr("Dark")); + connect(theme_combo_box, QOverload::of(&QComboBox::currentIndexChanged), this, + [this](int index) { theme = static_cast(index); }); + theme_combo_box->setCurrentIndex(static_cast(theme)); + appearance_layout->addWidget(theme_combo_box); + } + } + + { + /* Application locale */ + { + QLabel* locale_combo_box_label = new QLabel(tr("Set application locale (requires restart):"), appearance_group_box); + appearance_layout->addWidget(locale_combo_box_label); + } - QCheckBox* hl_anime_box = - new QCheckBox(tr("Highlight anime if next episode is available in library folders"), appearance_group_box); - QCheckBox* hl_above_anime_box = new QCheckBox(tr("Display highlighted anime above others"), appearance_group_box); - connect(hl_anime_box, &QCheckBox::stateChanged, this, [this, hl_above_anime_box](int state) { - highlight_anime_if_available = !(state == Qt::Unchecked); - hl_above_anime_box->setEnabled(state); - }); - connect(hl_above_anime_box, &QCheckBox::stateChanged, this, - [this](int state) { highlight_anime_if_available = !(state == Qt::Unchecked); }); - hl_anime_box->setCheckState(highlight_anime_if_available ? Qt::Checked : Qt::Unchecked); - hl_above_anime_box->setCheckState(highlighted_anime_above_others ? Qt::Checked : Qt::Unchecked); - hl_above_anime_box->setEnabled(hl_anime_box->checkState() != Qt::Unchecked); - hl_above_anime_box->setContentsMargins(10, 0, 0, 0); + { + QComboBox* locale_combo_box = new QComboBox(appearance_group_box); + const auto& available_locales = session.config.locale.GetAvailableLocales(); + for (const auto& l : available_locales) + locale_combo_box->addItem(Strings::ToQString(Locale::GetLocaleFullName(l)), l); + + connect(locale_combo_box, QOverload::of(&QComboBox::currentIndexChanged), this, + [this, locale_combo_box](int) { locale = locale_combo_box->currentData().toLocale(); }); + + for (size_t i = 0; i < available_locales.size(); i++) + if (available_locales[i] == locale) + locale_combo_box->setCurrentIndex(i); + appearance_layout->addWidget(locale_combo_box); + } + } + + { + /* Hopefully I made this easy to parse... */ + QCheckBox* hl_above_anime_box = new QCheckBox(tr("Display highlighted anime above others"), appearance_group_box); + hl_above_anime_box->setCheckState(highlighted_anime_above_others ? Qt::Checked : Qt::Unchecked); + hl_above_anime_box->setEnabled(highlight_anime_if_available); + hl_above_anime_box->setContentsMargins(10, 0, 0, 0); + + connect(hl_above_anime_box, &QCheckBox::stateChanged, this, + [this](int state) { highlight_anime_if_available = !(state == Qt::Unchecked); }); - /* Appearance */ - QVBoxLayout* appearance_layout = new QVBoxLayout(appearance_group_box); - appearance_layout->addWidget(lang_combo_box_label); - appearance_layout->addWidget(lang_combo_box); - appearance_layout->addWidget(theme_combo_box_label); - appearance_layout->addWidget(theme_combo_box); - appearance_layout->addWidget(hl_anime_box); - appearance_layout->addWidget(hl_above_anime_box); + { + /* This is here because the above checkbox actually depends on it to be checked. */ + QCheckBox* hl_anime_box = new QCheckBox(tr("Highlight anime if next episode is available in library folders"), appearance_group_box); + hl_anime_box->setCheckState(highlight_anime_if_available ? Qt::Checked : Qt::Unchecked); - QGroupBox* progress_group_box = new QGroupBox(tr("Progress"), result); - progress_group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + connect(hl_anime_box, &QCheckBox::stateChanged, this, [this, hl_above_anime_box](int state) { + highlight_anime_if_available = !(state == Qt::Unchecked); + hl_above_anime_box->setEnabled(state); + }); + + appearance_layout->addWidget(hl_anime_box); + } + + appearance_layout->addWidget(hl_above_anime_box); + } + + full_layout->addWidget(appearance_group_box); + } - QCheckBox* progress_display_aired_episodes = - new QCheckBox(tr("Display aired episodes (estimated)"), progress_group_box); - connect(progress_display_aired_episodes, &QCheckBox::stateChanged, this, - [this](int state) { display_aired_episodes = !(state == Qt::Unchecked); }); - progress_display_aired_episodes->setCheckState(display_aired_episodes ? Qt::Checked : Qt::Unchecked); + { + /* Progress */ + QGroupBox* progress_group_box = new QGroupBox(tr("Progress"), result); + progress_group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + + QVBoxLayout* progress_layout = new QVBoxLayout(progress_group_box); - QCheckBox* progress_display_available_episodes = - new QCheckBox(tr("Display available episodes in library folders"), progress_group_box); - connect(progress_display_available_episodes, &QCheckBox::stateChanged, this, - [this](int state) { display_available_episodes = !(state == Qt::Unchecked); }); - progress_display_available_episodes->setCheckState(display_available_episodes ? Qt::Checked : Qt::Unchecked); + { + QCheckBox* progress_display_aired_episodes = + new QCheckBox(tr("Display aired episodes (estimated)"), progress_group_box); + connect(progress_display_aired_episodes, &QCheckBox::stateChanged, this, + [this](int state) { display_aired_episodes = !(state == Qt::Unchecked); }); + progress_display_aired_episodes->setCheckState(display_aired_episodes ? Qt::Checked : Qt::Unchecked); + progress_layout->addWidget(progress_display_aired_episodes); + } + { + QCheckBox* progress_display_available_episodes = + new QCheckBox(tr("Display available episodes in library folders"), progress_group_box); + connect(progress_display_available_episodes, &QCheckBox::stateChanged, this, + [this](int state) { display_available_episodes = !(state == Qt::Unchecked); }); + progress_display_available_episodes->setCheckState(display_available_episodes ? Qt::Checked : Qt::Unchecked); + progress_layout->addWidget(progress_display_available_episodes); + } - QVBoxLayout* progress_layout = new QVBoxLayout(progress_group_box); - progress_layout->addWidget(progress_display_aired_episodes); - progress_layout->addWidget(progress_display_available_episodes); + full_layout->addWidget(progress_group_box); + } - QVBoxLayout* full_layout = new QVBoxLayout(result); - full_layout->addWidget(actions_group_box); - full_layout->addWidget(appearance_group_box); - full_layout->addWidget(progress_group_box); full_layout->setSpacing(10); full_layout->addStretch(); @@ -125,11 +196,13 @@ session.config.anime_list.display_aired_episodes = display_aired_episodes; session.config.anime_list.display_available_episodes = display_available_episodes; session.config.theme.SetTheme(theme); + session.config.locale.SetActiveLocale(locale); } SettingsPageApplication::SettingsPageApplication(QWidget* parent) : SettingsPage(parent, tr("Application")) { language = session.config.anime_list.language; theme = session.config.theme.GetTheme(); + locale = session.config.locale.GetLocale(); highlighted_anime_above_others = session.config.anime_list.highlighted_anime_above_others; highlight_anime_if_available = session.config.anime_list.highlight_anime_if_available; display_aired_episodes = session.config.anime_list.display_aired_episodes; diff -r 49c8d1976869 -r 2004b41d4a59 src/gui/dialog/settings/services.cc --- a/src/gui/dialog/settings/services.cc Sun Nov 05 17:44:49 2023 -0500 +++ b/src/gui/dialog/settings/services.cc Sun Nov 05 23:31:49 2023 -0500 @@ -15,27 +15,37 @@ QWidget* result = new QWidget(this); result->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); - QGroupBox* sync_group_box = new QGroupBox(tr("Synchronization"), result); - sync_group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + QVBoxLayout* full_layout = new QVBoxLayout(result); - QLabel* sync_combo_box_label = new QLabel(tr("Active service and metadata provider:"), sync_group_box); + { + QGroupBox* sync_group_box = new QGroupBox(tr("Synchronization"), result); + sync_group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); - QComboBox* sync_combo_box = new QComboBox(sync_group_box); - sync_combo_box->addItem(tr("AniList")); - connect(sync_combo_box, QOverload::of(&QComboBox::currentIndexChanged), this, - [this](int index) { service = static_cast(index + 1); }); - sync_combo_box->setCurrentIndex(static_cast(service) - 1); + QVBoxLayout* sync_layout = new QVBoxLayout(sync_group_box); + + { + QLabel* sync_combo_box_label = new QLabel(tr("Active service and metadata provider:"), sync_group_box); + sync_layout->addWidget(sync_combo_box_label); + } - QLabel* sync_note_label = - new QLabel(tr("Note: Minori is unable to synchronize multiple services at the same time."), sync_group_box); + { + QComboBox* sync_combo_box = new QComboBox(sync_group_box); + sync_combo_box->addItem(tr("AniList")); + connect(sync_combo_box, QOverload::of(&QComboBox::currentIndexChanged), this, + [this](int index) { service = static_cast(index + 1); }); + sync_combo_box->setCurrentIndex(static_cast(service) - 1); + sync_layout->addWidget(sync_combo_box); + } - QVBoxLayout* sync_layout = new QVBoxLayout(sync_group_box); - sync_layout->addWidget(sync_combo_box_label); - sync_layout->addWidget(sync_combo_box); - sync_layout->addWidget(sync_note_label); + { + QLabel* sync_note_label = + new QLabel(tr("Note: Minori is unable to synchronize multiple services at the same time."), sync_group_box); + sync_layout->addWidget(sync_note_label); + } - QVBoxLayout* full_layout = new QVBoxLayout(result); - full_layout->addWidget(sync_group_box); + full_layout->addWidget(sync_group_box); + } + full_layout->setSpacing(10); full_layout->addStretch(); @@ -46,51 +56,69 @@ QWidget* result = new QWidget(this); result->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - QGroupBox* group_box = new QGroupBox(tr("Account"), result); - group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + QVBoxLayout* full_layout = new QVBoxLayout(result); - /* this is outdated! usernames are retrieved through a request to AniList now. - although that's a bit... erm... cancerous, maybe this method IS useful. IDK */ - QLabel* username_entry_label = new QLabel(tr("Username: (not your email address)"), group_box); + { + /* Account */ + QGroupBox* group_box = new QGroupBox(tr("Account"), result); + group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + + QVBoxLayout* layout = new QVBoxLayout(group_box); - QWidget* auth_widget = new QWidget(group_box); - QLineEdit* username_entry = new QLineEdit(username, auth_widget); - connect(username_entry, &QLineEdit::editingFinished, this, - [this, username_entry] { username = username_entry->text(); }); + { + QLabel* username_entry_label = new QLabel(tr("Username: (not your email address)"), group_box); + layout->addWidget(username_entry_label); + } - QPushButton* auth_button = new QPushButton(auth_widget); - connect(auth_button, &QPushButton::clicked, this, [] { Services::AniList::AuthorizeUser(); }); - auth_button->setText(session.config.anilist.auth_token.empty() ? tr("Authorize...") : tr("Re-authorize...")); + { + /* Authorization */ + QWidget* auth_widget = new QWidget(group_box); + QHBoxLayout* auth_layout = new QHBoxLayout(auth_widget); - QHBoxLayout* auth_layout = new QHBoxLayout(auth_widget); - auth_layout->addWidget(username_entry); - auth_layout->addWidget(auth_button); + { + /* Username: this literally never gets used btw */ + QLineEdit* username_entry = new QLineEdit(username, auth_widget); + connect(username_entry, &QLineEdit::editingFinished, this, + [this, username_entry] { username = username_entry->text(); }); + auth_layout->addWidget(username_entry); + } + + { + /* The actual auth button */ + QPushButton* auth_button = new QPushButton(auth_widget); + connect(auth_button, &QPushButton::clicked, this, [] { Services::AniList::AuthorizeUser(); }); + auth_button->setText(session.config.anilist.auth_token.empty() ? tr("Authorize...") : tr("Re-authorize...")); + auth_layout->addWidget(auth_button); + } - QLabel* note_label = new QLabel(tr("Create a new AniList account"), group_box); - note_label->setTextFormat(Qt::RichText); - note_label->setTextInteractionFlags(Qt::TextBrowserInteraction); - note_label->setOpenExternalLinks(true); + layout->addWidget(auth_widget); + } - QVBoxLayout* layout = new QVBoxLayout(group_box); - layout->addWidget(username_entry_label); - layout->addWidget(auth_widget); - layout->addWidget(note_label); + { + /* Note on creating new accounts... */ + QLabel* note_label = new QLabel(tr("Create a new AniList account"), group_box); + note_label->setTextFormat(Qt::RichText); + note_label->setTextInteractionFlags(Qt::TextBrowserInteraction); + note_label->setOpenExternalLinks(true); + layout->addWidget(note_label); + } + + full_layout->addWidget(group_box); + } - QVBoxLayout* full_layout = new QVBoxLayout(result); - full_layout->addWidget(group_box); full_layout->setSpacing(10); full_layout->addStretch(); return result; } void SettingsPageServices::SaveInfo() { - // session.config.anilist.username = - Strings::ToUtf8String(username); + // see services/anilist.cc for why this is commented out + // session.config.anilist.username = Strings::ToUtf8String(username); session.config.service = service; } SettingsPageServices::SettingsPageServices(QWidget* parent) : SettingsPage(parent, tr("Services")) { - username = QString::fromUtf8(session.config.anilist.username.c_str()); + // username = QString::fromUtf8(session.config.anilist.username.c_str()); service = session.config.service; AddTab(CreateMainPage(), tr("Main")); AddTab(CreateAniListPage(), tr("AniList")); diff -r 49c8d1976869 -r 2004b41d4a59 src/gui/pages/anime_list.cc --- a/src/gui/pages/anime_list.cc Sun Nov 05 17:44:49 2023 -0500 +++ b/src/gui/pages/anime_list.cc Sun Nov 05 23:31:49 2023 -0500 @@ -29,42 +29,6 @@ #include #include -AnimeListPageDelegate::AnimeListPageDelegate(QObject* parent) : QStyledItemDelegate(parent) { -} - -QWidget* AnimeListPageDelegate::createEditor(QWidget*, const QStyleOptionViewItem&, const QModelIndex&) const { - // no edit 4 u - return nullptr; -} - -void AnimeListPageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, - const QModelIndex& index) const { - switch (index.column()) { -#if 0 /* I'm just commenting this out for now. Seems like a huge headache to do with Qt. */ - case AnimeListPageModel::AL_PROGRESS: { - const int progress = static_cast(index.data(Qt::UserRole).toReal()); - const int episodes = - static_cast(index.siblingAtColumn(AnimeListPageModel::AL_EPISODES).data(Qt::UserRole).toReal()); - - int text_width = 59; - QRectF text_rect(option.rect.x() + text_width, option.rect.y(), text_width, option.decorationSize.height()); - painter->save(); - painter->drawText(text_rect, tr("/"), QTextOption(Qt::AlignCenter | Qt::AlignVCenter)); - drawText(const QRectF &rectangle, const QString &text, const QTextOption &option = - QTextOption()) painter->drawText(QRectF(text_rect.x(), text_rect.y(), text_width / 2 - 2, - text_rect.height()), QString::number(progress), QTextOption(Qt::AlignRight | Qt::AlignVCenter)); - painter->drawText( - QRectF(text_rect.x() + text_width / 2 + 2, text_rect.y(), text_width / 2 - 2, text_rect.height()), - QString::number(episodes), QTextOption(Qt::AlignLeft | Qt::AlignVCenter)); - painter->restore(); - QStyledItemDelegate::paint(painter, option, index); - break; - } -#endif - default: QStyledItemDelegate::paint(painter, option, index); break; - } -} - AnimeListPageSortFilter::AnimeListPageSortFilter(QObject* parent) : QSortFilterProxyModel(parent) { } @@ -423,7 +387,6 @@ /* Tree view... */ QWidget* tree_widget = new QWidget(this); tree_view = new QTreeView(tree_widget); - tree_view->setItemDelegate(new AnimeListPageDelegate(tree_view)); tree_view->setUniformRowHeights(true); tree_view->setAllColumnsShowFocus(false); tree_view->setAlternatingRowColors(true); diff -r 49c8d1976869 -r 2004b41d4a59 src/gui/theme.cc --- a/src/gui/theme.cc Sun Nov 05 17:44:49 2023 -0500 +++ b/src/gui/theme.cc Sun Nov 05 23:31:49 2023 -0500 @@ -31,7 +31,7 @@ return theme; } -bool Theme::IsInDarkMode() { +bool Theme::IsInDarkTheme() { if (theme != Themes::OS) return (theme == Themes::DARK); #ifdef MACOSX diff -r 49c8d1976869 -r 2004b41d4a59 src/gui/window.cc --- a/src/gui/window.cc Sun Nov 05 17:44:49 2023 -0500 +++ b/src/gui/window.cc Sun Nov 05 23:31:49 2023 -0500 @@ -275,7 +275,7 @@ void MainWindow::showEvent(QShowEvent* event) { QMainWindow::showEvent(event); #ifdef WIN32 - win32::SetTitleBarToBlack(this, session.config.theme.IsInDarkMode()); + win32::SetTitleBarsToBlack(session.config.theme.IsInDarkTheme()); #endif } diff -r 49c8d1976869 -r 2004b41d4a59 src/main.cc --- a/src/main.cc Sun Nov 05 17:44:49 2023 -0500 +++ b/src/main.cc Sun Nov 05 23:31:49 2023 -0500 @@ -2,14 +2,13 @@ #include "gui/window.h" #include #include +#include #include Session session; int main(int argc, char** argv) { QApplication app(argc, argv); - /* this is a reasonable default, I presume */ - QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates)); session.config.Load(); diff -r 49c8d1976869 -r 2004b41d4a59 src/track/media.cc --- a/src/track/media.cc Sun Nov 05 17:44:49 2023 -0500 +++ b/src/track/media.cc Sun Nov 05 23:31:49 2023 -0500 @@ -4,7 +4,6 @@ #include "anitomy/anitomy.h" #include "core/filesystem.h" #include "core/strings.h" -#include #include #include #include @@ -17,14 +16,13 @@ std::vector pids = Animia::get_all_pids(); for (int i : pids) { for (const std::string& player : media_players) { - if (Animia::get_process_name(i) == player) { - std::vector files = Animia::filter_system_files(Animia::get_open_files(i)); - for (const std::string& f : files) { - Filesystem::Path p(f); - for (const std::string& ext : media_extensions) { - if (p.Extension() == ext) - return p; - } + if (Animia::get_process_name(i) != player) + continue; + for (const std::string& f : Animia::filter_system_files(Animia::get_open_files(i))) { + Filesystem::Path p(f); + for (const std::string& ext : media_extensions) { + if (p.Extension() == ext) + return p; } } }