diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-06 14:44:17 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-06 14:44:17 +0200 |
commit | eddc86b7b0795fe7c7a82d86f6ee151ccdca229f (patch) | |
tree | 7eea8ac70aa37d068adfc2d06dc066367a10572c /src | |
parent | 7fa64b95d0c63b243f50b23c5551a7e04fe90c30 (diff) |
Mobile: Dealing with keyboard height
The software keyboard obstructs part of the UI, so need to offset the view if the focused input widget would not be visible.
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 11 | ||||
-rw-r--r-- | src/ios.m | 69 | ||||
-rw-r--r-- | src/ui/inputwidget.c | 12 | ||||
-rw-r--r-- | src/ui/lookupwidget.c | 23 | ||||
-rw-r--r-- | src/ui/text.c | 20 | ||||
-rw-r--r-- | src/ui/text.h | 1 | ||||
-rw-r--r-- | src/ui/touch.c | 17 | ||||
-rw-r--r-- | src/ui/widget.c | 16 | ||||
-rw-r--r-- | src/ui/widget.h | 1 | ||||
-rw-r--r-- | src/ui/window.c | 20 | ||||
-rw-r--r-- | src/ui/window.h | 4 |
11 files changed, 160 insertions, 34 deletions
@@ -820,6 +820,11 @@ iLocalDef iBool isWaitingAllowed_App_(iApp *d) { | |||
820 | return iFalse; | 820 | return iFalse; |
821 | } | 821 | } |
822 | #endif | 822 | #endif |
823 | #if defined (iPlatformMobile) | ||
824 | if (!isFinished_Anim(&d->window->rootOffset)) { | ||
825 | return iFalse; | ||
826 | } | ||
827 | #endif | ||
823 | return !value_Atomic(&d->pendingRefresh) && isEmpty_SortedArray(&d->tickers); | 828 | return !value_Atomic(&d->pendingRefresh) && isEmpty_SortedArray(&d->tickers); |
824 | } | 829 | } |
825 | 830 | ||
@@ -929,7 +934,7 @@ void processEvents_App(enum iAppEventMode eventMode) { | |||
929 | } | 934 | } |
930 | } | 935 | } |
931 | #if defined (LAGRANGE_IDLE_SLEEP) | 936 | #if defined (LAGRANGE_IDLE_SLEEP) |
932 | if (d->isIdling && !gotEvents) { | 937 | if (d->isIdling && !gotEvents && isFinished_Anim(&d->window->rootOffset)) { |
933 | /* This is where we spend most of our time when idle. 60 Hz still quite a lot but we | 938 | /* This is where we spend most of our time when idle. 60 Hz still quite a lot but we |
934 | can't wait too long after the user tries to interact again with the app. In any | 939 | can't wait too long after the user tries to interact again with the app. In any |
935 | case, on macOS SDL_WaitEvent() seems to use 10x more CPU time than sleeping. */ | 940 | case, on macOS SDL_WaitEvent() seems to use 10x more CPU time than sleeping. */ |
@@ -1135,8 +1140,7 @@ iMimeHooks *mimeHooks_App(void) { | |||
1135 | } | 1140 | } |
1136 | 1141 | ||
1137 | iBool isLandscape_App(void) { | 1142 | iBool isLandscape_App(void) { |
1138 | const iApp *d = &app_; | 1143 | const iInt2 size = rootSize_Window(get_Window()); |
1139 | const iInt2 size = rootSize_Window(d->window); | ||
1140 | return size.x > size.y; | 1144 | return size.x > size.y; |
1141 | } | 1145 | } |
1142 | 1146 | ||
@@ -1700,6 +1704,7 @@ iBool handleCommand_App(const char *cmd) { | |||
1700 | #if defined (iPlatformAppleMobile) | 1704 | #if defined (iPlatformAppleMobile) |
1701 | /* Can't close the last on mobile. */ | 1705 | /* Can't close the last on mobile. */ |
1702 | if (tabCount_Widget(tabs) == 1) { | 1706 | if (tabCount_Widget(tabs) == 1) { |
1707 | postCommand_App("navigate.home"); | ||
1703 | return iTrue; | 1708 | return iTrue; |
1704 | } | 1709 | } |
1705 | #endif | 1710 | #endif |
@@ -31,28 +31,63 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
31 | static iBool isSystemDarkMode_ = iFalse; | 31 | static iBool isSystemDarkMode_ = iFalse; |
32 | static iBool isPhone_ = iFalse; | 32 | static iBool isPhone_ = iFalse; |
33 | 33 | ||
34 | static UIViewController *viewController_(iWindow *window) { | ||
35 | SDL_SysWMinfo wm; | ||
36 | SDL_VERSION(&wm.version); | ||
37 | if (SDL_GetWindowWMInfo(window->win, &wm)) { | ||
38 | return wm.info.uikit.window.rootViewController; | ||
39 | } | ||
40 | iAssert(false); | ||
41 | return NULL; | ||
42 | } | ||
43 | |||
44 | @interface KeyboardObserver : NSObject | ||
45 | -(void)keyboardOnScreen:(NSNotification *)notification; | ||
46 | @end | ||
47 | |||
48 | @implementation KeyboardObserver | ||
49 | -(void)keyboardOnScreen:(NSNotification *)notification { | ||
50 | NSDictionary *info = notification.userInfo; | ||
51 | NSValue *value = info[UIKeyboardFrameEndUserInfoKey]; | ||
52 | CGRect rawFrame = [value CGRectValue]; | ||
53 | UIView *view = [viewController_(get_Window()) view]; | ||
54 | CGRect keyboardFrame = [view convertRect:rawFrame fromView:nil]; | ||
55 | // NSLog(@"keyboardFrame: %@", NSStringFromCGRect(keyboardFrame)); | ||
56 | iWindow *window = get_Window(); | ||
57 | const iInt2 rootSize = rootSize_Window(window); | ||
58 | const int keyTop = keyboardFrame.origin.y * window->pixelRatio; | ||
59 | setKeyboardHeight_Window(window, rootSize.y - keyTop); | ||
60 | } | ||
61 | |||
62 | -(void)keyboardOffScreen:(NSNotification *)notification { | ||
63 | setKeyboardHeight_Window(get_Window(), 0); | ||
64 | } | ||
65 | @end | ||
66 | |||
34 | static void enableMouse_(iBool yes) { | 67 | static void enableMouse_(iBool yes) { |
35 | SDL_EventState(SDL_MOUSEBUTTONDOWN, yes); | 68 | SDL_EventState(SDL_MOUSEBUTTONDOWN, yes); |
36 | SDL_EventState(SDL_MOUSEMOTION, yes); | 69 | SDL_EventState(SDL_MOUSEMOTION, yes); |
37 | SDL_EventState(SDL_MOUSEBUTTONUP, yes); | 70 | SDL_EventState(SDL_MOUSEBUTTONUP, yes); |
38 | } | 71 | } |
39 | 72 | ||
73 | KeyboardObserver *keyObs_; | ||
74 | |||
40 | void setupApplication_iOS(void) { | 75 | void setupApplication_iOS(void) { |
41 | enableMouse_(iFalse); | 76 | enableMouse_(iFalse); |
42 | NSString *deviceModel = [[UIDevice currentDevice] model]; | 77 | NSString *deviceModel = [[UIDevice currentDevice] model]; |
43 | if ([deviceModel isEqualToString:@"iPhone"]) { | 78 | if ([deviceModel isEqualToString:@"iPhone"]) { |
44 | isPhone_ = iTrue; | 79 | isPhone_ = iTrue; |
45 | } | 80 | } |
46 | } | 81 | keyObs_ = [[KeyboardObserver alloc] init]; |
47 | 82 | NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; | |
48 | static UIViewController *viewController_(iWindow *window) { | 83 | [center addObserver:keyObs_ |
49 | SDL_SysWMinfo wm; | 84 | selector:@selector(keyboardOnScreen:) |
50 | SDL_VERSION(&wm.version); | 85 | name:UIKeyboardWillShowNotification |
51 | if (SDL_GetWindowWMInfo(window->win, &wm)) { | 86 | object:nil]; |
52 | return wm.info.uikit.window.rootViewController; | 87 | [center addObserver:keyObs_ |
53 | } | 88 | selector:@selector(keyboardOffScreen:) |
54 | iAssert(false); | 89 | name:UIKeyboardWillHideNotification |
55 | return NULL; | 90 | object:nil]; |
56 | } | 91 | } |
57 | 92 | ||
58 | static iBool isDarkMode_(iWindow *window) { | 93 | static iBool isDarkMode_(iWindow *window) { |
@@ -71,13 +106,15 @@ void safeAreaInsets_iOS(float *left, float *top, float *right, float *bottom) { | |||
71 | UIViewController *ctl = viewController_(window); | 106 | UIViewController *ctl = viewController_(window); |
72 | if (@available(iOS 11.0, *)) { | 107 | if (@available(iOS 11.0, *)) { |
73 | const UIEdgeInsets safe = ctl.view.safeAreaInsets; | 108 | const UIEdgeInsets safe = ctl.view.safeAreaInsets; |
74 | *left = safe.left * window->pixelRatio; | 109 | if (left) *left = safe.left * window->pixelRatio; |
75 | *top = safe.top * window->pixelRatio; | 110 | if (top) *top = safe.top * window->pixelRatio; |
76 | *right = safe.right * window->pixelRatio; | 111 | if (right) *right = safe.right * window->pixelRatio; |
77 | *bottom = safe.bottom * window->pixelRatio; | 112 | if (bottom) *bottom = safe.bottom * window->pixelRatio; |
78 | } else { | 113 | } else { |
79 | // Fallback on earlier versions | 114 | if (left) *left = 0.0f; |
80 | *left = *top = *right = *bottom = 0.0f; | 115 | if (top) *top = 0.0f; |
116 | if (right) *right = 0.0f; | ||
117 | if (bottom) *bottom = 0.0f; | ||
81 | } | 118 | } |
82 | } | 119 | } |
83 | 120 | ||
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 3f178fc5..06bb9474 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -21,6 +21,7 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ |
22 | 22 | ||
23 | #include "inputwidget.h" | 23 | #include "inputwidget.h" |
24 | #include "command.h" | ||
24 | #include "paint.h" | 25 | #include "paint.h" |
25 | #include "util.h" | 26 | #include "util.h" |
26 | #include "keys.h" | 27 | #include "keys.h" |
@@ -615,6 +616,17 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
615 | } | 616 | } |
616 | return iFalse; | 617 | return iFalse; |
617 | } | 618 | } |
619 | else if (isCommand_UserEvent(ev, "keyboard.changed")) { | ||
620 | if (isFocused_Widget(d) && arg_Command(command_UserEvent(ev))) { | ||
621 | iRect rect = bounds_Widget(w); | ||
622 | rect.pos.y -= value_Anim(&get_Window()->rootOffset); | ||
623 | const iInt2 visRoot = visibleRootSize_Window(get_Window()); | ||
624 | if (bottom_Rect(rect) > visRoot.y) { | ||
625 | setValue_Anim(&get_Window()->rootOffset, -(bottom_Rect(rect) - visRoot.y), 250); | ||
626 | } | ||
627 | } | ||
628 | return iFalse; | ||
629 | } | ||
618 | else if (isMetricsChange_UserEvent(ev)) { | 630 | else if (isMetricsChange_UserEvent(ev)) { |
619 | updateMetrics_InputWidget_(d); | 631 | updateMetrics_InputWidget_(d); |
620 | } | 632 | } |
diff --git a/src/ui/lookupwidget.c b/src/ui/lookupwidget.c index 10039e10..eabfc7d2 100644 --- a/src/ui/lookupwidget.c +++ b/src/ui/lookupwidget.c | |||
@@ -36,6 +36,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
36 | #include "util.h" | 36 | #include "util.h" |
37 | #include "visited.h" | 37 | #include "visited.h" |
38 | 38 | ||
39 | #if defined (iPlatformAppleMobile) | ||
40 | # include "../ios.h" | ||
41 | #endif | ||
42 | |||
39 | #include <the_Foundation/mutex.h> | 43 | #include <the_Foundation/mutex.h> |
40 | #include <the_Foundation/thread.h> | 44 | #include <the_Foundation/thread.h> |
41 | #include <the_Foundation/regexp.h> | 45 | #include <the_Foundation/regexp.h> |
@@ -641,8 +645,9 @@ static iBool processEvent_LookupWidget_(iLookupWidget *d, const SDL_Event *ev) { | |||
641 | if (isMetricsChange_UserEvent(ev)) { | 645 | if (isMetricsChange_UserEvent(ev)) { |
642 | updateMetrics_LookupWidget_(d); | 646 | updateMetrics_LookupWidget_(d); |
643 | } | 647 | } |
644 | else if (isResize_UserEvent(ev) || (equal_Command(cmd, "layout.changed") && | 648 | else if (isResize_UserEvent(ev) || equal_Command(cmd, "keyboard.changed") || |
645 | equal_Rangecc(range_Command(cmd, "id"), "navbar"))) { | 649 | (equal_Command(cmd, "layout.changed") && |
650 | equal_Rangecc(range_Command(cmd, "id"), "navbar"))) { | ||
646 | /* Position the lookup popup under the URL bar. */ { | 651 | /* Position the lookup popup under the URL bar. */ { |
647 | const iWindow *window = get_Window(); | 652 | const iWindow *window = get_Window(); |
648 | const iInt2 rootSize = rootSize_Window(window); | 653 | const iInt2 rootSize = rootSize_Window(window); |
@@ -651,13 +656,13 @@ static iBool processEvent_LookupWidget_(iLookupWidget *d, const SDL_Event *ev) { | |||
651 | (rootSize.y - bottom_Rect(navBarBounds)) / 2)); | 656 | (rootSize.y - bottom_Rect(navBarBounds)) / 2)); |
652 | setPos_Widget(w, bottomLeft_Rect(bounds_Widget(findWidget_App("url")))); | 657 | setPos_Widget(w, bottomLeft_Rect(bounds_Widget(findWidget_App("url")))); |
653 | #if defined (iPlatformAppleMobile) | 658 | #if defined (iPlatformAppleMobile) |
654 | /* TODO: Ask the system how tall the keyboard is. */ { | 659 | /* Adjust height based on keyboard size. */ { |
655 | if (isLandscape_App()) { | 660 | w->rect.size.y = visibleRootSize_Window(window).y - top_Rect(bounds_Widget(w)); |
656 | w->rect.size.y = rootSize.y * 4 / 10; | 661 | if (deviceType_App() == phone_AppDeviceType) { |
657 | } | 662 | float l, r; |
658 | else if (deviceType_App() == phone_AppDeviceType) { | 663 | safeAreaInsets_iOS(&l, NULL, &r, NULL); |
659 | w->rect.size.x = rootSize.x; | 664 | w->rect.size.x = rootSize.x - l - r; |
660 | w->rect.pos.x = 0; | 665 | w->rect.pos.x = l; |
661 | } | 666 | } |
662 | } | 667 | } |
663 | #endif | 668 | #endif |
diff --git a/src/ui/text.c b/src/ui/text.c index d3b8df37..b08bdc60 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -53,6 +53,7 @@ int enableHalfPixelGlyphs_Text = iTrue; /* debug setting */ | |||
53 | int enableKerning_Text = iTrue; /* looking up kern pairs is slow */ | 53 | int enableKerning_Text = iTrue; /* looking up kern pairs is slow */ |
54 | 54 | ||
55 | static iBool enableRaster_Text_ = iTrue; | 55 | static iBool enableRaster_Text_ = iTrue; |
56 | static int numPendingRasterization_Text_ = 0; | ||
56 | 57 | ||
57 | enum iGlyphFlag { | 58 | enum iGlyphFlag { |
58 | rasterized0_GlyphFlag = iBit(1), /* zero offset */ | 59 | rasterized0_GlyphFlag = iBit(1), /* zero offset */ |
@@ -423,6 +424,7 @@ static void initCache_Text_(iText *d) { | |||
423 | d->cacheSize.x, | 424 | d->cacheSize.x, |
424 | d->cacheSize.y); | 425 | d->cacheSize.y); |
425 | SDL_SetTextureBlendMode(d->cache, SDL_BLENDMODE_BLEND); | 426 | SDL_SetTextureBlendMode(d->cache, SDL_BLENDMODE_BLEND); |
427 | numPendingRasterization_Text_ = 0; | ||
426 | } | 428 | } |
427 | 429 | ||
428 | static void deinitCache_Text_(iText *d) { | 430 | static void deinitCache_Text_(iText *d) { |
@@ -501,6 +503,10 @@ void resetFonts_Text(void) { | |||
501 | initFonts_Text_(d); | 503 | initFonts_Text_(d); |
502 | } | 504 | } |
503 | 505 | ||
506 | int numPendingGlyphs_Text(void) { | ||
507 | return numPendingRasterization_Text_; | ||
508 | } | ||
509 | |||
504 | iLocalDef iFont *font_Text_(enum iFontId id) { | 510 | iLocalDef iFont *font_Text_(enum iFontId id) { |
505 | return &text_.fonts[id & mask_FontId]; | 511 | return &text_.fonts[id & mask_FontId]; |
506 | } | 512 | } |
@@ -569,7 +575,7 @@ static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) { | |||
569 | } | 575 | } |
570 | } | 576 | } |
571 | 577 | ||
572 | static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) { | 578 | static iBool cache_Font_(iFont *d, iGlyph *glyph, int hoff) { |
573 | iText * txt = &text_; | 579 | iText * txt = &text_; |
574 | SDL_Renderer *render = txt->render; | 580 | SDL_Renderer *render = txt->render; |
575 | SDL_Texture * tex = NULL; | 581 | SDL_Texture * tex = NULL; |
@@ -590,6 +596,7 @@ static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) { | |||
590 | if (surface) { | 596 | if (surface) { |
591 | SDL_FreeSurface(surface); | 597 | SDL_FreeSurface(surface); |
592 | } | 598 | } |
599 | return isRasterized_Glyph_(glyph, hoff); | ||
593 | } | 600 | } |
594 | 601 | ||
595 | iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | 602 | iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { |
@@ -659,15 +666,22 @@ static const iGlyph *glyph_Font_(iFont *d, iChar ch) { | |||
659 | allocate_Font_(font, glyph, 0); | 666 | allocate_Font_(font, glyph, 0); |
660 | allocate_Font_(font, glyph, 1); | 667 | allocate_Font_(font, glyph, 1); |
661 | insert_Hash(&font->glyphs, &glyph->node); | 668 | insert_Hash(&font->glyphs, &glyph->node); |
669 | numPendingRasterization_Text_ += 2; | ||
662 | } | 670 | } |
663 | if (enableRaster_Text_ && !isFullyRasterized_Glyph_(glyph)) { | 671 | if (enableRaster_Text_ && !isFullyRasterized_Glyph_(glyph)) { |
664 | SDL_Texture *oldTarget = SDL_GetRenderTarget(text_.render); | 672 | SDL_Texture *oldTarget = SDL_GetRenderTarget(text_.render); |
665 | SDL_SetRenderTarget(text_.render, text_.cache); | 673 | SDL_SetRenderTarget(text_.render, text_.cache); |
666 | if (!isRasterized_Glyph_(glyph, 0)) { | 674 | if (!isRasterized_Glyph_(glyph, 0)) { |
667 | cache_Font_(font, glyph, 0); | 675 | if (cache_Font_(font, glyph, 0)) { |
676 | numPendingRasterization_Text_--; | ||
677 | iAssert(numPendingRasterization_Text_ >= 0); | ||
678 | } | ||
668 | } | 679 | } |
669 | if (!isRasterized_Glyph_(glyph, 1)) { | 680 | if (!isRasterized_Glyph_(glyph, 1)) { |
670 | cache_Font_(font, glyph, 1); /* half-pixel offset */ | 681 | if (cache_Font_(font, glyph, 1)) { /* half-pixel offset */ |
682 | numPendingRasterization_Text_--; | ||
683 | iAssert(numPendingRasterization_Text_ >= 0); | ||
684 | } | ||
671 | } | 685 | } |
672 | SDL_SetRenderTarget(text_.render, oldTarget); | 686 | SDL_SetRenderTarget(text_.render, oldTarget); |
673 | } | 687 | } |
diff --git a/src/ui/text.h b/src/ui/text.h index f696b2e3..1118882d 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -161,6 +161,7 @@ void setContentFont_Text (enum iTextFont font); | |||
161 | void setHeadingFont_Text (enum iTextFont font); | 161 | void setHeadingFont_Text (enum iTextFont font); |
162 | void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */ | 162 | void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */ |
163 | void resetFonts_Text (void); | 163 | void resetFonts_Text (void); |
164 | int numPendingGlyphs_Text (void); | ||
164 | 165 | ||
165 | int lineHeight_Text (int fontId); | 166 | int lineHeight_Text (int fontId); |
166 | iInt2 measure_Text (int fontId, const char *text); | 167 | iInt2 measure_Text (int fontId, const char *text); |
diff --git a/src/ui/touch.c b/src/ui/touch.c index 27db0073..f15eda6f 100644 --- a/src/ui/touch.c +++ b/src/ui/touch.c | |||
@@ -190,6 +190,10 @@ static void update_TouchState_(void *ptr) { | |||
190 | iForEach(Array, m, d->moms) { | 190 | iForEach(Array, m, d->moms) { |
191 | if (numSteps == 0) break; | 191 | if (numSteps == 0) break; |
192 | iMomentum *mom = m.value; | 192 | iMomentum *mom = m.value; |
193 | if (!mom->affinity) { | ||
194 | remove_ArrayIterator(&m); | ||
195 | continue; | ||
196 | } | ||
193 | for (int step = 0; step < numSteps; step++) { | 197 | for (int step = 0; step < numSteps; step++) { |
194 | mulvf_F3(&mom->velocity, momFriction); | 198 | mulvf_F3(&mom->velocity, momFriction); |
195 | addv_F3(&mom->accum, mulf_F3(mom->velocity, stepDurationMs / 1000.0f)); | 199 | addv_F3(&mom->accum, mulf_F3(mom->velocity, stepDurationMs / 1000.0f)); |
@@ -248,8 +252,11 @@ static void dispatchButtonUp_Touch_(iFloat3 pos) { | |||
248 | static iWidget *findOverflowScrollable_Widget_(iWidget *d) { | 252 | static iWidget *findOverflowScrollable_Widget_(iWidget *d) { |
249 | const iInt2 rootSize = rootSize_Window(get_Window()); | 253 | const iInt2 rootSize = rootSize_Window(get_Window()); |
250 | for (iWidget *w = d; w; w = parent_Widget(w)) { | 254 | for (iWidget *w = d; w; w = parent_Widget(w)) { |
251 | if (flags_Widget(w) & overflowScrollable_WidgetFlag && height_Widget(w) > rootSize.y) { | 255 | if (flags_Widget(w) & overflowScrollable_WidgetFlag) { |
252 | return w; | 256 | if (height_Widget(w) > rootSize.y && !hasVisibleChildOnTop_Widget(w)) { |
257 | return w; | ||
258 | } | ||
259 | return NULL; | ||
253 | } | 260 | } |
254 | } | 261 | } |
255 | return NULL; | 262 | return NULL; |
@@ -262,9 +269,13 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
262 | } | 269 | } |
263 | iTouchState *d = touchState_(); | 270 | iTouchState *d = touchState_(); |
264 | iWindow *window = get_Window(); | 271 | iWindow *window = get_Window(); |
272 | if (!isFinished_Anim(&window->rootOffset)) { | ||
273 | return iFalse; | ||
274 | } | ||
265 | const iInt2 rootSize = rootSize_Window(window); | 275 | const iInt2 rootSize = rootSize_Window(window); |
266 | const SDL_TouchFingerEvent *fing = &ev->tfinger; | 276 | const SDL_TouchFingerEvent *fing = &ev->tfinger; |
267 | const iFloat3 pos = init_F3(fing->x * rootSize.x, fing->y * rootSize.y, 0); /* pixels */ | 277 | const iFloat3 pos = add_F3(init_F3(fing->x * rootSize.x, fing->y * rootSize.y, 0), /* pixels */ |
278 | init_F3(0, -value_Anim(&window->rootOffset), 0)); | ||
268 | const uint32_t nowTime = SDL_GetTicks(); | 279 | const uint32_t nowTime = SDL_GetTicks(); |
269 | if (ev->type == SDL_FINGERDOWN) { | 280 | if (ev->type == SDL_FINGERDOWN) { |
270 | /* Register the new touch. */ | 281 | /* Register the new touch. */ |
diff --git a/src/ui/widget.c b/src/ui/widget.c index c54ea444..2381548a 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -507,6 +507,9 @@ iRect bounds_Widget(const iWidget *d) { | |||
507 | } | 507 | } |
508 | addv_I2(&bounds.pos, pos); | 508 | addv_I2(&bounds.pos, pos); |
509 | } | 509 | } |
510 | #if defined (iPlatformMobile) | ||
511 | bounds.pos.y += value_Anim(&get_Window()->rootOffset); | ||
512 | #endif | ||
510 | return bounds; | 513 | return bounds; |
511 | } | 514 | } |
512 | 515 | ||
@@ -1131,6 +1134,19 @@ static void printTree_Widget_(const iWidget *d, int indent) { | |||
1131 | } | 1134 | } |
1132 | } | 1135 | } |
1133 | 1136 | ||
1137 | iBool hasVisibleChildOnTop_Widget(const iWidget *parent) { | ||
1138 | iConstForEach(ObjectList, i, parent->children) { | ||
1139 | const iWidget *child = i.object; | ||
1140 | if (~child->flags & hidden_WidgetFlag && child->flags & keepOnTop_WidgetFlag) { | ||
1141 | return iTrue; | ||
1142 | } | ||
1143 | if (hasVisibleChildOnTop_Widget(child)) { | ||
1144 | return iTrue; | ||
1145 | } | ||
1146 | } | ||
1147 | return iFalse; | ||
1148 | } | ||
1149 | |||
1134 | void printTree_Widget(const iWidget *d) { | 1150 | void printTree_Widget(const iWidget *d) { |
1135 | printTree_Widget_(d, 0); | 1151 | printTree_Widget_(d, 0); |
1136 | } | 1152 | } |
diff --git a/src/ui/widget.h b/src/ui/widget.h index b1ee8c73..602e86cb 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h | |||
@@ -232,6 +232,7 @@ iWidget *hover_Widget (void); | |||
232 | void unhover_Widget (void); | 232 | void unhover_Widget (void); |
233 | void setMouseGrab_Widget (iWidget *); | 233 | void setMouseGrab_Widget (iWidget *); |
234 | iWidget *mouseGrab_Widget (void); | 234 | iWidget *mouseGrab_Widget (void); |
235 | iBool hasVisibleChildOnTop_Widget(const iWidget *parent); | ||
235 | void printTree_Widget (const iWidget *); | 236 | void printTree_Widget (const iWidget *); |
236 | 237 | ||
237 | iBool equalWidget_Command (const char *cmd, const iWidget *widget, const char *checkCommand); | 238 | iBool equalWidget_Command (const char *cmd, const iWidget *widget, const char *checkCommand); |
diff --git a/src/ui/window.c b/src/ui/window.c index bf7214f4..9ff22d35 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -1136,6 +1136,7 @@ static void updateRootSize_Window_(iWindow *d, iBool notifyAlways) { | |||
1136 | iInt2 *size = &d->root->rect.size; | 1136 | iInt2 *size = &d->root->rect.size; |
1137 | const iInt2 oldSize = *size; | 1137 | const iInt2 oldSize = *size; |
1138 | SDL_GetRendererOutputSize(d->render, &size->x, &size->y); | 1138 | SDL_GetRendererOutputSize(d->render, &size->x, &size->y); |
1139 | size->y -= d->keyboardHeight; | ||
1139 | if (notifyAlways || !isEqual_I2(oldSize, *size)) { | 1140 | if (notifyAlways || !isEqual_I2(oldSize, *size)) { |
1140 | const iBool isHoriz = (d->place.lastNotifiedSize.x != size->x); | 1141 | const iBool isHoriz = (d->place.lastNotifiedSize.x != size->x); |
1141 | const iBool isVert = (d->place.lastNotifiedSize.y != size->y); | 1142 | const iBool isVert = (d->place.lastNotifiedSize.y != size->y); |
@@ -1287,6 +1288,8 @@ void init_Window(iWindow *d, iRect rect) { | |||
1287 | d->isMouseInside = iTrue; | 1288 | d->isMouseInside = iTrue; |
1288 | d->ignoreClick = iFalse; | 1289 | d->ignoreClick = iFalse; |
1289 | d->focusGainedAt = 0; | 1290 | d->focusGainedAt = 0; |
1291 | d->keyboardHeight = 0; | ||
1292 | init_Anim(&d->rootOffset, 0.0f); | ||
1290 | uint32_t flags = 0; | 1293 | uint32_t flags = 0; |
1291 | #if defined (iPlatformAppleDesktop) | 1294 | #if defined (iPlatformAppleDesktop) |
1292 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, shouldDefaultToMetalRenderer_MacOS() ? "metal" : "opengl"); | 1295 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, shouldDefaultToMetalRenderer_MacOS() ? "metal" : "opengl"); |
@@ -1704,6 +1707,7 @@ void draw_Window(iWindow *d) { | |||
1704 | if (d->isDrawFrozen) { | 1707 | if (d->isDrawFrozen) { |
1705 | return; | 1708 | return; |
1706 | } | 1709 | } |
1710 | // printf("num pending: %d\n", numPendingGlyphs_Text()); | ||
1707 | const int winFlags = SDL_GetWindowFlags(d->win); | 1711 | const int winFlags = SDL_GetWindowFlags(d->win); |
1708 | const iBool gotFocus = (winFlags & SDL_WINDOW_INPUT_FOCUS) != 0; | 1712 | const iBool gotFocus = (winFlags & SDL_WINDOW_INPUT_FOCUS) != 0; |
1709 | /* Clear the window. The clear color is visible as a border around the window | 1713 | /* Clear the window. The clear color is visible as a border around the window |
@@ -1797,6 +1801,10 @@ iInt2 rootSize_Window(const iWindow *d) { | |||
1797 | return d ? d->root->rect.size : zero_I2(); | 1801 | return d ? d->root->rect.size : zero_I2(); |
1798 | } | 1802 | } |
1799 | 1803 | ||
1804 | iInt2 visibleRootSize_Window(const iWindow *d) { | ||
1805 | return addY_I2(rootSize_Window(d), -d->keyboardHeight); | ||
1806 | } | ||
1807 | |||
1800 | iInt2 coord_Window(const iWindow *d, int x, int y) { | 1808 | iInt2 coord_Window(const iWindow *d, int x, int y) { |
1801 | #if defined (iPlatformMsys) || defined (iPlatformLinux) | 1809 | #if defined (iPlatformMsys) || defined (iPlatformLinux) |
1802 | /* On Windows, surface coordinates are in pixels. */ | 1810 | /* On Windows, surface coordinates are in pixels. */ |
@@ -1828,6 +1836,18 @@ iWindow *get_Window(void) { | |||
1828 | return theWindow_; | 1836 | return theWindow_; |
1829 | } | 1837 | } |
1830 | 1838 | ||
1839 | void setKeyboardHeight_Window(iWindow *d, int height) { | ||
1840 | if (d->keyboardHeight != height) { | ||
1841 | d->keyboardHeight = height; | ||
1842 | if (height == 0) { | ||
1843 | setFlags_Anim(&d->rootOffset, easeBoth_AnimFlag, iTrue); | ||
1844 | setValue_Anim(&d->rootOffset, 0, 250); | ||
1845 | } | ||
1846 | postCommandf_App("keyboard.changed arg:%d", height); | ||
1847 | postRefresh_App(); | ||
1848 | } | ||
1849 | } | ||
1850 | |||
1831 | void setSnap_Window(iWindow *d, int snapMode) { | 1851 | void setSnap_Window(iWindow *d, int snapMode) { |
1832 | if (!prefs_App()->customFrame) { | 1852 | if (!prefs_App()->customFrame) { |
1833 | if (snapMode == maximized_WindowSnap) { | 1853 | if (snapMode == maximized_WindowSnap) { |
diff --git a/src/ui/window.h b/src/ui/window.h index 7cd29d4b..8bc9911c 100644 --- a/src/ui/window.h +++ b/src/ui/window.h | |||
@@ -75,6 +75,8 @@ struct Impl_Window { | |||
75 | SDL_Cursor * cursors[SDL_NUM_SYSTEM_CURSORS]; | 75 | SDL_Cursor * cursors[SDL_NUM_SYSTEM_CURSORS]; |
76 | SDL_Cursor * pendingCursor; | 76 | SDL_Cursor * pendingCursor; |
77 | int loadAnimTimer; | 77 | int loadAnimTimer; |
78 | iAnim rootOffset; | ||
79 | int keyboardHeight; /* mobile software keyboards */ | ||
78 | }; | 80 | }; |
79 | 81 | ||
80 | iBool processEvent_Window (iWindow *, const SDL_Event *); | 82 | iBool processEvent_Window (iWindow *, const SDL_Event *); |
@@ -86,9 +88,11 @@ void setUiScale_Window (iWindow *, float uiScale); | |||
86 | void setFreezeDraw_Window (iWindow *, iBool freezeDraw); | 88 | void setFreezeDraw_Window (iWindow *, iBool freezeDraw); |
87 | void setCursor_Window (iWindow *, int cursor); | 89 | void setCursor_Window (iWindow *, int cursor); |
88 | void setSnap_Window (iWindow *, int snapMode); | 90 | void setSnap_Window (iWindow *, int snapMode); |
91 | void setKeyboardHeight_Window(iWindow *, int height); | ||
89 | 92 | ||
90 | uint32_t id_Window (const iWindow *); | 93 | uint32_t id_Window (const iWindow *); |
91 | iInt2 rootSize_Window (const iWindow *); | 94 | iInt2 rootSize_Window (const iWindow *); |
95 | iInt2 visibleRootSize_Window (const iWindow *); /* may be obstructed by software keyboard */ | ||
92 | float uiScale_Window (const iWindow *); | 96 | float uiScale_Window (const iWindow *); |
93 | iInt2 coord_Window (const iWindow *, int x, int y); | 97 | iInt2 coord_Window (const iWindow *, int x, int y); |
94 | iInt2 mouseCoord_Window (const iWindow *); | 98 | iInt2 mouseCoord_Window (const iWindow *); |