terminal/src/tsf/ConsoleTSF.h
Yoshiko 3f1befb06e
Fix Touch Keyboard invocation issue (#11389)
This fixes an issue that Touch Keyboard is not invoked when user taps on the PowerShell. 

Before this change, it was returning small rectangle on the right of the cursor. Touch Keyboard should be invoked by tapping anywhere inside the console.

## PR Checklist
* [ ] Closes #xxx
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx

## Detailed Description of the Pull Request / Additional comments
ITfContextOwner::GetScreenExt is used to define rectangle that can invoke Touch Keyboard. 
https://docs.microsoft.com/en-us/windows/win32/api/msctf/nf-msctf-itfcontextowner-getscreenext

## Validation Steps Performed
* [x] Touch keyboard was invoked by tapping inside the Console while Hardware Keyboard was not attached.
* [x] Selecting text worked as expected without invoking touch keyboard.
* [x] Long tapping the console invoked Touch Keyboard. I would like to confirm if this is the expected behavior.
2021-10-04 14:29:56 +00:00

223 lines
7 KiB
C++

/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
Module Name:
TfContext.h
Abstract:
This file defines the CConsoleTSF Interface Class.
Author:
Revision History:
Notes:
--*/
#pragma once
class CConversionArea;
class CConsoleTSF final :
public ITfContextOwner,
public ITfContextOwnerCompositionSink,
public ITfInputProcessorProfileActivationSink,
public ITfUIElementSink,
public ITfCleanupContextSink,
public ITfTextEditSink
{
public:
CConsoleTSF(HWND hwndConsole,
GetSuggestionWindowPos pfnPosition,
GetTextBoxAreaPos pfnTextArea) :
_hwndConsole(hwndConsole),
_pfnPosition(pfnPosition),
_pfnTextArea(pfnTextArea),
_cRef(1),
_tid()
{
}
virtual ~CConsoleTSF()
{
}
[[nodiscard]] HRESULT Initialize();
void Uninitialize();
public:
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj);
STDMETHODIMP_(ULONG)
AddRef(void);
STDMETHODIMP_(ULONG)
Release(void);
// ITfContextOwner
STDMETHODIMP GetACPFromPoint(const POINT*, DWORD, LONG* pCP)
{
if (pCP)
{
*pCP = 0;
}
return S_OK;
}
// This returns Rectangle of the text box of whole console.
// When a user taps inside the rectangle while hardware keyboard is not available,
// touch keyboard is invoked.
STDMETHODIMP GetScreenExt(RECT* pRect)
{
if (pRect)
{
*pRect = _pfnTextArea();
}
return S_OK;
}
// This returns rectangle of current command line edit area.
// When a user types in East Asian language, candidate window is shown at this position.
// Emoji and more panel (Win+.) is shown at the position, too.
STDMETHODIMP GetTextExt(LONG, LONG, RECT* pRect, BOOL* pbClipped)
{
if (pRect)
{
*pRect = _pfnPosition();
}
if (pbClipped)
{
*pbClipped = FALSE;
}
return S_OK;
}
STDMETHODIMP GetStatus(TF_STATUS* pTfStatus)
{
if (pTfStatus)
{
pTfStatus->dwDynamicFlags = 0;
pTfStatus->dwStaticFlags = TF_SS_TRANSITORY;
}
return pTfStatus ? S_OK : E_INVALIDARG;
}
STDMETHODIMP GetWnd(HWND* phwnd)
{
*phwnd = _hwndConsole;
return S_OK;
}
STDMETHODIMP GetAttribute(REFGUID, VARIANT*)
{
return E_NOTIMPL;
}
// ITfContextOwnerCompositionSink methods
STDMETHODIMP OnStartComposition(ITfCompositionView* pComposition, BOOL* pfOk);
STDMETHODIMP OnUpdateComposition(ITfCompositionView* pComposition, ITfRange* pRangeNew);
STDMETHODIMP OnEndComposition(ITfCompositionView* pComposition);
// ITfInputProcessorProfileActivationSink
STDMETHODIMP OnActivated(DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags);
// ITfUIElementSink methods
STDMETHODIMP BeginUIElement(DWORD dwUIElementId, BOOL* pbShow);
STDMETHODIMP UpdateUIElement(DWORD dwUIElementId);
STDMETHODIMP EndUIElement(DWORD dwUIElementId);
// ITfCleanupContextSink methods
STDMETHODIMP OnCleanupContext(TfEditCookie ecWrite, ITfContext* pic);
// ITfTextEditSink methods
STDMETHODIMP OnEndEdit(ITfContext* pInputContext, TfEditCookie ecReadOnly, ITfEditRecord* pEditRecord);
public:
CConversionArea* CreateConversionArea();
CConversionArea* GetConversionArea() { return _pConversionArea; }
ITfContext* GetInputContext() { return _spITfInputContext.get(); }
HWND GetConsoleHwnd() { return _hwndConsole; }
TfClientId GetTfClientId() { return _tid; }
BOOL IsInComposition() { return (_cCompositions > 0); }
void OnEditSession() { _fEditSessionRequested = FALSE; }
BOOL IsPendingCompositionCleanup() { return _fCleanupSessionRequested || _fCompositionCleanupSkipped; }
void OnCompositionCleanup(BOOL bSucceeded)
{
_fCleanupSessionRequested = FALSE;
_fCompositionCleanupSkipped = !bSucceeded;
}
void SetModifyingDocFlag(BOOL fSet) { _fModifyingDoc = fSet; }
void SetFocus(BOOL fSet)
{
if (!fSet && _cCompositions)
{
// Close (terminate) any open compositions when losing the input focus.
if (_spITfInputContext)
{
wil::com_ptr_nothrow<ITfContextOwnerCompositionServices> spCompositionServices(_spITfInputContext.try_query<ITfContextOwnerCompositionServices>());
if (spCompositionServices)
{
spCompositionServices->TerminateComposition(nullptr);
}
}
}
}
// A workaround for a MS Korean IME scenario where the IME appends a whitespace
// composition programmatically right after completing a keyboard input composition.
// Since post-composition clean-up is an async operation, the programmatic whitespace
// composition gets completed before the previous composition cleanup happened,
// and this results in a double insertion of the first composition. To avoid that, we'll
// store the length of the last completed composition here until it's cleaned up.
// (for simplicity, this patch doesn't provide a generic solution for all possible
// scenarios with subsequent synchronous compositions, only for the known 'append').
long GetCompletedRangeLength() const { return _cchCompleted; }
void SetCompletedRangeLength(long cch) { _cchCompleted = cch; }
private:
[[nodiscard]] HRESULT _OnUpdateComposition();
[[nodiscard]] HRESULT _OnCompleteComposition();
BOOL _HasCompositionChanged(ITfContext* pInputContext, TfEditCookie ecReadOnly, ITfEditRecord* pEditRecord);
private:
// ref count.
DWORD _cRef;
// Cicero stuff.
TfClientId _tid;
wil::com_ptr_nothrow<ITfThreadMgrEx> _spITfThreadMgr;
wil::com_ptr_nothrow<ITfDocumentMgr> _spITfDocumentMgr;
wil::com_ptr_nothrow<ITfContext> _spITfInputContext;
// Event sink cookies.
DWORD _dwContextOwnerCookie = 0;
DWORD _dwUIElementSinkCookie = 0;
DWORD _dwTextEditSinkCookie = 0;
DWORD _dwActivationSinkCookie = 0;
// Conversion area object for the languages.
CConversionArea* _pConversionArea = nullptr;
// Console info.
HWND _hwndConsole;
GetSuggestionWindowPos _pfnPosition;
GetTextBoxAreaPos _pfnTextArea;
// Miscellaneous flags
BOOL _fModifyingDoc = FALSE; // Set TRUE, when calls ITfRange::SetText
BOOL _fCoInitialized = FALSE;
BOOL _fEditSessionRequested = FALSE;
BOOL _fCleanupSessionRequested = FALSE;
BOOL _fCompositionCleanupSkipped = FALSE;
int _cCompositions = 0;
long _cchCompleted = 0; // length of completed composition waiting for cleanup
};
extern CConsoleTSF* g_pConsoleTSF;