View Issue Details
|ID||Project||Category||View Status||Date Submitted||Last Update|
|0035986||Lazarus||Widgetset||public||2019-08-19 23:52||2019-08-22 05:50|
|Reporter||Zoë Peterson||Assigned To||Dmitry Boyarintsev|
|Status||resolved||Resolution||no change required|
|Product Version||2.0.4||Product Build|
|Target Version||Fixed in Version|
|Summary||0035986: Treat Alt/Ctrl+Left Click as Right/Middle Click|
|Description||Cocoa apps all treat Ctrl+Left Click as a Right Click and Option+Left Click as a Middle Click. Ctrl+left clicking should bring up the context menu, for example, and Option+Clicking in Safari will open a link in a new tab. LCLCARBON mapped the buttons automatically, and this patch does the same thing for LCLCOCOA.|
In LCLCARBON, you could distinguish the fake state by looking at the TShiftState instead of the TMouseButton, and I've kept that behavior here. So:
Left Click => mbLeft + 
Ctrl+Right Click => mbRight + [ssCtrl, ssRight]
Ctrl+Left Click => mbRight + [ssCtrl, ssLeft]
We never use that fact, but it seemed reasonable to keep it.
|Steps To Reproduce||Using the attached sample project, left click, right click, and ctrl+left click on the form. The window caption will change to indicate what's currently being passed in, and it will show a context menu on a right click. |
In LCLCARBON, Ctrl+Left Click is reported as mbRight + [ssCtrl, ssLeft]. In the current LCLCOCOA, it's reported as mbLeft + [ssCtrl, ssLeft] and it doesn't show the context menu.
|Tags||No tags attached.|
|Fixed in Revision|
ctrl-click-remap.zip (110,878 bytes)
ctrl-click-remap.patch (612 bytes)
--- a/lcl/interfaces/cocoa/cocoawscommon.pas +++ b/lcl/interfaces/cocoa/cocoawscommon.pas @@ -947,7 +947,13 @@ begin // high word of XButton messages indicate the X button which is pressed Msg.Keys := Msg.Keys or (MButton - 2) shl 16; MButton := 3; - end; + end + else if MButton = 0 then + // treat ctrl+left click as right click and alt+left click as middle click + if Event.modifierFlags and NSControlKeyMask <> 0 then + MButton := 1 + else if Event.modifierFlags and NSAlternateKeyMask <> 0 then + MButton := 2; lEventType := Event.type_; if AForceAsMouseUp then
ctrl-click-remap.patch (612 bytes)
note that TextEdit works differently for RightClick vs LeftClick+Control
RightClick simply shows the context menu, where
LeftClick+Control also selects the word under the cursor and shows the context menu.
Option+LeftClicking in Safari tries to download the link. It doesn't open it in the new tab (like a middle click does).
Also, Option+Left clicking in Safari does nothing on the tab. MiddleClick closes the tab.
It doesn't seem that "option+left click" is supposed to do anything.
Neither Apple guidelines define a middle click in any manner:
(vs Primary and Secondary clicks)
||one need to note that "secondary" click is not "double click". it's either Right mouse click or Left mouse click.|
In my testing in TextEdit on macOS 10.14 both clicks work identically:
Left click: Move the caret to be under the mouse cursor
Right click / Left click + Ctrl: If the cursor is not over a selection, select the word or whitespace below it. Show the context menu.
The catch is that if there isn't a selection but the mouse cursor is over the caret, it considers that the selection. So if you move the mouse around and randomly right click it will always select what's under the cursor, but if you left click and then immediately right click it shows the menu without expanding the selection.
||I'm fine with dropping the Option+Left Clicking part of the patch. I included it because the Carbon version did, but it isn't something I've ever used and the customer who reported the lack of Ctrl+Left click support didn't mention it.|
1) if Option+Left <> Middle, then why should we trust Carbon code in the first place?! (and option+left=middle is just wrong)
It might have been applied (r14324) as a work around in order to be able to show a context menu.
(the revision contains the author name, but not the reasoning or a bug report behind the patch)
2) AppKit doesn't substitute Ctrl+Left as mouseRightDown or mouseRightUp.
(for example, "Primary Button" can be switched between "Left" and "Right". If it's down the actual mouseLeft and mouseRight actions will be swapped, within AppKit).
AppKit doesn't need the substitution because it has it a separate method for showing the Context menu.
Which would be called by the AppKit for control+left, and is emulated for control+left by CocoaWS.
Anyway. The patch IS applicable, since it somewhat matches the description of TMouseButton
as "LOGICAL" button, (meaning it doesn't have to match the physical button).
But, it might cause issues where the code is "unaware" of macOS specifics.
if buttom - mbRight then begin
if (ssCtrl in ShiftState) then ...
Do you've an ability to check how Qt for Mac handles Ctrl+Left?
On the other hand, it cannot be used.
For example: a user hold Control and clicks left mouse -> that's expected to be MouseDown mbRight.
But then user released Control.
and only after that release mouse. As a result, there's MouseUp mbLeft.
The proper implementation would, if "OnClick" event had TMouseButton method.
For such method it would make total sense to substitute Ctrl+Left as Right.
Instead of OnClick (or OnRightClick), there's OnContextPopup method. Which is being called on either RightClick or Ctrl+LeftClick.
ctrl-click-remap-v2.patch (947 bytes)
--- a/lcl/interfaces/cocoa/cocoawscommon.pas +++ b/lcl/interfaces/cocoa/cocoawscommon.pas @@ -177,6 +177,7 @@ uses var LastMouse: TLastMouseInfo; + LastMouseLeftButtonAsRight: Boolean; function ButtonStateToShiftState(BtnState: PtrUInt): TShiftState; begin @@ -953,6 +954,20 @@ begin if AForceAsMouseUp then lEventType := NSLeftMouseUp; + // treat ctrl+left button as right button + if (lEventType = NSLeftMouseDown) and + (Event.modifierFlags and NSControlKeyMask <> 0) then + LastMouseLeftButtonAsRight := True; + if LastMouseLeftButtonAsRight then + begin + if MButton = 0 then + MButton := 1; + if Msg.Keys and MK_LBUTTON <> 0 then + Msg.Keys := (Msg.Keys or MK_RBUTTON) and not MK_LBUTTON; + if lEventType = NSLeftMouseUp then + LastMouseLeftButtonAsRight := False; + end; + Result := Result or (BlockCocoaUpDown and not AOverrideBlock); case lEventType of NSLeftMouseDown,
ctrl-click-remap-v2.patch (947 bytes)
Here's a new version of the patch that matches LCLQT on macOS and drops the Options+Left click behavior.
The remapped state is now maintained until left mouse up, so doing "Ctrl+Left down, Ctrl up, Left up" will send it as a right mouse up.
Both LCLQT and LCLCARBON include ssCtrl if Ctrl is held down, regardless of the remapping occurring. mbRight + [ssRight] is always a plain right click, but mbRight + [ssRight, ssCtrl] could be either an actual Ctrl+Right click or Ctrl+Left click:
Left Click => mbLeft [ssLeft]
Right Click => mbRight [ssRight]
Ctrl+Left Click => mbRight [ssCtrl, ssRight]
Ctrl+Right Click => mbRight [ssCtrl, ssRight]
I also added code to update Shift to include ssRight instead of ssLeft when appropriate, which matches LCLQT but not LCLCARBON. That makes it impossible to detect the remapping, but makes the state consistent and fixes "Ctrl+Left down, Right down" to send "mbRight + [ssCtrl, ssRight]" instead of "mbRight + [ssCtrl, ssLeft, ssRight]" for the second mouse down. That does mean that if you release one of the buttons in that state, the first MouseUp will see "mbRight + [ssRight]" instead of "mbRight + ", which is weird but correct.
Qt does the mapping automatically, so LCLQT doesn't have or need any extra code to behave like this.
I verified that it also works correctly if the System Preferences "Primary mouse button" is set to "Right" instead of "Left". When that's set, the system just switches which physical button corresponds with which button indexes, so clicking on the right mouse button sends an NSLeftMouseDown event. We don't have to do anything special and the resulting output of my tests is identical as long as I swap button presses.
If someone has code that's not macOS aware but runs on macOS and has trouble with this patch, I suggest we let them speak up once this gets merged. The behavior has existed in LCLCARBON for 11 years and LCLQT since its creation, so it doesn't seem like it's causing a lot of problems. They should also update their code to make it macOS aware, since it'll be confusing to anyone used to the default system behavior unless they do.
Here's the full list of tests I've done and the event order and state passed in:
// Left click
// Right click
// Ctrl+Left click
// Ctrl+Right click
// Ctrl+Left down, Ctrl up, Left up
// Left down, Right click, Left up
// Left down, Right down, Left up, Right up
// Ctrl+Left down, Right click, Left up
// Ctrl+Left down, Ctrl up, Right click, Left up
Ctrl+Left = mbRight+[ssCtrl,ssRight]
that's not Carbon compatible.
and it cannot be distinguished from Ctrl+Right
> that's not Carbon compatible.
Your previous asked "why should we trust Carbon code in the first place?! (and option+left=middle is just wrong)". IMO, after looking into it, we shouldn't. Exact LCLCARBON compatibility is not desirable. It reports different values depending on whether you check Button or Shift, and doesn't send mbRight on MouseUp. Those are both bad and unexpected, and I believe they were unintentional oversights.
> and it cannot be distinguished from Ctrl+Right
That is true for LCLQT on macOS too.
I don't think there's a right answer here. I first ran in to this because Ctrl+LeftClicking on a selection in our text editor cleared the selection before showing the context menu. SynEdit under LCLCOCOA has the exact same bug. Anything that looks at Button and not Shift will have similar problems. We have a bunch of controls that are affected. To fix it, we have to check for ssCtrl before doing normal mbLeft handling, and while that's easy to do, I don't think most LCL applications will know to do it. It also pushes macOS-specific code up to all of those places. This patch makes it easier for them to work correctly.
On the other hand, a game would need to tell the two clicks apart. The current SVN code will work perfectly for them, because they never show context menus.
In that case, the best solution is not to do the change.
Hiding the information (by substituting Ctrl+Left to Ctrl+Right) is worse.
Having the actual information would at least allow the end application to reliably resolve the mouse event in the way it finds fit.
"Preview" by Apple - treats Ctrl+Left as just Left. It does allow to setup the selection. (Right click on the image does nothing )
"Chess" by Apple - treats Ctrl+Left as just Left. Right click does nothing.
"SketchBook" by Autodesk - ignores Ctrl+Left completely (even though there's a context menu showing up on the Right Click)
After all Apple declares the Secondary click as "need to show context menu".
Though doesn't explain what needs to happen if context menu is not used (specifically for Ctrl+Left).
Apple's handling of Ctrl+Left is never = Right click.
Ctrl+Left on a button - is actually pressing the button!
Right on a button - does nothing.
It's to an application to decide what it wants to do. WS should not obscure the system information
The best thing that a WS could do is to provide the information ahead of time,
IF the selection needs to be reset, on a certain mouse + shiftstate,
"the default" for all widgetset would be to return false for "rightClick"
for macOS it would return false for "rightClick" or "leftClick"+"ctrl"
I don't think such API exists, though.
|2019-08-19 23:52||Zoë Peterson||New Issue|
|2019-08-19 23:52||Zoë Peterson||File Added: ctrl-click-remap.zip|
|2019-08-19 23:52||Zoë Peterson||File Added: ctrl-click-remap.patch|
|2019-08-20 17:25||Dmitry Boyarintsev||Assigned To||=> Dmitry Boyarintsev|
|2019-08-20 17:25||Dmitry Boyarintsev||Status||new => assigned|
|2019-08-20 17:30||Dmitry Boyarintsev||Note Added: 0117746|
|2019-08-20 17:45||Dmitry Boyarintsev||Status||assigned => feedback|
|2019-08-20 17:45||Dmitry Boyarintsev||LazTarget||=> -|
|2019-08-20 17:45||Dmitry Boyarintsev||Note Added: 0117747|
|2019-08-20 17:46||Dmitry Boyarintsev||Note Added: 0117748|
|2019-08-20 17:46||Dmitry Boyarintsev||Note Edited: 0117747||View Revisions|
|2019-08-20 18:03||Zoë Peterson||Note Added: 0117749|
|2019-08-20 18:03||Zoë Peterson||Status||feedback => assigned|
|2019-08-20 18:07||Zoë Peterson||Note Added: 0117750|
|2019-08-21 03:52||Dmitry Boyarintsev||Status||assigned => feedback|
|2019-08-21 03:52||Dmitry Boyarintsev||Note Added: 0117758|
|2019-08-21 04:32||Dmitry Boyarintsev||Note Added: 0117759|
|2019-08-21 06:11||Dmitry Boyarintsev||Note Added: 0117760|
|2019-08-21 20:24||Zoë Peterson||File Added: ctrl-click-remap-v2.patch|
|2019-08-21 20:24||Zoë Peterson||Note Added: 0117770|
|2019-08-21 20:24||Zoë Peterson||Status||feedback => assigned|
|2019-08-21 21:52||Dmitry Boyarintsev||Note Added: 0117771|
|2019-08-21 23:52||Zoë Peterson||Note Added: 0117775|
|2019-08-22 05:39||Dmitry Boyarintsev||Status||assigned => resolved|
|2019-08-22 05:39||Dmitry Boyarintsev||Resolution||open => no change required|
|2019-08-22 05:39||Dmitry Boyarintsev||Widgetset||Cocoa => Cocoa|
|2019-08-22 05:39||Dmitry Boyarintsev||Note Added: 0117776|
|2019-08-22 05:43||Dmitry Boyarintsev||Note Added: 0117777|
|2019-08-22 05:43||Dmitry Boyarintsev||Note Edited: 0117777||View Revisions|
|2019-08-22 05:44||Dmitry Boyarintsev||Note Edited: 0117777||View Revisions|
|2019-08-22 05:49||Dmitry Boyarintsev||Note Added: 0117778|
|2019-08-22 05:50||Dmitry Boyarintsev||Note Edited: 0117778||View Revisions|