[Home] [Downloads] [Search] [Help/forum]


Register forum user name Search FAQ

Gammon Forum

[Folder]  Entire forum
-> [Folder]  MUSHclient
. -> [Folder]  Bug reports
. . -> [Subject]  'cancelmouseover' hotspot event not sent when the mouse leaves the output window

'cancelmouseover' hotspot event not sent when the mouse leaves the output window

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page


Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Tue 05 Jan 2010 08:41 AM (UTC)

Amended on Tue 05 Jan 2010 08:43 AM (UTC) by Twisol

Message
Because most of my testing is done with my widgets anchored to the top-left corner, I happened to notice that if I move the cursor up and out of the output window, no cancelmouseover is sent to the window until I mouse back over the output window (which could be anywhere else).

Version: 4.45

Example plugin:
<!DOCTYPE muclient>

<muclient>
<plugin
   name="test"
   author="Twisol"
   id="0053f8ba3ff6422de5c96169"
   language="Lua"
   purpose="Test"
   date_written="2010-01-02"
   requires="4.45"
   version="1.0"
   >
</plugin>

<script><![CDATA[

function cancelmouseover()
  print("Mouse left the area")
end

function OnPluginInstall()
  local win = GetPluginID() .. "_test"
  
  WindowCreate(win, 0, 0, 50, 50, 4, 0, 0xFFFFFF)
  WindowAddHotspot(win, "test", 0, 0, 50, 50, nil, "cancelmouseover", nil, nil, nil, nil, 0, 0)
  WindowShow(win, true)
end

]]></script>

</muclient>

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Nick Gammon   Australia  (23,000 posts)  [Biography] bio   Forum Administrator
Date Reply #1 on Tue 05 Jan 2010 09:18 PM (UTC)
Message
This isn't particularly easy to fix. The problem is that the cancelmouseover notification happens when a mouse movement is detected, the mouse movement is over the client area of the window, and it is appropriate (eg. there was a previous mouseover).

Now, outside the client window mouse movements return NCmousemove (non-client moves), which the MFC framework does not send to the CMUSHview class at all. So, an attempt to detect NC moves (which I have tried) has no result.

Also, it would be possible to quickly flick the mouse to another application window, if the window happened to be near the edge (which is probable) of the MUSHclient frame window. In that case, mouse movements (ie. the Windows event messages) now belong to the other application, not MUSHclient.

I would try to work around this as much as possible. Personally I regard mouse-over events as "lower importance" than mouse clicks. That is, you generally might make a button change colour, or display some mouse-over info.

One possible thing to do would be to make a timeout on the mouseover action. That is, if you mouseover something it shows some information, until a cancelmouseover, or 10 seconds elapses, something like that.

Alternatively, you could live with it. Say you mouseover an inventory item, and an information window pops up. Now you mouse out of the client window, take a sort of U-shaped trip, and come back in somewhere else. While the mouse is moving, perhaps the information window is still there, but I think this would be something that wouldn't happen that often that it is annoying.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #2 on Tue 05 Jan 2010 10:10 PM (UTC)
Message
I did a little searching and I came up with the TrackMouseEvent() Win32 method [1], which seems to allow you to register the given window for certain extra events, like WM_MOUSELEAVE [2]. You need do to implement a WindowProc callback (in MFC it's an overridable) in order to catch and process the message [3].

It just seems like it would be really strange and stupid (in terms of MFC, not you) if it was -that- hard to track when the mouse leaves the area, so I had to look for myself. It seems like a simple enough addition, although I could be wrong: add a TrackMouseEvent() call to ::OnMouseMove, and implement a ::WindowProc filter to catch the mouseleave when it occurs.


[1] http://msdn.microsoft.com/en-us/library/ms646265(VS.85).aspx

[2] http://msdn.microsoft.com/en-us/library/ms645533(VS.85).aspx
Quote:
WM_MOUSELEAVE
The WM_MOUSELEAVE message is posted to a window when the cursor leaves the client area of the window specified in a prior call to TrackMouseEvent.

A window receives this message through its WindowProc function.


[3] http://msdn.microsoft.com/en-us/library/8k57wfbs(VS.80).aspx

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #3 on Thu 07 Jan 2010 09:29 AM (UTC)
Message
If you're interested, I've composed a little patch for this issue, which I've tested and it seems to be working perfectly fine. Unfortunately, TrackMouseEvent() was introduced in Win98, which means Win95 users (which the MUSHclient front page claims to support) would be left in the dust...

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Nick Gammon   Australia  (23,000 posts)  [Biography] bio   Forum Administrator
Date Reply #4 on Thu 07 Jan 2010 07:53 PM (UTC)
Message
I see you sent me the patch. :)

This was along the lines of what I was working on. I have amended it slightly to allow for not attempting to do it under Windows 95, and also it is only done if you have a cancel mouse-over function. The TrackMouseEvent is also only called on a mouse-over, to save whatever extra overhead is caused by this additional function call, and the tracking, if the user is not interested in finding when the mouse moves away from this hotspot.

I don't think we need to track on a mouse-down, because you can legitimately keep the mouse down while it wanders out of a window, and then move it back over it again.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #5 on Thu 07 Jan 2010 07:58 PM (UTC)
Message
Ahh, cool. I'm glad it helped! :) Is there any chance I could see the diff for comparison? I'm intrigued.

(I just realized I said TrackMouseOver instead of TrackMouseEvent in the email. Well, you know what I meant.)

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Nick Gammon   Australia  (23,000 posts)  [Biography] bio   Forum Administrator
Date Reply #6 on Thu 07 Jan 2010 08:06 PM (UTC)

