Recent

Author Topic: adding BiDiMode to Control  (Read 440 times)

lazdeveloper

  • New Member
  • *
  • Posts: 14
adding BiDiMode to Control
« on: November 22, 2019, 12:16:49 am »
Pls execus me if the question is simple.
I am trying to add BiDiMode to TRLMemo in fortesreport-ce  as described in https://wiki.lazarus.freepascal.org/BidiMode
I edited the file RLReport.pas found in
https://github.com/fortesinformatica/fortesreport-ce/blob/master/Source/RLReport.pas

I have added the two properties
property BiDiMode: TBiDiMode read FBiDiMode write SetBiDiMode stored IsBiDiModeStored;
property ParentBiDiMode: Boolean read FParentBiDiMode write SetParentBiDiMode default True;
 Then I have added them to the published section.
Complied the package and I can see fortes report with the RlMemo
 component shows the two properties.
However, adjusting the values ie righttoleft or left to right has no effect on both design timer run time.
I placed a breakpoints.. traced the execution of the code. I was able to see that the function I.e. rightoleft behaves exactly as that of Lazarus tmemo. But the text on the TRLMemo does not act as that of normal TMemo. What do you think i am doing wrong?
Thanks

wp

  • Hero Member
  • *****
  • Posts: 6471
Re: adding BiDiMode to Control
« Reply #1 on: November 22, 2019, 12:32:48 am »
I don't know this particular memo. But I would guess that you must rewrite the painting routine such that it draws from right to left. This is not done automatically. The BiDiMode properties that you published are just state variables. The memo must read them and paint itself accordingly. As an example you could study the Lazarus grids which maybe are more complex but painting is fully accessible here and not hidden by the widgetset. Just search the source code of grids.pp (in folder lcl for your lazarus installation) for "BiDiMode" and look what happens at each occurence.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

lazdeveloper

  • New Member
  • *
  • Posts: 14
Re: adding BiDiMode to Control
« Reply #2 on: November 22, 2019, 08:27:57 am »
Thanks.. I will try that for sure.
The TRLMemo is from fortesreport-ce . It is a report tool for Lazarus. you can install the package easily using online package Manager of lazarus.
Regarding the painting routine: No paint routine is mentioned in the wiki page of lazarus that shows how to add BiDiMode to a control item https://wiki.lazarus.freepascal.org/BidiMode.
I guess that repainting is done in the ancesstor class?

m.abudrais

  • New Member
  • *
  • Posts: 47
Re: adding BiDiMode to Control
« Reply #3 on: November 22, 2019, 08:54:43 am »
I guess that repainting is done in the ancesstor class?
not all controls need rewriting repainting  routine, because some like Tbutton are painted by the OS, so you need to tell the OS to Draw Them as RTL, but other need, Like TStringGrid because they are painted  by LCL.
 bug Tracker has good examples:
 https://bugs.freepascal.org/view.php?id=8996
https://bugs.freepascal.org/view.php?id=28867
https://bugs.freepascal.org/view_all_bug_page.php?filter=5dd7923225c66
« Last Edit: November 22, 2019, 08:57:10 am by m.abudrais »

lazdeveloper

  • New Member
  • *
  • Posts: 14
Re: adding BiDiMode to Control
« Reply #4 on: November 22, 2019, 09:50:53 am »
Many thanks for the replies.
I have done the repaint as for the curiosity as
RLmemo1. Repaint;
After setting its bidimode to RightToLeft. After repaint nothing happend! The text was the same.

m.abudrais has kindly shows another way around it. To send a message to the component telling it to change its bidimode. I have done this code taken from the first link mentioned by by m.abudrais
procedure TCustomForm.CMBiDiModeChanged();
var
  lMessage:TLMessage;
begin
  inherited;
 
  lMessage.msg := CM_PARENTBIDIMODECHANGED;
  lMessage.wParam := 0;
  lMessage.lParam := 0;
  lMessage.Result := 0;
  // HERE we order the component to change its BiDimode
  RLMemo1.Dispatch(lMessage);
end;

I call it by a button click. But again the RLMemo of FortesReport Package did not impact and its text direction is the same.

wp

  • Hero Member
  • *****
  • Posts: 6471
Re: adding BiDiMode to Control
« Reply #5 on: November 22, 2019, 10:43:06 am »
I guess that repainting is done in the ancesstor class?
I installed FortesReportCE and had a look at TRLCustomMemo. It does not have its own Paint method, but its ancestor, TRLCustomMultiLine, has. This Paint method calls a "MemoDraw" which is a stand-alone procedure (not a method). It does not get any BiDi-related parameter which makes me suspect that this library is not prepared for BiDi at all. But maybe we are lucky and it is sufficient to mirror the Alignment parameter:

