在 C 中设计一个简单的 GUI 框架

Designing a simple GUI framework in C

我需要为学校项目设计一个非常简单的 GUI框架,它需要支持以下controls/widgets:Window、面板、图像、标签和按钮。

我考虑了以下解决方案,利用 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。您甚至可以免费获得私有方法:它们只是静态函数。