[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-panel-devel
Subject:    Start of extender API draft
From:       Rob Scheepmaker <r.scheepmaker () student ! utwente ! nl>
Date:       2008-05-24 14:21:44
Message-ID: 20080524142144.GD6640 () student ! utwente ! nl
[Download RAW message or body]

Hi,

As you may know, I'll be implementing 'extenders' for GSoC this year.
I've started drafting up some api for extenders and was looking for some
feedback. Note that this api is still incomplete and unpolished, but I
want some feedback on the general direction I'm taking here.
Of course there are lots of ways something like this could be
implemented. I've tried to explain why I made most design decision, but
if you have any questions, please ask and I'll try to explain better. :)
I'm also open to entirely different approaches. This is nothing more
then an early draft and a lot can change.
If you want to know what extenders actually are, I'd suggest you read my
blog post about it at:

http://pindablog.wordpress.com/2008/04/23/gsoc-extenders-project/

I'm looking forward to hearing from you. :)

Ok, enough talking, here we go:


namespace Plasma
{
    /**
     * First: some terminology:
     * - Extender: the applet that can contain detachable widgets. Here this is a subclass of
     *   applet, but all this could as well merge into Applet itself. So the applet making use of
     *   extenders is an extender as well.
     * - Detachable Widget: the widgets that can be detached from an extender. These are actually
     *   ordinary QGW's wrapped in a DetachableWidget, which provides drag handles and such.
     * - ExtenderApplet: an applet that does little more then just contain detachable widgets. When
     *   a detachable is dropped on the desktop, for example, a new ExtenderApplet is created an the
     *   detachable is added to it. This ExtenderApplet is now the detachable's:
     * - hostApplet: the applet that contains the detachable widget.
     * - sourceApplet: the applet that created the detachable widget in the first place. This is the
     *   class that knows how to create the appropriate detachables.
     *
     * This is the base class for applet that can contain and/or create detachable widgets. Since
     * applets will not always only store detachable widgets hidden away until being clicked, but
     * most applets will want to switch between showing detachable widgets within the source applet
     * itself, or on a seperate 'popup' applet, this is both the base class for applets that want to
     * create / handle extenders, and the ExtenderApplet.
     * This way, there isn't really a difference between 'detached' items and 'attached' items, 
     * making the distinction between the two irrelevant.
     *
     * This class handles the following:
     * - provide a factory system for detachable widgets.
     * - provide bookkeeping of detachable widget configuration.
     * - provide a way to fetch lists of detachable widgets with the subclass as source applet.
     * - position the applet on a 'correct' position on the screen (depending on that of the source
     *   applet)
     * - allow the applet to be shown on a toplevel window.
     * - and of course: add/remove detachable widgets.
     *
     * TODO:
     * - maybe provide some sort of globalConfig() and associated signals so applets can change
     *   global, probably appearance related configuration of all detachable widgets on the fly?
     * - we will probably often have 1 important KConfigGroup property that has to be checked on
     *   every dataUpdated: and identifier so specific notifications/emails/whatever can be
     *   identified and not added again. In dataUpdated, we would end up scanning all KConfigGroup
     *   objects for id values, to see if the item allready exists. What if I add a Id() to
     *   DetachableWidget and a function here to fetch a QList<int> so in dataUpdated we could just
     *   do something like
     *
     *      if (!detachableWidgetIds().contains(id)) { //add a widget }
     *
     * - terminology could be better.
     */
    class Extender : public Applet
    {
        public:
            /**
             * factory method. any subclassing applet that is also a source applet should implement
             * this to create the detachable widgets. Of course, all uglyness is nicely hidden away,
             * making it really trivial to provide this factory: K_EXPORT_PLASMA_APPLET can handle
             * this stuff. The reason we want a static function here (imo), is that detachable
             * widgets should be able to exist without their source applet. If the user has detached
             * a widget from an applet, removed the source applet, and then restarts plasma, the
             * detachable widget should be created again, without the need for an instance of the
             * source applet. The host applet just checks it's config, and this function will get
             * called for every detached widget it contains, on the appropriate source applets.
             *
             * Little example:
             * An clock applet that shows different timezones using extenders, might implement the
             * function like this:
             *
             * QGraphicsWidget *widget = new SuperAwesomeClockWidget(this);
             * hostApplet->dataEngine("time")->connectSource(config.readEntry("timezone"), widget,
             *                                               1000);
             * return widget;
             *
             * @param hostApplet The applet that will 'host' the applet. Use this to access applet
             * api like hostApplet->dataEngine().
             * @param config The configuration data associated with the to be instantiated widget.
             * Through this you can connect the correct dataengines, set properties, or even create
             * completely different widgets, depending on configuration data.
             * @return The instantiated widget, or 0 if the widget shouldn't be instantiated
             * (because the source has disappeared for example, think of a deleted email or
             * something like that)
             */
            static QGraphicsWidget *createDetachableWidget(Extender *hostApplet,
                                                           const KConfigGroup &config);

            /**
             * This will wrap the qgw in a DetachableWidget, in case it's not a DetachableWidget,
             * and add it to the layout.
             * @param widget The widget to be added.
             * @param config The additional configuration data to be associated with the widget
             * (basically all relevant parameters and identifiers needed to be able to recreate
             * the exact same widget in createDetachableWidget().
             * @param position The location to add the widget (relavant for drag & drop/reordering).
             * default will append the widget.
             */
            void addWidget(QGraphicsWidget *widget, const KConfigGroup &config,
                           const QPointF &position = QPointF());

            /**
             * @param widget The widget to be removed from the layout.
             */
            void removeWidget(QGraphicsWidget *widget);

            /**
             * Fetch a list of detachableWidgets in THIS extender. So all detachables where:
             *   detachable->hostApplet() == this
             *
             * @return a list containing detachableWidgets.
             */
            QList<DetachableWidget *> detachableWidgets();

            /**
             * implement to set custom criteria for wether or not to accept dropping detachable
             * widgets in this extender.
             * @param the widget that is about to be dropped on the extender.
             * @return true if the extender should just accept this widget, false when not (default
             * implementation should probably return true on widgets with this applet as source
             * applet, false otherwise)
             */
            virtual bool acceptDetachable(DetachableWidget *widget);

        protected:
            /**
             * Fetch a list of all detachableWidgets where
             *   detachable->sourceApplet() == this
             *
             * Using this function, the dataUpdated function can determine if a certain item
             * allready exists somewhere or not. Maybe some id approach (see the TODO) might work
             * better though.
             *
             * @return a list with detachableWidgets.
             */
            QList<DetachableWidget *> allDetachableWidgets();

            /**
             * sets an associated extender. This eases a common extender usecase: display some
             * widgets when on the desktop, hiding them when in a panel. The associated extender
             * is the extender where the detachable widgets are automagically moved when in
             * vertically constrained space. When moving back to a planar containment, widgets in
             * this extender are automagically moved back the this applet. Only detachables where
             * isDetachable() == true are moved this way.
             * The reason that this is part of the api, is that while this will probably get used in
             * 90% of the cases, there might also be applets who want to make use of multiple
             * ExtenderApplets, so want to do moving of widgets around by themselves.
             * @param applet The extender that is to become the associated extender. Set to 0 to
             * have no automatic widgets moving around.
             */
            void setAssociatedExtender(Extender *applet);

            /**
             * Creates a toplevel view focussed on this extender. This view is automatically
             * located at the 'correct' position but can of course be moved at will. Besides, this
             * will allow the applet to show or hide the extender.
             * @return the toplevel window.
             */
            QWidget *createTopLevelWindow();
    };
} // namespace Plasma

namespace Plasma
{
    /**
     * This class is meant as some sort of applethandle for detachable widgets. It wraps a
     * QGraphicsWidget, keeps track of it's configuration, source applet, but mainly provides drag
     * and drop handling, drag handles and optionally other user interface elements (think of maybe
     * a 'return to source extender' button, toggle visibility (only show titlebar, hide widget),
     * stuff like that.
     * So what should the KConfigGroup for detachablewidgets at least contain?
     * - sourceAppletName
     * - sourceAppletId
     * - widgetId ???
     */
    class DetachableWidget : public QGraphicsWidget
    {
        public:
            /**
             * Creating DetachableWidgets is something that an applet itself actually never has to
             * do (it can just use addWidget, which creates a detachable widget)
             * Why not pass a pointer to the applet instead of sourceAppletName and sourceAppletId?
             * Well, maybe we don't have an instance of the source applet (the detachable can live
             * on independently of the sourceApplet). But sourceApplet is still relevant: what if
             * the source applet was removed by the user, but later added again? It makes sense to
             * make this new applet the source applet of the detachable imo, I think that's what
             * users would expect...
             * @param widget The qgw that has to be wrapped in this widget. (the widget param from
             * Extender::addWidget(...))
             * @param sourceAppletName The name of the source applet.
             * @param sourceAppletId The id of the source applet.
             * @param config the configuration options associated with this widget.
             */
            DetachableWidget(QGraphicsWidget *widget, const QString &sourceAppletName,
                             int sourceAppletId, KConfigGroup &config)

            /**
             * fetch the configuration of this widget.
             * @return the configuration of this widget.
             */
            KConfigGroup config();

            /**
             * @return the name of the source applet.
             */
            QString sourceAppletName();

            /**
             * @return the id of the source applet.
             */
            int sourceAppletId();

            /**
             * @return a pointer to the source applet. First this function would want to check if
             * the sourceAppletName with that sourceAppletId still exists, and return a pointer to
             * it. If there exists one applet with sourceAppletName, but it has a different
             * sourceAppletId, it makes sence to return a pointer to that applet. (see explanation
             * of the ctor)
             */
            ExtenderApplet *sourceApplet();

            /**
             * @param whether or not this widget is actually detachable.
             */
            void setDetachable(bool);

            /**
             * @return whether or not this widget is actually detachable.
             */
            bool detachable();

            /**
             * @return !(sourceApplet == hostApplet)
             */
            bool isDetached();
    }
} // namespace Plasma

_______________________________________________
Panel-devel mailing list
Panel-devel@kde.org
https://mail.kde.org/mailman/listinfo/panel-devel
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic