Skip to content
This repository has been archived by the owner on Dec 22, 2023. It is now read-only.

RTL (right-to-left) support #459

Closed
VPKSoft opened this issue Jun 10, 2019 · 19 comments
Closed

RTL (right-to-left) support #459

VPKSoft opened this issue Jun 10, 2019 · 19 comments

Comments

@VPKSoft
Copy link

VPKSoft commented Jun 10, 2019

Hi,
I was wondering if there is a command to support right-to-left language with the Scintilla control.
There is the possibility to set the:

scintilla.RightToLeft = RightToLeft.Yes;

value, but the text alignment doesn't change the actual text layout. Notepad++ seems to support this feature, but is it custom build or can I issue a direct message to go to full right-to-left support by issuing a command such as:

scintilla.DirectMessage(...);
@VPKSoft
Copy link
Author

VPKSoft commented Jun 11, 2019

Seems the current version doesn't support the possibility.
Just a reminder for the future:

        private const int SC_TECHNOLOGY_DEFAULT = 0;
        private const int SC_TECHNOLOGY_DIRECTWRITE = 1;
        private const int SC_TECHNOLOGY_DIRECTWRITERETAIN = 2;
        private const int SC_TECHNOLOGY_DIRECTWRITEDC = 3;

        private const int SC_BIDIRECTIONAL_DISABLED = 0;
        private const int SC_BIDIRECTIONAL_L2R = 1;
        private const int SC_BIDIRECTIONAL_R2L = 2;
        private const int SCI_GETBIDIRECTIONAL = 2708;
        private const int SCI_SETBIDIRECTIONAL = 2709;

The actual code:

            // set the technology to direct write..
            scintilla.Technology = Technology.DirectWrite;

            // command the Scintilla to go the right-to-left mode..
            scintilla.DirectMessage(SCI_SETBIDIRECTIONAL, new IntPtr(SC_BIDIRECTIONAL_R2L), IntPtr.Zero);

@VPKSoft VPKSoft closed this as completed Jun 11, 2019
@ghost
Copy link

ghost commented Jan 16, 2021

Hi @VPKSoft, now that your latest unofficial build has an updated SciLexer dll, I was wondering if it would be possible to add support for RTL text?

@VPKSoft
Copy link
Author

VPKSoft commented Jan 16, 2021

Not without more debugging it seems:
image
Here is to code though if you wish to try and solve the thing:

/// <summary>
/// Gets or sets a value indicating whether control's elements are aligned to support locales using right-to-left fonts.
/// </summary>
/// <value>The right to left.</value>
[Category("Appearance")]
[Description("Indicates whether the component should drawn right-to-left for RTL languages.")]
public override RightToLeft RightToLeft
{
    get => base.RightToLeft;
    set
    {
        if (value != base.RightToLeft)
        {
            switch (value)
            {
                case RightToLeft.Yes:
                    DirectMessage(NativeMethods.SCI_SETBIDIRECTIONAL, new IntPtr(NativeMethods.SC_BIDIRECTIONAL_R2L));
                    break;
                case RightToLeft.No:
                    DirectMessage(NativeMethods.SCI_SETBIDIRECTIONAL, new IntPtr(NativeMethods.SC_BIDIRECTIONAL_L2R));
                    break;
                case RightToLeft.Inherit:
                    if (this.Parent?.RightToLeft == RightToLeft.Yes)
                    {
                        DirectMessage(NativeMethods.SCI_SETBIDIRECTIONAL, new IntPtr(NativeMethods.SC_BIDIRECTIONAL_R2L));
                    }
                    if (this.Parent?.RightToLeft == RightToLeft.No)
                    {
                        DirectMessage(NativeMethods.SCI_SETBIDIRECTIONAL, new IntPtr(NativeMethods.SC_BIDIRECTIONAL_L2R));
                    }
                    break;
            }
            base.RightToLeft = value;
        }
    }
}

I'll probably have time in the next week or so...

See also the documentation 👍

@VPKSoft VPKSoft reopened this Jan 16, 2021
@cyber960
Copy link

