View Issue Details

IDProjectCategoryView StatusLast Update
0025408LazarusLCLpublic2014-01-18 14:58
ReporterwpAssigned ToMaxim Ganetsky 
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
PlatformWindowsOSWindowsOS VersionWin 7
Product Version1.3 (SVN)Product Build 
Target Version1.2.0Fixed in Version1.2.0 
Summary0025408: BiDiMode and FlipChildren issues with TRadioGroup, TCheckGroup and TCheckedListbox
DescriptionWhen BiDiMode is set to Right-to-left and child controls are flipped by means of FlipChildren irregularities are seen for above-mentioned controls:
- TCheckListbox does not change when BiDiMode is changed
- TRadioGroup and TCheckGroup show the radio/checkbox at the left of the text when BiDiMode is at RTL. Therefore the position of the radio/checkboxes is "ragged". They should be aligned along the right edge of the control as can be confirmed with Delphi XE2.

Screenshot "Delphi_XE2" shows the output of the test program after BiDiMode has been set to R-T-L and FlipChildren has been executed.

Screenshot "Lazarus_unpatched" is the output of Lazarus 1.1 r.42799, for the same situation.

Screenshot "Lazarus_patched" is the output of current Laz trunk after application of the attached patch showing the correct behavior.
Steps To ReproduceRun attached demo and press the buttons "BiDiMode" and/or "FlipChildren".
Additional InformationThe attached patch fixes the issues.
Tagsbidi, BiDiMode
Fixed in Revision43576, 43577, 43584
LazTarget-
WidgetsetWin32/Win64
Attached Files
  • bidi.patch (6,083 bytes)
    Index: checklst.pas
    ===================================================================
    --- checklst.pas	(revision 43521)
    +++ checklst.pas	(working copy)
    @@ -185,8 +185,12 @@
     
     procedure TCustomCheckListBox.DrawItem(AIndex: Integer; ARect: TRect; State: TOwnerDrawState);
     begin
    -  if not Header[AIndex] then
    -    Inc(ARect.Left, GetCheckWidth);
    +  if not Header[AIndex] then begin
    +    if UseRightToLeftAlignment then
    +      Dec(ARect.Right, GetCheckWidth)
    +    else
    +      Inc(ARect.Left, GetCheckWidth);
    +  end;
       inherited;
     end;
     
    Index: extctrls.pp
    ===================================================================
    --- extctrls.pp	(revision 43521)
    +++ extctrls.pp	(working copy)
    @@ -639,6 +639,7 @@
         constructor Create(TheOwner: TComponent); override;
         destructor Destroy; override;
         function CanModify: boolean; virtual;
    +    procedure FlipChildren(AllLevels: Boolean); override;
         function Rows: integer;
       public
         property AutoFill: Boolean read FAutoFill write SetAutoFill;
    @@ -750,6 +751,7 @@
       public
         constructor Create(TheOwner: TComponent); override;
         destructor Destroy; override;
    +    procedure FlipChildren(AllLevels: Boolean); override;
         function Rows: integer;
       public
         property AutoFill: boolean read FAutoFill write SetAutoFill;
    Index: include/customcheckgroup.inc
    ===================================================================
    --- include/customcheckgroup.inc	(revision 43521)
    +++ include/customcheckgroup.inc	(working copy)
    @@ -341,5 +341,10 @@
         Result:=0;
     end;
     
    +procedure TCustomCheckGroup.FlipChildren(AllLevels: Boolean);
    +begin
    +  // no flipping
    +end;
    +
     // included by extctrls.pp
     
    Index: include/radiogroup.inc
    ===================================================================
    --- include/radiogroup.inc	(revision 43521)
    +++ include/radiogroup.inc	(working copy)
    @@ -13,7 +13,6 @@
       Delphi compatibility:
     
        - the interface is almost like in delphi 5
    -   - FlipChildren procedure is missing
     }
     
     
    @@ -562,6 +561,11 @@
       UpdateControlsPerLine;
     end;
     
    +procedure TCustomRadioGroup.FlipChildren(AllLevels: Boolean);
    +begin
    +  // no flipping
    +end;
    +
     {------------------------------------------------------------------------------
       procedure TCustomRadioGroup.UpdateRadioButtonStates;
       
    Index: interfaces/win32/win32wschecklst.pp
    ===================================================================
    --- interfaces/win32/win32wschecklst.pp	(revision 43521)
    +++ interfaces/win32/win32wschecklst.pp	(working copy)
    @@ -74,7 +74,10 @@
         for I := 0 to Windows.SendMessage(Window, LB_GETCOUNT, 0, 0) - 1 do
         begin
           Windows.SendMessage(Window, LB_GETITEMRECT, I, PtrInt(@ItemRect));
    -      ItemRect.Right := ItemRect.Left + ItemRect.Bottom - ItemRect.Top;
    +      if TCheckListbox(WindowInfo^.WinControl).UseRightToLeftAlignment then
    +        ItemRect.Left := ItemRect.Right - ItemRect.Bottom + ItemRect.Top
    +      else
    +        ItemRect.Right := ItemRect.Left + ItemRect.Bottom - ItemRect.Top;
           if Windows.PtInRect(ItemRect, MousePos) then
           begin
             // item clicked: toggle
    @@ -178,6 +181,7 @@
         Enabled, Selected: Boolean;
         ARect, TextRect: Windows.Rect;
         Details: TThemedElementDetails;
    +    TextFlags: DWord;
         OldColor: COLORREF;
         OldBkMode: Integer;
       {$ifdef WindowsUnicodeSupport}
    @@ -190,7 +194,10 @@
     
         ARect := Data^.rcItem;
         TextRect := ARect;
    -    TextRect.Left := TextRect.Left + TextRect.Bottom - TextRect.Top + 4;
    +    if CheckListBox.UseRightToLeftAlignment then
    +      TextRect.Right := TextRect.Right - TextRect.Bottom + TextRect.Top - 4
    +    else
    +      TextRect.Left := TextRect.Left + TextRect.Bottom - TextRect.Top + 4;
     
         // fill the background
         if Selected then
    @@ -202,13 +209,23 @@
           Windows.FillRect(Data^._HDC, ARect, CheckListBox.Brush.Reference.Handle);
     
         // draw checkbox
    -    ARect.Right := ARect.Left + ARect.Bottom - ARect.Top;
    +    if CheckListBox.UseRightToLeftAlignment then
    +      ARect.Left := ARect.Right - ARect.Bottom + ARect.Top
    +    else
    +      ARect.Right := ARect.Left + ARect.Bottom - ARect.Top;
     
         Details := ThemeServices.GetElementDetails(ThemeStateMap[CheckListBox.State[Data^.ItemID], Enabled]);
         ThemeServices.DrawElement(Data^._HDC, Details, ARect);
     
         // draw text
    -    TextRect.Left := TextRect.Left + 2;
    +    if CheckListBox.UseRightToLeftAlignment then begin
    +      TextRect.Right := TextRect.Right - 2;
    +      TextFlags := DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX or DT_RIGHT;
    +    end
    +    else begin
    +      TextRect.Left := TextRect.Left + 2;
    +      TextFlags := DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX;
    +    end;
         OldBkMode := Windows.SetBkMode(Data^._HDC, TRANSPARENT);
         if not Enabled then
           OldColor := Windows.SetTextColor(Data^._HDC, Windows.GetSysColor(COLOR_GRAYTEXT))
    @@ -227,17 +244,17 @@
         begin
           WideBuffer := UTF8ToUTF16(CheckListBox.Items[Data^.ItemID]);
           Windows.DrawTextW(Data^._HDC, PWideChar(WideBuffer), -1,
    -       TextRect, DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
    +       TextRect, TextFlags);
         end
         else
         begin
           AnsiBuffer := Utf8ToAnsi(CheckListBox.Items[Data^.ItemID]);
           Windows.DrawText(Data^._HDC, PChar(AnsiBuffer), -1,
    -       TextRect, DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
    +       TextRect, TextFlags);
         end;
       {$else}
         Windows.DrawText(Data^._HDC, PChar(CheckListBox.Items[Data^.ItemID]), -1,
    -      TextRect, DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
    +      TextRect, TextFlags);
       {$endif}
         // restore old colors
         Windows.SetTextColor(Data^._HDC, OldColor);
    @@ -244,7 +261,10 @@
         Windows.SetBkMode(Data^._HDC, OldBkMode);
         if Enabled and ((Data^.itemState and ODS_FOCUS) > 0) and CheckListBox.Focused then
         begin
    -      TextRect.Left := TextRect.Left - 2;
    +      if CheckListBox.UseRightToLeftAlignment then
    +        TextRect.Right := TextRect.Right + 2
    +      else
    +        TextRect.Left := TextRect.Left - 2;
           Windows.DrawFocusRect(Data^._HDC, TextRect);
         end;
       end;
    
    bidi.patch (6,083 bytes)
  • Delphi XE2.jpg (14,511 bytes)
    Delphi XE2.jpg (14,511 bytes)
  • Laz_unpatched.jpg (19,744 bytes)
    Laz_unpatched.jpg (19,744 bytes)
  • Laz_patched.jpg (19,600 bytes)
    Laz_patched.jpg (19,600 bytes)
  • BiDiMode_FlipChildren.zip (2,651 bytes)

Relationships

has duplicate 0019158 closedMaxim Ganetsky Packages TCheckListBox does not mirror properly when BiDiMode:= bdRightToLeft. 
related to 0025450 closedMaxim Ganetsky Lazarus Text direction in TCheckList is incorrect when BiDiMode<>bdLeftToRight 
related to 0019303 acknowledged Lazarus BiDi meta issue 

Activities

wp

2013-12-08 01:07

developer  

bidi.patch (6,083 bytes)
Index: checklst.pas
===================================================================
--- checklst.pas	(revision 43521)
+++ checklst.pas	(working copy)
@@ -185,8 +185,12 @@
 
 procedure TCustomCheckListBox.DrawItem(AIndex: Integer; ARect: TRect; State: TOwnerDrawState);
 begin
-  if not Header[AIndex] then
-    Inc(ARect.Left, GetCheckWidth);
+  if not Header[AIndex] then begin
+    if UseRightToLeftAlignment then
+      Dec(ARect.Right, GetCheckWidth)
+    else
+      Inc(ARect.Left, GetCheckWidth);
+  end;
   inherited;
 end;
 
Index: extctrls.pp
===================================================================
--- extctrls.pp	(revision 43521)
+++ extctrls.pp	(working copy)
@@ -639,6 +639,7 @@
     constructor Create(TheOwner: TComponent); override;
     destructor Destroy; override;
     function CanModify: boolean; virtual;
+    procedure FlipChildren(AllLevels: Boolean); override;
     function Rows: integer;
   public
     property AutoFill: Boolean read FAutoFill write SetAutoFill;
@@ -750,6 +751,7 @@
   public
     constructor Create(TheOwner: TComponent); override;
     destructor Destroy; override;
+    procedure FlipChildren(AllLevels: Boolean); override;
     function Rows: integer;
   public
     property AutoFill: boolean read FAutoFill write SetAutoFill;
Index: include/customcheckgroup.inc
===================================================================
--- include/customcheckgroup.inc	(revision 43521)
+++ include/customcheckgroup.inc	(working copy)
@@ -341,5 +341,10 @@
     Result:=0;
 end;
 
+procedure TCustomCheckGroup.FlipChildren(AllLevels: Boolean);
+begin
+  // no flipping
+end;
+
 // included by extctrls.pp
 
Index: include/radiogroup.inc
===================================================================
--- include/radiogroup.inc	(revision 43521)
+++ include/radiogroup.inc	(working copy)
@@ -13,7 +13,6 @@
   Delphi compatibility:
 
    - the interface is almost like in delphi 5
-   - FlipChildren procedure is missing
 }
 
 
