summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-12-09 15:06:02 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-12-09 15:06:02 +0200
commita265ab290cc9bce2f0c305a3eaf9d743380b967b (patch)
treeb0842b3fa63ecfb7b135341e40abf70659bf1101 /src
parent38bc51a65be35488857249e5ed5949172f06ee89 (diff)
iOS: Fixes and new edit menu for UploadWidget
The sizing and behavior of the input field on the plain text upload page is much improved.
Diffstat (limited to 'src')
-rw-r--r--src/ios.h7
-rw-r--r--src/ios.m37
-rw-r--r--src/ui/inputwidget.c25
-rw-r--r--src/ui/labelwidget.c4
-rw-r--r--src/ui/uploadwidget.c92
-rw-r--r--src/ui/util.c1
6 files changed, 130 insertions, 36 deletions
diff --git a/src/ios.h b/src/ios.h
index 6c5ec8d5..fbe7c2eb 100644
--- a/src/ios.h
+++ b/src/ios.h
@@ -38,6 +38,7 @@ void playHapticEffect_iOS (enum iHapticEffect effect);
38void exportDownloadedFile_iOS(const iString *path); 38void exportDownloadedFile_iOS(const iString *path);
39void pickFileForOpening_iOS (void); 39void pickFileForOpening_iOS (void);
40void pickFile_iOS (const char *command); /* ` path:%s` will be appended */ 40void pickFile_iOS (const char *command); /* ` path:%s` will be appended */
41void openTextActivityView_iOS(const iString *text);
41 42
42iBool isPhone_iOS (void); 43iBool isPhone_iOS (void);
43void safeAreaInsets_iOS (float *left, float *top, float *right, float *bottom); 44void safeAreaInsets_iOS (float *left, float *top, float *right, float *bottom);
@@ -79,9 +80,11 @@ iDeclareType(SystemTextInput)
79iDeclareTypeConstructionArgs(SystemTextInput, iRect rect, int flags) 80iDeclareTypeConstructionArgs(SystemTextInput, iRect rect, int flags)
80 81
81void setRect_SystemTextInput (iSystemTextInput *, iRect rect); 82void setRect_SystemTextInput (iSystemTextInput *, iRect rect);
82void setText_SystemTextInput (iSystemTextInput *, const iString *text); 83void setText_SystemTextInput (iSystemTextInput *, const iString *text, iBool allowUndo);
83void setFont_SystemTextInput (iSystemTextInput *, int fontId); 84void setFont_SystemTextInput (iSystemTextInput *, int fontId);
84void setTextChangedFunc_SystemTextInput (iSystemTextInput *, void (*textChangedFunc)(iSystemTextInput *, void *), void *); 85void setTextChangedFunc_SystemTextInput
86 (iSystemTextInput *, void (*textChangedFunc)(iSystemTextInput *, void *), void *);
87void selectAll_SystemTextInput(iSystemTextInput *);
85 88
86const iString * text_SystemTextInput (const iSystemTextInput *); 89const iString * text_SystemTextInput (const iSystemTextInput *);
87int preferredHeight_SystemTextInput (const iSystemTextInput *); 90int preferredHeight_SystemTextInput (const iSystemTextInput *);
diff --git a/src/ios.m b/src/ios.m
index 2705a350..b6fbdec0 100644
--- a/src/ios.m
+++ b/src/ios.m
@@ -536,6 +536,15 @@ void pickFile_iOS(const char *command) {
536 [viewController_(get_Window()) presentViewController:picker animated:YES completion:nil]; 536 [viewController_(get_Window()) presentViewController:picker animated:YES completion:nil];
537} 537}
538 538
539void openTextActivityView_iOS(const iString *text) {
540 UIActivityViewController *actView =
541 [[UIActivityViewController alloc]
542 initWithActivityItems:@[
543 [NSString stringWithUTF8String:cstr_String(text)]]
544 applicationActivities:nil];
545 [viewController_(get_Window()) presentViewController:actView animated:YES completion:nil];
546}
547
539/*----------------------------------------------------------------------------------------------*/ 548/*----------------------------------------------------------------------------------------------*/
540 549
541enum iAVFAudioPlayerState { 550enum iAVFAudioPlayerState {
@@ -784,7 +793,16 @@ void deinit_SystemTextInput(iSystemTextInput *d) {
784 } 793 }
785} 794}
786 795
787void setText_SystemTextInput(iSystemTextInput *d, const iString *text) { 796void selectAll_SystemTextInput(iSystemTextInput *d) {
797 if (d->field) {
798 [REF_d_field selectAll:nil];
799 }
800 if (d->view) {
801 [REF_d_view selectAll:nil];
802 }
803}
804
805void setText_SystemTextInput(iSystemTextInput *d, const iString *text, iBool allowUndo) {
788 NSString *str = [NSString stringWithUTF8String:cstr_String(text)]; 806 NSString *str = [NSString stringWithUTF8String:cstr_String(text)];
789 if (d->field) { 807 if (d->field) {
790 [REF_d_field setText:str]; 808 [REF_d_field setText:str];
@@ -793,9 +811,22 @@ void setText_SystemTextInput(iSystemTextInput *d, const iString *text) {
793 } 811 }
794 } 812 }
795 else { 813 else {
796 [REF_d_view setText:str]; 814 UITextView *view = REF_d_view;
815// if (allowUndo) {
816// [view selectAll:nil];
817// if ([view shouldChangeTextInRange:[view selectedTextRange] replacementText:@""]) {
818// [[view textStorage] beginEditing];
819// [[view textStorage] replaceCharactersInRange:[view selectedRange] withString:@""];
820// [[view textStorage] endEditing];
821// }
822// }
823// else {
824 // TODO: How to implement `allowUndo`, given that UITextView does not exist when unfocused?
825 // Maybe keep the UITextStorage (if it has the undo?)?
826 [view setText:str];
827// }
797 if (d->flags & selectAll_SystemTextInputFlags) { 828 if (d->flags & selectAll_SystemTextInputFlags) {
798 [REF_d_view selectAll:nil]; 829 [view selectAll:nil];
799 } 830 }
800 } 831 }
801} 832}
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index 3be6a383..22f983eb 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -715,7 +715,8 @@ static void updateAllLinesAndResizeHeight_InputWidget_(iInputWidget *d) {
715 const int height = measure_WrapText(&wt, d->font).bounds.size.y; 715 const int height = measure_WrapText(&wt, d->font).bounds.size.y;
716 /* We use this to store the number wrapped lines for determining widget height. */ 716 /* We use this to store the number wrapped lines for determining widget height. */
717 d->visWrapLines.start = 0; 717 d->visWrapLines.start = 0;
718 d->visWrapLines.end = iMin(d->maxWrapLines, height / lineHeight_Text(d->font)); 718 d->visWrapLines.end = iMax(d->minWrapLines,
719 iMin(d->maxWrapLines, height / lineHeight_Text(d->font)));
719 updateMetrics_InputWidget_(d); 720 updateMetrics_InputWidget_(d);
720} 721}
721 722
@@ -733,8 +734,10 @@ static int contentHeight_InputWidget_(const iInputWidget *d) {
733 if (d->buffered && ~d->inFlags & needUpdateBuffer_InputWidgetFlag) { 734 if (d->buffered && ~d->inFlags & needUpdateBuffer_InputWidgetFlag) {
734 return iClamp(d->buffered->size.y, minHeight, maxHeight); 735 return iClamp(d->buffered->size.y, minHeight, maxHeight);
735 } 736 }
736#endif 737 return minHeight;
738#else
737 return (int) size_Range(&d->visWrapLines) * lineHeight; 739 return (int) size_Range(&d->visWrapLines) * lineHeight;
740#endif
738} 741}
739 742
740static void updateTextInputRect_InputWidget_(const iInputWidget *d) { 743static void updateTextInputRect_InputWidget_(const iInputWidget *d) {
@@ -1111,7 +1114,7 @@ void setText_InputWidget(iInputWidget *d, const iString *text) {
1111#else 1114#else
1112 set_String(&d->text, nfcText); 1115 set_String(&d->text, nfcText);
1113 if (d->sysCtrl) { 1116 if (d->sysCtrl) {
1114 setText_SystemTextInput(d->sysCtrl, nfcText); 1117 setText_SystemTextInput(d->sysCtrl, nfcText, iTrue);
1115 } 1118 }
1116 else { 1119 else {
1117 updateAllLinesAndResizeHeight_InputWidget_(d); /* need to know the new height */ 1120 updateAllLinesAndResizeHeight_InputWidget_(d); /* need to know the new height */
@@ -1134,7 +1137,11 @@ void setTextCStr_InputWidget(iInputWidget *d, const char *cstr) {
1134} 1137}
1135 1138
1136void selectAll_InputWidget(iInputWidget *d) { 1139void selectAll_InputWidget(iInputWidget *d) {
1137#if !LAGRANGE_USE_SYSTEM_TEXT_INPUT 1140#if LAGRANGE_USE_SYSTEM_TEXT_INPUT
1141 if (d->sysCtrl) {
1142 selectAll_SystemTextInput(d->sysCtrl);
1143 }
1144#else
1138 d->mark = (iRanges){ 0, lastLine_InputWidget_(d)->range.end }; 1145 d->mark = (iRanges){ 0, lastLine_InputWidget_(d)->range.end };
1139 refresh_Widget(as_Widget(d)); 1146 refresh_Widget(as_Widget(d));
1140#endif 1147#endif
@@ -1177,7 +1184,7 @@ void begin_InputWidget(iInputWidget *d) {
1177 (isAllowedToInsertNewline_InputWidget_(d) ? insertNewlines_SystemTextInputFlag : 0) | 1184 (isAllowedToInsertNewline_InputWidget_(d) ? insertNewlines_SystemTextInputFlag : 0) |
1178 (d->inFlags & selectAllOnFocus_InputWidgetFlag ? selectAll_SystemTextInputFlags : 0)); 1185 (d->inFlags & selectAllOnFocus_InputWidgetFlag ? selectAll_SystemTextInputFlags : 0));
1179 setFont_SystemTextInput(d->sysCtrl, d->font); 1186 setFont_SystemTextInput(d->sysCtrl, d->font);
1180 setText_SystemTextInput(d->sysCtrl, &d->oldText); 1187 setText_SystemTextInput(d->sysCtrl, &d->oldText, iFalse);
1181 setTextChangedFunc_SystemTextInput(d->sysCtrl, systemInputChanged_InputWidget_, d); 1188 setTextChangedFunc_SystemTextInput(d->sysCtrl, systemInputChanged_InputWidget_, d);
1182 iConnect(Root, w->root, visualOffsetsChanged, d, updateAfterVisualOffsetChange_InputWidget_); 1189 iConnect(Root, w->root, visualOffsetsChanged, d, updateAfterVisualOffsetChange_InputWidget_);
1183 updateTextInputRect_InputWidget_(d); 1190 updateTextInputRect_InputWidget_(d);
@@ -2157,10 +2164,6 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
2157 } 2164 }
2158 return iTrue; 2165 return iTrue;
2159 } 2166 }
2160 else if (isCommand_UserEvent(ev, "input.selectall") && isEditing_InputWidget_(d)) {
2161 selectAll_InputWidget(d);
2162 return iTrue;
2163 }
2164 else if (isCommand_UserEvent(ev, "text.insert")) { 2167 else if (isCommand_UserEvent(ev, "text.insert")) {
2165 pushUndo_InputWidget_(d); 2168 pushUndo_InputWidget_(d);
2166 deleteMarked_InputWidget_(d); 2169 deleteMarked_InputWidget_(d);
@@ -2169,6 +2172,10 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
2169 return iTrue; 2172 return iTrue;
2170 } 2173 }
2171#endif 2174#endif
2175 else if (isCommand_UserEvent(ev, "input.selectall") && isEditing_InputWidget_(d)) {
2176 selectAll_InputWidget(d);
2177 return iTrue;
2178 }
2172 else if (isCommand_UserEvent(ev, "theme.changed")) { 2179 else if (isCommand_UserEvent(ev, "theme.changed")) {
2173 if (d->buffered) { 2180 if (d->buffered) {
2174 d->inFlags |= needUpdateBuffer_InputWidgetFlag; 2181 d->inFlags |= needUpdateBuffer_InputWidgetFlag;
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c
index 9e0d37e4..5ad43b0e 100644
--- a/src/ui/labelwidget.c
+++ b/src/ui/labelwidget.c
@@ -134,6 +134,10 @@ static iBool processEvent_LabelWidget_(iLabelWidget *d, const SDL_Event *ev) {
134 refresh_Widget(d); 134 refresh_Widget(d);
135 return iFalse; 135 return iFalse;
136 } 136 }
137 else if (isCommand_Widget(w, ev, "trigger")) {
138 trigger_LabelWidget_(d);
139 return iTrue;
140 }
137 if (!isEmpty_String(&d->command)) { 141 if (!isEmpty_String(&d->command)) {
138#if 0 && defined (iPlatformAppleMobile) 142#if 0 && defined (iPlatformAppleMobile)
139 /* Touch allows activating any button on release. */ 143 /* Touch allows activating any button on release. */
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c
index e11970ec..58106dcb 100644
--- a/src/ui/uploadwidget.c
+++ b/src/ui/uploadwidget.c
@@ -92,17 +92,19 @@ static void updateProgress_UploadWidget_(iGmRequest *request, size_t current, si
92static void updateInputMaxHeight_UploadWidget_(iUploadWidget *d) { 92static void updateInputMaxHeight_UploadWidget_(iUploadWidget *d) {
93 iWidget *w = as_Widget(d); 93 iWidget *w = as_Widget(d);
94 /* Calculate how many lines fits vertically in the view. */ 94 /* Calculate how many lines fits vertically in the view. */
95 const iInt2 inputPos = topLeft_Rect(bounds_Widget(as_Widget(d->input))); 95 const iInt2 inputPos = topLeft_Rect(bounds_Widget(as_Widget(d->input)));
96 const int footerHeight = isUsingPanelLayout_Mobile() ? 0 : 96 int footerHeight = 0;
97 (height_Widget(d->token) + 97 if (!isUsingPanelLayout_Mobile()) {
98 height_Widget(findChild_Widget(w, "dialogbuttons")) + 98 footerHeight = (height_Widget(d->token) +
99 12 * gap_UI); 99 height_Widget(findChild_Widget(w, "dialogbuttons")) +
100 const int avail = bottom_Rect(safeRect_Root(w->root)) - footerHeight - 100 12 * gap_UI);
101 get_MainWindow()->keyboardHeight; 101 }
102 setLineLimits_InputWidget(d->input, 102 const int avail = bottom_Rect(visibleRect_Root(w->root)) - footerHeight - inputPos.y;
103 minLines_InputWidget(d->input), 103 /* On desktop, retain the previously set minLines value. */
104 iMaxi(minLines_InputWidget(d->input), 104 int minLines = isUsingPanelLayout_Mobile() ? 1 : minLines_InputWidget(d->input);
105 (avail - inputPos.y) / lineHeight_Text(font_InputWidget(d->input)))); 105 int maxLines = iMaxi(minLines, avail / lineHeight_Text(font_InputWidget(d->input)));
106 /* On mobile, the height is fixed to the available space. */
107 setLineLimits_InputWidget(d->input, isUsingPanelLayout_Mobile() ? maxLines : minLines, maxLines);
106} 108}
107 109
108static const iGmIdentity *titanIdentityForUrl_(const iString *url) { 110static const iGmIdentity *titanIdentityForUrl_(const iString *url) {
@@ -208,11 +210,7 @@ void init_UploadWidget(iUploadWidget *d) {
208 enableUploadButton_UploadWidget_(d, iFalse); 210 enableUploadButton_UploadWidget_(d, iFalse);
209 } 211 }
210 iWidget *title = findChild_Widget(w, "heading.upload.text"); 212 iWidget *title = findChild_Widget(w, "heading.upload.text");
211 iLabelWidget *menu = makeMenuButton_LabelWidget(midEllipsis_Icon, (iMenuItem[]){ 213 iLabelWidget *menu = new_LabelWidget(midEllipsis_Icon, "upload.editmenu.open");
212 { export_Icon " ${upload.text.export}", 0, 0, "upload.text.export" },
213 { "---" },
214 { delete_Icon " " uiTextCaution_ColorEscape "${menu.delete}", 0, 0, "upload.text.delete" }
215 }, 3);
216 setTextColor_LabelWidget(menu, uiTextAction_ColorId); 214 setTextColor_LabelWidget(menu, uiTextAction_ColorId);
217 setFont_LabelWidget(menu, uiLabelBigBold_FontId); 215 setFont_LabelWidget(menu, uiLabelBigBold_FontId);
218 addChildFlags_Widget(title, iClob(menu), frameless_WidgetFlag | moveToParentRightEdge_WidgetFlag); 216 addChildFlags_Widget(title, iClob(menu), frameless_WidgetFlag | moveToParentRightEdge_WidgetFlag);
@@ -428,12 +426,19 @@ static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) {
428 } 426 }
429 else if (equal_Command(cmd, "panel.changed")) { 427 else if (equal_Command(cmd, "panel.changed")) {
430 showOrHideUploadButton_UploadWidget_(d); 428 showOrHideUploadButton_UploadWidget_(d);
431 setFocus_Widget(NULL); 429 if (currentPanelIndex_Mobile(w) == 0) {
430 setFocus_Widget(as_Widget(d->input));
431 }
432 else {
433 setFocus_Widget(NULL);
434 }
435 refresh_Widget(d->input);
432 return iFalse; 436 return iFalse;
433 } 437 }
434#if defined (iPlatformAppleMobile) 438#if defined (iPlatformAppleMobile)
435 else if (deviceType_App() != desktop_AppDeviceType && equal_Command(cmd, "menu.opened")) { 439 else if (deviceType_App() != desktop_AppDeviceType && equal_Command(cmd, "menu.opened")) {
436 setFocus_Widget(NULL); /* overlaid text fields! */ 440 setFocus_Widget(NULL); /* overlaid text fields! */
441 refresh_Widget(d->input);
437 return iFalse; 442 return iFalse;
438 } 443 }
439#endif 444#endif
@@ -474,6 +479,44 @@ static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) {
474 updateIdentityDropdown_UploadWidget_(d); 479 updateIdentityDropdown_UploadWidget_(d);
475 return iTrue; 480 return iTrue;
476 } 481 }
482 if (isCommand_Widget(w, ev, "upload.editmenu.open")) {
483 setFocus_Widget(NULL);
484 refresh_Widget(as_Widget(d->input));
485 iWidget *editMenu = makeMenu_Widget(root_Widget(w), (iMenuItem[]){
486 { select_Icon " ${menu.selectall}", 0, 0, "upload.text.selectall" },
487 { export_Icon " ${menu.upload.export}", 0, 0, "upload.text.export" },
488 { "---" },
489 { delete_Icon " " uiTextCaution_ColorEscape "${menu.upload.delete}", 0, 0, "upload.text.delete" }
490 }, 4);
491 openMenu_Widget(editMenu, topLeft_Rect(bounds_Widget(as_Widget(d->input))));
492 return iTrue;
493 }
494 if (isCommand_UserEvent(ev, "upload.text.export")) {
495#if defined (iPlatformAppleMobile)
496 openTextActivityView_iOS(text_InputWidget(d->input));
497#endif
498 return iTrue;
499 }
500 if (isCommand_UserEvent(ev, "upload.text.delete")) {
501 if (argLabel_Command(command_UserEvent(ev), "confirmed")) {
502 setTextCStr_InputWidget(d->input, "");
503 setFocus_Widget(as_Widget(d->input));
504 }
505 else {
506 iWidget *confirm = makeMenu_Widget(root_Widget(w), (iMenuItem[]){
507 { delete_Icon " " uiTextCaution_ColorEscape "${menu.upload.delete.confirm}", 0, 0,
508 "upload.text.delete confirmed:1" }
509 }, 1);
510 openMenu_Widget(confirm, zero_I2());
511 }
512 return iTrue;
513 }
514 if (isCommand_UserEvent(ev, "upload.text.selectall")) {
515 setFocus_Widget(as_Widget(d->input));
516 refresh_Widget(as_Widget(d->input));
517 postCommand_Widget(d->input, "input.selectall");
518 return iTrue;
519 }
477 if (isCommand_Widget(w, ev, "upload.accept")) { 520 if (isCommand_Widget(w, ev, "upload.accept")) {
478 iBool isText; 521 iBool isText;
479 iWidget *tabs = findChild_Widget(w, "upload.tabs"); 522 iWidget *tabs = findChild_Widget(w, "upload.tabs");
@@ -569,11 +612,16 @@ static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) {
569 destroy_Widget(w); 612 destroy_Widget(w);
570 return iTrue; 613 return iTrue;
571 } 614 }
572 else if (!isUsingPanelLayout_Mobile() && isCommand_Widget(w, ev, "input.resized")) { 615 else if (isCommand_Widget(w, ev, "input.resized")) {
573 resizeToLargestPage_Widget(findChild_Widget(w, "upload.tabs")); 616 if (!isUsingPanelLayout_Mobile()) {
574 arrange_Widget(w); 617 resizeToLargestPage_Widget(findChild_Widget(w, "upload.tabs"));
575 refresh_Widget(w); 618 arrange_Widget(w);
576 return iTrue; 619 refresh_Widget(w);
620 return iTrue;
621 }
622 else {
623 refresh_Widget(as_Widget(d->input));
624 }
577 } 625 }
578 else if (isCommand_Widget(w, ev, "upload.pickfile")) { 626 else if (isCommand_Widget(w, ev, "upload.pickfile")) {
579#if defined (iPlatformAppleMobile) 627#if defined (iPlatformAppleMobile)
diff --git a/src/ui/util.c b/src/ui/util.c
index 73fafe84..daefc279 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -671,6 +671,7 @@ static iBool isCommandIgnoredByMenus_(const char *cmd) {
671 equal_Command(cmd, "window.reload.update") || 671 equal_Command(cmd, "window.reload.update") ||
672 equal_Command(cmd, "window.mouse.exited") || 672 equal_Command(cmd, "window.mouse.exited") ||
673 equal_Command(cmd, "window.mouse.entered") || 673 equal_Command(cmd, "window.mouse.entered") ||
674 equal_Command(cmd, "input.backup") ||
674 (equal_Command(cmd, "mouse.clicked") && !arg_Command(cmd)); /* button released */ 675 (equal_Command(cmd, "mouse.clicked") && !arg_Command(cmd)); /* button released */
675} 676}
676 677