winsvega / GCP

SDL2 GUI Forms, classes
8 stars 1 forks source link

Working example #1

Closed lemonka closed 8 years ago

lemonka commented 10 years ago

Hi winsvega

Could you kindly show some working example for creating form with button and assigning some action to a button?

I would really appreciate. Your project seems to be very interesting but all I was able to figure was to create a message box :/

Sorry for contacting here but I dont see any other options here on GitHub

winsvega commented 10 years ago

Hi! Sure. Here is some quick example on how to use current GCP interface:

#include "SDL.h"
#include "GCP.h"

SDL_Window *sdlWindow;
SDL_Renderer *sdlRenderer;
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;

class gcpWindow
{
    private:
        GCP_SPointer<GCP_Controller> Interface;
        GCP_SPointer<GCP_Form> MainInterfaceForm;
        GCP_SPointer<SStyle> mainStyle;
        GCP_SPointer<GCP_ContextMenu> RightButtonMenu;

    public:
        gcpWindow(){init();}

    void init()
    {
        //Main controller and form
        Interface = GCP_SPointer<GCP_Controller> (new GCP_Controller(sdlRenderer,SCREEN_WIDTH,SCREEN_HEIGHT));
        MainInterfaceForm = Interface->createForm(sdlRenderer);     

        ////TTF Init (Weak point here)
        string working_directory = "C:\\";
        std::stringstream directory_path;       
        directory_path << working_directory.c_str()   << "arial.ttf";
        std::string directory_path_string = directory_path.str();
        Interface->setFont(directory_path.str());

        MainInterfaceForm->setCaption("Example Form");

        //Example button with message box handler
        GCP_SPointer<GCP_Button> bNew = GCP_SPointer<GCP_Button> (new GCP_Button());        
        bNew->setWidthHeight(105,25);
        bNew->setCaption("Example Button");
        bNew->setPosition(SCREEN_WIDTH/2-bNew->width/2,SCREEN_HEIGHT/2-bNew->height/2);
        bNew->iType = GCP_BUTTON_ROUNDRECT;
        bNew->setOnMouseLeftClick(this,&gcpWindow::onHelloWorldButtonClick);
        MainInterfaceForm->addComponent(GCP_SPointer<GCP_FormComponent>(bNew));

        int x0 = 100, y0 = 100, separation=30, i=0; 
        //Example Label with hint
        GCP_SPointer<GCP_Label> label6 = GCP_SPointer<GCP_Label>(new GCP_Label());
        label6->setPosition(x0,y0+separation*i);
        label6->text = "Example Label:";
        label6->INFO = "Example Hint";
        MainInterfaceForm->addComponent(GCP_SPointer<GCP_FormComponent>(label6));

        //Example Select with options
        GCP_SPointer<GCP_Select> select = GCP_SPointer<GCP_Select>(new GCP_Select());
        select->setPosition(x0+140,y0+separation*i-5);
        select->setWidthHeight(130,separation-5);   
        select->options.draw_order = -1;
        select->addItem("ex. select 1");
        select->addItem("ex. select 2");
        select->addItem("ex. select 3");
        select->selectItem(0);
        MainInterfaceForm->addComponent(GCP_SPointer<GCP_FormComponent>(select));

        i++;
        //Example Checkbox
        GCP_SPointer<GCP_CheckBox> CheckBox1 = GCP_SPointer<GCP_CheckBox>(new GCP_CheckBox());
        CheckBox1->setPosition(x0,y0+separation*i);
        CheckBox1->text="Example Checkbox"; 
        MainInterfaceForm->addComponent(GCP_SPointer<GCP_FormComponent>(CheckBox1));    

        //Some color schemas for interface
        mainStyle = GCP_SPointer<SStyle>(new SStyle);
        mainStyle->setColor(c_lime,c_red,c_orange,c_black);
        mainStyle->iBorderWidth = 2;
        mainStyle->cBorderColor = c_black;
        mainStyle->iRoundCff = 2;       
        mainStyle->cTextFieldBackColor = c_orange;
        mainStyle->colorHeadMenuBackground = c_orange;
        mainStyle->colorHeadMenuFade = c_grey;
        mainStyle->colorButtonBackground = c_white;

        //Example Blocking Subform
        GCP_SPointer<GCP_Form> OptionsForm = GCP_SPointer<GCP_Form>(new GCP_Form(sdlRenderer, 300, 400)); 
        OptionsForm->isParentLocking = true;
        OptionsForm->setStyle(mainStyle.getPointer());
        OptionsForm->isVisible = false;
        OptionsForm->isDragable = true; 
        OptionsForm->setPosition(SCREEN_WIDTH/2 - OptionsForm->width/2,SCREEN_HEIGHT/2 - OptionsForm->height/2);
        string bcaption4 = "Example Form";
        OptionsForm->setCaption(bcaption4);
        MainInterfaceForm->addSubForm(OptionsForm);

        i++;
        //Button that opens subform
        GCP_SPointer<GCP_Button> bForm = GCP_SPointer<GCP_Button> (new GCP_Button());       
        bForm->setWidthHeight(105,25);
        bForm->setCaption("Example Form");
        bForm->setPosition(x0,y0+separation*i);
        bForm->iType = GCP_BUTTON_ROUNDRECT;
        bForm->setOnMouseLeftClick(OptionsForm.getPointer(),&GCP_Form::toggleVisibility);
        MainInterfaceForm->addComponent(GCP_SPointer<GCP_FormComponent>(bForm));

        //Right button context menu
        RightButtonMenu = GCP_SPointer<GCP_ContextMenu> (new GCP_ContextMenu());
        RightButtonMenu->setPosition(100,100);

        //First button in context menu
        GCP_SPointer<GCP_Button> bcCreatePoint = GCP_SPointer<GCP_Button>(new GCP_Button());
        bcCreatePoint->setWidthHeight(120,25);
        bcCreatePoint->setCaption("Example Menu 1");
        bcCreatePoint->iType = GCP_BUTTON_ROUNDRECT;
        bcCreatePoint->setOnMouseLeftClick(this,&gcpWindow::onHelloWorldButtonClick);
        RightButtonMenu->addButton(bcCreatePoint);

        //A button with image in context menu
        GCP_SPointer<GCP_Button> bcCenterView = GCP_SPointer<GCP_Button>(new GCP_Button());
        bcCenterView->setWidthHeight(120,25);
        bcCenterView->setIcon(working_directory+"image.bmp");
        bcCenterView->iType = GCP_BUTTON_ROUNDRECT;
        bcCenterView->setOnMouseLeftClick(this,&gcpWindow::onHelloWorldButtonClick);
        RightButtonMenu->addButton(bcCenterView);   

        //Hide out menu
        RightButtonMenu->isVisible = false;
        RightButtonMenu->iType = GCP_MENU_MVERTICAL;    
        MainInterfaceForm->addComponent(RightButtonMenu);

        //Add Main form handlers
        MainInterfaceForm->setOnMouseRightClick(this,&gcpWindow::OnMouseGlobalRightClick);
        MainInterfaceForm->setOnMouseLeftClick(RightButtonMenu.getPointer(),&GCP_ContextMenu::close);
    }

