Recent

Author Topic: Help with MSWindows and RightToLeft Images  (Read 21744 times)

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4715
  • I like bugs.
Re: Help with MSWindows and RightToLeft Images
« Reply #15 on: April 04, 2013, 06:38:31 pm »
RightToLeftLayout is a Windows settings if I understand right.

Which one is more desirable to work right, RightToLeftLayout or the components' BiDiMode?
They are duplicate info for the same thing after all.

With BiDiMode you can build a GUI which is partly LeftToRight and partly RightToLeft. I don't know is it ever needed.

Does BiDiMode work in Delphi?

Juha
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

Avishai

  • Hero Member
  • *****
  • Posts: 1021
Re: Help with MSWindows and RightToLeft Images
« Reply #16 on: April 04, 2013, 07:20:14 pm »
Thanks for your input JuhaManninen. 

BiDiMode and RightToLeftLayout are not the same.  BiDiMode only handles Text display but it does not always mean Mirroring.  If you set BiDiMode:= bdRightToLeft for TForm all it does is shift the TForm.Caption to the right of the Form and passes it to it's children.  This is incorrect and not acceptable in the RightToLeft world.

RightToLeftLayout mirrors TForm, including the Forms Canvas so that the origin is at Top-Right and increases to the left.  As a result, anything that is drawn to that canvas is also mirrored.  So things such as TLabel show correctly.  Anchors are also mirrored so nothing needs to be done.  It's a True RightToLeft Form.

Another exaple is TPageControl.  Setting it to bdRightToLeft does nothing to the TPageControl.  The Tabs are still aligned to the Left.  But RightToLeftLayout mirrors it so that Tabs are aligned to the Right and the origin is from RightToLeft.

When designing a RightToLeft form you either have to design LeftToRight and then do a FlipChildren, or you have to spend a huge amount of time trying to get Right edges of control lined up so that it looks normal.  With LeftToRight you just set the Left property and your done.

I don't think anyone can really appreciate the difficulty until they try to design a complex Form for RightToLeft and get it to function correctly.

Delphi's BiDiMode was introduced before MSWindows added RightToLeftLayout.  But they have continued with BiDiMode instead of fixing the problem.  So to answer your question, Delphi's BiDiMode has been a complete failure from the beginning whereas MS Visual Studio does a very competent job of it.  I don't know how MS Visual Studio handles bitmaps/icons/images though and I don't have a way to check it.

The objective is to get Lazarus to the point that when you set a Form (or Application) to RightToLeftLayout, the Form is mirrored at Design-Time and you proceed in exactly the same way as designing for LeftToRight.  I have succeeded to some degree but only when Themes are disabled and there are still many issues that need to be resolved.  Issues that I don't have the skills to fix.  It will take someone with a far better understanding of Lazarus and an understanding of RightToLeft.
Lazarus Trunk / fpc 2.6.2 / Win32

Avishai

  • Hero Member
  • *****
  • Posts: 1021
Re: Help with MSWindows and RightToLeft Images
« Reply #17 on: April 04, 2013, 09:24:46 pm »
Maybe this screen shot will explain RightToLeftLayout better than my words.  Notice that even the Form itself is mirrored as well as all of the components on it.  But BiDiMode is bdLeftToRight for all controls.

Edit: http://social.msdn.microsoft.com/Search/en-US?query=RightToLeftLayout&ac=2
« Last Edit: April 04, 2013, 11:12:18 pm by Avishai »
Lazarus Trunk / fpc 2.6.2 / Win32

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4715
  • I like bugs.
Re: Help with MSWindows and RightToLeft Images
« Reply #18 on: April 05, 2013, 12:53:28 am »
Ok. So BiDiMode is quite useless then.

LCL components bind to native widgets which obey the settings of the underlying OS/widgetset. I don't see why they would need any special code for RightToLeftLayout.

LCL forms are a different thing. LCL positions its child controls based on coordinates, alignment and anchors. RightToLeftLayout support must be implemented there. The same things applies to components that implement their own internal layout.

This solution will be cross-platform. I don't know why you try to make a Windows only solution for a cross-platform system.
You need to study LCL layout engine instead of WinAPI.

Quote
I don't know how MS Visual Studio handles bitmaps/icons/images though and I don't have a way to check it.

There is a free version of Visual Studio available. You only need a proper computer to run it.

Juha
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

Avishai

  • Hero Member
  • *****
  • Posts: 1021
