зеркало из https://github.com/mozilla/gecko-dev.git
Bug 875674 part.6 Implement insertText:replacementRange: of NSTextInputClient r=smichaud
This commit is contained in:
Родитель
146b45259e
Коммит
0c7378b147
|
@ -340,6 +340,16 @@ public:
|
|||
*/
|
||||
bool DispatchEvent(nsGUIEvent& aEvent);
|
||||
|
||||
/**
|
||||
* SetSelection() dispatches NS_SELECTION_SET event for the aRange.
|
||||
*
|
||||
* @param aRange The range which will be selected.
|
||||
* @return TRUE if setting selection is succeeded and
|
||||
* the widget hasn't been destroyed.
|
||||
* Otherwise, FALSE.
|
||||
*/
|
||||
bool SetSelection(NSRange& aRange);
|
||||
|
||||
/**
|
||||
* InitKeyEvent() initializes aKeyEvent for aNativeKeyEvent.
|
||||
*
|
||||
|
@ -961,8 +971,11 @@ protected:
|
|||
* is no composition, this starts a composition and commits it immediately.
|
||||
*
|
||||
* @param aAttrString A string which is committed.
|
||||
* @param aReplacementRange The range which will be replaced with the
|
||||
* aAttrString instead of current selection.
|
||||
*/
|
||||
void InsertTextAsCommittingComposition(NSAttributedString* aAttrString);
|
||||
void InsertTextAsCommittingComposition(NSAttributedString* aAttrString,
|
||||
NSRange* aReplacementRange);
|
||||
|
||||
private:
|
||||
// If mIsIMEComposing is true, the composition string is stored here.
|
||||
|
@ -1120,8 +1133,11 @@ public:
|
|||
* the composition by the aAttrString.
|
||||
*
|
||||
* @param aAttrString An inserted string.
|
||||
* @param aReplacementRange The range which will be replaced with the
|
||||
* aAttrString instead of current selection.
|
||||
*/
|
||||
void InsertText(NSAttributedString *aAttrString);
|
||||
void InsertText(NSAttributedString *aAttrString,
|
||||
NSRange* aReplacementRange = nullptr);
|
||||
|
||||
/**
|
||||
* doCommandBySelector event handler.
|
||||
|
|
|
@ -1929,7 +1929,8 @@ TextInputHandler::DispatchKeyEventForFlagsChanged(NSEvent* aNativeEvent,
|
|||
}
|
||||
|
||||
void
|
||||
TextInputHandler::InsertText(NSAttributedString *aAttrString)
|
||||
TextInputHandler::InsertText(NSAttributedString* aAttrString,
|
||||
NSRange* aReplacementRange)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
|
@ -1941,10 +1942,13 @@ TextInputHandler::InsertText(NSAttributedString *aAttrString)
|
|||
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p TextInputHandler::InsertText, aAttrString=\"%s\", "
|
||||
"aReplacementRange=%p { location=%llu, length=%llu }, "
|
||||
"IsIMEComposing()=%s, IgnoreIMEComposition()=%s, "
|
||||
"keyevent=%p, keypressDispatched=%s",
|
||||
this, GetCharacters([aAttrString string]), TrueOrFalse(IsIMEComposing()),
|
||||
TrueOrFalse(IgnoreIMEComposition()),
|
||||
this, GetCharacters([aAttrString string]), aReplacementRange,
|
||||
aReplacementRange ? aReplacementRange->location : 0,
|
||||
aReplacementRange ? aReplacementRange->length : 0,
|
||||
TrueOrFalse(IsIMEComposing()), TrueOrFalse(IgnoreIMEComposition()),
|
||||
currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr,
|
||||
currentKeyEvent ?
|
||||
TrueOrFalse(currentKeyEvent->mKeyPressDispatched) : "N/A"));
|
||||
|
@ -1953,14 +1957,54 @@ TextInputHandler::InsertText(NSAttributedString *aAttrString)
|
|||
return;
|
||||
}
|
||||
|
||||
InputContext context = mWidget->GetInputContext();
|
||||
bool isEditable = (context.mIMEState.mEnabled == IMEState::ENABLED ||
|
||||
context.mIMEState.mEnabled == IMEState::PASSWORD);
|
||||
NSRange selectedRange = SelectedRange();
|
||||
|
||||
nsAutoString str;
|
||||
nsCocoaUtils::GetStringForNSString([aAttrString string], str);
|
||||
if (!IsIMEComposing() && str.IsEmpty()) {
|
||||
return; // nothing to do
|
||||
// nothing to do if there is no content which can be removed.
|
||||
if (!isEditable) {
|
||||
return;
|
||||
}
|
||||
// If replacement range is specified, we need to remove the range.
|
||||
// Otherwise, we need to remove the selected range if it's not collapsed.
|
||||
if (aReplacementRange && aReplacementRange->location != NSNotFound) {
|
||||
// nothing to do since the range is collapsed.
|
||||
if (aReplacementRange->length == 0) {
|
||||
return;
|
||||
}
|
||||
// If the replacement range is different from current selected range,
|
||||
// select the range.
|
||||
if (!NSEqualRanges(selectedRange, *aReplacementRange)) {
|
||||
NS_ENSURE_TRUE_VOID(SetSelection(*aReplacementRange));
|
||||
}
|
||||
selectedRange = SelectedRange();
|
||||
}
|
||||
NS_ENSURE_TRUE_VOID(selectedRange.location != NSNotFound);
|
||||
if (selectedRange.length == 0) {
|
||||
return; // nothing to do
|
||||
}
|
||||
// If this is caused by a key input, the keypress event which will be
|
||||
// dispatched later should cause the delete. Therefore, nothing to do here.
|
||||
// Although, we're not sure if such case is actually possible.
|
||||
if (!currentKeyEvent) {
|
||||
return;
|
||||
}
|
||||
// Delete the selected range.
|
||||
nsRefPtr<TextInputHandler> kungFuDeathGrip(this);
|
||||
nsContentCommandEvent deleteCommandEvent(true, NS_CONTENT_COMMAND_DELETE,
|
||||
mWidget);
|
||||
DispatchEvent(deleteCommandEvent);
|
||||
NS_ENSURE_TRUE_VOID(deleteCommandEvent.mSucceeded);
|
||||
// Be aware! The widget might be destroyed here.
|
||||
return;
|
||||
}
|
||||
|
||||
if (str.Length() != 1 || IsIMEComposing()) {
|
||||
InsertTextAsCommittingComposition(aAttrString);
|
||||
InsertTextAsCommittingComposition(aAttrString, aReplacementRange);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1972,6 +2016,14 @@ TextInputHandler::InsertText(NSAttributedString *aAttrString)
|
|||
|
||||
nsRefPtr<nsChildView> kungFuDeathGrip(mWidget);
|
||||
|
||||
// If the replacement range is specified, select the range. Then, the
|
||||
// selection will be replaced by the later keypress event.
|
||||
if (isEditable &&
|
||||
aReplacementRange && aReplacementRange->location != NSNotFound &&
|
||||
!NSEqualRanges(selectedRange, *aReplacementRange)) {
|
||||
NS_ENSURE_TRUE_VOID(SetSelection(*aReplacementRange));
|
||||
}
|
||||
|
||||
// Dispatch keypress event with char instead of textEvent
|
||||
nsKeyEvent keypressEvent(true, NS_KEY_PRESS, mWidget);
|
||||
keypressEvent.isChar = IsPrintableChar(str.CharAt(0));
|
||||
|
@ -2602,28 +2654,63 @@ IMEInputHandler::InitCompositionEvent(nsCompositionEvent& aCompositionEvent)
|
|||
|
||||
void
|
||||
IMEInputHandler::InsertTextAsCommittingComposition(
|
||||
NSAttributedString* aAttrString)
|
||||
NSAttributedString* aAttrString,
|
||||
NSRange* aReplacementRange)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p IMEInputHandler::InsertTextAsCommittingComposition, "
|
||||
"aAttrString=\"%s\", Destroyed()=%s, IsIMEComposing()=%s, "
|
||||
"aAttrString=\"%s\", aReplacementRange=%p { location=%llu, length=%llu }, "
|
||||
"Destroyed()=%s, IsIMEComposing()=%s, "
|
||||
"mMarkedRange={ location=%llu, length=%llu }",
|
||||
this, GetCharacters([aAttrString string]), TrueOrFalse(Destroyed()),
|
||||
TrueOrFalse(IsIMEComposing()),
|
||||
this, GetCharacters([aAttrString string]), aReplacementRange,
|
||||
aReplacementRange ? aReplacementRange->location : 0,
|
||||
aReplacementRange ? aReplacementRange->length : 0,
|
||||
TrueOrFalse(Destroyed()), TrueOrFalse(IsIMEComposing()),
|
||||
mMarkedRange.location, mMarkedRange.length));
|
||||
|
||||
if (IgnoreIMECommit()) {
|
||||
MOZ_CRASH("IMEInputHandler::InsertTextAsCommittingComposition() must not"
|
||||
"be called while canceling the composition");
|
||||
}
|
||||
|
||||
if (Destroyed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// First, commit current composition with the latest composition string if the
|
||||
// replacement range is different from marked range.
|
||||
if (IsIMEComposing() && aReplacementRange &&
|
||||
aReplacementRange->location != NSNotFound &&
|
||||
!NSEqualRanges(MarkedRange(), *aReplacementRange)) {
|
||||
NSString* latestStr =
|
||||
nsCocoaUtils::ToNSString(mLastDispatchedCompositionString);
|
||||
NSAttributedString* attrLatestStr =
|
||||
[[[NSAttributedString alloc] initWithString:latestStr] autorelease];
|
||||
InsertTextAsCommittingComposition(attrLatestStr, nullptr);
|
||||
if (Destroyed()) {
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p IMEInputHandler::InsertTextAsCommittingComposition, "
|
||||
"destroyed by commiting composition for setting replacement range",
|
||||
this));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<IMEInputHandler> kungFuDeathGrip(this);
|
||||
|
||||
nsString str;
|
||||
nsCocoaUtils::GetStringForNSString([aAttrString string], str);
|
||||
|
||||
if (!IsIMEComposing()) {
|
||||
// If there is no selection and replacement range is specified, set the
|
||||
// range as selection.
|
||||
if (aReplacementRange && aReplacementRange->location != NSNotFound &&
|
||||
!NSEqualRanges(SelectedRange(), *aReplacementRange)) {
|
||||
NS_ENSURE_TRUE_VOID(SetSelection(*aReplacementRange));
|
||||
}
|
||||
|
||||
// XXXmnakano Probably, we shouldn't emulate composition in this case.
|
||||
// I think that we should just fire DOM3 textInput event if we implement it.
|
||||
nsCompositionEvent compStart(true, NS_COMPOSITION_START, mWidget);
|
||||
|
@ -2640,13 +2727,6 @@ IMEInputHandler::InsertTextAsCommittingComposition(
|
|||
OnStartIMEComposition();
|
||||
}
|
||||
|
||||
if (IgnoreIMECommit()) {
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p IMEInputHandler::InsertTextAsCommittingComposition, "
|
||||
"IgnoreIMECommit()=%s", this, TrueOrFalse(IgnoreIMECommit())));
|
||||
str.Truncate();
|
||||
}
|
||||
|
||||
NSRange range = NSMakeRange(0, str.Length());
|
||||
DispatchTextEvent(str, aAttrString, range, true);
|
||||
if (Destroyed()) {
|
||||
|
@ -4176,6 +4256,22 @@ TextInputHandlerBase::GetWindowLevel()
|
|||
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSNormalWindowLevel);
|
||||
}
|
||||
|
||||
bool
|
||||
TextInputHandlerBase::SetSelection(NSRange& aRange)
|
||||
{
|
||||
MOZ_ASSERT(!Destroyed());
|
||||
|
||||
nsRefPtr<TextInputHandlerBase> kungFuDeathGrip(this);
|
||||
nsSelectionEvent selectionEvent(true, NS_SELECTION_SET, mWidget);
|
||||
selectionEvent.mOffset = aRange.location;
|
||||
selectionEvent.mLength = aRange.length;
|
||||
selectionEvent.mReversed = false;
|
||||
selectionEvent.mExpandToClusterBoundary = false;
|
||||
DispatchEvent(selectionEvent);
|
||||
NS_ENSURE_TRUE(selectionEvent.mSucceeded, false);
|
||||
return !Destroyed();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
TextInputHandlerBase::IsPrintableChar(PRUnichar aChar)
|
||||
{
|
||||
|
|
|
@ -4956,7 +4956,7 @@ static int32_t RoundUp(double aDouble)
|
|||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
NS_ENSURE_TRUE(mGeckoChild, );
|
||||
NS_ENSURE_TRUE_VOID(mGeckoChild);
|
||||
|
||||
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
||||
|
||||
|
@ -5095,6 +5095,26 @@ static int32_t RoundUp(double aDouble)
|
|||
#pragma mark -
|
||||
// NSTextInputClient implementation
|
||||
|
||||
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
NS_ENSURE_TRUE_VOID(mGeckoChild);
|
||||
|
||||
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
||||
|
||||
NSAttributedString* attrStr;
|
||||
if ([aString isKindOfClass:[NSAttributedString class]]) {
|
||||
attrStr = static_cast<NSAttributedString*>(aString);
|
||||
} else {
|
||||
attrStr = [[[NSAttributedString alloc] initWithString:aString] autorelease];
|
||||
}
|
||||
|
||||
mTextInputHandler->InsertText(attrStr, &replacementRange);
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
}
|
||||
|
||||
- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)aRange
|
||||
actualRange:(NSRangePointer)actualRange
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче