[gtkmm/kjellahl/application-run] Gtk::Application: Add make_window_and_run()



commit 531777206609d15778795389c30fc5ecfbaa160f
Author: Kjell Ahlstedt <kjellahlstedt gmail com>
Date:   Tue Oct 20 15:22:08 2020 +0200

    Gtk::Application: Add make_window_and_run()
    
    and remove the run() overloads that take a Window&.
    
    There are new restrictions on Widgets. From the class description:
    Most widgets can't safely be created before the application has been
    registered or activated. They can't safely be deleted after run() or
    make_window_and_run() returns.
    
    The main() functions in the demo program and in most test programs
    have been updated.
    
    Fixes #78

 demos/gtk-demo/main.cc              |   6 +-
 gtk/src/application.ccg             |  30 +---------
 gtk/src/application.hg              | 109 +++++++++++++++++++-----------------
 tests/builder/main.cc               |  52 +++++++++++------
 tests/child_widget/main.cc          |   5 +-
 tests/child_widget2/main.cc         |   6 +-
 tests/child_widget_managed/main.cc  |   4 +-
 tests/delete_cpp_child/main.cc      |   3 +-
 tests/dialog_deletethis/main.cc     |   1 +
 tests/main_with_options/main.cc     |   3 +-
 tests/property_notification/main.cc |  28 ++++-----
 tests/refcount_dialog/main.cc       |   4 +-
 tests/scrolledwindow/main.cc        |   3 +-
 tests/wrap_existing/main.cc         |   3 +-
 14 files changed, 124 insertions(+), 133 deletions(-)
---
diff --git a/demos/gtk-demo/main.cc b/demos/gtk-demo/main.cc
index c302ae73..c158786f 100644
--- a/demos/gtk-demo/main.cc
+++ b/demos/gtk-demo/main.cc
@@ -20,11 +20,9 @@
 #include <gtkmm/application.h>
 #include "demowindow.h"
 
-int main (int argc, char *argv[])
+int main(int argc, char* argv[])
 {
   auto app = Gtk::Application::create();
 
-  DemoWindow window;
-
-  return app->run(window, argc, argv);
+  return app->make_window_and_run<DemoWindow>(argc, argv);
 }
