SmartSplitter 1.3.2

by Harry Hooie
http://www.harryhooie.com/source/

I think you underestimate
my smartyness!

What is it ?

SmartSplitter is a cross-platform open-source control splitter class for REALbasic 5 or greater. The SmartSplitter control allows the user to resize other controls contained in the window as desired. It can automatically attach nearby controls thus achieving full functionality with NO coding and NO binding! SmartSplitter is developed for and tested on Linux, Mac OS 9, Mac OS X, and Windows 98/2000/XP.

SmartSplitter began like so many other classes do - as a simple crappy class in an early crappy project. But as I used this divider thingy in more of my projects, I gradually added in smarts until so great the intellect of this amazing class, I felt obliged to spend the extra effort to simplify it, clean it up, and make it more accessible.

IMPORTANT: If your window is to be resizable, a call to each SmartSplitter's behave method needs to be added to the parent window's Resized event (AND to the Resizing event if the window is set to LiveResize) see how


How much is it ?

The SmartSplitter class is provided free for all to use. However, I do maintain ownership of SmartSplitter. You are free to redistribute the source code. If you distribute it as a standalone class, please include this documentation with it. If you distribute source code containing SmartSplitter, you do not need to include this documentation. There is no requirement to credit me in your program for using SmartSplitter.

If you make any code improvements, you are welcome to submit them, and I will try to incorporate them in the next update so that everyone using SmartSplitter can benefit.

If you use SmartSplitter in a project that generates actual profit (unlike any of mine;) or in a project for your work, you may feel compelled to offer compensation for the benefit it provided. Any monetary gift of thanks would be appreciated and would encourage continued development and support of this and other projects. To donate, go here.


Using SmartSplitter

First add the SmartSplitter class to your project by dragging the "SmartSplitter" folder into your project window.

To add a SmartSplitter to one of your project windows, add a canvas to the window and change the canvas's Super property from "Canvas" to "SmartSplitter". (or drag the SmartSplitter class from Project Controls onto the window) Then resize it so that it covers the area between the controls you wish to be adjustable.

If the splitter's height is greater than its width, it will act as a vertical splitter and look for controls to the left and right. Otherwise it will act as a horizontal splitter looking for controls above and below. Controls with edges within 5 pixels of the edges of the splitter will automatically attach unless you command otherwise.

All controls you wish to adjust with the splitter should be position-locked (i.e. LockLeft, LockTop, etc.) so that they resize correctly with the window - even if the window is not going to be resizable.

If you are going to set the SmartSplitter to DisableLiveDrag, its control order should be greater than that of the attached controls so that it will appear above the controls during the drag. Otherwise, for live dragging, set the SmartSplitter to be behind any attached controls so that in case one of those controls is showing a focus ring, it will not be visibly clipped in composite windows.

The SmartSplitter folder also contains the CanvasSmartSplitterDebugger canvas class which may be used to better see what SmartSplitter is doing.


Now behave

If your window is to be resizable, add a call to each SmartSplitter's behave method to the parent window's Resized event:

Sub Resized()

SmartSplitter1.behave
SmartSplitter2.behave

End Sub

And also to the Resizing event if the window is set to LiveResize:

Sub Resizing()

SmartSplitter1.behave
SmartSplitter2.behave

End Sub

The behave method keeps the SmartSplitter locked to its MinLimit and MaxLimit if it was previously locked and adjusts its proportions if StayProportional is set to true. (see Setting SmartSplitter properties)

For most common cases, the above is all you will need to do !

Getting adjusted

Controls are determined when first attached whether they need to be moved, resized, or both.

SmartSplitter can only determine control behaviour correctly when the controls being attached are first position-locked so that they resize correctly with the window. So if you are having problems, resize your window and see if your controls are resizing as they should.

SmartSplitter also looks at the control's class, orientation, and settings to determine whether it should be resized. For example, a EditField or StaticText based control would never be set to resize vertically if MultiLine = False.

An embedded control gets set differently since REALbasic is supposed to move it with the parent control. An embedded control set to LockLeft will be suggested to resize by a vertical splitter (LockTop by a horizontal) since it will assume the parent control is being resized as well.

But invariably, SmartSplitter will sometimes guess wrong no matter what you do, so...


Preferential treatment

To save the splitters position to restore later, such as with preferences, save the SmartSplitter's position or proportion method value:

i = SmartSplitter1.position ' dim as integer
or

x = SmartSplitter2.proportion ' dim as double 0 ≤ x ≤ 1

All references to position (Min & Max) and proportion are from left to right with vertical orientation and from top to bottom with horizontal orientation.

Restore the splitter's (and its attached controls') position using the companion method - setPosition or setProportion (see Setting SmartSplitter properties for more details):

SmartSplitter1.setPosition = i ' as integer
or

SmartSplitter2.setProportion = x ' as double

Position should be referenced as an integer. Proportion must be referenced as a single or double - double is preferred as that is how it is referenced internally. Proportion should be equal or greater than 0 and less than or equal to 1.
Primary SmartSplitter methods

These 3 methods are the meat of SmartSplitter.


Getting attached

If there are additional controls that should be impacted by the splitter, then the following should be added to the sub's Open method:

Sub Open()

me.attachNearbyControls ' (if you want it to auto-attach what it can)
me.attach otherControl1
me.attach otherControl2
me.attach otherControl3

End Sub

Attempts to attach a control that has already been attached will be ignored.

