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 日
