Oberon/ETH Oberon/Tutorial/GadgetsOberon
这些教程页面由 André Fischer (afi) 编写,由 Hannes Marais 提供编辑协助,托管在 ETHZ 上,并保留在 ETH 许可 下。相关内容通过 Book.Tool 在系统中找到。扩展内容也可用 纸质 获得。一些教程页面位于 WayBack 档案 中。
您可能已经阅读了教程 使用小工具,了解如何以交互方式创建用户界面,以及如何为这些界面定义简单的行为。掌握了使用 Oberon 系统 3 的第一步,我们邀请您挑战下一步,更具挑战性的步骤,即在程序控制下放置用户界面的行为。这需要在 Oberon 中编写相当简单的过程。为了说明如何实现这一点,最多 20 行的代码片段将被展示和解释。您应该将这些片段视为可靠的理论构建元素,能够集成到您可能想开发的可行过程中。为了帮助您更快更好地实现您的目标,所有片段都已在 Examples.Mod 模块中实现,该模块将在整个教程中用于实时演示。
预计时间:60 分钟。
在您的 桌面 上单击“日志”图标,打开“Oberon.Log”。将该查看器移动到桌面的右上角,并在做练习时记住要留意日志查看器。现在将本教程查看器移动到桌面的左上角,确保桌面上有一些空闲空间。您将在以后使用该空闲空间作为操场。如果操场变得拥挤,您可以主动删除多余的视觉小工具。练习并不局限于教程中建议的那些,您可以将练习应用于桌面上找到的其他对象。现在,您被邀请在纸上打印源文本“Examples.Mod”以供参考,例如使用本地打印机上的 Edit.Print LPT1 Examples.Mod ~。
Oberon 系统的一个重要特性是能够从普通文本中调用命令。Gadgets 模块导出了一些命令,您可以使用这些命令直接查询或更改小工具。这些命令在 Gadgets 模块 和 属性 中描述。几个小工具,例如 按钮,具有一个 Cmd 属性,该属性可能包含一个命令。当小工具被激活时(例如,当按钮被点击时),此命令被执行。此设备使您能够将小工具的控制权委托给另一个小工具。
以下面板演示了这种情况。
在右侧,您有一个 颜色选择器,在左侧有一个名为“颜色”的 矩形,即“颜色”是其 Name 属性的值。颜色选择器的 Cmd 属性的值为
- Gadgets.Set Color.Color #Col ~.
现在,在颜色选择器上按住鼠标中键,将箭头指针定位在您选择的颜色上,然后松开按键。矩形的颜色会改变。在这种情况下,使用 Gadgets 模块导出的命令之一,但您不限于这些命令。
如果构建新命令是本教程和您的明确目标,那么一个编程问题是如何为命令提供有关其执行环境的信息。
Gadgets 模块导出四个变量 Gadgets.context、Gadgets.executorObj、Gadgets.senderObj 和 Gadgets.receiverObj,为命令提供有关环境或上下文的必要信息。
当小工具执行命令时,此全局变量被设置,并包含执行命令的小工具的 上下文或父级。然后,可以使用此值在该上下文中检索小工具(视觉小工具或模型小工具)。在程序控制下设置此值会更改上下文。
示例:过程 Gadgets.FindObj(context, name) 可用于在给定上下文中查找命名的小工具。
当小工具执行命令时,此全局变量被设置,并包含执行命令的小工具。此值也存储在全局变量 Oberon.Par.obj 中。
此变量仅在执行消耗操作(拖放)时包含有效值。它包含被消耗的小工具(例如,被按钮消耗的图片小工具)或被放置到另一个小工具上的小工具(例如,放置到面板上的按钮)。
此变量仅在执行消耗操作(拖放)时包含有效值。它包含消耗另一个小工具的小工具(例如,消耗标题的按钮)。
Oberon 模块导出了变量 Oberon.Par.obj 和 Oberon.Par.frame,为命令提供与环境信息类似的供给。Objects 模块也提供了一个信息。
当小工具执行命令时,此全局变量被设置,并且包含与 Gadgets.executorObj 相同的值。
当小工具执行命令时,此全局变量被设置,并包含命令执行的最外层框架(即桌面框架)的指针。
当使用过程 CreateObject 或过程 CreateViewModel 创建一个新对象时,此全局变量被设置。它包含新对象。此变量允许其他命令或过程处理新对象,最简单的过程是在显示空间的插入符位置 插入 新的小工具。
来自 Display.FrameMsg 的 Display 模块中的框架消息在帧间通信中起着核心作用。这些构建了一个通信协议,允许框架相互通信,而无需了解彼此过多信息。当将外部或未知对象集成到系统中时,以及应用程序需要相互交换对象时,后一点至关重要。FrameMsg 定义如下
DEFINITION Display; (* excerpt only! *) FrameMsg = RECORD (Objects.ObjMsg) F: Frame; (* target frame *) x, y, res: INTEGER END END Display.
F 在 FrameMsg 中起着核心作用。它确定消息的目标框架。通常,消息的目标框架是未知的。例如,当模型更新消息广播时,就会发生这种情况。在这种情况下,F 字段设置为 NIL。
注意:正确初始化消息字段非常重要。所有消息共有的 res 字段用于存储结果值(>= 0)。因此,M.res 始终应初始化为 -1。
客户端模块可以通过将 SelectMsg 广播到显示空间或给定框架来询问和控制显示空间。可以寻址一系列视觉小工具,前提是它们都具有相同的祖先。
DEFINITION Display; (* excerpt only! *) CONST get = 0; set = 1; reset = 2; TYPE SelectMsg = RECORD (FrameMsg) id: INTEGER; time: LONGINT; sel: Frame; obj: Objects.Object END END Display.
其中
- id 是消息标识符,它决定对小工具执行的操作。定义了以下操作
- get: 当询问显示空间中选择的小工具时,消息的目标是未知的。因此,F 字段必须设置为 NIL。返回时,将设置 obj 和 sel 字段。
- set: 选择目标框架字段 F 中指定的小工具。
- reset: 取消选择目标框架字段 F 中指定的小工具。
当 id=set 或 id=reset 时,视觉小工具不会重新绘制。客户端模块必须通过广播 DisplayMsg 来请求重新绘制。
- time 包含返回时选择的时刻(当 id=get 时)。如果未选择任何对象,此字段将保持不变。因此,用负值(time:=-1)初始化它很重要。
- sel 包含返回时选择对象的祖先(当 id=get 时)。
- obj 包含返回时选择的对象(或列表中的第一个对象)(当 id=get 时)。
id = | get | set | reset | |
---|---|---|---|---|
F | NIL | NIL | dest. | dest. |
time | -1 -> -1 | -1 -> time | -- | -- |
sel | -- | ancest. | -- | -- |
obj | NIL | obj | -- | -- |
没有对象。 已选择 |
对象(s) 已选择 |
(*-- Access a list of selected gadget(s) one by one. --*) PROCEDURE EnumSelection; VAR S: Display.SelectMsg; obj: Objects.Object; BEGIN S.id := Display.get; S.F := NIL; S.time := -1; Display.Broadcast(S); IF (S.time > 0) & (S.obj # NIL) THEN obj := S.obj; (* Access the first object *) WHILE obj # NIL DO (*-- Process this object --*) obj := obj.slink (* Access the next object *) END END END EnumSelection;
练习:
1) 在显示空间中选择任意数量的小工具,并使用 Examples.GetSelection(使用 id=get)询问它们。
2) 取消选择所有小工具,然后重复上述问题。
(*-- Select a gadget --*) PROCEDURE SelectGadget*; VAR S: Display.SelectMsg; obj: Objects.Object; BEGIN obj := Gadgets.FindObj(Gadgets.context, "Test"); IF (obj # NIL) THEN S.id := Display.set; S.F := obj(Display.Frame); S.obj := NIL; S.sel := NIL; Display.Broadcast(S); (* Use the information collected *) Info(S.obj); Info(S.sel); (* Redraw the gadget *) ... END END SelectGadget;
练习:
1) 单击此面板中的左按钮,以执行命令 Examples.SelectGadget(使用 id=set)
2) 显示刚刚选择的小工具的信息,名为“Test” Examples.GetSelection(使用 id=get)。
3) 使用 Examples.DeselectGadget(使用 id=reset)再次取消选择该小工具。如果你尝试使用鼠标右键单击来取消选择“Test”按钮,它将失败:该面板被锁定!
客户端模块可以通过将 ControlMsg 广播到给定框架(在基本 FrameMsg 的目标框架字段 F 中指定)来控制显示空间。可以寻址一系列视觉小工具,前提是它们都具有相同的祖先。
DEFINITION Display; (* excerpt only! *) CONST remove = 0; suspend = 1; restore = 2; TYPE ControlMsg = RECORD (FrameMsg) id: INTEGER END END Display.
其中
- id 是消息标识符,它决定在显示空间中执行的操作。定义了以下操作
- remove: 从显示空间中移除目标框架。仅框架从显示空间中移除。对象仍然存在,可以使用 Gadgets.Integrate(obj) 例如,将它们恢复到显示空间中的插入符位置。
- 这用于删除它或将其插入到一个新的父级中。
- suspend: 从显示空间中临时移除从目标框架向下延伸的所有框架。
- restore: 将从目标框架向下延伸的所有框架恢复到显示空间。在挂起期间可能错过消息的框架将更新其内部数据结构。
移除显示空间中小工具的模型程序
PROCEDURE RemoveGadget(obj: Objects.Object); VAR C: Display.ControlMsg; BEGIN C.id := Display.remove; C.F := obj(Gadgets.Frame); C.res := -1; Display.Broadcast(C) END RemoveGadget;
练习:
1) 将插入符放置在桌面的空闲区域(此查看器之外)。
2) 使用 Gadgets.Insert Panels.NewPanel 在该位置插入一个面板小工具。
3) 选择该小工具。
4) 使用 Examples.RemoveSelection(使用 id=remove)移除它。
5) 再次将插入符放置在桌面的空闲区域。
6) 使用 Examples.RestoreRemoved 将小工具重新插入到插入符处。
客户端模块可以通过将 ModifyMsg 广播到给定框架(在基本 FrameMsg 的目标框架字段 F 中指定)来调整显示空间中视觉小工具的大小或移动它。
DEFINITION Display; (* excerpt only! *) CONST reduce = 0; extend = 1; move = 2; display = 0; state = 1; TYPE ModifyMsg = RECORD (FrameMsg) id: INTEGER; (* reduce, extend, move *) mode: INTEGER; (* display, state *) dX, dY, dW, dH: INTEGER; X, Y, W, H: INTEGER END END Display.
其中
- id 是消息标识符,它决定在显示空间中执行的操作。定义了以下操作
- reduce 或 extend: 将框架调整到指定的宽度和高度。
- move: 将框架移动到新的坐标 X 和 Y。
- mode 设置为 state,框架不应立即显示自身,而应仅更新其坐标或大小。此消息绝不能失效,并且 dX、dY、dW、dH 变化坐标应始终设置。
(*-- Resize a gadget to (W, H) --*) PROCEDURE ResizeTo(F: Gadgets.Frame; W, H: INTEGER); VAR M: Display.ModifyMsg; BEGIN M.id := Display.extend; (* or M.id := Display.reduce *) M.mode := Display.display; M.F := F; M.X := F.X; M.Y := F.Y; (* Same coordinates *) M.dX := 0; M.Y := 0; M.W := W; M.H := H; (* New size *) M.dW := W-F.W; M.dH := H-F.H; Display.Broadcast(M) END ResizeTo;
练习:
1) 将插入符放置在桌面的空闲区域(此查看器之外)。
2) 使用 Gadgets.Insert Panels.NewPanel 在该位置插入一个面板小工具。
3) 选择该小工具。
4) 使用 Examples.LocateGadget(使用 SelectMsg)确定小工具的坐标和大小。
5) 使用 Examples.Resize 120 45 调整小工具的大小。大小值 W=125 和 H=45 在此查看器中不可编辑。
如果你将这两个 Example 命令复制到可编辑的查看器中,例如 Oberon.Log,你可以在桌面上使用其他小工具和尺寸进行练习。可以使用步骤 4)中生成的值来选择适当的大小值。
(*-- Move a gadget to (X, Y) --*) PROCEDURE MoveToXY(F: Gadgets.Frame; X, Y: INTEGER); VAR M: Display.ModifyMsg; BEGIN M.id := Display.move; M.mode := Display.display; M.F := F; M.X := X; M.Y := Y; (* New coordinates *) M.dX := X-F.X; M.dY := Y-F.Y; M.W := F.W; M.H := F.H; (* Same size *) M.dW := 0; M.dH := 0; Display.Broadcast(M) END MoveToXY;
练习:
1) 将插入符放置在桌面的空闲区域(此查看器之外)。
2) 使用 Gadgets.Insert Panels.NewPanel 在该位置插入一个面板。
3) 选择该小工具。
4) 使用 Examples.LocateGadget(使用 SelectMsg)确定小工具的坐标和大小。
5) 将此 Examples.MoveGadget X Y 命令复制到可编辑的查看器中,例如 Oberon.Log,并将 X 和 Y 替换为从上一步中获得的数据推断出的合理坐标值。
你也可以在桌面上使用其他小工具和坐标值进行练习。
客户端模块可以通过向显示空间广播一个LocateMsg来查询显示空间。
DEFINITION Display; (* excerpt only! *) TYPE LocateMsg = RECORD (FrameMsg) loc: Frame; X, Y, u, v: INTEGER END END Display.
其中
- loc 在返回时包含包含绝对坐标 X、Y 的点所在的框架。如果未找到框架,则包含 NIL。
- X、Y 是查询显示空间的点的绝对坐标。
- u、v 在返回时包含该点在框架中的相对坐标(相对于框架的左上角)。
(*-- Locate gadget at screen coordinates X, Y --*) PROCEDURE Locate*; VAR L: Display.LocateMsg; X, Y: INTEGER; BEGIN ... (* Set X and Y *) L.X := X; L.Y := Y; L.res := -1; L.F := NIL; L.loc := NIL; Display.Broadcast(L); (* Use the information collected *) Info(L.loc); Out.String("u="); Out.Int(L.u, 5); Out.String(" v="); Out.Int(L.v, 5); Out.Ln ... END Locate;
练习:
1) 移动鼠标指针并将星形指针放在桌面上的框架范围内。
2) 使用 Examples.Locate 确定该点位于哪个小部件,并观察指针相对于框架左上角的位置。解释一下。
3) 对其他小部件重复此操作,并将指针放在桌面的空闲区域。
客户端模块可以通过广播一个DisplayMsg来请求重新绘制小部件。
DEFINITION Display; (* excerpt only! *) CONST frame = 0; area = 1; TYPE DisplayMsg = RECORD (FrameMsg) id: INTEGER; u, v, w, h: INTEGER END END Display.
其中
- id 是消息标识符,它决定在显示空间中执行的操作。定义了以下操作
- frame: 重新绘制目标框架。
- area: 重新绘制目标框架内由u、v、w、h 定义的区域。
- u、v、w、h 当id=area 时,定义了目标框架内宽为h、高为w 的区域的相对坐标u、v。这些坐标是相对于框架的左上角确定的。
(*-- Select gadget --*) PROCEDURE SelectGadget*; VAR S: Display.SelectMsg; obj: Objects.Object; D: Display.DisplayMsg; BEGIN obj := Gadgets.FindObj(Gadgets.context, "Test"); IF (obj # NIL) THEN (* Select the gadget *) ... (* Redraw the gadget *) D.id := Display.frame; D.F := obj(Display.Frame); Display.Broadcast(D); ... END END SelectGadget;
每个小部件都有一组属性,代表小部件的状态、配置或行为。属性是(名称、值)对,其中值是布尔型、整型、实型、字符型或字符串型。属性用于存储小部件的名称、值、命令字符串、编辑特征或其他信息。例如,名为“Cmd”的属性可以包含一个 Oberon 命令(字符串),该命令在激活小部件(单击鼠标中键)时执行。
在 Oberon 系统 3 的小部件系统中,属性管理是严格且排他地通过发送消息来处理的,也就是说,通过向小部件直接发送一个类型为AttrMsg(在模块Objects中定义)的消息,通过调用它的处理程序来检索和更改小部件属性。
- obj.handle(obj, message)
请注意,对象属性也可以使用通用编辑器名称“Inspector”进行可视化和编辑。
DEFINITION Objects; (* excerpt only! *) CONST enum = 0; get = 1; set = 2; Inval = 0; String = 2; Int = 3; Real = 4; LongReal = 5; Char = 6; Bool = 7; TYPE AttrMsg = RECORD (ObjMsg) id: INTEGER; Enum: PROCEDURE (name: ARRAY OF CHAR); name: Name; res, class: INTEGER; i: LONGINT; x: REAL; y: LONGREAL; c: CHAR; b: BOOLEAN; s: ARRAY 64 OF CHAR END END Objects.
其中
- id 是消息标识符,它决定对属性执行哪个操作。定义了以下值
- get: 检索属性name的值。
- set: 设置属性name的值。
- enum: 枚举对象的所有属性。enum 过程被反复调用,每个属性调用一次
- Enum 是一个枚举过程,当id=enum 时,发送方必须提供。
- name 是属性的名称。当id=get 或id=set 时,必须指定它。
- res 报告结果:< 0 消息未处理,>= 0 消息已处理。
- class 是属性的类型。上面列出了预定义类型:Inval、String、...
- i、x、y、c、b、s: 仅使用这些字段中的一个来存储属性值。使用的字段是与class 值指定的类型匹配的字段。当id=get 时,属性值将返回到这些字段中的一个。当id=set 时,要分配的属性值必须在发送消息之前存储在这些字段中的一个。
使用id=Objects.get 检索给定对象的属性值。结果将存储在i、x、y、c、b、s 字段之一中。
(*-- Retrieve a named attribute of an object --*) PROCEDURE GetAttr(obj: Objects.Object; name: ARRAY OF CHAR); VAR A: Objects.AttrMsg; BEGIN A.id := Objects.get; COPY(name, A.name); A.res := -1; obj.handle(obj, A); IF A.res >= 0 THEN (* Attribute exists *) IF A.class = Objects.String THEN (* value A.s *) ELSIF A.class = Objects.Int THEN (* value A.i *) ELSIF A.class = Objects.Real THEN (* value A.x *) ELSIF A.class = Objects.LongReal THEN(* value A.y *) ELSIF A.class = Objects.Char THEN (* value A.c *) ELSIF A.class = Objects.Bool THEN (* value A.b *) ELSE (* unknown class *) END ELSE (* Attribute does not exist *) END END GetAttr;
练习:
1) 选择此对象
2) 使用 Examples.ShowValue 显示该对象的属性。
3) 移动滑块并重复查询。
使用id=Objects.set 设置属性值。
(*-- Store a LONGINT value in a named object --*) PROCEDURE SetAttr(obj: Objects.Object; name: ARRAY OF CHAR; i: LONGINT); VAR A: Objects.AttrMsg; BEGIN A.id := Objects.set; COPY(name, A.name); A.class := Objects.Int; A.i := i; A.res := -1; obj.handle(obj, A) END SetAttr;
使用id=Objects.enum 检索所有属性值。
VAR tmp: Objects.Object; (*-- Retrieve a named attribute --*) PROCEDURE RetrObjAttr(name: ARRAY OF CHAR); VAR A: Objects.AttrMsg; BEGIN A.id := Objects.get; COPY(name, A.name); A.res := -1; tmp.handle(tmp, A); IF A.res >= 0 THEN (* Attribute exists *) (* A.class as in GetAttr *) ELSE (* Attribute does not exist *) END END RetrObjAttr;
PROCEDURE EnumAttr*(obj: Objects.Object); VAR A: Objects.AttrMsg; BEGIN A.id := Objects.enum; A.Enum := RetrObjAttr; A.res := -1; tmp := obj; obj.handle(obj, A) END EnumAttrs;
预备说明:Gadgets 模块导出的命令过程在 使用小部件 教程中进行了解释。小部件模块导出了一整套过程,这里只描述并注释了其中一部分。其中一些过程仅用于简化编程,也就是说,可以通过发送上一章描述的一些消息来获得相同的功能。文本中散布的注释会提醒您注意这些情况。
对新小部件程序员特别感兴趣的过程在 编程新小部件 教程中进行了解释。
Gadgets.CreateObject(newproc: ARRAY OF CHAR): Objects.Object 从新过程返回一个新对象。新创建的对象存储在Objects 模块中的全局变量NewObj 中,可以通过Objects.NewObj 来引用它。请注意,新的小部件实例,无论是可视小部件还是模型小部件,都只存在于系统中。您可以通过调用下面描述的 Gadgets.Integrate 过程,让可视小部件出现在显示空间中。
请查看下面示例中的实现方式。
Gadgets.Integrate(obj: Objects.Object) 将对象O 集成(即插入)到显示空间中的插入符号位置。
(*-- Create a slider gadget and insert it at the caret position --*) PROCEDURE InsertAtCaret*; VAR obj: Objects.Object; BEGIN ... obj := Gadgets.CreateObject("BasicGadgets.NewSlider"); Gadgets.Integrate(obj) END InsertAtCaret;
练习:
1) 将插入符放置在桌面的空闲区域(此查看器之外)。
2) 使用 Examples.InsertAtCaret 在该点插入一个滑块小部件。
Gadgets.CreateViewModel(viewnewproc, modelnewproc: ARRAY OF CHAR): Display.Frame 创建一个视图/模型小部件对,即一个与模型小部件关联的可视小部件,例如文本字段和字符串。新创建的可视小部件存储在全局变量Objects.NewObj 中,新创建的模型小部件可以通过Objects.NewObj(Gadgets.Frame).obj 来引用。
(*-- Create a text field linked to an integer and insert it at the caret position --*) PROCEDURE InsertPair*; VAR F: Display.Frame; obj: Objects.Object; BEGIN F := Gadgets.CreateViewModel("TextFields.NewTextField", "BasicGadgets.NewInteger"); Gadgets.Integrate(F); (* Name the model "Volts" *) Gadgets.NameObj(F(Gadgets.Frame).obj, "Volts"); (*-- Create a slider named "Slider" and link it to the integer --*) obj := Gadgets.CreateObject("BasicGadgets.NewSlider"); Gadgets.Integrate(obj); Gadgets.NameObj(obj, "Slider"); obj(Gadgets.Frame).obj := F(Gadgets.Frame).obj END InsertPair;
练习:
1) 将插入符号放在桌面上的空闲区域(不要放在此查看器中!)。
2) 在该位置插入一个滑块小工具,使用 Examples.InsertPair。
Gadgets.NameObj(obj: Objects.Object; name: ARRAY OF CHAR) 更改任何对象的名称(注意:任何对象,即不必是小工具)。
参见上例中是如何实现的。
Gadgets.GetObjName(obj: Objects.Object; VAR name: ARRAY OF CHAR) 检索任何对象的名称(注意:任何对象,即不必是小工具)。
(*-- Display names assigned in previous example --*) PROCEDURE ShowNames*; VAR S: Display.SelectMsg; ObjName: ARRAY 64 OF CHAR; BEGIN ..... Out.String("Visual gadget name: "); Gadgets.GetObjName(S.obj, ObjName); Out.String(ObjName); Out.Ln; (*-- This visual gadget may have no model --*) Out.String("Model gadget name: "); Gadgets.GetObjName(S.obj(Gadgets.Frame).obj, ObjName); Out.String(ObjName); Out.Ln END ShowNames;
练习 1:
1) 选择之前创建的滑块。
2) 使用 Examples.ShowNames 证明分配的名称是正确的。
3) 取消选择滑块,选择文本字段。
4) 再次执行 Examples.ShowNames 并解释结果。
练习 2:
在桌面上的其他小工具上重复练习(使用 Examples.Shownames),并注意有些小工具没有模型小工具。解释一下。
Gadgets.FindPublicObj(name: ARRAY OF CHAR): Objects.Object 搜索一个公共对象,其名称指定为“L.O”。其中 L 是公共库的名称(例如 Icons.Lib,一个公共面板……)而 O 是对象的名称。如果不存在给定名称的对象,则返回 NIL。
Gadgets.FindObj(context: Objects.Object; name: ARRAY OF CHAR): Objects.Object 在指定的上下文搜索命名对象。如果不存在给定名称的对象,则返回 NIL。
(* Find a named gadget in a specified context. *) PROCEDURE FindObj*; VAR obj: Objects.Object; BEGIN (* Note that if this command is executed from a gadget the context is already set at execution time, before reaching this point. *) obj := Gadgets.FindObj(Gadgets.context, "Test"); IF (obj # NIL) & (obj IS BasicGadgets.Button) THEN ... END END FindObj;
练习:
1) 在此面板中单击鼠标左键,以执行命令 Examples.FindObj
2) 阅读 Oberon.Log 中的新信息并解释。
Gadgets.Update(obj: Objects.Object) 如果 obj 是一个框架,则广播一个 Display.DisplayMsg,否则广播一个 Gadgets.UpdateMsg。如果直接更改了视觉小工具或模型小工具的数据,则需要这样做。
Gadgets.ThisFrame(X, Y: INTEGER; VAR F: Display.Frame; VAR u, v: INTEGER) 检索位于显示空间中的绝对坐标 X、Y 处的框架 F。u、v 包含该点在框架中的相对坐标。这些坐标是相对于框架左上角确定的。
(*-- Locate gadget at screen coordinates X, Y --*) PROCEDURE LocateP*; VAR F: Display.Frame; X, Y: INTEGER; u, v: INTEGER; BEGIN .... (* Set X and Y *) Gadgets.ThisFrame(X, Y, F, u, v); (* Use the information collected *) Info(F); Out.String("u="); Out.Int(u, 5); Out.String(" v="); Out.Int(v, 5); Out.Ln .... END LocateP;
练习:
1) 移动鼠标指针并将星形指针放在桌面上的框架范围内。
2) 使用 Examples.LocateP 确定位于该位置的小工具,并观察指针相对于框架左上角的位置。解释一下。
3) 对其他小部件重复此操作,并将指针放在桌面的空闲区域。
Gadgets.Execute(cmd: ARRAY OF CHAR; executor, dlink, sender, receiver: Objects.Object) 执行命令字符串 cmd。对于消耗操作,发送方和接收方仅具有值。Dlink 复制到 Gadgets.context;Oberon.Par.obj 设置为执行器,而 Oberon.Par.frame 设置为执行命令的最外层框架。
Gadgets.ExecuteAttr(F: Frame; attr: ARRAY OF CHAR; dlink, sender, receiver: Objects.Object) 搜索 F 的特定属性 attr 以作为命令执行。
BasicGadgets.SetValue(obj: Objects.Object) 更改在 BasicGadgets 模块中定义的类型的小工具的“值”属性,在直接更改其“val”字段后。视觉小工具会自动更新。
(*-- Look for an integer model gadget called "Test" in the current context and increment its val field. The model is visualized by a text field. --*) (*-- This command must be executed in a given context. --*) PROCEDURE Inc*; VAR obj: Objects.Object; BEGIN obj := Gadgets.FindObj(Gadgets.context, "Test"); IF (obj # NIL) & (obj IS BasicGadgets.Integer) THEN WITH obj: BasicGadgets.Integer DO INC(obj.val); BasicGadgets.SetValue(obj) END END END Inc;
1) 单击面板右上角的“Inc”按钮,并观察计数器文本字段(从 0 开始)和滑块(从 50 开始)更改值或位置。“Inc”按钮的“Cmd”字段包含“Examples.Inc”。执行该命令会导致两个小工具值的直接增量,并更新两个视图。不需要 Objects.AttrMsg。
Gadgets 模块提供了一些命令,用户或小工具可以使用这些命令来控制小工具和显示空间。可以使用程序(中间列)以编程方式执行相同的操作。还可以通过直接向对象发送 Objects 模块中定义的消息来检索或更改对象和小工具的属性。下表对此进行了总结
命令 | 过程 | 发送到对象的訊息 |
---|---|---|
Change | ||
ChangeAttr | AttrMsg 设置任何属性 | |
Copy | ||
GetAttr | AttrMsg 获取任何属性 | |
GetModelName | GetObjName | AttrMsg 获取名称 |
GetViewName | GetObjName | AttrMsg 获取名称 |
Insert | CreateObj CreateViewModel Integrate |
|
Link | ||
NameView | NameObj | AttrMsg 设置名称 |
NameModel | NameObj | AttrMsg 设置名称 |
Set | AttrMsg 设置任何属性 |
您可以编写自己的过程和命令过程来控制新文档或现有文档的呈现。一些标准过程将帮助您完成此操作。
Documents.Open(name: ARRAY OF CHAR): Document 从磁盘加载命名文档并返回该文档。如果找不到该名称的文件,则返回 NIL。请注意,外部文档已内部化,但并未在显示空间中呈现。要向用户呈现它,您必须使用下一个过程。
有关示例,请参见下面过程中的语句 (*1*)。
Desktops.ShowDoc(D: Documents.Document) 在桌面或查看器中打开文档,具体取决于上下文。
PROCEDURE OpenDoc(name: ARRAY OF CHAR); VAR D: Documents.Document; BEGIN D := Documents.Open(name); (*1*) IF D # NIL THEN Desktops.ShowDoc(D) (*2*) END END OpenDoc;
练习:
1) 使用 Examples.OpenDoc 启动名为“Tutorial User's Guide”的教程(Book 文档),该教程会加载文档“Tutorials.Book”(*1*) 并呈现它(*2*)。
2) 关闭教程。
本版本中包含多种文档类型。每种文档类型都关联着一个名为“新建”的过程。调用此过程会创建(一个新实例的)关联类型的文档。新建过程的名称也会记录在文档本身中。这样,可以稍后重新创建已存档的文档。
当前可用的新建过程列在后续章节中出现的表格中。
下面的“InsertDoc”过程展示了如何创建新的文本文档(*1*)以及如何为它命名(*1a*)。
提醒:Desktops.OpenDoc 命令使用相同的新过程在显示空间中创建和显示新文档。
Documents.Init(F: Document, main: Gadgets.Frame) 用一个“容器”小部件初始化文档。
(*-- Insert a new text document in the display space --*)
(*-- and give it a name, e.g. "MyDoc.Text". --*)
PROCEDURE InsertDoc(name: ARRAY OF CHAR);
VAR D: Documents.Document;
obj: Objects.Object;
BEGIN
TextDocs.NewDoc; (*1*)
D := Objects.NewObj(Documents.Document);
COPY(name, D.name); (*1a*)
obj := Gadgets.CreateObject("TextGadgets.New"); (*2*)
Documents.Init(D, obj(Gadgets.Frame); (*3*)
Desktops.ShowDoc(D, FALSE)
END InsertDoc;
小部件 新建过程 |
文档小部件 新建过程 |
文件 扩展名 | |
---|---|---|---|
面板 | Panels.NewPanel | PanelDocs.NewDoc | .Panel |
图片 | PicturePanels.NewPictPanel | PictureDocs.NewDoc | .Pict |
文本 | TextGadgets.New | TextDocs.NewDoc | .Text |
插入到 显示空间 使用 |
↑ Gadgets.Insert |
↑ Desktops.OpenDoc (...) |
当由用户或小部件执行时,Gadgets.Insert 会导致在显示空间中显示新的可视(容器)小部件,而 Desktops.OpenDoc 会导致显示文档小部件。所选的新过程分别决定了小部件类型或文档类型。
使用标准过程可以在程序控制下获得相同的结果。请参阅上面的 InsertDoc 过程,了解所需的三步 (*1*)、(*2*) 和 (*3*)。下表总结了每种文档类型所需的內容。
小部件 新建过程 |
文档小部件 新建过程 | |
---|---|---|
面板 | Panels.NewPanel | PanelDocs.NewDoc |
图片 | PicturePanels.NewPictPanel | PictureDocs.NewDoc |
文本 | TextGadgets.New | TextDocs.NewDoc |
↑ (2) Gadgets.CreateObject("...") |
↑ (1) |
第三步是用合适的小部件初始化文档(*3*)。
系统还附带了一些专用文档。
包含 小部件类型 |
文档小部件 新建过程 | |
---|---|---|
书籍 | Books.NewPanel | BookDocs.NewDoc |
检查器 详细信息 检查器 |
Inspectors.NewInspectorP | Inspectors.NewDoc Inspectors.NewDetailDoc |
公共面板 | Panels.NewPanel | PanelDocs.NewPublicDoc |
日志 | TextGadgets.New | TextDocs.NewLog |
插入到 显示空间 使用 |
↑ Desktops.OpenDoc (...) |
作为一个最后的例子,我们有一个简单的 面板,它只将两个实数相加。在三个 文本字段 中的任何一个中输入数字,都会保持方程的正确性。
一年级方程 | ||||||
x | + | a | = | b | ||
20.5 | -8 | 12.5 | ||||
────────────────────────────────────────────── | ||||||
模型小部件(实数) | ||||||
xx | aa | bb |
使用 哥伦布检查器 查看所选小部件的属性。请注意,文本字段 链接了 实数 模型小部件,并将 命令属性 设置为“Examples.Add”。
此面板背后的程序很简单
MODULE Examples; IMPORT Objects, Gadgets, BasicGadgets, Out; PROCEDURE Add*; VAR x, a, b: BasicGadgets.Real; PROCEDURE GetReal(name: ARRAY OF CHAR): BasicGadgets.Real; VAR obj: Objects.Object; BEGIN obj := Gadgets.FindObj(Gadgets.context, name); IF (obj # NIL) & (obj IS BasicGadgets.Real) THEN RETURN obj(BasicGadgets.Real) ELSE RETURN NIL END END GetReal; BEGIN (* 1. get the real gadgets *) x := GetReal("xx"); a := GetReal("aa"); b := GetReal("bb"); IF (x = NIL) OR (a = NIL) OR (b = NIL) THEN RETURN END; (* 2. solve the equation *) IF Gadgets.executorObj(Gadgets.Frame).obj # x THEN (* command executed from text field aa or bb *) x.val := b.val -a.val END; (* 3. notify clients of model x that x.val has changed *) BasicGadgets.SetValue(x) END Add END Examples.
Examples.Add 命令可以分为三个部分
(1) 检索对象:Gadgets.FindObj(context, name) 用于在 上下文 中查找命名的小部件。由于命令是从一个小部件中执行的,因此上下文(包含文本字段的面板)存储在变量 Gadgets.context 中。
(2) 解方程。
(3) 通知客户端已更改的对象:对于实数小部件,这可以通过调用 BasicGadgets.SetValue(obj) 来完成(或者可以使用 Gadgets.Update(obj))。
如果您想开发自己的小部件,请阅读 编程新小部件 中的所有内容。
B
D
Desktops.ShowDoc
Display.ControlMsg
Display.DisplayMsg
Display.LocateMsg
Display.ModifyMsg
Display.SelectMsg
Documents.Init
Documents.Open
G
Gadgets.context
Gadgets.CreateObject
Gadgets.CreateViewModel
Gadgets.Execute
Gadgets.ExecuteAttr
Gadgets.executorObj
Gadgets.FindObj
Gadgets.FindPublicObj
Gadgets.GetObjName
Gadgets.Integrate
Gadgets.NameObj
Gadgets.receiverObj
Gadgets.senderObj
Gadgets.ThisFrame
Gadgets.Update
N
O
Oberon.Par.frame
Oberon.Par.obj
Objects.AttrMsg
Objects.NewObj
P
修订,afi 1995 年 3 月 3 日
安装于 1997 年 5 月 30 日