# Menu/MenuBar 对象

用于修改和显示菜单或菜单栏.

    class Menu extends Object

菜单对象用于定义, 修改和显示弹出菜单. Menu(), MenuFromHandle 和 A_TrayMenu 返回此类对象.

    class MenuBar extends Menu

MenuBar 对象用于定义和修改与 Gui.MenuBar 一起使用的菜单栏. 它们是使用 MenuBar() 创建的. 如果给定菜单栏句柄, 则 MenuFromHandle 返回此类型的对象.

下面使用 "MyMenu" 作为任何 Menu 对象的占位符, 因为 "Menu" 就是类本身.

除了从 Object 继承的方法和属性外, Menu 对象还具有以下预定义的方法和属性.

## 目录

- 静态方法:
  - Call: 创建一个新的 Menu 或 MenuBar 对象.
- 方法:
  - Add: 添加或修改菜单项.
  - AddStandard: 添加标准托盘菜单项.
  - Check: 在菜单项旁边添加一个可见的选中标记.
  - Delete: 删除一个或所有菜单项.
  - Disable: 将菜单项更改为灰色, 表示用户无法选择它.
  - Enable: 允许用户再次选择以前禁用的菜单项(灰色).
  - Insert: 在指定的项之前插入一个新项.
  - Rename: 重命名菜单项.
  - SetColor: 改变菜单的背景颜色.
  - SetIcon: 设置显示在菜单项旁边的图标.
  - Show: 显示菜单.
  - ToggleCheck: 切换菜单项旁边的复选标记.
  - ToggleEnable: 启用或禁用菜单项.
  - Uncheck: 移除菜单项上的复选标记(如果有的话).
- 属性:
  - ClickCount: 检索或设置必须点击多少次托盘图标才能选择其默认菜单项.
  - Default: 检索或设置默认菜单项.
  - Handle: 检索菜单的 Win32 句柄.
- 常规:
  - MenuItemName
  - Win32 菜单
  - 备注
  - 相关
  - 示例

## 静态方法

### Call

创建新的 Menu 或 MenuBar 对象.

    MyMenu := Menu()
    MyMenuBar := MenuBar()

### Add

添加或修改菜单项.

    MyMenu.Add(MenuItemName, Function-or-Submenu, Options)

MenuItemName

  类型: 字符串

  要显示在菜单项上的文字, 或者要修改的现有项的位置&. 请参阅 MenuItemName.

Function-or-Submenu

  类型: 函数对象或 Menu

  当选择菜单项时, 函数对象作为新线程调用, 或对 Menu 对象的引用, 作为子菜单使用.

  创建新项目时需要此参数, 但在更新现有项目的 Options 时, 此参数是可选的.

  该函数应该接受以下参数:

      FunctionName(ItemName, ItemPos, MyMenu)

Options

  类型: 字符串

  如果没有省略, Options 必须是由下列一个或多个选项组成的以空格或制表符分隔的列表:

  选项 | 描述
  --- | ---
  Pn | 指定 n 为菜单项的线程优先级, 例如 `P1`. 如果在添加菜单项时省略该选项, 优先级将为 0, 这是标准的默认值. 如果在更新菜单项时省略该选项, 则该项目的优先级将不会改变. 请使用十进制(不是十六进制) 数字作为优先级.
  +Radio | 如果该项目被选中, 则使用个空心圆代替复选标记.
  +Right | 该项目在菜单栏内右对齐. 这只适用于菜单栏, 不适用于弹出式菜单或子菜单.
  +Break | 该项在弹出式菜单中开始一个新的列.
  +BarBreak | 同上, 但在列之间有一条分界线.

  加号(+) 是可选的, 可以用减号(-) 代替来删除选项, 如 `-Radio`. 选项不区分大小写.

  要改变一个现有项目的选项而不影响它的回调或子菜单, 只需省略 Callback-or-Submenu 参数.

### 备注

这是一个多用途的方法, 它可以添加一个菜单项, 用新的子菜单或回调更新一个菜单项, 或者将一个菜单项从普通项转换为子菜单(反之亦然). 如果 MenuItemName 还不存在, 它将被添加到菜单中. 否则, 将用新指定的 Callback-or-Submenu 和/或 Options 更新 MenuItemName.

要添加菜单分隔线, 请省略所有三个参数.

