summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.c8
-rw-r--r--src/gmdocument.c20
-rw-r--r--src/gmdocument.h4
-rw-r--r--src/gmutil.c54
-rw-r--r--src/gmutil.h1
-rw-r--r--src/ios.m3
-rw-r--r--src/media.c16
-rw-r--r--src/ui/documentwidget.c37
-rw-r--r--src/ui/labelwidget.c2
-rw-r--r--src/ui/lookupwidget.c2
-rw-r--r--src/ui/metrics.c2
-rw-r--r--src/ui/mobile.c6
-rw-r--r--src/ui/root.c29
-rw-r--r--src/ui/sidebarwidget.c2
-rw-r--r--src/ui/text.c3
-rw-r--r--src/ui/window.c38
16 files changed, 149 insertions, 78 deletions
diff --git a/src/app.c b/src/app.c
index f8dc5697..be96bf6c 100644
--- a/src/app.c
+++ b/src/app.c
@@ -1583,7 +1583,11 @@ iBool isLandscape_App(void) {
1583} 1583}
1584 1584
1585enum iAppDeviceType deviceType_App(void) { 1585enum iAppDeviceType deviceType_App(void) {
1586#if defined (iPlatformAppleMobile) 1586#if defined (iPlatformMobilePhone)
1587 return phone_AppDeviceType;
1588#elif defined (iPlatformMobileTablet)
1589 return tablet_AppDeviceType;
1590#elif defined (iPlatformAppleMobile)
1587 return isPhone_iOS() ? phone_AppDeviceType : tablet_AppDeviceType; 1591 return isPhone_iOS() ? phone_AppDeviceType : tablet_AppDeviceType;
1588#else 1592#else
1589 return desktop_AppDeviceType; 1593 return desktop_AppDeviceType;
@@ -2484,7 +2488,7 @@ iBool handleCommand_App(const char *cmd) {
2484 } 2488 }
2485 else if (equal_Command(cmd, "tabs.close")) { 2489 else if (equal_Command(cmd, "tabs.close")) {
2486 iWidget *tabs = findWidget_App("doctabs"); 2490 iWidget *tabs = findWidget_App("doctabs");
2487#if defined (iPlatformAppleMobile) 2491#if defined (iPlatformMobile)
2488 /* Can't close the last on mobile. */ 2492 /* Can't close the last on mobile. */
2489 if (tabCount_Widget(tabs) == 1 && numRoots_Window(get_Window()) == 1) { 2493 if (tabCount_Widget(tabs) == 1 && numRoots_Window(get_Window()) == 1) {
2490 postCommand_App("navigate.home"); 2494 postCommand_App("navigate.home");
diff --git a/src/gmdocument.c b/src/gmdocument.c
index b9832f38..75f6f06b 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -82,6 +82,7 @@ struct Impl_GmDocument {
82 iString url; /* for resolving relative links */ 82 iString url; /* for resolving relative links */
83 iString localHost; 83 iString localHost;
84 iInt2 size; 84 iInt2 size;
85 int outsideMargin;
85 iArray layout; /* contents of source, laid out in document space */ 86 iArray layout; /* contents of source, laid out in document space */
86 iPtrArray links; 87 iPtrArray links;
87 enum iGmDocumentBanner bannerType; 88 enum iGmDocumentBanner bannerType;
@@ -239,7 +240,10 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li
239 iString *path = newRange_String(parts.path); 240 iString *path = newRange_String(parts.path);
240 if (endsWithCase_String(path, ".gif") || endsWithCase_String(path, ".jpg") || 241 if (endsWithCase_String(path, ".gif") || endsWithCase_String(path, ".jpg") ||
241 endsWithCase_String(path, ".jpeg") || endsWithCase_String(path, ".png") || 242 endsWithCase_String(path, ".jpeg") || endsWithCase_String(path, ".png") ||
242 endsWithCase_String(path, ".tga") || endsWithCase_String(path, ".psd") || 243 endsWithCase_String(path, ".tga") || endsWithCase_String(path, ".psd") ||
244#if defined (LAGRANGE_ENABLE_WEBP)
245 endsWithCase_String(path, ".webp") ||
246#endif
243 endsWithCase_String(path, ".hdr") || endsWithCase_String(path, ".pic")) { 247 endsWithCase_String(path, ".hdr") || endsWithCase_String(path, ".pic")) {
244 link->flags |= imageFileExtension_GmLinkFlag; 248 link->flags |= imageFileExtension_GmLinkFlag;
245 } 249 }
@@ -896,6 +900,12 @@ static void doLayout_GmDocument_(iGmDocument *d) {
896 pos.y += margin; 900 pos.y += margin;
897 run.bounds.pos = pos; 901 run.bounds.pos = pos;
898 run.bounds.size.x = d->size.x; 902 run.bounds.size.x = d->size.x;
903 /* Extend the image to full width, including outside margin, if the viewport
904 is narrow enough. */
905 if (d->outsideMargin < 5 * gap_UI) {
906 run.bounds.size.x += d->outsideMargin * 2;
907 run.bounds.pos.x -= d->outsideMargin;
908 }
899 const float aspect = (float) imgSize.y / (float) imgSize.x; 909 const float aspect = (float) imgSize.y / (float) imgSize.x;
900 run.bounds.size.y = d->size.x * aspect; 910 run.bounds.size.y = d->size.x * aspect;
901 run.visBounds = run.bounds; 911 run.visBounds = run.bounds;
@@ -990,6 +1000,7 @@ void init_GmDocument(iGmDocument *d) {
990 init_String(&d->url); 1000 init_String(&d->url);
991 init_String(&d->localHost); 1001 init_String(&d->localHost);
992 d->bannerType = siteDomain_GmDocumentBanner; 1002 d->bannerType = siteDomain_GmDocumentBanner;
1003 d->outsideMargin = 0;
993 d->size = zero_I2(); 1004 d->size = zero_I2();
994 init_Array(&d->layout, sizeof(iGmRun)); 1005 init_Array(&d->layout, sizeof(iGmRun));
995 init_PtrArray(&d->links); 1006 init_PtrArray(&d->links);
@@ -1543,8 +1554,9 @@ void setBanner_GmDocument(iGmDocument *d, enum iGmDocumentBanner type) {
1543 d->bannerType = type; 1554 d->bannerType = type;
1544} 1555}
1545 1556
1546void setWidth_GmDocument(iGmDocument *d, int width) { 1557void setWidth_GmDocument(iGmDocument *d, int width, int outsideMargin) {
1547 d->size.x = width; 1558 d->size.x = width;
1559 d->outsideMargin = outsideMargin; /* distance to edge of the viewport */
1548 doLayout_GmDocument_(d); /* TODO: just flag need-layout and do it later */ 1560 doLayout_GmDocument_(d); /* TODO: just flag need-layout and do it later */
1549} 1561}
1550 1562
@@ -1698,7 +1710,7 @@ void setUrl_GmDocument(iGmDocument *d, const iString *url) {
1698 updateIconBasedOnUrl_GmDocument_(d); 1710 updateIconBasedOnUrl_GmDocument_(d);
1699} 1711}
1700 1712
1701void setSource_GmDocument(iGmDocument *d, const iString *source, int width, 1713void setSource_GmDocument(iGmDocument *d, const iString *source, int width, int outsideMargin,
1702 enum iGmDocumentUpdate updateType) { 1714 enum iGmDocumentUpdate updateType) {
1703// printf("[GmDocument] source update (%zu bytes), width:%d, final:%d\n", 1715// printf("[GmDocument] source update (%zu bytes), width:%d, final:%d\n",
1704// size_String(source), width, updateType == final_GmDocumentUpdate); 1716// size_String(source), width, updateType == final_GmDocumentUpdate);
@@ -1713,7 +1725,7 @@ void setSource_GmDocument(iGmDocument *d, const iString *source, int width,
1713 if (isNormalized_GmDocument_(d)) { 1725 if (isNormalized_GmDocument_(d)) {
1714 normalize_GmDocument(d); 1726 normalize_GmDocument(d);
1715 } 1727 }
1716 setWidth_GmDocument(d, width); /* re-do layout */ 1728 setWidth_GmDocument(d, width, outsideMargin); /* re-do layout */
1717} 1729}
1718 1730
1719void foldPre_GmDocument(iGmDocument *d, uint16_t preId) { 1731void foldPre_GmDocument(iGmDocument *d, uint16_t preId) {
diff --git a/src/gmdocument.h b/src/gmdocument.h
index 9a7a70df..9f8ee2ef 100644
--- a/src/gmdocument.h
+++ b/src/gmdocument.h
@@ -174,11 +174,11 @@ enum iGmDocumentUpdate {
174void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed); 174void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed);
175void setFormat_GmDocument (iGmDocument *, enum iSourceFormat format); 175void setFormat_GmDocument (iGmDocument *, enum iSourceFormat format);
176void setBanner_GmDocument (iGmDocument *, enum iGmDocumentBanner type); 176void setBanner_GmDocument (iGmDocument *, enum iGmDocumentBanner type);
177void setWidth_GmDocument (iGmDocument *, int width); 177void setWidth_GmDocument (iGmDocument *, int width, int outsideMargin);
178void redoLayout_GmDocument (iGmDocument *); 178void redoLayout_GmDocument (iGmDocument *);
179iBool updateOpenURLs_GmDocument(iGmDocument *); 179iBool updateOpenURLs_GmDocument(iGmDocument *);
180void setUrl_GmDocument (iGmDocument *, const iString *url); 180void setUrl_GmDocument (iGmDocument *, const iString *url);
181void setSource_GmDocument (iGmDocument *, const iString *source, int width, 181void setSource_GmDocument (iGmDocument *, const iString *source, int width, int outsideMargin,
182 enum iGmDocumentUpdate updateType); 182 enum iGmDocumentUpdate updateType);
183void foldPre_GmDocument (iGmDocument *, uint16_t preId); 183void foldPre_GmDocument (iGmDocument *, uint16_t preId);
184 184
diff --git a/src/gmutil.c b/src/gmutil.c
index 9552c2a1..d87de8f6 100644
--- a/src/gmutil.c
+++ b/src/gmutil.c
@@ -511,54 +511,64 @@ const iString *findContainerArchive_Path(const iString *path) {
511 return NULL; 511 return NULL;
512} 512}
513 513
514const char *mediaType_Path(const iString *path) { 514const char *mediaTypeFromFileExtension_String(const iString *d) {
515 if (endsWithCase_String(path, ".gmi") || endsWithCase_String(path, ".gemini")) { 515 if (endsWithCase_String(d, ".gmi") || endsWithCase_String(d, ".gemini")) {
516 return "text/gemini; charset=utf-8"; 516 return "text/gemini; charset=utf-8";
517 } 517 }
518 else if (endsWithCase_String(path, ".pem")) { 518 else if (endsWithCase_String(d, ".pem")) {
519 return "application/x-pem-file"; 519 return "application/x-pem-file";
520 } 520 }
521 else if (endsWithCase_String(path, ".zip")) { 521 else if (endsWithCase_String(d, ".zip")) {
522 return "application/zip"; 522 return "application/zip";
523 } 523 }
524 else if (endsWithCase_String(path, ".gpub")) { 524 else if (endsWithCase_String(d, ".gpub")) {
525 return "application/gpub+zip"; 525 return "application/gpub+zip";
526 } 526 }
527 else if (endsWithCase_String(path, ".xml")) { 527 else if (endsWithCase_String(d, ".xml")) {
528 return "text/xml"; 528 return "text/xml";
529 } 529 }
530 else if (endsWithCase_String(path, ".png")) { 530 else if (endsWithCase_String(d, ".png")) {
531 return "image/png"; 531 return "image/png";
532 } 532 }
533 else if (endsWithCase_String(path, ".jpg") || endsWithCase_String(path, ".jpeg")) { 533 else if (endsWithCase_String(d, ".webp")) {
534 return "image/webp";
535 }
536 else if (endsWithCase_String(d, ".jpg") || endsWithCase_String(d, ".jpeg")) {
534 return "image/jpeg"; 537 return "image/jpeg";
535 } 538 }
536 else if (endsWithCase_String(path, ".gif")) { 539 else if (endsWithCase_String(d, ".gif")) {
537 return "image/gif"; 540 return "image/gif";
538 } 541 }
539 else if (endsWithCase_String(path, ".wav")) { 542 else if (endsWithCase_String(d, ".wav")) {
540 return "audio/wave"; 543 return "audio/wave";
541 } 544 }
542 else if (endsWithCase_String(path, ".ogg")) { 545 else if (endsWithCase_String(d, ".ogg")) {
543 return "audio/ogg"; 546 return "audio/ogg";
544 } 547 }
545 else if (endsWithCase_String(path, ".mp3")) { 548 else if (endsWithCase_String(d, ".mp3")) {
546 return "audio/mpeg"; 549 return "audio/mpeg";
547 } 550 }
548 else if (endsWithCase_String(path, ".mid")) { 551 else if (endsWithCase_String(d, ".mid")) {
549 return "audio/midi"; 552 return "audio/midi";
550 } 553 }
551 else if (endsWithCase_String(path, ".txt") || 554 else if (endsWithCase_String(d, ".txt") ||
552 endsWithCase_String(path, ".md") || 555 endsWithCase_String(d, ".md") ||
553 endsWithCase_String(path, ".c") || 556 endsWithCase_String(d, ".c") ||
554 endsWithCase_String(path, ".h") || 557 endsWithCase_String(d, ".h") ||
555 endsWithCase_String(path, ".cc") || 558 endsWithCase_String(d, ".cc") ||
556 endsWithCase_String(path, ".hh") || 559 endsWithCase_String(d, ".hh") ||
557 endsWithCase_String(path, ".cpp") || 560 endsWithCase_String(d, ".cpp") ||
558 endsWithCase_String(path, ".hpp")) { 561 endsWithCase_String(d, ".hpp")) {
559 return "text/plain"; 562 return "text/plain";
560 } 563 }
561 const char *mtype = "application/octet-stream"; 564 return "application/octet-stream";
565}
566
567const char *mediaType_Path(const iString *path) {
568 const char *mtype = mediaTypeFromFileExtension_String(path);
569 if (iCmpStr(mtype, "application/octet-stream")) {
570 return mtype; /* extension recognized */
571 }
562 /* If the file is reasonably small and looks like UTF-8, we'll display it as text/plain. */ 572 /* If the file is reasonably small and looks like UTF-8, we'll display it as text/plain. */
563 if (fileExists_FileInfo(path) && fileSize_FileInfo(path) <= 5000000) { 573 if (fileExists_FileInfo(path) && fileSize_FileInfo(path) <= 5000000) {
564 iFile *f = new_File(path); 574 iFile *f = new_File(path);
diff --git a/src/gmutil.h b/src/gmutil.h
index f8491781..3c10d45b 100644
--- a/src/gmutil.h
+++ b/src/gmutil.h
@@ -133,6 +133,7 @@ const iString * withSpacesEncoded_String(const iString *);
133const iString * canonicalUrl_String (const iString *); 133const iString * canonicalUrl_String (const iString *);
134 134
135const char * mediaType_Path (const iString *path); 135const char * mediaType_Path (const iString *path);
136const char * mediaTypeFromFileExtension_String (const iString *);
136iRangecc mediaTypeWithoutParameters_Rangecc (iRangecc mime); 137iRangecc mediaTypeWithoutParameters_Rangecc (iRangecc mime);
137 138
138const iString * findContainerArchive_Path (const iString *path); 139const iString * findContainerArchive_Path (const iString *path);
diff --git a/src/ios.m b/src/ios.m
index b82d54a7..c493e4f9 100644
--- a/src/ios.m
+++ b/src/ios.m
@@ -264,7 +264,6 @@ void setupApplication_iOS(void) {
264 selector:@selector(keyboardOffScreen:) 264 selector:@selector(keyboardOffScreen:)
265 name:UIKeyboardWillHideNotification 265 name:UIKeyboardWillHideNotification
266 object:nil]; 266 object:nil];
267 [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
268 /* Media player remote controls. */ 267 /* Media player remote controls. */
269 MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter]; 268 MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
270 [[commandCenter pauseCommand] addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { 269 [[commandCenter pauseCommand] addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
@@ -487,6 +486,8 @@ void init_AVFAudioPlayer(iAVFAudioPlayer *d) {
487 d->player = NULL; 486 d->player = NULL;
488 d->volume = 1.0f; 487 d->volume = 1.0f;
489 d->state = initialized_AVFAudioPlayerState; 488 d->state = initialized_AVFAudioPlayerState;
489 /* Playback is imminent. */
490 [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
490} 491}
491 492
492void deinit_AVFAudioPlayer(iAVFAudioPlayer *d) { 493void deinit_AVFAudioPlayer(iAVFAudioPlayer *d) {
diff --git a/src/media.c b/src/media.c
index eb4a8311..5240ab5d 100644
--- a/src/media.c
+++ b/src/media.c
@@ -30,6 +30,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
30#include "stb_image.h" 30#include "stb_image.h"
31#include "stb_image_resize.h" 31#include "stb_image_resize.h"
32 32
33#if defined (LAGRANGE_ENABLE_WEBP)
34# include <webp/decode.h>
35#endif
36
33#include <the_Foundation/file.h> 37#include <the_Foundation/file.h>
34#include <the_Foundation/ptrarray.h> 38#include <the_Foundation/ptrarray.h>
35#include <SDL_hints.h> 39#include <SDL_hints.h>
@@ -86,8 +90,16 @@ void deinit_GmImage(iGmImage *d) {
86void makeTexture_GmImage(iGmImage *d) { 90void makeTexture_GmImage(iGmImage *d) {
87 iBlock *data = &d->partialData; 91 iBlock *data = &d->partialData;
88 d->numBytes = size_Block(data); 92 d->numBytes = size_Block(data);
89 uint8_t *imgData = stbi_load_from_memory( 93 uint8_t *imgData = NULL;
90 constData_Block(data), size_Block(data), &d->size.x, &d->size.y, NULL, 4); 94 if (cmp_String(&d->props.mime, "image/webp") == 0) {
95#if defined (LAGRANGE_ENABLE_WEBP)
96 imgData = WebPDecodeRGBA(constData_Block(data), size_Block(data), &d->size.x, &d->size.y);
97#endif
98 }
99 else {
100 imgData = stbi_load_from_memory(
101 constData_Block(data), size_Block(data), &d->size.x, &d->size.y, NULL, 4);
102 }
91 if (!imgData) { 103 if (!imgData) {
92 d->size = zero_I2(); 104 d->size = zero_I2();
93 d->texture = NULL; 105 d->texture = NULL;
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 7e138902..76e843d5 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -1054,9 +1054,12 @@ static void documentWasChanged_DocumentWidget_(iDocumentWidget *d) {
1054 1054
1055void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { 1055void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) {
1056 setUrl_GmDocument(d->doc, d->mod.url); 1056 setUrl_GmDocument(d->doc, d->mod.url);
1057 const int docWidth = documentWidth_DocumentWidget_(d);
1058 const int outsideMargin = (width_Widget(d) - docWidth) / 2;
1057 setSource_GmDocument(d->doc, 1059 setSource_GmDocument(d->doc,
1058 source, 1060 source,
1059 documentWidth_DocumentWidget_(d), 1061 docWidth,
1062 outsideMargin,
1060 isFinished_GmRequest(d->request) ? final_GmDocumentUpdate 1063 isFinished_GmRequest(d->request) ? final_GmDocumentUpdate
1061 : partial_GmDocumentUpdate); 1064 : partial_GmDocumentUpdate);
1062 documentWasChanged_DocumentWidget_(d); 1065 documentWasChanged_DocumentWidget_(d);
@@ -1165,13 +1168,25 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode
1165 iString *key = collectNew_String(); 1168 iString *key = collectNew_String();
1166 toString_Sym(SDLK_s, KMOD_PRIMARY, key); 1169 toString_Sym(SDLK_s, KMOD_PRIMARY, key);
1167 appendFormat_String(src, "\n```\n%s\n```\n", cstr_String(meta)); 1170 appendFormat_String(src, "\n```\n%s\n```\n", cstr_String(meta));
1168 makeFooterButtons_DocumentWidget_( 1171 const char *mtype = mediaTypeFromFileExtension_String(d->mod.url);
1169 d, 1172 iArray items;
1170 (iMenuItem[]){ { translateCStr_Lang(download_Icon " " saveToDownloads_Label), 1173 init_Array(&items, sizeof(iMenuItem));
1171 0, 1174 if (iCmpStr(mtype, "application/octet-stream")) {
1172 0, 1175 pushBack_Array(
1173 "document.save" } }, 1176 &items,
1174 1); 1177 &(iMenuItem){ translateCStr_Lang(format_CStr("View as \"%s\"", mtype)),
1178 SDLK_RETURN,
1179 0,
1180 format_CStr("document.setmediatype mime:%s", mtype) });
1181 }
1182 pushBack_Array(
1183 &items,
1184 &(iMenuItem){ translateCStr_Lang(download_Icon " " saveToDownloads_Label),
1185 0,
1186 0,
1187 "document.save" });
1188 makeFooterButtons_DocumentWidget_(d, data_Array(&items), size_Array(&items));
1189 deinit_Array(&items);
1175 break; 1190 break;
1176 } 1191 }
1177 default: 1192 default:
@@ -2254,7 +2269,7 @@ static iBool updateDocumentWidthRetainingScrollPosition_DocumentWidget_(iDocumen
2254 /* TODO: First *fully* visible run? */ 2269 /* TODO: First *fully* visible run? */
2255 voffset = visibleRange_DocumentWidget_(d).start - top_Rect(run->visBounds); 2270 voffset = visibleRange_DocumentWidget_(d).start - top_Rect(run->visBounds);
2256 } 2271 }
2257 setWidth_GmDocument(d->doc, newWidth); 2272 setWidth_GmDocument(d->doc, newWidth, (width_Widget(d) - newWidth) / 2);
2258 documentRunsInvalidated_DocumentWidget_(d); 2273 documentRunsInvalidated_DocumentWidget_(d);
2259 if (runLoc && !keepCenter) { 2274 if (runLoc && !keepCenter) {
2260 run = findRunAtLoc_GmDocument(d->doc, runLoc); 2275 run = findRunAtLoc_GmDocument(d->doc, runLoc);
@@ -3173,6 +3188,10 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
3173 document_App() == d) { 3188 document_App() == d) {
3174 return handleSwipe_DocumentWidget_(d, cmd); 3189 return handleSwipe_DocumentWidget_(d, cmd);
3175 } 3190 }
3191 else if (equal_Command(cmd, "document.setmediatype") && document_App() == d) {
3192 setUrlAndSource_DocumentWidget(d, d->mod.url, string_Command(cmd, "mime"), &d->sourceContent);
3193 return iTrue;
3194 }
3176 return iFalse; 3195 return iFalse;
3177} 3196}
3178 3197
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c
index ef306ab9..3bfa600b 100644
--- a/src/ui/labelwidget.c
+++ b/src/ui/labelwidget.c
@@ -68,7 +68,7 @@ static iInt2 padding_LabelWidget_(const iLabelWidget *d, int corner) {
68 : corner == 1 ? init_I2(w->padding[2], w->padding[1]) 68 : corner == 1 ? init_I2(w->padding[2], w->padding[1])
69 : corner == 2 ? init_I2(w->padding[2], w->padding[3]) 69 : corner == 2 ? init_I2(w->padding[2], w->padding[3])
70 : init_I2(w->padding[0], w->padding[3])); 70 : init_I2(w->padding[0], w->padding[3]));
71#if defined (iPlatformAppleMobile) 71#if defined (iPlatformMobile)
72 return add_I2(widgetPad, 72 return add_I2(widgetPad,
73 init_I2(flags & tight_WidgetFlag ? 2 * gap_UI : (4 * gap_UI), 73 init_I2(flags & tight_WidgetFlag ? 2 * gap_UI : (4 * gap_UI),
74 (flags & extraPadding_WidgetFlag ? 1.5f : 1.0f) * 3 * gap_UI / 2)); 74 (flags & extraPadding_WidgetFlag ? 1.5f : 1.0f) * 3 * gap_UI / 2));
diff --git a/src/ui/lookupwidget.c b/src/ui/lookupwidget.c
index a0a507ca..bf190d58 100644
--- a/src/ui/lookupwidget.c
+++ b/src/ui/lookupwidget.c
@@ -388,7 +388,7 @@ void init_LookupWidget(iLookupWidget *d) {
388 init_Widget(w); 388 init_Widget(w);
389 setId_Widget(w, "lookup"); 389 setId_Widget(w, "lookup");
390 setFlags_Widget(w, focusable_WidgetFlag, iTrue); 390 setFlags_Widget(w, focusable_WidgetFlag, iTrue);
391#if defined (iPlatformAppleMobile) 391#if defined (iPlatformMobile)
392 setFlags_Widget(w, unhittable_WidgetFlag, iTrue); 392 setFlags_Widget(w, unhittable_WidgetFlag, iTrue);
393#endif 393#endif
394 d->list = addChildFlags_Widget(w, iClob(new_ListWidget()), 394 d->list = addChildFlags_Widget(w, iClob(new_ListWidget()),
diff --git a/src/ui/metrics.c b/src/ui/metrics.c
index 32561ed7..53a52afb 100644
--- a/src/ui/metrics.c
+++ b/src/ui/metrics.c
@@ -33,7 +33,7 @@ iInt2 gap2_UI = { defaultGap_Metrics, defaultGap_Metrics };
33int fontSize_UI = defaultFontSize_Metrics; 33int fontSize_UI = defaultFontSize_Metrics;
34 34
35void setScale_Metrics(float scale) { 35void setScale_Metrics(float scale) {
36#if defined (iPlatformAppleMobile) 36#if defined (iPlatformMobile)
37 /* iPad needs a bit larger UI elements as the viewing distance is generally longer.*/ 37 /* iPad needs a bit larger UI elements as the viewing distance is generally longer.*/
38 if (deviceType_App() == tablet_AppDeviceType) { 38 if (deviceType_App() == tablet_AppDeviceType) {
39 scale *= 1.1f; 39 scale *= 1.1f;
diff --git a/src/ui/mobile.c b/src/ui/mobile.c
index 0ff3fe85..168a92b8 100644
--- a/src/ui/mobile.c
+++ b/src/ui/mobile.c
@@ -57,11 +57,13 @@ static enum iFontId labelBoldFont_(void) {
57 57
58static void updatePanelSheetMetrics_(iWidget *sheet) { 58static void updatePanelSheetMetrics_(iWidget *sheet) {
59 iWidget *navi = findChild_Widget(sheet, "panel.navi"); 59 iWidget *navi = findChild_Widget(sheet, "panel.navi");
60 iWidget *naviPad = child_Widget(navi, 0); 60// iWidget *naviPad = child_Widget(navi, 0);
61 int naviHeight = lineHeight_Text(labelFont_()) + 4 * gap_UI; 61 int naviHeight = lineHeight_Text(labelFont_()) + 4 * gap_UI;
62#if defined (iPlatformMobile)
63 float left = 0.0f, right = 0.0f, top = 0.0f, bottom = 0.0f;
62#if defined (iPlatformAppleMobile) 64#if defined (iPlatformAppleMobile)
63 float left, right, top, bottom;
64 safeAreaInsets_iOS(&left, &top, &right, &bottom); 65 safeAreaInsets_iOS(&left, &top, &right, &bottom);
66#endif
65 setPadding_Widget(sheet, left, 0, right, 0); 67 setPadding_Widget(sheet, left, 0, right, 0);
66 navi->rect.pos = init_I2(left, top); 68 navi->rect.pos = init_I2(left, top);
67 iConstForEach(PtrArray, i, findChildren_Widget(sheet, "panel.toppad")) { 69 iConstForEach(PtrArray, i, findChildren_Widget(sheet, "panel.toppad")) {
diff --git a/src/ui/root.c b/src/ui/root.c
index a8b9f998..a72f002c 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -83,7 +83,7 @@ static const iMenuItem navMenuItems_[] = {
83}; 83};
84#endif 84#endif
85 85
86#if defined (iPlatformAppleMobile) 86#if defined (iPlatformMobile)
87/* Tablet menu. */ 87/* Tablet menu. */
88static const iMenuItem tabletNavMenuItems_[] = { 88static const iMenuItem tabletNavMenuItems_[] = {
89 { folder_Icon " ${menu.openfile}", SDLK_o, KMOD_PRIMARY, "file.open" }, 89 { folder_Icon " ${menu.openfile}", SDLK_o, KMOD_PRIMARY, "file.open" },
@@ -120,9 +120,9 @@ static const iMenuItem phoneNavMenuItems_[] = {
120 { "---", 0, 0, NULL }, 120 { "---", 0, 0, NULL },
121 { gear_Icon " Settings...", SDLK_COMMA, KMOD_PRIMARY, "preferences" }, 121 { gear_Icon " Settings...", SDLK_COMMA, KMOD_PRIMARY, "preferences" },
122}; 122};
123#endif /* AppleMobile */ 123#endif /* Mobile */
124 124
125#if defined (iPlatformAppleMobile) 125#if defined (iPlatformMobile)
126static const iMenuItem identityButtonMenuItems_[] = { 126static const iMenuItem identityButtonMenuItems_[] = {
127 { "${menu.identity.notactive}", 0, 0, "ident.showactive" }, 127 { "${menu.identity.notactive}", 0, 0, "ident.showactive" },
128 { "---", 0, 0, NULL }, 128 { "---", 0, 0, NULL },
@@ -498,9 +498,10 @@ static void checkLoadAnimation_Root_(iRoot *d) {
498 498
499void updatePadding_Root(iRoot *d) { 499void updatePadding_Root(iRoot *d) {
500 if (d == NULL) return; 500 if (d == NULL) return;
501#if defined (iPlatformAppleMobile)
502 iWidget *toolBar = findChild_Widget(d->widget, "toolbar"); 501 iWidget *toolBar = findChild_Widget(d->widget, "toolbar");
503 float left, top, right, bottom; 502 float bottom = 0.0f;
503#if defined (iPlatformAppleMobile)
504 float left, top, right;
504 safeAreaInsets_iOS(&left, &top, &right, &bottom); 505 safeAreaInsets_iOS(&left, &top, &right, &bottom);
505 /* Respect the safe area insets. */ { 506 /* Respect the safe area insets. */ {
506 setPadding_Widget(findChild_Widget(d->widget, "navdiv"), left, top, right, 0); 507 setPadding_Widget(findChild_Widget(d->widget, "navdiv"), left, top, right, 0);
@@ -508,6 +509,7 @@ void updatePadding_Root(iRoot *d) {
508 setPadding_Widget(toolBar, left, 0, right, bottom); 509 setPadding_Widget(toolBar, left, 0, right, bottom);
509 } 510 }
510 } 511 }
512#endif
511 if (toolBar) { 513 if (toolBar) {
512 /* TODO: get this from toolBar height, but it's buggy for some reason */ 514 /* TODO: get this from toolBar height, but it's buggy for some reason */
513 const int sidebarBottomPad = isPortrait_App() ? 11 * gap_UI + bottom : 0; 515 const int sidebarBottomPad = isPortrait_App() ? 11 * gap_UI + bottom : 0;
@@ -517,7 +519,6 @@ void updatePadding_Root(iRoot *d) {
517 are not arranged correctly until it's hidden and reshown. */ 519 are not arranged correctly until it's hidden and reshown. */
518 } 520 }
519 /* Note that `handleNavBarCommands_` also adjusts padding and spacing. */ 521 /* Note that `handleNavBarCommands_` also adjusts padding and spacing. */
520#endif
521} 522}
522 523
523void updateToolbarColors_Root(iRoot *d) { 524void updateToolbarColors_Root(iRoot *d) {
@@ -829,13 +830,13 @@ static iBool handleSearchBarCommands_(iWidget *searchBar, const char *cmd) {
829 return iFalse; 830 return iFalse;
830} 831}
831 832
832#if defined (iPlatformAppleMobile) 833#if defined (iPlatformMobile)
833static void dismissSidebar_(iWidget *sidebar, const char *toolButtonId) { 834static void dismissSidebar_(iWidget *sidebar, const char *toolButtonId) {
834 if (isVisible_Widget(sidebar)) { 835 if (isVisible_Widget(sidebar)) {
835 postCommandf_App("%s.toggle", cstr_String(id_Widget(sidebar))); 836 postCommandf_App("%s.toggle", cstr_String(id_Widget(sidebar)));
836 if (toolButtonId) { 837// if (toolButtonId) {
837 // setFlags_Widget(findWidget_App(toolButtonId), noBackground_WidgetFlag, iTrue); 838 // setFlags_Widget(findWidget_App(toolButtonId), noBackground_WidgetFlag, iTrue);
838 } 839// }
839 setVisualOffset_Widget(sidebar, height_Widget(sidebar), 250, easeIn_AnimFlag); 840 setVisualOffset_Widget(sidebar, height_Widget(sidebar), 250, easeIn_AnimFlag);
840 } 841 }
841} 842}
@@ -909,7 +910,7 @@ static iBool handleToolBarCommands_(iWidget *toolBar, const char *cmd) {
909 } 910 }
910 return iFalse; 911 return iFalse;
911} 912}
912#endif /* defined (iPlatformAppleMobile) */ 913#endif /* defined (iPlatformMobile) */
913 914
914static iLabelWidget *newLargeIcon_LabelWidget(const char *text, const char *cmd) { 915static iLabelWidget *newLargeIcon_LabelWidget(const char *text, const char *cmd) {
915 iLabelWidget *lab = newIcon_LabelWidget(text, 0, 0, cmd); 916 iLabelWidget *lab = newIcon_LabelWidget(text, 0, 0, cmd);
@@ -1198,8 +1199,8 @@ void createUserInterface_Root(iRoot *d) {
1198#if defined (iPlatformMobile) 1199#if defined (iPlatformMobile)
1199 const iBool isPhone = (deviceType_App() == phone_AppDeviceType); 1200 const iBool isPhone = (deviceType_App() == phone_AppDeviceType);
1200#endif 1201#endif
1201#if !defined (iHaveNativeMenus) 1202#if !defined (iHaveNativeMenus) || defined (iPlatformMobile)
1202# if defined (iPlatformAppleMobile) 1203# if defined (iPlatformMobile)
1203 iLabelWidget *navMenu = 1204 iLabelWidget *navMenu =
1204 makeMenuButton_LabelWidget(menu_Icon, isPhone ? phoneNavMenuItems_ : tabletNavMenuItems_, 1205 makeMenuButton_LabelWidget(menu_Icon, isPhone ? phoneNavMenuItems_ : tabletNavMenuItems_,
1205 isPhone ? iElemCount(phoneNavMenuItems_) : iElemCount(tabletNavMenuItems_)); 1206 isPhone ? iElemCount(phoneNavMenuItems_) : iElemCount(tabletNavMenuItems_));
@@ -1280,9 +1281,9 @@ void createUserInterface_Root(iRoot *d) {
1280 addChild_Widget(searchBar, iClob(newIcon_LabelWidget(" \u2b9d ", 'g', KMOD_PRIMARY | KMOD_SHIFT, "find.prev"))); 1281 addChild_Widget(searchBar, iClob(newIcon_LabelWidget(" \u2b9d ", 'g', KMOD_PRIMARY | KMOD_SHIFT, "find.prev")));
1281 addChild_Widget(searchBar, iClob(newIcon_LabelWidget(close_Icon, SDLK_ESCAPE, 0, "find.close"))); 1282 addChild_Widget(searchBar, iClob(newIcon_LabelWidget(close_Icon, SDLK_ESCAPE, 0, "find.close")));
1282 } 1283 }
1283#if defined (iPlatformAppleMobile) 1284#if defined (iPlatformMobile)
1284 /* Bottom toolbar. */ 1285 /* Bottom toolbar. */
1285 if (isPhone_iOS()) { 1286 if (deviceType_App() == phone_AppDeviceType) {
1286 iWidget *toolBar = new_Widget(); 1287 iWidget *toolBar = new_Widget();
1287 addChild_Widget(root, iClob(toolBar)); 1288 addChild_Widget(root, iClob(toolBar));
1288 setId_Widget(toolBar, "toolbar"); 1289 setId_Widget(toolBar, "toolbar");
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index c2ad7bc6..b816b572 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -644,7 +644,7 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) {
644 d->buttonFont = uiLabel_FontId; 644 d->buttonFont = uiLabel_FontId;
645 d->itemFonts[0] = uiContent_FontId; 645 d->itemFonts[0] = uiContent_FontId;
646 d->itemFonts[1] = uiContentBold_FontId; 646 d->itemFonts[1] = uiContentBold_FontId;
647#if defined (iPlatformAppleMobile) 647#if defined (iPlatformMobile)
648 if (deviceType_App() == phone_AppDeviceType) { 648 if (deviceType_App() == phone_AppDeviceType) {
649 d->itemFonts[0] = defaultBig_FontId; 649 d->itemFonts[0] = defaultBig_FontId;
650 d->itemFonts[1] = defaultBigBold_FontId; 650 d->itemFonts[1] = defaultBigBold_FontId;
diff --git a/src/ui/text.c b/src/ui/text.c
index 006a4d0b..231281eb 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -361,7 +361,7 @@ static void initFonts_Text_(iText *d) {
361 h12Font = &fontIosevkaTermExtended_Embedded; 361 h12Font = &fontIosevkaTermExtended_Embedded;
362 h3Font = &fontIosevkaTermExtended_Embedded; 362 h3Font = &fontIosevkaTermExtended_Embedded;
363 } 363 }
364#if defined (iPlatformAppleMobile) 364#if defined (iPlatformMobile)
365 const float uiSize = fontSize_UI * 1.1f; 365 const float uiSize = fontSize_UI * 1.1f;
366#else 366#else
367 const float uiSize = fontSize_UI; 367 const float uiSize = fontSize_UI;
@@ -889,6 +889,7 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh
889 resize_Array(&d->visualToLogical, length); 889 resize_Array(&d->visualToLogical, length);
890 d->bidiLevels = length ? malloc(length) : NULL; 890 d->bidiLevels = length ? malloc(length) : NULL;
891 FriBidiParType baseDir = (FriBidiParType) FRIBIDI_TYPE_ON; 891 FriBidiParType baseDir = (FriBidiParType) FRIBIDI_TYPE_ON;
892 /* TODO: If this returns zero (error occurred), act like everything is LTR. */
892 fribidi_log2vis(constData_Array(&d->logical), 893 fribidi_log2vis(constData_Array(&d->logical),
893 length, 894 length,
894 &baseDir, 895 &baseDir,
diff --git a/src/ui/window.c b/src/ui/window.c
index f8391ed9..3ac02495 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -443,7 +443,11 @@ void init_Window(iWindow *d, iRect rect) {
443 if (left_Rect(rect) >= 0 || top_Rect(rect) >= 0) { 443 if (left_Rect(rect) >= 0 || top_Rect(rect) >= 0) {
444 SDL_SetWindowPosition(d->win, left_Rect(rect), top_Rect(rect)); 444 SDL_SetWindowPosition(d->win, left_Rect(rect), top_Rect(rect));
445 } 445 }
446#if defined (iPlatformMobile)
447 const iInt2 minSize = zero_I2(); /* windows aren't independently resizable */
448#else
446 const iInt2 minSize = init_I2(425, 325); 449 const iInt2 minSize = init_I2(425, 325);
450#endif
447 SDL_SetWindowMinimumSize(d->win, minSize.x, minSize.y); 451 SDL_SetWindowMinimumSize(d->win, minSize.x, minSize.y);
448 SDL_SetWindowTitle(d->win, "Lagrange"); 452 SDL_SetWindowTitle(d->win, "Lagrange");
449 /* Some info. */ { 453 /* Some info. */ {
@@ -712,7 +716,6 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {
712 } 716 }
713 } 717 }
714#endif /* defined LAGRANGE_ENABLE_CUSTOM_FRAME */ 718#endif /* defined LAGRANGE_ENABLE_CUSTOM_FRAME */
715 //printf("MOVED: %d, %d\n", ev->data1, ev->data2); fflush(stdout);
716 if (unsnap_Window_(d, &newPos)) { 719 if (unsnap_Window_(d, &newPos)) {
717 return iTrue; 720 return iTrue;
718 } 721 }
@@ -753,7 +756,13 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {
753 case SDL_WINDOWEVENT_MINIMIZED: 756 case SDL_WINDOWEVENT_MINIMIZED:
754 d->isMinimized = iTrue; 757 d->isMinimized = iTrue;
755 return iTrue; 758 return iTrue;
756#endif /* defined (iPlatformDesktop) */ 759#else /* if defined (!iPlatformDesktop) */
760 case SDL_WINDOWEVENT_RESIZED:
761 /* On mobile, this occurs when the display is rotated. */
762 invalidate_Window(d);
763 postRefresh_App();
764 return iTrue;
765#endif
757 case SDL_WINDOWEVENT_LEAVE: 766 case SDL_WINDOWEVENT_LEAVE:
758 unhover_Widget(); 767 unhover_Widget();
759 d->isMouseInside = iFalse; 768 d->isMouseInside = iFalse;
@@ -763,19 +772,12 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {
763 d->isMouseInside = iTrue; 772 d->isMouseInside = iTrue;
764 postCommand_App("window.mouse.entered"); 773 postCommand_App("window.mouse.entered");
765 return iTrue; 774 return iTrue;
766#if defined (iPlatformMobile)
767 case SDL_WINDOWEVENT_RESIZED:
768 /* On mobile, this occurs when the display is rotated. */
769 invalidate_Window(d);
770 postRefresh_App();
771 return iTrue;
772#endif
773 case SDL_WINDOWEVENT_FOCUS_GAINED: 775 case SDL_WINDOWEVENT_FOCUS_GAINED:
774 d->focusGainedAt = SDL_GetTicks(); 776 d->focusGainedAt = SDL_GetTicks();
775 setCapsLockDown_Keys(iFalse); 777 setCapsLockDown_Keys(iFalse);
776 postCommand_App("window.focus.gained"); 778 postCommand_App("window.focus.gained");
777 d->isExposed = iTrue; 779 d->isExposed = iTrue;
778#if defined (iPlatformMobile) 780#if !defined (iPlatformDesktop)
779 /* Returned to foreground, may have lost buffered content. */ 781 /* Returned to foreground, may have lost buffered content. */
780 invalidate_Window_(d, iTrue); 782 invalidate_Window_(d, iTrue);
781 postCommand_App("window.unfreeze"); 783 postCommand_App("window.unfreeze");
@@ -783,7 +785,7 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {
783 return iFalse; 785 return iFalse;
784 case SDL_WINDOWEVENT_FOCUS_LOST: 786 case SDL_WINDOWEVENT_FOCUS_LOST:
785 postCommand_App("window.focus.lost"); 787 postCommand_App("window.focus.lost");
786#if defined (iPlatformMobile) 788#if !defined (iPlatformDesktop)
787 setFreezeDraw_Window(d, iTrue); 789 setFreezeDraw_Window(d, iTrue);
788#endif 790#endif
789 return iFalse; 791 return iFalse;
@@ -949,6 +951,10 @@ iBool setKeyRoot_Window(iWindow *d, iRoot *root) {
949 return iFalse; 951 return iFalse;
950} 952}
951 953
954iLocalDef iBool isEscapeKeypress_(const SDL_Event *ev) {
955 return (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) && ev->key.keysym.sym == SDLK_ESCAPE;
956}
957
952iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) { 958iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) {
953 if (ev->type == SDL_MOUSEMOTION) { 959 if (ev->type == SDL_MOUSEMOTION) {
954 /* Hover widget may change. */ 960 /* Hover widget may change. */
@@ -964,7 +970,11 @@ iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) {
964 } 970 }
965 if ((ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP || ev->type == SDL_TEXTINPUT) 971 if ((ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP || ev->type == SDL_TEXTINPUT)
966 && d->keyRoot != root) { 972 && d->keyRoot != root) {
967 continue; /* Key events go only to the root with keyboard focus. */ 973 if (!isEscapeKeypress_(ev)) {
974 /* Key events go only to the root with keyboard focus, with the exception
975 of Escape that will also affect the entire window. */
976 continue;
977 }
968 } 978 }
969 if (ev->type == SDL_MOUSEWHEEL && !contains_Rect(rect_Root(root), 979 if (ev->type == SDL_MOUSEWHEEL && !contains_Rect(rect_Root(root),
970 coord_MouseWheelEvent(&ev->wheel))) { 980 coord_MouseWheelEvent(&ev->wheel))) {
@@ -1015,7 +1025,6 @@ void draw_Window(iWindow *d) {
1015 if (d->isDrawFrozen) { 1025 if (d->isDrawFrozen) {
1016 return; 1026 return;
1017 } 1027 }
1018//#if defined (iPlatformMobile)
1019 /* Check if root needs resizing. */ { 1028 /* Check if root needs resizing. */ {
1020 iInt2 renderSize; 1029 iInt2 renderSize;
1021 SDL_GetRendererOutputSize(d->render, &renderSize.x, &renderSize.y); 1030 SDL_GetRendererOutputSize(d->render, &renderSize.x, &renderSize.y);
@@ -1024,7 +1033,6 @@ void draw_Window(iWindow *d) {
1024 processEvents_App(postedEventsOnly_AppEventMode); 1033 processEvents_App(postedEventsOnly_AppEventMode);
1025 } 1034 }
1026 } 1035 }
1027//#endif
1028 const int winFlags = SDL_GetWindowFlags(d->win); 1036 const int winFlags = SDL_GetWindowFlags(d->win);
1029 const iBool gotFocus = (winFlags & SDL_WINDOW_INPUT_FOCUS) != 0; 1037 const iBool gotFocus = (winFlags & SDL_WINDOW_INPUT_FOCUS) != 0;
1030 iPaint p; 1038 iPaint p;
@@ -1032,7 +1040,7 @@ void draw_Window(iWindow *d) {
1032 /* Clear the window. The clear color is visible as a border around the window 1040 /* Clear the window. The clear color is visible as a border around the window
1033 when the custom frame is being used. */ { 1041 when the custom frame is being used. */ {
1034 setCurrent_Root(d->roots[0]); 1042 setCurrent_Root(d->roots[0]);
1035#if defined (iPlatformAppleMobile) 1043#if defined (iPlatformMobile)
1036 iColor back = get_Color(uiBackground_ColorId); 1044 iColor back = get_Color(uiBackground_ColorId);
1037 if (deviceType_App() == phone_AppDeviceType) { 1045 if (deviceType_App() == phone_AppDeviceType) {
1038 /* Page background extends to safe area, so fill it completely. */ 1046 /* Page background extends to safe area, so fill it completely. */