Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RTL support #21

Open
hichamboushaba opened this issue Jun 1, 2018 · 13 comments
Open

RTL support #21

hichamboushaba opened this issue Jun 1, 2018 · 13 comments
Assignees
Labels
area/a11y Categorizes an issue or PR as relevant to a11y difficulty/challenging 🤯 Categorizes an issue for which the difficulty level is reachable with internals understanding epic kind/enhancement New feature or request project/layout 🧱 Categorizes an issue or PR as relevant to layouting and containers (Measure/Arrange, Collections,..) triage/most-wanted

Comments

@hichamboushaba
Copy link

FlowDirection is not supported yet, when do you plan to add it to support RTL in all platforms.

@jeromelaban jeromelaban added the kind/enhancement New feature or request label Jun 26, 2018
@ghuntley ghuntley added the area/a11y Categorizes an issue or PR as relevant to a11y label Oct 17, 2019
@agneszitte agneszitte added the project/layout 🧱 Categorizes an issue or PR as relevant to layouting and containers (Measure/Arrange, Collections,..) label Sep 21, 2020
@jeromelaban jeromelaban added the difficulty/tbd Categorizes an issue for which the difficulty level needs to be defined. label Feb 15, 2021
@Arlodotexe
Copy link
Contributor

Also curious about this, low priority but something we'd like to support in our app.

@MartinZikmund MartinZikmund added difficulty/medium 🤔 Categorizes an issue for which the difficulty level is reachable with a good understanding of WinUI and removed difficulty/tbd Categorizes an issue for which the difficulty level needs to be defined. labels May 31, 2021
@Youssef1313
Copy link
Member

@jeromelaban @MartinZikmund Any pointers on how to start working on this? Thanks!

@jeromelaban
Copy link
Member

@Youssef1313 identifying where the RTL support is lacking would be a great start. Some of the controls do support it, either because the platform provides it by default, or because we imported code that supports it, but we're not tracking it yet.

@MartinZikmund
Copy link
Member

It should also somehow tie in with the localization infrastructure - e.g. the RTL should be enabled automatically when the current culture has CultureInfo.TextInfo.IsRightToLeft = true

@Youssef1313
Copy link
Member

I've tried setting FlowDirection="RightToLeft" on TextBlock and StackPanel, Grid, and it seems to work correctly. Are there known specific controls that doesn't support RTL?

@jeromelaban
Copy link
Member

@Youssef1313 on which platforms have you tried ? Can you post screenshots of the results as well ?

@Youssef1313
Copy link
Member

Oh I was trying on the UWP project. I should have tried on another platform. Will re-take a look.

@Youssef1313 Youssef1313 mentioned this issue Aug 7, 2021
6 tasks
@avikeid2007
Copy link

Is there any update for RTL support?

@MartinZikmund MartinZikmund added difficulty/challenging 🤯 Categorizes an issue for which the difficulty level is reachable with internals understanding and removed difficulty/medium 🤔 Categorizes an issue for which the difficulty level is reachable with a good understanding of WinUI labels Oct 24, 2022
@MartinZikmund
Copy link
Member

We should break this down into individual platforms and prioritize each

@balbarak
Copy link

Any update.

This feature is very important for RTL languages and speakers

@balbarak
Copy link

balbarak commented Sep 4, 2023

@Youssef1313 @MartinZikmund

Hello, I did some attached property that do the job maybe will give you a starting point here is the code

