Creating a docker style widget

Discussion related to the Cross Codebot open source framework
Posts: 108
Joined: Thu Feb 05, 2015 6:31 pm

Creating a docker style widget

Postby sysrpl » Sat Mar 28, 2015 12:02 am

This article describes how to create cross platform desktop docker style widgets as demonstrated in the example below. This example is included in the Cross Codebot git repository.

A docker style widget is linear series of images buttons which grow or shrink based on their distance from the mouse pointer. The quintessential docker widget is the "OSX Dock", though the idea of a docker certainly isn't constrained to Apple's desktop operating system. All operating systems, and even applications, can have dockers. In this post I will show you how easy it is to create your own docker style widgets.

For this example I've decided to create a TDocker class which handles the layout of dockers items. The TDocker class does not care if the docker is positioned vertically or horizontally. It's only concerned with the smallest size of an item, the largest size of an item, and the number of items.

To arrange the layout of buttons, simply hook up mouse movement, either X or Y mouse coords depending on whether you want a horizontal docker or vertical docker, to the TDocker.MouseMove function:

TDockerItem = record
Offset: Float;
Size: Float;

TDockerItems = TArray<TDockerItem>;

function TDocker.MouseMove(MouseCoord, Area: Integer; out Items: TDockerItems): Integer;

This TDocker method will output an array of Count docker items, and return the index of the docker item under the mouse cursor, or -1 if no item was under the mouse. MouseCoord can be the mouse X or Y coordinate, and Area is the size used to center the docker items within.

It is important to note that TDocker does not actually draw anything or respond to events, it's a simple class to manage the layout of docker items.

To render a docker, like in the example above, it is actually very simple. Here are a few tips and trick which use the Cross Codebot graphics classes:

  • There many possible ways to render and stylize a docker, and no one way is wrong
  • You can use TImageStrip to hold docker images
  • Try resampling large and small images using IBitmap or TImageStrip Resample. This improves the final look of docker images, since Resample resizing quality is far superior than ISurface.CopyTo resizing quality.
  • If you are going to draw text, either provide a bubble with a background area for the text, or draw a text shadow to help make the text stand out.

And finally, here is the routine I am using to render a docker the in example at the top of the page. It's actually quite simple. The basic steps are, get the layout using MouseMove, and loop through the items to creating rectangles for use with ImageStrip.Draw. Notice that I use two image strips, one for the smallest items, and another larger one for all other items.

{ Draw a docker unto a surface given a mouse location, area to center it upon,
font to draw text, and small and large images. For best appearance Images should
have been resampled using IBitmap.Resample with bicubic filtering }

procedure DrawDocker(Surface: ISurface; Docker: TDocker; MouseCoord: Integer;
Area: TRectI; Font: TFont; SmallImages, LargeImages: TImageStrip);
DockerItems: TDockerItems;
DockerIndex: Integer;
R: TRectF;
F: IFont;
P: IPen;
I: Integer;
{ Calculate the docker items based on a mouse coord and area.
Note: A docker can be arranged horizontal or vertical, with text above
below, left, or to the right. In this procedure we assume horizontal
with text above. Writing to those other alignments is trivial given this
example }
DockerIndex := Docker.MouseMove(MouseCoord, Area.Width, DockerItems);
{ For each docker item }
for I := 0 to Length(DockerItems) - 1 do
{ Get the rectangle }
R := TRectF.Create(DockerItems[I].Size, DockerItems[I].Size);
R.X := DockerItems[I].Offset;
R.Y := Area.Height - R.Height - 24;
{ If it's selected, draw a highlight }
if I = DockerIndex then
FillRectSelected(Surface, TRectI(R), 8);
{ If it's smallest use the small resampled image strip }
if R.Width = Docker.SmallSize then
SmallImages.Draw(Surface, I, TRectI(R))
{ All other sizes are streched using the larger image strip }
LargeImages.Draw(Surface, I, TRectI(R));
{ If the item is selected }
if I = DockerIndex then
R.Bottom := R.Top - 4;
R.Top := 0;
{ Prepare the font in white }
F := NewFont(Font);
F.Size := 24;
F.Style := [fsBold];
F.Color := clWhite;
{ But first create a path }
Surface.TextOut(F, Docker.Name[I], R, drDown, False);
P := NewPen(clBlack, 6);
{ And stroke the path over and over again with a
dark nearly transparent pen }
P.Color := P.Color.Fade(0.1);
while P.Width > 0 do
{ Which ends up creating a dark blurry shadow effect }
Surface.Stroke(P, True);
P.Width := P.Width - 2;
{ And finally write out the text in white }
Surface.TextOut(F, Docker.Name[I], R, drDown);

Posts: 4
Joined: Thu Mar 26, 2015 1:53 am
Location: Rio de Janeiro / Brazil

Re: Docker

Postby mdbs99 » Sat Mar 28, 2015 1:31 am

WOW!! :shock:

Return to “Cross Codebot”

Who is online

Users browsing this forum: No registered users and 1 guest