Recent

Author Topic: Is it possible to fire all MouseEnter events for same position?  (Read 434 times)

tonyp

  • New Member
  • *
  • Posts: 23
Is it possible to fire all MouseEnter events for same position?
« on: October 24, 2020, 01:18:52 pm »
I have fully or partially overlapping TShape objects.

When I move the mouse over them, I want to have their MouseEnter events fired (one after the other, in any sequence) for the objects under the mouse, not just the top most one.

Is something like this possible?  Examples?

jamie

  • Hero Member
  • *****
  • Posts: 3774
Re: Is it possible to fire all MouseEnter events for same position?
« Reply #1 on: October 24, 2020, 06:01:18 pm »
You can call the events directly...

Write a Method and place it in your class code somewhere..

Procedure UpdateAllShapes;
Begin
   Shape1.OnEnter(shape1);
   Shape2.OnEnter(Shape2);
   Shape3.OnEnter(Shape3);
  etc;
end;

and for each shape implement the OnEnter event for example.

Shape1Enter(Semder:Tobject);
Begin
  If Sender <> Self then UpdateAllShapes else
   begin
     // do your code for this shape;
   end;
end;

do that for shape or point all the shapes to the same event..

The idea is that if the Sender coming is is the same as the SELF then you don't want to execute that one because it will cause a loop.
The only true wisdom is knowing you know nothing

lucamar

  • Hero Member
  • *****
  • Posts: 3179
Re: Is it possible to fire all MouseEnter events for same position?
« Reply #2 on: October 24, 2020, 09:46:35 pm »
The idea is that if the Sender coming is is the same as the SELF then you don't want to execute that one because it will cause a loop.

The problem with that is that Self is the form not the shape, assuming he means processing shapes inside a form. The following code will do what tonyp wants nicely (if I understood what he wants):

Code: Pascal  [Select][+][-]
  1. procedure TForm1.EnterShape(Sender: TObject);
  2. const
  3.   Current: TShape = nil;
  4. var
  5.   i: Integer;
  6. begin
  7.   if Sender.InheritsFrom(TShape) then begin
  8.     { Do whatever you want for the "active" shape }
  9.     Memo1.Lines.Add('Shape: %s', [TComponent(Sender).Name]);
  10.     { And now process the rest of them }
  11.     if not Assigned(Current) then begin
  12.       { Keep track of which Shape called first}
  13.       Current := TShape(Sender);
  14.       { Find all shapes and EnterShape() for each EXCEPT the Current one }
  15.       for i := 0 to ComponentCount-1 do
  16.         if Components[i].InheritsFrom(TShape) { Is it a shape ... }
  17.         and (Components[i] <> Current) then { and not the "top"? }
  18.           EnterShape(Components[i]);
  19.       { All done? Back to defaults }
  20.       Current := Nil;
  21.       { This just to make the log look nicer ;) }
  22.       Memo1.Lines.Add('----------');
  23.     end;
  24.   end;
  25. end;

ETA: Of course, this code assumes you want to do basically the same for each frame so all of them must have this handler for their OnEnter events. Otherwise it would be better to call each shape's OnEnter, as jamie showed.
« Last Edit: October 24, 2020, 09:57:33 pm by lucamar »
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.10/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

tonyp

  • New Member
  • *
  • Posts: 23
Re: Is it possible to fire all MouseEnter events for same position?
« Reply #3 on: October 24, 2020, 10:16:36 pm »
If I'm not mistaken, the first suggestion by 'jamie' assumes I know in advance which shapes are affected.

I don't.  There are many shapes all over the screen (inside the form), and I only want those under the mouse pointer to have the MouseEnter event fire.

The second suggestion by 'lucamar' seems like it will fire all shapes' MouseEnter events, not just the ones under the mouse pointer.

If I didn't understand either solution correctly, please correct me.

Maybe I can describe the issue a bit better.

The form has various shapes spread all over.  Some are fully or partially overlapping.  When the user moves the mouse pointer to location x,y whatever shape covers that location (any part of that shape) I want it to have its MouseEnter triggered.

The idea is to find out which shapes the user is interested in based on location (and not depth) and show relevant information about all of them, but none of the remaining ones on the same form.

I could iterate all components of the form and check if any of the shapes includes the x,y of the mouse pointer.  But, I thought maybe there is a simpler way, like having the MouseEnter event propagate to the next shape under the current one, and so on for all shapes covering the same x,y position.

jamie

  • Hero Member
  • *****
  • Posts: 3774
Re: Is it possible to fire all MouseEnter events for same position?
« Reply #4 on: October 24, 2020, 10:23:58 pm »
just point the onmousemove for example of all the shapes to a single handler.

