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

Potential wrong text measure with Inter Font #11804

Closed
llfab opened this issue Jun 15, 2023 · 18 comments
Closed

Potential wrong text measure with Inter Font #11804

llfab opened this issue Jun 15, 2023 · 18 comments
Labels

Comments

@llfab
Copy link

llfab commented Jun 15, 2023

Describe the bug
I have an application that shows different panels dependent on the workflow state. The app currently consists of empty panels for each state that are 100% identical. When clicking through different workflow states I noticed that one specific TextBlock text slightly moves left and right. Also Dev Tool shows that in one case the exact same TextBlock has a different width by one px.

It is worth noting that for the 3 workflow states there would be 3 user controls (one for each state) within the same grid whereas only one of which is set to IsVisible=true. Hence all exact same state specific controls exist at the same time. But only one is set to visible.

Here the differences:
grafik

grafik

In the 2nd image you can see that the blue selection starts at the same x pos but the gap to the "I" is larger.

And the image diff shows in the area of the information text:
grafik

In order to exclude potential differences in the switched user control, I have then used the very same user control for all states, sitll having different instances for each state. Same result.

If I remove the image left of the text the effect is still there.

The font is Bold, so I changed it to Normal which removes the effect also proven by the image differ. Also if I remove the .WithInterFont() from the builder (which lets Windows choose another default font, the effect goes away. Or is simply not noticable at least also not by the image differ.

Could there be a problem with the font measure?

To Reproduce
I have failed to create a minimal shareable sample so far.

Expected behavior
The text should appear at the very same position

Desktop (please complete the following information):

  • OS: Windows 10
  • Version 11.0.0-rc1.1
@llfab llfab added the bug label Jun 15, 2023
@llfab
Copy link
Author

llfab commented Jun 15, 2023

It seems that it is dependent on which of the controls is visible initially (hence, wich of the state is set on startup. The above behavior where the 2nd control is a bit further left happens if the 2nd item is selected on startup. If the 1st item is selected on startup the behavior is the other way around.

It has nothing to do with the order within Xaml (I have checked that).

@Gillibald
Copy link
Contributor

The highlight rectangle is pixel aligned and the text isn't. So the rendering might end up at different positions depending on the surrounding drawing operations. We might need to introduce pixel alignment for text as well.

@llfab
Copy link
Author

llfab commented Jun 16, 2023

Ok. Do you see a workaround? For now without limiting myself on Font Style I would potentially remove WithInterFont() as the app is only planned for Windows.

I also assumed it may be other drawing calls and I had removed a lot of things but did not find the causing other control.

Gout feeling is that it is the 3 user controls within the same Grid "behind" each other where only one is visible because the problem always goes with the one control that is displayed on startup. And only for that one not for the other 2.

@llfab
Copy link
Author

llfab commented Jun 16, 2023

Just to allow for better understanding, here some Xaml parts:

The parent bottom left control that contains the 3 different (but equal) controls for each state

  <Grid>
     <Grid IsVisible="{Binding CurrentGuidanceWorkflowState, Mode=OneWay, Converter={StaticResource guidanceWorkflowStateConverter}, ConverterParameter=Templating}" >
      <v:GuidanceInfoTemplatingView DataContext="{Binding GuidanceInfoTemplatingViewModel}" />
    </Grid>

    <Grid IsVisible="{Binding CurrentGuidanceWorkflowState, Mode=OneWay, Converter={StaticResource guidanceWorkflowStateConverter}, ConverterParameter=PlatePreview}" >
      <v:GuidanceInfoPlatePreviewView DataContext="{Binding GuidanceInfoPlatePreviewViewModel}" />
    </Grid>

    <Grid IsVisible="{Binding CurrentGuidanceWorkflowState, Mode=OneWay, Converter={StaticResource guidanceWorkflowStateConverter}, ConverterParameter=ArticularTargeting}" >
      <v:GuidanceInfoArticularTargetingView DataContext="{Binding GuidanceInfoArticularTargetingViewModel}" />
    </Grid>
  </Grid>

Sample of the contained user controls (all equal):

  <Grid Background="{StaticResource BackgroundDefault}">
    <bui:StretchPanel Orientation="Vertical"
                      Padding="16"
                      ItemSpacing="16">

      <bui:StretchPanel Orientation="Horizontal"
                        ItemSpacing="16">
        <Image Source="{StaticResource GuidanceInfoPlatePreviewViewInformationIcon}"
               Stretch="None" />
        <TextBlock Classes="titleText"
                   Text="{Binding Source={StaticResource GuidanceInfoPlatePreviewViewInformation}, Converter={StaticResource I18nMand}}" />
      </bui:StretchPanel>
    </bui:StretchPanel>
  </Grid>

Note: StretchPanel is similar to StackPanel. I also tried StackPanel which does not change the problematic behavior.

@Gillibald
Copy link
Contributor

We can see that the glyph layout itself matches. The text is rendered at a different x-offset which results in a bigger bounding box. We need to compare the baseline origins of the underlying TextBlock.TextLayout's. In detail each TextLayout contains TextLines and each TextLine contains TextRuns. A ShapedTextRun holds a GlyphRun and that is rendered at the GlyphRun.BaselineOrigin

Maybe you can compare the resulting values in your application. Ideally, it would be possible to extract your custom control to be able to reproduce it locally. In the end, this might be related to pixel boundaries. So measurements are the same but pixels are snapped to different edges.

@llfab
Copy link
Author

llfab commented Jun 16, 2023

I have tried a bit more but still cannot reproduce in decently simple app. Could you let me know which values behind TextBlock I were interested?

@timunie
Copy link
Contributor

timunie commented Jun 16, 2023

@llfab is any animation involved in the real world App? Idea is, that animation may not "finish" correctly causing this.

@llfab
Copy link
Author

llfab commented Jun 16, 2023

There are animations but only in other areas of the app. This main view is currently not animated. It sits in a Grid statically whereas in the same top level Grid there are additional TransitioningContentPresenters that work with anims. The state selection that makes the relevant controls visible/invisible is non-animated. So gout feeling is it's not that.

Anyhow, I find it interesting that the x-offset is always with the control that is displayed during startup. So, yes it may have to do with someting not being in its final state.

@llfab
Copy link
Author

llfab commented Jun 16, 2023

Even more. If I choose a startup state that does not have any control on the left side, then the offset appears in the one left control that you then selected first through the top level menu and the problem stays with that first selected state. Nothing animated there.

@timunie
Copy link
Contributor

timunie commented Jun 16, 2023

There are animations but only in other areas of the app. This main view is currently not animated. It sits in a Grid statically whereas in the same top level Grid there are additional TransitioningContentPresenters that work with anims. The state selection that makes the relevant controls visible/invisible is non-animated. So gout feeling is it's not that.

Anyhow, I find it interesting that the x-offset is always with the control that is displayed during startup. So, yes it may have to do with someting not being in its final state.

hm but that brings up another idea. So if in the same grid, any row or column below or above has size Auto, that can be re-calculated maybe. So in this case, can you try to hard code the columsn, rows for a test?

@llfab
Copy link
Author

llfab commented Jun 16, 2023

The gridd is auto yes but the size of the content is set:

  <Grid Classes="contentView"
        ColumnDefinitions="auto,*"
        DragDrop.AllowDrop="{Binding DropAllowed}" >
    <Grid Grid.Column="0"
          
          RowDefinitions="*,4,*">
      <v:GuidanceControlView Grid.Row="0"
                             DataContext="{Binding GuidanceControlViewModel}"
                             HorizontalAlignment="Left"
                             Width="{StaticResource XrayGuidanceViewControlViewWidth}"/>
      <Grid Grid.Row="1" Background="{StaticResource BackgroundLow}"/>
      <v:GuidanceInfoView Grid.Row="2"
                          DataContext="{Binding GuidanceInfoViewModel}"
                          HorizontalAlignment="Left"
                          Width="{StaticResource XrayGuidanceViewControlViewWidth}"/>
    </Grid>
    <v:GuidanceVisualizationView Grid.Column="1"
                        DataContext="{Binding GuidanceVisualizationViewModel}"
                        VerticalAlignment="Stretch"
                        HorizontalAlignment="Stretch" />
  </Grid>

@llfab
Copy link
Author

llfab commented Jun 16, 2023

hm but that brings up another idea. So if in the same grid, any row or column below or above has size Auto, that can be re-calculated maybe. So in this case, can you try to hard code the columsn, rows for a test?

As I thought, I had tested that already and it seems not to change anything:

  <Grid Classes="contentView"
        ColumnDefinitions="300,*"
        DragDrop.AllowDrop="{Binding DropAllowed}" >
    <v:GuidanceInfoView Grid.Column="0"
                        DataContext="{Binding GuidanceInfoViewModel}"
                        HorizontalAlignment="Left"/>
    <v:GuidanceVisualizationView Grid.Column="1"
                        DataContext="{Binding GuidanceVisualizationViewModel}"
                        VerticalAlignment="Stretch"
                        HorizontalAlignment="Stretch" />
  </Grid>

@llfab
Copy link
Author

llfab commented Jun 22, 2023

@Gillibald: I have created a sample app that shows the behavior by using my app but removing all dependencies to our libraries: https://github.com/llfab/Samples/tree/main/FontShift

Hope that helps.

In GuidanceModel.cs you will find:

        public GuidanceModel()
        {
            _trace = Trace.ForType<GuidanceModel>();

            _currentWorkflowState = GuidanceWorkflowStateType.PlatePositioning;

            _trace.Info("{0} successful.", Trace.Site());
        }

which defines the initial workflow state, hence, the initial views to be selected. This one does not contain any left side inner controls. Therefore, the first other state controls - that you click - will contain the font shift and the font shift will stay with that first selected one. If you restart again you can do a different first select that will again stick with that one.

@Gillibald
Copy link
Contributor

Thanks for taking the time to build a sample. Will have a look shortly.

@llfab
Copy link
Author

llfab commented Jul 29, 2023

@Gillibald : have you been able to track this down? Or is it difficult to get to the root cause?

@Gillibald
Copy link
Contributor

I did not have time yet to investigate this further. My theory is that the Grid control is caching the first measure because both times the available space is the same. Will get back to this in late August.

@llfab
Copy link
Author

llfab commented Jul 30, 2023

Thanks for the quick reply. No urgency here, Good to know that it is on the radar...

@Gillibald
Copy link
Contributor

Fixed in master

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

No branches or pull requests

3 participants