Plasma Media Center and state machines

Christophe Olinger olingerc at binarylooks.com
Thu Apr 1 21:48:42 CEST 2010


Replies inline,

Adapted workflow at the end of this message.

(I think its almost time to start coding this)


On Thu, Apr 1, 2010 at 8:16 PM, Christophe Olinger <olingerc at binarylooks.com>
wrote:
> From Aaron:
>
>> //This class knows about things that are important for every toplevel
>> thing needed in the MediaCenter like
>>  MediaType for example
>>
>> enum State {
>>     BrowseVideos;
>>     PlayVideos;
>>     PlayMusic;
>>     MusicVisualizations;
>>     BrowsePictures;
>>     SinglePictures;
>>     PicturesSlideshow;
>> }
>
> is this enum really needed? it's really only something the state machine
needs
> to know about.

moved to the MediaCenterState class

>
> the only thing that needs to get at this would be the Home Page (or
whatever
> it's being called :), but if that was also part of the state machine
system
> then the states could quite easily just add themselves to the home page.
>
>> //Not sure if we need this enum
>> enum playbackState {
>>     Playing;
>>     Paused;
>>     Stopped;
>> }
>
> maybe just playing/paused as a global setting. this is probably separate
from
> the state machine work, however.

will remove the stopped state eventually

>
>> Important: The main MediaCenter lib knwos which types of UIComponents
>> exist. If we add a new type, it needs to be included here
>
> correct..
>
>> ________________________________________________
>>
>> ***MediaCenterState (subclass of QAbstractState)***
>>
>> //This class allows for the MediaCenter to have states. In here we
>> have only things that are needed for the containment to do its work.
>> It also hands out pointers to widgets
>> //It also keeps a collection of all subComponents needed within all
>> states (maybe each state should keep its own subcomponents instead)
>> // the problem would be that if we change between states that have the
>> same subComponents, we would delete and recreate them?)
>>
>>
>> #include "mediacenter.h"
>> #include "mediacenter_export.h"
>>
>> namespace MediaCenter
>> {
>>
>> enum subComponent {
>>     iconVideoMode;
>
> small detail: the first letter should be capitalized as well. also,
instead of
> pepending "icon", "slider", etc. i'd recommend just stating what it does.
> "JumpToVideoMode", "MusicModeVolume". that way if we switch the actual UI
> elements that accomplish these tasks, we don't end up with odd / outdated
> enums :)

will do.

>
>>     iconMusciMode;
>>     iconPictureMode;
>>     iconHomeScreen;
>>     iconTogglePlaylistAutohide;
>>     iconToggleControlBarAutohide;
>>     iconToggleInfoBarAutohide;
>>     iconVideoModePlayPause;
>>     iconVideoModeSkipForward;
>>     iconVideoModeSkipBackward;
>>     iconVideoModeStop;
>>     iconMusicModePlayPause;
>>     iconMusicModeSkipForward;
>>     iconMusicModeSkipBackward;
>>     iconMusicModeStop;
>>     sliderMusicModeVolume;
>>     sliderMusicModeSeek;
>>     iconSlideshow;
>>     iconRotateCW;
>>     iconRotateCCW;
>
> would these rotation ones belong to the image browser, and be provided as
> "custom" elements rather than named elements in the main enum?

I thought we could to this:
Have a main enum with things common to the states (enum MainSubComponents)
Have an enum with things specific to a state. (enum
BrowseVideoModeSubComponents)
In that case, the rotate buttons would be part of the BrowsePictureMode and
BrowseSinglePictureMode enums?
Custom widgets always belong to some state, don't they?

>
>> //here we handle the pointers of the created objects (the enum,
>> initialilzation and pointer handling could be in the actual subclass
>> of the state objects? makes it easier to add modes/plugins?)
>>
>> QGraphicsWidget
>> *BrowseVideoState::giveComponent(MediaCenter::subComponent component)
>
> should probably be createComponent(..) or just component(..)
>
>> {
>>    if (component == iconVideoMode) {
>>
>>        m_videoModeWidget->setIcon("bla");
>>
>>        return m_videoModeWidget;
>>    }
>> }
>
> this is probably a bad example; the standard items such as JumpToVideoMode
> should be provided by the base class. it will be shared by all modes,
after
> all. only custom items specific to the state should be here. for instance,
the
> VideoState might offer (as a made up example) a channel listing display
and a
> button for that should appear in the top bar. in this case, that buton
would
> be provided by VideoState as a custom item.

