diff --git a/src/lay/lay/gsiDeclLayEditorOptionsPage.cc b/src/lay/lay/gsiDeclLayEditorOptionsPage.cc index b5860e3ef..a59044b35 100644 --- a/src/lay/lay/gsiDeclLayEditorOptionsPage.cc +++ b/src/lay/lay/gsiDeclLayEditorOptionsPage.cc @@ -48,10 +48,20 @@ Class decl_EditorOptionsPageBase (QT_EXTERNAL_BASE (QWid "@brief Sets a flag indicating whether the page is a focus page\n" "The focus page is the page that is selected when the tab key is pressed during some plugin action.\n" ) + - method ("make_current", &lay::EditorOptionsPage::make_current, - "@brief Brings the page to the front of the tab stack\n" - "Calling this method will make this page the current one in the tab stack, provided " - "the page is visible." + method ("is_modal_page?", &lay::EditorOptionsPage::is_modal_page, + "@brief Gets a flag indicating whether the page is a modal page\n" + "See \\modal_page= for a description is this attribute.\n" + ) + + method ("modal_page=", &lay::EditorOptionsPage::set_modal_page, gsi::arg ("flag"), + "@brief Sets a flag indicating whether the page is a modal page\n" + "A modal page is shown in a modal dialog upon \\show. Non-modal pages are shown in the " + "editor options dock.\n" + ) + + method ("show", &lay::EditorOptionsPage::show, + "@brief Shows the page\n" + "Provided the page is selected because the plugin is active, this method will " + "open a dialog to show the page if it is modal, or locate the page in the editor options " + "dock and bring it to the front if it is non-modal." ) + method ("do_apply", &lay::EditorOptionsPage::apply, gsi::arg ("dispatcher"), "@brief Transfers data from the page to the configuration\n" @@ -72,6 +82,9 @@ Class decl_EditorOptionsPageBase (QT_EXTERNAL_BASE (QWid "It features some useful methods such as 'view' and provides a slot to call for triggering a data " "transfer ('edited').\n" "\n" + "Note that even though the page class is derived from QWidget, you can call QWidget methods " + "but not overload virtual methods from QWidget.\n" + "\n" "This class has been introduced in version 0.30.4.\n" ); diff --git a/src/laybasic/laybasic/layEditorOptionsPage.cc b/src/laybasic/laybasic/layEditorOptionsPage.cc index 7ef7ded95..a33d53c4b 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.cc +++ b/src/laybasic/laybasic/layEditorOptionsPage.cc @@ -27,6 +27,9 @@ #include "layEditorOptionsPages.h" #include "layLayoutViewBase.h" +#include +#include + namespace lay { @@ -34,15 +37,20 @@ namespace lay // EditorOptionsPage implementation EditorOptionsPage::EditorOptionsPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) - : QWidget (0), mp_owner (0), m_active (true), m_focus_page (false), mp_plugin_declaration (0), mp_dispatcher (dispatcher), mp_view (view) + : QWidget (0), mp_owner (0), m_active (true), m_focus_page (false), m_modal_page (false), mp_plugin_declaration (0), mp_dispatcher (dispatcher), mp_view (view) { attach_events (); } EditorOptionsPage::EditorOptionsPage () - : QWidget (0), mp_owner (0), m_active (true), m_focus_page (false), mp_plugin_declaration (0), mp_dispatcher (0), mp_view (0) + : QWidget (0), mp_owner (0), m_active (true), m_focus_page (false), m_modal_page (false), mp_plugin_declaration (0), mp_dispatcher (0), mp_view (0) { - // .. nothing here -> call init to set view and dispatcher + // .. nothing yet .. +} + +EditorOptionsPage::~EditorOptionsPage () +{ + set_owner (0); } void @@ -53,16 +61,69 @@ EditorOptionsPage::init (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) attach_events (); } -EditorOptionsPage::~EditorOptionsPage () +void +EditorOptionsPage::edited () { - set_owner (0); + apply (dispatcher ()); +} + +static bool is_parent_widget (QWidget *w, QWidget *parent) +{ + while (w && w != parent) { + w = dynamic_cast (w->parent ()); + } + return w == parent; +} + +bool +EditorOptionsPage::focusNextPrevChild (bool next) +{ + bool res = QWidget::focusNextPrevChild (next); + + // Stop making the focus leave the page - this way we can jump back to the + // view on "enter" + if (res && ! is_modal_page () && ! is_parent_widget (QApplication::focusWidget (), this) && focusWidget ()) { + focusWidget ()->setFocus (); + } + + return res; } void -EditorOptionsPage::make_current () +EditorOptionsPage::keyPressEvent (QKeyEvent *event) +{ + if (! is_modal_page () && event->modifiers () == Qt::NoModifier && event->key () == Qt::Key_Return) { + + // The Return key on a non-modal page commits the values and gives back the focus + // to the view + edited (); + if (view ()->canvas ()->widget ()) { + view ()->canvas ()->widget ()->setFocus (Qt::TabFocusReason); + } + + event->accept (); + + } else { + QWidget::keyPressEvent (event); + } +} + +void +EditorOptionsPage::set_focus () +{ + setFocus (Qt::TabFocusReason); + QWidget::focusNextPrevChild (true); +} + +void +EditorOptionsPage::show () { if (mp_owner && m_active) { - mp_owner->make_page_current (this); + if (! is_modal_page ()) { + mp_owner->make_page_current (this); + } else { + mp_owner->exec_modal (this); + } } } diff --git a/src/laybasic/laybasic/layEditorOptionsPage.h b/src/laybasic/laybasic/layEditorOptionsPage.h index 85b92a57d..50e90eb54 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.h +++ b/src/laybasic/laybasic/layEditorOptionsPage.h @@ -68,11 +68,16 @@ public: bool is_focus_page () const { return m_focus_page; } void set_focus_page (bool f) { m_focus_page = f; } + void set_focus (); + + bool is_modal_page () const { return m_modal_page; } + void set_modal_page (bool f) { m_modal_page = f; } + bool active () const { return m_active; } void activate (bool active); void set_owner (EditorOptionsPages *owner); - void make_current (); + void show (); const lay::PluginDeclaration *plugin_declaration () const { return mp_plugin_declaration; } void set_plugin_declaration (const lay::PluginDeclaration *pd) { mp_plugin_declaration = pd; } @@ -90,19 +95,20 @@ public: } protected slots: - void edited () - { - apply (dispatcher ()); - } + void edited (); protected: virtual void active_cellview_changed () { } virtual void technology_changed (const std::string & /*tech*/) { } + virtual bool focusNextPrevChild (bool next); + virtual void keyPressEvent (QKeyEvent *event); + private: EditorOptionsPages *mp_owner; bool m_active; bool m_focus_page; + bool m_modal_page; const lay::PluginDeclaration *mp_plugin_declaration; lay::Dispatcher *mp_dispatcher; lay::LayoutViewBase *mp_view; diff --git a/src/laybasic/laybasic/layEditorOptionsPages.cc b/src/laybasic/laybasic/layEditorOptionsPages.cc index 738528d27..e1efbc650 100644 --- a/src/laybasic/laybasic/layEditorOptionsPages.cc +++ b/src/laybasic/laybasic/layEditorOptionsPages.cc @@ -34,6 +34,8 @@ #include #include #include +#include +#include namespace lay { @@ -52,6 +54,8 @@ struct EOPCompareOp EditorOptionsPages::EditorOptionsPages (QWidget *parent, const std::vector &pages, lay::Dispatcher *dispatcher) : QFrame (parent), mp_dispatcher (dispatcher) { + mp_modal_pages = new EditorOptionsModalPages (this); + QVBoxLayout *ly1 = new QVBoxLayout (this); ly1->setContentsMargins (0, 0, 0, 0); @@ -73,6 +77,9 @@ EditorOptionsPages::~EditorOptionsPages () while (m_pages.size () > 0) { delete m_pages.front (); } + + delete mp_modal_pages; + mp_modal_pages = 0; } void @@ -84,17 +91,51 @@ EditorOptionsPages::focusInEvent (QFocusEvent * /*event*/) } } -bool EditorOptionsPages::has_content () const +bool +EditorOptionsPages::has_content () const { for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - if ((*p)->active ()) { + if ((*p)->active () && ! (*p)->is_modal_page ()) { return true; } } return false; } -void EditorOptionsPages::activate (const lay::Plugin *plugin) +bool +EditorOptionsPages::has_modal_content () const +{ + for (std::vector ::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) { + if ((*p)->active () && (*p)->is_modal_page ()) { + return true; + } + } + return false; +} + +bool +EditorOptionsPages::exec_modal (EditorOptionsPage *page) +{ + QTabWidget *modal_pages = mp_modal_pages->pages_widget (); + + for (int i = 0; i < modal_pages->count (); ++i) { + + if (modal_pages->widget (i) == page) { + + // found the page - make it current and show the dialog + modal_pages->setCurrentIndex (i); + page->set_focus (); + return mp_modal_pages->exec () != 0; + + } + + } + + return false; +} + +void +EditorOptionsPages::activate (const lay::Plugin *plugin) { for (auto op = m_pages.begin (); op != m_pages.end (); ++op) { bool is_active = false; @@ -126,6 +167,7 @@ EditorOptionsPages::make_page_current (lay::EditorOptionsPage *page) for (int i = 0; i < mp_pages->count (); ++i) { if (mp_pages->widget (i) == page) { mp_pages->setCurrentIndex (i); + page->set_focus (); break; } } @@ -151,6 +193,8 @@ EditorOptionsPages::update (lay::EditorOptionsPage *page) std::vector sorted_pages = m_pages; std::sort (sorted_pages.begin (), sorted_pages.end (), EOPCompareOp ()); + QTabWidget *modal_pages = mp_modal_pages->pages_widget (); + if (! page && m_pages.size () > 0) { page = m_pages.back (); } @@ -158,17 +202,39 @@ EditorOptionsPages::update (lay::EditorOptionsPage *page) while (mp_pages->count () > 0) { mp_pages->removeTab (0); } + + while (modal_pages->count () > 0) { + modal_pages->removeTab (0); + } + int index = -1; + int modal_index = -1; + for (std::vector ::iterator p = sorted_pages.begin (); p != sorted_pages.end (); ++p) { if ((*p)->active ()) { - if ((*p) == page) { - index = mp_pages->count (); + if (! (*p)->is_modal_page ()) { + if ((*p) == page) { + index = mp_pages->count (); + } + mp_pages->addTab (*p, tl::to_qstring ((*p)->title ())); + } else { + if ((*p) == page) { + modal_index = modal_pages->count (); + } + modal_pages->addTab (*p, tl::to_qstring ((*p)->title ())); } - mp_pages->addTab (*p, tl::to_qstring ((*p)->title ())); } else { (*p)->setParent (0); } } + + lay::EditorOptionsPage *single_modal_page = modal_pages->count () == 1 ? dynamic_cast (modal_pages->widget (0)) : 0; + if (single_modal_page) { + mp_modal_pages->setWindowTitle (tl::to_qstring (single_modal_page->title ())); + } else { + mp_modal_pages->setWindowTitle (tr ("Editor Options")); + } + if (index < 0) { index = mp_pages->currentIndex (); } @@ -177,27 +243,33 @@ EditorOptionsPages::update (lay::EditorOptionsPage *page) } mp_pages->setCurrentIndex (index); + if (modal_index < 0) { + modal_index = modal_pages->currentIndex (); + } + if (modal_index >= int (modal_pages->count ())) { + modal_index = modal_pages->count () - 1; + } + modal_pages->setCurrentIndex (modal_index); + setVisible (mp_pages->count () > 0); } void EditorOptionsPages::setup () { - try { +BEGIN_PROTECTED - for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - if ((*p)->active ()) { - (*p)->setup (mp_dispatcher); - } + for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { + if ((*p)->active ()) { + (*p)->setup (mp_dispatcher); } - - // make the display consistent with the status (this is important for - // PCell parameters where the PCell may be asked to modify the parameters) - do_apply (); - - } catch (...) { - // catch any errors related to configuration file errors etc. } + + // make the display consistent with the status (this is important for + // PCell parameters where the PCell may be asked to modify the parameters) + do_apply (); + +END_PROTECTED_W (this) } void @@ -219,6 +291,54 @@ BEGIN_PROTECTED END_PROTECTED_W (this) } +// ------------------------------------------------------------------ +// EditorOptionsModalPages implementation + +EditorOptionsModalPages::EditorOptionsModalPages (EditorOptionsPages *parent) + : QDialog (parent), mp_parent (parent) +{ + QVBoxLayout *ly = new QVBoxLayout (this); + + mp_pages = new QTabWidget (this); + ly->addWidget (mp_pages, 1); + mp_pages->setTabBarAutoHide (true); + + mp_button_box = new QDialogButtonBox (this); + ly->addWidget (mp_button_box); + mp_button_box->setOrientation (Qt::Horizontal); + mp_button_box->setStandardButtons (QDialogButtonBox::Cancel | QDialogButtonBox::Apply | QDialogButtonBox::Ok); + + connect (mp_button_box, SIGNAL (clicked(QAbstractButton *)), this, SLOT (clicked(QAbstractButton *))); + connect (mp_button_box, SIGNAL (accepted()), this, SLOT (accept())); + connect (mp_button_box, SIGNAL (rejected()), this, SLOT (reject())); +} + +EditorOptionsModalPages::~EditorOptionsModalPages () +{ + // .. nothing yet .. +} + +void +EditorOptionsModalPages::accept () +{ + mp_parent->apply (); + QDialog::accept (); +} + +void +EditorOptionsModalPages::reject () +{ + QDialog::reject (); +} + +void +EditorOptionsModalPages::clicked (QAbstractButton *button) +{ + if (button == mp_button_box->button (QDialogButtonBox::Apply)) { + mp_parent->apply (); + } +} + } #endif diff --git a/src/laybasic/laybasic/layEditorOptionsPages.h b/src/laybasic/laybasic/layEditorOptionsPages.h index 10024e982..b549e1dd6 100644 --- a/src/laybasic/laybasic/layEditorOptionsPages.h +++ b/src/laybasic/laybasic/layEditorOptionsPages.h @@ -28,14 +28,16 @@ #include "laybasicCommon.h" #include "layEditorOptionsPage.h" -#include - #include +#include + #include #include class QTabWidget; class QLabel; +class QDialogButtonBox; +class QAbstractButton; namespace lay { @@ -43,9 +45,10 @@ namespace lay class PluginDeclaration; class Dispatcher; class Plugin; +class EditorOptionsModalPages; /** - * @brief The object properties dialog + * @brief The object properties tab widget */ class LAYBASIC_PUBLIC EditorOptionsPages : public QFrame @@ -61,6 +64,7 @@ public: void activate (const lay::Plugin *plugin); void focusInEvent (QFocusEvent *event); void make_page_current (lay::EditorOptionsPage *page); + bool exec_modal (lay::EditorOptionsPage *page); const std::vector &pages () const { @@ -68,6 +72,7 @@ public: } bool has_content () const; + bool has_modal_content () const; public slots: void apply (); @@ -77,11 +82,40 @@ private: std::vector m_pages; lay::Dispatcher *mp_dispatcher; QTabWidget *mp_pages; + EditorOptionsModalPages *mp_modal_pages; void update (lay::EditorOptionsPage *page); void do_apply (); }; +/** + * @brief The object properties modal page dialog + */ +class LAYBASIC_PUBLIC EditorOptionsModalPages + : public QDialog +{ +Q_OBJECT + +public: + EditorOptionsModalPages (EditorOptionsPages *parent); + ~EditorOptionsModalPages (); + + QTabWidget *pages_widget () + { + return mp_pages; + } + +private slots: + void accept (); + void reject (); + void clicked (QAbstractButton *button); + +private: + EditorOptionsPages *mp_parent; + QTabWidget *mp_pages; + QDialogButtonBox *mp_button_box; +}; + } #endif diff --git a/src/laybasic/laybasic/layEditorServiceBase.cc b/src/laybasic/laybasic/layEditorServiceBase.cc index e20f200a5..e28e1ba5e 100644 --- a/src/laybasic/laybasic/layEditorServiceBase.cc +++ b/src/laybasic/laybasic/layEditorServiceBase.cc @@ -367,8 +367,7 @@ EditorServiceBase::key_event (unsigned int key, unsigned int buttons) auto pages = editor_options_pages (); for (auto p = pages.begin (); p != pages.end (); ++p) { if ((*p)->is_focus_page ()) { - (*p)->make_current (); - (*p)->setFocus (Qt::TabFocusReason); + (*p)->show (); return true; } }