Non-rectangular shapes - Part I
Copyright © 2000 Alirio A. Gavidia B.. Para ver más de
mis artículos visite Programación
Orientada en Delphi
![]() |
Introduction
The present essay intends to show the possibilities of use of non-rectangular
shapes under Windows. The use is simple, but the possibilities look interesting.
Additionally, in particular in this first part, we will make use of two
important elements: Canvas and Timer.
The first example
In the previous article (see "Post-it". Sizable
windows without border or title) I made reference to the possibility
of creating an analog round clock like the one of Windows "PowerToys".
Making a round form is simple and it is limited to calling a couple of
procedures. The clock must also draw the hands (which requires certain
mathematic calculation) and update about 60 times per minute (this is
what the TTimer is for).
Note: Analog Clock in opposition to Digital. The term is NOT correct because the clock we create here -even if it has hands- is digital. For the clock to really imitate an actual analog behavior it would have to have a continuos movement. The one we create here moves from second to second and therefore it has finite and defined states and that makes it qualify as digital. It seems a more adequate term is "hands clock".
Without title
The clock is based on a simple borderless form, eliminating the title
bar, which results in a bother when we have a round form. This generates
the additional problem of moving the form. For this reason the drag
operation should be attended from the client area of the form. To solve
this inconvenience there are various ways. I choose the one applied in
the previous article (use of wm_Syscommand $f012):
procedure TForm1.FormMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
ReleaseCapture;
Perform(wm_Syscommand, $f012, 0)
end;
Another technique is responding to Wm_NcHitTest and
returning htCaption as the result. This is simply telling
Windows that the click was made in the title area. It seems to me that
there is at least another technique, but it isn't the objective of this essay.
The "timer"
In the form we included a TTimer control. This control fires
an event approximately at intervals (defined by the Interval property
given in milliseconds). Its use is well documented in Delphi, but I must add
something: it isn't exact.
The event associated with the timer executes three actions: eliminates the hands from the previous position, takes the system time, and puts the hands in a new position.
The "Canvas"
The Canvas provides graphical capacities for graphic controls
and forms. I offers possibilities to draw lines, ellipses, polygons, text
and others. Its usage is simple and practical.
We made use of the following elements of the Canvas:
Pen, to set the color and mode of the line draw.MoveTo, to set the initial point of a line draw.LineTo, to draw a line from the last position to the new one.Rectangle, to draw a rectangle according to the given parameters.
Particularly we have to note the property Canvas.Pen.Mode that
allows the use of values like pmXor or pmCopy (among
many others).
Note: The use ofXoras a logical operator has an interesting property which is that applied twice you get the original value, i.e.:x xor yresults inz, and if you applyxor yagain tozit results inx. This is useful for simple encrypting where we want to apply a key to encrypt and the same key and the same algorithm to decrypt. It is also useful when we want to draw something and then erase it because it is only required to draw it again applying a "xor" between the background and the pixel to draw.
The Paint event of the form certainly makes use of
the Mode and Color properties of
the Pen obtaining a curious effect. The result, at least
for me, wasn't the expected, however it was nice. All this seems to be
the consequence of the way in which a rectangle is created. It is filled
and then the upper and left border are drawn, that under
the Xor operator generate a white color and the resulting
image generates a 3D effect. I suggest the reader to comment out the
lines Mode and Color to draw his own
conclusions.
As an additional comment, Pen.Color only affects the
border of the rectangle. The filling is defined by
the Brush property of the Canvas which
in this case remains with its default value.
procedure TForm1.FormPaint(Sender: TObject);
Var
X1, Y1, i : integer;
Angle : double;
begin
Canvas.Pen.Mode := pmXor;
Canvas.Pen.Color := clBtnFace;
for i:=1 to 12 do
begin
Angle := i * 2 * Pi / 12;
X1 := trunc(X0 + Len * Sin(Angle));
Y1 := trunc(Y0 - Len * Cos(Angle));
Canvas.Rectangle(X1-4,Y1-4,X1+4,Y1+4)
end;
Canvas.Pen.Mode := pmCopy;
Canvas.Pen.Color := clBlack;
end;
Arithmetic
The coordinates system has the point zero in the upper-left corner. To determine the position of the hand we need the center and the ending point. The center is the half of the width and the height. For the length of the hands we take a fraction of the minimum between the width and the height.
Knowing the center and length of the hand, we only need to know the
position of its ending point and for this we determine the angle. Here
we end up turning to trigonometry. The angle is determined with the zero
in 12 o'clock, 90 degrees at 3 o'clock, 180 degrees at 6 and 360 at 12.
The sine of the angle gives us the opening in the "X" coordinate, while
the cosine gives us the opening of the "Y" coordinate. As an additional
remark, the coordinates system in the "Y" axis is inverted with respect
of the usual trigonometric systems. All angles must be expressed in
radians to be used with the sine and cosine functions. The
procedure DrawWatchHand makes the calculations (also
in FormPaint)
procedure Tform1.DrawWatchHand;
:
:
// Now draw Second
Canvas.MoveTo(X0, Y0);
Angle := S * 2 * Pi / 60;
X1 := trunc(X0 + 9*Len * Sin(Angle)/10);
Y1 := trunc(Y0 - 9*Len * Cos(Angle)/10);
Canvas.LineTo(X1, Y1);
:
:
Notice that each hand hand uses a proportion of the Len value. For example the seconds are 9/10 of Len, minutes are 3/4 and hours are 1/2.
Round
Until now we have used the Canvas to draw, eliminated
the title bar and substituted its functionality, we have done some
arithmetic and we have made use of a timer. The central
point that gives title to this article is the creation of a non-rectangular
shape. Well, this is tremendously easy and is solved with two calls to
Windows in the FormCreate of the main form:
hRegion := CreateEllipticRgnIndirect(R);
SetWindowRgn(handle, hRegion, true);
CreateEllipticRgnIndirect is a version
of CreateEllipticRgn with the parameters (a rectangle)
packed in only one structure. This routine returns a "handle" to an
elliptic region circumscribed to the given rectangle. If you want you can
define non circular regions using CreatePolygonRgn for
polygons, CreateRectRgn for rectangles (without
sense in our case, but it is possible), CreateRoundRectRgn for
rectangles with round corners, or CombineRgn to combine the
results of other regions.
SetWindowRgn defines the area in which a window is
visible. It is interesting that under this concept the rectangular
window still exists and we only defined something like a hole from
where we can see it.
A "Bug", a second example
My first attempt of a non rectangular shape was a couple of years ago with Delphi 3. It was a mischief motivated in that I know people who terribly hate cockroaches (and here in the tropic there are spectacular ones). So I created a cockroach that wanders on the Windows desktop. Its form is elliptic and I fundamentally created some images for different angles (four: two verticals and two horizontals). The result is a bit rustic, but I enjoyed creating this "bug". I very well know there are better ways to do this, but it comes to the case because of the subject of this article.
The next
Well, this opens interesting possibilities of use (as I mentioned before). I think the most adequate seems to be the one of "Skins" like some applications. For reference you have "Winamp", "NeoPlanet", "Windows Media Player" (new version). The technique that allows non rectangular shapes is applicable to buttons and other controls.
Bibliography:
Delphi Developer's Handbook,
Marco Cantů, Tim Gooch, John F. Lam
SYBEX
ISBN: 0-7821-1987-5
1.998La Biblia de Delphi 5
Marco Cantů
ANAYA
ISBN: 84-415-0994-8
2.000
The full source code of the examples are available for download.
![]() |



