GUI Refactoring
(Part of GSoC 2011)
Goal
Goal of this project is to allow applets to load a smaller and quicker version of GeoGebra in certain cases and to refactor certain parts of GeoGebra's GUI in the process to speed up application startup in general.
Pre-Project State
While minimal applets were supported in GeoGebra 3.2 this feature did not work properly in beta releases of GeoGebra 4 as new components were not adapted to support the necessary minimal application initialization.
The main problem has been the strong coupling between file loading and the graphical user interface. Settings stored in documents were applied to GUI elements immediately, which forced their initialization, even if the elements themselves were not required or used.
Concept
Following the reasoning above the main goal of this refactoring is to introduce an additional layer between application settings and GUI elements.
This lightweight layer makes it possible to load settings without being forced to initialize the GUI, but it is not only intended to be used as a helper while loading. By keeping the settings in this container throughout the life cycle of the application it's possible to use an Observer pattern to decentralize the logic about what should happen if a setting changed.
A motivation of this design pattern's usage is the following problem with the current design: The menu is using some kind of information from the euclidian view, like the grid visibility. The visibility is stored as attribute of the euclidian view. If this attribute is changed the euclidian view has to determine that the menu has to be updated. What if the menu is not initialized — as it may happen in applets? The euclidian view has to take care about this case as well.
Reasonably the logic about when to update the menu should be part of the menu, not of the euclidian view. But there's not just the bad software design as an measure of aesthetic or the higher maintenance costs speaking against the current design: If multiple settings of the euclidian view (which are also used in the menu) are updated, e.g. while loading another file, there is no simple way to detect that the menu probably has to be updated just once: After all settings were changed.
The new design solves both problems at once. Settings can be observed using SettingListeners, which will be informed as soon as a setting changed. If some part of the application updates multiple related settings at once it can use batching to save performance: If a batch is started listeners are not notified about single changes, but just after the batch has been ended again.
As most parts of the application are concerned about multiple (but probably related) settings not single settings are observed, but setting containers. They are a group of logically related settings and just share a simple toolset for notifications and batching. One setting container could be about settings of a single euclidian view, called EuclidianSettings, another could be about settings of the algebra view (AlgebraSettings). There's also a general ApplicationSettings container, which can be used for miscellaneous settings.
Class Diagram
Loading Files
The diagrams below indicate how file loading will work at startup (using the commandline argument or in applets) and while the application is running.
At Startup
The main difference between loading at startup and loading while running is that the GUI is initialized after the file is loaded at startup. Therefore few listeners will listen to changes of settings, the GUI is initialized and will be using the information stored in the settings afterwards for initial construction. The parsing itself is not affected by this difference, however, so no differentiation is necessary there. Note the usage of batching while parsing to notify listeners about changes just once.
While Running
The GUI is already initialized and additional components will be initialized afterwards if necessary. Existing GUI elements will be listening to changes.
Technical Notes
There is no specification about how to store, fetch or retrieve settings in the individual setting containers. It's up to the developer to implement these methods. If a setting's value has been changed (not if the 'new' value is identical to the old one) the developer must call AbstractSetting::settingChanged(), otherwise listeners are not informed about the change. That method also takes care of batching.
There is no golden rule about when to extend an existing setting container or to implementing a new one. If new settings are similar to existing ones or not updated frequently extending a container is probably the way to go. If a container has listeners which perform time-consuming tasks while updating creating a new container may be a good idea.
And a final note: Not all GUI elements which use settings should be notified about changes, just those which need to be informed immediately. The option dialog, for example, should not be implemented as setting listener as it can be updated at the time the dialog is opened as well.
Necessary Changes
- Implement general classes outlined above
- Take use of these classes while loading files
- Replace settings spread across the different classes with central setting containers (like EuclidianSettings).
- Use settings container while saving as well.
- Implement listener interfaces for views and other GUI components
Attachments
-
classes.png
(246.6 KB) -
added by florian 10 months ago.
Class diagram.
-
loading-comparison.png
(8.4 KB) -
added by florian 10 months ago.
Comparison between file loading using the old system and using the new concept.
-
loading-init.png
(52.2 KB) -
added by florian 10 months ago.
Sequence of commands while loading at startup.
-
loading-running.png
(38.5 KB) -
added by florian 10 months ago.
Sequence of commands while running.




