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

[Android] Entire words omitted & letters truncated from Label display #17884

Open
jonmdev opened this issue Oct 7, 2023 · 6 comments
Open

[Android] Entire words omitted & letters truncated from Label display #17884

jonmdev opened this issue Oct 7, 2023 · 6 comments
Labels
area-controls-label Label, Span area-drawing Shapes, Borders, Shadows, Graphics, BoxView, custom drawing platform/android 🤖 s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working
Milestone

Comments

@jonmdev
Copy link

jonmdev commented Oct 7, 2023

Description

With specific settings, entire words are not rendered in Labels in Android. And in other circumstances letters are being truncated off. This is obviously a major problem as we cannot have random words and letters not displayed.

For example, here is my test code which reproduces both a missing word and a missing letter, to replace the code in App.xaml.cs in a default program:

using Microsoft.Maui.Controls.Shapes;
using Microsoft.Maui.Platform;
using System.Diagnostics;

namespace Label_Shadow_Bug {
    public partial class App : Application {

        public event Action screenSizeChanged = null;
        public double screenWidth = 0;
        public double screenHeight = 0;
        ContentPage mainPage;
        public App() {

            InitializeComponent();

            //=========
            //LAYOUT
            //=========
            mainPage = new();
            mainPage.Background = Colors.MediumAquamarine;
            MainPage = mainPage;
            mainPage.SizeChanged += delegate {
                invokeScreenSizeChangeEvent();
            };

            AbsoluteLayout abs = new();
            mainPage.Content = abs;

            VerticalStackLayout vert = new();
            abs.Add(vert);

            Label label = new();
            label.Text = "CHAT MISSING_WORD";
            label.TextColor = Colors.White;
            label.FontSize = 23; //changing font size can fix bug
            label.FontFamily = "MontserratBold";
            label.Shadow = new() { Offset = new Point(0, 1), Radius = 1, Brush = Colors.Aqua }; 
            label.MaxLines = 1;
            label.LineBreakMode = LineBreakMode.NoWrap;
            label.HorizontalOptions = LayoutOptions.Center;

            Border newBorder = new();
            newBorder.HorizontalOptions = LayoutOptions.Center;
            newBorder.Content = label;
            newBorder.BackgroundColor = Colors.DarkCyan;
            newBorder.Padding = new Thickness(6, 2);
            newBorder.Margin = new Thickness(0, 4);
            newBorder.StrokeShape = new RoundRectangle() { CornerRadius = 12 };
            newBorder.StrokeThickness = 0.5;
            newBorder.HandlerChanged += delegate {
#if ANDROID
                Android.Views.View androidView = ElementExtensions.ToPlatform(newBorder, newBorder.Handler.MauiContext);
                newBorder.Shadow = new() { Offset = new Point(0, androidView.Context.ToPixels(5)), Radius = androidView.Context.ToPixels(4) }; //android needs re-scaling of shadows due to other reported bug elsewhere
#else
                newBorder.Shadow = new() { Offset = new Point(0, 5), Radius = 4 };
#endif
            };
            
            newBorder.Content = label;
            vert.Add(newBorder);

            Label label2 = new();
            label2.Text = "ALEXANDRA";
            label2.FontFamily = "NotoSansBold";
            label2.FontSize = 42;
            label2.HorizontalOptions = LayoutOptions.Center;
            label2.TextColor = Colors.White;
            label2.Margin = new Thickness(0, -5, 0, 10);
            label2.HandlerChanged += delegate {
#if ANDROID
                Android.Views.View androidView = ElementExtensions.ToPlatform(label2, label2.Handler.MauiContext);
                label2.Shadow = new() { Offset = new Point(0, androidView.Context.ToPixels(5)), Radius = androidView.Context.ToPixels(7) }; //android needs re-scaling of shadows due to other reported bug elsewhere
#else
                label2.Shadow = new() { Offset = new Point(0, 5), Radius = 7 };
#endif
            };
            vert.Add(label2);

            //==================
            //RESIZE FUNCTION
            //==================
            screenSizeChanged += delegate {
                vert.HeightRequest = screenHeight;
                vert.WidthRequest = screenWidth;
            };
            Debug.WriteLine("FINISHED BUILD OKAY");

        }
        private void invokeScreenSizeChangeEvent() {
            if (mainPage.Width > 0 && mainPage.Height > 0) {
                screenWidth = mainPage.Width;
                screenHeight = mainPage.Height;
                Debug.WriteLine("main page size changed | width: " + screenWidth + " height: " + screenHeight);
                screenSizeChanged?.Invoke();
            }
        }
    }
    
}

The code should display this as it does in Windows:

android missing words windows

This is what actually happens in Android:

android missing words android

As you can see, the word "MISSING_WORD" is missing. Yet space is still maintained for it. You just don't see the word. In fact, it seems to have wrapped the "MISSING_WORD" to the next line as extra space is maintained for it vertically.

And the name "ALEXANDRA" is now "ALEXANDR" - the last letter has been chopped off.

By contrast in Windows/iOS I have not seen this bug at all.

We obviously can't have entire words going missing in our Labels on Android. Even to just display an app's terms of service, this is a legal impossibility. A fix would be great ASAP.

