gtkconstrain widget




Hi,

I wrote a quick container to solve the problem of widgets in different
containers that should have the same size. For example, if you have two
different frames with buttons in each, and you want the buttons to have 
the same size; you can't use a vbox because of the frames.

GtkConstrain is just a GtkBin that "groups" widgets together and scales
them as a percentage of the largest one, for example to make two buttons
the same width:

GtkWidget* constrain1;
GtkWidget* constrain2;
GtkWidget* button1;
GtkWidget* button2;

constrain1 = gtk_constrain_new(TRUE, /* constrain horizontally */ 
                               1.0,  /* scale to 100% of max horizontal
                                        request*/
                               FALSE, /* don't constrain vertically */
                               0.0);  /* irrelevant given FALSE */

constrain2 = gtk_constrain_new(TRUE, 1.0, FALSE, 0.0);

gtk_constrain_set_group(GTK_CONSTRAIN(constrain2),
                        gtk_constrain_get_group(GTK_CONSTRAIN(constrain1));                     

gtk_container_add(GTK_CONTAINER(constrain1), button1);
gtk_container_add(GTK_CONTAINER(constrain2), button2);


Comments welcome. The implementation is a bit incomplete (no object
arguments, no _set_hscale(), etc.). Hope this is useful to someone, feel
free to use in your own code.

Havoc


/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

#ifndef __GTK_CONSTRAIN_H__
#define __GTK_CONSTRAIN_H__


#include <gdk/gdk.h>
#include <gtk/gtkbin.h>


#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

typedef struct _GtkConstrainGroup GtkConstrainGroup;

struct _GtkConstrainGroup {
  gpointer dummy;
};

#define GTK_TYPE_CONSTRAIN		(gtk_constrain_get_type ())
#define GTK_CONSTRAIN(obj)		(GTK_CHECK_CAST ((obj), GTK_TYPE_CONSTRAIN, GtkConstrain))
#define GTK_CONSTRAIN_CLASS(klass)	(GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_CONSTRAIN, GtkConstrainClass))
#define GTK_IS_CONSTRAIN(obj)		(GTK_CHECK_TYPE ((obj), GTK_TYPE_CONSTRAIN))
#define GTK_IS_CONSTRAIN_CLASS(klass)	(GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CONSTRAIN))


typedef struct _GtkConstrain        GtkConstrain;
typedef struct _GtkConstrainClass   GtkConstrainClass;
typedef struct _GtkConstrainChild   GtkConstrainChild;

struct _GtkConstrain
{
  GtkBin bin;

  /*< private >*/
  
  GtkConstrainGroup* group;

  gfloat hscale;
  gfloat vscale;
  guint use_hscale : 1;
  guint use_vscale : 1;
};

struct _GtkConstrainClass
{
  GtkBinClass parent_class;
};

GtkType	   gtk_constrain_get_type	       (void);

GtkWidget* gtk_constrain_new                   (gboolean use_hscale,
                                                gfloat hscale,
                                                gboolean use_vscale, 
                                                gfloat vscale);

void       gtk_constrain_set_group             (GtkConstrain* constrain,
                                                GtkConstrainGroup* group);

GtkConstrainGroup* gtk_constrain_get_group     (GtkConstrain* constrain);

#ifdef __cplusplus
}
#endif /* __cplusplus */


#endif /* __GTK_CONSTRAIN_H__ */
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

#include "gtkconstrain.h"

static GtkConstrainGroup* gtk_constrain_group_new    (void);
static void               gtk_constrain_group_unref  (GtkConstrainGroup* group);
static void               gtk_constrain_group_ref    (GtkConstrainGroup* group);
static void               gtk_constrain_group_add    (GtkConstrainGroup* group,
                                                      GtkConstrain*      constrain);
static void               gtk_constrain_group_remove (GtkConstrainGroup* group,
                                                      GtkConstrain*      constrain);
static void               gtk_constrain_group_size_request (GtkConstrainGroup* group,
                                                            GtkRequisition* requisition);

enum {
  ARG_0
};


static void gtk_constrain_class_init    (GtkConstrainClass  *klass);
static void gtk_constrain_init          (GtkConstrain       *constrain);
static void gtk_constrain_set_arg       (GtkObject      *object,
                                         GtkArg         *arg,
                                         guint           arg_id);
static void gtk_constrain_get_arg       (GtkObject      *object,
                                         GtkArg         *arg,
                                         guint           arg_id);
static void gtk_constrain_finalize      (GtkObject      *object);
static void gtk_constrain_paint         (GtkWidget      *widget,
                                         GdkRectangle   *area);
static void gtk_constrain_draw          (GtkWidget      *widget,
                                         GdkRectangle   *area);
static gint gtk_constrain_expose        (GtkWidget      *widget,
                                         GdkEventExpose *event);
static void gtk_constrain_size_request  (GtkWidget      *widget,
                                         GtkRequisition *requisition);
static void gtk_constrain_size_allocate (GtkWidget      *widget,
                                         GtkAllocation  *allocation);

static GtkBinClass *parent_class = NULL;


