跳转到内容

Oberon/ETH Oberon/Tutorial/GadgetsOberon

来自 Wikibooks,开放世界的开放书籍

这些教程页面由 André Fischer (afi) 编写,由 Hannes Marais 提供编辑协助,托管在 ETHZ 上,并保留在 ETH 许可 下。相关内容通过 Book.Tool 在系统中找到。扩展内容也可用 纸质 获得。一些教程页面位于 WayBack 档案 中。

在 Oberon 中使用小工具

您可能已经阅读了教程 使用小工具,了解如何以交互方式创建用户界面,以及如何为这些界面定义简单的行为。掌握了使用 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.contextGadgets.executorObjGadgets.senderObjGadgets.receiverObj,为命令提供有关环境或上下文的必要信息。

Gadgets.context

[编辑 | 编辑源代码]

当小工具执行命令时,此全局变量被设置,并包含执行命令的小工具的 上下文或父级。然后,可以使用此值在该上下文中检索小工具(视觉小工具或模型小工具)。在程序控制下设置此值会更改上下文。

示例:过程 Gadgets.FindObj(context, name) 可用于在给定上下文中查找命名的小工具。

Gadgets.executorObj

[编辑 | 编辑源代码]

当小工具执行命令时,此全局变量被设置,并包含执行命令的小工具。此值也存储在全局变量 Oberon.Par.obj 中。

Gadgets.senderObj

[编辑 | 编辑源代码]

此变量仅在执行消耗操作(拖放)时包含有效值。它包含被消耗的小工具(例如,被按钮消耗的图片小工具)或被放置到另一个小工具上的小工具(例如,放置到面板上的按钮)。

Gadgets.receiverObj

[编辑 | 编辑源代码]

此变量仅在执行消耗操作(拖放)时包含有效值。它包含消耗另一个小工具的小工具(例如,消耗标题的按钮)。

替代环境信息源

[编辑 | 编辑源代码]

Oberon 模块导出了变量 Oberon.Par.objOberon.Par.frame,为命令提供与环境信息类似的供给。Objects 模块也提供了一个信息。

Oberon.Par.obj

[编辑 | 编辑源代码]

当小工具执行命令时,此全局变量被设置,并且包含与 Gadgets.executorObj 相同的值。

Oberon.Par.frame

[编辑 | 编辑源代码]

当小工具执行命令时,此全局变量被设置,并包含命令执行的最外层框架(即桌面框架)的指针。

Objects.NewObj

[编辑 | 编辑源代码]

当使用过程 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。

访问小工具 - Display.SelectMsg

[编辑 | 编辑源代码]

客户端模块可以通过将 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。返回时,将设置 objsel 字段。
set: 选择目标框架字段 F 中指定的小工具。
reset: 取消选择目标框架字段 F 中指定的小工具。

id=setid=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”按钮,它将失败:该面板被锁定!

移除小工具 - Display.ControlMsg

[编辑 | 编辑源代码]

客户端模块可以通过将 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 将小工具重新插入到插入符处。

调整大小/移动小工具 - Display.ModifyMsg

[编辑 | 编辑源代码]

客户端模块可以通过将 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 是消息标识符,它决定在显示空间中执行的操作。定义了以下操作

reduceextend: 将框架调整到指定的宽度和高度。
move: 将框架移动到新的坐标 X 和 Y。

- mode 设置为 state,框架不应立即显示自身,而应仅更新其坐标或大小。此消息绝不能失效,并且 dXdYdWdH 变化坐标应始终设置。

