Run-time Component at cursor position - delphi

I couldn't find the answer anywhere on the web. Tried Google and many others.
In Delphi 7 how to create a run-time component at cursor position?
I tried a simple code:
procedure TForm1.TButton1Click(Sender: TObject);
var NewCheckBox: TCheckBox;
MB: TMouseButton;
CPos: TPoint;
begin
GetCursorPos(CPos);
NewCheckBox:=TCheckBox.Create(Self);
NewCheckBox.Parent:=Form1;
NewCheckBox.Caption:='NewCheckBox';
NewCheckBox.Left:=CPos.X;
NewCheckBox.Top:=CPos.Y;
end;
But this doesn't work right. The components appear not at the cursor and I cannot place them wherever I want. The code places the component just as I click the button not when I click on the form where I want it to be placed. I want to create a visual of the component that is about to be created and drag it all the way to the form from the button on a toolbar.
I tried Drag-And-Drop but nothing works then, the Drop procedure always shows me a deny sign and does nothing.

The code below will create your checkbox when you right-click on the form. It could do with a bit of refinement, e.g. to handle adding multiple checkboxes, etc, but might help you get going in the right direction.
procedure TForm1.CreateCheckBox(X, Y : Integer);
begin
// NewCheckBox is a Form variable
NewCheckBox:=TCheckBox.Create(Self);
NewCheckBox.Parent:=Form1;
NewCheckBox.Caption:='NewCheckBox';
NewCheckBox.Left:= X;
NewCheckBox.Top:= Y;
end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift:
TShiftState; X, Y: Integer);
begin
if Button = mbRight then
if NewCheckbox = Nil then
CreateCheckBox(X, Y);
end;
Btw, when you use drag & drop on your form, getting the entry sign means that you have not set up an OnDragOver event for the form which sets the Accept parameter to True.

Related

How to close custom positioned PopupMenu in delphi?

I have a project with CoolTrayIcon and PopupMenu with disabled AutoPopup property.
I would like to position the PopupMenu and show it for the user.
The position is OK but menu doesn't close if the user clicks away or press ESC button.
I have not found any property like Active which could help if the menu is used or not.
Here I position the menu:
procedure TForm1.CoolTrayIcon1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
pnt: TPoint; yy:integer;
begin
GetCursorPos(pnt);
yy:=pnt.y; yy:=yy-500;
if (Button=mbRight) then begin
PopupMenu1.Popup(pnt.X, yy);
end;
end;
How could I manage to close menu if it is needed?
This is a known issue that is discussed here:
PRB: Menus for Notification Icons Do Not Work Correctly
You need to wrap the call to Popup() as follows:
SetForegroundWindow(Handle);
PopupMenu1.Popup(pnt.X, yy);
PostMessage(Handle, WM_NULL, 0, 0);
In this code, Handle is the window handle of the form associated with the notification icon.

Cursor position change in TMemo

I have a component derived from TMemo. Do you know what Windows message I should intercept to react on text cursor position changes? I mean text cursor, which change position by pressing arrow keys or by left mouse button click. I am on Delphi 7. OnMouseDown or OrKeyPress events work for arrow keys but not for LMB.
You can store CaretPos property value and compare it in OnKeyPress and OnClick events. Calling some procedure if it has changed.
I sorted it out. Not working mouse events was my mistake. To respond to caret position changes in TMemo you can make combination of two events: OnKeyUp (for arrow keys) and OnMouseDown:
procedure TSomeMemo.OnKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if Key in [VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT] then
OnMouseDown(Sender, mbLeft, Shift, 0, 0);
end;
procedure TSomeMemo.OnMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
row,col: integer;
begin
if Button = mbLeft then
begin
row := SendMessage(Handle, EM_LINEFROMCHAR, SelStart, 0);
col := SelStart - SendMessage(Handle, EM_LINEINDEX, row, 0);
...
end;
end;
Do you know what Windows message I should intercept to react on text cursor position changes?
There is no notification event for change in caret position in a Win32 edit control.
You could perhaps detect such a change by polling, in response to the application's OnIdle event.
If that's the only speciality you want, there are already some answers. If you want to extend on TMemo's functionality more than that you will sooner or later find, that you're better off to use something like SynEdit, which incidentally also supports reacting to cursor position changes.

Listbox highlight only when pressed