The System.AccessViolationException seems to come from messing with Control.RightToLeft.

I was able to get the code below to correctly call SCI_SETBIDIRECTIONAL (as you can verify by calling SCI_GETBIDIRECTIONAL afterwards and it should return SC_BIDIRECTIONAL_R2L or SC_BIDIRECTIONAL_L2R).

Note that from the documentation, SCI_SETTECHNOLOGY must first be set to one of SC_TECHNOLOGY_DIRECTWRITE, SC_TECHNOLOGY_DIRECTWRITEDC, or SC_TECHNOLOGY_DIRECTWRITERETAIN.

While the code below runs, correctly sets the properties and doesn't throw any exceptions, it still shows the text as left-to-right. @VPKSoft any ideas?

public RightToLeft ScintillaRightToLeft
{
    get
    {
        int bidi = DirectMessage(NativeMethods.SCI_GETBIDIRECTIONAL, IntPtr.Zero, IntPtr.Zero).ToInt32();
        if (bidi == NativeMethods.SC_BIDIRECTIONAL_R2L)
            return RightToLeft.Yes;
        return RightToLeft.No; //NativeMethods.SC_BIDIRECTIONAL_L2R or NativeMethods.SC_BIDIRECTIONAL_DISABLED
    }
    set
    {
        DirectMessage(NativeMethods.SCI_SETTECHNOLOGY, new IntPtr(NativeMethods.SC_TECHNOLOGY_DIRECTWRITE));
        //DirectMessage(NativeMethods.SCI_SETTECHNOLOGY, new IntPtr(NativeMethods.SC_TECHNOLOGY_DIRECTWRITEDC));
        //DirectMessage(NativeMethods.SCI_SETTECHNOLOGY, new IntPtr(NativeMethods.SC_TECHNOLOGY_DIRECTWRITERETAIN));

        switch (value)
        {
            case RightToLeft.Yes:
                DirectMessage(NativeMethods.SCI_SETBIDIRECTIONAL, new IntPtr(NativeMethods.SC_BIDIRECTIONAL_R2L));
                break;
            case RightToLeft.Inherit when Parent != null:
                DirectMessage(NativeMethods.SCI_SETBIDIRECTIONAL, new IntPtr(Parent.RightToLeft == RightToLeft.Yes ? NativeMethods.SC_BIDIRECTIONAL_R2L : NativeMethods.SC_BIDIRECTIONAL_L2R));
                break;
            default:
                DirectMessage(NativeMethods.SCI_SETBIDIRECTIONAL, new IntPtr(NativeMethods.SC_BIDIRECTIONAL_L2R));
                break;
        }
    }
}

@VPKSoft
Copy link
Author

VPKSoft commented Feb 19, 2021

Hi,
Here is what I have gathered. The previous exception in the screen show was thrown because the control didn't have an handle created. Somehow this seems to be effective, because the control reports having R2L.
The property implementation:

  // TODO::When solved why this is not working.
  /// <summary>
  /// Gets or sets a value indicating whether control's elements are aligned to support locales using right-to-left fonts.
  /// </summary>
  /// <value>The right to left.</value>
  [Category("Appearance")]
  [Description("Indicates whether the component should drawn right-to-left for RTL languages.")]
  public override RightToLeft RightToLeft
  {
      get
      {
          // Prevent the protected memory read error.
          if (!IsHandleCreated || IsDisposed)
          {
              return RightToLeft.No;
          }

          var result = (int)DirectMessage(NativeMethods.SCI_GETBIDIRECTIONAL, IntPtr.Zero);
          switch (result)
          {
              case NativeMethods.SC_BIDIRECTIONAL_L2R: 
                  return RightToLeft.No;
              case NativeMethods.SC_BIDIRECTIONAL_R2L: 
                  return RightToLeft.Yes;
          }

          return RightToLeft.No;
      } 
          
      set
      {
          // Prevent the protected memory read error.
          if (!IsHandleCreated || IsDisposed)
          {
              return;
          }

          if (value != RightToLeft)
          {
              switch (value)
              {
                  case RightToLeft.Yes:
                      DirectMessage(NativeMethods.SCI_SETBIDIRECTIONAL,
                          new IntPtr(NativeMethods.SC_BIDIRECTIONAL_R2L));
                      break;
                  case RightToLeft.No:
                      DirectMessage(NativeMethods.SCI_SETBIDIRECTIONAL,
                          new IntPtr(NativeMethods.SC_BIDIRECTIONAL_L2R));
                      break;
                  case RightToLeft.Inherit:
                      if (Parent?.RightToLeft == RightToLeft.Yes)
                      {
                          DirectMessage(NativeMethods.SCI_SETBIDIRECTIONAL,
                              new IntPtr(NativeMethods.SC_BIDIRECTIONAL_R2L));
                      }

                      if (Parent?.RightToLeft == RightToLeft.No)
                      {
                          DirectMessage(NativeMethods.SCI_SETBIDIRECTIONAL,
                              new IntPtr(NativeMethods.SC_BIDIRECTIONAL_L2R));
                      }

                      break;
              }
          }
      }
  }