    void OnMouseGlobalRightClick(void *obj)
    {
        RightButtonMenu->open(MainInterfaceForm->mouse_x,MainInterfaceForm->mouse_y);
    }
    void onHelloWorldButtonClick(void *obj)
    {
        MainInterfaceForm->showmessage("Hello World!");
    }

    int update()
    {   
        SDL_Event event;
        SDL_RenderClear(sdlRenderer);   
        while( SDL_PollEvent( &event ) )        
        {               
            Interface->handleEvents(event);     //Handle interface events   
            if(!MainInterfaceForm->isVisible)
            return -1;
        }   
        draw();                                 //Draw your logic
        Interface->draw();                      //Draw interface over it    
        SDL_RenderPresent(sdlRenderer);
        return 0;
    }

    void draw()
    {
        //Draw your logic here
    }
};
int main(int argc, char *argv[]) {

    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0)
        return 1;

    sdlWindow = SDL_CreateWindow("GUI Check Point", 100, 100, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
    if (sdlWindow == NULL)
        return 2;

    sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    if (sdlRenderer == NULL)
        return 3;

    SDL_SetRenderDrawColor(sdlRenderer,0,220,120,0);

    gcpWindow wnd;
    while(wnd.update() == 0)
        SDL_Delay(10);  

    SDL_DestroyRenderer(sdlRenderer);
    SDL_DestroyWindow(sdlWindow);

    SDL_Quit();
    return 0;
}
lemonka commented 10 years ago

