From f3a4607d6a722a862af0eb9747a15dcdf624b6fb Mon Sep 17 00:00:00 2001 From: Aqua-sama Date: Sun, 3 Nov 2019 00:18:10 +0200 Subject: Drop boost dependency - wrote not-invented-here config file parser and conf class - spent obscene amount of time plugging in said conf class --- src/mainwindow/widgets/completer.cpp | 81 ++++++++++++++++ src/mainwindow/widgets/completer.h | 35 +++++++ src/mainwindow/widgets/navigationbar.cpp | 18 ++-- src/mainwindow/widgets/navigationbar.h | 2 +- src/mainwindow/widgets/urllineedit.cpp | 157 +++++++++++++++++++++++++++++++ src/mainwindow/widgets/urllineedit.h | 51 ++++++++++ 6 files changed, 335 insertions(+), 9 deletions(-) create mode 100644 src/mainwindow/widgets/completer.cpp create mode 100644 src/mainwindow/widgets/completer.h create mode 100644 src/mainwindow/widgets/urllineedit.cpp create mode 100644 src/mainwindow/widgets/urllineedit.h (limited to 'src/mainwindow/widgets') diff --git a/src/mainwindow/widgets/completer.cpp b/src/mainwindow/widgets/completer.cpp new file mode 100644 index 0000000..578f745 --- /dev/null +++ b/src/mainwindow/widgets/completer.cpp @@ -0,0 +1,81 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#include "completer.h" +#include + +Completer::Completer(QWidget *parent) + : QListView(parent) +{ + setObjectName("Completer"); + setWindowFlags(Qt::ToolTip); + setEditTriggers(QAbstractItemView::NoEditTriggers); + + connect(this, &Completer::activated, [=](const QModelIndex &index) { + hide(); + emit completionActivated(index.data().toString()); + }); +} + +bool Completer::updateItems(const QStringList &list) +{ + if(list.isEmpty()) + return false; + + auto *model = new QStringListModel(list, this); + setModel(model); + + delete completionModel; + completionModel = model; + + return true; +} + +bool Completer::keyPressed(QKeyEvent *event) +{ + if(isHidden()) + return false; + + Q_CHECK_PTR(completionModel); + + int count = completionModel->rowCount(); + const QModelIndex currentIndex = this->currentIndex(); + + switch(event->key()) { + case Qt::Key_Down: + if(currentIndex.row() + 1 >= count) { + setCurrentIndex(completionModel->index(0, 0)); + } else { + setCurrentIndex(completionModel->index(currentIndex.row() + 1, 0)); + } + break; + + case Qt::Key_Up: + if(currentIndex.row() == 0) { + setCurrentIndex(completionModel->index(count - 1, 0)); + } else { + setCurrentIndex(completionModel->index(currentIndex.row() - 1, 0)); + } + break; + + case Qt::Key_Escape: + hide(); + break; + + case Qt::Key_Enter: + case Qt::Key_Return: + hide(); + emit completionActivated(currentIndex.data().toString()); + break; + + default: + return false; + } + + return true; +} diff --git a/src/mainwindow/widgets/completer.h b/src/mainwindow/widgets/completer.h new file mode 100644 index 0000000..656a80f --- /dev/null +++ b/src/mainwindow/widgets/completer.h @@ -0,0 +1,35 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#ifndef SMOLBOTE_COMPLETER_H +#define SMOLBOTE_COMPLETER_H + +#include +#include +#include + +class Completer : public QListView +{ + + Q_OBJECT + +public: + explicit Completer(QWidget *parent = nullptr); + + bool updateItems(const QStringList &list); + + bool keyPressed(QKeyEvent *event); + +signals: + void completionActivated(const QString &url); + +private: + QStringListModel *completionModel = nullptr; +}; + +#endif //SMOLBOTE_COMPLETER_H diff --git a/src/mainwindow/widgets/navigationbar.cpp b/src/mainwindow/widgets/navigationbar.cpp index e77ce6d..f57d678 100644 --- a/src/mainwindow/widgets/navigationbar.cpp +++ b/src/mainwindow/widgets/navigationbar.cpp @@ -20,12 +20,14 @@ #include #include "webprofile.h" -NavigationBar::NavigationBar(const Configuration *config, QWidget *parent) +NavigationBar::NavigationBar(QWidget *parent) : QToolBar(parent) { + Configuration config; + // Back button backAction = addAction(Util::icon(QStyle::SP_ArrowBack), tr("Back")); - config->setShortcut(backAction, "navigation.shortcuts.back"); + setShortcut(backAction, "shortcuts.navigation.back"); connect(backAction, &QAction::triggered, this, [this]() { m_view->history()->back(); }); @@ -43,7 +45,7 @@ NavigationBar::NavigationBar(const Configuration *config, QWidget *parent) }); backAction->setMenu(backMenu); - auto *backMenuShortcut = new QShortcut(QKeySequence(config->value("navigation.shortcuts.backMenu").value()), this); + auto *backMenuShortcut = new QShortcut(QKeySequence(config.value("shortcuts.navigation.backmenu").value()), this); connect(backMenuShortcut, &QShortcut::activated, backMenu, [this, backMenu]() { if(backAction->isEnabled()) { auto *widget = this->widgetForAction(backAction); @@ -53,7 +55,7 @@ NavigationBar::NavigationBar(const Configuration *config, QWidget *parent) // Forward button forwardAction = addAction(Util::icon(QStyle::SP_ArrowForward), tr("Forward")); - config->setShortcut(forwardAction, "navigation.shortcuts.forward"); + setShortcut(forwardAction, "shortcuts.navigation.forward"); connect(forwardAction, &QAction::triggered, this, [this]() { m_view->history()->forward(); }); @@ -71,7 +73,7 @@ NavigationBar::NavigationBar(const Configuration *config, QWidget *parent) }); forwardAction->setMenu(forwardMenu); - auto *forwardMenuShortcut = new QShortcut(QKeySequence(config->value("navigation.shortcuts.forwardMenu").value()), this); + auto *forwardMenuShortcut = new QShortcut(QKeySequence(config.value("shortcuts.navigation.forwardmenu").value()), this); connect(forwardMenuShortcut, &QShortcut::activated, forwardMenu, [this, forwardMenu]() { if(forwardAction->isEnabled()) { auto *widget = this->widgetForAction(forwardAction); @@ -81,7 +83,7 @@ NavigationBar::NavigationBar(const Configuration *config, QWidget *parent) // Stop/Refresh button stopReloadAction = addAction(Util::icon(QStyle::SP_BrowserReload), tr("Refresh")); - config->setShortcut(stopReloadAction, "navigation.shortcuts.refresh"); + setShortcut(stopReloadAction, "shortcuts.navigation.refresh"); connect(stopReloadAction, &QAction::triggered, this, [this]() { if(m_view->isLoaded()) m_view->reload(); @@ -89,14 +91,14 @@ NavigationBar::NavigationBar(const Configuration *config, QWidget *parent) m_view->stop(); }); - auto *reloadShortcut = new QShortcut(QKeySequence(config->value("navigation.shortcuts.reload").value()), this); + auto *reloadShortcut = new QShortcut(QKeySequence(config.value("shortcuts.navigation.reload").value()), this); connect(reloadShortcut, &QShortcut::activated, this, [this]() { m_view->triggerPageAction(QWebEnginePage::ReloadAndBypassCache); }); // Home button homeAction = addAction(Util::icon(QStyle::SP_DirHomeIcon), tr("Home")); - config->setShortcut(homeAction, "navigation.shortcuts.home"); + setShortcut(homeAction, "shortcuts.navigation.home"); connect(homeAction, &QAction::triggered, this, [this]() { m_view->load(m_view->profile()->homepage()); }); diff --git a/src/mainwindow/widgets/navigationbar.h b/src/mainwindow/widgets/navigationbar.h index 0b5a319..b8c73e1 100644 --- a/src/mainwindow/widgets/navigationbar.h +++ b/src/mainwindow/widgets/navigationbar.h @@ -18,7 +18,7 @@ class NavigationBar : public QToolBar Q_OBJECT public: - explicit NavigationBar(const Configuration *config, QWidget *parent = nullptr); + explicit NavigationBar(QWidget *parent = nullptr); public slots: void connectWebView(WebView *view); diff --git a/src/mainwindow/widgets/urllineedit.cpp b/src/mainwindow/widgets/urllineedit.cpp new file mode 100644 index 0000000..378945f --- /dev/null +++ b/src/mainwindow/widgets/urllineedit.cpp @@ -0,0 +1,157 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#include "urllineedit.h" +#include +#include +#include +#include +#include "../addressbar.h" + +UrlLineEdit::UrlLineEdit(QWidget *parent) + : QLineEdit(parent) + , m_listView(new Completer(this)) +{ + setObjectName("UrlBar"); + setPlaceholderText(tr("Enter address")); + + m_listView->setVisible(false); + connect(m_listView, &Completer::completionActivated, this, &UrlLineEdit::setText); + + addressbar = qobject_cast(parent); + Q_CHECK_PTR(addressbar); + + auto *copyAction = new QAction(tr("Copy URL"), this); + connect(copyAction, &QAction::triggered, this, [this]() { + qApp->clipboard()->setText(this->text()); + }); + actions.append(copyAction); + + auto *pasteAction = new QAction(tr("Paste URL"), this); + connect(pasteAction, &QAction::triggered, this, [this]() { + this->setText(qApp->clipboard()->text()); + this->setFocus(); + }); + actions.append(pasteAction); + + auto *loadAction = new QAction(tr("Paste and load"), this); + connect(loadAction, &QAction::triggered, this, [=]() { + emit addressbar->load(QUrl::fromUserInput(qApp->clipboard()->text())); + }); + actions.append(loadAction); + + auto *searchAction = new QAction(tr("Paste and search"), this); + connect(searchAction, &QAction::triggered, this, [=]() { + emit addressbar->search(qApp->clipboard()->text()); + }); + actions.append(searchAction); + + menuAction = addAction(style()->standardIcon(QStyle::SP_DriveNetIcon), QLineEdit::LeadingPosition); + connect(menuAction, &QAction::triggered, this, [this]() { + auto *menu = new QMenu(); + menu->setAttribute(Qt::WA_DeleteOnClose, true); + menu->setMinimumWidth(240); + menu->addActions(actions); + + menu->exec(this->mapToGlobal(QPoint(0, height()))); + }); + + auto *goAction = addAction(style()->standardIcon(QStyle::SP_DialogOkButton), QLineEdit::TrailingPosition); + connect(goAction, &QAction::triggered, this, [this]() { + emit returnPressed(); + }); + + QTextCharFormat hostnameFormat; + hostnameFormat.setFontWeight(QFont::Bold); + m_hostFormat.format = hostnameFormat; +} + +void UrlLineEdit::setUrl(const QUrl &url) +{ + QString urlText = url.toString(); + QString domain = url.host(); + + m_hostFormat.start = urlText.indexOf(domain); + m_hostFormat.length = domain.length(); + + clear(); + clearTextFormat(); + setTextFormat(m_hostFormat); + setText(urlText); +} + +void UrlLineEdit::updateCompleter(const QStringList &l) +{ + if(!m_listView->updateItems(l)) { + m_listView->hide(); + return; + } + + // positioning + m_listView->setFixedWidth(width()); + m_listView->move(mapToGlobal(QPoint(0, height()))); + m_listView->show(); +} + +void UrlLineEdit::focusInEvent(QFocusEvent *event) +{ + // a context menu event also causes a focusInEvent, so if text is selected + // skip the formatting step + if(event->reason() == Qt::PopupFocusReason) { + QLineEdit::focusInEvent(event); + return; + } + + clearTextFormat(); + QLineEdit::focusInEvent(event); +} + +void UrlLineEdit::focusOutEvent(QFocusEvent *event) +{ + // a context menu event causes a focusOutEvent, and setUrl will clear the + // selection, and this would prevent the menu from working properly + if(event->reason() == Qt::PopupFocusReason) { + QLineEdit::focusOutEvent(event); + return; + } + + const QUrl url = QUrl::fromUserInput(text()); + if(url.isValid()) + setUrl(url); + + emit addressbar->giveFocus(); + QLineEdit::focusOutEvent(event); +} + +void UrlLineEdit::keyPressEvent(QKeyEvent *event) +{ + if(m_listView->keyPressed(event)) { + event->accept(); + return; + } else if(event->key() == Qt::Key::Key_Escape) { + clearFocus(); + event->accept(); + return; + } + + QLineEdit::keyPressEvent(event); +} + +// formatting taken from: https://forum.qt.io/topic/60962/setting-qlineedit-text-bold +void UrlLineEdit::setTextFormat(const QTextLayout::FormatRange &format) +{ + QList attributes; + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, format.start, format.length, format.format)); + QInputMethodEvent ev(QString(), attributes); + event(&ev); +} + +void UrlLineEdit::clearTextFormat() +{ + setTextFormat(QTextLayout::FormatRange()); +} diff --git a/src/mainwindow/widgets/urllineedit.h b/src/mainwindow/widgets/urllineedit.h new file mode 100644 index 0000000..88780a1 --- /dev/null +++ b/src/mainwindow/widgets/urllineedit.h @@ -0,0 +1,51 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#ifndef SMOLBOTE_URLLINEEDIT_H +#define SMOLBOTE_URLLINEEDIT_H + +#include "completer.h" +#include +#include +#include + +class AddressBar; +class WebView; +class UrlLineEdit : public QLineEdit +{ + Q_OBJECT +public: + explicit UrlLineEdit(QWidget *parent = nullptr); + +public slots: + void setUrl(const QUrl &url); + + void updateCompleter(const QStringList &l); + +public: + QAction *menuAction = nullptr; + +protected: + void focusInEvent(QFocusEvent *event) override; + void focusOutEvent(QFocusEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + +private: + void setTextFormat(const QTextLayout::FormatRange &format); + void clearTextFormat(); + + QList actions; + + QTextLayout::FormatRange m_hostFormat; + + // completer + Completer *m_listView; + AddressBar *addressbar; +}; + +#endif // SMOLBOTE_URLLINEEDIT_H -- cgit v1.2.1