-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathzh_ui.h
204 lines (153 loc) · 4.91 KB
/
zh_ui.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// Copyright (c) 2021 Stoiko Todorov
// This work is licensed under the terms of the MIT license.
// For a copy, see https://opensource.org/licenses/MIT.
#ifndef ZH_UI_H
#define ZH_UI_H
typedef enum {
UIBR_IDLE,
UIBR_HOT,
UIBR_ACTIVE,
UIBR_RELEASED,
UIBR_NUM_RESULTS,
} uiWidgetResult_t;
// == user drawing routines ==
// TODO: redefine these with user implementations
// void UI_DrawFilledRect( int x, int y, int w, int h );
// void UI_DrawString( int x, int y, const char *str );
// void UI_MeasureString( const char *str, int *outW, int *outH );
// == core ==
int ZH_UI_OnMouseButton( int down );
void ZH_UI_Begin( int cursorX, int cursorY );
void ZH_UI_End( void );
#ifndef __COUNTER__
#error "Requires a compiler with a __COUNTER__ built-in macro"
#endif
#ifndef FILE_UID
#warning "A unique identifier per include should be defined. Defaulting to 0"
#define FILE_UID 0
#endif
// zero is reserved handle, thus COUNTER + 1
#define WG_GEN_ID(handle) (((handle & 0xffff)<<16)|(((__COUNTER__+1)&0xff)<<8)|(FILE_UID&0xff))
#define WG_DEFAULT_ID WG_GEN_ID(0xffff)
// == widgets ==
// use the corresponding macros for 'auto-generated' handles
uiWidgetResult_t ZH_UI_ClickRect_wg( int x, int y, int w, int h, int handle );
#define ZH_UI_ClickRect(x,y,w,h) ZH_UI_ClickRect_wg(x,y,w,h,WG_DEFAULT_ID )
#ifdef ZH_UI_IMLEMENTATION
#ifndef ZH_UI_MAX_RECTS
#define ZH_UI_MAX_RECTS 256
#endif
typedef enum {
UIMBS_NONE,
UIMBS_DOWN = 1 << 0,
UIMBS_UP = 1 << 1,
} uiMouseButtonState;
typedef struct {
int x, y, w, h;
int handle;
} uiRect_t;
static int ui_numRects;
static uiRect_t ui_rects[ZH_UI_MAX_RECTS];
static uiMouseButtonState ui_mouseButton;
// this widget is hovered over
static int ui_hotWidget;
// this widget is usually pressed but not released yet
static int ui_activeWidget;
static int ui_oldCursorX, ui_cursorX;
static int ui_oldCursorY, ui_cursorY;
static inline int UI_CursorInRect( int x, int y, int w, int h ) {
return ui_cursorX >= x && ui_cursorY >= y
&& ui_cursorX < x + w && ui_cursorY < y + h;
}
static void UI_DragPosition( uiWidgetResult_t res, int *ioX, int *ioY ) {
if ( res == UIBR_ACTIVE ) {
int dx = ui_cursorX - ui_oldCursorX;
int dy = ui_cursorY - ui_oldCursorY;
*ioX += dx;
*ioY += dy;
}
}
// this callback might be called multiple times inside a single frame
int ZH_UI_OnMouseButton( int down ) {
if ( down ) {
ui_mouseButton |= UIMBS_DOWN;
} else {
ui_mouseButton |= UIMBS_UP;
}
return ( ui_hotWidget || ui_activeWidget );
}
void ZH_UI_Begin( int cursorX, int cursorY ) {
ui_cursorX = cursorX;
ui_cursorY = cursorY;
}
void ZH_UI_End( void ) {
#define BUTTON_RELEASED() ((ui_mouseButton & UIMBS_UP)||ui_mouseButton==UIMBS_NONE)
// check if 'active' widget is still present on screen
if ( ui_activeWidget ) {
int i;
for ( i = 0; i < ui_numRects; i++ ) {
if ( ui_rects[i].handle == ui_activeWidget ) {
break;
}
}
// 'active' is no longer drawn/tested
if ( i == ui_numRects ) {
ui_activeWidget = 0;
}
}
// 'hot' widgets are obtained each frame
ui_hotWidget = 0;
// go back-to-front in the rectangles stack and check for hits
for ( int i = ui_numRects - 1; i >= 0; i-- ) {
uiRect_t *r = &ui_rects[i];
if ( UI_CursorInRect( r->x, r->y, r->w, r->h ) ) {
if ( BUTTON_RELEASED() ) {
// there is only one hot widget
ui_activeWidget = 0;
ui_hotWidget = r->handle;
} else if ( ! ui_activeWidget ) {
ui_activeWidget = r->handle;
}
break;
}
}
if ( BUTTON_RELEASED() ) {
ui_activeWidget = 0;
}
// clear rects stack
ui_numRects = 0;
// handle mouse buttons specially
if ( ui_mouseButton & UIMBS_UP ) {
ui_mouseButton = UIMBS_NONE;
}
ui_oldCursorX = ui_cursorX;
ui_oldCursorY = ui_cursorY;
#undef BUTTON_RELEASED
}
uiWidgetResult_t ZH_UI_ClickRect_wg( int x, int y, int w, int h, int handle ) {
uiWidgetResult_t res = UIBR_IDLE;
// push rectangle in the list
// stop checking widgets if too many
if ( ui_numRects == ZH_UI_MAX_RECTS ) {
return res;
}
ui_rects[ui_numRects] = ( uiRect_t ) { x, y, w, h, handle, };
ui_numRects++;
// click
if ( ui_activeWidget == handle ) {
if ( ( ui_mouseButton & UIMBS_UP ) && UI_CursorInRect( x, y, w, h ) ) {
res = UIBR_RELEASED;
} else {
res = UIBR_ACTIVE;
}
} else if ( handle == ui_hotWidget ) {
res = UIBR_HOT;
}
return res;
}
void ZH_UI_Drag_wg( int *ioX, int *ioY, int w, int h, int handle ) {
uiWidgetResult_t res = ZH_UI_ClickRect_wg( *ioX, *ioY, w, h, handle );
UI_DragPosition( res, ioX, ioY );
}
#endif // ZH_UI_IMLEMENTATION
#endif // ZH_UI_H