0

my application is showontop type window with a popupmenu(mastermenu) popping up at cursor location meant to be outside of the main form(demoForm), triggered by an external winapi message (clipboard change).

the annoying problem is that the menu won't disappear when clicked outside of the application instead of clicking on any menu items or the main form as when normally done to dismiss a menu. The focus goes out, my application stays on top, and the menu remains floating.

Tried to follow many articles and even changed from D7 to XE5 without success. Checked this too : Automatically Hide or Close PopUp Menu when Mouse Pointer is outside it - Delphi Mine is not complicated by delay timers or tray control.

Specifically, borrowing from a solution I did this:

procedure TDemoForm.tmrMenumouseOutMonitorTimer(Sender: TObject);
var
  hPopupWnd: HWND;
  R: TRect;
  PT: TPoint;
begin
   hPopupWnd :=  FindWindow('#32768', mastermenu);
  if hPopupWnd = 0 then Exit;
  GetWindowRect(hPopupWnd, R);
  GetCursorPos(Pt);
  if PtInRect(R, Pt) then begin
  //do something
  end else begin
  //do something
  end;
end;

Where I am trying to poll the cursor position with a timer (MenumouseOutMonitorTimer) to detect if the cursor moved out of the menu (mastermenu). In case it moves out i will issue a .closeMenu()

But, this code throws - string, pAnsiChar/pwidestring mismatch in D7/XE5 at the FindWindow() last argument. maybe i should use FindWindowEx? XE5 directly returns some handles from a TPopupMenu but I don't know how to use them to solve my problem.

(on Win7, also targeting XP)

I am a total beginner, any help will be appreciated.

Full code here:

unit FmDemo;

interface

uses
  System.Classes,
  Vcl.Controls,
  Vcl.StdCtrls,
  Vcl.Forms, Menus, Dialogs, FileCtrl, ExtCtrls,PJCBView;// ....;

type
  TDemoForm = class(TForm)
    //......
    PJCBViewer1: TPJCBViewer; //custom control
    masterMenu: TPopupMenu;
    tmrMenumouseOutMonitor: TTimer;
    procedure tmrMenumouseOutMonitorTimer(Sender: TObject);

  private
    //........
    procedure menuItemClickHandler(Sender: TObject);
  end;

var
  DemoForm: TDemoForm;

implementation

uses
      Jpeg, Shellapi, Graphics, SysUtils, RichEdit, Messages;//GifImage

{$R *.dfm}

procedure tdemoform.menuItemClickHandler(Sender: TObject);
begin
  //.......
end;

procedure TDemoForm.PJCBViewer1ClipboardChanged(Sender: TObject);
var
   pnt: TPoint;
begin
  demoform.BringToFront; //formStyle -> fsStayOnTop already
  ///////////////////////////////////
  ///menu under cursor display code//
  ///////////////////////////////////

  if GetCursorPos(pnt) then
   begin
      masterMenu.Popup(pnt.X, pnt.Y);
   end;
  //remember to return focus to source window after each menu action (not implemented)
end;

procedure TDemoForm.tmrMenumouseOutMonitorTimer(Sender: TObject);
var
  hPopupWnd: HWND;
  R: TRect;
  PT: TPoint;
begin
  hPopupWnd :=  FindWindow('#32768', masterMenu);
  if hPopupWnd = 0 then Exit;
  GetWindowRect(hPopupWnd, R);
  GetCursorPos(Pt);
  if PtInRect(R, Pt) then begin
  //do something
  end else begin
  //do something
  end;
end;

//... other business logic

initialization
  CF_RTF := RegisterClipboardFormat( richedit.CF_RTF );
end.
Community
  • 1
  • 1
user30478
  • 347
  • 1
  • 3
  • 13

1 Answers1

2

Here is a MCVE that doesn't require a third party control.

...

implementation

uses
  menus;

{$R *.dfm}

var
  Pop: TPopupMenu;
  Wnd: HWND;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Left := 200;
  Top := 100;
  Pop := TPopupMenu.Create(nil);
  Pop.Items.Add(TMenuItem.Create(Pop));
  Pop.Items[0].Caption := 'test';
  Wnd := GetForegroundWindow;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  SetForegroundWindow(Wnd); // comment this for the popup to be released when clicked outside
  Pop.Popup(100, 50);
end;

Click outside the form and the popup will not be released.

As you can see I had to artificially impose the condition that reproduces the problem, which is that your window is not in the foreground when you popup the menu.

As mentioned at several places at the page you linked, for the popup to be released normally, your window has to be in the foreground when you pop the menu, then you won't need to poll and find it and then manually release it. SetForegroundWindow does not guarantee that your window will come to the front. For more information about this issue and for several solutions, see this question.

Community
  • 1
  • 1
Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169
  • This seemed to work for a while but, now i see the same problem in both win7, XP. As directed, I commented out the setforegroundWindow() but the problem persists : 1. I am triggering the popup outside the application through clipboard change (not button click). 2. application goes to background but popup displays. if i click again in the same external window(not my application's but the source of clipboard change). the menu 'floats'. – user30478 Oct 09 '16 at 18:09
  • 2
    @user - I hope you did not comment your SerForegroundWindow, that is how I replicated the issue. You should incorporate one of the solutions in the page I linked to bring your form to the foreground, did you? – Sertac Akyuz Oct 09 '16 at 18:17
  • Instead of just the setForegroundWindow(), Used your linked answer inside the trigger handler method which invokes the menu. Now the menu behaves properly. I am not worried about what happens to the form because it will be hidden/minimized anyway. Tested D7/XP/Win7 I saw that i consulted that linked question too and had upped your answer but, implemented someone else's. BTW this tool is for meta-programming i.e. will help generating delphi tutorials for many people out there like me in a world of scant resources. Thanks Sertac, and David too. – user30478 Oct 09 '16 at 18:45