$END_LICENSE */
#include "searchview.h"
+#include "channelsuggest.h"
#include "constants.h"
#include "fontutils.h"
#include "searchparams.h"
#include "ytsuggester.h"
-#include "channelsuggest.h"
#ifdef APP_MAC_SEARCHFIELD
#include "searchlineedit_mac.h"
#else
#include "searchlineedit.h"
#endif
+#ifdef APP_MAC
+#include "macutils.h"
+#endif
#ifdef APP_EXTRA
#include "extra.h"
#endif
#include "activation.h"
#include "activationview.h"
#endif
+#include "clickablelabel.h"
+#include "iconutils.h"
#include "mainwindow.h"
+#include "messagebar.h"
#include "painterutils.h"
-#include "iconutils.h"
-#include "clickablelabel.h"
+
+#ifdef UPDATER
+#include "updater.h"
+#endif
namespace {
-static const QString recentKeywordsKey = "recentKeywords";
-static const QString recentChannelsKey = "recentChannels";
-}
+const QString recentKeywordsKey = "recentKeywords";
+const QString recentChannelsKey = "recentChannels";
+} // namespace
SearchView::SearchView(QWidget *parent) : View(parent) {
- const int padding = 30;
+ setBackgroundRole(QPalette::Base);
+ setForegroundRole(QPalette::Text);
+ setAutoFillBackground(true);
- // speedup painting since we'll paint the whole background
- // by ourselves anyway in paintEvent()
- setAttribute(Qt::WA_OpaquePaintEvent);
+ const int padding = 30;
QBoxLayout *vLayout = new QVBoxLayout(this);
vLayout->setMargin(padding);
vLayout->setSpacing(0);
- // hidden message widget
- message = new QLabel(this);
- message->hide();
- vLayout->addWidget(message);
+ messageBar = new MessageBar();
+ messageBar->hide();
+ vLayout->addWidget(messageBar, 0, Qt::AlignCenter);
+ vLayout->addSpacing(padding);
+ maybeShowMessage();
vLayout->addStretch();
hLayout->addStretch();
- logo = new ClickableLabel(this);
- logo->setPixmap(IconUtils::pixmap(":/images/app.png"));
+ logo = new ClickableLabel();
+ auto setLogoPixmap = [this] {
+ logo->setPixmap(IconUtils::pixmap(":/images/app.png", logo->devicePixelRatioF()));
+ };
+ setLogoPixmap();
+ connect(window()->windowHandle(), &QWindow::screenChanged, this, setLogoPixmap);
connect(logo, &ClickableLabel::clicked, MainWindow::instance(), &MainWindow::visitSite);
hLayout->addWidget(logo, 0, Qt::AlignTop);
hLayout->addSpacing(padding);
layout->setAlignment(Qt::AlignCenter);
hLayout->addLayout(layout);
- QColor titleColor = palette().color(QPalette::WindowText);
- titleColor.setAlphaF(.75);
- int r,g,b,a;
- titleColor.getRgb(&r,&g,&b,&a);
- QString cssColor = QString::asprintf("rgba(%d,%d,%d,%d)", r, g, b, a);
-
- QLabel *welcomeLabel =
- new QLabel(QString("<h1 style='font-weight:300;color:%1'>").arg(cssColor) +
- tr("Welcome to <a href='%1'>%2</a>,")
- .replace("<a ", "<a style='text-decoration:none; color:palette(text)' ")
- .arg(Constants::WEBSITE, Constants::NAME)
- + "</h1>");
+ QLabel *welcomeLabel = new QLabel();
+ auto setupWelcomeLabel = [this, welcomeLabel] {
+ QColor titleColor = palette().color(QPalette::WindowText);
+ titleColor.setAlphaF(.75);
+ int r, g, b, a;
+ titleColor.getRgb(&r, &g, &b, &a);
+ QString cssColor = QString::asprintf("rgba(%d,%d,%d,%d)", r, g, b, a);
+ QString text =
+ QString("<h1 style='font-weight:300;color:%1'>").arg(cssColor) +
+ tr("Welcome to <a href='%1'>%2</a>,")
+ .replace("<a ", "<a style='text-decoration:none; color:palette(text)' ")
+ .arg(Constants::WEBSITE, Constants::NAME) +
+ "</h1>";
+ welcomeLabel->setText(text);
+ };
+ setupWelcomeLabel();
+ connect(qApp, &QGuiApplication::paletteChanged, this, setupWelcomeLabel);
welcomeLabel->setOpenExternalLinks(true);
- welcomeLabel->setProperty("heading", true);
- welcomeLabel->setFont(FontUtils::light(welcomeLabel->font().pointSize() * 1.25));
+ welcomeLabel->setFont(FontUtils::light(welcomeLabel->font().pointSize()));
layout->addWidget(welcomeLabel);
layout->addSpacing(padding / 2);
-#ifndef APP_MAC
- const QFont &biggerFont = FontUtils::big();
-#endif
-
//: "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos"
// QLabel *tipLabel = new QLabel(tr("Enter"), this);
-
QString tip;
if (qApp->layoutDirection() == Qt::RightToLeft) {
tip = tr("to start watching videos.") + " " + tr("a keyword") + " " + tr("Enter");
} else {
tip = tr("Enter") + " " + tr("a keyword") + " " + tr("to start watching videos.");
}
- QLabel *tipLabel = new QLabel(tip);
-
-#ifndef APP_MAC
- tipLabel->setFont(biggerFont);
-#endif
- layout->addWidget(tipLabel);
-
- /*
- typeCombo = new QComboBox(this);
- typeCombo->addItem(tr("a keyword"));
- typeCombo->addItem(tr("a channel"));
-#ifndef APP_MAC
- typeCombo->setFont(biggerFont);
-#endif
- connect(typeCombo, SIGNAL(currentIndexChanged(int)), SLOT(searchTypeChanged(int)));
- tipLayout->addWidget(typeCombo);
-
- tipLabel = new QLabel(tr("to start watching videos."), this);
-#ifndef APP_MAC
- tipLabel->setFont(biggerFont);
-#endif
- tipLayout->addWidget(tipLabel);
- */
layout->addSpacing(padding / 2);
- QHBoxLayout *searchLayout = new QHBoxLayout();
+ QBoxLayout *searchLayout = new QHBoxLayout();
searchLayout->setAlignment(Qt::AlignVCenter);
#ifdef APP_MAC_SEARCHFIELD
setFocusProxy(slem);
#else
SearchLineEdit *sle = new SearchLineEdit(this);
- sle->setFont(biggerFont);
+ sle->setFont(FontUtils::medium());
+ int tipWidth = sle->fontMetrics().size(Qt::TextSingleLine, tip).width();
+ sle->setMinimumWidth(tipWidth + sle->fontMetrics().width('m') * 6);
+ sle->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
queryEdit = sle;
#endif
- connect(queryEdit->toWidget(), SIGNAL(search(const QString&)), SLOT(watch(const QString&)));
- connect(queryEdit->toWidget(), SIGNAL(textChanged(const QString &)), SLOT(textChanged(const QString &)));
- connect(queryEdit->toWidget(), SIGNAL(textEdited(const QString &)), SLOT(textChanged(const QString &)));
- connect(queryEdit->toWidget(), SIGNAL(suggestionAccepted(Suggestion*)), SLOT(suggestionAccepted(Suggestion*)));
+ connect(queryEdit->toWidget(), SIGNAL(search(const QString &)), SLOT(watch(const QString &)));
+ connect(queryEdit->toWidget(), SIGNAL(textChanged(const QString &)),
+ SLOT(textChanged(const QString &)));
+ connect(queryEdit->toWidget(), SIGNAL(textEdited(const QString &)),
+ SLOT(textChanged(const QString &)));
+ connect(queryEdit->toWidget(), SIGNAL(suggestionAccepted(Suggestion *)),
+ SLOT(suggestionAccepted(Suggestion *)));
+ queryEdit->setPlaceholderText(tip);
youtubeSuggest = new YTSuggester(this);
channelSuggest = new ChannelSuggest(this);
- connect(channelSuggest, SIGNAL(ready(QVector<Suggestion*>)), SLOT(onChannelSuggestions(QVector<Suggestion*>)));
+ connect(channelSuggest, SIGNAL(ready(QVector<Suggestion *>)),
+ SLOT(onChannelSuggestions(QVector<Suggestion *>)));
searchTypeChanged(0);
searchLayout->addWidget(queryEdit->toWidget(), 0, Qt::AlignBaseline);
- searchLayout->addSpacing(padding);
-
- watchButton = new QPushButton(tr("Watch"));
-#ifndef APP_MAC
- watchButton->setFont(biggerFont);
-#endif
- watchButton->setDefault(true);
- watchButton->setEnabled(false);
- watchButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
- connect(watchButton, SIGNAL(clicked()), this, SLOT(watch()));
- searchLayout->addWidget(watchButton, 0, Qt::AlignBaseline);
- layout->addItem(searchLayout);
+ layout->addLayout(searchLayout);
layout->addSpacing(padding);
recentKeywordsLayout->setSpacing(0);
recentKeywordsLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
recentKeywordsLabel = new QLabel(tr("Recent keywords"));
- recentKeywordsLabel->setEnabled(false);
recentKeywordsLabel->setProperty("recentHeader", true);
recentKeywordsLabel->hide();
+ recentKeywordsLabel->setEnabled(false);
recentKeywordsLayout->addWidget(recentKeywordsLabel);
recentLayout->addLayout(recentKeywordsLayout);
recentChannelsLayout->setSpacing(0);
recentChannelsLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
recentChannelsLabel = new QLabel(tr("Recent channels"));
- recentChannelsLabel->setEnabled(false);
recentChannelsLabel->setProperty("recentHeader", true);
recentChannelsLabel->hide();
+ recentChannelsLabel->setEnabled(false);
recentChannelsLayout->addWidget(recentChannelsLabel);
recentLayout->addLayout(recentChannelsLayout);
#ifdef APP_ACTIVATION
if (!Activation::instance().isActivated())
- vLayout->addWidget(ActivationView::buyButton(tr("Get the full version")), 0, Qt::AlignRight);
+ vLayout->addWidget(ActivationView::buyButton(tr("Get the full version")), 0,
+ Qt::AlignRight);
#endif
}
void SearchView::appear() {
MainWindow *w = MainWindow::instance();
- w->showActionInStatusBar(w->getAction("manualplay"), true);
- w->showActionInStatusBar(w->getAction("safeSearch"), true);
- w->showActionInStatusBar(w->getAction("definition"), true);
+ w->showActionsInStatusBar(
+ {w->getAction("manualplay"), w->getAction("safeSearch"), w->getAction("definition")},
+ true);
updateRecentKeywords();
updateRecentChannels();
queryEdit->selectAll();
queryEdit->enableSuggest();
- if (!queryEdit->toWidget()->hasFocus()) queryEdit->toWidget()->setFocus();
-
- connect(window()->windowHandle(), SIGNAL(screenChanged(QScreen*)), SLOT(screenChanged()), Qt::UniqueConnection);
-
- qApp->processEvents();
- update();
-
-#ifdef APP_MAC
- // Workaround cursor bug on macOS
- window()->unsetCursor();
-#endif
+ QTimer::singleShot(0, queryEdit->toWidget(), SLOT(setFocus()));
}
void SearchView::disappear() {
MainWindow *w = MainWindow::instance();
- w->showActionInStatusBar(w->getAction("safeSearch"), false);
- w->showActionInStatusBar(w->getAction("definition"), false);
- w->showActionInStatusBar(w->getAction("manualplay"), false);
+ w->showActionsInStatusBar(
+ {w->getAction("manualplay"), w->getAction("safeSearch"), w->getAction("definition")},
+ false);
}
void SearchView::updateRecentKeywords() {
// cleanup
QLayoutItem *item;
- while ((item = recentKeywordsLayout->takeAt(1)) != 0) {
+ while (recentKeywordsLayout->count() - 1 > recentKeywords.size() &&
+ (item = recentKeywordsLayout->takeAt(1)) != nullptr) {
item->widget()->close();
delete item;
}
const int maxDisplayLength = 25;
+#ifdef APP_MAC
+ QPalette p = palette();
+ p.setColor(QPalette::Highlight, mac::accentColor());
+#endif
+
+ int counter = 1;
for (const QString &keyword : keywords) {
QString link = keyword;
QString display = keyword;
- if (keyword.startsWith(QLatin1String("http://")) || keyword.startsWith(QLatin1String("https://"))) {
+ if (keyword.startsWith(QLatin1String("http://")) ||
+ keyword.startsWith(QLatin1String("https://"))) {
int separator = keyword.indexOf('|');
if (separator > 0 && separator + 1 < keyword.length()) {
link = keyword.left(separator);
- display = keyword.mid(separator+1);
+ display = keyword.mid(separator + 1);
}
}
- bool needStatusTip = false;
- if (display.length() > maxDisplayLength) {
+ bool needStatusTip = display.length() > maxDisplayLength;
+ if (needStatusTip) {
display.truncate(maxDisplayLength);
display.append(QStringLiteral("\u2026"));
- needStatusTip = true;
}
- QPushButton *itemButton = new QPushButton(display);
- itemButton->setAttribute(Qt::WA_DeleteOnClose);
- itemButton->setProperty("recentItem", true);
- itemButton->setCursor(Qt::PointingHandCursor);
- itemButton->setFocusPolicy(Qt::TabFocus);
- itemButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
+
+ ClickableLabel *item;
+ if (recentKeywordsLayout->count() - 1 >= counter) {
+ item = qobject_cast<ClickableLabel *>(recentKeywordsLayout->itemAt(counter)->widget());
+ } else {
+ item = new ClickableLabel();
+#ifdef APP_MAC
+ item->setPalette(p);
+#endif
+ item->setAttribute(Qt::WA_DeleteOnClose);
+ item->setProperty("recentItem", true);
+ item->setFocusPolicy(Qt::TabFocus);
+ connect(item, &ClickableLabel::hovered, this, [item, this](bool value) {
+ item->setForegroundRole(value ? QPalette::Highlight : QPalette::WindowText);
+ if (value) {
+ for (int i = 1; i < recentKeywordsLayout->count(); ++i) {
+ QWidget *w = recentKeywordsLayout->itemAt(i)->widget();
+ if (w != item) {
+ w->setForegroundRole(QPalette::WindowText);
+ }
+ }
+ }
+ });
+ item->setContextMenuPolicy(Qt::ActionsContextMenu);
+ auto removeAction = new QAction(tr("Remove"));
+ item->addAction(removeAction);
+ connect(removeAction, &QAction::triggered, item, [item] {
+ QSettings settings;
+ QStringList keywords = settings.value(recentKeywordsKey).toStringList();
+ QString keyword = item->property("keyword").toString();
+ keywords.removeOne(keyword);
+ settings.setValue(recentKeywordsKey, keywords);
+ item->deleteLater();
+ });
+ recentKeywordsLayout->addWidget(item);
+ }
+
+ item->setText(display);
if (needStatusTip)
- itemButton->setStatusTip(link);
- connect(itemButton, &QPushButton::clicked, [this,link]() {
- watchKeywords(link);
- });
+ item->setStatusTip(link);
+ else
+ item->setStatusTip(QString());
+ item->setProperty("keyword", keyword);
- recentKeywordsLayout->addWidget(itemButton);
- }
+ disconnect(item, &ClickableLabel::clicked, nullptr, nullptr);
+ connect(item, &ClickableLabel::clicked, this, [this, link]() { watchKeywords(link); });
+ counter++;
+ }
}
void SearchView::updateRecentChannels() {
// cleanup
QLayoutItem *item;
- while ((item = recentChannelsLayout->takeAt(1)) != 0) {
+ while ((item = recentChannelsLayout->takeAt(1)) != nullptr) {
item->widget()->close();
delete item;
}
recentChannelsLabel->setVisible(!keywords.isEmpty());
- // TODO MainWindow::instance()->getAction("clearRecentKeywords")->setEnabled(!keywords.isEmpty());
+
+#ifdef APP_MAC
+ QPalette p = palette();
+ p.setColor(QPalette::Highlight, mac::accentColor());
+#endif
for (const QString &keyword : keywords) {
QString link = keyword;
int separator = keyword.indexOf('|');
if (separator > 0 && separator + 1 < keyword.length()) {
link = keyword.left(separator);
- display = keyword.mid(separator+1);
+ display = keyword.mid(separator + 1);
}
- QPushButton *itemButton = new QPushButton(display);
- itemButton->setAttribute(Qt::WA_DeleteOnClose);
- itemButton->setProperty("recentItem", true);
- itemButton->setCursor(Qt::PointingHandCursor);
- itemButton->setFocusPolicy(Qt::TabFocus);
- itemButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
- connect(itemButton, &QPushButton::clicked, [this,link]() {
- watchChannel(link);
+
+ ClickableLabel *item = new ClickableLabel(display);
+ item->setProperty("keyword", keyword);
+#ifdef APP_MAC
+ item->setPalette(p);
+#endif
+ item->setAttribute(Qt::WA_DeleteOnClose);
+ item->setProperty("recentItem", true);
+ item->setFocusPolicy(Qt::TabFocus);
+ connect(item, &ClickableLabel::clicked, [this, link]() { watchChannel(link); });
+ connect(item, &ClickableLabel::hovered, item, [item](bool value) {
+ item->setForegroundRole(value ? QPalette::Highlight : QPalette::WindowText);
+ });
+ item->setContextMenuPolicy(Qt::ActionsContextMenu);
+ auto removeAction = new QAction(tr("Remove"));
+ item->addAction(removeAction);
+ connect(removeAction, &QAction::triggered, item, [item] {
+ QSettings settings;
+ QStringList keywords = settings.value(recentChannelsKey).toStringList();
+ QString keyword = item->property("keyword").toString();
+ keywords.removeOne(keyword);
+ settings.setValue(recentChannelsKey, keywords);
+ item->deleteLater();
});
- recentChannelsLayout->addWidget(itemButton);
+ recentChannelsLayout->addWidget(item);
}
}
}
void SearchView::textChanged(const QString &text) {
- watchButton->setEnabled(!text.simplified().isEmpty());
lastChannelSuggestions.clear();
}
return;
}
- /*
- if (typeCombo->currentIndex() == 1) {
- // Channel search
- MainWindow::instance()->channelSearch(q);
- return;
- }
- */
-
SearchParams *searchParams = new SearchParams();
searchParams->setKeywords(q);
}
QString id = channelId;
-
- // Fix old settings
- const QLatin1String uc("UC");
- if (!id.startsWith(uc)) id = uc + id;
-
+ qDebug() << "Channel" << id;
SearchParams *searchParams = new SearchParams();
searchParams->setChannelId(id);
searchParams->setSortBy(SearchParams::SortByNewest);
return;
}
- // if (typeCombo->currentIndex() == 0) {
- queryEdit->setText(q);
- watchButton->setEnabled(true);
- // }
+ queryEdit->setText(q);
SearchParams *searchParams = new SearchParams();
searchParams->setKeywords(q);
emit search(searchParams);
}
-void SearchView::paintEvent(QPaintEvent *event) {
- QWidget::paintEvent(event);
- QBrush brush;
- if (window()->isActiveWindow()) {
- brush = palette().base();
- } else {
- brush = palette().window();
- }
- QPainter painter(this);
- painter.fillRect(0, 0, width(), height(), brush);
-}
-
void SearchView::searchTypeChanged(int index) {
if (index == 0) {
queryEdit->setSuggester(youtubeSuggest);
void SearchView::suggestionAccepted(Suggestion *suggestion) {
if (suggestion->type == QLatin1String("channel")) {
watchChannel(suggestion->userData);
- } else watch(suggestion->value);
-}
-
-void SearchView::screenChanged() {
- logo->setPixmap(IconUtils::pixmap(":/images/app.png"));
+ } else
+ watch(suggestion->value);
}
void SearchView::onChannelSuggestions(const QVector<Suggestion *> &suggestions) {
lastChannelSuggestions = suggestions;
}
+
+void SearchView::maybeShowMessage() {
+ QSettings settings;
+ QString key;
+
+ bool showMessages = true;
+#ifdef APP_ACTIVATION
+ showMessages = Activation::instance().isActivated();
+#endif
+
+#if defined APP_MAC && !defined APP_MAC_STORE
+ if (showMessages && !settings.contains(key = "sofa")) {
+ QString msg = tr("Need a remote control for %1? Try %2!").arg(Constants::NAME).arg("Sofa");
+ msg = "<a href='https://" + QLatin1String(Constants::ORG_DOMAIN) + '/' + key +
+ "' style='text-decoration:none;color:palette(windowText)'>" + msg + "</a>";
+ messageBar->setMessage(msg);
+ messageBar->setOpenExternalLinks(true);
+ disconnect(messageBar);
+ connect(messageBar, &MessageBar::closed, this, [key] {
+ QSettings settings;
+ settings.setValue(key, true);
+ });
+ messageBar->show();
+ showMessages = false;
+ }
+#endif
+
+ if (showMessages) {
+ key = "donate" + QLatin1String(Constants::VERSION);
+ if (!settings.contains(key)) {
+ bool oneYearUsage = true;
+#ifdef APP_ACTIVATION
+ oneYearUsage = (QDateTime::currentSecsSinceEpoch() -
+ Activation::instance().getLicenseTimestamp()) > 86400 * 365;
+#elif defined APP_MAC_STORE
+ oneYearUsage = false;
+#endif
+ if (oneYearUsage) {
+ QString msg =
+ tr("I keep improving %1 to make it the best I can. Support this work!")
+ .arg(Constants::NAME);
+ msg = "<a href='https://" + QLatin1String(Constants::ORG_DOMAIN) + "/donate" +
+ "' style='text-decoration:none;color:palette(windowText)'>" + msg + "</a>";
+ messageBar->setMessage(msg);
+ messageBar->setOpenExternalLinks(true);
+ disconnect(messageBar);
+ connect(messageBar, &MessageBar::closed, this, [key] {
+ QSettings settings;
+ settings.setValue(key, true);
+ });
+ messageBar->show();
+ }
+ }
+ }
+
+#ifdef UPDATER
+ connect(&Updater::instance(), &Updater::statusChanged, this, [this](auto status) {
+ if (status == Updater::Status::UpdateDownloaded) {
+ QString msg = tr("An update is ready to be installed. Quit and install update.");
+ msg = "<a href='http://quit' style='text-decoration:none;color:palette(windowText)'>" +
+ msg + "</a>";
+ messageBar->setMessage(msg);
+ messageBar->setOpenExternalLinks(false);
+ disconnect(messageBar);
+ connect(messageBar, &MessageBar::linkActivated, this, [] {
+ Updater::instance().setRelaunchAfterInstall(true);
+ qApp->quit();
+ });
+ messageBar->show();
+ }
+ });
+#endif
+}