fjvallarino/monomer

Edge case where label text is not displayed

Closed this issue · 12 comments

Hello,

I have found an edge case where the text on a label does not get drawn.

The UI code looks like this:

buildUI
  :: WidgetEnv AppModel AppEvent
  -> AppModel
  -> WidgetNode AppModel AppEvent
buildUI wenv _model =
  hstack [
    label "a" `styleBasic` [paddingH 4]
  ]

Reducing paddingH to 3 makes the "a" show up. Changing "a" to "b" also makes the text show up.

Example with text not showing:
Screenshot at 2021-12-31 12-49-43

Example with text changed to "b" and therefore showing:
Screenshot at 2021-12-31 12-50-11

I have also attached a modified hello world that demonstrates this phenomenon:
monomer-label-text-not-displayed.zip

I suppose there is some edge case in the text rendering code that is not working quite correctly.

PS. Sorry for logging bugs! My experience with Monomer is actually really positive: I am converting a gi-gtk-declarative app and although it is not completely trivial (e.g. I am hacking together a 2d-grid container like Gtk Grid), it has been pretty easy to bend Monomer to my will.

Hi! No problems at all about reporting bugs! It's super helpful for me, thanks!

I will take a look as soon as possible, although I may not be able to do it today. I just wanted to suggest you take a look at Grid.hs if you are creating a custom layout widget. It's the most basic layout provided by the library, since it assigns the same space vertically/horizontally to each widget, and it may give you a general idea about how you can implement your own. Stack.hs may be interesting too if you want to assign the available space according to what widgets requested.

I will take a look as soon as possible, although I may not be able to do it today.

Thanks :) (there is no rush though!)

I just wanted to suggest you take a look at Grid.hs ... Stack.hs

Indeed, I have been looking at these and it has been useful. 👍

I have been myself trying to track down what I think might be the same edge case, which, as you might expect, seems to be due to floating point math. My own test code is:

buildUI
    :: WidgetEnv AppModel AppEvent
    -> AppModel
    -> WidgetNode AppModel AppEvent
buildUI _ _ = widgetTree where
    widgetTree =
        vstack [
            buildLine "Previous" 1.0,
            buildLine "Previous" 0.75,
            buildLine "Previous" 0.5,
            buildLine "Previous" 0.0,
            buildLine "Previousthing" 1.0
            ] `styleBasic` [bgColor slateBlue]

buildLine :: Text -> Double -> WidgetNode s e
buildLine caption pad =
        hstack [
            label caption `styleBasic` [padding pad, bgColor darkGreen]
        ]

Which gives an output of:

Screenshot from 2022-01-14 16-00-08

The issue appears to be that the function getTextLinesSize in Text.hs, line 192 called for the string 'previous' returns a value of 62.666666666666664. When the sizeReqAddStyle function in SizeReq.hs is called for a padding of 1.0 it adds 2.0, and the result of 62.666666666666664 + 2.0 is 64.66666666666666.

There are 14 6s in each case, so the two values are:
62.666666666666664
64.66666666666666

When the label widget tries to determine if the text overflows the given view port, it removes the padding of 2.0, and then checks if the width of the text, 62.666666666666664, is greater then the view port width, 62.66666666666666, which it is, so it truncates the text.

The same issue happens when adding a padding of 0.75, which adds 1.5. However, with 1.0 the sum is 63.66666666666666, hence no truncation. I expect that this will depend on font chosen, the exact text etc.

I am not sure what would the best way to handle this. Perhaps introducing a small tolerance to determine if the text should be truncated? But I'm not sure what else that might effect.

Hi! I'm a bit puzzled. I just tested the two examples (@Dretch's and @Kyarigwo's) in macOS Monterrey and Ubuntu 21.04, and they work fine on both platforms. I tried resizing and changing padding, but I could not reproduce the issue.

Which OS/version are you using?

I'll review the overflow code. I can add some tolerance to account for these rounding issues; since widgets are responsible for applying a scissor if their content may overflow, an extra character should not cause problems.

Doing a bit of debugging, I added a traceShow call to getTextLines, and the results I get are a bit different:

Size {_sW = 62.5, _sH = 16.0}
Size {_sW = 62.5, _sH = 16.0}
Size {_sW = 62.5, _sH = 16.0}
Size {_sW = 62.5, _sH = 16.0}
Size {_sW = 98.5, _sH = 16.0}

I assume the 62.666666666666664 you're getting corresponds to the 62.5 I got, but I wonder what is causing the difference...

So far, I have not been able to reproduce the issue, but it's clear that it happens. Unfortunately, this means I can't create unit tests for future scenarios. I'm wondering if the problem is CPU-dependent, OS-dependent, or some other thing.

Just in case, I created a branch that includes a simple check that hopefully helps with this issue. Originally, I planned to check for a percent difference, but maybe it's not necessary. To use it, you will need to update your stack.yaml:

- git: https://github.com/fjvallarino/monomer.git
  commit: b503405102ddc1ea25ff8cd36738abc5f1a4ee27

If you get a chance to test it, please let me know if it works for you.

Thanks for the detailed reports!

The fix works for me.

(original bug happens)
System specs: Ryzen 5600g, no GPU, Void Linux, xmonad, X11

The fix works for me as well.

On the main branch the issue occurs even for the Todo example for me, so I expected it to be specific to my machine setup.

My graphics card is a Nvidia GTX 1060, OS is Ubuntu 20.4.3, GNOME version 3.36.8, windowing system is X11.

I also need to use the appRenderOnMainThread switch.

@Litoprobka @Kyarigwo thanks for reporting it works for you!

I'll merge the PR to main, so it's included in the next release.

The fix works for me too.

Also, I noticed that the issue only happens when I start the app on my laptop screen. When I start the app on my external screen (higher resolution and higher pixel density) then everything bigger (as in, it uses more pixels) -- which is another issue by itself, I guess -- and the issue does not occur.

So perhaps the issue is related to HDPI-scaling, or something...

I'm interested in the external monitor situation. Is this on Windows? There are some DPI calculations/adjustments in Platform.hs when the application starts, but they are not applied when the window is moved to a different screen.

If you are launching the application on the laptop's screen and then moving it to the external monitor, maybe that's the cause for the bigger item size; I have not had the chance to test this scenario, since I use a laptop for testing on Windows. Alternatively, you can provide the appScaleFactor configuration when the application starts.

I'll close this issue since the fix is already on main. Maybe we can move the DPI discussion to a new issue.

Thanks @Dretch @Kyarigwo @Litoprobka for the help figuring this out!

I'm interested in the external monitor situation. Is this on Windows?

This is on Ubuntu Mate 20.04. I think the problem is that this uses X11 rather than Wayland -- and Platform.hs says "Currently only tested on Wayland (Ubuntu 21.04)". It seems Mate is going to get Wayland support soon anyway, so maybe this issue will disappear (at least for me) ;)