when the handler is called, the SENDER represents the actual control

 so while inside of this event...


 With TShape(Sender) do
  begin
    caption := Name+','+Left.Tostring+'.'+Top.Tostring+',';
  end;
   
The only true wisdom is knowing you know nothing

howardpc

  • Hero Member
  • *****
  • Posts: 3579
Re: Is it possible to fire all MouseEnter events for same position?
« Reply #5 on: October 24, 2020, 11:01:30 pm »
A common OnMouseMove handler only reports on the TShape that is highest in the Z order.
There is no way to know if the topmost shape is covering another shape without iterating through all the form's shapes and actually testing whether any of them encloses the point where the mouse pointer currently sits.

lucamar

  • Hero Member
  • *****
  • Posts: 3179
Re: Is it possible to fire all MouseEnter events for same position?
« Reply #6 on: October 25, 2020, 12:44:11 am »
Let me see if I'm understanding correctly. Conceptually, you have, let's say, "groups" of overlaping shapes (along with independent ones) and inside each group you want to know when the user is entering, say, Shape1, Shape 2, or the intersection of both.

Shape "grouping" is relatively easy to achieve; you could, for example, use their Tag property to store a "group number" and test in the handler:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.EnterShape(Sender: TObject);
  2. const
  3.   Current: TShape = nil;
  4.   GroupId: Integer = 0;
  5. var
  6.   i: Integer;
  7.  
  8.   function InGroup(AComponent: TComponent): Boolean;
  9.   begin
  10.     Result := AComponent.InheritsFrom(TShape)
  11.               and (AComponent <> Current)
  12.               and (AComponent.Tag = GroupId);
  13.   end;
  14.  
  15. begin
  16.   if Sender.InheritsFrom(TShape) then begin
  17.     Memo1.Lines.Add('Shape: %s', [TComponent(Sender).Name]);
  18.     if not Assigned(Current) then begin
  19.       Current := TShape(Sender);
  20.       GroupId := Current.Tag;
  21.       if GroupId <> 0 then
  22.         for i := 0 to ComponentCount-1 do
  23.           if InGroup(Component[i]) then
  24.             EnterShape(Components[i]);
  25.       Current := Nil;
  26.       GroupId := 0;
  27.       Memo1.Lines.Add('----------');
  28.     end;
  29.   end;
  30. end;

Now it will process only those shapes inside the same "group", based on the Tag of the first one.

As for ascertaining whether the user is in one shape, or other, or the intersection ... that's a little more complex; for example, you might set a handler for OnMouseDown(). That gives you the coordinates of the mouse at clicking time, which you can test (through a process similar to the above) against the surrounding shapes's boundaries. It's a little late now but I'll try to write an example tomorrow, if I get some free time  ;)
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.10/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Sieben

  • Full Member
  • ***
  • Posts: 138
Re: Is it possible to fire all MouseEnter events for same position?
« Reply #7 on: October 25, 2020, 01:30:02 am »
Another question would be if he wants to know if a point is within the shape that is drawn by TShape or just within TShape, for TShape bounds are always rectangular no matter what shape. And it provides no method to tell the difference.
Lazarus 2.0.10, FPC 3.2.0, .deb install on Ubuntu Xenial 32

Blaazen

  • Hero Member
  • *****
  • Posts: 2994
  • POKE 54296,15
    • Eye-Candy Controls
Re: Is it possible to fire all MouseEnter events for same position?
« Reply #8 on: October 25, 2020, 02:15:53 am »
@ Another question would be if he wants to know if a point is within the shape that is drawn by TShape or just within TShape, for TShape bounds are always rectangular no matter what shape. And it provides no method to tell the difference.

I guess OP tries to abuse TShape for sprites of a computer game. :D
Lazarus 2.1.0 r64115 FPC 3.3.1 r40507 x86_64-linux-qt Chakra, Qt 4.8.7/5.13.2, Plasma 5.17.3
Lazarus 1.8.2 r57369 FPC 3.0.4 i386-win32-win32/win64 Wine 3.21

Try Eye-Candy Controls: https://sourceforge.net/projects/eccontrols/files/

winni

  • Hero Member
  • *****
  • Posts: 1992
Re: Is it possible to fire all MouseEnter events for same position?
« Reply #9 on: October 25, 2020, 02:47:51 am »
Hi!

Yes, Blaazen is right.

All MouseEvents are triggered through the BoundingBox and not through the shape.

Lousy implementation.
But - I am convinced - compatible with Delphi.

Winni

 

TinyPortal © 2005-2018