Re: Help with MSWindows and RightToLeft Images
« Reply #19 on: April 05, 2013, 01:49:35 am »
Thank you JuhaManninen,

I don't know other platforms well enough to know if RightToLeftLayout is correct for them.  I have heard conflicting statements about how RightToLeft is normally handled on Linux and Mac.  Some say only the text needs to be RightToLeft and others say it is similar to MSWindows.  I have never seen Hebrew programs on Linux or Mac so I can't say.  I haven't even found a way to programmatically switch keyboard languages for either Linux or Mac, although there must be a way.  As a result of my lack of knowledge about other platforms, I have tried to find solutions by following MSWindows guidelines and other programming IDEs.  What I found was that, for the most part, RightToLeftLayout solves most of the problems for MSWindows.  But because it wasn't designed that way from the start, it also creates a few problems. I have found solutions for some of these problems but not all.  But as it stands now, for MSWindows at least, It's a great deal of extra work to design a RightToLeft Application with Lazarus and too many things simply don't work RightToLeft.  Tpagecontrol, Ttreeview, Tbarchart are perfect examples.

To the best of my knowledge, there is no free version of Visual Studio although there is a trial version.  I tried it and I hated it, but I was able to see how easy it can be to create true RightToLeft applications.
Lazarus Trunk / fpc 2.6.2 / Win32

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Help with MSWindows and RightToLeft Images
« Reply #20 on: April 05, 2013, 02:43:09 am »
To the best of my knowledge, there is no free version of Visual Studio although there is a trial version.  I tried it and I hated it, but I was able to see how easy it can be to create true RightToLeft applications.

the express edition is free.

http://www.microsoft.com/visualstudio/eng/downloads#d-2012-express
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4715
  • I like bugs.
Re: Help with MSWindows and RightToLeft Images
« Reply #21 on: April 05, 2013, 09:19:19 am »
It's a great deal of extra work to design a RightToLeft Application with Lazarus and too many things simply don't work RightToLeft.  Tpagecontrol, Ttreeview, Tbarchart are perfect examples.

Those components implement their own layouting. They should be improved to support RightToLeftLayout.
Still, no WinAPI is needed for that.

Juha
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

Avishai

  • Hero Member
  • *****
  • Posts: 1021
Re: Help with MSWindows and RightToLeft Images
« Reply #22 on: April 06, 2013, 07:58:30 am »
Well, until that happens, I have to continue following MSWindows guidelines.  And that means Windows API and RightToLeftLayout. 

I would love to get some input from others that are familiar with RightToLeft on platforms other than MSWindows.  I have no idea how it is typically handled.  Do you mirror the Form?  Is BiDiMode enough?  Is it as much extra work making thing RightToLeft?  How do you activate the keyboard language?  What problems and solutions have you found?  Are you forced to except a mix of RightToLeft and LeftToRight?
Lazarus Trunk / fpc 2.6.2 / Win32

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4715
  • I like bugs.
Re: Help with MSWindows and RightToLeft Images
« Reply #23 on: April 06, 2013, 11:56:06 am »
Well, until that happens, I have to continue following MSWindows guidelines.  And that means Windows API and RightToLeftLayout. 

What are the MSWindows guidelines for RightToLeftLayout when using Windows API?
My understanding now is that there is no proper support for RightToLeftLayout in WinAPI. That is why you could not make it work either. If there were clear guidelines then you would have used them.

You wrote that Visual Studio supports RightToLeftLayout well. I guess the support is built in .NET directly and any .NET app gets the support automatically. Recent versions of Visual Studio create .NET apps which may create the confusion.
.NET has a much bigger and better API than the traditional WinAPI.
In practice the .NET code supporting RightToLeftLayout deals with the layout engine and custom components, the same way that LCL should deal with its layout engine and custom components.

Quote
I would love to get some input from others that are familiar with RightToLeft on platforms other than MSWindows.

I hope you get answers for that, yet I guess other people also find it difficult to do.

About the difference between RightToLeft and BiDiMode, I understand that BiDiMode is designed for the exact same purpose but is not implemented properly.
One valid solution would be to implement it properly. Then it would not be Delphi compatible but only because Delphi's implementation is buggy.

On the other hand, a global setting like TApplication.RightToLeft is more intuitive because typically you want the whole app to behave consistently, not mixing different BiDi styles.
The TApplication level setting could come from the underlying OS but it could also be changed in project settings.

I recommend you study the LCL layout code. It is not trivial and it interacts with the form designer. Good luck!
Make experimental changes. The author Mattias will be helpful when he sees that somebody is truly learning the code.
Use either SVN or Git for your experiments. Git's local branches are nice for such experimental features.


