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;

Reply via email to