It is currently Tue Jul 25, 2017 1:47 am

All times are UTC - 5 hours




 Page 1 of 1 [ 13 posts ] 
Author Message
 Post subject: Handle mouse input in gui
PostPosted: Wed Jun 24, 2015 12:34 am 

Joined: Mon Mar 24, 2014 2:25 pm
Posts: 69
Very much liked adding this std::queue to the Screen tree.

I've started updating the EngineLoop/Game class to handle input to a GUI, for instance moving a chat / inventory around. So far I can drag a Layout around the screen by intercepting mouse clicks and mousemoves via the below code. It works well, but I've unfortunately hacked alot of this into the game code which is a bit messy. I commented the code up in attempt to help anyone that wants to use it, although I know Marek will have a better implementation soon ;)

void FGame::HandleGUIInput(glm::vec2& InMousePosition, WPARAM& InState, bool InIsMoving)
{
   // TODO: Loop through all GUILayouts and determine if click is within BoundingRect
   FGUILayoutAbsolute* GUI = dynamic_cast<FGUILayoutAbsolute*>(AssetStorage->GetGUIElement("GUI1", GUI_RENDERABLE));
   FBoundingRect BoundingRect = GUI->GetBoundingRect();

   // Set last click position and difference between intercepted FGUILayout top left corner
   // DeltaX, DeltaY allow smooth moving; prevents rebounding of final mouse release position as new top left corner
   // Always called independent of single click or holding button
   if ((InState & MK_LBUTTON) != 0 && InIsMoving == false) {
      ClickPosition = InMousePosition;
      DeltaX = ClickPosition.x - BoundingRect.TopLeft.x;
      DeltaY = ClickPosition.y - BoundingRect.TopLeft.y;
   }

   if (InIsMoving == false) {
      if (InMousePosition.x > BoundingRect.TopLeft.x && InMousePosition.x < BoundingRect.BottomRight.x
         && InMousePosition.y > BoundingRect.TopLeft.y && InMousePosition.y < BoundingRect.BottomRight.y) {
         // DEBUG ONLY: Visual indication we single clicked a GUI element, did not drag it
         GUI->SetColor(glm::vec4(0.1f, 0.1f, 0.3f, 1.0f));
      }
   } else {
      // Allows dragging and updates per frame
      // TODO: Fix, extremely fast drag moves mouse outside BoundingRect before the update to position; need to set flag of dragging on GUILayout
      if (InMousePosition.x > BoundingRect.TopLeft.x && InMousePosition.x < BoundingRect.BottomRight.x
         && InMousePosition.y > BoundingRect.TopLeft.y && InMousePosition.y < BoundingRect.BottomRight.y) {
         if ((InState & MK_LBUTTON) != 0) {
            GUI->SetOffsetFromParent(glm::vec2(InMousePosition.x - DeltaX, InMousePosition.y - DeltaY));
         }
      }
   }
}


The engine architecture part is where I continue to struggle, especially with passing around data appropriately. Would you recommend:
  • A GUI manager singleton that accesses the AssetStorage for the unordered_map of GUIAssets
  • Does this belong in AssetStorage since it already has the unordered_map
  • Or any other recommended location for the code?

Any advice is greatly appreciated.

Scott


Offline
 Profile  
 
 Post subject: Re: Handle mouse input in gui
PostPosted: Wed Jun 24, 2015 7:27 am 
Site Admin

Joined: Sun Feb 11, 2007 8:59 am
Posts: 1102
Location: Ontario Canada
Mebourne wrote:
The engine architecture part is where I continue to struggle, especially with passing around data appropriately. Would you recommend:
  • A GUI manager singleton that accesses the AssetStorage for the unordered_map of GUIAssets
  • Does this belong in AssetStorage since it already has the unordered_map
  • Or any other recommended location for the code?


I'm not sure I understand what you are trying to do when you say "passing around data". What data do you want to pass?


Offline
 Profile  
 
 Post subject: Re: Handle mouse input in gui
PostPosted: Thu Jun 25, 2015 11:32 pm 

Joined: Mon Mar 24, 2014 2:25 pm
Posts: 69
Hey Marek,

The original code I posted was just looking for one known GUI, but I wanted to improve it to test all layouts on the screen (ie. movable windows in a game, inventory, chat, etc.). After some work this evening the following code seems to work to allow independent dragging of GUILayouts around the screen, but has problems listed below.

Updated Code:
void FGame::HandleGUIInput(glm::vec2& InMousePosition, WPARAM& InState, bool InIsMoving) {
   
   if ((InState & MK_LBUTTON) == 0)
      return;

   for (auto GUI : AssetStorage->GetGUIRenderableAssets()) {
      FGUILayout* CurrentGUI = dynamic_cast<FGUILayout*>(AssetStorage->GetGUIElement(GUI.first, GUI_RENDERABLE));
      if (CurrentGUI != nullptr) {
         FBoundingRect BoundingRect = CurrentGUI->GetBoundingRect();

         if (InMousePosition.x > BoundingRect.TopLeft.x && InMousePosition.x < BoundingRect.BottomRight.x
            && InMousePosition.y > BoundingRect.TopLeft.y && InMousePosition.y < BoundingRect.BottomRight.y) {

            // Set last click position and difference between intercepted FGUILayout top left corner
            // DeltaX, DeltaY allow smooth moving; prevents rebounding of final mouse release position as new top left corner
            // Always called independent of single click or holding button
            if ((InState & MK_LBUTTON) != 0 && InIsMoving == false) {
               ClickPosition = InMousePosition;
               DeltaX = ClickPosition.x - BoundingRect.TopLeft.x;
               DeltaY = ClickPosition.y - BoundingRect.TopLeft.y;
            }

            if (InIsMoving == false) {
               // DEBUG ONLY: Visual indication we single clicked a GUI element, did not drag it
               CurrentGUI->SetColor(glm::vec4(0.1f, 0.1f, 0.3f, 1.0f));
               break;
            }
            else {
               // Allows dragging and updates per frame
               // TODO: Fix, extremely fast drag moves mouse outside BoundingRect before the update to position; need to set flag of dragging on GUILayout
               if ((InState & MK_LBUTTON) != 0) {
                  CurrentGUI->SetOffsetFromParent(glm::vec2(InMousePosition.x - DeltaX, InMousePosition.y - DeltaY));
               }
               break;
            }
         }
      }
   }   
}


Current Problems:
1) Dragging Layout over other Layout now selects Layout underneath since we are within it's bounding rectangle
2) Moving mouse too fast if not dragging from center of object has potential to set mouse outside bounding rectangle and lose it's notice of dragging

Questions
1) First is the method I came up with for looping the GUIs in AssetStorage good or do you have a recommended faster/better approach?
2) To prevent the two issues noted above, I think it would be beneficial to add a "being moved/clicked" state flag to the layout that when clicked the HandleGUIInput only updates state if this is true, so we can't pickup a GUI underneath by dragging over. Do you have a recommended different approach?


Offline
 Profile  
 
 Post subject: Re: Handle mouse input in gui
PostPosted: Thu Jun 25, 2015 11:58 pm 

Joined: Mon Mar 24, 2014 2:25 pm
Posts: 69
Alright the following code solves the two problems I had earlier. I went ahead and added the flag I mentioned in the previous post and the Game class now holds a pointer to a GUILayout that is currently interacted with. I'm pretty happy with this solution so far. There are alot more cases to work through went interacting with GUIs, but it's a decent start.

I'd appreciate any advice, updates, feedback anyone has for making this better:

