pspp-dev
[Top][All Lists]
Advanced

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

[PATCH 11/17] pspp-widget-facade: New code to measure and render some GT


From: Ben Pfaff
Subject: [PATCH 11/17] pspp-widget-facade: New code to measure and render some GTK+ widgets.
Date: Sun, 22 Apr 2012 11:12:29 -0700

There are two upcoming uses for this in the tree, both related to
rendering fake buttons.  First, creating, maintaining, and rendering
the column header buttons in a PsppSheetView is a huge cost when
there are hundreds or thousands of columns.  Using this facade code
to "fake" buttons makes the sheet much faster.  Second, this code
will be used to implement a cell renderer for GtkButton, so that
buttons can be put into PsppSheetView rows.
---
 src/ui/gui/automake.mk          |    2 +
 src/ui/gui/pspp-widget-facade.c |  356 +++++++++++++++++++++++++++++++++++++++
 src/ui/gui/pspp-widget-facade.h |   87 ++++++++++
 3 files changed, 445 insertions(+), 0 deletions(-)
 create mode 100644 src/ui/gui/pspp-widget-facade.c
 create mode 100644 src/ui/gui/pspp-widget-facade.h

diff --git a/src/ui/gui/automake.mk b/src/ui/gui/automake.mk
index 010cdb4..924379f 100644
--- a/src/ui/gui/automake.mk
+++ b/src/ui/gui/automake.mk
@@ -139,6 +139,8 @@ src_ui_gui_psppire_SOURCES = \
        src/ui/gui/pspp-sheet-view-column.h \
        src/ui/gui/pspp-sheet-view.c \
        src/ui/gui/pspp-sheet-view.h \
+       src/ui/gui/pspp-widget-facade.c \
+       src/ui/gui/pspp-widget-facade.h \
        src/ui/gui/psppire-dialog.c \
        src/ui/gui/psppire-keypad.c \
        src/ui/gui/psppire-selector.c \
