summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--po/en.po9
-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
7 files changed, 139 insertions, 36 deletions
diff --git a/po/en.po b/po/en.po
index 8e690649..89a05939 100644
--- a/po/en.po
+++ b/po/en.po
@@ -1017,6 +1017,15 @@ msgstr "Default"
1017msgid "heading.upload.text" 1017msgid "heading.upload.text"
1018msgstr "Text" 1018msgstr "Text"
1019 1019
1020msgid "menu.upload.export"
1021msgstr "Export Text"
1022
1023msgid "menu.upload.delete"
1024msgstr "Delete All"
1025
1026msgid "menu.upload.delete.confirm"
1027msgstr "Really Delete All (No Undo)"
1028
1020msgid "hint.upload.text" 1029msgid "hint.upload.text"
1021msgstr "enter text to upload" 1030msgstr "enter text to upload"
1022 1031
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