summaryrefslogtreecommitdiff
path: root/src/ui/widget.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/widget.c')
-rw-r--r--src/ui/widget.c96
1 files changed, 77 insertions, 19 deletions
diff --git a/src/ui/widget.c b/src/ui/widget.c
index 8a7127a2..b509cbe2 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -34,6 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
34#include <the_Foundation/ptrarray.h> 34#include <the_Foundation/ptrarray.h>
35#include <the_Foundation/ptrset.h> 35#include <the_Foundation/ptrset.h>
36#include <SDL_mouse.h> 36#include <SDL_mouse.h>
37#include <SDL_timer.h>
37#include <stdarg.h> 38#include <stdarg.h>
38 39
39#if defined (iPlatformAppleMobile) 40#if defined (iPlatformAppleMobile)
@@ -1136,18 +1137,36 @@ void scrollInfo_Widget(const iWidget *d, iWidgetScrollInfo *info) {
1136 } 1137 }
1137} 1138}
1138 1139
1139iBool scrollOverflow_Widget(iWidget *d, int delta) { 1140static iBool isOverflowScrollPossible_Widget_(const iWidget *d, int delta) {
1141 if (~d->flags & overflowScrollable_WidgetFlag) {
1142 return iFalse;
1143 }
1140 iRect bounds = boundsWithoutVisualOffset_Widget(d); 1144 iRect bounds = boundsWithoutVisualOffset_Widget(d);
1141 const iRect winRect = adjusted_Rect(safeRect_Root(d->root), 1145 const iRect winRect = adjusted_Rect(safeRect_Root(d->root),
1142 zero_I2(), 1146 zero_I2(),
1143 init_I2(0, -get_MainWindow()->keyboardHeight)); 1147 init_I2(0, -get_MainWindow()->keyboardHeight));
1144 const int yTop = top_Rect(winRect); 1148 const int yTop = top_Rect(winRect);
1145 const int yBottom = bottom_Rect(winRect); 1149 const int yBottom = bottom_Rect(winRect);
1146 if (top_Rect(bounds) >= yTop && bottom_Rect(bounds) < yBottom) { 1150 if (delta == 0) {
1147 return iFalse; /* fits inside just fine */ 1151 if (top_Rect(bounds) >= yTop && bottom_Rect(bounds) <= yBottom) {
1152 return iFalse; /* fits inside just fine */
1153 }
1154 }
1155 else if (delta > 0) {
1156 return top_Rect(bounds) < yTop;
1157 }
1158 return bottom_Rect(bounds) > yBottom;
1159}
1160
1161iBool scrollOverflow_Widget(iWidget *d, int delta) {
1162 if (!isOverflowScrollPossible_Widget_(d, delta)) {
1163 return iFalse;
1148 } 1164 }
1149 //const int safeBottom = rootSize.y - yBottom; 1165 iRect bounds = boundsWithoutVisualOffset_Widget(d);
1150 iRangei validPosRange = { bottom_Rect(winRect) - height_Rect(bounds), yTop }; 1166 const iRect winRect = adjusted_Rect(safeRect_Root(d->root),
1167 zero_I2(),
1168 init_I2(0, -get_MainWindow()->keyboardHeight));
1169 iRangei validPosRange = { bottom_Rect(winRect) - height_Rect(bounds), top_Rect(winRect) };
1151 if (validPosRange.start > validPosRange.end) { 1170 if (validPosRange.start > validPosRange.end) {
1152 validPosRange.start = validPosRange.end; /* no room to scroll */ 1171 validPosRange.start = validPosRange.end; /* no room to scroll */
1153 } 1172 }
@@ -1170,21 +1189,29 @@ iBool scrollOverflow_Widget(iWidget *d, int delta) {
1170 else { 1189 else {
1171 bounds.pos.y = iClamp(bounds.pos.y, validPosRange.start, validPosRange.end); 1190 bounds.pos.y = iClamp(bounds.pos.y, validPosRange.start, validPosRange.end);
1172 } 1191 }
1173// if (delta >= 0) {
1174// bounds.pos.y = iMin(bounds.pos.y, yTop);
1175// }
1176// else {
1177// bounds.pos.y = iMax(bounds.pos.y, );
1178// }
1179 const iInt2 newPos = windowToInner_Widget(d->parent, bounds.pos); 1192 const iInt2 newPos = windowToInner_Widget(d->parent, bounds.pos);
1180 if (!isEqual_I2(newPos, d->rect.pos)) { 1193 if (!isEqual_I2(newPos, d->rect.pos)) {
1181 d->rect.pos = newPos; 1194 d->rect.pos = newPos;
1182// refresh_Widget(d);
1183 postRefresh_App(); 1195 postRefresh_App();
1184 } 1196 }
1185 return height_Rect(bounds) > height_Rect(winRect); 1197 return height_Rect(bounds) > height_Rect(winRect);
1186} 1198}
1187 1199
1200static uint32_t lastHoverOverflowMotionTime_;
1201
1202static void overflowHoverAnimation_(iAny *widget) {
1203 iWindow *win = window_Widget(widget);
1204 iInt2 coord = mouseCoord_Window(win, 0);
1205 /* A motion event will cause an overflow window to scroll. */
1206 SDL_MouseMotionEvent ev = {
1207 .type = SDL_MOUSEMOTION,
1208 .windowID = SDL_GetWindowID(win->win),
1209 .x = coord.x / win->pixelRatio,
1210 .y = coord.y / win->pixelRatio,
1211 };
1212 SDL_PushEvent((SDL_Event *) &ev);
1213}
1214
1188iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { 1215iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) {
1189 if (d->flags & commandOnClick_WidgetFlag && 1216 if (d->flags & commandOnClick_WidgetFlag &&
1190 (ev->type == SDL_MOUSEBUTTONDOWN || ev->type == SDL_MOUSEBUTTONUP) && 1217 (ev->type == SDL_MOUSEBUTTONDOWN || ev->type == SDL_MOUSEBUTTONUP) &&
@@ -1202,14 +1229,45 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) {
1202 postCommand_Widget(d, "mouse.moved coord:%d %d", ev->motion.x, ev->motion.y); 1229 postCommand_Widget(d, "mouse.moved coord:%d %d", ev->motion.x, ev->motion.y);
1203 return iTrue; 1230 return iTrue;
1204 } 1231 }
1205 else if (d->flags & overflowScrollable_WidgetFlag && ev->type == SDL_MOUSEWHEEL && 1232 else if (d->flags & overflowScrollable_WidgetFlag && ~d->flags & visualOffset_WidgetFlag) {
1206 ~d->flags & visualOffset_WidgetFlag) { 1233 if (ev->type == SDL_MOUSEWHEEL) {
1207 int step = ev->wheel.y; 1234 int step = ev->wheel.y;
1208 if (!isPerPixel_MouseWheelEvent(&ev->wheel)) { 1235 if (!isPerPixel_MouseWheelEvent(&ev->wheel)) {
1209 step *= lineHeight_Text(uiLabel_FontId); 1236 step *= lineHeight_Text(uiLabel_FontId);
1237 }
1238 if (scrollOverflow_Widget(d, step)) {
1239 return iTrue;
1240 }
1210 } 1241 }
1211 if (scrollOverflow_Widget(d, step)) { 1242 else if (ev->type == SDL_MOUSEMOTION && ev->motion.which != SDL_TOUCH_MOUSEID &&
1212 return iTrue; 1243 ev->motion.y >= 0) {
1244 /* TODO: Motion events occur frequently. Maybe it would help if these were handled
1245 via audiences that specifically register to listen for motion, to minimize the
1246 number of widgets that need to process them. */
1247 const int hoverScrollLimit = 2 * lineHeight_Text(default_FontId);
1248 float speed = 0.0f;
1249 if (ev->motion.y < hoverScrollLimit) {
1250 speed = (hoverScrollLimit - ev->motion.y) / (float) hoverScrollLimit;
1251 }
1252 else {
1253 const int bottomLimit = bottom_Rect(rect_Root(d->root)) - hoverScrollLimit;
1254 if (ev->motion.y > bottomLimit ) {
1255 speed = -(ev->motion.y - bottomLimit) / (float) hoverScrollLimit;
1256 }
1257 }
1258 if (speed != 0.0f && isOverflowScrollPossible_Widget_(d, speed > 0 ? 1 : -1)) {
1259 const uint32_t nowTime = SDL_GetTicks();
1260 uint32_t elapsed = nowTime - lastHoverOverflowMotionTime_;
1261 if (elapsed > 100) {
1262 elapsed = 16;
1263 }
1264 int step = elapsed * gap_UI / 16 * iClamp(speed, -1.0f, 1.0f);
1265 if (step != 0) {
1266 lastHoverOverflowMotionTime_ = nowTime;
1267 scrollOverflow_Widget(d, step);
1268 }
1269 addTicker_App(overflowHoverAnimation_, d);
1270 }
1213 } 1271 }
1214 } 1272 }
1215 switch (ev->type) { 1273 switch (ev->type) {