虽说以前我也短暂尝试过GTK,但它对我来说还是很陌生的。在此之前,我在用户界面上的大部分经验都来自于React应用程序的构建。从React到GTK的过渡带来了一些挑战,其中多数是小部件原理上的差异造成的。用Rust写GTK是尤其困难的事情,因为Rust强制执行一些额外的规则来防止内存管理错误,并避免在线程上下文中执行不安全的操作。
在本文中,我将主要讨论如何将React的理念应用到GTK中,并重点介绍一些使GTK符合Rust规则所必需的技巧。Rust制订了一些不好对付的强制规则,这些规则对于大多数开发人员来说都是陌生的;规则主要涉及值的共享方式,但在可变性方面也有严格的限制。我将在本文中遇到这些场景时指出它们。
一些术语的注释:
所有组件中最简单的,就像React组件一样是一小组小部件,这些小部件创建后就永远不会更新。这可以简单地实现为返回一个GTK小部件的函数。
pubfndate_c(date:&chrono::Date
具有内部小部件状态的组件肯定要复杂得多,但仍然可以实现为一个返回GTK小部件的函数。调用方可以直接从返回的GTK小部件中读取数据;在调用方提供一个回调,并且组件代码写明了何时调用回调时,这种模式可以说是最有效的。
2020-01-31:事实证明,我在本节的代码中犯了一些大错。我需要对其进行相当大的修改,以更有效地处理组件更新,并在一个GTK回调中更改组件状态。
当我使用上面提到的这种简单组件构建应用程序时,我将它们组合到一些更复杂的组件中,这些组件具有多份逻辑上互相归属,但机制上可以在各个子组件中编辑的数据。为此,我在设置内部状态时会独立于子组件的状态。
所幸我一般来说还是可以将其实现为一个函数。
回顾一下,在上面的函数中,我们有一段代码来克隆包含记录的Arc。该克隆将移至’duration_edit_c’的回调函数中(这意味着该回调函数现在拥有这个克隆)。在这个回调函数中将可变地借用记录、更新记录、克隆数据并将其传递给’on_update’,然后将在该块末尾自动删除写锁定。
最后,第四个模式涵盖了需要响应系统更改的所有组件。用React术语来说,这意味着属性可能从Redux更改。
在较高的层级上,我们需要一个’struct’来跟踪在给定新数据时可能会更新的所有可视组件,以及一个将处理这些更新并返回根级小部件的’render’函数。
在这里我用自己的History组件举例。
structHistoryComponent{widget:gtk::Box,history_box:gtk::Box,}pubstructHistory{component:Option
pubfnrender(&mutself,range:DateRange,records:Vec
就这些了。经过数周的学习,在理解如何编写GTK的过程中我发现了四种高级模式。我觉得这些模式已经很完备了。
在撰写本文的整个过程中,我也对我的组件做了大量修改、重构和简化。我想这四种模式将帮助我进一步改进我的应用程序,同时我也希望在继续学习的过程中能学到更多内容。