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

Gammon Forum

See www.mushclient.com/spam for dealing with forum spam. Please read the MUSHclient FAQ!

[Folder]  Entire forum
-> [Folder]  MUSHclient
. -> [Folder]  Bug reports
. . -> [Subject]  Hotspot mousedown callback not being executed
Home  |  Users  |  Search  |  FAQ
Username:
Register forum user name
Password:
Forgotten password?

Hotspot mousedown callback not being executed

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


Pages: 1 2  

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Mon 23 Nov 2009 07:36 AM (UTC)

Amended on Tue 24 Nov 2009 01:50 AM (UTC) by Twisol

Message
(MUSHclient v4.43)

I am writing a test case for my CharacterGrid widget type, and I'm using three of the hotspot callbacks. While two of them work great - mouseup and mouseover - the third, cancelmouseover, doesn't appear to be executing at all. However I am perfectly able to get its name from WindowHotspotInfo and execute it directly [1].


Here is the relevant snippet of code that I used:

CellFromHotspot = function(id)
  local _, _, x, y = string.find(id, "-h%((%d+),(%d+)%)")
  return grid:Cell(tonumber(x), tonumber(y))
end

CellBlur = function(flags, id)
  local cell = CellFromHotspot(id)
  
  cell.backcolor = 0xFFFFFF
  
  grid:Draw(true)
end

OnPluginInstall = function()
  grid = CharacterGrid.new(3, 3)
  grid:Anchor(12)
  grid:Font(TICTACTOE_FONT, 15)
  grid.backcolor = 0xFFFFFF
  
  for y = 1, 3 do
    for x = 1, 3 do
      grid:Cell(x, y).hotspot = {
        mouseup = "CellClick",
        mouseover = "CellFocus",
        cancelmouseover = "CellBlur",
      }
    end
  end
end


This uses my under-development widget framework [2]. The per-cell .hotspot entry maps directly to a WindowAddHotspot() call, and I can verify that there are no typos or mistakes in the code along the way [1]. (There was a typo in my passing of cancelmousedown fields, but it was unrelated and I fixed it.)

I jumped into my copy of the MUSHclient source, and it appears to me that there are two problems surrounding cancelmouseover. Firstly, it appears to only even be considered if the mouse leaves the miniwindow, not the hotspot [3]. Further, even then it doesn't appear to move past a "did we mouseover this hotspot" check.


[1] This code works as expected, calling the nominated cancelmouseover function.

name = "03829336f30e9f2dcace86e9:w1"
hotspot = "03829336f30e9f2dcace86e9:w1-h(2,2)"

_G[WindowHotspotInfo(name, hotspot, 6)](0, hotspot)


[2] http://github.com/Twisol/MUSHclient-MWidget

[3] The relevant MUSHclient source code begins at line 6393 in mushview.cpp, method CMUSHView::Mouse_Move_MiniWindow.


'Soludra' on Achaea

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

Posted by WillFa   USA  (525 posts)  [Biography] bio
Date Reply #1 on Mon 23 Nov 2009 04:20 PM (UTC)
Message
Looking at what's on github...

The only WindowShow is in Window.lua's draw routine (base.Draw). The way things cascade you're doing:

WindowCreate
WindowShow
WindowText
WindowText
WindowText
WindowText
WindowText
...


Does this actually display anything that's drawn ever?
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #2 on Mon 23 Nov 2009 05:14 PM (UTC)

Amended on Mon 23 Nov 2009 05:16 PM (UTC) by Twisol

Message
Yes - the plugin itself calls Show(), either directly or through :Draw(true) (the parameter saying "Display -now-").

If it wasn't displaying things, I daresay my problems would be rather different. If you'd like, you can download the framework yourself and test it out. Here's the plugin I'm writing - but please note that it's not done yet.

EDIT: You'll have to uncomment the mouseover/cancelmouseover lines if you want to check that specific issue.


<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>

<muclient>
<plugin
   name="TicTacToe"
   author="Soludra"
   id="03829336f30e9f2dcace86e9"
   language="Lua"
   purpose="Play a game of Tic Tac Toe!"
   date_written="2009-11-22 07:46:00"
   requires="4.43"
   version="1.0"
/>

<!--  Triggers  -->

<triggers>
</triggers>

<!--  Aliases  -->

