跳轉到內容

Aros/開發者/Zune/初學者

來自華夏公益教科書,開放書籍,開放世界
Aros 華夏公益教科書的導航欄
Aros 使用者
Aros 使用者文件
Aros 使用者常見問題解答
Aros 使用者應用程式
Aros 使用者 DOS Shell
Aros/使用者/AmigaLegacy
Aros 開發文件
Aros 開發者文件
從 AmigaOS/SDL 移植軟體
面向 Zune 初學者
Zune .MUI 類
面向 SDL 初學者
Aros 開發者構建系統
特定平臺
Aros x86 完整系統 HCL
Aros x86 音訊/影片支援
Aros x86 網路支援
Aros Intel AMD x86 安裝
Aros 儲存支援 IDE SATA 等
Aros Poseidon USB 支援
x86-64 支援
Motorola 68k Amiga 支援
Linux 和 FreeBSD 支援
Windows Mingw 和 MacOSX 支援
Android 支援
Arm Raspberry Pi 支援
PPC Power Architecture
其他
Aros 公共許可證

此文件已過時,請使用 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 實現的基礎。

類由其名稱、父類和排程器定義。

名稱
對於公共類,它是一個字串,以便任何系統程式都可以使用它,或者如果是僅供單個應用程式使用的私有類,則沒有名稱。
父類
所有 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.

Hello world

[編輯 | 編輯原始碼]

截圖“Hello World”

首先!我知道你一定會很興奮。

讓我們研究第一個真實案例

      // 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 建立

[編輯 | 編輯原始碼]

我們使用基於宏的語言來輕鬆構建我們的 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 回撥鉤子:這是一個平均的選擇,不是面向物件的,但也不那麼醜陋。
自定義方法
該方法屬於你的自定義類之一。它是最佳解決方案,因為它支援應用程式中的面向物件設計。它需要你建立自定義類,因此對於初學者或趕時間的人來說可能不是最容易的。

Zune 示例

[編輯 | 編輯原始碼]

參見 Aros/Developer/Zune/Examples

華夏公益教科書