Skip to content
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

Merged
merged 3 commits into from
Jul 23, 2024

Conversation

rc-swag
Copy link
Contributor

@rc-swag rc-swag commented Jun 27, 2024

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

  1. Select the EuroLatin Keyboard
  2. Open MS Word or WordPad
  3. Type mySchool
  4. Select School and type backspace

Expected result
my

Some regression tests.

TEST_BACKSPACE_NO_SELECTION

Install a Keyman keyboard like euro_latin

  1. Select the EuroLatin Keyboard
  2. Open MS Word or WordPad
  3. Type mySchool
  4. With the cursor still to the right of 'l' type backspace

Expected result
mySchoo

TEST_SELECTION_TYPE_CHAR

  1. Select the EuroLatin Keyboard
  2. Open MS Word or WordPad
  3. Type mySchool
  4. Select School and type t

Expected result
myt

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.
@keymanapp-test-bot keymanapp-test-bot bot added the user-test-missing User tests have not yet been defined for the PR label Jun 27, 2024
@keymanapp-test-bot
Copy link

keymanapp-test-bot bot commented Jun 27, 2024

User Test Results

Test specification and instructions

Test Artifacts

@rc-swag
Copy link
Contributor Author

rc-swag commented Jul 1, 2024

When KeymanGetContext calls GetLeftofSelection add the cFetched, tfSelection and hr result as parameters so the the TSF API call GetSelection is just called once and the information will be available to IsKeymanIsTextSelected, no need for it to make that API call again. Make KeymanIsTextSelected a private function as it should not be called independently of KeymanGetContext.

@darcywong00 darcywong00 modified the milestones: A18S5, A18S6 Jul 5, 2024
Only call GetSelection once, pace the parameters through to the
GetLeftSelection in order to get the results needed for
KeymanIsTextSelected
@keymanapp-test-bot keymanapp-test-bot bot added has-user-test user-test-required User tests have not been completed and removed user-test-missing User tests have not yet been defined for the PR labels Jul 10, 2024
@rc-swag rc-swag marked this pull request as ready for review July 10, 2024 01:47
@dinakaranr
Copy link

Test Results

I 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:

  1. Installed the "keyman-18.0.61.exe" file.
  2. Keyman keyboard added in the system tray.
  3. Open the keyman "Configuration" window.
  4. Add the "EuroLatin" keyboard by clicking the "Download keyboard" button.
  • TEST_DELETE_SELECTED_TEXT (Passed):
  1. Followed the above prerequisite steps:
  2. Open the Wordpad.
  3. Type "mySchool" and select "School" by clicking the mouse.
  4. Type the "Backspace" key.
    Results: Here, It is showing the "my" word in the Wordpad.
  • TEST_BACKSPACE_NO_SELECTION (Passed):
  1. Followed the above prerequisite steps:
  2. Open the Wordpad.
  3. Type "myschool" and then move the cursor to the right of "l"
  4. Type the "Backspace" key.
    Results: Here, It is showing the "mySchoo" word in the Wordpad.
  • TEST_SELECTION_TYPE_CHAR (Passed):
  1. Followed the above prerequisite steps:
  2. Open the Wordpad.
  3. Type "mySchool" and select "School" by clicking the mouse.
  4. Type the "t" key.
    Results: Here, It is showing the "myt" word in the Wordpad.
    It is working as per the expected results. Thank you.

@keymanapp-test-bot keymanapp-test-bot bot removed the user-test-required User tests have not been completed label Jul 10, 2024
@darcywong00 darcywong00 modified the milestones: A18S6, A18S7 Jul 19, 2024
@rc-swag rc-swag merged commit 4e25bbe into master Jul 23, 2024
6 checks passed
@rc-swag rc-swag deleted the fix/windows/7870/add-is-text-selected-flag branch July 23, 2024 01:19
@keyman-server
Copy link
Collaborator

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);
Copy link
Member

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.

Copy link
Member

@mcdurdin mcdurdin left a 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();
Copy link
Member

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) {
Copy link
Member

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;
}

@mcdurdin
Copy link
Member

@rc-swag just following up on the concerns I noted post-merge

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

bug(windows): backspace with selected text deletes an extra character in TSF only
6 participants