The Form Turorial
The problem that i found with the tutorial is that its quite long. Thats good because it teaches lots-o-knowledge. The downside is that this is a forum. And unless i make 25 posts it wont be easily navigate able. So i have now put it in wiki format, with some fixing of errors, here
*note*If you want the updated version, go to the wiki version. It's easier to navigate and will be updated past what is on the forums.
Chapter 1 - Beginning
- 1.0 Preface
- 1.1 Using the Form Editor
- 1.2 Properties
- 1.3 Events
- 1.4 Methods
- 1.5 Making a Simple Form
- 1.6 Good Form Syntax
Chapter 2 - Alternative Forms
- 1.0 Preface
- 1.1 ReadLn
- 1.2 GetApplication
- 1.3 MessageDlg
- 1.4 InputQuery/InputBox
- 1.5 ShowMessage
Chapter 3 - Good Things to Know
- 1.1 Parent Vs. Owner
- 1.2 To be Determined
Chapter 4 - Working with Various Components
- 1.1 Timer
- 1.2 MainMenu
- 1.3 Tabs
- 1.4 PopupMenu
- 1.5 Statusbar
- 1.6 Dialogs
- 1.7 etc
Chapter 5 - Drawing
- 1.1 Drawing
- 1.2 Adding Images
- 1.3 Adding Images/Drawing on Objects
Chapter 6 - Projects
- 1.1 Making a Calculator
- 1.2 Making an Analog Clock
- 1.3 Making a Game
Chapter 1 - Beginning
1.0 Preface
In Windows, most elements of the user interface are windows. All windows of a SCAR are based on TForm object. Form objects are the basic building blocks of a SCAR form, the actual windows with which a user interacts when they run the application. Forms have their own properties, events, and methods with which you can control their appearance and behavior. Here is a list of the supported objects in SCAR
- TApplication
- TButton
- TCanvas
- TCheckBox
- TColorDialog
- TColorPicker
- TComboBox
- TEdit
- TFindDialog
- TFontDialog
- TForm
- TGroupBox
- TImage
- TLabel
- TListBox
- TMainMenu
- TMemo
- TMenuItem
- TMenu
- TOpenDialog
- TPageControl
- TPanel
- TPopupMenu
- TProgressBar
- TRadioButton
- TReplaceDialog
- TRichEdit
- TSaveDialog
- TScrollBar
- TShape
- TStatusBar
- TStatusPanel
- TTabControl
- TTabSheet
- TTimer
and more, as of SCAR 3.20
1.1 Using the Form Editor
I personally don't use the Form Editor that much, and when i do its briefly, to maybe check something or make a quick .dfm file. Even though I don't use it, I'm going to go over it because i guess its easier for a person that's new to forms to learn to use it, though i don't suggest becoming completely dependent on it, because it is quite lacking, and has some errors.
- Now i hope you already know this, but you go to "Tools -> Form Editor" to get to it. Once you're there you have 3 windows:
- To add a Form Component your form, all you do is click on one of the form components' icons in the Form Designer and click again on your form. When you click on your form, the object's attributes are all at their default, except for the left and right(that changes based on where you click). If you also want to change the default size and shape of your object you can drag out the shape onto your form instead of clicking on the form.
- To edit a property, you have to click on the appropriate property that you want edited, and change it, either by dropping down the drop-down menu(if that property is constrained to certain parameters) or just typing in the change.
- To export a form, so that you can use it in your script, is also quite simple. First, save your form as a .dfm file to somewhere that you can find.
Then close the form editor and go to Tools -> Load DFM From Form. Then find your form and click open. You should notice some code pop up into your debug box. Starting with this generic code -
SCAR Code:
procedure InitForm;
begin
end;
procedure ShowFormModal;
begin
frmDesign.ShowModal;
end;
var
v: TVariantArray;
begin
setarraylength(V, 0);
ThreadSafeCall('InitForm', v);
setarraylength(V, 0);
ThreadSafeCall('ShowFormModal', v);
end.
add the beginning code to the InitForm procedure, and the vars above it.
*Note* you may have a problem with your code right now. When you run it, you will probably get an error saying that it cant run with an invisible form. If this is the case, then delete "Visible := true" from the form's properties in the InitForm.
Then run the code, you should see your form pop up, with whatever components you placed on it. That is your first form!
1.2 Properties
Properties, are what you use to change the appearance of your form components, and how they work. Some of the most common properties are: left, top, width, height, visible, enabled, and cursor. Though, properties are simple, they are needed to make your forms do what you want. They are, for the most part, self explanatory.
- Left will start the left of your component at that pixel of its parent.
- Top will start the top of your component at that pixel of its parent.
- Width and Height will make your component that tall and wide, in pixels.
- Visible, if false will make that component invisible
- Enabled, if false will disable that component
- Cursor, depending on the input will change what your cursor looks like upon mousing over the component
The biggest reason i have a section on properties, is that many are not needed in your script. For example, the default output from the form editor for a simple for is
SCAR Code:
frmDesign := CreateForm;
frmDesign.Left := 250;
frmDesign.Top := 114;
frmDesign.Width := 696;
frmDesign.Height := 480;
frmDesign.Caption := 'frmDesign';
frmDesign.Color := clBtnFace;
frmDesign.Font.Color := clWindowText;
frmDesign.Font.Height := -11;
frmDesign.Font.Name := 'MS Sans Serif';
frmDesign.Font.Style := [];
frmDesign.Visible := True;
frmDesign.PixelsPerInch := 96;
however, the color, font properties, visible, and pixels per inch aren't needed because they are set to that be default. It can be shortened to
SCAR Code:
frmDesign := CreateForm;
frmDesign.Left := 250;
frmDesign.Top := 114;
frmDesign.Width := 696;
frmDesign.Height := 480;
frmDesign.Caption := 'frmDesign';
(or even further if you really want to shorten it.)
All components have default heights and widths too. So if you know that you want your form to be the default size of a form, then just dont put it in, and it will work just as well. I just find it to be easier to leave default properties, out and it shortens your code too!
1.3 Events
Events are a bit more complicated than properties. Every component has its own set of events at its disposal. Every time the event for that event happens, whichever procedure you're sending it to is called. To put that more simply. The names of events, which are fairly self explanatory, tell what the event catches. For example, the OnClick event catches every time the mouse it left clicked on that component. Every time that event happens, it will call the specified procedure and run that code.
Now, lets say you have this form.
SCAR Code:
var
frmDesign : TForm;
Button1 : TButton;
procedure InitForm;
begin
frmDesign := CreateForm;
frmDesign.Left := 259;
frmDesign.Top := 132;
frmDesign.Caption := 'Clicker';
Button1 := TButton.Create(frmDesign);
Button1.OnClick:= @buttonclick;
Button1.Parent := frmDesign;
Button1.Left := 69;
Button1.Top := 61;
Button1.Width := 75;
Button1.Caption := 'Click Me';
end;
procedure SafeInitForm;
var
v: TVariantArray;
begin
setarraylength(V, 0);
ThreadSafeCall('InitForm', v);
end;
procedure ShowFormModal;
begin
frmDesign.ShowModal;
end;
procedure SafeShowFormModal;
var
v: TVariantArray;
begin
setarraylength(V, 0);
ThreadSafeCall('ShowFormModal', v);
end;
begin
SafeInitForm;
SafeShowFormModal;
end.
Now when the button is clicked, you want it to pop up a message saying "You've clicked this button x number of times!". To do this, you'll need to implement the OnClick event. just add
SCAR Code:
button1.OnClick := @YourProcedureName;//any name will work...well any name that would usually work as a procedure name :)
to your InitForm procedure (preferably grouped with the other button1 properties). The only thing that should look unfamiliar is the @. That my friends is a function sender. It basically just reroutes your code to go to that procedure when the event is called. Now just create a procedure with the name you inputted earlier.
But wait! theres a catch. All events' procedures have special parameters that need to be there for the script to work. To find these parameters, go to Tools -> Event Handlers List, and find OnClick. In the debug box, its now going to have
Originally Posted by
SCAR
procedure OnClick(Sender: TObject);
Just stick "(Sender: TObject);" on to the end of your procedure and thats it. Then every time you click the button it will run whatever code you have in that procedure. in our case, the final code will look something like this -
SCAR Code:
var
frmDesign : TForm;
Button1 : TButton;
i: integer;
procedure YourProcedureName(Sender: TObject);
begin
i := i + 1;
ShowMessage('You''ve clicked this button ' + IntToStr(i) + ' times!');
end;
procedure InitForm;
begin
frmDesign := CreateForm;
frmDesign.Left := 259;
frmDesign.Top := 132;
frmDesign.Caption := 'Clicker';
Button1 := TButton.Create(frmDesign);
Button1.Parent := frmDesign;
Button1.Left := 69;
Button1.Top := 61;
Button1.Width := 75;
Button1.Caption := 'Click Me';
button1.OnClick := @YourProcedureName;
end;
procedure SafeInitForm;
var
v: TVariantArray;
begin
setarraylength(V, 0);
ThreadSafeCall('InitForm', v);
end;
procedure ShowFormModal;
begin
frmDesign.ShowModal;
end;
procedure SafeShowFormModal;
var
v: TVariantArray;
begin
setarraylength(V, 0);
ThreadSafeCall('ShowFormModal', v);
end;
begin
SafeInitForm;
SafeShowFormModal;
end.
There are a few things to know though.
1. You must have a procedure with the name you sent it, else your script will not compile.
2. You can put the event for more than one component in the same procedure. For example.
SCAR Code:
Button1 := TButton.Create(frmDesign);
Button1.Parent := frmDesign;
Button1.Left := 69;
Button1.Top := 61;
Button1.Width := 75;
Button1.Caption := 'Click Me';
button1.OnClick := @YourProcedureName;
Button2 := TButton.Create(frmDesign);
Button2.Parent := frmDesign;
Button2.Left := 100;
Button2.Top := 61;
Button2.Caption := 'Click Me Too!';
button2.OnClick := @YourProcedureName;
then, often you'll run into the problem of no knowing which button was clicked. This, though, can be easily fixed by using a case. the sender variable holds the name of the object that was clicked, so you could do this.
SCAR Code:
procedure YourProcedureName(Sender: TObject);
begin
case sender of
button1: begin
i := i + 1;
ShowMessage('You''ve clicked this button ' + IntToStr(i) + ' times!');
end;
button2: begin
e := e + 1;
ShowMessage('You''ve clicked this button ' + IntToStr(e) + ' times!');
end;
end;
3. You can mix and match events as long as their parameters match. For example. the parameters for OnCreate are (Sender: TObject). So you could put that in the same procedure as OnClick, but the parameters for the OnMouseDown event are (Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer), so you couldnt put that in the same procedure, even though, the OnClick event has Sender as a parameter too.
1.4 Methods
Methods are quite simple. They are just pre-built procedures and functions that are built into the object. Free, SendToBack, BringToFront, and Repaint are all methods. There's not much to explain about them. Each object has their own set of methods, though there are some that are consistent within all objects (the 4 above are by the way). You essentially add your own if you were to make yourself a record with a form component in it, but that is something that you'll learn in a different tutorial.
1.5 Making a Simple Form
Well, now that you've learned the basics, lets make a form! Im not going to make anything too complicated just, yet, but bear with me for the moment.
Firstly, Open up your Form Editor and lets start out with a simple form with two Tbuttons on it, a Tlistbox, and a TEdit too. You can change the names to Ok, Cancel, List, and Name (in that order). And You can change the Caption of the buttons to Ok, and Cancel.
Now Save that and put it into a working script (like in 1.1).
i came out with this
SCAR Code:
program New;
var
frmDesign : TForm;
Ok, Clear: TButton;
List: TListbox;
Name: TEdit;
procedure InitForm;
begin
frmDesign := CreateForm;
frmdesign.Position := poScreenCenter; //a property that can take poScreenCenter, poDefault, poDefaultPosOnly, poDefaultSizeOnly, poDesktopCenter, poMainFormCenter, and poOwnerFormCenter
frmDesign.Width := 269;
frmDesign.Height := 359;
frmDesign.Caption := 'Add Names';
frmdesign.AutoScroll := False; //Keeps the form from adding a scrollbar.
Ok := TButton.Create(frmDesign); //Every Object must be created before using it.
Ok.Parent := frmDesign; //every object must have a parent (except for TForm) usually its the same as the owner
Ok.SetBounds(88,288, 75, 25); //Adds the Width, Height, Left, and Top in one line.
Ok.Caption := 'Ok';
Clear := TButton.Create(frmDesign);
Clear.Parent := frmDesign;
Clear.SetBounds(160,16,75,25);
Clear.Caption := 'Clear';
Name := TEdit.Create(frmDesign);
Name.Parent := frmDesign;
Name.SetBounds(24,16,121,21);
List := TListBox.Create(frmDesign);
List.Parent := frmDesign;
List.SetBounds(24, 48, 209, 233);
end;
procedure ShowFormModal;
begin
frmDesign.ShowModal;
end;
var
v: TVariantArray;
begin
setarraylength(V, 0);
ThreadSafeCall('InitForm', v);
setarraylength(V, 0);
ThreadSafeCall('ShowFormModal', v);
end.
Now add OnClick Events to Ok, and Clear and add this procedure up top
SCAR Code:
procedure Go(Sender: TObject);
begin
case sender of
Ok: begin
List.Items.Append(Name.Text); //Adds an item to the end of the list. The text of the item is the text in the Tedit.
Name.Clear; //Clears the text from the Tedit.
Name.SetFocus; //Keeps the cursor in the TEdit.
end;
Clear: Name.Clear; //Clears the text from the Tedit.
List: if List.ItemIndex <> 0 then //ItemIndex is the currently selected Item (starts at 0).
Name.Text := List.Items[List.ItemIndex]; //Changes, the text of the TEdit to the text of the selected item in the TListbox
end;
end;
Now you have a simple form that adds things into a list.
1.6 Good Form Syntax
One of the worst thing i see in people that are new to forms is what i've been showing you since the beginning of the chapter -
I kept it like that to make it easier to understand, but there is a better way to do it.
If you surround the properties, methods, and events by
SCAR Code:
{form component}.create(frmdesign);
with {form component} do
begin
//properties, methods, and events
end;
then its much better. It looks neater, and you don't have to type in the object's name before every property.
Another hood habit to get into is putting your main procedure into a try/finally block. It will possibly keep your form from crashing SCAR, and is generally just a good thing to do.
Chapter 2 - Alternative Forms
2.0 Preface
The only reason i put this in here, is because sometimes you don't need to make your own form. There are a bunch of already made "forms" that you can use, and while many people know about ReadLn, I'm not so sure about GetApplication and others. But just, for future reference! you can use #10#13 in any of these, and the text will become multi-lined!
2.1 ReadLn
ReadLn is basically a command that you can use to make a form with a question, and a TEdit for the user to type the answer in. It looks like this -
Pretty simple, the syntax for that would look something like this
SCAR Code:
begin
if Pos(LowerCase('all of it'), ReadLn('How much wood could a woodchuck chuck' + #10#13 + 'if a woodchuck could Chuck Norris?')) <> - 1 then
WriteLn('Yayz!');
end.
//or just the ReadLn as...
//ReadLn('How much wood could a woodchuck chuck' + #10#13 + 'if a woodchuck could Chuck Norris?')
Thats basically it, you ask a question, and the user inputs something.
2.2 GetApplication
GetApplication is a bit more complicated. Basically all you'll be doing is pulling up the message box. The Syntax of using it is
SCAR Code:
GetApplication.MessageBox({What you want to say}, {Caption of your form}, {type of form});
The big, interesting, and useful part of this is there are 7 different forms that you can make with just this one thing!
0 -
1 -
2 -
3 -
4 -
5 -
6 -
Thats all well and good, but how are you supposed to get what they clicked?! Fear not young lad! I haveth the answer! Usually what i do is something along the lines of -
SCAR Code:
case GetApplication.MessageBox('Imma try an'' save again if thats ok!', 'Save', 3) of
mrYes: Save
mrNo: DontSave
//mrCancel: not needed because i dont want it to do anything upon canceling
end;
Now if you're wondering how you're going to know all of the variables, then look at the next section
2.2 MessageDlg
This is almost exactly like GetApplication.MessageDlg, but it is quite a bit more customizable. With this you can add icons, add different combinations of buttons, and more.
The Syntax for this command is
SCAR Code:
function MessageDlg ( const Message : string; DialogType : TMsgDlgType; Buttons : TMsgDlgButtons; HelpContext : Longint ) : Integer;
The Message can be anything, and the
DialogType may have one of the following values:
mtWarning : Displays a exclamation symbol
mtError : Displays a red 'X'
mtInformation : Displays an 'i' in a bubble
mtConfirmation : Displays an question mark
mtCustom : Displays just the message
The
Buttons value may be one or more of the following values:
mbYes : Displays a 'Yes' button
mbNo : Displays a 'No' button
mbOK : Displays an 'OK' button
mbCancel : Displays a 'Cancel' button
mbAbort : Displays an 'Abort' button
mbRetry : Displays a 'Retry' button
mbIgnore : Displays an 'Ignore' button
mbAll : Displays an 'All' button
mbNoToAll : Displays a 'No to all' button
mbYesToAll : Displys a 'Yes to all' button
mbHelp : Displays a 'Help' button
You must put these values in between brackets ( "[ ]" ), and you can leave
HelpContext as 0 for now. Now for examples!
SCAR Code:
MessageDlg('You shouldnt be here! Leave now?', mtWarning, [mbYes,mbNo,mbAbort, mbRetry], 0);
That will show up as
Now to that list i was telling you about!
- mrOK = 1
- mrCancel = 2
- mrAbort = 3
- mrRetry = 4
- mrIgnore = 5
- mrYes = 6
- mrNo = 7
- mrAll = 8
- mrNoToAll = 9
- mrYesToAll = 10
you can use the enumerated values of the actual return values when checking to see which button was pressed.
1.4 InputQuery/InputBox
Now for more! InputQuery and InputBox are almost the same as ReadLn. Except for a few things. As far as i know InputQuery, InputBox, and ShowMessage (in the next section) need forms to already have been created, in order to work. Their synax are as follows -
SCAR Code:
function InputBox ( const Caption, Prompt, Default : string ) : string;
function InputQuery ( const Caption, Prompt : string; var UserValue : string ) : Boolean;
First, InputBox. InputBox works the same as ReadLn in the effect that it gets input from the user. Thats where their similarities end. It allows you to Change the caption of the window as well as setting the window text. But the really cool thing about InputBox, is that it allows you to set default text. For example, with the following script...
SCAR Code:
var
frmDesign : TForm;
procedure InitForm;
begin
frmDesign := CreateForm;
frmDesign.Position := poScreenCenter;
frmDesign.Width := 354;
frmDesign.Height := 254;
frmDesign.Caption := 'frmDesign';
InputBox('Ham','Do you like ham?','Yesm, i love ham!');
end;
procedure ShowFormModal;
begin
frmDesign.ShowModal;
end;
var
v: TVariantArray;
begin
setarraylength(V, 0);
ThreadSafeCall('InitForm', v);
setarraylength(V, 0);
ThreadSafeCall('ShowFormModal', v);
end.
will look like this, when run -
. Not much else to say!
Now to MessageQuery! MessageQuery, is a bit different. It also gets test from the user, but it returns true or false depending on whether text was inputted or not. It also requires a variable to be inputted as the
Value parameter. With this code...
SCAR Code:
program New;
var
frmDesign : TForm;
a: string;
procedure InitForm;
begin
frmDesign := CreateForm;
frmDesign.Position := poScreenCenter;
frmDesign.Width := 354;
frmDesign.Height := 254;
if InputQuery('Yays','You am here?', a) then
writeln(a);
end;
procedure ShowFormModal;
begin
frmDesign.ShowModal;
end;
var
v: TVariantArray;
begin
setarraylength(V, 0);
ThreadSafeCall('InitForm', v);
setarraylength(V, 0);
ThreadSafeCall('ShowFormModal', v);
end.
it looks a little something like this!
1.5 ShowMessage
Now for the final one...ShowMessage! It also requires a form to be created o be used. And it is also quite an easy one.
SCAR Code:
procedure ShowMessage ( const Text : string ) ;
All you need to do is to type in the text you want to output and it will do so for you! In other words, this -
SCAR Code:
var frmdesign: TForm;
procedure InitForm;
begin
frmDesign := CreateForm;
frmDesign.Position := poScreenCenter;
frmDesign.Width := 354;
frmDesign.Height := 254;
ShowMessage('I''ve got a lovely bunch of coconuts!');
end;
procedure ShowFormModal;
begin
frmDesign.ShowModal;
end;
var
v: TVariantArray;
begin
setarraylength(V, 0);
ThreadSafeCall('InitForm', v);
setarraylength(V, 0);
ThreadSafeCall('ShowFormModal', v);
end.
will look like this -
Chapter 3 - Good Things to Know
3.1 - Parent vs. Owner
While its true that most of the time your parent and owner will be the same object, they are not the same thing. The owner is the object's name that you put into the () after creating it. For example -
SCAR Code:
Button := TButton.create(frmdesign);
In that case frmdesign is the owner of the object Button. When you free an owner, it takes care of freeing all of the owned components, so you dont have to. That is why you dont have to go around freeing all of the components on your form when you close it. The form is the owner of all of the objects, so it frees them all.
The parent on the other hand is a property of an object. For example -
In this case Panel is the parent of Button. The the child of the parent (any object that uses Panel as its parent in the example above) will be "on top" of the owner. That means that the position of the object will be based off of the parent. Example- (simple example...not the whole code)
Panel. left := 40;
Button.parent := Panel;
button.left := 5;
With the form as Button's parent, the left of the button would be 5 pixels past the left edge of the form. With Panel as Button's parent, it will be 5 pixels past the left of the Panel.
Now for a nice big example with pictures .
SCAR Code:
var
frmDesign : TForm;
Onbutton, Offbutton: TButton;
Panel: TPanel;
procedure InitForm;
begin
frmDesign := CreateForm;
frmDesign.Position := poScreenCenter;
frmDesign.Width := 354;
frmDesign.Height := 254;
offbutton := TButton.Create(frmDesign);
with offbutton do
begin
Parent := frmDesign;
Left := 60;
Top := 60;
Width := 100;
Caption := 'Im on the Form!';
end;
panel := Tpanel.Create(frmDesign);
with panel do
begin
Parent := frmDesign;
Left := 60;
Top := 100;
end;
onbutton := TButton.Create(frmDesign);
with onbutton do
begin
Parent := Panel;
Left := 5;
Top := 5;
Width := 100;
Caption := 'Im on the Panel!';
end;
end;
procedure ShowFormModal;
begin
frmDesign.ShowModal;
end;
var
v: TVariantArray;
begin
setarraylength(V, 0);
ThreadSafeCall('InitForm', v);
setarraylength(V, 0);
ThreadSafeCall('ShowFormModal', v);
end.
Normally with 5 as the top and left, the Onbutton would be way to the left of the form, but with Panel as the parent look at it now!
Chapter 4 - Working with Various Components
1.1 Timer
Now lets say we want to make a stop watch. and show it as a form. Hmmm...i think i see a problem with that. You arent going to make it update the form! But fear not! Timers, are the solution. All a timer basically does is, it calls the Procedure that you send from the event, OnTimer. And it calls that procedure every interval milliseconds. You set the interval of the timer after creating it. got it? Well lets go make that stopwatch just to be sure.
All you need to make a timer is this
SCAR Code:
var timer: TTimer;
begin
timer := timer.create(frmdesign);
timer.interval := 1000; // can be anything...1000 is 1 second by the way.
timer.OnTimer := @//procedurename
end;
So this is what my code looks like after adding the form, two buttons (called start and stop, a label,and the timer.
SCAR Code:
var
frmdesign: TForm;
Start, Stop: TButton;
Timer: TTimer;
time: TLabel;
procedure GoStop(Sender: TObject);
begin
end;
procedure Update(Sender: TObject);
begin
end;
procedure InitForm;
begin
frmDesign := CreateForm;
with frmDesign do
begin
Position := poScreenCenter;
Height := 100;
Width := 400;
Caption := 'Stop Watch';
autoscroll := false;
end;
Start := TButton.create(frmdesign);
with start do
begin
parent := frmdesign;
SetBounds(5, 5, 35,25);
Caption := 'Start';
OnClick := @GoStop;
end;
Stop := TButton.create(frmdesign);
with stop do
begin
parent := frmdesign;
SetBounds(5, 35, 35,25);
Caption := 'Stop';
OnClick := @GoStop;
end;
Time := TLabel.create(Frmdesign);
with time do
begin
caption := '00:00:00';
parent := frmdesign;
SetBounds(50,0, 100, 100);
Font.Size := 40;
end;
Timer := TTimer.create(frmdesign);
Timer.interval := 100;
Timer.OnTimer := @Update;
Timer.enabled := false;
end;
procedure SafeInitForm;
var
v: TVariantArray;
begin
setarraylength(V, 0);
ThreadSafeCall('InitForm', v);
end;
procedure ShowFormModal;
begin
frmDesign.ShowModal;
end;
procedure SafeShowFormModal;
var
v: TVariantArray;
begin
setarraylength(V, 0);
ThreadSafeCall('ShowFormModal', v);
end;
begin
try
SafeInitForm;
SafeShowFormModal;
finally
FreeForm(frmDesign);
except
WriteLn('meh!!!');
end;
end.
Now in the GoStop procedure we can just add a simple case and disable/enable the timer based on which button was clicked.
SCAR Code:
procedure GoStop(Sender: TObject);
begin
case sender of
Start: Timer.Enabled := true;
Stop: Timer.Enabled := false;
end;
end;
Now for the cool Part. Add 4 global variables - mil, sec, min, hour. Then add this into your Update procedure
SCAR Code:
procedure Update(Sender: TObject);
begin
mil := mil + 1; //adds a millisecond
if mil >= 100 then
begin
mil := 0; //resets the miliseconds
sec := sec + 1; //adds a second
if sec >= 60 then
begin
sec := 0; //resets the seconds
min := min + 1; //adds a minute
if min >= 60 then
begin
min := 0; //resets the minutes
hour := hour + 1; //adds an hour
end;
end;
end;
time.caption := inttostr(hour) + ':' + inttostr(min) + ':' + inttostr(sec) + ':' + inttostr(mil);
end;
And there is your...probably not so accurate, but still working example of a stopwatch.
1.2 MainMenu
And Now! The TMainMenu. Of the few people that I've seen use MainMenus in SCAR, i don't think i've seen anyone make an actual useful one (meaning more than 1 menu with 2-3 options). I don't plan to make a useful one right now, this one is just a proof of concept. Also, just so you know, the way i do this, you won't have a variable to look at and automatically know what menu it is, but the way i do it is more efficient and easier to add/subtract menus
Ok, now start off with a blank form.
SCAR Code:
var
frmDesign : TForm;
procedure InitForm;
begin
frmDesign := CreateForm;
frmDesign.Position := poScreenCenter;
frmDesign.Width := 354;
frmDesign.Height := 254;
frmDesign.Caption := 'frmDesign';
end;
procedure ShowFormModal;
begin
frmDesign.ShowModal;
end;
var
v: TVariantArray;
begin
GetSelf.WindowState := wsMinimized; //Minimizes SCAR for you.
try
setarraylength(V, 0);
ThreadSafeCall('InitForm', v);
setarraylength(V, 0);
ThreadSafeCall('ShowFormModal', v);
finally
FreeForm(frmDesign);
except
WriteLn('meh!!!');
end;
GetSelf.WindowState := wsMaximized; //Maximaizes SCAR for you.
end.
Now add: a TMainMenu, an array of array of TMenuItem, and an array of TStringArray as global variables. The String array is to hold the names of your menu. So set the length of your array and add some Menu names!
SCAR Code:
SetArrayLength(Names, 4); //include the name of the Menus at the beginning (File, Edit, etc)
Names[0] := ['File', 'New', 'Open', 'Save', 'Save As', 'Exit'];
Names[1] := ['Edit', 'Cut', 'Copy', 'Paste', 'Delete', 'Select All'];
Names[2] := ['Tools', 'Options']; //got all these from the SCAR menus :P
Names[3] := ['Help', 'Help', 'Manual', 'About'];
Now to creating the actual menus. This is where it might get a little bit complicated.
SCAR Code:
SetArrayLength(Menu, GetArrayLength(names)); //Sets the length of the Menu array to the number of menus
Main := TMainMenu.Create(frmdesign); //Creates the MainMenu that holds all of the items
for i := 0 to high(names) do //Cycles through each Names array
begin
SetArrayLength(Menu[i], GetArrayLength(Names[i]));//Sets the length of menu items to the number of items in the corresponding names array
for e := 0 to high(names[i]) do //Cycles through each item in Names[i]
begin
Menu[i][e] := TMenuItem.Create(frmdesign); //Creates each MenuItem
Menu[i][e].Caption := Names[i][e]; //Makes the Caption of each Item, the specified string
if e = 0 then //if its the first item in the array then
Main.Items.Add(Menu[i][e]) //add it as a Menu
else //else
Main.Items.Items[i].add(Menu[i][e]); //add it as an Item to the "i" menu
Menu[i][e].OnClick := @MenuClick; //OnClick event for the menu items
end;
end;
This is why we have an array of array. You could do it as just an array, but then it would get more confusing. This way you at least will know which menu its in. Also, your OnClick event will work like it did in the earlier chapter (using a case). So it should look something like this.
SCAR Code:
procedure MenuClick(Sender: TObject);
begin
case sender of
Menu[0][1]: begin //I skipped [0][0] because thats the actual menu, and you probably
end; //won't want an onclick for that one. (though you can)
Menu[0][2]: begin
end;
/////etc/////
end;
And in the end the entire script should look like this.
SCAR Code:
var
frmDesign : TForm;
Main: TMainMenu;
Menu: array of array of TMenuItem;
Names: array of TStringArray;
procedure MenuClick(Sender: TObject);
begin
case sender of
Menu[0][1]: begin //I skipped [0][0] because thats the actual menu, and you probably
end; //won't want an onclick for that one. (though you can)
Menu[0][2]: begin
end;
/////etc/////
end;
procedure InitForm;
var i, e: integer;
begin
frmDesign := CreateForm;
frmDesign.Position := poScreenCenter;
frmDesign.Width := 354;
frmDesign.Height := 254;
frmDesign.Caption := 'frmDesign';
SetArrayLength(Names, 4);
Names[0] := ['File', 'New', 'Open', 'Save', 'Save As', 'Exit'];
Names[1] := ['Edit', 'Cut', 'Copy', 'Paste', 'Delete', 'Select All'];
Names[2] := ['Tools', 'Options'];
Names[3] := ['Help', 'Help', 'Manual', 'About'];
SetArrayLength(Menu, GetArrayLength(names)); //Sets the length of the Menu array to the number of menus
Main := TMainMenu.Create(frmdesign); //Creates the MainMenu that holds all of the items
for i := 0 to high(names) do //Cycles through each Names array
begin
SetArrayLength(Menu[i], GetArrayLength(Names[i]));//Sets the length of menu items to the number of items in the corresponding names array
for e := 0 to high(names[i]) do //Cycles through each item in Names[i]
begin
Menu[i][e] := TMenuItem.Create(frmdesign); //Creates each MenuItem
Menu[i][e].Caption := Names[i][e]; //Makes the Caption of each Item, the specified string
if e = 0 then //if its the first item in the array then
Main.Items.Add(Menu[i][e]) //add it as a Menu
else //else
Main.Items.Items[i].add(Menu[i][e]); //add it as an Item to the "i" menu
Menu[i][e].OnClick := @MenuClick; //OnClick event for the menu items
end;
end;
end;
procedure ShowFormModal;
begin
frmDesign.ShowModal;
end;
var
v: TVariantArray;
begin
GetSelf.WindowState := wsMinimized; //Minimizes SCAR for you.
try
setarraylength(V, 0);
ThreadSafeCall('InitForm', v);
setarraylength(V, 0);
ThreadSafeCall('ShowFormModal', v);
finally
FreeForm(frmDesign);
except
WriteLn('meh!!!');
end;
GetSelf.WindowState := wsMaximized; //Maximaizes SCAR for you.
end.
1.3 Tabs
Now tabs are pretty nifty and can come in handy for quite a few things. There are two types of tabs that i'll be teaching about, the TTabControl and the TPageControl. They are quite similar, but for a few things. First off, the TTabControl contains all of its tabs in its own object, but the TPageControl on the other hand, needs TTabSheets to hold its tabs (like the MainMenu).
1.3 TTabControl
The TTabControl is more suited towards applcations where the objects in each tab are all the same, like a tabbed text editor (like notepad++). The only thing on your tab is the box you type into. The TTabControl has some extra options, but in general the TPageControl is easier to use. Now for the learning part
First start off with a blank form. then add into your global vars. Then you have to create the TTabControl.
SCAR Code:
Tabs := TTabControl.Create(frmdesign);
Tabs.Parent := frmdesign;
Tabs.SetBounds(0,0,frmdesign.width, 21);
Tabs.Tabs.Append('First'); //the name of the tab you want.
Tabs.Tabs.Append('Second');
Tabs.Tabs.Append('Third');
Tabs.Tabs.Append('Fourth');
Now you have working tabs! The only problem is that you cant tell the difference between any of them, because you dont have anything on them. So now how about you add a TMemo to the global variables. Create it with this code (inserted after the creation of the TTabControl).
SCAR Code:
Memo := TMemo.Create(frmdesign);
with Memo do
begin
Parent := frmdesign;
SetBounds(0, tab.height, frmdesign.width - 7, frmdesign.height - tab.Height - 32);
end;
Ok now that thats done, add a TStringArray to your global variables and set its array length to 4. Also, add an onChange event to Tab and add this to the procedure you send it to-
SCAR Code:
Memo.text := s[Tab.TabIndex];//tabindex is the currently selected tab
Now add an OnChanging event to tab and add this-
SCAR Code:
procedure Changing(Sender: TObject; var AllowChange: Boolean);
begin
s[Tab.TabIndex] := memo.text; //this saves the text before the tab is changed.
end;
Now you should have a fully functioning, tab-changing type thing.
1.3 TPageControl
The TPageControl is more suited towards applications where each tab has its own different items, like in an options menu. That's because you can set those objects' owner to the TTabSheets and they will change with the changing of the tab.
Well thats it for now. It should be updated pretty soon! I welcome any suggestions, and comments. Also if anything is hard to understand, then feel free to ask, and i'll make things more clear, up here in the first post.