2012-04-10 10:36:38 +00:00
// Simple immediate mode UI implementation.
//
// Heavily inspired by Sol's tutorial at http://sol.gfxile.net/imgui/.
//
// A common pattern is Adapter classes for changing how things are drawn
// in lists, for example.
//
2012-07-26 11:47:15 +00:00
// Immediate UI works great for overlay UI for games, for example, but is actually
// not really a good idea for full app UIs. Also, animations are difficult because
// there's not really any good place to store state.
//
2012-04-10 10:36:38 +00:00
// hrydgard@gmail.com
# pragma once
// Simple ID generators. Absolutely no guarantee of collision avoidance if you implement
// multiple parts of a single screen of UI over multiple files unless you use IMGUI_SRC_ID.
# ifdef IMGUI_SRC_ID
2013-03-30 18:23:02 +00:00
# define GEN_ID (int)((IMGUI_SRC_ID) + (__LINE__))
# define GEN_ID_LOOP(i) (int)((IMGUI_SRC_ID) + (__LINE__) + (i) * 13612)
2012-04-10 10:36:38 +00:00
# else
# define GEN_ID (__LINE__)
2013-04-13 19:22:03 +00:00
# define GEN_ID_LOOP(i) ((__LINE__) + ((int)i) * 13612)
2012-04-10 10:36:38 +00:00
# endif
2012-04-12 10:52:55 +00:00
# include <string>
2012-04-10 10:36:38 +00:00
# include <vector>
2013-11-04 14:31:33 +00:00
# include "gfx_es2/draw_buffer.h"
# include "input/input_state.h"
2013-03-30 18:23:02 +00:00
class Texture ;
class UIContext ;
2012-04-12 17:45:21 +00:00
class LayoutManager {
public :
2012-10-29 13:49:09 +00:00
virtual void GetPos ( float * w , float * h , float * x , float * y ) const = 0 ;
2012-04-12 17:45:21 +00:00
} ;
class Pos : public LayoutManager {
public :
2012-10-29 13:49:09 +00:00
Pos ( float x , float y ) : x_ ( x ) , y_ ( y ) { }
virtual void GetPos ( float * w , float * h , float * x , float * y ) const {
* x = x_ ;
* y = y_ ;
}
2012-04-12 17:45:21 +00:00
private :
2012-10-29 13:49:09 +00:00
float x_ ;
float y_ ;
2012-04-12 17:45:21 +00:00
} ;
class HLinear : public LayoutManager {
public :
2012-10-29 13:49:09 +00:00
HLinear ( float x , float y , float spacing = 2.0f ) : x_ ( x ) , y_ ( y ) , spacing_ ( spacing ) { }
virtual void GetPos ( float * w , float * h , float * x , float * y ) const {
* x = x_ ;
* y = y_ ;
x_ + = * w + spacing_ ;
}
void Space ( float x ) {
x_ + = x ;
}
2012-04-12 17:45:21 +00:00
private :
2012-10-29 13:49:09 +00:00
mutable float x_ ;
float y_ ;
float spacing_ ;
2012-04-12 17:45:21 +00:00
} ;
class VLinear : public LayoutManager {
public :
2012-10-29 13:49:09 +00:00
VLinear ( float x , float y , float spacing = 2.0f ) : x_ ( x ) , y_ ( y ) , spacing_ ( spacing ) { }
virtual void GetPos ( float * w , float * h , float * x , float * y ) const {
* x = x_ ;
* y = y_ ;
y_ + = * h + spacing_ ;
}
2012-04-12 17:45:21 +00:00
private :
2012-10-29 13:49:09 +00:00
float x_ ;
mutable float y_ ;
float spacing_ ;
2012-04-12 17:45:21 +00:00
} ;
2013-04-07 00:46:59 +00:00
class HGrid : public LayoutManager {
public :
HGrid ( float x , float y , float xMax , float xSpacing = 2.0f , float ySpacing = 2.0f )
: x_ ( x ) , y_ ( y ) , xInit_ ( x ) , xMax_ ( xMax ) , xSpacing_ ( xSpacing ) , ySpacing_ ( ySpacing ) { }
virtual void GetPos ( float * w , float * h , float * x , float * y ) const {
* x = x_ ;
* y = y_ ;
x_ + = * w + xSpacing_ ;
if ( x_ > = xMax_ ) {
x_ = xInit_ ;
y_ + = * h + ySpacing_ ;
}
}
private :
mutable float x_ ;
mutable float y_ ;
float xInit_ ;
float xMax_ ;
float xSpacing_ ;
float ySpacing_ ;
} ;
class VGrid : public LayoutManager {
public :
VGrid ( float x , float y , float yMax , float xSpacing = 2.0f , float ySpacing = 2.0f )
: x_ ( x ) , y_ ( y ) , yInit_ ( y ) , yMax_ ( yMax ) , xSpacing_ ( xSpacing ) , ySpacing_ ( ySpacing ) { }
virtual void GetPos ( float * w , float * h , float * x , float * y ) const {
* x = x_ ;
* y = y_ ;
y_ + = * h + ySpacing_ ;
2013-04-14 09:57:38 +00:00
if ( y_ + * h > = yMax_ ) {
2013-04-07 00:46:59 +00:00
x_ + = * w + xSpacing_ ;
y_ = yInit_ ;
}
}
private :
mutable float x_ ;
mutable float y_ ;
float yInit_ ;
float yMax_ ;
float xSpacing_ ;
float ySpacing_ ;
} ;
2012-10-29 13:49:09 +00:00
// "Mouse" out of habit, applies just as well to touch events.
// TODO: Change to "pointer"
2012-05-13 22:42:42 +00:00
// This struct is zeroed on init, so should be valid at that state.
2012-10-28 10:37:10 +00:00
// Never inherit from this.
2012-04-10 10:36:38 +00:00
struct UIState {
2012-10-29 13:49:09 +00:00
int mousex [ MAX_POINTERS ] ;
int mousey [ MAX_POINTERS ] ;
bool mousedown [ MAX_POINTERS ] ;
bool mousepressed [ MAX_POINTERS ] ;
short mouseframesdown [ MAX_POINTERS ] ;
2012-04-10 10:36:38 +00:00
2012-10-29 13:49:09 +00:00
int mouseStartX [ MAX_POINTERS ] ;
int mouseStartY [ MAX_POINTERS ] ;
2012-04-10 10:36:38 +00:00
2012-10-29 13:49:09 +00:00
int hotitem [ MAX_POINTERS ] ;
int activeitem [ MAX_POINTERS ] ;
2012-04-10 10:36:38 +00:00
2012-10-29 13:49:09 +00:00
// keyboard focus, not currently used
int kbdwidget ;
int lastwidget ;
2012-04-11 15:07:28 +00:00
2012-10-29 13:49:09 +00:00
int ui_tick ;
2012-10-30 15:30:55 +00:00
// deprecated: tempfloat
float tempfloat ;
2012-04-10 10:36:38 +00:00
} ;
// This needs to be extern so that additional UI controls can be developed outside this file.
extern UIState uistate ;
struct Atlas ;
// This is the drawbuffer used for UI. Remember to flush it at the end of the frame.
// TODO: One should probably pass it in through UIInit.
extern DrawBuffer ui_draw2d ;
2012-10-29 13:49:09 +00:00
extern DrawBuffer ui_draw2d_front ; // for things that need to be on top of the rest
2012-04-10 10:36:38 +00:00
2012-07-16 22:00:48 +00:00
struct UITheme {
2012-10-29 13:49:09 +00:00
int uiFont ;
int uiFontSmall ;
int uiFontSmaller ;
int buttonImage ;
2012-10-30 16:36:28 +00:00
int buttonSelected ;
2012-10-29 13:49:09 +00:00
int checkOn ;
int checkOff ;
2012-07-16 22:00:48 +00:00
} ;
// The atlas needs to stick around, the theme is copied.
void UIInit ( const Atlas * atlas , const UITheme & theme ) ;
2012-11-20 22:35:40 +00:00
// Between these, UI components won't see pointer events.
void UIDisableBegin ( ) ;
void UIDisableEnd ( ) ;
2012-07-16 22:00:48 +00:00
// Just lets you retrieve the theme that was passed into UIInit, for your own controls for example.
UITheme & UIGetTheme ( ) ;
2012-04-10 10:36:38 +00:00
// TODO: These don't really belong here.
const int UI_SPACE = 32 ;
const int SMALL_BUTTON_WIDTH = 128 ;
const int LARGE_BUTTON_WIDTH = 192 ;
const int BUTTON_HEIGHT = 72 ;
struct SlideItem {
2012-10-29 13:49:09 +00:00
const char * text ;
int image ;
uint32_t bgColor ;
2012-04-10 10:36:38 +00:00
} ;
struct UISlideState {
2012-10-29 13:49:09 +00:00
float scroll ;
2012-04-10 10:36:38 +00:00
} ;
// Implement this interface to style your lists
class UIListAdapter {
public :
2012-10-29 13:49:09 +00:00
virtual size_t getCount ( ) const = 0 ;
virtual void drawItem ( int item , int x , int y , int w , int h , bool active ) const = 0 ;
virtual float itemHeight ( int itemIndex ) const { return 64 ; }
virtual bool itemEnabled ( int itemIndex ) const { return true ; }
2012-04-10 10:36:38 +00:00
} ;
class StringVectorListAdapter : public UIListAdapter {
public :
2012-10-29 13:49:09 +00:00
StringVectorListAdapter ( const std : : vector < std : : string > * items ) : items_ ( items ) { }
virtual size_t getCount ( ) const { return items_ - > size ( ) ; }
virtual void drawItem ( int item , int x , int y , int w , int h , bool active ) const ;
2012-04-10 10:36:38 +00:00
private :
2012-10-29 13:49:09 +00:00
const std : : vector < std : : string > * items_ ;
2012-04-10 10:36:38 +00:00
} ;
2012-04-11 15:07:28 +00:00
// Utility functions, useful when implementing your own controls
2012-04-26 22:48:30 +00:00
bool UIRegionHit ( int pointerId , int x , int y , int w , int h , int margin ) ;
2012-04-11 15:07:28 +00:00
2012-04-10 10:36:38 +00:00
// Call at start of frame
2013-03-30 18:23:02 +00:00
void UIBegin ( const GLSLProgram * shader ) ;
// Call at end of frame.
void UIEnd ( ) ;
void UIFlush ( ) ;
2012-04-10 10:36:38 +00:00
2012-04-26 22:48:30 +00:00
void UIUpdateMouse ( int i , float x , float y , bool down ) ;
2012-04-10 10:36:38 +00:00
2012-10-28 10:37:10 +00:00
// Call when you switch screens
void UIReset ( ) ;
2012-04-10 10:36:38 +00:00
// Returns 1 if clicked
2013-04-13 13:01:09 +00:00
int UIButton ( int id , const LayoutManager & layout , float w , float h , const char * text , int button_align ) ;
2012-10-29 13:49:09 +00:00
int UIImageButton ( int id , const LayoutManager & layout , float w , int image_id , int button_align ) ; // uses current UI atlas for fetching images.
2013-04-17 15:08:31 +00:00
int UITextureButton ( UIContext * ctx , int id , const LayoutManager & layout , float w , float h , Texture * texture , int button_align , uint32_t color , int drop_shadow = 0 ) ; // uses current UI atlas for fetching images.
2012-04-10 10:36:38 +00:00
// Returns 1 if clicked, puts the value in *value (where it also gets the current state).
int UICheckBox ( int id , int x , int y , const char * text , int align , bool * value ) ;
// Vertical slider. Not yet working.
2012-04-26 22:48:30 +00:00
// int UIVSlider(int id, int x, int y, int h, int max, int *value);
2012-04-10 10:36:38 +00:00
// Horizontal slider. Not yet working.
int UIHSlider ( int id , int x , int y , int w , int max , int * value ) ;
// Draws static text, that does not participate in any focusing scheme etc, it just is.
2012-04-12 10:52:55 +00:00
void UIText ( int font , int x , int y , const char * text , uint32_t color , float scale = 1.0f , int align = ALIGN_TOPLEFT ) ;
2012-04-10 10:36:38 +00:00
void UIText ( int x , int y , const char * text , uint32_t color , float scale = 1.0f , int align = ALIGN_TOPLEFT ) ;
2013-01-02 19:56:09 +00:00
void UIText ( int font , const LayoutManager & layout , const char * text , uint32_t color , float scale = 1.0f , int align = ALIGN_TOPLEFT ) ;
2012-04-10 10:36:38 +00:00
// Slide choice, like the Angry Birds level selector. Not yet working.
void UISlideChoice ( int id , int y , const SlideItem * items , int numItems , UISlideState * state ) ;
2012-09-17 19:21:34 +00:00
class UIList {
2012-10-29 13:49:09 +00:00
public :
UIList ( ) ;
bool scrolling ;
int activePointer ;
2012-10-30 12:07:55 +00:00
float startScrollY ;
2012-10-29 13:49:09 +00:00
float scrollY ;
2012-10-30 12:07:55 +00:00
float lastX ;
float lastY ;
2012-10-29 13:49:09 +00:00
float startDragY ;
2013-03-10 23:24:40 +00:00
float movedDistanceX ;
2012-10-29 13:49:09 +00:00
float movedDistanceY ;
2012-10-30 12:07:55 +00:00
float inertiaY ;
2012-10-29 13:49:09 +00:00
int dragFinger ;
int selected ;
// List view.
// return -1 = no selection
int Do ( int id , int x , int y , int w , int h , UIListAdapter * adapter ) ;
2012-10-31 12:35:11 +00:00
// Call this when the content has changed, to reset scroll position etc.
void contentChanged ( ) {
scrollY = 0.0f ;
inertiaY = 0.0f ;
}
2013-07-08 09:03:14 +00:00
void scrollRelative ( float delta ) {
scrollY + = delta ;
}
2012-10-29 13:49:09 +00:00
private :
2012-10-30 12:07:55 +00:00
// TODO: Migrate to using these directly.
void pointerDown ( int pointer , float x , float y ) ;
void pointerUp ( int pointer , float x , float y , bool inside ) ;
2012-11-21 14:30:43 +00:00
void pointerMove ( int pointer , float x , float y , bool inside ) ;
2012-10-30 12:07:55 +00:00
2012-10-29 13:49:09 +00:00
DISALLOW_COPY_AND_ASSIGN ( UIList ) ;
2012-09-17 19:21:34 +00:00
} ;
2012-04-10 10:36:38 +00:00