Hey All,

Attached is a Working Sample of a Rectangle with a Dashed Border. (99% Done)

One issue still is when you use the mouse Wheel Scroll really fast...
... it will resize the control too many times and the resize cant keep up.

Any feedback is much appreciated.

Thank you all,
Izzy.
#include <glib.h>
#include <gmodule.h>
#include <stdlib.h>
#include <clutter/clutter.h>
#include <cogl/cogl.h>

// ------ in Controls Header file ------>
G_BEGIN_DECLS

#define TYPE_RECTGRADDASH rect_GradDash_get_type()

#define RECTGRADDASH(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
  TYPE_RECTGRADDASH, RectGradDashClass))

#define RECTGRADDASH_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST ((klass), \
  TYPE_RECTGRADDASH, RectGradDashClass))

#define IS_RECTGRADDASH(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
  TYPE_RECTGRADDASH))

#define IS_RECTGRADDASH_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
  TYPE_RECTGRADDASH))

#define RECTGRADDASH_GET_CLASS(obj) \
  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
  TYPE_RECTGRADDASH, RectGradDashClass))

typedef struct _RectGradDash        RectGradDash;
typedef struct _RectGradDashClass   RectGradDashClass;
typedef struct _RectGradDashChild   RectGradDashChild;
typedef struct _RectGradDashPrivate RectGradDashPrivate;

struct _RectGradDash
{
  ClutterActor           parent;

  /* Allocation of the box */
  ClutterActorBox allocation;

  /* List of Child structures */
  //GList *children;

  /*< private >*/
  RectGradDashPrivate *priv;
};

struct _RectGradDashClass
{
  ClutterActorClass parent_class;
};

static GType rect_GradDash_get_type (void) G_GNUC_CONST;

G_END_DECLS
// ------ in Controls Header file ------<




// ------ in Controls C file ------>
G_DEFINE_TYPE (RectGradDash, rect_GradDash, CLUTTER_TYPE_ACTOR);
//static void clutter_container_iface_init (ClutterContainerIface *iface);
// G_DEFINE_TYPE_WITH_CODE (RectGradDash, rect_GradDash, CLUTTER_TYPE_ACTOR, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_iface_init));

#define RECTGRADDASH_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), TYPE_RECTGRADDASH, RectGradDashPrivate))

struct _RectGradDashPrivate
{
	uint line_thickness;
	uint line_dash_type;
	ClutterColor *line_color;
	ClutterColor *grad_color_stop_1;
	ClutterColor *grad_color_stop_2;
	ClutterColor *grad_color_stop_3;
	ClutterColor *grad_color_stop_4;
	//grad position stuff etc...

	ClutterActor *group;

	// --- A gradient rectangle ---
	ClutterActor *rect;
	ClutterColor *rectColor1;
	ClutterColor *rectColor2;
	// gradient position 1
	// gradient position 2

	// --- 4 line clone rectangles, one template rectangle texture ---
	ClutterActor *rectLine;
	ClutterActor *rectLineClone1;
	ClutterActor *rectLineClone2;
	ClutterActor *rectLineClone3;
	ClutterActor *rectLineClone4;

	// --- rectangle line properties ---
	guint rectLineWidthMax;
	//guint rectLineWidth;
	//guint rectLineHeight;
	guint rectLineThickness;
	guint rectLineThickness_Half;
	guint rectLineType;

	gfloat newWidth;
	gfloat newHeight;

	GTimer *timer_paint_elapsed;
	guint timerID_paint_pending;
	gboolean bNeedToPaintOnce;

	gboolean bInitAllocate;
	gboolean bWasAllocated;

};

static void
rect_GradDash_allocate( ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags )
{
	//g_print( "allocate called..\n" );

	RectGradDash *self = RECTGRADDASH( actor );
	RectGradDashPrivate *priv = self->priv;

	// FOR ACTOR...
	// set the allocation for the whole actor
	CLUTTER_ACTOR_CLASS( rect_GradDash_parent_class )->allocate( actor, box, flags );

	guint w = clutter_actor_box_get_width( box );
	guint h = clutter_actor_box_get_height( box );

	// FOR GROUP - has Rectangle in there
	ClutterActorBox child_box = { 0, };
	child_box.x1 = 0.0;
	child_box.y1 = 0.0;
	child_box.x2 = w;
	child_box.y2 = h;
	clutter_actor_allocate( priv->group, &child_box, flags );

	clutter_actor_allocate( priv->rect, &child_box, flags );

	if( priv->bInitAllocate == FALSE )
	{
		priv->bInitAllocate = TRUE;
		priv->newHeight = clutter_actor_get_height( CLUTTER_ACTOR( self ) );
		priv->newWidth = clutter_actor_get_width( CLUTTER_ACTOR( self ) );
		UpdateLineLayout( priv );
	}

	priv->bWasAllocated = TRUE;
}

/*
static gboolean timeout_paint_pending( gpointer userdata )
{
	RectGradDashPrivate *priv = RECTGRADDASH_GET_PRIVATE( userdata );		// is this SLOW?

	//g_print( "timeout.\n" );

	static gdouble secs = 0;
	static gulong  msecs = 0;
	secs = g_timer_elapsed( priv->timer_paint_elapsed, &msecs );

	if ( priv->bNeedToPaintOnce )
	{
		g_print( "need to paint once\n" );
		// PAINT
		priv->bNeedToPaintOnce = FALSE;
	}

	if ( msecs >= 10000 )
	{
		// PAINT
		g_print( "msecs above 10000...\n" );
		g_timer_start( priv->timer_paint_elapsed );
	}

	return TRUE;	// FALSE Quits this Timeout Callback
}
*/

static void
rect_GradDash_paint(ClutterActor *actor)
{
	RectGradDash *self = RECTGRADDASH( actor );
	RectGradDashPrivate *priv = self->priv;

	clutter_actor_paint( priv->group );
}

static void
rect_GradDash_finalize (GObject *object)
{
  G_OBJECT_CLASS (rect_GradDash_parent_class)->finalize (object);
}

static void
rect_GradDash_dispose (GObject *object)
{
	RectGradDash *self = RECTGRADDASH( object );
	RectGradDashPrivate *priv = self->priv;

/*
  RectGradDashPrivate *priv = RECTGRADDASH_GET_PRIVATE (object);

  if( priv->timer_paint_elapsed != NULL )
  {
	  g_timer_stop( priv->timer_paint_elapsed );
	  g_timer_destroy( priv->timer_paint_elapsed );
	  priv->timer_paint_elapsed = NULL;
  }

  if( priv->timerID_paint_pending != 0 )
  {
	  g_source_remove( priv->timerID_paint_pending );
	  priv->timerID_paint_pending = 0;
  }
*/

  clutter_actor_unparent( CLUTTER_ACTOR( priv->group ) );

  if (priv->group)
  {
      clutter_actor_destroy (priv->group);
      priv->group = NULL;
  }


  G_OBJECT_CLASS (rect_GradDash_parent_class)->dispose (object);
}

void
UpdateLineLayout( RectGradDashPrivate *priv )
{
	// top
	clutter_actor_set_clip( priv->rectLineClone1, 0, 0, priv->newWidth, priv->rectLineThickness );
	clutter_actor_set_position( priv->rectLineClone1, 0, 0 );

	// bottom
	clutter_actor_set_clip( priv->rectLineClone3, 0, 0, priv->newWidth, priv->rectLineThickness);
	clutter_actor_set_position( priv->rectLineClone3, 0, priv->newHeight - priv->rectLineThickness_Half );

	// left
	clutter_actor_set_clip( priv->rectLineClone2, 0, 0, priv->newHeight, priv->rectLineThickness);
	clutter_actor_set_rotation( priv->rectLineClone2, CLUTTER_Z_AXIS, 90.f, 0, 0, 0 );
	clutter_actor_set_position( priv->rectLineClone2, priv->rectLineThickness_Half, 0 );

	// right
	clutter_actor_set_clip( priv->rectLineClone4, 0, 0, priv->newHeight, priv->rectLineThickness);
	clutter_actor_set_rotation( priv->rectLineClone4, CLUTTER_Z_AXIS, 90.f, 0, 0, 0 );
	clutter_actor_set_position( priv->rectLineClone4, priv->newWidth, 0 );
}

void
on_group_notify_changed( GObject *gobject, GParamSpec *pspec, gpointer user_data )
{
	//RectGradDash *self = RECTGRADDASH( clutter_actor_get_parent(gobject) );
	RectGradDash *self = RECTGRADDASH( gobject );
	RectGradDashPrivate *priv = self->priv;

	if( priv->bWasAllocated == FALSE )
		return;

	//g_print("group notify changed...\n");

	switch( pspec->param_id )
	{
	case 4:	// Width
		priv->newHeight = clutter_actor_get_height( CLUTTER_ACTOR( self ) );
		priv->newWidth = clutter_actor_get_width( CLUTTER_ACTOR( self ) );
		//g_print("Width...\n");
		UpdateLineLayout( priv );
		priv->bWasAllocated = FALSE;
		break;
/*
	case 5:	// Height
		priv->newHeight = clutter_actor_get_height( CLUTTER_ACTOR( self ) );
		g_print("Height...\n");
		break;
*/
	}

}

