Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: PopUpMenu from Context-menu key VK_APPS


This question is answered. Helpful answers available: 0. Correct answers available: 1.


Permlink Replies: 7 - Last Post: Jan 28, 2018 3:22 AM Last Post By: Richard Foersom
Richard Foersom

Posts: 10
Registered: 7/11/99
PopUpMenu from Context-menu key VK_APPS  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 23, 2018 4:54 PM
PopupMenu component in Delphi (10.2) works fine with mouse context-menu-click (right-click). However I have problems how to support the keyboard context-menu key (Windows lingo: VK_APPS key).

When using a PopupMenu on a StringGrid, in the OnPopUp event how do you know how the pop up was activated? Also in the OnPopUp event how can you change the position of the menu?

The problem is that the PopupMenu position will be correct if activated by mouse cursor, but that is not what you want when using VK_APPS key where you want it to be at text cursor (Windows lingo: caret) position.

When using a spread sheet like LibreOffice Calc, you can right click a cell and will get a context menu for that cell. If you are using cursor keys move to a cell and press VK_APPS key it will place the context-menu at current selected cell and menu is related to the selected cell, not to some other cell where the mouse cursor is hovering.

How do you do the similar with PopUpMenu component?

Similar in Delphi RAD studio, in the editor when using VK_Apps key gives a context menu related to and positioned at the current text cursor position, not somewhere else where mouse cursor is hovering.

It is clear that the developers of RAD studio did not simply use the PopUpMenu component in a straight forward manner. How did they do it?
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: PopUpMenu from Context-menu key VK_APPS
Helpful
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 23, 2018 7:05 PM   in response to: Richard Foersom in response to: Richard Foersom
Richard Foersom wrote:

PopupMenu component in Delphi (10.2) works fine with mouse
context-menu-click (right-click). However I have problems how to
support the keyboard context-menu key (Windows lingo: VK_APPS key).

Windows handles that key for you, and generates a WM_CONTEXTMENU
message for it, same as for right-clicks. The VCL uses that message to
fire a control's OnContextPopup event.

When using a PopupMenu on a StringGrid, in the OnPopUp event how do
you know how the pop up was activated?

You don't, well not directly anyway. The PopupMenu could be activated
by any number of ways, and the VCL abtracts away that information.