Thanks for your help.

Steps to Reproduce

  1. Open GitHub repository.
  2. Play in standard Google Pixel 5 API33 Android emulator and see the words and letters missing.

Link to public reproduction project repository

https://github.com/jonmdev/Label-Shadow-Bug

Version with bug

7.0.92

Is this a regression from previous behavior?

No, this is something new

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

Android API 33, .NET 7.0, Google Pixel 5 Emulator

Did you find any workaround?

There seems to be none.

@jonmdev jonmdev added the t/bug Something isn't working label Oct 7, 2023
@jsuarezruiz jsuarezruiz added platform/android 🤖 area-drawing Shapes, Borders, Shadows, Graphics, BoxView, custom drawing area-controls-label Label, Span labels Oct 9, 2023
@jsuarezruiz jsuarezruiz added this to the Backlog milestone Oct 9, 2023
@ghost ghost added the legacy-area-controls Label, Button, CheckBox, Slider, Stepper, Switch, Picker, Entry, Editor label Oct 9, 2023
@ghost
Copy link

ghost commented Oct 9, 2023

We've added this issue to our backlog, and we will work to address it as time and resources allow. If you have any additional information or questions about this issue, please leave a comment. For additional info about issue management, please read our Triage Process.

@jonmdev jonmdev changed the title Entire words ommitted from Label display in Android with specific shadow/hierarchy settings (no issue in Windows/iOS) Entire words ommitted & letters truncated from Label display in Android under consistently reproducable conditions (Simple repro project included) Oct 11, 2023
@jonmdev
Copy link
Author

jonmdev commented Oct 11, 2023

I have revised my issue report, as I am now observing this issue also truncates letters off of words (previously I noted it was truncating words entirely - now today I see it can truncate words mid-way as well under other circumstances).

I simplified my code to < 100 lines and demonstrated the bug in both situations - a word being missing completely from one label and a word being partially truncated from another. I updated my post accordingly.

This is an absolutely crippling bug for Android. @jsuarezruiz @samhouts

There is no reasonable way someone can release an app for Android using MAUI when it is truncating letters and words. How could we possibly? Even just to display our app "Terms of Service" this is legally impossible. Words and letters cannot be missing even just from a legal standpoint. I don't believe the lawyers will permit it.

Is there any way this might be possible to set at a high priority? If we could get this fixed for .NET 8.0 (along with an Editor fix for iOS which is now p/1 and thanks for that: #17757) then at least .NET 8.0 may be reliably usable which would be great.

Thanks for all your help.

@XamlTest XamlTest added s/verified Verified / Reproducible Issue ready for Engineering Triage s/triaged Issue has been reviewed labels Oct 12, 2023
@XamlTest
Copy link
Collaborator

XamlTest commented Oct 12, 2023

Verified this on Visual Studio Enterprise 17.8.0 Preview 3.0(8.0.0-rc.2.9373). Repro on Android 13.0-API33, not repro on Windows 11 and iOS 16.4 with below Project:
Label Shadow Bug.zip

@samhouts samhouts changed the title Entire words ommitted & letters truncated from Label display in Android under consistently reproducable conditions (Simple repro project included) [Android] Entire words omitted & letters truncated from Label display Oct 14, 2023
@tschramme86
Copy link

Probably related to #11358 - although this issue was closed as fixed, I still can reproduce it under certain conditions. Multiline labels are cutting words on Android, because apparently their height is calculated wrong.

@williambuchanan2
Copy link

I have had a lot of these issues. Most of the time I have managed to work around them by changing the container (i.e. the border or stack layout).

For example, a recent issue I had where a label was just vanishing off the right side of the screen rather than wrapping was caused by it being in a grid with the columndefinition=Auto setting. Changing it to columndefinition=* fixed the problem.

I have also noticed that using VerticalStackLayout and HorizontalStackLayout cause problems when you want to use the full screen, so I mostly just use StackLayout which fixes most of these problems. Also need to play a bit with horizontalalignment - most of the time you need to specify FillAndExpand otherwise it doesn't fill...

@jonmdev
Copy link
Author

jonmdev commented Jun 13, 2024

This appears to be due to rounding errors in the measurement system.

Swapping the demo project App.xaml.cs for this "fixes" the problem and you can see the missizing that triggers the issue with the debugs also by turning the "fix" bool on/off. You can see the "fix" function by building to Pixel 5 emulator. If false, the words will clip. If true, they won't.

#if ANDROID
using Android.Content;
#endif
using Microsoft.Maui.Controls.Shapes;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using System.Diagnostics;

namespace Label_Shadow_Bug {
#if ANDROID
    class CMauiTextView: MauiTextView {
        
        bool applyFix = true; //TO APPLY FIX FOR TEXT CLIPPING IN ANDROID, VISIBLE ON PIXEL 5 EMULATOR

        public CMauiTextView(Context context) : base(context) {
            System.Diagnostics.Debug.WriteLine("CREATE CMAUITEXTVIEW");
        }
        protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) {

            //https://stackoverflow.com/questions/14493732/what-are-widthmeasurespec-and-heightmeasurespec-in-android-custom-views

            var measured = Paint.MeasureText(Text);

            int widthSize = widthMeasureSpec.GetSize();
            Android.Views.MeasureSpecMode widthMode = widthMeasureSpec.GetMode();

            if (applyFix) {
                widthSize += 1;
            }

            int newWidthSpec = Android.Views.View.MeasureSpec.MakeMeasureSpec(widthSize, widthMode);

            var scaledDensity = DeviceDisplay.Current.MainDisplayInfo.Density; //= 2.75 on Pixel 5 emulator
            System.Diagnostics.Debug.WriteLine("LABEL: " + Text + " ANDROID WIDTH RAW " + + widthSize + " SCALED " + widthSize / scaledDensity);

            base.OnMeasure(newWidthSpec, heightMeasureSpec); //must be for cases where difference is greater than one char (layout width limit)
            //base.OnMeasure(widthMeasureSpec, heightMeasureSpec); //must be for cases where difference is greater than one char (layout width limit)
        }
    }
#endif
    public partial class App : Application {

        public event Action screenSizeChanged = null;
        public double screenWidth = 0;
        public double screenHeight = 0;
        ContentPage mainPage;

        
        public App() {

#if ANDROID
            LabelHandler.PlatformViewFactory = (handler) => {
                return new CMauiTextView(handler.Context); //Create custom MauiTextView for CLabelHandler
            };
#endif 

            //=========
            //LAYOUT
            //=========
            mainPage = new();
            mainPage.Background = Colors.MediumAquamarine;
            MainPage = mainPage;
            mainPage.SizeChanged += delegate {
                invokeScreenSizeChangeEvent();
            };

            AbsoluteLayout abs = new();
            mainPage.Content = abs;

            VerticalStackLayout vert = new();
            abs.Add(vert);

            Label label = new();
            label.Text = "CHAT MISSING_WORD";
            label.TextColor = Colors.White;
            label.FontSize = 23; //changing font size can fix bug
            label.FontFamily = "MontserratBold";
            label.Shadow = new() { Offset = new Point(0, 1), Radius = 1, Brush = Colors.Aqua }; 
            label.MaxLines = 1;
            label.LineBreakMode = LineBreakMode.NoWrap;
            label.HorizontalOptions = LayoutOptions.Center;

            Border newBorder = new();
            newBorder.HorizontalOptions = LayoutOptions.Center;
            newBorder.Content = label;
            newBorder.BackgroundColor = Colors.DarkCyan;
            newBorder.Padding = new Thickness(6, 2);
            newBorder.Margin = new Thickness(0, 4);
            newBorder.StrokeShape = new RoundRectangle() { CornerRadius = 12 };
            newBorder.StrokeThickness = 0.5;
            newBorder.HandlerChanged += delegate {
#if ANDROID
                Android.Views.View androidView = ElementExtensions.ToPlatform(newBorder, newBorder.Handler.MauiContext);
                newBorder.Shadow = new() { Offset = new Point(0, androidView.Context.ToPixels(5)), Radius = androidView.Context.ToPixels(4) }; //android needs re-scaling of shadows due to other reported bug elsewhere
#else
                newBorder.Shadow = new() { Offset = new Point(0, 5), Radius = 4 };
#endif
            };
            
            newBorder.Content = label;
            vert.Add(newBorder);
            
            Label label2 = new();
            label2.Text = "ALEXANDRA";
            label2.FontFamily = "NotoSansBold";
            label2.FontSize = 42;
            label2.HorizontalOptions = LayoutOptions.Center;
            label2.TextColor = Colors.White;
            label2.Margin = new Thickness(0, -5, 0, 10);
            label2.HandlerChanged += delegate {
#if ANDROID
                Android.Views.View androidView = ElementExtensions.ToPlatform(label2, label2.Handler.MauiContext);
                label2.Shadow = new() { Offset = new Point(0, androidView.Context.ToPixels(5)), Radius = androidView.Context.ToPixels(7) }; //android needs re-scaling of shadows due to other reported bug elsewhere
#else
                label2.Shadow = new() { Offset = new Point(0, 5), Radius = 7 };
#endif
            };
            label2.SizeChanged += delegate {
                Debug.WriteLine("LABEL: " + label2.Text + " SIZE W " + label2.Width + " H " + label2.Height);
            };
            vert.Add(label2);

            //==================
            //RESIZE FUNCTION
            //==================
            screenSizeChanged += delegate {
                vert.HeightRequest = screenHeight;
                vert.WidthRequest = screenWidth;
            };
            Debug.WriteLine("FINISHED BUILD OKAY");

        }
        private void invokeScreenSizeChangeEvent() {
            if (mainPage.Width > 0 && mainPage.Height > 0) {
                screenWidth = mainPage.Width;
                screenHeight = mainPage.Height;
                Debug.WriteLine("main page size changed | width: " + screenWidth + " height: " + screenHeight);
                screenSizeChanged?.Invoke();

                
            }
        }
    }
    
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-controls-label Label, Span area-drawing Shapes, Borders, Shadows, Graphics, BoxView, custom drawing platform/android 🤖 s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

6 participants