diff --git a/doomsday/client/include/ui/widgets/contextwidgetorganizer.h b/doomsday/client/include/ui/widgets/contextwidgetorganizer.h index 339c6a9e6f..f2266ba791 100644 --- a/doomsday/client/include/ui/widgets/contextwidgetorganizer.h +++ b/doomsday/client/include/ui/widgets/contextwidgetorganizer.h @@ -54,12 +54,12 @@ class ContextWidgetOrganizer * appropriately. * * After construction, the widget is automatically updated with - * updateWidgetForItem(). + * updateitemWidget(). * * @param item Item that has the content. * @param parent Future parent of the widget, if any (can be @c NULL). */ - virtual GuiWidget *makeWidgetForItem(ui::Item const &item, GuiWidget const *parent) = 0; + virtual GuiWidget *makeitemWidget(ui::Item const &item, GuiWidget const *parent) = 0; /** * Called whenever the item's content changes and this should be reflected @@ -68,7 +68,7 @@ class ContextWidgetOrganizer * @param widget Widget that represents the item. * @param item Item that has the content. */ - virtual void updateWidgetForItem(GuiWidget &widget, ui::Item const &item) = 0; + virtual void updateitemWidget(GuiWidget &widget, ui::Item const &item) = 0; }; /** @@ -108,13 +108,13 @@ class ContextWidgetOrganizer * * @param context Context with items. */ - void setContext(ui::Context &context); + void setContext(ui::Context const &context); void unsetContext(); - ui::Context &context() const; + ui::Context const &context() const; - GuiWidget *widgetForItem(ui::Item const &item) const; + GuiWidget *itemWidget(ui::Item const &item) const; private: DENG2_PRIVATE(d) @@ -127,8 +127,8 @@ class ContextWidgetOrganizer class DefaultWidgetFactory : public ContextWidgetOrganizer::IWidgetFactory { public: - GuiWidget *makeWidgetForItem(ui::Item const &item, GuiWidget const *parent); - void updateWidgetForItem(GuiWidget &widget, ui::Item const &item); + GuiWidget *makeitemWidget(ui::Item const &item, GuiWidget const *parent); + void updateitemWidget(GuiWidget &widget, ui::Item const &item); }; #endif // DENG_CLIENT_CONTEXTWIDGETORGANIZER_H diff --git a/doomsday/client/include/ui/widgets/menuwidget.h b/doomsday/client/include/ui/widgets/menuwidget.h index 2a202c8dfd..5c3b28528b 100644 --- a/doomsday/client/include/ui/widgets/menuwidget.h +++ b/doomsday/client/include/ui/widgets/menuwidget.h @@ -38,25 +38,6 @@ */ class MenuWidget : public ScrollAreaWidget { -public: -#if 0 - class ISortOrder - { - public: - virtual ~ISortOrder() {} - - /** - * Determines the sort order for a pair of menu items. - * - * @param a First menu item. - * @param b Second menu item. - * - * @return -1 if a < b; +1 if a > b; 0 if equal. - */ - virtual int compareMenuItemsForSorting(Widget const &a, Widget const &b) const = 0; - }; -#endif - public: MenuWidget(de::String const &name = ""); @@ -83,30 +64,19 @@ class MenuWidget : public ScrollAreaWidget void setGridSize(int columns, ui::SizePolicy columnPolicy, int rows, ui::SizePolicy rowPolicy); - /** - * Sets the sort order for item layout. - * - * @param sorting Sort order object. MenuWidget takes ownership. - */ - //void setLayoutSortOrder(ISortOrder *sorting); - ui::Context &items(); ui::Context const &items() const; - ContextWidgetOrganizer const &organizer() const; - - /* - GuiWidget *addItem(GuiWidget *anyWidget); - - ButtonWidget *addItem(de::String const &styledText, de::Action *action = 0); - - ButtonWidget *addItem(de::Image const &image, de::String const &styledText, de::Action *action = 0); - - GuiWidget *addSeparator(de::String const &labelText = ""); + /** + * Sets the data context of the menu to some existing context. The context + * must remain in existence until the MenuWidget is deleted. + * + * @param items Ownership not taken. + */ + void setItems(ui::Context const &items); - void removeItem(GuiWidget *child); - */ + ContextWidgetOrganizer const &organizer() const; /** * Returns the number of visible items in the menu. Hidden items are not diff --git a/doomsday/client/include/ui/widgets/popupmenuwidget.h b/doomsday/client/include/ui/widgets/popupmenuwidget.h index ce7b12038f..b9e8c63cac 100644 --- a/doomsday/client/include/ui/widgets/popupmenuwidget.h +++ b/doomsday/client/include/ui/widgets/popupmenuwidget.h @@ -32,14 +32,6 @@ class PopupMenuWidget : public PopupWidget MenuWidget &menu() const; - /* - ButtonWidget *addItem(de::String const &styledText, de::Action *action = 0, - bool dismissOnTriggered = true); - LabelWidget *addItem(LabelWidget *anyLabelBasedWidget); - */ - - //GuiWidget *addSeparator(de::String const &optionalLabel = ""); - protected: void glMakeGeometry(DefaultVertexBuf::Builder &verts); void preparePopupForOpening(); diff --git a/doomsday/client/include/ui/widgets/popupwidget.h b/doomsday/client/include/ui/widgets/popupwidget.h index 1700ad1e51..fe27743338 100644 --- a/doomsday/client/include/ui/widgets/popupwidget.h +++ b/doomsday/client/include/ui/widgets/popupwidget.h @@ -31,6 +31,9 @@ class PopupWidget : public GuiWidget { Q_OBJECT +public: + DENG2_DEFINE_AUDIENCE(Close, void popupBeingClosed(PopupWidget &)) + public: PopupWidget(de::String const &name = ""); diff --git a/doomsday/client/src/ui/widgets/contextwidgetorganizer.cpp b/doomsday/client/src/ui/widgets/contextwidgetorganizer.cpp index bfc3cd2293..c035795b24 100644 --- a/doomsday/client/src/ui/widgets/contextwidgetorganizer.cpp +++ b/doomsday/client/src/ui/widgets/contextwidgetorganizer.cpp @@ -34,7 +34,7 @@ DENG2_OBSERVES(ui::Context, OrderChange), DENG2_OBSERVES(ui::Item, Change ) { GuiWidget *container; - ui::Context *context; + ui::Context const *context; IWidgetFactory *factory; typedef QMap Mapping; @@ -48,7 +48,15 @@ DENG2_OBSERVES(ui::Item, Change ) factory(&defaultWidgetFactory) {} - void set(ui::Context *ctx) + ~Instance() + { + DENG2_FOR_EACH_CONST(Mapping, i, mapping) + { + i.value()->audienceForDeletion -= this; + } + } + + void set(ui::Context const *ctx) { if(context) { @@ -72,12 +80,12 @@ DENG2_OBSERVES(ui::Item, Change ) } } - void addWidgetForItem(ui::Context::Pos pos, bool alwaysAppend = false) + void additemWidget(ui::Context::Pos pos, bool alwaysAppend = false) { DENG2_ASSERT(factory != 0); ui::Item const &item = context->at(pos); - GuiWidget *w = factory->makeWidgetForItem(item, container); + GuiWidget *w = factory->makeitemWidget(item, container); if(!w) return; // Unpresentable. // Others may alter the widget in some way. @@ -87,6 +95,7 @@ DENG2_OBSERVES(ui::Item, Change ) } // Update the widget immediately. + mapping.insert(&item, w); itemChanged(item); if(alwaysAppend || pos == context->size() - 1) @@ -99,9 +108,7 @@ DENG2_OBSERVES(ui::Item, Change ) container->insertBefore(w, *mapping[&context->at(pos + 1)]); } - mapping.insert(&item, w); - - // Observe: + // Observe. w->audienceForDeletion += this; // in case it's manually deleted item.audienceForChange += this; } @@ -113,7 +120,7 @@ DENG2_OBSERVES(ui::Item, Change ) for(ui::Context::Pos i = 0; i < context->size(); ++i) { - addWidgetForItem(i, true /*always append*/); + additemWidget(i, true /*always append*/); } } @@ -136,8 +143,10 @@ DENG2_OBSERVES(ui::Item, Change ) void widgetBeingDeleted(Widget &widget) { - // Note: this should not occur normally, as the widgets created by the - // Organizer are not manually deleted. + /* + * Note: this should not occur normally, as the widgets created by the + * Organizer are not usually manually deleted. + */ MutableMappingIterator iter(mapping); while(iter.hasNext()) { @@ -152,7 +161,7 @@ DENG2_OBSERVES(ui::Item, Change ) void contextItemAdded(ui::Context::Pos pos, ui::Item const &) { - addWidgetForItem(pos); + additemWidget(pos); } void contextItemBeingRemoved(ui::Context::Pos, ui::Item const &item) @@ -183,7 +192,7 @@ DENG2_OBSERVES(ui::Item, Change ) DENG2_ASSERT(mapping.contains(&item)); GuiWidget &w = *mapping[&item]; - factory->updateWidgetForItem(w, item); + factory->updateitemWidget(w, item); // Notify. DENG2_FOR_PUBLIC_AUDIENCE(WidgetUpdate, i) @@ -204,7 +213,7 @@ ContextWidgetOrganizer::ContextWidgetOrganizer(GuiWidget &container) : d(new Instance(this, &container)) {} -void ContextWidgetOrganizer::setContext(ui::Context &context) +void ContextWidgetOrganizer::setContext(ui::Context const &context) { d->set(&context); } @@ -214,7 +223,7 @@ void ContextWidgetOrganizer::unsetContext() d->set(0); } -ui::Context &ContextWidgetOrganizer::context() const +ui::Context const &ContextWidgetOrganizer::context() const { DENG2_ASSERT(d->context != 0); return *d->context; @@ -231,12 +240,12 @@ ContextWidgetOrganizer::IWidgetFactory &ContextWidgetOrganizer::widgetFactory() return *d->factory; } -GuiWidget *ContextWidgetOrganizer::widgetForItem(ui::Item const &item) const +GuiWidget *ContextWidgetOrganizer::itemWidget(ui::Item const &item) const { return d->find(item); } -GuiWidget *DefaultWidgetFactory::makeWidgetForItem(ui::Item const &item, GuiWidget const *) +GuiWidget *DefaultWidgetFactory::makeitemWidget(ui::Item const &item, GuiWidget const *) { // The default implementation uses simple labels. LabelWidget *w = new LabelWidget; @@ -244,7 +253,7 @@ GuiWidget *DefaultWidgetFactory::makeWidgetForItem(ui::Item const &item, GuiWidg return w; } -void DefaultWidgetFactory::updateWidgetForItem(GuiWidget &, ui::Item const &) +void DefaultWidgetFactory::updateitemWidget(GuiWidget &, ui::Item const &) { // nothing to do } diff --git a/doomsday/client/src/ui/widgets/gameselectionwidget.cpp b/doomsday/client/src/ui/widgets/gameselectionwidget.cpp index 03589a74a9..f2f3f1bf38 100644 --- a/doomsday/client/src/ui/widgets/gameselectionwidget.cpp +++ b/doomsday/client/src/ui/widgets/gameselectionwidget.cpp @@ -32,30 +32,17 @@ DENG2_OBSERVES(Games, Addition), DENG2_OBSERVES(App, StartupComplete), DENG2_OBSERVES(ContextWidgetOrganizer, WidgetCreation) { - typedef QMap Buttons; - Buttons buttons; - -#if 0 - /** - * Sorts the game buttons by label text. - */ - struct Sorting : public ISortOrder - { - int compareMenuItemsForSorting(Widget const &a, Widget const &b) const - { - ButtonWidget const &x = a.as(); - ButtonWidget const &y = b.as(); - return x.text().compareWithoutCase(y.text()); - } + struct GameItem : public ui::ActionItem { + GameItem(Game const &gameRef, de::String const &label, de::Action *action) + : ui::ActionItem(label, action), game(gameRef) {} + Game const &game; }; -#endif Instance(Public *i) : Base(i) { App_Games().audienceForAddition += this; App::app().audienceForStartupComplete += this; - //self.setLayoutSortOrder(new Sorting); self.organizer().audienceForWidgetCreation += this; } @@ -81,7 +68,7 @@ DENG2_OBSERVES(ContextWidgetOrganizer, WidgetCreation) .arg(Str_Text(game.author())) .arg(idKey); - ui::ActionItem *item = new ui::ActionItem(label, loadAction); + GameItem *item = new GameItem(game, label, loadAction); /// @todo The name of the plugin should be accessible via the plugin loader. String plugName; @@ -123,17 +110,22 @@ DENG2_OBSERVES(ContextWidgetOrganizer, WidgetCreation) void updateGameAvailability() { - DENG2_FOR_EACH(Buttons, i, buttons) + for(uint i = 0; i < self.items().size(); ++i) { - if(i.key()->allStartupFilesFound()) + GameItem const &item = self.items().at(i).as(); + + GuiWidget *w = self.organizer().itemWidget(item); + DENG2_ASSERT(w != 0); + + if(item.game.allStartupFilesFound()) { - i.value()->setOpacity(1.f, .5f); - i.value()->enable(); + w->setOpacity(1.f, .5f); + w->enable(); } else { - i.value()->setOpacity(.3f, .5f); - i.value()->disable(); + w->setOpacity(.3f, .5f); + w->disable(); } } diff --git a/doomsday/client/src/ui/widgets/item.cpp b/doomsday/client/src/ui/widgets/item.cpp index 2ad18e7612..cee2a87e5a 100644 --- a/doomsday/client/src/ui/widgets/item.cpp +++ b/doomsday/client/src/ui/widgets/item.cpp @@ -50,7 +50,7 @@ String Item::label() const String Item::sortKey() const { - return ""; + return _label; } void Item::notifyChange() diff --git a/doomsday/client/src/ui/widgets/popupmenuwidget.cpp b/doomsday/client/src/ui/widgets/popupmenuwidget.cpp index ff2ad98be5..d1efdda5bb 100644 --- a/doomsday/client/src/ui/widgets/popupmenuwidget.cpp +++ b/doomsday/client/src/ui/widgets/popupmenuwidget.cpp @@ -34,10 +34,7 @@ DENG2_OBSERVES(ContextWidgetOrganizer, WidgetUpdate) Rectanglei hoverHighlightRect; Instance(Public *i) : Base(i), hover(0) - { - self.menu().organizer().audienceForWidgetCreation += this; - self.menu().organizer().audienceForWidgetUpdate += this; - } + {} void widgetCreatedForItem(GuiWidget &widget, ui::Item const &item) { @@ -146,6 +143,9 @@ PopupMenuWidget::PopupMenuWidget(String const &name) setContent(new MenuWidget(name.isEmpty()? "" : name + "-content")); menu().setGridSize(1, ui::Expand, 0, ui::Expand); + + menu().organizer().audienceForWidgetCreation += d; + menu().organizer().audienceForWidgetUpdate += d; } MenuWidget &PopupMenuWidget::menu() const @@ -153,72 +153,6 @@ MenuWidget &PopupMenuWidget::menu() const return static_cast(content()); } -#if 0 -ButtonWidget *PopupMenuWidget::addItem(String const &styledText, Action *action, bool dismissOnTriggered) -{ - ButtonWidget *b = menu().addItem(styledText, action); - b->setSizePolicy(ui::Expand, ui::Expand); - b->setMargin("unit"); - b->set(Background()); - - b->audienceForStateChange += d; - if(dismissOnTriggered) - { - b->audienceForTriggered += d; - } - - // We want items to be hittable throughout the width of the menu. - b->hitRule() - .setInput(Rule::Left, rule().left()) - .setInput(Rule::Right, rule().right()); - - return b; -} - -LabelWidget *PopupMenuWidget::addItem(LabelWidget *anyLabelBasedWidget) -{ - LabelWidget *w = anyLabelBasedWidget; - if(!w) return w; - - menu().addItem(w); - - ButtonWidget *button = dynamic_cast(w); - if(button) - { - button->audienceForStateChange += d; - } - - w->setSizePolicy(ui::Expand, ui::Expand); - w->setMargin("unit"); - w->set(Background()); - - // We want items to be hittable throughout the width of the menu. - w->hitRule() - .setInput(Rule::Left, rule().left()) - .setInput(Rule::Right, rule().right()); - - return w; -} - -GuiWidget *PopupMenuWidget::addSeparator(String const &optionalLabel) -{ - GuiWidget *sep = menu().addSeparator(optionalLabel); - if(optionalLabel.isEmpty()) - { - sep->setMargin(""); - sep->setFont("separator.empty"); - } - else - { - sep->setMargin("halfunit"); - sep->setFont("separator.label"); - } - sep->setTextColor("label.accent"); - sep->set(Background()); - return sep; -} -#endif - void PopupMenuWidget::glMakeGeometry(DefaultVertexBuf::Builder &verts) { PopupWidget::glMakeGeometry(verts); diff --git a/doomsday/client/src/ui/widgets/popupwidget.cpp b/doomsday/client/src/ui/widgets/popupwidget.cpp index 8ad1684b91..11db70aab5 100644 --- a/doomsday/client/src/ui/widgets/popupwidget.cpp +++ b/doomsday/client/src/ui/widgets/popupwidget.cpp @@ -137,62 +137,24 @@ DENG2_PIMPL(PopupWidget) .setInput(Rule::Height, content->rule().height()); } - //Rectanglei const view = Rectanglei::fromSize(self.root().viewSize()); - //Vector2i const pos(anchorX->valuei(), anchorY->valuei()); - //Vector2i const size(self.rule().width().valuei(), self.rule().height().valuei()); - // Let's first try the requested direction. - if(dir == ui::Up) + switch(dir) { - //if(pos.y - marker->valuei() >= size.y) - { - layoutAbove(); - } - /* - else - { - layoutBelow(); - }*/ - return; - } + case ui::Up: + layoutAbove(); + break; - if(dir == ui::Down) - { - //if(int(view.height()) - pos.y - marker->valuei() >= size.y) - { - layoutBelow(); - } - /*else - { - layoutAbove(); - }*/ - return; - } + case ui::Down: + layoutBelow(); + break; - if(dir == ui::Left) - { - //if(pos.x - marker->valuei() >= size.x) - { - layoutLeft(); - } - /*else - { - layoutRight(); - }*/ - return; - } + case ui::Left: + layoutLeft(); + break; - if(dir == ui::Right) - { - //if(int(view.width()) - pos.x - marker->valuei() >= size.x) - { - layoutRight(); - } - /*else - { - layoutLeft(); - }*/ - return; + case ui::Right: + layoutRight(); + break; } } @@ -223,6 +185,11 @@ DENG2_PIMPL(PopupWidget) self.popupClosing(); + DENG2_FOR_PUBLIC_AUDIENCE(Close, i) + { + i->popupBeingClosed(self); + } + emit self.closed(); dismissTimer.start(); diff --git a/doomsday/libdeng2/src/widgets/widget.cpp b/doomsday/libdeng2/src/widgets/widget.cpp index 0217cc9729..2239068604 100644 --- a/doomsday/libdeng2/src/widgets/widget.cpp +++ b/doomsday/libdeng2/src/widgets/widget.cpp @@ -70,6 +70,8 @@ Widget::~Widget() root().setFocus(0); } + audienceForParentChange.clear(); + // Remove from parent automatically. if(d->parent) { diff --git a/doomsday/libshell/include/de/shell/action.h b/doomsday/libshell/include/de/shell/action.h index a7fdcf30f2..9d341cbbe6 100644 --- a/doomsday/libshell/include/de/shell/action.h +++ b/doomsday/libshell/include/de/shell/action.h @@ -59,12 +59,16 @@ class Action : public QObject, public de::Action void trigger(); + Action *duplicate() const; + signals: void triggered(); private: KeyEvent _event; String _label; + QObject *_target; + char const *_slot; }; } // namespace shell diff --git a/doomsday/libshell/src/action.cpp b/doomsday/libshell/src/action.cpp index eef3f692e6..90ba639062 100644 --- a/doomsday/libshell/src/action.cpp +++ b/doomsday/libshell/src/action.cpp @@ -21,11 +21,11 @@ namespace de { namespace shell { -Action::Action(String const &label) : _event(KeyEvent("")), _label(label) +Action::Action(String const &label) : _event(KeyEvent("")), _label(label), _target(0), _slot(0) {} Action::Action(String const &label, QObject *target, char const *slot) - : _event(KeyEvent("")), _label(label) + : _event(KeyEvent("")), _label(label), _target(target), _slot(slot) { if(target && slot) { @@ -34,7 +34,7 @@ Action::Action(String const &label, QObject *target, char const *slot) } Action::Action(String const &label, KeyEvent const &event, QObject *target, char const *slot) - : _event(event), _label(label) + : _event(event), _label(label), _target(target), _slot(slot) { if(target && slot) { @@ -43,7 +43,7 @@ Action::Action(String const &label, KeyEvent const &event, QObject *target, char } Action::Action(KeyEvent const &event, QObject *target, char const *slot) - : _event(event) + : _event(event), _target(target), _slot(slot) { if(target && slot) { @@ -80,5 +80,10 @@ void Action::trigger() emit triggered(); } +Action *Action::duplicate() const +{ + return new Action(_label, _event, _target, _slot); +} + } // namespace shell } // namespace de