The TMemo component is a multiline text box that enables us to load, edit, and save plain text files. The contents of the text file are stored in the Lines property.
Listing 15-4 shows how to use the TMemo and TListBox components to emulate Delphi's Code Editor and its code completion feature.
Figure 15-19: Code completion
Start by creating a new VCL Forms application project and then drop a TMemo component on the Designer Surface. Rename the TMemo component Editor.
The first thing that we have to do is dynamically create a TListBox component that will be displayed when the user types a period (full stop) in the editor.
Listing 15-4: Creating the code completion list
type TMainForm = class(TForm) Editor: TMemo; procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } CodeList: TListBox; end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.FormCreate(Sender: TObject); begin CodeList := TListBox.Create(Self); CodeList.Parent := Editor; CodeList.Visible := False; CodeList.Width := 200; CodeList.Height := 100; CodeList.Items.Add('Caption'); CodeList.Items.Add('Top'); CodeList.Items.Add('Left'); end;
Now that we have the code completion list, we have to write code that displays it when the user types a period (full stop) in the editor. The best place for this code is the OnKeyPress event of the Editor component.
Listing 15-5: Displaying the code completion list
procedure TMainForm.EditorKeyPress(Sender: TObject; var Key: Char); var CliPos: TPoint; ListX: Integer; ListY: Integer; begin if Key = '.' then begin { get caret position } GetCaretPos(CliPos); { adjust list position } ListX := CliPos.X; ListY := CliPos.Y + CodeList.Canvas.TextHeight('W') + 4; if ListX + CodeList.Width >= Editor.Width then ListX := Editor.Width - CodeList.Width - 20; if ListY + CodeList.Height >= Editor.Height then ListY := Editor.Height - CodeList.Height - 20; CodeList.Top := ListY; CodeList.Left := ListX; CodeList.ItemIndex := 0; CodeList.Visible := True; CodeList.SetFocus; end else CodeList.Visible := False; end;
The most important thing when displaying the code completion list is to display it as close to the caret as possible. To get the position of the caret in pixels, we have to use the Windows API GetCaretPos function. After we read the caret's position with the GetCaretPos function, we have to adjust it so that the list appears beneath the line that contains the caret. This adjustment is done by calling the Canvas.TextHeight method to calculate the height of a line in the editor. In this case, we can call the CodeList's TextHeight method because both the Editor and CodeList components use the same font.
If you run the application now and type a period in the editor, you will see the custom code completion list.
Figure 15-20: The code completion list
Finally, we have to write the code completion code — the code that adds the selected item from the code completion list to the editor.
Listing 15-6: Code completion
procedure TMainForm.CodeListClick(Sender: TObject); var currentLine: Integer; begin if CodeList.ItemIndex <> -1 then begin currentLine := Editor.CaretPos.Y; { add text from the list to the editor } Editor.Lines[currentLine] := Editor.Lines[currentLine] + CodeList.Items[CodeList.ItemIndex]; end; CodeList.Visible := False; Editor.SetFocus; end;
To determine the X and Y coordinates of the caret, we can use the TMemo's CaretPos property. The difference between the GetCaretPos function and the CaretPos property of the TMemo component is that the CaretPos property doesn't contain pixel values. The X value of the CaretPos property specifies the horizontal coordinate of the caret, in characters. The Y value of the CaretPos property specifies the vertical coordinate of the caret, the index of the line that contains the caret.
The last thing we have to do is assign the CodeListClick method to the OnDblClick event of the CodeList component in the OnCreate event handler.
Listing 15-7: Creating the code completion list, revisited
procedure TMainForm.FormCreate(Sender: TObject); begin CodeList := TListBox.Create(Self); CodeList.Parent := Editor; CodeList.Visible := False; CodeList.Width := 200; CodeList.Height := 100; CodeList.Items.Add('Caption'); CodeList.Items.Add('Top'); CodeList.Items.Add('Left'); CodeList.OnDblClick := CodeListClick; end;