理解 MyGUI 界面库的 IUnlinkWidget

67 篇文章 1 订阅
40 篇文章 2 订阅

作者:庄晓立(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


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值