Odd behavior on mouse move event on track
I am trying to implement a feature similar to most media players, where by moving the mouse over the media duration track bar, a small popup will be displayed informing you of the time your mouse is currently higher. I noticed strange behavior though when implementing the code below.
procedure TForm1.TrackBar1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Single);
var
pers: Extended;
begin
pers := (X/TrackBar1.Width);
PixelLabel.Text := FloatToStr(pers * TrackBar1.Max);
end;
If I clicked in the middle of the track strip, I get a value very close to the actual track value at that point, so for example, the track range is 0 to 2000 and I click somewhere in the middle, I get 1000 , but moving left or right, I start to get lower and higher values ββrespectively. So if my mouse is close to the start, for example, I can get 180 instead of 100 , which should be the actual value at that point. Can anyone point out what I am doing wrong here?
EDIT
By the actual value of the track line, I mean the value obtained from:
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
ActualLabel.Text := 'Actual Val: '+FloatToStr(TrackBar1.Value);
end;
So, I will move the mouse, say, at position 308 (here is the track from 0 to 609) and I get a perch value of 0.50574, which tells me that the track value under the mouse is at position 308 - 10114, but by clicking the mouse and activating function onChange, I get the value 10116. This difference increases as we move further from the middle of the track to either side of it.
EDIT 2
A clearer example would be this. As you can see in the image below, I move the mouse to position X = 572. This position, if expressed as a percentage for the entire track, will be 572/609 = 0.9392. Thus, one would expect the percentage of the track value at that position (min: 0 - max: 200 as shown in the figure) to be the same. In other words, MValue / max = 0.9392.
But after clicking on the track bar at that exact position and then asking for its value, it will not return what I calculated as "MValue" as shown in the image below (the mouse is not visible, but this image is valid after I clicked the bar track in the same position as the ActualValue update has been updated)
source to share
Problem 1
Your value calculation does not match the control you are using. The control does this with this code, which can be found in FMX.StdCtrls
:
function PosToValue(MinValue, MaxValue, ViewportSize, ThumbSize, TrackSize,
Pos: Single; IgnoreViewportSize: boolean): Single;
var ValRel: Double;
begin
Result := MinValue;
if (ViewportSize < 0) or IgnoreViewportSize then
ViewportSize := 0;
ValRel := TrackSize - ThumbSize;
if ValRel > 0 then
begin
ValRel := (Pos - ThumbSize / 2) / ValRel;
if ValRel < 0 then
ValRel := 0;
if ValRel > 1 then
ValRel := 1;
Result := MinValue + ValRel * (MaxValue - MinValue - ViewportSize);
end;
end;
The difference is that the code here respects the thumb size. A formula is equivalent to calling this function and passing a value 0
for ThumbSize
.
If you want to replicate the behavior of a control, you will need to use this algorithm as well. You will need a secure hack to crack the class.
type
THackedTrackBar = class(TTrackBar);
procedure TForm1.TrackBar1MouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Single);
var
tb: THackedTrackBar;
begin
tb := THackedTrackBar(TrackBar1);
Label1.Text := FloatToStr(
PosToValue(
tb.Min,
tb.Max,
tb.ViewportSize,
tb.GetThumbSize(tb.FIgnoreViewportSize),
tb.Width,
X,
tb.FIgnoreViewportSize
)
);
end;
Problem 2
OnMouseMove
does not fire when the cursor is over a track. This appears to be the main limitation of FMX control TTrackBar
. The main framework obviously knows that the cursor is over the thumb because it draws it in a different color, the so-called hot-watch effect. However, the structure seems to do this at the expense of giving you information that the mouse is moving.
The thumb on the track bar is implemented as a separate object. It is an object of type TThumb
. The control TTrackBar
exposes the object through a protected property. You can use a secure hack to grab the thumb object and then set its event handler OnMouseMove
. Not much fun, but certainly one way to get around this problem.
source to share
For your initial problem, you need to dig into the style to find the exact amount of left and right padding around the track. Please note that this will vary by platform, style, and Delphi version.
You can use your own style so you know that everything will be permanent.
To solve the problem raised by David Heffernan, you can transparently control the TTrackBar (make it a click-aligned child of the tracks panel), intercept the mouse events, do your own handling, and pipe them to the tracks panel.
source to share