-1

I’m considering switching from the abandoned TCoolTrayIcon to Delphi’s own TTrayIcon. The only thing that I’m missing is OnMouseEnter and OnMouseExit (≘OnMouseLeave) that I need in my case.

Is there an easy way to add these events to TTrayIcon? (CoolTrayIcon does this with a timer ... I'm not sure if that's really the best solution)

Fabrizio
  • 7,603
  • 6
  • 44
  • 104
CodeX
  • 717
  • 7
  • 23
  • 1
    Why wouldn't you just stick to your existing code. It's not going to stop working. – David Heffernan Apr 18 '18 at 10:12
  • Well, I'm upgrading my Delphi version and I've realized that many third-party components that I previously used can now be replaced with integrated components. TrayIcon is just one of them. In my opinion this makes the project much cleaner and easier to maintain. – CodeX Apr 18 '18 at 10:38
  • Except that in this instance the built in component doesn't have the functionality that you want. I do have to ask though, why would you want to know about mouse enter and exit on a notification icon? Anyway, I believe that you can listen for `WM_MOUSEMOVE` messages. – David Heffernan Apr 18 '18 at 10:47
  • You are not saying why you need these event handlers. TTrayIcon has a Hint property. – Jan Doggen Apr 18 '18 at 14:12
  • For showing custom details to the user. A simple hint is not suitable here. – CodeX Apr 18 '18 at 14:36
  • If I were you I'd stick to code that works and meets your requirements. This feels like change for the sake of it. Anyway, if you are dead set on changing, then you'll need to do some coding. – David Heffernan Apr 18 '18 at 20:43

2 Answers2

1

Although nobody seemed to be really interested in an actual solution to this question, I guess it would be fair to post my solution anyway, in case somebody else ever searches for the same problem.

It was way less coding than I initially expected.

unit TrayIconEx;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.ExtCtrls;

type
  TTrayIconEx = class(TTrayIcon)
  private
    CursorPosX, CursorPosY: Integer;
    FOnMouseEnter: TNotifyEvent;
    FOnMouseLeave: TNotifyEvent;
    EnterLeaveTimer: TTimer;
    procedure EnterLeaveEvent(Sender: TObject);
  protected
    procedure WindowProc(var Msg: TMessage); override;
    procedure MouseEnter; virtual;
    procedure MouseLeave; virtual;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property OnMouseEnter: TNotifyEvent read FOnMouseEnter write FOnMouseEnter;
    property OnMouseLeave: TNotifyEvent read FOnMouseLeave write FOnMouseLeave;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Custom', [TTrayIconEx]);
end;

constructor TTrayIconEx.Create(AOwner: TComponent);
begin
  inherited;
  EnterLeaveTimer := TTimer.Create(Self);
  EnterLeaveTimer.Enabled := False;
  EnterLeaveTimer.Interval := 200;
  EnterLeaveTimer.OnTimer := EnterLeaveEvent;
end;

procedure TTrayIconEx.WindowProc(var Msg: TMessage);
var
  p: TPoint;
begin
  if Assigned(FOnMouseEnter) or Assigned(FOnMouseLeave) then
  begin
    if (Msg.Msg = WM_SYSTEM_TRAY_MESSAGE) and (Msg.lParam = WM_MOUSEMOVE) then
    begin
      GetCursorPos(p);
      CursorPosX := p.X;
      CursorPosY := p.Y;
      if not EnterLeaveTimer.Enabled then
        MouseEnter();
    end;
  end;
  inherited WindowProc(Msg);
end;

procedure TTrayIconEx.EnterLeaveEvent(Sender: TObject);
var
  p: TPoint;
begin
  //Win7+ supports Shell_NotifyIconGetRect(), but to support Vista and XP a workaround is required.
  //-> If the position differs from the last captured position in MouseMove, then the cursor was moved away.
  GetCursorPos(p);
  if (CursorPosX <> p.X) or (CursorPosY <> p.Y) then
    MouseLeave();
end;

procedure TTrayIconEx.MouseEnter;
begin
  if Assigned(FOnMouseEnter) then
    FOnMouseEnter(Self);
  EnterLeaveTimer.Enabled := True;
end;

procedure TTrayIconEx.MouseLeave;
begin
  EnterLeaveTimer.Enabled := False;
  if Assigned(FOnMouseLeave) then
    FOnMouseLeave(Self);
end;

end.
CodeX
  • 717
  • 7
  • 23
  • 1
    Note that your `WindowProc`'s use of `WM_MOUSEMOVE` assumes `TTrayIcon` registers `NOTIFYICONDATA.uVersion < 4`. But, if it registers `>= 4` instead, the meaning of the `wParam`/`lParam` values will be different. In which case, you would need to use this instead: `if (Msg.Msg = WM_SYSTEM_TRAY_MESSAGE) and (LOWORD(Msg.lParam) = WM_MOUSEMOVE) then begin CursorPosX := GET_X_LPARAM(Msg.wParam); CursorPosY := GET_Y_LPARAM(Msg.wParam); end;` – Remy Lebeau Nov 01 '21 at 17:02
  • @RemyLebeau Thanks for your addition! Just to clarify: Version 4 would only be registered if one explicitly uses NOTIFYICON_VERSION_4, right? Or is there a situation where this would happen automatically? – CodeX Nov 01 '21 at 21:21
  • You would have to look at `TTrayIcon`'s source code to determine that. One would hope it used newer `NOTIFYICONDATA` versions in modern Windows versions. I don't know how `TTrayIcon` behaves in current Delphi versions. In older versions, it used `NOTIFYICONDATA.uVersion=0`. – Remy Lebeau Nov 01 '21 at 21:32
-2

If all you want is a more advanced hint then you can Personalization its own hint like any other string with special character such as Enter and etc like this :

edit1.hint:='first row'+#13+'second row'+#13+#13+'last row';
toto
  • 17
  • 1
  • 6
  • You're answering the question "How to create multi-line hints?". Sorry, but this has nothing to do with my question. – CodeX Apr 21 '18 at 15:57