diff --git a/doomsday/client/client.pro b/doomsday/client/client.pro index 8a499700de..84027ac1ef 100644 --- a/doomsday/client/client.pro +++ b/doomsday/client/client.pro @@ -441,7 +441,8 @@ INCLUDEPATH += \ HEADERS += \ $$DENG_API_HEADERS \ - $$DENG_HEADERS + $$DENG_HEADERS \ + include/ui/widgets/guiwidgetprivate.h # Platform-specific sources. win32 { diff --git a/doomsday/client/include/ui/widgets/blurwidget.h b/doomsday/client/include/ui/widgets/blurwidget.h index f78848d3f2..fa5ea57af0 100644 --- a/doomsday/client/include/ui/widgets/blurwidget.h +++ b/doomsday/client/include/ui/widgets/blurwidget.h @@ -30,6 +30,7 @@ class BlurWidget : public GuiWidget { public: BlurWidget(de::String const &name = ""); + ~BlurWidget(); }; #endif // DENG_CLIENT_BLURWIDGET_H diff --git a/doomsday/client/include/ui/widgets/consolewidget.h b/doomsday/client/include/ui/widgets/consolewidget.h index 9a281a8360..760e98eb3f 100644 --- a/doomsday/client/include/ui/widgets/consolewidget.h +++ b/doomsday/client/include/ui/widgets/consolewidget.h @@ -78,10 +78,6 @@ public slots: protected slots: void logContentHeightIncreased(int delta); -protected: - void glInit(); - void glDeinit(); - private: DENG2_PRIVATE(d) }; diff --git a/doomsday/client/include/ui/widgets/guiwidget.h b/doomsday/client/include/ui/widgets/guiwidget.h index b35fd8b9e4..6c8922d70c 100644 --- a/doomsday/client/include/ui/widgets/guiwidget.h +++ b/doomsday/client/include/ui/widgets/guiwidget.h @@ -26,6 +26,7 @@ #include "../uidefs.h" #include "ui/style.h" +#include "guiwidgetprivate.h" class GuiRootWidget; class BlurWidget; @@ -68,6 +69,10 @@ class BlurWidget; * * QObject is a base class for the signals and slots capabilities. * + * @note The destructor of a derived class is required to first call + * GuiWidget::deinitialize(). This will ensure all the GL resources of the + * involved widget classes are released as appropriate. + * * @ingroup gui */ class GuiWidget : public QObject, public de::Widget @@ -241,6 +246,8 @@ class GuiWidget : public QObject, public de::Widget bool geometryRequested() const; + bool isInitialized() const; + protected: /** * Called by GuiWidget::update() the first time an update is being carried @@ -252,10 +259,11 @@ class GuiWidget : public QObject, public de::Widget virtual void glInit(); /** - * Called by GuiWidget before the widget is destroyed. This is the - * appropriate place for the widget to release its GL resources. If one - * waits until the widget's destructor to do so, it may already have lost - * access to some required information (such as the root widget). + * Called from deinitialize(). Deinitialization must occur before the + * widget is destroyed. This is the appropriate place for the widget to + * release its GL resources. If one waits until the widget's destructor to + * do so, it may already have lost access to some required information + * (such as the root widget, or derived classes' private instances). */ virtual void glDeinit(); diff --git a/doomsday/client/include/ui/widgets/guiwidgetprivate.h b/doomsday/client/include/ui/widgets/guiwidgetprivate.h new file mode 100644 index 0000000000..d661b0dc80 --- /dev/null +++ b/doomsday/client/include/ui/widgets/guiwidgetprivate.h @@ -0,0 +1,71 @@ +#ifndef DENG_CLIENT_GUIWIDGETPRIVATE_H +#define DENG_CLIENT_GUIWIDGETPRIVATE_H + +#include +#include "guirootwidget.h" + +/** + * Base class for GuiWidget-derived widgets' private implementation. Provides + * easy access to the root widget and shared GL resources. This should be used + * as the base class for private implementations if GL resources are being + * used (i.e., glInit() and glDeinit() are being called). + * + * The destructor of classes derived from GuiWidgetPrivate must make the + * following call:
self.deserialize()
+ * + * Use DENG_GUI_PIMPL() instead of the DENG2_PIMPL() macro. + */ +template +class GuiWidgetPrivate : public de::Private +{ +public: + typedef GuiWidgetPrivate Base; // shadows de::Private<>::Base + +public: + GuiWidgetPrivate(PublicType &i) : de::Private(i) {} + + GuiWidgetPrivate(PublicType *i) : de::Private(i) {} + + virtual ~GuiWidgetPrivate() + { + /** + * Ensure that the derived's class's glDeinit() method has been + * called before the private class instance is destroyed. At least + * classes that have GuiWidget as the immediate parent class need to + * call deinitialize() in their destructors. + */ + DENG2_ASSERT(!self.isInitialized()); + } + + bool hasRoot() const + { + return self.hasRoot(); + } + + GuiRootWidget &root() const + { + DENG2_ASSERT(hasRoot()); + return self.root(); + } + + de::AtlasTexture &atlas() const + { + return root().atlas(); + } + + de::GLUniform &uAtlas() const + { + return root().uAtlas(); + } + + de::GLShaderBank &shaders() const + { + return root().shaders(); + } +}; + +#define DENG_GUI_PIMPL(ClassName) \ + typedef ClassName Public; \ + struct ClassName::Instance : public GuiWidgetPrivate + +#endif // DENG_CLIENT_GUIWIDGETPRIVATE_H diff --git a/doomsday/client/src/ui/widgets/blurwidget.cpp b/doomsday/client/src/ui/widgets/blurwidget.cpp index 6b187daa0b..4c1721f6ca 100644 --- a/doomsday/client/src/ui/widgets/blurwidget.cpp +++ b/doomsday/client/src/ui/widgets/blurwidget.cpp @@ -24,3 +24,8 @@ BlurWidget::BlurWidget(String const &name) : GuiWidget(name) { set(Background(Vector4f(1, 1, 1, 0), Background::Blurred)); } + +BlurWidget::~BlurWidget() +{ + deinitialize(); +} diff --git a/doomsday/client/src/ui/widgets/busywidget.cpp b/doomsday/client/src/ui/widgets/busywidget.cpp index 3daa3033e8..3f9017b1a0 100644 --- a/doomsday/client/src/ui/widgets/busywidget.cpp +++ b/doomsday/client/src/ui/widgets/busywidget.cpp @@ -33,7 +33,7 @@ using namespace de; -DENG2_PIMPL(BusyWidget) +DENG_GUI_PIMPL(BusyWidget) { typedef DefaultVertexBuf VertexBuf; @@ -56,6 +56,11 @@ DENG2_PIMPL(BusyWidget) self.add(progress); } + ~Instance() + { + self.deinitialize(); + } + void glInit() { VertexBuf *buf = new VertexBuf; @@ -65,7 +70,7 @@ DENG2_PIMPL(BusyWidget) buf->setVertices(gl::TriangleStrip, verts, gl::Static); drawable.addBuffer(buf); - self.root().shaders().build(drawable.program(), "generic.textured.color") + shaders().build(drawable.program(), "generic.textured.color") << uMvpMatrix << uTex; } diff --git a/doomsday/client/src/ui/widgets/buttonwidget.cpp b/doomsday/client/src/ui/widgets/buttonwidget.cpp index 17d901de73..b627a0931d 100644 --- a/doomsday/client/src/ui/widgets/buttonwidget.cpp +++ b/doomsday/client/src/ui/widgets/buttonwidget.cpp @@ -24,7 +24,7 @@ using namespace de; -DENG2_PIMPL(ButtonWidget), +DENG_GUI_PIMPL(ButtonWidget), DENG2_OBSERVES(Action, Triggered) { State state; @@ -42,6 +42,11 @@ DENG2_OBSERVES(Action, Triggered) setDefaultBackground(); } + ~Instance() + { + self.deinitialize(); + } + void setState(State st) { if(state == st) return; diff --git a/doomsday/client/src/ui/widgets/choicewidget.cpp b/doomsday/client/src/ui/widgets/choicewidget.cpp index 2e7c0702f3..7d78d75b30 100644 --- a/doomsday/client/src/ui/widgets/choicewidget.cpp +++ b/doomsday/client/src/ui/widgets/choicewidget.cpp @@ -23,7 +23,7 @@ using namespace de; using namespace ui; -DENG2_PIMPL(ChoiceWidget), +DENG_GUI_PIMPL(ChoiceWidget), DENG2_OBSERVES(Context, Addition), DENG2_OBSERVES(Context, Removal), DENG2_OBSERVES(ContextWidgetOrganizer, WidgetCreation) @@ -75,6 +75,11 @@ DENG2_OBSERVES(ContextWidgetOrganizer, WidgetCreation) updateStyle(); } + ~Instance() + { + self.deinitialize(); + } + void updateStyle() { // Popup background color. diff --git a/doomsday/client/src/ui/widgets/consolecommandwidget.cpp b/doomsday/client/src/ui/widgets/consolecommandwidget.cpp index fb525ad105..03acb0c574 100644 --- a/doomsday/client/src/ui/widgets/consolecommandwidget.cpp +++ b/doomsday/client/src/ui/widgets/consolecommandwidget.cpp @@ -30,7 +30,7 @@ using namespace de; -DENG2_PIMPL(ConsoleCommandWidget), +DENG_GUI_PIMPL(ConsoleCommandWidget), DENG2_OBSERVES(App, StartupComplete), public IGameChangeObserver { @@ -70,6 +70,7 @@ public IGameChangeObserver ~Instance() { + self.deinitialize(); App::app().audienceForStartupComplete -= this; audienceForGameChange -= this; } diff --git a/doomsday/client/src/ui/widgets/consolewidget.cpp b/doomsday/client/src/ui/widgets/consolewidget.cpp index 9129c91267..5ef79b83ac 100644 --- a/doomsday/client/src/ui/widgets/consolewidget.cpp +++ b/doomsday/client/src/ui/widgets/consolewidget.cpp @@ -81,14 +81,7 @@ DENG2_PIMPL(ConsoleWidget) releaseRef(horizShift); releaseRef(width); releaseRef(height); - } - - void glInit() - { - } - - void glDeinit() - { + self.deinitialize(); } void expandLog(int delta, bool useOffsetAnimation) @@ -292,17 +285,6 @@ void ConsoleWidget::update() } } -void ConsoleWidget::glInit() -{ - LOG_AS("ConsoleWidget"); - d->glInit(); -} - -void ConsoleWidget::glDeinit() -{ - d->glDeinit(); -} - bool ConsoleWidget::handleEvent(Event const &event) { // Hovering over the right edge shows the <-> cursor. diff --git a/doomsday/client/src/ui/widgets/contextwidgetorganizer.cpp b/doomsday/client/src/ui/widgets/contextwidgetorganizer.cpp index 1c1de020d2..73613a167a 100644 --- a/doomsday/client/src/ui/widgets/contextwidgetorganizer.cpp +++ b/doomsday/client/src/ui/widgets/contextwidgetorganizer.cpp @@ -86,6 +86,9 @@ DENG2_OBSERVES(ui::Item, Change ) ui::Item const &item = context->at(pos); GuiWidget *w = factory->makeItemWidget(item, container); + + w->setName(item.label()); + if(!w) return; // Unpresentable. // Others may alter the widget in some way. diff --git a/doomsday/client/src/ui/widgets/dialogwidget.cpp b/doomsday/client/src/ui/widgets/dialogwidget.cpp index daea1e9fdd..418fc12ee0 100644 --- a/doomsday/client/src/ui/widgets/dialogwidget.cpp +++ b/doomsday/client/src/ui/widgets/dialogwidget.cpp @@ -31,6 +31,15 @@ using namespace de; static TimeDelta const FLASH_ANIM_SPAN = 0.75; +/** + * Compares dialog button items to determine the order in which they + * should appear in the UI. + * + * @param a Dialog button item. + * @param b Dialog button item. + * + * @return @c true, if a < b. + */ static bool dialogButtonOrder(ui::Item const &a, ui::Item const &b) { DialogButtonItem const &left = a.as(); @@ -66,7 +75,7 @@ static bool dialogButtonOrder(ui::Item const &a, ui::Item const &b) return false; } -DENG2_PIMPL(DialogWidget), +DENG_GUI_PIMPL(DialogWidget), DENG2_OBSERVES(ContextWidgetOrganizer, WidgetCreation), DENG2_OBSERVES(ContextWidgetOrganizer, WidgetUpdate), DENG2_OBSERVES(Widget, ChildAddition), // for styling the contents @@ -125,14 +134,18 @@ DENG2_OBSERVES(ui::Context, Removal) self.setContent(container); } + ~Instance() + { + self.deinitialize(); + } + void updateContentHeight() { // The container's height is limited by the height of the view. Normally // the dialog tries to show the full height of the content area. - DENG2_ASSERT(self.hasRoot()); self.content().rule().setInput(Rule::Height, - OperatorRule::minimum(self.root().viewHeight(), + OperatorRule::minimum(root().viewHeight(), area->contentRule().height() + area->margin() + buttons->rule().height())); @@ -151,7 +164,7 @@ DENG2_OBSERVES(ui::Context, Removal) void updateButtonLayout() { buttons->items().sort(dialogButtonOrder); - buttons->updateLayout(); + //buttons->updateLayout(); needButtonUpdate = false; } diff --git a/doomsday/client/src/ui/widgets/documentwidget.cpp b/doomsday/client/src/ui/widgets/documentwidget.cpp index 3a17cf1a1d..5e0dc8c573 100644 --- a/doomsday/client/src/ui/widgets/documentwidget.cpp +++ b/doomsday/client/src/ui/widgets/documentwidget.cpp @@ -17,7 +17,6 @@ */ #include "ui/widgets/documentwidget.h" -#include "ui/widgets/guirootwidget.h" #include "ui/widgets/progresswidget.h" #include "ui/widgets/gltextcomposer.h" @@ -31,7 +30,7 @@ using namespace de; static int const ID_BACKGROUND = 1; // does not scroll static int const ID_TEXT = 2; // scrolls -DENG2_PIMPL(DocumentWidget), +DENG_GUI_PIMPL(DocumentWidget), DENG2_OBSERVES(Atlas, Reposition), public Font::RichFormat::IStyle { @@ -110,11 +109,8 @@ public Font::RichFormat::IStyle { // Wait until background tasks finish. tasks.waitForDone(); - } - GuiRootWidget &root() - { - return self.root(); + self.deinitialize(); } bool isBeingWrapped() const @@ -171,27 +167,27 @@ public Font::RichFormat::IStyle void glInit() { - root().atlas().audienceForReposition += this; - composer.setAtlas(root().atlas()); + atlas().audienceForReposition += this; + composer.setAtlas(atlas()); composer.setText(text, format); - self.setIndicatorUv(root().atlas().imageRectf(root().solidWhitePixel()).middle()); + self.setIndicatorUv(atlas().imageRectf(root().solidWhitePixel()).middle()); drawable.addBuffer(ID_BACKGROUND, new VertexBuf); drawable.addBuffer(ID_TEXT, new VertexBuf); - root().shaders().build(drawable.program(), "generic.textured.color_ucolor") - << uMvpMatrix << uColor << root().uAtlas(); + shaders().build(drawable.program(), "generic.textured.color_ucolor") + << uMvpMatrix << uColor << uAtlas(); - root().shaders().build(drawable.addProgram(ID_TEXT), "generic.textured.color_ucolor") - << uScrollMvpMatrix << uColor << root().uAtlas(); + shaders().build(drawable.addProgram(ID_TEXT), "generic.textured.color_ucolor") + << uScrollMvpMatrix << uColor << uAtlas(); drawable.setProgram(ID_TEXT, drawable.program(ID_TEXT)); drawable.setState(ID_TEXT, clippedTextState); } void glDeinit() { - root().atlas().audienceForReposition -= this; + atlas().audienceForReposition -= this; composer.release(); drawable.clear(); } diff --git a/doomsday/client/src/ui/widgets/guiwidget.cpp b/doomsday/client/src/ui/widgets/guiwidget.cpp index e537c24eee..0aca0460d3 100644 --- a/doomsday/client/src/ui/widgets/guiwidget.cpp +++ b/doomsday/client/src/ui/widgets/guiwidget.cpp @@ -92,8 +92,17 @@ DENG2_PIMPL(GuiWidget) ~Instance() { - // Deinitialize now if it hasn't been done already. - self.deinitialize(); + // Get rid of all child widgets. + self.clearTree(); + + deinitBlur(); + + /* + * Deinitialization must occur before destruction so that GL resources + * are not leaked. Derived classes are responsible for deinitializing + * first before beginning destruction. + */ + DENG2_ASSERT(!inited); } #ifdef DENG2_DEBUG @@ -582,6 +591,11 @@ bool GuiWidget::geometryRequested() const return d->needGeometry; } +bool GuiWidget::isInitialized() const +{ + return d->inited; +} + void GuiWidget::glMakeGeometry(DefaultVertexBuf::Builder &verts) { if(d->background.type != Background::Blurred && diff --git a/doomsday/client/src/ui/widgets/labelwidget.cpp b/doomsday/client/src/ui/widgets/labelwidget.cpp index 0110bd3212..0e047a60d4 100644 --- a/doomsday/client/src/ui/widgets/labelwidget.cpp +++ b/doomsday/client/src/ui/widgets/labelwidget.cpp @@ -20,7 +20,6 @@ #include "ui/widgets/gltextcomposer.h" #include "ui/widgets/fontlinewrapping.h" #include "ui/widgets/atlasproceduralimage.h" -#include "ui/widgets/guirootwidget.h" #include #include @@ -29,7 +28,7 @@ using namespace de; using namespace ui; -DENG2_PIMPL(LabelWidget), +DENG_GUI_PIMPL(LabelWidget), DENG2_OBSERVES(Atlas, Reposition), public Font::RichFormat::IStyle { @@ -95,6 +94,7 @@ public Font::RichFormat::IStyle ~Instance() { + self.deinitialize(); releaseRef(width); releaseRef(height); } @@ -152,17 +152,11 @@ public Font::RichFormat::IStyle return self.style().richStyleFormat(contentStyle, sizeFactor, fontWeight, fontStyle, colorIndex); } - AtlasTexture &atlas() - { - DENG2_ASSERT(self.hasRoot()); - return self.root().atlas(); - } - void glInit() { drawable.addBuffer(new VertexBuf); - self.root().shaders().build(drawable.program(), "generic.textured.color_ucolor") - << uMvpMatrix << uColor << self.root().uAtlas(); + shaders().build(drawable.program(), "generic.textured.color_ucolor") + << uMvpMatrix << uColor << uAtlas(); composer.setAtlas(atlas()); composer.setWrapping(wraps); @@ -362,7 +356,7 @@ public Font::RichFormat::IStyle if(horizPolicy == Expand) { // Expansion can occur to full view width. - w = self.root().viewSize().x - (tlMargin.x + brMargin.x); + w = root().viewSize().x - (tlMargin.x + brMargin.x); } else { diff --git a/doomsday/client/src/ui/widgets/legacywidget.cpp b/doomsday/client/src/ui/widgets/legacywidget.cpp index 70201a88f8..15a66798e6 100644 --- a/doomsday/client/src/ui/widgets/legacywidget.cpp +++ b/doomsday/client/src/ui/widgets/legacywidget.cpp @@ -57,8 +57,12 @@ using namespace de; DENG2_PIMPL(LegacyWidget) { - Instance(Public *i) : Base(i) - {} + Instance(Public *i) : Base(i) {} + + ~Instance() + { + self.deinitialize(); + } void draw() { diff --git a/doomsday/client/src/ui/widgets/lineeditwidget.cpp b/doomsday/client/src/ui/widgets/lineeditwidget.cpp index 471861e7ca..ab6ea4e690 100644 --- a/doomsday/client/src/ui/widgets/lineeditwidget.cpp +++ b/doomsday/client/src/ui/widgets/lineeditwidget.cpp @@ -35,7 +35,7 @@ static TimeDelta const ANIM_SPAN = .5f; static duint const ID_BUF_TEXT = 1; static duint const ID_BUF_CURSOR = 2; -DENG2_PIMPL(LineEditWidget), +DENG_GUI_PIMPL(LineEditWidget), DENG2_OBSERVES(Atlas, Reposition) { typedef GLBufferT VertexBuf; @@ -78,6 +78,7 @@ DENG2_OBSERVES(Atlas, Reposition) ~Instance() { + self.deinitialize(); releaseRef(height); } @@ -109,7 +110,7 @@ DENG2_OBSERVES(Atlas, Reposition) void updateProjection() { - uMvpMatrix = self.root().projMatrix2D(); + uMvpMatrix = root().projMatrix2D(); } void updateBackground() @@ -125,11 +126,6 @@ DENG2_OBSERVES(Atlas, Reposition) self.set(bg); } - AtlasTexture &atlas() - { - return self.root().atlas(); - } - void glInit() { composer.setAtlas(atlas()); @@ -138,12 +134,12 @@ DENG2_OBSERVES(Atlas, Reposition) drawable.addBuffer(ID_BUF_TEXT, new VertexBuf); drawable.addBufferWithNewProgram(ID_BUF_CURSOR, new VertexBuf, "cursor"); - self.root().shaders().build(drawable.program(), "generic.textured.color_ucolor") + shaders().build(drawable.program(), "generic.textured.color_ucolor") << uMvpMatrix << uColor - << self.root().uAtlas(); + << uAtlas(); - self.root().shaders().build(drawable.program("cursor"), "generic.color_ucolor") + shaders().build(drawable.program("cursor"), "generic.color_ucolor") << uMvpMatrix << uCursorColor; diff --git a/doomsday/client/src/ui/widgets/logwidget.cpp b/doomsday/client/src/ui/widgets/logwidget.cpp index 85a497c9c1..023227014d 100644 --- a/doomsday/client/src/ui/widgets/logwidget.cpp +++ b/doomsday/client/src/ui/widgets/logwidget.cpp @@ -22,7 +22,6 @@ */ #include "ui/widgets/logwidget.h" -#include "ui/widgets/guirootwidget.h" #include "ui/widgets/fontlinewrapping.h" #include "ui/widgets/gltextcomposer.h" #include "ui/widgets/styledlogsinkformatter.h" @@ -45,7 +44,7 @@ using namespace de; using namespace ui; -DENG2_PIMPL(LogWidget), +DENG_GUI_PIMPL(LogWidget), DENG2_OBSERVES(Atlas, Reposition), DENG2_OBSERVES(Atlas, OutOfSpace), public Font::RichFormat::IStyle @@ -372,6 +371,7 @@ public Font::RichFormat::IStyle ~Instance() { LogBuffer::appBuffer().removeSink(sink); + self.deinitialize(); } void clear() @@ -463,13 +463,13 @@ public Font::RichFormat::IStyle uColor = Vector4f(1, 1, 1, 1); background.addBuffer(bgBuf = new VertexBuf); - self.root().shaders().build(background.program(), "generic.textured.color") + shaders().build(background.program(), "generic.textured.color") << uBgMvpMatrix - << self.root().uAtlas(); + << uAtlas(); // Vertex buffer for the log entries. contents.addBuffer(buf = new VertexBuf); - self.root().shaders().build(contents.program(), "generic.textured.color_ucolor") + shaders().build(contents.program(), "generic.textured.color_ucolor") << uMvpMatrix << uShadowColor << uTex; @@ -647,7 +647,7 @@ public Font::RichFormat::IStyle void updateProjection() { - projMatrix = self.root().projMatrix2D(); + projMatrix = root().projMatrix2D(); uBgMvpMatrix = projMatrix; } diff --git a/doomsday/client/src/ui/widgets/menuwidget.cpp b/doomsday/client/src/ui/widgets/menuwidget.cpp index d3e565dc8f..640fe7d451 100644 --- a/doomsday/client/src/ui/widgets/menuwidget.cpp +++ b/doomsday/client/src/ui/widgets/menuwidget.cpp @@ -83,7 +83,7 @@ public ContextWidgetOrganizer::IWidgetFactory _widget->open(); } - SubmenuAction *duplicate() const + Action *duplicate() const { DENG2_ASSERT(false); // not needed return 0; @@ -120,6 +120,11 @@ public ContextWidgetOrganizer::IWidgetFactory setContext(&defaultItems); } + ~Instance() + { + self.deinitialize(); + } + void setContext(Context const *ctx) { if(items) diff --git a/doomsday/client/src/ui/widgets/notificationwidget.cpp b/doomsday/client/src/ui/widgets/notificationwidget.cpp index 643e2ee8cd..6a26ab1ecb 100644 --- a/doomsday/client/src/ui/widgets/notificationwidget.cpp +++ b/doomsday/client/src/ui/widgets/notificationwidget.cpp @@ -17,7 +17,6 @@ */ #include "ui/widgets/notificationwidget.h" -#include "ui/widgets/guirootwidget.h" #include "ui/widgets/sequentiallayout.h" #include @@ -31,7 +30,7 @@ using namespace de; static TimeDelta const ANIM_SPAN = .5; -DENG2_PIMPL(NotificationWidget), +DENG_GUI_PIMPL(NotificationWidget), DENG2_OBSERVES(Widget, ChildAddition), DENG2_OBSERVES(Widget, ChildRemoval) { @@ -67,6 +66,7 @@ DENG2_OBSERVES(Widget, ChildRemoval) ~Instance() { releaseRef(shift); + self.deinitialize(); } void updateStyle() @@ -79,7 +79,7 @@ DENG2_OBSERVES(Widget, ChildRemoval) { drawable.addBuffer(new VertexBuf); - self.root().shaders().build(drawable.program(), "generic.color_ucolor") + shaders().build(drawable.program(), "generic.color_ucolor") << uMvpMatrix << uColor; } diff --git a/doomsday/client/src/ui/widgets/popupmenuwidget.cpp b/doomsday/client/src/ui/widgets/popupmenuwidget.cpp index 5bddcb697c..6972658245 100644 --- a/doomsday/client/src/ui/widgets/popupmenuwidget.cpp +++ b/doomsday/client/src/ui/widgets/popupmenuwidget.cpp @@ -33,8 +33,7 @@ DENG2_OBSERVES(ContextWidgetOrganizer, WidgetUpdate) ButtonWidget *hover; Rectanglei hoverHighlightRect; - Instance(Public *i) : Base(i), hover(0) - {} + Instance(Public *i) : Base(i), hover(0) {} void widgetCreatedForItem(GuiWidget &widget, ui::Item const &item) { diff --git a/doomsday/client/src/ui/widgets/popupwidget.cpp b/doomsday/client/src/ui/widgets/popupwidget.cpp index 98984e6ff4..725263e449 100644 --- a/doomsday/client/src/ui/widgets/popupwidget.cpp +++ b/doomsday/client/src/ui/widgets/popupwidget.cpp @@ -32,7 +32,7 @@ using namespace de; static TimeDelta const OPENING_ANIM_SPAN = 0.4; static TimeDelta const CLOSING_ANIM_SPAN = 0.3; -DENG2_PIMPL(PopupWidget) +DENG_GUI_PIMPL(PopupWidget) { typedef DefaultVertexBuf VertexBuf; @@ -75,6 +75,8 @@ DENG2_PIMPL(PopupWidget) ~Instance() { + self.deinitialize(); + releaseRef(openingRule); releaseRef(anchorX); releaseRef(anchorY); @@ -83,8 +85,8 @@ DENG2_PIMPL(PopupWidget) void glInit() { drawable.addBuffer(new VertexBuf); - self.root().shaders().build(drawable.program(), "generic.textured.color") - << uMvpMatrix << self.root().uAtlas(); + shaders().build(drawable.program(), "generic.textured.color") + << uMvpMatrix << uAtlas(); } void glDeinit() diff --git a/doomsday/client/src/ui/widgets/progresswidget.cpp b/doomsday/client/src/ui/widgets/progresswidget.cpp index fc45f1b007..087f4853b3 100644 --- a/doomsday/client/src/ui/widgets/progresswidget.cpp +++ b/doomsday/client/src/ui/widgets/progresswidget.cpp @@ -24,7 +24,7 @@ using namespace de; -DENG2_PIMPL(ProgressWidget), public Lockable +DENG_GUI_PIMPL(ProgressWidget), public Lockable { Mode mode; Rangei range; @@ -53,6 +53,11 @@ DENG2_PIMPL(ProgressWidget), public Lockable updateStyle(); } + ~Instance() + { + self.deinitialize(); + } + void updateStyle() { self.setImageColor(self.style().colors().colorf(colorId) * Vector4f(1, 1, 1, .5f)); @@ -68,11 +73,6 @@ DENG2_PIMPL(ProgressWidget), public Lockable atlas().release(gearTex); gearTex = Id::None; } - - AtlasTexture &atlas() - { - return self.root().atlas(); - } }; ProgressWidget::ProgressWidget(String const &name) : d(new Instance(this)) diff --git a/doomsday/client/src/ui/widgets/scrollareawidget.cpp b/doomsday/client/src/ui/widgets/scrollareawidget.cpp index af04a1f782..c949670ac2 100644 --- a/doomsday/client/src/ui/widgets/scrollareawidget.cpp +++ b/doomsday/client/src/ui/widgets/scrollareawidget.cpp @@ -82,6 +82,7 @@ DENG2_PIMPL(ScrollAreaWidget), public Lockable releaseRef(y); releaseRef(maxX); releaseRef(maxY); + self.deinitialize(); } void updateStyle() diff --git a/doomsday/client/src/ui/widgets/taskbarwidget.cpp b/doomsday/client/src/ui/widgets/taskbarwidget.cpp index d32f0473d5..e81f2009d8 100644 --- a/doomsday/client/src/ui/widgets/taskbarwidget.cpp +++ b/doomsday/client/src/ui/widgets/taskbarwidget.cpp @@ -50,7 +50,7 @@ static uint POS_PANEL = 0; static uint POS_UNLOAD = 3; static uint POS_UPDATER_SETTINGS = 6; -DENG2_PIMPL(TaskBarWidget), +DENG_GUI_PIMPL(TaskBarWidget), public IGameChangeObserver { typedef DefaultVertexBuf VertexBuf; @@ -89,15 +89,16 @@ public IGameChangeObserver ~Instance() { audienceForGameChange -= this; - releaseRef(vertShift); + + self.deinitialize(); } void glInit() { drawable.addBuffer(new VertexBuf); - self.root().shaders().build(drawable.program(), "generic.color_ucolor") + shaders().build(drawable.program(), "generic.color_ucolor") << uMvpMatrix << uColor; @@ -124,7 +125,7 @@ public IGameChangeObserver void updateProjection() { - uMvpMatrix = self.root().projMatrix2D(); + uMvpMatrix = root().projMatrix2D(); } GuiWidget &itemWidget(uint pos) const diff --git a/doomsday/client/src/ui/widgets/togglewidget.cpp b/doomsday/client/src/ui/widgets/togglewidget.cpp index 499a4beab9..edd386c588 100644 --- a/doomsday/client/src/ui/widgets/togglewidget.cpp +++ b/doomsday/client/src/ui/widgets/togglewidget.cpp @@ -31,6 +31,7 @@ static TimeDelta const SWITCH_ANIM_SPAN = 0.3; DENG2_PIMPL(ToggleWidget), DENG2_OBSERVES(ButtonWidget, Press) { + /// Draws the animated I/O toggle indicator. struct ToggleProceduralImage : public ProceduralImage { ToggleProceduralImage(GuiWidget &owner) @@ -96,14 +97,14 @@ DENG2_OBSERVES(ButtonWidget, Press) }; ToggleState state; - ToggleProceduralImage *procImage; + ToggleProceduralImage *procImage; // not owned Instance(Public *i) : Base(i), state(Inactive), procImage(new ToggleProceduralImage(self)) { - self.setImage(procImage); + self.setImage(procImage); // base class owns it self.audienceForPress += this; } diff --git a/doomsday/libdeng2/src/widgets/widget.cpp b/doomsday/libdeng2/src/widgets/widget.cpp index 9e410482c4..22f0c77457 100644 --- a/doomsday/libdeng2/src/widgets/widget.cpp +++ b/doomsday/libdeng2/src/widgets/widget.cpp @@ -51,10 +51,13 @@ DENG2_PIMPL(Widget) void clear() { + qDebug() << "clearing children of" << &self << name; while(!children.isEmpty()) { children.first()->d->parent = 0; - delete children.takeFirst(); + Widget *w = children.takeFirst(); + qDebug() << "deleting" << w << w->name(); + delete w; } index.clear(); }