The attachNearbyControls method is called internally in the SmartSplitter super AFTER your SmartSplitter sub's Open method is called - but ONLY if attach has not yet been called. This is why if you want to auto-attach, me.attachNearbyControls must be called before any additional controls are attached.

To attach all your controls manually, leave out the me.attachNearbyControls and use me.attach to attach them.

When a control that commonly has embedded controls (TabPanel, PagePanel, etc.) is attached, all its embedded controls are recursively attached as well. If you want to manually attach embedded controls as well, set doNotAttachEmbeddedControls = true before you attach any controls that contain embedded controls. Controls that are searched for embedded controls are determined in the controlCommonlyHasEmbeddedControls method.

Only controls whose superclass is a RectControl can be attached. In REALbasic 5, the Line Control is a RectControl, but since it ignores the Left, Top, Width, and Height properties, it would have required additional code that was likely to break in a future version of REALbasic. So SmartSplitter will ignore any attempts to attach a Line Control. If you want the splitter to move/resize a Line Control, you will have to put the appropriate code in that SmartSplitter sub's Moved method.


Setting attached control properties

Controls MUST be attached before attempting to modify their properties.

The most commonly used will be setAttachedControlShouldMove, setAttachedControlShouldResize, and setAttachedControlNormallyVisible.

Example of changing control properties in the splitter's Open method:

Sub Open()

me.attachNearbyControls
me
.setAttachedControlShouldMove BevelButton1, true
me.setAttachedControlShouldResize Canvas1, false
me.setAttachedControlShouldCenter BevelButton3, false
me.setAttachedControlMinSizeVisible MoviePlayer1, 64

End Sub

There may be certain configurations of embedded controls inside of embedded controls that cannot be controlled correctly by adjusting the move and resize properties. Those controls will require the appropriate code in that SmartSplitter sub's Moved method.
Setting SmartSplitter properties

--- Using the IDE "Properties" window ---
The following additional properties should be available for SmartSplitters in REALbasic's "Properties" widow if you are using REALbasic 4.5 or greater:

  • MaxLimit: Maximum the splitter can be moved. If = 0, it will be auto-set. Setting this in the IDE "Properties" window tells the SmartSplitter to NOT adjust it with window/control resize.
  • MinLimit: Minimum the splitter can be moved. If = 0, it will be auto-set.
  • MaxLimitOffset: Offset distance from the MaxLimit that the splitter will stop at before snapping to the MaxLimit. If = 0, it will be auto-set. If < 0, it will be set = 0.
  • MinLimitOffset: Offset distance from the MinLimit that the splitter will stop at before snapping to the MinLimit. If = 0, it will be auto-set. If < 0, it will be set = 0.
  • StopAtMaxOffset: If checked, the splitter will stop at MaxLimitOffset instead of MaxLimit.
  • StopAtMinOffset: If checked, the splitter will stop at MinLimitOffset instead of MinLimit.
  • StayProportional: If checked, the splitter will stay proportional within its movement range if its parent window/control is resized.
  • Debug: If checked, information about the splitter and attached controls will be drawn to the CanvasSmartSplitterDebugger. (Requires that a canvas of class CanvasSmartSplitterDebugger be somewhere in the window)
  • SwapOrientation: If checked, the automatically determined orientation will be swapped. Vertical to Horizontal or Horizontal to Vertical.
  • DisableLiveDrag: If checked, attached controls will update only on MouseUp event instead of on MouseDrag events. When checked, the splitter's control order needs to be higher than the other controls so that it will be visible above them when dragging.
  • HandleType: Type of handle to be painted. -1 = No Handle; 0 = Default Handle; 1 = Small Bar Handle; 2 = Large Bar Handle; 3 = Small Dimple Handle; 4 = Large Dimple Handle

--- Using Code ---
I recommend attaching all controls before setting SmartSplitter "Limit"-related properties using code since "Limit"-related values will likely auto-adjust as controls are added.


Other available methods
Debugging SmartSplitter

The CanvasSmartSplitterDebugger canvas class may be used to display debugging information while one or more SmartSplitters are adjusted. It will display the minLimit, MaxLimit, minLimitOffset, and maxLimitOffset values of a SmartSplitter as it is being dragged. It will also draw a red rectangle around all attached controls each time they are adjusted - including when the splitter's behave method is called. Invisible controls are denoted by a red X that spans the red rectangle.

To use, add 1 CanvasSmartSplitterDebugger canvas anywhere in the window.

Then check the Debug property in each SmartSplitter's "Properties" widow that you wish to debug. (or call setDebug(true))

Note that when not debugging, the canvas is not used so no extra memory is consumed.

You may set SmartSplitter.enableDebugCanvasCode constant to FALSE in the IDE to completely skip all
debug code. Also, if you delete SmartSplitter's debugCanvas property, CanvasSmartSplitterDebugger will no longer be required in your project.


Known issues with SmartSplitter
Revision history:

Version 1.3.2

Version 1.3.1

Version 1.3

Version 1.2.2

Version 1.2.1

Version 1.2

Version 1.1


Legal mumbo jumbo
SmartSplitter is provided "as is" without warranty of any kind, either expressed or implied. The entire risk arising out of use or performance of SmartSplitter remains with you. While the SmartSplitter source code itself may not be sold or distributed for profit, the SmartSplitter source code MAY be included with other software, services, publications, or products which are sold or distributed for profit as long as this documentation is included with it. Harry doesn't mind if he doesn't make the scene. He's got a daytime job, he's doing alright.