Hello,
this is my first patch. I hope i have made it in the correct way.
This patch fixes two bugs of the TScrollBox.
1) If AutoScroll is set to False and a Range is assigned. The Position
increases after the ScrollBar reaches the end. This bug is also
descirbed in Bug # 0001986.
2) The Position of the ScrollBar changes if the Window was resized. (I
think this happens only if AutoScroll was set to False)
- To fix the Bug i had to put some {$IFDEF VerboseScrollingWinControl}.
If you wish i coud delete this lines and create a new patch. But i think
this could usefull for further bug-fixing.
- I compared the size of the ScrollBox, Position of ScrollBar and so on
with a delphi project and worked out that there was a difference. To get
the same value i hat to substract 21 on a few places. I declared a
constant named "ScrollBarPageOffset" for that value.
I hope it can be included in lazarus.
Thank You.
Andy
Index: controlscrollbar.inc
===================================================================
--- controlscrollbar.inc (revision 11648)
+++ controlscrollbar.inc (working copy)
@@ -17,12 +17,31 @@
const
IntfBarKind: array[TScrollBarKind] of Integer = (SB_HORZ,SB_VERT);
+ // Offset that decrements the page of a Scrollbar (to be delphi-compatible)
+ ScrollBarPageOffset = 21;
+
+{$IFDEF VerboseScrollingWinControl}
+procedure TControlScrollBar.VerboseScrollingWinControl(aText : String);
+begin
+ if (Kind=sbHorizontal) and
+ (FControl.Tag = 1) then
+ begin
+ DebugLn([aText]);
+ DebugLn(['FAutoRange ',FAutoRange,', FPosition ',FPosition,', FPage',FPage,
+ 'FVisible ',FVisible]);
+ end;
+end;
+{$ENDIF}
procedure TControlScrollBar.SetPosition(const Value: Integer);
var
OldPosition: Integer;
+ MaxValue : Integer;
begin
if Value < 0 then begin
+ {$IFDEF VerboseScrollingWinControl}
+ VerboseScrollingWinControl('TControlScrollBar.SetPosition [Value<0]');
+ {$ENDIF}
SetPosition(0);
exit;
end;
@@ -30,32 +49,38 @@
If ControlAutoScroll then begin
if FAutoRange < 0 then
AutoCalcRange;
+ end;
- if Value > FAutoRange then begin
- {$IFDEF VerboseScrollingWinControl}
- if Kind=sbHorizontal then
- DebugLn(['TControlScrollBar.SetPosition FAutoRange Value=',Value,' >
AutoRange=',FAutoRange]);
- {$ENDIF}
- SetPosition(FAutoRange);
- exit;
- end;
+ // Calculate the highest Position that the ScrollBarButton (Page) can reach.
+ // Exsample:
+ // <-----PPPP>
+ // 123456789 => Range 0..9 = 10
+ // 1234 => Page = 4
+ // The Range is 9, but we can only scroll to position 6, thats the reason
+ // why we have to calculate "Range - Page" to get the max position.
+ // The ScrollBarPageOffset is only for Delphi-compatiblity. This Constant
+ // is used on other places too.
+ MaxValue := Range - Page - ScrollBarPageOffset;
+
+ if (Value > MaxValue) and
+ (Value > 0) then begin
+ {$IFDEF VerboseScrollingWinControl}
+ VerboseScrollingWinControl('TControlScrollBar.SetPosition '+
+ '[Value>MaxValue]; Value: '+IntToStr(Value));
+ {$ENDIF}
+ SetPosition(MaxValue);
+ exit;
end;
- if Value>Range then begin
+ if Value=FPosition then
+ begin
{$IFDEF VerboseScrollingWinControl}
- if Kind=sbHorizontal then
- DebugLn(['TControlScrollBar.SetPosition Range Value=',Value,' >
Range=',Range]);
+ VerboseScrollingWinControl('TControlScrollBar.SetPosition '+
+ '[Value=FPosition]');
{$ENDIF}
- SetPosition(Range);
exit;
end;
- {$IFDEF VerboseScrollingWinControl}
- if Kind=sbHorizontal then
- DebugLn(['TControlScrollBar.SetPosition Value=',Value,'
FPosition=',FPosition]);
- {$ENDIF}
- if Value=FPosition then exit;
-
// scroll content of FControl
OldPosition:=FPosition;
FPosition := Value;
@@ -67,8 +92,8 @@
and (GetScrollPos(ControlHandle, IntfBarKind[Kind]) <> FPosition) then begin
InvalidateScollInfo;
{$IFDEF VerboseScrollingWinControl}
- if Kind=sbHorizontal then
- DebugLn(['TControlScrollBar.SetPosition FPosition=',FPosition]);
+ VerboseScrollingWinControl('TControlScrollBar.SetPosition '+
+ '[SetScrollPos]');
{$ENDIF}
SetScrollPos(ControlHandle, IntfBarKind[Kind], FPosition, Visible);
end;
@@ -176,8 +201,7 @@
if FRange=Value then exit;
FRange := Value;
{$IFDEF VerboseScrollingWinControl}
- if Kind=sbHorizontal then
- DebugLn(['TControlScrollBar.SetRange ',Self,' fRange=',FRange]);
+ VerboseScrollingWinControl('TControlScrollBar.SetRange');
{$ENDIF}
ControlUpdateScrollBars;
end;
@@ -230,7 +254,7 @@
if not C.IsControlVisible then Continue;
if (c.Align <> alLeft) and (c.Align <> alNone) then Continue;
{$IFDEF VerboseScrollingWinControl}
- DebugLn(['AutoCalcHRange ',DbgSName(c),' Left=',c.Left]);
+ VerboseScrollingWinControl('TControlScrollBar.AutoCalcHRange');
{$ENDIF}
TmpRange := Max(TmpRange, c.Left + c.Width);
end;
@@ -251,27 +275,30 @@
var
ScrollInfo: TScrollInfo;
begin
+ {$IFDEF VerboseScrollingWinControl}
+ VerboseScrollingWinControl('TControlScrollBar.UpdateScrollBar');
+ {$ENDIF}
if HandleAllocated
and (FControl is TScrollingWinControl) then begin
FillChar(ScrollInfo,SizeOf(ScrollInfo),0);
ScrollInfo.cbSize := SizeOf(ScrollInfo);
ScrollInfo.fMask := SIF_ALL;
ScrollInfo.nMin := 0;
- ScrollInfo.nMax := FRange;
+ ScrollInfo.nMax := FRange - ScrollBarPageOffset;
ScrollInfo.nPos := FPosition;
ScrollInfo.nPage := FPage;
ScrollInfo.nTrackPos := FPosition;
if (not FOldScrollInfoValid)
or (not CompareMem(@ScrollInfo,@FOldScrollInfo,SizeOf(TScrollInfo))) then
begin
+ {$IFDEF VerboseScrollingWinControl}
+ VerboseScrollingWinControl('TControlScrollBar.UpdateScrollBar '+
+ '[SetScrollInfo]');
+ {$ENDIF}
FOldScrollInfo:=ScrollInfo;
FOldScrollInfoValid:=true;
SetScrollInfo(FControl.Handle, IntfBarKind[Kind], ScrollInfo, FVisible);
end;
- {$IFDEF VerboseScrollingWinControl}
- if Kind=sbHorizontal then
- DebugLn(['TControlScrollBar.UpdateScrollBar ',DbgSName(FControl),'
',DbgSName(Self),' FVisible=',FVisible,' Range=',FRange,'
FPosition=',FPosition,' FPage=',FPage,' FAutoRange=',FAutoRange]);
- {$ENDIF}
end;
SetPosition(FPosition);
@@ -324,7 +351,10 @@
Exit;
end;
{$IFDEF VerboseScrollingWinControl}
- DebugLn(['TControlScrollBar.ScrollHandler
Message.ScrollCode=',Message.ScrollCode,' FPosition=',FPosition,'
NewPos=',NewPos,' Range=',Range]);
+ VerboseScrollingWinControl('TControlScrollBar.ScrollHandler '+
+ '[Message.ScrollCode='+
+ IntToStr(Message.ScrollCode)+'; '+
+ 'NewPos='+InttoStr(NewPos)+']');
{$ENDIF}
if NewPos < 0 then NewPos := 0;
if NewPos > Range then NewPos := Range;
Index: scrollingwincontrol.inc
===================================================================
--- scrollingwincontrol.inc (revision 11648)
+++ scrollingwincontrol.inc (working copy)
@@ -76,12 +76,14 @@
// true if something changed
// update Page, AutoRange, Visible
- procedure UpdateRange(p_Bar: TControlScrollBar);
+ // returns true, if Range was changed
+ function UpdateRange(p_Bar: TControlScrollBar) : Boolean;
var
SBSize: Longint;
OtherScrollbar: TControlScrollBar;
OldAutoRange: LongInt;
begin
+ result := false;
OldAutoRange:=p_Bar.FAutoRange;
p_Bar.FAutoRange := 0;
OtherScrollbar := p_Bar.GetOtherScrollBar;
@@ -90,62 +92,80 @@
else
SBSize := 0;
if p_Bar.Kind = sbVertical then
- SBSize := ClientHeight - SBSize
+ SBSize := ClientHeight
else
- SBSize := ClientWidth - SBSize;
+ SBSize := ClientWidth;
if (p_Bar.fRange>SBSize) and (SBSize>0) then
- p_Bar.FAutoRange := (p_Bar.FRange - SBSize)
+ p_Bar.FAutoRange := p_Bar.FRange
else
p_Bar.FAutoRange := 0;
{$IFDEF VerboseScrollingWinControl}
- if p_Bar.Kind = sbHorizontal then
- DebugLn(['UpdateRange p_Bar.fRange=',p_Bar.fRange,' SBSize=',SBSize,'
ClientWidth=',ClientWidth,' FAutoRange=',p_Bar.FAutoRange]);
+ p_Bar.VerboseScrollingWinControl('TScrollingWinControl.UpdateRange '+
+ '[SBSize='+InttoStr(SBSize)+', '+
+ 'ClientWidth='+IntToStr(ClientWidth)+']');
{$ENDIF}
- if OldAutoRange<>p_Bar.FAutoRange then
- Result:=true;
+
+ result := (OldAutoRange<>p_Bar.FAutoRange);
end;
- procedure UpdateVisible(p_Bar: TControlScrollBar);
+ // returns true, if visibility was changed
+ function UpdateVisible(p_Bar: TControlScrollBar) : Boolean;
var
CurMax: Integer;
OldVisible: Boolean;
begin
OldVisible:=p_Bar.FVisible;
if p_Bar.Kind = sbVertical then
- CurMax := Height
+ CurMax := ClientHeight
else
- CurMax := Width;
+ CurMax := ClientWidth;
if (p_Bar.FVisible and not FAutoScroll)
or (FAutoScroll and (p_Bar.FRange > 0) and (p_Bar.FRange > CurMax))
then
p_Bar.FVisible := True
else
p_Bar.FVisible := False;
- if OldVisible<>p_Bar.FVisible then
+ {$IFDEF VerboseScrollingWinControl}
+ p_Bar.VerboseScrollingWinControl('TScrollingWinControl.UpdateVisible');
+ {$ENDIF}
+
+ result := (OldVisible<>p_Bar.FVisible);
+ end;
+
+ // The ClientWidth/ClientHeight includes the Position that is already
+ // moved to right/bottom. So we have to substract the Position from it.
+ function UpdatePage(p_Bar: TControlScrollBar; ClientHW : Integer) : Boolean;
+ var
+ NewPage: Integer;
+ begin
+ NewPage := ClientHW-ScrollBarPageOffset-p_Bar.Position;
+ NewPage := Max(1,Min(NewPage,High(p_Bar.FPage)));
+ if NewPage<>p_Bar.FPage then begin
+ p_Bar.FPage := NewPage;
Result:=true;
+ end;
end;
-var
- NewPage: Integer;
begin
Result:=false;
- // page
- NewPage:=Max(1,Min(ClientWidth -1, High(HorzScrollbar.FPage)));
- if NewPage<>HorzScrollbar.FPage then begin
- HorzScrollbar.FPage := NewPage;
- Result:=true;
- end;
- NewPage := Max(1,Min(ClientHeight -1, High(VertScrollbar.FPage)));
- if NewPage<>VertScrollbar.FPage then begin
- VertScrollbar.FPage := NewPage;
- Result:=true;
- end;
// range
- UpdateRange(HorzScrollbar);
- UpdateRange(VertScrollbar);
+ if UpdateRange(HorzScrollbar) or
+ UpdateRange(VertScrollbar)
+ then result := true;
// visible
- UpdateVisible(HorzScrollbar);
- UpdateVisible(VertScrollbar);
+ if UpdateVisible(HorzScrollbar) or
+ UpdateVisible(VertScrollbar) then begin
+ result := true;
+ // If a ScrollBar gets visible or unvisible the size of the clientarea
+ // changes. Thats the Reason why we must Update the Range a second time.
+ UpdateRange(HorzScrollbar);
+ UpdateRange(VertScrollbar);
+ end;
+ // page - Calculated after we know wether the Scrollbars are visible or not.
+ // (The Client-Widht/Heigt depends on that)
+ if UpdatePage(HorzScrollBar,ClientWidth) or
+ UpdatePage(VertScrollBar,ClientHeight) then
+ Result := true;
end;
procedure TScrollingWinControl.UpdateScrollbars;