Snapping windows to monitor halves / quadrants revisited

In my last post I talked about snapping windows to monitor halves and quadrants. I have been using that code for a few days and found it has a few shortcomings:

  1. If a window has size constraints, these will still be respected (which is good) but this will result in the window not being moved correctly. Snapping to the left and top half works fine, but snapping to the right or bottom half will move part of the window outside the active monitor.
  2. If a window has size constraints, moving from the left half of the right hand side monitor to the right half of left hand side monitor will not work. the same applies from the top half of the bottom monitor to the bottom half of the top monitor.
  3. It’s a bit inconvenient to move a window from a quadrant of one monitor to a quadrant of a different monitor. E.g. sometimes I want to move a window from the top right quadrant of the right hand side monitor to the top right quadrant of the left hand side monitor and vice versa. In order to do that I have to press Ctrl+Alt+Left (moves it to the left half of that monitor) Ctrl+Alt+Left (moves it to the right half of the other monitor) Ctrl+Alt+PgUp (finally moves it to the top right quadrant of that monitor). It would be nice to accomplish this
    1. with less key strokes
    2. without resizing the window

    Currently I am leaning towards Ctrl+Alt+PgUp moving the window to the top right quadrant of the same monitor and pressing it again moving it to the same quadrant of the monitor to the right of that monitor, or if that doesn’t exist, to the monitor above. But I’m not yet sure about the order of monitors here. If you want to voice your opinion, use my corresponding Google+ post

The fix for the constraints issues isn’t that difficult: Just read the form’s constraints and adjust the position accordingly. Here is the new code:

procedure TForm_MoveTo(_frm: TCustomForm; _Position: TdzWindowPositions);

  procedure ToTop(var _Re: TRect; _MinHeight, _MaxHeight: Integer);
  begin
    _Re.Bottom := _Re.Top + TRect_Height(_Re) div 2;
    if TRect_Height(_Re) < _MinHeight then
      _Re.Bottom := _Re.Top + _MinHeight;
    if (_MaxHeight > 0) and (TRect_Height(_Re) > _MaxHeight) then
      _Re.Bottom := _Re.Top + _MaxHeight;
  end;

  procedure ToBottom(var _Re: TRect; _MinHeight, _MaxHeight: Integer);
  begin
    _Re.Top := _Re.Top + TRect_Height(_Re) div 2;
    if TRect_Height(_Re) < _MinHeight then
      _Re.Top := _Re.Bottom - _MinHeight;
    if (_MaxHeight > 0) and (TRect_Height(_Re) > _MaxHeight) then
      _Re.Top := _Re.Bottom - _MaxHeight;
  end;

  procedure ToLeft(var _Re: TRect; _MinWidth, _MaxWidth: Integer);
  begin
    _Re.Right := _Re.Left + TRect_Width(_Re) div 2;
    if TRect_Width(_Re) < _MinWidth then
      _Re.Right := _Re.Left + _MinWidth;
    if (_MaxWidth > 0) and (TRect_Width(_Re) > _MaxWidth) then
      _Re.Right := _Re.Left + _MaxWidth;
  end;

  procedure ToRight(var _Re: TRect; _MinWidth, _MaxWidth: Integer);
  begin
    _Re.Left := _Re.Left + TRect_Width(_Re) div 2;
    if TRect_Width(_Re) < _MinWidth then
      _Re.Left := _Re.Right - _MinWidth;
    if (_MaxWidth > 0) and (TRect_Width(_Re) > _MaxWidth) then
      _Re.Left := _Re.Right - _MaxWidth;
  end;

  function SamePoint(const _pnt1, _pnt2: TPoint): Boolean;
  begin
    Result := (_pnt1.X = _pnt2.X) and (_pnt1.Y = _pnt2.Y);
  end;

  function SameRect(const _re1, _re2: TRect): Boolean;
  begin
    Result := SamePoint(_re1.TopLeft, _re2.TopLeft) and SamePoint(_re1.BottomRight, _re2.BottomRight);
  end;

var
  re: TRect;
  Bounds: TRect;
  NewMonitor: TMonitor;
  Constraints: TSizeConstraints;
begin
  re := _frm.Monitor.WorkareaRect;
  Bounds := _frm.BoundsRect;
  Constraints := _frm.Constraints;
  case _Position of
    dwpTop: begin
        ToTop(re, Constraints.MinHeight, Constraints.MaxHeight);
        if SameRect(re, Bounds) then begin
          NewMonitor := MonitorFromPoint(Point((re.Left + re.Right) div 2, re.Top - TRect_Height(re) div 2));
          if Assigned(NewMonitor) then begin
            re := NewMonitor.WorkareaRect;
            ToBottom(re, Constraints.MinHeight, Constraints.MaxHeight);
          end;
        end;
      end;
    dwpBottom: begin
        ToBottom(re, Constraints.MinHeight, Constraints.MaxHeight);
        if SameRect(re, Bounds) then begin
          NewMonitor := MonitorFromPoint(Point((re.Left + re.Right) div 2, re.Bottom + TRect_Height(re) div 2));
          if Assigned(NewMonitor) then begin
            re := NewMonitor.WorkareaRect;
            ToTop(re, Constraints.MinHeight, Constraints.MaxHeight);
          end;
        end;
      end;
    dwpLeft: begin
        ToLeft(re, Constraints.MinWidth, Constraints.MaxWidth);
        if SameRect(re, Bounds) then begin
          NewMonitor := MonitorFromPoint(Point(re.Left - TRect_Width(re) div 2, (re.Top + re.Bottom) div 2));
          if Assigned(NewMonitor) then begin
            re := NewMonitor.WorkareaRect;
            ToRight(re, Constraints.MinWidth, Constraints.MaxWidth);
          end;
        end;
      end;
    dwpRight: begin
        ToRight(re, Constraints.MinWidth, Constraints.MaxWidth);
        if SameRect(re, Bounds) then begin
          NewMonitor := MonitorFromPoint(Point(re.Right + TRect_Width(re) div 2, (re.Top + re.Bottom) div 2));
          if Assigned(NewMonitor) then begin
            re := NewMonitor.WorkareaRect;
            ToLeft(re, Constraints.MinWidth, Constraints.MaxWidth);
          end;
        end;
      end;
    dwpTopLeft: begin
        ToTop(re, Constraints.MinHeight, Constraints.MaxHeight);
        ToLeft(re, Constraints.MinWidth, Constraints.MaxWidth);
      end;
    dwpTopRight: begin
        ToTop(re, Constraints.MinHeight, Constraints.MaxHeight);
        ToRight(re, Constraints.MinWidth, Constraints.MaxWidth);
      end;
    dwpBottomLeft: begin
        ToBottom(re, Constraints.MinHeight, Constraints.MaxHeight);
        ToLeft(re, Constraints.MinWidth, Constraints.MaxWidth);
      end;
    dwpBottomRight: begin
        ToBottom(re, Constraints.MinHeight, Constraints.MaxHeight);
        ToRight(re, Constraints.MinWidth, Constraints.MaxWidth);
      end;
  end;
  _frm.BoundsRect := re;
end;

It’s already in the dzlib repository on sourceforge, unit u_dzVclUtils.