diff options
author | Aqua-sama <aqua@iserlohn-fortress.net> | 2018-04-16 17:07:36 +0200 |
---|---|---|
committer | Aqua-sama <aqua@iserlohn-fortress.net> | 2018-04-16 17:07:36 +0200 |
commit | d796821f8304306dbe088701724243b39e8eb358 (patch) | |
tree | 836c85aa421db5c495e2b36f6a02cea924b6d919 /src/mainwindow | |
parent | Branch of Qt5.11 features (diff) | |
download | smolbote-d796821f8304306dbe088701724243b39e8eb358.tar.xz |
Multiple subwindows interface
Subwindows are similar to tab groups.
- Rewrote Browser and MainWindow, so they should be somewhat cleaner now
- Moved AboutDialog to lib/about
What's broken:
- loading bar
- search box
- address bar bookmark suggestions
- plugins
Diffstat (limited to 'src/mainwindow')
-rw-r--r-- | src/mainwindow/mainwindow.cpp | 362 | ||||
-rw-r--r-- | src/mainwindow/mainwindow.h | 84 | ||||
-rw-r--r-- | src/mainwindow/mainwindow.ui | 63 | ||||
-rw-r--r-- | src/mainwindow/widgets/loadingbar.cpp | 2 | ||||
-rw-r--r-- | src/mainwindow/widgets/navigationbar.cpp | 115 | ||||
-rw-r--r-- | src/mainwindow/widgets/navigationbar.h | 41 | ||||
-rw-r--r-- | src/mainwindow/widgets/searchform.cpp | 4 | ||||
-rw-r--r-- | src/mainwindow/widgets/tabbar.cpp | 171 | ||||
-rw-r--r-- | src/mainwindow/widgets/tabbar.h | 56 | ||||
-rw-r--r-- | src/mainwindow/widgets/tabwidget.cpp | 121 | ||||
-rw-r--r-- | src/mainwindow/widgets/tabwidget.h | 40 | ||||
-rw-r--r-- | src/mainwindow/window.cpp | 68 | ||||
-rw-r--r-- | src/mainwindow/window.h | 42 |
13 files changed, 481 insertions, 688 deletions
diff --git a/src/mainwindow/mainwindow.cpp b/src/mainwindow/mainwindow.cpp index 99a80b8..c039c8e 100644 --- a/src/mainwindow/mainwindow.cpp +++ b/src/mainwindow/mainwindow.cpp @@ -7,301 +7,169 @@ */ #include "mainwindow.h" -#include "addressbar/urllineedit.h" -#include "configuration/configuration.h" -#include "forms/aboutdialog.h" -#include "mainwindow/widgets/searchform.h" -#include "ui_mainwindow.h" -#include "widgets/mainwindowmenubar.h" -#include "mainwindow/widgets/tabbar.h" +#include "webengine/webview.h" +#include "widgets/navigationbar.h" +#include "window.h" +#include <QApplication> +#include <QCloseEvent> #include <QDockWidget> +#include <QLineEdit> +#include <QMdiArea> +#include <QMdiSubWindow> +#include <QMenuBar> #include <QMessageBox> -#include <bookmarks/bookmarksview.h> -#include <bookmarks/bookmarkswidget.h> -#include <downloads/downloadswidget.h> +#include <QShortcut> +#include <QToolBar> +#include <QUrl> +#include <about/aboutdialog.h> +#include <configuration/configuration.h> -MainWindow::MainWindow(std::shared_ptr<Configuration> config, QWidget *parent) +MainWindow::MainWindow(std::shared_ptr<Configuration> &config, QWidget *parent) : QMainWindow(parent) - , ui(new Ui::MainWindow) - , tabBar(new TabBar(config->section("tabbar"), this)) - , m_addressBar(new UrlLineEdit(config->section("addressbar"), this)) - , m_progressBar(new LoadingBar(this)) - , menuBar(new MainWindowMenuBar(config, this)) + , mdiArea(new QMdiArea(this)) { Q_ASSERT(config); m_config = config; - // delete this window when it closes - setAttribute(Qt::WA_DeleteOnClose, true); - - // set up UI - ui->setupUi(this); - - QAction *fullscreenAction = new QAction(this); - fullscreenAction->setShortcut(QKeySequence(QString::fromStdString(m_config->value<std::string>("browser.shortcuts.fullscreen").value()))); - connect(fullscreenAction, &QAction::triggered, this, &MainWindow::toggleFullscreen); - addAction(fullscreenAction); - - // Dockable widget styling - setDockOptions(dockOptions() | AllowTabbedDocks | ForceTabbedDocks); - setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::North); - setTabPosition(Qt::RightDockWidgetArea, QTabWidget::North); - - // Add the toolbars - // tabToolBar: main menu and tab list - ui->mainToolBar->setMovable(m_config->value<bool>("browser.ui.tabtoolbarMovable").value()); - ui->mainToolBar->addWidget(menuBar); - //tabToolBar->addWidget(tabBar); - - // navigationToolBar: address bar - ui->navigationToolBar->setMovable(m_config->value<bool>("browser.ui.navtoolbarMovable").value()); - insertToolBarBreak(ui->navigationToolBar); - - // page actions - m_navigationBar = new NavigationBar(this); - m_navigationBar->addWidgetsTo(ui->navigationToolBar); - - ui->navigationToolBar->addWidget(m_addressBar); + // create UI + resize(config->value<int>("mainwindow.width").value(), config->value<int>("mainwindow.height").value()); + titleSuffix = QString::fromStdString(config->value<std::string>("mainwindow.title").value()); + setWindowTitle(tr("smolbote")); + if(config->value<bool>("mainwindow.maximized").value()) + showMaximized(); + else + show(); + + createMenuBar(); + auto *navigationToolBar = new NavigationBar(config->section("navigation"), this); + navigationToolBar->setMovable(config->value<bool>("navigation.movable").value()); + addToolBar(Qt::TopToolBarArea, navigationToolBar); + navigationToolBar->connectWebView(nullptr); + + setCentralWidget(mdiArea); + mdiArea->setFocus(); + + connect(mdiArea, &QMdiArea::subWindowActivated, this, [this, navigationToolBar](QMdiSubWindow *window) { + disconnect(titleChangedConnection); + disconnect(navigationBarConnection); + + auto *w = qobject_cast<Window *>(window); + if(w != nullptr) { + setWindowTitle(w->windowTitle() + titleSuffix); + titleChangedConnection = connect(w, &Window::windowTitleChanged, this, [this](const QString &title) { + this->setWindowTitle(title + titleSuffix); + }); - // connect signals - connect(m_addressBar, &UrlLineEdit::addressEntered, this, [&](const QUrl &url) { - tabBar->currentView()->load(url); - }); - connect(m_addressBar, &UrlLineEdit::searchTermEntered, this, [&](const QString &term) { - QString t = term; - t.replace(' ', '+'); - QString url = QString::fromStdString(m_config->value<std::string>("profile.search").value()); - url.replace("$term", t); - tabBar->currentView()->load(QUrl::fromUserInput(url)); + navigationToolBar->connectWebView(w->currentView()); + navigationBarConnection = connect(w, &Window::currentViewChanged, navigationToolBar, &NavigationBar::connectWebView); + } }); - connect(tabBar, &TabBar::currentTabChanged, this, &MainWindow::handleTabChanged); - // loading bar - ui->statusBar->addPermanentWidget(m_progressBar); - - // search box - m_searchBox = new SearchForm(this); - ui->statusBar->addWidget(m_searchBox); - m_searchBox->setVisible(false); - - resize(m_config->value<int>("browser.window.width").value(), m_config->value<int>("browser.window.height").value()); - if(m_config->value<bool>("browser.window.maximized").value()) { - showMaximized(); - } + auto *tileShortcut = new QShortcut(QKeySequence(config->value<std::string>("mainwindow.shortcuts.tileWindows").value().c_str()), this); + connect(tileShortcut, &QShortcut::activated, this, [this]() { + mdiArea->tileSubWindows(); + }); } MainWindow::~MainWindow() { - // Release all dock widgets before deleting so we don't accidentally delete them - // Also fixes that annoying crash when closing - QList<QDockWidget *> allDockWidgets = findChildren<QDockWidget *>(); - for(QDockWidget *w : allDockWidgets) { - if(w->widget()) { - w->widget()->setParent(nullptr); - } - } - - delete ui; + disconnect(titleChangedConnection); + disconnect(navigationBarConnection); } -void MainWindow::addTabbedDock(Qt::DockWidgetArea area, QWidget *widget) +void MainWindow::createMenuBar() { - // make a list of widgets in the area we want - // this way we can append the new dock widget to the last one - QVector<QDockWidget *> areaDockWidgets; - for(QDockWidget *w : findChildren<QDockWidget *>()) { - // check if widget is already shown - if(w->widget() == widget) { - // in this case, close the dock and return - w->close(); - return; - } + auto *smolboteMenu = menuBar()->addMenu(qApp->applicationDisplayName()); + smolboteMenu->addAction(tr("New tab"), this, [this]() { + createTab(QUrl::fromUserInput("about:blank")); + }, + QKeySequence(m_config->value<std::string>("mainwindow.shortcuts.newTab").value().c_str())); - if(dockWidgetArea(w) == area) { - areaDockWidgets.append(w); - } - } + smolboteMenu->addAction(tr("New tab group"), this, [this]() { + createSubWindow(QUrl::fromUserInput("about:blank")); + }, + QKeySequence(m_config->value<std::string>("mainwindow.shortcuts.newGroup").value().c_str())); + smolboteMenu->addAction(tr("New window"))->setEnabled(false); - // create a dock widget - QDockWidget *dock = new QDockWidget(widget->windowTitle(), this); - dock->setAttribute(Qt::WA_DeleteOnClose, true); + smolboteMenu->addSeparator(); - // when the dock widget becomes invisble, release the docked widget - // setting the widget makes the dock its parent; setting parent back to nullptr - // makes the dock not show the widget any more - connect(dock, &QDockWidget::visibilityChanged, [dock](bool visible) { - if(!visible && dock->widget()) { - dock->widget()->setParent(nullptr); - } - }); - dock->setWidget(widget); + smolboteMenu->addAction(tr("About"), this, [this]() { + auto *dlg = new AboutDialog(this); + dlg->exec(); + }, + QKeySequence(m_config->value<std::string>("mainwindow.shortcuts.about").value().c_str())); + smolboteMenu->addAction(tr("About Qt"), qApp, &QApplication::aboutQt); - // the same widget may be shown by docks in other windows - // in that case, they grab ownership and the current dock won't be showing anything - // so the current widget needs to be closed - auto *w = dynamic_cast<BookmarksWidget *>(widget); - w->closeOthers(); - if(w) { - connect(w, &BookmarksWidget::closeOthersSignal, dock, [dock]() { - dock->close(); - }); - } - - if(areaDockWidgets.empty()) { - // no other widgets - addDockWidget(area, dock); - } else { - // there are other widgets, so put it after the last one - tabifyDockWidget(areaDockWidgets.last(), dock); - } -} + smolboteMenu->addSeparator(); -void MainWindow::newTab(const QUrl &url) -{ - if(!m_tabBarAdded) { - m_tabBarAdded = true; - ui->mainToolBar->addWidget(tabBar); - } - tabBar->addTab(createWebView(url, m_profile.get(), this)); -} + smolboteMenu->addAction(tr("Quit"), qApp, &QApplication::quit, + QKeySequence(m_config->value<std::string>("mainwindow.shortcuts.quit").value().c_str())); -MainWindow *MainWindow::newWindow(const QUrl &url) -{ - auto *instance = dynamic_cast<Browser *>(QApplication::instance()); - return instance->createSession(m_profile->storageName(), true, QStringList(url.toString())); + toolsMenu = menuBar()->addMenu(tr("Tools")); } -void MainWindow::closeEvent(QCloseEvent *event) +void MainWindow::addAction(ActionLocation where, QAction *action) { - if(tabBar->count() > 1) { - int ret = QMessageBox::warning(this, tr("Close window?"), tr("Close multiple tabs?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - if(ret == QMessageBox::No) { - event->ignore(); - return; - } + switch(where) { + case ToolsMenu: + toolsMenu->addAction(action); + break; + default: + QMainWindow::addAction(action); + break; } - QMainWindow::closeEvent(event); -} - -void MainWindow::about() -{ - auto *dlg = new AboutDialog(this); - dlg->exec(); } -void MainWindow::setProfile(std::shared_ptr<WebEngineProfile> profile) +void MainWindow::addDockWidget(Qt::DockWidgetArea area, QWidget *widget) { - Q_ASSERT(profile); - m_profile = profile; - tabBar->setProfile(profile.get()); - menuBar->profileAction()->setText(tr("Current profile: %1").arg(profile->name())); -} - -WebEngineProfile *MainWindow::profile() -{ - Q_ASSERT(m_profile); - return m_profile.get(); -} + QDockWidget *dock = new QDockWidget(widget->windowTitle(), this); + dock->setAttribute(Qt::WA_DeleteOnClose, true); + dock->setWidget(widget); -void MainWindow::setBookmarksWidget(std::shared_ptr<BookmarksWidget> &widget) -{ - Q_ASSERT(widget); - m_bookmarksWidget = widget; - m_addressBar->setCompleterModel(m_bookmarksWidget->model()); - connect(menuBar->bookmarksAction(), &QAction::triggered, this, [this]() { - addTabbedDock(Qt::RightDockWidgetArea, m_bookmarksWidget.get()); - }); - connect(m_bookmarksWidget.get(), &BookmarksWidget::openUrl, this, [this](const QUrl &url) { - if(isActiveWindow()) { - newTab(url); + connect(dock, &QDockWidget::visibilityChanged, [dock](bool visible) { + if(!visible && dock->widget()) { + dock->widget()->setParent(nullptr); } }); -} -void MainWindow::setDownloadsWidget(std::shared_ptr<DownloadsWidget> &widget) -{ - Q_ASSERT(widget); - m_downloadsWidget = widget; + QMainWindow::addDockWidget(area, dock); } -void MainWindow::toggleFullscreen() +void MainWindow::createTab(const QUrl &url) { - if(isFullScreen()) { - setWindowState(Qt::WindowMaximized | Qt::WindowActive); + auto *w = qobject_cast<Window *>(mdiArea->currentSubWindow()); + if(w == nullptr) { + w = createSubWindow(url); } else { - setWindowState(Qt::WindowFullScreen | Qt::WindowActive); + w->addTab(url); } } -void MainWindow::handleTabChanged(WebView *view) +Window *MainWindow::createSubWindow(const QUrl &url) { - Q_CHECK_PTR(view); + auto *w = new Window(this); + mdiArea->addSubWindow(w); + w->showMaximized(); + w->setFocus(); - disconnect(titleChangedConnection); - disconnect(newBookmarkConnection); + w->addTab(url); - m_currentView = view; - - // centralWidget can be a nullptr - if(centralWidget()) { - // clear the parent of the central widget so it doesn't get deleted - centralWidget()->setParent(nullptr); - - // disconnect signals - disconnect(centralWidget()); - } - - // set new central widget - setCentralWidget(view); - - // connect signals - m_navigationBar->connectWebView(view); - - m_addressBar->connectWebView(view); - - titleChangedConnection = connect(view, &WebView::titleChanged, this, &MainWindow::handleTitleUpdated); - newBookmarkConnection = connect(view, &WebView::newBookmark, this, [this](const QString &title, const QUrl &url) { - if(m_bookmarksWidget) { - auto *bookmark = m_bookmarksWidget->model()->createBookmark(nullptr); - bookmark->setText(0, title); - bookmark->setText(1, url.toString()); - } - }); - - m_progressBar->connectWebView(view); - - // update UI - this->handleTitleUpdated(view->title()); - centralWidget()->setFocus(); + return w; } -void MainWindow::handleTitleUpdated(const QString &title) -{ - QString t = QString::fromStdString(m_config->value<std::string>("browser.window.title").value()); - t.replace("title", title); - t.replace("profile", m_profile->name()); - setWindowTitle(t); - //setWindowTitle(browser->settings()->value("window.title").toString().replace("title", title).replace("profile", tabBar->profile()->name())); -} - -void MainWindow::addPlugins(const QVector<Browser::Plugin> &plugins) +void MainWindow::closeEvent(QCloseEvent *event) { - for(const Browser::Plugin &plugin : plugins) { - ProfileInterface *iProfilePlugin = qobject_cast<ProfileInterface *>(plugin.pointer); - if(iProfilePlugin) { - QWidget *w = iProfilePlugin->createWidget(m_profile.get(), this); - - menuBar->profileAction()->setEnabled(true); - connect(menuBar->profileAction(), &QAction::triggered, this, [this, w]() { - w->setVisible(!w->isVisible()); - if(w->isVisible()) { - QPoint pos = ui->navigationToolBar->pos(); - pos.setX(pos.x() + ui->navigationToolBar->width() - w->width()); - pos.setY(pos.y() + ui->navigationToolBar->height()); - w->move(mapToGlobal(pos)); - w->show(); - } - }); + if(mdiArea->subWindowList().count() > 1) { + int choice = QMessageBox::question(this, tr("Close multiple subwindows?"), tr("Do you want to close all subwindows?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + if(choice == QMessageBox::No) { + event->ignore(); + return; } } + + mdiArea->closeAllSubWindows(); + if(mdiArea->currentSubWindow()) + event->ignore(); + else + event->accept(); } diff --git a/src/mainwindow/mainwindow.h b/src/mainwindow/mainwindow.h index 8b205ab..1817fdd 100644 --- a/src/mainwindow/mainwindow.h +++ b/src/mainwindow/mainwindow.h @@ -6,93 +6,49 @@ * SPDX-License-Identifier: GPL-3.0 */ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H +#ifndef SMOLBOTE_MAINWINDOW_H +#define SMOLBOTE_MAINWINDOW_H -#include "browser.h" -#include "webengine/webengineprofile.h" -#include "widgets/loadingbar.h" -#include "widgets/navigationbar.h" #include <QMainWindow> -#include <QUrl> -#include <interfaces.h> #include <memory> -namespace Ui -{ -class MainWindow; -} - -class TabBar; -class SearchForm; +class QMdiArea; class Configuration; -class BookmarksWidget; -class DownloadsWidget; -class MainWindowMenuBar; -class UrlLineEdit; - +class Window; class MainWindow : public QMainWindow { Q_OBJECT - friend class WebView; - friend class SearchForm; - - friend class MainWindowMenuBar; - - friend class NavigationBar; - public: - explicit MainWindow(std::shared_ptr<Configuration> config, QWidget *parent = nullptr); + enum ActionLocation { + ToolsMenu + }; + + explicit MainWindow(std::shared_ptr<Configuration> &config, QWidget *parent = nullptr); Q_DISABLE_COPY(MainWindow) ~MainWindow() override; - void addTabbedDock(Qt::DockWidgetArea area, QWidget *widget); - -public slots: - void about(); - - void newTab(const QUrl &url = QUrl("")); - MainWindow *newWindow(const QUrl &url = QUrl("")); + void createMenuBar(); - void setProfile(std::shared_ptr<WebEngineProfile> profile); - WebEngineProfile *profile(); + void addAction(ActionLocation where, QAction *action); + void addDockWidget(Qt::DockWidgetArea area, QWidget *widget); - void setBookmarksWidget(std::shared_ptr<BookmarksWidget> &widget); - void setDownloadsWidget(std::shared_ptr<DownloadsWidget> &widget); - void addPlugins(const QVector<Browser::Plugin> &plugins); - - void toggleFullscreen(); +public slots: + void createTab(const QUrl &url); + Window *createSubWindow(const QUrl &url); protected: void closeEvent(QCloseEvent *event) override; -private slots: - void handleTabChanged(WebView *view); - void handleTitleUpdated(const QString &title); - private: - Ui::MainWindow *ui; - SearchForm *m_searchBox; - - TabBar *tabBar; - WebView *m_currentView; - - MainWindowMenuBar *menuBar; - - // navigation - NavigationBar *m_navigationBar; - UrlLineEdit *m_addressBar; - LoadingBar *m_progressBar; + QString titleSuffix; + QMenu *toolsMenu = nullptr; + QMdiArea *mdiArea; - bool m_tabBarAdded = false; - std::shared_ptr<WebEngineProfile> m_profile; std::shared_ptr<Configuration> m_config; - std::shared_ptr<BookmarksWidget> m_bookmarksWidget; - std::shared_ptr<DownloadsWidget> m_downloadsWidget; QMetaObject::Connection titleChangedConnection; - QMetaObject::Connection newBookmarkConnection; + QMetaObject::Connection navigationBarConnection; }; -#endif // MAINWINDOW_H +#endif // SMOLBOTE_MAINWINDOW_H diff --git a/src/mainwindow/mainwindow.ui b/src/mainwindow/mainwindow.ui deleted file mode 100644 index 12a3a6c..0000000 --- a/src/mainwindow/mainwindow.ui +++ /dev/null @@ -1,63 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MainWindow</class>
- <widget class="QMainWindow" name="MainWindow">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>400</width>
- <height>300</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>MainWindow</string>
- </property>
- <widget class="QWidget" name="centralWidget"/>
- <widget class="QStatusBar" name="statusBar">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>24</height>
- </size>
- </property>
- </widget>
- <widget class="QToolBar" name="mainToolBar">
- <property name="windowTitle">
- <string>Main</string>
- </property>
- <property name="allowedAreas">
- <set>Qt::TopToolBarArea</set>
- </property>
- <attribute name="toolBarArea">
- <enum>TopToolBarArea</enum>
- </attribute>
- <attribute name="toolBarBreak">
- <bool>false</bool>
- </attribute>
- </widget>
- <widget class="QToolBar" name="navigationToolBar">
- <property name="windowTitle">
- <string>Navigation</string>
- </property>
- <property name="allowedAreas">
- <set>Qt::TopToolBarArea</set>
- </property>
- <attribute name="toolBarArea">
- <enum>TopToolBarArea</enum>
- </attribute>
- <attribute name="toolBarBreak">
- <bool>false</bool>
- </attribute>
- </widget>
- </widget>
- <layoutdefault spacing="6" margin="11"/>
- <resources/>
- <connections/>
-</ui>
diff --git a/src/mainwindow/widgets/loadingbar.cpp b/src/mainwindow/widgets/loadingbar.cpp index 76eafc4..61e3eae 100644 --- a/src/mainwindow/widgets/loadingbar.cpp +++ b/src/mainwindow/widgets/loadingbar.cpp @@ -7,8 +7,8 @@ */ #include "loadingbar.h" -#include <QTimer> #include "webengine/webview.h" +#include <QTimer> LoadingBar::LoadingBar(QWidget *parent) : QProgressBar(parent) diff --git a/src/mainwindow/widgets/navigationbar.cpp b/src/mainwindow/widgets/navigationbar.cpp index 0652159..e2c714e 100644 --- a/src/mainwindow/widgets/navigationbar.cpp +++ b/src/mainwindow/widgets/navigationbar.cpp @@ -7,7 +7,9 @@ */ #include "navigationbar.h" -#include "mainwindow/mainwindow.h" +#include "addressbar/urllineedit.h" +#include "configuration/configuration.h" +#include "webengine/webview.h" #include <QHBoxLayout> #include <QMenu> #include <QShortcut> @@ -15,99 +17,94 @@ #include <QToolBar> #include <QToolButton> #include <QWebEngineHistory> -#include "webengine/webview.h" -#include "configuration/configuration.h" -NavigationBar::NavigationBar(MainWindow *parent) - : QObject(parent) +NavigationBar::NavigationBar(const QHash<QString, QString> &conf, QWidget *parent) + : QToolBar(parent) { qStyle = parent->style(); // Back button - backButton = new QToolButton(parent); - backButton->setIcon(qStyle->standardIcon(QStyle::SP_ArrowBack)); - backButton->setShortcut(QString::fromStdString(parent->m_config->value<std::string>("browser.shortcuts.back").value())); - connect(backButton, &QToolButton::clicked, this, [this]() { + backAction = addAction(qStyle->standardIcon(QStyle::SP_ArrowBack), tr("Back")); + backAction->setShortcut(QKeySequence(conf.value("navigation.shortcuts.back"))); + connect(backAction, &QAction::triggered, this, [this]() { m_view->history()->back(); }); - auto *backMenu = new QMenu(backButton); - backButton->setMenu(backMenu); + auto *backMenu = new QMenu(this); connect(backMenu, &QMenu::aboutToShow, this, [this, backMenu]() { backMenu->clear(); - const QList<QWebEngineHistoryItem> items = m_view->history()->backItems(10); - for(const QWebEngineHistoryItem &i : items) { - QAction *a = backMenu->addAction(i.title()); - connect(a, &QAction::triggered, this, [i, this]() { - m_view->history()->goToItem(i); + for(const QWebEngineHistoryItem &item : m_view->history()->backItems(10)) { + auto *action = backMenu->addAction(item.title()); + connect(action, &QAction::triggered, this, [item, this]() { + m_view->history()->goToItem(item); }); } }); + backAction->setMenu(backMenu); // Forward button - forwardButton = new QToolButton(parent); - forwardButton->setIcon(qStyle->standardIcon(QStyle::SP_ArrowForward)); - forwardButton->setShortcut(QString::fromStdString(parent->m_config->value<std::string>("browser.shortcuts.forward").value())); - connect(forwardButton, &QToolButton::clicked, this, [this]() { + forwardAction = addAction(qStyle->standardIcon(QStyle::SP_ArrowForward), tr("Forward")); + forwardAction->setShortcut(QKeySequence(conf.value("navigation.shortcuts.forward"))); + connect(forwardAction, &QAction::triggered, this, [this]() { m_view->history()->forward(); }); - auto *forwardMenu = new QMenu(forwardButton); - forwardButton->setMenu(forwardMenu); + auto *forwardMenu = new QMenu(this); connect(forwardMenu, &QMenu::aboutToShow, this, [this, forwardMenu]() { forwardMenu->clear(); - const QList<QWebEngineHistoryItem> items = m_view->history()->forwardItems(10); - for(const QWebEngineHistoryItem &i : items) { - QAction *a = forwardMenu->addAction(i.title()); - connect(a, &QAction::triggered, this, [i, this]() { - m_view->history()->goToItem(i); + for(const QWebEngineHistoryItem &item : m_view->history()->forwardItems(10)) { + auto *action = forwardMenu->addAction(item.title()); + connect(action, &QAction::triggered, this, [item, this]() { + m_view->history()->goToItem(item); }); } }); + forwardAction->setMenu(forwardMenu); // Stop/Refresh button - stopReloadButton = new QToolButton(parent); - stopReloadButton->setIcon(qStyle->standardIcon(QStyle::SP_BrowserReload)); - stopReloadButton->setShortcut(QString::fromStdString(parent->m_config->value<std::string>("browser.shortcuts.refresh").value())); - connect(stopReloadButton, &QToolButton::clicked, this, [this]() { + stopReloadAction = addAction(qStyle->standardIcon(QStyle::SP_BrowserReload), tr("Reload")); + stopReloadAction->setShortcut(QKeySequence(conf.value("navigation.shortcuts.refresh"))); + connect(stopReloadAction, &QAction::triggered, this, [this]() { if(m_view->isLoaded()) m_view->reload(); else m_view->stop(); }); - auto *reloadShortcut = new QShortcut( - QString::fromStdString(parent->m_config->value<std::string>("browser.shortcuts.reload").value()), - parent); - connect(reloadShortcut, &QShortcut::activated, this, [this]() { - m_view->reload(); - }); - // Home button - homeButton = new QToolButton(parent); - homeButton->setIcon(qStyle->standardIcon(QStyle::SP_DirHomeIcon)); - homeButton->setShortcut(QString::fromStdString(parent->m_config->value<std::string>("browser.shortcuts.home").value())); - connect(homeButton, &QToolButton::clicked, this, [this, parent]() { - m_view->load(parent->m_profile->homepage()); + homeAction = addAction(qStyle->standardIcon(QStyle::SP_DirHomeIcon), tr("Home")); + homeAction->setShortcut(QKeySequence(conf.value("navigation.shortcuts.home"))); + connect(homeAction, &QAction::triggered, this, [this]() { + m_view->triggerViewAction(WebView::GoHome); }); -} -void NavigationBar::addWidgetsTo(QToolBar *toolBar) -{ - toolBar->addWidget(backButton); - toolBar->addWidget(forwardButton); - toolBar->addWidget(stopReloadButton); - toolBar->addWidget(homeButton); + QHash<QString, QString> a; + addressBar = new UrlLineEdit(a, this); + addWidget(addressBar); + + auto *focusShortcut = new QShortcut(QKeySequence("F4"), this); + connect(focusShortcut, &QShortcut::activated, this, [this]() { + addressBar->setFocus(); + addressBar->selectAll(); + }); } void NavigationBar::connectWebView(WebView *view) { - Q_CHECK_PTR(view); m_view = view; disconnect(loadStartedConnection); disconnect(loadFinishedConnection); + if(view == nullptr) { + backAction->setEnabled(false); + forwardAction->setEnabled(false); + stopReloadAction->setEnabled(false); + homeAction->setEnabled(false); + + return; + } + if(view->isLoaded()) { update_loadFinished(); } else { @@ -116,18 +113,22 @@ void NavigationBar::connectWebView(WebView *view) loadStartedConnection = connect(view, &QWebEngineView::loadStarted, this, &NavigationBar::update_loadStarted); loadFinishedConnection = connect(view, &WebView::loaded, this, &NavigationBar::update_loadFinished); + stopReloadAction->setEnabled(true); + homeAction->setEnabled(true); + + addressBar->connectWebView(view); } void NavigationBar::update_loadStarted() { - backButton->setEnabled(m_view->history()->canGoForward()); - forwardButton->setEnabled(m_view->history()->canGoForward()); - stopReloadButton->setIcon(qStyle->standardIcon(QStyle::SP_BrowserStop)); + backAction->setEnabled(m_view->history()->canGoForward()); + forwardAction->setEnabled(m_view->history()->canGoForward()); + stopReloadAction->setIcon(qStyle->standardIcon(QStyle::SP_BrowserStop)); } void NavigationBar::update_loadFinished() { - backButton->setEnabled(m_view->history()->canGoBack()); - forwardButton->setEnabled(m_view->history()->canGoForward()); - stopReloadButton->setIcon(qStyle->standardIcon(QStyle::SP_BrowserReload)); + backAction->setEnabled(m_view->history()->canGoBack()); + forwardAction->setEnabled(m_view->history()->canGoForward()); + stopReloadAction->setIcon(qStyle->standardIcon(QStyle::SP_BrowserReload)); } diff --git a/src/mainwindow/widgets/navigationbar.h b/src/mainwindow/widgets/navigationbar.h index fd0579d..1b4cc05 100644 --- a/src/mainwindow/widgets/navigationbar.h +++ b/src/mainwindow/widgets/navigationbar.h @@ -6,36 +6,21 @@ * SPDX-License-Identifier: GPL-3.0 */ -/* - * Why is this class a QObject and not a QWidget, and why don't we add the - * NavigationBar itself to the toolbar? - * That was the original idea: make a NavBar class, friend it from MainWindow - * to gain access to the config, but there's a couple of issues: - * - adding QToolButtons to a widget that's inside a toolbar makes the buttons - * look absolutely hideous: they're smaller and have a border for some reason - * - if you stylesheet the border away (which is a pain), they're still not - * the same size - * And so we ended up having this class only exist to contain all of the logic - * for the nav buttons (which cuts down the code in MainWindow). - */ - -#ifndef NAVIGATIONBAR_H -#define NAVIGATIONBAR_H +#ifndef SMOLBOTE_NAVIGATIONBAR_H +#define SMOLBOTE_NAVIGATIONBAR_H -#include <QObject> +#include <QToolBar> -class QStyle; -class QToolBar; -class QToolButton; -class MainWindow; +class UrlLineEdit; class WebView; -class NavigationBar : public QObject +class NavigationBar : public QToolBar { Q_OBJECT + public: - explicit NavigationBar(MainWindow *parent = nullptr); + explicit NavigationBar(const QHash<QString, QString> &conf, QWidget *parent = nullptr); - void addWidgetsTo(QToolBar *toolBar); +public slots: void connectWebView(WebView *view); private slots: @@ -45,11 +30,13 @@ private slots: private: QStyle *qStyle; WebView *m_view; - QToolButton *backButton, *forwardButton; - QToolButton *stopReloadButton; - QToolButton *homeButton; + + QAction *backAction, *forwardAction; + QAction *stopReloadAction; + QAction *homeAction; + UrlLineEdit *addressBar; QMetaObject::Connection loadStartedConnection, loadFinishedConnection; }; -#endif //NAVIGATIONBAR_H +#endif // SMOLBOTE_NAVIGATIONBAR_H diff --git a/src/mainwindow/widgets/searchform.cpp b/src/mainwindow/widgets/searchform.cpp index 8ac1bc2..07b8426 100644 --- a/src/mainwindow/widgets/searchform.cpp +++ b/src/mainwindow/widgets/searchform.cpp @@ -7,11 +7,11 @@ */ #include "searchform.h" +#include "configuration/configuration.h" #include "mainwindow/mainwindow.h" #include "ui_searchform.h" -#include <QAction> #include "webengine/webview.h" -#include "configuration/configuration.h" +#include <QAction> SearchForm::SearchForm(MainWindow *parentWindow, QWidget *parent) : QWidget(parent) diff --git a/src/mainwindow/widgets/tabbar.cpp b/src/mainwindow/widgets/tabbar.cpp deleted file mode 100644 index ef32ef0..0000000 --- a/src/mainwindow/widgets/tabbar.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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/smolbote.hg - * - * SPDX-License-Identifier: GPL-3.0 - */ - -#include "tabbar.h" -#include "mainwindow/mainwindow.h" -#include "webengine/webengineprofile.h" -#include "webengine/webview.h" -#include <QContextMenuEvent> -#include <QMenu> - -TabBar::TabBar(const QHash<QString, QString> &config, MainWindow *parent) - : QTabBar(parent) -{ - Q_CHECK_PTR(parent); - - setElideMode(Qt::ElideRight); - setTabsClosable(true); - setMovable(true); - setContextMenuPolicy(Qt::DefaultContextMenu); - - connect(this, &TabBar::tabCloseRequested, this, &TabBar::removeTab); - connect(this, &TabBar::currentChanged, this, &TabBar::handleCurrentChanged); - connect(this, &TabBar::tabMoved, this, &TabBar::updateVectorArrangement); - - newTab_action = new QAction(tr("New Tab"), parent); - newTab_action->setObjectName("newTab_action"); - newTab_action->setShortcut(QKeySequence(config["tabbar.shortcuts.new"])); - connect(newTab_action, &QAction::triggered, parent, [parent, this]() { - parent->newTab(parent->profile()->newtab()); - }); - parent->addAction(newTab_action); - - closeTab_action = new QAction(tr("Close Tab"), parent); - closeTab_action->setObjectName("closeTab_action"); - closeTab_action->setShortcut(QKeySequence(config["tabbar.shortcuts.close"])); - connect(closeTab_action, &QAction::triggered, this, [this]() { - removeTab(currentIndex()); - }); - parent->addAction(closeTab_action); - - leftTab_action = new QAction(tr("Left Tab"), parent); - leftTab_action->setObjectName("leftTab_action"); - leftTab_action->setShortcut(QKeySequence(config["tabbar.shortcuts.left"])); - connect(leftTab_action, &QAction::triggered, this, [this]() { - setCurrentIndex(currentIndex() - 1); - }); - parent->addAction(leftTab_action); - - rightTab_action = new QAction(tr("Right Tab"), parent); - rightTab_action->setObjectName("rightTab_action"); - rightTab_action->setShortcut(QKeySequence(config["tabbar.shortcuts.right"])); - connect(rightTab_action, &QAction::triggered, this, [this]() { - setCurrentIndex(currentIndex() + 1); - }); - parent->addAction(rightTab_action); - - // tab context menu - { - tabContextMenu = new QMenu(this); - - auto *closeTab = tabContextMenu->addAction(tr("Close Tab")); - connect(closeTab, &QAction::triggered, this, [this]() { - removeTab(tabAt(mapFromGlobal(tabContextMenu->pos()))); - }); - - auto *closeTabsLeft = tabContextMenu->addAction(tr("Close Tabs left")); - connect(closeTabsLeft, &QAction::triggered, this, [this]() { - int idx = tabAt(mapFromGlobal(tabContextMenu->pos())); - for(int i = idx - 1; i >= 0; --i) { - removeTab(i); - } - }); - - auto *closeTabsRight = tabContextMenu->addAction(tr("Close Tabs right")); - connect(closeTabsRight, &QAction::triggered, this, [this]() { - int idx = tabAt(mapFromGlobal(tabContextMenu->pos())); - for(int i = count() - 1; i > idx; --i) { - removeTab(i); - } - }); - } -} - -TabBar::~TabBar() -{ - // cleanup - qDeleteAll(m_views); - m_views.clear(); -} - -void TabBar::setProfile(WebEngineProfile *profile) -{ - Q_CHECK_PTR(profile); - - for(auto view : qAsConst(m_views)) { - auto *page = new WebPage(profile); - page->load(view->url()); - view->page()->deleteLater(); - view->setPage(page); - } -} - -WebView *TabBar::currentView() -{ - return m_views.at(currentIndex()); -} - -int TabBar::addTab(WebView *view) -{ - m_views.append(view); - - connect(view, &QWebEngineView::titleChanged, [this, view](const QString &title) { - int index = m_views.indexOf(view); - setTabText(index, title); - setTabToolTip(index, title); - }); - connect(view, &QWebEngineView::iconChanged, [this, view](const QIcon &icon) { - int index = m_views.indexOf(view); - setTabIcon(index, icon); - }); - - return QTabBar::addTab("New Tab"); -} - -void TabBar::removeTab(int index) -{ - // remove the tab data from the index - m_views.at(index)->deleteLater(); - m_views.remove(index); - - // remove the tab from the QTabBar - // this emits the currentTabChanged signal, so it should be done after the view is removed from the index - QTabBar::removeTab(index); -} - -void TabBar::contextMenuEvent(QContextMenuEvent *event) -{ - // check if the context menu was called on a tab - int tabIndex = tabAt(event->pos()); - if(tabIndex < 0) { - return; - } - - tabContextMenu->exec(event->globalPos()); -} - -QSize TabBar::tabSizeHint(int index) const -{ - Q_UNUSED(index) - return QSize(200, this->height()); -} - -void TabBar::handleCurrentChanged(int index) -{ - if(index < 0) { - newTab_action->trigger(); - return; - } - - emit currentTabChanged(m_views.at(index)); -} - -void TabBar::updateVectorArrangement(int from, int to) -{ - m_views.move(from, to); -} diff --git a/src/mainwindow/widgets/tabbar.h b/src/mainwindow/widgets/tabbar.h deleted file mode 100644 index f4b7414..0000000 --- a/src/mainwindow/widgets/tabbar.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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/smolbote.hg - * - * SPDX-License-Identifier: GPL-3.0 - */ - -#ifndef SMOLBOTE_TABBAR_H -#define SMOLBOTE_TABBAR_H - -#include <QTabBar> - -class MainWindow; -class WebView; -class WebEngineProfile; -class QMenu; -class TabBar : public QTabBar -{ - Q_OBJECT - -public: - explicit TabBar(const QHash<QString, QString> &config, MainWindow *parent = nullptr); - ~TabBar() override; - - void setProfile(WebEngineProfile *profile); - WebView *currentView(); - -signals: - void currentTabChanged(WebView *view); - -public slots: - int addTab(WebView *view); - void removeTab(int index); - -protected: - void contextMenuEvent(QContextMenuEvent *event) override; - QSize tabSizeHint(int index) const override; - -private slots: - void handleCurrentChanged(int index); - void updateVectorArrangement(int from, int to); - -private: - // store all views in a vector since tabs can only store a QVariant, and that can't easily take a pointer - QVector<WebView *> m_views; - - QMenu *tabContextMenu; - - QAction *newTab_action; - QAction *closeTab_action; - QAction *leftTab_action; - QAction *rightTab_action; -}; - -#endif // SMOLBOTE_TABBAR_H diff --git a/src/mainwindow/widgets/tabwidget.cpp b/src/mainwindow/widgets/tabwidget.cpp new file mode 100644 index 0000000..21476cd --- /dev/null +++ b/src/mainwindow/widgets/tabwidget.cpp @@ -0,0 +1,121 @@ +/* + * 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/smolbote.hg + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#include "tabwidget.h" +#include "webengine/webview.h" +#include <QAction> +#include <QContextMenuEvent> +#include <QMenu> +#include <QTabBar> + +TabWidget::TabWidget(QWidget *parent) + : QTabWidget(parent) +{ + setStyleSheet("QTabBar::tab { width: 200px; }"); + + setTabsClosable(true); + setTabBarAutoHide(true); + setElideMode(Qt::ElideRight); + setMovable(true); + + connect(this, &TabWidget::tabCloseRequested, this, &TabWidget::deleteTab); + + // general actions + closeTab_action = new QAction(this); + closeTab_action->setShortcut(QKeySequence("Ctrl+X")); + connect(closeTab_action, &QAction::triggered, this, [this]() { + this->deleteTab(this->currentIndex()); + }); + addAction(closeTab_action); + + leftTab_action = new QAction(this); + leftTab_action->setShortcut(QKeySequence("Ctrl+O")); + connect(leftTab_action, &QAction::triggered, this, [this]() { + this->setCurrentIndex(qMax(0, this->currentIndex() - 1)); + }); + addAction(leftTab_action); + + rightTab_action = new QAction(this); + rightTab_action->setShortcut(QKeySequence("Ctrl+P")); + connect(rightTab_action, &QAction::triggered, this, [this]() { + this->setCurrentIndex(qMin(this->currentIndex() + 1, this->count() - 1)); + }); + addAction(rightTab_action); + + // context menu + tabContextMenu = new QMenu(this); + auto *closeTab = tabContextMenu->addAction(tr("Close Tab")); + connect(closeTab, &QAction::triggered, this, [this]() { + deleteTab(this->tabBar()->tabAt(mapFromGlobal(tabContextMenu->pos()))); + }); + + auto *closeTabsLeft = tabContextMenu->addAction(tr("Close Tabs left")); + connect(closeTabsLeft, &QAction::triggered, this, [this]() { + int idx = this->tabBar()->tabAt(mapFromGlobal(tabContextMenu->pos())); + for(int i = idx - 1; i >= 0; --i) { + deleteTab(i); + } + }); + + auto *closeTabsRight = tabContextMenu->addAction(tr("Close Tabs right")); + connect(closeTabsRight, &QAction::triggered, this, [this]() { + int idx = this->tabBar()->tabAt(mapFromGlobal(tabContextMenu->pos())); + for(int i = count() - 1; i > idx; --i) { + deleteTab(i); + } + }); +} + +TabWidget::~TabWidget() +{ + for(int i = count() - 1; i >= 0; i--) { + delete widget(i); + } +} + +int TabWidget::addTab(WebView *view) +{ + Q_CHECK_PTR(view); + + int idx = QTabWidget::addTab(view, view->title()); + connect(view, &WebView::titleChanged, [this, view](const QString &title) { + int idx = this->indexOf(view); + Q_ASSERT(idx != -1); + + this->setTabText(idx, title); + }); + connect(view, &WebView::iconChanged, [this, view](const QIcon &icon) { + int idx = this->indexOf(view); + Q_ASSERT(idx != -1); + + this->setTabIcon(idx, icon); + }); + + return idx; +} + +void TabWidget::deleteTab(int index) +{ + // deleting the widget automatically removes the tab? + if(count() > 1) { + widget(index)->deleteLater(); + removeTab(index); + } else + parentWidget()->close(); +} + +void TabWidget::contextMenuEvent(QContextMenuEvent *event) +{ + // check if the context menu was called on a tab + int tabIndex = tabBar()->tabAt(event->pos()); + if(tabIndex < 0) { + return; + } + + tabContextMenu->exec(event->globalPos()); +} diff --git a/src/mainwindow/widgets/tabwidget.h b/src/mainwindow/widgets/tabwidget.h new file mode 100644 index 0000000..b7aad63 --- /dev/null +++ b/src/mainwindow/widgets/tabwidget.h @@ -0,0 +1,40 @@ +/* + * 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/smolbote.hg + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#ifndef SMOLBOTE_TABWIDGET_H +#define SMOLBOTE_TABWIDGET_H + +#include <QTabWidget> + +class QAction; +class QMenu; +class WebView; +class TabWidget : public QTabWidget +{ + Q_OBJECT + +public: + explicit TabWidget(QWidget *parent = nullptr); + ~TabWidget() override; + +public slots: + int addTab(WebView *view); + void deleteTab(int index); + +protected: + void contextMenuEvent(QContextMenuEvent *event) override; + +private: + QAction *closeTab_action; + QAction *leftTab_action; + QAction *rightTab_action; + + QMenu *tabContextMenu; +}; + +#endif // SMOLBOTE_TABWIDGET_H diff --git a/src/mainwindow/window.cpp b/src/mainwindow/window.cpp new file mode 100644 index 0000000..67397fe --- /dev/null +++ b/src/mainwindow/window.cpp @@ -0,0 +1,68 @@ +/* + * 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/smolbote.hg + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#include "window.h" +#include "webengine/webprofile.h" +#include "webengine/webview.h" +#include "widgets/tabwidget.h" +#include <QUrl> + +Window::Window(QWidget *parent, Qt::WindowFlags flags) + : QMdiSubWindow(parent, flags) + , tabWidget(new TabWidget(this)) +{ + // delete this window when it closes + setAttribute(Qt::WA_DeleteOnClose, true); + + resize(800, 600); + setWidget(tabWidget); + + connect(tabWidget, &TabWidget::currentChanged, [this](int index) { + if(index < 0) { + // last tab has been closed + close(); + } else { + auto *view = dynamic_cast<WebView *>(tabWidget->widget(index)); + Q_CHECK_PTR(view); + + disconnect(titleConnection); + connect(view, &WebView::titleChanged, this, &Window::setWindowTitle); + setWindowTitle(view->title()); + + emit currentViewChanged(view); + } + }); +} + +Window::~Window() +{ + delete tabWidget; +} + +WebView *Window::currentView() +{ + return qobject_cast<WebView *>(tabWidget->currentWidget()); +} + +int Window::addTab(WebView *view) +{ + Q_CHECK_PTR(view); + return tabWidget->addTab(view); +} + +int Window::addTab(const QUrl &url) +{ + auto *view = new WebView(WebProfile::defaultProfile(), this); + view->load(url); + return tabWidget->addTab(view); +} + +void Window::swapToTab(int index) +{ + tabWidget->setCurrentIndex(index); +} diff --git a/src/mainwindow/window.h b/src/mainwindow/window.h new file mode 100644 index 0000000..743d75f --- /dev/null +++ b/src/mainwindow/window.h @@ -0,0 +1,42 @@ +/* + * 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/smolbote.hg + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#ifndef SMOLBOTE_WINDOW_H +#define SMOLBOTE_WINDOW_H + +#include <QMdiSubWindow> +#include <memory> + +class TabWidget; +class WebView; +class Window : public QMdiSubWindow +{ + Q_OBJECT + +public: + explicit Window(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); + ~Window() override; + + WebView *currentView(); + +signals: + void currentViewChanged(WebView *view); + +public slots: + int addTab(WebView *view); + int addTab(const QUrl &url); + + void swapToTab(int index); + +private: + TabWidget *tabWidget; + + QMetaObject::Connection titleConnection; +}; + +#endif // SMOLBOTE_WINDOW_H |