controls that reversed are StackPanel Grid TextBox TextBlock FrameworkElement Control

        private static void ChangeDirection(UIElement element, ElementFlowDirection dir)
        {
            var grids = new List<Grid>();
            var stackPanels = new List<StackPanel>();
            var frameWorkElements = new List<FrameworkElement>();
            var textBlocks = new List<TextBlock>();
            var textBoxes = new List<TextBox>();
            var controls = new List<Control>();

            UIElementHelper.FindChildren(grids, element);
            UIElementHelper.FindChildren(stackPanels, element);
            UIElementHelper.FindChildren(frameWorkElements, element);
            UIElementHelper.FindChildren(textBlocks, element);
            UIElementHelper.FindChildren(textBoxes, element);
            UIElementHelper.FindChildren(controls, element);

            grids.ForEach(ReverseGridChilds);
            stackPanels.ForEach(ReverseStackPanel);
            frameWorkElements.ForEach(ReverseMarginsAndHorizontalAlignment);
            textBoxes.ForEach(ReverseTextBoxes);
            textBlocks.ForEach(ReverseTextBlock);
            controls.ForEach(ReversePadding);
        }

        private static void ReverseGridChilds(Grid grid)
        {
            var childs = grid.Children;

            var colCount = grid.ColumnDefinitions.Count;

            var colDefs = grid.ColumnDefinitions.ToList();

            colDefs = Enumerable.Reverse(colDefs).ToList();

            grid.ColumnDefinitions.Clear();

            foreach (var item in colDefs)
            {
                grid.ColumnDefinitions.Add(item);
            }

            if (colCount <= 0)
                return;

            var array = Enumerable.Range(0, colCount).ToArray();
            array = Enumerable.Reverse(array).ToArray();

            foreach (var item in childs)
            {
                var frame = item as FrameworkElement;

                var colNo = Grid.GetColumn(frame);
                var colSpan = Grid.GetColumnSpan(frame);

                if (colNo > array.Length - 1)
                    continue;

                int targetVale = array[colNo];

                if (colSpan > 1)
                {
                    Grid.SetColumnSpan(frame, colSpan);

                    int targetValue = targetVale / colSpan;

                    Grid.SetColumn(frame, targetValue);
                }
                else
                {
                    Grid.SetColumn(frame, targetVale);
                }
            }
        }

        private static void ReverseStackPanel(StackPanel panel)
        {
            if (panel.Orientation != Orientation.Horizontal)
                return;

            var childs = panel.Children.ToArray();

            childs = Enumerable.Reverse(childs).ToArray();

            panel.Children.Clear();

            foreach (var item in childs)
            {
                panel.Children.Add(item);
            }
        }

        private static void ReverseMarginsAndHorizontalAlignment(FrameworkElement element)
        {
            var top = element.Margin.Top;
            var bottom = element.Margin.Bottom;
            var left = element.Margin.Left;
            var right = element.Margin.Right;

            element.Margin = new Thickness(right, top, left, bottom);

            if (element.HorizontalAlignment == HorizontalAlignment.Left)
            {
                element.HorizontalAlignment = HorizontalAlignment.Right;
            }
            else if (element.HorizontalAlignment == HorizontalAlignment.Right)
            {
                element.HorizontalAlignment = HorizontalAlignment.Left;
            }
        }

        private static void ReversePadding(Control element)
        {
            var top = element.Padding.Top;
            var bottom = element.Padding.Bottom;
            var left = element.Padding.Left;
            var right = element.Padding.Right;

            element.Padding = new Thickness(right, top, left, bottom);
        }

        private static void ReverseTextBoxes(TextBox textBox)
        {
            var alignment = textBox.TextAlignment;

            var shouldNotChanged = alignment == TextAlignment.Center || alignment == TextAlignment.Justify;

            if (shouldNotChanged)
                return;

            var isLeft = alignment == TextAlignment.Left || alignment == TextAlignment.Start;
            var isRight = alignment == TextAlignment.Right || alignment == TextAlignment.End;


#if ANDROID
            textBox.TextAlignment = TextAlignment.Start;
            if (textBox.LayoutDirection == Android.Views.LayoutDirection.Ltr)
            {
                textBox.LayoutDirection = Android.Views.LayoutDirection.Rtl;
                textBox.TextDirection = Android.Views.TextDirection.Rtl;
            }
            else
            {
                textBox.LayoutDirection = Android.Views.LayoutDirection.Ltr;
                textBox.TextDirection = Android.Views.TextDirection.Ltr;
            }
#else

            if (isLeft)
            {
                textBox.TextAlignment = TextAlignment.Right;
            }
            else if (isRight)
            {
                textBox.TextAlignment = TextAlignment.Left;
            }
#endif
        }

        private static void ReverseTextBlock(TextBlock text)
        {
            var textAlignment = text.TextAlignment;

            var isLeft = textAlignment == TextAlignment.Left;
            var isRight = textAlignment == TextAlignment.Right;

#if ANDROID
            text.TextAlignment = TextAlignment.Start;

            if (text.LayoutDirection == Android.Views.LayoutDirection.Ltr)
            {
                text.LayoutDirection = Android.Views.LayoutDirection.Rtl;
                text.TextDirection = Android.Views.TextDirection.Rtl;
            }
            else
            {
                text.LayoutDirection = Android.Views.LayoutDirection.Ltr;
                text.TextDirection = Android.Views.TextDirection.Ltr;
            }
#else
            if (isLeft)
            {
                text.TextAlignment = TextAlignment.Right;

            }
            else if (isRight)
            {
                text.TextAlignment = TextAlignment.Left;
            }
#endif

        }
    }

@MartinZikmund
Copy link
Member

@balbarak thank you a lot! We will try to prioritize this as soon as possible!

@Youssef1313
Copy link
Member

Thanks @balbarak.

Opened #13524 to support that on Skia.

There are some specific elements that shouldn't probably inherit FlowDirection

  • Image
  • WebView
  • MediaPlayerElement

After getting #13524 merged, I'll work on getting this for other platforms plus handle the specific elements that don't inherit FlowDirection.

@Youssef1313 Youssef1313 self-assigned this Sep 6, 2023
lukeblevins pushed a commit to lukeblevins/uno that referenced this issue Jan 17, 2024
* feat: Added Icon to the project

* feat: Added new info about the target platform

* feat: added appIcons to the sample project

* fix: fixed output path for app icons

* chore: removed default app icons values from appxmanifest

* chore: renamed MaiuIcon > UnoIcon

* feat: make sure crazy paths still works

* feat: added support for wpf

* chore: renamed Maui names to Uno names

* chore: fixed merge

* feat: Added Output on C# code

* feat: Added output on targets and use it

* feat: updated WinUI to use the Output property

* chore: removed null as default value

* chore: removed info tha isn't needed

* test: updated tests to match the current API

* fix: updated targets

* fix: changed property to be the OS and not the TFM

* fix: fixed app icon for wpf

* fix: fixed images for wpf

* chore: removed duplicated code

* chore: clean up on DpiPath class

* chore: removed duplicated ApplicationIcon property
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/a11y Categorizes an issue or PR as relevant to a11y difficulty/challenging 🤯 Categorizes an issue for which the difficulty level is reachable with internals understanding epic kind/enhancement New feature or request project/layout 🧱 Categorizes an issue or PR as relevant to layouting and containers (Measure/Arrange, Collections,..) triage/most-wanted
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants