RichMemo LineSpacing does not work in Linux? lazarus / 8-Other ---------------------------------------------------------------------- paxnet_be · 2024-01-23 13:23 Hi, I tested in RichMemo both procedures SetParaMetric and SetRangeParaParams with LineSpacing. Both procedures works well in Windows, but not in Linux. What did I miss? See here the code: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit uLineSpacing; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Menus,  RichMemo; type   { TForm1 }   TForm1 = class(TForm)    Button1: TButton;    RatioSingle: TMenuItem;    RatioOneHalf: TMenuItem;    RatioDouble: TMenuItem;    PMLineSpacing: TPopupMenu; // PM = PopMenu    RichMemo1: TRichMemo;    procedure Button1Click(Sender: TObject);    procedure RatioSingleClick(Sender: TObject);    procedure RatioOneHalfClick(Sender: TObject);    procedure RatioDoubleClick(Sender: TObject);  private    ParagraphMetric: TParaMetric;  public   end; var  Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.Button1Click(Sender: TObject);begin  {    PopUp menu with 3 items:    1.0: OnClick --> RatioSingleClick    1.5: OnClick --> RatioOneHalfClick    2.0: OnClick --> RatioDoubleClick  }  PMLineSpacing.PopUp;end; procedure TForm1.RatioSingleClick(Sender: TObject);begin  InitParaMetric(ParagraphMetric);  with RichMemo1 do begin    GetParaMetric(SelStart, ParagraphMetric);    ParagraphMetric.LineSpacing := DefLineSpacing;    ShowMessage('Line Space:' + #13 +                'SelStart = ' + IntToStr(SelStart) + #13 +                'SelLength = ' + IntToStr(SelLength) + #13 +              'LineSpacing = ' + FloatToStr(ParagraphMetric.LineSpacing));    {      The use of SetParaMetric or SetRangeParaParams works both well      in Windows, but not in Linux.    }    SetParaMetric(SelStart, SelLength, ParagraphMetric);    //SetRangeParaParams(SelStart, SelLength, [pmm_LineSpacing], ParagraphMetric);  end;end; procedure TForm1.RatioOneHalfClick(Sender: TObject);begin  InitParaMetric(ParagraphMetric);  with RichMemo1 do begin    GetParaMetric(SelStart, ParagraphMetric);    ParagraphMetric.LineSpacing := DefLineSpacing * 1.5;    ShowMessage('One Half Line Space:' + #13 +                'SelStart = ' + IntToStr(SelStart) + #13 +                'SelLength = ' + IntToStr(SelLength) + #13 +                'LineSpacing = ' + FloatToStr(ParagraphMetric.LineSpacing));    {      The use of SetParaMetric or SetRangeParaParams works both well      in Windows, but not in Linux.    }    SetParaMetric(SelStart, SelLength, ParagraphMetric);    //SetRangeParaParams(SelStart, SelLength, [pmm_LineSpacing], ParagraphMetric);  end;end; procedure TForm1.RatioDoubleClick(Sender: TObject);begin  InitParaMetric(ParagraphMetric);  with RichMemo1 do begin    GetParaMetric(SelStart, ParagraphMetric);    ParagraphMetric.LineSpacing := DefLineSpacing * 2;    ShowMessage('Double Line Space:' + #13 +                'SelStart = ' + IntToStr(SelStart) + #13 +                'SelLength = ' + IntToStr(SelLength) + #13 +                'LineSpacing = ' + FloatToStr(ParagraphMetric.LineSpacing));    {      The use of SetParaMetric or SetRangeParaParams works both well      in Windows, but not in Linux.    }    SetParaMetric(SelStart, SelLength, ParagraphMetric);    //SetRangeParaParams(SelStart, SelLength, [pmm_LineSpacing], ParagraphMetric);  end;end; end.  Thanks in advance for your comments. -------------------------- Lazarus 3.0 (rev lazarus_3_0) FPC 3.2.2 x86_64-linux-gtk2 ---------------------------------------------------------------------- Thaddy · 2024-01-23 14:39 :'(Change #13 to LineSpacing,  or sLinebreak which is a default constant per platform.  Under windows it is #13#10, under linux it is just #10. ---------------------------------------------------------------------- paxnet_be · 2024-01-23 14:56 Sorry Thaddy, I was meaning the vertical line spacing, not the horizontal. #13 is just for ShowMessage, but you are write about it. ---------------------------------------------------------------------- rvk · 2024-01-23 17:14 Quote from: Thaddy on January 23, 2024, 03:39:28 pm :'(Change #13 to LineSpacing,  or sLinebreak which is a default constant per platform.  Under windows it is #13#10, under linux it is just #10. You are correct for the #13, but you might have missed that this is only used for the ShowMessage, NOT for the RichMemo itself. @paxnet_be The linespacing is done correctly WITHIN the paragraph on Linux/TRichMemo. But when using linebreak (enter) you begin a new paragraph. If you, for example, type a very long line and let it wrap a few times, then select that text/paragraph and press your button, you'll see the linespacing change. So the TRichMemo interprets the linespacing different for Windows vs Linux (because there it's for every linebreak including inside a paragraph). (You could try to play with ParagraphMetric.SpaceBefore but you'll need to set that per paragraph, you can't do that for a selection and have it be for every line.) ---------------------------------------------------------------------- paxnet_be · 2024-01-24 05:52 Thank you, RVK, I tried with word wrapping, and indeed it works that way. So the different interpretation in Windows and Linux is what I missed. Case resolved.  Now I have to figure out, how I could apply it with several paragraphs. ---------------------------------------------------------------------- rvk · 2024-01-24 08:29 Quote from: paxnet_be on January 24, 2024, 06:52:42 amNow I have to figure out, how I could apply it with several paragraphs.  Actually, setting ParagraphMetric.SpaceBefore := 15 * DefLineSpacing did apply to all paragraphs for me (selecting multiple lines/paragraphs). (I used 15 as an example for fontpixelsize which is used in setting ParagraphMetric.LineSpacing) But when setting it back to 0 it somehow switched the SpaceBefore to SpaceAfter. Not sure why, could be due to the GetParaMetric at SelStart. (Doing it multiple times it did get back to 0  ) The relevant code for setting the paragraph in Linux is: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   tag := gtk_text_buffer_create_tag (buffer, nil,      'pixels-above-lines',   [ gint(round(AMetric.SpaceBefore*DPIFactor)),      'pixels-above- lines-set', gboolean(gTRUE),      'pixels-below-lines',     gint(round(AMetric.SpaceAfter*DPIFactor)),      'pixels-below-lines- set', gboolean(gTRUE),      'left-margin',            gint(round(h*DPIFactor)),      'left-margin-set',        gboolean(gTRUE),      'right-margin',           gint(round(AMetric.TailIndent*DPIFactor)),      'right-margin-set',     gboolean(gTRUE),      'indent',                 gint(round(fl*DPIFactor)),      'indent-set',             gboolean(gTRUE),      'pixels-inside-wrap',     gint((round(fp.Size*(AMetric.LineSpacing- DefLineSpacing)*DPIFactor))),      'pixels-inside_wrap-set', gboolean(gTRUE),      nil]);You can find documentation for gtktexttag everywhere. http://irtfweb.ifa.hawaii.edu/SoftwareDocs/gtk20/gtk/gtktexttag.html See the fp.Size* in pixels-inside-wrap but not in pixels-above-lines. So SpaceBefore and SpaceAfter are in pixels. You need to multiply them with the DefLineSpacing * FontPixelHeight. Further... for Linux you need to apply both the SpaceBefore (for the paragraph) AND LineSpacing (for inside the paragraph) to get what you want, where on Windows just LineSpacing would suffice (so you need to work with {$IFDEF LINUX} for that). Happy coding ---------------------------------------------------------------------- paxnet_be · 2024-02-14 16:10 Thanks to you RVK, for the GTK-tips. Before I was able to apply them, I had to teach myself a little bit about GTK. After searching for information, I found a GTK manual for Free Pascal at:   https://www.mimuw.edu.pl/~zbyszek/pgtk/html/contents.html All examples works with the FP-IDE (some with minor changes), except for the calendar, wich gives GTK-crtical errors, that I was not able to fix with my knowledge as a novice. Then I applied your tips in my Lazarus program, without success, until ... I copied the values of SelStart and SelLength into a local variable and then passed these to the function SetParaMetric... Finally, it turned out, that GTK calls are not required in Linux, only (local) variables with the values of SelStart and SelLength are needed. Why I don't know.  Here the working code:  Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit uLineSpacing; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Menus,  RichMemo; type   { TForm1 }   TForm1 = class(TForm)    Button1: TButton;    RatioSingle: TMenuItem;    RatioOneHalf: TMenuItem;    RatioDouble: TMenuItem;    PMLineSpacing: TPopupMenu; // PM = PopupMenu    RichMemo1: TRichMemo;    procedure Button1Click(Sender: TObject);    procedure RatioSingleClick(Sender: TObject);    procedure RatioOneHalfClick(Sender: TObject);    procedure RatioDoubleClick(Sender: TObject);  private    ParagraphMetric: TParaMetric;  public   end; const  DPIFactor = 1.3333; // = 96 / 72 (Unit = Pixel / Point)var  Form1: TForm1;  //Tag: PGtkTextTag;  //Buffer: PGtkTextBuffer; implementation {$R *.lfm} { TForm1 } procedure TForm1.Button1Click(Sender: TObject);begin  {    PopUp menu with 3 items:    1.0: OnClick --> RatioSingleClick    1.5: OnClick --> RatioOneHalfClick    2.0: OnClick --> RatioDoubleClick  }  PMLineSpacing.PopUp;end; procedure TForm1.RatioSingleClick(Sender: TObject);var  RMTextStart, RMTextLen: Integer;begin  InitParaMetric(ParagraphMetric);  with RichMemo1 do begin    GetParaMetric(SelStart, ParagraphMetric);    {      The function SetParaMetric does not use the variables      SelStart and SelLength properly in Linux (why?),      so we save their values in local variables:    }    RMTextStart := SelStart;    RMTextLen := SelLength;    ParagraphMetric.LineSpacing := DefLineSpacing; // Single ratio    ParagraphMetric.SpaceBefore := ParagraphMetric.LineSpacing;  SetParaMetric(RMTextStart, RMTextLen, ParagraphMetric);  end;end; procedure TForm1.RatioOneHalfClick(Sender: TObject);var  RMTextStart, RMTextLen: Integer;begin  InitParaMetric(ParagraphMetric);  with RichMemo1 do begin    GetParaMetric(SelStart, ParagraphMetric);    {      The function SetParaMetric does not use the variables      SelStart and SelLength properly in Linux (why?),      so we save their values in local variables:    }    RMTextStart := SelStart;    RMTextLen := SelLength;    ParagraphMetric.LineSpacing := DefLineSpacing * 1.5; // One half ratio    ParagraphMetric.SpaceBefore := ParagraphMetric.LineSpacing * 2;    SetParaMetric(RMTextStart, RMTextLen, ParagraphMetric);  end;end; procedure TForm1.RatioDoubleClick(Sender: TObject);var  RMTextStart, RMTextLen: Integer;begin  InitParaMetric(ParagraphMetric);  with RichMemo1 do begin    GetParaMetric(SelStart, ParagraphMetric);    {      The function SetParaMetric does not use the variables      SelStart and SelLength properly in Linux (why?),      so we save their values in local variables:    }    RMTextStart := SelStart;    RMTextLen := SelLength;    ParagraphMetric.LineSpacing := DefLineSpacing * 2; // Double ratio    ParagraphMetric.SpaceBefore := ParagraphMetric.LineSpacing * 4;    SetParaMetric(RMTextStart, RMTextLen, ParagraphMetric);  end;end; end.  Cheers! Pax_Be