I agree. Separation of general widgets and specific widgets will be in the
indiviual state object classes or the main MediaCenterState class.

>
>> }//ending namesapce MediaCenter
>>
>> __________________________________
>>
>> ***PlaybackControl (=ControlBar)***
>> //This class defines what an actual implementation of a controlbar
>> needs to be able to do
>>
>> //When adding a subComponent we need to return it. This is necessary
>> to keep a track of them in a list in the state object
>> MediaCenter::subComponent addSubComponent(MediaCenter::SubComponent)
>> {
>> }
>>
>> //This class gets the exact pointers of who do remove in a list
>> void
>>
removeSubComponents(MediaCenter::SubComponent(QList<MediaCenter::SubCompon
>> ents>) {
>> }
>
>
>
>> //these are all empty classes
>>
>> Important: The correct public slots and signals need to exist and
>> reimplememted in the actual applet.
>>
>> signals
>>     browserGoPrevious
>>     playerPlayPauseVideo
>>     playerPlayPauseMusic
>>     playerPlayPausePictures
>
> what would thse signals be used for, exactly? and why are there separate
ones
> for Video, Music, Pictures, etc?

I thought that if the controlBar needs to tell the browser to go up one
levelm it needs a signal to do that and the browser needs a slot to accept
that. When we handle connecting applets between them in the mediacontainment
or whereever, we only have the type implemenations of the applets available.
By adding public slots and signals to the type classes, we make sure that we
do not forget reimplementing these in the actual applets.

They have "duplicates", since for some modes, the buttons look the same, but
they do different things. To avoid having to disconnect and reconnect
things, I thought that having distinctive signals and slots would allow us
to connect things once and not think about them anymore. Maybe I am wrong?

> - Show quoted text -
> why is a subComponentList needed? should the definition of which
components to
> show be done in the constructor instead, and only action taken to reflect
that
> in the actual UI done in the enter event?

On enter event, the applets get told which widgets to add to themselves. By
collecting in a list it would be easy to know on exit which widgets to hide.

if there are multiple possible
> states for a mode, then those could be child states, which would make it
such
> that each state object would represent one configuration of widgets which
> wouldn't change within that state object.

Hmm, for me, mode and state were actuall the same. When in a state, the UI
configuration is fix.

>
>> subComponentsList <<
>>
addSubComponent(MediaCenterState::giveComponent(MediaCenter::iconPictureMod
>> e)); ...//add all widgets
>>             configure UIComponent (show, hide, autohide,...)
>>         }
>>         if UIComponent = "InfolBar" {
>>             ...//add widgets
>>         }
>>         ...//treat all UIComponents
>>     }
>> }
>
>> void BrowseVideoState::exit() {
>>     For each UIComponent in list {
>>         if (UIComponent == "ControlBar") {
>>             UIComponent->removeSubComponents(subComponentsList);
>>         }
>>     }
>> }
>
> i think the MediaCenterState object should simply remove/hide all
components
> that were visible in the last state but which are not visible in the now-
> current state.

Thats why I thought keeping a list when entering a state would be a good
idea.
When each state object adds widgets to applets, how can the MediaCenterState
object know about this?
Would this be done by having the list defined in the MediaCenterState Object
and the individual states would use that list. A simple public (or
protected) member variable?

>
> iow, if we have the current state and it's list of components (set up
using
> addComponent or whatever), and a mapping between each UI element (e.g. a
> button) and the corresponding component enum, then the code can iterate
over
> each item and check to see if it should be shown. sth like this:
>

the below code should go into the entry() function of all state objects?

> QList<MediaCenter::SubComponent> components = newState->subComponents();

Okay, so in each State class I need a subComponents() function that returns
pointers of all subComponents needed in that state. What about the general
ones from the MediaCenterState class? "newState" will be replaced by the
actual object that is the next stage?

> QListIterator<MediaCenter::SubComponent> it (m_visibleSubComponents);

The m_VisibleSubComponents was filled on state change by the last state and
is stored in the MediaCeterState object?

