View Issue Details

IDProjectCategoryView StatusLast Update
0018912LazarusPatchpublic2011-12-01 11:26
ReporterVladimir ZhirovAssigned ToPaul Ishenin 
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
Product Version0.9.31 (SVN)Product Build 
Target VersionFixed in Version0.9.31 (SVN) 
Summary0018912: TListBox horizontal scrolling on Win32
DescriptionThe attached patch adds horizontal scrolling to TListBox on Win32 widgetset.

On Delphi it could be done using ScrollWidth property that is missing in LCL.

Additional code to setup horizontal scrollbar costs some performance on large lists, but IMHO the lack of horizontal scrolling makes TListBox component hardly usable if Win32 widgetset is one of the targets.
TagsNo tags attached.
Fixed in Revision30138
LazTarget0.99.0
WidgetsetWin32/Win64
Attached Files
  • listboxscroll.patch (4,033 bytes)
    Index: lcl/interfaces/win32/win32listslh.inc
    ===================================================================
    --- lcl/interfaces/win32/win32listslh.inc	(revision 29799)
    +++ lcl/interfaces/win32/win32listslh.inc	(working copy)
    @@ -36,6 +36,7 @@
         FSorted: Boolean;
         FSender: TWinControl;
         FlastInsertedIndex: Integer;
    +    FWidthIncrement: Integer;
       protected
         //Win32 Flags
         FFlagSort: Cardinal;
    @@ -66,7 +67,9 @@
         procedure SetSorted(Val: Boolean); Virtual;
         function SaveData(AIndex: Integer): Pointer; virtual;
         procedure RestoreData(AIndex: Integer; AData: Pointer); virtual;
    +    procedure UpdateListScrollWidth; virtual;
       public
    +    procedure Assign(Source: TPersistent); override;
         constructor Create(List : HWND; TheOwner: TWinControl);
         function Add(const S: string): Integer; override;
         procedure AddStrings(TheStrings: TStrings); override;
    Index: lcl/interfaces/win32/win32listsl.inc
    ===================================================================
    --- lcl/interfaces/win32/win32listsl.inc	(revision 29799)
    +++ lcl/interfaces/win32/win32listsl.inc	(working copy)
    @@ -50,6 +50,8 @@
       InitFlags;
       // Determine if the list is sorted
       FSorted := (UINT(GetWindowLong(FWin32List, GWL_STYLE)) and FFlagSort <> 0);
    +  // Get system metric needed by UpdateListScrollWidth to correctly update width
    +  FWidthIncrement := GetSystemMetrics(SM_CXFRAME);
     end;
     
     procedure TWin32ListStringList.InitFlags;
    @@ -96,6 +98,32 @@
       PutObject(AIndex, TObject(AData));
     end;
     
    +procedure TWin32ListStringList.UpdateListScrollWidth;
    +var
    +  ListScrollWidth: Integer;
    +  ItemWidth: Integer;
    +  Counter: Integer;
    +begin
    +  if (UpdateCount = 0) and (FSender is TCustomListBox) then
    +  begin
    +    ListScrollWidth := 0;
    +    for Counter := 0 to Count - 1 do
    +    begin
    +      ItemWidth := TCustomListBox(FSender).Canvas.TextWidth(Strings[Counter]);
    +      if ListScrollWidth < ItemWidth then
    +        ListScrollWidth := ItemWidth;
    +    end;
    +    SendMessage(FWin32List, LB_SETHORIZONTALEXTENT,
    +      ListScrollWidth + FWidthIncrement, 0);
    +  end;
    +end;
    +
    +procedure TWin32ListStringList.Assign(Source: TPersistent);
    +begin
    +  inherited Assign(Source);
    +  UpdateListScrollWidth;
    +end;
    +
     {------------------------------------------------------------------------------
       Method: TWin32ListStringList.Sort
       Params:
    @@ -137,6 +165,7 @@
         {$endif}
         PutObject(AnIndex, TheStrings.Objects[Counter]);
       end;
    +  UpdateListScrollWidth;
     end;
     
     {------------------------------------------------------------------------------
    @@ -151,6 +180,7 @@
       Insert(Count, S);
       if FSorted then
         Result := FLastInsertedIndex;
    +  UpdateListScrollWidth;
     end;
     
     {------------------------------------------------------------------------------
    @@ -214,6 +244,7 @@
     procedure TWin32ListStringList.Clear;
     begin
       Windows.SendMessage(FWin32List, FFlagResetContent, 0, 0);
    +  UpdateListScrollWidth;
     end;
     
     {------------------------------------------------------------------------------
    @@ -225,6 +256,7 @@
     procedure TWin32ListStringList.Delete(Index: Integer);
     begin
       Windows.SendMessage(FWin32List, FFlagDeleteString, Index, 0);
    +  UpdateListScrollWidth;
     end;
     
     {------------------------------------------------------------------------------
    @@ -269,6 +301,7 @@
         Windows.SendMessage(FWin32List, FFlagInsertString, Index, LPARAM(PChar(S)));
         {$endif}
       end;
    +  UpdateListScrollWidth;
     end;
     
     procedure TWin32ListStringList.Put(Index: integer; const S: string);
    @@ -327,7 +360,10 @@
     begin
       Windows.SendMessage(FWin32List, WM_SETREDRAW, WPARAM(not Updating), 0);
       if not Updating then
    -    Windows.RedrawWindow(FWin32List, nil, 0, RDW_INVALIDATE or RDW_ALLCHILDREN or RDW_ERASE)
    +  begin
    +    Windows.RedrawWindow(FWin32List, nil, 0, RDW_INVALIDATE or RDW_ALLCHILDREN or RDW_ERASE);
    +    UpdateListScrollWidth;
    +  end;
     end;
     
     { TWin32ComboBoxStringList }
    
    listboxscroll.patch (4,033 bytes)
  • listboxtest.zip (2,939 bytes)

Relationships

related to 0019079 acknowledged Introduce TListBox.AutoScrollWidth property 

Activities

2011-03-11 18:12

 

listboxscroll.patch (4,033 bytes)
Index: lcl/interfaces/win32/win32listslh.inc
===================================================================
--- lcl/interfaces/win32/win32listslh.inc	(revision 29799)
+++ lcl/interfaces/win32/win32listslh.inc	(working copy)
@@ -36,6 +36,7 @@
     FSorted: Boolean;
     FSender: TWinControl;
     FlastInsertedIndex: Integer;
+    FWidthIncrement: Integer;
   protected
     //Win32 Flags
     FFlagSort: Cardinal;
@@ -66,7 +67,9 @@
     procedure SetSorted(Val: Boolean); Virtual;
     function SaveData(AIndex: Integer): Pointer; virtual;
     procedure RestoreData(AIndex: Integer; AData: Pointer); virtual;
+    procedure UpdateListScrollWidth; virtual;
   public
+    procedure Assign(Source: TPersistent); override;
     constructor Create(List : HWND; TheOwner: TWinControl);
     function Add(const S: string): Integer; override;
     procedure AddStrings(TheStrings: TStrings); override;
Index: lcl/interfaces/win32/win32listsl.inc
===================================================================
--- lcl/interfaces/win32/win32listsl.inc	(revision 29799)
+++ lcl/interfaces/win32/win32listsl.inc	(working copy)
@@ -50,6 +50,8 @@
   InitFlags;
   // Determine if the list is sorted
   FSorted := (UINT(GetWindowLong(FWin32List, GWL_STYLE)) and FFlagSort <> 0);
+  // Get system metric needed by UpdateListScrollWidth to correctly update width
+  FWidthIncrement := GetSystemMetrics(SM_CXFRAME);
 end;
 
 procedure TWin32ListStringList.InitFlags;
@@ -96,6 +98,32 @@
   PutObject(AIndex, TObject(AData));
 end;
 
+procedure TWin32ListStringList.UpdateListScrollWidth;
+var
+  ListScrollWidth: Integer;
+  ItemWidth: Integer;
+  Counter: Integer;
+begin
+  if (UpdateCount = 0) and (FSender is TCustomListBox) then
+  begin
+    ListScrollWidth := 0;
+    for Counter := 0 to Count - 1 do
+    begin
+      ItemWidth := TCustomListBox(FSender).Canvas.TextWidth(Strings[Counter]);
+      if ListScrollWidth < ItemWidth then
+        ListScrollWidth := ItemWidth;
+    end;
+    SendMessage(FWin32List, LB_SETHORIZONTALEXTENT,
+      ListScrollWidth + FWidthIncrement, 0);
+  end;
+end;
+
+procedure TWin32ListStringList.Assign(Source: TPersistent);
+begin
+  inherited Assign(Source);
+  UpdateListScrollWidth;
+end;
+
 {------------------------------------------------------------------------------
   Method: TWin32ListStringList.Sort
   Params:
@@ -137,6 +165,7 @@
     {$endif}
     PutObject(AnIndex, TheStrings.Objects[Counter]);
   end;
+  UpdateListScrollWidth;
 end;
 
 {------------------------------------------------------------------------------
@@ -151,6 +180,7 @@
   Insert(Count, S);
   if FSorted then
     Result := FLastInsertedIndex;
+  UpdateListScrollWidth;
 end;
 
 {------------------------------------------------------------------------------
@@ -214,6 +244,7 @@
 procedure TWin32ListStringList.Clear;
 begin
   Windows.SendMessage(FWin32List, FFlagResetContent, 0, 0);
+  UpdateListScrollWidth;
 end;
 
 {------------------------------------------------------------------------------
@@ -225,6 +256,7 @@
 procedure TWin32ListStringList.Delete(Index: Integer);
 begin
   Windows.SendMessage(FWin32List, FFlagDeleteString, Index, 0);
+  UpdateListScrollWidth;
 end;
 
 {------------------------------------------------------------------------------
@@ -269,6 +301,7 @@
     Windows.SendMessage(FWin32List, FFlagInsertString, Index, LPARAM(PChar(S)));
     {$endif}
   end;
+  UpdateListScrollWidth;
 end;
 
 procedure TWin32ListStringList.Put(Index: integer; const S: string);
@@ -327,7 +360,10 @@
 begin
   Windows.SendMessage(FWin32List, WM_SETREDRAW, WPARAM(not Updating), 0);
   if not Updating then
-    Windows.RedrawWindow(FWin32List, nil, 0, RDW_INVALIDATE or RDW_ALLCHILDREN or RDW_ERASE)
+  begin
+    Windows.RedrawWindow(FWin32List, nil, 0, RDW_INVALIDATE or RDW_ALLCHILDREN or RDW_ERASE);
+    UpdateListScrollWidth;
+  end;
 end;
 
 { TWin32ComboBoxStringList }
listboxscroll.patch (4,033 bytes)

2011-03-11 18:12

 

listboxtest.zip (2,939 bytes)

Paul Ishenin

2011-04-02 17:36

manager   ~0047086

Last edited: 2011-04-02 17:37

I don't think this patch can be accepted because it slow down the performance too much.

José Mejuto

2011-04-02 19:26

reporter   ~0047097

Windows autosizes does not perform the calculation over all lines everytime, only on insert new elements, so if you insert a large element and then remove it, the horizontal scrollbar has the "old" width.

In pseudocode:
procedure insert(string)
begin
  if TextWidth(String) > CurrentHScrollWidth then
  begin
   CurrentHScrollWidth=TextWidth(String);
   UpdateScrollBars;
  end;
end;

This way the performance impact is quite limited.

Paul Ishenin

2011-04-02 19:31

manager   ~0047098

Please test and close if ok.

Vladimir Zhirov

2011-04-03 21:36

reporter   ~0047146

Last edited: 2011-04-03 21:42

Paul, while your solution seems correct to me in terms of Delphi compatibility, it creates an inconvenience from LCL API usability point of view, putting the burden of scrollbar management to the programmer:

* On Win32 widgetset we have to calculate largest ListBox item width ourselves. This is tedious but isn't much of a problem, since we had to do this in Delphi anyway.
* On GTK and Qt widgetsets using the same code as on Win32 will result in duplicating GTK/Qt work and wasting performance. To avoid this waste we will have to do something like:

{ifdef LCLWIN32}
  ListBox1.ScrollWidth := CalculateScrollWidth(ListBox1);
{else}
  ListBox1.ScrollWidth := ListBox1.Width + 1;
{$endif}

IMHO, such workarounds look quite ugly, and this work is better to be done by LCL. We should not sacrifice code readability (or LCL-GTK and LCL-Qt performance) for Win32 performance.

If keeping ScrollWidth is important, I would suggest adding new property to control automatic scrollbar adjustment for TListBox (something like AutomaticScrollWidth). If it is acceptable please let me know, and I will reopen this bugreport or create new feature request.

Paul Ishenin

2011-04-04 00:23

manager   ~0047148

I think that introducing AutoScrollWidth property is ok.

Vladimir Zhirov

2011-04-04 09:44

reporter   ~0047181

Created feature request: 0019079. It refers to comments and patch for this issue.
Is it safe to close this issue, or it's better to keep it "resolved" until 0019079 is resolved too?

Issue History

Date Modified Username Field Change
2011-03-11 18:12 Vladimir Zhirov New Issue
2011-03-11 18:12 Vladimir Zhirov File Added: listboxscroll.patch
2011-03-11 18:12 Vladimir Zhirov Widgetset => Win32/Win64
2011-03-11 18:12 Vladimir Zhirov File Added: listboxtest.zip
2011-03-30 22:46 Vincent Snijders LazTarget => 0.99.0
2011-03-30 22:46 Vincent Snijders Status new => acknowledged
2011-03-30 22:46 Vincent Snijders Target Version => 0.99.0
2011-04-02 17:36 Paul Ishenin Note Added: 0047086
2011-04-02 17:37 Paul Ishenin Note Edited: 0047086
2011-04-02 17:37 Paul Ishenin Note Edited: 0047086
2011-04-02 19:26 José Mejuto Note Added: 0047097
2011-04-02 19:31 Paul Ishenin Fixed in Revision => 30138
2011-04-02 19:31 Paul Ishenin Status acknowledged => resolved
2011-04-02 19:31 Paul Ishenin Fixed in Version => 0.9.31 (SVN)
2011-04-02 19:31 Paul Ishenin Resolution open => fixed
2011-04-02 19:31 Paul Ishenin Assigned To => Paul Ishenin
2011-04-02 19:31 Paul Ishenin Note Added: 0047098
2011-04-03 21:36 Vladimir Zhirov Note Added: 0047146
2011-04-03 21:42 Vladimir Zhirov Note Edited: 0047146
2011-04-04 00:23 Paul Ishenin Note Added: 0047148
2011-04-04 09:44 Vladimir Zhirov Note Added: 0047181
2011-10-04 16:01 Vincent Snijders Relationship added related to 0019079
2011-12-01 11:26 Marc Weustink Status resolved => closed