diff --git a/gtk/src/application.ccg b/gtk/src/application.ccg
index 01891927..023dee7d 100644
--- a/gtk/src/application.ccg
+++ b/gtk/src/application.ccg
@@ -93,7 +93,7 @@ Application::Application(const Glib::ustring& application_id, Gio::Application::
 {
   // gtk_init() is called by the 'startup' default signal handler when g_application_run() is called.
   // It's also called here, to make it possible for users of gtkmm to create
-  // a window and other widgets before calling run().
+  // a window and other widgets before calling run(). (This is not recommended.)
   // See https://bugzilla.gnome.org/show_bug.cgi?id=639925
   gtk_init();
   set_cxx_locale_to_c_locale();
@@ -109,39 +109,11 @@ int Application::run(int argc, char** argv)
   return Gio::Application::run(argc, argv);
 }
 
-int Application::run(Window& window, int argc, char** argv)
-{
-  //We cannot add and show the window until the GApplication::activate signal
-  //has been emitted, or we will crash because the application has not been
-  //registered. (At least if window is an ApplicationWindow.)
-  signal_activate().connect(
-    sigc::bind(
-      sigc::mem_fun(*this, &Application::on_activate_add_and_show_main_window),
-      &window));
-
-  return Gio::Application::run(argc, argv);
-}
-
-
-int Application::run(Window& window)
-{
-  return run(window, 0, nullptr);
-}
-
 int Application::run()
 {
   return Gio::Application::run(0, nullptr);
 }
 
-void Application::on_activate_add_and_show_main_window(Window* window)
-{
-  if(window)
-  {
-    add_window(*window);
-    window->show();
-  }
-}
-
 void Application::set_accel_for_action(const Glib::ustring& detailed_action_name, const Glib::ustring& accel)
 {
   std::vector<Glib::ustring> vec;
diff --git a/gtk/src/application.hg b/gtk/src/application.hg
index 9a50a245..4039d2c4 100644
--- a/gtk/src/application.hg
+++ b/gtk/src/application.hg
@@ -18,6 +18,8 @@
 _CONFIGINCLUDE(gtkmmconfig.h)
 
 #include <vector>
+#include <type_traits>
+#include <utility>
 #include <giomm/application.h>
 #include <giomm/menumodel.h>
 #include <giomm/menu.h>
@@ -80,6 +82,12 @@ class GTKMM_API Window;
  * inform the user about the negative consequences of ending the
  * session while inhibitors are present.
  *
+ * @note
+ * Most widgets can't safely be created before the application has been registered
+ * (Gio::Application::register_application() called) or activated
+ * (Gio::Application::signal_activate() emitted).
+ * They can't safely be deleted after run() or make_window_and_run() returns.
+ *
  * @newin{3,4}
  */
 class GTKMM_API Application
@@ -109,7 +117,6 @@ protected:
   _IGNORE(gtk_application_new)
   _IGNORE(gtk_application_window_new)
 
-
 public:
   _WRAP_ENUM(InhibitFlags, GtkApplicationInhibitFlags, decl_prefix GTKMM_API)
 
@@ -166,73 +173,53 @@ public:
 
   /** Starts the application.
    *
-   * The default implementation of this virtual function will simply run
-   * a main loop.
-   *
-   * It is an error to call this function if @a application is a proxy for
-   * a remote application.
-   *
-   * @param argc The argc from main() (or 0 if @a argv is <tt>0</tt>).
-   * @param argv The argv from main(), or <tt>0</tt>.
+   * @param argc The argc from main() (or 0 if @a argv is <tt>nullptr</tt>).
+   * @param argv The argv from main(), or <tt>nullptr</tt>.
    * @return The exit status.
    *
+   * @see Gio::Application::run()
+   *
    * @newin{3,4}
    */
   int run(int argc, char** argv);
 
   /** Starts the application.
    *
-   * The default implementation of this virtual function will simply run
-   * a main loop.
-   *
-   * It is an error to call this function if @a application is a proxy for
-   * a remote application.
-   *
-   * @note If you call Gio::Application::quit() while a window is connected to
-   * the application, and then return from main() without removing the window
-   * from the application, the application's destructor will not be called.
-   *
-   * @param window The window to show. This method will return when the window is hidden.
-   * @param argc The argc from main() (or 0 if @a argv is <tt>0</tt>).
-   * @param argv The argv from main(), or <tt>0</tt>.
    * @return The exit status.
    *
+   * @see Gio::Application::run()
+   *
    * @newin{3,4}
    */
-  int run(Window& window, int argc, char** argv);
+  int run();
 
-  /** Starts the application.
-   *
-   * The default implementation of this virtual function will simply run
-   * a main loop.
-   *
-   * It is an error to call this function if @a application is a proxy for
-   * a remote application.
+  /** Starts the application, creates and shows a window.
    *
-   * @note If you call Gio::Application::quit() while a window is connected to
-   * the application, and then return from main() without removing the window
-   * from the application, the application's destructor will not be called.
+   * A window of type T_Window is constructed and added to the application
+   * in a signal_activate() handler. The window is deleted when it is hidden
+   * or removed from the application. The method returns when the window is hidden,
+   * unless other windows have been added but not removed.
    *
-   * @param window The window to show. This method will return when the window is hidden.
+   * @tparam T_Window The type of window to show. Must be Gtk::Window or a class type
+   *                  that inherits from Gtk::Window.
+   * @param argc The argc from main() (or 0 if @a argv is <tt>nullptr</tt>).
+   * @param argv The argv from main(), or <tt>nullptr</tt>.
+   * @param args Arguments to T_Window's constructor, if any.
    * @return The exit status.
    *
-   * @newin{3,4}
+   * @see Gio::Application::run()
+   *
+   * @newin{3,98}
    */
-  int run(Window& window);
+  template <typename T_Window, typename... T_Args>
+  int make_window_and_run(int argc, char** argv, T_Args&&... args);
 
-  /** Starts the application.
-   *
-   * The default implementation of this virtual function will simply run
-   * a main loop.
-   *
-   * It is an error to call this function if @a application is a proxy for
-   * a remote application.
-   *
-   * @return The exit status.
-   *
-   * @newin{3,4}
+  /** Get the window, constructed by make_window_and_run().
    */
-  int run();
+  Window* get_run_window() { return m_run_window; }
+  /** Get the window, constructed by make_window_and_run().
+   */
+  const Window* get_run_window() const { return m_run_window; }
 
   _WRAP_METHOD(Glib::RefPtr<Gio::MenuModel> get_menubar(), gtk_application_get_menubar, refreturn)
   _WRAP_METHOD(Glib::RefPtr<const Gio::MenuModel> get_menubar() const, gtk_application_get_menubar, 
refreturn, constversion)
@@ -302,8 +289,30 @@ private:
    */
   const Glib::Class& custom_class_init();
 
-  void on_activate_add_and_show_main_window(Window* window);
-  void on_window_hide(Window* window);
+  Window* m_run_window = nullptr;
 };
 
+template <typename T_Window, typename... T_Args>
+int Application::make_window_and_run(int argc, char** argv, T_Args&&... args)
+{
+  static_assert(std::is_base_of<Window, T_Window>::value);
+  
+  signal_activate().connect([this, &args...] () {
+    auto window = new T_Window(std::forward<T_Args>(args)...);
+    m_run_window = window;
+    add_window(*window);
+    window->show();
+  });
+
+  signal_window_removed().connect([this] (Window* window) {
+    if (window == m_run_window)
+    {
+      delete window;
+      m_run_window = nullptr;
+    }
+  });
+
+  return Gio::Application::run(argc, argv);
+}
+
 } // namespace Gtk