Code: Pascal  [Select]
  1. procedure TRLCustomMultiLine.Paint;
  2. var
  3.   R: TRect;
  4.   S: string;
  5.   P: TRLCustomReport;
  6.   algnmt: TRLTextAlignment;
  7. begin
  8.   inherited;
  9.  
  10.   S := Caption;
  11.   if (S = '') and not IsPreparing then
  12.     S := Name;
  13.   R := GetClientRect;
  14.   with Canvas do
  15.   begin
  16.     Font := Self.Font;
  17.     if IsTransparent(Self) then
  18.       Brush.Style := bsClear
  19.     else
  20.     begin
  21.       Brush.Style := bsSolid;
  22.       Brush.Color := Self.Color;
  23.     end;
  24.  
  25.     if Alignment = taLeftJustify then     // <---- wp: added ....
  26.       algnmnt := taRightJustify
  27.     else if Alignment = taRightJustify then
  28.       algnmnt := taLeftJustify
  29.     else
  30.       algnmnt := Alignment;                 // --------------->
  31.    
  32.     MemoDraw(S, Self.Canvas, algmnt, R, Self.WordWrap);   // <--- wp: Replace Self.Alignment by algnment
  33.   end;
  34.   P := FindParentReport;
  35.   if not Assigned(P) or P.ShowDesigners then
  36.     DrawBounds;
  37. end;

But I fear this is not all because the LCL Canvas must be told to draw text from right to left. I think this is what Canvas.TextStyle.RightToLeft is good for. Unfortunately Canvas.TextStyle is ignored when the text is written by Canvas.TextOut (Canvas.TextRect is ok). Following the chain of function calls leads to CanvasTextRectEx where
Code: Pascal  [Select]
  1. procedure CanvasTextRectEx(ACanvas: TCanvas; const ARect: TRect; AX, AY: Integer; const AText: String; AAlignment: TRLMetaTextAlignment; ALayout: TRLMetaTextLayout; ATextFlags: TRLMetaTextFlags);
  2. [...]
  3.   if (ATextFlags and MetaTextFlagAutoSize) = MetaTextFlagAutoSize then
  4.     ACanvas.TextOut(left, top, buff)
  5.   else
  6.     ACanvas.TextRect(ARect, left, top, buff);
  7. end;
i.e. you must replace the TextOut call here by a TextRect call.

There are certainly several more of these traps.

Since TextStyle is a LCL-only property such changes will break the library for usage in Delphi.

None of the information given here has been tested, it even may be complete nonsense... Make a backup copy of the original files before making any changes so that you can restore the initial state in case something goes wrong badly.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

m.abudrais

  • New Member
  • *
  • Posts: 47
Re: adding BiDiMode to Control
« Reply #6 on: November 22, 2019, 10:46:38 am »
I think you should modify repaint method, because its a custom painted components.
in the RLReport.pas this is the inheritance chain
TRLMemo = class(TRLCustomMemo)
 TRLCustomMemo = class(TRLCustomMultiLine)