Upon any mouse interaction we:
1) Clear our "InteractedGUI" if left mouse isn't pressed and return
2) Test if mouse is moving and if "InteractedGUI" is mapped; if so update the offset from parent screen; and return
3) If we make it this far loop through GUIRenderableAssets and test for GUILayout dynamic_cast and if inside a bounding rect, set "InteractedGUI"

void FGame::HandleGUIInput(glm::vec2& InMousePosition, WPARAM& InState, bool InIsMoving) {
   
   if ((InState & MK_LBUTTON) == 0) {
      InteractedGUI = nullptr;
      return;
   }

   if (InIsMoving == true && InteractedGUI != nullptr) {
      if ((InState & MK_LBUTTON) != 0)
         // Allows dragging and updates per frame
         InteractedGUI->SetOffsetFromParent(glm::vec2(InMousePosition.x - DeltaX, InMousePosition.y - DeltaY));
      return;
   }

   for (auto GUI : AssetStorage->GetGUIRenderableAssets()) {
      FGUILayout* CurrentGUI = dynamic_cast<FGUILayout*>(AssetStorage->GetGUIElement(GUI.first, GUI_RENDERABLE));
      if (CurrentGUI != nullptr) {
         FBoundingRect BoundingRect = CurrentGUI->GetBoundingRect();

         if (InMousePosition.x > BoundingRect.TopLeft.x && InMousePosition.x < BoundingRect.BottomRight.x
            && InMousePosition.y > BoundingRect.TopLeft.y && InMousePosition.y < BoundingRect.BottomRight.y) {

            // Set last click position and difference between intercepted FGUILayout top left corner
            // DeltaX, DeltaY allow smooth moving; prevents rebounding of final mouse release position as new top left corner
            // Always called independent of single click or holding button
            if ((InState & MK_LBUTTON) != 0 && InIsMoving == false) {
               ClickPosition = InMousePosition;
               DeltaX = ClickPosition.x - BoundingRect.TopLeft.x;
               DeltaY = ClickPosition.y - BoundingRect.TopLeft.y;
               InteractedGUI = CurrentGUI;
            }

            if (InIsMoving == false) {
               // DEBUG ONLY: Visual indication we single clicked a GUI element or started the dragging process
               CurrentGUI->SetColor(glm::vec4(0.1f, 0.1f, 0.3f, 1.0f));
               break;
            }
         }
      }
   }   
}


Newest Issue to Work Through:
1) If two layouts are overlapped after clicking a third layout they are both back at 0 z-order base with their text at 1. (Note: I set priority of current interacted object high and then return to normal if a new layout is interacted with). The following picture shows the issue. The solution would be to have every GUIlayout and it's children get it's own priority and each time a layout is clicked it would go to the top and then the queue gets resorted in backwards order, but I can't figure out how to map all the children properly as well.

Image


Offline
 Profile  
 
 Post subject: Re: Handle mouse input in gui
PostPosted: Fri Jun 26, 2015 7:49 am 
Site Admin

Joined: Sun Feb 11, 2007 8:59 am
Posts: 1102
Location: Ontario Canada
are you use the changePriority() function to bump the priority level of the object that you are moving?


Offline
 Profile  
 
 Post subject: Re: Handle mouse input in gui
PostPosted: Fri Jun 26, 2015 9:00 am 

Joined: Mon Mar 24, 2014 2:25 pm
Posts: 69
Hey Marek, yes I was setting the current interacting GUI using prioritychange(5) as a test case. I wasn't seeing a clean way to de-increment by one, so I used prioritychange(0) on the previously interacted GUI to move it's priority back down. It worked when interacting between the two objects you see on top of each other in the picture, but when selecting the layout just to the right, both the previous GUIs were back to baseline 0 priority with the text at 1.

Any thoughts?


Offline
 Profile  
 
 Post subject: Re: Handle mouse input in gui
PostPosted: Fri Jun 26, 2015 4:20 pm 

