I need code that is able to intercept several taps on a tablet at the same moment. In a previous question concerning how to handle several OnMouseDown's at the same moment (not possible), a link was provided that answered the question on how to handle multi touch clicks in Delphi-Android. However, this code returns (x, y) positions in screen coordinates and I don't know how to translate these to local coordinates of a specific control. The Delphi documentation refers to a ScreenToClient function but that only translates screen coordinates to form coordinates, which is hardly useful in Android (the documentation is about XE2 but the function still exists in XE5 but the function has been removed from FMX.Platform to FMX.Form).
Is there a simple way to convert screen coordinates to TControl coordinates in FMX like in VCL? Of course I can 'de-parent' a control, notate its top-left coordinates and do so for each parent until arrived at the base form, but that's quite tedious.
Edit 1
My current approach is to get the top (x, y) coordinates of the TControl (a TPanel) that is parent to the controls to be tapped (TRectangle's in fact) and add these to check whether a click is inside that rectangle. See sample code below.
procedure TKeyBoard.process_touch (Event: TTouchEvent; status_byte: Int32);
var
key: TKey;
p, q: TPointF;
i: Integer;
x, y: single;
begin
// Check whether at least one event is present. If so, i points to the last event
i := Length (Event.Points) - 1;
if i < 0 then Exit;
// Get (x, y) coordinates from event. It's in screen coordinates
x := Event.Points [i].Position.X;
y := Event.Points [i].Position.Y;
// FControl is a reference to the panel holding the keys
// Find its rectangle position and convert to screen coordinates
p := TPointF.Create (FControl.Position.X, FControl.Position.Y);
q := TPointF.Create (p.X + FControl.Width, p.Y + FControl.Height);
p := Application.MainForm.ClientToScreen (p);
q := Application.MainForm.ClientToScreen (q);
logd ('control [%.0f %.0f - %.0f, %.0f]', [FControl.Position.X, FControl.Position.Y, FControl.Width, FControl.Height]);
logd ('to screen [%.0f %.0f - %.0f, %.0f]', [p.X, p.Y, q.X, q.Y]);
// Determine whether a black key has been pressed
for i := Low (Fkeys) to High (FKeys) do
begin
if not cOctave_Major [i mod nOctave] then
begin
key := FKeys [i];
logd ('%d (%.0f, %.0f) - (%.0f, %.0f) (%.0f, %.0f)', [i, x, y,
key.Position.X + p.X, key.Position.Y + p.Y,
key.Position.X + P.X + Key.Width, key.Position.Y + p.Y + key.Height]);
if (x >= key.Position.X + p.X) and (x <= key.Position.X + p.X + Key.Width) and
(y >= key.Position.Y + p.Y) and (y <= key.Position.Y + p.Y + key.Height)
then break;
key := nil;
end; // if
end; // for
// if not, check whether a white key has been pressed
if key = nil then
begin
logd ('Major');
for i := Low (Fkeys) to High (FKeys) do
begin
if cOctave_Major [i mod nOctave] then
begin
key := FKeys [i];
logd ('%d (%.0f, %.0f) - (%.0f, %.0f) (%.0f, %.0f)', [i, x, y,
key.Position.X + p.X, key.Position.Y + p.Y,
key.Position.X + P.X + Key.Width, key.Position.Y + p.Y + key.Height]);
if (x >= key.Position.X + p.X) and (x <= key.Position.X + p.X + Key.Width) and
(y >= key.Position.Y + p.Y) and (y <= key.Position.Y + p.Y + key.Height)
then break;
key := nil;
end; // if
end; // for
end; // if
if key <> nil
then putShort (status_byte, key.Note, 127);
if key <> nil
then logd (' found %s', [key.Text.Text]);
end; // process_touch //
This code is in fact very untidy because it assumes that the Parent control has the Application.MainForm as its parent which need not be the case. Another observation is that the taps might still be slightly wrong in the Y-position. For that reason i'd like to transfer the screen coordinates directly to the control's coordinates.
Edit 2
I tried to use the IsMouseOver check for each key control as suggested by @Sentient but strangely enough that only yields true when a MouseUp event is being processed.
I am the author of the multi-touch code you are using. When I saw you struggle with coordinates I looked what can be done and updated the code so it now gives you the touched control and the relative coordinates. Also if you are wondering how to get that its simple.
The blog post about it is here:
http://www.cromis.net/blog/2014/02/multi-touch-touched-control-and-relative-coordinates/
The code to solve it look like this:
if Screen.ActiveForm <> nil then
begin
for I := 0 to Length(Event.Points) - 1 do
begin
Control := Screen.ActiveForm.ObjectAtPoint(Event.Points[I].Position);
if Control <> nil then
begin
Event.Points[I].Control := Control as TFmxObject;
Event.Points[I].RelPosition := Control.ScreenToLocal(Event.Points[I].Position);
end
else
begin
Event.Points[I].Control := Screen.ActiveForm;
Event.Points[I].RelPosition := Screen.ActiveForm.ScreenToClient(Event.Points[I].Position);
end;
end;
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With