thank you for this code - somehow i have managed to run basic code based on source from messagebox cpp file. As I see you are using some new version of GCP since I cannot find anywhere GCP_SPointer definition.

Additionally I would suggest:

  1. to implement property "name" for each control and method - getElementByName
  2. implement flags for elements like isButton, isCheckbox ect. and implement getElementsByType

Could you kindly also explain to me this code:

template< class T, class U> void setOnMouseLeftClick(T* i_class, U i_method )       {
            if(FUNCTIONS[GCP_ON_LEFT_CLICK]!=0) delete FUNCTIONS[GCP_ON_LEFT_CLICK];
            FUNCTIONS[GCP_ON_LEFT_CLICK] = new Container< T, U > ( i_class, i_method);
        }

I find this difficult to understand this (it is working in my test code but i have no idea what is going on) :/

Overall I think you have a chance to become primary GUI for SDL2 basing only on SDL - I didn't manage to find anything what would be working for me :+1:

lemonka commented 10 years ago

As I see you have updated source meanwhile and my "repo" is outdated :dart:

winsvega commented 10 years ago
template< class T, class U> void setOnMouseLeftClick(T* i_class, U i_method )       {
            if(FUNCTIONS[GCP_ON_LEFT_CLICK]!=0) delete FUNCTIONS[GCP_ON_LEFT_CLICK];
            FUNCTIONS[GCP_ON_LEFT_CLICK] = new Container< T, U > ( i_class, i_method);
        }

Well, this is not exactly my code either. I used this example: http://habrahabr.ru/post/78299/

The point is to send a class pointer (T* i_class) and a method of class T (U i_method) to some object. So later this object would be able to call this method as a delegate. More preciesly class T and its method would be stored in Container class described at GCP_Delegate.h So later when needed we could call it like this

if(FUNCTIONS[GCP_ON_LEFT_CLICK] != 0)
        FUNCTIONS[GCP_ON_LEFT_CLICK]->Call((void*)this);

getElementsByType would work just fine. But getElementByName already proved to be inefficient on a large amount of components.

lemonka commented 10 years ago

How to change text of this element:

  int x0 = 100, y0 = 100, separation=30, i=0; 
        //Example Label with hint
        GCP_SPointer<GCP_Label> label6 = GCP_SPointer<GCP_Label>(new GCP_Label());
        label6->setPosition(x0,y0+separation*i);
        label6->text = "Example Label:";
        label6->INFO = "Example Hint";
        MainInterfaceForm->addComponent(GCP_SPointer<GCP_FormComponent>(label6));

By pointer to MainInterfaceForm only??

winsvega commented 10 years ago

In order to change labels text you should store a GCP_SPointer label6 as a variable somewhere. Like MainInterfaceForm is declared as private variable of class gcpWindow and we could call it where ever we want.

Drawing dynamic text is a challange. This is the most consuming operation which is really noticable at android devices. I store a data buffer structure which is changing (redrawing) only when text has been changed, otherwise it will just be applied by sdlRenderer. It helps, but not that much. The dynamic text rendering stiil needs to be improved.

lemonka commented 10 years ago
  1. Somehow I'm unable to compile latest version of GCP (on win7, codeblocks 12.13 and mingw 4.8.1 but i will refer to this topic most probably tomorrow since i need to to some work at home :/ so belows comments are regarding previous version and might be not applicable for latest one.
  2. Regarding dynamic text drawing - what do you think about this:

