01 November 2016

Getting Rid of the Side-Scroll Effect in Pharo

!! the salvation script is at the bottom of this blogpost !!

There is a super old bug in Pharo (described in the Case 5296) that affects only the users of trackpads and magic mouses. When you scroll on a touch surface, it is not possible to scroll in a perfectly vertical line. So your scroll always generates at least some minor side-scroll events and they are represented by the VM as ⇧⌘ +  or ⇧⌘ +  keystrokes. This in turn causes the current window to switch focus to the next of previous widgets, and then they will start to receive events.
So imagine that you scroll a method list, but suddenly the focus switches to protocols, classes, packages and continues to scroll while selecting new packages. Yes, it is super frustrating and I don't know why no-one cares about that :(.

I got really annoyed with that last week and decided to finally change something. It was not hard to find out that all the keystrokes are handled by HandMorph>>#sendKeyboardEvent:. Having that knowledge I was able to write a guard at the beginning of the method like this:
sendKeyboardEvent: anEvent 
    ((anEvent keyCharacter = Character arrowLeft or: [
      anEvent keyCharacter = Character arrowRight ])   and: [
          anEvent controlKeyPressed and: [
          anEvent commandKeyPressed and: [
          anEvent shiftPressed and: [
          anEvent altKeyPressed ] ] ] ])
    ifTrue: [ ^ anEvent ].
    .
    .
    .

This way the system will simply ignore all the events generated by side-scrolling. The arrow keypresses accompanied by all the modifier keys will be ignored as well, but I couldn't care less about that. The problem was that I had to modify the method all the time, so this could not be easily automatable by startup scrips and couldn't be shared with the others. At that point I decided to use the power of metalinks to skip the method is the parameter was one of the keystrokes. With a help of Nicolai Hess the link was constructed the following way:
self
    condition: [ :args | self eventIsSidescroll: args first ]
    arguments: #(arguments);
    metaObject: [ :context | context return ];
    selector: #value:;
    arguments: #(context);
    control: #before

Please note: here self refers to my metalink which is a subclass of the generic MetaLink. Also note that #eventIsSidescroll: checks the keystroke event with the same conditional I've used in my first hack. The link is active only if the condition evaluates to true, and in this case it just returns from the current context (which is in the #sendKeyboardEvent: method).

Now you can load the hack to ignore side-scrolling by evaluating:
Metacello new
    baseline: 'IgnoreSideScrollHack';
    repository: 'github://Uko/IgnoreSideScrollHack';
    load.

No comments:

Post a Comment