diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 96ff368b..f93ce14e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -66,13 +66,14 @@ jobs: run: | sudo apt-get update sudo apt-get install -y libunarr-dev libgl-dev libgles2-mesa-dev \ - libfontconfig1-dev libfreetype-dev libxkbcommon-dev libpoppler-qt6-dev + libfontconfig1-dev libfreetype-dev libxkbcommon-dev libpoppler-qt6-dev \ + libspeechd-dev - name: Install Qt uses: jurplel/install-qt-action@v4 with: version: '6.9.3' - modules: 'qt5compat qtmultimedia qtimageformats qtshadertools' + modules: 'qt5compat qtmultimedia qtimageformats qtshadertools qtspeech' cache: true - name: Build @@ -98,13 +99,14 @@ jobs: run: | sudo apt-get update sudo apt-get install -y libgl-dev libgles2-mesa-dev \ - libfontconfig1-dev libfreetype-dev libxkbcommon-dev libpoppler-qt6-dev + libfontconfig1-dev libfreetype-dev libxkbcommon-dev libpoppler-qt6-dev \ + libspeechd-dev - name: Install Qt uses: jurplel/install-qt-action@v4 with: version: '6.9.3' - modules: 'qt5compat qtmultimedia qtimageformats qtshadertools' + modules: 'qt5compat qtmultimedia qtimageformats qtshadertools qtspeech' cache: true - name: Build @@ -134,7 +136,7 @@ jobs: - name: Install dependencies run: | pip3 install --break-system-packages aqtinstall - python3 -m aqt install-qt mac desktop 6.9.3 -m qt5compat qtmultimedia qtimageformats qtshadertools + python3 -m aqt install-qt mac desktop 6.9.3 -m qt5compat qtmultimedia qtimageformats qtshadertools qtspeech echo "${{ github.workspace }}/6.9.3/macos/bin" >> $GITHUB_PATH npm install -g appdmg @@ -189,7 +191,7 @@ jobs: pip install -U pip pip install aqtinstall mkdir C:\Qt - python -m aqt install-qt windows desktop 6.9.3 win64_msvc2022_64 -O c:\Qt -m qt5compat qtmultimedia qtimageformats qtshadertools + python -m aqt install-qt windows desktop 6.9.3 win64_msvc2022_64 -O c:\Qt -m qt5compat qtmultimedia qtimageformats qtshadertools qtspeech dir C:\Qt\6.9.3\msvc2022_64\bin curl.exe -L --retry 5 --retry-delay 5 --retry-all-errors "https://aka.ms/vs/17/release/vc_redist.x64.exe" -o "%GITHUB_WORKSPACE%\vc_redist.x64.exe" where iscc @@ -333,8 +335,8 @@ jobs: run: | pip install aqtinstall mkdir C:\Qt - python -m aqt install-qt windows desktop 6.9.3 win64_msvc2022_64 -O c:\Qt -m qt5compat qtmultimedia qtimageformats qtshadertools - python -m aqt install-qt windows desktop 6.9.3 win64_msvc2022_arm64_cross_compiled -O c:\Qt -m qt5compat qtmultimedia qtimageformats qtshadertools + python -m aqt install-qt windows desktop 6.9.3 win64_msvc2022_64 -O c:\Qt -m qt5compat qtmultimedia qtimageformats qtshadertools qtspeech + python -m aqt install-qt windows desktop 6.9.3 win64_msvc2022_arm64_cross_compiled -O c:\Qt -m qt5compat qtmultimedia qtimageformats qtshadertools qtspeech dir C:\Qt\6.9.3\msvc2022_arm64\bin curl.exe -L --retry 5 --retry-delay 5 --retry-all-errors "https://aka.ms/vs/17/release/vc_redist.arm64.exe" -o "%GITHUB_WORKSPACE%\vc_redist.arm64.exe" where iscc diff --git a/CHANGELOG.md b/CHANGELOG.md index aeda4c96..bdc88b95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,12 @@ Version counting is based on semantic versioning (Major.Feature.Patch) ### YACReader * Add support for continuous scroll mode. +* Fix the translator. ### All GUI Apps * Migrate Flow implementation from OpenGL to QRhi. This is a full new implementation with better performance and compatibility with operating systems and hardware. * Add light/dark themes support that follow the system configuration. +* Add a theme editor and support for custom themes. ## 9.16.4 diff --git a/CMakeLists.txt b/CMakeLists.txt index badd9f25..7e3012b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,6 +106,7 @@ else() Sql Svg Test + TextToSpeech Widgets ) endif() diff --git a/YACReader/CMakeLists.txt b/YACReader/CMakeLists.txt index 62cf5864..f5e9eeec 100644 --- a/YACReader/CMakeLists.txt +++ b/YACReader/CMakeLists.txt @@ -89,6 +89,7 @@ target_link_libraries(YACReader PRIVATE Qt::Network Qt::Widgets Qt::Multimedia + Qt::TextToSpeech Qt::Svg Qt::Core5Compat comic_backend diff --git a/YACReader/themes/builtin_classic.json b/YACReader/themes/builtin_classic.json index f3edf088..bf1b3415 100644 --- a/YACReader/themes/builtin_classic.json +++ b/YACReader/themes/builtin_classic.json @@ -24,6 +24,17 @@ "shortcutsIcons": { "iconColor": "#404040" }, + "translator": { + "backgroundColor": "#404040", + "borderColor": "#212121", + "iconColor": "#cccccc", + "inputBackgroundColor": "#2a2a2a", + "inputDarkerBackgroundColor": "#272727", + "primaryTextColor": "#ffffff", + "scrollbarHandleColor": "#dddddd", + "secondaryTextColor": "#e3e3e3", + "selectionBackgroundColor": "#202020" + }, "toolbar": { "backgroundColor": "#f3f3f3", "checkedButtonColor": "#cccccc", diff --git a/YACReader/themes/builtin_dark.json b/YACReader/themes/builtin_dark.json index 61038dfa..193fa0b6 100644 --- a/YACReader/themes/builtin_dark.json +++ b/YACReader/themes/builtin_dark.json @@ -24,6 +24,17 @@ "shortcutsIcons": { "iconColor": "#d0d0d0" }, + "translator": { + "backgroundColor": "#404040", + "borderColor": "#212121", + "iconColor": "#cccccc", + "inputBackgroundColor": "#2a2a2a", + "inputDarkerBackgroundColor": "#272727", + "primaryTextColor": "#ffffff", + "scrollbarHandleColor": "#dddddd", + "secondaryTextColor": "#e3e3e3", + "selectionBackgroundColor": "#202020" + }, "toolbar": { "backgroundColor": "#202020", "checkedButtonColor": "#3a3a3a", diff --git a/YACReader/themes/builtin_light.json b/YACReader/themes/builtin_light.json index dbd77f07..a7aadc67 100644 --- a/YACReader/themes/builtin_light.json +++ b/YACReader/themes/builtin_light.json @@ -24,6 +24,17 @@ "shortcutsIcons": { "iconColor": "#606060" }, + "translator": { + "backgroundColor": "#e8e8e8", + "borderColor": "#cccccc", + "iconColor": "#404040", + "inputBackgroundColor": "#f5f5f5", + "inputDarkerBackgroundColor": "#ebebeb", + "primaryTextColor": "#1a1a1a", + "scrollbarHandleColor": "#909090", + "secondaryTextColor": "#2a2a2a", + "selectionBackgroundColor": "#dcdcdc" + }, "toolbar": { "backgroundColor": "#f3f3f3", "checkedButtonColor": "#cccccc", diff --git a/YACReader/themes/theme.h b/YACReader/themes/theme.h index 964e619f..99715f64 100644 --- a/YACReader/themes/theme.h +++ b/YACReader/themes/theme.h @@ -21,6 +21,31 @@ struct ViewerThemeTemplates { QString infoLabelQSS = "QLabel { color : %1; font-size:%2px; }"; }; +struct TranslatorThemeTemplates { + // %1 = track color, %2 = handle color + QString scrollBarQSS = "QScrollBar:vertical { border: none; background: %1; width: 7px; margin: 0 3px 0 0; }" + "QScrollBar::handle:vertical { background: %2; width: 7px; min-height: 20px; }" + "QScrollBar::add-line:vertical { border: none; background: %1; height: 10px; subcontrol-position: bottom; subcontrol-origin: margin; margin: 0 3px 0 0;}" + "QScrollBar::sub-line:vertical { border: none; background: %1; height: 10px; subcontrol-position: top; subcontrol-origin: margin; margin: 0 3px 0 0;}" + "QScrollBar::up-arrow:vertical {border:none;width: 9px;height: 6px;background: url(':/images/folders_view/line-up.png') center top no-repeat;}" + "QScrollBar::down-arrow:vertical {border:none;width: 9px;height: 6px;background: url(':/images/folders_view/line-down.png') center top no-repeat;}" + "QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {background: none; }"; + // %1 = background, %2 = text color + QString textEditQSS = "QTextEdit{border:none;background:%1;color:%2; font-size:12px; padding:6px;}"; + // %1 = background, %2 = text color, %3 = arrow icon path, %4 = list background, %5 = selection background + QString comboBoxQSS = "QComboBox {border:none;background:%1;color:%2;font-size:12px;font-family:Arial;padding-left:8px;}" + "QComboBox::down-arrow {image: url(%3);}" + "QComboBox::drop-down {border:none; padding-right:10px;}" + "QComboBox QAbstractItemView {border: none; background:%4; color:%2; selection-background-color: %5; outline:none;}" + "QComboBox QAbstractItemView::item {padding-left:8px;}"; + // %1 = border color, %2 = background, %3 = text color + QString clearButtonQSS = "QPushButton {border:1px solid %1; background:%2; color:%3; font-family:Arial; font-size:12px; padding-top:5px; padding-bottom:5px;}"; + // %1 = text color + QString titleQSS = "QLabel {font-size:18px; font-family:Arial; color:%1;}"; + QString resultsTitleQSS = "QLabel {font-family:Arial;font-size:14px;color:%1;}"; + QString resultTextQSS = "QLabel {color:%1;font-size:12px;}"; +}; + struct GoToFlowWidgetThemeTemplates { QString sliderQSS = "QSlider::groove:horizontal {" " border: 1px solid %1;" @@ -41,6 +66,22 @@ struct GoToFlowWidgetThemeTemplates { QString labelQSS = "QLabel { color: %1; }"; }; +struct TranslatorTheme { + QColor backgroundColor; + QColor inputBackgroundColor; + QString scrollBarQSS; + QString textEditQSS; + QString comboBoxQSS; + QString clearButtonQSS; + QString titleQSS; + QString resultsTitleQSS; + QString resultTextQSS; + QIcon closeIcon; + QIcon speakerIcon; + QIcon searchIcon; + QPixmap fromToPixmap; +}; + struct ToolbarTheme { QString toolbarQSS; @@ -147,6 +188,7 @@ struct Theme { ThemeMeta meta; QJsonObject sourceJson; + TranslatorTheme translator; ToolbarTheme toolbar; ViewerTheme viewer; GoToFlowWidgetTheme goToFlowWidget; diff --git a/YACReader/themes/theme_factory.cpp b/YACReader/themes/theme_factory.cpp index a0cb207f..7b7d052a 100644 --- a/YACReader/themes/theme_factory.cpp +++ b/YACReader/themes/theme_factory.cpp @@ -56,6 +56,20 @@ struct ShortcutsIconsParams { QColor iconColor; // Main icon color (replaces #f0f) }; +struct TranslatorParams { + TranslatorThemeTemplates t; + + QColor backgroundColor { 0x40, 0x40, 0x40 }; + QColor inputBackgroundColor { 0x2a, 0x2a, 0x2a }; + QColor inputDarkerBackgroundColor { 0x27, 0x27, 0x27 }; + QColor selectionBackgroundColor { 0x20, 0x20, 0x20 }; + QColor primaryTextColor { Qt::white }; + QColor secondaryTextColor { 0xe3, 0xe3, 0xe3 }; + QColor scrollbarHandleColor { 0xdd, 0xdd, 0xdd }; + QColor borderColor { 0x21, 0x21, 0x21 }; + QColor iconColor { 0xcc, 0xcc, 0xcc }; +}; + struct ThemeParams { ThemeMeta meta; @@ -65,6 +79,7 @@ struct ThemeParams { HelpAboutDialogTheme helpAboutDialogParams; WhatsNewDialogParams whatsNewDialogParams; ShortcutsIconsParams shortcutsIconsParams; + TranslatorParams translatorParams; }; void setToolbarIconPair(QIcon &icon, @@ -211,6 +226,43 @@ Theme makeTheme(const ThemeParams ¶ms) theme.dialogIcons.findFolderIcon = QIcon(renderSvgToPixmap(path, 13, 13, dpr)); } + // Translator + { + const auto &tr = params.translatorParams; + theme.translator.backgroundColor = tr.backgroundColor; + theme.translator.inputBackgroundColor = tr.inputBackgroundColor; + theme.translator.scrollBarQSS = tr.t.scrollBarQSS.arg( + tr.backgroundColor.name(), + tr.scrollbarHandleColor.name()); + theme.translator.textEditQSS = tr.t.textEditQSS.arg( + tr.inputBackgroundColor.name(), + tr.primaryTextColor.name()); + const QString dropDownArrowPath = recoloredSvgToThemeFile( + ":/images/translator/dropDownArrow.svg", tr.iconColor, params.meta.id); + theme.translator.comboBoxQSS = tr.t.comboBoxQSS.arg( + tr.inputBackgroundColor.name(), + tr.primaryTextColor.name(), + dropDownArrowPath, + tr.inputDarkerBackgroundColor.name(), + tr.selectionBackgroundColor.name()); + theme.translator.clearButtonQSS = tr.t.clearButtonQSS.arg( + tr.borderColor.name(), + tr.inputBackgroundColor.name(), + tr.primaryTextColor.name()); + theme.translator.titleQSS = tr.t.titleQSS.arg(tr.primaryTextColor.name()); + theme.translator.resultsTitleQSS = tr.t.resultsTitleQSS.arg(tr.secondaryTextColor.name()); + theme.translator.resultTextQSS = tr.t.resultTextQSS.arg(tr.primaryTextColor.name()); + theme.translator.closeIcon = QIcon(recoloredSvgToThemeFile( + ":/images/translator/close.svg", tr.iconColor, params.meta.id)); + theme.translator.speakerIcon = QIcon(recoloredSvgToThemeFile( + ":/images/translator/speaker.svg", tr.iconColor, params.meta.id)); + theme.translator.searchIcon = QIcon(recoloredSvgToThemeFile( + ":/images/translator/translatorSearch.svg", tr.iconColor, params.meta.id)); + theme.translator.fromToPixmap = QPixmap(recoloredSvgToThemeFile( + ":/images/translator/fromTo.svg", tr.iconColor, params.meta.id)); + } + // end Translator + return theme; } @@ -288,6 +340,20 @@ Theme makeTheme(const QJsonObject &json) p.shortcutsIconsParams.iconColor = colorFromJson(s, "iconColor", p.shortcutsIconsParams.iconColor); } + if (json.contains("translator")) { + const auto t = json["translator"].toObject(); + auto &tp = p.translatorParams; + tp.backgroundColor = colorFromJson(t, "backgroundColor", tp.backgroundColor); + tp.inputBackgroundColor = colorFromJson(t, "inputBackgroundColor", tp.inputBackgroundColor); + tp.inputDarkerBackgroundColor = colorFromJson(t, "inputDarkerBackgroundColor", tp.inputDarkerBackgroundColor); + tp.selectionBackgroundColor = colorFromJson(t, "selectionBackgroundColor", tp.selectionBackgroundColor); + tp.primaryTextColor = colorFromJson(t, "primaryTextColor", tp.primaryTextColor); + tp.secondaryTextColor = colorFromJson(t, "secondaryTextColor", tp.secondaryTextColor); + tp.scrollbarHandleColor = colorFromJson(t, "scrollbarHandleColor", tp.scrollbarHandleColor); + tp.borderColor = colorFromJson(t, "borderColor", tp.borderColor); + tp.iconColor = colorFromJson(t, "iconColor", tp.iconColor); + } + if (json.contains("meta")) { const auto o = json["meta"].toObject(); p.meta.id = o["id"].toString(p.meta.id); diff --git a/YACReader/translator.cpp b/YACReader/translator.cpp index a2524706..d1a447e0 100644 --- a/YACReader/translator.cpp +++ b/YACReader/translator.cpp @@ -1,6 +1,6 @@ -#include - -#include +#include +#include +#include #include #include @@ -26,44 +26,28 @@ #include -#define APPID "417CEAD93449502CC3C9B69FED26C54118E62BCC" - YACReaderTranslator::YACReaderTranslator(Viewer *parent) : QWidget(parent), drag(false) { - QString scrollBarStyle = "QScrollBar:vertical { border: none; background: #404040; width: 7px; margin: 0 3px 0 0; }" - "QScrollBar::handle:vertical { background: #DDDDDD; width: 7px; min-height: 20px; }" - "QScrollBar::add-line:vertical { border: none; background: #404040; height: 10px; subcontrol-position: bottom; subcontrol-origin: margin; margin: 0 3px 0 0;}" - - "QScrollBar::sub-line:vertical { border: none; background: #404040; height: 10px; subcontrol-position: top; subcontrol-origin: margin; margin: 0 3px 0 0;}" - "QScrollBar::up-arrow:vertical {border:none;width: 9px;height: 6px;background: url(':/images/folders_view/line-up.png') center top no-repeat;}" - "QScrollBar::down-arrow:vertical {border:none;width: 9px;height: 6px;background: url(':/images/folders_view/line-down.png') center top no-repeat;}" - - "QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {background: none; }"; - this->setCursor(QCursor(Qt::ArrowCursor)); this->setAutoFillBackground(true); this->setBackgroundRole(QPalette::Window); - QPalette p(this->palette()); - p.setColor(QPalette::Window, QColor(0x404040)); - this->setPalette(p); auto layout = new QVBoxLayout(this); // TITLE BAR auto titleBar = new QHBoxLayout(); - auto close = new QPushButton(QIcon(":/images/close.svg"), ""); - close->setFlat(true); - auto title = new QLabel(tr("YACReader translator")); - title->setStyleSheet("QLabel {font-size:18px; font-family:Arial; color:white;}"); - titleBar->addWidget(title); + closeButton = new QPushButton(this); + closeButton->setFlat(true); + titleLabel = new QLabel(tr("YACReader translator")); + titleBar->addWidget(titleLabel); titleBar->addStretch(); - close->resize(14, 14); - close->setStyleSheet("QPushButton {margin:0;padding:0;border:none;}"); - titleBar->addWidget(close); + closeButton->resize(14, 14); + closeButton->setStyleSheet("QPushButton {margin:0;padding:0;border:none;}"); + titleBar->addWidget(closeButton); titleBar->setContentsMargins(0, 0, 0, 0); titleBar->setSpacing(0); - connect(close, &QAbstractButton::clicked, parent, &Viewer::animateHideTranslator); + connect(closeButton, &QAbstractButton::clicked, parent, &Viewer::animateHideTranslator); layout->addLayout(titleBar); @@ -73,32 +57,19 @@ YACReaderTranslator::YACReaderTranslator(Viewer *parent) text->setMaximumHeight(110); layout->addSpacing(12); layout->addWidget(text); - text->setStyleSheet("QTextEdit{border:none;background:#2a2a2a;color:white; font-size:12px; padding:6px;}" + scrollBarStyle); // COMBOBOXES auto combos = new QHBoxLayout(); from = new QComboBox(this); to = new QComboBox(this); - QString comboBoxStyle = "QComboBox {border:none;background:#2a2a2a;color:white;font-size:12px;font-family:Arial;padding-left:8px;}" - "QComboBox::down-arrow {image: url(:/images/dropDownArrow.png);}" - "QComboBox::drop-down {border:none; padding-right:10px;}" - "QComboBox QAbstractItemView {border: none; background:#272727; color:white; selection-background-color: #202020; outline:none;}" - "QComboBox QAbstractItemView::item {padding-left:8px;}" + - scrollBarStyle; - from->setStyleSheet(comboBoxStyle); - to->setStyleSheet(comboBoxStyle); from->setFixedHeight(22); to->setFixedHeight(22); - QLabel *arrow = new QLabel(this); - QPixmap arrowPixmap(":/images/fromTo.png"); - arrow->setPixmap(arrowPixmap); - auto searchButton = new QPushButton(this); - searchButton->setIcon(QIcon(":/images/translatorSearch.png")); - searchButton->setStyleSheet("QPushButton {border:none; background:#2a2a2a;}"); + arrowLabel = new QLabel(this); + searchButton = new QPushButton(this); searchButton->setFixedSize(22, 22); combos->addWidget(from, 1); combos->addSpacing(9); - combos->addWidget(arrow, 0); + combos->addWidget(arrowLabel, 0); combos->addSpacing(9); combos->addWidget(to, 1); combos->addSpacing(9); @@ -109,10 +80,8 @@ YACReaderTranslator::YACReaderTranslator(Viewer *parent) // RESULTS auto resultsTitleLayout = new QHBoxLayout(); resultsTitle = new QLabel(tr("Translation")); - resultsTitle->setStyleSheet("QLabel {font-family:Arial;font-size:14px;color:#e3e3e3;}"); speakButton = new QPushButton(this); speakButton->setStyleSheet("QPushButton {border:none;}"); - speakButton->setIcon(QIcon(":/images/speaker.png")); resultsTitleLayout->addWidget(resultsTitle, 0, Qt::AlignVCenter); resultsTitleLayout->addSpacing(10); resultsTitleLayout->addWidget(speakButton, 0, Qt::AlignVCenter); @@ -124,7 +93,6 @@ YACReaderTranslator::YACReaderTranslator(Viewer *parent) resultText = new QLabel(); resultText->setWordWrap(true); - resultText->setStyleSheet("QLabel {color:white;font-size:12px;}"); resultText->setText(""); layout->addWidget(resultText); @@ -134,7 +102,6 @@ YACReaderTranslator::YACReaderTranslator(Viewer *parent) clearButton = new QPushButton(tr("clear")); layout->addWidget(clearButton, 0, Qt::AlignRight); clearButton->setMinimumWidth(95); - clearButton->setStyleSheet("QPushButton {border:1px solid #212121; background:#2a2a2a; color:white; font-family:Arial; font-size:12px; padding-top:5px; padding-bottom:5px;}"); resize(400, 479); @@ -149,13 +116,41 @@ YACReaderTranslator::YACReaderTranslator(Viewer *parent) busyIndicator->move((this->width() - busyIndicator->width()) / 2, (this->height() - busyIndicator->height()) * 2 / 3); busyIndicator->hide(); - show(); - connect(searchButton, &QAbstractButton::pressed, this, &YACReaderTranslator::translate); connect(speakButton, &QAbstractButton::pressed, this, &YACReaderTranslator::play); connect(clearButton, &QAbstractButton::pressed, this, &YACReaderTranslator::clear); - player = new QMediaPlayer; + tts = new QTextToSpeech(this); + + initTheme(this); + + show(); +} + +void YACReaderTranslator::applyTheme(const Theme &theme) +{ + const auto &tr = theme.translator; + + QPalette p(this->palette()); + p.setColor(QPalette::Window, tr.backgroundColor); + this->setPalette(p); + + text->setStyleSheet(tr.textEditQSS + tr.scrollBarQSS); + from->setStyleSheet(tr.comboBoxQSS + tr.scrollBarQSS); + to->setStyleSheet(tr.comboBoxQSS + tr.scrollBarQSS); + + titleLabel->setStyleSheet(tr.titleQSS); + resultsTitle->setStyleSheet(tr.resultsTitleQSS); + resultText->setStyleSheet(tr.resultTextQSS); + clearButton->setStyleSheet(tr.clearButtonQSS); + + searchButton->setStyleSheet( + QString("QPushButton {border:none; background:%1;}").arg(tr.inputBackgroundColor.name())); + + closeButton->setIcon(tr.closeIcon); + speakButton->setIcon(tr.speakerIcon); + searchButton->setIcon(tr.searchIcon); + arrowLabel->setPixmap(tr.fromToPixmap); } void YACReaderTranslator::hideResults() @@ -179,20 +174,16 @@ void YACReaderTranslator::translate() QString from = this->from->itemData(this->from->currentIndex()).toString(); QString to = this->to->itemData(this->to->currentIndex()).toString(); + speakText = text; + speakLocale = from; + TranslationLoader *translationLoader = new TranslationLoader(text, from, to); connect(translationLoader, &TranslationLoader::requestFinished, this, &YACReaderTranslator::setTranslation); connect(translationLoader, &TranslationLoader::error, this, &YACReaderTranslator::error); connect(translationLoader, &TranslationLoader::timeOut, this, &YACReaderTranslator::error); connect(translationLoader, &QThread::finished, translationLoader, &QObject::deleteLater); - TextToSpeachLoader *tts = new TextToSpeachLoader(text, from); - connect(tts, &TextToSpeachLoader::requestFinished, this, &YACReaderTranslator::setSpeak); - connect(tts, &TextToSpeachLoader::error, this, &YACReaderTranslator::error); - connect(tts, &TextToSpeachLoader::timeOut, this, &YACReaderTranslator::error); - connect(tts, &QThread::finished, tts, &QObject::deleteLater); - translationLoader->start(); - tts->start(); resultsTitle->setText(tr("Translation")); @@ -208,19 +199,12 @@ void YACReaderTranslator::error() busyIndicator->hide(); } -void YACReaderTranslator::setSpeak(const QUrl &url) -{ - resultsTitle->setHidden(false); - speakButton->setHidden(false); - - ttsSource = url; -} - void YACReaderTranslator::setTranslation(const QString &string) { resultText->setText(string); resultsTitle->setHidden(false); + speakButton->setHidden(false); resultText->setHidden(false); busyIndicator->hide(); } @@ -236,8 +220,8 @@ void YACReaderTranslator::populateCombos() combo->addItem("Arabic", "ar"); combo->addItem("Bulgarian", "bg"); combo->addItem("Catalan", "ca"); - combo->addItem("Chinese Simplified", "zh-CHS"); - combo->addItem("Chinese Traditional", "zh-CHT"); + combo->addItem("Chinese Simplified", "zh-CN"); + combo->addItem("Chinese Traditional", "zh-TW"); combo->addItem("Czech", "cs"); combo->addItem("Danish", "da"); combo->addItem("Dutch", "nl"); @@ -277,10 +261,8 @@ void YACReaderTranslator::populateCombos() void YACReaderTranslator::play() { - - player->setSource(ttsSource); - - player->play(); + tts->setLocale(QLocale(speakLocale)); + tts->say(speakText); } void YACReaderTranslator::mousePressEvent(QMouseEvent *event) @@ -324,10 +306,10 @@ void TranslationLoader::run() connect(&tT, &QTimer::timeout, &q, &QEventLoop::quit); connect(&manager, &QNetworkAccessManager::finished, &q, &QEventLoop::quit); - QString url = "http://api.microsofttranslator.com/V2/Ajax.svc/Translate?appid=%1&from=%2&to=%3&text=%4&contentType=text/plain"; - url = url.arg(APPID, from, to, text); + QString urlStr = QString("https://api.mymemory.translated.net/get?q=%1&langpair=%2|%3") + .arg(QString::fromUtf8(QUrl::toPercentEncoding(text)), from, to); - QNetworkReply *reply = manager.get(QNetworkRequest(QUrl(url))); + QNetworkReply *reply = manager.get(QNetworkRequest(QUrl(urlStr))); tT.start(5000); // 5s timeout q.exec(); @@ -335,55 +317,12 @@ void TranslationLoader::run() if (tT.isActive()) { // download complete if (reply->error() == QNetworkReply::NoError) { - QString utf8 = QString::fromUtf8(reply->readAll()); - utf8 = utf8.remove(0, 1); - utf8 = utf8.remove(utf8.count() - 1, 1); - - QString translated(utf8); - emit requestFinished(translated); - } else - emit error(); - } else { - emit timeOut(); - } -} - -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- - -TextToSpeachLoader::TextToSpeachLoader(QString text, QString language) - : QThread(), text(text), language(language) -{ -} - -void TextToSpeachLoader::run() -{ - QNetworkAccessManager manager; - QEventLoop q; - QTimer tT; - - tT.setSingleShot(true); - connect(&tT, &QTimer::timeout, &q, &QEventLoop::quit); - connect(&manager, &QNetworkAccessManager::finished, &q, &QEventLoop::quit); - - QString url = "http://api.microsofttranslator.com/V2/Ajax.svc/Speak?appid=%1&language=%2&text=%3&contentType=text/plain"; - url = url.arg(APPID, language, text); - - QNetworkReply *reply = manager.get(QNetworkRequest(QUrl(url))); - - tT.start(5000); // 5s timeout - q.exec(); - - if (tT.isActive()) { - // download complete - if (reply->error() == QNetworkReply::NoError) { - QString utf8 = QString::fromUtf8(reply->readAll()); - utf8 = utf8.remove(0, 1); - utf8 = utf8.remove(utf8.count() - 1, 1); - utf8 = utf8.replace("\\", ""); - - emit requestFinished(QUrl(utf8)); + QJsonDocument doc = QJsonDocument::fromJson(reply->readAll()); + QString translated = doc["responseData"]["translatedText"].toString(); + if (!translated.isEmpty()) + emit requestFinished(translated); + else + emit error(); } else emit error(); } else { diff --git a/YACReader/translator.h b/YACReader/translator.h index be50f8b6..3cfb7fa5 100644 --- a/YACReader/translator.h +++ b/YACReader/translator.h @@ -1,7 +1,6 @@ #ifndef __TRANSLATOR_H #define __TRANSLATOR_H -class QUrl; class QMouseEvent; class QPoint; class QTextEdit; @@ -9,15 +8,15 @@ class QComboBox; class QLabel; class QPushButton; class YACReaderBusyWidget; +class QTextToSpeech; #include #include #include #include "viewer.h" +#include "themable.h" -class QMediaPlayer; - -class YACReaderTranslator : public QWidget +class YACReaderTranslator : public QWidget, protected Themable { Q_OBJECT public: @@ -28,12 +27,12 @@ public slots: protected slots: void translate(); - void setSpeak(const QUrl &url); void setTranslation(const QString &string); void error(); void clear(); protected: + void applyTheme(const Theme &theme) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; @@ -44,16 +43,21 @@ protected: QPoint click; private: - QMediaPlayer *player; + QTextToSpeech *tts; + QString speakText; + QString speakLocale; QTextEdit *text; QComboBox *from; QComboBox *to; + QLabel *titleLabel; QLabel *resultsTitle; + QLabel *arrowLabel; QPushButton *speakButton; + QPushButton *closeButton; + QPushButton *searchButton; QLabel *resultText; YACReaderBusyWidget *busyIndicator; - QUrl ttsSource; QPushButton *clearButton; }; @@ -74,19 +78,4 @@ private: void run() override; }; -class TextToSpeachLoader : public QThread -{ - Q_OBJECT -public: - TextToSpeachLoader(QString text, QString language); -signals: - void requestFinished(QUrl); - void timeOut(); - void error(); - -private: - QString text; - QString language; - void run() override; -}; #endif diff --git a/YACReader/yacreader_images.qrc b/YACReader/yacreader_images.qrc index df996b67..a5b011ed 100644 --- a/YACReader/yacreader_images.qrc +++ b/YACReader/yacreader_images.qrc @@ -10,15 +10,15 @@ ../images/flow5.png ../images/notCover.png ../images/close.svg - ../images/up.png - ../images/down.png ../images/centerFlow.svg ../images/gotoFlow.svg ../images/defaultCover.png - ../images/fromTo.png - ../images/dropDownArrow.png - ../images/translatorSearch.png - ../images/speaker.png + + ../images/translator/close.svg + ../images/translator/dropDownArrow.svg + ../images/translator/fromTo.svg + ../images/translator/speaker.svg + ../images/translator/translatorSearch.svg ../images/shortcuts/clear_shortcut.svg ../images/shortcuts/accept_shortcut.svg ../images/shortcuts/shortcuts_group_comics.svg diff --git a/ci/win/build_installer_qt6.iss b/ci/win/build_installer_qt6.iss index 87e03c4d..06930e90 100644 --- a/ci/win/build_installer_qt6.iss +++ b/ci/win/build_installer_qt6.iss @@ -60,6 +60,7 @@ Source: Qt6QuickTemplates2.dll; DestDir: {app} Source: Qt6QuickWidgets.dll; DestDir: {app} Source: Qt6Sql.dll; DestDir: {app} Source: Qt6Svg.dll; DestDir: {app} +Source: Qt6TextToSpeech.dll; DestDir: {app} Source: opengl32sw.dll; DestDir: {app}; Flags: skipifsourcedoesntexist Source: D3Dcompiler_47.dll; DestDir: {app}; Flags: skipifsourcedoesntexist @@ -74,6 +75,7 @@ Source:qml\*; DestDir: {app}\qml\; Flags: recursesubdirs Source:qmltooling\*; DestDir: {app}\qmltooling\ Source:sqldrivers\qsqlite.dll; DestDir: {app}\sqldrivers\ Source:styles\*; DestDir: {app}\styles\ +Source:texttospeech\*; DestDir: {app}\texttospeech\ Source:tls\*; DestDir: {app}\tls\ Source:translations\*; DestDir: {app}\translations\ diff --git a/custom_widgets/whats_new_dialog.cpp b/custom_widgets/whats_new_dialog.cpp index d3c69407..c1467e62 100644 --- a/custom_widgets/whats_new_dialog.cpp +++ b/custom_widgets/whats_new_dialog.cpp @@ -41,10 +41,12 @@ YACReader::WhatsNewDialog::WhatsNewDialog(QWidget *parent) "
" "YACReader
" " • Add support for continuous scroll mode
" + " • Fix the translator
" "
" "All GUI Apps
" " • Migrate Flow implementation from OpenGL to QRhi. This is a full new implementation with better performance and compatibility with operating systems and hardware
" " • Add light/dark themes support that follow the system configuration
" + " • Add a theme editor and support for custom themes
" "
" "I hope you enjoy the new update. Please, if you like YACReader consider to become a patron in Patreon " "or donate some money using Pay-Pal and help keeping the project alive. " diff --git a/images/down.png b/images/down.png deleted file mode 100644 index 44d91d42..00000000 Binary files a/images/down.png and /dev/null differ diff --git a/images/dropDownArrow.png b/images/dropDownArrow.png deleted file mode 100644 index db5ea8d8..00000000 Binary files a/images/dropDownArrow.png and /dev/null differ diff --git a/images/fromTo.png b/images/fromTo.png deleted file mode 100644 index 87ec680d..00000000 Binary files a/images/fromTo.png and /dev/null differ diff --git a/images/speaker.png b/images/speaker.png deleted file mode 100644 index e75be689..00000000 Binary files a/images/speaker.png and /dev/null differ diff --git a/images/translator/close.svg b/images/translator/close.svg new file mode 100644 index 00000000..26729e51 --- /dev/null +++ b/images/translator/close.svg @@ -0,0 +1,15 @@ + + + + + + + + \ No newline at end of file diff --git a/images/translator/dropDownArrow.svg b/images/translator/dropDownArrow.svg new file mode 100644 index 00000000..46e0d587 --- /dev/null +++ b/images/translator/dropDownArrow.svg @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/images/translator/fromTo.svg b/images/translator/fromTo.svg new file mode 100644 index 00000000..53e0843d --- /dev/null +++ b/images/translator/fromTo.svg @@ -0,0 +1,22 @@ + + + + + + + + \ No newline at end of file diff --git a/images/translator/speaker.svg b/images/translator/speaker.svg new file mode 100644 index 00000000..27fe7bf4 --- /dev/null +++ b/images/translator/speaker.svg @@ -0,0 +1,19 @@ + + + + + + + + + \ No newline at end of file diff --git a/images/translator/translatorSearch.svg b/images/translator/translatorSearch.svg new file mode 100644 index 00000000..c8fddaef --- /dev/null +++ b/images/translator/translatorSearch.svg @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/images/translatorSearch.png b/images/translatorSearch.png deleted file mode 100644 index 066f21e9..00000000 Binary files a/images/translatorSearch.png and /dev/null differ diff --git a/images/up.png b/images/up.png deleted file mode 100644 index cae28b7e..00000000 Binary files a/images/up.png and /dev/null differ