More GUI development

posted in Beals Software
Published November 27, 2008
Advertisement


A simple screenshot of child controls in action. I've got a pretty basic window ordering system in place. I might implement something better later on if I need to, but for now this works.

There's a small problem with message handling when it comes to mousemove. This message always gives me a problem for the simple fact that even though a control handles it, the rest of the controls still need to receive it so that (like in the case of a button) it can changes it's state. However, you don't want every control at that point to receive the message, because then all of the controls under the mouse will change their state. Can be routed via adding a "Handled" argument, but that seems like a hack to me and the only message that really needs it is mousemove.

Anyway, it's not much a problem if controls in the same container don't overlap (which, they shouldn't really.) The only time this was really a problem in the past was drop-down lists and since I don't plan on having those, everything should be kosher.

I'm still deciding how to implement alignment. I'd like to have controls that are aligned via my Alignments enum, but it's a lot of calculating during rendering and input messages (which is bad.) I'm thinking that I'll implement something like WinForm's anchoring. I can call "Align()" passing my Alignment flags and then anchor it where ever. It'll take a little more code on the resize end of things, but that shouldn't be too bad.

I need to cache each control's screen location. At the moment, I just call ToScreen(0, 0) and render from there, but that'll be slow in the end (well, not necessarily. Only if you have deep nested controls. Which is a very good possibility (root->form->tab_container->tab_page->groupbox->button for example.)

Anyway, just to show off some code:
class CApp : public P16::IApplication{    P16_ClassBindSetup(CApp);    P16::CHtmlLog Log;    P16::Graphics::HFont Font;    P16::UI::CPanel Root;    P16::UI::CPanel Panel, Panel2;    P16::UI::CButton Button, Button2, Button3, Button4;    bool panelX_OnClick(P16::UI::IControl *Control, P16::Input::EMouseButton Button, long X, long Y)    {        // This is just test code. The only that should ever need to call BringToFront() is a dialog        // and that'll be done automatically.        Control->BringToFront();        return true;    }public:    CApp() : Root("root"), Panel("panel0"), Panel2("panel1"), Button("button0"), Button2("button1"), Button3("button2"), Button4("button3") { }    virtual bool OnRun()    {        Log.Initialize("Debug.HTML");        P16::BindHtmlLog(&Log);        P16::InitializeLog();        if(!P16::Graphics::Initialize() || !P16::Graphics::CreateDevice(this->GetWindowHandle(), 640, 480))            return false;        if(!(Font = P16::Graphics::LoadFontFromFile("Resources/COAH.pbmf")))            return false;        P16::Input::Initialize();        P16::Input::OnKeyDown = P16_BindMethod1(OnKeyDown);        P16::Input::OnMouseMove = P16_BindMethod2(OnMouseMove);        P16::Input::OnMouseClick = P16_BindMethod3(OnMouseClick);        P16::Input::OnMouseRelease = P16_BindMethod3(OnMouseRelease);        Root.Move(0, 0);        Root.Resize(P16::Graphics::GetResolutionWidth(), P16::Graphics::GetResolutionHeight());        Root.SetBackgroundColor(P16::Colors::Black);        Root.SetBackgroundImage(P16::Graphics::LoadTextureFromFile("Resources/PanelBg.png"));        Root.SetBackgroundImageLayout(P16::UI::ImageLayouts::Tile);        Panel.Move(25, 25);        Panel.Resize(200, 300);        Panel.SetBackgroundColor(160, 255, 255, 255);        Panel.SetBackgroundImage(P16::Graphics::LoadTextureFromFile("Resources/RockBg2.png"));        Panel.SetBackgroundImageLayout(P16::UI::ImageLayouts::Stretch);        Panel.SetBorderColor(0, 0, 0);        Panel.SetBorderSize(1);        Panel.OnClick = P16_BindMethod4(panelX_OnClick);        Panel.SetParent(&Root);        Button.Move(10, 10);        Button.Resize(100, 26);        Button.SetText(Button.GetName());        Button.SetFont(Font);        Button.SetBackgroundColor(128, 255, 255, 255);        Button.SetParent(&Panel);        Button2.Move(10, 41);        Button2.Resize(100, 26);        Button2.SetText(Button2.GetName());        Button2.SetFont(Font);        Button2.SetBackgroundColor(128, 255, 255, 255);        Button2.SetParent(&Panel);        Panel2.Move(100, 100);        Panel2.Resize(200, 300);        Panel2.SetBackgroundColor(160, 255, 255, 255);        Panel2.SetBackgroundImage(P16::Graphics::LoadTextureFromFile("Resources/RockBg2.png"));        Panel2.SetBackgroundImageLayout(P16::UI::ImageLayouts::Stretch);        Panel2.SetBorderColor(0, 0, 0);        Panel2.SetBorderSize(1);        Panel2.OnClick = P16_BindMethod4(panelX_OnClick);        Panel2.SetParent(&Root);        Button3.Move(10, 10);        Button3.Resize(100, 26);        Button3.SetText(Button3.GetName());        Button3.SetFont(Font);        Button3.SetBackgroundColor(128, 255, 255, 255);        Button3.SetBorderSize(2);        Button3.SetParent(&Panel2);        Button4.Move(10, 41);        Button4.Resize(100, 26);        Button4.SetText(Button4.GetName());        Button4.SetFont(Font);        Button4.SetBackgroundColor(128, 255, 255, 255);        Button4.SetBorderSize(2);        Button4.SetParent(&Panel2);        return true;    }    virtual void OnCleanUp()    {        P16::Graphics::CleanUp();        P16::CleanUpLog();        Log.CleanUp();    }    virtual void OnIdle(float Seconds)    {        P16::Graphics::ClearRenderTarget(P16::Colors::CornflowerBlue);        Root.Render();        P16::Graphics::PresentRenderTarget();    }    virtual bool OnMessage(HWND Handle, UINT Message, WPARAM Param1, LPARAM Param2)    {        P16::Input::HandleMessage(Message, Param1, Param2);        /*        In the future, all I'll have to do is make sure the application        doesn't handle the message and then call something like        P16::UI::HandleMessage(Message, Param1, Param2)        */        return false;    }    virtual void OnKeyDown(P16::Input::EKey Key)    {        if(Key == P16::Input::Keys::F1)            P16::Graphics::SetResolution(800, 600);        else if(Key == P16::Input::Keys::F2)            P16::Graphics::SetResolution(1024, 768);        else if(Key == P16::Input::Keys::F3)            P16::Graphics::SetResolution(1440, 900);        else if(Key == P16::Input::Keys::Tilde)            P16::Graphics::TakeScreenshot();    }    void OnMouseMove(long X, long Y)    {        Root.HandleMouseMove(X, Y);    }    void OnMouseClick(P16::Input::EMouseButton Button, long X, long Y)    {        Root.HandleMouseClick(Button, X, Y);    }    void OnMouseRelease(P16::Input::EMouseButton Button, long X, long Y)    {        Root.HandleMouseRelease(Button, X, Y);    }};


Questions? Comments? Feel free to reply!
Previous Entry Untitled
Next Entry UI Code Scrapped
0 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement
Advertisement