From 2b7be2e3f189aa9eb96393f8275cdcfa754a7f84 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Mon, 8 Jan 2018 13:56:24 +0100 Subject: [PATCH] Fixed: Make sure the delegate methods and notifications are handled in the same way as in the Cooca frameworks. CPTextViewDidChangeSelectionNotification should not be send if the text view is empty. CPTextDidBeginEditingNotification is send when an edit is started. CPTextDidEndEditingNotification is send when an edit is ending. textShouldBeginEditing: delegate method is called to allow control if edit should start. textShouldEndEditing: delegate method is called to allow control if edit should end. --- AppKit/CPTextView/CPTextView.j | 121 ++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 39 deletions(-) diff --git a/AppKit/CPTextView/CPTextView.j b/AppKit/CPTextView/CPTextView.j index ad00aa3c0..74444d1fb 100644 --- a/AppKit/CPTextView/CPTextView.j +++ b/AppKit/CPTextView/CPTextView.j @@ -156,6 +156,8 @@ var kDelegateRespondsTo_textShouldBeginEditing CPTimer _scrollingTimer; CPString _placeholderString; + + BOOL _firstResponderButNotEditingYet; } + (Class)_binderClassForBinding:(CPString)aBinding @@ -409,7 +411,7 @@ var kDelegateRespondsTo_textShouldBeginEditing - (void)_windowDidResignKey:(CPNotification)aNotification { if (![[self window] isKeyWindow]) - [self resignFirstResponder]; + [self _resignFirstResponder]; } - (void)_windowDidBecomeKey:(CPNotification)aNotification @@ -503,17 +505,33 @@ var kDelegateRespondsTo_textShouldBeginEditing - (BOOL)becomeFirstResponder { [super becomeFirstResponder]; + _firstResponderButNotEditingYet = YES; [self _becomeFirstResponder]; return YES; } -- (BOOL)resignFirstResponder +- (void)_resignFirstResponder { [self _reverseSetBinding]; [_caret stopBlinking]; [self setNeedsDisplay:YES]; [_CPNativeInputManager cancelCurrentInputSessionIfNeeded]; +} + +- (BOOL)resignFirstResponder +{ + if (_firstResponderButNotEditingYet) + _firstResponderButNotEditingYet = NO; + else + { + if ([self _sendDelegateTextShouldEndEditing]) + [[CPNotificationCenter defaultCenter] postNotificationName:CPTextDidEndEditingNotification object:self]; + else + return NO; + } + + [self _resignFirstResponder]; return YES; } @@ -708,7 +726,21 @@ var kDelegateRespondsTo_textShouldBeginEditing - (void)didChangeText { [[CPNotificationCenter defaultCenter] postNotificationName:CPTextDidChangeNotification object:self]; +} +- (BOOL)_didBeginEditing +{ + if (_firstResponderButNotEditingYet) + { + if ([self _sendDelegateTextShouldBeginEditing]) + { + [[CPNotificationCenter defaultCenter] postNotificationName:CPTextDidBeginEditingNotification object:self]; + _firstResponderButNotEditingYet = NO; + } else + return NO; + } + + return YES; } - (BOOL)shouldChangeTextInRange:(CPRange)aRange replacementString:(CPString)aString @@ -716,7 +748,7 @@ var kDelegateRespondsTo_textShouldBeginEditing if (![self isEditable]) return NO; - return [self _sendDelegateTextShouldBeginEditing] && [self _sendDelegateShouldChangeTextInRange:aRange replacementString:aString]; + return [self _sendDelegateShouldChangeTextInRange:aRange replacementString:aString]; } @@ -748,7 +780,7 @@ var kDelegateRespondsTo_textShouldBeginEditing var isAttributed = [aString isKindOfClass:CPAttributedString], string = isAttributed ? [aString string]:aString; - if (![self shouldChangeTextInRange:CPMakeRangeCopy(_selectionRange) replacementString:string]) + if (![self _didBeginEditing] || ![self shouldChangeTextInRange:CPMakeRangeCopy(_selectionRange) replacementString:string]) return; if (!isAttributed) @@ -863,38 +895,45 @@ var kDelegateRespondsTo_textShouldBeginEditing - (void)_setSelectedRange:(CPRange)range affinity:(CPSelectionAffinity)affinity stillSelecting:(BOOL)selecting overwriteTypingAttributes:(BOOL)doOverwrite { - var maxRange = CPMakeRange(0, [_layoutManager numberOfCharacters]); + var maxRange = CPMakeRange(0, [_layoutManager numberOfCharacters]), + newSelectionRange; range = CPIntersectionRange(maxRange, range); if (!selecting && [self _delegateRespondsToWillChangeSelectionFromCharacterRangeToCharacterRange]) { - _selectionRange = [self _sendDelegateWillChangeSelectionFromCharacterRange:_selectionRange toCharacterRange:range]; + newSelectionRange = [self _sendDelegateWillChangeSelectionFromCharacterRange:_selectionRange toCharacterRange:range]; } else { - _selectionRange = CPMakeRangeCopy(range); - _selectionRange = [self selectionRangeForProposedRange:_selectionRange granularity:[self selectionGranularity]]; + newSelectionRange = [self selectionRangeForProposedRange:range granularity:[self selectionGranularity]]; } - if (_selectionRange.length) - [_layoutManager invalidateDisplayForGlyphRange:_selectionRange]; + var isNewSelection = !CPEqualRanges(newSelectionRange, _selectionRange); + + if (isNewSelection) + _selectionRange = newSelectionRange; + + if (newSelectionRange.length) + { + if (isNewSelection) + [_layoutManager invalidateDisplayForGlyphRange:newSelectionRange]; + } else [self setNeedsDisplay:YES]; if (!selecting) { if ([self _isFirstResponder]) - [self updateInsertionPointStateAndRestartTimer:((_selectionRange.length === 0) && ![_caret isBlinking])]; + [self updateInsertionPointStateAndRestartTimer:((newSelectionRange.length === 0) && ![_caret isBlinking])]; - if (doOverwrite && _placeholderString === nil) + if (doOverwrite && _placeholderString === nil && isNewSelection) [self setTypingAttributes:[_textStorage attributesAtIndex:CPMaxRange(range) effectiveRange:nil]]; [[CPNotificationCenter defaultCenter] postNotificationName:CPTextViewDidChangeSelectionNotification object:self]; - } - if (!selecting && _selectionRange.length > 0) + if (!selecting && newSelectionRange.length > 0) [_CPNativeInputManager focusForClipboardOfTextView:self]; } @@ -978,18 +1017,18 @@ var kDelegateRespondsTo_textShouldBeginEditing { var fraction = [], point = [self convertPoint:point fromView:nil]; - + // convert to container coordinate point.x -= _textContainerOrigin.x; point.y -= _textContainerOrigin.y; - + var index = [_layoutManager glyphIndexForPoint:point inTextContainer:_textContainer fractionOfDistanceThroughGlyph:fraction]; - + if (index === CPNotFound) index = [_layoutManager numberOfCharacters]; else if (fraction[0] > 0.5) index++; - + return index; } - (CGPoint)_characterIndexFromEvent:(CPEvent)event @@ -1012,9 +1051,9 @@ var kDelegateRespondsTo_textShouldBeginEditing [_CPNativeInputManager cancelCurrentInputSessionIfNeeded]; [_caret setVisibility:NO]; - + _startTrackingLocation = [self _characterIndexFromEvent:event]; - + var granularities = [CPNotFound, CPSelectByCharacter, CPSelectByWord, CPSelectByParagraph]; [self setSelectionGranularity:granularities[[event clickCount]]]; @@ -1027,20 +1066,20 @@ var kDelegateRespondsTo_textShouldBeginEditing placeholderFrame = CGRectIntersection([_layoutManager boundingRectForGlyphRange:placeholderRange inTextContainer:_textContainer], _frame), rangeToHide = CPMakeRange(0, _selectionRange.location - lineBeginningIndex), dragPlaceholder; - + // hide the left part of the first line of the selection that is not included [placeholderString addAttribute:CPForegroundColorAttributeName value:[CPColor colorWithRed:1 green:1 blue:1 alpha:0] range:rangeToHide]; - + _movingSelection = CPMakeRange(_startTrackingLocation, 0); - + dragPlaceholder = [[CPTextView alloc] initWithFrame:placeholderFrame]; [dragPlaceholder._textStorage replaceCharactersInRange:CPMakeRange(0, 0) withAttributedString:placeholderString]; [dragPlaceholder setBackgroundColor:[CPColor colorWithRed:1 green:1 blue:1 alpha:0]]; [dragPlaceholder setAlphaValue:0.5]; - + var stringForPasting = [_textStorage attributedSubstringFromRange:CPMakeRangeCopy(_selectionRange)], richData = [_CPRTFProducer produceRTF:stringForPasting documentAttributes:@{}], draggingPasteboard = [CPPasteboard pasteboardWithName:CPDragPboard]; @@ -1055,12 +1094,12 @@ var kDelegateRespondsTo_textShouldBeginEditing pasteboard:draggingPasteboard source:self slideBack:YES]; - + return; } - + var setRange = CPMakeRange(_startTrackingLocation, 0); - + if ([event modifierFlags] & CPShiftKeyMask) setRange = _MakeRangeFromAbs(_startTrackingLocation < _MidRange(_selectionRange) ? CPMaxRange(_selectionRange) : _selectionRange.location, _startTrackingLocation); else @@ -1081,7 +1120,7 @@ var kDelegateRespondsTo_textShouldBeginEditing if (_movingSelection) return; - + var oldRange = [self selectedRange], index = [self _characterIndexFromEvent:event]; @@ -1531,7 +1570,7 @@ var kDelegateRespondsTo_textShouldBeginEditing - (void)_deleteForRange:(CPRange)changedRange { - if (![self shouldChangeTextInRange:changedRange replacementString:@""]) + if (![self _didBeginEditing] || ![self shouldChangeTextInRange:changedRange replacementString:@""]) return; changedRange = CPIntersectionRange(CPMakeRange(0, [_layoutManager numberOfCharacters]), changedRange); @@ -1655,7 +1694,6 @@ var kDelegateRespondsTo_textShouldBeginEditing [[CPNotificationCenter defaultCenter] postNotificationName:CPTextViewDidChangeTypingAttributesNotification object:self]; - } - (CPDictionary)_attributesForFontPanel @@ -1798,7 +1836,7 @@ var kDelegateRespondsTo_textShouldBeginEditing - (void)underline:(id)sender { - if (![self shouldChangeTextInRange:_selectionRange replacementString:nil]) + if (![self _didBeginEditing] || ![self shouldChangeTextInRange:_selectionRange replacementString:nil]) return; if (!CPEmptyRange(_selectionRange)) @@ -2099,7 +2137,7 @@ var kDelegateRespondsTo_textShouldBeginEditing { var point = [info draggingLocation], location = [self _characterIndexFromRawPoint:CGPointCreateCopy(point)]; - + _movingSelection = CPMakeRange(location, 0); [_caret _drawCaretAtLocation:_movingSelection.location]; [_caret setVisibility:YES]; @@ -2112,11 +2150,11 @@ var kDelegateRespondsTo_textShouldBeginEditing { var location = [self convertPoint:[aSender draggingLocation] fromView:nil], pasteboard = [aSender draggingPasteboard]; - + if ([pasteboard availableTypeFromArray:[CPRTFPboardType, CPStringPboardType]]) { [_caret setVisibility:NO]; - + if (CPLocationInRange(_movingSelection.location, _selectionRange)) { [self setSelectedRange:_movingSelection]; @@ -2141,7 +2179,7 @@ var kDelegateRespondsTo_textShouldBeginEditing [self insertText:dataForPasting]; }, 0); } - + if ([pasteboard availableTypeFromArray:[CPColorDragType]]) [self setTextColor:[CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]] range:_selectionRange]; } @@ -2215,6 +2253,14 @@ var kDelegateRespondsTo_textShouldBeginEditing return [_delegate textShouldBeginEditing:self]; } +- (BOOL)_sendDelegateTextShouldEndEditing +{ + if (!(_delegateRespondsToSelectorMask & kDelegateRespondsTo_textShouldEndEditing)) + return YES; + + return [_delegate textShouldEndEditing:self]; +} + - (BOOL)_sendDelegateShouldChangeTextInRange:(CPRange)aRange replacementString:(CPString)aString { if (!(_delegateRespondsToSelectorMask & kDelegateRespondsTo_textView_shouldChangeTextInRange_replacementString)) @@ -2458,7 +2504,7 @@ var CPTextViewAllowsUndoKey = @"CPTextViewAllowsUndoKey", _caretTimer = nil; } } - + - (void)_drawCaretAtLocation:(int)aLoc { var rect = [_textView._layoutManager boundingRectForGlyphRange:CPMakeRange(aLoc, 1) inTextContainer:_textView._textContainer]; @@ -2791,6 +2837,3 @@ var _CPCopyPlaceholder = '-'; } @end - - -