Regards,
Juha
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

Avishai

  • Hero Member
  • *****
  • Posts: 1021
Re: Help with MSWindows and RightToLeft Images
« Reply #24 on: April 06, 2013, 02:32:51 pm »
But I have been able to make it work.  There are still a few small problems to work out, but they are only because the controls weren't written to handle it from the beginning.  The changes that I have made to Lazarus code have been trivial. 

BidiMode and RightToLeftLayout are not the same thing.  BiDiMode was only intended to handle RightToLeft Text and nothing more.  RightToLeftLayout handles RightToLeft Text AND mirroring.

The one change that I made to 'Win32Themes' to disable Themes can not be accepted by Lazarus because it isn't the right answer.  It's just a hack until the real problem with Themes is found and fixed.  But without that change nothing else works.

Code: [Select]
Lazarus code changes

{====== C:\lazarus\lcl\interfaces\win32\win32themes.pas - Around Line 283 ======}

function TWin32ThemeServices.UseThemes: Boolean;
begin
  //Result := UxTheme.UseThemes and (GetFileVersion(comctl32) >= ComCtlVersionIE6);
  Result:= False;  // Added by Avishai
end; 

{====== C:\lazarus\lcl\LclType.pas ======}
{=========== Around Line 952 ============}
 
  RightToLeftLayout = $400000;  // Added by Avishai;
  NoInheritLayout = $00100000;  // Added by Avishai;

{====== C:\lazarus\lcl\include\CustomForm.inc ======}
{================ Around Line 2030 =================}

procedure TCustomForm.CreateParams(var Params : TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do
  begin
    if (Parent = nil) and (ParentWindow = 0) then
      ...
  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;

Code: [Select]
Avishai's InitForm

procedure InitForm(AForm: TForm; Lang: String);
var
  I: Integer;
begin
  Application.BidiMode:= Application.Direction(Lang);
  if Application.IsRightToLeft then begin
    for I:= 0 to AForm.ComponentCount-1 do begin
      if (AForm.Components[I].Name='pnlToolBar') then begin // Workaround
        with TPanel(AForm.Components[I]) do begin
          LtrCtrl(AForm.Components[I]);
          TPanel(AForm.Components[I]).FlipChildren(True);
        end;
      end else
      if (AForm.Components[I] is TComboBox) then begin      // Workaround
        LtrCtrl(AForm.Components[I]);
        TControl(AForm.Components[I]).BiDiMode:= bdRightToLeftNoAlign;
      end else
      if (AForm.Components[I] is TBitBtn) then begin        // Workaround
        LtrCtrl(AForm.Components[I]);
        TControl(AForm.Components[I]).BiDiMode:= bdLeftToRight;
        TControl(AForm.Components[I]).BiDiMode:= bdRightToLeft;
      end else
      if (AForm.Components[I] is TButtonPanel) then begin   // Workaround
        RtlButtonPanel(AForm.Components[I] as TButtonPanel);
      end else
        RtlCtrl(AForm.Components[I]);
      AForm.Caption:= StringReplace(AForm.Caption,'Form',SysToUTF8('טופס'),[]);
      case UpperCase(Lang) of
        'AR': SetKBD(Ar_KBD);  // Arabic Keyboard
        'FA': SetKBD(Fa_KBD);  // Farsi Keyboard
        'HE': SetKBD(He_KBD);  // Hebrew Keyboard
        'UR': SetKBD(Ur_KBD);  // Urdu Keyboard
      end;
    end;
  end;
end;

procedure RTLCtrl(ACtrl: TObject);
{ Make TWinControl RightToLeft - MSWindows ONLY! }
begin
{$IfDef MSWindows}
  if ACtrl is TWinControl then
    with TWinControl(ACtrl) do begin
      SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE)
        or RightToLeftLayout or NoInheritLayout);
  end;
{$EndIf}
end;

function SetKBD(LangID: HKL): Word;
begin
{$IfDef MSWindows}
  if not(ActivateKeyboardLayout(LangID, $0)) then
    LoadKeyboardLayoutA(PChar(IntToHex(LangID,8)),KLF_ACTIVATE);
  CurrentLang:= GetKBD;
  Result:= CurrentLang;
{$EndIf}
end;

