tags:

views:

254

answers:

1

My question follows on from the answer in http://stackoverflow.com/questions/235498/how-to-create-a-button-with-drop-down-menu

I am trying to duplicate the behaviour of the "Office Button" found in the top left hand corner of the Mircosoft Office 2007 UI; in particular where the user clicks on the button, the popup menu appears and the button is redrawn in a "Down" state. The button image remains in the Down state until the user clicks on the mouse on the menu, OR elsewhere on the form, OR even outside of the application.

I need to detect this mouse click so I can redraw the button in the normal image, and if I am using my own dialog as the popup menu, to hide the menu.

I am using D6.
Thanks for any advice,
Regards,
PhilW.

clarification:

I now realize that the answer is trivial when a TPopUpMenu is used, having been under the mistaken understanding that once the popup menu was shown, it was left to the fate of the main event loop. And I have happily coded as such in the past. Wiser, and slightly embarrassed (but it's okay amongst friends) my question should have been more specific, and said:

"How do I detect this mouse click when I am using a dialog form to act like a TPopUpMenu?"

Sorry for the confusion.

+1  A: 

Maybe I'm missing something, but at least for a popup menu this is easy:

procedure TForm1.FormCreate(Sender: TObject);
begin
  SpeedButton1.AllowAllUp := TRUE;
  SpeedButton1.GroupIndex := 1;
end;

procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  CurPos: TPoint;
begin
  CurPos := Mouse.CursorPos;
  PopupMenu1.Popup(CurPos.x, CurPos.y);
  SpeedButton1.Down := FALSE;
end;

This works, as a popup menu is shown using a secondary message loop, and clicking outside of it does dismiss it just the same as clicking on a menu item.

If you want to show a form instead of a popup menu you just need to provide a wrapper function that does return only when the form has been closed, similar to Popup() in the code above. You could for example show the form non-modal, and use the SetCaptureControl() method to handle all mouse events, even when the mouse cursor is outside the form area.

Edit:

Some code to get you started - it demonstrates the principle, but is certainly not complete or optimal. Instead of the popup menu a form is shown:

procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  PtLeftTop: TPoint;
begin
  PtLeftTop := ClientToScreen(Point(SpeedButton1.Left + SpeedButton1.Width,
    SpeedButton1.Top + SpeedButton1.Height));
  TForm2.ShowFormAsPopup(PtLeftTop);
  SpeedButton1.Down := FALSE;
end;

The form has the following code:

type
  TForm2 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormDeactivate(Sender: TObject);
  public
    class procedure ShowFormAsPopup(ATopLeft: TPoint);
  end;

// boilerplate snipped

class procedure TForm2.ShowFormAsPopup(ATopLeft: TPoint);
var
  Form2: TForm2;
  OldDeactivate: TNotifyEvent;
begin
  Form2 := TForm2.Create(nil);
  try
    OldDeactivate := Application.OnDeactivate;
    try
      Application.OnDeactivate := Form2.FormDeactivate;

      Form2.Left := ATopLeft.x;
      Form2.Top := ATopLeft.y;
      Form2.Show;
      SetCaptureControl(Form2);
      while Form2.Visible do
        Application.ProcessMessages;
    finally
      Application.OnDeactivate := OldDeactivate;
    end;
  finally
    Form2.Release;
  end;
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  KeyPreview := TRUE;
end;

procedure TForm2.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if Key = VK_ESCAPE then
    Visible := FALSE;
end;

procedure TForm2.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  ScreenPos: TPoint;
begin
  ScreenPos := ClientToScreen(Point(X, Y));
  if (ScreenPos.X < Left) or (ScreenPos.Y < Top)
    or (ScreenPos.X > Left + Width) or (ScreenPos.Y > Top + Height)
  then begin
    Visible := FALSE;
  end;
end;

procedure TForm2.FormDeactivate(Sender: TObject);
begin
  Visible := FALSE;
end;
mghie
Au contraire, it was me. It took a long time to understand your answer, until I realized that the Popup statement returned control to the line immediately after(!). I have clarified my question.
PhilW
Fantastic! Much appreciate the advice and code. Answer accepted with thanks.
PhilW