The open file method:

  private void mnuOpen_Click(object sender, EventArgs e)
  {
      if (odFile.ShowDialog() == DialogResult.OK)
      {
          scintilla.RightToLeft = RightToLeft.Yes;
          scintilla.Text = File.ReadAllText(odFile.FileName);
          scintilla.EmptyUndoBuffer();

          MessageBox.Show(scintilla.RightToLeft.ToString());
      }
  }

After opening the document and querying the value:
image

There is something obvious that I'm missing. The weird thing is the horizontal scroll bar event though the document is "fixed width":
image

@VPKSoft
Copy link
Author

VPKSoft commented Feb 19, 2021

I committed the non-working changes to the fork as a base if a solution is found. I also thought about the Margins as there is right margin also - this might have something to do with the case 🤔

@cyber960
Copy link

The scrollbar visibility issue can be fixed by setting the following as per #10:

scintilla.ScrollWidth = 1;
scintilla.ScrollWidthTracking = true;

I also posted this issue in the Scintilla bug tracker to see if we can get some extra help: https://sourceforge.net/p/scintilla/bugs/2233/

@cyber960
Copy link

@VPKSoft we got a response in the Scintilla bug tracker.

First off, the Scintilla feature for bidirectional text does not right align the text like you would expect in a WinForms TextBox control. Instead it changes the behvior of the caret when the arrow keys are used when there is a mixture of left-to-right and right-to-left text like in the string "hello world in Hebrew: שלום עולם!".

There are 3 different settings:

  1. SC_BIDIRECTIONAL_DISABLED: the caret moves according to the logical position of the string
  2. SC_BIDIRECTIONAL_L2R: the caret moves acording to the bidi display algorithm. This seems consistent with the behvaior in a WinForms TextBox with RightToLeft set.
  3. SC_BIDIRECTIONAL_R2L: Appears to have the same functionality as SC_BIDIRECTIONAL_L2R, but is incomplete because the developer didn't have enough time to complete it.

See this quote from the Scintilla issue tracker:
The main goal was to present and edit R2L text more correctly within an L2R document as most languages use ASCII keywords with R2L mostly appearing in quoted strings and comments. The contributors worked on SC_BIDIRECTIONAL_R2L but ran out of time before finishing it.

So we should probably not call the property LeftToRight and should probably have something like this (and you would only want it to really be set to either Disabled or LeftToRight):

public enum BiDirectionalDisplayType
{
    Disabled = 0,
    LeftToRight = 1,
    RightToLeft = 2
}

