MaterialFX icon indicating copy to clipboard operation
MaterialFX copied to clipboard

Sluggishness when loading new MFX controls in scene

Open SylvainBertrand opened this issue 1 year ago • 14 comments

Hi!

Before I start, your library looks quite nice! I've been using JFoenix, but like you had to previously, I inevitably will have to switch to something else that is actually maintained. I've been looking at MaterialFX a few times now and it does seem like it'd be the best move.

Alright so, it appears that in comparison to JFoenix controls, MaterialFX controls are much heavier to load for some reason. I tried to look around to see if I was doing something wrong, but I couldn't find anything for sure. I've got the styling working, but it's just that the controls take a long time to load which is a concern to me as the app I'm hoping to use this library for has many controls. Also when creating a layout in SceneBuilder, as soon as I add/edit MaterialFX controls, it takes a long to refresh the layout. I also downloaded the demo and it does look like it also take a long time to initialize the controls as well.

Once the controls are loaded though, everything is very responsive though. So it seems to me that there is a lot of file operations when creating the controls.

Alright, I'm done here with my ramble, I wanted to know if that's something you're aware of, if there's a solution/workaround to it, or maybe I'm doing something stupid 😅

I've tested MaterialFX on Windows 10, on a computer that runs JavaFX pretty nicely typically.

Cheers

SylvainBertrand avatar Dec 21 '23 02:12 SylvainBertrand

I don't know for sure, it could be anything. In general loading many nodes could be an issue.

  1. When do slowdowns occur exactly?
  2. What kind of operations are you running?
  3. What are the specs of the PC? GPU can really speed up things

The demo tries to mitigate this by caching all the different Scenes at init time which of course results is a much longer time to show the app. Then there's the new way themes are built, one thing you could do is to cache it on the disk and on subsequent runs check for its existence and load it eventually. It could also be the CSS not being optimized (I suspect this may be the real troublemaker)

As for SceneBuilder, the issue should occur only on drag and drop of MFX controls, because at that time each controls inject CSS in the Scene, doing this at runtime is indeed heavy on performance. The problem is, since the theming system has changed, SceneBuilder support has gotten much more complicated and as far as I know there's no solution for now.

palexdev avatar Dec 21 '23 07:12 palexdev

  1. I tested switching just a single window in my app. The window has about 10 text fields, 1 combo box, and 2 radio buttons. It loads quickly when using JFoenix, but when switching to MaterialFX, the time it takes to load the scene (from FXMLLoader call to the stage actually showing) is significantly longer, something like 0.5 second for the window to show up.
  2. I'm loading an FXML which depends itself on another FXML. Not doing anything else crazy.
  3. I've tested on a gaming laptop and now on a gaming desktop. Once loaded everything runs smoothly, it is loading part that is sluggish.

For the demo, I just re-downloaded the 11.16.0 demo and from the moment I launch the demo (using the launch script from a terminal) to the moment the demo window shows, it takes 15 seconds. I'm under the impression that this is not expected. This is the second Windows computer I'm testing on and getting the same behavior.

I figured reading your struggle with the themes that SceneBuilder might be a tricky to support properly. However, changing a single property of an already imported control can take close to 1 second.

Hopefully, I did a better job explaining the behavior I'm seeing on my end.

SylvainBertrand avatar Dec 21 '23 15:12 SylvainBertrand

I forgot to mention that I'm using the Azul Zulu Build of the JDK 17.0.3.

SylvainBertrand avatar Dec 21 '23 16:12 SylvainBertrand

  1. I tested switching just a single window in my app. The window has about 10 text fields, 1 combo box, and 2 radio buttons. It loads quickly when using JFoenix, but when switching to MaterialFX, the time it takes to load the scene (from FXMLLoader call to the stage actually showing) is significantly longer, something like 0.5 second for the window to show up.
  2. I'm loading an FXML which depends itself on another FXML. Not doing anything else crazy.
  3. I've tested on a gaming laptop and now on a gaming desktop. Once loaded everything runs smoothly, it is loading part that is sluggish.

For the demo, I just re-downloaded the 11.16.0 demo and from the moment I launch the demo (using the launch script from a terminal) to the moment the demo window shows, it takes 15 seconds. I'm under the impression that this is not expected. This is the second Windows computer I'm testing on and getting the same behavior.

I figured reading your struggle with the themes that SceneBuilder might be a tricky to support properly. However, changing a single property of an already imported control can take close to 1 second.

Hopefully, I did a better job explaining the behavior I'm seeing on my end.

  1. 0.5 is not that much though. It's noticeable yes, but I don't think it's that disruptive of the UX. You should also take into account that if the loading includes the building of the UserAgent theme then there are disk operations which of course slow things down. One thing you could do is to cache the theme on the disk.
  2. Good to know, so the machines are good, thus we can exclude hardware issues

As for the demo... it's complicated. The huge amount of time to show is expected, I'd like to avoid it but from my testings it's actually the best thing for UX. You see, the demo loads every Scene at init time, before the app is shown, and also caches the Scenes. This way the entire app will feel responsive once it's shown. If this process is skipped, the app would open pretty fast but then switching between the various tabs would cause some lag. I believe the demo could be rewritten.

As for SceneBuilder the only thing I could do is to investigate further if there is a way to add my themes in a better way that would not cause performance issues. Although, I'd like to drop support for it one day and build a new tool that is more flexible and modern.

Unfortunately I don't have enough time to resolve these issues. Like I said somewhere here (in the Discussions I believe) the main branch is on hold, and technically the rewrite one too, because I'm focusing on something else right now (the rewrite of VirtualizedFX, and "irl" commitments unfortunately). The one thing I can say is that I want to work on my projects real bad, because I love what I do, it's fun! So, eventually, issues will be addressed, rewrites will be completed, ideas will be brought to life.