>
> while (it.hasNext()) {
>   MediaCenter::SubComponent c = it.next();
>   if (!components.contains(c)) {
>       MediaCenterComponent *w = m_subComponents.value(c);
>        if (w) {
>             w->hide(); // TODO animate
>         }
>   }
> }
>
> m_visibleSubComponents = components;
> it = m_visibleSubComponents;
> int index = 0;
> while (it.hasNext()) {
>   MediaCenter::SubComponent c = it.next();
>   MediaCenterComponent *w = m_subComponents.value(c);
>   if (!w && c > MediaCenter::CustomComponent) {
>        w = newState->customComponent(c);
>   }
>
>   if (w) {
>        // insert into the layout appropriately
>   }
> }
>

>> applets//
>> _________________________________________
>>
>> ***MediaController class (or any other applet implementation)***
>>
>> controller will be told to add a widget, how this is done is its own
>> problem and needs to be implememnted in the actual applet of type
>> playbackcontrol.
>> also the alignement and stuff
>>
>> reimplement addSunComponent (with return of subComponent
>>
>> we need to connect signal clicked() stuff to the external signals,
>> how? can we be sure that all widgets will arrive. I guess yes.
>> we need to get the pointers of the actual widgets.
>>
>>
>> MediaCenter::subComponent addSubComponent(MediaCenter::SubComponent)
>> {
>> create layout
>> add subComponent
>> show subComponent
>> }
>>
>> void removeSubComponents(QList<MediaCenter::SubComponents>)
>> {
>> for each subComponent
>> hide subcomponent
>> remove them from layout
>> }
>
> again, i don't think an explicit remove is needed. just switch between
states,
> where each state defines all the SubComponents it contains.

Understood, the code in the actual state classes just tells the
SubComponents to hide

>
> i do think we'll end up wanting a QGraphicsWidget subclass for
SubComponents
> (if there isn't aleady one?). this would be used to store information for
> layouting, such as where it belongs in a bar (left, center, right?) and
the
> precedence (Home button should always be first precedent, left)

okay, another class for the libs (tehre is none yet): MediaCenterWidgets
with things like this:
void precedence(int);
int precedence();

void alignement(QtAlignment);
...

____________________________________________

1) the top level libs (*mediacenter.h*) class knows about which types of
applets (=UIComponents) exist

Example:

enum UIComponent {
    ControlBar;
    InfoBar;
    Browser;
    Player;
    Playlist;
    HomeScreen;
}
_________________________________

2) there is a *MediaCenterState class* which keeps track of all smallwidgets
(=subComponents) which are common for all states for the whole PMC and can
hand out pointers to them. These are widgets like buttons, icons slider,
labels in the control or information bar.
This class also knows which states exist

Example:

enum State {
    BrowseVideos,
    PlayVideos,
    PlayMusic,
    MusicVisualizations,
    BrowsePictures,
    SinglePictures,
    PicturesSlideshow,
    HomeScreen
}

enum MainSubComponent {
    JumpToVideoMode,
    JumpToMusicMode,
    JumpToPictureMode,
    JumpToHomeScreen,
    ToggleControlBarAutohide,
    ToggleInfoBarAutohide,
    ...
}

class MEDIACENTER_EXPORT MediaCenterState(QObject *parent)

    : QAbstractState(parent)

    m_iconVideosMode(new Plasma::IconWidget(this))

    //initialize all of them

{

}


//here we handle the pointers of the created objects (the enum,
initialilzation and pointer handling could be in the actual subclass of the
state objects

QGraphicsWidget *BrowseVideoState::giveComponent(MediaCenter::subComponent
component)
{
   if (component == iconVideoMode) {

       m_videoModeWidget->setIcon("bla");

       return m_videoModeWidget;
   }
}
_________________________________

3) there will be subclasses (like *BrowseVideoState*) of the above class
that can handle each mode. They have enums of things that are specific to
their state and can also hand out pointers using a similar fucntion to the
one in the MediaCenterState Class.

Example:

enum BrowseVideoModeSubComponent {
    PlayPause,
    SkipForward,
    SkipBackward,
    Stop,
    Volume,
    Seek,
    ...
}

enum BrowsePicturesModeSubComponent {
    StartSlideShow
    RotatePictureCCW
    ...
}

_________________________________



4) The *MediaContainment* starts the state machine

Example:
//Prepare StateMachine
QStateMachine machine; //this is not a pointer

//Set up all possible states
MediaCenterState mediaCenterState = new MediaCenterState(); //these are
pointers
VideoBrowserState videoBrowserState = new
VideoBrowserState(mediaCenterState);
....
//Define state change buttons
mediaCenterState->addTransition(m_control,
SIGNAL(switchToVideoBrowseState()), videoBrowseState);
mediaCenterState->addTransition(m_control,
SIGNAL(switchToPicturesBrowseState()), pictureBrowseState);

