This evil word … refactoring. People wan’t to see progress and not some nice and clean code, but those two things are very strongly connected: There’ll be less progress when the code is obfuscated, complicated and hacky. Unfortunately clean code becomes hacky and unnecessary complicated all by itself 🙁 … no really, sometimes it just feels like good code is changing itself into bad code …
Nearly one month ago (27th of may) I started a huge refactoring of the whole UI system I wrote in the early days of METRO. Unfortunately it was not build to handle a great amount of UI elements in a smart way. I’m currently changing everything in this package and make things less bad. This also includes the way you interact with UI elements which is sometimes a bit … tricky and buggy.
tl;dr – Rewriting the UI system. Will take some time.
The old system was build around a class called ControlActionManager. This class holds all controls (buttons, input fields, labels, …) and manages interactions like clicks and key presses. It also drawed all controls.
The problem was that there were windows which controls are always above others, which should not get interaction events.
I solved this by saving the controls of a window in the window class itself and letting the ControlActionManager not know these controls. Unfortunately the window class had to handle all the interactions which is normally not the task of a normal window or container class.
But how do I separate clicks between normal controls and controls on a window or rather ignoring controls behind a window? Therefore I put the list with all windows directly into the METRO class … right … the init class of the whole project manages ingame-windows …
When the user clicks the touchDown method checks whether the click was on a window or not and then forwards the click to the window or to the ControlActionManager. You can now think of various other scenarios which will make the whole system even more unstable, e.g. “What if two windows are overlapping?” and also the very important question “How to delete controls when clicking on a control?”
Deleting controls after clicking on a control
Deleting controls when clicking on a control is a veeeery tricky thing. All controls in the ControlActionManager were stored in a normal ArrayList.
But there was also the GameScreen class, which held all controls that are visible in the game screen. The controls were stored in the ControlActionManager and in the GameScreen class … I think you can clearly see that this is not good … but wait for it 😛
Oh yes, every game screen of course knew the ControlActionManager, which was necessary in this construction.
When the ControlActionManager received a call to notify all controls about a click, the click-method iterated in a simple foreach-loop over controls in the list. It can now happen that I get a ConcurrentModificationException when removing a control from the ControlActionManager, which is the action a button does.
To understand this here’s a simple example control flow for simply closing a game screen via a button click:
METRO classes touchDown method → ControlActionManager → Buttons click-method → Observer-Pattern → The game screen containing that button → Calling the close method of the game screen → calling remove method of the ControlActionManager passing the local list of controls → BOOM ConcurrentModificationException
Solving the problem (you don’t want to read this -.- )
Okay, so I decided to create a buffer list to save all controls that should be removed after calling the click-method on all controls. This is a so bad idea that I hate myself for this. It absolutely did not solve the problem, because after calling the remove-method in the game screen, the controls are still there … after calling remove()!!!
I’m sorry 🙁
I hope you now understand why I need to refactor this whole mess (you can view the code by browsing through this commit: link). There’ll be an article about the system I implemented a few days ago. It already works but I have to use it whenever I used the old system and that are a lot of occurrences.