2

I'm designing a simple GUI framework from scratch as a project, using OpenGL and nothing else external and need some advice on how I might implement user interaction.

Basically, I've a base class GUIItem from which all elements inherit. This gives each item some basic variables such as position, a vector to contain child elements as well as some basic functions for mouse movement and clicking.

All elements are setup as above, with their relevant member variables.

What I'm struggling with is how to implement user interaction properly. In my window manager I would create a new instance of an item, say GUIButton and call it button1. The window manager would, upon a click occurring, iterate through its list of elements and any child elements they may have, calculating a rectangular area around the object based on its coordinates, height and width, then running any "on click" function associated with said item, like change the value of textlabel1.

Firstly, is there a better way to do this calculation? It would work for rectangular elements but spherical objects and others would have a much larger erroneous area which could be clicked. Ideally I would check pixels but I've no real idea how that would be achieved. I've heard about but never used GLUT (my project only allows use of this for handling mouse/keyboard interaction though). Does GLUT provide anything to assist in this case?

My main issue is with handling what would occur when an "On click" event actually occurred. At the moment GUIButton for example, has an "On click" function built in, so as far as I can see, I'd have to do something like make it a virtual function, meaning that each new button I created would have to have its own class just to overwrite the "on click" function and each instance of a button would be an instance of a unique class that simply inherited off of GUIButton. This seems messy to me, as I've no idea where I would store all those classes, and it seems a lot of extra code. Would I be creating a button1.cpp and button1.h file?

Any advice on this really would be welcome as I'm new to C++, OpenGL and it's the first time I've been exposed to GUI programming and there's not a lot to go on when an existing GUI framework is the usual choice.

Hexodus
  • 369
  • 6
  • 18
  • Your `GUIItem` has some problems. Not all GUI elements should be able to have children etc.etc. – deviantfan Dec 13 '15 at 03:58
  • About your spherical objects: In the parent, just use a rectangle check, and the spherical thing can can check more itself, sepending on it's shape. ... Don't iterate through all pixels. There has to be some regularity in the object, or not? Depending on the shape, it will require a bit math to check if it is clicked, but still, don't iterate pixels. – deviantfan Dec 13 '15 at 04:00
  • About what a Button should do: Do you know function pointers and functors? There's no reason to make a new class for every button. – deviantfan Dec 13 '15 at 04:03
  • And finally, "if" you're using OpenGL because you don't know something else, stop right now. OpenGL for "boring" normal windows is overkill and resource wasting. – deviantfan Dec 13 '15 at 04:06
  • @deviantfan thanks for the suggestions. As for function pointers, not something I've used before but I'll take a look. OpenGL is unfortunately a requirement of my project spec but I otherwise agree. – Hexodus Dec 13 '15 at 04:11
  • You may implement in addition to your virtual onClick, a function array to launch when button is clicked. In my c# gui toolkit, I have both. a default overridable onEvent, and an event handler allowing registration of custom handler without subclassing the gui item. – j-p Dec 13 '15 at 08:41

1 Answers1

2

if you want something stupidly simple and fast then you could:

  1. create shadow screen buffer containing ID/index/pointer instead of color

  2. pre-render this buffer

    Just render each of your visual component to it but instead coloring/texturing just fill in the ID/index/pointer of rendered component. Do not forget to clear this with some NULL first ... After this you should have mask of your components. You need to do this just once ...

  3. On mouse events

    you simply convert mouse coordinates to the shadow screen space and pick the value. If it is NULL then you clicked or whatever on empty area. If it contains ID instead update or call the callbacks for component ID. if you have a list of all components then ID can be the list index, otherwise use its actual pointer or encode in style (component_type, component_index). As you can see this is pretty fast O(1) item selection no matter how many components you have ... The shadow screen can have different resolution then your actual screen (to preserve memory).

This have pixel perfect mouse selection accuracy no matter the shape of your components without the need for nested component search loops.

[Notes]

As I did this stuff here are some hints:

create a window class containing configuration of your components for single screen. Programs have usually more screens with different set of components and doing dynamically the screens over and over again just because you switch page/screen sucks.

use separate list of components one list per component type.

create IDE editor for your windows see drag & drop example in C++ it might get handy for this. Add get,set functions controlled by string/enum or flag to easy obtain/change properties to make Object Inspector possible. Also this is how mine IDE looks like:

vcl ide

The window is saved from IDE directly as C++ code I can just copy to my App. This is the above example without the knob (forgot to save it):

//---------------------------------------------------------------------------
// OpenGL VCL window beg: win
    win.grid.allocate(0);
    win.grid.num=0;
    win.scale.allocate(0);
    win.scale.num=0;
    win.button.allocate(0);
    win.button.num=0;
    win.knob.allocate(0);
    win.knob.num=0;
    win.scrollbar.allocate(3);
    win.scrollbar.num=3;
    win.scrollbar[0].x0=200.0;
    win.scrollbar[0].y0=19.0;
    win.scrollbar[0].xs=256.0;
    win.scrollbar[0].ys=16.0;
    win.scrollbar[0].fxs=8.0;
    win.scrollbar[0].fys=19.0;
    win.scrollbar[0].name="_vcl_scrollbar0";
    win.scrollbar[0].hint="";
    win.scrollbar[0].min=0.000;
    win.scrollbar[0].max=1.000;
    win.scrollbar[0].pos=0.000;
    win.scrollbar[0].dpos=0.100;
    win.scrollbar[0].horizontal=1;
    win.scrollbar[0].style=0;
    win.scrollbar[0].resize();
    win.scrollbar[1].x0=200.0;
    win.scrollbar[1].y0=45.0;
    win.scrollbar[1].xs=256.0;
    win.scrollbar[1].ys=16.0;
    win.scrollbar[1].fxs=8.0;
    win.scrollbar[1].fys=19.0;
    win.scrollbar[1].name="_vcl_scrollbar1";
    win.scrollbar[1].hint="";
    win.scrollbar[1].min=0.000;
    win.scrollbar[1].max=1.000;
    win.scrollbar[1].pos=0.000;
    win.scrollbar[1].dpos=0.100;
    win.scrollbar[1].horizontal=1;
    win.scrollbar[1].style=0;
    win.scrollbar[1].resize();
    win.scrollbar[2].x0=200.0;
    win.scrollbar[2].y0=70.0;
    win.scrollbar[2].xs=256.0;
    win.scrollbar[2].ys=16.0;
    win.scrollbar[2].fxs=8.0;
    win.scrollbar[2].fys=19.0;
    win.scrollbar[2].name="_vcl_scrollbar2";
    win.scrollbar[2].hint="";
    win.scrollbar[2].min=0.000;
    win.scrollbar[2].max=1.000;
    win.scrollbar[2].pos=0.000;
    win.scrollbar[2].dpos=0.100;
    win.scrollbar[2].horizontal=1;
    win.scrollbar[2].style=0;
    win.scrollbar[2].resize();
    win.interpbox.allocate(0);
    win.interpbox.num=0;
    win.dblist.allocate(0);
    win.dblist.num=0;
// OpenGL VCL window end: win
//---------------------------------------------------------------------------

Look at images here plotting real time Data on Oscillocope for some ideas (I got this working for both GDI and OpenGL)

It is better to use pixel units instead of OpenGL <-1,+1> screen units for better visual quality and editing comfort.

Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380