I am creating a Delphi XE6 Firemonkey mobile application and want to highlight a listbox item but only while it is being pressed. For an example of the effect I am after, create a new Firemonkey desktop application, add a TListBox and add the following event handlers and code:-
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
begin
//populate the listbox
for i := 0 to 19 do
ListBox1.Items.Add(IntToStr(i));
end;
procedure TForm1.ListBox1ItemClick(const Sender: TCustomListBox;
const Item: TListBoxItem);
begin
ListBox1.ItemIndex:=-1;
end;
Now click an item in the listbox and the highlight should disappear on release of the mouse button. Repeating the exercise for mobile sees only a long press producing the desired result and a short press causes the highlight to remain. So I dropped a timer on the form, setting enabled to FALSE, an interval of 200 and creating an OnTimer event:-
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
begin
//populate the listbox
for i := 0 to 19 do
ListBox1.Items.Add(IntToStr(i));
end;
procedure TForm1.ListBox1ItemClick(const Sender: TCustomListBox;
const Item: TListBoxItem);
begin
Timer1.Enabled:=TRUE;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
ListBox1.ItemIndex:=-1;
Timer1.Enabled:=FALSE;
end;
Progress is made but by rapid pressing of the listbox it is easily possible for the listbox to remain highlighted. I tried the timer option on a TListView and it appears to work as hoped but I'm eager to find a solution for TListBox.
Next brainwave was to add a button to the listboxitem:-
procedure TForm1.FormCreate(Sender: TObject);
var i:integer;
BoxItem: TListBoxItem;
ListBoxSpeedButton: TSpeedButton;
begin
for i := 0 to 99 do
begin
ListBox1.Items.Add(IntToStr(i));
BoxItem := ListBox1.ListItems[ListBox1.Items.Count-1];
ListBoxSpeedButton:=TSpeedButton.Create(nil);
ListBoxSpeedButton.Parent:=BoxItem;
ListBoxSpeedButton.CanFocus:=FALSE;
ListBoxSpeedButton.Align:=TAlignLayout.Client;
end;
end;
However, when scrolling the listbox, the button gets activated and when using a custom Speedbutton, the scrolling is jerky and unresponsive and I can't help feeling I'm using controls when there is no need.
Is there a simple solution here?
Uhm... I will start off by saying DONT USE listboxes when scrolling.... FMX Listboxes are meant to be stagnant and performance when scrolling is horrendous. Use a TListView and TListViewItems. There are tons of examples on SO and in the packaged Delphi XE6 examples on how to implement a list via TListView. That being said, there is no need for timers.. Make use of events already available to use for such things, such as OnMouseDown and onMouseUp which are events assigned to basically every FMX control ( ListBox ListBoxItem or ListView, etc.).... Tons of ways to go about implementing this...
Try this - setting onMouseDown and MouseUp events for every listboxItem to what you see below:
procedure TForm1.ListBoxItem5MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
if sender is TListboxItem then
ListBox.ItemIndex:=TListBoxItem(sender).index
end;
procedure TForm1.ListBoxItem5MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
ListBox.ItemIndex:=-1;
end;

How to connect 20 shapes to a single 'OnMouseDown'?

I have a Delphi program which contains the following code:
procedure TForm1.Shape1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
<code here>
end;
Thing is, I want the exact same code for all 20 shapes. How do I go around doing this effectively? Thanks!
Use multi-select (e.g. SHIFT+CLICK, or click and drag a selection rectangle) to select all 20 shapes.
In the Object Inspector set the OnMouseDown event handler to be the common event handler. This will assign the same event handler to all 20 selected shapes.
If you need to know which shape was clicked from inside your common event handler, use (Sender as TShape).

TStringGrid - OnMouseUp is not called!

I have a weird behavior with TStringGrid in Delphi 7.
Delphi does not call the OnMouseUp event if a pop-up menu is associated to the grid. Basically, when the RMB is pressed, the pop of the menu somehow cancels/delays the OnMouseUp. Actually, to be 100% accurate, next time you push a mouse button the OnMouseUp is called twice – once for the current event, and once for the lost/delayed event.
This will screwup the entire logic of the program as unwanted code will be called next time when the user presses a mouse button.
The automatic popping up of a context menu is a response to a right click of the mouse. The same click also fires the OnMouseUp event. The VCL developers could either choose to fire the 'OnMouseUp' event before the popup is shown, or after. Apparently the latter is in effect, that is, the event is fired when the popup is closed (either by mouse or by the keyboard like pressing 'Esc').
There's no doubling of the event, when you press the left button to close the popup, you're firing the 'OnMouseUp' event again by releasing the left button.
You have several alternatives. One is to derive a new class and override the MouseDown method to fire your own event. An example;
type
TMyStringGrid = class(TStringGrid)
private
FOnRButtonUp: TMouseEvent;
protected
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
published
property OnRButtonUp: TMouseEvent read FOnRButtonUp write FOnRButtonUp;
end;
[...]
procedure TStringGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
begin
if (Button = mbRight) and Assigned(FOnRButtonUp) then
FOnRButtonUp(Self, Button, Shift, X, Y);
inherited;
end;
Another alternative can be to handle VM_RBUTTONUP message. This can either be done by deriving a new class as above, or replacing the WindowProc of the grid. There's an example of replacing the WindowProc here in this question.
Another alternative can be to leave the mouse-up event alone and do your processing in the OnPopup event of the popup menu. This event is fired before the popup is shown. You can get the mouse coordinates with Mouse.CursorPos.
Still, another alternative can be to set the AutoPopup property of the popup menu to False, and in the OnMouseUp event (or better yet in the OnContextMenu event) first do some processing and then show the popup. An example;
procedure TForm1.StringGrid1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
Pt: TPoint;
begin
// Do processing
if Button = mbRight then begin
Pt := (Sender as TStringGrid).ClientToScreen(Point(X, Y));
PopupMenu1.Popup(Pt.X, Pt.Y);
end;
end;
I already took an approach somehow similar with the one described by Sertac: I just don't use the PopupMenu property anymore to assign a pop-up menu to the grid. Instead, inside my grid (my grid is a heavily modified string grid derived from TStringGrid) I handle the mouse down event and display the pop-up the way I want AND do the extra processing I wanted to do BEFORE the menu pops.

Resources