I use TApplication.BiDiMode only as a trigger.  But with the changes I have made, all Forms will be RightToLeft, including Dialog boxes that are not designed to handle RightToLeft.
« Last Edit: April 06, 2013, 02:40:10 pm by Avishai »
Lazarus Trunk / fpc 2.6.2 / Win32

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4715
  • I like bugs.
Re: Help with MSWindows and RightToLeft Images
« Reply #25 on: April 06, 2013, 04:06:16 pm »
Q: Why you must set EXSTYLE with SetWindowLong?
We are dealing with native Windows controls. They should obey the RightToLeftLayout setting of Windows itself.
Is there such setting in Windows at all? Maybe it is always the application's duty to change the components' appearance. I don't know. Or, is this a Windows version dependent issue?

You use FlipChildren method for a Panel. What about Form? Can you use FlipChildren also for it?
FlipChildren does not seem very robust. It handles Alignment ok but not anchors. It must be improved if you use it for RightToLeftLayout.

You should make your code more object oriented. Now you have a global InitForm() with if..then..else blocks.
The component specific code should be integrated in the components instead.

I don't know enough about LCL or the BiDi issues to give advice. You must finally create a patch to attract interest from any LCL developer.

Regards,
Juha
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

Avishai

  • Hero Member
  • *****
  • Posts: 1021
Re: Help with MSWindows and RightToLeft Images
« Reply #26 on: April 06, 2013, 04:49:20 pm »
I'm not sure I know enough to answer all of your questions, but I'll answer the best I can. :) 

I'm not sure when EXSTYLE was introduced in MSWindows.  But I think RightToLeftLayout was introduced in Vista sp2.  So I guess it is a windows version issue.

I could avoid using FlipChildren all together but because I'm forced to Design LeftToRight (for now) I use it as a workaround.  I could set the buttons to the right at design-time but it looks confusing to me when the rest of the Form is LeftToRight at design-time.  Also, all of the code I show is still experimental at this stage, so until I have all the pieces working, it doesn't make sense to spend more time making objects.

You may have noticed that I activate RightToLeftLayout only at run-time.    I could mirror the Form at design-time, but something in Lazarus is broken there as well.  Example:  Add a TPanel to a TForm then add a TEdit to the TPanel.  It looks right, but for some unknown reason the TEdit.Parent is the TForm and you must change the Parent manually.  Doable, but not practical.

All of the issues I have found seem to be because Lazarus followed Delphi and Delphi never really designed to handle RightToLeft.  The result 'appears' to be that Lazarus wasn't developed to handle it either, which is understandable.  It wasn't that long ago that RightToLeft on computers wasn't doable at all so almost all development was done in the Western world.  Now things are changing.
Lazarus Trunk / fpc 2.6.2 / Win32

Ocye

  • Hero Member
  • *****
  • Posts: 518
    • Scrabble3D
Re: Help with MSWindows and RightToLeft Images
« Reply #27 on: April 09, 2013, 11:14:01 am »
I really appreciate your effort. Because fpc/lcl is cross platform the solution should work for Linux and MacOS too. I never tried to switch my desktop because reading r2l is even more difficult to me as l2r for you. But I added r2l support to my application just by form1.bidimode:=, form2.bidimode... etc. And this works for Linux (qt and gtk) in the same way as for Windows. That means at least toolbar, pagecontrol, and statusbar are not flipped.
I would support Juha's idea of a TApplication property. You should consider anchoring too (I place controls at 8px left and 8px top which looks weird when flipped), but as well other localization aspects.
Lazarus 1.7 (SVN) FPC 3.0.0

Avishai

  • Hero Member
  • *****
  • Posts: 1021
Re: Help with MSWindows and RightToLeft Images
« Reply #28 on: April 09, 2013, 11:33:40 am »
Thank Ocye, it's always good to get input from you :)  And the information is good.  I wanted to know how things work on Linux and other OS's.  Now I know, at least for Lazarus and Linux.  I wonder if it's the same for other IDE's?  Is there something like MS Visual Studio for Linux?  It would be interesting to know what would happen there.

As for anchoring, the approach I am using takes care of that.  Things are still anchored to the Left, but because the canvas is flipped, Left is now Right and Right is now Left.  Confusing, I know :)  I think that's why MS changed to Location.x and Location.y.  Thanks for taking the time to look into it and letting me know.
Lazarus Trunk / fpc 2.6.2 / Win32

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4715
  • I like bugs.
Re: Help with MSWindows and RightToLeft Images
« Reply #29 on: April 09, 2013, 02:39:53 pm »
I'm not sure when EXSTYLE was introduced in MSWindows.  But I think RightToLeftLayout was introduced in Vista sp2.  So I guess it is a windows version issue.

