summaryrefslogtreecommitdiff
path: root/src/input.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2024-07-13 10:52:24 -0700
committer3gg <3gg@shellblade.net>2024-07-13 10:52:24 -0700
commita4294e4a94189dffb1fdf99c9a60d87d77272926 (patch)
tree2e92f7c95116861bc39f4dae1d0ab5d388550000 /src/input.c
parentcf9579d7546c04dbc708bd8719e3f935a28088bd (diff)
Restructure project.
Diffstat (limited to 'src/input.c')
-rw-r--r--src/input.c178
1 files changed, 178 insertions, 0 deletions
diff --git a/src/input.c b/src/input.c
new file mode 100644
index 0000000..c4b1be7
--- /dev/null
+++ b/src/input.c
@@ -0,0 +1,178 @@
1#include <ui.h>
2
3#include "event.h"
4#include "uiLibrary.h"
5#include "widget/widget.h"
6
7#include <cassert.h>
8
9#define Max(a, b) ((a) > (b) ? (a) : (b))
10
11/// Return true if the rectangle contains the point.
12static bool RectContains(uiRect rect, uiPoint point) {
13 return (rect.x <= point.x) && (point.x <= (rect.x + rect.width)) &&
14 (rect.y <= point.y) && (point.y <= (rect.y + rect.height));
15}
16
17/// Get the bottom-most widget under the given mouse position.
18static uiWidget* GetWidgetUnderMouse(uiWidget* parent, uiPoint mouse) {
19 assert(parent);
20
21 // First check the children so that the selection is from "most specific" to
22 // "less specific" from the user's perspective.
23 list_foreach(parent->children, child, {
24 uiWidget* target = GetWidgetUnderMouse(child, mouse);
25 if (target != 0) {
26 return target;
27 }
28 });
29
30 if (RectContains(parent->rect, mouse)) {
31 return parent;
32 }
33
34 return 0;
35}
36
37/// Get the table row at the given pixel position.
38static void GetTableRowColAtXy(
39 const uiTable* table, uiPoint p, int* out_row, int* out_col) {
40 assert(table);
41 assert(out_row);
42 assert(out_col);
43
44 const uiWidget* widget = (uiWidget*)table;
45
46 int col = -1;
47 int row = -1;
48
49 if (RectContains(widget->rect, p)) {
50 int x = p.x - widget->rect.x;
51 for (col = 0; (col < table->cols) && (x > table->widths[col]); ++col) {
52 x -= table->widths[col];
53 }
54 // 0 is the header and we want to map the first row to 0, so -1.
55 row = table->offset +
56 ((p.y - widget->rect.y) / g_ui.font->header.glyph_height) - 1;
57 // Out-of-bounds check.
58 if ((col >= table->cols) || (row >= table->rows)) {
59 col = row = -1;
60 }
61 }
62
63 *out_col = col;
64 *out_row = row;
65}
66
67/// Process a table click event.
68static void ClickTable(uiTable* table, const uiMouseClickEvent* event) {
69 assert(table);
70 assert(event);
71
72 int row, col;
73 GetTableRowColAtXy(table, event->mouse_position, &row, &col);
74
75 if ((row != -1) && (col != -1)) {
76 PushWidgetEvent(&(uiWidgetEvent){
77 .type = uiWidgetEventClick,
78 .widget = uiMakeTablePtr(table),
79 .table_click = (uiTableClickEvent){.row = row, .col = col}
80 });
81 }
82}
83
84/// Process a table scroll event.
85static void ScrollTable(uiTable* table, const uiMouseScrollEvent* event) {
86 assert(table);
87 assert(event);
88 table->offset = Max(0, table->offset - event->scroll_offset);
89}
90
91/// Process a scroll event.
92static bool ProcessScrollEvent(
93 uiWidget* widget, const uiMouseScrollEvent* event) {
94 assert(widget);
95 assert(event);
96
97 bool processed = false;
98
99 switch (widget->type) {
100 case uiTypeTable:
101 ScrollTable((uiTable*)widget, event);
102 processed = true;
103 break;
104 default:
105 break;
106 }
107
108 return processed;
109}
110
111/// Process a click event.
112static bool ProcessClickEvent(
113 uiWidget* widget, const uiMouseClickEvent* event) {
114 assert(widget);
115 assert(event);
116
117 bool processed = false;
118
119 switch (widget->type) {
120 case uiTypeTable:
121 ClickTable((uiTable*)widget, event);
122 processed = true;
123 break;
124 default:
125 break;
126 }
127
128 return processed;
129}
130
131bool uiSendEvent(uiFrame* frame, const uiInputEvent* event) {
132 assert(frame);
133 assert(event);
134
135 uiWidget* widget = (uiWidget*)frame;
136
137 bool processed = false;
138
139 switch (event->type) {
140 case uiEventMouseButton: {
141 const uiMouseButtonEvent* ev = &event->mouse_button;
142
143 uiMouseButtonState* prev_state = &g_ui.mouse_button_state[ev->button];
144
145 if ((*prev_state == uiMouseDown) && (ev->state == uiMouseUp)) {
146 // Click.
147 uiSendEvent(
148 frame,
149 &(uiInputEvent){
150 .type = uiEventMouseClick,
151 .mouse_click = (uiMouseClickEvent){
152 .button = ev->button, .mouse_position = ev->mouse_position}
153 });
154 }
155
156 *prev_state = ev->state;
157 break;
158 }
159 case uiEventMouseClick: {
160 const uiMouseClickEvent* ev = &event->mouse_click;
161 uiWidget* target = GetWidgetUnderMouse(widget, ev->mouse_position);
162 if (target) {
163 processed = ProcessClickEvent(target, ev);
164 }
165 break;
166 }
167 case uiEventMouseScroll: {
168 const uiMouseScrollEvent* ev = &event->mouse_scroll;
169 uiWidget* target = GetWidgetUnderMouse(widget, ev->mouse_position);
170 if (target) {
171 processed = ProcessScrollEvent(target, ev);
172 }
173 break;
174 }
175 }
176
177 return processed;
178}