Oberon/ETH Oberon/Tutorial/GadgetsIntro
这些教程页面由 André Fischer (afi) 编写,并由 Hannes Marais 提供编辑协助,托管在 ETHZ 并保留在 ETH 许可 下。相关内容可在系统中通过 Book.Tool 找到。扩展内容也可在 纸质版 上找到。一些教程页面在 WayBack 存档 中。
向您介绍名为部件的 Oberon System 3 图形用户界面 (GUI)。介绍了视觉部件和模型部件的概念。然后描述了面板、文档和桌面等部件。
预计时间:60 分钟。
部件是用于 Oberon System 3 的图形用户界面管理系统。它包含一组用于构建图形用户界面的最终用户对象。此构建过程在运行时完成,因此是最终用户对象。这些最终用户对象被称为部件。部件范围从典型的对话框元素(如 按钮 和 复选框)到更复杂的元素(如 文本 和绘图编辑器)。您不仅可以使用使用部件系统构建的应用程序,还可以使用提供的部件构建自己的图形用户界面。此外,如果提供的部件不能满足您的需求,您可以通过扩展现有部件或构建全新的部件来编写自己的部件。您还可以从其他应用程序中 借用 部件。
从技术上讲,部件是具有不同粒度的用户界面组件。例如,绘图中的线或图形是部件,就像绘图本身也是一个(复合)部件一样。稍后,我们将介绍术语 容器部件 来表达这一点。每个部件都知道一个定义明确的通信协议,使它能够作为具有独立功能的单元发挥作用,并且可以集成到任何为其提供通信协议的环境中。部件可以在文本中流动,可以集成到 面板(与其他系统中的窗口大致相当)中,或者用在页面布局系统中。由于它们是活动对象,因此它们在内部构建了编辑器,允许它们在出现的地方进行编辑或使用。每个部件实例的行为都具有其独特的方式。但是,它们都遵循一组对 Oberon 用户来说已经很熟悉的准则。最重要的是,它们响应某些 鼠标点击 和 键盘输入。
Oberon System 3 中有几个部件类。部件类决定了部件在系统中的作用、外观和行为。我们现在将回顾其中的一些,从一些更有趣的视觉部件开始,然后是更抽象的 模型部件。在 使用部件 中回顾了此 Oberon System 3 交付中提供的全部部件。其他重要的部件(如面板、文档和桌面)将在后面的章节中介绍。
视觉部件在您的屏幕上由一个图形符号表示,通常具有 3D 外观(见下面的按钮),用作您和计算机之间的通信媒介。此外,此类媒介在受到鼠标点击刺激时会表现出简单的行为。Oberon 引入了一种与计算机交互的新方式。
通过单击按钮上的中间鼠标键,可以按下并释放按钮。现在试试。按钮被按下,显示一个模拟 LED 的矩形红色标记,并在释放键时恢复到其原始状态。单击按钮会导致执行 Oberon 命令,如果已定义。标题应(最好)表示按钮的功能或操作。
或者,按钮可以定义为具有两种状态的切换按钮。在这种情况下,必须单击按钮两次才能循环遍历两种状态。
复选框与切换按钮非常相似,但没有标题,两个状态由是否存在复选标记来标识。尝试用鼠标中键单击。如果已定义,单击复选框将执行 Oberon 命令。
滑块
[edit | edit source]滑块用于根据拖动鼠标中键定位的旋钮的相对位置来设置(隐藏的)数值。最小值和最大值是滑块规范(也隐藏)的一部分。
如果已定义,当操作滑块时会执行 Oberon 命令。
列表
[edit | edit source]列表按字母顺序显示一组字符串,没有重复项,就像这里一样。或者,列表可以保持未排序,在这种情况下允许重复条目。与按钮或复选框相比,列表的控制和行为不再那么简单。
单击列表中的任何位置的左键将使列表获得焦点:左边缘将出现一条垂直线。获得焦点后,通过键入几个初始字符,可以在更长的排序列表中快速定位。列表将滚动,以便具有匹配初始字符的列表项位于第一行。按回车键将清除键入的单词。同样,获得焦点后,可以使用右键和中键组合单击将选定的文本复制到列表中。在排序列表中,新项目按字母顺序插入,并且是唯一的。在未排序列表中,新项目将插入列表的末尾。
单击列表项的右键将选中它。再次单击选定的项目将取消选中它。向上或向下拖动右键会将选择扩展到多个连续项目。如果需要,列表将自动向前或向后滚动。单击未选中的项目将开始新的选择。如果存在,还可以使用滚动条控制滚动。
如果已定义,单击列表项的中键将执行 Oberon 命令。
文本字段
[edit | edit source]文本字段允许编辑单行文本。焦点使用左键单击设置。文本字段随后进入本地编辑模式,并可以使用完整的 Oberon 文本编辑功能。一旦光标移出(例如,当按下 Esc 键)或按下回车键,本地编辑模式就会退出。这通过字段的 3D 方面的改变变得可见。
如果已定义,按下回车键将执行 Oberon 命令。
使用鼠标控制工具
[edit | edit source]工具的一个有趣且新颖的方面是,除了使用之外,它们还可以从创建开始到最终被用户显式锁定之前,改变其大小和位置。这与其他系统形成对比,在其他系统中,用户会收到锁定的用户界面,并且必须“接受或拒绝”。一个工具系统用户的大量时间都花在组织和构建新的用户界面上。在本节中,我们将解释如何在运行时进行工具的交互式组合。
基本鼠标键操作
[edit | edit source]可视工具的形状通常是矩形,只有少数例外。工具占据的矩形区域可以在概念上划分为一个控制区域,它位于内部边框上,宽度只有几个像素,以及一个活动区域。控制区域的宽度根据工具的大小和状态而有所不同。
当鼠标焦点位于这些区域中的一个或另一个区域时,工具会以不同的方式响应鼠标事件。
在活动区域中,工具通常遵循 Oberon 约定,或者与其保持一致;也就是说,如果您已经习惯使用鼠标控制文本,您将立即识别出相似之处,因此控制工具将很快成为小菜一碟。
■ _ _ 点键:设置插入符号,即新工具的插入点。将插入符号放在工具中称为使工具获得焦点。一次只能有一个工具获得焦点。
_ ■ _ 执行键:按下按钮,切换复选框(选中/未选中),移动滑块等。如果定义了 Cmd 属性,则执行该命令。
_ _ ■ 选择键:选择工具。选定的工具被覆盖在一个白色半透明图案中。
尝试使用这三个单键单击来验证自己。
将插入符号设置在面板中并执行命令Gadgets.Insert Clock ~。
应特别注意右键的使用。
- 可以依次选择位于同一容器中的多个工具。选定的工具组将成为选择。
- 按下该键并拖动会导致出现一个跨越矩形。完全包含在这个矩形中的所有工具都将被选中:该组也将成为选择。这类似于选择文本。
- 单击空白区域将清除选择。
- 单击选定的工具将取消选中它。
例外:某些工具只能在鼠标焦点位于控制区域中时才能被选中。这种情况适用于面板、名称板、文本字段和文本工具(除了左边的滚动条)。对于最后三个,在活动区域中,选择操作于包含的文本。
最后,记住这一点。
■ ■ ■ 抵消单击或组合单击。
复制和删除选择
[edit | edit source]□ _ ■ 选择工具并删除选择。
_ □ ■ 选择工具并将选择复制到插入符号。
■ □ _ 设置插入符号并将选择复制到插入符号。
练习使用此面板中的工具
选择通常是 Oberon 命令的目标,并根据以下说明对鼠标中键单击做出反应。
某些工具不会响应所有这些鼠标键单击。
移动和调整工具大小 - 中键
[edit | edit source]根据鼠标焦点位于控制区域中的位置,鼠标中键的解释会有所不同:边用于移动工具,角用于改变工具的大小。因此,将鼠标焦点放在:
_ ■ _ 边上并拖动中键会移动工具(鼠标指针将变成抓取的手)。移动仅限于当前容器,并且在尝试将工具完全移出其当前容器时可能会被拒绝。
_ ■ _ 角上并拖动中键会调整工具的大小(鼠标指针将变成一个带有指向索引的手)。
在这两种情况下,当释放键时,都会设置新的位置或大小。
练习使用此彩色面板中的三个工具调整大小和移动。
您可以轻松地验证控制区域的存在(或不存在),方法是缓慢地将鼠标移动到工具上,同时反复单击鼠标中键。如果工具不能如上所述调整大小或移动,则控制区域不存在。
只能使用以下描述的显式左键或右键组合单击移动到另一个容器。
移动和复制工具 - 使用中键进行拖放
[edit | edit source]当移动工具(即从其侧面抓取工具)时,如果在鼠标移动结束时使用
□ ■ _ 左键:将小部件移动到鼠标焦点所在的新容器,或者如果它保留在当前容器中,则将其置于重叠的小部件的前面。
_ ■ □ 右键:在鼠标焦点处插入小部件的副本(在同一容器或不同的容器中)。
使用此彩色面板中的三个小部件进行练习
将小部件从一个容器移动到另一个容器称为消费操作。新容器消费小部件,小部件从其旧位置移除。这不仅用于将对象从一个面板移动到另一个面板,还用于提供一般的拖放功能。消费者(称为接收者或接收方)可以以不同的方式解释放置事件:它可以将被消费方(称为发送者或发起者)作为后代吸收(就像大多数容器一样),或者它可以启动其他事件。例如,垃圾桶可以删除它消费的文件小部件,或者编译器可以编译源文本。一些(接收者)小部件可以在消费事件上执行用户定义的命令。图标小部件就是这样一种接收者。
移动和复制选择:另一种方法
[edit | edit source]当一个小部件被选中时,控制区域会扩展到该小部件覆盖的整个区域,并且它具有与边框相同的功能。
移动或复制选择存在另一种方法:在任何选定的小部件上按住鼠标中键并拖动选择到新位置。如果选择多个小部件,则会显示一个跨越所有小部件的矩形。左键或右键单击与拖放操作具有相同的含义。
小部件锁定
[edit | edit source]存在一个灵活的机制,允许用户锁定一个小部件,从而防止意外更改小部件。
一个锁定的容器,例如图标化器、笔记本或面板不能调整大小。当鼠标焦点位于控制区域的任何位置时,它可以用鼠标中键移动,并且可以在小部件的任何位置用右键移动单击进行选择。
大多数应用程序界面都处于锁定状态,但它们可以随时解锁以根据用户的需求或个人喜好进行定制。
模型小部件
[edit | edit source]模型小部件存储对应用程序有用的数据值。它的结构和行为非常简单,它不能在显示器上可视化自身。小部件系统只提供了一组有限的模型小部件,可以存储基本的 Oberon 数据类型,即 BOOLEAN、LONGINT、LONGREAL 和字符串(ARRAY 64 OF CHAR)。
为了可视化存储在模型小部件中的值,需要借助像上面介绍过的那些可视小部件。 这是众所周知的 Smalltalk 模型-视图-控制器框架(MVC)。模型小部件包含由可视小部件可视化的数据。
在实践中,几个可视小部件可以显示相同模型小部件的值 - 下面给出一个示例。每个视图确保它与其表示的模型保持一致。我们也说可视小部件链接到特定模型小部件。
模型小部件背后的理念是,它可以很容易地被 Oberon 程序操作。作为回报,模型的可视化将相应地更新,而应用程序程序不知道或有多少这样的链接可视小部件存在。这允许应用程序程序员将代码与对用户界面所做的更改隔离开来。
但是,您应该清楚地理解,许多可视小部件(前面描述的只是它们中的一小部分)是以这样的方式编写的,即使它们没有链接到模型小部件也可以正常工作。
布尔值
[edit | edit source]存储 BOOLEAN 值。它可以链接到按钮或复选框。
整数
[edit | edit source]存储 LONGINT 值。它可以链接到按钮、复选框、滑块或文本字段。
实数
[edit | edit source]存储 LONGREAL 值。它可以链接到滑块和文本字段。
字符串
[edit | edit source]存储最多 64 个字符的 ARRAY OF CHAR。它可以链接到滑块或文本字段。
一些具体的例子
[edit | edit source]单选按钮
[edit | edit source]这里有三个单选按钮。就像收音机或电视调谐器上的按钮一样,单击一个单选按钮只会选择几个(这里为 3 个)相互排斥的选项中的一个。当选择一个选项时,其关联的 LED 会亮起。
这种效果是通过将一组单选按钮链接到一个整数小部件来实现的。每个按钮都给定一个整数值的规格,当模型小部件具有该值时,该按钮将显示为按下状态。
复选框作为单选按钮
[edit | edit source]红色 | 绿色 | 蓝色 | |
选择一种颜色 |
这里还有另一种使用复选框的单选按钮形式。在这种模式下,复选标记被替换为矩形标记。
同样,这种效果是通过将一组复选框链接到一个整数小部件来实现的。每个复选框都给定一个整数值的规格,当模型小部件具有该值时,该框将被选中。
具有两个视图的模型小部件
[edit | edit source]这里是一个小部件星座的示例,其中滑块和文本字段都链接到同一个整数模型小部件。这两个可视小部件充当同一个不可见对象的视图。在幕后,事物的组织方式如下
更改滑块的位置会导致整数小部件相应地更新,这反过来会导致文本字段被告知其模型的值已更改。无需编写任何程序即可实现此效果。只需指定小部件及其链接即可。
现在,看看这个类似的示例,其中整数小部件的值由一个(非常简单的!)程序操作,该程序只能将该值递增或递减 1。您可以观察到,通过单击任何一个蓝色三角形。
通常,应用程序程序员会根据自己的需要添加自己的模型小部件。 这需要编程。 例如,构建存储多个数据值的复合模型通常很有用。 标准小部件集合中提供的唯一这样的复合模型是复数小部件,它存储复数的实部和虚部。 该小部件的源代码在模块Complex.Mod 中提供,其在应用程序中的使用示例在模块Lissajous.Mod 中给出。
面板是小部件的二维组织器。 它可以被看作一张纸,小部件以二维方式在其上组织。 包含在面板内的这些小部件被称为后代小部件或其后代,而面板本身被称为祖先,或者也称为这些后代的上下文。 将面板归类为容器小部件也是足够的。 由于面板本身就是一个小部件,因此面板可能会出现在面板内部,嵌套到任何深度。
面板还支持对后代小部件组的操作以及提供其后代最佳布局和对齐方式的操作。 一个捕捉网格 允许轻松放置后代。 这些可以对齐到相同的基线、高度、大小或宽度,或者垂直和水平组织。 面板上的操作可以从Gadgets.Panel 中的工具激活。 此面板是用于创建和操作小部件的用户界面。
后代按优先级组织,并且可能相互重叠。 后代通常在被移动后保持其当前优先级。 Oberon 命令将一个小部件放在其他小部件的后面或前面。 新插入的小部件始终放置在前面。 将一个小部件带到最前面的最简单方法是将其拾取并再次使用(中间 + 左键鼠标点击)。
面板中的鼠标事件,除了少数例外,与之前关于小部件的描述一致。 右键拖动以中间键点击结束将对所选内容进行浅拷贝,并将该拷贝复制到插入符号位置。
以下是处理方式不同的内容。
当面板未锁定时:
□ ■ □ 执行键:当鼠标焦点定位在其上时,由后代小部件识别,在活动区域的其他地方无效。
□ □ ■ 选择键:仅在鼠标焦点位于控制区域内时识别。
当面板锁定:
□ ■ □ 执行键:当鼠标焦点定位在其上时,由后代小部件识别,在活动区域的其他地方无效。
□ □ ■ 选择键:可以单击任何位置以选择它。
锁定的面板无法调整大小,并且当鼠标焦点位于控制区域的任何位置时,可以使用中间鼠标键移动它。
面板可以链接到模型小部件。 在这种情况下,面板不用作模型小部件的查看器,而是用作重要数据的占位符,这些数据通常由后代表示。
文件是命名的集合,它可以作为一个原子操作存储到磁盘和从磁盘加载。 看一下此文档左上角的名称牌:它的名称是“GadgetsIntro.Book”。 磁盘文件与文档的名称相同。
文件可以在众所周知、熟悉的 Oberon 查看器 系统中打开和显示。 此外,文件可以在我们所谓的“桌面”中插入(即打开),这将在下一章中解释。
文件通常以一个菜单栏的形式呈现自身,其中包含与该文件相关的常用命令,例如“关闭”、“搜索”、“存储”等,以及一个名称牌。 此外观对 Oberon 用户来说是熟悉的。 或者,文件可以在没有菜单栏的情况下插入到桌面上。
文件具有一个主要类型,该类型由它们包含的根框架决定。 例如,存在文本和面板文件类型。 文件可能包含任何类型的其他子框架。 每个文件都存储在一个文件中,并且当仅知道文件名时,可以由文件管理器从磁盘重建/加载。 文件是自包含的,并且了解有关自身足够的信息以加载、存储和打印。 主要优点是用户无需知道打开特定文件需要哪个应用程序程序。
文件类型由所谓的文档 New 过程决定,该过程在打开新文件时(见下文)也记录在文件中,当文件被存储时。 这些信息在打开文件时再次使用。 这是一个“文档 New 过程”到“文档类型”的对应表
TextDocs.NewDoc 文本文件 PanelDocs.NewDoc 面板文件
Desktops.OpenDoc "("documentNewProcedure")" 打开一个空的未命名文件,其类型为指定类型,并将该文件插入桌面。
试试这个 Desktops.OpenDoc (PanelDocs.NewDoc) 。 注意名称牌:它是空的。
Desktops.OpenDoc fileName 打开文件fileName。 如果之前已存储具有该名称的文件,则会加载该文件,并将文件插入到显示空间中。 否则,将创建一个特定类型的空文件。 类型由文件名后缀隐式决定
.Text 文本文件 .Panel 面板文件 .Pict 图片文件 其他任何内容 文本文件
Desktops.OpenDoc fileName "("documentNewProcedure")" 执行与上述操作相同的功能,并尝试(并非总是成功)将原始文件转换为新类型。
Desktops.InsertDoc fileName 打开文件fileName。 如果之前已存储具有该名称的文件,则会加载该文件,并将文件插入到显示空间中的插入符号处,没有菜单栏。 否则,将打开一个空文件。 文件类型由之前为 Desktops.OpenDoc 描述的规则决定。
Desktops.ReplaceDoc fileName 用另一个文档fileName替换执行命令的文档。如果之前已存储具有该名称的文档,则加载该文档并将其替换为当前文档。否则,将打开一个空文档。文档类型由之前为 Desktops.OpenDoc 描述的规则确定。文档将就地切换。
应用示例:此命令经常用于系统提供的工具文档中,例如 Desktops.Tool、Programming.Tool 等。该命令在超文本链接中使用,例如 [桌面]、[应用程序] 等。
Desktops.StoreDoc * 将标记的文档存储在一个文件中,其文件名取自文档的名称板。如果从文档本身执行,则执行命令的文档是隐含的。然后,文档不必被标记,* 参数也不需要。该文件存储在当前目录中。如果该名称的文件已经存在,则将其重命名:将后缀 '.Bak' 附加到其原始名称。
Desktops.PrintDoc printerName * 在指定的打印机上打印标记的文档。
在 DOS 版本中,您可以选择打印机服务器的名称或 LPT1、LPT2 等。
在 Oberon for Windows 中,printerName 参数将被忽略,并使用在 Windows 打印管理器中定义的默认打印机来打印文档。此外,在 Oberon for Windows 中,打印由反向视频鼠标指针指示,直到文档被放置到打印机队列中。
在 MacOberon 版本中,您可以选择打印机名称 QuickDraw、PostScript 和 EPS。QuickDraw 打印到在选择器中选择的打印机,PostScript 生成一个 PostScript 文件,EPS 生成一个封装的 PostScript 文件。
如果从文档本身执行,则执行命令的文档是隐含的。然后,文档不必被标记。
Desktops.Recall 在执行命令的桌面上重新打开最后一个关闭的文档。文档以关闭时的状态打开,并放置在中心,但如果首先设置了星形标记,则标记焦点将确定文档的左上角。
如前所述,文档充当其包含的主部件的“包装器”。为了区分主部件的选择和包装器的选择,使用了两阶段选择。在主部件的控制区域中单击鼠标右键将选择它(以白色显示),而第二次单击将选择文档(以蓝色显示);也就是说,部件以从内到外的顺序选择,并通过其颜色识别。
桌面是一个命名的工作台面,文档和其他各种部件以二维方式排列,并包含重叠的对象。请查看此桌面左上角的名称板:它的名称很可能是“Oberon.Desk”。桌面可以作为一个原子操作存储到磁盘并从磁盘加载。磁盘文件与桌面具有相同的名称。这当然会让您想起之前关于文档的说明。
当桌面被存储时,只记录工作台面的布局:桌面的文档没有存储,这些文档必须由用户显式存储。稍后,相同的桌面可以从记录的信息中重新加载和重新创建。
这样,用户就可以创建许多不同的工作环境,这些环境适合手头的任务和可用的显示分辨率。同样,多个用户可以共享同一台计算机并打开他们个性化的桌面。像任何其他文件一样,桌面可以移植到其他计算机。
Desktops.Open fileName 打开一个桌面fileName。如果之前已存储具有该名称的桌面,则加载它并将其插入显示空间。否则,将创建一个新的默认桌面。通常,Gadget 使用与 Oberon 的Edit.Open 相同的策略来放置桌面,以放置查看器,即在用户轨道中。但是,您可以覆盖此首选项,并通过放置星形标记来指示桌面应出现在哪里。桌面放置在指针的轨道中,其顶部边缘将与指针的高度一致。
桌面显示为一个没有菜单栏的 Oberon 查看器。虽然菜单栏不存在,但它可以调整大小:将鼠标焦点放置在顶部的一个狭窄的不可见区域,然后按左键将导致一条粗蓝色的水平线出现,告诉您已经进入了跟踪模式。拖动左键将确定轨道中新的顶部位置。释放按键后,顶部边缘将移动到鼠标焦点的高度,桌面内容将重新绘制。移动范围与 Oberon 查看器的移动范围相同。
如果在拖动过程中中间鼠标键被快速点击,则桌面可以放置在显示屏上的任何位置。特别是,它可以跨轨道边界重新定位。当然,这只有在混合(Oberon - Gadget)显示布局中才有可能。
尝试 Desktops.Open YourNewDT。
您刚刚意识到显示表面可能一次包含多个桌面:桌面也是一个(容器)部件。在这种布局中,部件和文档可以跨桌面移动或复制。
Desktops.Grow 扩展执行命令的桌面。桌面扩展到覆盖其显示的整个轨道,或者如果它已经填满了轨道,则扩展到覆盖整个显示屏。
Desktops.Copy 打开执行命令的桌面的副本。副本显示与原始桌面相同的数据(它是克隆)。
Desktops.Close 折叠或删除执行命令的桌面。
Desktops.Store fileName 将执行命令的桌面存储在具有指定名称的磁盘文件中,即名称板中显示的名称。如果该名称的文件已经存在,则将其重命名:附加后缀.Bak。
Desktops.ChangeBackdrop picturefileName 更改标记桌面的背景。使用不存在的图片名称来重置背景。
视图是一个相机视图,它对一个无法轻松地在显示表面上完整呈现的小部件具有可变的视角。例如,一个大型绘图可能就是这种情况。同一个可视小部件的第二个视图也可以显示相同绘图的不同部分。甚至可以构建多个视图:任何视图都可以编辑,因为它们都代表同一个基本小部件。
原始图片 | 缩放视图 #1 | 缩放视图 #2 |
在幕后,这种情况与您在具有两个视图的模型中所经历的情况类似,但这里的模型是一个可视小部件(一个图片)。
当鼠标焦点位于这些视图之一内部时,按下中间键会导致出现一个跨越矩形,显示正在查看对象的实际尺寸。在该键上向任何方向拖动会导致矩形移动。当释放该键时,会绘制新的视图。这里使用的图片名为“Default.Pict”,包含在本 Oberon 系统 3 中。
视图也是一个小部件,并且视图可以嵌套。视图可以为空,即不显示任何内容。将一个小部件拖放到一个空视图中会使视图显示该被消费的小部件。
右侧的最后一个示例显示了一个带有按钮的面板的相机视图。可以通过拾取面板的边界并将其拖动到新位置来更改视点。
在使用小部件中,深入了解小部件的使用和组成,并了解扩展的小部件集合。
在从 Oberon 程序中使用小部件中了解如何从 Oberon 程序内部操作小部件。
如果您想开发自己的小部件,可以在编程新小部件中学习所有相关内容。
A
B
C
复选框
复杂小部件
消费操作
容器
上下文
控制区域
复制选择
D
删除选择
后代
Desktops.ChangeBackdrop
Desktops.Close
Desktops.Copy
Desktops.Grow
Desktops.InsertDoc
Desktops.OpenDoc
Desktops.Open
Desktops.PrintDoc
Desktops.Recall
Desktops.ReplaceDoc
Desktops.StoreDoc
桌面
Desktops.Store
文档 New 过程
文档
拖放
拖放
E
G
L
M
菜单栏
模型小部件
模型-视图-控制器
鼠标键操作
移动
MVC
P
R
S
T
V
1996 年 7 月 6 日 修订
1997 年 5 月 30 日 安装