?? magnetunit.pas
字號:
begin
aTop := aRect.Top;
Result := True;
end;
// bottom edge
if (aTop + aHeight > aRect.Bottom - Range) then
begin
aTop := aRect.Bottom - aHeight;
Result := True;
end;
end
else
if (aBorder in [sbOuter, sbNear]) then
begin
RangeRect := GrowRect(aRect, Range);
IntersectRect(ISect, RangeRect, Rect(aLeft, aTop, aLeft + aWidth, aTop +
aHeight));
if IsRectEmpty(ISect) then Exit;
// if sbOuter is specified the larger part of the rectangle must be outside,
if (aBorder = sbOuter) then
begin
IntersectRect(ISect, aRect, Rect(aLeft, aTop, aLeft + aWidth, aTop +
aHeight));
if (RectArea(ISect) >=
RectArea(Rect(aLeft, aTop, aLeft + aWidth, aTop + aHeight)) div 2) then
Exit;
end;
// left to the right border
if (Abs(aLeft - aRect.Right) < Range) then
begin
aLeft := aRect.Right;
Result := True;
end;
// left on the left border
if (Abs(aLeft - aRect.Left) < Range) then
begin
aLeft := aRect.Left;
Result := True;
end;
// right to the left border
if (Abs(aLeft + aWidth - aRect.Left) < Range) then
begin
aLeft := aRect.Left - aWidth;
Result := True;
end;
// right on the right border
if (Abs(aLeft + aWidth - aRect.Right) < Range) then
begin
aLeft := aRect.Right - aWidth;
Result := True;
end;
// top to the bottom border
if (Abs(aTop - aRect.Bottom) < Range) then
begin
aTop := aRect.Bottom;
Result := True;
end;
// top to the top border
if (Abs(aTop - aRect.Top) < Range) then
begin
aTop := aRect.Top;
Result := True;
end;
// if bottom to the top border
if (Abs(ATop + aHeight - aRect.Top) < Range) then
begin
aTop := aRect.Top - aHeight;
Result := True;
end;
// if bottom to the bottom border
if (Abs(ATop + aHeight - aRect.Bottom) < Range) then
begin
aTop := aRect.Bottom - aHeight;
Result := True;
end;
end;
end;
procedure TMagnet.WindowPosChanging(var aPos: TPoint; const W, H: integer; Sizing: Boolean = False);
var
i, j: integer;
MainFormR: TRect;
Delta,
P: TPoint;
M: TMagnet;
begin
if (SnapOptions = []) then Exit;
// determine delta request
Delta.X := (aPos.X - Area.Left);
Delta.Y := (aPos.Y - Area.Top);
// detect any active magnet in the same group in the neighbourhood
if (soMagnet in SnapOptions) then
begin
i := ActiveMagnets.Count - 1;
while (i >= 0) do
begin
// only enable snap to active magnets not in this cluster
if (FCluster.IndexOf(ActiveMagnets[i]) < 0) and
(TMagnet(ActiveMagnets[i]).GroupIndex = GroupIndex) then
begin
if SnapToRect(aPos.x, aPos.y, W, H,
TMagnet(ActiveMagnets[i]).Area, sbOuter) then
begin
FSnapList.Add(ActiveMagnets[i]);
end;
end;
Dec(i);
end;
end;
// every magnet in the cluster has it's own snap and is to be considered
// if in the cluster
if (InCluster) and (Dragging) and (ClusterSnapping) then
begin
for i := 0 to FCluster.Count - 1 do
begin
M := TMagnet(Cluster[i]);
if (M <> Self) then
begin
// transform to M new position request
P.X := M.Area.Left + Delta.X;
P.Y := M.Area.Top + Delta.Y;
// allow M to snap from the virtual new position
M.WindowPosChanging(P, M.Area.Right-M.Area.Left, M.Area.Bottom-M.Area.Top);
// should merge snaplist with the most recent snaplist of M
for j:=0 to M.FSnapList.Count-1 do
if FSnapList.IndexOf(M.FSnapList[j]) < 0 then
FSnapList.Add(M.FSnapList[j]);
// transform virtual snapped M back to a suggested delta
P.X := P.X - M.Area.Left;
P.Y := P.Y - M.Area.Top;
// apply M's suggestion if it is different from the original delta
if ((P.X <> Delta.X) or (P.Y <> Delta.Y)) then
begin
// transform the adjusted delta to current coordinates
aPos.X := aPos.X - Delta.x + P.X;
aPos.Y := aPos.Y - Delta.y + P.Y;
end;
end;
end;
end;
// only snap to the mainform if this is not the mainform and it is active
// (if it's not active and it would be behind the mainform it would never
// reappear, even while moving the mainform aside)
if (soInMainForm in SnapOptions) and
(Dragging) then
begin
MainFormR := Application.MainForm.BoundsRect;
if RectOverlap(Area, MainFormR) then
begin
MainFormR := Classes.Rect(
Application.MainForm.ClientToScreen(
Application.MainForm.ClientRect.TopLeft),
Application.MainForm.ClientToScreen(
Application.MainForm.ClientRect.BottomRight));
end;
SnapToRect(aPos.x, aPos.y, W, H, MainFormR, sbNear);
end;
// determine screen snapping
// implemented multimonitor support
if (soInScreen in SnapOptions) then
begin
if Sizing then
begin
{$IFDEF CODESITE}
if (Delta.X = 0) and (Delta.Y = 0) and
((aPos.X > Area.Left) or (aPos.Y > Area.Top)) then
begin
Delta.X := (aPos.X - Area.Left);
Delta.Y := (aPos.Y - Area.Top);
CodeSite.SendPoint('BottomRight snapsize suggestion', Delta);
end
else
if (Delta.X > (aPos.X - Area.Left)) or (Delta.Y > (aPos.Y - Area.Top)) then
begin
Delta.X := (aPos.X - Area.Left);
Delta.Y := (aPos.Y - Area.Top);
CodeSite.SendPoint('TopLeft snapsize suggestion', Delta);
end;
{$ENDIF}
end;
// MultiMonitor support is simply handled by turning off the Inner and
// change the snap behaviour to Near.
// TO DO:
// To solve this properly the outer edge of multiple monitors should
// be considered.
if (Screen.MonitorCount = 1) then
SnapToRect(aPos.X, aPos.Y, W, H, Form.Monitor.WorkareaRect, sbInner)
else
SnapToRect(aPos.X, aPos.Y, W, H, Form.Monitor.WorkareaRect, sbNear);
(*
if (Sizing) and () then
begin
if ((aPos.Y = Area.Top) and (Delta.Y <> 0))
{$IFDEF CODESITE}
// Once snapped to the screen edge, the resizing doesn't snap anymore...
// this is because the final code in this routine takes the screen into
// consideration. However this should only be the case if moving is
// active. If sizing is active the screen should be checked first and not
// last...?
{$ENDIF}
end;
*)
end;
end;
procedure TMagnet.UnCluster;
var
j: integer;
OldNeighbour: TMagnet;
OldNeighbours: TList;
begin
{$IFDEF CODESITE} CodeSite.EnterMethod(Form.Name+' Uncluster'); {$ENDIF}
// remove this magnet from all others in the Cluster
for j := Cluster.Count - 1 downto 0 do
begin
if (Cluster[j] <> Self) then
TMagnet(Cluster[j]).RemoveFromCluster(Self);
end;
// reclusterize old neighbours which were in this cluster
OldNeighbours := TList.Create;
try
for j:=Cluster.Count-1 downto 0 do
if RectAligned(FOldArea, TMagnet(Cluster[j]).Area) then
OldNeighbours.Add(Cluster[j]);
while (OldNeighbours.Count > 0) do
begin
OldNeighbour := TMagnet(OldNeighbours[0]);
OldNeighbour.ReCluster(nil);
OldNeighbours.Extract(OldNeighbour);
// make sure other old neighbours which are now reclustered are no longer
// in the list to be reclustered again
for j:=0 to OldNeighbour.Cluster.Count-1 do
OldNeighbours.Extract(OldNeighbour.Cluster[j]);
end;
// empty the cluster list
for j:=Cluster.Count-1 downto 0 do
if Cluster[j] <> Self then
Cluster.Delete(j);
finally
OldNeighbours.Free;
end;
{$IFDEF CODESITE} CodeSite.ExitMethod(Form.Name+' Uncluster'); {$ENDIF}
end;
function TMagnet.Area: TRect;
begin
Result := Form.BoundsRect;
end;
procedure TMagnet.SetEnableClustering(const Value: Boolean);
begin
if (EnableClustering <> Value) then
begin
if not (Value) and InCluster then
UnCluster;
FEnableClustering := Value;
end;
end;
procedure TMagnet.RemoveFromCluster(aMagnet: TMagnet);
begin
{$IFDEF CODESITE}
CodeSite.SendMsg(Form.Name+' removes '+aMagnet.Form.Name+' from clusterlist');
{$ENDIF}
Cluster.Extract(aMagnet);
end;
procedure TMagnet.SetAutoSnap(const Value: boolean);
begin
FAutoSnap := Value;
end;
procedure TMagnet.SetClusterSnapping(const Value: Boolean);
begin
FClusterSnapping := Value;
end;
function TMagnet.Center: TPoint;
begin
Result.X := Area.Left + (Area.Right - Area.Left) div 2;
Result.Y := Area.Top + (Area.Bottom - Area.Top) div 2;
end;
procedure TMagnet.ClusterSnapList;
var
i, j: integer;
Candidate: TMagnet;
begin
try
if (FSnapList.Count > 0) and
(EnableClustering) then
begin
// The snaplist contains all magnets which the current magnet or cluster
// snapped into. Every candidate in this snaplist should be considered to
// become part of the cluster.
// For this the candidate must be aligned to the already existing cluster,
// it must share the same ClusterIndex and must have ClusterSnapping
// enabled.
for j := 0 to FSnapList.Count - 1 do
begin
Candidate := TMagnet(FSnapList[j]);
if (Candidate.ClusterIndex = ClusterIndex) and
(Candidate.ClusterSnapping) then
for i:=0 to Cluster.Count-1 do
if (RectAligned(TMagnet(Cluster[i]).Area, Candidate.Area)) then
AppendCluster(Candidate.Cluster);
end;
// distribute the newly assembled Cluster to all magnets involved
for j := 0 to Cluster.Count - 1 do
TMagnet(Cluster[j]).AppendCluster(Cluster);
end;
finally
FSnapList.Clear;
end;
end;
procedure TMagnet.ReCluster(NewCluster: TList);
var
i, j: integer;
NewC: TList;
begin
{$IFDEF CODESITE}
CodeSite.EnterMethod(Form.Name+' ReClustering');
{$ENDIF}
// assign the new clusterlist, of none is assigned this magnet is the initiator
// for the new cluster to be build.
NewC := NewCluster;
try
if (NewC = nil) then
NewC := TList.Create;
// since we're asked to recluster we should be neighbour and be added
NewC.Add(Self);
// ask all magnets from the old cluster which is not yet in the new cluster
// to check for its neighbours.
for j:=0 to Cluster.Count-1 do
begin
if (NewC.IndexOf(Cluster[j]) = -1) and
(RectAligned(Area, TMagnet(Cluster[j]).Area)) then
TMagnet(Cluster[j]).ReCluster(NewC);
end;
// if this is the initiator for the new cluster broadcast the changes
if (NewCluster = nil) then
begin
// extract all magnets in the new list from the old cluster
for j:=Cluster.Count-1 downto 0 do
for i:=0 to NewC.Count-1 do
TMagnet(Cluster[j]).RemoveFromCluster(TMagnet(NewC[i]));
// publish the new clusterlist to all magnets in this new list (incl self)
for j:=0 to NewC.Count-1 do
TMagnet(NewC[j]).Cluster.Assign(NewC);
end;
finally
if (NewC <> NewCluster) then
begin
{$IFDEF CODESITE}
CodeSite.SendInteger(Form.Name+' clustersize', NewC.Count);
{$ENDIF}
NewC.Free;
end;
end;
{$IFDEF CODESITE}
CodeSite.ExitMethod(Form.Name+' ReClustering');
{$ENDIF}
end;
procedure TMagnet.WindowSizeChanging(var aRect: TRect);
var
P: TPoint;
begin
// determine potential move snap and apply this to the width and height
P := aRect.TopLeft;
WindowPosChanging(
P,
aRect.Right-aRect.Left,
aRect.Bottom-aRect.Top, True);
// snapping is translated to the left position or the width
if (aRect.Left <> FDragStart.Left) then
aRect.Left := P.X
else
if (aRect.Right <> FDragStart.Right) then
aRect.Right := aRect.Right + (P.X - aRect.Left);
// snapping is translated to the top position or the height
if (aRect.Top <> FDragStart.Top) then
aRect.Top := P.Y
else
if (aRect.Bottom <> FDragStart.Bottom) then
aRect.Bottom := aRect.Bottom + (P.Y - aRect.Top);
// reclustering is performed
if InCluster and Dragging then
UnCluster;
end;
procedure TMagnet.AdjustCluster(Delta: TPoint);
var
i: integer;
begin
// if this is the main magnet being dragged it should drag other InCluster magnets along
if Dragging then
begin
for i := 0 to Cluster.Count - 1 do
if (Cluster[i] <> Self) then
begin
TMagnet(Cluster[i]).ApplyDeltaPos(Delta);
end;
end;
end;
procedure TMagnet.SetImmediateCluster(const Value: boolean);
begin
FImmediateCluster := Value;
end;
procedure TMagnet.SetGroupIndex(const Value: integer);
begin
FGroupIndex := Value;
end;
procedure TMagnet.SetClusterIndex(const Value: integer);
begin
if (FClusterIndex <> Value) then
begin
if InCluster then
UnCluster;
FClusterIndex := Value;
end;
end;
initialization
ActiveMagnets := TList.Create;
AllMagnets := TList.Create;
finalization
AllMagnets.Free;
ActiveMagnets.Free;
end.
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -