You probably know about the Windows 7+ feature to snap a window to the left or right side of the monitor via Windows+Left / Windows+Right hotkey. You might even know that Windows 10 extended this to snap a window to the top or bottom and even to one of the quadrants of your monitor. (I for one had read about that Windows 10 feature but had already forgotten it, since I don’t plan to upgrade in the near future.)
But what if you don’t have Windows 10? Can this be done programmatically? Of course it can! Just add appropriate Actions, assign the shortcuts to your form and write the code. Or set the form’s KeyPreview property to true and put the code into the FormKeyDown event handler. Unfortunately this requires some rather tiresome copy and paste plus clicking on the object inspector for each of the forms. And, of course, copy and paste is evil, because it violates the DRY principle: If you make a mistake once, you will have to correct it in all the copies you have made in the meantime.
Enter TForm_ActivatePositoning: A utility function which I have added to dzLib today:
Just by calling
TForm_ActivatePositioning(Self);
you can add this functionality to every form in every project you’ll ever write.
Ok, so what does it do?
It subclasses the window (that is: Replaces its WindowProc (Wow, I just learnt that there is a new approach to subclassing, must have missed that for a few years)) to intercept the CM_CHILDKEY message which is sent by the VCL whenever any child control of a form receives a WM_KEYDOWN message:
type TFormPositioningActivator = class(TWindowProcHook) private FModifier: TShiftState; procedure CmChildKey(var _Msg: TMessage); function TheForm: TForm; protected procedure NewWindowProc(var _Msg: TMessage); override; public constructor Create(_Form: TForm; _Modifier: TShiftState); end; constructor TFormPositioningActivator.Create(_Form: TForm; _Modifier: TShiftState); begin inherited Create(_Form); FModifier := _Modifier; end; procedure TFormPositioningActivator.CmChildKey(var _Msg: TMessage); var Key: Word; begin Key := (_Msg.WParamLo and $FF); if GetModifierKeyState = FModifier then begin case Key of VK_LEFT, VK_NUMPAD4: TForm_MoveTo(TheForm, dwpLeft); VK_RIGHT, VK_NUMPAD6: TForm_MoveTo(TheForm, dwpRight); VK_UP, VK_NUMPAD8: TForm_MoveTo(TheForm, dwpTop); VK_DOWN, VK_NUMPAD2: TForm_MoveTo(TheForm, dwpBottom); VK_PRIOR, VK_NUMPAD9: TForm_MoveTo(TheForm, dwpTopRight); VK_NEXT, VK_NUMPAD3: TForm_MoveTo(TheForm, dwpBottomRight); VK_HOME, VK_NUMPAD7: TForm_MoveTo(TheForm, dwpTopLeft); VK_END, VK_NUMPAD1: TForm_MoveTo(TheForm, dwpBottomLeft); else Exit; //==> exit, so Result doesn't get set to 1 end; _Msg.Result := 1; end; end; procedure TFormPositioningActivator.NewWindowProc(var _Msg: TMessage); begin if _Msg.Msg = CM_CHILDKEY then CmChildKey(_Msg); inherited NewWindowProc(_Msg); end; function TFormPositioningActivator.TheForm: TForm; begin Result := TForm(FCtrl); end; function TForm_ActivatePositioning(_Form: TForm; _Modifier: TShiftState = [ssCtrl, ssAlt]): TObject; begin Result := TFormPositioningActivator.Create(_Form, _Modifier); end;
It uses the existing TWindowProcHook class which is already used for drag&drop and autocomplete (wow, was that really two years ago?) so the actual code is quite simple, as you can see above.
I did not use the Windows key as modifier but rather Ctrl+Alt because Microsoft says we should not use the Windows key. But I’m not quite sure about this. Maybe I could instead check for the Windows version to see if those hotkeys are already available and only add those that aren’t, using the Windows key.
But for now, the following hotkeys are implemented:
- Ctrl+Alt+Up – snap window to the top of the monitor, or if already there, to the bottom of the monitor above it
- Ctrl+Alt+Down – same for the bottom of the monitor
- Ctrl+Alt+Left – same for the left of the monitor
- Ctrl+Alt+Right – same for the left of the monitor
- Ctrl+Alt+Home – snap the window to the upper left quadrant of the monitor
- Ctrl+Alt+End – same for the lower left quadrant
- Ctrl+Alt+PgUp – same for the upper right quadrant
- Ctrl+Alt+PgDown- same for the lower right quadrant
Those hotkeys also work for the keys on the numeric keypad, regardless whether NumLock is active or not. If you look at your keyboard you will notice why I choose these particular keys: The nicely correspond to the quadrants / halves of the monitor and should it easier to memorize them.