Docking windows (overview of From.ScreenSnap included)

  Здравейте :) Днес ще Ви покажа как можете да накарате вашия прозорец да се прикача към краищата на работната площ, когато достигне дадено разстояние. Наблюдавах тази функция на доста популярния Winamp и реших, че ще е полезно да го разработя, за да го ползвам в някои мои програмки.

  Известно време след написването на тази статия се появи Delphi 7 и вниманието ми веднага се насочи към новата възможност за snap на TForm. Погледнах кода на Borland - 10 реда. По това време моят код беше към 100 реда и естествено веднага се зарибих да оправя нещата. Резултата не закъсня :)

  TfmMain = class(TForm)
    ...
  private
    pMoved: TPoint;
  protected
    procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
    procedure WMMoving(var Msg: TWMMoving); message WM_MOVING;
  end;

var
  fmMain: TfmMain;

implementation

procedure TfmMain.WMSysCommand(var Msg: TWMSysCommand);
const
  SC_DRAGMOVE = $F012;
begin
  if (Msg.CmdType = SC_MOVE) or (Msg.CmdType = SC_DRAGMOVE) then
  begin //a new move was started
    pMoved.X := 0;
    pMoved.Y := 0;
  end;

  inherited;
end;

procedure TfmMain.WMMoving(var Msg: TWMMoving);

  procedure BorderDock(const ABorder: Integer; var APosition, AChangedPosition: Integer);
  begin
    if Abs(ABorder - APosition) < iDockDistance then
    begin //the position has to change
      if Abs(AChangedPosition) > iDockDistance then
      begin //we need undocking
        APosition := APosition + AChangedPosition; //realize position
        AChangedPosition := 0; //nil the moved variable
      end
        else begin //we need to dock the window
          AChangedPosition := AChangedPosition + APosition - ABorder; //add new change to the old
          APosition := ABorder; //set new position
        end;
    end;
  end;

begin
  if bHasToDock then
  begin
    with Screen.WorkAreaRect do
    begin //do dock for all four borders
      BorderDock(Left, Msg.DragRect^.Left, pMoved.X);
      BorderDock(Top, Msg.DragRect^.Top, pMoved.Y);
      BorderDock(Right - Width, Msg.DragRect^.Left, pMoved.X);
      BorderDock(Bottom - Height, Msg.DragRect^.Top, pMoved.Y);
    end;

    //realize drag rectangle dimensions
    Msg.DragRect^.Right := Msg.DragRect^.Left + Width;
    Msg.DragRect^.Bottom := Msg.DragRect^.Top + Height;
  end;

  inherited;
end;

  Моя код отново е по-дълъг, но това се дължи на факта, че използвам друг подход: прихващам съобщението WM_MOVING, което налага и съхранението на промяната в координатите. Забелязах, че при Borland не се използва такава глобална промелива, което е добре (това е така заради закачането към WM_WINDOWPOSCHANGING). Лошото на техния код е, че ако е изключен "Show window contents while dragging" (в Windows), няма да се прикача рамката за индикация на движението (която се използва), тъй като реалното преместване на прозореца става след одобряване на промяната на позицията. Затова и съм избрал WM_MOVING. Той винаги работи :)

  Докато разглеждах кода на Forms.pas видях, че има и друг проблем:
    HandleEdge(x, Monitor.WorkareaRect.Left, Monitor.WorkareaRect.Left);
    HandleEdge(y, Monitor.WorkareaRect.Top, Monitor.WorkareaRect.Top);

  Като се вземе предвид, че последните два парамеръра се вадят един от друг, то винаги резултата ще бъде равен на нула. Пример: ако TaskBar (или някоя друга лента) е прикачен вляво или горе. Е, това си е направо бъг. Може би са се объркали с добавянето на третите параметри в тези два случая. Ако се изпуснат ще се използва стойността, която е точно колкото трябва SnapDistance = 0 (за ляв и горен ръб на формата).

  ПП В случай, че Ви трябва само главната форма да може да се прикача може да ползвате кода, отделен в собствен модул. Ето ви сорса на моето предложение DockManU.pas , а информация за употребата му ще намерите вътре.

  ПП2 Можете да си свалите и компонента TADocker (прикачане на формата, на която е поставен) от страницата с компоненти на моя сайт. Адреса е :
    http://www.kamburov.net/comps.html

 

  Ако по някаква случайност съм изпуснал нещо или сте намерили грешка, Ви моля да отделите една минутка и да драснете някой ред. Надявам се да ползвате моя код, защото смятам, че е добре написан и че съм обърнал внимание на детайлите. Няма смисъл да се създава едно и също нещо два пъти...  

Copyright © 2002-2003 Wise Guy
wise_guybg at yahoo.com
Created: 18.08.2002

Last revised: 31.01.2006