Joined: Sat Aug 16, 2008 7:58 am
Posts: 448
I don't know if it would help but since one gui object can be a child to another I would think that using this hierarchy would help in the repositioning of the elements. Let's say you have a Layout Grid object and it contains other Gui Elements: if you were to use the mouse to move this object all of its children would move with it accordingly. The other case would be if you were to move a child gui element then it would be bounded to the limits of the size of its parent meaning you wouldn't be able to move it outside of its parents. Do either of these while keeping the priority the same may simplify things. Another thing that could be added to this functionality would be to have a console command to save these new positions and this would update the gui text file that is parsed; so the next time you run the program these new coordinate positions are saved, however this might involve a little more work since each GUI class would need a new member variable as a flag to indicate if the objects were moved and you want to save their new positions for writing out to a file.


Offline
 Profile  
 
 Post subject: Re: Handle mouse input in gui
PostPosted: Fri Jun 26, 2015 8:32 pm 

Joined: Mon Mar 24, 2014 2:25 pm
Posts: 69
Hey Skilz,

I don't know if it would help but since one gui object can be a child to another I would think that using this hierarchy would help in the repositioning of the elements. Let's say you have a Layout Grid object and it contains other Gui Elements: if you were to use the mouse to move this object all of its children would move with it accordingly. The other case would be if you were to move a child gui element then it would be bounded to the limits of the size of its parent meaning you wouldn't be able to move it outside of its parents.


It already handles the children elements when moving a layout since they are based off the parent layout. Currently the code only dynamic_casts to a FGUILayout, as I'm using those as "windows" in the engine so mouse interaction only works at a parent layout and I don't have the issue with an inner GUI moving outside the bounds at the moment. The problem I'm really having is the priority rendering from the BatchManager. I can move two objects around and layer them on top of each other no problem as the last handled GUI is always higher priority. Where it gets tricky is leaving two on top of each other and then moving on to a third. I need to figure out a way to decrement a parent layout and it's children instead of fixing them right back to 0 "changepriority"

Another thing that could be added to this functionality would be to have a console command to save these new positions and this would update the gui text file that is parsed; so the next time you run the program these new coordinate positions are saved, however this might involve a little more work since each GUI class would need a new member variable as a flag to indicate if the objects were moved and you want to save their new positions for writing out to a file.


Funny you say this, just got this implemented this evening. On close down of the engine, the final GUI positions are saved to a file for easy reading back. I should have it parse back to the original .gui file, so that will be the next upgrade.


Offline
 Profile  
 
 Post subject: Re: Handle mouse input in gui
PostPosted: Sat Jun 27, 2015 4:32 am 
Site Admin

Joined: Sun Feb 11, 2007 8:59 am
Posts: 1102
Location: Ontario Canada
Mebourne wrote:
I can move two objects around and layer them on top of each other no problem as the last handled GUI is always higher priority. Where it gets tricky is leaving two on top of each other and then moving on to a third. I need to figure out a way to decrement a parent layout and it's children instead of fixing them right back to 0 "changepriority"


I'm not sure I understand what you mean. Could you show me an example of what you are seeing, and what you are expecting.

If you want the priority to be lower, you can pass a negative value into changePriority() to move it earlier in the queue.


Offline
 Profile  
 
 Post subject: Re: Handle mouse input in gui
PostPosted: Sat Jun 27, 2015 5:35 pm 

Joined: Mon Mar 24, 2014 2:25 pm
Posts: 69
Hey everyone, sorry for the confusion and I appreciate everyone's help. Hopefully the images of my test case below clear up the issue I'm having.

Here is the related part of the code:
if (CurrentGUI != PreviousGUI && PreviousGUI != nullptr)
PreviousGUI->ChangePriority(0);

InteractedGUI = CurrentGUI;
InteractedGUI->ChangePriority(2);
PreviousGUI = InteractedGUI;


Step 1: Move a layout over another, the current interacted layout is the highest priority as desired (no issues)
Image

Step 2: Click the layout underneath and it now has higher priority (no issues)
Image