diff --git a/tests/builder/main.cc b/tests/builder/main.cc
index 68a13db8..b1beb3cd 100644
--- a/tests/builder/main.cc
+++ b/tests/builder/main.cc
@@ -208,6 +208,7 @@ int main(int argc, char* argv[])
 
   auto app = Gtk::Application::create();
   g_assert_nonnull(app);
+  app->register_application();
 
   DerivedButton::ensure_type();
   auto builder = Gtk::Builder::create_from_string(gladefile);
@@ -233,37 +234,52 @@ int main(int argc, char* argv[])
   const GObject* const standard_button = (GObject*)main_win->get_standard_button()->gobj();
   const GObject* const adjustment_gobj = (GObject*)adjustment->gobj();
 
-  std::cout << "Before app->run(*main_win, argc1, argv)" << std::endl
+  std::cout << "Before app->run(argc1, argv)" << std::endl
     << "  ref_count(MainWindow)=" << window->ref_count << std::endl
     << "  ref_count(DerivedButton)=" << derived_button->ref_count << std::endl
     << "  ref_count(Gtk::Button)=" << standard_button->ref_count << std::endl
     << "  ref_count(orphaned_button)=" << orphaned_button->ref_count << std::endl
     << "  ref_count(adjustment)=" << adjustment_gobj->ref_count << std::endl;
 
-  const int result = app->run(*main_win, argc1, argv);
-
-  std::cout << "After app->run(*main_win, argc1, argv)" << std::endl
-    << "  ref_count(MainWindow)=" << window->ref_count << std::endl
-    << "  ref_count(DerivedButton)=" << derived_button->ref_count << std::endl
-    << "  ref_count(Gtk::Button)=" << standard_button->ref_count << std::endl
-    << "  ref_count(orphaned_button)=" << orphaned_button->ref_count << std::endl
-    << "  ref_count(adjustment)=" << adjustment_gobj->ref_count << std::endl;
+  // This is approximately what Gtk::Application::make_window_and_run() would do.
+  app->signal_activate().connect([&app, main_win] ()
+  {
+    app->add_window(*main_win);
+    main_win->show();
+  });
+  main_win->signal_hide().connect([&] ()
+  {
+    delete main_win;
 
-  delete main_win;
+    std::cout << "After delete main_win" << std::endl
+      << "  ref_count(MainWindow)=" << window->ref_count << std::endl
+      << "  ref_count(DerivedButton)=" << derived_button->ref_count << std::endl
+      << "  ref_count(Gtk::Button)=" << standard_button->ref_count << std::endl
+      << "  ref_count(orphaned_button)=" << orphaned_button->ref_count << std::endl
+      << "  ref_count(adjustment)=" << adjustment_gobj->ref_count << std::endl;
 
-  std::cout << "After delete main_win" << std::endl
-    << "  ref_count(MainWindow)=" << window->ref_count << std::endl
-    << "  ref_count(DerivedButton)=" << derived_button->ref_count << std::endl
-    << "  ref_count(Gtk::Button)=" << standard_button->ref_count << std::endl
-    << "  ref_count(orphaned_button)=" << orphaned_button->ref_count << std::endl
-    << "  ref_count(adjustment)=" << adjustment_gobj->ref_count << std::endl;
+    builder.reset();
+
+    std::cout << "After builder.reset()" << std::endl;
+    if (print_after_deletion)
+    {
+      // If Builder is correct, this code will access deallocated memory.
+      std::cout
+        << "  ref_count(MainWindow)=" << window->ref_count << std::endl
+        << "  ref_count(DerivedButton)=" << derived_button->ref_count << std::endl
+        << "  ref_count(Gtk::Button)=" << standard_button->ref_count << std::endl
+        << "  ref_count(orphaned_button)=" << orphaned_button->ref_count << std::endl
+        << "  ref_count(adjustment)=" << adjustment_gobj->ref_count << std::endl;
+    }
+  });
 
