diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-12-05 07:54:15 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-12-05 07:54:15 +0200 |
commit | b8656d5a2e0f540d47d2dc5ccc269f709dd0b923 (patch) | |
tree | fbfc90bddc79bf35fb024293270f71496186fb22 /src/ios.m | |
parent | 8050511b683d89c5ef907bc461b98e9aaaa051a7 (diff) |
iOS: Multiline text input using UITextView
Diffstat (limited to 'src/ios.m')
-rw-r--r-- | src/ios.m | 158 |
1 files changed, 126 insertions, 32 deletions
@@ -161,7 +161,7 @@ API_AVAILABLE(ios(13.0)) | |||
161 | 161 | ||
162 | /*----------------------------------------------------------------------------------------------*/ | 162 | /*----------------------------------------------------------------------------------------------*/ |
163 | 163 | ||
164 | @interface AppState : NSObject<UIDocumentPickerDelegate, UITextFieldDelegate> { | 164 | @interface AppState : NSObject<UIDocumentPickerDelegate, UITextFieldDelegate, UITextViewDelegate> { |
165 | iString *fileBeingSaved; | 165 | iString *fileBeingSaved; |
166 | iString *pickFileCommand; | 166 | iString *pickFileCommand; |
167 | iSystemTextInput *sysCtrl; | 167 | iSystemTextInput *sysCtrl; |
@@ -273,16 +273,21 @@ didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls { | |||
273 | SDL_Event ev = { .type = SDL_KEYDOWN }; | 273 | SDL_Event ev = { .type = SDL_KEYDOWN }; |
274 | ev.key.keysym.sym = SDLK_RETURN; | 274 | ev.key.keysym.sym = SDLK_RETURN; |
275 | SDL_PushEvent(&ev); | 275 | SDL_PushEvent(&ev); |
276 | printf("Return pressed\n"); | ||
277 | return NO; | 276 | return NO; |
278 | } | 277 | } |
279 | 278 | ||
280 | - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { | 279 | - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range |
280 | replacementString:(NSString *)string { | ||
281 | iSystemTextInput *sysCtrl = [appState_ systemTextInput]; | 281 | iSystemTextInput *sysCtrl = [appState_ systemTextInput]; |
282 | notifyChange_SystemTextInput_(sysCtrl); | 282 | notifyChange_SystemTextInput_(sysCtrl); |
283 | return YES; | 283 | return YES; |
284 | } | 284 | } |
285 | 285 | ||
286 | - (void)textViewDidChange:(UITextView *)textView { | ||
287 | iSystemTextInput *sysCtrl = [appState_ systemTextInput]; | ||
288 | notifyChange_SystemTextInput_(sysCtrl); | ||
289 | } | ||
290 | |||
286 | @end | 291 | @end |
287 | 292 | ||
288 | static void enableMouse_(iBool yes) { | 293 | static void enableMouse_(iBool yes) { |
@@ -538,6 +543,10 @@ void init_AVFAudioPlayer(iAVFAudioPlayer *d) { | |||
538 | 543 | ||
539 | void deinit_AVFAudioPlayer(iAVFAudioPlayer *d) { | 544 | void deinit_AVFAudioPlayer(iAVFAudioPlayer *d) { |
540 | setInput_AVFAudioPlayer(d, NULL, NULL); | 545 | setInput_AVFAudioPlayer(d, NULL, NULL); |
546 | if (d->player) { | ||
547 | CFBridgingRelease(d->player); | ||
548 | d->player = nil; | ||
549 | } | ||
541 | } | 550 | } |
542 | 551 | ||
543 | static const char *cacheDir_ = "~/Library/Caches/Audio"; | 552 | static const char *cacheDir_ = "~/Library/Caches/Audio"; |
@@ -638,70 +647,155 @@ iBool isPaused_AVFAudioPlayer(const iAVFAudioPlayer *d) { | |||
638 | /*----------------------------------------------------------------------------------------------*/ | 647 | /*----------------------------------------------------------------------------------------------*/ |
639 | 648 | ||
640 | struct Impl_SystemTextInput { | 649 | struct Impl_SystemTextInput { |
641 | void *ctrl; | 650 | int flags; |
651 | void *field; /* single-line text field */ | ||
652 | void *view; /* multi-line text view */ | ||
642 | void (*textChangedFunc)(iSystemTextInput *, void *); | 653 | void (*textChangedFunc)(iSystemTextInput *, void *); |
643 | void *textChangedContext; | 654 | void *textChangedContext; |
644 | }; | 655 | }; |
645 | 656 | ||
646 | iDefineTypeConstructionArgs(SystemTextInput, (int flags), flags) | 657 | iDefineTypeConstructionArgs(SystemTextInput, (iRect rect, int flags), rect, flags) |
647 | 658 | ||
648 | #define REF_d_ctrl (__bridge UITextField *)d->ctrl | 659 | #define REF_d_field (__bridge UITextField *)d->field |
660 | #define REF_d_view (__bridge UITextView *)d->view | ||
649 | 661 | ||
650 | void init_SystemTextInput(iSystemTextInput *d, int flags) { | 662 | static CGRect convertToCGRect_(const iRect *rect) { |
651 | d->ctrl = (void *) CFBridgingRetain([[UITextField alloc] init]); | 663 | const iWindow *win = get_Window(); |
652 | UITextField *field = REF_d_ctrl; | 664 | CGRect frame; |
665 | // TODO: Convert coordinates properly! | ||
666 | frame.origin.x = rect->pos.x / win->pixelRatio; | ||
667 | frame.origin.y = (rect->pos.y - gap_UI + 2) / win->pixelRatio; | ||
668 | frame.size.width = rect->size.x / win->pixelRatio; | ||
669 | frame.size.height = rect->size.y / win->pixelRatio; | ||
670 | return frame; | ||
671 | } | ||
672 | |||
673 | static UIColor *makeUIColor_(enum iColorId colorId) { | ||
674 | iColor color = get_Color(colorId); | ||
675 | return [UIColor colorWithRed:color.r / 255.0 | ||
676 | green:color.g / 255.0 | ||
677 | blue:color.b / 255.0 | ||
678 | alpha:color.a / 255.0]; | ||
679 | } | ||
680 | |||
681 | void init_SystemTextInput(iSystemTextInput *d, iRect rect, int flags) { | ||
682 | d->flags = flags; | ||
683 | d->field = NULL; | ||
684 | d->view = NULL; | ||
685 | CGRect frame = convertToCGRect_(&rect); | ||
686 | if (flags & multiLine_SystemTextInputFlags) { | ||
687 | d->view = (void *) CFBridgingRetain([[UITextView alloc] initWithFrame:frame textContainer:nil]); | ||
688 | [[viewController_(get_Window()) view] addSubview:REF_d_view]; | ||
689 | } | ||
690 | else { | ||
691 | d->field = (void *) CFBridgingRetain([[UITextField alloc] initWithFrame:frame]); | ||
692 | [[viewController_(get_Window()) view] addSubview:REF_d_field]; | ||
693 | } | ||
694 | UIControl<UITextInputTraits> *traits = (UIControl<UITextInputTraits> *) (d->view ? REF_d_view : REF_d_field); | ||
653 | // TODO: Use the right font: https://developer.apple.com/documentation/uikit/text_display_and_fonts/adding_a_custom_font_to_your_app?language=objc | 695 | // TODO: Use the right font: https://developer.apple.com/documentation/uikit/text_display_and_fonts/adding_a_custom_font_to_your_app?language=objc |
654 | [[viewController_(get_Window()) view] addSubview:REF_d_ctrl]; | ||
655 | if (flags & returnGo_SystemTextInputFlags) { | 696 | if (flags & returnGo_SystemTextInputFlags) { |
656 | [field setReturnKeyType:UIReturnKeyGo]; | 697 | [traits setReturnKeyType:UIReturnKeyGo]; |
657 | } | 698 | } |
658 | if (flags & returnSend_SystemTextInputFlags) { | 699 | if (flags & returnSend_SystemTextInputFlags) { |
659 | [field setReturnKeyType:UIReturnKeySend]; | 700 | [traits setReturnKeyType:UIReturnKeySend]; |
660 | } | 701 | } |
661 | if (flags & disableAutocorrect_SystemTextInputFlag) { | 702 | if (flags & disableAutocorrect_SystemTextInputFlag) { |
662 | [field setAutocorrectionType:UITextAutocorrectionTypeNo]; | 703 | [traits setAutocorrectionType:UITextAutocorrectionTypeNo]; |
663 | [field setAutocapitalizationType:UITextAutocapitalizationTypeNone]; | 704 | [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone]; |
664 | [field setSpellCheckingType:UITextSpellCheckingTypeNo]; | 705 | [traits setSpellCheckingType:UITextSpellCheckingTypeNo]; |
665 | } | 706 | } |
666 | if (flags & alignRight_SystemTextInputFlag) { | 707 | if (flags & alignRight_SystemTextInputFlag) { |
667 | [field setTextAlignment:NSTextAlignmentRight]; | 708 | if (d->field) { |
709 | [REF_d_field setTextAlignment:NSTextAlignmentRight]; | ||
710 | } | ||
711 | } | ||
712 | UIColor *textColor = makeUIColor_(uiInputTextFocused_ColorId); | ||
713 | UIColor *backgroundColor = makeUIColor_(uiInputBackgroundFocused_ColorId); | ||
714 | [appState_ setSystemTextInput:d]; | ||
715 | if (d->field) { | ||
716 | [REF_d_field setTextColor:textColor]; | ||
717 | [REF_d_field setDelegate:appState_]; | ||
718 | [REF_d_field becomeFirstResponder]; | ||
719 | } | ||
720 | else { | ||
721 | [REF_d_view setTextColor:textColor]; | ||
722 | [REF_d_view setBackgroundColor:backgroundColor]; | ||
723 | [REF_d_view setEditable:YES]; | ||
724 | // [REF_d_view setSelectable:YES]; | ||
725 | [REF_d_view setDelegate:appState_]; | ||
726 | [REF_d_view becomeFirstResponder]; | ||
668 | } | 727 | } |
669 | [field setDelegate:appState_]; | ||
670 | [field becomeFirstResponder]; | ||
671 | d->textChangedFunc = NULL; | 728 | d->textChangedFunc = NULL; |
672 | d->textChangedContext = NULL; | 729 | d->textChangedContext = NULL; |
673 | [appState_ setSystemTextInput:d]; | ||
674 | } | 730 | } |
675 | 731 | ||
676 | void deinit_SystemTextInput(iSystemTextInput *d) { | 732 | void deinit_SystemTextInput(iSystemTextInput *d) { |
677 | [appState_ setSystemTextInput:nil]; | 733 | [appState_ setSystemTextInput:nil]; |
678 | [REF_d_ctrl removeFromSuperview]; | 734 | if (d->field) { |
679 | d->ctrl = nil; // TODO: Does this need to be released?? | 735 | [REF_d_field removeFromSuperview]; |
736 | CFBridgingRelease(d->field); | ||
737 | d->field = nil; | ||
738 | } | ||
739 | if (d->view) { | ||
740 | [REF_d_view removeFromSuperview]; | ||
741 | CFBridgingRelease(d->view); | ||
742 | d->view = nil; | ||
743 | } | ||
680 | } | 744 | } |
681 | 745 | ||
682 | void setText_SystemTextInput(iSystemTextInput *d, const iString *text) { | 746 | void setText_SystemTextInput(iSystemTextInput *d, const iString *text) { |
683 | [REF_d_ctrl setText:[NSString stringWithUTF8String:cstr_String(text)]]; | 747 | NSString *str = [NSString stringWithUTF8String:cstr_String(text)]; |
684 | [REF_d_ctrl selectAll:nil]; | 748 | if (d->field) { |
749 | [REF_d_field setText:str]; | ||
750 | if (d->flags & selectAll_SystemTextInputFlags) { | ||
751 | [REF_d_field selectAll:nil]; | ||
752 | } | ||
753 | } | ||
754 | else { | ||
755 | [REF_d_view setText:str]; | ||
756 | if (d->flags & selectAll_SystemTextInputFlags) { | ||
757 | [REF_d_view selectAll:nil]; | ||
758 | } | ||
759 | } | ||
760 | } | ||
761 | |||
762 | int preferredHeight_SystemTextInput(const iSystemTextInput *d) { | ||
763 | if (d->view) { | ||
764 | CGRect usedRect = [[REF_d_view layoutManager] usedRectForTextContainer:[REF_d_view textContainer]]; | ||
765 | return usedRect.size.height * get_Window()->pixelRatio; | ||
766 | } | ||
767 | return 0; | ||
685 | } | 768 | } |
686 | 769 | ||
687 | void setFont_SystemTextInput(iSystemTextInput *d, int fontId) { | 770 | void setFont_SystemTextInput(iSystemTextInput *d, int fontId) { |
688 | int height = lineHeight_Text(fontId); | 771 | int height = lineHeight_Text(fontId); |
689 | UIFont *font = [UIFont systemFontOfSize:0.65f * height / get_Window()->pixelRatio]; | 772 | UIFont *font = [UIFont systemFontOfSize:0.65f * height / get_Window()->pixelRatio]; |
690 | [REF_d_ctrl setFont:font]; | 773 | if (d->field) { |
774 | [REF_d_field setFont:font]; | ||
775 | } | ||
776 | if (d->view) { | ||
777 | [REF_d_view setFont:font]; | ||
778 | } | ||
691 | } | 779 | } |
692 | 780 | ||
693 | const iString *text_SystemTextInput(const iSystemTextInput *d) { | 781 | const iString *text_SystemTextInput(const iSystemTextInput *d) { |
694 | return collectNewCStr_String([[REF_d_ctrl text] cStringUsingEncoding:NSUTF8StringEncoding]);; | 782 | if (d->field) { |
783 | return collectNewCStr_String([[REF_d_field text] cStringUsingEncoding:NSUTF8StringEncoding]); | ||
784 | } | ||
785 | if (d->view) { | ||
786 | return collectNewCStr_String([[REF_d_view text] cStringUsingEncoding:NSUTF8StringEncoding]); | ||
787 | } | ||
788 | return NULL; | ||
695 | } | 789 | } |
696 | 790 | ||
697 | void setRect_SystemTextInput(iSystemTextInput *d, iRect rect) { | 791 | void setRect_SystemTextInput(iSystemTextInput *d, iRect rect) { |
698 | const iWindow *win = get_Window(); | 792 | CGRect frame = convertToCGRect_(&rect); |
699 | CGRect frame; | 793 | if (d->field) { |
700 | frame.origin.x = rect.pos.x / win->pixelRatio; | 794 | [REF_d_field setFrame:frame]; |
701 | frame.origin.y = (rect.pos.y - gap_UI + 2) / win->pixelRatio; | 795 | } |
702 | frame.size.width = rect.size.x / win->pixelRatio; | 796 | else { |
703 | frame.size.height = rect.size.y / win->pixelRatio; | 797 | [REF_d_view setFrame:frame]; |
704 | [REF_d_ctrl setFrame:frame]; | 798 | } |
705 | } | 799 | } |
706 | 800 | ||
707 | void setTextChangedFunc_SystemTextInput(iSystemTextInput *d, | 801 | void setTextChangedFunc_SystemTextInput(iSystemTextInput *d, |