View Issue Details

IDProjectCategoryView StatusLast Update
0020506LazarusRTLpublic2014-09-15 15:07
ReporterAvishai Assigned ToFelipe Monteiro de Carvalho  
PrioritynormalSeverityminorReproducibilityalways
Status assignedResolutionopen 
OSWindows 
Summary0020506: Form.Canvas corrupted when Form is mirrored for BiDiMode
DescriptionIn Windows OS, when a Form is set to BiDiMode:= bdRightToLeft, the Form should be Mirrored so that the Close, Maximize and Minimize buttons are on the Left and the Icon and Caption are on the Right. (See http://msdn.microsoft.com/en-us/goglobal/bb688119.aspx) I can achieve this with the following code, but the Form's Canvas becomes unusable.

procedure MakeFormRightToLeft(AForm: TObject);
{ Make Form RightToLeft - Windows ONLY! }
begin
{$IfDef Windows}
  if AForm is TForm then
    with AForm as TForm do begin
      SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE)
        or WS_EX_LAYOUTRTL or WS_EX_NOINHERITLAYOUT);
    invalidate;
  end;
{$EndIf}
end;

The Form's Canvas becomes Black. If you have Edit components on the Form, as soon as you leave the first Edit component, the canvas of the Edit component is mirrored and echoed to the Top-Right corner of the Form's canvas and is visible. Top-Right is now (0,0) and this is correct.

Also, I must leave the Form's BiDiMode:= bdLeftToRight to keep the Form Caption in it's correct location. If the Form BiDiMode:= bdRightToLeft, the Caption is misplaced near the Minimize Button.

I have found that I can do a Form.Repaint in the OnResize event and this will partially correct the problem, but only when you make the Form smaller. If you make it larger, it returns to Black. But if I Maximize the Form and then return it to Normal in the OnShow event, then it will remain Gray. But the Echoed Edit Canvas is still visible in the Top-Right corner.

My present workaround is simply to cover the Form canvas with a Panel Align:= alClient and it becomes the Parent of all controls. I set the Panel BiDiMode:= bdRightToLeft. It works, but it is a dirty solution.
TagsNo tags attached.
Fixed in Revision
LazTarget-
Widgetset
Attached Files

Relationships

related to 0019497 closedPaul Ishenin StatusBar1.Canvas.TextWidth() return result is false 

Activities

Avishai

2011-10-18 08:52

reporter   ~0053119

Last edited: 2011-10-18 08:58

I forgot to mention that components like Labels and some other non-edits are not visible at all.

Anton Kavalenka

2011-10-18 11:00

reporter   ~0053124

This is Lazarus LCL bug, not FPC.
Canvas.handle is cached from previous form (see 0019497).
New form is recreated, got a new handle -> the canvas handle should be obtained again.

Marco van de Voort

2011-10-18 11:07

manager   ~0053125

Moved what is basically a Lazarus bugreport to Lazarus till a clear FPC problem or FPC specific reproduction arises

Avishai

2011-10-18 11:24

reporter   ~0053126

My apologies. I thought the Canvas was a FPC issue. I found the declaration in an FPC file. I will try to be more careful next time :)

Felipe Monteiro de Carvalho

2011-10-20 08:01

developer   ~0053187

Well, if you hack around with some Windows APIs then you are responsible for inventing solutions for the problems they bring. The way this is put, this is not a Lazarus bug as all. Try using TForm.Invalidate

If you want to do this properly then you should send a patch for Lazarus which implements a cross-platform interface for this. Maybe it could be a new option in BorderIcons, and then implement this when the form is created in TWSWin32CustomForm.CreateHandle

Avishai

2011-10-20 11:16

reporter   ~0053192

Last edited: 2012-05-17 11:36

Thank you Mr. Carvalho.

