Merge pull request #519 from YACReader/fix-translator
YACReader 10: Fix translator
18
.github/workflows/build.yml
vendored
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -106,6 +106,7 @@ else()
|
||||
Sql
|
||||
Svg
|
||||
Test
|
||||
TextToSpeech
|
||||
Widgets
|
||||
)
|
||||
endif()
|
||||
|
||||
@ -89,6 +89,7 @@ target_link_libraries(YACReader PRIVATE
|
||||
Qt::Network
|
||||
Qt::Widgets
|
||||
Qt::Multimedia
|
||||
Qt::TextToSpeech
|
||||
Qt::Svg
|
||||
Qt::Core5Compat
|
||||
comic_backend
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include <QUrl>
|
||||
|
||||
#include <QMediaPlayer>
|
||||
#include <QTextToSpeech>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QPalette>
|
||||
@ -26,44 +26,28 @@
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
#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 {
|
||||
|
||||
@ -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 <QWidget>
|
||||
#include <QThread>
|
||||
#include <QUrl>
|
||||
#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
|
||||
|
||||
@ -10,15 +10,15 @@
|
||||
<file>../images/flow5.png</file>
|
||||
<file>../images/notCover.png</file>
|
||||
<file>../images/close.svg</file>
|
||||
<file>../images/up.png</file>
|
||||
<file>../images/down.png</file>
|
||||
<file>../images/centerFlow.svg</file>
|
||||
<file>../images/gotoFlow.svg</file>
|
||||
<file>../images/defaultCover.png</file>
|
||||
<file>../images/fromTo.png</file>
|
||||
<file>../images/dropDownArrow.png</file>
|
||||
<file>../images/translatorSearch.png</file>
|
||||
<file>../images/speaker.png</file>
|
||||
|
||||
<file>../images/translator/close.svg</file>
|
||||
<file>../images/translator/dropDownArrow.svg</file>
|
||||
<file>../images/translator/fromTo.svg</file>
|
||||
<file>../images/translator/speaker.svg</file>
|
||||
<file>../images/translator/translatorSearch.svg</file>
|
||||
<file>../images/shortcuts/clear_shortcut.svg</file>
|
||||
<file>../images/shortcuts/accept_shortcut.svg</file>
|
||||
<file>../images/shortcuts/shortcuts_group_comics.svg</file>
|
||||
|
||||
@ -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\
|
||||
|
||||
|
||||
@ -41,10 +41,12 @@ YACReader::WhatsNewDialog::WhatsNewDialog(QWidget *parent)
|
||||
"<br/>"
|
||||
"<span style=\"font-weight:600\">YACReader</span><br/>"
|
||||
" • Add support for continuous scroll mode<br/>"
|
||||
" • Fix the translator<br/>"
|
||||
"<br/>"
|
||||
"<span style=\"font-weight:600\">All GUI Apps</span><br/>"
|
||||
" • Migrate Flow implementation from OpenGL to QRhi. This is a full new implementation with better performance and compatibility with operating systems and hardware<br/>"
|
||||
" • Add light/dark themes support that follow the system configuration<br/>"
|
||||
" • Add a theme editor and support for custom themes<br/>"
|
||||
"<br/>"
|
||||
"I hope you enjoy the new update. Please, if you like YACReader consider to become a patron in <a href=\"https://www.patreon.com/yacreader\" style=\"color:%1;\">Patreon</a> "
|
||||
"or donate some money using <a href=\"https://www.paypal.com/donate?business=5TAMNQCDDMVP8&item_name=Support+YACReader\" style=\"color:%1;\">Pay-Pal</a> and help keeping the project alive. "
|
||||
|
||||
BIN
images/down.png
|
Before Width: | Height: | Size: 689 B |
|
Before Width: | Height: | Size: 135 B |
|
Before Width: | Height: | Size: 171 B |
|
Before Width: | Height: | Size: 200 B |
15
images/translator/close.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: none;
|
||||
stroke: #f0f;
|
||||
stroke-miterlimit: 10;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<line class="cls-1" x1="2" y1="2" x2="12" y2="12"/>
|
||||
<line class="cls-1" x1="12" y1="2" x2="2" y2="12"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 414 B |
11
images/translator/dropDownArrow.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 5">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #f0f;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<polygon class="cls-1" points="5 5 5 5 1 0 9 0 5 5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 281 B |
22
images/translator/fromTo.svg
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.cls-1, .cls-2 {
|
||||
fill: none;
|
||||
stroke: #f0f;
|
||||
stroke-miterlimit: 10;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
stroke-width: 1.8px;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<polyline class="cls-2" points="6 13 12 7 6 1"/>
|
||||
<line class="cls-1" x1="11.5" y1="7" x2="1.5" y2="7"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 499 B |
19
images/translator/speaker.svg
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: none;
|
||||
stroke: #f0f;
|
||||
stroke-miterlimit: 10;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #f0f;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<polygon class="cls-2" points="4 4 0 4 0 10 4 10 8 13 8 1 4 4"/>
|
||||
<path class="cls-1" d="M9.65,4.75c.23.54.35,1.13.35,1.75s-.13,1.21-.35,1.75"/>
|
||||
<path class="cls-1" d="M10.45,1.55c.63.63,1.15,1.39,1.5,2.23s.55,1.76.55,2.72-.2,1.89-.55,2.72-.87,1.59-1.5,2.23"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 589 B |
11
images/translator/translatorSearch.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #f0f;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path class="cls-1" d="M13.5,11.64l-2.03-2.03c.58-.92.92-2,.92-3.17,0-3.28-2.66-5.94-5.94-5.94S.5,3.16.5,6.44s2.67,5.94,5.94,5.94c1.16,0,2.25-.34,3.17-.92l2.03,2.03s1.86-1.86,1.86-1.86ZM6.44,10.65c-2.31,0-4.19-1.88-4.19-4.19s1.88-4.2,4.19-4.2,4.19,1.88,4.19,4.19-1.88,4.19-4.19,4.19h0Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 517 B |
|
Before Width: | Height: | Size: 241 B |
BIN
images/up.png
|
Before Width: | Height: | Size: 702 B |