GtkType
gtk_constrain_get_type (void)
{
  static GtkType constrain_type = 0;

  if (!constrain_type)
    {
      static const GtkTypeInfo constrain_info =
      {
	"GtkConstrain",
	sizeof (GtkConstrain),
	sizeof (GtkConstrainClass),
	(GtkClassInitFunc) gtk_constrain_class_init,
	(GtkObjectInitFunc) gtk_constrain_init,
        /* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
	(GtkClassInitFunc) NULL,
      };

      constrain_type = gtk_type_unique (gtk_bin_get_type (), &constrain_info);
    }

  return constrain_type;
}

static void
gtk_constrain_class_init (GtkConstrainClass *class)
{
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;

  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;

  parent_class = gtk_type_class (gtk_bin_get_type ());

  object_class->set_arg = gtk_constrain_set_arg;
  object_class->get_arg = gtk_constrain_get_arg;
  object_class->finalize = gtk_constrain_finalize;

  widget_class->draw = gtk_constrain_draw;
  widget_class->expose_event = gtk_constrain_expose;
  widget_class->size_request = gtk_constrain_size_request;
  widget_class->size_allocate = gtk_constrain_size_allocate;
}

static void
gtk_constrain_init (GtkConstrain *constrain)
{
  constrain->group = gtk_constrain_group_new();
  gtk_constrain_group_add(constrain->group, constrain);
  constrain->hscale = 1.0;
  constrain->vscale = 1.0;
  constrain->use_hscale = TRUE;
  constrain->use_vscale = TRUE;
}

static void
gtk_constrain_set_arg (GtkObject      *object,
                       GtkArg         *arg,
                       guint           arg_id)
{
  GtkConstrain *constrain;

  constrain = GTK_CONSTRAIN (object);

  switch (arg_id)
    {

    default:
      break;
    }
}

static void
gtk_constrain_get_arg (GtkObject      *object,
                       GtkArg         *arg,
                       guint           arg_id)
{
  GtkConstrain *constrain;

  constrain = GTK_CONSTRAIN (object);

  switch (arg_id)
    {

    default:
      arg->type = GTK_TYPE_INVALID;
      break;
    }
}

GtkWidget*
gtk_constrain_new (gboolean use_hscale, gfloat hscale,
                   gboolean use_vscale, gfloat vscale)
{
  GtkConstrain *constrain;

  constrain = gtk_type_new (gtk_constrain_get_type ());

  constrain->hscale = hscale;
  constrain->vscale = vscale;
  constrain->use_hscale = use_hscale;
  constrain->use_vscale = use_vscale;
  
  return GTK_WIDGET (constrain);
}

static void
gtk_constrain_finalize (GtkObject *object)
{
  GtkConstrain *constrain;

  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_CONSTRAIN (object));

  constrain = GTK_CONSTRAIN (object);

  (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
}

static void
gtk_constrain_paint (GtkWidget    *widget,
		 GdkRectangle *area)
{
  GtkConstrain *constrain;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_CONSTRAIN (widget));
  g_return_if_fail (area != NULL);

  if (GTK_WIDGET_DRAWABLE (widget))
    {
      constrain = GTK_CONSTRAIN (widget);

    }
}

static void
gtk_constrain_draw (GtkWidget    *widget,
		GdkRectangle *area)
{
  GtkBin *bin;
  GdkRectangle child_area;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_CONSTRAIN (widget));
  g_return_if_fail (area != NULL);

  if (GTK_WIDGET_DRAWABLE (widget))
    {
      bin = GTK_BIN (widget);

      gtk_constrain_paint (widget, area);

      if (bin->child && gtk_widget_intersect (bin->child, area, &child_area))
	gtk_widget_draw (bin->child, &child_area);
    }
}

static gint
gtk_constrain_expose (GtkWidget      *widget,
		  GdkEventExpose *event)
{
  GtkBin *bin;
  GdkEventExpose child_event;

  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_CONSTRAIN (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  if (GTK_WIDGET_DRAWABLE (widget))
    {
      bin = GTK_BIN (widget);

      gtk_constrain_paint (widget, &event->area);

      child_event = *event;
      if (bin->child &&
	  GTK_WIDGET_NO_WINDOW (bin->child) &&
	  gtk_widget_intersect (bin->child, &event->area, &child_event.area))
	gtk_widget_event (bin->child, (GdkEvent*) &child_event);
    }

  return FALSE;
}

static void
gtk_constrain_size_request (GtkWidget      *widget,
			GtkRequisition *requisition)
{
  GtkConstrain *constrain;
  GtkBin *bin;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_CONSTRAIN (widget));
  g_return_if_fail (requisition != NULL);

  constrain = GTK_CONSTRAIN (widget);
  bin = GTK_BIN (widget);

  requisition->width = (GTK_CONTAINER (widget)->border_width * 2);

  requisition->height = (GTK_CONTAINER (widget)->border_width * 2);

  if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
    {
      GtkRequisition child_requisition;
      GtkRequisition group_requisition;
      
      gtk_widget_size_request (bin->child, &child_requisition);

      gtk_constrain_group_size_request(constrain->group,
                                       &group_requisition);

      if (constrain->use_hscale)
        child_requisition.width = constrain->hscale*group_requisition.width;

      if (constrain->use_vscale)
        child_requisition.height = constrain->vscale*group_requisition.height;
      
      requisition->width += child_requisition.width;
      requisition->height += child_requisition.height;
    }
}

static void
gtk_constrain_size_allocate (GtkWidget     *widget,
                             GtkAllocation *allocation)
{
  GtkConstrain *constrain;
  GtkBin *bin;
  GtkAllocation child_allocation;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_CONSTRAIN (widget));
  g_return_if_fail (allocation != NULL);

  constrain = GTK_CONSTRAIN (widget);
  bin = GTK_BIN (widget);
  
  widget->allocation = *allocation;

  if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
    {
      child_allocation.x = GTK_CONTAINER (constrain)->border_width;
      child_allocation.width = MAX(1, (gint)allocation->width - child_allocation.x * 2);

      child_allocation.y = GTK_CONTAINER (constrain)->border_width;
      
      child_allocation.height = MAX (1, ((gint)allocation->height - child_allocation.y -
					 (gint)GTK_CONTAINER (constrain)->border_width));

      child_allocation.x += allocation->x;
      child_allocation.y += allocation->y;

      gtk_widget_size_allocate (bin->child, &child_allocation);
    }
}

/*
 *  Non-widget methods
 */

void
gtk_constrain_set_group (GtkConstrain* constrain,
                         GtkConstrainGroup* group)
{
  g_return_if_fail(constrain != NULL);
  g_return_if_fail(GTK_IS_CONSTRAIN(constrain));

  /* ref first in case constrain->group == group */
  gtk_constrain_group_ref(group);

  gtk_constrain_group_remove(constrain->group, constrain);
  
  gtk_constrain_group_unref(constrain->group);

  constrain->group = group;

  gtk_constrain_group_add(group, constrain);
}

GtkConstrainGroup*
gtk_constrain_get_group     (GtkConstrain* constrain)
{
  g_return_val_if_fail(constrain != NULL, NULL);
  
  return constrain->group;
}

/*
 * GtkConstrainGroup
 */

typedef struct _GtkConstrainGroupPrivate GtkConstrainGroupPrivate;

struct _GtkConstrainGroupPrivate {
  GSList* members;
  guint refcount;
};

static GtkConstrainGroup*
gtk_constrain_group_new    (void)
{
  GtkConstrainGroupPrivate* priv;

  priv = g_new0(GtkConstrainGroupPrivate, 1);

  priv->refcount = 1;
  
  return (GtkConstrainGroup*)priv;
}

static void
gtk_constrain_group_unref  (GtkConstrainGroup* group)
{
  GtkConstrainGroupPrivate* priv = (GtkConstrainGroupPrivate*)group;

  g_return_if_fail(priv != NULL);
  g_return_if_fail(priv->refcount != 0);

  priv->refcount -= 1;

  if (priv->refcount == 0)
    {
      g_return_if_fail(priv->members == NULL);
      g_free(priv);
    }
}

static void
gtk_constrain_group_ref    (GtkConstrainGroup* group)
{
  GtkConstrainGroupPrivate* priv = (GtkConstrainGroupPrivate*)group;

  g_return_if_fail(priv != NULL);
  g_return_if_fail(priv->refcount != 0);

  priv->refcount += 1;
}

static void
gtk_constrain_group_add    (GtkConstrainGroup* group,
                            GtkConstrain*      constrain)
{
  GtkConstrainGroupPrivate* priv = (GtkConstrainGroupPrivate*)group;
  
  g_return_if_fail(priv != NULL);
  g_return_if_fail(priv->refcount != 0);

  priv->members = g_slist_prepend(priv->members, constrain);
}

static void
gtk_constrain_group_remove (GtkConstrainGroup* group,
                            GtkConstrain*      constrain)
{
  GtkConstrainGroupPrivate* priv = (GtkConstrainGroupPrivate*)group;

  g_return_if_fail(priv != NULL);
  g_return_if_fail(priv->refcount != 0);
  
  priv->members = g_slist_remove(priv->members, constrain);
}

static void
gtk_constrain_group_size_request (GtkConstrainGroup* group,
                                  GtkRequisition*    requisition)
{
  GtkConstrainGroupPrivate* priv = (GtkConstrainGroupPrivate*)group;
  GSList* tmp;
  
  g_return_if_fail(priv != NULL);
  g_return_if_fail(priv->refcount != 0);
  g_return_if_fail(priv->members != NULL);

  requisition->width = 0;
  requisition->height = 0;
  
  tmp = priv->members;

  while (tmp != NULL)
    {
      GtkRequisition this_req;

      gtk_widget_size_request(GTK_BIN(tmp->data)->child, &this_req);
      
      requisition->width = MAX(this_req.width, requisition->width);
      requisition->height = MAX(this_req.height, requisition->height);
          
      tmp = g_slist_next(tmp);
    }
}








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