Add 总是在菜单底部添加新的菜单项, 但 Insert 可以用来在现有的自定义菜单项之前插入一个项目.

### AddStandard

添加标准的托盘菜单项.

    MyMenu.AddStandard()

此方法可以与托盘菜单或任何其他菜单一起使用.

标准项目被插入到任何现有项目之后. 任何已经在菜单中的标准项不会被重复, 但任何缺失的项目会被添加. 下表显示了在空菜单上调用 AddStandard 后标准项的名称和位置:

编译后的脚本默认只包含最后三个. 只有在 A_AllowMainWindow 为 1 时, 调用 AddStandard 才会包含 `&Open`(在这种情况下, 在第三列显示的位置上加 1). 如果托盘菜单中包含标准项目, 每当改变 A_AllowMainWindow 时, 就会插入或移除 `&Open`. 对于其他菜单, 如果 A_AllowMainWindow 为 0, 则 `&Open` 没有效果.

每个标准项都有一个内部的菜单项 ID, 与它所执行的功能相对应, 但也可以像其他菜单项一样被修改或删除. AddStandard 通过 ID 而不是名称来检测现有的项目. 如果使用 Add 方法来改变与标准菜单项相关联的回调函数, 那么它将被分配一个新的唯一 ID, 并且不再被认为是一个标准项.

将 `&Open` 项添加到托盘菜单中会导致它成为默认项, 如果还没有的话.

### Check

在 MenuItemName 旁边添加一个可见的复选标记(如果还没有).

    MyMenu.Check(MenuItemName)

MenuItemName

  类型: 字符串

  菜单项的名称或位置. 请参阅 MenuItemName.

### Delete

删除一个或所有自定义菜单项.

    MyMenu.Delete(MenuItemName)

MenuItemName

  类型: 字符串

  菜单项的名称或位置. 请参阅 MenuItemName.

如果省略 MenuItemName, 则所有项目都会从菜单中删除, 使菜单为空. 空菜单仍然存在, 因此, 任何使用它作为子菜单的其他菜单将保留这些子菜单.

要删除一个分隔线, 请通过它在菜单中的位置来识别它. 例如, 如果分隔符前面有两个项目, 则使用 `MyMenu.Delete("3&")`.

如果删除了 default 菜单项, 其效果将类似于设置了 `Menu.Default := ""`.

### Disable

将 MenuItemName 更改为灰色, 表示用户无法选择它.

    MyMenu.Disable(MenuItemName)

MenuItemName

  类型: 字符串

  菜单项的名称或位置. 请参阅 MenuItemName.

### Enable

允许用户再次选择 MenuItemName, 如果它之前被禁用(灰色).

    MyMenu.Enable(MenuItemName)

MenuItemName

  类型: 字符串

  菜单项的名称或位置. 请参阅 MenuItemName.

### Insert

在指定的项之前插入一个新项.

    Menu.Insert(ItemToInsertBefore, NewItemName, Callback-or-Submenu, Options)

ItemToInsertBefore

  类型: 字符串

  现有项目的名称或位置&, 介于 1 和当前自定义项目数加 1 之间(遵循与 MenuItemName 相同的规则). 也可以通过省略 ItemToInsertBefore 来追加项目.

NewItemName

  类型: 字符串

  要在菜单项上显示的文字. 与 Add 方法不同, 它不能是一个位置.

除了 Insert 会创建一个新的项目, 即使 NewItemName 与现有项目的名称相匹配, 其余的参数与 Add 方法一样.

### Rename

重命名 MenuItemName 为 NewName.

    MyMenu.Rename(MenuItemName , NewName)

MenuItemName

  类型: 字符串

  菜单项的名称或位置. 请参阅 MenuItemName.

NewName

  类型: 字符串

  新名称. 如果为空或省略, MenuItemName 将被转换为分隔线.

菜单项的当前回调或子菜单不变.

通过指定分隔线的位置, 如 `"1&"` for MenuItemName 和一个非空白名称 for NewName, 然后使用 Add 方法给项目一个回调或子菜单, 可以将分隔线转换为一个普通项目.

### SetColor

改变菜单的背景颜色为 ColorValue.

    MyMenu.SetColor(ColorValue, ApplyToSubmenus)