If you need to detect VK_APPS specifically (which you really don't),
you would have to handle the WM_KEYDOWN/WM_KEYUP messages directly, and
use them to set/clear a flag if the key code is VK_APPS, and then have
your OnPopup event look at that flag and act accordingly.

However, when WM_CONTEXTMENU is generated by keyboard input, either by
VK_APPS, or SHIFT+F10, etc, it reports the coordinates of the click as
-1,-1. Those coordinates are provided in the MousePos parameter of a
control's OnContextMenu event. So, simply use that as an indicator of
when to query your UI for whatever control is currently active/selected
and then use appropriate coordinates to display/update the PopupMenu as
needed.

Also in the OnPopUp event how can you change the position of the menu?

You can't, it is too late by then.

But, you can set the TPopupMenu.AutoPopup property to false, and then
in a control's OnContextPopup event, you can manually call the
TPopupMenu.Popup() method specifying whatever coordinates you want,
particularly when the provided MousePos coordinates are -1,-1.

When using a spread sheet like LibreOffice Calc, you can right click
a cell and will get a context menu for that cell.

In that situation, the WM_CONTEXTMENU message carries the client
coordinates of the click relative to the control that is being clicked
on. Those coordinates are provided in the MousePos parameter of a
control's OnContextPopup event.

If you are using cursor keys move to a cell and press VK_APPS key it
will place the context-menu at current selected cell and menu is
related to the selected cell, not to some other cell where the mouse
cursor is hovering.

How do you do the similar with PopUpMenu component?

Detect if the clicked coordinates are -1,-1 and act accordingly.

Similar in Delphi RAD studio, in the editor when using VK_Apps key
gives a context menu related to and positioned at the current text
cursor position, not somewhere else where mouse cursor is hovering.

It is clear that the developers of RAD studio did not simply use the
PopUpMenu component in a straight forward manner.

Actually, they did.

How did they do it?

See above.

--
Remy Lebeau (TeamB)
Richard Foersom

Posts: 10
Registered: 7/11/99
Re: PopUpMenu from Context-menu key VK_APPS  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 25, 2018 4:00 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thanks for the reply.

Remy Lebeau (TeamB) wrote:
Richard Foersom wrote:
Also in the OnPopUp event how can you change the position of the menu?

You can't, it is too late by then.

That is the problem. So then you have to do the multi step process with WM_CONTEXTMENU event and then manually start the PopupMenu. That is what I had hoped to avoid.

I have made a test program using this method, but I find it is a lot of additional mumbo-jumbo code to solve limitations of the PopupMenu component. Test app available here:

[http://foersom.org/forum/embarcadero/delphi/2018-01-25_StringGridPopUpMenu.zip]

The test app has a StringGrid and log output. The StringGrid can be context menu clicked by mouse or keyboard. For unknown reasons the WM_CONTEXTMENU event fires twice for each mouse click or VK_APPS keypress (see log output). In the OnContextMessage() I use a timer event (20 ms) to finish the WM_CONTEXTMENU event before starting the PopupMenu in Timer1Timer(). It eliminates the problem of the double WM_CONTEXTMENU event.

It is clear that the developers of RAD studio did not simply use the
PopUpMenu component in a straight forward manner.

Actually, they did.

No they certainly did not. They must have added the WM_CONTEXTMENU etc. handling as you have explained yourself above.

The TPopupMenu is intended to provide easy and complete handling of context-menu with AutoPopup. It handles mouse but should also handle VK_APPS key.

The TPopupMenu component could be updated with adding a property for trigger-source: mouse or VK_APPS key, and properties for X,Y positions. In the TPopupMenu OnPopup you would then be able to use source to determine if a change of X,Y position is needed in case of VK_APPS. Alternatively there could be a new OnBeforePopup event with parameters trigger-source and as var parameters position X,Y.
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: PopUpMenu from Context-menu key VK_APPS [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 25, 2018 4:52 PM   in response to: Richard Foersom in response to: Richard Foersom
Richard Foersom wrote:

That is the problem. So then you have to do the multi step process
with WM_CONTEXTMENU event and then manually start the PopupMenu. That
is what I had hoped to avoid.

Think about it. The OS tells your app that an action happened that may
need a popup menu, and in the case of a mouse click, it even tells your
app where the click occurred. If the default location is not to your
liking, then of course you need to do more work to change it.

The test app has a StringGrid and log output. The StringGrid can be
context menu clicked by mouse or keyboard. For unknown reasons the
WM_CONTEXTMENU event fires twice for each mouse click or VK_APPS
keypress (see log output).

Doesn't happen when I try it.

In the OnContextMessage() I use a timer event (20 ms) to finish the
WM_CONTEXTMENU event before starting the PopupMenu in Timer1Timer().
It eliminates the problem of the double WM_CONTEXTMENU event.

You don't need to (nor should you) handle the WM_CONTEXTMENU message
directly. The VCL already handles it for you. Use the StringGrid's
OnContextPopup event instead. Your example can be simplied to this:

type
  TMainForm = class(TForm)
    StringGrid1: TStringGrid;
    GridPopupMenu: TPopupMenu;
    ...
    procedure StringGrid1ContextPopup(Sender: TObject; MousePos: TPoint;
      var Handled: Boolean);
    ...
  end;
 
procedure TMainForm.StringGrid1ContextPopup(Sender: TObject; MousePos:
TPoint;
  var Handled: Boolean)
var
  Col, Row: integer;
  R: TRect;
  GR: TGridRect;
begin
  if MousePos = Point(-1, -1) then
  begin
    R := StringGrid1.CellRect(StringGrid1.Col, StringGrid1.Row);
    if R.IsEmpty then Exit;
    MousePos := R.BottomRight;
    Dec(MousePos.X, 8);
    Dec(MousePos.Y, 8);
  end else
  begin
    StringGrid1.MouseToCell(MousePos.X, MousePos.Y, Col, Row);
    if (Col = -1) or (Row = -1) then Exit;
    if (Col >= StringGrid1.FixedCols) and (Row >=
StringGrid1.FixedRows) then
    begin
      {StringGrid1.Col := Col;
      StringGRid1.Row := Row;}
      GR.Left := Col; GR.Right := Col;
      GR.Top := Row; GR.Bottom := Row;
      StringGrid1.Selection := GR;
    end;
  end;
  MousePos := StringGrid1.ClientToScreen(MousePos);
  GridPopupMenu.Popup(MousePos.X, MousePos.Y);
  Handled := True;
end;


The TPopupMenu is intended to provide easy and complete handling of
context-menu with AutoPopup. It handles mouse but should also handle
VK_APPS key.

TPopupMenu itself doesn't handle anything it is not told to. It is not
the one responding to the mouse or keyboard (at least for determining
the popup coordinates), TControl is doing that. When TControl receives
WM_CONTEXTMENU, it fires the OnContextPopup event, and if Handled=False
then it locates the control's assigned TPopupMenu, and if
AutoPopup=True then TControl calls TPopupMenu.Popup() with the
coordinates provided by M_CONTEXTMENU if a mouse click, or (0,0) if a
keyboard click. The OnContextPopup event is your oppurtunity to bypass
the default handling and invoke your own custom logic.

The TPopupMenu component could be updated with adding a property for
trigger-source: mouse or VK_APPS key, and properties for X,Y
positions.

No, it can't. That would not make any sense, since it doesn't care who
the source of a click is to begin with, all it cares about is the (X,Y)
coordinate that the caller wants the menu to display at.

In the TPopupMenu OnPopup you would then be able to use source to
determine if a change of X,Y position is needed in case of VK_APPS.

Nope, because:

1) that information is not made available to TPopupMenu in the first
place, since it is not the one directly reacting to click actions. The
UI control being clicked on is responsible for that, and then it
delegates to TPopupMenu after determining where to display the menu.

2) it is too late to change the (X,Y) coordinates in the
TPopupMenu.OnPopup event, as it has already been locked in by the
caller before the event is fired.

Alternatively there could be a new OnBeforePopup event with
parameters trigger-source and as var parameters position X,Y.

I can imagine such an event being added to allow tweaking the final
(X,Y) coordinates, but it would not be feisible to also have it report
source information. That would require additional changes be made to
the VCL outside of TPopupMenu itself.

--
Remy Lebeau (TeamB)
Richard Foersom

Posts: 10
Registered: 7/11/99
Re: PopUpMenu from Context-menu key VK_APPS [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 28, 2018 3:12 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
You don't need to (nor should you) handle the WM_CONTEXTMENU message
directly. The VCL already handles it for you. Use the StringGrid's
OnContextPopup event instead. Your example can be simplied to this:

That simplifies the solution! I see now what it was Peter Below was pointing to. I only focused on the StringGrid PopupMenu event, when in reality there is a different event to handle it.

Use the StringGrid OnContextPopup to determine VK_APPS or mouse and set the grid cell and menu position. Then simply use Popup to display context menu.

I have updated my test app using event StringGrid1ContextPopup() in a style similar to yours. Much simpler than using message WM_CONTEXTMENU, and it also avoid the double event happening.

Test app available here:

[http://foersom.org/forum/embarcadero/delphi/2018-01-28_StringGridPopUpMenu.zip]
Peter Below

Posts: 1,227
Registered: 12/16/99
Re: PopUpMenu from Context-menu key VK_APPS
Helpful
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 23, 2018 11:05 PM   in response to: Richard Foersom in response to: Richard Foersom
Richard Foersom wrote:

PopupMenu component in Delphi (10.2) works fine with mouse
context-menu-click (right-click). However I have problems how to
support the keyboard context-menu key (Windows lingo: VK_APPS key).

When using a PopupMenu on a StringGrid, in the OnPopUp event how do
you know how the pop up was activated?

The OnContextPopup event handler gets the mouse position as one of the
parameters. If the event was fired due to the context menu key this
position will be (-1, -1).

Also in the OnPopUp event how
can you change the position of the menu?

Set the HAndled parameter to true and then call the popup menu's Popup
method yourself, passing the correct position. THat you calculate from
the cell rectangle, by passing the position inside this rectangle you
want (e.g. the bottom left corner) to the grid control's ClientToScreen
method.


--
Peter Below
TeamB

Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: PopUpMenu from Context-menu key VK_APPS  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 24, 2018 11:35 AM   in response to: Peter Below in response to: Peter Below
Peter Below wrote:

Also in the OnPopUp event how
can you change the position of the menu?

Set the HAndled parameter to true and then call the popup menu's Popup
method yourself, passing the correct position.

You are thinking of the OnContextMenu event (which is the right
answer). But Richard asked about the OnPopup event instead, which is
an event of TPopupMenu itself. You can't specify the coordinates of
the popup menu in the OnPopup event.

--
Remy Lebeau (TeamB)
Richard Foersom

Posts: 10
Registered: 7/11/99
Re: PopUpMenu from Context-menu key VK_APPS  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 28, 2018 3:22 AM   in response to: Peter Below in response to: Peter Below
Peter Below wrote:
The OnContextPopup event handler gets the mouse position as one of the
parameters. If the event was fired due to the context menu key this
position will be (-1, -1).

Thanks. I had not realized that there is an OnContextPopup in TStringGrid! This is the best way to solve it, nice and simple the Delphi way.
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02