public BiDirectionalDisplayType BiDirectional
{
    get
    {
        var bidirectional = DirectMessage(NativeMethods.SCI_GETBIDIRECTIONAL).ToInt32();
        switch (bidirectional)
        {
            case NativeMethods.SC_BIDIRECTIONAL_L2R:
                return BiDirectionalDisplayType.LeftToRight;
            case NativeMethods.SC_BIDIRECTIONAL_R2L:
                return BiDirectionalDisplayType.RightToLeft;
            case NativeMethods.SC_BIDIRECTIONAL_DISABLED:
            default:
                return BiDirectionalDisplayType.Disabled;
        }
    }

    set
    {
        if (value != BiDirectionalDisplayType.Disabled)
        {
            var technology = DirectMessage(NativeMethods.SCI_GETTECHNOLOGY).ToInt32();
            if (technology == NativeMethods.SC_TECHNOLOGY_DEFAULT)
            {
                DirectMessage(NativeMethods.SCI_SETTECHNOLOGY, new IntPtr(NativeMethods.SC_TECHNOLOGY_DIRECTWRITE));
            }
        }

        DirectMessage(NativeMethods.SCI_SETBIDIRECTIONAL, new IntPtr((int) value));
    }
}

Now to get the text to right justify as it does in NotePad++, it was suggested to look at WS_EX_LAYOUTRTL. I checked the NotePad++ source and it indeed uses WS_EX_LAYOUTRTL, but I was unable to get the text to right justify and it seemed to instead just mess up the ScintillaNET control border.
image

Here is what I have so far by looking at the NotePad++ code. Hopefully you have some ideas as to why this isn't working correctly.

public static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
{
    if (IntPtr.Size == 8)
        return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
    else
        return new IntPtr(SetWindowLong32(hWnd, nIndex, dwNewLong.ToInt32()));
}

