summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-03-06 14:44:17 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-03-06 14:44:17 +0200
commiteddc86b7b0795fe7c7a82d86f6ee151ccdca229f (patch)
tree7eea8ac70aa37d068adfc2d06dc066367a10572c /src
parent7fa64b95d0c63b243f50b23c5551a7e04fe90c30 (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.c11
-rw-r--r--src/ios.m69
-rw-r--r--src/ui/inputwidget.c12
-rw-r--r--src/ui/lookupwidget.c23
-rw-r--r--src/ui/text.c20
-rw-r--r--src/ui/text.h1
-rw-r--r--src/ui/touch.c17
-rw-r--r--src/ui/widget.c16
-rw-r--r--src/ui/widget.h1
-rw-r--r--src/ui/window.c20
-rw-r--r--src/ui/window.h4
11 files changed, 160 insertions, 34 deletions
diff --git a/src/app.c b/src/app.c
index a7539203..ada142f0 100644
--- a/src/app.c
+++ b/src/app.c
@@ -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
1137iBool isLandscape_App(void) { 1142iBool 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
diff --git a/src/ios.m b/src/ios.m
index 5abf87df..4d1aac37 100644
--- a/src/ios.m
+++ b/src/ios.m
@@ -31,28 +31,63 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
31static iBool isSystemDarkMode_ = iFalse; 31static iBool isSystemDarkMode_ = iFalse;
32static iBool isPhone_ = iFalse; 32static iBool isPhone_ = iFalse;
33 33
34static 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
34static void enableMouse_(iBool yes) { 67static 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
73KeyboardObserver *keyObs_;
74
40void setupApplication_iOS(void) { 75void 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];
48static 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
58static iBool isDarkMode_(iWindow *window) { 93static 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
21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 21SOFTWARE, 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 */
53int enableKerning_Text = iTrue; /* looking up kern pairs is slow */ 53int enableKerning_Text = iTrue; /* looking up kern pairs is slow */
54 54
55static iBool enableRaster_Text_ = iTrue; 55static iBool enableRaster_Text_ = iTrue;
56static int numPendingRasterization_Text_ = 0;
56 57
57enum iGlyphFlag { 58enum 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
428static void deinitCache_Text_(iText *d) { 430static 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
506int numPendingGlyphs_Text(void) {
507 return numPendingRasterization_Text_;
508}
509
504iLocalDef iFont *font_Text_(enum iFontId id) { 510iLocalDef 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
572static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) { 578static 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
595iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { 602iLocalDef 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);
161void setHeadingFont_Text (enum iTextFont font); 161void setHeadingFont_Text (enum iTextFont font);
162void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */ 162void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */
163void resetFonts_Text (void); 163void resetFonts_Text (void);
164int numPendingGlyphs_Text (void);
164 165
165int lineHeight_Text (int fontId); 166int lineHeight_Text (int fontId);
166iInt2 measure_Text (int fontId, const char *text); 167iInt2 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) {
248static iWidget *findOverflowScrollable_Widget_(iWidget *d) { 252static 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
1137iBool 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
1134void printTree_Widget(const iWidget *d) { 1150void 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);
232void unhover_Widget (void); 232void unhover_Widget (void);
233void setMouseGrab_Widget (iWidget *); 233void setMouseGrab_Widget (iWidget *);
234iWidget *mouseGrab_Widget (void); 234iWidget *mouseGrab_Widget (void);
235iBool hasVisibleChildOnTop_Widget(const iWidget *parent);
235void printTree_Widget (const iWidget *); 236void printTree_Widget (const iWidget *);
236 237
237iBool equalWidget_Command (const char *cmd, const iWidget *widget, const char *checkCommand); 238iBool 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
1804iInt2 visibleRootSize_Window(const iWindow *d) {
1805 return addY_I2(rootSize_Window(d), -d->keyboardHeight);
1806}
1807
1800iInt2 coord_Window(const iWindow *d, int x, int y) { 1808iInt2 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
1839void 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
1831void setSnap_Window(iWindow *d, int snapMode) { 1851void 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
80iBool processEvent_Window (iWindow *, const SDL_Event *); 82iBool processEvent_Window (iWindow *, const SDL_Event *);
@@ -86,9 +88,11 @@ void setUiScale_Window (iWindow *, float uiScale);
86void setFreezeDraw_Window (iWindow *, iBool freezeDraw); 88void setFreezeDraw_Window (iWindow *, iBool freezeDraw);
87void setCursor_Window (iWindow *, int cursor); 89void setCursor_Window (iWindow *, int cursor);
88void setSnap_Window (iWindow *, int snapMode); 90void setSnap_Window (iWindow *, int snapMode);
91void setKeyboardHeight_Window(iWindow *, int height);
89 92
90uint32_t id_Window (const iWindow *); 93uint32_t id_Window (const iWindow *);
91iInt2 rootSize_Window (const iWindow *); 94iInt2 rootSize_Window (const iWindow *);
95iInt2 visibleRootSize_Window (const iWindow *); /* may be obstructed by software keyboard */
92float uiScale_Window (const iWindow *); 96float uiScale_Window (const iWindow *);
93iInt2 coord_Window (const iWindow *, int x, int y); 97iInt2 coord_Window (const iWindow *, int x, int y);
94iInt2 mouseCoord_Window (const iWindow *); 98iInt2 mouseCoord_Window (const iWindow *);