ListViewControl Right Click quirk

Discussions related to Visual Prolog
Harrison Pratt
VIP Member
Posts: 284
Joined: 5 Nov 2000 0:01

ListViewControl Right Click quirk

Unread post by Harrison Pratt » 3 Nov 2019 13:20

I ran across this quirk when trying to implement a right-click function in a ListviewControl -- unlike Left-clicking, Right-clicking on a column label is responded to as though the first data Item row were clicked.

It is possible to work around that behavior by testing to see if the PNT clicked is inside the data Item RECT, as shown in the test code below. Of course, it is not necessary to do this test for Left-clicking in production code.

My question is this: Is there a built-in predicate in ListViewControl that would handle this more gracefully than checking for PNT in RECT?

Code: Select all

predicates     onListViewControlMouseRightClick : listViewControl::mouseRightClickListener. clauses     onListViewControlMouseRightClick(Source, Pnt) :-         % RIGHT CLICK DOES NOT BEHAVE THE SAME WAY AS LEFT CLICKING ON COLUMN LABELS         if Source:hitTest(Pnt, ItemID, SubItemId) and listViewControl::item(ItemID, KeyStr, _, _, MoreSS) = Source:getItem(ItemID) then             %-- get item RCT and see if Pnt lies inside it             if pntInside(Pnt, Source:getItemRect(ItemID, listViewControl::bounds)) then                 % ARRIVE HERE IF RIGHT CLICK OUTSIDE THE DATA GRID                 stdio::write("\n\nRight Clicked Inside Data Area")             else                 % ARRIVE HERE IF RIGHT CLICK ON COLUMN LABEL                 stdio::write("\n\nRight Clicked Outside Data Area - you should ignore returned value!")             end if,             stdio::writef("\n% \t % \tClicked on  cell containing '%'", ItemID, KeyStr, list::nth(SubItemId, [KeyStr | MoreSS]))         else             stdio::write("\nNOT on a DATA CELL or COLUMN LABEL")         end if.   predicates     onListViewControlMouseClick : listViewControl::mouseClickListener. clauses     onListViewControlMouseClick(Source, Pnt) :-         if Source:hitTest(Pnt, ItemID, SubItemId) and listViewControl::item(ItemID, KeyStr, _, _, MoreSS) = Source:getItem(ItemID) then             %-- get item RCT and see if Pnt lies inside it             if pntInside(Pnt, Source:getItemRect(ItemID, listViewControl::bounds)) then                 stdio::write("\n\nLeft Clicked Inside Data Area")             else                 % NEVER GET HERE WHEN LEFT CLICKING so checking for PNT in RECT is unnecessary                 stdio::write("\n\nLeft Clicked Outside Data Area - you should ignore returned value!")             end if,             stdio::writef("\n% \t % \tClicked on cell containing '%'", ItemID, KeyStr, list::nth(SubItemId, [KeyStr | MoreSS]))         else             % ARRIVE HERE IF LEFT CLICK OUTSIDE THE DATA GRID             stdio::write("\nNOT on a DATA CELL or COLUMN LABEL")         end if.   class predicates     pntInside : (pnt, rct) determ. clauses     pntInside(pnt(X, Y), rct(L, T, R, B)) :-         L <= X,         X <= R,         T <= Y,         Y <= B.
The verbose test code can be reduced to these predicates to retrieve listview item text :

Code: Select all

class predicates     tryGetTextClickRt : (listViewControl, pnt PointClicked) -> string determ. clauses     tryGetTextClickRt(LVC, Pnt) = Text :-         LVC:hitTest(Pnt, ItemID, SubItemId),         pntInside(Pnt, LVC:getItemRect(ItemID, listViewControl::bounds)), % only necessary for Rt. click         listViewControl::item(ItemID, KeyStr, _, _, MoreSS) = LVC:getItem(ItemID),         Text = list::nth(SubItemId, [KeyStr | MoreSS]).   class predicates     tryGetTextClickLt : (listViewControl, pnt PointClicked) -> string determ. clauses     tryGetTextClickLt(LVC, Pnt) = Text :-         LVC:hitTest(Pnt, ItemID, SubItemId),         listViewControl::item(ItemID, KeyStr, _, _, MoreSS) = LVC:getItem(ItemID),         Text = list::nth(SubItemId, [KeyStr | MoreSS]).

User avatar
Thomas Linder Puls
VIP Member
Posts: 1622
Joined: 28 Feb 2000 0:01

Re: ListViewControl Right Click quirk

Unread post by Thomas Linder Puls » 5 Nov 2019 12:34

The listView control is a standard Windows control. The hitTest predicates uses the LVM_SUBITEMHITTEST to determine where the point is. For reasons unknown to me this message think that most of the header row belongs to the first data line.

The difference in the left and right click happens because the left click is "removed" when the header handles the click.

We were not aware of this problem, so I don't believe there are any "nicer" way around it.

Notice that the gui class contains a "point in rectangle" test (with different border handling):

Code: Select all

predicates     rectPntInside : (vpiDomains::rct Rectangle, vpiDomains::pnt Point) determ.     % @short Succeeds if #Point is inside #Rectangle.     % A point on the right or bottom side is considered outside the rectangle.     % @end
Regards Thomas Linder Puls
PDC

Harrison Pratt
VIP Member
Posts: 284
Joined: 5 Nov 2000 0:01

Re: ListViewControl Right Click quirk

Unread post by Harrison Pratt » 5 Nov 2019 13:54

Thanks for the deeper-dive into this quirk.
It took a while for me to sort out this unexpected behavior.
Using a code equivalent of my tryGetTextClickRt/2 for both left and right-click text retrieval should work well.

User avatar
Thomas Linder Puls
VIP Member
Posts: 1622
Joined: 28 Feb 2000 0:01

Re: ListViewControl Right Click quirk

Unread post by Thomas Linder Puls » 5 Nov 2019 15:07

I have updated hitTest like this (i.e. the lines after the cut)::

Code: Select all

clauses     hitTest(PNT, ItemId, SubItem) :-         LVHITTESTINFO = gui_native::lvhittestinfo(PNT, 0, 0, 0, 0),         _ = gui_native::sendMessage(nativeWindow, lvm_subItemHitTest, gui_api::wNull,              uncheckedConvert(gui_native::lParam, LVHITTESTINFO)),         gui_native::lvhittestinfo(_, _, Item, SubItem, _) = LVHITTESTINFO,         lv_item_index(ItemId, uncheckedConvert(unsigned, Item), _),         !,         if gui_api::lNull = ItemId then             % The control reports "first row" far into the header (bug in control)             % we test that the PNT is actually inside the relevant bounds (which the control responds corectly)             gui::rectPntInside(getItemRect(ItemId, bounds), PNT)         end if.
For your original problem you should notice that hitTest will also fail for points that are to right of the right most column, no matter which row (or header) the point is in.
Regards Thomas Linder Puls
PDC

Harrison Pratt
VIP Member
Posts: 284
Joined: 5 Nov 2000 0:01

Re: ListViewControl Right Click quirk

Unread post by Harrison Pratt » 5 Nov 2019 23:38

That's better!

I assume that the updates will be in the next update of VP 9.x and I should patch VP 8.02 myself or shadow the predicate.

"... hitTest will also fail for points that are to right of the right most column" is expected behavior, and it looks like hitTest/3 also will fail if the point is below the last Item row, which is also expected behavior.

Post Reply