Step 3: Issue: The two previous layouts are still on top of each other and now click a third different layout (I clicked the one just to the right) and the two previous GUIs have their text back at the same priority.
Image

Changepriority with a negative number doesn't seem to work as intended, so right now I'm just setting to zero which I know causes the text problem. I'm thinking of storing a vector of "PreviousGUI" and interating through to reduce their priorities. I feel like this solution will work but defeats the whole purposes of the batchmanager and reducing draw calls.


Offline
 Profile  
 
 Post subject: Re: Handle mouse input in gui
PostPosted: Sat Jun 27, 2015 7:16 pm 
Site Admin

Joined: Sun Feb 11, 2007 8:59 am
Posts: 1102
Location: Ontario Canada
have a look at the Gui Objects output in the console, and you'll see why you are getting the results on the screen. Both Absolute layouts are at the same priority level, so the green one is rendered first and then the blue one is rendered next (on top of the green on). The text in the green box is one level higher, but so is the text in the blue box. They both have the same priority.

One way you could fix this is to bump up the priority level of all the layouts in the second row. If you are going to be manipulating gui objects using the mouse, perhaps you should add an option to bump the priority levels using some other control as well (+/- on the keyboard?).

I don't know why you would want to create a layout such as what you show, but if that is what you want then you'd need to make the priority level correct to make things appear in the correct z order.

Another way you could handle this is to do the following. When you click and drag a gui, remove it from the GuiScreen completely. That way it won't be in the default render queue. You will then need to add a bit of extra code in Game::render to render the gui that you are dragging so that you can still see it. Then when you let it go, put it back into the GuiScreen. Since you will be adding it to the Screen, it will be at the end of the list of children, so when it gets rendered it will appear on top of all the other gui objects.

There are lots of possibilities when implementing drag and drop operations.


Offline
 Profile  
 
 Post subject: Re: Handle mouse input in gui
PostPosted: Sat Jun 27, 2015 9:47 pm 

Joined: Mon Mar 24, 2014 2:25 pm
Posts: 69
Quote:
have a look at the Gui Objects output in the console, and you'll see why you are getting the results on the screen. Both Absolute layouts are at the same priority level, so the green one is rendered first and then the blue one is rendered next (on top of the green on). The text in the green box is one level higher, but so is the text in the blue box. They both have the same priority.

Yeah both are set back to 0 with setting my prioritychange to 0 as I couldn't figure another solution.

I don't know why you would want to create a layout such as what you show, but if that is what you want then you'd need to make the priority level correct to make things appear in the correct z order.

This is the use case I'm thinking of from WurmOnline, where multiple windows including skills, inventory etc are all stacked on top of each other.
Image

Another way you could handle this is to do the following. When you click and drag a gui, remove it from the GuiScreen completely. That way it won't be in the default render queue. You will then need to add a bit of extra code in Game::render to render the gui that you are dragging so that you can still see it. Then when you let it go, put it back into the GuiScreen. Since you will be adding it to the Screen, it will be at the end of the list of children, so when it gets rendered it will appear on top of all the other gui objects.

I really like this idea and I think it will work well. I'll try to implement it tonight.

Appreciate everyone's input.


Offline
 Profile  
 
 Post subject: Re: Handle mouse input in gui
PostPosted: Tue Jun 30, 2015 7:47 am 
Site Admin

Joined: Sun Feb 11, 2007 8:59 am
Posts: 1102
Location: Ontario Canada
If you are going to be creating GUI windows such as those in your screen shot then you should really create a new class to do that. This class would respond to mouse clicks and perform the appropriate operations to move when they are being dragged. Additionally, I would then also create a window manager class that handles the z-ordering automatically so that the windows always appear in the correct order.


Offline
 Profile  
 
Display posts from previous:  Sort by  
 Page 1 of 1 [ 13 posts ] 

All times are UTC - 5 hours


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Jump to:  

cron