David Kroll
David Kroll

Reputation: 39

Delphi apps - Windows tablet mode keyboard covering app

We are looking to move our service technicians from laptops to Windows tablets. One issue while testing is that when the onscreen keyboard pops up, it obscures the bottom part of the application. How can we catch when the keyboard pops up and resize the application so this doesn't happen? App currently in Delphi XE5, but moving to Delphi 11.1.

Upvotes: 2

Views: 204

Answers (1)

EugeneK
EugeneK

Reputation: 2214

You need to use IFrameworkInputPaneHandler Here is sample unit implementing it.

unit UFrameworkInputPaneHandler;

interface

uses
  Winapi.Windows;

const
  CLSID_FrameworkInputPane: TGUID = '{D5120AA3-46BA-44C5-822D-CA8092C1FC72}';
  SID_IFrameworkInputPane = '{5752238B-24F0-495A-82F1-2FD593056796}';
  SID_IFrameworkInputPaneHandler = '{226C537B-1E76-4D9E-A760-33DB29922F18}';

type
  IFrameworkInputPaneHandler = interface(IInterface)
    [SID_IFrameworkInputPaneHandler]
    function Showing(var AInputPaneScreenLocation: TRect;
      AEnsureFocusedElementInView: BOOL): HResult; stdcall;
    function Hiding(AEnsureFocusedElementInView: BOOL): HResult; stdcall;
  end;

  IFrameworkInputPane = interface(IInterface)
    [SID_IFrameworkInputPane]
    function Advise(AWindow: IUnknown; AHandler: IFrameworkInputPaneHandler;
      var Cookie: DWORD): HRESULT; stdcall;
    function AdviseWithHWND(AWnd: HWND; const AHandler: IFrameworkInputPaneHandler;
      var Cookie: DWORD): HRESULT; stdcall;
    function Unadvise(dwCookie: DWORD): HRESULT; stdcall;
    function Location(var rcInputPaneScreenLocation: TRect): HRESULT; stdcall;
  end;

  TTouchKeyboardChangeEvent = procedure(Sender: TObject; const IsShowing: Boolean; var Rect: TRect;
    const EnsureFocusedElementInView: Boolean) of object;

  TFrameworkInputHandler = class(TInterfacedObject, IFrameworkInputPaneHandler)
  strict private
    FAdviseCookie: DWORD;
    FInputPane: IFrameworkInputPane;
    FOnTouchKeyboardVisibilityChanged: TTouchKeyboardChangeEvent;
  public
    { IFrameworkInputPaneHandler }
    function Showing(var AInputPaneScreenLocation: TRect; AEnsureFocusedElementInView: BOOL): HRESULT; stdcall;
    function Hiding(AEnsureFocusedElementInView: BOOL): HRESULT; stdcall;

    constructor Create(const AWnd: HWND);
    destructor Destroy; override;
    function GetLocation(var ARect: TRect): Boolean;
    property OnTouchKeyboardChanged: TTouchKeyboardChangeEvent
      read FOnTouchKeyboardVisibilityChanged write FOnTouchKeyboardVisibilityChanged;
    property InputPane: IFrameworkInputPane read FInputPane;
  end;

implementation

uses
  Winapi.ActiveX, System.Win.ComObj, System.Types;

constructor TFrameworkInputHandler.Create(const AWnd: HWND);
var
  HR: HRESULT;
begin
  inherited Create();

  FAdviseCookie := 0;
  FInputPane    := nil;
  FOnTouchKeyboardVisibilityChanged := nil;

  HR := CoCreateInstance(CLSID_FrameworkInputPane, nil, CLSCTX_ALL,
    StringToGUID(SID_IFrameworkInputPane), FInputPane);
  if (not FAILED(HR)) and Assigned(FInputPane) then
  begin
    FInputPane.AdviseWithHWND(AWnd, Self, FAdviseCookie);
  end;
end;

destructor TFrameworkInputHandler.Destroy();
begin
  if Assigned(FInputPane) then
  begin
    FInputPane.Unadvise(FAdviseCookie);
    FInputPane := nil;
  end;

  inherited Destroy();
end;

function TFrameworkInputHandler.GetLocation(var ARect: TRect): Boolean;
begin
  Result := False;
  ARect := TRect.Empty;
  if Assigned(FInputPane) then
  begin
    Result := not FAILED(FInputPane.Location(ARect));
  end;
end;

function TFrameworkInputHandler.Hiding(AEnsureFocusedElementInView: BOOL): HRESULT;
begin
  if Assigned(FOnTouchKeyboardVisibilityChanged) then
  begin
    var Rect := TRect.Empty;
    FOnTouchKeyboardVisibilityChanged(Self, False, Rect, AEnsureFocusedElementInView);
  end;

  Result := S_OK;
end;

function TFrameworkInputHandler.Showing(var AInputPaneScreenLocation: TRect;
  AEnsureFocusedElementInView: BOOL): HRESULT;
begin
  if Assigned(FOnTouchKeyboardVisibilityChanged) then
  begin
    FOnTouchKeyboardVisibilityChanged(Self, True, AInputPaneScreenLocation,
      AEnsureFocusedElementInView);
  end;

  Result := S_OK;
end;

end.

In your form declare it as

FInputHandler: IFrameworkInputPaneHandler;

and create it as

  if not Assigned(FInputHandler) then
  begin
    FInputHandler := TFrameworkInputHandler.Create(Handle);
    (FInputHandler as TFrameworkInputHandler).OnTouchKeyboardChanged := OnTouchKeyboardChanged;
  end;

In OnTouchKeyboardChanged it will give you position of onscreen keyboard

Upvotes: 4

Related Questions