Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override the call to show the CapsLock hint window in a TEdit?

Basically I have this problem: CapsLock password message in TEdit visually fails with VCL Styles.

What I want to do is not to solve the problem as shown in the answer or the comments.

I want to disable that ugly hint window entirely. and instead show an image letting the user know that the caps are locked.

like this

enter image description here

like image 537
Nasreddine Galfout Avatar asked Dec 11 '17 14:12

Nasreddine Galfout


1 Answers

I found the solution to my problem, It involves a hack that I would rather not use.

It goes like this.

  1. Override WndProc.

code

type
  TEdit = class (Vcl.StdCtrls.TEdit)
  protected
    procedure WndProc(var Message: TMessage); override;
  end;
  1. Intercept the EM_SHOWBALLOONTIPmessage and you are done

code

procedure TEdit.WndProc(var Message: TMessage);
begin
 if Message.Msg = EM_SHOWBALLOONTIP then
   showmessage('Do your thing.')
 else
  inherited;
end;

For more information check the MSDN documentation:

How do I suppress the CapsLock warning on password edit controls?


This is a descendant of TEdit that would allow to suppress the CapsLock warning on password edit controls, if a certain FOnPasswordCaps events are assigned with PasswordChar <> #0

unit NCREditUnit;

interface

uses
  Vcl.StdCtrls,
  vcl.Controls,
  Winapi.Messages,
  System.Classes;

type
  TNCREdit = class(TEdit)
  private
    FOnPasswordCapsLocked: TNotifyEvent;
    FIsCapsLocked: boolean;
    FOnPasswordCapsFreed: TNotifyEvent;
    FBlockCapsBalloonTip: boolean;
    FValuePasswordChrOnCaps: boolean;
    procedure SetOnPasswordCapsEvents;
    procedure SetOnPasswordCapsFreed(const aValue: TNotifyEvent);
    procedure SetOnPasswordCapsLocked(const aValue: TNotifyEvent);
  protected
    procedure WndProc(var Message: TMessage); override;
    procedure KeyUp(var Key: Word; Shift: TShiftState); override;
    procedure DoEnter; override;
    procedure DoExit; override;
  published
    property BlockCapsBalloonTip: boolean read FBlockCapsBalloonTip write FBlockCapsBalloonTip default False;
    property ValuePasswordChrOnCaps: boolean read FValuePasswordChrOnCaps write FValuePasswordChrOnCaps default True;

//... The usual property declaration of TEdit

    property OnPasswordCapsLocked: TNotifyEvent read FOnPasswordCapsLocked write SetOnPasswordCapsLocked;
    property OnPasswordCapsFreed: TNotifyEvent read FOnPasswordCapsFreed write SetOnPasswordCapsFreed;
  end;


implementation

uses
  Winapi.CommCtrl,
  Winapi.Windows;

{ TNCREdit }

procedure TNCREdit.DoEnter;
begin
  inherited;
  if FBlockCapsBalloonTip then
    begin
      FIsCapsLocked := Odd(GetKeyState(VK_CAPITAL));
      SetOnPasswordCapsEvents;
    end;
end;

procedure TNCREdit.DoExit;
begin
  if FBlockCapsBalloonTip and (FIsCapsLocked) then
    begin
      FIsCapsLocked := False;
      SetOnPasswordCapsEvents;
    end;
  inherited;
end;

procedure TNCREdit.KeyUp(var Key: Word; Shift: TShiftState);
begin
  if Key = VK_CAPITAL then
    FIsCapsLocked := not FIsCapsLocked;
  SetOnPasswordCapsEvents;
  inherited;
end;

