Advertisement

Creating a GUI

Started by September 19, 2021 06:49 PM
6 comments, last by theo27 3 years, 2 months ago

Hello,

I'm writing a simple GUI with SDL (1.2).

It consists of differents classes to represent different widgets (frame, button, label...). Each class extends an abstract base class GUIObject.

Each widget has a Render method and methods for event handling : MouseButtonDown, MouseButtonUp

Pseudo-code :

class GUIObject :
   //position
   Left: int
   Top: int
   Width: int
   Height: int
   function IsIn(X, Y) : Boolean //checks if the mouse cursor is on the component
   //abstract methogs
   procedure Render()
   procedure MouseButtonDown(Button, X, Y)
   procedure MouseButtonUp(Button, X, Y)
   procedure MouseMoved(Button, X, Y, XRel, YRel)

GUIContainer is an element that can maintain a list of child elements (e.g. a panel, a window, a dialog...) :

class GUIContainer
   Objects[]
   Add(Object)
   procedure MouseButtonDown(Button, X, Y)
   procedure MouseButtonUp(Button, X, Y)
   procedure MouseMoved(Button, X, Y, XRel, YRel)

For event handling I hesitate between two solutions :

1) GUIContainer iterates through each of its child element. GUIContainer is responsible for checking the mouse position and the state of the child component.

class GUIContainer extends GUIObject :
   procedure MouseButtonDown(Button, X, Y)
       for each Object in Self.Objects :
           if Object.IsVisible and Object.IsIn(X, Y) and Object.IsEnabled then
               Object.MouseButtonDown(Button, X, Y)
               Break // Exit loop

2) GUIContainer simply passes the event to all of its children. The child element is responsible for checking its state and the mouse position.

class GUIContainer :
   procedure MouseButtonDown(Button, X, Y)
       for each Object in Self.Objects :
           Object.MouseButtonDown(Button, X, Y)
class GUIButton extends GUIObject :
   procedure MouseButtonDown(Button, X, Y)
       if Visible and IsIn(X, Y) then
   //...

According to you, what is the best solution ?

The software said "Requires Windows98, Win2000, or better." So I installed Linux.

From my research for our UI framework, neither of them. If you want to speed up your UI input handling, you should use a spatial positioning data structure and register every UI element which is able to receive input. Then when input is processed, you can make a quiet fast lookup into that tree and determine the cluster of UI elements which might process the input. This is at least our plan to speed things up.

I also prefer data driven UI over inheritance, even if the later one is more easy to implement, data driven has the advantage that rendering and layouting could be solved from a system and so you can have faster iteration because your UI is just data which don't need recompilation

Advertisement

@shaarigan I'm currently creating a small UI for two projects. One of them only needs a window with text inside and a scroll bar. The other needs drag&drop and connect UI elements together with lines. I'm using data driven UI to define each entity (button, window, text, ….) and entity hierarchy to link them.
I have a question about how you implement the drag&drop of a window with different entities inside,
- does the parent window send events to their children?
- or, you define all together in one entity (window+scroll bar+button+text)?

None

@wintermute Your container can select the affected child based on its covered area and the position in the event.

I have no idea what you want to do with the mouse up/down/moved methods, what makes you think I don't move the mouse at the same time? (Mouse down, move to other widget, mouse up.) In my view, mouse clicks and moves are window things rather than widget things.

Also, you realize the application rather than the widgets have the data you want to display right? (That is, do you really want to copy data between widgets and application all the time?)

Gotanod said:
I have a question about how you implement the drag&drop of a window with different entities inside, - does the parent window send events to their children? - or, you define all together in one entity (window+scroll bar+button+text)?

This is something WIP at the moment but what I can tell is that we have a root element per tree, which is a window. But this is taken into account only if we need to determine the back color for example. That tree is computed into different trees, a layout tree, a render tree which contains elements which have to be drawn only and the action tree which contains elements which have interaction behavior. Whenever an input message arrives from the OS, the tree action tree is called for the position given in the input message and all elements, ordered by z-index are returned and asked to handle the input from top to bottom

Shaarigan said:
This is something WIP at the moment but what I can tell is that we have a root element per tree, which is a window. But this is taken into account only if we need to determine the back color for example. That tree is computed into different trees, a layout tree, a render tree which contains elements which have to be drawn only and the action tree which contains elements which have interaction behavior. Whenever an input message arrives from the OS, the tree action tree is called for the position given in the input message and all elements, ordered by z-index are returned and asked to handle the input from top to bottom

Thanks for the answer. At the moment, I have used the hierarchy (tree) for action events (drag, click, …) and for drawing. My root element is the System itself.

None

Advertisement

Wintermute2 said:

Hello,

I'm writing a simple GUI with SDL (1.2).

According to you, what is the best solution ?

For simple GUI's it doesn't matter what you do. If you ever have overlapping GUI elements, then you may want option 1) and let the container work out which element is being hit, based on the element which is ‘on top’ .

For the actual mouse / keyboard events I've always used message passing, which is how Windows works (WM_MOUSEMOVE etc). So you might have something like this (pseudo code).
Makes testing very easy, because you can just hammer your UI with random mouse / keyboard events in an attempt to break it.


while (SDL_PollEvent(&test_event)) 
{
     Read SDL mouse / keyboard event... convert to our own custom message format 
     Gui->ProcessMessage( message );   // gui could be a container
      

}
// then inside you GUIContainer 
GUIContainer :: ProcessMessage
{ 
	switch (Msg->CommandID)
    {
        case LEFT_BUTTON_DOWN:
         if ( PointInsideControl ( this ) == false)
           return;
         else
         {
            // pass the message on to child objects
         }
          
    }      
}

If you ever end up with the ui getting bigger, worth looking at topics such as composition vs inheritance. For widget layouting, Facebook Yoga is very good https://yogalayout.com/

This topic is closed to new replies.

Advertisement