@@ -562,6 +561,11 @@
   UpdateControlsPerLine;
 end;
 
+procedure TCustomRadioGroup.FlipChildren(AllLevels: Boolean);
+begin
+  // no flipping
+end;
+
 {------------------------------------------------------------------------------
   procedure TCustomRadioGroup.UpdateRadioButtonStates;
   
Index: interfaces/win32/win32wschecklst.pp
===================================================================
--- interfaces/win32/win32wschecklst.pp	(revision 43521)
+++ interfaces/win32/win32wschecklst.pp	(working copy)
@@ -74,7 +74,10 @@
     for I := 0 to Windows.SendMessage(Window, LB_GETCOUNT, 0, 0) - 1 do
     begin
       Windows.SendMessage(Window, LB_GETITEMRECT, I, PtrInt(@ItemRect));
-      ItemRect.Right := ItemRect.Left + ItemRect.Bottom - ItemRect.Top;
+      if TCheckListbox(WindowInfo^.WinControl).UseRightToLeftAlignment then
+        ItemRect.Left := ItemRect.Right - ItemRect.Bottom + ItemRect.Top
+      else
+        ItemRect.Right := ItemRect.Left + ItemRect.Bottom - ItemRect.Top;
       if Windows.PtInRect(ItemRect, MousePos) then
       begin
         // item clicked: toggle
@@ -178,6 +181,7 @@
     Enabled, Selected: Boolean;
     ARect, TextRect: Windows.Rect;
     Details: TThemedElementDetails;
+    TextFlags: DWord;
     OldColor: COLORREF;
     OldBkMode: Integer;
   {$ifdef WindowsUnicodeSupport}
@@ -190,7 +194,10 @@
 
     ARect := Data^.rcItem;
     TextRect := ARect;
-    TextRect.Left := TextRect.Left + TextRect.Bottom - TextRect.Top + 4;
+    if CheckListBox.UseRightToLeftAlignment then
+      TextRect.Right := TextRect.Right - TextRect.Bottom + TextRect.Top - 4
+    else
+      TextRect.Left := TextRect.Left + TextRect.Bottom - TextRect.Top + 4;
 
     // fill the background
     if Selected then
@@ -202,13 +209,23 @@
       Windows.FillRect(Data^._HDC, ARect, CheckListBox.Brush.Reference.Handle);
 
     // draw checkbox
-    ARect.Right := ARect.Left + ARect.Bottom - ARect.Top;
+    if CheckListBox.UseRightToLeftAlignment then
+      ARect.Left := ARect.Right - ARect.Bottom + ARect.Top
+    else
+      ARect.Right := ARect.Left + ARect.Bottom - ARect.Top;
 
     Details := ThemeServices.GetElementDetails(ThemeStateMap[CheckListBox.State[Data^.ItemID], Enabled]);
     ThemeServices.DrawElement(Data^._HDC, Details, ARect);
 
     // draw text
-    TextRect.Left := TextRect.Left + 2;
+    if CheckListBox.UseRightToLeftAlignment then begin
+      TextRect.Right := TextRect.Right - 2;
+      TextFlags := DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX or DT_RIGHT;
+    end
+    else begin
+      TextRect.Left := TextRect.Left + 2;
+      TextFlags := DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX;
+    end;
     OldBkMode := Windows.SetBkMode(Data^._HDC, TRANSPARENT);
     if not Enabled then
       OldColor := Windows.SetTextColor(Data^._HDC, Windows.GetSysColor(COLOR_GRAYTEXT))
@@ -227,17 +244,17 @@
     begin
       WideBuffer := UTF8ToUTF16(CheckListBox.Items[Data^.ItemID]);
       Windows.DrawTextW(Data^._HDC, PWideChar(WideBuffer), -1,
-       TextRect, DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
+       TextRect, TextFlags);
     end
     else
     begin
       AnsiBuffer := Utf8ToAnsi(CheckListBox.Items[Data^.ItemID]);
       Windows.DrawText(Data^._HDC, PChar(AnsiBuffer), -1,
-       TextRect, DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
+       TextRect, TextFlags);
     end;
   {$else}
     Windows.DrawText(Data^._HDC, PChar(CheckListBox.Items[Data^.ItemID]), -1,
-      TextRect, DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
+      TextRect, TextFlags);
   {$endif}
     // restore old colors
     Windows.SetTextColor(Data^._HDC, OldColor);
@@ -244,7 +261,10 @@
     Windows.SetBkMode(Data^._HDC, OldBkMode);
     if Enabled and ((Data^.itemState and ODS_FOCUS) > 0) and CheckListBox.Focused then
     begin
-      TextRect.Left := TextRect.Left - 2;
+      if CheckListBox.UseRightToLeftAlignment then
+        TextRect.Right := TextRect.Right + 2
+      else
+        TextRect.Left := TextRect.Left - 2;
       Windows.DrawFocusRect(Data^._HDC, TextRect);
     end;
   end;
bidi.patch (6,083 bytes)

wp

2013-12-08 01:07

developer  

Delphi XE2.jpg (14,511 bytes)
Delphi XE2.jpg (14,511 bytes)

wp

2013-12-08 01:08

developer  

Laz_unpatched.jpg (19,744 bytes)
Laz_unpatched.jpg (19,744 bytes)

wp

2013-12-08 01:08

developer  

Laz_patched.jpg (19,600 bytes)
Laz_patched.jpg (19,600 bytes)

wp

2013-12-16 09:40

developer   ~0071916

Anybody taking care of this patch?

Maxim Ganetsky

2013-12-20 22:07

developer   ~0071989

Where is the demo?

wp

2013-12-21 01:15

developer  

BiDiMode_FlipChildren.zip (2,651 bytes)

wp

2013-12-21 01:16

developer   ~0071992

Sorry...

Maxim Ganetsky

2013-12-21 15:41

developer   ~0071997

Applied, thanks. Will be merged to 1.2 if it won't cause regressions.

What's your full name? I will add you to contributors list.

wp

2013-12-21 15:44

developer   ~0071998

Werner Pamler. Thanks

wp

2013-12-21 15:45

developer   ~0071999

You're saying that it will be merged to 1.2, but the header says that target version is 1.4. Is that ok?

Maxim Ganetsky

2013-12-21 16:11

developer   ~0072000

I added you to contributors list and changed target.

Issue History

Date Modified Username Field Change
2013-12-08 01:07 wp New Issue
2013-12-08 01:07 wp File Added: bidi.patch
2013-12-08 01:07 wp File Added: Delphi XE2.jpg
2013-12-08 01:08 wp File Added: Laz_unpatched.jpg
2013-12-08 01:08 wp File Added: Laz_patched.jpg
2013-12-16 09:40 wp Note Added: 0071916
2013-12-16 14:30 Reinier Olislagers Tag Attached: bidi
2013-12-16 14:30 Reinier Olislagers Tag Attached: BiDiMode
2013-12-20 22:07 Maxim Ganetsky LazTarget => -
2013-12-20 22:07 Maxim Ganetsky Note Added: 0071989
2013-12-20 22:07 Maxim Ganetsky Assigned To => Maxim Ganetsky
2013-12-20 22:07 Maxim Ganetsky Status new => feedback
2013-12-20 22:07 Maxim Ganetsky Target Version => 1.4
2013-12-21 01:15 wp File Added: BiDiMode_FlipChildren.zip
2013-12-21 01:16 wp Note Added: 0071992
2013-12-21 01:16 wp Status feedback => assigned
2013-12-21 15:41 Maxim Ganetsky Note Added: 0071997
2013-12-21 15:41 Maxim Ganetsky Status assigned => resolved
2013-12-21 15:41 Maxim Ganetsky Fixed in Version => 1.4
2013-12-21 15:41 Maxim Ganetsky Resolution open => fixed
2013-12-21 15:44 wp Note Added: 0071998
2013-12-21 15:45 wp Note Added: 0071999
2013-12-21 16:11 Maxim Ganetsky Note Added: 0072000
2013-12-21 16:11 Maxim Ganetsky Target Version 1.4 => 1.2.0
2013-12-21 16:12 Maxim Ganetsky Fixed in Revision => 43576, 43577
2013-12-22 23:19 Maxim Ganetsky Fixed in Revision 43576, 43577 => 43576, 43577, 43584
2013-12-22 23:19 Maxim Ganetsky Fixed in Version 1.4 => 1.2.0
2013-12-23 21:07 Maxim Ganetsky Relationship added related to 0025450
2013-12-23 21:15 Maxim Ganetsky Relationship added has duplicate 0019158
2013-12-23 22:05 Maxim Ganetsky Relationship added related to 0019303
2014-01-18 14:58 wp Status resolved => closed