1. I do not want to use Windows API but I do not know another way. I use MicroSoft Publication for this code. (http://msdn.microsoft.com/en-us/goglobal/bb688119.aspx). Very interesting to read.

2. TForm.Invalidate is in my code.

3. Other Linux and Mac users tell me that this is not needed on those platforms. They tell me that they do not have true RightToLeft Installation, only RightToLeft support in a LeftToRight OS. If this is true then no need for cross platform.

4. I will try TWSWin32CustomForm.CreateHandle but I do not have the skills for this :( Thank you for this suggestion. I really do not know where to begin.

Update:
  http://www.microsoft.com/middleeast/msdn/visualstudio2005.aspx [^]
  http://www.microsoft.com/middleeast/msdn/mirror.aspx [^]

Felipe Monteiro de Carvalho

2012-02-04 18:54

developer   ~0056482

Well, adding the extra styles in TWin32CustomForm.CreateHandle is trivial, but like he says, it might ruin the Canvas, so it has heavy side effects and no proposed solution to fix them = risky = postponed.

Avishai

2012-06-09 14:25

reporter   ~0060397

I just found something by accident. If you unset "Use manifest file to enable Themes (Windows Only)" in "Project Options", the TCanvas appears to be OK. I just made this discovery so I expect to find some new problems, but at least it is a step in the right direction.

Avishai

2012-06-10 09:14

reporter   ~0060413

Some observations:

I tried flipping TForm at Design Time. It flipped the Form but it was very bad. I can explain if you need.

So I found that I had to Design as though it were a LeftToRight Form, then flip the Form in the TForm.OnCreate. This flipped anything that was directly of the Form (TLabel, TEdit, TMemo, TMenu...). However, if I had a TPanel with components, it was not flipped. So there I had to Design RightToLeft. The important thing is that it works, so long as Themes are disabled.

Also, since the Canvas origin is at Top-Right and increasing to the left, things that are Anchored to the left are now anchored to the Right, as it should be.

It takes a bit of imagination, but you really can make a RightToLeft TForm.

Avishai

2012-06-15 14:09

reporter   ~0060552

Last edited: 2012-08-07 19:53

More Experiments: I have tried Flipping many TWinControls and have had some very surprising results. Many Controls came very, very close to working with no change to code. I leave the controls BiDiMode:= bdLeftToRight and simply Flip them. I will add a screen shot to illustrate. I think the biggest shock was how close TStringGrid came to working. Unfortunately, when Flipping a Control, it does not Left Align the vertical ScrollBars if there are any.

I know it is Windows Only, but still... And I don't know if such a thing is possibly on other OS's.

Oops! I almost forgot. You MUST turn Themes OFF for any of this to work.

procedure RTLCtrl(ACtrl: TObject);
{ Make Ctrl RightToLeft - Windows ONLY! }
begin
  if ACtrl is TWinControl then
    with ACtrl as TWinControl do begin
      SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE)
        or WS_EX_LAYOUTRTL);
  end;
end;

Update: I just got the verical ScrollBar issue resolved. Ignore RightToLeft01.JPG

2012-06-15 14:11

 

RightToLeft01.JPG (81,084 bytes)   
RightToLeft01.JPG (81,084 bytes)   

2012-06-15 14:26

 

RightToLeft02.JPG (79,184 bytes)   
RightToLeft02.JPG (79,184 bytes)   

Avishai

2012-08-07 20:02

reporter   ~0061489

Last edited: 2012-08-07 22:27

I am attaching a RightToLeft Demo. For it to work, you need to go to Project/Project Options/Application and UnCheck 'Use manifest file to enable Themes (Windows Only)'. I have not been able to find a way to do this with code.

It conforms to MicroSoft guidelines. The Demo is for Windows ONLY

At present, Mirroring can only be done at Run-Time. I tried doing it at Design-Time, but since Lazarus uses Themes, you get the corrupted Canvas problem during Disgn-Time and can not tell what you are doing.

2012-08-07 20:03

 

RTLDemo.zip (129,237 bytes)

Avishai

2012-08-23 00:48

reporter   ~0061819

This works, but of course there are problems. You must still turn Themes off. Leave most things as BiDiMode:= bdLeftToRight. For controls that you can edit (TEdit, TMemo...), set BiDiMode:= bdRightToLeftNoAlign. Leave the anchors as default and Design as though the Form was LeftToRight. At Run-Time it will be RightToLeft. Many things work perfectly, some do not (Menu images and others). I have workarounds for many of the things that do not work. Please let me know when someone begins working on this. I have done a lot of experimenting and feel I can help save some time.

Uses
  Classes, SysUtils, Forms, Controls, Graphics, LclType;

Type
  TForm1 = Class(TForm)
  protected
    procedure CreateParams(var Params : TCreateParams); override;
  End;

Var
  Form1: TForm1;

Implementation

{$R *.lfm}

procedure TForm1.CreateParams(var Params : TCreateParams);
const
  RightToLeftLayout = $400000;
begin
  inherited CreateParams(Params);
  {$IFDEF WINDOWS}
  Params.ExStyle:= Params.ExStyle or RightToLeftLayout;
  {$ENDIF}
end;

Avishai

2012-09-12 10:57

reporter   ~0062299

MAJOR BREAKTHROUGH! In the file Themes.pas, if you can find a way to make the line "FThemesAvailable := InitThemes;" conditional, then Mirroring works almost perfectly.

constructor TThemeServices.Create;
begin
  FDottedBrush := 0;

  FThemesAvailable := InitThemes; // This is the line - Line 0000543
  UpdateThemes;
end;

I commented that line out and can now create RightToLeft Forms that look the way they should. I think I may even be able to Mirror during Design-Time, but have not tried it yet. I will have to create a component to test.

Avishai

2012-09-29 13:10

reporter   ~0062723

Last edited: 2012-09-30 08:58

After further investigation, I suspect that the problem with Mirroring TCanvas is actually in unit Graphics. Maybe something to do with ClipRect.

The changes I have made to Lazarus can not be made for others. It kills Themes.
But for my own use ONLY I have modified unit Themes as follows.

-------------------------------
//{$Define NoRightToLeftLayout}

constructor TThemeServices.Create;
begin
  FDottedBrush := 0;

  // IfDef Added by Avishai
  {$IfDef NoRightToLeftLayout}
  FThemesAvailable:= InitThemes;
  UpdateThemes;
  {$EndIf}
end;

And I have modified unit Forms as follows.

---------------------------------
procedure TCustomForm.CreateParams(var Params : TCreateParams);
const // Added by Avishai
  RightToLeftLayout = $400000; // Added by Avishai
begin
  inherited CreateParams(Params);
  with Params do
  begin
    if (Parent = nil) and (ParentWindow = 0) then
    begin
      // define Parent according to PopupMode and PopupParent
      if not (csDesigning in ComponentState) then
      begin
        if (Application.MainForm <> Self) then
        begin
          case PopupMode of
            pmNone:;
            pmAuto:
              if (Screen.ActiveForm <> nil) then
                WndParent := Screen.ActiveForm.Handle;
            pmExplicit:
              if (PopupParent <> nil) then
                WndParent := PopupParent.Handle;
          end;
        end;
        if (WndParent = 0) and
           (((Self = Application.MainForm) and Application.MainFormOnTaskBar) or (GetEffectiveShowInTaskBar = stAlways)) then
          ExStyle := ExStyle or WS_EX_APPWINDOW;
      end;
      Style := Style and not Cardinal(WS_GROUP or WS_TABSTOP or WS_CHILD);
    end;
  end;

  // Added by Avishai
  {$IFDEF WINDOWS}
  if not (csDesigning in ComponentState) then begin
    if Self.IsRightToLeft then begin
      Params.ExStyle:= Params.ExStyle or RightToLeftLayout;
      Self.BiDiMode:= bdLeftToRight;
    end;
  end;
  {$ENDIF}
end;

This allows me to have RightToLeft Forms at Run-Time by simply setting TApplication.BidiMode:= bdRightToLeftNoAlign;, but I must Design LeftToRight.

In my opinion, Lazarus seriously needs to implement RightToLeftLayout to conform to Windows design. This is the way that the OS is designed to handle RightToLeft.

Issue History

Date Modified Username Field Change
2011-10-18 08:29 Avishai New Issue
2011-10-18 08:52 Avishai Note Added: 0053119
2011-10-18 08:58 Avishai Note Edited: 0053119
2011-10-18 11:00 Anton Kavalenka Note Added: 0053124
2011-10-18 11:06 Marco van de Voort Project FPC => Lazarus
2011-10-18 11:07 Marco van de Voort Note Added: 0053125
2011-10-18 11:24 Avishai Note Added: 0053126
2011-10-20 08:01 Felipe Monteiro de Carvalho Note Added: 0053187
2011-10-20 08:03 Felipe Monteiro de Carvalho Relationship added related to 0019497
2011-10-20 08:06 Felipe Monteiro de Carvalho Status new => assigned
2011-10-20 08:06 Felipe Monteiro de Carvalho Assigned To => Felipe Monteiro de Carvalho
2011-10-20 08:07 Felipe Monteiro de Carvalho LazTarget => -
2011-10-20 08:07 Felipe Monteiro de Carvalho Status assigned => feedback
2011-10-20 11:16 Avishai Note Added: 0053192
2011-10-20 11:17 Avishai Note Edited: 0053192
2011-10-30 14:01 Zeljan Rikalo LazTarget - => 1.0
2011-10-30 14:01 Zeljan Rikalo Product Version 2.5.1 =>
2012-02-04 18:54 Felipe Monteiro de Carvalho LazTarget 1.0 => 1.2
2012-02-04 18:54 Felipe Monteiro de Carvalho Note Added: 0056482
2012-03-15 21:12 Vincent Snijders Status feedback => assigned
2012-05-03 10:14 Avishai Note Edited: 0053192
2012-05-17 11:36 Avishai Note Edited: 0053192
2012-06-09 14:25 Avishai Note Added: 0060397
2012-06-10 09:14 Avishai Note Added: 0060413
2012-06-15 14:09 Avishai Note Added: 0060552
2012-06-15 14:11 Avishai File Added: RightToLeft01.JPG
2012-06-15 14:16 Avishai Note Edited: 0060552
2012-06-15 14:22 Avishai Note Edited: 0060552
2012-06-15 14:26 Avishai File Added: RightToLeft02.JPG
2012-06-15 14:27 Avishai Note Edited: 0060552
2012-08-07 19:53 Avishai Note Edited: 0060552
2012-08-07 20:02 Avishai Note Added: 0061489
2012-08-07 20:03 Avishai File Added: RTLDemo.zip
2012-08-07 22:27 Avishai Note Edited: 0061489
2012-08-23 00:48 Avishai Note Added: 0061819
2012-09-12 10:57 Avishai Note Added: 0062299
2012-09-29 13:10 Avishai Note Added: 0062723
2012-09-29 14:05 Avishai Note Edited: 0062723
2012-09-30 08:58 Avishai Note Edited: 0062723
2014-01-14 15:16 Martin Friebe LazTarget 1.2 => 1.4
2014-09-15 15:07 Felipe Monteiro de Carvalho LazTarget 1.4 => -