procedure TNCREdit.SetOnPasswordCapsEvents;
begin
  if FIsCapsLocked then
    begin
      if Assigned(FOnPasswordCapsLocked) and
         ((self.PasswordChar <> #0) or ( not FValuePasswordChrOnCaps)) then
      begin
      FOnPasswordCapsLocked(Self);
      end;
    end
  else
    begin
      if Assigned(FOnPasswordCapsLocked) and
         ((self.PasswordChar <> #0) or ( not FValuePasswordChrOnCaps)) then
      begin
      FOnPasswordCapsFreed(Self);
      end;
    end;
end;

procedure TNCREdit.SetOnPasswordCapsFreed(const aValue: TNotifyEvent);
begin
  FOnPasswordCapsFreed := aValue;
  FBlockCapsBalloonTip := True;
end;

procedure TNCREdit.SetOnPasswordCapsLocked(const aValue: TNotifyEvent);
begin
  FOnPasswordCapsLocked := aValue;
  FBlockCapsBalloonTip := True;
end;

procedure TNCREdit.WndProc(var Message: TMessage);
begin
  if (Message.Msg = EM_SHOWBALLOONTIP) and FBlockCapsBalloonTip then Exit; 
  inherited;
end;

end.

Mr Kobik made a very elegant piece of code that I think PasteBin should not be trusted to host, so I decided to add it here.

From what I understood it lets you handle TPasswordCapsLockState in one event handler that is fired when the TPasswordEdit receives focus, loses focus, CapsLock key pressed while on focus and an optional firing when PasswordChar is changed.

Using this approach I could use the OnPasswordCapsLock event to show/hide the image in my question instead of forcing the consumer of the component to use two event handlers for each state (very clever by the way and less error prone).

also as long as LNeedHandle := FBlockCapsBalloonTip and IsPassword; is True I have another added feature to TPasswordEdit which is the handling of OnEnter and OnExit in OnPasswordCapsLock as well,

So what can I say Mr Kobik Je vous tire mon chapeau.

type
  TPasswordCapsLockState = (pcsEnter, pcsExit, pcsKey, pcsSetPasswordChar);

  TPasswordCapsLockEvent = procedure(Sender: TObject;
    Locked: Boolean; State: TPasswordCapsLockState) of object;

  TPasswordEdit = class(TCustomEdit)
  private
    FIsCapsLocked: boolean;
    FBlockCapsBalloonTip: boolean;
    FOnPasswordCapsLock: TPasswordCapsLockEvent;
  protected
    procedure WndProc(var Message: TMessage); override;
    procedure KeyUp(var Key: Word; Shift: TShiftState); override;
    procedure DoEnter; override;
    procedure DoExit; override;
    procedure HandlePasswordCapsLock(State: TPasswordCapsLockState); virtual;
    function GetIsPassword: Boolean; virtual;
  public
    property IsPassword: Boolean read GetIsPassword;
  published
    property BlockCapsBalloonTip: boolean read FBlockCapsBalloonTip write FBlockCapsBalloonTip default False;
//... The usual property declaration of TEdit
    property OnPasswordCapsLock: TPasswordCapsLockEvent read FOnPasswordCapsLock write FOnPasswordCapsLock;
  end;

implementation

function TPasswordEdit.GetIsPassword: Boolean;
begin
  Result := ((PasswordChar <> #0) or
   // Edit control can have ES_PASSWORD style with PasswordChar == #0
   // if it was creaed with ES_PASSWORD style
   (HandleAllocated and (GetWindowLong(Handle, GWL_STYLE) and ES_PASSWORD <> 0)));
end;

procedure TPasswordEdit.HandlePasswordCapsLock;
var
  LNeedHandle: Boolean;
begin
  LNeedHandle := FBlockCapsBalloonTip and IsPassword;
  if LNeedHandle then
  begin
    FIsCapsLocked := Odd(GetKeyState(VK_CAPITAL));
    if Assigned(FOnPasswordCapsLock) then
      FOnPasswordCapsLock(Self, FIsCapsLocked, State);
  end;
end;

procedure TPasswordEdit.DoEnter;
begin
  inherited;
  HandlePasswordCapsLock(pcsEnter);
end;

procedure TPasswordEdit.DoExit;
begin
  inherited;
  HandlePasswordCapsLock(pcsExit);
end;

procedure TPasswordEdit.KeyUp(var Key: Word; Shift: TShiftState);
begin
  inherited;
  if Key = VK_CAPITAL then
    HandlePasswordCapsLock(pcsKey);
end;

procedure TPasswordEdit.WndProc(var Message: TMessage);
begin
  if (Message.Msg = EM_SHOWBALLOONTIP) and FBlockCapsBalloonTip and IsPassword then
    Exit;
  // Optional - if password char was changed
  if (Message.Msg = EM_SETPASSWORDCHAR) and Self.Focused then
    HandlePasswordCapsLock(pcsSetPasswordChar);
  inherited;
end;
like image 72
Nasreddine Galfout Avatar answered Sep 22 '22 13:09

Nasreddine Galfout