Saturday, March 31, 2007

XNA WindowSystem Source Code

Tomorrow I'm moving back to England, and won't be able to work on the project for a little while, so I thought this was as good a time as any to release an early version of my GUI for XNA. For screenshots, see my previous post.

Download Source Code

Features
  • Input events
  • Individual control transparency
  • Controls can have multiple skins, representing different states (hover, pressed etc.)
  • Skinnable at the GUI level through static calls, or at the control level
  • GUI and input event system implemented as game components
  • Custom mouse cursors
  • Modal windows
High-Level Controls
  • Check boxes
  • Combo boxes
  • Dialogs
  • Display settings dialog
  • Icons
  • Images
  • Image buttons
  • Text buttons
  • Labels
  • Menus
  • Context menus
  • Message boxes
  • Radio buttons
  • Radio groups
  • Vertical scrollbars
  • Text boxes
  • Windows
Future Plans
  • Flexible system for control animations
  • Possibly a settings file for customizing skins
  • Possibly bring the GUI into 3D for cooler animations
  • Context menus for any control, currently has to be done manually for each case
  • More complete textbox, with text selection and copy/paste
  • Horizontal scrollbars
Most of those features wouldn't be that hard to implement, it's just a matter of time.

The code is in a pretty stable state at the moment, but I'm sure there's hundreds of bugs to be found in there. Please feel free to contact me regarding any bugs, questions, or suggestions.

One know issue is that hideous amounts of temporary textures are created while resizing windows. I know the reason, but am waiting to find a graceful solution. Basically the GUI draws each control to it's own texture, allowing simple clipping and transparencies. While resizing windows, the control texture is recreated many times a second, sometimes using up all available texture memory. The simple fix in place at the moment, is that the garbage collector is called whenever this happens. Not pretty, but it doesn't crash. Everything goes back to normal when resizing ends.

This project uses code based on the Event Based Input System of the XNA5D library, and to a lesser extent the GUI components. The XNA5D library is by John Sedlak, and is available at jsedlak.org/Xna5D.

The library also uses the BitmapFont portion of XNAExtras for text rendering. To add new fonts to your projects, use the BMFontGen tool which part of the same project. XNAExtras is by Gary Kacmarcik, and is available at blogs.msdn.com/garykac/articles/749188.aspx.

Finally, although I drew the complete graphical skin, the message box icons are by Ken Saunders, and are available at mouserunner.com/Spheres_ColoCons1_Free_Icons.html.

Sometime soon I will post an overview of the system, and possibly a few quick tutorials on how to use it. It's pretty well documented, so this shouldn't pose too much of a problem for most of you.

In addition, I'll be releasing the source code of my first project to actually use the GUI, a tile map editor. It's already in a pretty usable state, but is very much a work in progress.

Oh yeah, and I need a name for the project. At the moment I'm just using WindowSystem for XNA, which is really just a description. Any ideas would be appreciated.

22 comments:

Boris Yangel said...

Thank you for your great work. Good luck with the project.

Mario said...

OY! This is some great stuff!! I had to adjust the properties from trying to look on the C: drive for stuff (c:\Dev\3rdParty\Import or whatever it was) to my local location of that DLL you provided. Anyways, with a minimum of fiddling it was up and going! Thanks a ton for this! Hope the trip back to England is a pleasant one! I'm keeping an eye on this project!

Shad said...

Sorry but I just can't work this out...with your testbed I can only put the menubar, without any other items in it, running...Every time I try to put another item like a listbox, for example, the program allways breaks on the Label class in the LoadContent method saying the content manager of the gui manager is null...I've been around this for hours. I'm missing something here for sure, since you have those screen shots with the library working fine...It must be me me missing something, can you point me in any direction..? or maybe mario, you said you put it up and running with some fighting, what did you have to do?

Good job anyway.
Best regards,
shad

Mario said...

Hey Shad! Well, I'm still working within the GUI_Testbed code provided, I never started from scratch, but from within there, I just altered the below method (I have a list with my 'planet' objects in it. I simply put the textname of my planets into the list)

' void KeyDownFunction(KeyEventArgs args)
' method to put my own goodies in it;

ListBox listBox = new ListBox(this, gui);
listBox.X = 20;
listBox.Y = 144;
listBox.ZOrder = 1.0f;

foreach (marioPlanet thePlanet in planetList)
{
listBox.AddEntry(thePlanet.PlanetName);
}
window.Add(listBox);


(sorry, don't know how to format for code)

and my stuff appeared within the test window each time. Granted, every time you hit the 'enter' key it would simply duplicate the same window creation method each time, but it puts my stuff on-screen and in one of those neat GUI windows. Fire off an email if you'd like, I can email you what I have over here. mroberti [AT] nethere [DOT] com . :)

Now if anyone can point ME in the right direction, how the heck would I actually interact with the windows contents once they're created? For example, I have a listbox, how do I check to see which item I have selected in a particular window? Hope we can figure it out together!

Aaron said...

mario - thanks for the comments.

All controls have events attached to them, allowing you to create methods that are called when an event takes place. ListBox actually has a SelectionChanged event. Now make sure you hold a reference to that listbox, and simply ask it for the currently selected index or text whenever the event is fired. The same goes for the ComboBox control.

Hope that helps a little. I will definitely write some tutorials as soon as I get rid of this flu.

Mario said...

Hey Aaron! Thanks for the info! Wouldn't you know it, I have no experience with event-driven programming, so I'll keep an eye peeled for any info you can give me down the road!

Kind regards,
Mario

Mario said...
This comment has been removed by the author.
Sérgio said...

Hi again! :) I've been working around with your WindowSystem Aaron, after putting it to work... Don't know why, but has I stated in my previous post the ContentManager in the GuiManager was allways null. Because of this I initialized, in your guimanager constructor, the ContentManager. I repeat don't why it was never initialized but this way it is working...it may not be the best of programing but ok... But this was some 2 weeks ago...Now I have allready been playing with making a ScreenMenu class for displaying Screen Menus in the game with the controls inside. During this period I came accross something unusual...if I changed the visibility of a control (from true to false) this had no effect what so ever...the control would be draw anyway. Solved this one too...by adding an if state in the texture draw of the UIComponent class. Of course this does not solve all of the problem...because the control continues with the focus, this one is easy... every time a control visible state is set to false the CanHaveFocus state must be set to false too...otherwise if you click in the area where the control should have been, if the visibility was set to true, the click would have a focus on the "invisible" control.
Another thing I created was the ScreenMenuManager, this class will manage the menu to be displayed on the screen, this is the ScreenMenu that will be in the GuiManager.
One other thing I'll try to integrate in your WindowsSystem is the new FontSample available at the XNA Creators Club. This FontSample provides support for TrueType fonts and other functionalitys.

