PIKApp/libpikawidgets/pikaframe.c

265 lines
8.1 KiB
C

/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikaframe.c
* Copyright (C) 2004 Sven Neumann <sven@gimp.org>
*
* This library 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 3 of the License, or (at your option) any later version.
*
* This library 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, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <babl/babl.h>
#include <gtk/gtk.h>
#include "pikawidgetstypes.h"
#include "pikaframe.h"
#include "pikawidgetsutils.h"
/**
* SECTION: pikaframe
* @title: PikaFrame
* @short_description: A widget providing a HIG-compliant subclass
* of #GtkFrame.
*
* A widget providing a HIG-compliant subclass of #GtkFrame.
**/
#define DEFAULT_LABEL_SPACING 6
#define DEFAULT_LABEL_BOLD TRUE
#define PIKA_FRAME_INDENT_KEY "pika-frame-indent"
#define PIKA_FRAME_IN_EXPANDER_KEY "pika-frame-in-expander"
static void pika_frame_style_updated (GtkWidget *widget);
static gboolean pika_frame_draw (GtkWidget *widget,
cairo_t *cr);
static void pika_frame_label_widget_notify (PikaFrame *frame);
static void pika_frame_child_added (PikaFrame *frame,
GtkWidget *child,
gpointer user_data);
static void pika_frame_apply_margins (PikaFrame *frame);
static gint pika_frame_get_indent (PikaFrame *frame);
static gint pika_frame_get_label_spacing (PikaFrame *frame);
G_DEFINE_TYPE (PikaFrame, pika_frame, GTK_TYPE_FRAME)
#define parent_class pika_frame_parent_class
static void
pika_frame_class_init (PikaFrameClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
widget_class->style_updated = pika_frame_style_updated;
widget_class->draw = pika_frame_draw;
gtk_widget_class_install_style_property (widget_class,
g_param_spec_boolean ("label-bold",
"Label Bold",
"Whether the frame's label should be bold",
DEFAULT_LABEL_BOLD,
G_PARAM_READABLE));
gtk_widget_class_install_style_property (widget_class,
g_param_spec_int ("label-spacing",
"Label Spacing",
"The spacing between the label and the frame content",
0,
G_MAXINT,
DEFAULT_LABEL_SPACING,
G_PARAM_READABLE));
}
static void
pika_frame_init (PikaFrame *frame)
{
g_signal_connect (frame, "notify::label-widget",
G_CALLBACK (pika_frame_label_widget_notify),
NULL);
g_signal_connect (frame, "add",
G_CALLBACK (pika_frame_child_added),
NULL);
}
static void
pika_frame_style_updated (GtkWidget *widget)
{
GTK_WIDGET_CLASS (parent_class)->style_updated (widget);
/* font changes invalidate the indentation */
g_object_set_data (G_OBJECT (widget), PIKA_FRAME_INDENT_KEY, NULL);
pika_frame_label_widget_notify (PIKA_FRAME (widget));
pika_frame_apply_margins (PIKA_FRAME (widget));
}
static gboolean
pika_frame_draw (GtkWidget *widget,
cairo_t *cr)
{
GtkWidgetClass *widget_class = g_type_class_peek_parent (parent_class);
return widget_class->draw (widget, cr);
}
static void
pika_frame_label_widget_notify (PikaFrame *frame)
{
GtkWidget *label_widget = gtk_frame_get_label_widget (GTK_FRAME (frame));
if (label_widget)
{
GtkLabel *label = NULL;
if (GTK_IS_LABEL (label_widget))
{
gfloat xalign, yalign;
label = GTK_LABEL (label_widget);
gtk_frame_get_label_align (GTK_FRAME (frame), &xalign, &yalign);
gtk_label_set_xalign (GTK_LABEL (label), xalign);
gtk_label_set_yalign (GTK_LABEL (label), yalign);
}
else if (GTK_IS_BIN (label_widget))
{
GtkWidget *child = gtk_bin_get_child (GTK_BIN (label_widget));
if (GTK_IS_LABEL (child))
label = GTK_LABEL (child);
}
if (label)
{
gboolean bold;
gtk_widget_style_get (GTK_WIDGET (frame),
"label-bold", &bold,
NULL);
pika_label_set_attributes (label,
PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
-1);
}
}
}
static void
pika_frame_child_added (PikaFrame *frame,
GtkWidget *child,
gpointer user_data)
{
pika_frame_apply_margins (frame);
}
static void
pika_frame_apply_margins (PikaFrame *frame)
{
GtkWidget *child = gtk_bin_get_child (GTK_BIN (frame));
if (child)
{
gtk_widget_set_margin_start (child, pika_frame_get_indent (frame));
gtk_widget_set_margin_top (child, pika_frame_get_label_spacing (frame));
}
}
static gint
pika_frame_get_indent (PikaFrame *frame)
{
gint width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (frame),
PIKA_FRAME_INDENT_KEY));
if (! width)
{
PangoLayout *layout;
/* the HIG suggests to use four spaces so do just that */
layout = gtk_widget_create_pango_layout (GTK_WIDGET (frame), " ");
pango_layout_get_pixel_size (layout, &width, NULL);
g_object_unref (layout);
g_object_set_data (G_OBJECT (frame),
PIKA_FRAME_INDENT_KEY, GINT_TO_POINTER (width));
}
return width;
}
static gint
pika_frame_get_label_spacing (PikaFrame *frame)
{
GtkWidget *label_widget = gtk_frame_get_label_widget (GTK_FRAME (frame));
gint spacing = 0;
if ((label_widget && gtk_widget_get_visible (label_widget)) ||
(g_object_get_data (G_OBJECT (frame), PIKA_FRAME_IN_EXPANDER_KEY)))
{
gtk_widget_style_get (GTK_WIDGET (frame),
"label-spacing", &spacing,
NULL);
}
return spacing;
}
/**
* pika_frame_new:
* @label: (nullable): text to set as the frame's title label (or %NULL for no title)
*
* Creates a #PikaFrame widget. A #PikaFrame is a HIG-compliant
* variant of #GtkFrame. It doesn't render a frame at all but
* otherwise behaves like a frame. The frame's title is rendered in
* bold and the frame content is indented four spaces as suggested by
* the GNOME HIG (see https://developer.gnome.org/hig/stable/).
*
* Returns: a new #PikaFrame widget
*
* Since: 2.2
**/
GtkWidget *
pika_frame_new (const gchar *label)
{
GtkWidget *frame;
gboolean expander = FALSE;
/* somewhat hackish, should perhaps be an object property of PikaFrame */
if (label && strcmp (label, "<expander>") == 0)
{
expander = TRUE;
label = NULL;
}
frame = g_object_new (PIKA_TYPE_FRAME,
"label", label,
NULL);
if (expander)
g_object_set_data (G_OBJECT (frame),
PIKA_FRAME_IN_EXPANDER_KEY, (gpointer) TRUE);
return frame;
}