Ok, it is supported in recent versions only.
Maybe you didn't understand my question about EXSTYLE and SetWindowLong.
Basically I am asking "what does RightToLeftLayout in Windows do?". Does it adjust individual controls?
If yes, then you don't need to do it in your application or library.
If no, then what is it good for as it does nothing apparently?


Quote
You may have noticed that I activate RightToLeftLayout only at run-time.    I could mirror the Form at design-time, but something in Lazarus is broken there as well.  Example:  Add a TPanel to a TForm then add a TEdit to the TPanel.  It looks right, but for some unknown reason the TEdit.Parent is the TForm and you must change the Parent manually.  Doable, but not practical.

The designer calculates the control positions dynamically.
If you mirror controls inside a TCustomControl handler, it very likely breaks things (as it did).
The right design is to build RightToLeft support into LCL layout engine, as I explained earlier.

Quote
All of the issues I have found seem to be because Lazarus followed Delphi and Delphi never really designed to handle RightToLeft.  The result 'appears' to be that Lazarus wasn't developed to handle it either, which is understandable.  It wasn't that long ago that RightToLeft on computers wasn't doable at all so almost all development was done in the Western world.  Now things are changing.

Now, studying Lazarus code, BiDiMode already has lots of support.
Look for method "SetBiDiMode". It is implemented for many widgetsets, for example for TWin32WSWinControl in Windows:

Code: [Select]
class procedure TWin32WSWinControl.SetBiDiMode(const AWinControl : TWinControl;
  UseRightToLeftAlign, UseRightToLeftReading, UseRightToLeftScrollBar : Boolean);
var
  FlagsEx: dword;
begin
  if not WSCheckHandleAllocated(AWinControl, 'SetBiDiMode') then
    Exit;
  FlagsEx := GetWindowLong(AWinControl.Handle, GWL_EXSTYLE);
  FlagsEx := FlagsEx and not (WS_EX_RTLREADING or WS_EX_RIGHT or WS_EX_LEFTSCROLLBAR);
  if UseRightToLeftAlign then
    FlagsEx := FlagsEx or WS_EX_RIGHT;
  if UseRightToLeftReading then
    FlagsEx := FlagsEx or WS_EX_RTLREADING ;
  if UseRightToLeftScrollBar then
    FlagsEx := FlagsEx or WS_EX_LEFTSCROLLBAR;
  SetWindowLong(AWinControl.Handle, GWL_EXSTYLE, FlagsEx);
end;

It clearly does the same what your global function does and more.
You wrote that BiDiMode is not for the same purpose as RightToLeftLayout. I am still saying that they are for the same purpose but BiDiMode is poorly implemented.
Or, can you point a use case where the current BiDiMode should be used instead of your new RightToLeftLayout?
For example what should TForm.BiDiMode do? I guess it should always mirror the layout. (?)
Its current behavior can be considered a bug and should be fixed, even it is not Delphi compatible then.

A flag in TApplication might be more logical but BiDiMode and ParentBiDiMode are not bad either if they worked correctly.
If you implement a new flag, say TApplication.RightToLeftLayout which directly competes and overlaps with BiDiMode, I believe any depeloper will ask the same question as I do:
  Why not improve BiDiMode instead?

If they had different purposes then they both should exist. From your writing I understood BiDiMode is simply broken and not useful as it is. IMO the logical solution is to improve it.

I got the impression you have not studied the code in Lazarus and LCL much before hacking your functions.
The problem you try to solve is so integral part of LCL that you really must study it.
Otherwise you end up doing some ugly hack, but later realize it was already implemented properly somewhere else. I know the feeling, I have done it many times myself.

Everybody can create new code but undertanding and modifying existing code is a challenge. (...or let's say everybody wants to create new code).
It is important to keep the design good. In a big project it is especially important.
Quick hacks tend to cumulate. Later you must do a new hack on top of old hack ...

Quote
As for anchoring, the approach I am using takes care of that.  Things are still anchored to the Left, but because the canvas is flipped, Left is now Right and Right is now Left.

Earlier I understood it created a problem by flipping images (?)

BTW, there is no MS Visual Studio for Linux. Microsoft likes its own OS only.
I still believe RightToLeft support is built into .NET. You can check if Mono has it, too.

Juha

« Last Edit: April 09, 2013, 05:05:09 pm by JuhaManninen »
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

 

TinyPortal © 2005-2018