<aliases>
  <alias
   match="^ttt=(.*)$"
   enabled="y"
   regexp="y"
   send_to="12"
   ignore_case="y"
   sequence="100"
  >
  <send>print(%1)</send>
  </alias>
</aliases>

<!--  Script  -->

<script>
<![CDATA[

require('tprint')

CharacterGrid = require("MWidget\\CharacterGrid")

-- Defines the default grid font.
TICTACTOE_FONT = "Lucida Console"

players = {
  [1] = {
    piece = 'X',
    color = 0x00FF00,
    lastmove = nil,
  },
  [2] = {
    piece = 'O',
    color = 0x0000FF,
    lastmove = nil,
  },
}
current_player = 1


CellFromHotspot = function(id)
  local _, _, x, y = string.find(id, "-h%((%d+),(%d+)%)")
  return grid:Cell(tonumber(x), tonumber(y))
end

CellClick = function(flags, id)
  local cell = CellFromHotspot(id)
  
  if cell.char ~= " " then
    return
  end
  
  if players[current_player].lastmove then
    players[current_player].lastmove.backcolor = 0xFFFFFF
  end
  
  cell.char = players[current_player].piece
  cell.backcolor = players[current_player].color
  
  players[current_player].lastmove = cell
  current_player = (current_player%2) + 1
  grid:Draw(true)
end

CellFocus = function(flags, id)
  local cell = CellFromHotspot(id)

  cell.backcolor = 0xFF0000
  
  grid:Draw(true)
end

CellBlur = function(flags, id)
  local cell = CellFromHotspot(id)
  
  cell.backcolor = 0xFFFFFF
  
  grid:Draw(true)
end

Reset = function()
  grid:ResetGrid()
  
  for y = 1, 3 do
    for x = 1, 3 do
      local cell = grid:Cell(x, y)
      cell.forecolor = 0x000000
      cell.hotspot = {
        mouseup = "CellClick",
        -- mouseover = "CellFocus",
        -- cancelmouseover = "CellBlur",
      }
    end
  end
  
  grid:Draw(true)
end

OnPluginInstall = function()
  grid = CharacterGrid.new(3, 3)
  grid:Anchor(12)
  grid:Font(TICTACTOE_FONT, 15)
  grid.backcolor = 0xFFFFFF

  Reset()
end

OnPluginClose = function()
  grid:Destroy()
end

OnPluginEnable = OnPluginInstall
OnPluginDisable = OnPluginClose

]]>
</script>
</muclient>

'Soludra' on Achaea

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

Posted by Nick Gammon   Australia  (21,322 posts)  [Biography] bio   Forum Administrator
Date Reply #3 on Mon 23 Nov 2009 07:08 PM (UTC)

Amended on Mon 23 Nov 2009 07:09 PM (UTC) by Nick Gammon

Message
Is this fixed yet? I see on your website a fix entitled "Fixed misspelling of 'cancel' ...".

My test shows that my test case works (I haven't tested yours).

Quote:

... cancelmouseover, doesn't appear to be executing at all ... and it appears to me that there are two problems surrounding cancelmousedown ...


Well, you are talking about cancelmouseover, not cancelmousedown, right?

At line 6447 in the (MUSHclient) code there is some code that appears to handle that, starting:


    // cancel previous move-over hotspot (in this miniwindow)


In my test you get a debugging display correctly once you move the mouse away from a hotspot. Otherwise my inventory and map plugins wouldn't have worked.

- Nick Gammon

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

Posted by Nick Gammon   Australia  (21,322 posts)  [Biography] bio   Forum Administrator
Date Reply #4 on Mon 23 Nov 2009 07:27 PM (UTC)

Amended on Mon 23 Nov 2009 07:28 PM (UTC) by Nick Gammon

Message
In your code for Draw:


 Draw = function(self, show)
    local flags = self.flags or 0
    if self.position.absolute then
      flags = bit.bor(flags, 2)
    end
    
    WindowCreate(self.name, self.position.x, self.position.y,
	     self.width, self.height, self.position.anchor, flags, self.backcolor)
    if show then
      WindowShow(self.name, true)
    end
  end,


Why do you call WindowCreate in a draw function? This will make a new miniwindow, deleting existing hotspots. In the code for CMiniWindow::Create it says:


  // a newly created window has no hotspots
  DeleteAllHotspots ();



See: http://www.gammon.com.au/mushclient/mw_creation.htm

I quote from there:

Miniwindow Creation documentation said:

What happens when I re-create an existing miniwindow?

You can do a WindowCreate on an existing miniwindow with no problems. This will have the following effects:


  • The existing offscreen image will be discarded, and the memory it used freed.

  • A new offscreen image will be created, using the new dimensions. Thus you can use WindowCreate to resize a window. For example, you might do this for an inventory window, if you get more inventory.

  • The image will be cleared to the background colour you specify.

  • Any hotspots you added are removed. The assumption is that if you are starting drawing from scratch, any hotspot items will probably appear in a different place.

  • The window's "show" flag is set to false. Thus you need to call WindowShow before it will become visible.


The following items are retained:


  • Any installed fonts (see WindowFont)

  • Any installed images (see WindowLoadImage and WindowCreateImage)




- Nick Gammon

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

Posted by Nick Gammon   Australia  (21,322 posts)  [Biography] bio   Forum Administrator
Date Reply #5 on Mon 23 Nov 2009 07:31 PM (UTC)
Message
I have amended the help for WindowCreate to make this more explicit. However the existing help does refer you to the above web page with the extra information.

- Nick Gammon

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

Posted by WillFa   USA  (525 posts)  [Biography] bio
Date Reply #6 on Mon 23 Nov 2009 07:47 PM (UTC)
Message
The bug happens because your hotspots overlap. :)


      WindowAddHotspot(self.name, self.name .. "-h(" .. x .. "," .. y .. ")",
         left, top, left+self.fonts["f"].width-1, top+self.fonts["f"].height-1,
         cell.hotspot.mouseover, cell.hotspot.cancelmouseover,
         cell.hotspot.mousedown, cell.hotspot.cancelmousedown,
         cell.hotspot.mouseup, cell.hotspot.tooltip,
         cell.hotspot.cursor, 0)

fixes the problem.
[Go to top] top

Posted by WillFa   USA  (525 posts)  [Biography] bio
Date Reply #7 on Mon 23 Nov 2009 08:14 PM (UTC)
Message
Re: Why call WindowCreate in a draw function...

It's easier to draw the whole miniwindow again instead of keeping state on what needs to be updated. And you point out that reCreating the window is a reset button that doesn't nuke fonts/images....
[Go to top] top

Posted by Nick Gammon   Australia  (21,322 posts)  [Biography] bio   Forum Administrator
Date Reply #8 on Mon 23 Nov 2009 08:43 PM (UTC)
Message
But it *does* nuke hotspots...

- Nick Gammon

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

Posted by WillFa   USA  (525 posts)  [Biography] bio
Date Reply #9 on Mon 23 Nov 2009 09:17 PM (UTC)

Amended on Mon 23 Nov 2009 10:07 PM (UTC) by WillFa

Message
what happens if you nuke a hotspot in the middle of a mousedown event script?

I can really only see it as a problem if you're doing animation for a button click effect.



OHHHHHHHH... I get it... You're never getting a cancelMouseOver because you're nuking the hotspot in the MouseOver.
[Go to top] top

Posted by Nick Gammon   Australia  (21,322 posts)  [Biography] bio   Forum Administrator
Date Reply #10 on Mon 23 Nov 2009 10:38 PM (UTC)
Message
Indeed. You can't really complain about not getting a mouse-over cancel for a hotspot that has been deleted. Even if you recreate it later. Part of adding a hotspot clears any "memory" of what the previous hotspot (of the same name) was doing. Otherwise if you deleted a hotspot, and added it later on, somewhere else, it might still give a mouse-over cancel at an inappropriate time.

Basically, this isn't a bug. It is working as documented.

As a workaround either don't re-create the window every time you want to draw in it (after all, then you have to re-add all hotspots, and you have other problems like the one this thread is about, and it is inefficient in that memory is being freed and then reallocated), or if you must have the creation code there, do a check first that the window does not already exist of the correct dimensions, and in the same place (in which case you can skip that line).

- Nick Gammon

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

Posted by Nick Gammon   Australia  (21,322 posts)  [Biography] bio   Forum Administrator
Date Reply #11 on Mon 23 Nov 2009 10:59 PM (UTC)

Amended on Mon 23 Nov 2009 11:00 PM (UTC) by Nick Gammon

Message
BTW, you can avoid using the "self" parameter on function calls if you declare them a bit differently. Here is an example:


local Instance = {}

function Instance:Show ()
  print (self.name)
end

Instance.name = "nick"

Instance:Show ()  --> prints "nick"


By declaring the Show function as "Instance:Show" (note the colon) it gets an automatic "self" argument, so you don't have to explicitly declare it.

This is a bit more of an object-oriented way of doing it, as it is clearer that "self" isn't just "some argument that happens to be called 'self' " but a genuine reference to the current object by the compiler.

- Nick Gammon

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

Posted by WillFa   USA  (525 posts)  [Biography] bio
Date Reply #12 on Mon 23 Nov 2009 10:59 PM (UTC)

Amended on Mon 23 Nov 2009 11:13 PM (UTC) by WillFa

Message
EDIT: Nevermind, I figured out github... My changes are forked and uploaded, Twisol.
[Go to top] top

Posted by Nick Gammon   Australia  (21,322 posts)  [Biography] bio   Forum Administrator
Date Reply #13 on Mon 23 Nov 2009 11:44 PM (UTC)
Message
Your code:


    if ok == 30073 then -- eNoSuchWindow
      return false, "no such window"
    elseif ok == 30065 then -- eCannotAddFont
      return false, "unable to add font"
    else


Could be written as:


    if ok == error_code.eNoSuchWindow then 
      return false, "no such window"
    elseif ok == error_code.eCannotAddFont then
      return false, "unable to add font"
    else


This saves coding "magic numbers" into your code.

- Nick Gammon

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

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #14 on Tue 24 Nov 2009 02:03 AM (UTC)

Amended on Tue 24 Nov 2009 02:12 AM (UTC) by Twisol

Message
Nick Gammon said:

Is this fixed yet? I see on your website a fix entitled "Fixed misspelling of 'cancel' ...".

Which goes on to say '... that would hinder cancelmousedown' specifically.


Nick Gammon said:

Well, you are talking about cancelmouseover, not cancelmousedown, right?

Oops, yes. Minor brain fart which I'll remedy so others don't get confused reading this. (I guess I can't blame you for misunderstanding the commit message!)


Nick Gammon said:

Why do you call WindowCreate in a draw function?

Exactly as WillFa said, it's easier than maintaining a list of changes that have and have not been done, and whether or not to redo ones that belong "behind" the changes on the Z axis.


WillFa said:

OHHHHHHHH... I get it... You're never getting a cancelMouseOver because you're nuking the hotspot in the MouseOver.

Doh. That would explain a lot. This was more of a bug in the workings of the framework than MUSHclient, then. *cough*


Nick Gammon said:

As a workaround either don't re-create the window every time you want to draw in it (after all, then you have to re-add all hotspots, and you have other problems like the one this thread is about, and it is inefficient in that memory is being freed and then reallocated), or if you must have the creation code there, do a check first that the window does not already exist of the correct dimensions, and in the same place (in which case you can skip that line).

It's so much easier to just recreate and redraw, heheh. Hmm, this will require some thought.

Nick Gammon said:

BTW, you can avoid using the "self" parameter on function calls if you declare them a bit differently.

I'm aware, but I've read certain things about being efficient with tables, like declaring things in the table initializer {} rather than after-the-fact. I could probably do something like this, though... and it would be a rather C++-like approach. In fact I'm already doing it with the .new() functions *laughs*.


tbl = {
  foo = nil,
  bar = nil,
}

function tbl:foo()
end

function tbl:bar()
end


I'll probably change the code to do this. I've been juggling design and implementation decisions, though, so I didn't bother with a few things at first (like this).


WillFa said:

My changes are forked and uploaded, Twisol.

Thanks! I'll check them out, I really appreciate the assistance.


Nick Gammon said:

This saves coding "magic numbers" into your code.

I wasn't sure whether I could access error_code and the others without importing constants.lua first (which is really wrapped by XML). Now I see error_code is an entirely separate table, and it seems that I can access it, so thank you for letting me know!


EDIT: Will, your editor is trampling the extra indentation on blank lines. >_> You also might want to look here [1].

[1] http://help.github.com/dealing-with-lineendings/

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[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.


12,895 views.

This is page 1, subject is 2 pages long: 1 2  [Next page]

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 FutureQuest]