zoukankan      html  css  js  c++  java
  • Irrlicht 3D Engine 笔记系列 之 教程5- User Interface

    作者:i_dovelemon

    日期:2014 / 12 / 18

    来源:CSDN

    主题:GUI


    引言

                 今天。博主学习了第五个教程。

    这个教程解说了怎样使用Irrlicht内置的一个基础模块。GUI模块,来开发一些GUI程序。作为Irrlicht的重要基础模块,博主有必要对此进行一些代码跟踪和深入的了解。具体的教程过程请看官网。


    设备选择

                曾经在使用Ogre的时候,它的程序执行之初都会问你须要使用的是哪一种API。那么在Irrlicht引擎中是否有相同的功能了?答案是肯定,在Irrlicht的driverChoice.h这个头文件里包括了一个函数。driverChoiceConsole()。这个函数就会打开一个控制台窗体,显示出能够选择的设备程序,例如以下图所看到的:

                                                                                           

                  这个控制台窗体列举了非常多能够使用的API程序,用户仅仅要选择对应的API就可以。博主安装的是Direct3D 9.0c全部选择了b。

                在使用这个功能的时候,读者须要注意,仅仅要当我们的程序类型是控制台类型的时候,这个函数才会起作用,否则这个函数不会弹出窗体,而且仅仅会返回一个EDT_COUNT表示没有有效的API。

    这一点,仅仅要我们在程序的开头加上这种宜居宏命令就可以:

    #ifdef _IRR_WINDOWS_
    #pragma comment(lib,"irrlicht.lib")
    #pragma comment(linker,"/subsystem:console /ENTRY:mainCRTStartup")
    #endif

                注意这里的subsystem是console,而故事windows了。


    GUIEvent

                   在这个教程中,使用了前面教程4中介绍的事件处理机制。教程中创建了一个事件接收器,而且处理了GUIEvent。还记得的博主前面的文章中讲述过,在Irrlicht中的事件大致的分为6种类型。各自是GUI事件,Mouse事件,Keyboard事件,Joystick事件。Log事件以及用户自己定义的事件。这6个基础事件终于导致了SEvent这样的结构体,使用了共用体技术,将这6中基本类型都包括在了SEvent这个结构中了。前面我们仅仅是对这个结构体有大概的了解,而且博主说过,等到以后遇到某种消息的时候,我们在具体的来解说相应这样的消息类型的明白结构。

                  在教程5中,使用的事件类型是GUI事件。

    也就是说,如今SEvent里面包括的是一个SGUIEvent的结构。我们打开GUIEvent结构的定义,例如以下所看到的:

    	//! Any kind of GUI event.
    	struct SGUIEvent
    	{
    		//! IGUIElement who called the event
    		gui::IGUIElement* Caller;
    
    		//! If the event has something to do with another element, it will be held here.
    		gui::IGUIElement* Element;
    
    		//! Type of GUI Event
    		gui::EGUI_EVENT_TYPE EventType;
    
    	};

                    这个结构里面包括了3个成员。分别意思例如以下:

                  成员: IGUIElement* Caller -- 发生GUI事件的GUI元素对象

                  成员: IGUIElement* Element -- 与发生GUI事件的GUI元素可能发生交互的另外一个GUI元素

                  成员: EGUI_EVENT_TYPE EventType -- 确切的GUI事件的类型。

                  在Irrlicht引擎中。将事件分为了6大基础的事件类型。全部当某个事件发生的时候,事件接受器接受到一个SEvent结构,这个结构中的EventType表示的就是这个事件是6大基础事件类型中的哪一种,然后详细的事件类型在交由详细的事件结构体来保存。比方这里的基础事件类型是EET_GUI_EVENT。然后详细的事件类型保存在SGUIEvent结构体里面的EventType中了。



    GUISkin

                     在博主编写这个教程程序的时候,对这个教程里面的GUISkin的概念不是非常理解。

    所以,有必要探究一下这个GUISkin究竟是个什么东东。

    那么,最easy的方法先看下官方文档对这个GUISkin的概括性解释,我将描写叙述摘录下来:

                 “A skin modifies the look of the GUI elements.”

                     官网上的描写叙述就是这么的简单粗暴,这个描写叙述仅仅是告诉了我Skin和元素的外表有关系。但这么简单的描写叙述并不能满足我。

    所以,我们直接到GUISkin的定义出去看源代码,套用侯捷大师的一句话。“源代码面前,了无秘密“,下面就是IGUISkin的定义:

    	//! A skin modifies the look of the GUI elements.
    	class IGUISkin : public virtual io::IAttributeExchangingObject
    	{
    	public:
    
    		//! returns default color
    		virtual video::SColor getColor(EGUI_DEFAULT_COLOR color) const = 0;
    
    		//! sets a default color
    		virtual void setColor(EGUI_DEFAULT_COLOR which, video::SColor newColor) = 0;
    
    		//! returns size for the given size type
    		virtual s32 getSize(EGUI_DEFAULT_SIZE size) const = 0;
    
    		//! Returns a default text.
    		/** For example for Message box button captions:
    		"OK", "Cancel", "Yes", "No" and so on. */
    		virtual const wchar_t* getDefaultText(EGUI_DEFAULT_TEXT text) const = 0;
    
    		//! Sets a default text.
    		/** For example for Message box button captions:
    		"OK", "Cancel", "Yes", "No" and so on. */
    		virtual void setDefaultText(EGUI_DEFAULT_TEXT which, const wchar_t* newText) = 0;
    
    		//! sets a default size
    		virtual void setSize(EGUI_DEFAULT_SIZE which, s32 size) = 0;
    
    		//! returns the default font
    		virtual IGUIFont* getFont(EGUI_DEFAULT_FONT which=EGDF_DEFAULT) const = 0;
    
    		//! sets a default font
    		virtual void setFont(IGUIFont* font, EGUI_DEFAULT_FONT which=EGDF_DEFAULT) = 0;
    
    		//! returns the sprite bank
    		virtual IGUISpriteBank* getSpriteBank() const = 0;
    
    		//! sets the sprite bank
    		virtual void setSpriteBank(IGUISpriteBank* bank) = 0;
    
    		//! Returns a default icon
    		/** Returns the sprite index within the sprite bank */
    		virtual u32 getIcon(EGUI_DEFAULT_ICON icon) const = 0;
    
    		//! Sets a default icon
    		/** Sets the sprite index used for drawing icons like arrows,
    		close buttons and ticks in checkboxes
    		param icon: Enum specifying which icon to change
    		param index: The sprite index used to draw this icon */
    		virtual void setIcon(EGUI_DEFAULT_ICON icon, u32 index) = 0;
    
    		//! draws a standard 3d button pane
    		/** Used for drawing for example buttons in normal state.
    		It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and
    		EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details.
    		param element: Pointer to the element which wishes to draw this. This parameter
    		is usually not used by IGUISkin, but can be used for example by more complex
    		implementations to find out how to draw the part exactly.
    		param rect: Defining area where to draw.
    		param clip: Clip area. */
    		virtual void draw3DButtonPaneStandard(IGUIElement* element,
    			const core::rect<s32>& rect,
    			const core::rect<s32>* clip=0) = 0;
    
    		//! draws a pressed 3d button pane
    		/** Used for drawing for example buttons in pressed state.
    		It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and
    		EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details.
    		param element: Pointer to the element which wishes to draw this. This parameter
    		is usually not used by IGUISkin, but can be used for example by more complex
    		implementations to find out how to draw the part exactly.
    		param rect: Defining area where to draw.
    		param clip: Clip area. */
    		virtual void draw3DButtonPanePressed(IGUIElement* element,
    			const core::rect<s32>& rect,
    			const core::rect<s32>* clip=0) = 0;
    
    		//! draws a sunken 3d pane
    		/** Used for drawing the background of edit, combo or check boxes.
    		param element: Pointer to the element which wishes to draw this. This parameter
    		is usually not used by IGUISkin, but can be used for example by more complex
    		implementations to find out how to draw the part exactly.
    		param bgcolor: Background color.
    		param flat: Specifies if the sunken pane should be flat or displayed as sunken
    		deep into the ground.
    		param fillBackGround: Specifies if the background should be filled with the background
    		color or not be drawn at all.
    		param rect: Defining area where to draw.
    		param clip: Clip area. */
    		virtual void draw3DSunkenPane(IGUIElement* element,
    			video::SColor bgcolor, bool flat, bool fillBackGround,
    			const core::rect<s32>& rect,
    			const core::rect<s32>* clip=0) = 0;
    
    		//! draws a window background
    		/** Used for drawing the background of dialogs and windows.
    		param element: Pointer to the element which wishes to draw this. This parameter
    		is usually not used by IGUISkin, but can be used for example by more complex
    		implementations to find out how to draw the part exactly.
    		param titleBarColor: Title color.
    		param drawTitleBar: True to enable title drawing.
    		param rect: Defining area where to draw.
    		param clip: Clip area.
    		param checkClientArea: When set to non-null the function will not draw anything,
    		but will instead return the clientArea which can be used for drawing by the calling window.
    		That is the area without borders and without titlebar.
    		
    eturn Returns rect where it would be good to draw title bar text. This will
    		work even when checkClientArea is set to a non-null value.*/
    		virtual core::rect<s32> draw3DWindowBackground(IGUIElement* element,
    			bool drawTitleBar, video::SColor titleBarColor,
    			const core::rect<s32>& rect,
    			const core::rect<s32>* clip=0,
    			core::rect<s32>* checkClientArea=0) = 0;
    
    		//! draws a standard 3d menu pane
    		/** Used for drawing for menus and context menus.
    		It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and
    		EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details.
    		param element: Pointer to the element which wishes to draw this. This parameter
    		is usually not used by IGUISkin, but can be used for example by more complex
    		implementations to find out how to draw the part exactly.
    		param rect: Defining area where to draw.
    		param clip: Clip area. */
    		virtual void draw3DMenuPane(IGUIElement* element,
    			const core::rect<s32>& rect,
    			const core::rect<s32>* clip=0) = 0;
    
    		//! draws a standard 3d tool bar
    		/** Used for drawing for toolbars and menus.
    		param element: Pointer to the element which wishes to draw this. This parameter
    		is usually not used by IGUISkin, but can be used for example by more complex
    		implementations to find out how to draw the part exactly.
    		param rect: Defining area where to draw.
    		param clip: Clip area. */
    		virtual void draw3DToolBar(IGUIElement* element,
    			const core::rect<s32>& rect,
    			const core::rect<s32>* clip=0) = 0;
    
    		//! draws a tab button
    		/** Used for drawing for tab buttons on top of tabs.
    		param element: Pointer to the element which wishes to draw this. This parameter
    		is usually not used by IGUISkin, but can be used for example by more complex
    		implementations to find out how to draw the part exactly.
    		param active: Specifies if the tab is currently active.
    		param rect: Defining area where to draw.
    		param clip: Clip area.
    		param alignment Alignment of GUI element. */
    		virtual void draw3DTabButton(IGUIElement* element, bool active,
    			const core::rect<s32>& rect, const core::rect<s32>* clip=0, gui::EGUI_ALIGNMENT alignment=EGUIA_UPPERLEFT) = 0;
    
    		//! draws a tab control body
    		/** param element: Pointer to the element which wishes to draw this. This parameter
    		is usually not used by IGUISkin, but can be used for example by more complex
    		implementations to find out how to draw the part exactly.
    		param border: Specifies if the border should be drawn.
    		param background: Specifies if the background should be drawn.
    		param rect: Defining area where to draw.
    		param clip: Clip area.
    		param tabHeight Height of tab.
    		param alignment Alignment of GUI element. */
    		virtual void draw3DTabBody(IGUIElement* element, bool border, bool background,
    			const core::rect<s32>& rect, const core::rect<s32>* clip=0, s32 tabHeight=-1, gui::EGUI_ALIGNMENT alignment=EGUIA_UPPERLEFT ) = 0;
    
    		//! draws an icon, usually from the skin's sprite bank
    		/** param element: Pointer to the element which wishes to draw this icon.
    		This parameter is usually not used by IGUISkin, but can be used for example
    		by more complex implementations to find out how to draw the part exactly.
    		param icon: Specifies the icon to be drawn.
    		param position: The position to draw the icon
    		param starttime: The time at the start of the animation
    		param currenttime: The present time, used to calculate the frame number
    		param loop: Whether the animation should loop or not
    		param clip: Clip area. */
    		virtual void drawIcon(IGUIElement* element, EGUI_DEFAULT_ICON icon,
    			const core::position2di position, u32 starttime=0, u32 currenttime=0,
    			bool loop=false, const core::rect<s32>* clip=0) = 0;
    
    		//! draws a 2d rectangle.
    		/** param element: Pointer to the element which wishes to draw this icon.
    		This parameter is usually not used by IGUISkin, but can be used for example
    		by more complex implementations to find out how to draw the part exactly.
    		param color: Color of the rectangle to draw. The alpha component specifies how
    		transparent the rectangle will be.
    		param pos: Position of the rectangle.
    		param clip: Pointer to rectangle against which the rectangle will be clipped.
    		If the pointer is null, no clipping will be performed. */
    		virtual void draw2DRectangle(IGUIElement* element, const video::SColor &color,
    			const core::rect<s32>& pos, const core::rect<s32>* clip = 0) = 0;
    
    		//! get the type of this skin
    		virtual EGUI_SKIN_TYPE getType() const { return EGST_UNKNOWN; }
    	};

                 从这个接口的定义。能够看得出,这个接口基本的功能就是对一个GUI元素的颜色,外观进行设置和改变。从这里面也没有办法了解到更加具体的信息了,所以,接下来我们就从总体上来了解下整个Irrlicht引擎的GUI模块的架构。博主奉行的原则是“从高究竟的进行探究”。具体的细节问题等到我们实际使用某个对象的时候在来细究。

    毕竟像Irrlicht这种一个引擎。它的GUI模块中包括的各种控件对象就多达百个以上。不可能一下子所有了解的很透彻。



    GUI模块总体结构

                     在使用Irrlicht引擎的GUI模块的时候,总是会和一个名为IGUIEnvironment的接口打交道,这个似乎就是整个GUI模块向我们打开的大门,所以我们就从这里下手来一窥GUI模块的总体结构。

                  首先第一步。看官网上对这个接口的描写叙述,例如以下摘录:

                   "GUI Environment. Used as factory and manager of all other GUI elements"

                     这个接口作为GUI元素的工厂和管理器。嗯!从这个结构似乎我们就行窥视整个GUI模块的结构。

    好了,接下来老样子。直接看源代码,看下这个接口的定义:

    class IGUIEnvironment : public virtual IReferenceCounted
    {
    public:
    
    	//! Draws all gui elements by traversing the GUI environment starting at the root node.
    	virtual void drawAll() = 0;
            ...........
    
                    因为这个接口的定义实在太长,假设所有贴上来的话,会造成篇幅过大,这里仅仅进行摘录,用来点出接口类提供的主要接口等。

                  从这个类的申明能够发现,这个接口是继承至IReferenceCounter的。还记得前面我们具体的解说过Irrlicht引擎关于对象管理的方式吗?採用的就是这里的IReferenceCounted引用计数方法。

    这里的IGUIEnvironment接口也相同使用这种方法来进行管理。

                 接下来的是一个很重要的函数:drawAll()。这个函数会依次的遍历GUI环境中保存的全部的GUI元素,然后绘制它们。这也是我们在进行程序主循环的时候调用的方法。

                 除了这个功能之外剩下的函数各自是进行例如以下的工作:

    •              Focus焦点相关的操作
    •              Skin相关的操作
    •              Font相关的操作
    •              加入各种空间的操作
    •              GUI元素创建工厂的操作
    •              GUI序列化操作

                    上面这些就是这个接口大致提供的接口操作。

                  使用接口的方法优点就是将内部的管理方式对用户隔离。这样用户就不须要知道内部的管理方式,而非常easy的通过暴露出来的接口对各种模块进行操作了。

    可是博主的目的是为了学习这样的管理的方式。所以还须要在此基础上,更加深入的了解。

                  接口中不可能含有对内部对象管理方式的不论什么描写叙述,唯一的方法是要找到实际继承这个接口,而且被引擎实例化的对象。所以我们须要又一次回到最開始引擎初始化的函数其中来找寻实际的进行GUI元素管理的类。

                  对于Irrlicht引擎来说,整个引擎的启动就是通过一个createDevice函数来启动的,所以。我们就从这个函数開始,一直路由下去,直到找到终于创建GUI环境的那个代码块处,终于路由到例如以下的函数中:

    void CIrrDeviceStub::createGUIAndScene()
    {
    	#ifdef _IRR_COMPILE_WITH_GUI_
    	// create gui environment
    	GUIEnvironment = gui::createGUIEnvironment(FileSystem, VideoDriver, Operator);
    	#endif
    
    	// create Scene manager
    	SceneManager = scene::createSceneManager(VideoDriver, FileSystem, CursorControl, GUIEnvironment);
    
    	setEventReceiver(UserReceiver);
    }

                  也就是说,真实的对象是通过gui::createGUIEnvironment这个函数来创建的。我们继续跟进去。看下代码。例如以下所看到的:

    //! creates an GUI Environment
    IGUIEnvironment* createGUIEnvironment(io::IFileSystem* fs,
    					video::IVideoDriver* Driver,
    					IOSOperator* op)
    {
    	return new CGUIEnvironment(fs, Driver, op);
    }

                  哈哈!这里最终找到了最终实例化的GUI环境对象了。也就是说实际进行GUI管理的就是这个CGUIEnvironment类了。好了,以下我们全部的工作就是对这个对象进行分析了。



    CGUIEnvironment

                  在源码中找到这个类的定义,以下是这个类的定义:

    class CGUIEnvironment : public IGUIEnvironment, public IGUIElement
    {
    public:
    
    <span style="font-family:Microsoft YaHei;">          //All method from Super-class ......
    
    </span> private:
    
    	IGUIElement* getNextElement(bool reverse=false, bool group=false);
    
    	void updateHoveredElement(core::position2d<s32> mousePos);
    
    	void loadBuiltInFont();
    
    	struct SFont
    	{
    		io::SNamedPath NamedPath;
    		IGUIFont* Font;
    
    		bool operator < (const SFont& other) const
    		{
    			return (NamedPath < other.NamedPath);
    		}
    	};
    
    	struct SSpriteBank
    	{
    		io::SNamedPath NamedPath;
    		IGUISpriteBank* Bank;
    
    		bool operator < (const SSpriteBank& other) const
    		{
    			return (NamedPath < other.NamedPath);
    		}
    	};
    
    	struct SToolTip
    	{
    		IGUIStaticText* Element;
    		u32 LastTime;
    		u32 EnterTime;
    		u32 LaunchTime;
    		u32 RelaunchTime;
    	};
    
    	SToolTip ToolTip;
    
    	core::array<IGUIElementFactory*> GUIElementFactoryList;
    
    	core::array<SFont> Fonts;
    	core::array<SSpriteBank> Banks;
    	video::IVideoDriver* Driver;
    	IGUIElement* Hovered;
    	IGUIElement* HoveredNoSubelement;	// subelements replaced by their parent, so you only have 'real' elements here
    	IGUIElement* Focus;
    	core::position2d<s32> LastHoveredMousePos;
    	IGUISkin* CurrentSkin;
    	io::IFileSystem* FileSystem;
    	IEventReceiver* UserReceiver;
    	IOSOperator* Operator;
    	static const io::path DefaultFontName;
    };

                   这个类中有非常多的成员属性。主要有下面几个对象:

                 GUI元素创建工厂列表

                 字体数组

                 精灵组数组

                 悬浮元素对象的指针

                 悬浮对象的父元素指针

                 焦点元素指针

                 上一次鼠标悬浮位置

                 当前GUI元素的Skin对象指针

                 。。。

                 这里须要注意一个元素的两种状态。各自是Hovered和Focus状态。

    Hovered状态即为悬浮状态,这样的状态是仅仅在有鼠标设备的机器上才会实用,即当鼠标移动到某个GUI元素上时可是没有按下的这样的“悬而未决”的状态。称之为悬浮状态。另外一个Focus状态,有过编程经验的都知道,当点击某个GUI元素的时候,这个元素就具有了焦点。

                可是读者啊,你们有诶有发现,这个类里面好像没有直接保存那些被创建的GUI元素啊?这些GUI元素究竟被放在了什么地方了?在这个类里面好像也仅仅有GUI元素创建工厂列表这个属性可能会保存。所以,根据这个思路,我们来看下GUI元素创建工厂的内容。



    IGUIElementFactory

                  首先,一看到这个类前面有个“I”,我们就应该知道这个类是一个接口,里面仅仅会含有一些操作。那么我们就先来了解下一个GUI元素工厂有哪些操作吧。

                相同的方法,先来看下官网上关于这个接口的描写叙述,摘录例如以下:

                “Interface making it possible to dynamically create GUI elements”

                   用于动态创建GUI元素的接口。

    好了,依旧是那么的简单粗暴。打开源码,看下接口的定义吧:

    namespace gui
    {
    	class IGUIElement;
    
    	//! Interface making it possible to dynamically create GUI elements
    	/** To be able to add custom elements to Irrlicht and to make it possible for the
    	scene manager to save and load them, simply implement this interface and register it
    	in your gui environment via IGUIEnvironment::registerGUIElementFactory.
    	Note: When implementing your own element factory, don't call IGUIEnvironment::grab() to
    	increase the reference counter of the environment. This is not necessary because the
    	it will grab() the factory anyway, and otherwise cyclic references will be created.
    	*/
    	class IGUIElementFactory : public virtual IReferenceCounted
    	{
    	public:
    
    		//! adds an element to the gui environment based on its type id
    		/** param type: Type of the element to add.
    		param parent: Parent scene node of the new element, can be null to add to the root.
    		
    eturn Pointer to the new element or null if not successful. */
    		virtual IGUIElement* addGUIElement(EGUI_ELEMENT_TYPE type, IGUIElement* parent=0) = 0;
    
    		//! adds a GUI element to the GUI Environment based on its type name
    		/** param typeName: Type name of the element to add.
    		param parent: Parent scene node of the new element, can be null to add it to the root.
    		
    eturn Pointer to the new element or null if not successful. */
    		virtual IGUIElement* addGUIElement(const c8* typeName, IGUIElement* parent=0) = 0;
    
    		//! Get amount of GUI element types this factory is able to create
    		virtual s32 getCreatableGUIElementTypeCount() const = 0;
    
    		//! Get type of a createable element type
    		/** param idx: Index of the element type in this factory. Must be a value between 0 and
    		getCreatableGUIElementTypeCount() */
    		virtual EGUI_ELEMENT_TYPE getCreateableGUIElementType(s32 idx) const = 0;
    
    		//! Get type name of a createable GUI element type by index
    		/** param idx: Index of the type in this factory. Must be a value between 0 and
    		getCreatableGUIElementTypeCount() */
    		virtual const c8* getCreateableGUIElementTypeName(s32 idx) const = 0;
    
    		//! returns type name of a createable GUI element
    		/** param type: Type of GUI element.
    		
    eturn Name of the type if this factory can create the type, otherwise 0. */
    		virtual const c8* getCreateableGUIElementTypeName(EGUI_ELEMENT_TYPE type) const = 0;
    	};
    
    
    } // end namespace gui

                这个接口不是非常复杂。提供了诸如:

                addGUIElement  -- 添加特定类型的GUI元素

                getCreatableGUIElementTypeCount -- 获取当前工厂可以创建的元素类型的数目

                getCreatableGUIElementType -- 获取指定下标相应的元素类型

                getCreatableGUIElementTypeName -- 获取指定下标相应的元素类型名称

                getCreatableGUIElementTypeName -- 获取指定元素类型的元素类型名称



    Oops!

                唔!

    !好像和GUI元素的对象管理没有半毛钱关系啊。

    在博主找到这里的时候,心都凉了!

    忙了半天,却发现还没有找到对象的管理模式。这里博主突然想到。它对对象的管理方式可能不是通过一个数组或者某个详细的容器来包括全部的已经被创建的GUI元素。可能类似于我们在编写Octree场景管理的时候,仅仅在管理器里面保存一个根节点的指针,然后通过这个指针来进行一次的遍历,也就是说可能是使用树状的结构来进行保存的。

                想到这里,博主兴奋不已。

    由于博主突然发如今CGUIEnvironment接口中有一个成员函数,名为getRootElement。这个函数明显就是对管理器中包括元素进行遍历的一个函数嘛。!!我们能够从这个函数下手,来窥探它究竟是怎样组织和管理GUI元素的。以下先来看下这个函数的实现:

    //! Returns the root gui element.
    IGUIElement* CGUIEnvironment::getRootGUIElement()
    {
        return this;
    }
                这个函数,十分的简洁,直接返回的是当前管理器对象的指针。这非常奇怪不是吗?可是,我们在来看下CGUIEnvironment的定义就会发现,这个类实际上也是继承至IGUIElement这个类的。也就是说。这个管理器本身就作为了整个管理结构的根节点。那么究竟是怎样通过这个根节点来进行GUI元素的訪问的了?

               哎,写到这里的时候,博主突然发如今探究的过程中少了非常重要的一个元素,那就是对象管理器所管理的对象本身。

    博主一直专注于管理器。却忽视了管理器所管理的对象。接下来,我们就来看下这个管理对象究竟是什么。从上面的种种迹象能够判断出。管理器管理的实际上就是IGUIElement这个接口。也就是说,全部的GUI元素都将从这个接口继承而来。好了,以下就来一窥这个对象的究竟吧。!!


    IGUIElement

                老规矩,先来看下官网对它的描写叙述,摘录例如以下:

              "Base class of all GUI elements"

                全部GUI元素的基类。

              在来看下它的申明吧:

    //! Base class of all GUI elements.
    class IGUIElement : public virtual io::IAttributeExchangingObject, public IEventReceiver
    {
    public:
    
    	<span style="font-family:Microsoft YaHei;">...................................</span>
    protected:
    
    	//! List of all children of this element
    	core::list<IGUIElement*> Children;
    
    	//! Pointer to the parent
    	IGUIElement* Parent;
    
    	//! relative rect of element
    	core::rect<s32> RelativeRect;
    
    	//! absolute rect of element
    	core::rect<s32> AbsoluteRect;
    
    	//! absolute clipping rect of element
    	core::rect<s32> AbsoluteClippingRect;
    
    	//! the rectangle the element would prefer to be,
    	//! if it was not constrained by parent or max/min size
    	core::rect<s32> DesiredRect;
    
    	//! for calculating the difference when resizing parent
    	core::rect<s32> LastParentRect;
    
    	//! relative scale of the element inside its parent
    	core::rect<f32> ScaleRect;
    
    	//! maximum and minimum size of the element
    	core::dimension2du MaxSize, MinSize;
    
    	//! is visible?
    	bool IsVisible;
    
    	//! is enabled?
    	bool IsEnabled;
    
    	//! is a part of a larger whole and should not be serialized?

    bool IsSubElement; //! does this element ignore its parent's clipping rectangle? bool NoClip; //! caption core::stringw Text; //! tooltip core::stringw ToolTipText; //! users can set this for identificating the element by string core::stringc Name; //! users can set this for identificating the element by integer s32 ID; //! tab stop like in windows bool IsTabStop; //! tab order s32 TabOrder; //! tab groups are containers like windows, use ctrl+tab to navigate bool IsTabGroup; //! tells the element how to act when its parent is resized EGUI_ALIGNMENT AlignLeft, AlignRight, AlignTop, AlignBottom; //! GUI Environment IGUIEnvironment* Environment; //! type of element EGUI_ELEMENT_TYPE Type; };

                 老规矩。先看下这个类的成员,将操作函数省去。

    一眼看过去。wow!成员属性很的多。所以以下就简单的做下归纳好了:

                 子元素的列表

                 父元素指针

                 尺寸位置相关的属性

                 是否可视

                   是否启用

                 是否是子元素

                 是否忽视父节点的裁剪

                 文字显示相关属性

                 ID

                 Tab Group相关属性

                 从这个属性列表中对于对象管理来说最重要的就是前面两个属性了:

                 子元素列表和父元素指针。

                 也就是说。在Irrlicht的GUI模块中。对GUI元素的管理是採用这样的多叉树的树状结构进行的。

    类似于3D引擎中进行场景管理的场景图方法。



    总结

                  今天。我们对Irrlicht引擎的GUI模块总体结构进行了了解。总的来说,全部的GUI元素都继承至IGUIElement这个接口。

    而我们是通过IGUIEnvironment这个接口来操作总体的GUI。对于GUI元素的管理方式,Irrlicht採用的是多叉树的方式进行管理。所以对GUI元素的操作就变成了对一颗多叉树进行的操作,这个树的根节点就是GUI管理器本身,能够通过getRootElement来得到这个Element。剩下的内容就是Irrlicht引擎的GUI模块支持了哪些控件,以及对元素的訪问有哪些功能,哪些属性了,这些东西都和详细的控件相关。等到我们须要实际的编写一个控件的时候。我们在来深入的了解下Irrlicht中每一个控件是怎样实现的。

                 好了,今天到这里就结束了,感谢大家的一直关注!!


                  


  • 相关阅读:
    洛谷P3569 [POI2014]KAR-Cards(线段树)
    洛谷P3295 [SCOI2016]萌萌哒(倍增+并查集)
    GFS分布式文件系统脚本
    源码安装apache脚本
    python如何安装cv2
    从npz文件中读取图片并显示的小例子
    读取npz,并显示图像
    Python中.npz文件的读取
    导出MNIST的数据集
    Keras下载的数据集以及预训练模型保存在哪里
  • 原文地址:https://www.cnblogs.com/brucemengbm/p/7000420.html
Copyright © 2011-2022 走看看