Krypton-Suite / Standard-Toolkit

An update to Component factory's krypton toolkit to support .NET Framework 4.6.2 - 4.8.1 to .NET 8 - 9
BSD 3-Clause "New" or "Revised" License
408 stars 62 forks source link

[Bug] KryptonForms do not know how to do RTL setting correctly #343

Closed Smurf-IV closed 4 months ago

Smurf-IV commented 3 years ago

As seen with the fix for #313 Both Set: image

RightAlign only: image

RTL Only: image

Originally posted by @Smurf-IV in https://github.com/Krypton-Suite/Standard-Toolkit/issues/313#issuecomment-918412005

And an old reference to an active Component Krypton Bug: https://github.com/ComponentFactory/Krypton/issues/94

Smurf-IV commented 3 years ago

Help needed with RTL Language Developers to check if Normal font in an RTL is revered like the Bug indicates above, or is "Normal" and only RTL languages are reversed, or it's the RTL font that does the standard reversing.

PWagner1 commented 3 years ago

Help needed with RTL Language Developers to check if Normal font in an RTL is revered like the Bug indicates above, or is "Normal" and only RTL languages are reversed, or it's the RTL font that does the standard reversing.

Posting a pinned comment to Discord

Ahmed-Abdelhameed commented 3 years ago

Help needed with RTL Language Developers to check if Normal font in an RTL is revered like the Bug indicates above, or is "Normal" and only RTL languages are reversed, or it's the RTL font that does the standard reversing.

I'm not sure if I understand you correctly. Does my screenshot posted to the linked issue not help with this? If not, please try to elaborate and I'll be happy to help if it's still needed.

Smurf-IV commented 3 years ago

@Ahmed-Abdelhameed Help needed is for a developer who has RTL set as the OS, would be needed to work out why the internals of Krypton do not work, and then to apply the fix across all components and related dll's (Navigator / Ribbon / Extended / etc.) Your images are the same as the ones at the start of this bug.

But, you have also added that the Title elements should also be swapped around as well. 👍

Ahmed-Abdelhameed commented 3 years ago

@Smurf-IV Okay, here's my progress so far...

First of all, I don't think the OS language or RTL plays a role here since the RightToLeft and RightToLeftLayout properties can be toggled regardless. Please correct me if I missed something but I got the same behavior when I tested on Windows with Arabic language (RTL layout).

So, after a lot of investigating, I figured out the cause of the text (and buttons) being flipped/mirrored. I'm not submitting a PR though because I don't have a complete solution yet. I'm just posting what I found so far and maybe you or @Wagnerp can pick up from where I left as you both obviously have more experience with the internals of Krypton than me.

The problem begins in the VisualForm.OnNonClientPaint() method, specifically at this line:

using (Graphics g = Graphics.FromHdc(_screenDC))

Here's the complete code for context:

// Create one the correct size and cache for future drawing
IntPtr hBitmap = PI.CreateCompatibleBitmap(hDC, windowBounds.Width, windowBounds.Height);

// If we managed to get a compatible bitmap
if (hBitmap != IntPtr.Zero)
{
    try
    {
        // Must use the screen device context for the bitmap when drawing into the 
        // bitmap otherwise the Opacity and RightToLeftLayout will not work correctly.
        PI.SelectObject(_screenDC, hBitmap);

        // Drawing is easier when using a Graphics instance
        using (Graphics g = Graphics.FromHdc(_screenDC))
        {
            WindowChromePaint(g, windowBounds);
        }

        // Now blit from the bitmap to the screen
        PI.BitBlt(hDC, 0, 0, windowBounds.Width, windowBounds.Height, _screenDC, 0, 0, PI.SRCCOPY);
    }
    finally
    {
        // Delete the temporary bitmap
        PI.DeleteObject(hBitmap);
    }
}
else
{
    // Drawing is easier when using a Graphics instance
    using Graphics g = Graphics.FromHdc(hDC);
    WindowChromePaint(g, windowBounds);
}

As you can see, it's using the screen's device context and only when that fails does it fall back to using the window's device context (i.e., hDC) in the else block. What I found out is that when creating a Graphics instance from the window's hdc, we get one that is already set up with RTL rendering (assuming RTL is enabled). However, when using the screen's device context, the Graphics instance is not set up for that (hence, the mirroring).

Now, I'm not entirely sure whether using the screen device context is necessary or how the caching mentioned in the code comment above works. If it's not necessary, then we can just get rid of the if block and use the code that's in the else block (i.e., Graphics.FromHdc(hDC)). If it is necessary, then we need to set up the Graphics instance to (un-)mirror the rendering. This can be achieved using something like the following:

using (Graphics g = Graphics.FromHdc(_screenDC))
{
    if (this.RightToLeftLayout && this.RightToLeft == RightToLeft.Yes)
    {
        g.TranslateTransform(windowBounds.Width, 0);
        g.ScaleTransform(-1, 1);
    }
    WindowChromePaint(g, windowBounds);
}

Using either of the fixes described above causes the RTL layout to work correctly (icon and text on the right, displayed correctly/unmirrored, and the buttons are on the left):

image

However, there are two things I still didn't figure out. 1) Part of the form/border is still not displayed. I'm not sure why yet. 2) Even though the buttons are now on the left, the HitTest (triggered by WM_NCHITTEST) still thinks they're on the right. So, the HitTest logic needs to be fixed too; I just didn't have enough time to investigate this part yet. Here's a demo that shows both issues:

RTL

PWagner1 commented 2 years ago

As per comment in #786, button specs including help should be flipped along with the hit areas.

PWagner1 commented 10 months ago

@Smurf-IV & @Ahmed-Abdelhameed These may help adjust the 'hit' targets, but they're not used in KForm

image

giduac commented 5 months ago

Hi @Smurf-IV,

Is this still being worked on?

PWagner1 commented 5 months ago

Hi @Smurf-IV,

Is this still being worked on?

Hi @giduac

No, but the bug still remains. Hit test logic needs to be flipped plus a slice of the KForm is sliced off. You can see for yourself if you toggle the RightToLeftLayout property.

PWagner1 commented 4 months ago

Hi @Smurf-IV

Do we know where the bug is on this one, or is it a case of creating a 'special' KForm?

Smurf-IV commented 4 months ago

Do we know where the bug is on this one, or is it a case of creating a 'special' KForm?

@Ahmed-Abdelhameed: created a demo (As in the gif above), but did not supply it. So yes, "we" will need to replicate of not supplied

PWagner1 commented 4 months ago

Do we know where the bug is on this one, or is it a case of creating a 'special' KForm?

@Ahmed-Abdelhameed: created a demo (As in the gif above), but did not supply it. So yes, "we" will need to replicate of not supplied

@Smurf-IV

I wonder if there's something in the WinForms repo that can be used?

PWagner1 commented 4 months ago

Hi @Smurf-IV

Created a demo, the behaviour is even more unusual...

RTLBug.zip

PWagner1 commented 4 months ago

Hi @Smurf-IV & @giduac

I think I've found something... look at https://github.com/dotnet/winforms/blob/main/src/System.Windows.Forms/src/System/Windows/Forms/Form.cs, specifically lines 867 - 873...

PWagner1 commented 4 months ago

Superseded by #1570