[Beta 4 Bug w/ Fix] Canvas.setCursorPos() Moves Cursor to "Wrong" Place
by Ryan Mounts · in Torque 3D Professional · 07/31/2009 (9:15 am) · 8 replies
Someone brought up an issue with the Canvas.setCursorPos() method in the TGEA forum:
www.garagegames.com/community/forums/viewthread/98433
T3D has this problem too, where Canvas.getCursorPos() returns the cursor position in canvas space coordinates, but Canvas.setCursorPos() sets the cursor position in screen space coordinates (so <0,0> represents the top left corner of your monitor, not the Canvas... not to be confused with "screen space" shaders... here I mean screen space == monitor space). I say "wrong" place in the title because the code comments actually say that these two methods work in screen space, but only the latter does. It is *much* more intuitive and useful to work in canvas space, so I changed setCursorPos() to work like getCursorPos(). To do this, I made these changes in "gui/core/guiCanvas.cpp":
Near the top, add a new header:
In the "GuiCanvas::setCursorPos()" method, add these lines:
This fixes the problem on the Windows operating system so that <0,0> now represents the upper left corner of the Canvas... I do not know if Macs suffer from this problem, but this fix could probably be easily updated to address them.
www.garagegames.com/community/forums/viewthread/98433
T3D has this problem too, where Canvas.getCursorPos() returns the cursor position in canvas space coordinates, but Canvas.setCursorPos() sets the cursor position in screen space coordinates (so <0,0> represents the top left corner of your monitor, not the Canvas... not to be confused with "screen space" shaders... here I mean screen space == monitor space). I say "wrong" place in the title because the code comments actually say that these two methods work in screen space, but only the latter does. It is *much* more intuitive and useful to work in canvas space, so I changed setCursorPos() to work like getCursorPos(). To do this, I made these changes in "gui/core/guiCanvas.cpp":
Near the top, add a new header:
#include "gui/core/guiControl.h" #include "console/consoleTypes.h" #include "gfx/screenshot.h" #include "windowManager/win32/win32Window.h" // Add this header
In the "GuiCanvas::setCursorPos()" method, add these lines:
void GuiCanvas::setCursorPos(const Point2I &pt)
{
mCursorPt.x = F32(pt.x);
mCursorPt.y = F32(pt.y);
AssertISV(mPlatformWindow, "GuiCanvas::setCursorPos - no window present!");
/// NEW CODE STARTS HERE
POINT topleft;
topleft.x = 0;
topleft.y = 0;
Win32Window* w = dynamic_cast<Win32Window*>(mPlatformWindow);
if(w != NULL)
{
ClientToScreen(w->getHWND(), &topleft);
/// This clamps the cursor inside the Canvas bounds
Point2I extent = mPlatformWindow->getClientExtent();
if(pt.x >= extent.x)
topleft.x -= pt.x - extent.x + 1;
if(pt.y >= extent.y)
topleft.y -= pt.y - extent.y + 1;
}
mPlatformWindow->setCursorPosition( topleft.x + pt.x, topleft.y + pt.y );
/// NEW CODE ENDS HERE
// mPlatformWindow->setCursorPosition( pt.x, pt.y ); // Remove this line
}This fixes the problem on the Windows operating system so that <0,0> now represents the upper left corner of the Canvas... I do not know if Macs suffer from this problem, but this fix could probably be easily updated to address them.
#2
Thanks for the fix, Ryan. I'll merge the changes, though it may not make it for the immediate next release.
And yes, same problem on Mac.
BTW, a GuiCanvas holds a reference to the PlatformWindow it belongs to. You'll probably want to use that instead of taking the first window from the manager.
08/03/2009 (9:30 am)
Thanks for the fix, Ryan. I'll merge the changes, though it may not make it for the immediate next release.
And yes, same problem on Mac.
BTW, a GuiCanvas holds a reference to the PlatformWindow it belongs to. You'll probably want to use that instead of taking the first window from the manager.
#3
Also, PlatformWindow has a method getPosition() that returns the window's position in screen coordinates. Using this, the code above becomes platform-independent. (i.e. use mPlatformWindow->getPosition() instead of ClientToScreen).
08/03/2009 (9:36 am)
Also, PlatformWindow has a method getPosition() that returns the window's position in screen coordinates. Using this, the code above becomes platform-independent. (i.e. use mPlatformWindow->getPosition() instead of ClientToScreen).
#4
You're right, I should not have grabbed the first window from the manager, so I changed the code snippet above to reflect that. But I don't think you want to use mPlatformWindow->getPosition(). That gives you the upper left corner of the window, not the Canvas. The only way I've found to get the upper left corner of the Canvas is with ClientToScreen().
Obviously you don't want platform-dependent code in the Canvas code like that, but since I'm no Mac programmer, I'll leave that up to the professionals. ;)
Also now that I look at it again, you might not want to clamp the cursor position to the Canvas bounds like I did, seeing as how GuiCanvas::getCursorPos() can return values outside the bounds. They should work in a complementary fashion.
08/03/2009 (10:38 am)
Hey Rene,You're right, I should not have grabbed the first window from the manager, so I changed the code snippet above to reflect that. But I don't think you want to use mPlatformWindow->getPosition(). That gives you the upper left corner of the window, not the Canvas. The only way I've found to get the upper left corner of the Canvas is with ClientToScreen().
Obviously you don't want platform-dependent code in the Canvas code like that, but since I'm no Mac programmer, I'll leave that up to the professionals. ;)
Also now that I look at it again, you might not want to clamp the cursor position to the Canvas bounds like I did, seeing as how GuiCanvas::getCursorPos() can return values outside the bounds. They should work in a complementary fashion.
#5
True. You're right about getPosition(). Don't think the current PlatformWindow has a method for determining the upper left corner of the client rectangle. Will see about that.
About the clamping, the getCursorPos and setCursorPos methods are documented to return the cursor position in screen coordinates. So far, this has been a problem here of course, but I still think it's the right way.
Your change above solves the problem by changing the semantics of setCursorPos to operate in local space (which apparently getCursorPos does, too). I think a better solution would be to make both methods do what they actually claim to do.
08/03/2009 (11:20 am)
True. You're right about getPosition(). Don't think the current PlatformWindow has a method for determining the upper left corner of the client rectangle. Will see about that.
About the clamping, the getCursorPos and setCursorPos methods are documented to return the cursor position in screen coordinates. So far, this has been a problem here of course, but I still think it's the right way.
Your change above solves the problem by changing the semantics of setCursorPos to operate in local space (which apparently getCursorPos does, too). I think a better solution would be to make both methods do what they actually claim to do.
#6
I really can't think of a reason why you would need to set the cursor position relative to the screen, because you have no control over anything that exists outside your game's window during gameplay. It makes much more sense to be able to manipulate the cursor in Canvas space, since all of your GUIs are in Canvas space, allowing you to easily place the cursor relative to your GUI elements. With that said, maybe there should be methods for getting and setting the cursor position in both screen and Canvas space... I'd just really hate to see only the screen space control make it into the release, because I find Canvas space so much more useful. Just my two cents.
08/03/2009 (12:08 pm)
I agree that the two methods (get and set) need to work in the same coordinate space, and they are documented to be in sceen space. But I found this bug in response to the thread listed in the body of this post. The author of that thread needed to be able to set the cursor position in Canvas space. I looked into it because I need to do the same thing for my project. I really can't think of a reason why you would need to set the cursor position relative to the screen, because you have no control over anything that exists outside your game's window during gameplay. It makes much more sense to be able to manipulate the cursor in Canvas space, since all of your GUIs are in Canvas space, allowing you to easily place the cursor relative to your GUI elements. With that said, maybe there should be methods for getting and setting the cursor position in both screen and Canvas space... I'd just really hate to see only the screen space control make it into the release, because I find Canvas space so much more useful. Just my two cents.
#7
Agree that this solves the problem as such quite nicely. I'm still undecided as to what is the better solution. Canvas space has the problem that you lose the ability to tell whether the cursor is actually on the canvas--which may or may not be a problem. Considering that only setCursorPos has so far worked in screen space, it may be safe to change the behavior.
Will look into this some more later.
Definitely thanks for posting your changes.
08/03/2009 (12:19 pm)
Agree that this solves the problem as such quite nicely. I'm still undecided as to what is the better solution. Canvas space has the problem that you lose the ability to tell whether the cursor is actually on the canvas--which may or may not be a problem. Considering that only setCursorPos has so far worked in screen space, it may be safe to change the behavior.
Will look into this some more later.
Definitely thanks for posting your changes.
#8
Finally fixed this for the next release. Went the other route and made the two methods properly operate in screen space.
09/09/2009 (8:32 pm)
Finally fixed this for the next release. Went the other route and made the two methods properly operate in screen space.
Torque Owner Ryan Mounts
CornerstoneViz