diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2bc1f9fa4..c465a6aaa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -181,6 +181,8 @@ qt_add_executable(NotepadNext widgets/StatusLabel.h Sorter.h Sorter.cpp ScintillaSorter.h ScintillaSorter.cpp + widgets/TabsQuickActionsBar.cpp + widgets/TabsQuickActionsBar.h ) set_target_properties(NotepadNext PROPERTIES diff --git a/src/DockedEditor.cpp b/src/DockedEditor.cpp index 9e6b4771c..f813f0471 100644 --- a/src/DockedEditor.cpp +++ b/src/DockedEditor.cpp @@ -62,6 +62,7 @@ DockedEditor::DockedEditor(QWidget *parent) : QObject(parent) ads::CDockManager::setConfigFlag(ads::CDockManager::FocusHighlighting, true); ads::CDockManager::setConfigFlag(ads::CDockManager::EqualSplitOnInsertion, true); ads::CDockManager::setConfigFlag(ads::CDockManager::MiddleMouseButtonClosesTab, true); + ads::CDockManager::setConfigFlag(ads::CDockManager::DockAreaHasTabsMenuButton, false); dockManager = new ads::CDockManager(parent); dockManager->setStyleSheet(""); diff --git a/src/dialogs/MainWindow.cpp b/src/dialogs/MainWindow.cpp index fd25f62db..9cbe9a133 100644 --- a/src/dialogs/MainWindow.cpp +++ b/src/dialogs/MainWindow.cpp @@ -43,7 +43,6 @@ #include #include - #ifdef Q_OS_WIN #include #include @@ -74,6 +73,8 @@ #include "PreferencesDialog.h" #include "ColumnEditorDialog.h" +#include "TabsQuickActionsBar.h" + #include "QuickFindWidget.h" #include "EditorPrintPreviewRenderer.h" @@ -850,6 +851,29 @@ MainWindow::MainWindow(NotepadNextApplication *app) : mb.exec(); }); + tabsQuickActionsBar = new TabsQuickActionsBar(ui->menuBar); + ui->menuBar->setCornerWidget(tabsQuickActionsBar, Qt::TopRightCorner); + connect(tabsQuickActionsBar, &TabsQuickActionsBar::createNewTabClicked, this, &MainWindow::newFile); + connect(tabsQuickActionsBar, &TabsQuickActionsBar::closeCurrentTabClicked, this, &MainWindow::closeCurrentFile); + connect(tabsQuickActionsBar, &TabsQuickActionsBar::tabsMenuAboutToShow, this, [this](QMenu *editorsMenu) { + const auto editorsList = editors(); + + editorsMenu->clear(); + + for (const auto editor : editorsList) { + const auto iconPath = editor->isSavedToDisk() ? ":/icons/saved.png" : ":/icons/unsaved.png"; + const auto action = editorsMenu->addAction(QIcon(iconPath), editor->getName()); + + if (editor->isActiveWindow()) { + auto font = action->font(); + font.setBold(true); + action->setFont(font); + } + + connect(action, &QAction::triggered, this, [this, editor]() { switchToEditor(editor); }); + } + }); + #ifdef Q_OS_WIN connect(ui->actionShowInExplorer, &QAction::triggered, this, [=]() { QString filePath = QDir::toNativeSeparators(currentEditor()->getFileInfo().canonicalFilePath()); diff --git a/src/dialogs/MainWindow.h b/src/dialogs/MainWindow.h index 5c510a51b..607cec6c2 100644 --- a/src/dialogs/MainWindow.h +++ b/src/dialogs/MainWindow.h @@ -42,6 +42,7 @@ class QuickFindWidget; class ZoomEventWatcher; class Converter; class DefaultDirectoryManager; +class TabsQuickActionsBar; class MainWindow : public QMainWindow { @@ -167,6 +168,8 @@ private slots: QActionGroup *languageActionGroup; + TabsQuickActionsBar *tabsQuickActionsBar = Q_NULLPTR; + //NppImporter *npp; MacroManager macroManager; diff --git a/src/icons/cross.svg b/src/icons/cross.svg new file mode 100644 index 000000000..eb194fd2e --- /dev/null +++ b/src/icons/cross.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/list_with_icons.svg b/src/icons/list_with_icons.svg new file mode 100644 index 000000000..9d3230f78 --- /dev/null +++ b/src/icons/list_with_icons.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/plus.svg b/src/icons/plus.svg new file mode 100644 index 000000000..12fd70c58 --- /dev/null +++ b/src/icons/plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/resources.qrc b/src/resources.qrc index d373be086..9f390c4bd 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -42,5 +42,8 @@ icons/application_osx_terminal.png icons/folder_go.png icons/wrapindicator.png + icons/cross.svg + icons/plus.svg + icons/list_with_icons.svg diff --git a/src/widgets/TabsQuickActionsBar.cpp b/src/widgets/TabsQuickActionsBar.cpp new file mode 100644 index 000000000..9e76a830f --- /dev/null +++ b/src/widgets/TabsQuickActionsBar.cpp @@ -0,0 +1,95 @@ +/* + * This file is part of Notepad Next. + * Copyright 2026 Justin Dailey + * + * Notepad Next is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Notepad Next is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Notepad Next. If not, see . + */ + + +#include +#include +#include +#include + +#include + +#include "TabsQuickActionsBar.h" + +namespace +{ + constexpr QLatin1StringView IconPlusPath(":/icons/plus.svg"); + constexpr QLatin1StringView IconListPath(":/icons/list_with_icons.svg"); + constexpr QLatin1StringView IconCrossPath(":/icons/cross.svg"); +} + +TabsQuickActionsBar::TabsQuickActionsBar(const Buttons &visibileButtons, QWidget *parent) + : QToolBar(parent) +{ + createNewTabAction = addAction(QIcon(IconPlusPath), ""); + createNewTabAction->setToolTip(tr("Create a new file")); + + showTabsMenuAction = addAction(QIcon(IconListPath), ""); + showTabsMenuAction->setToolTip(tr("Show opened files list")); + + const auto tabsMenu = new QMenu(this); + showTabsMenuAction->setMenu(tabsMenu); + + closeCurrentTabAction = addAction(QIcon(IconCrossPath), ""); + closeCurrentTabAction->setToolTip(tr("Close the current file")); + + const auto iconSize = qApp->style()->pixelMetric(QStyle::PM_SmallIconSize); + setIconSize({ iconSize, iconSize }); + setStyleSheet( + "QToolBar { padding: 0px; margin: 0px; }" + "QToolButton::menu-indicator { image: none; }" + ); + + // Trick, cause addWidget will lose some style things + const auto toolButton = qobject_cast(widgetForAction(showTabsMenuAction)); + if (toolButton) toolButton->setPopupMode(QToolButton::InstantPopup); + + connect(createNewTabAction, &QAction::triggered, this, &TabsQuickActionsBar::createNewTabClicked); + connect(tabsMenu, &QMenu::aboutToShow, this, [this, tabsMenu]() { emit tabsMenuAboutToShow(tabsMenu); }); + connect(closeCurrentTabAction, &QAction::triggered, this, &TabsQuickActionsBar::closeCurrentTabClicked); + + setVisibileButtons(visibileButtons); +} + +void TabsQuickActionsBar::setVisibileButtons(const Buttons &buttons) +{ + if (visibileButtons == buttons) + return; + + visibileButtons = buttons; + + const std::map mapping { + { createNewTabAction, CreateNewTab }, + { showTabsMenuAction, ShowTabsMenu }, + { closeCurrentTabAction, CloseCurrentTab } + }; + + for (const auto &pair : mapping) + pair.first->setVisible(buttons.testFlag(pair.second)); + + emit visibileButtonsChanged(buttons); +} + +void TabsQuickActionsBar::setVisibileButton(Button button, bool on) +{ + const auto ¤tOptions = visibileButtons; + setVisibileButtons( + on ? (currentOptions | button) + : (currentOptions & ~button) + ); +} diff --git a/src/widgets/TabsQuickActionsBar.h b/src/widgets/TabsQuickActionsBar.h new file mode 100644 index 000000000..3dc386215 --- /dev/null +++ b/src/widgets/TabsQuickActionsBar.h @@ -0,0 +1,78 @@ +/* + * This file is part of Notepad Next. + * Copyright 2026 Justin Dailey + * + * Notepad Next is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Notepad Next is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Notepad Next. If not, see . + */ + + +#ifndef TABSQUICKACTIONSBAR_H +#define TABSQUICKACTIONSBAR_H + +#include + +class TabsQuickActionsBar : public QToolBar +{ + Q_OBJECT + + Q_PROPERTY( + Buttons visibileButtons + READ getVisibileButtons + WRITE setVisibileButtons + NOTIFY visibileButtonsChanged + ) + +public: + enum Button { + None = 0x00, + CreateNewTab = 0x01, + ShowTabsMenu = 0x02, + CloseCurrentTab = 0x04, + + All = CreateNewTab | + ShowTabsMenu | + CloseCurrentTab + }; + + Q_FLAG(Button) + Q_DECLARE_FLAGS(Buttons, Button) + +signals: + void createNewTabClicked(); + // Tabs menu must be filled with actual data from outside + void tabsMenuAboutToShow(QMenu *menu); + void closeCurrentTabClicked(); + + void visibileButtonsChanged(const TabsQuickActionsBar::Buttons &visibileButtons); + +public: + inline TabsQuickActionsBar(QWidget *parent = nullptr) : TabsQuickActionsBar(Button::All, parent) { } + explicit TabsQuickActionsBar(const Buttons &visibileButtons = Button::All, QWidget *parent = nullptr); + virtual ~TabsQuickActionsBar() = default; + + inline Buttons getVisibileButtons() const { return visibileButtons; } + void setVisibileButtons(const Buttons &buttons); + void setVisibileButton(Button button, bool on = true); + +private: + Buttons visibileButtons = TabsQuickActionsBar::All; + + QAction *createNewTabAction = nullptr; + QAction *showTabsMenuAction = nullptr; + QAction *closeCurrentTabAction = nullptr; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(TabsQuickActionsBar::Buttons); + +#endif // TABSQUICKACTIONSBAR_H