diff --git a/src/lay/lay/gsiDeclLayMainWindow.cc b/src/lay/lay/gsiDeclLayMainWindow.cc index b9ab8ded02..ff6946a029 100644 --- a/src/lay/lay/gsiDeclLayMainWindow.cc +++ b/src/lay/lay/gsiDeclLayMainWindow.cc @@ -656,6 +656,30 @@ Class decl_MainWindow (QT_EXTERNAL_BASE (QMainWindow) "lay", "M "it can be beneficial for test or automation purposes, i.e. if a screenshot needs to be " "produced once the application has finished drawing." ) + + gsi::method ("synchronous", &lay::MainWindow::synchronous, + "@brief Gets a value indicating whether synchronous mode is activated\n" + "See \\synchronous= for details about this attribute\n" + "\n" + "This property getter was introduced in version 0.29." + ) + + gsi::method ("title=", &lay::MainWindow::set_title, gsi::arg ("title"), + "@brief Sets the window title\n" + "If the window title is not empty, it will be used for the application window's title. Otherwise " + "the default title is used. The title string is subject to expression interpolation. So it is " + "possible to implement the default scheme of adding the current view using the following code:\n" + "\n" + "@code\n" + "add_view_info = \"$(var view=LayoutView.current; view ? ' - ' + (view.is_dirty ? '[+] ' : '') + view.title : '')\"\n" + "RBA::MainWindow.instance.title = \"Custom Title\" + add_view_info\n" + "@/code\n" + "\n" + "This property was introduced in version 0.29." + ) + + gsi::method ("title", &lay::MainWindow::title, + "@brief Gets the window title\n" + "See \\title= for a description of this property.\n" + "This property was introduced in version 0.29." + ) + gsi::method ("close_all", &lay::MainWindow::close_all, "@brief Closes all views\n" "\n" diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 7df5070dd8..1adfb3bb7c 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -3597,18 +3597,35 @@ MainWindow::view_title_changed (lay::LayoutView *view) } } +void +MainWindow::set_title (const std::string &title) +{ + if (title != m_title) { + m_title = title; + update_window_title (); + } +} + void MainWindow::update_window_title () { - if (current_view ()) { - std::string sep = " - "; - if (current_view ()->is_dirty ()) { - sep += "[+] "; - } - setWindowTitle (tl::to_qstring (lay::ApplicationBase::version () + sep + current_view ()->title ())); + std::string title = m_title; + + if (! title.empty ()) { + tl::Eval eval; + title = eval.interpolate (title); } else { - setWindowTitle (tl::to_qstring (lay::ApplicationBase::version ())); + title = lay::ApplicationBase::version (); + if (current_view ()) { + std::string sep = " - "; + if (current_view ()->is_dirty ()) { + sep += "[+] "; + } + title += sep + current_view ()->title (); + } } + + setWindowTitle (tl::to_qstring (title)); } void @@ -3616,7 +3633,6 @@ MainWindow::current_view_changed () { update_window_title (); current_view_changed_event (); - // TODO: required? current_view_changed_event (int (view_index_org)); } double diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index 30d51af613..a0e9e6d62d 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -426,6 +426,22 @@ Q_OBJECT return m_synchronous; } + /** + * @brief Sets the title string + * + * If empty, the default title will be created. Otherwise this string will + * be used. It is subject to expression interpolation. + */ + void set_title (const std::string &title); + + /** + * @brief Gets the title string + */ + const std::string &title () const + { + return m_title; + } + /** * @brief Returns true, if the edit functions of the current view are enabled */ @@ -760,6 +776,7 @@ protected slots: std::string m_message; std::unique_ptr mp_printer; std::vector m_changed_files; + std::string m_title; // the object manager (undo/redo mechanism and others) db::Manager m_manager; diff --git a/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc b/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc index f997c5a3e7..1075f321d5 100644 --- a/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc +++ b/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc @@ -474,6 +474,12 @@ static lay::AbstractMenu *menu (lay::LayoutViewBase *view) return view->menu (); } +static bool view_is_dirty (lay::LayoutViewBase *view) +{ + view->refresh (); + return view->is_dirty (); +} + LAYBASIC_PUBLIC Class decl_LayoutViewBase ("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" @@ -1136,6 +1142,13 @@ LAYBASIC_PUBLIC Class decl_LayoutViewBase ("lay", "LayoutVi "Show the layout in full depth down to the deepest level of hierarchy. " "This method may cause a redraw." ) + + gsi::method_ext ("is_dirty?", &view_is_dirty, + "@brief Gets a flag indicating whether one of the layouts displayed needs saving\n" + "A layout is 'dirty' if it is modified and needs saving. This method returns " + "true if this is the case for at least one of the layouts shown in the view.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + gsi::method ("resize", static_cast (&lay::LayoutViewBase::resize), "@brief Resizes the layout view to the given dimension\n" "\n" diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index 903e2f1532..c6181b6b9b 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -2749,6 +2749,11 @@ class LAYBASIC_PUBLIC LayoutViewBase : return m_options; } + /** + * @brief Calls deferred methods and updates view ops, provided for GSI bindings mainly + */ + void refresh (); + private: // event handlers used to connect to the layout object's events void signal_hier_changed (); @@ -2917,8 +2922,6 @@ class LAYBASIC_PUBLIC LayoutViewBase : void init_layer_properties (LayerProperties &props, const LayerPropertiesList &lp_list) const; void merge_dither_pattern (lay::LayerPropertiesList &props); - void refresh (); - protected: /** * @brief Constructor for calling from a LayoutView diff --git a/src/rba/unit_tests/rbaTests.cc b/src/rba/unit_tests/rbaTests.cc index e45531c948..f9dc6a7484 100644 --- a/src/rba/unit_tests/rbaTests.cc +++ b/src/rba/unit_tests/rbaTests.cc @@ -143,6 +143,7 @@ RUBYTEST (extNetTracer, "extNetTracer.rb") RUBYTEST (imgObject, "imgObject.rb") RUBYTEST (layLayers, "layLayers.rb") RUBYTEST (layLayoutView, "layLayoutView.rb") +RUBYTEST (layMainWindow, "layMainWindow.rb") RUBYTEST (layMarkers, "layMarkers.rb") RUBYTEST (layMacro, "layMacro.rb") RUBYTEST (layMenuTest, "layMenuTest.rb") diff --git a/testdata/ruby/layLayoutView.rb b/testdata/ruby/layLayoutView.rb index 0a01dd0dce..66002e1e3f 100644 --- a/testdata/ruby/layLayoutView.rb +++ b/testdata/ruby/layLayoutView.rb @@ -570,6 +570,30 @@ def test_8 end + def test_9 + + if !RBA.constants.member?(:Application) + return + end + + app = RBA::Application.instance + mw = app.main_window + mw.close_all + + mw.create_layout(1) + assert_equal(false, mw.current_view.is_dirty?) + + cv = mw.current_view.cellview(0) + cv.cell = cv.layout.create_cell("TOP") + + assert_equal(true, mw.current_view.cellview(0).is_dirty?) + + if cv.layout.is_editable? + assert_equal(true, mw.current_view.is_dirty?) + end + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/layMainWindow.rb b/testdata/ruby/layMainWindow.rb new file mode 100644 index 0000000000..ba4d0f35a9 --- /dev/null +++ b/testdata/ruby/layMainWindow.rb @@ -0,0 +1,69 @@ +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2024 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 + +if !$:.member?(File::dirname($0)) + $:.push(File::dirname($0)) +end + +load("test_prologue.rb") + +class LAYMainWindow_TestClass < TestBase + + # Basic view creation and MainWindow events + def test_1 + + if !RBA.constants.member?(:Application) + return + end + + app = RBA::Application.instance + mw = app.main_window + + mw.title = "ABC$(1+2)" + assert_equal("ABC$(1+2)", mw.title) + + if RBA.constants.member?(:QWidget) + # string is interpolated + assert_equal("ABC3", mw.windowTitle) + end + + end + + def test_2 + + # smoke test + + app = RBA::Application.instance + mw = app.main_window + s = mw.synchronous + + mw.synchronous = true + assert_equal(true, mw.synchronous) + + mw.synchronous = false + assert_equal(false, mw.synchronous) + + mw.synchronous = true + + end + +end + +load("test_epilogue.rb") +