Don't know when or if you'r going to read this post. But give me a feedback if you do, maybe I'm mistaken in some way with your WindowSystem and so some of the things I "discovered" there are just part of a big misunderstanding of mine.

Thanks in advance! And thank you for releasing this WindowSystem! ;) :)

Best regards,
Shad

Mario said...

Hey I got it going. Not sure how the hell I missed the simple "GetSelectedText" method. w00t!

Aaron said...

Hi Sergio. My development computer doesn't currently have an Internet connection, so I'm doing this from memory as best I can.

The screen manager is a good idea, and is something I've been working on myself, but not as part of the GUI. I'm trying to keep everything as seperate as possible. The reason the visible property isn't recognised, is because the control components aren't actually added to the game components list. This is so the GUI can handle the draw order itself, and for various other early problems that occured. The way you have fixed that is the way I shall handle it in the next release. You'll notice that even thought the GUI is quite fully featured, sometimes some obvious things haven't been included. This is simply because I add features and fixes as I need them for my own projects.

I tried to use the XNA font sample code, but it was only providing anti-aliased text, which completely screws with the look of text labels. The reason is that each control is drawn to it's own texture, so the alpha values mess up as each child control is drawn onto the parent. If anybody comes up with a solution, then please let me know, I was racking my brain for like a week on that one!

I'll look into the ContentManager problem for the next release, as well as giving the resources relative paths instead of absolute ones. I honestly thought Visual Studio handled things like that!

Mark said...

Apparantly, Visual Studio is appropriately adding relative paths for everything except the content pipeline assemblies. To fix this, I had to edit the .csproj manually in Notepad, but once I did that both myself and my other dev buddy were able to use the project via source control on separate computers without interferring with eachother.

Aaron said...

Would anybody consider sending me their fixed project files? It's just a pain in the arse at the moment, without an Internet connection on my devlopment machine.

aaronkm [AT] gmail [DOT] com

Mario said...

For fun, I was going to make a function that would auto-arrange the windows created in the testbed app, but I'm not sure how to enumerate the windows. I was thinking a 'foreach' loop, but I'm not sure where the gui manager stores the windows in particular. Something to the effect of


foreach (XNAWindowSystem.Window theWindow in *what would I use here?*)
{
// Do stuff with windows positions
theWindow.X=
theWindow.Y=

// and so on and so forth...


}

Any directions you could point me in?

Aaron said...

To be honest, it would be much better to keep your own references to the windows, in a list or something similar. Then you could simply loop through them to line up the windows.

I'll have a think about whether to allow access to controls from outside the GUIManager.

Hope that helps.

Thuan said...

Aaron,
I have a simple xna game that use your xnawindowsystem dll. The Game contains one window, on that window I place a label "Your Name:", a text box for the user to enter their name, and TextButton so the user can click to start the game.

I handled the click event of the TextButton like so:

txtButton.Click += ...(txtButton_Click);

void txtButton_Click(UIComponent sender)
{
....
}

I want to be able to access the list of the controls that is in that window to retrieve the value of the textbox to display later in the game, however, I am not seeing any accessor property to allow access to the child controls of the window. Am I missing something or the library does not allow that access? Or there is another way to access the child controls of window that I do not see.


Thank you for providing this package, it is great.

Thuan

Aaron said...

thuan, I don't think that accessing controls through the GUIManager is the best way to handle these kinds of situations. The current method of accessing data from a control after an event, is to keep a reference to the control, and poll it after the event takes place.

Mario said...

Thanks Aaron! That was my plan (to have a separate list of goodies I want to keep track of), I just wanted to make sure I wasn't doing more work than I was supposed to. :)

Mario said...

Hey Aaron! I was wondering what font utility you used to make your bitmap fonts? I'd like to use a couple of the fonts I have on this side?

Aaron said...

It's a command-line utility included as part of XNAExtras, available at:

blogs.msdn.com/garykac/articles/749188.aspx

I'll be explaining that and skinning the GUI in more detail in a future article.

Mario said...

Hey thanks! I appreciate you answering the myriad questions that are thrown your way!

Aaron said...

Hey dude it's no problem. To be honest, I feel a little lazy for not providing more information yet.

I'm working on Tutorial 2, but there never seems to be enough time when you're working!

Smorpg said...

Hey!

I modified the TextBox class a bit to include a password-char, basically a single character that gets drawn instead of the text, so that you can use it to make password boxes too.

If anybody wants it I can share.

-Insane