rounded rectangles

classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

rounded rectangles

James Muir-2
Hello,

I've been reviewing the man pages, FAQ, and various demos but have been
unable to find a way to make a rectangle with rounded corners. How would
I go about making one? Must I extend a base class like
Gnome2::Canvas::RE or Gnome2::Canvas::Shape?

-James

_______________________________________________
gtk-perl-list mailing list
[hidden email]
http://mail.gnome.org/mailman/listinfo/gtk-perl-list
Reply | Threaded
Open this post in threaded view
|

Re: rounded rectangles

Grant McLean
On Mon, 2005-08-08 at 09:45 -0400, James Muir wrote:
> Hello,
>
> I've been reviewing the man pages, FAQ, and various demos but have been
> unable to find a way to make a rectangle with rounded corners. How would
> I go about making one?

I've done that in Sprog using a canvas shape object with a path made up
from straight lines and bezier curves.  I guess a bit of sub-classing
would allow some of the messy (but fairly straightforward) geometry
calculations to be hidden away.

Cheers
Grant

_______________________________________________
gtk-perl-list mailing list
[hidden email]
http://mail.gnome.org/mailman/listinfo/gtk-perl-list
Reply | Threaded
Open this post in threaded view
|

rounded rectangles problem

James Muir-2
Hi All,

I'm trying to subclass Gnome2::Canvas::Shape to get a rounded rectangle.
No success yet. I need to set the Bpath for the shape. As far as I can
tell the pathdef can not be set up in INIT_INSTANCE as this is too soon
and the properties have not been passed in. I'm not sure if it's OK to
instantiate a pathdef inside my RoundedRect or not, and I'm not sure how
to call set_path_def. I've seen in the Mup::ColorButton example that an
event is used to set the button color for the first time. I tried
something similar but without success.

This software is new to me and maybe I'm being a bit too ambitious, and
obviously I'm misunderstanding something. Any help would be appreciated.

Here is my subclass (without event handler):

#!/usr/bin/perl -w
#
# RoundedRect: add a rounded rectangle.
#
# TODO: Check size of corner radius.
# -------------------------------------------------------------------------
package RoundedRect;

use strict;
use Gnome2::Canvas;

use Glib ':constants';
use Glib::Object::Subclass
    Gnome2::Canvas::Shape::,
    properties => [
                   Glib::ParamSpec->int ('corner-radius',
                                         'Corner Radius',
                                         'Radius of corners',
                                         10,
                                         100,
                                         10,
                                         G_PARAM_READWRITE),
                   Glib::ParamSpec->int ('x1',
                                         'x1',
                                         'Upper left X coord',
                                         0,
                                         32767,
                                         0,
                                         G_PARAM_READWRITE),
                   Glib::ParamSpec->int ('y1',
                                         'y1',
                                         'Upper left Y coord',
                                         0,
                                         32767,
                                         0,
                                         G_PARAM_READWRITE),
                   Glib::ParamSpec->int ('x2',
                                         'x2',
                                         'Lower right X coord',
                                         0,
                                         32767,
                                         0,
                                         G_PARAM_READWRITE),
                   Glib::ParamSpec->int ('y2',
                                         'y2',
                                         'Lower right Y coord',
                                         0,
                                         32767,
                                         0,
                                         G_PARAM_READWRITE),
                   ]
    ;


sub INIT_INSTANCE
{
    my $self = @_;

    print "INIT_INSTANCE:\n";
}

sub GET_PROPERTY
{
    my ($self, $pspec) = @_;

    print "GET_PROPERTY: " . $pspec->get_name . "  value: " .  
$self->{$pspec->get_name} . "\n";

    return ($self->{$pspec->get_name} || $pspec->get_default_value);
}

sub SET_PROPERTY
{
# Do: Limit size of corner radius to 3/8ths of min(height,width) of
rectangle.
    my ($self, $pspec, $newval) = @_;

    print "SET_PROPERTY: " . $pspec->get_name . "  newval: $newval\n";

    $self->{$pspec->get_name} = $newval;
}

# _roundedRect: return the rounded rectangle path.
# --------------------------------------------------------
sub _roundedRect
{
    my ($r, $x1, $y1, $x2, $y2) = @_;

    # Make sure (x1,y1) is upper left.

    if (($x1 < $x2) && ($y1 > $y2))
    {
        my $t1 = $y1; $y1 = $y2; $y2 = $t1;
    }

    if (($y1 < $y2) && ($x1 > $x2))
    {
        my $t1 = $x1; $x1 = $x2; $x2 = $t1;
    }

    print "x1: $x1  y1: $y1  x2: $x2  y2: $y2\n";

    # Get the points for the path.

    my @p = ();

    push @p, _bezier('UPPER_LEFT',  $r, $x1, $y1);
    push @p, _bezier('UPPER_RIGHT', $r, $x2, $y1);
    push @p, _bezier('LOWER_RIGHT', $r, $x2, $y2);
    push @p, _bezier('LOWER_LEFT',  $r, $x1, $y2);

    print "p: @p\n";

    # Build the rounded rectangle path. Problem?

    my $pathdef = Gnome2::Canvas::PathDef->new();

    $pathdef->moveto  ($p[0],  $p[1]);
    $pathdef->curveto ($p[2],  $p[3],  $p[4],  $p[5],  $p[6],  $p[7]);
    $pathdef->lineto  ($p[8],  $p[9]);
    $pathdef->curveto ($p[10], $p[11], $p[12], $p[13], $p[14], $p[15]);
    $pathdef->lineto  ($p[16], $p[17]);
    $pathdef->curveto ($p[18], $p[19], $p[20], $p[21], $p[22], $p[23]);
    $pathdef->lineto  ($p[24], $p[25]);
    $pathdef->curveto ($p[26], $p[27], $p[28], $p[29], $p[30], $p[31]);
    $pathdef->lineto  ($p[0],  $p[1]);

    return $pathdef;
}


# _bezier: return corner bezier points.
# --------------------------------------------------------
sub _bezier
{
    my $corner = shift(@_);
    my $r      = shift(@_);
    my $x      = shift(@_);
    my $y      = shift(@_);

    print "_bezier: $corner  r: $r  x: $x  y: $y\n";

    if ($corner == 'UPPER_LEFT')
    {
        return ($x,$y+$r, $x,$y+($r/2), $x+($r/2),$y, $x+$r, $y);
    }

    if ($corner == 'UPPER_RIGHT')
    {
        return ($x-$r,$y, $x-($r/2),$y, $x,$y+($r/2), $x, $y+$r);
    }

    if ($corner == 'LOWER_RIGHT')
    {
        return ($x,$y-$r, $x,$y-($r/2), $x-($r/2),$y, $x-$r, $y);
    }

    if ($corner == 'LOWER_LEFT')
    {
        return ($x+$r,$y, $x+($r/2),$y, $x,$y-($r/2), $x, $y-$r);
    }

    return ();
}


1;

_______________________________________________
gtk-perl-list mailing list
[hidden email]
http://mail.gnome.org/mailman/listinfo/gtk-perl-list
Reply | Threaded
Open this post in threaded view
|

Re: rounded rectangles problem

Jan Hudec
On Wed, Aug 10, 2005 at 13:56:18 -0400, James Muir wrote:
> Hi All,
>
> I'm trying to subclass Gnome2::Canvas::Shape to get a rounded rectangle.
> No success yet. I need to set the Bpath for the shape. As far as I can
> tell the pathdef can not be set up in INIT_INSTANCE as this is too soon
> and the properties have not been passed in. I'm not sure if it's OK to

So set it up in SET_PROPERTY. Whenever some of the properties affecting the
shape is set, look whether all the others are defined, and if they are, set
the shape. SET_PROPERY /is/ called for default values if not explicitly
specified, so you can be sure all the properties will be set before the
object is shown.

> instantiate a pathdef inside my RoundedRect or not, and I'm not sure how
> to call set_path_def. I've seen in the Mup::ColorButton example that an
> event is used to set the button color for the first time. I tried
> something similar but without success.

The problem is, that Gnome2::Canvas::Shape is NOT a Gtk2::Widget, so it does
not have realize nor map signals. The SET_PROPERTY approach should be better
and has the added benefit that the shape is reset when the properties are
changed later.

I think the code you have shown is mostly ok. Just place the shape generation
in SET_PROPERTY.

You can also look in the code for GnomeCanvasRect and GnomeCanvasRE (if you
know C) how they do it. In fact, you may even be able to derive from
GnomeCanvasRE, depending on how it is implemented.

-------------------------------------------------------------------------------
                                                 Jan 'Bulb' Hudec <[hidden email]>

_______________________________________________
gtk-perl-list mailing list
[hidden email]
http://mail.gnome.org/mailman/listinfo/gtk-perl-list

