GTK+ Forums

Discussion forum for GTK+ and Programming. Ask questions, troubleshoot problems, view and post example code, or express your opinions.
It is currently Fri Oct 24, 2014 9:37 am

All times are UTC




Post new topic Reply to topic  [ 3 posts ] 
Author Message
 Post subject: GtkReflector widget
PostPosted: Wed Sep 17, 2008 2:36 am 
Offline
Never Seen the Sunlight

Joined: Thu Jun 14, 2007 11:02 pm
Posts: 923
Location: Falun, WI USA
I saw this on youtube and I had to try to do it myself
http://www.youtube.com/watch?v=2JNDC6K15Wk

I've made an implementation of this using GdkPixbuf.
So far I've only had the chance to test it on windows, but hopefully it will work on other platforms and I'll be able to fix any bugs that are reported here.
Any feedback is welcome.
EDIT: It just occurred to me that GtkMirror may be a better name, what do you think?

Here's the source:
gtkreflector.c:
Code:
/* you'll want the new version */


gtkreflector.h:
Code:
/******************************************************************************
* Copyright (C) 2008 by Tanner Jotblad <dreblen@users.sourceforge.net>       *
*                                                                            *
* GtkReflector 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.                                        *
*                                                                            *
* GtkReflector 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 GtkReflector.  If not, see <http://www.gnu.org/licenses/>.      *
*****************************************************************************/

#ifndef GTK_REFLECTOR_H_INCLUDED
#define GTK_REFLECTOR_H_INCLUDED

#include <gtk/gtk.h>

G_BEGIN_DECLS

#define GTK_TYPE_REFLECTOR (gtk_reflector_get_type())
#define GTK_REFLECTOR(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GTK_TYPE_REFLECTOR, GtkReflector))
#define GTK_REFLECTOR_CLASS(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GTK_TYPE_REFLECTOR))
#define GTK_IS_REFLECTOR(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GTK_TYPE_REFLECTOR))
#define GTK_IS_REFLECTOR_CLASS(o) (G_TYPE_CHECK_CLASS_TYPE((o), GTK_TYPE_REFLECTOR))
#define GTK_REFLECTOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GTK_TYPE_REFLECTOR, GtkReflectorClass))

typedef struct _GtkReflector GtkReflector;
typedef struct _GtkReflectorClass GtkReflectorClass;

struct _GtkReflector
{
   GtkVBox parent_instance;
};

struct _GtkReflectorClass
{
   GtkVBoxClass parent_class;
};

GType gtk_reflector_get_type(void);

GtkWidget *gtk_reflector_new(GtkWindow *parent);

G_END_DECLS

#endif /* GTK_REFLECTOR_H_INCLUDED */


main.c (example program):
Code:
#include <gtk/gtk.h>
#include "gtkreflector.h"

int main(int argc, char **argv)
{
   GtkWidget *win;
   GtkWidget *vbox, *vbox2;
   GtkWidget *refl;
   GtkWidget *hbox;
   GtkWidget *label;
   GtkWidget *entry;
   GtkWidget *button;

   gtk_init(&argc, &argv);

   win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
   g_signal_connect(win, "delete-event", G_CALLBACK(gtk_main_quit), NULL);

   vbox = gtk_vbox_new(FALSE, 2);
   gtk_container_add(GTK_CONTAINER(win), vbox);
   gtk_widget_show(vbox);

   refl = gtk_reflector_new(GTK_WINDOW(win));
   gtk_box_pack_start(GTK_BOX(vbox), refl, FALSE, FALSE, 0);
   gtk_widget_show(refl);

   label = gtk_label_new("This isn't reflected");
   gtk_box_pack_start(GTK_BOX(refl), label, FALSE, FALSE, 0);
   gtk_widget_show(label);

   vbox2 = gtk_vbox_new(TRUE, 2);
   gtk_box_pack_start(GTK_BOX(refl), vbox2, FALSE, FALSE, 0);
   gtk_widget_show(vbox2);

   hbox = gtk_hbox_new(TRUE, 0);
   gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
   gtk_widget_show(hbox);

   label = gtk_label_new("Enter some text:");
   gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
   gtk_widget_show(label);

   entry = gtk_entry_new();
   gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
   gtk_widget_show(entry);

   button = gtk_button_new_with_label("Some button");
   gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0);
   gtk_widget_show(button);

   label = gtk_label_new("This isn't reflected either");
   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
   gtk_widget_show(label);

   gtk_widget_show(win);

   gtk_main();

   return 0;
}


