Aros/開發者/Zune/初學者
此文件已過時,請使用 Aros/開發者/Zune
在處理 Zune 或 MUI 時,一些面向物件 (OO) 程式設計知識非常受歡迎。話雖如此,所使用的原理和技術對於完全的初學者來說應該足夠容易理解。 谷歌 也可以幫助搜尋關於這個經典主題的良好入門論文。
瞭解 AROS(或 AmigaOS)API 和概念,例如使用標記列表 - 當然還有 BOOPSI,對於使用 Zune 進行開發至關重要。擁有官方“Amiga 參考手冊”(也稱為 RKM - Rom 核心手冊)的副本可能非常有用。
由於 Zune 是 'MUI' 的克隆,因此所有與之相關的文件也適用於 Zune。特別是,可以在 此處 找到最新的可用 'MUI 開發工具包'。在 LHA 存檔中,兩個文件最受關注
- MUIdev.guide,MUI 程式設計師文件。
- PSI.c,演示所有現代 MUI 實踐(如面向物件設計和動態物件建立)的應用程式的原始碼。
此外,此存檔包含 MUI 自動文件,也可以用作所有 Zune 類的參考文件。
最後,您將需要一個良好的“構建”環境來進行開發。AROS 傾向於使用 GCC,因為整個主要系統都是用它生成的。
BOOPSI 中有四個主要概念至關重要。它們是類、物件、屬性和方法,是 BOOPSI 實現的基礎。
類由其名稱、父類和排程器定義。
- 名稱
- 對於公共類,它是一個字串,以便任何系統程式都可以使用它,或者如果是僅供單個應用程式使用的私有類,則沒有名稱。
- 父類
- 所有 BOOPSI 類都從名為 rootclass 的類開始形成一個層次結構。BOOPSI 允許每個子類實現自己版本的特定父操作/函式,或者回退到其“父類”提供的版本。也稱為基類或超類。
- 排程器
- 這是 BOOPSI“類”的入口點。它為類可以執行的所有操作(稱為方法)提供一個單一訪問點,確保每個操作由正確的程式碼處理或傳遞到其“超類”。
BOOPSI 類型別是 ..
Class *
.. 也稱為 IClass。
物件是類的例項:每個物件都有其特定的資料,但同一類的所有物件都共享相同的行為。如果我們從其真實類(最派生類)到 rootclass 計算其所有父類,那麼一個物件將具有多個類。
BOOPSI 物件型別是 ..
Object *
它沒有你可以直接訪問的欄位。
屬性與每個物件的例項(私有)資料相關。
您不能直接訪問物件的內部資料,因此它提供了一組“屬性”,您可以設定或獲取,以修改其內部狀態。屬性的實現方式是標記(ULONG 值或'ed with TAG_USER)。
GetAttr() 和 SetAttrs() 用於修改物件的屬性。
AROS 還有宏 SET() 和 GET()。要修改/讀取狀態,物件將呼叫其 OM_GET 或 OM_SET“方法”。
屬性可以是以下一項或多項
* Initialization-settable (I) : the attribute can be given as parameter
at the object creation.
* Settable (S) : You can set this attribute at any time (or at least, not
only creation).
* Gettable (G) : You can get the value of this attribute.
BOOPSI 方法是一個函式,它接收物件、類和訊息作為引數
* object: the object you act on
* class: the considered class for this object.
* message: contains a method ID which determines the function to call within
a dispatcher, and is followed by its parameters.
要向物件傳送訊息,請使用 DoMethod() 呼叫。它將首先嚐試使用物件的真實類執行選定的方法。如果類實現了該方法,它將執行所需的操作。如果類沒有實現該方法,它將嘗試使用父類,以此類推,直到訊息被處理或 rootclass 被訪問(在這種情況下,未知訊息將被靜默丟棄)。
最小的類可能包含 1 個方法 - OM_NEW,儘管它的使用非常有限。
讓我們看看這個 OOP 框架的基本示例
我們將查詢 MUI 字串物件以獲取其內容
void f(Object *string)
{
IPTR result;
GetAttr(string, MUIA_String_Contents, &result);
printf("String content is: %s\n", (STRPTR)result);
}
- Object * 是 BOOPSI 物件的型別。
- IPTR 必須用於結果型別,該型別可以是整數
或指標。IPTR 始終寫入記憶體,因此使用較小的型別會導致記憶體損壞!
- 這裡我們查詢 MUI 字串物件以獲取其內容:MUIA_String_Contents,
與任何其他屬性一樣,它是一個 ULONG(它是一個標記)
Zune 應用程式更經常使用 get() 和 XGET() 宏而不是
get(string, MUIA_String_Contents, &result);
result = XGET(string, MUIA_String_Contents);
讓我們改變字串的內容
SetAttrs(string, MUIA_String_Contents, (IPTR)"hello", TAG_DONE);
* Pointers parameters must be casted to IPTR to avoid warnings.
* After the object parameter, a taglist is passed to SetAttrs and thus must
end with TAG_DONE.
你會發現 set() 宏很有用
set(string, MUIA_String_Contents, (IPTR)"hello");
但只有使用 SetAttrs() 你才能一次設定多個屬性
SetAttrs(string,
MUIA_Disabled, TRUE,
MUIA_String_Contents, (IPTR)"hmmm...",
TAG_DONE);
讓我們看看 Zune 程式中最常呼叫的方法,即在你的主迴圈中呼叫的事件處理方法
result = DoMethod(obj, MUIM_Application_NewInput, (IPTR)&sigs);
* Parameters are not a taglist, and thus don't end with TAG_DONE.
* You have to cast pointers to IPTR to avoid warnings.
首先!我知道你一定會很興奮。
讓我們研究第一個真實案例
// gcc hello.c -lmui
#include <exec/types.h>
#include <libraries/mui.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/muimaster.h>
#include <clib/alib_protos.h>
int main(void)
{
Object *wnd, *app, *but;
// GUI creation
app = ApplicationObject,
SubWindow, wnd = WindowObject,
MUIA_Window_Title, "Hello world!",
WindowContents, VGroup,
Child, TextObject,
MUIA_Text_Contents, "\33cHello world!\nHow are you?",
End,
Child, but = SimpleButton("_Ok"),
End,
End,
End;
if (app != NULL)
{
ULONG sigs = 0;
// Click Close gadget or hit Escape to quit
DoMethod(wnd, MUIM_Notify, MUIA_Window_CloseRequest, TRUE,
(IPTR)app, 2,
MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
// Click the button to quit
DoMethod(but, MUIM_Notify, MUIA_Pressed, FALSE,
(IPTR)app, 2,
MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
// Open the window
set(wnd, MUIA_Window_Open, TRUE);
// Check that the window opened
if (XGET(wnd, MUIA_Window_Open))
{
// Main loop
while((LONG)DoMethod(app, MUIM_Application_NewInput, (IPTR)&sigs)
!= MUIV_Application_ReturnID_Quit)
{
if (sigs)
{
sigs = Wait(sigs | SIGBREAKF_CTRL_C);
if (sigs & SIGBREAKF_CTRL_C)
break;
}
}
}
// Destroy our application and all its objects
MUI_DisposeObject(app);
}
return 0;
}
我們不會手動開啟庫,它會自動為我們完成。
我們使用基於宏的語言來輕鬆構建我們的 GUI。一個 Zune 應用程式始終擁有且僅擁有一個 Application 物件
app = ApplicationObject,
一個應用程式可以擁有 0 個、1 個或多個 Window 物件。最常見的情況是隻有一個
SubWindow, wnd = WindowObject,
請善意地給視窗新增標題
MUIA_Window_Title, "Hello world!",
一個視窗必須擁有且僅擁有一個子元素,通常是一個組。這個組是垂直的,這意味著它的子元素將垂直排列
WindowContents, VGroup,
一個組必須至少擁有一個子元素,這裡只是一個文字
Child, TextObject,
Zune 接受各種轉義程式碼(這裡,用於居中文字)和換行符
MUIA_Text_Contents, "\33cHello world!\nHow are you? :)",
每個 xxxObject 宏(這裡,TextObject)都必須匹配一個 End 宏
End,
讓我們向我們的組新增第二個子元素,一個按鈕!使用帶下劃線的鍵盤快捷鍵 o 表示
Child, but = SimpleButton("_Ok"),
完成組
End,
完成視窗
End,
完成應用程式
End;
那麼,誰還需要 GUI 構建器? :-)
如果應用程式樹中的任何物件都無法建立,Zune 會銷燬所有已建立的物件,並且應用程式建立失敗。否則,你將擁有一個完全可用的應用程式
if (app != NULL)
{
...
完成後,只需對你的應用程式物件呼叫 MUI_DisposeObject() 即可銷燬應用程式中當前的所有物件,並釋放所有資源
...
MUI_DisposeObject(app);
}
通知是對事件做出反應的最簡單方式。原理是什麼?我們希望在某個物件的某個屬性被設定為某個值時收到通知
DoMethod(wnd, MUIM_Notify, MUIA_Window_CloseRequest, TRUE,
在這裡,我們將監聽 Window 物件的 MUIA_Window_CloseRequest,並在該屬性被設定為 TRUE 時收到通知。那麼,當觸發通知時會發生什麼?一條訊息被髮送到一個物件,這裡我們告訴我們的 Application 在下次事件迴圈迭代時返回 MUIV_Application_ReturnID_Quit
(IPTR)app, 2,
MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
由於我們可以在此處指定任何我們想要的內容,因此我們必須告訴我們提供給 MUIM_Notify 的額外引數數量:這裡,2 個引數。
對於按鈕,我們監聽它的 MUIA_Pressed 屬性:每當按鈕被釋放時,它都會被設定為 FALSE(在按鈕被按下時做出反應是不好的做法,你可能希望在按鈕外部釋放滑鼠以取消你的操作——另外,我們希望看看按鈕被按下時它是什麼樣子)。操作與之前相同,嚮應用程式傳送一條訊息
DoMethod(but, MUIM_Notify, MUIA_Pressed, FALSE,
(IPTR)app, 2,
MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
除非你要求視窗開啟,否則它們不會開啟
set(wnd, MUIA_Window_Open, TRUE);
如果一切順利,你的視窗應該會在這個時候顯示出來。但它可能會失敗!因此,請不要忘記透過查詢屬性進行檢查,該屬性應該為 TRUE
if (XGET(wnd, MUIA_Window_Open))
讓我介紹一下我的小傢伙,理想的 Zune 事件迴圈
ULONG sigs = 0;
不要忘記將訊號初始化為 0 ... 迴圈的測試是 MUIM_Application_NewInput 方法
...
while((LONG) DoMethod(app, MUIM_Application_NewInput, (IPTR)&sigs)
!= MUIV_Application_ReturnID_Quit)
它以它必須處理的事件的訊號(來自 Wait() 的結果,或 0)作為輸入,將修改此值以放置 Zune 正在等待的訊號(用於下一個 Wait()),並將返回一個值。這種返回值機制在歷史上是唯一對事件做出反應的方式,但它很醜陋,已被棄用,轉而使用自定義類和麵向物件的設計。
迴圈的主體是空的,我們只等待訊號並處理 Ctrl-C 以退出迴圈
{
if (sigs)
{
sigs = Wait(sigs | SIGBREAKF_CTRL_C);
if (sigs & SIGBREAKF_CTRL_C)
break;
}
}
這個程式讓你開始使用 Zune,並允許你玩弄 GUI 設計,但僅此而已。
通知允許你響應你的應用程式/GUI 或任何其他物件可能觸發的事件。由於 Zune 的屬性和方法基礎,以及一些特殊屬性,大多數應用程式可以透過使用通知來實現幾乎完全自動化。
如 hello.c 中所示,你使用 MUIM_Notify 使物件/類在發生特定條件時做出反應。如果你希望你的應用程式以特定方式對事件做出反應,你可以使用以下方案之一
- 設定一個屬性。你可以自動將來自小工具等的值傳遞給其他小工具。設定值也可能觸發其他通知事件。
- 呼叫一個方法,例如
- MUIM_Application_ReturnID
- 你可以要求你的應用程式在下一次迴圈迭代時返回一個任意 ID,並在迴圈中檢查該值。這是骯髒的舊方法。
- MUIM_CallHook
- 呼叫標準 Amiga 回撥鉤子:這是一個平均的選擇,不是面向物件的,但也不那麼醜陋。
- 自定義方法
- 該方法屬於你的自定義類之一。它是最佳解決方案,因為它支援應用程式中的面向物件設計。它需要你建立自定義類,因此對於初學者或趕時間的人來說可能不是最容易的。