-
-
Notifications
You must be signed in to change notification settings - Fork 112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(windows): add text selected bool emit backspace key when text selected in TSF #11884
Conversation
Add a bool to the KeymanGetContext call, allowing the windows engine to make the decistion to emit backspace key stroke when text is selecting allowing the application to make the correct decision on how to handle it. Usually deleting the selected text.
When |
Only call GetSelection once, pace the parameters through to the GetLeftSelection in order to get the results needed for KeymanIsTextSelected
Test ResultsI tested this issue with the attached "keyman-18.0.61-alpha-test-11884" build on the Windows 10 & 11 OS environment: Here is my observation. Prerequisite steps:
|
Changes in this pull request will be available for download in Keyman version 18.0.73-alpha |
@@ -151,7 +151,7 @@ class Globals | |||
/* External interface functions */ | |||
|
|||
typedef HRESULT (WINAPI *PKEYMANPROCESSOUTPUTFUNC)(int n, WCHAR *buf, int nbuf); | |||
typedef HRESULT (WINAPI *PKEYMANGETCONTEXTFUNC)(int n, PWSTR buf); | |||
typedef HRESULT (WINAPI *PKEYMANGETCONTEXTFUNC)(int n, PWSTR buf, BOOL* isTextSelected); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One concern here is that changing an existing API endpoint can be catastrophic if we end up with divergent versions of the DLLs loaded (e.g. if a user doesn't restart Windows after upgrading Keyman -- then any processes with Keyman loaded can crash). Is is usually safer to add another API and mark the original as deprecated; the same checks for whether the API is available can be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is after-merge, but I had some concerns... sorry!
if (!isEmpty) { | ||
*isTextSelected = TRUE; | ||
} | ||
tfSelection.range->Release(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't be here -- it doesn't correspond to acquisition of the range. It should be done in the caller -- even though it is implicitly calling tfSelection.range->AddRef()
due to the call to GetLeftOfSelection
, as least that clarifies the lifecycle due to the variable being local to that function. Also helps where range is not released on line 303 above.
} | ||
|
||
HRESULT | ||
CKeymanEditSession::KeymanIsTextSelected(HRESULT hrGetSelection, ULONG cFetched, TF_SELECTION tfSelection, BOOL *isTextSelected) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels weird to be passing hrGetSelection and cFetched in to this function. It seems like we could avoid a bunch of complexity by making this a simple BOOL return value if we didn't have to control for errors -- you don't use the hr result in the caller, so just log failure within the function and return TRUE/FALSE.
// now check for selected text
isTextSelected = KeymanIsTextSelected(hrGetSelection, cFetched, tfSelection);
if(tfSelection.range) {
tfSelection.range->Release();
}
// ...
BOOL
CKeymanEditSession::KeymanIsTextSelected(HRESULT hrGetSelection, ULONG cFetched, TF_SELECTION tfSelection) {
if (hrGetSelection == TF_E_NOSELECTION || cFetched == 0 || !SUCCEEDED(hrGetSelection) || !tfSelection.range) {
// No selection is present, or an error occurred trying to get the selection
return FALSE;
}
// check if the range is empty
HRESULT hr;
BOOL isEmpty = TRUE;
// Compare the start and end of the range
if (!SUCCEEDED(hr = tfSelection.range->IsEmpty(_ec, &isEmpty))) {
SendDebugMessageFormat(L"KeymanIsTextSelected: Exit: Testing range->isEmpty failed with %x", hr);
return FALSE;
}
return !isEmpty;
}
@rc-swag just following up on the concerns I noted post-merge |
Fixes: #7870
Add a bool to the KeymanGetContext call, the Windows engine will emit backspace keystroke when text is selected and a backspace key is pressed allowing the application to make the correct decision on how to handle it.
This change feels like it is half the solution the full solution would be to have a mechanism for allowing the Keyman Core to know if text has been selected. &hasSelection As discussed in issue #9073 as a Future solution.
With this change the core will request the character closest to the selection to be deleted its internal cache will match this, even though the engine will essentially overide this request and just emit the backspace keystroke. As it is TSF - context aware it will be updated on the next key stroke anyway with the correct context.
User Testing
TEST_DELETE_SELECTED_TEXT
Install the Keyman for Windows build artifact with this PR.
This test must be done with an application using the Text Services Framework (TSF). You cannot use
the Web version of Word. Use WordPad that comes installed with Windows 10 and Windows 11.
Install a Keyman keyboard like EuroLatin
mySchool
School
and type backspaceExpected result
my
Some regression tests.
TEST_BACKSPACE_NO_SELECTION
Install a Keyman keyboard like euro_latin
mySchool
Expected result
mySchoo
TEST_SELECTION_TYPE_CHAR
mySchool
School
and type tExpected result
myt