How can I connect items in my inventory with GUI items

Started by
5 comments, last by speciesUnknown 13 years, 9 months ago
Hi,

My GUI uses callbacks to deal with widgets being clicked or otherwise handled, which ultimately ends up in functions like this:

	void ButtonsTest::butnCancel_onClick(GUI::Event&)	{		exit();	}


My inventory has a list of items, and I am thinking of using the MVC architecture, so the callbacks would be to a view which would then call operations on a controller. The question is, how do I associate items in the inventory with GUI widgets?

The other issue is that the number of items in the inventory will change frequently, so how should I represent each item? Should I create a new ImageBox whenever a new item is added? Or should I pool them?

I've also considered not using a widget per item at all, but rather, having a single widget for each item list. This widget could internally manage its list of items, and allow drag / drop. Here is an image of what the inventory screen currently looks like:


Click to view in full


The idea is that I want to drag an item from the item list (e.g. item 1) into a weapon slot (if its a weapon), and the radio buttons are used to filter down to specific item types.

The area at the bottom right is context sensitive, with different buttons appearing when a user clicks at item in the list.
Don't thank me, thank the moon's gravitation pull! Post in My Journal and help me to not procrastinate!
Advertisement
The single widget thing seems to be a good idea.
And I don't really understand the question. You use callbacks, that means a callback can do anything you want. You can associate anything with it, any behaviour.
Maybe it won't be the neatest solution ever, but I (for one) wouldn't worry about it too much.

Maybe you could have a separate inventory class (or whatever), and the GUI callbacks would just call the inventory callbacks. Maybe you could add some more information about the inventory design. But I wouldn't worry much, even if this solution would end up with a huge switch statement.

Okay, I'm a C guy, so maybe it's not so simple in C++.
IMO you would only lose flexibility when not having items as widgets. If they are widgets, you have unified calls for drawing, drag&drop, input handling, etc.; just make sure that the concept of a "widget" doesn't become a CPU-intensive monstrosity.

List views are usually handled something like this:

  1. Determine visible items from viewport and backing model (gives a list of visible items with data relevant to positioning, item sizes, etc.)

  2. Draw all visible items to the list view's "content" viewport (determined by styling elements such as borders, scroll controls, etc.)

  3. Draw view styling elements (done last to ensure overlay effects work properly)



A similar thing goes for input handling. If the user clicks inside the list view, and doesn't hit a control element, the list determines visibility and positioning of the contents, and hands off handling to the child widget.
Quote:Original post by Shadowdancer
IMO you would only lose flexibility when not having items as widgets. If they are widgets, you have unified calls for drawing, drag&drop, input handling, etc.; just make sure that the concept of a "widget" doesn't become a CPU-intensive monstrosity.

List views are usually handled something like this:

  1. Determine visible items from viewport and backing model (gives a list of visible items with data relevant to positioning, item sizes, etc.)

  2. Draw all visible items to the list view's "content" viewport (determined by styling elements such as borders, scroll controls, etc.)

  3. Draw view styling elements (done last to ensure overlay effects work properly)



A similar thing goes for input handling. If the user clicks inside the list view, and doesn't hit a control element, the list determines visibility and positioning of the contents, and hands off handling to the child widget.


It's step 1 that is the problem - how do I do the following:
a) Associate each widget with the inventory item it represents? It's already easy to get callbacks whenever something happens to a GUI widget, but knowing which inventory item it is associated with is harder.
b) Create and destroy widgets when items get added or removed from the inventory. Should I instead use an "object pool" of widgets, or whenever the inventory screen is displayed, if the inventory has changed, recreate GUI widgets for each item? STALKER games do the latter, but there is always a noticeble pause when you open the inventory.
Don't thank me, thank the moon's gravitation pull! Post in My Journal and help me to not procrastinate!
That's the way I do it in my game:
All widges are predefined and fix ! I.e. a listbox has only X elements which are visible, so you predefine X list box entries. When I don't use a widget, I only hide it. In some situation I have a pool of X widgets when the number of widgets changes dynamically (i.e. visible objects on the map).

Then I have an updateWidges methods, which fills all widgets with game related data (set icon id, text etc, scrollbar position). I call the updateWidgets method when I open the dialog or when an event changes the content.
Each dialog has an event handler (pressed button, drag'n'drop) and an optional update method, which updates parts of dialog permanently (i.e. a map where you see moving objects).

When I need to link widgets with game objects, I use either the widget index (widget.index + dialog.inventory_offset = item_slot_index) or a map (map widget id to object).
The problem here is in the details, and without knowing the exact way you bind your GUI together it's hard to give a decent answer.

If the problem is passing some sort of context through to associate a given widget with a given game item, then you can either do it explicitly or implicitly.

Explicitly - the extra data can form part of the interface.

- Example 1: have an arbitrary 'tag' field on every Widget. Your code can use this for whatever you like, but in this case, set the tag to the item ID. When you get the callback saying the image has been clicked, you can read the tag from the widget to see which item it corresponds to. (If the source widget isn't in your GUI::Event, maybe it needs changing so that it is.)

- Example 2: bake it into the callback itself. Using boost::bind you can transform a function that takes a GUI::Event and an integer into one that looks like it takes just a a GUI::Event, by providing that integer yourself at creation time. So your callbacks would look slightly different, but they would get the item ID passed directly in.

Implicitly - the extra data can be divined from the context.

- Example: You have 16 inventory slots. Create 16 widgets and bind them to 16 callbacks. (Probably thin wrappers for a single callback which takes a number from 1-16, or use the boost::bind thing above to point them all at a single callback.) The callback knows about your inventory and can look up which item is which from that 1-16.

Note that I've assumed the callback knows about your game data. It doesn't matter if that is not the case, due to separation of concerns, MVC, etc. The callback has the context now, whether it understands it or not - it can just forward that to the controller and from there to the model, where it gets acted upon accordingly.
Quote:Original post by Kylotan
*snip*

- Example 1: have an arbitrary 'tag' field on every Widget. Your code can use this for whatever you like, but in this case, set the tag to the item ID. When you get the callback saying the image has been clicked, you can read the tag from the widget to see which item it corresponds to. (If the source widget isn't in your GUI::Event, maybe it needs changing so that it is.)

*snip*



Having read some ideas, I think the best option is to give individual widgets the data for linking them to inventory items. My inventory uses int's for item ID's so this should be relatively easy. My events already contain the widget which originated the event (bubbling can make this more complex but for now, using the originator is simple and functional)

I think I'll take a leaf out of Havok's book, where all rigid bodies in Havok can have any number of integer pairs, and you call hkpRigidBody::addProperty(int, int). I'll store these internally with a map<int,int>.

As for which widget I use to contain the system, I'll use an object pool inside my list widget. Drag and drop works through avatars, rather than through moving the actual widget, which is advantageous because I don't need to fart about with moving widgets from one parent to another.
Don't thank me, thank the moon's gravitation pull! Post in My Journal and help me to not procrastinate!

This topic is closed to new replies.

Advertisement