About
MogreBetaGUI is a port of BetaGUI. It's using overlays, therefore no external libraries are needed except Mogre itself.
The BetaGUI is intended to be a very short example how to create a GUI. It is NOT intended to be a fully operational GUI supporting a lot of different controls and features. For a full GUI check out the other GUI systems available for Mogre.
For questions etc. use this forum thread.
The talk about the original Ogre add-on BetaGUI is in this thread.
Table of contents
Features
- Creating a Window
- Resizing the window via a bottom-right button.
- Moving the window around via a top bar button.
- Creating a push button in a window with a inactive and active material state.
- Calling back to a function or class to report when that button has been "pushed"
- Creating a functional text gadget on a window to accept textual input to n characters.
- Creating a static piece of text on a window.
A video demonstrating the features 1.26Mb, playable in VLC.
Foreword of the porter
Here's a couple of hours (and then some) work attempting to do a "direct" port of BetaGUI (v015) to Mogre.
As it turned out, it's not 100% similar, but it's currently working for me with the following differences:
- All OverlayElements are put inside an extra OverlayContainer; I just couldn't figure out how to do that particular casting trick with C#; this introduces foreach-loops when setting the (dis-)activated material for buttons, textinputs. If you're interested in fixing this, check out GUI.createOverlay.
- Added a keyDelay timer, because it was handy for me.
- Some extra checks for validity here and there (backspace handling).
- I didn't need the type=1 callbacks, so they're left out for the moment; if your needs differ, look at C# delegates.
- Maybe something else, but I've got a GUI now; using Mogre 0.2.0 & Visual Studio 2005 EE. Thanks again for Betajaen, his code is brilliant ^^
Usage instructions are as with the C++ version (here), but see bottom of page.
Cheers,
funguine (forums) / ketarax (#ogre3d)
Differences to Ogre version
- All OverlayElements are put inside an extra OverlayContainer; I just couldn't figure out how to do that particular casting trick with C#; this introduces foreach-loops when setting the (dis-)activated material for buttons, textinputs. If you're interested in fixing this, check out GUI.createOverlay.
- Added a keyDelay timer.
- Some extra checks for validity here and there (backspace handling).
- No "type=1" callbacks, so they're left out for the moment; if your needs differ, look at C# delegates.
Main source code
The Mogre port is related to BetaGUI revision 015 (also called 1.0'15).
/// Betajaen's GUI 015 Uncompressed /// Originally written by Robin "Betajaen" Southern 07-Nov-2006, http://www.ogre3d.org/wiki/index.php/BetaGUI /// This code is under the Whatevar! licence. Do what you want; but keep the original copyright header. /// This code is not meant to be readable, if you base your future source on this, I will laugh at you. using Mogre; using System; using System.Collections.Generic; namespace BetaGUI { enum wt { NONE = 0, MOVE = 1, RESIZE = 2, RESIZE_AND_MOVE = 3 }; public class GUI { public uint wc, bc, tc, oc; public Overlay mO, mMPo; public List<Window> WN = new List<Window>(); public Window mXW; public OverlayContainer mMP; public String mFont; public uint mFontSize; public Timer keyDelay = null; public GUI(String font, uint fontSize) { mXW = null; mMP = null; mFont = font; mFontSize = fontSize; wc = 0; bc = 0; tc = 0; oc = 0; mO = OverlayManager.Singleton.Create("BetaGUI"); mO.Show(); } public void hide() { for (int i = 0; i < WN.Count; i++) WN[i].hide(); mMP.Hide(); } public void show() { for (int i = 0; i < WN.Count; i++) WN[i].show(); if ( mMP != null) mMP.Show(); } public void killGUI() { for (int i = 0; i < WN.Count; i++) WN[i].killWindow(); WN.Clear(); mMP.Hide(); foreach (OverlayContainer container in mO.Get2DElementsIterator()) { foreach (OverlayElement element in container.GetChildIterator()) OverlayManager.Singleton.DestroyOverlayElement(element); mO.Remove2D(container); OverlayManager.Singleton.DestroyOverlayElement(container); } OverlayManager.Singleton.Destroy(mO); foreach ( OverlayContainer container in mMPo.Get2DElementsIterator()) { foreach (OverlayElement element in container.GetChildIterator()) OverlayManager.Singleton.DestroyOverlayElement(element); mMPo.Remove2D(container); OverlayManager.Singleton.DestroyOverlayElement(container); } OverlayManager.Singleton.Destroy(mMPo); mMP = null; if (keyDelay != null) keyDelay.Dispose(); } public bool injectMouse(uint x, uint y, bool LMB) { if (mMP != null) mMP.SetPosition(x, y); if (mXW != null) { int i = 0; foreach (Window win in WN) { if (mXW == win) { mXW.killWindow(); WN.RemoveAt(i); return false; } i++; } } for (int i = 0; i < WN.Count; i++) { if (WN[i].check(x,y, LMB)) { return true; } } return false; } public bool injectKey(String key, uint x, uint y) { if (keyDelay == null) keyDelay = new Timer(); if (keyDelay.Milliseconds > 150) { for (int i = 0; i < WN.Count; i++) { if (WN[i].checkKey(key, x, y)) return true; } } return false; } public void injectBackspace(uint x, uint y) { injectKey("!b", x, y); } public Window createWindow(Vector4 D, String M, int T, String C) { Window w = new Window(D, M, T, C, this); WN.Add(w); return w; } public void destroyWindow(Window w) { mXW = w; } public OverlayContainer createOverlay(String N, Vector2 P, Vector2 D, String M, String C, bool A) { String t = "Panel"; if (C != "") t = "TextArea"; OverlayElement e = OverlayManager.Singleton.CreateOverlayElement(t, N); e.MetricsMode = GuiMetricsMode.GMM_PIXELS; e.SetDimensions(D.x, D.y); e.SetPosition(0, 0); //if (M != "") // e.MaterialName = M; if (C != "") { e.Caption = C; e.SetParameter("font_name", mFont); e.SetParameter("char_height", StringConverter.ToString(mFontSize)); } OverlayContainer c = (OverlayContainer)OverlayManager.Singleton.CreateOverlayElement("Panel", "ContainerHack" + (oc++).ToString()); c.MetricsMode = GuiMetricsMode.GMM_PIXELS; c.SetDimensions(D.x, D.y); c.SetPosition(P.x, P.y); if (M != "") c.MaterialName = M; c.AddChild(e); if (A) { mO.Add2D(c); c.Show(); } return c; } public OverlayContainer createMousePointer(Vector2 d, String m) { mMPo = OverlayManager.Singleton.Create("BetaGUI.MP"); mMPo.ZOrder = 649; mMP = createOverlay("bg.mp", new Vector2(0, 0), d, m, "", false); mMPo.Add2D(mMP); mMPo.Show(); mMP.Show(); return mMP; } } // class GUI public class Button { public OverlayContainer mO, mCP; public String mmn, mma; public Callback callback; public uint x, y, w, h; public Button(Vector4 D, String M, String T, Callback C, Window P) { x = (uint)D.x; y = (uint)D.y; w = (uint)D.z; h = (uint)D.w; mmn = M; mma = M; ResourcePtr ma = MaterialManager.Singleton.GetByName(mmn + ".active"); if (ma != null) { mma += ".active"; } mO = P.mGUI.createOverlay(P.mO.Name + "b" + StringConverter.ToString(P.mGUI.bc++), new Vector2(x, y), new Vector2(w, h), M, "", false); mCP = P.mGUI.createOverlay(mO.Name + "c", new Vector2(4, (h - P.mGUI.mFontSize) / 2), new Vector2(w, h), "", T, false); P.mO.AddChild(mO); mO.Show(); mO.AddChild(mCP); mCP.Show(); callback = C; } public void killButton() { foreach (OverlayContainer container in mO.GetChildContainerIterator()) foreach (OverlayElement element in container.GetChildIterator()) OverlayManager.Singleton.DestroyOverlayElement(element); foreach (OverlayElement element in mO.GetChildIterator()) OverlayManager.Singleton.DestroyOverlayElement(element); mO.Parent.RemoveChild(mO.Name); OverlayManager.Singleton.DestroyOverlayElement(mO); } public void activate(bool a) { if (!a && mmn != "") mO.MaterialName = mmn; if (a && mma != "") mO.MaterialName = mma; } public bool isin(uint mx, uint my, uint px, uint py) { return (!(mx >= x + px && my >= y + py)) || (!(mx <= x + px + w && my <= y + py + h)); } } public class TextInput { public OverlayContainer mO, mCP; public String mmn, mma, value; public uint x, y, w, h, length; public String getValue() { return value; } public void setValue(String v) { mO.Caption = value = v; } // mCP here if ... public TextInput(Vector4 D, String M, String V, uint L, Window P) { x = (uint)D.x; y = (uint)D.y; w = (uint)D.z; h = (uint)D.w; value = V; mmn = M; mma = M; length = L; ResourcePtr ma = MaterialManager.Singleton.GetByName(mmn + ".active"); if ( ma != null) mma += ".active"; mO = P.mGUI.createOverlay(P.mO.Name + "t" + StringConverter.ToString(P.mGUI.tc++), new Vector2(x, y), new Vector2(w, h), M, "", false); mCP = P.mGUI.createOverlay(mO.Name + "c", new Vector2(0, (h - P.mGUI.mFontSize) / 2), new Vector2(w, h), "", V, false); P.mO.AddChild(mO); mO.Show(); mO.AddChild(mCP); mCP.Show(); } public void killTextInput() { foreach (OverlayContainer container in mO.GetChildContainerIterator()) foreach (OverlayElement element in container.GetChildIterator()) OverlayManager.Singleton.DestroyOverlayElement(element); foreach (OverlayElement element in mO.GetChildIterator()) OverlayManager.Singleton.DestroyOverlayElement(element); mO.Parent.RemoveChild(mO.Name); OverlayManager.Singleton.DestroyOverlayElement(mO); } public void activate(bool a) { if (!a && mmn != "") mO.MaterialName = mmn; if (a && mma != "") mO.MaterialName = mma; } public bool isin(uint mx, uint my, uint px, uint py) { return (!(mx >= x + px && my >= y + py)) || (!(mx <= x + px + w && my <= y + py + h)); } } public class Window { public TextInput mATI; public Button mRZ, mAB, mTB; // resize, activebutton, titlebar public uint x, y, w, h; public GUI mGUI; public OverlayContainer mO; public List<Button> mB = new List<Button>(); public List<TextInput> mT = new List<TextInput>(); public Button createButton(Vector4 D, String M, String T, Callback C) { Button x = new Button(D, M, T, C, this); mB.Add(x); return x; } public TextInput createTextInput(Vector4 D, String M, String V, uint L) { TextInput x = new TextInput(D, M, V, L, this); mT.Add(x); return x; } public void createStaticText(Vector4 D, String T) { OverlayContainer x = mGUI.createOverlay(mO.Name + StringConverter.ToString(mGUI.tc++), new Vector2(D.x, D.y), new Vector2(D.z, D.w), "", T, false); mO.AddChild(x); x.Show(); } public void hide() { mO.Hide(); } public void show() { mO.Show(); } public bool isVisible() { return mO.IsVisible; } public Window(Vector4 D, String M, int t, String C, GUI G) { x = (uint) D.x; y = (uint) D.y; w = (uint) D.z; h = (uint) D.w; mGUI = G; mTB = null; mRZ = null; mATI = null; mAB = null; mO = G.createOverlay("BetaGUI.w" + StringConverter.ToString(G.wc++), new Vector2(D.x, D.y), new Vector2(D.z, D.w), M, C, true); if (t >= 2) { Callback c = new Callback(); c.t = 4; mRZ = createButton(new Vector4(D.z - 16, D.w - 16, 16, 16), M + ".resize", "", c); } if (t == 1 || t == 3) { Callback c = new Callback(); c.t = 3; mTB = createButton(new Vector4(0, 0, D.z, 22), M + ".titlebar", C, c); } } public void killWindow() { for (int i = 0; i < mB.Count; i++) mB[i].killButton(); for (int i = 0; i < mT.Count; i++) mT[i].killTextInput(); } public bool check(uint px, uint py, bool LMB) { if (!mO.IsVisible) return false; if (!(px >= x && py >= y) || !(px <= x + w && py <= y + h)) { if (mAB != null) mAB.activate(false); return false; } if (mAB != null) mAB.activate(false); for (int i = 0; i < mB.Count; i++) { if (mB[i].isin(px, py, x, y)) continue; if (mAB != null ) mAB.activate(false); mAB = mB[i]; mAB.activate(true); if (mATI != null) { mATI.activate(false); mATI = null; } if (!LMB) return true; switch (mAB.callback.t) { default: return true; case 1: //mAB.callback.fp(mAB); // FIX / what is this return true; case 2: mAB.callback.LS.onButtonPress(mAB); return true; case 3: mO.SetPosition(x = px - (mAB.w / 2), y = py - (mAB.h / 2)); return true; case 4: mO.SetDimensions(w = px - x + 8, h = py - y + 8); mRZ.mO.SetPosition(mRZ.x = w - 16, mRZ.y = h - 16); if (mTB != null) { mTB.mO.Width = mTB.w = w; } return true; } } if (!LMB) return true; for (int i = 0; i < mT.Count; i++) { if (mT[i].isin(px, py, x, y)) continue; mATI = mT[i]; mATI.activate(true); return true; } if (mATI != null) { mATI.activate(false); mATI = null; } return true; } public bool checkKey(String k, uint px, uint py) { if (mATI == null) return false; if (!mO.IsVisible) return false; if (!(px >= x && py >= y) || !(px <= x + w && py <= y + h)) return false; if (k == "!b" && mATI.value.Length > 0) { mATI.setValue(mATI.value.Substring(0, mATI.value.Length - 1)); foreach (OverlayElement element in mATI.mCP.GetChildIterator()) element.Caption = mATI.value; mGUI.keyDelay.Reset(); return true; } if (mATI.value.Length >= mATI.length) return true; if ( k != "!b") mATI.value += k; foreach ( OverlayElement element in mATI.mCP.GetChildIterator() ) // take from mCP if ... element.Caption = mATI.value; mGUI.keyDelay.Reset(); return true; } } public interface BetaGUIListener { void onButtonPress(Button referer); } public class Callback { public uint t; public BetaGUIListener LS; public Callback() { t=0; } public Callback (BetaGUIListener L) { t=2; LS=L; } } }
Usage
- Add a link to the MOIS.dll library (see MOIS)
- Add the Betajaen's GUI Resources
- Add :BetaGUIListener inheritence to your Scene() class definition
Here's the creation of a simple user interface:
Button single; Button lanmulti; Button netmulti; GUI mGui = new GUI("StarWars", 24); mGui.createMousePointer(new Vector2(30, 30), "bgui.pointer"); BetaGUI.Window window = mGui.createWindow(new Vector4(viewport.ActualWidth/4, viewport.ActualHeight/4, viewport.ActualWidth/2, viewport.ActualHeight/2), "bgui.window", (int)BetaGUI.wt.RESIZE_AND_MOVE, "Select game mode"); Callback cc = new Callback(this); // remember to give your program the BetaGUIListener interface single = window.createButton(new Vector4(0, 30, viewport.ActualWidth/2, 30), "bgui.button", "Single Player", cc); lanmulti = window.createButton(new Vector4(0, 60, viewport.ActualWidth/2, 30), "bgui.button", "LAN Multiplay", cc); netmulti = window.createButton(new Vector4(0, 90, viewport.ActualWidth/2, 30), "bgui.button", "Internet Multiplay", cc); window.createTextInput(new Vector4(0, 120, viewport.ActualWidth / 2, 30), "bgui.textinput", "testing", 20); window.show();
Then the BetaGUIListener callback (the buttons single, lanmulti and netmulti as well as mGui are globally accessible):
The enumeration PlayMode doesn't really exist, it's just for demonstration purposes.
public void onButtonPress(Button butt) { if (butt == single) { Console.WriteLine("Play solo!"); } else if (butt == lanmulti) { Console.WriteLine("Play in LAN!"); } else if (butt == netmulti) { Console.WriteLine("Play in Internet!"); } else { Console.WriteLine("*Some* button was pressed!"); } mGui.hide(); }
And an amazingly crude delivery of user input to the gui; I do this in frameStarted():
Note: This is just an example, you have to implement your own keycodes to support more keys.
if (mGui != null) { MOIS.MouseState_NativePtr mouseState = inputMouse.MouseState; uint screenx = (uint)(viewport.ActualWidth * ((float)mouseState.X.abs / 50f)); uint screeny = (uint)(viewport.ActualHeight * ((float)mouseState.Y.abs / 50f)); if (inputKeyboard.IsKeyDown(MOIS.KeyCode.KC_BACK)) { //mGui.injectBackspace(screenx, screeny); mGui.injectKey("!b", screenx, screeny); } if (inputKeyboard.IsKeyDown(MOIS.KeyCode.KC_1)) { mGui.injectKey("1", screenx, screeny); } if (mouseState.ButtonDown(MOIS.MouseButtonID.MB_Left)) { mGui.injectMouse(screenx, screeny, true); } else { mGui.injectMouse(screenx, screeny, false); } }
Documentation?
No class member is documented and many names are cryptical at the moment.
''It would be great, if someone could add XML descriptions to important members!
''
''Maybe also rename variable / method / attribute names?
''
On changing the code please tell it in wiki log.
See also
- BetaGUI
- MOIS (key and mouse interaction)
- MQuickGUI (an other GUI system for Mogre)
- Mogre CEGUI
- Overlays (the basic 2D elements in Ogre)
- Source code to extend MogreBetaGUI with alterable overlays