-  builder.reset();
+  const int result = app->run(argc1, argv);
 
+  std::cout << "After app->run(argc1, argv)" << std::endl;
   if (print_after_deletion)
   {
     // If Builder is correct, this code will access deallocated memory.
-    std::cout << "After builder.reset()" << std::endl
+    std::cout
       << "  ref_count(MainWindow)=" << window->ref_count << std::endl
       << "  ref_count(DerivedButton)=" << derived_button->ref_count << std::endl
       << "  ref_count(Gtk::Button)=" << standard_button->ref_count << std::endl
diff --git a/tests/child_widget/main.cc b/tests/child_widget/main.cc
index ebb538d3..9b68a745 100644
--- a/tests/child_widget/main.cc
+++ b/tests/child_widget/main.cc
@@ -17,10 +17,9 @@
 #include <gtkmm/application.h>
 #include "testwindow.h"
 
-int main(int argc, char *argv[])
+int main(int argc, char* argv[])
 {
   auto app = Gtk::Application::create();
 
-  TestWindow testWindow;
-  return app->run(testWindow, argc, argv); //Shows the window and returns when it is closed.
+  return app->make_window_and_run<TestWindow>(argc, argv); //Shows the window and returns when it is closed.
 }
diff --git a/tests/child_widget2/main.cc b/tests/child_widget2/main.cc
index a6f2f6c2..8ec1fd04 100644
--- a/tests/child_widget2/main.cc
+++ b/tests/child_widget2/main.cc
@@ -18,10 +18,8 @@ MyWindow::MyWindow() :
   vbox.append(b);
 }
 
-int main (int argc, char *argv[])
+int main(int argc, char* argv[])
 {
   auto app = Gtk::Application::create();
-
-  MyWindow window;
-  return app->run(window, argc, argv);
+  return app->make_window_and_run<MyWindow>(argc, argv);
 }
diff --git a/tests/child_widget_managed/main.cc b/tests/child_widget_managed/main.cc
index 692b2729..00ca6b7f 100644
--- a/tests/child_widget_managed/main.cc
+++ b/tests/child_widget_managed/main.cc
@@ -44,7 +44,5 @@ ExampleWindow::~ExampleWindow()
 int main(int argc, char* argv[])
 {
   auto app = Gtk::Application::create();
-
-  ExampleWindow window;
-  return app->run(window, argc, argv);
+  return app->make_window_and_run<ExampleWindow>(argc, argv);
 }
diff --git a/tests/delete_cpp_child/main.cc b/tests/delete_cpp_child/main.cc
index 6b709a72..9a911c81 100644
--- a/tests/delete_cpp_child/main.cc
+++ b/tests/delete_cpp_child/main.cc
@@ -47,6 +47,5 @@ void AppWindow::on_button_clicked()
 int main(int argc, char *argv[])
 {
   auto app = Gtk::Application::create();
-  AppWindow window;
-  return app->run(window, argc, argv);
+  return app->make_window_and_run<AppWindow>(argc, argv);
 }
