跳轉到內容

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 日

華夏公益教科書