a. every object with text have some private "surface" which is updated only in case if "old" text differs from "current" text b. ondraw private surface will be copied to renderer without any changes c. every GCP_Form object have map implemented defined as:

std::map<std::string,* GCP_FormComponent> elements

it would cuase rather simple way of accessing any element of form?

I'm rather weak programmer so I will not be surprised if my thinking is not efficient enough ;)

winsvega commented 10 years ago

a.b. - this is how it is working already. c. - that might work. you should redefine addComponent method with additional parameter 'name'

unable to compile latest version of GCP? What errors?

lemonka commented 10 years ago

Errors:

  1. Include path to SDL_ttf (this is rather related to env. configuration
  2. In GCP_Math you are using:
#ifndef M_PI
#include <cmath>
#endif

and some later: using namespace std;

Since i have loaded math.h it is defining M_PI but then sqrt and pow are causing similar error:

D:\Programming\SDL_Init\SDL_Init\GCP_Math.h|50|error: 'pow' was not declared in this scope|

Including always cmath resolves this.

  1. D:\Programming\SDL_Init\SDL_Init\GCP_Math.h|202|error: extra qualification 'GCP_Math::' on member 'bin2dec' [-fpermissive]|

Removing GCP_Math:: fix issue

  1. D:\Programming\SDL_Init\SDL_Init\GCP_Math.h|180|error: 'strstr' was not declared in this scope|

including this fix this:

#include <cstring>
#include <cstdlib>

I have failed with rest:

||=== Build: Debug Win32 in SDL_Init (compiler: GNU GCC Compiler) ===| D:\Programming\SDL_Init\SDL_Init\GCP_Select.h||In constructor 'GCP_Select::GCP_Select()':| D:\Programming\SDL_Init\SDL_Init\GCP_Select.h|23|error: invalid user-defined conversion from 'GCP_SPointer' to 'int' [-fpermissive]| D:\Programming\SDL_Init\SDL_Init\GCP_SPointer.h|102|note: candidate is: GCPSPointer::operator T() [with T = GCP_Button] | D:\Programming\SDL_Init\SDL_Init\GCP_SPointer.h|102|note: no known conversion for implicit 'this' parameter from 'GCPButton' to 'int'| D:\Programming\SDL_Init\SDL_Init\GCP_Select.h||In member function 'void GCP_Select::addItem(std::string)':| D:\Programming\SDL_Init\SDL_Init\GCP_Select.h|37|error: invalid user-defined conversion from 'GCP_SPointer' to 'int' [-fpermissive]| D:\Programming\SDL_Init\SDL_Init\GCP_SPointer.h|102|note: candidate is: GCPSPointer::operator T() [with T = GCP_Button] | D:\Programming\SDL_Init\SDL_Init\GCP_SPointer.h|102|note: no known conversion for implicit 'this' parameter from 'GCPButton' to 'int'| D:\Programming\SDL_Init\SDL_Init\GCP_Select.h||In member function 'void GCPSelect::OnDroppedButtonClick(void)':| D:\Programming\SDL_Init\SDL_Init\GCP_Select.h|81|error: invalid user-defined conversion from 'GCP_SPointer' to 'int' [-fpermissive]| D:\Programming\SDL_Init\SDL_Init\GCP_SPointer.h|102|note: candidate is: GCPSPointer::operator T() [with T = GCP_Button] | D:\Programming\SDL_Init\SDL_Init\GCP_SPointer.h|102|note: no known conversion for implicit 'this' parameter from 'GCPButton' to 'int'| D:\Programming\SDL_Init\SDL_Init\game.cpp||In member function 'void game::init()':| D:\Programming\SDL_Init\SDL_Init\game.cpp|74|error: no matching function for call to 'GCP_Form::addComponent(GCPButton&)'| D:\Programming\SDL_Init\SDL_Init\game.cpp|74|note: candidates are:| D:\Programming\SDL_Init\SDL_Init\GCP_Form.h|63|note: void GCP_Form::addComponent(GCP_SPointer&)| D:\Programming\SDL_Init\SDL_Init\GCP_Form.h|63|note: no known conversion for argument 1 from 'GCPButton' to 'GCP_SPointer&'| D:\Programming\SDL_Init\SDL_Init\GCP_Form.h|64|note: void GCP_Form::addComponent(GCP_SPointer&)| D:\Programming\SDL_Init\SDL_Init\GCP_Form.h|64|note: no known conversion for argument 1 from 'GCPButton' to 'GCP_SPointer&'| D:\Programming\SDL_Init\SDL_Init\GCP_Select.h||In constructor 'GCP_Select::GCP_Select()':| D:\Programming\SDL_Init\SDL_Init\GCP_Select.h|23|error: invalid user-defined conversion from 'GCP_SPointer' to 'int' [-fpermissive]| D:\Programming\SDL_Init\SDL_Init\GCP_SPointer.h|102|note: candidate is: GCPSPointer::operator T() [with T = GCP_Button] | D:\Programming\SDL_Init\SDL_Init\GCP_SPointer.h|102|note: no known conversion for implicit 'this' parameter from 'GCPButton' to 'int'| D:\Programming\SDL_Init\SDL_Init\GCP_Select.h||In member function 'void GCP_Select::addItem(std::string)':| D:\Programming\SDL_Init\SDL_Init\GCP_Select.h|37|error: invalid user-defined conversion from 'GCP_SPointer' to 'int' [-fpermissive]| D:\Programming\SDL_Init\SDL_Init\GCP_SPointer.h|102|note: candidate is: GCPSPointer::operator T() [with T = GCP_Button] | D:\Programming\SDL_Init\SDL_Init\GCP_SPointer.h|102|note: no known conversion for implicit 'this' parameter from 'GCPButton' to 'int'| D:\Programming\SDL_Init\SDL_Init\GCP_Select.h||In member function 'void GCPSelect::OnDroppedButtonClick(void)':| D:\Programming\SDL_Init\SDL_Init\GCP_Select.h|81|error: invalid user-defined conversion from 'GCP_SPointer' to 'int' [-fpermissive]| D:\Programming\SDL_Init\SDL_Init\GCP_SPointer.h|102|note: candidate is: GCPSPointer::operator T() [with T = GCP_Button] | D:\Programming\SDL_Init\SDL_Init\GCP_SPointer.h|102|note: no known conversion for implicit 'this' parameter from 'GCPButton' to 'int'| D:\Programming\SDL_Init\SDL_Init\GCP.cpp||In constructor 'GCP_Controller::GCP_Controller(SDLRenderer, int, int)':| D:\Programming\SDL_Init\SDL_Init\GCP.cpp|14|warning: passing NULL to non-pointer argument 1 of 'GCP_SPointer& GCP_SPointer::operator=(int) [with T = GCP_Form]' [-Wconversion-null]| D:\Programming\SDL_Init\SDL_Init\GCP.cpp||In member function 'GCP_SPointer GCP_Controller::createForm(SDLRenderer)':| D:\Programming\SDL_Init\SDL_Init\GCP.cpp|38|error: invalid user-defined conversion from 'GCP_SPointer' to 'int' [-fpermissive]| D:\Programming\SDL_Init\SDL_Init\GCP_SPointer.h|102|note: candidate is: GCPSPointer::operator T() [with T = GCP_Form] | D:\Programming\SDL_Init\SDL_Init\GCP_SPointer.h|102|note: no known conversion for implicit 'this' parameter from 'GCP_Form*' to 'int'| ||=== Build finished: 8 error(s), 1 warning(s) (0 minute(s), 1 second(s)) ===| ||=== Build finished: 8 error(s), 1 warning(s) (0 minute(s), 1 second(s)) ===|

winsvega commented 10 years ago

I don't have GNU GCC But I have MinGw 32 compiler. So i've fixed some issues to that compiler. See if it helps for your instance. (Commit MinGw Support)

lemonka commented 10 years ago
  1. Function atoi in GCP_Genetic seems to be non standard :/

I had to implement one this one:

http://www.jb.man.ac.uk/~slowe/cpp/itoa.html

  1. In GCP_Math -> d() and RAND_MAX where not defined in current scope (std)

I had to include

#include <cstdlib>
  1. Im compiling this at win7 but i cannot locate sprinf_s functon :/
            #ifdef __WIN32__
                sprintf_s(buffer,32, "%g", value);
            #else
                //snprintf(buffer, sizeof(buffer), "%.10g", value);
                //sprintf_s(buffer,32, "%g", value);
                sprintf (buffer, "%g", 8, value);
                gcp_itoa(value,buffer);
            #endif

Instead of i simply use only else part

  1. In GCP_SPointer - This seems to cause Segmentation Error:
        T* operator->() const   {   return _pointee;    }

0 00451B9C GCP_SPointer::operator->(this=0x1ae) (D:/Programming/SDL_Init/SDL_Init/GCP_SPointer.h:100)

1 0040A7F5 GCP_Form::OnEvent(this=0x37a608, GCP_EVENT=1, events=...) (D:\Programming\SDL_Init\SDL_Init\GCP_Form.cpp:349)

2 00402697 GCP_Controller::handleEvents(this=0x37a5a8, event=...) (D:\Programming\SDL_Init\SDL_Init\GCP.cpp:98)

3 00411B91 SDL_main(argc=argc@entry=1, argv=argv@entry=0x360008) (D:\Programming\SDL_Init\SDL_Init\main.cpp:54)

4 004230EC console_main(argc=argc@entry=1, argv=argv@entry=0x360008) (../src/main/windows/SDL_windows_main.c:140)

5 004232AD WinMain@16(hInst=0x400000, hPrev=0x0, szCmdLine=0x913758 "", sw=10) (../src/main/windows/SDL_windows_main.c:177)

6 0049E59B main () (??:??)

I also using mingw32 and IDE CodeBlocks on Win7x64- what is your IDE??

winsvega commented 10 years ago

I tested in VS2008/2012 Qt5:QtCreator MinGw

Now I installed CodeBlocks. I was able to fix some errors and compile, but linking phase give me this error:

C:/MinGW/share/locale/ru/LC_MESSAGES/ld.mo' unrecognized
collect2.exe: error: ld returned 5 exit status

Seems having problem with locale files. Anyway the code has been compiled so it should work on your side. Try test it. Commit: CodeBlocks compiler support.

lemonka commented 10 years ago

Everything seems to be working now. Why you have introduced this SPointer? Whitout it everything looked easier :/

Are you planning to add name property and function getbynameelement?

winsvega commented 10 years ago

In my program I manage a lot of objects (FormComponent) To this objects many interfaces are attached. (objects could be accessed both from gui and logic) So when I delete one object through GUI interface I used to do it like this

delete objectPtr;

But if I do it so, the other interface would encounter an error when trying deleting his objectPtr. Or worse if it tries to modify deleted objectPtr. If I type objectPtr=NULL; it will work only for one interface, other will not notice.

So I implemented SmartPointer to resolve such conflicts. If I delete an instance pointer somewhere I call object_spointer.setEmpty(true); So now other interfaces would know that pointer is to be deleted. And one of the most usefull things. Smart pointer keep reference count so it knows exactly when to call delete objectPtr;

You don't need to trace your every pointer to prevent memory leaks.

lemonka commented 10 years ago

I'm afraid that i also had to implement shared_ptr as well ;)

What about this: Are you planning to add name property and function getbynameelement?

lemonka commented 10 years ago

And I think it would be nice if Yours GUI would be rounded by some namespace just for cleaning up purposes

winsvega commented 10 years ago

well, its an opensource project. you can create fork & pull all the changes you want

lemonka commented 10 years ago

challenge accepted ;)