Amended on Thu 07 Jan 2010 08:09 PM (UTC) by Nick Gammon

Message
What you had, except this:


***************
*** 6524,6529 ****
--- 6524,6545 ----
            m_nLastToolTipColumn = 0;
              }   // end of having tooltip window

+
+         // capture mouse movements out of the miniwindow (version 4.46)
+         // see: http://www.gammon.com.au/forum/?id=9980
+
+         if (!m_mousedover && !bWin95 && !pHotspot->m_sCancelMouseOver.empty ())
+           {
+           TRACKMOUSEEVENT tme;
+           ZeroMemory (&tme, sizeof tme);
+           tme.cbSize = sizeof(TRACKMOUSEEVENT);
+           tme.dwFlags = TME_LEAVE;
+           tme.hwndTrack = m_hWnd;
+           _TrackMouseEvent(&tme);
+
+               m_mousedover = true;
+          }
+
          }
        else
          {  // same hotspot (new in version 4.45)


Also my CPoint doesn't see to have a SetPoint function (must be newer) so handling the event looked like:


LRESULT CMUSHView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
 switch (message)
   {
    case WM_MOUSELEAVE:
    {
      CMUSHclientDoc* pDoc = GetDocument();
      ASSERT_VALID(pDoc);
      CPoint point (-1, -1);

      m_mousedover = false;

      Mouse_Move_MiniWindow (pDoc, point);

      return 0;
    }  // end of WM_MOUSELEAVE

   }  // end of switch
  
  return CView::WindowProc(message, wParam, lParam);
}



(And the default case wasn't really required).

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #7 on Thu 07 Jan 2010 08:13 PM (UTC)

Amended on Thu 07 Jan 2010 08:14 PM (UTC) by Twisol

Message
Ahh, makes sense. Thanks!

EDIT: I only used the default case out of personal preference, I like it better if all of the "events" are handled and returned from in the same place. Clearly it doesn't matter, but I happened to like the the indentation and location better.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Nick Gammon   Australia  (23,000 posts)  [Biography] bio   Forum Administrator
Date Reply #8 on Thu 07 Jan 2010 08:24 PM (UTC)
Message
You are probably right, in which case you could argue the final return is not necessary as it can never be reached. How about:


LRESULT CMUSHView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
  switch (message)
   {
    case WM_MOUSELEAVE:
    {
      CMUSHclientDoc* pDoc = GetDocument();
      ASSERT_VALID(pDoc);
      CPoint point (-1, -1);

      m_mousedover = false;

      Mouse_Move_MiniWindow (pDoc, point);

      return 0;
    }  // end of WM_MOUSELEAVE

    default: 
      return CView::WindowProc(message, wParam, lParam);

   }  // end of switch
  
 
  // cannot get here

}


- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #9 on Thu 07 Jan 2010 08:30 PM (UTC)
Message
Makes sense to me! Actually, if you wanted to handle a message but still pass it upwards, your way would be better: just omit the return 0 from the case block. With mine, you'd need to add a CView::WindowProc() call in the case block instead.

How about a best of both worlds approach?

LRESULT CMUSHView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
  switch (message)
   {
    case WM_MOUSELEAVE:
    {
      CMUSHclientDoc* pDoc = GetDocument();
      ASSERT_VALID(pDoc);
      CPoint point (-1, -1);

      m_mousedover = false;

      Mouse_Move_MiniWindow (pDoc, point);

      return 0;
    }  // end of WM_MOUSELEAVE

    default: 
      return CView::WindowProc(message, wParam, lParam);

   }  // end of switch
  
 
  return CView::WindowProc(message, wParam, lParam);
}

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #10 on Thu 07 Jan 2010 08:40 PM (UTC)

Amended on Thu 07 Jan 2010 08:42 PM (UTC) by Twisol

Message
You might find that redundant, but I think it's a more accurate representation of my thought process in this function. The switch statement is for explicit handling of events. 'default' just happens to mean all events not mentioned. The second WindowProc() call is just there to pass upwards for any events that have been handled, basically saying "This is by default a -filter- method. To stop a message from propogating, return explicitly when handling it."

(That said, yes, it's redundant. But I like it!)

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by David Haley   USA  (3,881 posts)  [Biography] bio
Date Reply #11 on Fri 08 Jan 2010 04:40 PM (UTC)
Message
Generally it's considered "good practice" to avoid putting in code that looks like it gets executed but actually never will. It's kind of like:


if (a) {
  return 1;
}
else if (b) {
  return 2;
}
else {
  return 3;
}

return 4;


If the function ends with /* not reached */ or something like that, it makes it completely obvious that the switch statement is handling all possible cases. If it ends with a return statement, you need to go figure out what the switch is doing.

Furthermore, and this is an argument that's not just style but actually helpful in practice :-), if at some point you remove the default case from the switch, then having no return statement will cause the compiler to complain at you (not all control paths return for non-void function, or something like that). If you have a return statement there, you might not catch the fact that you have changed your switch statement but need to handle things it doesn't cover anymore.



Basically, as a general principle, redundancy should be avoided when reasonable as it confuses things often without much (if any) gain.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
[Go to top] top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


28,180 views.

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]


Written by Nick Gammon - 5K   profile for Nick Gammon on Stack Exchange, a network of free, community-driven Q&A sites   Marriage equality

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( https://gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Hosted at HostDash]