作者:庄晓立(Liigo),2012-8-10。
原创链接:http://blog.csdn.net/liigo/article/details/7851835
转载请注明出处:http://blog.csdn.net/liigo
MyGUI::IUnlinkWidget是一个纯虚类,以Java的语言来说它是一个interface,它有唯一的纯虚函数 void _unlinkWidget (Widget *_widget) (除去虚拟析构函数)。文档中没有对这个接口做任何说明,而 “unlink” 字面含义又太宽泛,导致不好理解。本文试图通过阅读MyGUI源代码的方式揭开IUnlinkWidget的面纱。
IUnlinkWidget纯接口类完整定义如下:
class MYGUI_EXPORT IUnlinkWidget { public: virtual ~IUnlinkWidget() { } virtual void _unlinkWidget(Widget* _widget) = 0; };
咱们开门见山的说吧:MyGUI::WidgetManager内部维护着一个IUnlinkWidget对象数组(std::vector<IUnlinkWidget*>),当某个MyGUI::Widget(窗口组件)即将被销毁之前,调用每个IUnlinkWidget对象的_unlinkWidget()方法。也就是说,用户通过向WidgetManager注册一个IUnlinkWidget对象,就可以监控任意Widget的销毁动作,以便在其被销毁之前做一些清理工作。
MyGUI::WidgetManager内部维护和调用IUnlinkWidget对象数组的相关代码如下:
//typedef std::vector<IUnlinkWidget*> VectorIUnlinkWidget; private: VectorIUnlinkWidget mVectorIUnlinkWidget; void WidgetManager::registerUnlinker(IUnlinkWidget* _unlink) { unregisterUnlinker(_unlink); mVectorIUnlinkWidget.push_back(_unlink); } void WidgetManager::unregisterUnlinker(IUnlinkWidget* _unlink) { VectorIUnlinkWidget::iterator iter = std::remove(mVectorIUnlinkWidget.begin(), mVectorIUnlinkWidget.end(), _unlink); if (iter != mVectorIUnlinkWidget.end()) mVectorIUnlinkWidget.erase(iter); } void WidgetManager::unlinkFromUnlinkers(Widget* _widget) { for (VectorIUnlinkWidget::iterator iter = mVectorIUnlinkWidget.begin(); iter != mVectorIUnlinkWidget.end(); ++iter) { (*iter)->_unlinkWidget(_widget); } }
上述代码中,registerUnlinker() 负责向WidgetManager注册一个IUnlinkWidget对象,unregisterUnlinker()负责取消注册。而unlinkFromUnlinkers()负责调用IUnlinkWidget对象,传入(即将被销毁的)Widget对象。但是WidgetManager本身并没有直接调用过unlinkFromUnlinkers(),那么它是被谁调用的呢,又是在什么时机调用的呢?现在我们把目光投向Widget和Gui这两个类,看看下面的代码:
void Widget::_destroyChildWidget(Widget* _widget) { MYGUI_ASSERT(nullptr != _widget, "invalid widget pointer"); if (mParent != nullptr && mParent->getClientWidget() == this) mParent->onWidgetDestroy(_widget); onWidgetDestroy(_widget); VectorWidgetPtr::iterator iter = std::find(mWidgetChild.begin(), mWidgetChild.end(), _widget); if (iter != mWidgetChild.end()) { // сохраняем указатель MyGUI::Widget* widget = *iter; // удаляем из списка mWidgetChild.erase(iter); // отписываем от всех WidgetManager::getInstance().unlinkFromUnlinkers(_widget); // непосредственное удаление WidgetManager::getInstance()._deleteWidget(widget); } else { MYGUI_EXCEPT("Widget '" << _widget->getName() << "' not found"); } } void Widget::_destroyAllChildWidget() { WidgetManager& manager = WidgetManager::getInstance(); while (!mWidgetChild.empty()) { // сразу себя отписывем, иначе вложенной удаление убивает все Widget* widget = mWidgetChild.back(); mWidgetChild.pop_back(); // отписываем от всех manager.unlinkFromUnlinkers(widget); // и сами удалим, так как его больше в списке нет WidgetManager::getInstance()._deleteWidget(widget); } } void Gui::_destroyChildWidget(Widget* _widget) { MYGUI_ASSERT(nullptr != _widget, "invalid widget pointer"); VectorWidgetPtr::iterator iter = std::find(mWidgetChild.begin(), mWidgetChild.end(), _widget); if (iter != mWidgetChild.end()) { // сохраняем указатель MyGUI::Widget* widget = *iter; // удаляем из списка mWidgetChild.erase(iter); // отписываем от всех mWidgetManager->unlinkFromUnlinkers(_widget); // непосредственное удаление WidgetManager::getInstance()._deleteWidget(widget); } else { MYGUI_EXCEPT("Widget '" << _widget->getName() << "' not found"); } } void Gui::_destroyAllChildWidget() { while (!mWidgetChild.empty()) { // сразу себя отписывем, иначе вложенной удаление убивает все Widget* widget = mWidgetChild.back(); mWidgetChild.pop_back(); // отписываем от всех mWidgetManager->unlinkFromUnlinkers(widget); // и сами удалим, так как его больше в списке нет WidgetManager::getInstance()._deleteWidget(widget); } }
从上面的四处代码我们看到,无一例外,都是在销毁窗口组件(WidgetManager::_deleteWidget(widget))“之前”调用了WidgetManager::unlinkFromUnlinkers(widget)(进而调用了每一个IUnlinkWidget::_unlinkWidget(widget))。至此事情就很明了了,通过向WidgetManager注册IUnlinkWidget可以监听所有窗口组件的“即将被销毁”事件。
使用IUnlinkWidget也很简单,首先继承它并实现接口函数_unlinkWidget(Widget*),然后使用WidgetManager::registerUnlinker(IUnlinkWidget*)注册,完事后使用WidgetManager::unregisterUnlinker(IUnlinkWidget*)反注册。中间就等着_unlinkWidget(Widget*)被调用就行了,每一次被调用意味着一个Widget即将被销毁。
在MyGUI内部,也多次使用了IUnlinkWidget机制监听窗口组件被销毁事件。如ToolTipManager在Widget销毁前重置提示框,LayerManager在Widget销毁前使其脱离layer,PointerManager在Widget销毁前重置鼠标指针,InputManager在Widget销毁前处理窗口焦点和模态窗口,ControllerManager在Widget销毁前更新其管理的动态窗口,Gui在Widget销毁前清理该Widget的所有帧事件delegate对象。下面我(liigo)就以Gui类为例看看它是如何使用IUnlinkWidget的:
void Gui::initialise(const std::string& _core) { //... WidgetManager::getInstance().registerUnlinker(this); //... } void Gui::shutdown() { //... WidgetManager::getInstance().unregisterUnlinker(this); //... } void Gui::_unlinkWidget(Widget* _widget) { eventFrameStart.clear(_widget); }
全文到此结束。最后给出一个思考题:为什么MyGUI不使用更具通用性的delegate事件委派机制处理Widget将被销毁事件?既然delegate已经在MyGUI内广泛使用了,为什么还要设计出另外一套IUnlinkWidget机制呢?后者比前者有何区别和优势?原创链接:http://blog.csdn.net/liigo/article/details/7851835