Apple 脚本:返回菜单中的功能

Apple Script: Go back-function in menus

我不太精通 AppleScript,所以也许有人可以在这里提供帮助:

我有几个菜单。比方说,菜单 A 有三个选项,这三个选项导致菜单 B1、B2、B3,具体取决于在菜单 A 中选择的选项。B 菜单导致 C 菜单,依此类推。我有点烦恼,每次我“取消”时,我都需要从头开始。 “返回”按钮在这种情况下会非常有用。

我知道 choose from list-函数只允许是和否,可以这么说,但是有没有可能以某种方式实现后退按钮?

我的代码看起来像这样(我们在菜单 B 中):

if MenuB is false then error number -128 -- user canceled

然后我尝试通过以下方式实现“返回”功能:

if MenuB is false then set MenuA to (choose from list {"MenuB1", "MenuB2", "MenuB3"} with prompt "Menu A" default items "None" OK button name {"Go"} cancel button name {"Quit"})

然而,这会创建一个新菜单,而不是指在菜单 B 脚本之前创建的菜单。

Tl;dr:那么是否可以在不创建新菜单的情况下引用现有菜单 based on choose from list。或者有没有办法通过 AppleScript 在菜单中实现“后退”按钮?

期待您的想法!

代码摘录:

# Main Menu
set MainMenu to (choose from list {"Item1", "Item2", "Item3"} with prompt "Main Menu" default items "None" OK button name {"Go"} cancel button name {"Quit"})
if MainMenu is false then error number -128 -- user canceled

# Button 1: Item 1
if MainMenu contains "Item1" then
    
    # Sub-Button 1: Item 1
    set SubButton to (choose from list {"Create", "Rename"} with prompt "Settings" default items "None" OK button name {"Choose"} cancel button name {"Quit"})
    if SubButton is false then error number -128 -- user canceled
    
    # Sub-Sub-Button 1: Item 1
    if SubButton contains "Create" then
(...)

在不知道菜单的确切数量以及它们如何交互的情况下,最简单的方法可能是实现一个堆栈,其中可以根据对话框结果将项目推入或弹出堆栈。然后这只是组织和连接菜单的问题,因为在 运行 失去耐心之前,您可能会 运行 出栈 space。尝试仅使用一长串 if 语句在嵌套或互连的菜单中来回导航会导致阴暗面。

为了使菜单列表更易于管理,可以将它们放入记录之类的集合对象中,而不是对对话语句进行硬编码,记录键用于跟踪当前和上一个列出。从每个 choose from list 对话框中,可以将当前选择推到堆栈顶部,如果返回,则从中弹出前一个项目。

以下示例中使用了一点点 AppleScriptObjC 来动态查找记录键,因为常规 AppleScript 无法做到这一点。列表包含各种菜单项,它们也用于查找其他菜单列表的键 - 用管道围绕记录键允许它们具有 spaces 和标点符号,因此几乎任何文本都可以用作关键。使用菜单项文本作为记录键还允许绕圈和跳转(示例脚本也演示了这一点),所以在布置菜单连接时要注意。最终的最终值保存在不同的记录中,并且是单个项目(字符串或列表)。最终结果也用于选择对话框中以进行验证并允许返回。

请注意,使用时髦的逗号位置是为了让记录格式更易于阅读。

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

property stack : {"base"} -- LIFO, starting with key of the base menu
property menus : ¬
   {base:{"menu A", "menu B", "menu C"} ¬
      , |menu A|:{"subMenu A1", "subMenu A2", "subMenu A3"} ¬
      , |menu B|:{"subMenu B1", "subMenu B2", "subMenu B3"} ¬
      , |menu C|:{"A key", "A little longer key", "The quick brown fox jumped over the lazy dog's key"} ¬
      , |subMenu A1|:{"Action1", "Action2", "Action3"} ¬
      , |subMenu A2|:{"Action4", "Action5", "Action6"} ¬
      , |subMenu A3|:{"Action7", "Action8", "Action9"} ¬
      , |subMenu B1|:{"menu A", "menu B", "menu C"} ¬
      , |subMenu B2|:{"subMenu A1", "subMenu A2", "subMenu A3"} ¬
      , |subMenu B3|:{"A key", "A little longer key", "The quick brown fox jumped over the lazy dog's key"} ¬
      , |A key|:{"Action1", "Action2", "Action3"} ¬
      , |A little longer key|:{"Action4", "Action5", "Action6"} ¬
      , |The quick brown fox jumped over the lazy dog's key|:{"Action7", "Action8", "Action9"}}
property actions : ¬
   {Action1:"Action one", Action2:"Action two", Action3:"Action three", Action4:"Action four", Action5:"Action five", Action6:"Action six", Action7:"Action seven", Action8:"Action eight", Action9:"Action 9"}

on run -- example
   set menuDict to current application's NSDictionary's dictionaryWithDictionary:menus
   set actionDict to current application's NSDictionary's dictionaryWithDictionary:actions
   set cancelName to "Quit"
   repeat
      set tos to first item of stack
      set choices to (menuDict's objectForKey:tos) -- get menu
      if choices is missing value then -- handle missing key
         log "menu key " & quoted form of tos & " not found, trying actions"
         set choices to (actionDict's objectForKey:tos) -- try actions
         if choices is missing value then log "actions key " & quoted form of tos & " not found"
      end if
      set theChoice to (choose from list (choices as list) default items {"msng"} cancel button name cancelName) -- select missing value
      if theChoice is false then
         if (count stack) is 1 then error number -128 -- cancel
         set stack to rest of stack -- pop
         if (count stack) is 1 then set cancelName to "Quit" -- last one
      else
         if (count (choices as list)) is 1 then exit repeat -- final action result selected
         set beginning of stack to theChoice as text -- push
         set cancelName to "Go Back"
      end if
   end repeat
   #showResult(stack, actionDict) -- uncomment to show the result
   runWorkflow(((actionDict's objectForKey:(first item of stack)) as text))
end run

to showResult(stack, dictionary) -- show the final result action
   set {tempTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, " > "}
   set keys to (reverse of stack) as text
   set AppleScript's text item delimiters to tempTID
   set value to quoted form of ((dictionary's objectForKey:(first item of stack)) as text)
   if value is quoted form of "missing value" then set value to value & return & return & "A menu or actions key was not found - see log."
   display dialog "Key sequence:  " & keys & return & "Final value:  " & value with title "Final Result" buttons "OK" default button 1
end showResult

to runWorkflow(workflowName) -- run an Automator workflow
   set basepath to POSIX path of (path to desktop) -- change as desired
   set workflowpath to basepath & workflowName -- POSIX path/name to add to the basePath
   set command to "/usr/bin/automator " & quoted form of workflowpath
   set output to (do shell script command)
   -- do whatever with the output
end runAction