MogreBetaGUI        

BetaGUI_example.png

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.

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)

Original posting

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