diff --git a/lcl/include/customsplitter.inc b/lcl/include/customsplitter.inc index 7dbbbbafa4..9e027f74ff 100644 --- a/lcl/include/customsplitter.inc +++ b/lcl/include/customsplitter.inc @@ -20,7 +20,6 @@ ToDo: - Fix changing Mouse cursor - Fix gtk look - - AutoSnap - ResizeStyle } @@ -56,9 +55,9 @@ procedure TCustomSplitter.StartSplitterMove(Restart: boolean; const MouseXY: TPoint); begin if (not Restart) and FSplitDragging then exit; - FSplitDragging:=true; + FSplitDragging := True; fSplitterStartMouseXY:=MouseXY; - fSplitterStartLeftTop:=Point(Left,Top); + fSplitterStartLeftTop:= Point(Left,Top); end; procedure TCustomSplitter.MouseDown(Button: TMouseButton; Shift: TShiftState; X, @@ -69,76 +68,303 @@ begin inherited MouseDown(Button, Shift, X, Y); // While resizing X, Y are not valid. Use absolute mouse position. GetCursorPos(MousePos); - StartSplitterMove(true,MousePos); + StartSplitterMove(True,MousePos); +end; + +function FindOppositeControl(Control: TControl): TControl; +var + i,count: Integer; + CurControl: TControl; + fRect: TRect; + fPoint: TPoint; + alignList: TAlignSet; +begin + Result := nil; + count := Control.Parent.ControlCount; + if count > 0 then begin + fRect := Control.BoundsRect; + case Control.Align of + alTop: begin + fPoint := fRect.BottomRight; + fPoint.y := fPoint.y+1; + Dec(fPoint.x); + alignList := [alTop,alRight,alClient]; + end; + alBottom: begin + fPoint := fRect.TopLeft; + Dec(fPoint.y); + alignList := [alLeft,alBottom,alClient]; + end; + alLeft: begin + fPoint := fRect.BottomRight; + Inc(fPoint.x); + Dec(fPoint.y); + alignList := [alLeft,alClient]; + end; + alRight: begin + fPoint := fRect.TopLeft; + Dec(fPoint.x); + alignList := [alRight,alClient]; + end; + end; // case + Dec(count); + for i := 0 to count do begin + CurControl := Control.Parent.Controls[i]; + if (CurControl <> Control) + and CurControl.Visible + and (CurControl.Align in alignList) + and (PtInRect(CurControl.BoundsRect, fPoint)) + then Result := CurControl; + if Assigned(Result) then Break; + end; // for i + end; +end; + +function FindVirtualOppositeControl(Control: TControl): TControl; + function CompPos(CurControl, Control: TControl): Boolean; + begin + Result := False; + case Control.Align of + alTop: + if (CurControl.Align = Control.Align) + and (CurControl.Top >= Control.BoundsRect.Bottom) + then Result := True; + alBottom: + if (CurControl.Align = Control.Align) + and (CurControl.BoundsRect.Bottom <= Control.Top) + then Result := True; + alLeft: + if (CurControl.Align = Control.Align) + and (CurControl.Left >= Control.BoundsRect.Right) + then Result := True; + alRight: + if (CurControl.Align = Control.Align) + and (CurControl.BoundsRect.Right <= Control.Left) + then Result := True; + end; + end; + function OppositeControl(CurControl,Control: TControl): Boolean; + begin + Result := False; + case Control.Align of + alLeft: if (CurControl.Align = alRight) then Result := True; + alRight: if (CurControl.Align = alLeft) then Result := True; + alTop: if (CurControl.Align = alBottom) then Result := True; + alBottom: if (CurControl.Align = alTop) then Result := True; + end; + end; +var + i,count: Integer; + CurControl: TControl; +begin + Result := nil; + count := Control.Parent.ControlCount; + if count > 0 then begin + Dec(count); + for i := 0 to count do begin + CurControl := Control.Parent.Controls[i]; + if (CurControl <> Control) then begin + if ((Result = nil) and OppositeControl(CurControl, Control)) + or (Assigned(Result) and CompPos(CurControl, Result)) + then Result := CurControl; + end; + end; // for i + end; end; procedure TCustomSplitter.MouseMove(Shift: TShiftState; X, Y: Integer); +var + AlignControl, LastControl, VirtualOppositeControl, ClientControl: TControl; + + function GetParentClientSize: Integer; + begin + case Self.Align of + alLeft, alRight: Result := Parent.ClientWidth; + alTop, alBottom: Result := Parent.ClientHeight; + end; + end; + function GetControlMinPos(Control: TControl): Integer; + begin + if Assigned(Control) + then case Self.Align of + alLeft,alRight: Result := Control.Left; + alTop,alBottom: Result := Control.Top; + end + else case Self.Align of + alLeft,alTop: Result := 0; + alRight,alBottom: Result := GetParentClientSize; + end; + end; + function GetControlSize(Control: TControl): Integer; + begin + Result := 0; + if Assigned(Control) + then case Self.Align of + alLeft, alRight: Result := Control.Width; + alTop, alBottom: Result := Control.Height; + end; + end; + function GetControlConstraintsMinSize(Control: TControl): Integer; + begin + case Self.Align of + alLeft, alRight: Result := Control.Constraints.MinWidth; + alTop, alBottom: Result := Control.Constraints.MinHeight; + end; + end; + procedure SetAlignControlSize(NewSize: Integer); + begin + case Self.Align of + alLeft: + AlignControl.Width := NewSize; + alRight: + begin + Parent.DisableAlign; + AlignControl.Left := AlignControl.Left + (AlignControl.Width - NewSize); + AlignControl.Width := NewSize; + Parent.EnableAlign; + end; + alTop: + AlignControl.Height := NewSize; + alBottom: + begin + Parent.DisableAlign; + AlignControl.Top := AlignControl.Top + (AlignControl.Height - NewSize); + AlignControl.Height := NewSize; + Parent.EnableAlign; + end; + end; + end; + function CalcNewSize(StartSize, EndSize, Offset: Integer): Integer; + var + NewSize: Integer; + begin + NewSize := GetControlSize(AlignControl); + case Self.Align of + alLeft, alTop: Inc(NewSize, Offset); + alRight, alBottom: Dec(NewSize, Offset); + end; + + if NewSize > EndSize then NewSize := EndSize; + if NewSize < StartSize then NewSize := StartSize; + + if fAutoSnap and (NewSize < fMinSize) + then NewSize := StartSize; + Result := NewSize; + end; + function GetVirtualControlMinPos(Control: TControl): Integer; + begin + case Self.Align of + alLeft: Result := Control.Left; + alRight: Result := Control.BoundsRect.Right; + alTop: Result := Control.Top; + alBottom: Result := Control.BoundsRect.Bottom; + end; + end; + function CalcOffset(X,Y: Integer): Integer; + begin + case Self.Align of + alLeft, alRight: + Result := (X-fSplitterStartMouseXY.X) + - (Self.Left-fSplitterStartLeftTop.X); + alTop, alBottom: + Result := (Y-fSplitterStartMouseXY.Y) + - (Self.Top-fSplitterStartLeftTop.Y); + end; + end; + function FindClientControl: TControl; + var + CurControl: TControl; + count: Integer; + begin + Result := nil; + for count := Parent.ControlCount-1 downto 0 do begin + CurControl := Parent.Controls[count]; + if (CurControl <> Self) + and ((CurControl.Align = alClient) + or ((Self.Align in [alTop,alBottom]) + and (CurControl.Align in [alLeft,alRight]))) then begin + Result := CurControl; + Break; + end; + end; // for count + end; // function FindClientControl + function FindLastControl(Control: TControl): TControl; + var + CurControl: TControl; + begin + CurControl := Control; + while Assigned(CurControl) do begin + Control := CurControl; + CurControl := FindOppositeControl(Control); + end; + Result := Control; + end; // function FindLastControl var Offset: Integer; - MinLeft: Integer; - MaxLeft: Integer; - NewLeft: Integer; - AlignControl: TControl; - MinTop: Integer; - MaxTop: Integer; - NewTop: Integer; + StartSize: Integer; + EndSize: Integer; + NewSize: Integer; MousePos: TPoint; begin inherited MouseMove(Shift, X, Y); - if (ssLeft in Shift) and (Parent<>nil) then begin + if not (Self.Align in [alLeft, alRight, alTop, alBottom]) then Exit; + if (ssLeft in Shift) and (Parent <> nil) then begin + AlignControl := FindAlignControl; + if not Assigned(AlignControl) then Exit; + // While resizing X, Y are not valid. Use absolute mouse position. GetCursorPos(MousePos); - StartSplitterMove(false,MousePos); - if Align in [alLeft,alRight] then begin - Offset:=(MousePos.X-fSplitterStartMouseXY.X) - -(Left-fSplitterStartLeftTop.X); - if Offset=0 then exit; - MinLeft:=0; - MaxLeft:=Parent.ClientWidth-Width; - AlignControl:=FindAlignControl; - if AlignControl<>nil then - if Align=alLeft then - MinLeft:=AlignControl.Left+MinSize - else - MaxLeft:=Parent.ClientWidth-Width-MinSize; - NewLeft:=Left+Offset; - if NewLeft>MaxLeft then NewLeft:=MaxLeft; - if NewLeftnil then begin - if Align=alLeft then - AlignControl.Width:=NewLeft-AlignControl.Left - else - AlignControl.Left:=NewLeft+Width; - end else - Left:=NewLeft; - end - else if Align in [alTop, alBottom] then begin - Offset:=(MousePos.Y-fSplitterStartMouseXY.Y) - -(Top-fSplitterStartLeftTop.Y); - if Offset=0 then exit; - MinTop:=0; - MaxTop:=Parent.ClientHeight-Height; - AlignControl:=FindAlignControl; - if AlignControl<>nil then - if Align=alTop then - MinTop:=AlignControl.Top+MinSize - else - MaxTop:=Parent.ClientHeight-Height-MinSize; - NewTop:=Top+Offset; - if NewTop>MaxTop then NewTop:=MaxTop; - if NewTopnil then begin - if Align=alTop then - AlignControl.Height:=NewTop-AlignControl.Top - else - AlignControl.Top:=NewTop+Height; - end else - Top:=NewTop; + StartSplitterMove(False,MousePos); + + Offset := CalcOffset(MousePos.X, MousePos.Y); + if Offset = 0 then Exit; + + ClientControl := FindClientControl; + + StartSize := 1; + if not fAutoSnap + then Inc(StartSize, Max(fMinSize, GetControlConstraintsMinSize(AlignControl))); + if StartSize > 1 then Dec(StartSize); + + if Assigned(ClientControl) + then EndSize := GetControlSize(AlignControl) + + GetControlSize(ClientControl) + - fMinSize + else begin + VirtualOppositeControl := FindVirtualOppositeControl(Self); + LastControl := FindLastControl(Self); + case Self.Align of + alLeft, alTop: begin + if Assigned(VirtualOppositeControl) then + begin + EndSize := GetControlSize(AlignControl) + + (GetControlMinPos(VirtualOppositeControl) + - (GetControlMinPos(LastControl) + + GetControlSize(LastControl))); + end + else EndSize := GetControlSize(AlignControl) + + (GetParentClientSize + - GetControlMinPos(LastControl) + - GetControlSize(LastControl)) + end; + + alRight, alBottom: begin + if Assigned(VirtualOppositeControl) then + EndSize := GetControlSize(AlignControl) + + (GetControlMinPos(LastControl) + - (GetControlMinPos(VirtualOppositeControl) + + GetControlSize(VirtualOppositeControl))) + else EndSize := GetControlSize(AlignControl) + + GetControlMinPos(LastControl); + end; + end; // case Self.Align end; + + NewSize := CalcNewSize(StartSize, EndSize, Offset); + + // OnCanResize event + if CheckNewSize(NewSize) + then SetAlignControlSize(NewSize); end; end; @@ -146,9 +372,10 @@ procedure TCustomSplitter.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin inherited MouseUp(Button, Shift, X, Y); + if not (Self.Align in [alLeft,alTop,alRight,alBottom]) then Exit; if FSplitDragging then begin if Assigned(OnMoved) then OnMoved(Self); - FSplitDragging:=false; + FSplitDragging := False; end; end; @@ -157,26 +384,33 @@ var i: Integer; CurControl: TControl; begin - Result:=nil; - if (Parent=nil) then exit; - for i:=Parent.ControlCount-1 downto 0 do begin - CurControl:=Parent.Controls[i]; - if (CurControl.Align in ([alClient,Align])) - and (CurControl.Visible) - and (((Align=alLeft) and (CurControl.LeftLeft)) - or ((Align=alBottom) and (CurControl.Top>Top))) + Result := nil; + if (Parent = nil) then Exit; + for i := Parent.ControlCount-1 downto 0 do begin + CurControl := Parent.Controls[i]; + if (CurControl <> Self) + and (CurControl.Visible) + and (CurControl.Align = Self.Align) then begin - // candidate found - if (Result=nil) - or (((Align=alLeft) and (CurControl.Left>Result.Left)) - or ((Align=alTop) and (CurControl.Top>Result.Top)) - or ((Align=alRight) and (CurControl.Left