Design
UI Basics
UI design is a large and complex field of study so don’t expect a book here; just some terribly simplistic guidance. The basic guidance we attempt to follow for UI design is:
- Information design first, Interaction design later.
This means our first obligation is the presentation of information that allows the user to answer questions/make decisions that will most directly service their interests. This implies that when adding or altering the ui (or when performing ui reviews on merge requests) we attempt to:
1. Determine the users' specific interest(s).
2. Determine what information is necessary to service those interests.
3. Determine the best design for presenting this information that allows the user to answer questions/make a decision.
For step 3, the “best design for presenting information” includes a mixture of both objective (cognitive/physical workload impacts) and subjective (aesthetics) choices. Poor presentation (too much/too little information, clutter, poor grouping, poor balance, lack of/too much white space, etc.) is assumed to have some negative impact on the cognitive/physical workload necessary for the user to assimilate the information presented. Information presentation is, fundamentally, a graphic design exercise. We will bias our objective information design choices toward classically good graphic design practices that, where possible, minimizes interaction.
Most interaction(clicking, tabbing, touching, hovering, dragging, etc.) is assumed to have a significant negative impact on cognitive/physical workload; a hurdle the user must jump over to answer questions/make decisions relevant to a specific interest. Despite the above-stated bias, there are times when we will choose to take the negative impact of interaction to present more contextual information that will better service the specific user interest. This may happen when information may be excluded or otherwise limited due to choices made in the information design (limitations on space, information density, aesthetics, etc.) and it becomes necessary to switch the context to provide more information. e.g. We choose not to show all Alanis Morissette songs when presenting a list of artists, so we provide an interaction mechanic that changes the information context to Alanis Morissette where we can present more information – all her songs. The hope is that when such choices are made, the net impact to the user is positive.
Low Level Design
Most functions of Bangarang revolve around three basic design elements:
MediaItem
The most important design element in Bangarang is the MediaItem. This is a simple object that can represent any playable item of media (audio clip, music, video clip, movie, etc.). A MediaItem can also refer to a collection of MediaItems (Artist, Album, Genre, Movies, TV series, etc.). Multiple MediaItems, a Media List, are generally handled with QList< MediaItem >.
MediaItemModel
The second most important design element in Bangarang is the MediaItemModel. Media Lists are stored in a MediaItemModel which can be connected to a View for visualization in the UI.
ListEngine
MediaItemModels populate themselves using the third most important design element in Bangarang: the ListEngine. ListEngines can generate Media Lists from a variety of sources (datastore, files, CDs, DVDs, remote sources, etc.) for a variety of media types (music, audio clips, audio streams, movies, TV series, etc.). For example, when requested a ListEngine can query the nepomuk datastore to generate a Media List representing a list of songs by a particular artist, or a list of documentaries. Another may generate a Media List representing a list of files the user selected to open. Yet another could query a remote source to generate a Media List representing a list of online radio stations.
More details on MediaItemModels and ListEngines
MediaListProperties holds meta information on the media list currently stored in the MediaItemModel. There is one special property, MediaListProperties::lri. This is the list resource identifier. It looks very similar to a url but it is strictly used internally. The lri contains all the information needed to load a media list. It is made up of three parts: the engine, the argument and the filter. Everything up to and including “://” is the engine. Everything between that and “?” is the engineArg. Everything after that is the engineFilter. The MediaListProperties object has convenience methods( MediaListProperties::engine(), MediaListProperties::engineArg() and MediaListProperties::engineFilter() ) which does the parsing and returns the appropriate substring.
When the MediaItemModel needs to load a Media List based on the specified MediaListProperties, it does the following:
– Checks the ListEngineFactory to see if a ListEngine exists for the engine specified in the MediaListProperties lri.
– Requests an available ListEngine from the ListEngineFactory for the engine specified in the MediaListProperties lri
– Requests a unique request signature from the ListEngineFactory
– Sets the ListEngine to use the request signature
– Sets the ListEngine to use the MediaListProperties
– Start the ListEngine
Since ListEngines run asynchronously (to prevent ui blocking on long queries) they are provided a unique request signature so that when they return their resulting Media List, the MediaItemModel can decide if it is appropriate to display the results (since the user may have requested a different media list in the mean time).
The ListEngineFactory is responsible for creating and making the appropriate ListEngines available to the MediaItemModel. It will only create new ListEngine objects when necessary. It will reuse existing ListEngines if they’re idle. It also provides unique request signatures for the MediaItemModel to assign to ListEngines.
Note: The MediaItemModel never “interprets” the lri. Only the ListEngineFactory and the ListEngines truly “understand” an lri and know how to use it to retrieve a Media List.
MediaItem Categories and sources
MediaItems can represent either actual media resources(files, etc.) or collections/categories of MediaItems. Which means, it is conceivable for a category to refer to listings of other categories. For MediaItems that are categories, the MediaItem::url property is simply the lri representing the list of MediaItems the category represents. For example, the category Alanis Morissette could refer to a list of Alanis Morissette albums, each of which in turn could refer to a list of songs on that album. For lack of a better term, songs in this example are considered sources. i.e. The actual playable media resource is called a source.
If we want to load all the Alanis Morissette songs without having to first get each album, then the songs for each album, we can simply use the MediaItemModel::loadSources() method. Since the MediaItemModel doesn’t really “understand” the lri, it has to allow the ListEngine to figure out how to produce a Media List that contains sources as opposed to categories. The only thing the MediaItemModel needs to supply to the ListEngine is the correct filter so that the MediaList returned will be restricted to, for example, only Alanis Morissette songs. It uses the ListEngine::setFilterForSources(QString filter) to do this.
Note: If the ListEngine never generates MediaLists containing categories, then there is no reason for the ListEngine to implement the setFilterForSources method.
The MediaItem “Action”
In addition to categories and sources. MediaItem may also represent an “Action”. This is an action that would be needed to retrieve or otherwise act on a Media List. The difference between a category and an action is that an action is not selectable while a category is. For example you can select the “Alanis Morissette” MediaItem. If you select “Alanis Morissette” you should expect to be able to hit Play and the list of Alanis Morissette songs start playing. That MediaItem is a category. On the other hand, when you click “Open File(s)” you expect to be presented with an open file dialog. You do not expect to select “Open File(s)” and hit Play since “Open File(s)” is a command, an action. ListEngines can act on MediaItems that are clicked by implementing the actionActivated method.
Note: If the ListEngine never generates MediaItems that are actions then there is no reason to implement actionActivated.
Yes, I know, all this belongs in an apidox somewhere :–)

