# RT-Thread GUI Object ## RT-Thread GUI 物件架構 在 RTGUI 中,最小的物件為 widget,再來是 window,window 也是一個 widget;而每個 widget 也是一個 object,這是為了仿造 C++ 的物件導向所設計的,相同的概念我們在 RT-Thread 中已經看過許多次了,在 GUI engine 中也是相同的設計,其中在 object 結構中又串在 type 的結構上,type 中定義了兩個函式:`constructor` 與 `destructor`,在 C++ 的 class 中,常使用 `init` 函式來初始化新建的 class,這裡的 `constructor` 與 `destructor` 即用來初始化新建立的物件,及在刪除物件時,釋放該釋放的記憶體。[^1] [^1]:[RTGUI粗讲(个人见解篇之三、RTGUI WIDGET (2))](https://blog.csdn.net/xuzhenglim/article/details/11883351) --- ## 結構 :::success **File:** rtgui_object.h ::: ```c=121 /* rtgui base object */ struct rtgui_object { /* object type */ const rtgui_type_t *type; /* the event handler */ rtgui_event_handler_ptr event_handler; enum rtgui_object_flag flag; rt_uint32_t id; }; ``` ```c=52 /* rtgui type structure */ struct rtgui_type { /* type name */ char *name; /* parent type link */ const struct rtgui_type *parent; /* constructor and destructor */ rtgui_constructor_t constructor; rtgui_destructor_t destructor; /* size of type */ int size; }; ``` --- ## 定義物件類型 RTGUI 設計了一個巨集函數來定義不同的物件,如下: ```c=76 #define DEFINE_CLASS_TYPE(type, name, parent, constructor, destructor, size) \ const struct rtgui_type _rtgui_##type = { \ name, \ parent, \ RTGUI_CONSTRUCTOR(constructor), \ RTGUI_DESTRUCTOR(destructor), \ size }; \ const rtgui_type_t *_rtgui_##type##_get_type(void) { return &_rtgui_##type; } \ RTM_EXPORT(_rtgui_##type##_get_type) ``` `##` 為連字符,在[RT-Thread 理解 RTM_EXPORT](/rt-thread-RTM-EXPORT)裡有提過了,基本上就是填入值進去結構體 --- ## 建立物件 :::success **File:** rtgui_object.c ::: ### `rtgui_object_create` | 功能 | 回傳值 | | --- | ------ | | 建立物件 | 物件指標 | | `*object_type` | | -------------- | | 要建立的物件種類 | ```c=110 /** * @brief Creates a new object: it calls the corresponding constructors * (from the constructor of the base class to the constructor of the more * derived class) and then sets the values of the given properties * * @param object_type the type of object to create * @return the created object */ rtgui_object_t *rtgui_object_create(const rtgui_type_t *object_type) { rtgui_object_t *new_object; if (!object_type) return RT_NULL; new_object = rtgui_malloc(object_type->size); if (new_object == RT_NULL) return RT_NULL; #ifdef RTGUI_OBJECT_TRACE obj_info.objs_number ++; obj_info.allocated_size += object_type->size; if (obj_info.allocated_size > obj_info.max_allocated) obj_info.max_allocated = obj_info.allocated_size; #endif new_object->type = object_type; rtgui_type_object_construct(object_type, new_object); return new_object; } RTM_EXPORT(rtgui_object_create); ``` 建立物件相當簡單,透過欲建立的物件類型所定意義的 `construct` 函數來建立,其中 `rtgui_type_object_construct` 會呼叫正確的建立函式來初始化資料。 --- ### `rtgui_type_object_construct` | 功能 | 回傳值 | | --- | ------ | | 呼叫正確的 `construct` 函式來初始化物件 | void | | `*type` | `*object` | | ------- | --------- | | 欲初始化的物件類型 | 物件本體 | ```c=54 void rtgui_type_object_construct(const rtgui_type_t *type, rtgui_object_t *object) { /* construct from parent to children */ if (type->parent != RT_NULL) rtgui_type_object_construct(type->parent, object); if (type->constructor) type->constructor(object); } ``` 如果欲建立的物件類型在某一個物件類型的底下,如 window 之於 widget,則先呼叫在上層的 `construct`;接著呼叫自己的 `construct` 來完成建立的動作。 --- 再仔細的看一下 "object" 的 `construct` 函式,其動作為:填入 vaild 的旗標,並將 id 填入 object 的記憶體指標;以上動作在 `_rtgui_object_constructor` 完成 ### `_rtgui_object_constructor` | 功能 | 回傳值 | | --- | ------ | | "object" 建立函式 | void | | `*object` | | --------- | | 要建立的物件 | ```c=28 static void _rtgui_object_constructor(rtgui_object_t *object) { if (!object) return; object->flag = RTGUI_OBJECT_FLAG_VALID; object->id = (rt_uint32_t)object; } ``` --- ## 刪除物件 ### `rtgui_object_destroy` | 功能 | 回傳值 | | --- | ------ | | 刪除物件 | void | | `*object` | | --------- | | 要刪除的物件 | ```c=143 /** * @brief Destroys the object. * * The object destructors will be called in inherited type order. * * @param object the object to destroy */ void rtgui_object_destroy(rtgui_object_t *object) { if (!object || object->flag & RTGUI_OBJECT_FLAG_STATIC) return; #ifdef RTGUI_OBJECT_TRACE obj_info.objs_number --; obj_info.allocated_size -= object->type->size; #endif /* call destructor */ RT_ASSERT(object->type != RT_NULL); rtgui_type_destructors_call(object->type, object); /* release object */ rtgui_free(object); } RTM_EXPORT(rtgui_object_destroy); ``` 這裡一樣透過 `rtgui_type_destructors_call` 來呼叫正確的 `destruct` 函式,`destruct` 負責釋放該釋放的記憶體;最後透過 `rtgui_free` 釋放整個物件。`regui_free` 則簡單的呼叫 `rt_free` 釋放記憶體,我們在前幾篇文章有討論過了([mempool](/rt-mem#Code-free)、[memheap](/rt-memheap#釋放記憶體)、[small mem](/rt-small-mem#釋放記憶體)、[slab](/rt-slab#釋放記憶體)) --- ### `rtgui_type_destructors_call` | 功能 | 回傳值 | | --- | ------ | | 呼叫正確的 `destructor` 函式來清除物件 | void | | `*type` | `*object` | | ------- | --------- | | 欲清除的物件類型 | 物件本體 | ```c=64 void rtgui_type_destructors_call(const rtgui_type_t *type, rtgui_object_t *object) { /* destruct from children to parent */ if (type->destructor) type->destructor(object); if (type->parent) rtgui_type_destructors_call(type->parent, object); } ``` 同樣的如果此物件類型是在某個物件類型的底下,先呼叫上層的 `destruct`;接著呼叫自己的 `desturct` 完成清除的動作。 --- 最後來看一下 "object" 的刪除函式:填入 none 的旗標,並將物件種類設為 NULL;動作在 `_rtgui_object_destructor` 完成 ### `_rtgui_object_destructor` | 功能 | 回傳值 | | --- | ------ | | "object" 的刪除函式 | void | | `*object` | | --------- | | 欲清除的物件 | ```c=37 /* Destroys the object */ static void _rtgui_object_destructor(rtgui_object_t *object) { /* Any valid objest should both have valid flag _and_ valid type. Only use * flag is not enough because the chunk of memory may be reallocted to other * object and thus the flag will become valid. */ object->flag = RTGUI_OBJECT_FLAG_NONE; object->type = RT_NULL; } ``` ###### tags: `GUI` `RT-Thread` `Object`