gtk2/gtk/gtkmagnifier.c
Matthias Clasen 10d0ca0a3d GtkMagnifier: Add a resizing mode
In addition to the fixed-size mode that is used by the text view,
add a mode in which the magnifier requests enough size to render
the entire inspected widget at the current magnification. In this
mode, the magnifier will update its size when the size of the
inspected widget changes. Also, make the magnifier redraw on its
own whenever the inspected widget draws.
2014-12-20 19:16:46 -05:00

462 lines
12 KiB
C

/* GTK - The GIMP Toolkit
* Copyright © 2013 Carlos Garnacho <carlosg@gnome.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 2 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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtk/gtk.h"
#include "gtkmagnifierprivate.h"
#include "gtkintl.h"
enum {
PROP_INSPECTED = 1,
PROP_RESIZE,
PROP_MAGNIFICATION
};
typedef struct _GtkMagnifierPrivate GtkMagnifierPrivate;
struct _GtkMagnifierPrivate
{
GtkWidget *inspected;
gdouble magnification;
gint x;
gint y;
gboolean resize;
gulong draw_handler;
gulong resize_handler;
};
G_DEFINE_TYPE_WITH_PRIVATE (GtkMagnifier, _gtk_magnifier,
GTK_TYPE_WIDGET)
static void
_gtk_magnifier_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec)
{
switch (param_id)
{
case PROP_INSPECTED:
_gtk_magnifier_set_inspected (GTK_MAGNIFIER (object),
g_value_get_object (value));
break;
case PROP_MAGNIFICATION:
_gtk_magnifier_set_magnification (GTK_MAGNIFIER (object),
g_value_get_double (value));
break;
case PROP_RESIZE:
_gtk_magnifier_set_resize (GTK_MAGNIFIER (object),
g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
}
}
static void
_gtk_magnifier_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec)
{
GtkMagnifier *magnifier;
GtkMagnifierPrivate *priv;
magnifier = GTK_MAGNIFIER (object);
priv = _gtk_magnifier_get_instance_private (magnifier);
switch (param_id)
{
case PROP_INSPECTED:
g_value_set_object (value, priv->inspected);
break;
case PROP_MAGNIFICATION:
g_value_set_double (value, priv->magnification);
break;
case PROP_RESIZE:
g_value_set_boolean (value, priv->resize);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
}
}
static gboolean
_gtk_magnifier_draw (GtkWidget *widget,
cairo_t *cr)
{
GtkAllocation allocation, inspected_alloc;
GtkMagnifier *magnifier;
GtkMagnifierPrivate *priv;
gdouble x, y;
magnifier = GTK_MAGNIFIER (widget);
priv = _gtk_magnifier_get_instance_private (magnifier);
if (priv->inspected == NULL)
return FALSE;
if (!gtk_widget_is_visible (priv->inspected))
return FALSE;
gtk_widget_get_allocation (widget, &allocation);
gtk_widget_get_allocation (priv->inspected, &inspected_alloc);
if (!priv->resize)
cairo_translate (cr, allocation.width / 2, allocation.height / 2);
x = CLAMP (priv->x, 0, inspected_alloc.width);
y = CLAMP (priv->y, 0, inspected_alloc.height);
cairo_save (cr);
cairo_scale (cr, priv->magnification, priv->magnification);
cairo_translate (cr, -x, -y);
g_signal_handler_block (priv->inspected, priv->draw_handler);
gtk_widget_draw (priv->inspected, cr);
g_signal_handler_unblock (priv->inspected, priv->draw_handler);
cairo_restore (cr);
return TRUE;
}
static void
gtk_magnifier_get_preferred_width (GtkWidget *widget,
gint *minimum_width,
gint *natural_width)
{
GtkMagnifier *magnifier;
GtkMagnifierPrivate *priv;
gint width;
magnifier = GTK_MAGNIFIER (widget);
priv = _gtk_magnifier_get_instance_private (magnifier);
if (priv->resize && priv->inspected)
width = priv->magnification * gtk_widget_get_allocated_width (priv->inspected);
else
width = 0;
*minimum_width = width;
*natural_width = width;
}
static void
gtk_magnifier_get_preferred_height (GtkWidget *widget,
gint *minimum_height,
gint *natural_height)
{
GtkMagnifier *magnifier;
GtkMagnifierPrivate *priv;
gint height;
magnifier = GTK_MAGNIFIER (widget);
priv = _gtk_magnifier_get_instance_private (magnifier);
if (priv->resize && priv->inspected)
height = priv->magnification * gtk_widget_get_allocated_height (priv->inspected);
else
height = 0;
*minimum_height = height;
*natural_height = height;
}
static void
resize_handler (GtkWidget *widget,
GtkAllocation *alloc,
GtkWidget *magnifier)
{
gtk_widget_queue_resize (magnifier);
}
static void
connect_resize_handler (GtkMagnifier *magnifier)
{
GtkMagnifierPrivate *priv;
priv = _gtk_magnifier_get_instance_private (magnifier);
if (priv->inspected && priv->resize)
priv->resize_handler = g_signal_connect (priv->inspected, "size-allocate",
G_CALLBACK (resize_handler), magnifier);
}
static void
disconnect_resize_handler (GtkMagnifier *magnifier)
{
GtkMagnifierPrivate *priv;
priv = _gtk_magnifier_get_instance_private (magnifier);
if (priv->resize_handler)
{
g_signal_handler_disconnect (priv->inspected, priv->resize_handler);
priv->resize_handler = 0;
}
}
static gboolean
draw_handler (GtkWidget *widget,
cairo_t *cr,
GtkWidget *magnifier)
{
gtk_widget_queue_draw (magnifier);
return FALSE;
}
static void
connect_draw_handler (GtkMagnifier *magnifier)
{
GtkMagnifierPrivate *priv;
priv = _gtk_magnifier_get_instance_private (magnifier);
if (priv->inspected)
priv->draw_handler = g_signal_connect (priv->inspected, "draw",
G_CALLBACK (draw_handler), magnifier);
}
static void
disconnect_draw_handler (GtkMagnifier *magnifier)
{
GtkMagnifierPrivate *priv;
priv = _gtk_magnifier_get_instance_private (magnifier);
if (priv->draw_handler)
{
g_signal_handler_disconnect (priv->inspected, priv->draw_handler);
priv->draw_handler = 0;
}
}
static void
_gtk_magnifier_destroy (GtkWidget *widget)
{
_gtk_magnifier_set_inspected (GTK_MAGNIFIER (widget), NULL);
GTK_WIDGET_CLASS (_gtk_magnifier_parent_class)->destroy (widget);
}
static void
_gtk_magnifier_class_init (GtkMagnifierClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->set_property = _gtk_magnifier_set_property;
object_class->get_property = _gtk_magnifier_get_property;
widget_class->destroy = _gtk_magnifier_destroy;
widget_class->draw = _gtk_magnifier_draw;
widget_class->get_preferred_width = gtk_magnifier_get_preferred_width;
widget_class->get_preferred_height = gtk_magnifier_get_preferred_height;
g_object_class_install_property (object_class,
PROP_INSPECTED,
g_param_spec_object ("inspected",
P_("Inspected"),
P_("Inspected widget"),
GTK_TYPE_WIDGET,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_MAGNIFICATION,
g_param_spec_double ("magnification",
P_("magnification"),
P_("magnification"),
1, G_MAXDOUBLE, 1,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_RESIZE,
g_param_spec_boolean ("resize",
P_("resize"),
P_("resize"),
FALSE,
G_PARAM_READWRITE));
}
static void
_gtk_magnifier_init (GtkMagnifier *magnifier)
{
GtkWidget *widget = GTK_WIDGET (magnifier);
GtkMagnifierPrivate *priv;
priv = _gtk_magnifier_get_instance_private (magnifier);
gtk_widget_set_events (widget,
gtk_widget_get_events (widget) |
GDK_EXPOSURE_MASK);
gtk_widget_set_has_window (widget, FALSE);
priv->magnification = 1;
priv->resize = FALSE;
}
GtkWidget *
_gtk_magnifier_new (GtkWidget *inspected)
{
g_return_val_if_fail (GTK_IS_WIDGET (inspected), NULL);
return g_object_new (GTK_TYPE_MAGNIFIER,
"inspected", inspected,
NULL);
}
GtkWidget *
_gtk_magnifier_get_inspected (GtkMagnifier *magnifier)
{
GtkMagnifierPrivate *priv;
g_return_val_if_fail (GTK_IS_MAGNIFIER (magnifier), NULL);
priv = _gtk_magnifier_get_instance_private (magnifier);
return priv->inspected;
}
void
_gtk_magnifier_set_inspected (GtkMagnifier *magnifier,
GtkWidget *inspected)
{
GtkMagnifierPrivate *priv;
g_return_if_fail (GTK_IS_MAGNIFIER (magnifier));
priv = _gtk_magnifier_get_instance_private (magnifier);
if (priv->inspected == inspected)
return;
disconnect_draw_handler (magnifier);
disconnect_resize_handler (magnifier);
priv->inspected = inspected;
connect_draw_handler (magnifier);
connect_resize_handler (magnifier);
g_object_notify (G_OBJECT (magnifier), "inspected");
}
void
_gtk_magnifier_set_coords (GtkMagnifier *magnifier,
gdouble x,
gdouble y)
{
GtkMagnifierPrivate *priv;
g_return_if_fail (GTK_IS_MAGNIFIER (magnifier));
priv = _gtk_magnifier_get_instance_private (magnifier);
if (priv->x == x && priv->y == y)
return;
priv->x = x;
priv->y = y;
if (gtk_widget_is_visible (GTK_WIDGET (magnifier)))
gtk_widget_queue_draw (GTK_WIDGET (magnifier));
}
void
_gtk_magnifier_get_coords (GtkMagnifier *magnifier,
gdouble *x,
gdouble *y)
{
GtkMagnifierPrivate *priv;
g_return_if_fail (GTK_IS_MAGNIFIER (magnifier));
priv = _gtk_magnifier_get_instance_private (magnifier);
if (x)
*x = priv->x;
if (y)
*y = priv->y;
}
void
_gtk_magnifier_set_magnification (GtkMagnifier *magnifier,
gdouble magnification)
{
GtkMagnifierPrivate *priv;
g_return_if_fail (GTK_IS_MAGNIFIER (magnifier));
priv = _gtk_magnifier_get_instance_private (magnifier);
if (priv->magnification == magnification)
return;
priv->magnification = magnification;
g_object_notify (G_OBJECT (magnifier), "magnification");
if (priv->resize)
gtk_widget_queue_resize (GTK_WIDGET (magnifier));
if (gtk_widget_is_visible (GTK_WIDGET (magnifier)))
gtk_widget_queue_draw (GTK_WIDGET (magnifier));
}
gdouble
_gtk_magnifier_get_magnification (GtkMagnifier *magnifier)
{
GtkMagnifierPrivate *priv;
g_return_val_if_fail (GTK_IS_MAGNIFIER (magnifier), 1);
priv = _gtk_magnifier_get_instance_private (magnifier);
return priv->magnification;
}
void
_gtk_magnifier_set_resize (GtkMagnifier *magnifier,
gboolean resize)
{
GtkMagnifierPrivate *priv;
g_return_if_fail (GTK_IS_MAGNIFIER (magnifier));
priv = _gtk_magnifier_get_instance_private (magnifier);
if (priv->resize == resize)
return;
priv->resize = resize;
gtk_widget_queue_resize (GTK_WIDGET (magnifier));
if (resize)
connect_resize_handler (magnifier);
else
disconnect_resize_handler (magnifier);
}
gboolean
_gtk_magnifier_get_resize (GtkMagnifier *magnifier)
{
GtkMagnifierPrivate *priv;
g_return_val_if_fail (GTK_IS_MAGNIFIER (magnifier), FALSE);
priv = _gtk_magnifier_get_instance_private (magnifier);
return priv->resize;
}