The example program results in this:
Image


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 18, 2008 4:24 am 
Offline
Never Seen the Sunlight

Joined: Thu Jun 14, 2007 11:02 pm
Posts: 923
Location: Falun, WI USA
Update: The idle function that's used to get the reflection was never destroyed when the widget was destroyed.
This caused crashes if you destroyed the reflector before the main loop was done.
This was fixed by creating a GObjectClass->finalize function.

The only file that's changed is gtkreflector.c, so you can still use the other two unchanged.

gtkreflector.c:
Code:
/******************************************************************************
* Copyright (C) 2008 by Tanner Jotblad <dreblen@users.sourceforge.net>       *
*                                                                            *
* GtkReflector 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.                                        *
*                                                                            *
* GtkReflector 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 GtkReflector.  If not, see <http://www.gnu.org/licenses/>.      *
*****************************************************************************/

#include <gtk/gtk.h>
#include "gtkreflector.h"

enum
{
   PROP_0 = 0,
   PROP_REFLECTION,
   PROP_PARENT,
};

typedef struct _GtkReflectorPrivate
{
   GtkWidget *reflection;
   GtkWidget *parent;
   guint idle_id;
} GtkReflectorPrivate;

#define GTK_REFLECTOR_GET_PRIVATE(o)   (G_TYPE_INSTANCE_GET_PRIVATE((o), GTK_TYPE_REFLECTOR, GtkReflectorPrivate))

G_DEFINE_TYPE(GtkReflector, gtk_reflector, GTK_TYPE_VBOX)

static gboolean gtk_reflector_reflection_expose_cb(GtkWidget *hbox, GdkEventExpose *ev, GtkWidget *refl);
static gboolean gtk_reflector_reflection_update_idle(gpointer user_data);
static void gtk_reflector_size_request_cb(GtkReflector *refl, GtkRequisition *req, gpointer user_data);

static void gtk_reflector_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
   GtkReflectorPrivate *priv = GTK_REFLECTOR_GET_PRIVATE(object);

   switch(prop_id)
   {
      case PROP_REFLECTION:
         priv->reflection = g_value_get_object(value);
         break;
      case PROP_PARENT:
         priv->parent = g_value_get_object(value);
         break;
      default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
   }
}

static void gtk_reflector_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
   GtkReflectorPrivate *priv = GTK_REFLECTOR_GET_PRIVATE(object);

   switch(prop_id)
   {
      case PROP_REFLECTION:
         g_value_set_object(value, priv->reflection);
         break;
      case PROP_PARENT:
         g_value_set_object(value, priv->parent);
         break;
      default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
   }
}

static GObject *gtk_reflector_constructor(GType type, guint n_props, GObjectConstructParam *construct_props)
{
   GObject *object;
   GtkReflectorPrivate *priv;

   object = G_OBJECT_CLASS(gtk_reflector_parent_class)->constructor(type, n_props, construct_props);

   priv = GTK_REFLECTOR_GET_PRIVATE(object);

   priv->reflection = gtk_drawing_area_new();
   g_signal_connect(priv->reflection, "expose-event", G_CALLBACK(gtk_reflector_reflection_expose_cb), (gpointer)object);
   gtk_box_pack_start(GTK_BOX(object), priv->reflection, TRUE, TRUE, 0);
   gtk_widget_show(priv->reflection);

   return object;
}

static void gtk_reflector_finalize(GObject *object)
{
   GtkReflectorPrivate *priv;

   priv = GTK_REFLECTOR_GET_PRIVATE(object);
   g_source_remove(priv->idle_id);
}

static void gtk_reflector_class_init(GtkReflectorClass *klass)
{
   GObjectClass *gobject_class;

   gobject_class = G_OBJECT_CLASS(klass);
   gobject_class->constructor = gtk_reflector_constructor;
   gobject_class->set_property = gtk_reflector_set_property;
   gobject_class->get_property = gtk_reflector_get_property;
   gobject_class->finalize = gtk_reflector_finalize;

   g_type_class_add_private(klass, sizeof(GtkReflectorPrivate));

   g_object_class_install_property(gobject_class, PROP_REFLECTION, g_param_spec_object("reflection",
                                                                  "Reflection",
                                                                  "The widget holding the reflection",
                                                                  GTK_TYPE_WIDGET,
                                                                  G_PARAM_READWRITE));
   g_object_class_install_property(gobject_class, PROP_PARENT, g_param_spec_object("parent",
                                                               "Parent",
                                                               "The widget's toplevel parent",
                                                               GTK_TYPE_WINDOW,
                                                               G_PARAM_READWRITE));
}

