[libdazzle] example: add some basic document handling to example app



commit 82f749af97944e6e1a76684f1a68e5e1b326ae59
Author: Christian Hergert <chergert redhat com>
Date:   Sat Jun 17 15:58:40 2017 -0700

    example: add some basic document handling to example app
    
    This is just going to be a stupid little text editor to demo some things
    and allow us to test stuff in a non-trivial fashion.

 examples/app/example-application.h    |    5 +-
 examples/app/example-document-view.c  |  126 +++++++++++++++++++++++++++++++++
 examples/app/example-document-view.h  |   15 ++++
 examples/app/example-document-view.ui |   16 ++++
 examples/app/example-document.c       |  109 ++++++++++++++++++++++++++++
 examples/app/example-document.h       |   13 ++++
 examples/app/example-window.c         |  111 +++++++++++++++++++++++++----
 examples/app/example-window.h         |   12 ++--
 examples/app/example-window.ui        |   48 ++++++++++---
 examples/app/example.gresources.xml   |    1 +
 examples/app/meson.build              |    4 +
 11 files changed, 425 insertions(+), 35 deletions(-)
---
diff --git a/examples/app/example-application.h b/examples/app/example-application.h
index 8ad96f7..2b8d624 100644
--- a/examples/app/example-application.h
+++ b/examples/app/example-application.h
@@ -1,5 +1,4 @@
-#ifndef EXAMPLE_APPLICATION_H
-#define EXAMPLE_APPLICATION_H
+#pragma once
 
 #include <dazzle.h>
 
@@ -10,5 +9,3 @@ G_BEGIN_DECLS
 G_DECLARE_FINAL_TYPE (ExampleApplication, example_application, EXAMPLE, APPLICATION, DzlApplication)
 
 G_END_DECLS
-
-#endif /* EXAMPLE_APPLICATION_H */
diff --git a/examples/app/example-document-view.c b/examples/app/example-document-view.c
new file mode 100644
index 0000000..dd9e1cf
--- /dev/null
+++ b/examples/app/example-document-view.c
@@ -0,0 +1,126 @@
+#include "example-document-view.h"
+
+struct _ExampleDocumentView
+{
+  GtkBin parent_instance;
+
+  GtkTextView *text_view;
+
+  ExampleDocument *document;
+};
+
+enum {
+  PROP_0,
+  PROP_DOCUMENT,
+  N_PROPS
+};
+
+G_DEFINE_TYPE (ExampleDocumentView, example_document_view, GTK_TYPE_BIN)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+example_document_view_set_document (ExampleDocumentView *self,
+                                    ExampleDocument     *document)
+{
+  g_assert (EXAMPLE_IS_DOCUMENT_VIEW (self));
+  g_assert (!document || EXAMPLE_IS_DOCUMENT (document));
+
+  if (g_set_object (&self->document, document))
+    {
+      gtk_text_view_set_buffer (self->text_view, GTK_TEXT_BUFFER (document));
+    }
+}
+
+static void
+example_document_view_grab_focus (GtkWidget *widget)
+{
+  ExampleDocumentView *self = EXAMPLE_DOCUMENT_VIEW (widget);
+
+  gtk_widget_grab_focus (GTK_WIDGET (self->text_view));
+}
+
+static void
+example_document_view_finalize (GObject *object)
+{
+  ExampleDocumentView *self = (ExampleDocumentView *)object;
+
+  g_clear_object (&self->document);
+
+  G_OBJECT_CLASS (example_document_view_parent_class)->finalize (object);
+}
+
+static void
+example_document_view_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  ExampleDocumentView *self = EXAMPLE_DOCUMENT_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_DOCUMENT:
+      g_value_set_object (value, self->document);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+example_document_view_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  ExampleDocumentView *self = EXAMPLE_DOCUMENT_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_DOCUMENT:
+      example_document_view_set_document (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+example_document_view_class_init (ExampleDocumentViewClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = example_document_view_finalize;
+  object_class->get_property = example_document_view_get_property;
+  object_class->set_property = example_document_view_set_property;
+
+  widget_class->grab_focus = example_document_view_grab_focus;
+
+  properties [PROP_DOCUMENT] =
+    g_param_spec_object ("document",
+                         "Document",
+                         "The document to be viewed",
+                         EXAMPLE_TYPE_DOCUMENT,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/example/ui/example-document-view.ui");
+  gtk_widget_class_bind_template_child (widget_class, ExampleDocumentView, text_view);
+}
+
+static void
+example_document_view_init (ExampleDocumentView *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+GtkWidget *
+example_document_view_new (void)
+{
+  return g_object_new (EXAMPLE_TYPE_DOCUMENT, NULL);
+}
diff --git a/examples/app/example-document-view.h b/examples/app/example-document-view.h
new file mode 100644
index 0000000..41fa8b0
--- /dev/null
+++ b/examples/app/example-document-view.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "example-document.h"
+
+G_BEGIN_DECLS
+
+#define EXAMPLE_TYPE_DOCUMENT_VIEW (example_document_view_get_type())
+
+G_DECLARE_FINAL_TYPE (ExampleDocumentView, example_document_view, EXAMPLE, DOCUMENT_VIEW, GtkBin)
+
+GtkWidget *example_document_view_new (void);
+
+G_END_DECLS
diff --git a/examples/app/example-document-view.ui b/examples/app/example-document-view.ui
new file mode 100644
index 0000000..fe3ebb3
--- /dev/null
+++ b/examples/app/example-document-view.ui
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="ExampleDocumentView" parent="GtkBin">
+    <child>
+      <object class="GtkScrolledWindow">
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkTextView" id="text_view">
+            <property name="monospace">true</property>
+            <property name="visible">true</property>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/examples/app/example-document.c b/examples/app/example-document.c
new file mode 100644
index 0000000..822de47
--- /dev/null
+++ b/examples/app/example-document.c
@@ -0,0 +1,109 @@
+#include "example-document.h"
+
+struct _ExampleDocument
+{
+  GtkTextBuffer parent_instance;
+  gchar *title;
+};
+
+enum {
+  PROP_0,
+  PROP_TITLE,
+  N_PROPS
+};
+
+G_DEFINE_TYPE (ExampleDocument, example_document, GTK_TYPE_TEXT_BUFFER)
+
+static guint last_untitled;
+static GParamSpec *properties [N_PROPS];
+
+static void
+example_document_constructed (GObject *object)
+{
+  ExampleDocument *self = (ExampleDocument *)object;
+
+  if (self->title == NULL)
+    self->title = g_strdup_printf ("Untitled Document %u", ++last_untitled);
+
+  G_OBJECT_CLASS (example_document_parent_class)->constructed (object);
+}
+
+static void
+example_document_finalize (GObject *object)
+{
+  ExampleDocument *self = (ExampleDocument *)object;
+
+  g_clear_pointer (&self->title, g_free);
+
+  G_OBJECT_CLASS (example_document_parent_class)->finalize (object);
+}
+
+static void
+example_document_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  ExampleDocument *self = EXAMPLE_DOCUMENT (object);
+
+  switch (prop_id)
+    {
+    case PROP_TITLE:
+      g_value_set_string (value, self->title);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+example_document_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  ExampleDocument *self = EXAMPLE_DOCUMENT (object);
+
+  switch (prop_id)
+    {
+    case PROP_TITLE:
+      g_free (self->title);
+      self->title = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+example_document_class_init (ExampleDocumentClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = example_document_finalize;
+  object_class->constructed = example_document_constructed;
+  object_class->get_property = example_document_get_property;
+  object_class->set_property = example_document_set_property;
+
+  properties [PROP_TITLE] =
+    g_param_spec_string ("title",
+                         "Title",
+                         "The title of the document",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+example_document_init (ExampleDocument *self)
+{
+}
+
+ExampleDocument *
+example_document_new (void)
+{
+  return g_object_new (EXAMPLE_TYPE_DOCUMENT, NULL);
+}
diff --git a/examples/app/example-document.h b/examples/app/example-document.h
new file mode 100644
index 0000000..e24815c
--- /dev/null
+++ b/examples/app/example-document.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EXAMPLE_TYPE_DOCUMENT (example_document_get_type())
+
+G_DECLARE_FINAL_TYPE (ExampleDocument, example_document, EXAMPLE, DOCUMENT, GtkTextBuffer)
+
+ExampleDocument *example_document_new (void);
+
+G_END_DECLS
diff --git a/examples/app/example-window.c b/examples/app/example-window.c
index 7b2d331..3951a1f 100644
--- a/examples/app/example-window.c
+++ b/examples/app/example-window.c
@@ -7,35 +7,118 @@
 
 struct _ExampleWindow
 {
-  GtkWindow   parent_instance;
+  GtkApplicationWindow parent_instance;
   DzlDockBin *dockbin;
+  GtkNotebook *notebook;
+  DzlEmptyState *empty_state;
+  GtkStack *stack;
 };
 
-G_DEFINE_TYPE (ExampleWindow, example_window, GTK_TYPE_WINDOW)
+G_DEFINE_TYPE (ExampleWindow, example_window, GTK_TYPE_APPLICATION_WINDOW)
 
 static const DzlShortcutEntry shortcuts[] = {
   { "com.example.window.NewDoc", NULL, N_("Editing"), N_("Documents"), N_("New Document"), N_("Create a new 
document") },
   { "com.example.window.CloseDoc", NULL, N_("Editing"), N_("Documents"), N_("Close Document"), N_("Close the 
current document") },
 };
 
+ExampleDocumentView *
+example_window_get_current_document_view (ExampleWindow *self)
+{
+  gint page;
+
+  g_assert (EXAMPLE_IS_WINDOW (self));
+
+  page = gtk_notebook_get_current_page (self->notebook);
+
+  if (page == -1)
+    return NULL;
+
+  return EXAMPLE_DOCUMENT_VIEW (gtk_notebook_get_nth_page (self->notebook, page));
+}
+
+static GtkWidget *
+new_label (ExampleDocument *document)
+{
+  GtkWidget *box;
+  GtkWidget *label;
+  GtkWidget *button;
+
+  box = g_object_new (GTK_TYPE_BOX,
+                      "visible", TRUE,
+                      "spacing", 3,
+                      NULL);
+  label = g_object_new (GTK_TYPE_LABEL,
+                        "xalign", 0.0f,
+                        "visible", TRUE,
+                        NULL);
+  g_object_bind_property (document, "title", label, "label", G_BINDING_SYNC_CREATE);
+  button = g_object_new (GTK_TYPE_BUTTON,
+                         "action-name", "win.close-document",
+                         "visible", TRUE,
+                         "child", g_object_new (GTK_TYPE_IMAGE,
+                                                "visible", TRUE,
+                                                "icon-name", "window-close-symbolic",
+                                                "icon-size", GTK_ICON_SIZE_MENU,
+                                                NULL),
+                         NULL);
+  dzl_gtk_widget_add_style_class (button, "flat");
+  dzl_gtk_widget_add_style_class (button, "image-button");
+  gtk_container_add (GTK_CONTAINER (box), label);
+  gtk_container_add (GTK_CONTAINER (box), button);
+
+  return box;
+}
+
 static void
-new_document_cb (ExampleWindow *self,
+new_document_cb (GSimpleAction *action,
+                 GVariant      *param,
                  gpointer       user_data)
 {
+  ExampleWindow *self = user_data;
+  g_autoptr(ExampleDocument) document = NULL;
+  GtkWidget *view;
+  GtkWidget *label;
+  gint page;
+
   g_assert (EXAMPLE_IS_WINDOW (self));
 
-  g_print ("New document!\n");
+  document = example_document_new ();
+  view = g_object_new (EXAMPLE_TYPE_DOCUMENT_VIEW,
+                       "visible", TRUE,
+                       "document", document,
+                       NULL);
+  label = new_label (document);
+
+  page = gtk_notebook_append_page (self->notebook, view, label);
+  gtk_notebook_set_current_page (self->notebook, page);
+  gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->notebook));
+  gtk_widget_grab_focus (view);
 }
 
 static void
-close_document_cb (ExampleWindow *self,
+close_document_cb (GSimpleAction *action,
+                   GVariant      *param,
                    gpointer       user_data)
 {
+  ExampleWindow *self = user_data;
+  ExampleDocumentView *view;
+
   g_assert (EXAMPLE_IS_WINDOW (self));
 
-  g_print ("Close document!\n");
+  view = example_window_get_current_document_view (self);
+
+  if (view != NULL)
+    gtk_widget_destroy (GTK_WIDGET (view));
+
+  if (gtk_notebook_get_n_pages (self->notebook) == 0)
+    gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->empty_state));
 }
 
+static const GActionEntry actions[] = {
+  { "new-document", new_document_cb },
+  { "close-document", close_document_cb },
+};
+
 static void
 example_window_class_init (ExampleWindowClass *klass)
 {
@@ -43,6 +126,9 @@ example_window_class_init (ExampleWindowClass *klass)
 
   gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/example/ui/example-window.ui");
   gtk_widget_class_bind_template_child (widget_class, ExampleWindow, dockbin);
+  gtk_widget_class_bind_template_child (widget_class, ExampleWindow, notebook);
+  gtk_widget_class_bind_template_child (widget_class, ExampleWindow, empty_state);
+  gtk_widget_class_bind_template_child (widget_class, ExampleWindow, stack);
 }
 
 static void
@@ -57,20 +143,15 @@ example_window_init (ExampleWindow *self)
 
   dzl_shortcut_manager_set_theme_name (NULL, "default");
 
-  dzl_shortcut_controller_add_command_callback (controller,
-                                                "com.example.window.NewDoc",
-                                                NULL,
-                                                (GtkCallback) new_document_cb, NULL, NULL);
-
-  dzl_shortcut_controller_add_command_callback (controller,
-                                                "com.example.window.CloseDoc",
-                                                NULL,
-                                                (GtkCallback) close_document_cb, NULL, NULL);
+  dzl_shortcut_controller_add_command_action (controller, "com.example.window.NewDoc", NULL, 
"win.new-document");
+  dzl_shortcut_controller_add_command_action (controller, "com.example.window.CloseDoc", NULL, 
"win.close-document");
 
   g_signal_connect_swapped (self,
                             "key-press-event",
                             G_CALLBACK (dzl_shortcut_manager_handle_event),
                             dzl_shortcut_manager_get_default ());
+
+  g_action_map_add_action_entries (G_ACTION_MAP (self), actions, G_N_ELEMENTS (actions), self);
 }
 
 GtkWidget *
diff --git a/examples/app/example-window.h b/examples/app/example-window.h
index f848403..c25d27a 100644
--- a/examples/app/example-window.h
+++ b/examples/app/example-window.h
@@ -1,16 +1,16 @@
-#ifndef EXAMPLE_WINDOW_H
-#define EXAMPLE_WINDOW_H
+#pragma once
 
 #include <gtk/gtk.h>
 
+#include "example-document-view.h"
+
 G_BEGIN_DECLS
 
 #define EXAMPLE_TYPE_WINDOW (example_window_get_type())
 
-G_DECLARE_FINAL_TYPE (ExampleWindow, example_window, EXAMPLE, WINDOW, GtkWindow)
+G_DECLARE_FINAL_TYPE (ExampleWindow, example_window, EXAMPLE, WINDOW, GtkApplicationWindow)
 
-GtkWidget *example_window_new (void);
+GtkWidget           *example_window_new                       (void);
+ExampleDocumentView *example_window_get_current_document_view (ExampleWindow *self);
 
 G_END_DECLS
-
-#endif /* EXAMPLE_WINDOW_H */
diff --git a/examples/app/example-window.ui b/examples/app/example-window.ui
index ea02939..28198bd 100644
--- a/examples/app/example-window.ui
+++ b/examples/app/example-window.ui
@@ -1,25 +1,53 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <template class="ExampleWindow" parent="GtkWindow">
+  <template class="ExampleWindow" parent="GtkApplicationWindow">
+    <child type="titlebar">
+      <object class="GtkHeaderBar" id="header_bar">
+        <property name="show-close-button">true</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkButton">
+            <property name="action-name">win.new-document</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkImage">
+                <property name="icon-name">document-new-symbolic</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="pack-type">start</property>
+          </packing>
+        </child>
+      </object>
+    </child>
     <child>
       <object class="DzlDockBin" id="dockbin">
         <property name="visible">true</property>
         <child>
-          <object class="GtkNotebook" id="notebook">
+          <object class="GtkStack" id="stack">
             <property name="expand">true</property>
+            <property name="transition-type">crossfade</property>
             <property name="visible">true</property>
             <child>
-              <object class="GtkScrolledWindow">
+              <object class="DzlEmptyState" id="empty_state">
+                <property name="icon-name">document-new-symbolic</property>
+                <property name="pixel-size">128</property>
+                <property name="title" translatable="yes">No documents are open</property>
+                <property name="subtitle" translatable="yes">Open a document to see it’s contents</property>
+                <property name="visible">true</property>
+              </object>
+              <packing>
+                <property name="name">empty_state</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkNotebook" id="notebook">
                 <property name="visible">true</property>
-                <child>
-                  <object class="GtkTextView">
-                    <property name="monospace">true</property>
-                    <property name="visible">true</property>
-                  </object>
-                </child>
               </object>
               <packing>
-                <property name="tab-label">Document 1</property>
+                <property name="name">notebook</property>
               </packing>
             </child>
           </object>
diff --git a/examples/app/example.gresources.xml b/examples/app/example.gresources.xml
index 955a67d..5675300 100644
--- a/examples/app/example.gresources.xml
+++ b/examples/app/example.gresources.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gresources>
   <gresource prefix="/org/gnome/example/ui">
+    <file>example-document-view.ui</file>
     <file>example-window.ui</file>
   </gresource>
 
diff --git a/examples/app/meson.build b/examples/app/meson.build
index 4b52352..dca5a96 100644
--- a/examples/app/meson.build
+++ b/examples/app/meson.build
@@ -7,6 +7,10 @@ example_resources = gnome.compile_resources(
 example_application_sources = [
   'example-application.c',
   'example-application.h',
+  'example-document.c',
+  'example-document.h',
+  'example-document-view.c',
+  'example-document-view.h',
   'example-window.c',
   'example-window.h',
   'main.c',


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