ColorValue

  类型: 字符串或整数

  16 种主要的 HTML 颜色名称之一, 十六进制的 RGB 颜色字符串(0x 前缀是可选的), 或一个纯数字 RGB 颜色值. 省略 ColorValue(或指定一个空字符串或单词 "Default") 来恢复菜单的默认颜色. 示例值: `"Silver"`, `"FFFFAA"`, `0xFFFFAA`, `"Default"`.

ApplyToSubmenus

  类型: 布尔值

  如果颜色应该应用于这个菜单的所有子菜单, 则为 1(true), 否则为 0(false). 默认值为 1(true).

### SetIcon

设置显示在 MenuItemName 旁边的图标.

    MyMenu.SetIcon(MenuItemName, FileName , IconNumber, IconWidth)

MenuItemName

  类型: 字符串

  菜单项的名称或位置. 请参阅 MenuItemName.

FileName

  类型: 字符串

  图标或图像文件的路径. 关于支持的格式列表, 请参阅 Picture 控件.

  位图或图标句柄可用来代替文件名. 例如, `"HICON:" handle`.

  省略 FileName 或指定一个空字符串或 `"*"` 来删除项目的当前图标.

IconNumber

  类型: 整数

  要使用文件中第一个图标以外的图标组, 请指定其编号为 IconNumber(如果省略, 则默认为 1). 如果 IconNumber 为负数, 它的绝对值被认为是一个可执行文件中图标的资源 ID.

IconWidth

  类型: 整数

  图标的宽度. 如果 IconNumber 表示的图标组包含多个图标尺寸, 则使用最接近的尺寸, 并将图标缩放到指定的尺寸. 请参阅示例部分了解使用方法.

目前, 在设置图标时必须指定 "实际大小" 以保持透明度, 例如 `MyMenu.SetIcon "My menu item", "Filename.png",, 0`.

位图或图标句柄可用来代替文件名. 例如, `"HBITMAP:" handle`.

### Show

显示菜单, 允许用户使用箭头按键, 菜单快捷键(下划线字母), 或鼠标来选择一个项目.

    MyMenu.Show(X, Y)

X, Y

  类型: 整数

  显示菜单的坐标. 如果 X 和 Y 都省略, 则菜单显示在鼠标光标的当前位置. 如果只省略其中一个, 则用鼠标光标的位置代替省略的这个. X 和 Y 默认是相对于活动窗口的客户端区域. 要覆盖这个默认值, 请使用 `CoordMode "Menu", Mode` 或 `A_CoordModeMenu := Mode`.

任何弹出式菜单都可以被显示, 包括子菜单和托盘菜单. 但是, 如果 Menu 是 MenuBar 对象, 则抛出异常.

### ToggleCheck

如果没有复选标记, 则添加; 否则, 就移除.

    MyMenu.ToggleCheck(MenuItemName)

MenuItemName

  类型: 字符串

  菜单项的名称或位置. 请参阅 MenuItemName.

### ToggleEnable

如果之前已经启用, 则禁用 MenuItemName; 否则启用.

    MyMenu.ToggleEnable(MenuItemName)

MenuItemName

  类型: 字符串

  菜单项的名称或位置. 请参阅 MenuItemName.

### Uncheck

移除菜单项上的复选标记(如果有的话).

    Menu.Uncheck(MenuItemName)

MenuItemName

  类型: 字符串

  菜单项的名称或位置. 请参阅 MenuItemName.

## 属性

### ClickCount

检索或设置必须点击多少次托盘图标才能选择其默认菜单项.

    MyMenu.ClickCount := Count

Count

  类型: 整数

  指定 1, 允许单次点击选择托盘菜单的默认菜单项. 指定 2 可返回默认行为(双击).

例如: `A_TrayMenu.ClickCount := 1`

### Default

检索或设置默认菜单项.

    CurrentDefault := MyMenu.Default

CurrentDefault

  类型: 字符串

  默认菜单项的名称, 如果没有默认项, 则为空字符串.

    MyMenu.Default := MenuItemName

MenuItemName

  类型: 字符串

  菜单项的名称或位置. 请参阅 MenuItemName.

  如果 MenuItemName 是一个空字符串, 则没有默认项.

设置默认项会使该项的字体变粗(在托盘菜单以外的其他菜单中设置默认项目前没有效果). 当用户双击托盘图标时, 其默认的菜单项就会被启动(即使该项目被禁用). 如果没有默认项, 双击没有效果.

