OnClick event firing speed for components - delphi

I was using two TButton components on a form that functioned as Plus and Minus. When clicked they would add or subtract from an integer which would then be displayed on a TLabel.
This functioned as desired where the speed at which I would click would fire the OnClick event without fault.
I have replaced the buttons with PNGButton components so that I could make them look more pretty. Everything still works as before except that the OnClick event doesn't seem to be firing every time I click the components. I tested this with a simple TImage component also and the result is the same.
If I click very slowly it will fire every time, but if I click at a regular pace it only seems to fire every second click.
What can I do to make sure that the OnClick event is fired every time?

Instead of using the OnClick event, use the OnMouseUp event:
procedure TForm.btnMinusMouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Self.NumSelected > 0 then begin
Self.NumSelected := Self.NumSelected - 1;
Self.UpdateLabel;
end;
end;

Related

Mouse Leave event in Firemonkey 10.3

I have a form with a control (myControl) and a child control (myChildControl).
I want to manage the mouse exit on myControl so that I can take some actions.
The problem is that the On Mouse Leave event is fired even though the mouse is still physically inside myControls but getting over myChildControl, while in this case I wouldn't need the event to be fired.
I cannot set the HitTest property in the child control as I need to take some actions on mouse events on it too.
Conceptually the Mouse Leave event fires properly, but what's the cleanest way to manage this case?
Set HitTest = False for the child control. Then in the OnMouseDown of the parent control, use PtInRect() to check whether the mouse down event occured over the child.
If needed you can do the same for other mouse events too.
Edit after comment regarding several child controls.
I don't really agree with you about messyness. Following example for any number of child controls that might need to respond to mouse down events, on the parent control (TPanel in this example). Adding the last two lines for eventual other events is not a big deal, IMO.
procedure TForm20.Panel1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
var
ctrl: TControl;
begin
for ctrl in (Sender as TControl).Controls do
if PtInRect(ctrl.ParentedRect, PointF(X, Y)) then
if assigned(ctrl.OnMouseDown) then
ctrl.OnMouseDown(ctrl, Button, Shift, x-ctrl.Position.X, y-ctrl.Position.Y);
end;
A completely different approach, would be to use the OnMouseEnter event of the control your parent control is placed on (or if placed on the form, which doesn't have a OnMouseEnter, use the OnMouseMove), to trigger the OnExit substitute. As this could trigger very often, you could avoid unneeded actions, by declareing a boolean for ActionRequired

Run-time Component at cursor position

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.

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.

How does a TCustomControl receive input focus?

I am writing a grid control which I based on TCustomControl so I can handle all the structure, painting and navigation myself. The bit that I can't seem to figure out is this:
In my constructor, I set ControlStyle to this:
ControlStyle := ControlStyle + [csCaptureMouse, csClickEvents,
csDoubleClicks, csNeedsBorderPaint, csPannable];
The idea being that if the control handles mouse events, I can do things like set selection etc. I noticed though that the control never receives the focus. I happen to have a TComboBox on the form and it is clearly focused when the form is created. No matter how many times I click within my grid, the focus stays on the combobox.
This of course has implications for my handling of keyboard events as well.
So the question is, how is it determined that the focus should shift to a control when you click on it?
The CustomControl should call SetFocus on itself when it is clicked upon.
procedure TMyCustomControl.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
inherited;
if not (csDesigning in ComponentState) and CanFocus then
SetFocus;

Resources