Zini
06-09-2007 15:13:35
Zini
06-09-2007 15:13:35
kungfoomasta
06-09-2007 16:15:15
Zini
06-09-2007 16:41:55
kungfoomasta
06-09-2007 17:04:09
Zini
06-09-2007 17:26:12
kungfoomasta
06-09-2007 17:33:57
bool mBringToFrontOnFocus;
Window::setBringToFrontOnFocus(bool BringToFront);
after clicking on the disabled window
Zini
06-09-2007 17:39:27
kungfoomasta
06-09-2007 17:51:12
Zini
06-09-2007 18:12:28
kungfoomasta
06-09-2007 18:24:25
What if you want to have some passive HUD-elements in front of all other stuff (like a big red warning message)?
Zini
10-09-2007 09:42:23
kungfoomasta
10-09-2007 16:17:13
void Widget::disable()
{
if((mWidgetType == Widget::TYPE_SHEET))
return;
mEnabled = false;
setTexture(mDisabledTextureName,false);
WidgetEventArgs args(this);
fireEvent(EVENT_DISABLED,args);
}
I will disable ability to disable/enable Sheet
void Window::onGainFocus(const EventArgs& args)
{
if(mBringToFrontOnFocus)
mQuadContainer->moveWindowGroupToEnd(this);
}
Zini
10-09-2007 17:07:35
As for sheet, we agreed you cannot disable it, so it will always accept mouse clicks. (disabling a sheet may crash the way GUIManager looks for mouse over widget)
kungfoomasta
10-09-2007 17:19:26
And since we are at it, maybe setBringToFrontOnClick would be more useful than setBringToFrontOnFocus?
Zini
10-09-2007 17:29:51
kungfoomasta
10-09-2007 18:37:13
Zini
10-09-2007 19:03:25
kungfoomasta
10-09-2007 19:50:07
kungfoomasta
11-09-2007 08:09:59
Zini
11-09-2007 08:23:56
kungfoomasta
11-09-2007 16:18:47
kungfoomasta
11-09-2007 16:37:45
Zini
11-09-2007 17:11:01
kungfoomasta
11-09-2007 17:20:56
Zini
12-09-2007 09:18:50
kungfoomasta
03-04-2008 20:27:08
Zini
03-04-2008 22:47:18
I've decided to remove the gainFocusOnClick functionality,
kungfoomasta
03-04-2008 23:10:59
Why should a message stop the player for interacting with other GUI elements?
bool GUIManager::injectMouseButtonDown(const MouseButtonID& button)
{
// If the mouse is disabled, we do not accept this injection of input
if( !mMouseCursor->getEnabled() )
return false;
// Users can manually inject single/double/triple click input, or have it detected
// from mouse button up/down injections.
if(mDetermineClickEvents)
{
// Record time of MouseDown, for single/double/triple click determination
mTimeOfButtonDown[button] = mTimer->getMilliseconds();
// Check if time since last double click is within triple click time
if((mTimeOfButtonDown[button] - mTimeOfDoubleClick[button]) <= mGUIManagerDesc.tripleClickTime)
{
// We have passed the criteria for a triple click injection, but we have to
// make sure the mouse button is going down on the same widget for all clicks.
if(mMouseButtonDown[button] == mWidgetUnderMouse)
return injectMouseTripleClick(button);
}
// Check if time since last click is within double click time
if((mTimeOfButtonDown[button] - mTimeOfClick[button]) <= mGUIManagerDesc.doubleClickTime)
{
// We have passed the criteria for a double click injection, but we have to
// make sure the mouse button is going down on the same widget for both clicks.
if(mMouseButtonDown[button] == mWidgetUnderMouse)
return injectMouseDoubleClick(button);
}
}
// If we make it here, a simple mouse button down has occurred.
// Modify the button mask
mButtonMask |= (1 << button);
// Record that the mouse button went down on this widget
mMouseButtonDown[button] = mWidgetUnderMouse;
// Create MouseEventArgs, for use with any fired events
MouseEventArgs args(mWidgetUnderMouse);
args.position = mMouseCursor->getPosition();
args.buttonMask = mButtonMask;
args.keyModifiers = mKeyModifiers;
// Create a boolean to track whether or not this injection caused any significant changes.
bool changesMade = false;
// Get the Window under the mouse cursor. If it is not the Sheet, but a child Window,
// make sure it has focus. (bring to front)
Window* win = mActiveSheet->getWindowAtPoint(args.position);
if(win != mActiveSheet)
// FOCUS_GAINED and FOCUS_LOST events will be fired if appropriate.
changesMade |= mActiveSheet->focusWindow(win);
Widget* w = win->getWidgetAtPoint(args.position);
// Set focus to the widget the mouse went down on.
// FOCUS_GAINED and FOCUS_LOST events will be fired if appropriate.
changesMade |= mActiveSheet->focusWidget(w);
w->setGrabbed(true);
// Fire EVENT_MOUSE_BUTTON_DOWN event to the widget in focus
changesMade |= w->fireEvent(Widget::EVENT_MOUSE_BUTTON_DOWN,args);
return changesMade;
}
bool GUIManager::injectMouseButtonUp(const MouseButtonID& button)
{
// If the mouse is disabled, we do not accept this injection of input
if( !mMouseCursor->getEnabled() )
return false;
// Modify the button mask
mButtonMask &= !(1 << button);
// If the mouse button goes up and is not over the same widget
// the mouse button went down on, disregard this injection.
if( mMouseButtonDown[button] != mWidgetUnderMouse )
return false;
// after this point, we know that the user had mouse button down on this widget, and is now doing mouse button up on the same widget.
// Create MouseEventArgs, for use with any fired events
MouseEventArgs args(mActiveSheet->getWidgetInFocus());
args.position = mMouseCursor->getPosition();
args.buttonMask = buttonMask;
args.keyModifiers = mKeyModifiers;
// Check if a widget is currently being dragged.
if(args.widget->getGrabbed())
{
args.widget->setGrabbed(false);
// We do not want the widget to receive an EVENT_MOUSE_BUTTON_UP event if we are dropping
// the widget. Think of Diablo II, dragging a potion to your belt. If we sent the mouse
// button up event the potion would be drank as soon as you dropped it into the belt.
return args.widget->fireEvent(Widget::EVENT_DROPPED,args);
}
// Create a boolean to track whether or not this injection caused any significant changes.
bool changesMade = false;
// Fire EVENT_MOUSE_BUTTON_UP event
changesMade |= args.widget->fireEvent(Widget::EVENT_MOUSE_BUTTON_UP,args);
// Users can manually inject single/double/triple click input, or have it detected
// from mouse button up/down injections.
if(mDetermineClickEvents)
{
if((mTimer->getMilliseconds() - mTimeOfButtonDown[button]) <= mGUIManagerDesc.clickTime)
{
changesMade |= injectMouseClick(button);
}
}
return changesMade;
}
bool GUIManager::injectMouseClick(const MouseButtonID& button)
{
// If the mouse is disabled, we do not accept this injection of input
if( !mMouseCursor->getEnabled() )
return false;
// Modify the button mask
mButtonMask |= (1 << button);
// Record the time the click occurred. Useful for generating double clicks.
mTimeOfClick[button] = mTimer->getMilliseconds();
// Create MouseEventArgs, for use with any fired events
MouseEventArgs args(mActiveSheet->getWidgetInFocus());
args.position = mMouseCursor->getPosition();
args.buttonMask = mButtonMask;
args.keyModifiers = mKeyModifiers;
return args.widget->fireEvent(Widget::EVENT_MOUSE_CLICK,args);
}
bool GUIManager::injectMouseDoubleClick(const MouseButtonID& button)
{
// If the mouse is disabled, we do not accept this injection of input
if( !mMouseCursor->getEnabled() )
return false;
// Modify the button mask
mButtonMask |= (1 << button);
// Record the time the click occurred. Useful for generating triple clicks.
mTimeOfDoubleClick[button] = mTimer->getMilliseconds();
// Create MouseEventArgs, for use with any fired events
MouseEventArgs args(mActiveSheet->getWidgetInFocus());
args.position = mMouseCursor->getPosition();
args.buttonMask = mButtonMask;
args.keyModifiers = mKeyModifiers;
return args.widget->fireEvent(Widget::EVENT_MOUSE_CLICK_DOUBLE,args);
}
bool GUIManager::injectMouseTripleClick(const MouseButtonID& button)
{
// If the mouse is disabled, we do not accept this injection of input
if( !mMouseCursor->getEnabled() )
return false;
// Modify the button mask
mButtonMask |= (1 << button);
// Create MouseEventArgs, for use with any fired events
MouseEventArgs args(mActiveSheet->getWidgetInFocus());
args.position = mMouseCursor->getPosition();
args.buttonMask = mButtonMask;
args.keyModifiers = mKeyModifiers;
return mWidgetContainingMouse->fireEvent(Widget::EVENT_MOUSE_CLICK_TRIPLE,args);
}
bool GUIManager::injectMouseMove(const int& xPixelOffset, const int& yPixelOffset)
{
// If the mouse is disabled, we do not accept this injection of input
if( !mMouseCursor->getEnabled() )
return false;
// See if we should be dragging a widget.
if( (mActiveSheet->getWidgetInFocus() == mWidgetUnderMouse) && (mWidgetUnderMouse->getDragable()) )
{
// Dragging, which uses move function, works with pixel values (uninfluenced by parent dimensions!)
mWidgetUnderMouse->drag(xPixelOffset,yPixelOffset);
return true;
}
// Now get the widget the cursor is over.
Widget* w = mActiveSheet->getWidgetAtPoint(args.position);
// Ignore disabled widgets
if(!w->getEnabled())
return false;
// Create MouseEventArgs, for use with any fired events
MouseEventArgs args(w);
args.position = mMouseCursor->getPosition();
args.moveDelta.x = xPixelOffset;
args.moveDelta.y = yPixelOffset;
args.buttonMask = mButtonMask;
args.keyModifiers = mKeyModifiers;
// Create a boolean to track whether or not this injection caused any significant changes.
bool changesMade = false;
// The Widget underneath the mouse cursor has changed.
if( mWidgetUnderMouse != w )
{
if(mWidgetUnderMouse != NULL)
{
changesMade |= mWidgetUnderMouse->fireEvent(Widget::EVENT_MOUSE_LEAVE,args);
}
// Update pointer
mWidgetContainingMouse = w;
if(mWidgetContainingMouse != NULL)
{
changesMade |= mWidgetUnderMouse->fireEvent(Widget::EVENT_MOUSE_ENTER,args);
}
}
// Notify the widget in focus the cursor has moved.
changesMade |= mActiveSheet->getWidgetInFocus()->fireEvent(Widget::EVENT_MOUSE_MOVE,args);
return changesMade;
}
bool GUIManager::injectMousePosition(const int& xPixelPosition, const int& yPixelPosition)
{
// If the mouse is disabled, we do not accept this injection of input
if( !mMouseCursor->getEnabled() )
return false;
Point oldPos = mMouseCursor->getPosition();
// Update cursor's position as seen on the screen.
mMouseCursor->setPosition(xPixelPosition,yPixelPosition);
// Determine the offset and inject a mouse movement input.
return injectMouseMove(xPixelPosition - oldPos.x,yPixelPosition - oldPos.y);
}
Zini
03-04-2008 23:43:29
kungfoomasta
04-04-2008 00:10:14
Zini
04-04-2008 00:35:21
For the Gothic III example, does the window get dismissed after some time, or how does it get hidden/closed? Are you able to click anything outside this window, or is it truly a modal window? Are there cases where you could have 2 of these windows at the same time, or does it seem like one slot is reserved for these always-on-top, input disabled windows? I would like to implement the ability to have 1 modal window visible at a time, which can be configured to allow or disallow outside gui interaction. (clicking a widget outside the button)
For the WoW example, this is a really good example. My thoughts on how to implement this without the focusOnClick functionality would be to add to the onMouseButtonUp handler that would set the focus to the previous focused widget. When you mouse over the button it calls a MOUSE_ENTER callback which stores the currently focused widget, and when clicked, does work and then restores focus back to the previously focused widget.
normal idea of "if you click it, it gains focus"
I want to start focusing on having the code clean and organized. The code base is quite big, and a lot of times I review/update a section of code and forget some specialized base case. I either need to document all special cases with more detail, or have fewer special cases, or both. (You can see the code I posted has a lot of documentation, making it easy to follow)
Thanks again for your input.
kungfoomasta
04-04-2008 01:32:06
Could disable be used to get the behaviour described above?
getWidgetAtPoint(const Point& p, bool ignoreDisabled=true);
kungfoomasta
04-04-2008 05:19:08
bool GUIManager::injectMouseButtonDown(const MouseButtonID& button)
{
// If the mouse is disabled, we do not accept this injection of input
if( !mMouseCursor->getEnabled() )
return false;
// Users can manually inject single/double/triple click input, or have it detected
// from mouse button up/down injections.
if(mGUIManagerDesc.determineClickEvents)
{
// Record time of MouseDown, for single/double/triple click determination
mTimeOfButtonDown[button] = mTimer->getMilliseconds();
// Check if time since last double click is within triple click time
if((mTimeOfButtonDown[button] - mTimeOfDoubleClick[button]) <= mGUIManagerDesc.tripleClickTime)
{
// We have passed the criteria for a triple click injection, but we have to
// make sure the mouse button is going down on the same widget for all clicks.
if(mMouseButtonDown[button] == mWidgetUnderMouseCursor)
return injectMouseTripleClick(button);
}
// Check if time since last click is within double click time
if((mTimeOfButtonDown[button] - mTimeOfClick[button]) <= mGUIManagerDesc.doubleClickTime)
{
// We have passed the criteria for a double click injection, but we have to
// make sure the mouse button is going down on the same widget for both clicks.
if(mMouseButtonDown[button] == mWidgetUnderMouseCursor)
return injectMouseDoubleClick(button);
}
}
// If we make it here, a simple mouse button down has occurred.
// Modify the button mask
mButtonMask |= (1 << button);
// Record that the mouse button went down on this widget
mMouseButtonDown[button] = mWidgetUnderMouseCursor;
// Create MouseEventArgs, for use with any fired events
MouseEventArgs args(mWidgetUnderMouseCursor);
args.position = mMouseCursor->getPosition();
args.buttonMask = mButtonMask;
args.keyModifiers = mKeyModifiers;
// Create a boolean to track whether or not this injection caused any significant changes.
bool changesMade = false;
// Get the Window under the mouse cursor. If it is not the Sheet, but a child Window,
// make sure it has focus. (bring to front)
Window* win = mActiveSheet->findWindowAtPoint(args.position);
if(win != mActiveSheet)
// FOCUS_GAINED and FOCUS_LOST events will be fired if appropriate.
changesMade |= mActiveSheet->focusWindow(win);
Widget* w = win->findWidgetAtPoint(args.position);
// Set focus to the widget the mouse went down on.
// FOCUS_GAINED and FOCUS_LOST events will be fired if appropriate.
changesMade |= mActiveSheet->focusWidget(w);
w->setGrabbed(true);
// Fire EVENT_MOUSE_BUTTON_DOWN event to the widget in focus
changesMade |= w->fireEvent(WIDGET_EVENT_MOUSE_BUTTON_DOWN,args);
return changesMade;
}
bool GUIManager::injectMouseButtonUp(const MouseButtonID& button)
{
// If the mouse is disabled, we do not accept this injection of input
if( !mMouseCursor->getEnabled() )
return false;
// Modify the button mask
mButtonMask &= !(1 << button);
// If the mouse button goes up and is not over the same widget
// the mouse button went down on, disregard this injection.
if( mMouseButtonDown[button] != mWidgetUnderMouseCursor )
return false;
// after this point, we know that the user had mouse button down on this widget, and is now doing mouse button up on the same widget.
// Create MouseEventArgs, for use with any fired events
MouseEventArgs args(mActiveSheet->getWidgetInFocus());
args.position = mMouseCursor->getPosition();
args.buttonMask = mButtonMask;
args.keyModifiers = mKeyModifiers;
// Check if a widget is currently being dragged.
if(args.widget->getGrabbed())
{
args.widget->setGrabbed(false);
// We do not want the widget to receive an EVENT_MOUSE_BUTTON_UP event if we are dropping
// the widget. Think of Diablo II, dragging a potion to your belt. If we sent the mouse
// button up event the potion would be drank as soon as you dropped it into the belt.
return args.widget->fireEvent(WIDGET_EVENT_DROPPED,args);
}
// Create a boolean to track whether or not this injection caused any significant changes.
bool changesMade = false;
// Fire EVENT_MOUSE_BUTTON_UP event
changesMade |= args.widget->fireEvent(WIDGET_EVENT_MOUSE_BUTTON_UP,args);
// Users can manually inject single/double/triple click input, or have it detected
// from mouse button up/down injections.
if(mGUIManagerDesc.determineClickEvents)
{
if((mTimer->getMilliseconds() - mTimeOfButtonDown[button]) <= mGUIManagerDesc.clickTime)
{
changesMade |= injectMouseClick(button);
}
}
return changesMade;
}
bool GUIManager::injectMouseClick(const MouseButtonID& button)
{
// If the mouse is disabled, we do not accept this injection of input
if( !mMouseCursor->getEnabled() )
return false;
// Modify the button mask
mButtonMask |= (1 << button);
// Record the time the click occurred. Useful for generating double clicks.
mTimeOfClick[button] = mTimer->getMilliseconds();
// Create MouseEventArgs, for use with any fired events
MouseEventArgs args(mActiveSheet->getWidgetInFocus());
args.position = mMouseCursor->getPosition();
args.buttonMask = mButtonMask;
args.keyModifiers = mKeyModifiers;
return args.widget->fireEvent(WIDGET_EVENT_MOUSE_CLICK,args);
}
bool GUIManager::injectMouseDoubleClick(const MouseButtonID& button)
{
// If the mouse is disabled, we do not accept this injection of input
if( !mMouseCursor->getEnabled() )
return false;
// Modify the button mask
mButtonMask |= (1 << button);
// Record the time the click occurred. Useful for generating triple clicks.
mTimeOfDoubleClick[button] = mTimer->getMilliseconds();
// Create MouseEventArgs, for use with any fired events
MouseEventArgs args(mActiveSheet->getWidgetInFocus());
args.position = mMouseCursor->getPosition();
args.buttonMask = mButtonMask;
args.keyModifiers = mKeyModifiers;
return args.widget->fireEvent(WIDGET_EVENT_MOUSE_CLICK_DOUBLE,args);
}
bool GUIManager::injectMouseTripleClick(const MouseButtonID& button)
{
// If the mouse is disabled, we do not accept this injection of input
if( !mMouseCursor->getEnabled() )
return false;
// Modify the button mask
mButtonMask |= (1 << button);
// Create MouseEventArgs, for use with any fired events
MouseEventArgs args(mActiveSheet->getWidgetInFocus());
args.position = mMouseCursor->getPosition();
args.buttonMask = mButtonMask;
args.keyModifiers = mKeyModifiers;
return mWidgetUnderMouseCursor->fireEvent(WIDGET_EVENT_MOUSE_CLICK_TRIPLE,args);
}
bool GUIManager::injectMouseMove(const int& xPixelOffset, const int& yPixelOffset)
{
// If the mouse is disabled, we do not accept this injection of input
if( !mMouseCursor->getEnabled() )
return false;
// See if we should be dragging a widget.
if( (mActiveSheet->getWidgetInFocus() == mWidgetUnderMouseCursor) && (mWidgetUnderMouseCursor->getDragable()) )
{
// Dragging, which uses move function, works with pixel values (uninfluenced by parent dimensions!)
mWidgetUnderMouseCursor->drag(xPixelOffset,yPixelOffset);
return true;
}
// Now get the widget the cursor is over.
Widget* w = mActiveSheet->findWidgetAtPoint(mMouseCursor->getPosition());
// Ignore disabled widgets
if(!w->getEnabled())
return false;
// Create MouseEventArgs, for use with any fired events
MouseEventArgs args(w);
args.position = mMouseCursor->getPosition();
args.moveDelta.x = xPixelOffset;
args.moveDelta.y = yPixelOffset;
args.buttonMask = mButtonMask;
args.keyModifiers = mKeyModifiers;
// Create a boolean to track whether or not this injection caused any significant changes.
bool changesMade = false;
// The Widget underneath the mouse cursor has changed.
if( mWidgetUnderMouseCursor != w )
{
if(mWidgetUnderMouseCursor != NULL)
{
changesMade |= mWidgetUnderMouseCursor->fireEvent(WIDGET_EVENT_MOUSE_LEAVE,args);
}
// Update pointer
mWidgetUnderMouseCursor = w;
if(mWidgetUnderMouseCursor != NULL)
{
changesMade |= mWidgetUnderMouseCursor->fireEvent(WIDGET_EVENT_MOUSE_ENTER,args);
}
}
// Notify the widget in focus the cursor has moved.
Widget* focusWidget = mActiveSheet->getWidgetInFocus();
if(focusWidget != NULL)
changesMade |= focusWidget->fireEvent(WIDGET_EVENT_MOUSE_MOVE,args);
return changesMade;
}
bool GUIManager::injectMousePosition(const int& xPixelPosition, const int& yPixelPosition)
{
// If the mouse is disabled, we do not accept this injection of input
if( !mMouseCursor->getEnabled() )
return false;
Point oldPos = mMouseCursor->getPosition();
// Update cursor's position as seen on the screen.
mMouseCursor->setPosition(xPixelPosition,yPixelPosition);
// Determine the offset and inject a mouse movement input.
return injectMouseMove(xPixelPosition - oldPos.x,yPixelPosition - oldPos.y);
}
tp
04-04-2008 06:20:28
Honestly the
normal idea of "if you click it, it gains focus"
is just a very bad idea IMHO.
kungfoomasta
04-04-2008 07:31:24
Why not implement it in the controls that use the keyboard? You click on a text box, the text box mouse button down event gets fired, and the text box tells the GUI system that it wants to obtain the keyboard focus. In the WoW example, the button bar doesn't do this, so nothing happens to the focus. This would also flexibly allow the control itself to determine when it's done with the keyboard (pressing esc etc.)
tp
04-04-2008 08:55:55
This sounds like a decent idea, but what if I was a user and wanted to make a window, such that when its in focus and 'c' is pressed, the window closes? Or any other odd behavior I would want in my game UI. How do I know where to send keyboard input? I could store the last text based widget that was focused, and send all keyboard input to it, but it doesn't allow the window scenario above, or things like pressing a spacebar to hit a button.
Zini
05-04-2008 11:19:41
kungfoomasta
05-04-2008 21:10:05
Zini
05-04-2008 21:29:16
Also when I was making these changes, I realized that I will need to store the last widget that was clicked on, for the purpose of the mouse wheel input. For example if you clicked on window w1, and moused over window w2, and scrolled the mouse wheel, w1 would receive the input.
kungfoomasta
05-04-2008 22:11:37
Zini
05-04-2008 22:55:35
I can post the code later today or tomorrow for anybody who might want to see what I'm doing. I did add in the ability to configure the scrolling, so that you can decide if the last clicked widget gets wheel events, or the widget under the cursor gets it.
The revision of the Input injection system is just one of the many things I need to implement before letting people use the new code. I'm also revising the rendering and how QuickGUI class instances are created, and it will be a wihle longer before its considered usable.