From ef6b0f52d7163b6897f4b4387a96176e44c1b457 Mon Sep 17 00:00:00 2001 From: Eugene Zelenko Date: Thu, 22 Sep 2022 17:43:32 -0700 Subject: [PATCH] Use enhanced tab widget for file tabs to easily navigate through large number of opened files. --- src/lay/lay/lay.pro | 2 + src/lay/lay/layEnhancedTabWidget.cc | 103 ++++++++++++++++++++++++++++ src/lay/lay/layEnhancedTabWidget.h | 62 +++++++++++++++++ src/lay/lay/layMainWindow.cc | 41 +++++------ src/lay/lay/layMainWindow.h | 60 ++++++++-------- 5 files changed, 218 insertions(+), 50 deletions(-) create mode 100644 src/lay/lay/layEnhancedTabWidget.cc create mode 100644 src/lay/lay/layEnhancedTabWidget.h diff --git a/src/lay/lay/lay.pro b/src/lay/lay/lay.pro index 8bd49c256..f9065ad92 100644 --- a/src/lay/lay/lay.pro +++ b/src/lay/lay/lay.pro @@ -11,6 +11,7 @@ HEADERS = \ layClipDialog.h \ layControlWidgetStack.h \ layCrashMessage.h \ + layEnhancedTabWidget.h \ layFillDialog.h \ layGSIHelpProvider.h \ layHelpAboutDialog.h \ @@ -121,6 +122,7 @@ SOURCES = \ layClipDialog.cc \ layControlWidgetStack.cc \ layCrashMessage.cc \ + layEnhancedTabWidget.cc \ layFillDialog.cc \ layGSIHelpProvider.cc \ layHelpAboutDialog.cc \ diff --git a/src/lay/lay/layEnhancedTabWidget.cc b/src/lay/lay/layEnhancedTabWidget.cc new file mode 100644 index 000000000..83804d442 --- /dev/null +++ b/src/lay/lay/layEnhancedTabWidget.cc @@ -0,0 +1,103 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2022 Matthias Koefferlein + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "layEnhancedTabWidget.h" + +#include +#include +#include +#include +#include + +namespace lay +{ + +// --------------------------------------------------------------------------------------------- +// EnhancedTabWidget implementation + +EnhancedTabWidget::EnhancedTabWidget (QWidget *parent) + : QTabWidget (parent) +{ + mp_list_tool_button = new QToolButton (this); + mp_list_tool_button->setAutoRaise (true); + ///\TODO: Replace placeholder with real icon. + mp_list_tool_button->setIcon (QIcon (QString::fromUtf8 (":/find_16px.png"))); + mp_list_tool_button->setIconSize (QSize (20, 20)); + mp_list_tool_button->setMenu (new QMenu (this)); + mp_list_tool_button->setPopupMode (QToolButton::InstantPopup); + mp_list_tool_button->setToolButtonStyle (Qt::ToolButtonIconOnly); + mp_list_tool_button->setToolTip ( tr ("List of all opened views")); + setCornerWidget (mp_list_tool_button, Qt::TopRightCorner); + + connect (mp_list_tool_button->menu (), &QMenu::aboutToShow, + this, &EnhancedTabWidget::list_tool_button_menu_about_to_show); + + mp_list_action_group = new QActionGroup (this); + mp_list_action_group->setExclusive (true); + + connect (mp_list_action_group, &QActionGroup::triggered, + this, &EnhancedTabWidget::list_action_group_triggered); +} + +EnhancedTabWidget::~EnhancedTabWidget () = default; + +void EnhancedTabWidget::tabInserted (int index) +{ + QTabWidget::tabInserted (index); + update_list_button_visibility (); +} + +void EnhancedTabWidget::tabRemoved (int index) +{ + QTabWidget::tabRemoved (index); + update_list_button_visibility (); +} + +void EnhancedTabWidget::list_action_group_triggered (QAction *action) +{ + setCurrentIndex (action->data ().toInt ()); +} + +void EnhancedTabWidget::list_tool_button_menu_about_to_show () +{ + mp_list_tool_button->menu ()->clear (); + if (count () > 1) { + for (int i = 0; i < count (); ++i) { + QAction *action = mp_list_tool_button->menu ()->addAction (tabText (i)); + action->setCheckable (true); + action->setData (QVariant (i)); + mp_list_action_group->addAction (action); + } + mp_list_action_group->actions ().at (currentIndex ())->setChecked (true); + } +} + +void EnhancedTabWidget::update_list_button_visibility() +{ + if (cornerWidget (Qt::TopRightCorner) != nullptr) { + cornerWidget (Qt::TopRightCorner)->setVisible (count () > 1); + } +} + +} + diff --git a/src/lay/lay/layEnhancedTabWidget.h b/src/lay/lay/layEnhancedTabWidget.h new file mode 100644 index 000000000..a88c34b85 --- /dev/null +++ b/src/lay/lay/layEnhancedTabWidget.h @@ -0,0 +1,62 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2022 Matthias Koefferlein + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_layEnhancedTabWidget +#define HDR_layEnhancedTabWidget + +#include + +class QActionGroup; +class QToolButton; + +namespace lay +{ + +class EnhancedTabWidget + : public QTabWidget +{ +Q_OBJECT + +public: + EnhancedTabWidget (QWidget *parent); + ~EnhancedTabWidget () override; + +protected: + void tabInserted (int index) override; + void tabRemoved (int index) override; + +private slots: + void list_action_group_triggered (QAction* action); + void list_tool_button_menu_about_to_show (); + +private: + QActionGroup *mp_list_action_group; + QToolButton *mp_list_tool_button; + + void update_list_button_visibility (); +}; + +} + +#endif + diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index ff2ec5db3..be44fecd3 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -70,6 +70,7 @@ #include "layDialogs.h" #include "laybasicConfig.h" #include "layConfig.h" +#include "layEnhancedTabWidget.h" #include "layMainWindow.h" #include "layHelpDialog.h" #include "layNavigator.h" @@ -203,7 +204,7 @@ MainWindow::MainWindow (QApplication *app, const char *name, bool undo_enabled) init_menu (); mp_assistant = 0; - + m_always_exit_without_saving = false; mp_pr = new lay::ProgressReporter (); @@ -227,12 +228,12 @@ MainWindow::MainWindow (QApplication *app, const char *name, bool undo_enabled) vbl->setContentsMargins (0, 0, 0, 0); vbl->setSpacing (0); - mp_tab_bar = new QTabBar (mp_main_frame); - vbl->addWidget (mp_tab_bar); - connect (mp_tab_bar, SIGNAL (currentChanged (int)), this, SLOT (view_selected (int))); + mp_tab_widget = new EnhancedTabWidget (mp_main_frame); + vbl->addWidget (mp_tab_widget); + connect (mp_tab_widget, SIGNAL (currentChanged (int)), this, SLOT (view_selected (int))); #if QT_VERSION >= 0x040500 - mp_tab_bar->setTabsClosable(true); - connect (mp_tab_bar, SIGNAL (tabCloseRequested (int)), this, SLOT (tab_close_requested (int))); + mp_tab_widget->setTabsClosable(true); + connect (mp_tab_widget, SIGNAL (tabCloseRequested (int)), this, SLOT (tab_close_requested (int))); #endif mp_hp_dock_widget = new QDockWidget (QObject::tr ("Cells"), this); @@ -682,8 +683,8 @@ MainWindow::close_all () // Clear the tab bar bool f = m_disable_tab_selected; m_disable_tab_selected = true; - while (mp_tab_bar->count () > 0) { - mp_tab_bar->removeTab (mp_tab_bar->count () - 1); + while (mp_tab_widget->count () > 0) { + mp_tab_widget->removeTab (mp_tab_widget->count () - 1); } m_disable_tab_selected = f; @@ -745,8 +746,8 @@ MainWindow::about_to_exec () // TODO: later, each view may get it's own editable flag if (lay::ApplicationBase::instance () && !lay::ApplicationBase::instance ()->is_editable ()) { - TipDialog td (this, - tl::to_string (QObject::tr ("KLayout has been started in viewer mode. In this mode, editor functions are not available.\n\nTo enable these functions, start KLayout in editor mode by using the \"-e\" command line switch or select it as the default mode in the setup dialog. Choose \"Setup\" in the \"File\" menu and check \"Use editing mode by default\" on the \"Editing Mode\" page in the \"Application\" section.")), + TipDialog td (this, + tl::to_string (QObject::tr ("KLayout has been started in viewer mode. In this mode, editor functions are not available.\n\nTo enable these functions, start KLayout in editor mode by using the \"-e\" command line switch or select it as the default mode in the setup dialog. Choose \"Setup\" in the \"File\" menu and check \"Use editing mode by default\" on the \"Editing Mode\" page in the \"Application\" section.")), "editor-mode"); if (td.exec_dialog ()) { // Don't bother the user with more dialogs. @@ -2314,7 +2315,7 @@ MainWindow::view_selected (int index) // Hint: setting the focus to the tab bar avoids problem with dangling keyboard focus. // Sometimes, the focus was set to the hierarchy level spin buttons which caught Copy&Paste // events in effect. - mp_tab_bar->setFocus (); + mp_tab_widget->setFocus (); if (! m_disable_tab_selected) { select_view (index); @@ -2335,7 +2336,7 @@ MainWindow::select_view (int index) tl_assert (index >= 0 && index < int (views ())); - mp_tab_bar->setCurrentIndex (index); + mp_tab_widget->setCurrentIndex (index); bool box_set = (m_synchronized_views && current_view () != 0); db::DBox box; @@ -2564,7 +2565,7 @@ MainWindow::clone_current_view () bool f = m_disable_tab_selected; m_disable_tab_selected = true; - int index = mp_tab_bar->insertTab (-1, tl::to_qstring (view->title ())); + int index = mp_tab_widget->insertTab (-1, mp_views.back (), tl::to_qstring (view->title ())); m_disable_tab_selected = f; view_created_event (index); @@ -2805,7 +2806,7 @@ MainWindow::close_view (int index) box = view (index)->viewport ().box (); } - mp_tab_bar->removeTab (index); + mp_tab_widget->removeTab (index); mp_view_stack->remove_widget (index); mp_lp_stack->remove_widget (index); mp_layer_toolbox_stack->remove_widget (index); @@ -3381,7 +3382,7 @@ MainWindow::create_view () bool f = m_disable_tab_selected; m_disable_tab_selected = true; - int index = mp_tab_bar->insertTab (-1, tl::to_qstring (current_view ()->title ())); + int index = mp_tab_widget->insertTab (-1, mp_views.back (), tl::to_qstring (current_view ()->title ())); m_disable_tab_selected = f; view_created_event (index); @@ -3444,7 +3445,7 @@ MainWindow::create_or_load_layout (const std::string *filename, const db::LoadLa bool f = m_disable_tab_selected; m_disable_tab_selected = true; - int index = mp_tab_bar->insertTab (-1, QString ()); + int index = mp_tab_widget->insertTab (-1, mp_views.back (), QString ()); update_tab_title (index); m_disable_tab_selected = f; view_created_event (index); @@ -3484,8 +3485,8 @@ MainWindow::update_tab_title (int i) title += v->title (); } - if (tl::to_string (mp_tab_bar->tabText (i)) != title) { - mp_tab_bar->setTabText (i, tl::to_qstring (title)); + if (tl::to_string (mp_tab_widget->tabText (i)) != title) { + mp_tab_widget->setTabText (i, tl::to_qstring (title)); } if (v) { @@ -3500,8 +3501,8 @@ MainWindow::update_tab_title (int i) files += tl::to_string (tr ("(not saved)")); } } - if (tl::to_string (mp_tab_bar->tabToolTip (i)) != files) { - mp_tab_bar->setTabToolTip (i, tl::to_qstring (files)); + if (tl::to_string (mp_tab_widget->tabToolTip (i)) != files) { + mp_tab_widget->setTabToolTip (i, tl::to_qstring (files)); } } } diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index c2a34468d..086ced6fb 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -51,7 +51,7 @@ #include "tlDeferredExecution.h" #include "gsi.h" -class QTabBar; +class QTabWidget; class QToolBar; class QLabel; class QAction; @@ -110,7 +110,7 @@ public: */ MainWindow (QApplication *app = 0, const char *name = "main_window", bool undo_enabled = true); - /** + /** * @brief Destructor */ ~MainWindow (); @@ -137,7 +137,7 @@ public: void close_all (); /** - * @brief Create a new view + * @brief Create a new view * * @return The index of the newly created view */ @@ -147,7 +147,7 @@ public: * @brief Startup behaviour immediately before the application is executed. * * This method will be called shortly the before the application is executed. - * It is supposed to perform some startup procedure, i.e. displaying tip-of-the-day + * It is supposed to perform some startup procedure, i.e. displaying tip-of-the-day * windows. */ void about_to_exec (); @@ -167,14 +167,14 @@ public: */ void restore_session (const std::string &fn); - /** + /** * @brief Reload the given cellview into the current view * * The cellview is given by index in the current view's cellview list. */ void reload_layout (unsigned int cv_index); - /** + /** * @brief Load a (new) file into the layout * * The mode param controls how to open the file: @@ -185,7 +185,7 @@ public: return load_layout (filename, m_initial_technology, mode); } - /** + /** * @brief Load a (new) file into the layout and associate it with the given technology * * The mode param controls how to open the file: @@ -196,7 +196,7 @@ public: return load_layout (filename, db::Technologies::instance ()->technology_by_name (technology)->load_layout_options (), technology, mode); } - /** + /** * @brief Load a (new) file into the layout using the given options * * The mode param controls how to open the file: @@ -207,7 +207,7 @@ public: return load_layout (filename, options, m_initial_technology, mode); } - /** + /** * @brief Load a (new) file into the layout using the given options and technology * * The mode param controls how to open the file: @@ -215,7 +215,7 @@ public: */ lay::CellViewRef load_layout (const std::string &filename, const db::LoadLayoutOptions &options, const std::string &technology, int mode = 0); - /** + /** * @brief Create new, empty layout * * The mode param controls how to create the layout @@ -226,7 +226,7 @@ public: return create_layout (m_initial_technology, mode); } - /** + /** * @brief Create new, empty layout and associate it with the given technology * * The mode param controls how to create the layout @@ -244,33 +244,33 @@ public: * * This version will load the layer properties file "as it is". No mapping of cellview index * is performed. Only adding of missing layers is supported. - * The file name can be an empty string. + * The file name can be an empty string. * - * @param fn The name of the .lyp file to load. + * @param fn The name of the .lyp file to load. * @param all_views Apply the .lyp file to all views. If false, load the layer properties for the current view. * @param add_default Add the missing layers for each view. */ void load_layer_properties (const std::string &fn, bool all_views, bool add_default); /** - * @brief Load a layer definition file for a specific cv + * @brief Load a layer definition file for a specific cv * * This version will load the layer properties file and apply it to the given cv. The cv_index - * parameter will denote the cv index to which to apply the layer properties file. It is assumed + * parameter will denote the cv index to which to apply the layer properties file. It is assumed * that the .lyp file will contain definitions for a single layout only. This definition is - * mapped to the specified cv_index after all definitions for this layout have been removed + * mapped to the specified cv_index after all definitions for this layout have been removed * from the layer properties list. - * "cv_index" can be -1. In that case, the layer properties file will be loaded multiple times, + * "cv_index" can be -1. In that case, the layer properties file will be loaded multiple times, * once for each cv present. * - * @param fn The name of the .lyp file to load. + * @param fn The name of the .lyp file to load. * @param cv_index See above * @param all_views Apply the .lyp file to all views. If false, load the layer properties for the current view. * @param add_default Add the missing layers for each view. */ void load_layer_properties (const std::string &fn, int cv_index, bool all_views, bool add_default); - /** + /** * @brief Grid accessor: get the grid value * * This is the version delivering a double value in micron. @@ -367,12 +367,12 @@ public: */ bool show_progress_bar (bool show); - /** + /** * @brief Implementation of the Plugin interface */ bool configure (const std::string &name, const std::string &value); - /** + /** * @brief Implementation of the Plugin interface */ void config_finalize (); @@ -383,7 +383,7 @@ public: * This method returns true if exit () has been called. This state is never reset. * The main purpose is to detect "exit ()" calls before the event loop is executed. */ - bool exited () + bool exited () { return m_exited; } @@ -393,9 +393,9 @@ public: * * In busy state, the main window will not try to ask for saving changes etc. * Instead, it will ask whether to shut down the application anyway. - * The intention of the busy state is to implement a special behavior while inside a processEvent + * The intention of the busy state is to implement a special behavior while inside a processEvent * loop. - */ + */ void enter_busy_mode (bool bm) { m_busy = bm; @@ -485,14 +485,14 @@ public: * @brief Show the assistant with the given URL */ void show_assistant_url (const std::string &url, bool modal = false); - + /** * @brief Show the assistant with the given topic */ void show_assistant_topic (const std::string &s, bool modal = false); /** - * @brief Show the macro editor + * @brief Show the macro editor * * The category can be given to specify the category to show. If empty, the current category * is shown. @@ -522,7 +522,7 @@ public: * @brief Open a new layout in mode 'mode' * * If mode is 2, the layout is opened in the current view in addition to the existing ones. - * If mode is 1, the layout is opened in a new view. If mode is 0, the layout is opened in + * If mode is 1, the layout is opened in a new view. If mode is 0, the layout is opened in * the current view deleting the existing ones. */ void open (int mode); @@ -572,7 +572,7 @@ public: /** * @brief Gets the technology used for loading or creating layouts */ - const std::string &initial_technology () + const std::string &initial_technology () { return m_initial_technology; } @@ -615,7 +615,7 @@ public slots: void message (const std::string &s, int ms); /** - * @brief Clears the current message + * @brief Clears the current message */ void clear_message (); @@ -682,7 +682,7 @@ private: TextProgressDelegate m_text_progress; // Main menu - QTabBar *mp_tab_bar; + QTabWidget *mp_tab_widget; QToolBar *mp_tool_bar; QDockWidget *mp_navigator_dock_widget; lay::Navigator *mp_navigator;