托盘菜单的默认项目最初是 `&Open`, 如果存在的话. 通过调用 AddStandard 将 `&Open` 添加到托盘菜单中或改变 A_AllowMainWindow 如果没有默认项, 也会使其成为默认项.

如果删除了默认项, 则菜单中没有默认项.

### Handle

返回一个 Win32 menu 的句柄(`HMENU` 类型的句柄), 必要时构造它.

    MyMenu.Handle

返回的句柄只在 Win32 菜单被销毁之前有效, 这通常发生在菜单对象被释放时. 一旦菜单被销毁, 操作系统可以将句柄值重新分配给脚本或任何其他程序随后创建的任何菜单.

## MenuItemName

菜单项的名称或位置. 一些通用规则适用于所有使用该参数的方法:

要为菜单项名称的某个字母加下划线, 在这个字母前加一个 & 符号. 当菜单显示出来时, 此项可以通过按键盘上对应的按键来选中. 要显示原义的 & 符号, 指定连续的两个 & 号, 如此例所示: `"Save && Exit"`

当引用到一个已存在的菜单或菜单项时, 其名称不区分大小写但 & 号不能少. 例如: `"&Open"`

菜单项的名称最多可以有 260 个字符.

要通过菜单中的位置来识别一个现有的项目, 请在项目的位置后写上一个 &. 例如, `"1&"` 表示第一个项目.

## Win32 Menus

Windows 提供了一组函数和通知, 用于创建, 修改和显示具有标准外观和行为的菜单. 我们把由这些函数之一创建的菜单称为 Win32 menu.

当项目被添加到菜单中或修改时, 每个项目的名称和其他属性都存储在 Menu 对象. 当菜单或它的父菜单第一次连接到 GUI 或显示时, 就会构造一个 Win32 menu. 当菜单对象被删除时, 它就会被自动销毁(当它的引用计数达到零时就会发生).

Menu.Handle 返回一个 Win32 menu 的句柄(`HMENU` 类型的句柄), 必要时构造它.

任何由 Win32 函数直接对菜单进行的修改都不会反映在脚本的 Menu 对象中, 所以如果一个项目被内置方法修改, 可能会丢失.

当每个菜单项第一次被添加到菜单中时, 它被分配了一个 ID. 脚本不能依赖一个项目收到一个特定的 ID, 但是可以通过使用 GetMenuItemID 来检索一个项目的 ID, 如示例 #5 所示. 这个 ID 不能用于 Menu 对象, 但可以用于各种 Win32 函数.

## 备注

菜单通常看起来像这样:

