I've wasted half a day trying to get something pretty basic working, so I guess I'm missing something, well, basic
I want to show a layout with some windows, on top of my game. When the layout windows are clicked, I want them naturally to handle those clicks, and when the click is outside of those windows, I want them to not handle it so I know to pass the click on to my actual game.
But I can't get this to work. If the layout has a root window which covers the entire window (but transparent so I can see the game behind it), then it captures input. Specifically, it captures every *other* input, since RiseOnClck is enabled - the invisible parent layout window 'rises', no matter where I click. Which makes sense I guess since it covers the entire screen. Now, setting RiseOnClick to False doesn't work, because then the _child_ windows don't rise anymore, and I need them to react when clicked. I've also tried setting PassthroughEnabled on the root window (so it wouldn't rise when clicked, the click would 'pass through'), but RiseOnClick ignores the passthrough property - the click is stil handled despite PassthroughEnabled being true.
The other solution seems to be to not have a parent root window in the layout which covers the entire screen. So say the layout just contains a single small window somewhere. But this doesn't work either, because when I click, getTargetWindow returns the small window in my layout, even though the click isn't on it - because that window is the 'active sheet'.
I honestly at this point have no further ideas about how to solve this, so I'm posting here...
Have a layout *not* handle all inputs
Moderators: CEGUI MVP, CEGUI Team
Followup: I patched my copy of CEGUI to work the way I need it to, here is the patch, perhaps it might be of use to others:
src/CEGUIWindow.cpp:onMouseButtonDown :
The simple change makes it not handle the event if, while the window rose on click, but it has mousepassthrough enabled. As the comment says, this works if we set the invisible layout root to be passthrough-enabled. When we then click outside of the visible windows, the root rises, but it isn't handled.
src/CEGUIWindow.cpp:onMouseButtonDown :
Code: Select all
void Window::onMouseButtonDown(MouseEventArgs& e)
{
// perform tooltip control
Tooltip* tip = getTooltip();
if (tip)
{
tip->setTargetWindow(0);
}
if (e.button == LeftButton)
{
// Kripken: if we rise on click, BUT have mouse passthrough, then do not handle.
// This lets the root (invisible) layout window rise - which lets the actual visible
// windows rise, and be interacted with - but does not 'handle' mouse clicks
// that are not on the actual visible windows. This lets us have a GUI, in
// which clicks outside of our visible windows are not 'handled' by an invisible
// screen-encompassing root window of the layout
bool rose = doRiseOnClick();
e.handled |= (rose && !d_mousePassThroughEnabled);
}
// if auto repeat is enabled and we are not currently tracking
// the button that was just pushed (need this button check because
// it could be us that generated this event via auto-repeat).
if (d_autoRepeat)
{
if (d_repeatButton == NoButton)
captureInput();
if ((d_repeatButton != e.button) && isCapturedByThis())
{
d_repeatButton = e.button;
d_repeatElapsed = 0;
d_repeating = false;
}
}
fireEvent(EventMouseButtonDown, e, EventNamespace);
}
The simple change makes it not handle the event if, while the window rose on click, but it has mousepassthrough enabled. As the comment says, this works if we set the invisible layout root to be passthrough-enabled. When we then click outside of the visible windows, the root rises, but it isn't handled.
- scriptkid
- Home away from home
- Posts: 1178
- Joined: Wed Jan 12, 2005 12:06
- Location: The Hague, The Netherlands
- Contact:
Hi,
sounds like a good catch. I think that CE is currently working on an overhaul to deal which situations such as these. Because now and then some forum post like yours pops up One situation that just doesn't fit exactly within the system.
sounds like a good catch. I think that CE is currently working on an overhaul to deal which situations such as these. Because now and then some forum post like yours pops up One situation that just doesn't fit exactly within the system.
Check out my released snake game using Cegui!
This sounds similar to an issue I was having, if it's something completely different, than forgive me <3
Here was my problem: the first time you clicked on the window to interact with game controls, the mouse click was absorbed by CEGUI; the second and all subsequent mouse clicks would correctly fall through to the game. This sounds like your "captures every other input" issue.
My first reaction was to simply inject (into CEGUI) a fake mouse click at 0,0 and this fixed my problem. However, I wasn't really satisfied with that as I wanted to know if this was an actual bug in CEGUI or I was doing something wrong.
Well I did a little more digging and it turns out that when you load/set your GUISheet, it's not actually considered 'active' until an event is fired that activates the root window. So I found the simpler solution to simply call CEGUI::System::GetSingleton().getGUISheet()->activate() after the call to setGUISheet(). It would be nice if this information was listed in the tutorial, or at least the call to activate() added to the sample code ^_^
Here was my problem: the first time you clicked on the window to interact with game controls, the mouse click was absorbed by CEGUI; the second and all subsequent mouse clicks would correctly fall through to the game. This sounds like your "captures every other input" issue.
My first reaction was to simply inject (into CEGUI) a fake mouse click at 0,0 and this fixed my problem. However, I wasn't really satisfied with that as I wanted to know if this was an actual bug in CEGUI or I was doing something wrong.
Well I did a little more digging and it turns out that when you load/set your GUISheet, it's not actually considered 'active' until an event is fired that activates the root window. So I found the simpler solution to simply call CEGUI::System::GetSingleton().getGUISheet()->activate() after the call to setGUISheet(). It would be nice if this information was listed in the tutorial, or at least the call to activate() added to the sample code ^_^
Jamarr wrote:This sounds similar to an issue I was having, if it's something completely different, than forgive me <3
Here was my problem: the first time you clicked on the window to interact with game controls, the mouse click was absorbed by CEGUI; the second and all subsequent mouse clicks would correctly fall through to the game. This sounds like your "captures every other input" issue.
My first reaction was to simply inject (into CEGUI) a fake mouse click at 0,0 and this fixed my problem. However, I wasn't really satisfied with that as I wanted to know if this was an actual bug in CEGUI or I was doing something wrong.
Well I did a little more digging and it turns out that when you load/set your GUISheet, it's not actually considered 'active' until an event is fired that activates the root window. So I found the simpler solution to simply call CEGUI::System::GetSingleton().getGUISheet()->activate() after the call to setGUISheet(). It would be nice if this information was listed in the tutorial, or at least the call to activate() added to the sample code ^_^
It sounds like a completely separate issue. In my case it absorbs every other click because of RiskOnClick, not just the first click because the layout isn't active. Also, I tried activate() on my sheet (and deactivate, and anything else I could think of), but it didn't help.
kripken wrote:It sounds like a completely separate issue. In my case it absorbs every other click because of RiskOnClick, not just the first click because the layout isn't active. Also, I tried activate() on my sheet (and deactivate, and anything else I could think of), but it didn't help.
I find that odd since I cannot reproduce that behavior, aside from what I described above. I also have a root window that encompases the entire window and is transparent; it is a "DefaultWindow" (eg: CEGUIWindow.cpp). By default, this window is visible (though transparent), has both RiseOnClick and ZOrdering enabled, and MousePassThrough disabled.
Meaning, if RiseOnClick really is the culprit, mine would be exhibiting the same behavior - but it is not. If I click outside of any non-root cegui windows, my input is handled by the game (and injectMouseButtonDown() will return false) because the root window does not consume it. I will also throw out a quote from CE himself:
crazyeddie wrote:To explain a bit more - say you had two sibling frame windows, if you clicked on the inactive one, the frame window would react to activate the window and consume the event. If the window is already active, it does nothing so the event is not consumed.
While he is talking about FrameWindows there, you can verify the highlighted behavior by simply looking at the code in CEGUIWindow.cpp. In other words, the only time a MouseButtonDown event is actually consumed is if your root window is not active (which relates back to my previous post).
Now you are saying that it happens "every other click"...well I'm saying that is not possible according to the information you have given. If you look at the CEGUI::Window code, you can see this behavior clear as day. It doesn't even care about MousePassThrough because, for a "DefaultWindow", mouse-click events are only considered handled if the window was activated. This leads me to believe that the problem lies in your code, not in CEGUI.
Are you using a "DefaultWindow" as your GUISheet?
Are you accidentally de-activiating the GUISheet somewhere in your code?
bool rose = doRiseOnClick();
e.handled |= (rose && !d_mousePassThroughEnabled);
The simple change makes it not handle the event if, while the window rose on click, but it has mousepassthrough enabled. As the comment says, this works if we set the invisible layout root to be passthrough-enabled. When we then click outside of the visible windows, the root rises, but it isn't handled.
Your reasoning does not make any sense. You are saying that when the event is handled, if MousePassThrough is enabled (which btw has no othher effect on a "DefaultWindow"!) we should hide the fact that the event was handled? That defies logic.
Thank you! Yes, it turns out that I was deactivating the GUI sheet, and that was causing the odd behaviour.
Now this leaves me with another problem, though: previously when I clicked outside of my visible windows, then I deactivated the GUI sheet. This had the effect of no longer capturing keyboard input for the editbox in my visible window. In other words, if I don't deactivate the GUI sheet then it continues to capture keyboard input, and my game doesn't receive those keypresses. What's the correct way to make it so this doesn't happen?
Now this leaves me with another problem, though: previously when I clicked outside of my visible windows, then I deactivated the GUI sheet. This had the effect of no longer capturing keyboard input for the editbox in my visible window. In other words, if I don't deactivate the GUI sheet then it continues to capture keyboard input, and my game doesn't receive those keypresses. What's the correct way to make it so this doesn't happen?
Well, I think specific implementions for this issue varies by application, though the general method is probably the same.
I store a cegui_keyboard_focus state. This state is nothing more than the stored result from CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::LeftButton). When the state is 1, then whenever a key is pressed I use the inject/KeyDown/KeyUp/Char methods to send the input to CEGUI. If the state is 0, then I just don't call those inject methods, and I let my game handle them instead.
I store a cegui_keyboard_focus state. This state is nothing more than the stored result from CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::LeftButton). When the state is 1, then whenever a key is pressed I use the inject/KeyDown/KeyUp/Char methods to send the input to CEGUI. If the state is 0, then I just don't call those inject methods, and I let my game handle them instead.
Jamarr wrote:Well, I think specific implementions for this issue varies by application, though the general method is probably the same.
I store a cegui_keyboard_focus state. This state is nothing more than the stored result from CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::LeftButton). When the state is 1, then whenever a key is pressed I use the inject/KeyDown/KeyUp/Char methods to send the input to CEGUI. If the state is 0, then I just don't call those inject methods, and I let my game handle them instead.
I think I understand, so you handle this yourself manually instead of letting CEGUI do it. It sounds like it should work, thanks, I'll try it.
It is funny because I have sort of come full circle back to this issue. The same logic that allows the GUISheet to work as expected, and is explained throughout this thread, is the same logic that I recently found to be problematic.
Here is the scenario:
The delima here is that we obviously want this behavior for the GUISheet, but we want a different behavior for the child-windows (consuming mouse-down events even if it is already active).
The normal behavior that the GUISheet should exhibit is to allow all mouse-down events that /do not hit/ any child-windows to passthrough to the application, while all mouse-down events that /do hit/ child-windows, even if they are already active, should be consumed /unless/ the child-window has passthrough enabled.
Unfortunately, the logical behavior is not the default behavior in CEGUI. Luckily I was able to work around this issue, and implement the logical window behavior without having to modify the CEGUI source, but it is a bit crude.
This is (essentially) the code I use to acheive this behavior:
Now I just have to ask...am I completely overlooking something here? Is there some reason why the logical behavior one would expect is not the default behavior CEGUI exhibits? I can't understand how this would go unnoticed for so long unless I'm doing it wrong, or unless /everyone/ just works around it...
Here is the scenario:
- An inactive window will consume a mouse-down event.
- Thus, if the GUISheet is activeted after being set, any mouse-down events on the GUISheet itself (eg, not on any child windows) will not be consumed. This allows the event to be passed onto the application.
- Likewise, if the GUISheet is active, a mouse-down event on an inactive child-window will be consumed by CEGUI. This gives the desired effect this thread was all about: allowing the user to switch input contexts depending on what they clicked on.
- However, an active window will not consume a mouse-down event.
- Because this behavior is exibited for all elements, a mouse-down event on an already active child-window will /not/ consume the event, which brings us right back to square 1 - the user is specifically clicking on a CEGUI element but instead of being consumed by CEGUI is mistakenly handed off to the application and doing who-knows-what.
The delima here is that we obviously want this behavior for the GUISheet, but we want a different behavior for the child-windows (consuming mouse-down events even if it is already active).
The normal behavior that the GUISheet should exhibit is to allow all mouse-down events that /do not hit/ any child-windows to passthrough to the application, while all mouse-down events that /do hit/ child-windows, even if they are already active, should be consumed /unless/ the child-window has passthrough enabled.
Unfortunately, the logical behavior is not the default behavior in CEGUI. Luckily I was able to work around this issue, and implement the logical window behavior without having to modify the CEGUI source, but it is a bit crude.
This is (essentially) the code I use to acheive this behavior:
Code: Select all
// cegui initialization
void CEManager::Init()
{
...
// setup root window
_pWin = CEGUI::WindowManager::getSingleton().createWindow("DefaultWindow", "Root");
CEGUI::System::getSingleton().setGUISheet(_pWin);
// NOTE: CEGUI v0.6.1: We need to make sure that we activate the
// GUISheet (root window). If the GUISheet is not active, then the user
// will need to click once on screen to activate it, thus consuming that
// click and giving the illusion that nothing happened.
CEGUI::System::getSingleton().getGUISheet()->activate();
...
}
// cegui mouse-input handler
int CEManager::HandleMouseInput(...)
{
...
// if cegui has consumed the mouse click, this generally means that a window was activated
if (CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::LeftButton))
{
// we need to make sure our application has the /keyboard/ focus
Focus();
return 1;
}
// NOTE: CEGUI v0.6: If the user clicks on an already _active_ cegui window,
// and neither the window or it's children used the event to activate themselves,
// (because they where already active) then injectMouseButtonDown() will return false!
// This is somewhat of an annoyance, as most would expect that clicking on a window
// that is already active would still return true because clicking on a top window
// should _not_ fall through unless passthrough is explicitely enabled. In essence,
// the user is simply re-activating the window which _should_ consume the event - but
// unfortunately does not; this is likely because the GUISheet uses the same logic (WHY!?!)
// and there we /do/ want the event to passthrough under the same circumstances!
// references
CEGUI::Window* pGuiSheet = CEGUI::System::getSingleton().getGUISheet();
CEGUI::Window* pActiveWin = pGuiSheet->getActiveChild();
CEGUI::Window* pPrevWin = 0;
// locate the active cegui window; don't ask my why there is no getActiveWindow()!!! ffs...
// oh, and don't even think for a second that getActiveChild(), getCaptureWindow(), etc.
// will give us what we want!
while (pActiveWin && pActiveWin != pGuiSheet)
{
pPrevWin = pActiveWin;
pActiveWin = pActiveWin->getParent();
}
pActiveWin = pPrevWin;
// convert screen coords to cegui coords
XPScreenToCeguiScreen(x,y);
// deactivate the active cegui window
if (pActiveWin)
{
// if passthrough is not enabled
if (0 == pActiveWin->isMousePassThroughEnabled())
{
// check to see if the user clicked on the _active_ cegui window;
if (pActiveWin->isHit(CEGUI::Vector2(static_cast<Float>(x),static_cast<Float>(y))))
return 1;
// otherwise deactivate the active window, and let the application handle this event!
pActiveWin->deactivate();
}
}
// finally, if cegui did not consume the event, and the user was not click on the
// active window, we need to return the /keyboard/ focus to xplane
Defocus();
...
}
Now I just have to ask...am I completely overlooking something here? Is there some reason why the logical behavior one would expect is not the default behavior CEGUI exhibits? I can't understand how this would go unnoticed for so long unless I'm doing it wrong, or unless /everyone/ just works around it...
- CrazyEddie
- CEGUI Project Lead
- Posts: 6760
- Joined: Wed Jan 12, 2005 12:06
- Location: England
- Contact:
@Jamarr:
The current 'inconsistent' behaviour was introduced by me when I 'fixed' an issue related to events for the 0.6.0 release. It wasn't until afterwards that it became apparent as to the size of the can of worms that I'd opened up
As scriptkid mentioned above, this is something that I will be addressing; although I have not stared any work on it at the present time, and as such have not come up with a final plan for how to handle these things, though I have a couple of ideas to try out when I get some time to work on CEGUI.
CE.
The current 'inconsistent' behaviour was introduced by me when I 'fixed' an issue related to events for the 0.6.0 release. It wasn't until afterwards that it became apparent as to the size of the can of worms that I'd opened up
As scriptkid mentioned above, this is something that I will be addressing; although I have not stared any work on it at the present time, and as such have not come up with a final plan for how to handle these things, though I have a couple of ideas to try out when I get some time to work on CEGUI.
CE.
Who is online
Users browsing this forum: No registered users and 13 guests