@sgold said:
I have a similar issue with button.setIsVisible(false)
The tool tip remains visible until I move the mouse cursor.
Must I clear the tool tip myself, or should the GUI take care of it?
Since the update is called on mouse move, I’m not sure how to check for this without unnecessary overhead. It may be best to leave this as is and force the hide through Screen.releaseForcedToolTip();
@sgold said:
Could you clear the tool tip in setIsVisible() and removeElement()?
releaseForcedToolTip() doesn’t apply because the tool tip is associated with an Element.
I follow you on the first one… this is totally doable. I’ll try and find other instances where this should be checked/cleared as I can. If you see any other places that are not caught by hide/remove, just let me know.
Ah… I see what you mean on the second one. The focus element isn’t cleared until the mouse is moved… so this wouldn’t help. Though, I though I had added a hideToolTip method to solve this issue. It may have been removed in the patch. I can add this back as a fallback for instances where the auto-hide on hide/remove element doesn’t work for some unforeseen reason.
I checked out a copy of the tonegod.gui project and started testing it. In the debugger, I could see that screen.hideToolTip() was being invoked by Element.hide(), yet the tool tip remained visible. I’m not sure why. Shall I write you a test case?
@sgold said:
I checked out a copy of the tonegod.gui project and started testing it. In the debugger, I could see that screen.hideToolTip() was being invoked by Element.hide(), yet the tool tip remained visible. I'm not sure why. Shall I write you a test case?
If it is not too much of a bother… surely. If it would be easier to just explain the test, I’ll recreate it here. Whichever make life easier for you.
public class TestToolTip extends SimpleApplication {
private Button button;
private Screen guiScreen;
final String buttonAction = "removeElement"; // "hide";
private String nextAction;
public static void main(String[] args) {
TestToolTip app = new TestToolTip();
app.start();
}
@Override
public void simpleInitApp() {
flyCam.setEnabled(false);
guiScreen = new Screen(this);
guiScreen.setUseToolTips(true);
guiNode.addControl(guiScreen);
Vector2f size = descale(0.25f, 0.25f);
Vector2f offset = new Vector2f(0.5f, 0.5f).multLocal(size);
Vector2f center = descale(0.5f, 0.5f);
Vector2f upperLeft = center.subtract(offset);
String iconAssetPath = "Interface/Logo/Monkey.jpg";
String buttonUID = UIDUtil.getUID();
Vector4f padding = new Vector4f(0f, 0f, 0f, 0f);
button = new ButtonAdapter(guiScreen, buttonUID, upperLeft,
size, padding, iconAssetPath) {
@Override
public void onButtonMouseLeftUp(MouseButtonEvent e, boolean t) {
nextAction = buttonAction;
}
};
guiScreen.addElement(button);
button.setToolTipText(buttonAction);
}
@Override
public void simpleUpdate(float tpf) {
if ("hide".equals(nextAction)) {
button.hide();
} else if ("setIsVisible".equals(nextAction)) {
button.setIsVisible(false);
} else if ("removeElement".equals(nextAction)) {
guiScreen.removeElement(button);
}
nextAction = null;
}
private Vector2f descale(float xFraction, float yFraction) {
float x = xFraction * guiScreen.getWidth();
float y = yFraction * guiScreen.getHeight();
Vector2f result = new Vector2f(x, y);
return result;
}
}
[/java]
I think I see the problem… you’re using the update loop to fire off commands that are already being handled by the update loop. You can simply move the whole nextAction to the button onMouseWhatever call:
[java]
button = new ButtonAdapter(guiScreen, buttonUID, upperLeft,
size, padding, iconAssetPath) { @Override
public void onButtonMouseLeftUp(MouseButtonEvent e, boolean t) {
// nextAction = buttonAction;
screen.removeElement(this);
}
};
[/java]
Whats happening here is the action isn’t being executed until the following frame. So:
The next action is set
A frame goes by before the next update loop is called
The check to hide is happening prior to the check for tooltip focus
The action is executed.
The tooltip is reshown
GUI commands (abstract listener calls) do not need to be enqueued or delayed as they are properly called during the update process.
@sgold said:
Thanks for looking into this. I'm unclear why the tooltip is being reshown after the button has been hidden or removed though. Care to elaborate?
It’s the order in which update is happening first. It relies on mouseFocusElement either being null… or an element containing tooltip text. If you are hiding this after the focus element is set, until the mouse moves and the check is performed again, the screen thinks it is still there.
EDIT: This is why I originally thought this would incur a bunch of unneeded overhead. But your solution works really well without having to add another ray cast check.
@t0neg0d said:
It's the order in which update is happening first. It relies on mouseFocusElement either being null... or an element containing tooltip text. If you are hiding this after the focus element is set, until the mouse moves and the check is performed again, the screen thinks it is still there.
Interesting! I greatly appreciate the attention you’ve paid to this issue.
The app I posted is, of course, just a condensed demonstration of the issue. The maze game I’m writing is more complex. Part of my strategy for managing its complexity is to channel all user input (including keypresses, raw mouse events, and GUI events) through a single, centralized method that’s invoked during updates.
While it’s true I could bypass this mechanism, I don’t want to, because that might open up some tricky race conditions.
If your library assumes this invariant (mouseFocusElement either null or an element containing tooltip text) then its high-level operations (like removeElement and hide) should preserve that invariant. If they don’t, then users who do unexpected things with your library are likely to encounter unexpected order-dependencies like this one.
So it seems to me that removeElement() and hide() should test and update mouseFocusElement. Do you agree?
@sgold said:
Interesting! I greatly appreciate the attention you've paid to this issue.
The app I posted is, of course, just a condensed demonstration of the issue. The maze game I’m writing is more complex. Part of my strategy for managing its complexity is to channel all user input (including keypresses, raw mouse events, and GUI events) through a single, centralized method that’s invoked during updates.
While it’s true I could bypass this mechanism, I don’t want to, because that might open up some tricky race conditions.
If your library assumes this invariant (mouseFocusElement either null or an element containing tooltip text) then its high-level operations (like removeElement and hide) should preserve that invariant. If they don’t, then users who do unexpected things with your library are likely to encounter unexpected order-dependencies like this one.
So it seems to me that removeElement() and hide() should test and update mouseFocusElement. Do you agree?
It certainly wouldn’t be a show stopper if the extra check was performed. I’ll need to run a few tests to make sure this doesn’t interrupt any other process (I’m doubting that it does… as I can’t think of a thing other than tooltips that is concerned with the previous mouse focus element).
Though, I would still caution against delaying the input this way as there is the potential that mouse down and mouse up could easily execute on sequential frames (especially in the case of Android where the frame rate is 60 frames per second at the absolute ideal… usually closer to 30) which would simply negate the mouse down all together.
This could also potentially break mousePressed Interval events (i.e. mouse still down). As the still down event is executed on the update loop:
Mouse down happens
Initial delay to ensure it is an intentional mouse still pressed
Button adds itself as a control
Update loop fires off the still down method on the set interval
Mouse release happens
Button removes itself as a control.
This ensures that the gui isn’t adding a million potentially useless controls to the update loop and only utilizes them when needed.
@sgold
Actually… another thought would be to not use the update loop to execute any of these events. Just simply call a centralized method… this way you get the centralized event handling without the potential mishaps of using the following frame via the update loop.
@t0neg0d said:
Though, I would still caution against delaying the input this way as there is the potential that mouse down and mouse up could easily execute on sequential frames (especially in the case of Android where the frame rate is 60 frames per second at the absolute ideal... usually closer to 30) which would simply negate the mouse down all together.
My delayed input mechanism is for game-level events only. It doesn’t affect mouse events which are handled by the GUI. Sorry if I misled you.
@sgold said:
My delayed input mechanism is for game-level events only. It doesn't affect mouse events which are handled by the GUI. Sorry if I misled you.
Ooooh! Gotcha… =) I’ll try experimenting with this and see if there are potential issues and let you know what the outcome is.