(*-- 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 替换为从上一步中获得的数据推断出的合理坐标值。

你也可以在桌面上使用其他小工具和坐标值进行练习。

定位框架 - Display.LocateMsg

[编辑 | 编辑源代码]

客户端模块可以通过向显示空间广播一个LocateMsg来查询显示空间。

DEFINITION Display; (* excerpt only! *)
TYPE
  LocateMsg = RECORD (FrameMsg)
    loc: Frame;
    X, Y, u, v: INTEGER
  END
END Display.

其中
- loc 在返回时包含包含绝对坐标 X、Y 的点所在的框架。如果未找到框架,则包含 NIL。
- XY 是查询显示空间的点的绝对坐标。
- uv 在返回时包含该点在框架中的相对坐标(相对于框架的左上角)。

(*-- 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) 对其他小部件重复此操作,并将指针放在桌面的空闲区域。

(重新)绘制小部件 - Display.DisplayMsg

[编辑 | 编辑源代码]

客户端模块可以通过广播一个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: 重新绘制目标框架内由uvwh 定义的区域。

- uvwhid=area 时,定义了目标框架内宽为h、高为w 的区域的相对坐标uv。这些坐标是相对于框架的左上角确定的。

(*-- 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;

属性管理 - Objects.AttrMsg

[编辑 | 编辑源代码]

每个小部件都有一组属性,代表小部件的状态、配置或行为。属性是(名称、值)对,其中值是布尔型、整型、实型、字符型或字符串型。属性用于存储小部件的名称、值、命令字符串、编辑特征或其他信息。例如,名为“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=getid=set 时,必须指定它。

- res 报告结果:< 0 消息未处理,>= 0 消息已处理。

- class 是属性的类型。上面列出了预定义类型:Inval、String、...

- ixycbs: 仅使用这些字段中的一个来存储属性值。使用的字段是与class 值指定的类型匹配的字段。当id=get 时,属性值将返回到这些字段中的一个。当id=set 时,要分配的属性值必须在发送消息之前存储在这些字段中的一个。

检索属性

[编辑 | 编辑源代码]

使用id=Objects.get 检索给定对象的属性值。结果将存储在ixycbs 字段之一中。

(*-- 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 模块导出的命令过程在 使用小部件 教程中进行了解释。小部件模块导出了一整套过程,这里只描述并注释了其中一部分。其中一些过程仅用于简化编程,也就是说,可以通过发送上一章描述的一些消息来获得相同的功能。文本中散布的注释会提醒您注意这些情况。

对新小部件程序员特别感兴趣的过程在 编程新小部件 教程中进行了解释。

创建对象 - CreateObject

[编辑 | 编辑源代码]

Gadgets.CreateObject(newproc: ARRAY OF CHAR): Objects.Object 从新过程返回一个新对象。新创建的对象存储在Objects 模块中的全局变量NewObj 中,可以通过Objects.NewObj 来引用它。请注意,新的小部件实例,无论是可视小部件还是模型小部件,都只存在于系统中。您可以通过调用下面描述的 Gadgets.Integrate 过程,让可视小部件出现在显示空间中。

请查看下面示例中的实现方式。

在插入符号位置插入小部件 - 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 在该点插入一个滑块小部件。

创建视图/模型对 - CreateViewModel

[编辑 | 编辑源代码]

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

命名对象 - NameObj

[编辑 | 编辑源代码]

Gadgets.NameObj(obj: Objects.Object; name: ARRAY OF CHAR) 更改任何对象的名称(注意:任何对象,即不必是小工具)。

参见上例中是如何实现的。

检索对象的名称 - GetObjName

[编辑 | 编辑源代码]

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),并注意有些小工具没有模型小工具。解释一下。

检索公共对象 - FindPublicObj

[编辑 | 编辑源代码]

Gadgets.FindPublicObj(name: ARRAY OF CHAR): Objects.Object 搜索一个公共对象,其名称指定为“L.O”。其中 L 是公共库的名称(例如 Icons.Lib,一个公共面板……)而 O 是对象的名称。如果不存在给定名称的对象,则返回 NIL。

检索对象 - FindObj

[编辑 | 编辑源代码]

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 中的新信息并解释。

更新小工具 - Update

[编辑 | 编辑源代码]

Gadgets.Update(obj: Objects.Object) 如果 obj 是一个框架,则广播一个 Display.DisplayMsg,否则广播一个 Gadgets.UpdateMsg。如果直接更改了视觉小工具或模型小工具的数据,则需要这样做。

检索框架 - ThisFrame

[编辑 | 编辑源代码]

Gadgets.ThisFrame(X, Y: INTEGER; VAR F: Display.Frame; VAR u, v: INTEGER) 检索位于显示空间中的绝对坐标 XY 处的框架 Fuv 包含该点在框架中的相对坐标。这些坐标是相对于框架左上角确定的。

(*-- 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) 对其他小部件重复此操作,并将指针放在桌面的空闲区域。

执行命令 - Execute

[编辑 | 编辑源代码]

Gadgets.Execute(cmd: ARRAY OF CHAR; executor, dlink, sender, receiver: Objects.Object) 执行命令字符串 cmd。对于消耗操作,发送方和接收方仅具有值。Dlink 复制到 Gadgets.contextOberon.Par.obj 设置为执行器,而 Oberon.Par.frame 设置为执行命令的最外层框架。

执行命令 - ExecuteAttr

[编辑 | 编辑源代码]

Gadgets.ExecuteAttr(F: Frame; attr: ARRAY OF CHAR; dlink, sender, receiver: Objects.Object) 搜索 F 的特定属性 attr 以作为命令执行。

直接更改小工具的值 - SetValue

[编辑 | 编辑源代码]

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

[编辑 | 编辑源代码]

Documents.Open(name: ARRAY OF CHAR): Document 从磁盘加载命名文档并返回该文档。如果找不到该名称的文件,则返回 NIL。请注意,外部文档已内部化,但并未在显示空间中呈现。要向用户呈现它,您必须使用下一个过程。

有关示例,请参见下面过程中的语句 (*1*)

呈现文档 - Desktops.ShowDoc

[编辑 | 编辑源代码]

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

[编辑 | 编辑源代码]

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 | G | N | O | P ]

B

BasicGadgets.SetValue

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 日

华夏公益教科书