diff --git a/src/ui/gui/pspp-widget-facade.c b/src/ui/gui/pspp-widget-facade.c
new file mode 100644
index 0000000..931de53
--- /dev/null
+++ b/src/ui/gui/pspp-widget-facade.c
@@ -0,0 +1,356 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2011, 2012 Free Software Foundation, Inc.
+
+   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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "pspp-widget-facade.h"
+
+#include <math.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void
+inset_rectangle (const GdkRectangle *src,
+                 const GtkBorder *inset,
+                 GdkRectangle *dst)
+{
+  dst->x = src->x + inset->left;
+  dst->y = src->y + inset->top;
+  dst->width = MAX (1, src->width - inset->left - inset->right);
+  dst->height = MAX (1, src->height - inset->top - inset->bottom);
+}
+
+static void
+thicken_border (gint x, gint y, GtkBorder *border)
+{
+  border->left += x;
+  border->right += x;
+  border->top += y;
+  border->bottom += y;
+}
+
+GtkStyle *
+facade_get_style (GtkWidget *base,
+                  GType type1,
+                  ...)
+{
+  GString path, class_path;
+  GType final_type;
+  GtkStyle *style;
+  va_list args;
+  GType type;
+
+  gtk_widget_path (base, NULL, &path.str, NULL);
+  path.len = path.allocated_len = strlen (path.str);
+
+  gtk_widget_class_path (base, NULL, &class_path.str, NULL);
+  class_path.len = class_path.allocated_len = strlen (class_path.str);
+
+  va_start (args, type1);
+  for (type = final_type = type1; type != 0; type = va_arg (args, GType))
+    {
+      const gchar *type_name = g_type_name (type);
+      g_string_append_printf (&path, ".%s", type_name);
+      g_string_append_printf (&class_path, ".%s", type_name);
+      final_type = type;
+    }
+
+  style = gtk_rc_get_style_by_paths (gtk_widget_get_settings (base),
+                                     path.str, class_path.str, final_type);
+
+  free (path.str);
+  free (class_path.str);
+
+  return style;
+}
+
+void
+facade_hbox_get_base_size_request (gint border_width,
+                                   gint spacing,
+                                   gint n_children,
+                                   GtkRequisition *request)
+{
+  request->width = border_width * 2;
+  if (n_children > 1)
+    request->width += spacing * (n_children - 1);
+
+  request->height = border_width * 2;
+}
+
+void
+facade_hbox_add_child_size_request (gint hbox_border_width,
+                                    const GtkRequisition *child_request,
+                                    gint child_padding,
+                                    GtkRequisition *request)
+{
+  request->width += child_request->width + child_padding * 2;
+  request->height = MAX (request->height,
+                         hbox_border_width * 2 + child_request->height);
+}
+
+void
+facade_arrow_get_size_request (gint xpad,
+                               gint ypad,
+                               GtkRequisition *request)
+{
+#define MIN_ARROW_SIZE 15
+  request->width = MIN_ARROW_SIZE + xpad * 2;
+  request->height = MIN_ARROW_SIZE + ypad * 2;
+}
+
+void
+facade_alignment_get_size_request (gint border_width,
+                                   gint padding_left,
+                                   gint padding_right,
+                                   gint padding_top,
+                                   gint padding_bottom,
+                                   const GtkRequisition *child_request,
+                                   GtkRequisition *request)
+{
+  request->width = (border_width * 2 + padding_left + padding_right
+                    + child_request->width);
+  request->height = (border_width * 2 + padding_top + padding_bottom
+                     + child_request->height);
+}
+
+void
+facade_label_get_size_request (gint xpad,
+                               gint ypad,
+                               GtkWidget *base,
+                               const char *text,
+                               GtkRequisition *request)
+{
+  PangoLayout *layout;
+
+  layout = facade_label_get_layout (base, text);
+  facade_label_get_size_request_from_layout (xpad, ypad, layout, request);
+  g_object_unref (layout);
+}
+
+void
+facade_label_get_size_request_from_layout (gint xpad,
+                                           gint ypad,
+                                           PangoLayout *layout,
+                                           GtkRequisition *request)
+{
+  PangoRectangle logical_rect;
+
+  pango_layout_get_extents (layout, NULL, &logical_rect);
+  request->width = xpad * 2 + PANGO_PIXELS (logical_rect.width);
+  request->height = ypad * 2 + PANGO_PIXELS (logical_rect.height);
+}
+
+PangoLayout *
+facade_label_get_layout (GtkWidget *base,
+                         const char *text)
+{
+  PangoAlignment alignment;
+  PangoLayout *layout;
+  gboolean rtl;
+
+  rtl = gtk_widget_get_direction (base) == GTK_TEXT_DIR_RTL;
+  alignment = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
+
+  layout = gtk_widget_create_pango_layout (base, text);
+  pango_layout_set_alignment (layout, alignment);
+
+  return layout;
+}
+
+static void
+facade_button_get_inner_border (GtkStyle *button_style,
+                                GtkBorder *inner_border)
+{
+  GtkBorder *tmp_border;
+
+  gtk_style_get (button_style, GTK_TYPE_BUTTON,
+                 "inner-border", &tmp_border,
+                 NULL);
+
+  if (tmp_border)
+    {
+      *inner_border = *tmp_border;
+      gtk_border_free (tmp_border);
+    }
+  else
+    {
+      static const GtkBorder default_inner_border = { 1, 1, 1, 1 };
+      *inner_border = default_inner_border;
+    }
+}
+
+void
+facade_button_get_size_request (gint border_width,
+                                GtkWidget *base,
+                                GtkStyle *button_style,
+                                const GtkRequisition *child_request,
+                                GtkRequisition *request)
+{
+  GtkBorder inner_border;
+  gint common_width;
+  gint focus_width;
+  gint focus_pad;
+
+  gtk_style_get (button_style, GTK_TYPE_BUTTON,
+                 "focus-line-width", &focus_width,
+                 "focus-padding", &focus_pad,
+                 NULL);
+  facade_button_get_inner_border (button_style, &inner_border);
+
+  common_width = 2 * (border_width + focus_width + focus_pad);
+  request->width = (common_width
+                    + 2 * button_style->xthickness
+                    + inner_border.left + inner_border.right
+                    + child_request->width);
+  request->height = (common_width
+                     + 2 * button_style->ythickness
+                     + inner_border.top + inner_border.bottom
+                     + child_request->height);
+}
+
+void
+facade_button_get_focus_inset (gint border_width,
+                               GtkWidget *base,
+                               GtkStyle *button_style,
+                               GtkBorder *focus_inset)
+{
+  facade_button_get_inner_border (button_style, focus_inset);
+  thicken_border (border_width + button_style->xthickness,
+                  border_width + button_style->ythickness,
+                  focus_inset);
+}
+
+static void
+facade_button_get_label_inset (gint border_width,
+                               GtkWidget *base,
+                               GtkStyle *button_style,
+                               GtkBorder *label_inset)
+{
+  gint focus_width;
+  gint focus_pad;
+
+  facade_button_get_focus_inset (border_width, base, button_style,
+                                 label_inset);
+
+  gtk_style_get (button_style, GTK_TYPE_BUTTON,
+                 "focus-line-width", &focus_width,
+                 "focus-padding", &focus_pad,
+                 NULL);
+  thicken_border (focus_width + focus_pad,
+                  focus_width + focus_pad,
+                  label_inset);
+}
+
+static void
+get_layout_location (GtkWidget *base,
+                     const GdkRectangle *label_area,
+                     PangoLayout *layout,
+                     gint xpad,
+                     gint ypad,
+                     gfloat xalign,
+                     gfloat yalign,
+                     gint *x,
+                     gint *y)
+{
+  PangoRectangle logical;
+  GtkRequisition req;
+
+  if (gtk_widget_get_direction (base) == GTK_TEXT_DIR_LTR)
+    xalign = xalign;
+  else
+    xalign = 1.0 - xalign;
+
+  pango_layout_get_pixel_extents (layout, NULL, &logical);
+
+  facade_label_get_size_request_from_layout (xpad, ypad, layout, &req);
+
+  *x = floor (label_area->x + xpad + xalign * (label_area->width - req.width));
+
+  if (gtk_widget_get_direction (base) == GTK_TEXT_DIR_LTR)
+    *x = MAX (*x, label_area->x + xpad);
+  else
+    *x = MIN (*x, label_area->x + label_area->width - xpad);
+  *x -= logical.x;
+
+  /* bgo#315462 - For single-line labels, *do* align the requisition with
+   * respect to the allocation, even if we are under-allocated.  For multi-line
+   * labels, always show the top of the text when they are under-allocated.
+   * The rationale is this:
+   *
+   * - Single-line labels appear in GtkButtons, and it is very easy to get them
+   *   to be smaller than their requisition.  The button may clip the label,
+   *   but the label will still be able to show most of itself and the focus
+   *   rectangle.  Also, it is fairly easy to read a single line of clipped
+   *   text.
+   *
+   * - Multi-line labels should not be clipped to showing "something in the
+   *   middle".  You want to read the first line, at least, to get some
+   *   context.
+   */
+  if (pango_layout_get_line_count (layout) == 1)
+    *y = floor (label_area->y + ypad
+                + (label_area->height - req.height) * yalign);
+  else
+    *y = floor (label_area->y + ypad
+                + MAX (((label_area->height - req.height) * yalign),
+                       0));
+}
+
+void
+facade_button_render (GtkWidget *base,
+                      GdkDrawable *window,
+                      GdkRectangle *expose_area,
+
+                      GdkRectangle *button_area,
+                      gint border_width,
+                      GtkStyle *button_style,
+                      GtkStateType state_type,
+
+                      GtkStyle *label_style,
+                      const gchar *label,
+                      gint xpad,
+                      gint ypad,
+                      gfloat xalign,
+                      gfloat yalign)
+{
+  GdkRectangle label_area;
+  PangoLayout *layout;
+  GtkBorder inset;
+  gint x, y;
+
+  /* Paint the button. */
+  gtk_paint_box (button_style, window,
+                 state_type,
+                 GTK_SHADOW_OUT, expose_area, base, "button",
+                 button_area->x + border_width,
+                 button_area->y + border_width,
+                 button_area->width - border_width * 2,
+                 button_area->height - border_width * 2);
+
+  /* Figure out where the label should go. */
+  facade_button_get_label_inset (border_width, base, button_style, &inset);
+  inset_rectangle (button_area, &inset, &label_area);
+
+  /* Paint the label. */
+  layout = facade_label_get_layout (base, label);
+  get_layout_location (base, &label_area, layout, xpad, ypad, xalign, yalign,
+                       &x, &y);
+  gtk_paint_layout (label_style, window, state_type, FALSE, expose_area,
+                    base, "label", x, y, layout);
+  g_object_unref (layout);
+}
+
diff --git a/src/ui/gui/pspp-widget-facade.h b/src/ui/gui/pspp-widget-facade.h
new file mode 100644
index 0000000..6583d32
--- /dev/null
+++ b/src/ui/gui/pspp-widget-facade.h
@@ -0,0 +1,87 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2011, 2012 Free Software Foundation, Inc.
+
+   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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#ifndef FACADE_WIDGET_FACADE_H
+#define FACADE_WIDGET_FACADE_H 1
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+GtkStyle *facade_get_style (GtkWidget *base, GType, ...);
+
+void facade_hbox_get_base_size_request (gint border_width,
+                                        gint spacing,
+                                        gint n_children,
+                                        GtkRequisition *);
+void facade_hbox_add_child_size_request (gint hbox_border_width,
+                                         const GtkRequisition *child_request,
+                                         gint child_padding,
+                                         GtkRequisition *);
+
+void facade_arrow_get_size_request (gint xpad,
+                                    gint ypad,
+                                    GtkRequisition *);
+
+
+void facade_alignment_get_size_request (gint border_width,
+                                        gint padding_left,
+                                        gint padding_right,
+                                        gint padding_top,
+                                        gint padding_bottom,
+                                        const GtkRequisition *child_request,
+                                        GtkRequisition *);
+
+void facade_label_get_size_request (gint xpad,
+                                    gint ypad,
+                                    GtkWidget *base,
+                                    const char *text,
+                                    GtkRequisition *);
+void facade_label_get_size_request_from_layout (gint xpad,
+                                                gint ypad,
+                                                PangoLayout *,
+                                                GtkRequisition *);
+PangoLayout *facade_label_get_layout (GtkWidget *base,
+                                      const char *text);
+
+void facade_button_get_size_request (gint border_width,
+                                     GtkWidget *base,
+                                     GtkStyle *button_style,
+                                     const GtkRequisition *child_request,
+                                     GtkRequisition *);
+void facade_button_render (GtkWidget *base,
+                           GdkDrawable *window,
+                           GdkRectangle *expose_area,
+
+                           GdkRectangle *button_area,
+                           gint border_width,
+                           GtkStyle *button_style,
+                           GtkStateType state_type,
+
+                           GtkStyle *label_style,
+                           const gchar *label,
+                           gint xpad,
+                           gint ypad,
+                           gfloat xalign,
+                           gfloat yalign);
+void facade_button_get_focus_inset (gint border_width,
+                                    GtkWidget *base,
+                                    GtkStyle *button_style,
+                                    GtkBorder *focus_inset);
+
+G_END_DECLS
+
+#endif /* FACADE_WIDGET_FACADE_H */
-- 
1.7.2.5




reply via email to

[Prev in Thread] Current Thread [Next in Thread]