如果一个菜单完全变空 -- 比如使用 `MyMenu.Delete()` -- 它就不能被显示. 如果托盘菜单变成空的, 右击和双击托盘图标将没有任何效果(在这种情况下, 通常最好使用 #NoTrayIcon).

如果一个菜单项的回调已经在运行, 而用户再次选择同一个菜单项, 则会创建一个新的线程来运行同样的回调, 从而中断之前的线程. 如果要把这些事件缓冲到以后, 可以使用 Critical 作为回调的第一行(然而, 这也会缓冲/延迟其他线程, 比如按下热键).

每当通过菜单项调用一个函数时, 它都会以默认值(如 SendMode) 重新开始. 这些默认值可以在脚本启动时更改.

当建立一个内容不总是相同的菜单时, 一种方法是将所有这样的菜单项指向同一个函数, 并让该函数参考它的参数来决定采取什么行动. 另外, 可以使用函数对象, 闭包或胖箭头函数将一个或多个值或变量绑定到菜单项的回调函数上.

## 相关

GUI, 线程, Thread, Critical, #NoTrayIcon, 函数, Return, SetTimer

## 示例

添加一个新菜单项到托盘图标菜单的底部.

    A_TrayMenu.Add()  ; 创建分隔线.
    A_TrayMenu.Add("Item1", MenuHandler)  ; 创建新菜单项.
    Persistent

    MenuHandler(ItemName, ItemPos, MyMenu) {
        MsgBox "You selected " ItemName " (position " ItemPos ")"
    }

创建一个弹出菜单, 当用户按下热键时显示.

    ; 添加一些菜单项来创建弹出菜单.
    MyMenu := Menu()
    MyMenu.Add "Item 1", MenuHandler
    MyMenu.Add "Item 2", MenuHandler
    MyMenu.Add  ; 添加分隔线.

    ; 添加子菜单到上面的菜单中.
    Submenu1 := Menu()
    Submenu1.Add "Item A", MenuHandler
    Submenu1.Add "Item B", MenuHandler

    ; 创建第一个菜单的子菜单(右箭头指示符). 当用户选择它时会显示第二个菜单.
    MyMenu.Add "My Submenu", Submenu1

    MyMenu.Add  ; 在子菜单下添加分隔线.
    MyMenu.Add "Item 3", MenuHandler  ; 在子菜单下添加另一个菜单项.

    MenuHandler(Item, *) {
        MsgBox "You selected " Item
    }

    #z::MyMenu.Show  ; 即按下 Win-Z 热键来显示菜单.

演示各种菜单对象成员.

    #SingleInstance
    Persistent
    tray := A_TrayMenu ; 为了方便.
    tray.delete ; 删除标准项目.
    tray.add ; 分隔线
    tray.add "TestToggleCheck", TestToggleCheck
    tray.add "TestToggleEnable", TestToggleEnable
    tray.add "TestDefault", TestDefault
    tray.add "TestAddStandard", TestAddStandard
    tray.add "TestDelete", TestDelete
    tray.add "TestDeleteAll", TestDeleteAll
    tray.add "TestRename", TestRename
    tray.add "Test", Test

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    TestToggleCheck(*)
    {
        tray.ToggleCheck TestToggleCheck
        tray.Enable "TestToggleEnable" ; 由于自己无法撤销禁用, 所以还能进行下一次测试.
        tray.add "TestDelete", TestDelete ; 类似于上面.
    }

    TestToggleEnable(*)
    {
        tray.ToggleEnable "TestToggleEnable"
    }

    TestDefault(*)
    {
        if tray.default = "TestDefault"
            tray.default := ""
        else
            tray.default := "TestDefault"
    }

    TestAddStandard(*)
    {
        tray.addStandard
    }

    TestDelete(*)
    {
        tray.delete "TestDelete"
    }

    TestDeleteAll(*)
    {
        tray.delete
    }

    TestRename(*)
    {
        static OldName := "", NewName := ""
        if NewName != "renamed"
        {
            OldName := "TestRename"
            NewName := "renamed"
        }
        else
        {
            OldName := "renamed"
            NewName := "TestRename"
        }
        tray.rename OldName, NewName
    }

    Test(Item, *)
    {
        MsgBox 'You selected "' Item '"'
    }

添加图标到其菜单项.

    FileMenu := Menu()
    FileMenu.Add("Script Icon", MenuHandler)
    FileMenu.Add("Suspend Icon", MenuHandler)
    FileMenu.Add("Pause Icon", MenuHandler)
    FileMenu.SetIcon("Script Icon", A_AhkPath, 2) ; 使用文件中的第二个图标组
    FileMenu.SetIcon("Suspend Icon", A_AhkPath, -206) ; 使用资源标识符 206 表示的图标
    FileMenu.SetIcon("Pause Icon", A_AhkPath, -207) ; 使用资源标识符 207 表示的图标
    MyMenuBar := MenuBar()
    MyMenuBar.Add("&File", FileMenu)
    MyGui := Gui()
    MyGui.MenuBar := MyMenuBar
    MyGui.Add("Button",, "Exit This Example").OnEvent("Click", (*) => WinClose())
    MyGui.Show

    MenuHandler(*) {
        ; 在这个例子中, 菜单项没有任何作用.
    }

报告菜单中的项目数量和最后一项的 ID.

    MyMenu := Menu()
    MyMenu.Add "Item 1", NoAction
    MyMenu.Add "Item 2", NoAction
    MyMenu.Add "Item B", NoAction

    ; 检索菜单中的项目数.
    item_count := DllCall("GetMenuItemCount", "ptr", MyMenu.Handle)

    ; 检索最后一个项目的 ID.
    last_id := DllCall("GetMenuItemID", "ptr", MyMenu.Handle, "int", item_count-1)

    MsgBox "MyMenu has " item_count " items, and its last item has ID " last_id

    NoAction(*) {
        ; 什么都不做.
    }

dftg