summaryrefslogtreecommitdiff
path: root/src/input.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/input.c')
-rw-r--r--src/input.c226
1 files changed, 195 insertions, 31 deletions
diff --git a/src/input.c b/src/input.c
index 4461c8b..6a7f4d0 100644
--- a/src/input.c
+++ b/src/input.c
@@ -6,9 +6,6 @@
6 6
7#include <cassert.h> 7#include <cassert.h>
8 8
9#define Min(a, b) ((a) < (b) ? (a) : (b))
10#define Max(a, b) ((a) > (b) ? (a) : (b))
11
12/// Return true if the rectangle contains the point. 9/// Return true if the rectangle contains the point.
13static bool RectContains(uiRect rect, uiPoint point) { 10static bool RectContains(uiRect rect, uiPoint point) {
14 return (rect.x <= point.x) && (point.x <= (rect.x + rect.width)) && 11 return (rect.x <= point.x) && (point.x <= (rect.x + rect.width)) &&
@@ -35,17 +32,59 @@ static uiWidget* GetWidgetUnderMouse(uiWidget* parent, uiPoint mouse) {
35 return 0; 32 return 0;
36} 33}
37 34
38/// Get the table row at the given pixel position. 35// -----------------------------------------------------------------------------
36// Scrollbar.
37
38/// Process a scrollbar mouse button event.
39static void MouseButtonScrollbar(
40 uiScrollbar* scrollbar, const uiMouseButtonEvent* event) {
41 assert(scrollbar);
42 assert(event);
43
44 if (event->button_state == uiMouseDown) {
45 UI_LOG("scroll start");
46 // TODO: I don't quite like this global state, but I also don't want to
47 // store input state inside the widgets. Define a struct for input handling
48 // and pass it to these functions instead?
49 g_ui.scroll.scrollbar_handle_y_start = scrollbar->handle_y;
50 g_ui.scroll.scrolling = true;
51 } else if (event->button_state == uiMouseUp) {
52 UI_LOG("scroll end");
53 g_ui.scroll.scrolling = false;
54 }
55}
56
57/// Process a scrollbar mouse move event.
58///
59/// Return the amount scrolled relative to the scroll starting position, in
60/// pixels.
61static int MouseMoveScrollbar(
62 uiScrollbar* scrollbar, const uiMouseMoveEvent* event) {
63 assert(scrollbar);
64 assert(event);
65
66 const int delta = event->mouse_position.y - g_ui.mouse_down.start_point.y;
67 ScrollbarScroll(scrollbar, g_ui.scroll.scrollbar_handle_y_start + delta);
68 return delta;
69}
70
71// -----------------------------------------------------------------------------
72// Table.
73
74/// Get the table row and column at the given pixel position, or whether the
75/// scrollbar was hit.
39static void GetTableRowColAtXy( 76static void GetTableRowColAtXy(
40 const uiTable* table, uiPoint p, int* out_row, int* out_col) { 77 const uiTable* table, uiPoint p, int* out_row, int* out_col,
78 bool* out_scrollbar) {
41 assert(table); 79 assert(table);
42 assert(out_row); 80 assert(out_row);
43 assert(out_col); 81 assert(out_col);
44 82
45 const uiWidget* widget = (uiWidget*)table; 83 const uiWidget* widget = (uiWidget*)table;
46 84
47 int col = -1; 85 int col = -1;
48 int row = -1; 86 int row = -1;
87 bool scrollbar = false;
49 88
50 if (RectContains(widget->rect, p)) { 89 if (RectContains(widget->rect, p)) {
51 int x = p.x - widget->rect.x; 90 int x = p.x - widget->rect.x;
@@ -55,14 +94,40 @@ static void GetTableRowColAtXy(
55 // 0 is the header, and we want to map the first row to 0, so -1. 94 // 0 is the header, and we want to map the first row to 0, so -1.
56 row = table->offset + 95 row = table->offset +
57 ((p.y - widget->rect.y) / g_ui.font->header.glyph_height) - 1; 96 ((p.y - widget->rect.y) / g_ui.font->header.glyph_height) - 1;
58 // Out-of-bounds check. 97 // Scrollbar area check.
59 if ((col >= table->cols) || (row >= table->rows)) { 98 if ((col >= table->cols) && (row < table->rows)) {
99 col = -1;
100 scrollbar = true;
101 }
102 // Out of bounds.
103 else if ((col >= table->cols) || (row >= table->rows)) {
60 col = row = -1; 104 col = row = -1;
61 } 105 }
62 } 106 }
63 107
64 *out_col = col; 108 *out_col = col;
65 *out_row = row; 109 *out_row = row;
110 *out_scrollbar = scrollbar;
111}
112
113/// Process a table mouse button event.
114static void MouseButtonTable(uiTable* table, const uiMouseButtonEvent* event) {
115 assert(table);
116 assert(event);
117
118 if (event->button_state == uiMouseDown) {
119 int row, col;
120 bool scrollbar;
121 GetTableRowColAtXy(table, event->mouse_position, &row, &col, &scrollbar);
122
123 if (scrollbar) {
124 MouseButtonScrollbar(&table->scrollbar, event);
125 }
126 } else if ((event->button_state == uiMouseUp) && g_ui.scroll.scrolling) {
127 // The mouse up event need not happen while the mouse is on the scrollbar,
128 // so process mouse-up regardless of whether the mouse is.
129 MouseButtonScrollbar(&table->scrollbar, event);
130 }
66} 131}
67 132
68/// Process a table click event. 133/// Process a table click event.
@@ -70,8 +135,9 @@ static void ClickTable(uiTable* table, const uiMouseClickEvent* event) {
70 assert(table); 135 assert(table);
71 assert(event); 136 assert(event);
72 137
73 int row, col; 138 int row, col;
74 GetTableRowColAtXy(table, event->mouse_position, &row, &col); 139 bool scrollbar;
140 GetTableRowColAtXy(table, event->mouse_position, &row, &col, &scrollbar);
75 141
76 if ((row != -1) && (col != -1)) { 142 if ((row != -1) && (col != -1)) {
77 PushWidgetEvent(&(uiWidgetEvent){ 143 PushWidgetEvent(&(uiWidgetEvent){
@@ -86,14 +152,25 @@ static void ClickTable(uiTable* table, const uiMouseClickEvent* event) {
86static void ScrollTable(uiTable* table, const uiMouseScrollEvent* event) { 152static void ScrollTable(uiTable* table, const uiMouseScrollEvent* event) {
87 assert(table); 153 assert(table);
88 assert(event); 154 assert(event);
89 table->offset = 155
90 Min(table->rows - table->num_visible_rows, 156 const int row = table->offset - event->scroll_offset;
91 Max(0, table->offset - event->scroll_offset)); 157 uiTableScroll(table, row);
92} 158}
93 159
94/// Process a scroll event. 160/// Process a table mouse move event.
95static bool ProcessScrollEvent( 161static void MouseMoveTable(uiTable* table, const uiMouseMoveEvent* event) {
96 uiWidget* widget, const uiMouseScrollEvent* event) { 162 assert(table);
163 assert(event);
164
165 if (g_ui.scroll.scrolling) {
166 MouseMoveScrollbar(&table->scrollbar, event);
167 SyncTableToScrollbar(table);
168 }
169}
170
171/// Process a mouse button event.
172static bool ProcessMouseButtonEvent(
173 uiWidget* widget, const uiMouseButtonEvent* event) {
97 assert(widget); 174 assert(widget);
98 assert(event); 175 assert(event);
99 176
@@ -101,7 +178,7 @@ static bool ProcessScrollEvent(
101 178
102 switch (widget->type) { 179 switch (widget->type) {
103 case uiTypeTable: 180 case uiTypeTable:
104 ScrollTable((uiTable*)widget, event); 181 MouseButtonTable((uiTable*)widget, event);
105 processed = true; 182 processed = true;
106 break; 183 break;
107 default: 184 default:
@@ -112,7 +189,7 @@ static bool ProcessScrollEvent(
112} 189}
113 190
114/// Process a click event. 191/// Process a click event.
115static bool ProcessClickEvent( 192static bool ProcessMouseClickEvent(
116 uiWidget* widget, const uiMouseClickEvent* event) { 193 uiWidget* widget, const uiMouseClickEvent* event) {
117 assert(widget); 194 assert(widget);
118 assert(event); 195 assert(event);
@@ -131,21 +208,89 @@ static bool ProcessClickEvent(
131 return processed; 208 return processed;
132} 209}
133 210
211/// Process a scroll event.
212static bool ProcessMouseScrollEvent(
213 uiWidget* widget, const uiMouseScrollEvent* event) {
214 assert(widget);
215 assert(event);
216
217 bool processed = false;
218
219 switch (widget->type) {
220 case uiTypeTable:
221 ScrollTable((uiTable*)widget, event);
222 processed = true;
223 break;
224 default:
225 break;
226 }
227
228 return processed;
229}
230
231/// Process a mouse move event.
232static bool ProcessMouseMoveEvent(
233 uiWidget* widget, const uiMouseMoveEvent* event) {
234 assert(widget);
235 assert(event);
236
237 bool processed = false;
238
239 switch (widget->type) {
240 case uiTypeTable:
241 MouseMoveTable((uiTable*)widget, event);
242 processed = true;
243 break;
244 default:
245 break;
246 }
247
248 return processed;
249}
250
134bool uiSendEvent(uiFrame* frame, const uiInputEvent* event) { 251bool uiSendEvent(uiFrame* frame, const uiInputEvent* event) {
135 assert(frame); 252 assert(frame);
136 assert(event); 253 assert(event);
137 254
138 uiWidget* widget = (uiWidget*)frame; 255 // TODO: processed != redraw. The client will redraw if this function
139 256 // returns
257 // true, but whether an event was processed does it imply that the UI needs
258 // a redraw.
259 //
260 // TODO: Also think about limiting redraws in xplorer to like 25fps or
261 // something in a "battery saving" mode of sorts.
140 bool processed = false; 262 bool processed = false;
141 263
142 switch (event->type) { 264 switch (event->type) {
143 case uiEventMouseButton: { 265 case uiEventMouseButton: {
144 const uiMouseButtonEvent* ev = &event->mouse_button; 266 const uiMouseButtonEvent* ev = &event->mouse_button;
145 267
146 uiMouseButtonState* prev_state = &g_ui.mouse_button_state[ev->button]; 268 // Update the mouse button state.
269 uiMouseButtonState* button_state = &g_ui.mouse_button_state[ev->button];
270 const uiMouseButtonState prev_state = *button_state;
271 *button_state = ev->button_state;
272
273 // If this is a mouse up event and a widget is currently being
274 // mouse-downed, send the event to that widget.
275 if ((ev->button_state == uiMouseUp) &&
276 !uiIsNullptr(g_ui.mouse_down.widget)) {
277 processed = ProcessMouseButtonEvent(g_ui.mouse_down.widget.widget, ev);
278 g_ui.mouse_down = (uiMouseDownState){0};
279 } else { // Mouse down or no widget.
280 uiWidget* target =
281 GetWidgetUnderMouse((uiWidget*)frame, ev->mouse_position);
282 if (target) {
283 processed = ProcessMouseButtonEvent(target, ev);
284 if (processed && (ev->button_state == uiMouseDown)) {
285 g_ui.mouse_down.widget = uiMakeWidgetPtr(target);
286 g_ui.mouse_down.start_point = ev->mouse_position;
287 } else {
288 g_ui.mouse_down = (uiMouseDownState){0};
289 }
290 }
291 }
147 292
148 if ((*prev_state == uiMouseDown) && (ev->state == uiMouseUp)) { 293 if ((prev_state == uiMouseDown) && (ev->button_state == uiMouseUp)) {
149 // Click. 294 // Click.
150 uiSendEvent( 295 uiSendEvent(
151 frame, 296 frame,
@@ -155,26 +300,45 @@ bool uiSendEvent(uiFrame* frame, const uiInputEvent* event) {
155 .button = ev->button, .mouse_position = ev->mouse_position} 300 .button = ev->button, .mouse_position = ev->mouse_position}
156 }); 301 });
157 } 302 }
158
159 *prev_state = ev->state;
160 break; 303 break;
161 } 304 }
162 case uiEventMouseClick: { 305 case uiEventMouseClick: {
163 const uiMouseClickEvent* ev = &event->mouse_click; 306 const uiMouseClickEvent* ev = &event->mouse_click;
164 uiWidget* target = GetWidgetUnderMouse(widget, ev->mouse_position); 307
308 uiWidget* target =
309 GetWidgetUnderMouse((uiWidget*)frame, ev->mouse_position);
165 if (target) { 310 if (target) {
166 processed = ProcessClickEvent(target, ev); 311 processed = ProcessMouseClickEvent(target, ev);
167 } 312 }
168 break; 313 break;
169 } 314 }
170 case uiEventMouseScroll: { 315 case uiEventMouseScroll: {
171 const uiMouseScrollEvent* ev = &event->mouse_scroll; 316 const uiMouseScrollEvent* ev = &event->mouse_scroll;
172 uiWidget* target = GetWidgetUnderMouse(widget, ev->mouse_position); 317
318 uiWidget* target =
319 GetWidgetUnderMouse((uiWidget*)frame, ev->mouse_position);
173 if (target) { 320 if (target) {
174 processed = ProcessScrollEvent(target, ev); 321 processed = ProcessMouseScrollEvent(target, ev);
175 } 322 }
176 break; 323 break;
177 } 324 }
325 case uiEventMouseMove: {
326 const uiMouseMoveEvent* ev = &event->mouse_move;
327
328 // If a widget is currently being moused-down, send the event to that
329 // widget.
330 if (!uiIsNullptr(g_ui.mouse_down.widget)) {
331 processed = ProcessMouseMoveEvent(g_ui.mouse_down.widget.widget, ev);
332 } else {
333 uiWidget* target =
334 GetWidgetUnderMouse((uiWidget*)frame, ev->mouse_position);
335 if (target) {
336 processed = ProcessMouseMoveEvent(target, ev);
337 }
338 }
339
340 break;
341 }
178 } 342 }
179 343
180 return processed; 344 return processed;