在 C 中设计一个简单的 GUI 框架
Designing a simple GUI framework in C
我需要为学校项目设计一个非常简单的 GUI框架,它需要支持以下controls/widgets:Window、面板、图像、标签和按钮。
- 我想到的第一个问题是
Window
是否应该是一个控件。我想应该可以。
- 我们有
Window
和 Panel
可以包含其他控件。 Button
、Label
和 Image
,不能。所以我们需要两种基本类型的控件;一个是容器,另一个不是(我已经看到在 Gtk 实现中后者也是一个容器,但只能包含一个 child。它被称为 GtkBin。我认为它是一个我的简单项目的开销。
- 我遇到的第三个问题是:我需要遍历UI树(用于绘图)但是由于 C 中没有多态性机制,它是变得有些问题。
我考虑了以下解决方案,利用 union
。基本上我需要一些函数来将泛型 Control
转换为它的实际类型。
typedef struct button {
char *image_path;
} Button;
typedef struct control_node {
Control *node;
struct control_node *next;
} ControlNode;
typedef struct panel {
ControlNode *children;
} Panel;
typedef union control_data {
Panel panel;
Button button;
} ControlData;
typedef struct control {
int x;
int y;
int type;
ControlData *data;
} Control;
所以我想了解您对我提出的问题的看法以及对我当前策略的看法(我 不是 寻找实施,而是 thoughts/ideas等)
谢谢。
您应该在每个 "GUI class" 的布局中放置一个 void *
第一件事,它将指向为每个类型手动实现的 v-tables。每个类型的 v-table 中的第一个函数可以是一个,为每个类型返回一个唯一的整数(或者只是一个唯一的整数),这样你就可以分辨什么是什么并实现了一些类型安全。虽然没有必要,但您可以使用 v-table 指针值来确定类型(因为它将是唯一的),这将节省内存,但不太明显。在对象销毁中具有虚拟性也很重要,因此处理每个唯一类型的内部结构且其在 v-table 中的位置保持一致的析构函数也是必须的。虽然对于您的任务来说这实际上是可以避免的,但对于生产来说这是必须的。
至于真正的树,这应该是建立在一个简单的父子关系上,围绕着叶子和节点的接口,叶子只持有一个指向父节点的指针,节点还包括一个动态列表类型你也必须落实。那么遍历树就相当简单了。
您可以通过使用 "interfaces" 结构和聚合来代替继承来稍微模块化,但是与单独实现每种类型相比,只要您保持 v -table 指向类型布局中的第一个对象,以便您知道您正在使用什么以及如何使用它。其余部分的布局对于遵循任何准则都不是那么重要,只要您知道类型并将指针转换为适当的结构类型即可。但是因为每个对象都有一个父对象,甚至是根,可以通过它的父对象为 0 来识别,你真的应该把父对象放在 v-table 指针之后的第二个,这样你就可以避免在你所有的时候进行强制转换需要访问的是父级。
您可以从中受益的另一件事是使用信号,因为在每个按钮中都有另一个指针,因此您可以将其分配给一个要在 "click" 按钮时执行的函数。
这里有两个不同的问题:GUI 的概念和它在 C 中的实现。如果 OO 模型中的概念更简单,那么就这样写。由于第一个 C++ 编译器只是 C 代码的预处理器,C 可以使用 vtables 实现 OO 建模。
首先明确定义你的对象是什么(classes):哪些可以是容器,哪些是可绘制对象,可以对鼠标或键盘事件作出反应。它们的属性是什么:文本、x-y 坐标、位图,可能还有 z 坐标。在该阶段结束时,您应该有一个 classes 的层次结构,其中包含属性和方法,以及子 classes 中可能的覆盖 - 如果您已经了解它,UML 可以帮助这部分。
其次,您必须在 C 中实现它。别担心。您不会免费获得复制和移动构造函数的语法糖,也不会得到 public、私有属性和受保护属性的概念,但是 struct
可以包含子对象、指向其他结构的指针和指向函数的指针.
你只需要一个词典来翻译 C 中的 OO 工具:
- class: 结构
- class的字段:结构
的字段
- class 的方法:第一个属性是指向结构实例的指针的函数
- 虚方法:指向方法的指针
- 构造函数和析构函数:必须明确调用的特殊方法 - 如果结构超出范围则不会自动销毁
一个非 final class(包含虚拟方法)应该包含一个 VTABLE,它只是一个指向虚拟方法的指针数组。这个 VTABLE 应该是结构的第一个元素,以简化 class 指针转换。派生的 class 首先包含它自己的 vtable(如果它不是最终的),然后是它的属性,然后是它的父对象的实例。从父指针到派生指针的转换只是添加偏移量的问题。如果需要多重继承,则在结构中第一个之后添加其他父级。虚拟继承会稍微难以实现,因为它涉及 VTABLE 中的指针。同样,如果您需要 class 感知对象,只需在 VTABLE 中添加一个常量。但我不认为你需要所有这些来满足你的要求。
因此,如果您真的想要更难的部分,请多看两眼:多重继承和 class 感知对象,然后只需在其 .h + .c 文件中实现每个 class。您甚至可以免费获得私有方法:它们只是静态函数。
我需要为学校项目设计一个非常简单的 GUI框架,它需要支持以下controls/widgets:Window、面板、图像、标签和按钮。
- 我想到的第一个问题是
Window
是否应该是一个控件。我想应该可以。 - 我们有
Window
和Panel
可以包含其他控件。Button
、Label
和Image
,不能。所以我们需要两种基本类型的控件;一个是容器,另一个不是(我已经看到在 Gtk 实现中后者也是一个容器,但只能包含一个 child。它被称为 GtkBin。我认为它是一个我的简单项目的开销。 - 我遇到的第三个问题是:我需要遍历UI树(用于绘图)但是由于 C 中没有多态性机制,它是变得有些问题。
我考虑了以下解决方案,利用 union
。基本上我需要一些函数来将泛型 Control
转换为它的实际类型。
typedef struct button {
char *image_path;
} Button;
typedef struct control_node {
Control *node;
struct control_node *next;
} ControlNode;
typedef struct panel {
ControlNode *children;
} Panel;
typedef union control_data {
Panel panel;
Button button;
} ControlData;
typedef struct control {
int x;
int y;
int type;
ControlData *data;
} Control;
所以我想了解您对我提出的问题的看法以及对我当前策略的看法(我 不是 寻找实施,而是 thoughts/ideas等)
谢谢。
您应该在每个 "GUI class" 的布局中放置一个 void *
第一件事,它将指向为每个类型手动实现的 v-tables。每个类型的 v-table 中的第一个函数可以是一个,为每个类型返回一个唯一的整数(或者只是一个唯一的整数),这样你就可以分辨什么是什么并实现了一些类型安全。虽然没有必要,但您可以使用 v-table 指针值来确定类型(因为它将是唯一的),这将节省内存,但不太明显。在对象销毁中具有虚拟性也很重要,因此处理每个唯一类型的内部结构且其在 v-table 中的位置保持一致的析构函数也是必须的。虽然对于您的任务来说这实际上是可以避免的,但对于生产来说这是必须的。
至于真正的树,这应该是建立在一个简单的父子关系上,围绕着叶子和节点的接口,叶子只持有一个指向父节点的指针,节点还包括一个动态列表类型你也必须落实。那么遍历树就相当简单了。
您可以通过使用 "interfaces" 结构和聚合来代替继承来稍微模块化,但是与单独实现每种类型相比,只要您保持 v -table 指向类型布局中的第一个对象,以便您知道您正在使用什么以及如何使用它。其余部分的布局对于遵循任何准则都不是那么重要,只要您知道类型并将指针转换为适当的结构类型即可。但是因为每个对象都有一个父对象,甚至是根,可以通过它的父对象为 0 来识别,你真的应该把父对象放在 v-table 指针之后的第二个,这样你就可以避免在你所有的时候进行强制转换需要访问的是父级。
您可以从中受益的另一件事是使用信号,因为在每个按钮中都有另一个指针,因此您可以将其分配给一个要在 "click" 按钮时执行的函数。
这里有两个不同的问题:GUI 的概念和它在 C 中的实现。如果 OO 模型中的概念更简单,那么就这样写。由于第一个 C++ 编译器只是 C 代码的预处理器,C 可以使用 vtables 实现 OO 建模。
首先明确定义你的对象是什么(classes):哪些可以是容器,哪些是可绘制对象,可以对鼠标或键盘事件作出反应。它们的属性是什么:文本、x-y 坐标、位图,可能还有 z 坐标。在该阶段结束时,您应该有一个 classes 的层次结构,其中包含属性和方法,以及子 classes 中可能的覆盖 - 如果您已经了解它,UML 可以帮助这部分。
其次,您必须在 C 中实现它。别担心。您不会免费获得复制和移动构造函数的语法糖,也不会得到 public、私有属性和受保护属性的概念,但是 struct
可以包含子对象、指向其他结构的指针和指向函数的指针.
你只需要一个词典来翻译 C 中的 OO 工具:
- class: 结构
- class的字段:结构 的字段
- class 的方法:第一个属性是指向结构实例的指针的函数
- 虚方法:指向方法的指针
- 构造函数和析构函数:必须明确调用的特殊方法 - 如果结构超出范围则不会自动销毁
一个非 final class(包含虚拟方法)应该包含一个 VTABLE,它只是一个指向虚拟方法的指针数组。这个 VTABLE 应该是结构的第一个元素,以简化 class 指针转换。派生的 class 首先包含它自己的 vtable(如果它不是最终的),然后是它的属性,然后是它的父对象的实例。从父指针到派生指针的转换只是添加偏移量的问题。如果需要多重继承,则在结构中第一个之后添加其他父级。虚拟继承会稍微难以实现,因为它涉及 VTABLE 中的指针。同样,如果您需要 class 感知对象,只需在 VTABLE 中添加一个常量。但我不认为你需要所有这些来满足你的要求。
因此,如果您真的想要更难的部分,请多看两眼:多重继承和 class 感知对象,然后只需在其 .h + .c 文件中实现每个 class。您甚至可以免费获得私有方法:它们只是静态函数。