I program occasionally. The occasions are often quite far apart, so when I start a new project I often find that I've forgotten how to do things. So I've started documenting some of the not-especially-challenging, but not-completely-trivial-either things I've done. And when someone else has produced a document that describes how to do something useful, but forgettable, then I include a link to that person's site. Feel free to use these techniques in your own programs.
|
|
Delphi |
| positions interface items relative to each other |
| changes the value of an arbitrary number of identifiers (usually tControl properties such as panel.top, panel.left) gradually over a specified time period. Useful for animating appearance or disappearance of controls, and movements. The intermediate value for the identifier is determined by a callback function progressFunction(relativeProgress). Three progress functions are supplied (linear, sine (soft-finish) and bulge (overshoots)). The default is linear; you can override it globally or on a per-identifier basis, and you can write your own progress functions. | |
|
function based on |
Searches a tTreeView for a node with a specified name. Makes it visible if it's hidden, either because the the part of the tree that contains the node is collapsed, or because the node is off the screen. |
|
Command undo/redo |
The units in this snippet allow you to add infinite undo/redo behaviour to your application, and to display a command trace that the user can interact with to control the opeation of the undo/redo. |
|
|
Javascript |
function
UrlExists(url)
{ |
| Interpolater class declaration
(includes nameAndValues)
Interpolater constructors and destructors
Some progress functions, including linear, the (essential) default
(All Delphi)
|
The interpolater generates a series of values
for a variable that are intermediate between some start value and some endvalue.
The values at any instant are a function of the time since the start of the
interpolation, as a proportion of the specified total duration. It is intended to allow a programmer to set up size and location animations with minimal fuss. You tell it the names of the variables whose values to be interpolated (typically properties of controls such as memo1.top, memo1.width and so on), the startvalue and endvalue for each variable, and optionally, the name of a function that defines an type of interpolation. It can handle any number of properties. For example, it can manage an animation in which a control's (top, left, bottom, right) properties are continuously varied from , say (0, 0, 100, 500) to, say, (300, 400, 20, 800), so that a control that is five times wider than it is high and is located in the top, left corner of the window moves so that its top left corner is at (300, 400) at the same time as its height reduces to 20 and its width increase to 800. If desired, other controls, which may or may not be children of the first control, can be moved and resized independently during the same interpolatoin. Any other integer-based property could also be varied continuously during the animation. The heart of the interpolater is really just a single statement:
which produces a current value for some variable. During the course of the interpolation, the value changes from startVal to endVal. relativeProgress varies from 0 to 1 during the course of the interpolation. The default easures the progress of the interpolation, and the default progressFunction (called linear) simply returns relativeProgress, so that the value changes steadily throughout the duration of the interpolation. The generality of the approach stems from two things
USAGE example interpolater := tInterpolater.create;
The application.processMessages command, executed once per iteration, ensures that the progress of the animation is displayed. |
| Put Procedure
(Delphi) |
The put procedure can be used to position
one control with respect to another.
USAGE examples
etc. The identifiers with the funny prefixes (ioOutside, rtTopEdge etc.) use the following two types that need to be declared. TYPE
There is more explanation in the code. |
| undo/redo CommandAdminUnit CommandsUnit (Delphi) |
This is a set of units that allow a programmer to create commands with
(infinite) undo/redo capabilities. Each user command may incorporate an
arbitrary number of system-level commands. The undo/redo facility acts at
the user command level. For example, the user may generate a Clear
command that deletes a number of items. each deletion effected by a separate
delete system command. The system commands are
derived from a superclass of type
tCommand, and are stored in a list.
For each command there are
three methods; enact,
unact, and
react.
React is often just a call to
enact, but sometimes there are initialisation actions that should not be
repeated, and in that case,
react is different.
CommandAdminUnit handles creating the command list, populating it and running the commands. You should basically be able to paste it straight into your program. CommandsUnit unit contains the class definitions of all the command classes and the definitions of the enact, unact, and react methods. These you will have to write. .Each command class will have a set of properties that contain the information used by its enact, unact, and react methods. When the program creates the command, it loads these properties. There is a special syntax for naming the command classes (at least if you want to take advantage of the command list display that helps the user see where in the undo/redo sequence they have reached). The trace procedure matches words (string with an upper case first letter) in the classname for the command and replaces underscores with values for those strings. For example if you had a command class called tAddPhoto_ToPanel_ and your enact method for that command included the code trace('photo', 17);
then the following line of text would be added to the command list display Add photo 17 to panel REJECT IMAGES PANEL Unfortunately, if an object is declared as type tCommand and assigned an object of type tAddPhoto_ToPanel_ (as happens when the command is passed as a parameter to the enact method) Delphi needs to be told that its type is tAddPhoto_ToPanel_ before it can execute the enact method unique to the tAddPhoto_ToPanel_ command. So the enact method of tCommand (the parent class of all the commands) contains a big nested if statement that decides which child command to execute. Here's the first three lines of such a nested if statement if self is
tAddTag_ToParent_InTagtree_
then (self as tAddTag_ToParent_InTagtree_)
.enact where self is the command. That sorta breaks the usual convention about inheritance, because the enact methods of those commands can't have an inherited statement, as that would cause an infinite loop. I don't like doing it, but I can't see a neat alternative. It's really annoying that you can't access the subclass method directly, without going through the is/as rigmarole. USAGE example Create a user command that copies one or more tags to a photo. Each copy operation involves a separate system level command, so if there's only one tag to copy, the commandType of the single command that's created is firstAndLastCommand. If there are multiple tags, then the first command has commandType firstcommand, and the last command has the commandType lastCommand. var command : tCopyTag_FromTaghierarchytreeToPhoto_; ... for i := 0 to tagTally - 1 do // set flags on the system
commands at the start and end of the the user command // create the command // add it to the command list // set any special values the command
needs; these are different for each command type // once all the system commands that make up
the user command have been set up, run them That code appears in the program somewhere other than the commandAdmin unit of the Commands unit (in a unit called eventHandler, in my program). The commandAdmin unit obeys the enactUserCommand method call by running through the command list from firstCommand to lastCommand. Thus the user sees all the tag copies as a single action. The command trace tells a more detailed story. The code above has generated system commands 0-5 in the trace listing below \_ _ _ _ _ _ _ _ _ _ _ _ _ _ The horizontal lines are divisions between user commands. Clicking on a dashed horizontal line undoes or redoes the command list to that point (and the solid line moves to that point to indicate the current state of the system). When some of the commands have been undone - so that the solid line has moved to the point between system command 4 and system command 5, for example - generating a new command wipes out the commands after the solid line. I could have built the commands into a tree instead of a list, and then it would have been possible to explore alternative paths without losing the original command sequence. One day, maybe...
|