static void gtk_reflector_init(GtkReflector *ob)
{
   GtkReflectorPrivate *priv;

   priv = GTK_REFLECTOR_GET_PRIVATE(ob);

   g_signal_connect(ob, "size-request", G_CALLBACK(gtk_reflector_size_request_cb), NULL);
   gtk_box_set_homogeneous(GTK_BOX(ob), FALSE);

   priv->idle_id = g_idle_add(gtk_reflector_reflection_update_idle, (gpointer)ob);
}

GtkWidget *gtk_reflector_new(GtkWindow *parent)
{
   g_return_val_if_fail(GTK_IS_WINDOW(parent), NULL);

   return g_object_new(GTK_TYPE_REFLECTOR, "parent", parent, NULL);
}

static gboolean gtk_reflector_reflection_expose_cb(GtkWidget *wi, GdkEventExpose *ev, GtkWidget *refl)
{
   GtkReflectorPrivate *priv;
   GList *children;
   GtkWidget *child;
   GdkPixbuf *temp_pixbuf;
   GdkPixbuf *child_pixbuf;
   GdkPixbuf *blank_pixbuf;

   gint len;
   gint w, h;
   gint x, y;
   gint i;
   gdouble alpha_step;

   priv = GTK_REFLECTOR_GET_PRIVATE(refl);

   children = gtk_container_get_children(GTK_CONTAINER(refl));

   len = g_list_length(children);
   if(len < 2)
      return TRUE;

   child = GTK_WIDGET(g_list_nth_data(children, len-2));
   g_list_free(children);

   w = child->allocation.width;
   h = child->allocation.height;

   x = child->allocation.x;
   y = child->allocation.y;

   child_pixbuf = gdk_pixbuf_get_from_drawable(NULL, child->window, NULL, x, y, 0, 0, w, h);
   temp_pixbuf = child_pixbuf;
   child_pixbuf = gdk_pixbuf_flip(child_pixbuf, FALSE);
   g_object_unref(temp_pixbuf);

   alpha_step = (gdouble)127/h;
   blank_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, w, h);
   gdk_pixbuf_fill(blank_pixbuf, 0x00000000);
   for(i = 0; i<h; i++)
   {
      temp_pixbuf = blank_pixbuf;
      gdk_pixbuf_composite(child_pixbuf, blank_pixbuf, 0, i, w, 1, 0, 0, 1, 1, GDK_INTERP_BILINEAR, (h-i)*alpha_step);
   }

   gdk_draw_pixbuf(priv->reflection->window, NULL, blank_pixbuf, 0, 0, 0, 0, w, h, GDK_RGB_DITHER_NONE, 0, 0);

   g_object_unref(blank_pixbuf);
   g_object_unref(child_pixbuf);

   return TRUE;
}

static gboolean gtk_reflector_reflection_update_idle(gpointer user_data)
{
   GtkReflectorPrivate *priv;

   priv = GTK_REFLECTOR_GET_PRIVATE(user_data);
   gdk_window_invalidate_rect(priv->reflection->window, NULL, FALSE);
   return TRUE;
}

static void gtk_reflector_size_request_cb(GtkReflector *refl, GtkRequisition *req, gpointer user_data)
{
   GtkReflectorPrivate *priv;
   GList *children;
   GtkWidget *size_ref;
   gint pos;
   gint len;

   priv = GTK_REFLECTOR_GET_PRIVATE(refl);

   children = gtk_container_get_children(GTK_CONTAINER(refl));

   pos = g_list_index(children, (gpointer)priv->reflection);
   len = g_list_length(children);
   if(len == 1)
      return;

   if(pos != len-1)
      gtk_box_reorder_child(GTK_BOX(refl), priv->reflection, -1);

   size_ref = GTK_WIDGET(g_list_nth_data(children, len-2));
   gtk_widget_set_size_request(priv->reflection, -1, size_ref->requisition.height);

   g_list_free(children);
}


Top
 Profile  
 
 Post subject:
PostPosted: Wed Sep 24, 2008 3:43 pm 
Offline
GTK+ Guru

Joined: Thu Aug 07, 2008 12:23 am
Posts: 103
Location: NYC
If you still haven't, I just compiled the test program on fedora 7 -- it looks great and seems to work fine.

I haven't though of anywhere to impliment it myself, but eventually something may present itself...
I imagine it would work great in a "loading..." window like that of the gimp.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 3 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group