the drawing is done in this procedure:
procedure TRLCustomMultiLine.Paint;
procedure MemoDraw( //line num 9004
procedure RunMemo //line num 8957
modify the  procedures to support RTL and rebuild the package.

 

lazdeveloper

  • New Member
  • *
  • Posts: 14
Re: adding BiDiMode to Control
« Reply #7 on: November 22, 2019, 02:27:08 pm »
Wp and m.abudrais thank you so much. Your comments provide deep insight.
 I am working on them right now to see if this can remedy the BiDiMode issue

lazdeveloper

  • New Member
  • *
  • Posts: 14
Re: adding BiDiMode to Control
« Reply #8 on: November 22, 2019, 03:18:14 pm »
I have implemented wp suggestions.
The mirrioring work great but unfortunately it was not the same BiDIMode behaviour. 
According to lazarus wiki
Quote
How to test RightToLeft reading
Create a new form, then add a TButton or TEdit. Set the caption to OK?. You must add ? or ! or . to the last word without a space. Now set the BidiMode to bdRightToLeft, and you should see the ? in the left of word if it works.
By the way how BiDImode is implemented from scratch if someone will not
Inherit from an ancesstor which already got it?

wp

  • Hero Member
  • *****
  • Posts: 6471
Re: adding BiDiMode to Control
« Reply #9 on: November 22, 2019, 04:00:43 pm »
Since Fortes implements its own drawing routines

By the way how BiDImode is implemented from scratch if someone will not
Inherit from an ancesstor which already got it?
I think you must investigate how Fortes does the text rendering; I think, in the end it is CanvasTextRectEx in unit RLMetaVCL. Before the last "if" you must merge in the code for RTL. Maybe like this:

Code: Pascal  [Select]
  1. procedure CanvasTextRectEx(ACanvas: TCanvas; const ARect: TRect; AX, AY: Integer; const AText: String;
  2.   AAlignment: TRLMetaTextAlignment; ALayout: TRLMetaTextLayout; ATextFlags: TRLMetaTextFlags; ARightToLeft:Boolean);   // ARightToLeft added
  3. var
  4.   ts: TTextStyle;
  5. begin
  6.   ...
  7.   ts := ACanvas.TextStyle;
  8.   ts.RightToLeft := ARightToLeft;
  9.   if (ATextFlags and MetaTextFlagAutoSize) = MetaTextFlagAutoSize then
  10.     //ACanvas.TextOut(left, top, buff)    // this cannot be used because it ignores TextStyle
  11.     ACanvas.TextRect(ARect, left, top, buff, ts)    // not sure about ARect...
  12.   else
  13.     ACanvas.TextRect(ARect, left, top, buff, ts);   // add "ts"

Since this code introduces an additional parameter you must update all occurences of this function. The calling function must set ARightToLeft according to BiDiMode (ARightToLeft := BiDiMode = rtRightToLeft)

Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

lazdeveloper

  • New Member
  • *
  • Posts: 14
Re: adding BiDiMode to Control
« Reply #10 on: November 22, 2019, 04:46:22 pm »
Many thanks wp. I am implementing it and will report the result - many thanks again.

lazdeveloper

  • New Member
  • *
  • Posts: 14
Re: adding BiDiMode to Control
« Reply #11 on: November 22, 2019, 05:53:23 pm »
Hi wp,
I can see under your name a label says "Hero member".
This is very true. You are really a hero. I've implemented your suggestion after stopping the mirroring
and it went right and it works very well. Many thanks.

One thing was not behaving as it should is the text alignment
The RLMemo and other text controls in RLReport package have text alignment as:

TRLTextAlignment = (taLeftJustify, taRightJustify, taCenter, taJustify);

When it set to taJustify, then if a half line is at the end of the text, then this half line should go
From right to left as well. It is now bizzar and comes from left to right as if the justify is set to taLeft
which is not correct behaviour.

Thank you so much again.


wp

  • Hero Member
  • *****
  • Posts: 6471
Re: adding BiDiMode to Control
« Reply #12 on: November 22, 2019, 06:18:54 pm »
"Hero member".
This is just related to the number of posts, nothing "heroic".

One thing was not behaving as it should is the text alignment
The RLMemo and other text controls in RLReport package have text alignment as:

TRLTextAlignment = (taLeftJustify, taRightJustify, taCenter, taJustify);

When it set to taJustify, then if a half line is at the end of the text, then this half line should go
From right to left as well. It is now bizzar and comes from left to right as if the justify is set to taLeft
which is not correct behaviour.
I suppose taJustify means expanding the spaces such that left and right edges of text blocks are aligned. This feature is not available in the standard LCL canvas. Therefore, Fortes must have implemented it by themselves, i.e. calculating the total width of the words in each line and adjusting the space width to the difference of the block width to the total word width divided by the word count (minus 1). Ignoring BiDi, I guess they are painting words from left to right, but with RTL probably the words must be painted from right to left - but maybe I am wrong, this is very confusing, and I don't live in a RTL country. I can only encourage you to study the source code of the text drawing objects. Maybe search for "taJustify" and see what they are doing there.

One thing should be mentioned: I found this github: https://github.com/fortesinformatica/fortesreport-ce. Ideally you should clone this repository, apply your changes and create a pull-request for the authors; this way your changes can be incorporated into the official sources and will not get lost.  But note that Fortes Report is not LCL-only, they support Delphi, too, and some of your changes are incompatible with Delphi. You must enclose such code by an {$IFDEF FPC}...{$ENDIF}
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

lazdeveloper

  • New Member
  • *
  • Posts: 14
Re: adding BiDiMode to Control
« Reply #13 on: November 22, 2019, 06:30:33 pm »
Thank you very much indeed. I am going to study that for sure.
As well, I will do the github stuff as you suggested.
Thanks.
Wish you all the best.