Sign in to follow this  
  • entries
    570
  • comments
    2427
  • views
    215649

Untitled

Sign in to follow this  
Mushu

41 views

  • Random Observation of the Day!
    Every system has its own inherent problems.


Okay, since JM asked about the GUI system I'm using I've decided to go ahead and write up a little mini-documentation on it. Partially because its a good habit which I normally don't have, partially because I honestly don't remember how most of it works (beyond its usage) so going back down into the bowels will probably be fun.

You can get a zipped folder with all the source (including the crazy BSOD control!) here. Just create a new project and slam all those files in there and you should get the Washu Watcher dialogue box thingy. Whoo.

Anyway, on with the internals.

The GUI controls are based upon three (or, arguably 2) core interfaces (which aren't technically interfaces as they provide functionality in themselves) - IComponent, IContainer and IWindow.

IComponent is the base class for all other components. It has a set of dimensions and core functionality, including listener lists (implemented as vectors, though this can easily be changed by changing the listtype typedefs).

Listener lists work essentially the same in each core interface - the instance of the component maintains a list of pointers to these listener interfaces (described later). Upon receiving an event (via process*Event(..) which is called internally) it passes the event down to its listeners based on some focus rules and such (again, detailed later).

IComponent methods -
  • void addKeyListener( KeyListener* );
    void addMouseListener( MouseListener* );
    void addMouseMoveListener( MouseMoveListener* );

    void delKeyListener( KeyListener* );
    void delMouseListener( MouseListener* );
    void delMouseMoveListener( MouseMoveListener* );


    These functions allow you to manipulate the listener lists of that component. A single listener can implement multiple interfaces and be assigned to multiple controls. A current problem with this system is that the event structures sent to the listeners do not have a hierarchical history of their origins. Although I've debated adding this in, but figure if you need that then you should be using a control which implements an ActionListener (which doesn't provide this functionality yet, but I'll probably add it in).

  • virtual void draw( int rel_x, int rel_y ) = 0;

    This is the drawing function. The values of rel_x and rel_y are the relative offsets for the control's coordinate system.

    Example - if control A has coordinates (10,10) and is a child of control B (with coordinates (20,20)) then control A's absolute coordinates will be (30,30). When A's draw function gets called, the value of rel_x and rel_y will be (20,20), assuming that B is a top-level control.

    Basically, when implementing this function you should use the following template -

    virtual void draw( int rel_x, int rel_y ) {
    int x = getX() + rel_x;
    int y = getY() + rel_y;

    ...
    }


    And use x and y to do all your drawing operations.

  • Rect& getDim();
    int getX();
    int getY();
    int getW();
    int getH();


    These are getter functions for the control's dimensions. Likewise, the setter functions -

    void setDim( Rect );
    void setDim( int x, int y, int w, int h );
    void setPos( int x, int y );
    void setSize( int w, int h );


    For absolute movement relative to the control's coordinate system. For movement relative to the control's current position use

    void move( int x, int y );

There are other functions which are basically just helper functions used internally, so I'm not really going to bother talking about them.

Every IComponent is a child of another control (excepting the "master" control class GUI). IContainer derives from IComponent and maintains lists (again, implemented as a vector by default) of pointer-to-controls.

IContainer overrides IComponent's process*Event functions in order to pass them to its child containers (after first passing the event to its own listeners).

A note on focus - a KeyEvent will only be passed to controls who have focus. Focus is obtained by clicking on that control, and lost when another control gains focus. Additionally, if the control has the zOrdered flag set, when it recieves a mouse click it will be brought to the front.

Anyway, on with the API

IContainer methods -
  • void addComponent( IComponent* );
    void delComponent( IComponent* );


    These are the functions for managing the container's control list. Note that the controls are not managed. That means if you remove a control from the list it its memory is not freed. Additionally, there are no ways to retrieve a component from the list, so if you want to dynamically remove them you'll need to store your pointers to them somewhere.

    In my example usage, I just allocated everything on the stack, which is much safer than using the new operator. Best bet is probably to use the RAII idiom to ensure that everything gets freed properly.

  • void drawChildren( int rel_x, int rel_y );

    This is a nifty helper function - when writing the draw function for a new container class the first thing you should do it call drawChildren. You should pass the rel_x and rel_y unaltered as drawChildren will automatically apply the coordinate system changes. It will also skip the drawing (cull) components which are completely hidden.

  • void moveToBack( IComponent* );
    void moveToFront( IComponent* );


    Some helper functions used internally to reorder the control list. Note that "ToBack" and "ToFront" refer to the position in the list itself, not to the drawing order. drawChildren draws from the front->back, so moveToBack will cause the component to be drawn on top.

    Events are sent out to child components from back->front, so as not to cause a little problem. I in fact just noticed this little quirk - I might end up renaming these functions for intuitivity reasons if it presents a problem. Right now their use is exclusively internal, so it doesn't seem like a huge problem. I expect Washu to be raving "OMG REFACTOR!~`1" right about now.


That's basically it for the 2 primary components of the system. From these you can basically derive about anything you could want, though there are a couple more base classes (derived from these) to speed up everything.

And I'm tired of typing all this up so I'm going to take a break. I'll try to get through the following interfaces in my next entry -

IWindow, IButton, ITextBox, *Listener, *Event as well as discussing where exactly the draw functions are implemented (at the waaay bottom of the hierarchy) and the Defaultstyle policy class of the pre-made drawable classes.

Har.

As a side note into my personal life, last night was pretty boring. I had a couple of beers, and a couple shots of some 196 proof shit. Then I mixed some of the 196 (no idea what to call it, it was some sketchy Russian stuff) with some orange juice. The flavor of the orange juice was waay overpowered.

So yeah. Didn't manage to get drunk, but that crap gave me a tummy ache. Yuck. Should have taken Pouya's advice: "Don't drink it if it burns to smell it."
Sign in to follow this  


1 Comment


Recommended Comments

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now