signature.asc (196 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: rounded rectangles problem

James Muir-2
Jan Hudec wrote:

>On Wed, Aug 10, 2005 at 13:56:18 -0400, James Muir wrote:
>  
>
>>Hi All,
>>
>>I'm trying to subclass Gnome2::Canvas::Shape to get a rounded rectangle.
>>No success yet. I need to set the Bpath for the shape. As far as I can
>>tell the pathdef can not be set up in INIT_INSTANCE as this is too soon
>>and the properties have not been passed in. I'm not sure if it's OK to
>>    
>>
>
>So set it up in SET_PROPERTY. Whenever some of the properties affecting the
>shape is set, look whether all the others are defined, and if they are, set
>the shape. SET_PROPERY /is/ called for default values if not explicitly
>specified, so you can be sure all the properties will be set before the
>object is shown.
>
>  
>
>>instantiate a pathdef inside my RoundedRect or not, and I'm not sure how
>>to call set_path_def. I've seen in the Mup::ColorButton example that an
>>event is used to set the button color for the first time. I tried
>>something similar but without success.
>>    
>>
>
>The problem is, that Gnome2::Canvas::Shape is NOT a Gtk2::Widget, so it does
>not have realize nor map signals. The SET_PROPERTY approach should be better
>and has the added benefit that the shape is reset when the properties are
>changed later.
>  
>
Thanks, Jan, for the suggestion. I've modified the SET_PROPERTY as you
suggested and still no rounded rectangle.

I've written a similar program using composition rather than inheritance
and I get a rounded rectangle. I'd use this rectangle except that I am
unable to set the 'fill-color', and I get events only when the mouse is
placed directly on the outline. No signals when the mouse is moved
inside the rounded rectangle. Is this the correct behavior of the
Gnome2::Canvas::Shape ? If so, I shall have to come up with a "plan b".

-James

_______________________________________________
gtk-perl-list mailing list
[hidden email]
http://mail.gnome.org/mailman/listinfo/gtk-perl-list
Reply | Threaded
Open this post in threaded view
|

Re: rounded rectangles problem

Jan Hudec
On Thu, Aug 11, 2005 at 10:41:08 -0400, James Muir wrote:

> Jan Hudec wrote:
> >On Wed, Aug 10, 2005 at 13:56:18 -0400, James Muir wrote:
> >>instantiate a pathdef inside my RoundedRect or not, and I'm not sure how
> >>to call set_path_def. I've seen in the Mup::ColorButton example that an
> >>event is used to set the button color for the first time. I tried
> >>something similar but without success.
> >The problem is, that Gnome2::Canvas::Shape is NOT a Gtk2::Widget, so it
> >does
> >not have realize nor map signals. The SET_PROPERTY approach should be
> >better
> >and has the added benefit that the shape is reset when the properties are
> >changed later.
>
> Thanks, Jan, for the suggestion. I've modified the SET_PROPERTY as you
> suggested and still no rounded rectangle.
Hm, strange. But I have only ever used this with Gtk2::Widget objects, so
there may be another gotcha in Gnome2::Canvas::Item, which is not a widget.

> I've written a similar program using composition rather than inheritance
> and I get a rounded rectangle. I'd use this rectangle except that I am
> unable to set the 'fill-color', and I get events only when the mouse is
> placed directly on the outline. No signals when the mouse is moved
> inside the rounded rectangle. Is this the correct behavior of the
> Gnome2::Canvas::Shape ? If so, I shall have to come up with a "plan b".

Gnome2::Canvas::Rect->isa('Gnome2::Canvas::Shape'), so shape must provide
enough base functionality for it. So though I don't know for sure, I'd think
it's not correct.

I suggest you take sources of the gnome canvas library itself (not the
bindings, the actual C library) and have a look at GnomeCanvasRect, how it
sets the shape.

By the way, you should be able to inherit GnomeCanvasRE, which comes with the
x1, y1, x2, y2 properties built in (and probably nothing else).

-------------------------------------------------------------------------------
                                                 Jan 'Bulb' Hudec <[hidden email]>

_______________________________________________
gtk-perl-list mailing list
[hidden email]
http://mail.gnome.org/mailman/listinfo/gtk-perl-list

signature.asc (196 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: rounded rectangles problem

muppet-6
In reply to this post by Jan Hudec

Jan Hudec said:

> On Wed, Aug 10, 2005 at 13:56:18 -0400, James Muir wrote:
>> Hi All,
>>
>> I'm trying to subclass Gnome2::Canvas::Shape to get a rounded rectangle.
>> No success yet. I need to set the Bpath for the shape. As far as I can
>> tell the pathdef can not be set up in INIT_INSTANCE as this is too soon
>> and the properties have not been passed in. I'm not sure if it's OK to
>
> So set it up in SET_PROPERTY. Whenever some of the properties affecting the
> shape is set, look whether all the others are defined, and if they are, set
> the shape. SET_PROPERY /is/ called for default values if not explicitly
> specified, so you can be sure all the properties will be set before the
> object is shown.

Erm, no, SET_PROPERTY is not called for values that default -- only if there
is a property to set.  You can usually set a default value in INIT_INSTANCE.
For a pathdef property, you should at least be able to create an empty pathdef
which you later replace when SET_PROPERTY is called.



--
muppet <scott at asofyet dot org>

_______________________________________________
gtk-perl-list mailing list
[hidden email]
http://mail.gnome.org/mailman/listinfo/gtk-perl-list
Reply | Threaded
Open this post in threaded view
|

Re: rounded rectangles problem

muppet-6
In reply to this post by James Muir-2

James Muir said:
> I've written a similar program using composition rather than inheritance
> and I get a rounded rectangle. I'd use this rectangle except that I am
> unable to set the 'fill-color', and I get events only when the mouse is
> placed directly on the outline. No signals when the mouse is moved
> inside the rounded rectangle. Is this the correct behavior of the
> Gnome2::Canvas::Shape ? If so, I shall have to come up with a "plan b".

If you use just a plain Gnome2::Canvas::Rect, you get mouse events inside the
rectangle only if fill-color is set.  The basic trick, then, is to figure out
how to get fill-color set.  Can you post some current code so we know what you
are and aren't doing?



--
muppet <scott at asofyet dot org>

_______________________________________________
gtk-perl-list mailing list
[hidden email]
http://mail.gnome.org/mailman/listinfo/gtk-perl-list
Reply | Threaded
Open this post in threaded view
|

Re: rounded rectangles problem

James Muir-2
muppet wrote:

>James Muir said:
>  
>
>>I've written a similar program using composition rather than inheritance
>>and I get a rounded rectangle. I'd use this rectangle except that I am
>>unable to set the 'fill-color', and I get events only when the mouse is
>>placed directly on the outline. No signals when the mouse is moved
>>inside the rounded rectangle. Is this the correct behavior of the
>>Gnome2::Canvas::Shape ? If so, I shall have to come up with a "plan b".
>>    
>>
>
>If you use just a plain Gnome2::Canvas::Rect, you get mouse events inside the
>rectangle only if fill-color is set.  The basic trick, then, is to figure out
>how to get fill-color set.  Can you post some current code so we know what you
>are and aren't doing?
>
Here is the latest code. I managed to get the rectangle to display since
my last posting. I had forgotten to set the 'outline-color' and so
nothing was displayed.  I am still not able to set the 'fill-color'.

#!/usr/bin/perl -w
#
# RoundedRect1: add a rounded rectangle.
#
# This version uses inheritance to create the rect.
#
# TODO: Check size of corner radius.
#       Fill rect with color.
#       Capture events inside rect.
# -------------------------------------------------------------------------
package RoundedRect1;

use strict;
use Gnome2::Canvas;

use Glib ':constants';
use Glib::Object::Subclass
    Gnome2::Canvas::Shape::,
    properties => [
           Glib::ParamSpec->int ('radius',
                     'corner_radius',
                     'Radius of corners',
                     10,
                     100,
                     10,
                     G_PARAM_READWRITE),
           Glib::ParamSpec->int ('x1',
                     'x1',
                     'Upper left X coord',
                     0,
                     32767,
                     0,
                     G_PARAM_READWRITE),
           Glib::ParamSpec->int ('y1',
                     'y1',
                     'Upper left Y coord',
                     0,
                     32767,
                     0,
                     G_PARAM_READWRITE),
           Glib::ParamSpec->int ('x2',
                     'x2',
                     'Lower right X coord',
                     0,
                     32767,
                     0,
                     G_PARAM_READWRITE),
           Glib::ParamSpec->int ('y2',
                     'y2',
                     'Lower right Y coord',
                     0,
                     32767,
                     0,
                     G_PARAM_READWRITE),
           ]
    ;


sub INIT_INSTANCE
{
    my $self = @_;

    print "INIT_INSTANCE:\n";
}


sub GET_PROPERTY
{
    my ($self, $pspec) = @_;

    print "GET_PROPERTY: " . $pspec->get_name . "  value: " .
    $self->{$pspec->get_name} . "\n";

    return ($self->{$pspec->get_name} || $pspec->get_default_value);
}


sub SET_PROPERTY
{
    my ($self, $pspec, $newval) = @_;

    print "SET_PROPERTY: " . $pspec->get_name . "  newval: $newval\n";

    $self->{$pspec->get_name} = $newval;

    if ((defined $self->{'radius'}) &&
    (defined $self->{x1}) && (defined $self->{y1}) &&
    (defined $self->{x2}) && (defined $self->{y2}))
    {
    $self->set_path_def(_roundedRect($self->{'radius'},
                     $self->{x1}, $self->{y1},
                     $self->{x2}, $self->{y2}));

    $self->set('outline-color'=>'black'); # default.

#    $self->set('fill-color'=>'white');

    my $color = Gtk2::Gdk::Color->new(255,255,255);

    $self->set('fill-color-gdk'=>$color);
    }
}


# _roundedRect: return the rounded rectangle path.
# --------------------------------------------------------
sub _roundedRect
{
    my ($r, $x1, $y1, $x2, $y2) = @_;

    # Make sure (x1,y1) is upper left.

    if (($x1 < $x2) && ($y1 > $y2))
    {
    my $t1 = $y1; $y1 = $y2; $y2 = $t1;
    }

    if (($y1 < $y2) && ($x1 > $x2))
    {
    my $t1 = $x1; $x1 = $x2; $x2 = $t1;
    }

    print "x1: $x1  y1: $y1  x2: $x2  y2: $y2\n";

    # Get the points for the path.

    my @p = ();

    push @p, _bezier('UPPER_LEFT',  $r, $x1, $y1);
    push @p, _bezier('UPPER_RIGHT', $r, $x2, $y1);
    push @p, _bezier('LOWER_RIGHT', $r, $x2, $y2);
    push @p, _bezier('LOWER_LEFT',  $r, $x1, $y2);

    # Build the rounded rectangle path. Problem?

    my $pathdef = Gnome2::Canvas::PathDef->new();

    $pathdef->moveto  ($p[0],  $p[1]);
    $pathdef->curveto ($p[2],  $p[3],  $p[4],  $p[5],  $p[6],  $p[7]);
    $pathdef->lineto  ($p[8],  $p[9]);
    $pathdef->curveto ($p[10], $p[11], $p[12], $p[13], $p[14], $p[15]);
    $pathdef->lineto  ($p[16], $p[17]);
    $pathdef->curveto ($p[18], $p[19], $p[20], $p[21], $p[22], $p[23]);
    $pathdef->lineto  ($p[24], $p[25]);
    $pathdef->curveto ($p[26], $p[27], $p[28], $p[29], $p[30], $p[31]);
    $pathdef->lineto  ($p[0],  $p[1]);

    return $pathdef;
}


# _bezier: return corner bezier points.
# --------------------------------------------------------
sub _bezier
{
    my $corner = shift(@_);
    my $r      = shift(@_);
    my $x      = shift(@_);
    my $y      = shift(@_);

    if ($corner eq 'UPPER_LEFT')
    {
    return ($x,$y+$r, $x,$y+($r/2), $x+($r/2),$y, $x+$r, $y);
    }

    if ($corner eq 'UPPER_RIGHT')
    {
    return ($x-$r,$y, $x-($r/2),$y, $x,$y+($r/2), $x, $y+$r);
    }

    if ($corner eq 'LOWER_RIGHT')
    {
    return ($x,$y-$r, $x,$y-($r/2), $x-($r/2),$y, $x-$r, $y);
    }

    if ($corner eq 'LOWER_LEFT')
    {
    return ($x+$r,$y, $x+($r/2),$y, $x,$y-($r/2), $x, $y-$r);
    }

    return ();
}


1;

_______________________________________________
gtk-perl-list mailing list
[hidden email]
http://mail.gnome.org/mailman/listinfo/gtk-perl-list
Reply | Threaded
Open this post in threaded view
|

Re: rounded rectangles problem

muppet-6

On Aug 11, 2005, at 7:50 PM, James Muir wrote:

> Here is the latest code. I managed to get the rectangle to display  
> since my last posting. I had forgotten to set the 'outline-color'  
> and so nothing was displayed.  I am still not able to set the 'fill-
> color'.

see below.

here is the driver code i am using:

   use RoundedRect1;
   use Gnome2::Canvas;
   use Gtk2 -init;

   my $window = Gtk2::Window->new;
   $window->set_default_size (400, 400);
   $window->signal_connect (destroy => sub {Gtk2->main_quit});

   my $canvas = Gnome2::Canvas->new;
   $window->add ($canvas);

   Gnome2::Canvas::Item->new ($canvas->root,
                  RoundedRect1::,
                  fill_color => 'red',
                  outline_color => 'blue',
                  x1 => 10, y1 => 10,
                  x2 => 100, y2 => 100,
                  width_pixels => 5,
                 );

   $window->show_all;
   Gtk2->main;


Note that i'm setting the colors and widths and things in the driver  
code, not in the RoundedRect1 implementation itself.


> #!/usr/bin/perl -w
> #
> # RoundedRect1: add a rounded rectangle.
> #
> # This version uses inheritance to create the rect.
> #
> # TODO: Check size of corner radius.
> #       Fill rect with color.
> #       Capture events inside rect.
> #  
> ----------------------------------------------------------------------
> ---
> package RoundedRect1;
>
> use strict;
> use Gnome2::Canvas;
>
> use Glib ':constants';
> use Glib::Object::Subclass
>    Gnome2::Canvas::Shape::,
>    properties => [

'radius' should be a double from [0.0, DBL_MAX(clipped in drawing)],  
not an int, because the coordinate space is continuous and may be  
scaled.  (see the comment below)

>           Glib::ParamSpec->int ('radius',
>                     'corner_radius',
>                     'Radius of corners',
>                     10,
>                     100,
>                     10,
>                     G_PARAM_READWRITE),
>           Glib::ParamSpec->int ('x1',
>                     'x1',
>                     'Upper left X coord',
>                     0,
>                     32767,

In general, these bounds are far too restrictive.  The canvas's  
coordinate space is virtually unbounded, and can have any units you  
like, and may be arbitrarily scaled, so the coords should actually be  
unbounded doubles.

    use POSIX qw(DBL_MAX);
    ...
        Glib::ParamSpec->double ('x1', 'X1', 'Upper left X coordinate',
                                -DBL_MAX, DBL_MAX, 0.0,
                                G_PARAM_READWRITE);

>                     0,
>                     G_PARAM_READWRITE),
>           Glib::ParamSpec->int ('y1',
>                     'y1',
>                     'Upper left Y coord',
>                     0,
>                     32767,
>                     0,
>                     G_PARAM_READWRITE),
>           Glib::ParamSpec->int ('x2',
>                     'x2',
>                     'Lower right X coord',
>                     0,
>                     32767,
>                     0,
>                     G_PARAM_READWRITE),
>           Glib::ParamSpec->int ('y2',
>                     'y2',
>                     'Lower right Y coord',
>                     0,
>                     32767,
>                     0,
>                     G_PARAM_READWRITE),
>           ]
>    ;

Since this INIT_INSTANCE is a no-op, you can leave it out and save  
the rather expensive no-op.

> sub INIT_INSTANCE
> {
>    my $self = @_;
>
>    print "INIT_INSTANCE:\n";
> }


This GET_PROPERTY is a perl implementation of exactly the behavior of  
the built-in fallback GET_PROPERTY added in 1.060, so you can leave  
it out and save a slight performance penalty.

> sub GET_PROPERTY
> {
>    my ($self, $pspec) = @_;
>
>    print "GET_PROPERTY: " . $pspec->get_name . "  value: " .
>    $self->{$pspec->get_name} . "\n";
>
>    return ($self->{$pspec->get_name} || $pspec->get_default_value);
> }


On the other hand, since you're doing non-default stuff in  
SET_PROPERTY, you *do* need this override.

> sub SET_PROPERTY
> {
>    my ($self, $pspec, $newval) = @_;
>
>    print "SET_PROPERTY: " . $pspec->get_name . "  newval: $newval\n";
>
>    $self->{$pspec->get_name} = $newval;

This check is clever -- "don't rebuild the pathdef if we don't have  
all the info yet" -- but is a little too strict.  You've set a  
default value for radius, but this check makes it impossible for that  
to be used (unless you duplicate the default value in your  
INIT_INSTANCE, which i don't recommend).

If you just drop the (defined $self->{'radius'}) clause, we can do  
another fix below that makes it work much better.

>    if ((defined $self->{'radius'}) &&
>    (defined $self->{x1}) && (defined $self->{y1}) &&
>    (defined $self->{x2}) && (defined $self->{y2}))
>    {
>    $self->set_path_def(_roundedRect($self->{'radius'},
>                     $self->{x1}, $self->{y1},
>                     $self->{x2}, $self->{y2}));

Here, we're digging straight into the object's copies of the data.  
Nice and efficient, but sidesteps all of the default-handling that  
would be nice to use.  If, instead, you use Glib::Object::get() (yes,  
even inside your own object implementation), then the default value  
machinery can take effect, and you don't have to set 'radius'  
explicitly before anything happens.

Note that ->get() will return a list of values if given a list of  
keys...

       $self->set_path_def (_roundedRect ($self->get (qw(radius x1 y1  
x2 y2))));

(Looking into this a little more, it turns out that there's a vfunc  
(that is, a virtual method with no associated signal) called "update"  
that is used by the C implementations to do the actual drawing of a  
canvas item.  The bindings currently do not offer the ability to  
implement that vfunc (for a variety of reasons, including "nobody  
ever pointed out that it was missing").  At some point i will play  
with that and find out if it makes this object easier to implement.)


Now strike everything from here to the end of the block.  Choosing  
the colors should be left to the user of this object.

>    $self->set('outline-color'=>'black'); # default.
>
> #    $self->set('fill-color'=>'white');
>
>    my $color = Gtk2::Gdk::Color->new(255,255,255);
>
>    $self->set('fill-color-gdk'=>$color);
>    }
> }


And the answer you've been dying for is just around the bend...

> # _roundedRect: return the rounded rectangle path.
> # --------------------------------------------------------
> sub _roundedRect
> {
>    my ($r, $x1, $y1, $x2, $y2) = @_;
>
>    # Make sure (x1,y1) is upper left.
>
>    if (($x1 < $x2) && ($y1 > $y2))
>    {
>    my $t1 = $y1; $y1 = $y2; $y2 = $t1;
>    }
>
>    if (($y1 < $y2) && ($x1 > $x2))
>    {
>    my $t1 = $x1; $x1 = $x2; $x2 = $t1;
>    }
>
>    print "x1: $x1  y1: $y1  x2: $x2  y2: $y2\n";
>
>    # Get the points for the path.
>
>    my @p = ();
>
>    push @p, _bezier('UPPER_LEFT',  $r, $x1, $y1);
>    push @p, _bezier('UPPER_RIGHT', $r, $x2, $y1);
>    push @p, _bezier('LOWER_RIGHT', $r, $x2, $y2);
>    push @p, _bezier('LOWER_LEFT',  $r, $x1, $y2);
>
>    # Build the rounded rectangle path. Problem?
>
>    my $pathdef = Gnome2::Canvas::PathDef->new();
>
>    $pathdef->moveto  ($p[0],  $p[1]);
>    $pathdef->curveto ($p[2],  $p[3],  $p[4],  $p[5],  $p[6],  $p[7]);
>    $pathdef->lineto  ($p[8],  $p[9]);
>    $pathdef->curveto ($p[10], $p[11], $p[12], $p[13], $p[14], $p[15]);
>    $pathdef->lineto  ($p[16], $p[17]);
>    $pathdef->curveto ($p[18], $p[19], $p[20], $p[21], $p[22], $p[23]);
>    $pathdef->lineto  ($p[24], $p[25]);
>    $pathdef->curveto ($p[26], $p[27], $p[28], $p[29], $p[30], $p[31]);
>    $pathdef->lineto  ($p[0],  $p[1]);

You can't fill an open path.  Close the path to allow fill-color to  
work.

    $pathdef->closepath_current;


>    return $pathdef;
> }
>
>
> # _bezier: return corner bezier points.
> # --------------------------------------------------------
> sub _bezier
> {
>    my $corner = shift(@_);
>    my $r      = shift(@_);
>    my $x      = shift(@_);
>    my $y      = shift(@_);
>
>    if ($corner eq 'UPPER_LEFT')
>    {
>    return ($x,$y+$r, $x,$y+($r/2), $x+($r/2),$y, $x+$r, $y);
>    }
>
>    if ($corner eq 'UPPER_RIGHT')
>    {
>    return ($x-$r,$y, $x-($r/2),$y, $x,$y+($r/2), $x, $y+$r);
>    }
>
>    if ($corner eq 'LOWER_RIGHT')
>    {
>    return ($x,$y-$r, $x,$y-($r/2), $x-($r/2),$y, $x-$r, $y);
>    }
>
>    if ($corner eq 'LOWER_LEFT')
>    {
>    return ($x+$r,$y, $x+($r/2),$y, $x,$y-($r/2), $x, $y-$r);
>    }
>
>    return ();
> }
>
>
> 1;


--
Walk softly, and carry a BFG-9000.
   -- unknown

_______________________________________________
gtk-perl-list mailing list
[hidden email]
http://mail.gnome.org/mailman/listinfo/gtk-perl-list
Reply | Threaded
Open this post in threaded view
|

Re: rounded rectangles problem

James Muir-2
muppet wrote:

>
> On Aug 11, 2005, at 7:50 PM, James Muir wrote:
>
>> Here is the latest code. I managed to get the rectangle to display
>> since my last posting. I had forgotten to set the 'outline-color' and
>> so nothing was displayed. I am still not able to set the 'fill- color'.
>
>
> see below.
>
> here is the driver code i am using:
>
> use RoundedRect1;
> use Gnome2::Canvas;
> use Gtk2 -init;
>
> my $window = Gtk2::Window->new;
> $window->set_default_size (400, 400);
> $window->signal_connect (destroy => sub {Gtk2->main_quit});
>
> my $canvas = Gnome2::Canvas->new;
> $window->add ($canvas);
>
> Gnome2::Canvas::Item->new ($canvas->root,
> RoundedRect1::,
> fill_color => 'red',
> outline_color => 'blue',
> x1 => 10, y1 => 10,
> x2 => 100, y2 => 100,
> width_pixels => 5,
> );
>
> $window->show_all;
> Gtk2->main;
>
>
> Note that i'm setting the colors and widths and things in the driver
> code, not in the RoundedRect1 implementation itself.
>
>
>> #!/usr/bin/perl -w
>> #
>> # RoundedRect1: add a rounded rectangle.
>> #
>> # This version uses inheritance to create the rect.
>> #
>> # TODO: Check size of corner radius.
>> # Fill rect with color.
>> # Capture events inside rect.
>> #
>> ----------------------------------------------------------------------
>> ---
>> package RoundedRect1;
>>
>> use strict;
>> use Gnome2::Canvas;
>>
>> use Glib ':constants';
>> use Glib::Object::Subclass
>> Gnome2::Canvas::Shape::,
>> properties => [
>
>
> 'radius' should be a double from [0.0, DBL_MAX(clipped in drawing)],
> not an int, because the coordinate space is continuous and may be
> scaled. (see the comment below)
>
>> Glib::ParamSpec->int ('radius',
>> 'corner_radius',
>> 'Radius of corners',
>> 10,
>> 100,
>> 10,
>> G_PARAM_READWRITE),
>> Glib::ParamSpec->int ('x1',
>> 'x1',
>> 'Upper left X coord',
>> 0,
>> 32767,
>
>
> In general, these bounds are far too restrictive. The canvas's
> coordinate space is virtually unbounded, and can have any units you
> like, and may be arbitrarily scaled, so the coords should actually be
> unbounded doubles.
>
> use POSIX qw(DBL_MAX);
> ...
> Glib::ParamSpec->double ('x1', 'X1', 'Upper left X coordinate',
> -DBL_MAX, DBL_MAX, 0.0,
> G_PARAM_READWRITE);
>
>> 0,
>> G_PARAM_READWRITE),
>> Glib::ParamSpec->int ('y1',
>> 'y1',
>> 'Upper left Y coord',
>> 0,
>> 32767,
>> 0,
>> G_PARAM_READWRITE),
>> Glib::ParamSpec->int ('x2',
>> 'x2',
>> 'Lower right X coord',
>> 0,
>> 32767,
>> 0,
>> G_PARAM_READWRITE),
>> Glib::ParamSpec->int ('y2',
>> 'y2',
>> 'Lower right Y coord',
>> 0,
>> 32767,
>> 0,
>> G_PARAM_READWRITE),
>> ]
>> ;
>
>
> Since this INIT_INSTANCE is a no-op, you can leave it out and save the
> rather expensive no-op.
>
>> sub INIT_INSTANCE
>> {
>> my $self = @_;
>>
>> print "INIT_INSTANCE:\n";
>> }
>
>
>
> This GET_PROPERTY is a perl implementation of exactly the behavior of
> the built-in fallback GET_PROPERTY added in 1.060, so you can leave it
> out and save a slight performance penalty.
>
>> sub GET_PROPERTY
>> {
>> my ($self, $pspec) = @_;
>>
>> print "GET_PROPERTY: " . $pspec->get_name . " value: " .
>> $self->{$pspec->get_name} . "\n";
>>
>> return ($self->{$pspec->get_name} || $pspec->get_default_value);
>> }
>
>
>
> On the other hand, since you're doing non-default stuff in
> SET_PROPERTY, you *do* need this override.
>
>> sub SET_PROPERTY
>> {
>> my ($self, $pspec, $newval) = @_;
>>
>> print "SET_PROPERTY: " . $pspec->get_name . " newval: $newval\n";
>>
>> $self->{$pspec->get_name} = $newval;
>
>
> This check is clever -- "don't rebuild the pathdef if we don't have
> all the info yet" -- but is a little too strict. You've set a default
> value for radius, but this check makes it impossible for that to be
> used (unless you duplicate the default value in your INIT_INSTANCE,
> which i don't recommend).
>
> If you just drop the (defined $self->{'radius'}) clause, we can do
> another fix below that makes it work much better.
>
>> if ((defined $self->{'radius'}) &&
>> (defined $self->{x1}) && (defined $self->{y1}) &&
>> (defined $self->{x2}) && (defined $self->{y2}))
>> {
>> $self->set_path_def(_roundedRect($self->{'radius'},
>> $self->{x1}, $self->{y1},
>> $self->{x2}, $self->{y2}));
>
>
> Here, we're digging straight into the object's copies of the data.
> Nice and efficient, but sidesteps all of the default-handling that
> would be nice to use. If, instead, you use Glib::Object::get() (yes,
> even inside your own object implementation), then the default value
> machinery can take effect, and you don't have to set 'radius'
> explicitly before anything happens.
>
> Note that ->get() will return a list of values if given a list of keys...
>
> $self->set_path_def (_roundedRect ($self->get (qw(radius x1 y1 x2 y2))));
>
> (Looking into this a little more, it turns out that there's a vfunc
> (that is, a virtual method with no associated signal) called "update"
> that is used by the C implementations to do the actual drawing of a
> canvas item. The bindings currently do not offer the ability to
> implement that vfunc (for a variety of reasons, including "nobody ever
> pointed out that it was missing"). At some point i will play with that
> and find out if it makes this object easier to implement.)
>
>
> Now strike everything from here to the end of the block. Choosing the
> colors should be left to the user of this object.
>
>> $self->set('outline-color'=>'black'); # default.
>>
>> # $self->set('fill-color'=>'white');
>>
>> my $color = Gtk2::Gdk::Color->new(255,255,255);
>>
>> $self->set('fill-color-gdk'=>$color);
>> }
>> }
>
>
>
> And the answer you've been dying for is just around the bend...
>
>> # _roundedRect: return the rounded rectangle path.
>> # --------------------------------------------------------
>> sub _roundedRect
>> {
>> my ($r, $x1, $y1, $x2, $y2) = @_;
>>
>> # Make sure (x1,y1) is upper left.
>>
>> if (($x1 < $x2) && ($y1 > $y2))
>> {
>> my $t1 = $y1; $y1 = $y2; $y2 = $t1;
>> }
>>
>> if (($y1 < $y2) && ($x1 > $x2))
>> {
>> my $t1 = $x1; $x1 = $x2; $x2 = $t1;
>> }
>>
>> print "x1: $x1 y1: $y1 x2: $x2 y2: $y2\n";
>>
>> # Get the points for the path.
>>
>> my @p = ();
>>
>> push @p, _bezier('UPPER_LEFT', $r, $x1, $y1);
>> push @p, _bezier('UPPER_RIGHT', $r, $x2, $y1);
>> push @p, _bezier('LOWER_RIGHT', $r, $x2, $y2);
>> push @p, _bezier('LOWER_LEFT', $r, $x1, $y2);
>>
>> # Build the rounded rectangle path. Problem?
>>
>> my $pathdef = Gnome2::Canvas::PathDef->new();
>>
>> $pathdef->moveto ($p[0], $p[1]);
>> $pathdef->curveto ($p[2], $p[3], $p[4], $p[5], $p[6], $p[7]);
>> $pathdef->lineto ($p[8], $p[9]);
>> $pathdef->curveto ($p[10], $p[11], $p[12], $p[13], $p[14], $p[15]);
>> $pathdef->lineto ($p[16], $p[17]);
>> $pathdef->curveto ($p[18], $p[19], $p[20], $p[21], $p[22], $p[23]);
>> $pathdef->lineto ($p[24], $p[25]);
>> $pathdef->curveto ($p[26], $p[27], $p[28], $p[29], $p[30], $p[31]);
>> $pathdef->lineto ($p[0], $p[1]);
>
>
> You can't fill an open path. Close the path to allow fill-color to work.
>
> $pathdef->closepath_current;
>
>
>> return $pathdef;
>> }
>>
>>
>> # _bezier: return corner bezier points.
>> # --------------------------------------------------------
>> sub _bezier
>> {
>> my $corner = shift(@_);
>> my $r = shift(@_);
>> my $x = shift(@_);
>> my $y = shift(@_);
>>
>> if ($corner eq 'UPPER_LEFT')
>> {
>> return ($x,$y+$r, $x,$y+($r/2), $x+($r/2),$y, $x+$r, $y);
>> }
>>
>> if ($corner eq 'UPPER_RIGHT')
>> {
>> return ($x-$r,$y, $x-($r/2),$y, $x,$y+($r/2), $x, $y+$r);
>> }
>>
>> if ($corner eq 'LOWER_RIGHT')
>> {
>> return ($x,$y-$r, $x,$y-($r/2), $x-($r/2),$y, $x-$r, $y);
>> }
>>
>> if ($corner eq 'LOWER_LEFT')
>> {
>> return ($x+$r,$y, $x+($r/2),$y, $x,$y-($r/2), $x, $y-$r);
>> }
>>
>> return ();
>> }
>>
>>
>> 1;
>
>
Wow! Thanks for the very instructive critique of the code. :-)
I shall revise my copy accordingly.
-James

_______________________________________________
gtk-perl-list mailing list
[hidden email]
http://mail.gnome.org/mailman/listinfo/gtk-perl-list
Reply | Threaded
Open this post in threaded view
|

Re: rounded rectangles problem

James Muir-2
Here's the latest RoundedRect.pm. It works great! Are there any
improvements that should be made before I call it done?

#!/usr/bin/perl -w
#
# RoundedRect: Create a Gnome2::Canvas::Item rounded rectangle.
#
# This module inherits from Gnome2::Canvas::Shape via
Glib::Object::Subclass.
#
# Author: James Muir ([hidden email]).
#
# Thanks: muppet, Jan Hudec, [hidden email]
#
# Sample Usage:
#
# use strict;
# use Gtk2 '-init';
# use Gnome2::Canvas;
# use RoundedRect;
#
# my $window = Gtk2::Window->new();
#
# $window->set_default_size(200,200);
# $window->signal_connect (destroy=>sub {Gtk2->main_quit;});
#
# my $canvas = Gnome2::Canvas->new_aa();
#
# $window->add($canvas);
#
# my $rect = Gnome2::Canvas::Item->new ($canvas->root, 'RoundedRect',
# radius=>10, x1=>0, y1=>0, x2=>100, y2=>100,
# width_pixels=>5, outline_color=>'black',
# fill_color=>'white');
#
# $window->show_all;
#
# Gtk2->main;
#
# Notes: In order to receive events while mousing over the interior of the
# rounded rectangle, you must fill the rounded rectangle with a color.
#
# The corner radius of the rectangle may be up to 3/8ths of the
# shortest side of the rectangle.
#
# -------------------------------------------------------------------------
package RoundedRect;

use strict;
use Gnome2::Canvas;
use POSIX qw(DBL_MAX);

use Glib ':constants';
use Glib::Object::Subclass
Gnome2::Canvas::Shape::,
properties => [
Glib::ParamSpec->double ('radius', 'corner_radius', 'Radius of corners',
0.0, DBL_MAX, 10.0, G_PARAM_READWRITE),

Glib::ParamSpec->double ('x1', 'x1', 'Upper left X coord',
-(DBL_MAX), DBL_MAX, 0.0, G_PARAM_READWRITE),

Glib::ParamSpec->double ('y1', 'y1', 'Upper left Y coord',
-(DBL_MAX), DBL_MAX, 0.0, G_PARAM_READWRITE),

Glib::ParamSpec->double ('x2', 'x2', 'Lower right X coord',
-(DBL_MAX), DBL_MAX, 0.0, G_PARAM_READWRITE),

Glib::ParamSpec->double ('y2', 'y2', 'Lower right Y coord',
-(DBL_MAX), DBL_MAX, 0.0, G_PARAM_READWRITE),
]
;


sub SET_PROPERTY
{
my ($self, $pspec, $newval) = @_;

$self->{$pspec->get_name} = $newval;

# Set the path for the rectangle after we've received all the coordinates.
# Use Glib::Object::get method to fetch values for parameters as it will
pickup
# default values.

if ((defined $self->{x1}) && (defined $self->{y1}) &&
(defined $self->{x2}) && (defined $self->{y2}))
{
$self->set_path_def(_roundedRect($self->get (qw(radius x1 y1 x2 y2))));
}
}


# _roundedRect: return the rounded rectangle path.
# --------------------------------------------------------
sub _roundedRect
{
my ($r, $x1, $y1, $x2, $y2) = @_;

# Make sure (x1,y1) is upper left.

if ($y1 > $y2)
{
my $t1 = $y1; $y1 = $y2; $y2 = $t1;
}

if ($x1 > $x2)
{
my $t1 = $x1; $x1 = $x2; $x2 = $t1;
}

# Limit size of the corner radius.

my $max_radius = _min(($x2 - $x1), ($y2 - $y1)) * 3 / 8;

$r = _max(0, _min($r, $max_radius));

# Get the points for the path.

my @p = ();

push @p, _bezier('UPPER_LEFT', $r, $x1, $y1);
push @p, _bezier('UPPER_RIGHT', $r, $x2, $y1);
push @p, _bezier('LOWER_RIGHT', $r, $x2, $y2);
push @p, _bezier('LOWER_LEFT', $r, $x1, $y2);

# Build the rounded rectangle path.

my $pathdef = Gnome2::Canvas::PathDef->new();

$pathdef->moveto ($p[0], $p[1]);
$pathdef->curveto ($p[2], $p[3], $p[4], $p[5], $p[6], $p[7]);
$pathdef->lineto ($p[8], $p[9]);
$pathdef->curveto ($p[10], $p[11], $p[12], $p[13], $p[14], $p[15]);
$pathdef->lineto ($p[16], $p[17]);
$pathdef->curveto ($p[18], $p[19], $p[20], $p[21], $p[22], $p[23]);
$pathdef->lineto ($p[24], $p[25]);
$pathdef->curveto ($p[26], $p[27], $p[28], $p[29], $p[30], $p[31]);
$pathdef->lineto ($p[0], $p[1]);

# Close the path so that 'fill-color' will work.

$pathdef->closepath_current;

return $pathdef;
}


# _bezier: return corner bezier points.
# --------------------------------------------------------
sub _bezier
{
my $corner = shift(@_);
my $r = shift(@_);
my $x = shift(@_);
my $y = shift(@_);

if ($corner eq 'UPPER_LEFT')
{
return ($x,$y+$r, $x,$y+($r/2), $x+($r/2),$y, $x+$r, $y);
}

if ($corner eq 'UPPER_RIGHT')
{
return ($x-$r,$y, $x-($r/2),$y, $x,$y+($r/2), $x, $y+$r);
}

if ($corner eq 'LOWER_RIGHT')
{
return ($x,$y-$r, $x,$y-($r/2), $x-($r/2),$y, $x-$r, $y);
}

if ($corner eq 'LOWER_LEFT')
{
return ($x+$r,$y, $x+($r/2),$y, $x,$y-($r/2), $x, $y-$r);
}

return ();
}


# _max: maximum of two values.
# --------------------------------------------------------
sub _max
{
my $i = shift(@_);
my $j = shift(@_);

return (($i > $j) ? $i : $j);
}


# _min: minimum of two values.
# --------------------------------------------------------
sub _min
{
my $i = shift(@_);
my $j = shift(@_);

return (($i > $j) ? $j : $i);
}



1;

_______________________________________________
gtk-perl-list mailing list
[hidden email]
http://mail.gnome.org/mailman/listinfo/gtk-perl-list
Reply | Threaded
Open this post in threaded view
|

Re: rounded rectangles problem

Torsten Schoenfeld
On Fri, 2005-08-12 at 13:49 -0400, James Muir wrote:

> Here's the latest RoundedRect.pm. It works great! Are there any
> improvements that should be made before I call it done?

I don't know if your mailer ate all spaces, but you should definitely
use some indention.

--
Bye,
-Torsten

_______________________________________________
gtk-perl-list mailing list
[hidden email]
http://mail.gnome.org/mailman/listinfo/gtk-perl-list