diff --git a/src/lay/lay/gsiDeclLayEditorOptionsPage.cc b/src/lay/lay/gsiDeclLayEditorOptionsPage.cc index a59044b35..c6bc94017 100644 --- a/src/lay/lay/gsiDeclLayEditorOptionsPage.cc +++ b/src/lay/lay/gsiDeclLayEditorOptionsPage.cc @@ -59,19 +59,16 @@ Class decl_EditorOptionsPageBase (QT_EXTERNAL_BASE (QWid ) + method ("show", &lay::EditorOptionsPage::show, "@brief Shows the page\n" + "@return A value indicating whether the page was opened non-modal (-1), accepted (1) or rejected (0)\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"), + method ("apply", &lay::EditorOptionsPage::apply, gsi::arg ("dispatcher"), "@brief Transfers data from the page to the configuration\n" - "Calling this method will call the actual 'apply' implementation which is " - "provided by a reimplementation - either on C++ or script side." ) + - method ("do_setup", &lay::EditorOptionsPage::setup, gsi::arg ("dispatcher"), + method ("setup", &lay::EditorOptionsPage::setup, gsi::arg ("dispatcher"), "@brief Transfers data from the configuration to the page\n" - "Calling this method will call the actual 'setup' implementation which is " - "provided by a reimplementation - either on C++ or script side." ), "@brief The plugin framework's editor options page base class\n" "\n" @@ -100,6 +97,16 @@ EditorOptionsPageImpl::call_edited () lay::EditorOptionsPage::edited (); } +static void apply_fb (EditorOptionsPageImpl *ep, lay::Dispatcher *root) +{ + ep->lay::EditorOptionsPage::apply (root); +} + +static void setup_fb (EditorOptionsPageImpl *ep, lay::Dispatcher *root) +{ + ep->lay::EditorOptionsPage::setup (root); +} + void EditorOptionsPageImpl::apply_impl (lay::Dispatcher *root) { @@ -148,11 +155,15 @@ Class decl_EditorOptionsPage (decl_EditorOptionsPageBase, "When some entry widget (for example 'editingFinished' slot of a QLineEdit), " "call this method to initiate a transfer of information from the page to the plugin.\n" ) + + // prevents infinite recursion + method_ext ("apply", &apply_fb, gsi::arg ("dispatcher"), "@hide") + callback ("apply", &EditorOptionsPageImpl::apply, &EditorOptionsPageImpl::f_apply, gsi::arg ("dispatcher"), "@brief Reimplement this method to transfer data from the page to the configuration\n" "In this method, you should transfer all widget data into corresponding configuration updates.\n" "Use \\Dispatcher#set_config on the dispatcher object ('dispatcher' argument) to set a configuration parameter.\n" ) + + // prevents infinite recursion + method_ext ("setup", &setup_fb, gsi::arg ("dispatcher"), "@hide") + callback ("setup", &EditorOptionsPageImpl::setup, &EditorOptionsPageImpl::f_setup, gsi::arg ("dispatcher"), "@brief Reimplement this method to transfer data from the configuration to the page\n" "In this method, you should transfer all configuration data to the widgets.\n" diff --git a/src/lay/lay/gsiDeclLayEditorOptionsPage.h b/src/lay/lay/gsiDeclLayEditorOptionsPage.h index f778150bd..0e1598898 100644 --- a/src/lay/lay/gsiDeclLayEditorOptionsPage.h +++ b/src/lay/lay/gsiDeclLayEditorOptionsPage.h @@ -51,9 +51,7 @@ public: } void call_edited (); - void apply_impl (lay::Dispatcher *root); virtual void apply (lay::Dispatcher *root); - void setup_impl (lay::Dispatcher *root); virtual void setup (lay::Dispatcher *root); gsi::Callback f_apply; @@ -64,6 +62,9 @@ private: tl::weak_ptr mp_dispatcher; std::string m_title; int m_index; + + void apply_impl (lay::Dispatcher *root); + void setup_impl (lay::Dispatcher *root); }; } diff --git a/src/lay/lay/gsiDeclLayPlugin.cc b/src/lay/lay/gsiDeclLayPlugin.cc index 1d055a537..88050e125 100644 --- a/src/lay/lay/gsiDeclLayPlugin.cc +++ b/src/lay/lay/gsiDeclLayPlugin.cc @@ -646,6 +646,15 @@ PluginImpl::tracking_position () const } } +int PluginImpl::focus_page_open(lay::EditorOptionsPage *fp) +{ + if (f_focus_page_open.can_issue ()) { + return f_focus_page_open.issue (&lay::EditorServiceBase::focus_page_open, fp); + } else { + return lay::EditorServiceBase::focus_page_open (fp); + } +} + lay::angle_constraint_type PluginImpl::connect_ac (lay::angle_constraint_type ac) const { @@ -970,6 +979,22 @@ Class decl_Plugin (decl_PluginBase, "lay", "Plugin", "\n" "This method has been added in version 0.30.4." ) + + gsi::method ("focus_page", &gsi::PluginImpl::focus_page, + "@brief Gets the (first) focus page\n" + "Focus pages are editor options pages that have a true value for \\EditorOptionsPage#is_focus_page.\n" + "The pages can be navigated to quickly or can be shown in a modal dialog from the editor function.\n" + "This method returns the first focus page present in the editor options pages stack.\n" + "\n" + "This method has been added in version 0.30.4." + ) + + callback ("focus_page_open", &gsi::PluginImpl::focus_page_open, &gsi::PluginImpl::f_focus_page_open, gsi::arg ("focus_page"), + "@brief Gets called when the focus page wants to be opened - i.e. if 'Tab' is pressed during editing\n" + "The default implementation calls \\EditorOptionsPage#show. This method can be overloaded to provide certain actions before " + "or after the page is shown, specifically if the page is a modal one. For example, it can update the page with current " + "dimensions of a shape that is created and after committing the page, adjust the shape accordingly.\n" + "\n" + "This method has been added in version 0.30.4." + ) + #endif gsi::method ("view", &gsi::PluginImpl::view, "@brief Gets the view object the plugin is associated with\n" diff --git a/src/lay/lay/gsiDeclLayPlugin.h b/src/lay/lay/gsiDeclLayPlugin.h index 52bd3b448..9fcd8a1bc 100644 --- a/src/lay/lay/gsiDeclLayPlugin.h +++ b/src/lay/lay/gsiDeclLayPlugin.h @@ -95,6 +95,8 @@ public: db::DPoint tracking_position_test () const; virtual db::DPoint tracking_position () const; + virtual int focus_page_open (lay::EditorOptionsPage *fp); + virtual lay::ViewService *view_service_interface () { return this; @@ -128,6 +130,7 @@ public: gsi::Callback f_update; gsi::Callback f_has_tracking_position; gsi::Callback f_tracking_position; + gsi::Callback f_focus_page_open; private: tl::weak_ptr mp_view; diff --git a/src/lay/lay/lay.pro b/src/lay/lay/lay.pro index 4a920fc86..9ccbc0867 100644 --- a/src/lay/lay/lay.pro +++ b/src/lay/lay/lay.pro @@ -121,7 +121,6 @@ FORMS = \ SOURCES = \ gsiDeclLayApplication.cc \ gsiDeclLayConfigPage.cc \ - gsiDeclLayDispatcher.cc \ gsiDeclLayEditorOptionsPage.cc \ gsiDeclLayHelpDialog.cc \ gsiDeclLayMainWindow.cc \ diff --git a/src/lay/lay/gsiDeclLayDispatcher.cc b/src/laybasic/laybasic/gsiDeclLayDispatcher.cc similarity index 100% rename from src/lay/lay/gsiDeclLayDispatcher.cc rename to src/laybasic/laybasic/gsiDeclLayDispatcher.cc diff --git a/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc b/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc index 15925208b..3306852bc 100644 --- a/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc +++ b/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc @@ -497,7 +497,9 @@ static bool view_is_dirty (lay::LayoutViewBase *view) return view->is_dirty (); } -LAYBASIC_PUBLIC Class decl_LayoutViewBase ("lay", "LayoutViewBase", +extern Class decl_Dispatcher; + +LAYBASIC_PUBLIC Class decl_LayoutViewBase (decl_Dispatcher, "lay", "LayoutViewBase", gsi::constant ("LV_NoLayers", (unsigned int) lay::LayoutViewBase::LV_NoLayers, "@brief With this option, no layers view will be provided (see \\layer_control_frame)\n" "Use this value with the constructor's 'options' argument.\n" diff --git a/src/laybasic/laybasic/layEditorOptionsPage.cc b/src/laybasic/laybasic/layEditorOptionsPage.cc index a33d53c4b..0a31eb3eb 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.cc +++ b/src/laybasic/laybasic/layEditorOptionsPage.cc @@ -26,6 +26,7 @@ #include "layEditorOptionsPage.h" #include "layEditorOptionsPages.h" #include "layLayoutViewBase.h" +#include "tlExceptions.h" #include #include @@ -92,20 +93,19 @@ EditorOptionsPage::focusNextPrevChild (bool next) void EditorOptionsPage::keyPressEvent (QKeyEvent *event) { +BEGIN_PROTECTED 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 (); + apply (dispatcher ()); if (view ()->canvas ()->widget ()) { view ()->canvas ()->widget ()->setFocus (Qt::TabFocusReason); } - event->accept (); - } else { QWidget::keyPressEvent (event); } +END_PROTECTED } void @@ -115,14 +115,15 @@ EditorOptionsPage::set_focus () QWidget::focusNextPrevChild (true); } -void +int EditorOptionsPage::show () { if (mp_owner && m_active) { if (! is_modal_page ()) { mp_owner->make_page_current (this); + return -1; } else { - mp_owner->exec_modal (this); + return mp_owner->exec_modal (this) ? 1 : 0; } } } diff --git a/src/laybasic/laybasic/layEditorOptionsPage.h b/src/laybasic/laybasic/layEditorOptionsPage.h index 50e90eb54..33b9fc1d3 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.h +++ b/src/laybasic/laybasic/layEditorOptionsPage.h @@ -77,7 +77,11 @@ public: void activate (bool active); void set_owner (EditorOptionsPages *owner); - void show (); + /** + * @brief Shows the editor page + * @return -1, if the page is shown non-modal, otherwise 1 or 0 if the dialog was accepted (1) or rejected (0) + */ + int show (); const lay::PluginDeclaration *plugin_declaration () const { return mp_plugin_declaration; } void set_plugin_declaration (const lay::PluginDeclaration *pd) { mp_plugin_declaration = pd; } diff --git a/src/laybasic/laybasic/layEditorOptionsPages.cc b/src/laybasic/laybasic/layEditorOptionsPages.cc index 650c26314..855515d2d 100644 --- a/src/laybasic/laybasic/layEditorOptionsPages.cc +++ b/src/laybasic/laybasic/layEditorOptionsPages.cc @@ -122,6 +122,7 @@ EditorOptionsPages::exec_modal (EditorOptionsPage *page) // found the page - make it current and show the dialog mp_modal_pages->set_current_index (i); + page->setup (mp_dispatcher); page->set_focus (); return mp_modal_pages->exec () != 0; @@ -165,6 +166,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->setup (mp_dispatcher); page->set_focus (); break; } @@ -256,16 +258,17 @@ BEGIN_PROTECTED // 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 (); + do_apply (false); + do_apply (true); END_PROTECTED_W (this) } void -EditorOptionsPages::do_apply () +EditorOptionsPages::do_apply (bool modal) { for (std::vector ::iterator p = m_pages.begin (); p != m_pages.end (); ++p) { - if ((*p)->active ()) { + if ((*p)->active () && modal == (*p)->is_modal_page ()) { // NOTE: we apply to the root dispatcher, so other dispatchers (views) get informed too. (*p)->apply (mp_dispatcher->dispatcher ()); } @@ -276,7 +279,7 @@ void EditorOptionsPages::apply () { BEGIN_PROTECTED - do_apply (); + do_apply (false); END_PROTECTED_W (this) } @@ -417,8 +420,10 @@ EditorOptionsModalPages::widget (int index) void EditorOptionsModalPages::accept () { - mp_parent->apply (); +BEGIN_PROTECTED + mp_parent->do_apply (true); QDialog::accept (); +END_PROTECTED } void @@ -430,9 +435,11 @@ EditorOptionsModalPages::reject () void EditorOptionsModalPages::clicked (QAbstractButton *button) { +BEGIN_PROTECTED if (button == mp_button_box->button (QDialogButtonBox::Apply)) { - mp_parent->apply (); + mp_parent->do_apply (true); } +END_PROTECTED } } diff --git a/src/laybasic/laybasic/layEditorOptionsPages.h b/src/laybasic/laybasic/layEditorOptionsPages.h index 97d5c07ba..7ce48559c 100644 --- a/src/laybasic/laybasic/layEditorOptionsPages.h +++ b/src/laybasic/laybasic/layEditorOptionsPages.h @@ -73,6 +73,7 @@ public: bool has_content () const; bool has_modal_content () const; + void do_apply (bool modal); public slots: void apply (); @@ -85,7 +86,6 @@ private: EditorOptionsModalPages *mp_modal_pages; void update (lay::EditorOptionsPage *page); - void do_apply (); }; /** diff --git a/src/laybasic/laybasic/layEditorServiceBase.cc b/src/laybasic/laybasic/layEditorServiceBase.cc index 06f736516..784889dac 100644 --- a/src/laybasic/laybasic/layEditorServiceBase.cc +++ b/src/laybasic/laybasic/layEditorServiceBase.cc @@ -344,6 +344,7 @@ EditorServiceBase::activated () } #if defined(HAVE_QT) + std::vector EditorServiceBase::editor_options_pages () { @@ -360,23 +361,52 @@ EditorServiceBase::editor_options_pages () return pages; } } -#endif + +lay::EditorOptionsPage * +EditorServiceBase::focus_page () +{ + auto pages = editor_options_pages (); + for (auto p = pages.begin (); p != pages.end (); ++p) { + if ((*p)->is_focus_page ()) { + return *p; + } + } + + return 0; +} bool EditorServiceBase::key_event (unsigned int key, unsigned int buttons) { -#if defined(HAVE_QT) if (is_active () && key == Qt::Key_Tab && buttons == 0) { - auto pages = editor_options_pages (); - for (auto p = pages.begin (); p != pages.end (); ++p) { - if ((*p)->is_focus_page ()) { - (*p)->show (); - return true; - } + EditorOptionsPage *fp = focus_page (); + if (fp) { + focus_page_open (fp); } + return true; + } else { + return false; } -#endif +} +int +EditorServiceBase::focus_page_open (EditorOptionsPage *fp) +{ + return fp->show (); +} + +void +EditorServiceBase::show_error (tl::Exception &ex) +{ + tl::error << ex.msg (); + QMessageBox::critical (ui ()->widget (), tr ("Error"), tl::to_qstring (ex.msg ())); +} + +#else + +bool +EditorServiceBase::key_event (unsigned int key, unsigned int buttons) +{ return false; } @@ -384,9 +414,14 @@ void EditorServiceBase::show_error (tl::Exception &ex) { tl::error << ex.msg (); -#if defined(HAVE_QT) - QMessageBox::critical (ui ()->widget (), tr ("Error"), tl::to_qstring (ex.msg ())); -#endif } +void +EditorServiceBase::focus_page_enter () +{ + // .. nothing yet .. +} + +#endif + } diff --git a/src/laybasic/laybasic/layEditorServiceBase.h b/src/laybasic/laybasic/layEditorServiceBase.h index 1c50bebbc..3fffcfcce 100644 --- a/src/laybasic/laybasic/layEditorServiceBase.h +++ b/src/laybasic/laybasic/layEditorServiceBase.h @@ -270,6 +270,18 @@ public: * @brief Gets the editor options pages associated with this plugin */ std::vector editor_options_pages (); + + /** + * @brief Gets the focus page or 0 if there is none + */ + lay::EditorOptionsPage *focus_page (); + + /** + * @brief Gets called when the focus page opens + * + * The default implementation will call fp->show() and return its return value. + */ + virtual int focus_page_open (EditorOptionsPage *fp); #endif private: diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index 4b839ed61..a19effbf0 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -157,8 +157,8 @@ struct LAYBASIC_PUBLIC LayerDisplayProperties * It manages the layer display list, bookmark list etc. */ class LAYBASIC_PUBLIC LayoutViewBase : - public lay::Editables, - public lay::Dispatcher + public lay::Dispatcher, // needs to be first as it is the GSI base class + public lay::Editables { public: typedef lay::CellView::unspecific_cell_path_type cell_path_type; diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index 8c5ba030d..52ce019fc 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -28,6 +28,7 @@ DEFINES += MAKE_LAYBASIC_LIBRARY SOURCES += \ gsiDeclLayLayers.cc \ + gsiDeclLayDispatcher.cc \ gsiDeclLayLayoutViewBase.cc \ gsiDeclLayMarker.cc \ gsiDeclLayMenu.cc \