Clipping System


In this article you will find a description of control areas and how controls hide it's parts or child controls when they should be hidden.



Control areas

Look at the picture below. You can see a GroupBox with Memo inside on the picture. Although controls can have different shapes all of them can be fit in a rectangle called a control rectangle. Controls may have some effects. On the picture below the effect is shadow. A rectangle which fit control and it's effects is called render rectangle. Effects are usualy just garments, they don't respond for user events like click. When a control have child controls usually, like shown below, the children don't take entire control part. Such a part is called client rectangle. All alignments happen in client rectangle of a control. By default in a CControl, from which all controls are derived, all the rectangles are the same. When creating a control you only need to specify different rectangles for client and render rectangles. You can do it by overwriting getClientRect and getRenderRect methods of the CControl. All coordinates are relative to control rectangle. For example, if your control has shadow with blur radius 5 pixels and it displaced on 10 pixels in X and Y directions, then you should override getRenderRect and return Rect{0, 0, getWidth() + 5 + 10, getHeight() + 5 + 10};. Controls use the same coordinate system as form uses. Y direction is from top to down.

Control areas

Local clipping

By local clipping we mean hiding some parts of a control when you draw control itself. It could be accomplished by many ways. There are two ways commonly used by the framework controls. The first one is when visible area is right rectangle. In this case you may create a temporal image(or texture - ITexture) with required size and draw on it. Then just draw temporary texture. The second method uses so called "block clipping". By block we mean a special shape. The shape has coordinates of left, top, right, and bottom borders. It also has borders and round corners. If a border isn't required we assume it's width is zero. The same is for round corners. If the corner isn't round, it's radius is zero. Most of the drawing methods of the IRenderer has a parameter called "block". You may pass a pointer to a block and a drawing will be clipped by this block. For example, it may be only inside area drawn, or only left border part. Look at the following picture. The panel has one round border and it is drawn with block applied so only the part which is inside the block(control rectangle minus borders and padding and minus round corner) is drawn(you may see the caption is clipped a little at right bottom corner).

Control clips own client area by block-clipping

Global clipping

Global clipping is a clipping applied to controls. This is done using images called "masks". Using IRenderer you can draw not only onto a form but on images or textures as well. You can create texture and set it as a render target by calling setRenderTarget method of renderer. A mask is a texture applied to all rendering methods of the renderer while mask is active. The only alpha or opacity channel of the mask texture is taken into concideration. If it's value is zero, the drawing is skipped, if it is greater than zero the pixel is multiplied by the mask alpha. Masks are cumulative. You may set several mask in renderer. This is used when drawing child controls to hide parts of them which are out of the parent control area. Look at the picture below. You can see the child Button control is properly clipped by client area of the parent GroupBox control. This is done by using a mask. When you want such a behaviour for your control just draw a mask for the client area(with alpha = 255 for visible area and 0 for invisible), activate mask using IRenderer before drawing child controls, deactivate the mask after drawing child controls. You have to know the child controls are rendered automatically but after rendering the parent control. So in you NotifyOnPaint notification you should render your control, then render mask, then activate the mask. And in you NotifyOnPainEnd, which happens after drawing all child controls, you should deactivate your mask. All standard controls has a possibility to turn off/on the mask. They have a property called "UseMask". It is recommended to turn off a mask if your sure no clipping is required. Don't forget, every drawing consume additional time, try to avoid unnessecary drawings. By default masks are on for all standard controls.

Clipping control by mask of another control