palexdev avatar Dec 22 '23 09:12 palexdev

You see, the demo loads every Scene at init time, before the app is shown, and also caches the Scenes. This way the entire app will feel responsive once it's shown.

This is a good solution @palexdev, is there a way I could do this with ease(what I currently do is create the FXML panes at start up in a splash screen, but this means for every new FXML screen I create I am required to create it's pane which can later be called or passed whenever needed. I feel this isn't a clean way of doing things)

infinite-dev22 avatar Dec 22 '23 14:12 infinite-dev22

@infinite-dev22 It's a good remedy, but ideally it shouldn't be done. Like I said the CSS should be optimized and the demo restructured. The load mechanism is here DemoController Although, there's one flaw, I'm using tasks/threads to load the Scenes, but I didn't make it async and I can't remember why, I definitely have to look into it

palexdev avatar Dec 22 '23 19:12 palexdev

@palexdev I want to rewrite the demo, but a certain feature that I'd like to add is still not yet perfect to the point that I want it to be. Though if you don't mind the "but" I can go ahead and build it.

infinite-dev22 avatar Dec 22 '23 20:12 infinite-dev22

@palexdev I want to rewrite the demo, but a certain feature that I'd like to add is still not yet perfect to the point that I want it to be. Though if you don't mind the "but" I can go ahead and build it.

What feature if I may ask? I don't mind a helping hand, but not right now. There's just too much going on lately and I wouldn't be able to properly follow the development, so take your time. I'll try to speed things up in the coming days (well festivities excluded...maybe haha)

palexdev avatar Dec 22 '23 20:12 palexdev

I want to make a Side Navigation Bar, I want it to be expandable i.e. Show iCons only or Show the Icon and Text as the user prefers it. I am having a side bar for now, though it's not animated(I am building it with JFX TreeView, I am thinking of using Accordion instead). I intend on adding this to the demo with aim of having navigation groups that are collapsible in the demo

infinite-dev22 avatar Dec 26 '23 21:12 infinite-dev22

Ah, fantastic! I've always wanted to create one myself but never found the time. There are multiple ways to implement it. One approach is through a VirtualizedFlow, which essentially acts as a list. You define the type 'T' for the buttons—for instance, a button could contain text, an icon description, and an action. Then, generate these objects, store them in an ObservableList, and supply it to the list. The new VirtualFlow offers variable breadth*; This means you could animate the list's size to display just icons or everything at once. Plus, as buttons are usually evenly spaced, this list makes for an ideal component. However, it doesn't support spacing, you'd need to adjust the cell size and padding to make it work.

*The breadth is the opposite size to the one virtualized: Width -> Height Height -> Width

palexdev avatar Dec 26 '23 22:12 palexdev

There are multiple ways to implement it. One approach is through a VirtualizedFlow, which essentially acts as a list. You define the type 'T' for the buttons—for instance, a button could contain text, an icon description, and an action. Then, generate these objects, store them in an ObservableList, and supply it to the list.

I haven't personally interracted with VirtualizedFlow yet... Kinda scary(complexity of creating new JFX components) to do so, though I let me explore and try it out. Thanks for the tip.

infinite-dev22 avatar Dec 26 '23 23:12 infinite-dev22

Hello, @palexdev . As earlier stated about my intention to do a Side Navigation Bar, I haven't built a JFX Widget from nearly scratch before I have only been extending existing widgets to create new ones with intended functionality. So after looking into VirtualizedFX... I was able to do some little work, though I put it on hold after I was asked for a Skin. I don't know how to create a custom skin and also am not sure if I am doing things right.

infinite-dev22 avatar Jan 29 '24 05:01 infinite-dev22

@infinite-dev22 Lately, I've been improving my technique at making custom controls from scratch. Use the MVC pattern.

  1. The Control class (which is the Model) contains the properties which define the capabilities and state of the component
  2. The Skin class (which is the View) contains the nodes that compose the component's look. They react to input and state changes
  3. The Behavior class (which is the Controller) contains methods that will change/update the state. The Skin, which is responsible for the graphic only, can add listeners and event handlers which will call methods in the behavior. For example, the click on a button should trigger the behavior.mouseClicked(me) method (You may or may not use the new MaterialFX APIs to achieve this, see MFXCore on the rewrite branch)

You can check examples of this on the rewrite branch: Button, IconButton

Don't be intimidated by making a custom skin, it's simple, really. You just have to add the nodes that compose the component to it and then define the layout strategy by overriding the layoutChildren(...) method. You may also want to adjust the component's size so that the layout is correct, just override the compute min/pref/max width/height methods. But I understand that doing this "manually" can be a little bit difficult for a beginner, you can avoid this by using JavaFX containers (Pane, HBox, VBox, GridPane,....) which will handle sizing and layout for you. If you follow this path, add the container to the skin (will act as the root node) and then add all the other nodes to the container

Anyway, for any possible follow-up, use the Discussions instead, let's not flood this thread ;)

palexdev avatar Jan 29 '24 09:01 palexdev

Thanks @palexdev , This is helpful. Contains most answers to the questions I had in mind.

But I understand that doing this "manually" can be a little bit difficult for a beginner, you can avoid this by using JavaFX containers (Pane, HBox, VBox, GridPane,....) which will handle sizing and layout for you

I will use the JavaFX containers route, then later I can experiment with the manual route.

infinite-dev22 avatar Jan 29 '24 10:01 infinite-dev22