/*
void
on_allocation_changed( ClutterActor *actor, const ClutterActorBox *allocation, ClutterAllocationFlags flags, gpointer user_data)
{
	//RectGradDash *self = RECTGRADDASH( clutter_actor_get_parent(gobject) );
	RectGradDash *self = RECTGRADDASH( actor );
	RectGradDashPrivate *priv = self->priv;

	if( priv->bWasAllocated == FALSE )
		return;

	g_print("group allocation changed...\n");


	priv->newWidth = clutter_actor_box_get_width( allocation );
	priv->newHeight = clutter_actor_box_get_height( allocation );

	UpdateLineLayout( priv );

	priv->bWasAllocated = FALSE;

}
*/

ClutterActor *
make_DashedLine_actor( guint length, double thickness, uint dashtype )
{
	cairo_t *cr;

	ClutterActor* texture = clutter_cairo_texture_new( length, thickness );

	// cast to CLUTTER_CAIRO_TEXTURE, as the functions used below require that type
	ClutterCairoTexture *cc_texture = CLUTTER_CAIRO_TEXTURE (texture);

	clutter_cairo_texture_clear (cc_texture);

	cr = clutter_cairo_texture_create (cc_texture);

	cairo_set_source_rgba(cr, 0, 0, 0, 1);
	cairo_set_line_width(cr, thickness);

	static double dashed[3];
	static int len1;

	switch(dashtype)
	{
	case 1:
		dashed[0] = 4.0;
		dashed[1] = 1.0;
		len1 = 2;
		break;

	case 2:
		dashed[0] = 4.0;
		dashed[1] = 1.0;
		dashed[2] = 4.0;
		len1 = 3;
		break;

	case 3:
		dashed[0] = 1.0;
		len1 = 1;
		break;
	}

	cairo_set_dash(cr, dashed, len1, 0);

	cairo_move_to(cr, 0, 0);
	cairo_line_to(cr, length, 0);
	cairo_stroke(cr);

	/* does the actual drawing onto the texture */
	cairo_destroy (cr);

	return texture;
}

ClutterActor *
make_gradient_actor( guint width, guint height )
{
	cairo_t *cr;

	ClutterActor* texture = clutter_cairo_texture_new( width, height );

	// cast to CLUTTER_CAIRO_TEXTURE, as the functions used below require that type
	ClutterCairoTexture *cc_texture = CLUTTER_CAIRO_TEXTURE (texture);

	clutter_cairo_texture_clear (cc_texture);

	cr = clutter_cairo_texture_create (cc_texture);

	cairo_pattern_t *pat;

	pat = cairo_pattern_create_linear (0.0, 0.0, width, height);

	cairo_pattern_add_color_stop_rgba (pat, 0, 0.5, 0.8, 1, 1);
	cairo_pattern_add_color_stop_rgba (pat, 1, 0.4, 0.8, 0.4, 1);

	cairo_rectangle (cr, 0, 0, width, height);
	cairo_set_source (cr, pat);
	cairo_fill (cr);
	cairo_pattern_destroy (pat);

	/* does the actual drawing onto the texture */
	cairo_destroy (cr);

	return texture;
}

static void
rect_GradDash_init (RectGradDash *self)
{
	RectGradDashPrivate *priv = self->priv = RECTGRADDASH_GET_PRIVATE(self);

/*
	priv->timer_paint_elapsed = g_timer_new();
	g_timer_stop( priv->timer_paint_elapsed );

	priv->timerID_paint_pending = g_timeout_add( 200, timeout_paint_pending, self );		// is self Slow?
*/

	priv->group = clutter_group_new();
	clutter_actor_set_parent( priv->group, CLUTTER_ACTOR( self ) );

	// ----- public Properties needed -----
	// Change These Values
	priv->rectLineWidthMax = 1440;
	priv->rectLineThickness = 4;		// 1 and up
	priv->rectLineType = 1;				// 1,2,3
	priv->rectLineThickness_Half = (priv->rectLineThickness / 2) + (priv->rectLineThickness % 2);
	priv->newWidth = 300;
	priv->newHeight = 300;


	priv->rect = make_gradient_actor( priv->newWidth, priv->newHeight );
	clutter_container_add_actor( CLUTTER_CONTAINER( priv->group ), priv->rect );

	priv->rectLine = make_DashedLine_actor( priv->rectLineWidthMax, priv->rectLineThickness, priv->rectLineType );
	clutter_container_add_actor( CLUTTER_CONTAINER( priv->group ), priv->rectLine );
	clutter_actor_hide( priv->rectLine );

	priv->rectLineClone1 = clutter_clone_new( priv->rectLine );
	clutter_actor_show( priv->rectLineClone1 );
	priv->rectLineClone2 = clutter_clone_new( priv->rectLine );
	clutter_actor_show( priv->rectLineClone2 );
	priv->rectLineClone3 = clutter_clone_new( priv->rectLine );
	clutter_actor_show( priv->rectLineClone3 );
	priv->rectLineClone4 = clutter_clone_new( priv->rectLine );
	clutter_actor_show( priv->rectLineClone4 );

	//UpdateLineLayout( priv );		// call instead from Allocate.. just once to Init, since Allocate will have the Right Size of the Actor, we Dont here.

	clutter_container_add_actor( CLUTTER_CONTAINER( priv->group ), priv->rectLineClone1 );
	clutter_container_add_actor( CLUTTER_CONTAINER( priv->group ), priv->rectLineClone2 );
	clutter_container_add_actor( CLUTTER_CONTAINER( priv->group ), priv->rectLineClone3 );
	clutter_container_add_actor( CLUTTER_CONTAINER( priv->group ), priv->rectLineClone4 );

	//g_signal_connect( self, "allocation-changed", G_CALLBACK( on_allocation_changed ), NULL );

	//g_signal_connect ( self, "notify::height", G_CALLBACK( on_group_notify_changed ), NULL );
	g_signal_connect ( self, "notify::width", G_CALLBACK( on_group_notify_changed ), NULL );
}

static void
rect_GradDash_class_init( RectGradDashClass *klass )
{
	GObjectClass      *gobject_class = G_OBJECT_CLASS (klass);
	ClutterActorClass *actor_class   = CLUTTER_ACTOR_CLASS (klass);

	gobject_class->finalize     = rect_GradDash_finalize;
	gobject_class->dispose      = rect_GradDash_dispose;
	actor_class->paint          = rect_GradDash_paint;
	actor_class->allocate       = rect_GradDash_allocate;

	g_type_class_add_private( gobject_class, sizeof( RectGradDashPrivate ) );
}

static ClutterActor*
rect_GradDash_new( void )
{
	return g_object_new( TYPE_RECTGRADDASH, NULL );
}
// ------ in Controls C file ------<




// ------ in Test C file ------>
static gboolean
on_mouse_wheel_scroll( ClutterActor *actor, ClutterEvent *event, gpointer user_data )
{
	static gboolean bStillScrolling = FALSE;

	if( bStillScrolling == TRUE )
	{
		g_print( "Skip Scroll Event\n" );
		return TRUE;
	}

	bStillScrolling = TRUE;

	ClutterActor *myRect = CLUTTER_ACTOR( user_data );
	ClutterScrollDirection direction = clutter_event_get_scroll_direction (event);
	gfloat w, h;

	switch (direction)
	{
	case CLUTTER_SCROLL_UP:
		clutter_actor_get_size( myRect, &w, &h );
		if( w-20 > 0 && h-15 > 0 )
			clutter_actor_set_size( myRect, w-20, h-15 );
		g_print( "Scrolled up. w:%0.0f, h:%0.0f \n", w-20, h-15 );
		break;

    case CLUTTER_SCROLL_DOWN:
		clutter_actor_get_size( myRect, &w, &h );
		clutter_actor_set_size( myRect, w+20, h+15 );
		g_print( "Scrolled down. w:%0.0f, h:%0.0f \n", w+20, h+15 );
		break;

    case CLUTTER_SCROLL_RIGHT:
		g_print( "Scrolled right\n" );
		break;

    case CLUTTER_SCROLL_LEFT:
		g_print( "Scrolled left\n" );
		break;
	}

	bStillScrolling = FALSE;

	return TRUE; /* event has been handled */
}

int
main (int argc, char *argv[])
{
	ClutterActor	*stage;
	ClutterActor	*rectGradDash1;
	ClutterColor	StageColor = {215,220,225,255};

	clutter_init( &argc, &argv );

	stage = clutter_stage_get_default ();
	clutter_stage_set_color( CLUTTER_STAGE(stage), &StageColor );
	clutter_stage_set_user_resizable( CLUTTER_STAGE( stage ), TRUE );
	clutter_actor_set_size( stage, 800, 600 );
	clutter_stage_set_title( CLUTTER_STAGE( stage ), "Test for Rectangle w/ Gradient Fill, Dashed Border" );

	rectGradDash1 = rect_GradDash_new();
	clutter_container_add_actor( CLUTTER_CONTAINER( stage ), rectGradDash1 );
	clutter_actor_set_position( rectGradDash1, 10, 10 );
	clutter_actor_set_size( rectGradDash1, 320, 240 );
	clutter_actor_show( rectGradDash1 );

	g_signal_connect( stage, "scroll-event", G_CALLBACK( on_mouse_wheel_scroll ), rectGradDash1 );

	clutter_actor_show( stage );

	clutter_main();

	return 0;
}
// ------ in Test C file ------<

_______________________________________________
clutter-app-devel-list mailing list
[email protected]
http://lists.clutter-project.org/listinfo/clutter-app-devel-list

Reply via email to