//Define other signals for state change
mediaCenterState->addTransition(player, SIGNAL(slideshowStopped()),
pictureBrowseState);

//Setup and start statemachine
machine.addState(mediaCenterState); //this is our toplevel state
mediaCenterState->setInitialState(videoBrowserState); //set this to the
homescreen state eventually
machine.start();

_________________________________


5) these sublasses will be used by the *MediaContainment* to create state
objects
The mediacontainment initiates state switches while handing pointers of the
UIComponents to the state objects

Example:
QList<MediaCenter::UIComponent> uiComponentsList; //pointer will be added in
the setApplet functions

_________________________________

6) the state objects (i*.e. BrowseVideoState*) use the pointers of the
UIComponents to tell them to add subComponents to them
They also configure the UIComponents. This all happens on state change
(enter and exit methods)
Here, previous widgets that are not needed will simply be hidden. Easy,
since we have the pinters to them

Example of functions to be reimplemented:
assignPoperty(Object, "property", "value")
invokeMethodOnEntry(this, "method")

Example of code:
void BrowseVideoState::invokeOnEntry(QList<MediaCenter::UIComponents> list)
{
    For each UIComponent in list {
        if (UIComponent == "ControlBar") {
            subComponentsList.clear();
            subComponentsList <<
addSubComponent(MediaCenterState::giveComponent(MediaCenter::iconVideoMode));
            subComponentsList <<
addSubComponent(MediaCenterState::giveComponent(MediaCenter::iconPictureMode));
            ...//add all widgets
            configure UIComponent (show, hide, autohide,...)
        }
        if UIComponent = "InfolBar" {
            ...//add widgets
        }
        ...//treat all UIComponents
    }
}

void BrowseVideoState::exit() {
    For each UIComponent in list {
        if (UIComponent == "ControlBar") {
            UIComponent->removeSubComponents(subComponentsList);
        }
    }
}

Aaron's way of doing this:

QList<MediaCenter::SubComponent> components = newState->subComponents();
QListIterator<MediaCenter::SubComponent> it (m_visibleSubComponents);

while (it.hasNext()) {
  MediaCenter::SubComponent c = it.next();
  if (!components.contains(c)) {
      MediaCenterComponent *w = m_subComponents.value(c);
       if (w) {
            w->hide(); // TODO animate
        }
  }
}

m_visibleSubComponents = components;
it = m_visibleSubComponents;
int index = 0;
while (it.hasNext()) {
  MediaCenter::SubComponent c = it.next();
  MediaCenterComponent *w = m_subComponents.value(c);
  if (!w && c > MediaCenter::CustomComponent) {
       w = newState->customComponent(c);
  }

  if (w) {
       // insert into the layout appropriately
  }
}

_________________________________

7) The type definitions of the UIComponents in the libs (*playbackcontrol.h*,
browser.h,...) predefine the necessary functions for adding subComponents
which need to be reimplemented by the actual UIComponents themselves
The type definitions also need to show which public slots and signals need
to be available by the actual applets.
Important are the switchState slots. that each UIComponent needs to have.

The public signals/slots part is still under discussion)

Example:
//This class defines what an actual implementation of a controlbar needs to
be able to do

//When adding a subComponent we need to return it. This is necessary to keep
a track of them in a list in the state object
MediaCenter::subComponent addSubComponent(MediaCenter::SubComponent)
{
}

//This class gets the exact pointers of who do remove in a list
void
removeSubComponents(MediaCenter::SubComponent(QList<MediaCenter::SubComponents>)
{
}

_________________________________

8) The actual UIComponents (*mediacontroller.h*, mediabrowser.h,...) need to
reimplement how to layout subComponents to themsleves.They will get the
pointers to the subComponents during state change by the state objects. (see
point 6)

Example:
MediaCenter::subComponent addSubComponent(MediaCenter::SubComponent)
{
create layout
add subComponent
show subComponent
}


9) All Widgets will be sublcasses by a MediaCenterWidget class (itslef
derived from QGraphicsWidget). which stores general layout information like
alignment or precedence.

Example:

void precedence(int);
int precedence();

void alignement(QtAlignment);
...
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/pipermail/plasma-devel/attachments/20100401/8878d934/attachment-0001.htm 


More information about the Plasma-devel mailing list