diff --git a/tests/dialog_deletethis/main.cc b/tests/dialog_deletethis/main.cc
index fc02ae5c..f3749d7f 100644
--- a/tests/dialog_deletethis/main.cc
+++ b/tests/dialog_deletethis/main.cc
@@ -48,6 +48,7 @@ class Dlg : public sigc::trackable
 int main (int argc, char **argv)
 {
   app = Gtk::Application::create();
+  app->register_application();
 
   new Dlg(); //Not a Gtk::Dialog - it creates one in its constructor.
 
diff --git a/tests/main_with_options/main.cc b/tests/main_with_options/main.cc
index 5e84c31c..aa4b3830 100644
--- a/tests/main_with_options/main.cc
+++ b/tests/main_with_options/main.cc
@@ -122,8 +122,7 @@ int main(int argc, char *argv[])
     }
     std::cout << std::endl;
 
-    Gtk::Window testWindow;
-    return app->run(testWindow); //Shows the window and returns when it is closed.
+    return app->make_window_and_run<Gtk::Window>(0, nullptr); //Shows the window and returns when it is 
closed.
   }
   catch(const Glib::Error& ex)
   {
diff --git a/tests/property_notification/main.cc b/tests/property_notification/main.cc
index 69f8ef13..d4970553 100644
--- a/tests/property_notification/main.cc
+++ b/tests/property_notification/main.cc
@@ -17,22 +17,24 @@ void on_property_name_changed()
   std::cout << "name property changed" << std::endl;
 }
 
-int main (int argc, char **argv)
+class TestWindow : public Gtk::Window
 {
-  auto app = Gtk::Application::create();
+public:
+  TestWindow()
+  {
+    button.connect_property_changed("rgba", sigc::ptr_fun(&on_property_rgba_changed));
+    button.property_rgba().signal_changed().connect(sigc::ptr_fun(&on_property_rgba_changed_nicer_api));
+    button.connect_property_changed("name", sigc::ptr_fun(&on_property_name_changed));
 
-  Gtk::Window window;
+    set_child(button);
+  }
 
+protected:
   Gtk::ColorButton button;
-  button.show();
-
-  button.connect_property_changed("rgba", sigc::ptr_fun(&on_property_rgba_changed));
-
-  button.property_rgba().signal_changed().connect(sigc::ptr_fun(&on_property_rgba_changed_nicer_api));
-
-  button.connect_property_changed("name", sigc::ptr_fun(&on_property_name_changed));
+};
 
-  window.set_child(button);
-
-  return app->run(window, argc, argv);
+int main (int argc, char **argv)
+{
+  auto app = Gtk::Application::create();
+  return app->make_window_and_run<TestWindow>(argc, argv);
 }
diff --git a/tests/refcount_dialog/main.cc b/tests/refcount_dialog/main.cc
index 30c543d0..07f74d79 100644
--- a/tests/refcount_dialog/main.cc
+++ b/tests/refcount_dialog/main.cc
@@ -67,7 +67,5 @@ void MyWindow::on_dialog_response(int /* response_id */)
 int main(int argc, char* argv[])
 {
   auto app = Gtk::Application::create();
-
-  MyWindow win;
-  return app->run(win, argc, argv);
+  return app->make_window_and_run<MyWindow>(argc, argv);
 }
diff --git a/tests/scrolledwindow/main.cc b/tests/scrolledwindow/main.cc
index 151b4533..75d8195e 100644
--- a/tests/scrolledwindow/main.cc
+++ b/tests/scrolledwindow/main.cc
@@ -55,9 +55,10 @@ protected:
   DerivedScrolledWindow m_ScrolledWindow;
 };
 
-int main (int /* argc */, char** /* argv */)
+int main(int /* argc */, char** /* argv */)
 {
   auto app = Gtk::Application::create();
+  app->register_application();
 
   Instance instance;
 
diff --git a/tests/wrap_existing/main.cc b/tests/wrap_existing/main.cc
index 1a1482ab..7b04ac00 100644
--- a/tests/wrap_existing/main.cc
+++ b/tests/wrap_existing/main.cc
@@ -18,9 +18,10 @@ void on_object_qdata_destroyed(gpointer data)
   g_warning("on_object_qdata_destroyed():  c instance=%p", (void*)data);
 }
 
-int main (int /* argc */, char** /* argv */)
+int main(int /* argc */, char** /* argv */)
 {
   auto app = Gtk::Application::create();
+  app->register_application();
 
   auto pDialog = new Gtk::Dialog();
   Gtk::Box* pBox = pDialog->get_content_area();


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]