[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
private static extern int SetWindowLong32(IntPtr hWnd, int nIndex, int dwNewLong);

[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);

public void ChangeTextDirection(bool isRTL)
{
    const long WS_EX_LAYOUTRTL = 0x00400000L;
    const int GWL_EXSTYLE = -20;
    long exStyle = GetWindowLongPtr(Handle, GWL_EXSTYLE).ToInt64();
    if (isRTL)
    {
        exStyle |= WS_EX_LAYOUTRTL;
    }
    else
    {
        exStyle &= ~WS_EX_LAYOUTRTL;
    }
    SetWindowLongPtr(Handle, GWL_EXSTYLE, new IntPtr(exStyle));
}

@cyber960
Copy link

cyber960 commented Feb 26, 2021

after a bit more testing WS_EX_LAYOUTRTL will work and text will be right aligned when DirectWrite is not enabled, however it still messes up the border of the ScintillaNET control.

The question is how can we get text right aligned when DirectWrite is enabled?

I did find this link which may be of interest: https://docs.microsoft.com/en-us/windows/win32/api/dwrite/ne-dwrite-dwrite_reading_direction

@VPKSoft
Copy link
Author

VPKSoft commented Feb 26, 2021

Interesting and thank you for making the Scintilla ticket. I'll try to spare some time in the weekend to look into this.

@VPKSoft
Copy link
Author

VPKSoft commented Feb 28, 2021

Hi, I'm not getting any border mess-up with this code piece:

set
            {
                // Prevent the protected memory read error.
                if (!IsHandleCreated || IsDisposed)
                {
                    return;
                }

                if (value != RightToLeft)
                {
                    switch (value)
                    {
                        case RightToLeft.Yes:
//                            DirectMessage(NativeMethods.SCI_SETBIDIRECTIONAL,
//                                new IntPtr(NativeMethods.SC_BIDIRECTIONAL_R2L));
                            Technology = Technology.Default;
                            Handle.ChangeTextDirection(true);
                            break;
........

Test application targeting NET 5:
image

With direct write this doesn't work, but is there any explicit need to use direct write?

I refactored your code a bit - a bad habit for a selective perfectionist (😅) :

 using System;
 using System.Runtime.InteropServices;

 internal static class WinApiHelpers
 {
     public static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
     {
         if (Environment.Is64BitProcess)
         {
             return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
         }

         return new IntPtr(SetWindowLong32(hWnd, nIndex, dwNewLong.ToInt32()));
     }

     [DllImport("user32.dll", EntryPoint = "SetWindowLong")]
     private static extern int SetWindowLong32(IntPtr hWnd, int nIndex, int dwNewLong);

     [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
     private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

     [DllImport("user32.dll", EntryPoint = "GetWindowLong")]
     private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);

     public static void ChangeTextDirection(this IntPtr handle, bool isRtl)
     {
         const long WS_EX_LAYOUTRTL = 0x00400000L;
         const int GWL_EXSTYLE = -20;
         long exStyle = GetWindowLongPtr(handle, GWL_EXSTYLE).ToInt64();
         if (isRtl)
         {
             exStyle |= WS_EX_LAYOUTRTL;
         }
         else
         {
             exStyle &= ~WS_EX_LAYOUTRTL;
         }
         SetWindowLongPtr(handle, GWL_EXSTYLE, new IntPtr(exStyle));
     }
 }

@VPKSoft
Copy link
Author

VPKSoft commented Feb 28, 2021

I forgot to test with the .NET Framework, the result doesn't seem to change:
image

@cyber960
Copy link

cyber960 commented Feb 28, 2021

The border appearance is actually not an issue, it just looks weird with the default border style. You can't see it in your example because you have the control docked with DockStyle.Fill. The default border style is BorderStyle.Fixed3D and it looks weird reversed but it is not really an issue. If you used another border style like BorderStyle.FixedSingle or BorderStyle.None then it looks fine.
image

In order to have true right-to-left support you would need both the bidirectional functionality where the keyboard keys type according to the bidi algorithm as well as right-aligned text. The easiest way to test this is with a WinForms TextBox control with RightToLeft enabled. You would also need to have some text that has right-to-left characters in it like Hebrew or Arabic. When there are right-to-left characters mixed in with left-to-right characters, the cursor position when you press the arrow keys or delete/backspace behaves differently than you would expect. I don't really understand how it all works but I suspect people who type on an Arabic keyboard are quite familiar with it.

What NotePad++ has (using WS_EX_LAYOUTRTL) is just a right-to-left reading layout, but I suspect for someone who actually uses a right-to-left language, typing in it would be quite hard because it does not have bidirectional support. Instead this just makes it easier to read Arabic or Hebrew text.

So for what we can currently acheive with the Scintilla control there are two options:

  1. use default technology along with WS_EX_LAYOUTRTL and we get the correct reading layout for right-to-left text
  2. use DirectWrite with SC_BIDIRECTIONAL_L2R and we get correct keyboard typing functionality for someone who types in a right-to-left language, but all text remains left aligned.

Unfortunately we cannot currently have both of those since WS_EX_LAYOUTRTL is incompatible with DirectWrite.

There is a third option but it is not currently completed and potentially has been abandoned since the developer who implemented the bidirectional functionality in the Scintilla control ran out of time.
3) use DirectWrite with SC_BIDIRECTIONAL_R2L and call IDWriteTextFormat::SetReadingDirection with parameter DWRITE_READING_DIRECTION_RIGHT_TO_LEFT. This option would give proper reading order along with proper typing and keyboard functionality. But this would need to be fixed/implemented in the Scintilla source and it aparently introduces a host of other issues that are difficult to fix. See this mailing list thread. Effectively this would give full right-to-left support with behavior that should be almost the same as a WinForms TextBox with RightToLeft enabled.

With respect to DirectWrite, you asked if there was a specific need for it. I see two advantages

  1. the previously mentioned bidirectional support for keyboard input
  2. better display of characters. Certain Unicode characters do not display correctly for some fonts.
    For example the default font in Scintilla is Verdana (you can see this using scintilla.Styles[Style.Default].Font, not scintilla.Font, which is a bit confusing and Scintilla.Font should maybe have its property overridden).
    Try this text "Egypt,Abdel Halim Hafez,ﻣﺼﺮ,ﻋﺑﺪﺍﻠﺣﻟﻳﻢ ﺤﺎﻓﻅ" with default technology, Verdana font and left-alignment and you see a bunch of squares instead of characters.
    image
    Oddly enough if you use WS_EX_LAYOUTRTL the characters display correctly.
    image

but with DirectWrite enabled, you can see the characters correctly in a left-to-right layout and they aren't as blurry and thick:
image

I think DirectWrite handles fallback fonts a lot better than the default technology (GDI) which is why it seems to have much better support for rendering characters.

Also see this bug I logged where Windows 7 and 8.1 couldn't render certain characters at all and instead showed them as white space when the default technology was used. Windows 10 probably has better fallback font support which is why it was able to render the characters. But with DirectWrite enabled, the characters rendered correctly on Windows 7/8.1 and 10. https://sourceforge.net/p/scintilla/bugs/2230/

So for the changes to ScintillaNET, I would recommend having two separate properties.

One to change reading direction to acheive similar functionality to NotePad++ and the order to get bidirectional keyboard support. They both have their own advantages. Since there are two different properties, I am not sure it makes sense to use the RightToLeft name. Maybe property names like RightToLeftReadingLayout and BiDirectionality would be good property names.

@VPKSoft
Copy link
Author

VPKSoft commented Feb 28, 2021

Okay - this is going to be a bit more difficult than I thought when suggesting this few years ago 🤔 I'd rather not touch the Scintilla core code so I think the third option is off the table. Not sure if a keyboard hack would work - i.e. through WndProc or such for the typing.

@cyber960
Copy link

I think the best we can do for now is to present the two RTL half measures and let the user decide which one they want to use based on their needs and which technologies they are using. I see big advantages to using DirectWrite (only compatible on Windows Vista and later) for its better character rendering. I am using .NET Framework for my development so DirectWrite is compatible, however if you are using .NET 5 and trying to make it cross platform then DirectWrite would not be an option whereas maybe WS_EX_LAYOUTRTL would work cross platform. I’m not sure but maybe .NET 5 may work with certain settings on Windows but not on other OSes, I don’t know if anyone has actually tried running it on a different OS.

I didn’t know about DirectWrite before looking into this issue, but because I like it much more than the default rendering technology, it seems that the best international support I can have, at this time, on my ScintillaNet control is DirectWrite + SC_BIDIRECTIONAL_L2R. Best to wait for proper support for SC_BIDIRECTIONAL_R2L and SetReadingDirection to be implemented in Scintilla core if and when someone gets around to it.

What do you think about the other, non-related, suggestion of overriding Scintilla.Font to use Scintilla.Styles[Style.Default].Font behind the scenes? I think that would be useful since the casual user won’t read the documentation too in depth and this would make it much easier to change the Font. Of course if someone wants to have multiple font styles then they would need to use Scintilla.Styles.

@VPKSoft
Copy link
Author

VPKSoft commented Mar 1, 2021

I'm fine with the half-measure approach and with the naming you suggested. Overriding the font is also ok. I was wondering if #398, displaying binary data should also be made easier - the method of doing so seems to be quite difficult to find. I'm currently using Scintilla only with Windows and don't know much about the possible QT or GTK implementations.

@cyber960
Copy link

cyber960 commented Mar 1, 2021

I will take a look at #398 in a bit.

I have made some proposed changes (found in Scintilla_updated.cs.txt, please go over them by comparing to the original file Scintilla_v3.8.4.cs.txt) with WinMerge or some other diff tool. Please do any refactoring, xml comments etc that you feel is neccessary. I put everything in one file to make it easier to diff.

The changes I propose are:

  1. Change the default value for WrapMode from WrapMode.None to WrapMode.Word
[DefaultValue(WrapMode.Word)]
  1. In OnHandleCreated, default to a scroll width of 1 so that no scrollbar is shown by default (ScrollWidthTracking is already enabled). Also hide all margins by deault. The default functionality should more closely resemble a multiline TextBox or RichTextBox.
DirectMessage(NativeMethods.SCI_SETSCROLLWIDTH, new IntPtr(1));
DirectMessage(NativeMethods.SCI_SETSCROLLWIDTHTRACKING, new IntPtr(1));

//hide all default margins
foreach (var margin in Margins)
{
    margin.Width = 0;
}
  1. Override the Font Property
public override Font Font
{
    get
    {
        if (!IsHandleCreated)
            return base.Font;

        var defaultFontStyle = Styles[Style.Default];

        var fontStyle = defaultFontStyle.Bold ? FontStyle.Bold : FontStyle.Regular;

        if (defaultFontStyle.Italic)
            fontStyle |= FontStyle.Italic;

        if (defaultFontStyle.Underline)
            fontStyle |= FontStyle.Underline;

        return new Font(defaultFontStyle.Font, defaultFontStyle.SizeF, fontStyle);
    }
    set
    {
        var defaultFontStyle = Styles[Style.Default];
        defaultFontStyle.Font = value.Name;
        defaultFontStyle.SizeF = value.Size;
        defaultFontStyle.Bold = value.Bold;
        defaultFontStyle.Italic = value.Italic;
        defaultFontStyle.Underline = value.Underline;
        base.Font = value;
    }
}
  1. Add property for setting Bidirectionality
public enum BiDirectionalDisplayType
{
    Disabled = NativeMethods.SC_BIDIRECTIONAL_DISABLED,
    LeftToRight = NativeMethods.SC_BIDIRECTIONAL_L2R,
    RightToLeft = NativeMethods.SC_BIDIRECTIONAL_R2L
}

public BiDirectionalDisplayType BiDirectionality
{
    get => (BiDirectionalDisplayType) DirectMessage(NativeMethods.SCI_GETBIDIRECTIONAL).ToInt32();

    set
    {
        if (value != BiDirectionalDisplayType.Disabled)
        {
            var technology = DirectMessage(NativeMethods.SCI_GETTECHNOLOGY).ToInt32();
            if (technology == NativeMethods.SC_TECHNOLOGY_DEFAULT)
            {
                DirectMessage(NativeMethods.SCI_SETTECHNOLOGY, new IntPtr(NativeMethods.SC_TECHNOLOGY_DIRECTWRITE));
            }
        }

        DirectMessage(NativeMethods.SCI_SETBIDIRECTIONAL, new IntPtr((int)value));
    }
}
  1. Add property for setting reading layout direction
public static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
{
    if (Environment.Is64BitProcess)
    {
        return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
    }

    return new IntPtr(SetWindowLong32(hWnd, nIndex, dwNewLong.ToInt32()));
}

[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
private static extern int SetWindowLong32(IntPtr hWnd, int nIndex, int dwNewLong);

[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);

private const long WS_EX_LAYOUTRTL = 0x00400000L;
private const int GWL_EXSTYLE = -20;

public bool UseRightToLeftReadingLayout
{
    get
    {
        if (!IsHandleCreated)
            return false;

        long exStyle = GetWindowLongPtr(Handle, GWL_EXSTYLE).ToInt64();
        return exStyle == (exStyle | WS_EX_LAYOUTRTL);
    }
    set
    {
        if (!IsHandleCreated)
            return;

        long exStyle = GetWindowLongPtr(Handle, GWL_EXSTYLE).ToInt64();

        if (value)
        {
            var technology = DirectMessage(NativeMethods.SCI_GETTECHNOLOGY).ToInt32();
            if (technology != NativeMethods.SC_TECHNOLOGY_DEFAULT)
            {
                DirectMessage(NativeMethods.SCI_SETTECHNOLOGY, new IntPtr(NativeMethods.SC_TECHNOLOGY_DEFAULT));
            }

            exStyle |= WS_EX_LAYOUTRTL;
        }
        else
        {
            exStyle &= ~WS_EX_LAYOUTRTL;
        }
        SetWindowLongPtr(Handle, GWL_EXSTYLE, new IntPtr(exStyle));
        DirectMessage(NativeMethods.SCI_SETFOCUS, new IntPtr(1)); //needs focus to update
    }
}

@VPKSoft
Copy link
Author

VPKSoft commented Mar 2, 2021

It would just be easier to fork the branch and make a PR, but I'll do the diff thing then 🙂

@VPKSoft
Copy link
Author

VPKSoft commented Mar 2, 2021

Hi,
I merged the changes - thanks again for the contribution 👍 Version 3.8.5 should now include the changes.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants