fjvallarino/monomer

Dropdown showing wrong data

Closed this issue ยท 24 comments

I have a dropdown declared like this:

     dropdown_
            selectedKeys
            (map (\k -> Just k) (model ^. keys))
            currentKeysTree
            currentKeysTree
            [ onChange KeysSelected ]
            `styleBasic` [ width 400 ]

Further there is a possibility to add more keys to model.keys. If a new entry is added, the dropdown gets longer, but the last element is duplicated in the UI, instead of being rendered correctly.

Using Debug.Trace proves the data in model.keys seems to be correct.

Start point (only one element):

[Just ("2970232b2c4a65ed3ab853fbbf54b894277a5236124480dea67b81e30da94b44749385522b940654ab1bdf69358e10a48c546182614eeb650fb09b6c87de82e32013bc3efa885efe545e3432498d834aaf4db2f0ff3f37c65389557432365c0e","e382de876c9bb00f65eb4e618261548ca4108e3569df1bab5406942b52859374",True)]

The value e382de876c9bb00f65eb4e618261548ca4108e3569df1bab5406942b52859374 is rendered correctly in the dropdown.

One more element added:

[Just ("db1b173fbc7d0c3fdc15d39b663e0b5535f61e4d41cb228fd337317da2ca2853c645209e2e99deb17736d92c68a38c94a49dfc85d1d6a1d0e2c7354b5d995fe2a4362ea8c002cfe5d5db2967da7127670ab946142405edf46212bc88679be804","e25f995d4b35c7e2d0a1d6d185fc9da4948ca3682cd93677b1de992e9e2045c6",True),Just ("2970232b2c4a65ed3ab853fbbf54b894277a5236124480dea67b81e30da94b44749385522b940654ab1bdf69358e10a48c546182614eeb650fb09b6c87de82e32013bc3efa885efe545e3432498d834aaf4db2f0ff3f37c65389557432365c0e","e382de876c9bb00f65eb4e618261548ca4108e3569df1bab5406942b52859374",False)]

Two elements e25f995d4b35c7e2d0a1d6d185fc9da4948ca3682cd93677b1de992e9e2045c6 and e382de876c9bb00f65eb4e618261548ca4108e3569df1bab5406942b52859374 rendered correctly.

Third element added:

[Just ("a9515fef92866221e144b17ea32cd8c5be2338e306c834be470b5b4216d03dcc80bb04c1e5039ec23e1f5f951a145d6dd4a39ab76f6a95f2e4c5505d51e749110f60e6b7a26fddff3ee9cae0ebb670d053e3da4d5b854e5e6f310706f5b8290b","1149e7515d50c5e4f2956a6fb79aa3d46d5d141a955f1f3ec29e03e5c104bb80",True),Just ("db1b173fbc7d0c3fdc15d39b663e0b5535f61e4d41cb228fd337317da2ca2853c645209e2e99deb17736d92c68a38c94a49dfc85d1d6a1d0e2c7354b5d995fe2a4362ea8c002cfe5d5db2967da7127670ab946142405edf46212bc88679be804","e25f995d4b35c7e2d0a1d6d185fc9da4948ca3682cd93677b1de992e9e2045c6",False),Just ("2970232b2c4a65ed3ab853fbbf54b894277a5236124480dea67b81e30da94b44749385522b940654ab1bdf69358e10a48c546182614eeb650fb09b6c87de82e32013bc3efa885efe545e3432498d834aaf4db2f0ff3f37c65389557432365c0e","e382de876c9bb00f65eb4e618261548ca4108e3569df1bab5406942b52859374",False)]

expected elements to be rendered:

1149e7515d50c5e4f2956a6fb79aa3d46d5d141a955f1f3ec29e03e5c104bb80, e25f995d4b35c7e2d0a1d6d185fc9da4948ca3682cd93677b1de992e9e2045c6 and e382de876c9bb00f65eb4e618261548ca4108e3569df1bab5406942b52859374.

actual elements being rendered:

1149e7515d50c5e4f2956a6fb79aa3d46d5d141a955f1f3ec29e03e5c104bb80, e382de876c9bb00f65eb4e618261548ca4108e3569df1bab5406942b52859374 and e382de876c9bb00f65eb4e618261548ca4108e3569df1bab5406942b52859374.

For each further element this goes on and on.

I replaced (map (\k -> Just k) (model ^. keys)) with (trace (show (map (\k -> Just k) (model ^. keys))) (map (\k -> Just k) (model ^. keys))) to get some debug output, the data seems to be correct, but the dropdown renders wrong content. The element itself seems to be correct though, the correct element is getting selected (using onChange), but the display text is wrong.

I just updated to latest commit of yours (55ae7f8), the problem still persists.

Do you have a complete example I can use for testing? If it is a single file, you can paste it here. You can format it with Haskell colors if you use:

```haskell

your code here

```

try this:

{-# LANGUAGE OverloadedStrings    #-}
{-# LANGUAGE TemplateHaskell      #-}

module Main where

import           Control.Lens
import           Data.Text                            (Text, pack)
import           Monomer
import           Monomer.Widgets.Single

type AppWenv = WidgetEnv AppModel AppEvent

type AppNode = WidgetNode AppModel AppEvent

data AppModel =
  AppModel
    { _fields         :: [Text]
    , _current        :: Int
    , _currentField   :: Text
    }
  deriving (Eq, Show)

data AppEvent
  = AppInit
  | AddField
  | FieldSelected Text
  deriving (Eq, Show)

makeLenses 'AppModel

handleEvent
  :: AppWenv
  -> AppNode
  -> AppModel
  -> AppEvent
  -> [AppEventResponse AppModel AppEvent]
handleEvent wenv node model evt =
  case evt of
    FieldSelected f ->
      [ Model $ model
        & currentField .~ f
      ]
    AppInit ->
      []
    AddField ->
      [ Model $ model
        & fields .~ newField : oldFields
        & current +~ 1
      ] where
        newField = pack ("Text_" ++ show (model ^. current))
        oldFields = model ^. fields

buildUI :: AppWenv -> AppModel -> AppNode
buildUI wenv model = widgetTree
  where
    widgetTree =
      vstack
        [ hstack
            [ filler
            , dropdown_
                currentField
                (model ^. fields)
                currentFieldNode
                currentFieldNode
                [ onChange FieldSelected ]
                `styleBasic` [ width 400 ]
            , spacer
            , button "Add" AddField
            ] `styleBasic` [ paddingL 10, paddingR 10]
          ] `styleBasic` [height 20]

currentFieldNode :: Text -> AppNode
currentFieldNode t = label t

main :: IO ()
main = do
  startApp model handleEvent buildUI config
  where
    config =
      [ appWindowTitle "Monomer Bug"
      , appTheme darkTheme
      , appFontDef "Regular" "./assets/fonts/Roboto-Regular.ttf"
      , appInitEvent AppInit
      ]
    model = AppModel ["Test_0"] 1 "Test_0"

Just hit the "Add" button a few times and see what's in the dropdown

Somehow related:

I continued some work on the project today and updated the function currentKeysNode (aka currentFieldNode in the example above) like this:

currentKeysNode :: [ReceivedEvent] -> Maybe Keys -> AppNode
currentKeysNode res mks = case mks of
    Just ks ->
      label $ profileName res (snd' ks)
    Nothing ->
      label ""

profileName function:

profileName :: [ReceivedEvent] -> XOnlyPubKey -> Text
profileName res xo =
  maybe (pack $ exportXOnlyPubKey xo) pdName (profileDataFromReceivedEvents res xo)

Dropdown is defined like this:

dropdown_
  selectedKeys
  (map (\k -> Just k) (model ^. keys))
  (currentKeysNode (model ^. receivedEvents))
  (currentKeysNode (model ^. receivedEvents))
  [ onChange KeysSelected ]
  `styleBasic` [ width 400 ]

As you can see, currentKeysNode is used twice, for the header as well as the list.

Now look at the output:

Screenshot_20220515_205741

The header shows correct data (aka pdName (profileDataFromReceivedEvents res xo)), the dropdown shows the fallback (aka pack $ exportXOnlyPubKey xo. How is this even possible?

There was a bug in selectList (which is used by dropdown) that would ignore WidgetRequests made by child widgets.

The label widget recalculates its text glyphs during resize, and since its WidgetResize request was being ignored, the label showed stale glyphs. You can check that the data was still correct by resizing the window; that would cause all widgets to resize, and labels would show the correct values.

You can test the fix by modifying your stack.yaml to point to:

- git: https://github.com/fjvallarino/monomer.git
  commit: 6dff90b4ddf9f61bd8634c754378c99df0f246ac

Please let me know if it fixes the problem. Thanks!

No actually it makes it worse.

It now shows the same values for all select-able options.

Ok I take this back, that is shows same values is a problem on my side, I reverted my latest commit and this is no more. But it still shows the fallback only in the dropdown, while it renders the correct value in the header.

I tested with your example. If I add 3/4 items and open the dropdown with the new version, the values it shows are correct. If I revert to the previous version, it shows incorrect labels.

Could you test with the example you provided initially? It's hard to validate when we're testing different versions since unrelated things could be happening. Otherwise, please provide the same example you're testing with.

Maybe running stack clean or stack purge to ensure you're using the latest commit can help.

I just received this upon stack run:

X Error of failed request:  BadValue (integer parameter out of range for operation)
  Major opcode of failed request:  152 (GLX)
  Minor opcode of failed request:  3 (X_GLXCreateContext)
  Value in failed request:  0x0
  Serial number of failed request:  101
  Current serial number in output stream:  102

Nothing changed related to GL context handling in the last several commits, particularly compared to the version you were using previously. That looks like an issue on your machine.

Restart the computer solved it.

I can confirm that the example I provided is fixed now. However it's not for my project. Trying to get more information.

If you can share it somehow, I can take a look.

Sure if you have the time. You'd need however a bit more than "only" haskell, you also would need to compile a C lib from source.

If you think the library is necessary, I can try to build it in a Linux VM.

If you think that library is not essential for the issue we're investigating, and replacing that invocation with some mock would be good enough, it would be better since it's easier for me to test it

Step 1:

git clone https://github.com/bitcoin-core/secp256k1
cd secp256k1
./autogen.sh
./configure --enable-module-schnorrsig --enable-module-extrakeys --enable-module-ecdh --enable-experimental
make
sudo make install

Step 2:

git clone https://github.com/prolic/FuTr.git
cd FuTr
stack run

In the top right corner you can add new keys, with the edit button you can set a profile name. Try switching between profiles and adding new ones.

I also attached a screen recording.

pl-next-2022-05-17_17.52.17.mp4

I do totally understand if you don't have the time to look into my mess, let me know and I try to provide another example, would take me a while though.

I think the problem is caused because the data you store as the list of keys does not really change; what changes is the name you retrieve from those keys. The selectList widget, used internally by dropdown, does not refresh its content if the items you provide have not changed. Since

type Keys = (KeyPair, XOnlyPubKey, Bool

did not change, it does not call your function for regenerating the internal nodes.

There are two options:

  • You can store the name of the Keys once you retrieve it (updating the list). That would trigger the dropdown update.
  • Use mergeRequired to force the dropdown to always update itself.

The problem with the second alternative is I forgot to export this configuration option from dropdown (it is available in selectList). I'll add it now and test (I'll let you know how it goes), but I still think the first option is preferable. These widgets (dropdown and selectList) avoid updating their content for performance reasons; if you have a long list of items, you'd be incurring a performance penalty that can be avoided by keeping all the data you need for rendering in your model.

An unimportant suggestion: you can use lenses to access arbitrarily sized tuples, with tuple ^. _5 retrieving the fifth element independently of the tuple's size (up to 10, I think).


The application looks very nice! It's great that you created custom widgets for rendering UI decorations ๐Ÿ˜ƒ

You can store the name of the Keys once you retrieve it (updating the list). That would trigger the dropdown update.

That's something I am working on at the moment.

An unimportant suggestion: you can use lenses to access arbitrarily sized tuples, with tuple ^. _5 retrieving the fifth element independently of the tuple's size (up to 10, I think).

That's awesome, however I am about to refactor the keys to this:

data Keys = Keys KeyPair XOnlyPubKey Bool
  deriving (Eq, Show)

I might just add the profile name here.

The application looks very nice!

Really? I hate it, but UI improvements are not my highest priority at the moment, I am working on profile and followers at the moment.

That's awesome, however I am about to refactor the keys to this:

Nice!

The problem is indeed caused by the data not changing; once you add the name to your Keys type, it should work fine.


I exported mergeRequired from dropdown as part of the PR, although you should not need it in this case.

Thank you very much. I'll keep you posted once I'm done with the changes.

Yes! Works like a charm. Thank you very much.

This has just been merged to main. I'll close the issue. Thanks for reporting!