[gtksourceview/wip/compact-completion] Compact completion popup window, with a custom container



commit b9acd86cb7e00d78d3bdd934c86d2c81e721de8a
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Tue Jun 11 20:11:25 2013 +0200

    Compact completion popup window, with a custom container
    
    Custom container for the sizing of the GtkTreeView containing the completion
    proposals. When the GtkTreeView exceeds a certain size, the container adds
    the GtkTreeView inside a scrolled window. When the GtkTreeView is small
    enough, no scrolled window is needed, and the container fits the natural size
    of the GtkTreeView.
    
    If the tree view is always in the scrolled window, there are sometimes some
    sizing issues.
    
    The purpose is to have a compact completion window, with a certain size
    limit.

 configure.ac                                 |    2 +-
 docs/reference/Makefile.am                   |    1 +
 gtksourceview/Makefile.am                    |    2 +
 gtksourceview/gtksourcecompletion.c          |   27 ++-
 gtksourceview/gtksourcecompletion.ui         |    2 +-
 gtksourceview/gtksourcecompletioncontainer.c |  330 ++++++++++++++++++++++++++
 gtksourceview/gtksourcecompletioncontainer.h |   61 +++++
 gtksourceview/gtksourcetypes-private.h       |    1 +
 po/POTFILES.in                               |    1 +
 9 files changed, 417 insertions(+), 10 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 853d19f..95c2568 100644
--- a/configure.ac
+++ b/configure.ac
@@ -60,7 +60,7 @@ AC_CHECK_HEADERS([unistd.h])
 GLIB_REQUIRED_VERSION=2.34.0
 GLIB_REQUIRED_VERSION_MACRO=GLIB_VERSION_2_34
 
-GTK_REQUIRED_VERSION=3.7.12
+GTK_REQUIRED_VERSION=3.8.0
 GDK_REQUIRED_VERSION_MACRO=GDK_VERSION_3_8
 
 LIBXML_REQUIRED_VERSION=2.6.0
diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am
index 0ca15ae..0ee0152 100644
--- a/docs/reference/Makefile.am
+++ b/docs/reference/Makefile.am
@@ -21,6 +21,7 @@ CFILE_GLOB = $(top_srcdir)/gtksourceview/*.c
 # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code
 IGNORE_HFILES =                                        \
        config.h                                \
+       gtksourcecompletioncontainer.h          \
        gtksourcecompletionmodel.h              \
        gtksourcecompletion-private.h           \
        gtksourcecompletionwordsbuffer.h        \
diff --git a/gtksourceview/Makefile.am b/gtksourceview/Makefile.am
index bf91768..ab68829 100644
--- a/gtksourceview/Makefile.am
+++ b/gtksourceview/Makefile.am
@@ -46,6 +46,7 @@ libgtksourceview_headers =                    \
        gtksourceview.h
 
 libgtksourceview_private_headers = \
+       gtksourcecompletioncontainer.h          \
        gtksourcecompletionmodel.h              \
        gtksourcecompletion-private.h           \
        gtksourcecontextengine.h                \
@@ -65,6 +66,7 @@ libgtksourceview_private_headers = \
        gtktextregion.h
 
 libgtksourceview_private_c_files = \
+       gtksourcecompletioncontainer.c  \
        gtksourcecompletionmodel.c      \
        gtksourcecontextengine.c        \
        gtksourceengine.c               \
diff --git a/gtksourceview/gtksourcecompletion.c b/gtksourceview/gtksourcecompletion.c
index f14ceb1..fcbdd0a 100644
--- a/gtksourceview/gtksourcecompletion.c
+++ b/gtksourceview/gtksourcecompletion.c
@@ -81,14 +81,12 @@
 #include "gtksourcecompletioninfo.h"
 #include "gtksourcecompletionproposal.h"
 #include "gtksourcecompletionprovider.h"
+#include "gtksourcecompletioncontainer.h"
 #include "gtksourcebuffer.h"
 #include "gtksourceview.h"
 #include "gtksourceview-marshal.h"
 #include "gtksourceview-i18n.h"
 
-#define WINDOW_WIDTH 350
-#define WINDOW_HEIGHT 200
-
 #define GTK_SOURCE_COMPLETION_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object),\
                                                  GTK_SOURCE_TYPE_COMPLETION,           \
                                                  GtkSourceCompletionPrivate))
@@ -2257,15 +2255,16 @@ init_main_window (GtkSourceCompletion *completion,
        gtk_window_set_attached_to (GTK_WINDOW (completion->priv->main_window),
                                    GTK_WIDGET (completion->priv->view));
 
-       gtk_widget_set_size_request (GTK_WIDGET (completion->priv->main_window),
-                                    WINDOW_WIDTH,
-                                    WINDOW_HEIGHT);
-
        g_signal_connect (completion->priv->main_window,
                          "configure-event",
                          G_CALLBACK (gtk_source_completion_configure_event),
                          completion);
 
+       g_signal_connect_swapped (completion->priv->main_window,
+                                 "size-allocate",
+                                 G_CALLBACK (update_window_position),
+                                 completion);
+
        g_signal_connect (completion->priv->main_window,
                          "delete-event",
                          G_CALLBACK (gtk_widget_hide_on_delete),
@@ -2307,21 +2306,33 @@ init_info_window (GtkSourceCompletion *completion)
 static void
 gtk_source_completion_init (GtkSourceCompletion *completion)
 {
+       GError *error = NULL;
        GtkBuilder *builder = gtk_builder_new ();
+       GtkSourceCompletionContainer *container = _gtk_source_completion_container_new ();
+       g_object_ref_sink (container);
 
        completion->priv = GTK_SOURCE_COMPLETION_GET_PRIVATE (completion);
 
        gtk_builder_set_translation_domain (builder, GETTEXT_PACKAGE);
 
+       /* GtkSourceCompletionContainer is a private type. */
+       gtk_builder_expose_object (builder, "completion_container", G_OBJECT (container));
+
        gtk_builder_add_from_resource (builder,
                                       "/org/gnome/gtksourceview/ui/gtksourcecompletion.ui",
-                                      NULL);
+                                      &error);
+
+       if (error != NULL)
+       {
+               g_error ("Error while loading the completion UI: %s", error->message);
+       }
 
        init_tree_view (completion, builder);
        init_main_window (completion, builder);
        init_info_window (completion);
 
        g_object_unref (builder);
+       g_object_unref (container);
 }
 
 void
diff --git a/gtksourceview/gtksourcecompletion.ui b/gtksourceview/gtksourcecompletion.ui
index 97c91db..a00fb2f 100644
--- a/gtksourceview/gtksourcecompletion.ui
+++ b/gtksourceview/gtksourcecompletion.ui
@@ -45,7 +45,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
         <property name="visible">True</property>
         <property name="orientation">vertical</property>
         <child>
-          <object class="GtkScrolledWindow" id="scrolled_window_completion">
+          <object class="GtkSourceCompletionContainer" id="completion_container">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <child>
diff --git a/gtksourceview/gtksourcecompletioncontainer.c b/gtksourceview/gtksourcecompletioncontainer.c
new file mode 100644
index 0000000..8c90019
--- /dev/null
+++ b/gtksourceview/gtksourcecompletioncontainer.c
@@ -0,0 +1,330 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
+/* gtksourcecompletioncontainer.c
+ * This file is part of GtkSourceView
+ *
+ * Copyright (C) 2013 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/*
+ * Custom container for the sizing of the GtkTreeView containing the completion
+ * proposals. When the GtkTreeView exceeds a certain size, the container adds
+ * the GtkTreeView inside a scrolled window. When the GtkTreeView is small
+ * enough, no scrolled window is needed, and the container fits the natural size
+ * of the GtkTreeView.
+ *
+ * If the tree view is always in the scrolled window, there are sometimes some
+ * sizing issues.
+ *
+ * The purpose is to have a compact completion window, with a certain size
+ * limit.
+ */
+
+#include "gtksourcecompletioncontainer.h"
+
+#define MAX_WIDTH  350
+#define MAX_HEIGHT 100
+
+struct _GtkSourceCompletionContainerPrivate
+{
+       GtkTreeView *tree_view;
+       GtkWidget *scrolled_window;
+};
+
+G_DEFINE_TYPE (GtkSourceCompletionContainer, _gtk_source_completion_container, GTK_TYPE_BIN);
+
+#define GTK_SOURCE_COMPLETION_CONTAINER_GET_PRIVATE(object)                    \
+       (G_TYPE_INSTANCE_GET_PRIVATE ((object),                                 \
+                                     GTK_SOURCE_TYPE_COMPLETION_CONTAINER,     \
+                                     GtkSourceCompletionContainerPrivate))
+
+/* gtk_container_add() is overridden. This function calls the GtkBin's add(). */
+static void
+bin_container_add (GtkContainer *container,
+                  GtkWidget    *widget)
+{
+       GTK_CONTAINER_CLASS (_gtk_source_completion_container_parent_class)->add (container, widget);
+}
+
+static gint
+get_vertical_scrollbar_width (void)
+{
+       gint width;
+       GtkWidget *scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, NULL);
+       g_object_ref_sink (scrollbar);
+       gtk_widget_show (scrollbar);
+
+       gtk_widget_get_preferred_width (scrollbar, NULL, &width);
+
+       g_object_unref (scrollbar);
+
+       return width;
+}
+
+static gint
+get_horizontal_scrollbar_height (void)
+{
+       gint height;
+       GtkWidget *scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, NULL);
+       g_object_ref_sink (scrollbar);
+       gtk_widget_show (scrollbar);
+
+       gtk_widget_get_preferred_height (scrollbar, NULL, &height);
+
+       g_object_unref (scrollbar);
+
+       return height;
+}
+
+static void
+remove_scrolled_window (GtkSourceCompletionContainer *container)
+{
+       GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (container->priv->tree_view));
+
+       if (parent != GTK_WIDGET (container))
+       {
+               gtk_container_remove (GTK_CONTAINER (container),
+                                     container->priv->scrolled_window);
+
+               gtk_container_remove (GTK_CONTAINER (container->priv->scrolled_window),
+                                     GTK_WIDGET (container->priv->tree_view));
+
+               bin_container_add (GTK_CONTAINER (container),
+                                  GTK_WIDGET (container->priv->tree_view));
+       }
+}
+
+static void
+add_scrolled_window (GtkSourceCompletionContainer *container)
+{
+       GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (container->priv->tree_view));
+
+       if (parent != container->priv->scrolled_window)
+       {
+               gtk_container_remove (GTK_CONTAINER (container),
+                                     GTK_WIDGET (container->priv->tree_view));
+
+               gtk_container_add (GTK_CONTAINER (container->priv->scrolled_window),
+                                  GTK_WIDGET (container->priv->tree_view));
+
+               bin_container_add (GTK_CONTAINER (container),
+                                  container->priv->scrolled_window);
+       }
+}
+
+static void
+check_scrolled_window (GtkSourceCompletionContainer *container,
+                      GtkRequisition                child_size)
+{
+       if (child_size.width <= MAX_WIDTH &&
+           child_size.height <= MAX_HEIGHT)
+       {
+               remove_scrolled_window (container);
+       }
+       else
+       {
+               add_scrolled_window (container);
+       }
+}
+
+static GtkSizeRequestMode
+_gtk_source_completion_container_get_request_mode (GtkWidget *widget)
+{
+       return GTK_SIZE_REQUEST_CONSTANT_SIZE;
+}
+
+static void
+_gtk_source_completion_container_get_preferred_width (GtkWidget *widget,
+                                                     gint      *min_width,
+                                                     gint      *nat_width)
+{
+       GtkSourceCompletionContainer *container = GTK_SOURCE_COMPLETION_CONTAINER (widget);
+       GtkRequisition nat_size;
+       gint width;
+
+       gtk_widget_get_preferred_size (GTK_WIDGET (container->priv->tree_view),
+                                      NULL,
+                                      &nat_size);
+
+       check_scrolled_window (container, nat_size);
+
+       width = nat_size.width;
+
+       if (MAX_HEIGHT < nat_size.height)
+       {
+               width += get_vertical_scrollbar_width ();
+       }
+
+       width = MIN (width, MAX_WIDTH);
+
+       if (min_width != NULL)
+       {
+               *min_width = width;
+       }
+
+       if (nat_width != NULL)
+       {
+               *nat_width = width;
+       }
+}
+
+/* Return a height at a row boundary of the GtkTreeView. */
+static void
+_gtk_source_completion_container_get_preferred_height (GtkWidget *widget,
+                                                      gint      *min_height,
+                                                      gint      *nat_height)
+{
+       GtkSourceCompletionContainer *container = GTK_SOURCE_COMPLETION_CONTAINER (widget);
+       GtkTreeViewColumn *column;
+       GtkTreeModel *model;
+       GtkRequisition nat_size;
+       gint nb_rows = 0;
+       gint row_height = 0;
+       gint vertical_separator = 0;
+       gint scrollbar_height = 0;
+       gint total_height = 0;
+       gint ret_height = 0;
+
+       if (min_height != NULL)
+       {
+               *min_height = 0;
+       }
+
+       if (nat_height != NULL)
+       {
+               *nat_height = 0;
+       }
+
+       column = gtk_tree_view_get_column (container->priv->tree_view, 0);
+
+       if (column == NULL)
+       {
+               return;
+       }
+
+       gtk_tree_view_column_cell_get_size (column, NULL, NULL, NULL, NULL, &row_height);
+
+       gtk_widget_style_get (GTK_WIDGET (container->priv->tree_view),
+                             "vertical-separator", &vertical_separator,
+                             NULL);
+
+       row_height += vertical_separator;
+
+       model = gtk_tree_view_get_model (container->priv->tree_view);
+
+       if (model == NULL)
+       {
+               return;
+       }
+
+       nb_rows = gtk_tree_model_iter_n_children (model, NULL);
+
+       total_height = nb_rows * row_height;
+
+       gtk_widget_get_preferred_size (GTK_WIDGET (container->priv->tree_view),
+                                      NULL,
+                                      &nat_size);
+
+       check_scrolled_window (container, nat_size);
+
+       if (MAX_WIDTH < nat_size.width)
+       {
+               scrollbar_height = get_horizontal_scrollbar_height ();
+               total_height += scrollbar_height;
+       }
+
+       if (total_height <= MAX_HEIGHT)
+       {
+               ret_height = total_height;
+       }
+       else
+       {
+               gint height_available_for_rows = MAX_HEIGHT - scrollbar_height;
+               gint nb_rows_allowed = height_available_for_rows / row_height;
+
+               ret_height = nb_rows_allowed * row_height + scrollbar_height;
+       }
+
+       if (min_height != NULL)
+       {
+               *min_height = ret_height;
+       }
+
+       if (nat_height != NULL)
+       {
+               *nat_height = ret_height;
+       }
+}
+
+static void
+_gtk_source_completion_container_add (GtkContainer *gtk_container,
+                                     GtkWidget    *widget)
+{
+       GtkSourceCompletionContainer *container = GTK_SOURCE_COMPLETION_CONTAINER (gtk_container);
+
+       g_return_if_fail (GTK_IS_TREE_VIEW (widget));
+
+       g_clear_object (&container->priv->tree_view);
+       container->priv->tree_view = GTK_TREE_VIEW (widget);
+       g_object_ref (container->priv->tree_view);
+
+       bin_container_add (gtk_container, widget);
+}
+
+static void
+_gtk_source_completion_container_init (GtkSourceCompletionContainer *container)
+{
+       container->priv = GTK_SOURCE_COMPLETION_CONTAINER_GET_PRIVATE (container);
+
+       container->priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+       g_object_ref_sink (container->priv->scrolled_window);
+       gtk_widget_show (container->priv->scrolled_window);
+}
+
+static void
+_gtk_source_completion_container_finalize (GObject *object)
+{
+       GtkSourceCompletionContainer *container = GTK_SOURCE_COMPLETION_CONTAINER (object);
+
+       g_clear_object (&container->priv->scrolled_window);
+       g_clear_object (&container->priv->tree_view);
+
+       G_OBJECT_CLASS (_gtk_source_completion_container_parent_class)->finalize (object);
+}
+
+static void
+_gtk_source_completion_container_class_init (GtkSourceCompletionContainerClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+       GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+       object_class->finalize = _gtk_source_completion_container_finalize;
+
+       widget_class->get_request_mode = _gtk_source_completion_container_get_request_mode;
+       widget_class->get_preferred_width = _gtk_source_completion_container_get_preferred_width;
+       widget_class->get_preferred_height = _gtk_source_completion_container_get_preferred_height;
+
+       container_class->add = _gtk_source_completion_container_add;
+
+       g_type_class_add_private (object_class, sizeof (GtkSourceCompletionContainerPrivate));
+}
+
+GtkSourceCompletionContainer *
+_gtk_source_completion_container_new (void)
+{
+       return g_object_new (GTK_SOURCE_TYPE_COMPLETION_CONTAINER, NULL);
+}
diff --git a/gtksourceview/gtksourcecompletioncontainer.h b/gtksourceview/gtksourcecompletioncontainer.h
new file mode 100644
index 0000000..5e44ae1
--- /dev/null
+++ b/gtksourceview/gtksourcecompletioncontainer.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
+ * gtksourcecompletioncontainer.h
+ * This file is part of GtkSourceView
+ *
+ * Copyright (C) 2013 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GTK_SOURCE_COMPLETION_CONTAINER_H__
+#define __GTK_SOURCE_COMPLETION_CONTAINER_H__
+
+#include <gtk/gtk.h>
+#include "gtksourcetypes-private.h"
+
+G_BEGIN_DECLS
+
+#define GTK_SOURCE_TYPE_COMPLETION_CONTAINER             (_gtk_source_completion_container_get_type ())
+#define GTK_SOURCE_COMPLETION_CONTAINER(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GTK_SOURCE_TYPE_COMPLETION_CONTAINER, GtkSourceCompletionContainer))
+#define GTK_SOURCE_COMPLETION_CONTAINER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), 
GTK_SOURCE_TYPE_COMPLETION_CONTAINER, GtkSourceCompletionContainerClass)
+#define GTK_SOURCE_IS_COMPLETION_CONTAINER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GTK_SOURCE_TYPE_COMPLETION_CONTAINER))
+#define GTK_SOURCE_IS_COMPLETION_CONTAINER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GTK_SOURCE_TYPE_COMPLETION_CONTAINER))
+#define GTK_SOURCE_COMPLETION_CONTAINER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GTK_SOURCE_TYPE_COMPLETION_CONTAINER, GtkSourceCompletionContainerClass))
+
+typedef struct _GtkSourceCompletionContainerPrivate    GtkSourceCompletionContainerPrivate;
+typedef struct _GtkSourceCompletionContainerClass      GtkSourceCompletionContainerClass;
+
+struct _GtkSourceCompletionContainer
+{
+       GtkBin parent;
+
+       GtkSourceCompletionContainerPrivate *priv;
+};
+
+struct _GtkSourceCompletionContainerClass
+{
+       GtkBinClass parent_class;
+};
+
+G_GNUC_INTERNAL
+GType           _gtk_source_completion_container_get_type              (void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL
+GtkSourceCompletionContainer *
+                _gtk_source_completion_container_new                   (void);
+
+G_END_DECLS
+
+#endif /* __GTK_SOURCE_COMPLETION_CONTAINER_H__ */
diff --git a/gtksourceview/gtksourcetypes-private.h b/gtksourceview/gtksourcetypes-private.h
index 475b294..6b88c2c 100644
--- a/gtksourceview/gtksourcetypes-private.h
+++ b/gtksourceview/gtksourcetypes-private.h
@@ -26,6 +26,7 @@
 
 G_BEGIN_DECLS
 
+typedef struct _GtkSourceCompletionContainer   GtkSourceCompletionContainer;
 typedef struct _GtkSourceCompletionModel       GtkSourceCompletionModel;
 typedef struct _GtkSourceContextEngine         GtkSourceContextEngine;
 typedef struct _GtkSourceEngine                        GtkSourceEngine;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 002687c..aebf5cf 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -113,6 +113,7 @@ data/styles/tango.xml
 gtksourceview/completion-providers/words/gtksourcecompletionwords.c
 gtksourceview/gtksourcebuffer.c
 gtksourceview/gtksourcecompletion.c
+gtksourceview/gtksourcecompletioncontainer.c
 gtksourceview/gtksourcecompletioncontext.c
 gtksourceview/gtksourcecompletioninfo.c
 gtksourceview/gtksourcecompletionitem.c


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