跳转到内容

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

华夏公益教科书