Consilidate custom script tray menus into a single one to declutter the tray

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Gewerd_Strauss
Posts: 24
Joined: 12 Nov 2020, 02:34

Consilidate custom script tray menus into a single one to declutter the tray

Post by Gewerd_Strauss » 25 Sep 2022, 15:44

Hello,

I am up to *counts scripts* - 24 scripts I have regularly running now. My systray is getting more and more cluttered, therefore I would like to consolidate all my running scripts (at least those with a visible tray icon) into a single icon. It is getting kinda ridiculous. Depending on workload this can rise to maybe 30-35 scripts, and then it's really just silly.

Building the Menu itself is not hard - collect all scripts, then just use the respective postmessages and methods to

- open
- reload
- edit
- suspend hotkeys
- pause
- exit

respectively. The menu should be buildable recursively, so that's not a problem (hopefully). For some of those I need to dig up how to do so, but I assume it's possible.

If it were only those, this project would be easy, and this post would not exist.

However, as I quite frequently use the tray menu as a simple way to attach a menu to a script, I would have to figure out how to collect custom tray-menu items and recreate them. After that, I would have to figure out how to actually trigger the respective item of the respective script. And that I am unsure as to how I should do so.

I know you can technically get two scripts to talk to each other by posing a hidden GUI edit control. Script A is our master tray icon script ("this one I am talking about right now"), script B one of the standard scripts running, for example a text expander.
Script B now needs a hidden GUI with an edit box and a corresponding g-label. You then target and set text to that edit control with the master script A, therefore triggering the glabel in script B. Et voilà, we had a message one-way communication. The contents could then be decoded and handled to act upon. We then either need the same applied in the opposite way as well in order to act upon changes reliably, or we say fuck it, hope it works and just change the GUIs state accordingly.

There are several issues I can think of, and probably more somewhere else:
1. This seems extremely time-consuming and complicated to do so, because you essentially have to manually create a message handler for every script, create your own distinct messages and then keep that uniform across a lot of scripts.
2. Because the label of a menu-item does not have to correspond to the label's text, and because several menu-items can trigger the same subroutine, this becomes very complicated immediately.
3. How to deal with menu items which are designed to rename themselves? I'd need to track that manually I believe?
4. Handling nested tray menu's could become complicated as well.
5. We also would need to extract and add the icons for the respective scripts, but that is possible iirc.
6. Includes will be painful to deal with.


I am sure it could be automated, but - to me at least - it seems like the wrong rabbit hole to treck down.
Or maybe I am overcomplicating it?

In addition, this could in theory allow menus on scripts who have had #NoTrayIcon set, because some of those are just hidden because they are too insignificant to justify taking up yet another place :P

Ignoring the issue above which right now makes or breakes this project, here's the overall idea:

1. Get all running scripts paths - repeated on timer, searches for ahk-windows and isolates the paths from the titles of the scripts hidden window, then updates and performs steps 2-End
2. Create a 1st-level menu item containing a submenu, named after the script
3. populate the submenu
3.1. open*, reload, edit, exit, suspend hotkeys*,pause*
3.1.1. *: not sure right now how to trigger those specific events, all others I have a more or less solid idea
3.1.2. overall should be doable with the correct messages and work-arounds. Most I have figured out from my own scriptlauncher, the rest should be doable.
3.2. custom menu options set within the respective script
3.2.1. no clue how to fetch in a sensible manner, beyond parsing all scripts for menu, tray, add. Includes pose a problem because they are a nightmare, or even impossible to track down - can be located in a variety of places, and because they integrate into the code it's painfully complicated to track down properly

---

Does any of you have input on this?
Any thoughts, tips, inputs, opinions and so on are welcome.

Sincerely,
~Gw

P.S.: I am sorry I could not figure out the damn list syntax here, I gave up at some point.

Rohwedder
Posts: 7625
Joined: 04 Jun 2014, 08:33
Location: Germany

Re: Consilidate custom script tray menus into a single one to declutter the tray

Post by Rohwedder » 26 Sep 2022, 10:30

Hallo,
no idea why you need so many scripts at the same time, but if this need is real, the solution is called AutoHotkey_H.
https://hotkeyit.github.io/v2/
AutoHotkey_H adds functionality to original AutoHotkey and offers true multi-threading using NewThread() function (v2 only) and AutoHotkey.dll.
Then you can put all the small scripts into one big and run them as multiple threads. A big advantage is, all threads can communicate with each other.
I played around with Autohotkey_H_v1 for a while, but since I don't have such requirements as you, I left it again. This post had facilitated me the Autohotkey_H beginning very much:
viewtopic.php?t=83449#p365440

User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: Consilidate custom script tray menus into a single one to declutter the tray

Post by FanaticGuru » 26 Sep 2022, 15:40

Gewerd_Strauss wrote:
25 Sep 2022, 15:44
I am up to *counts scripts* - 24 scripts I have regularly running now. My systray is getting more and more cluttered, therefore I would like to consolidate all my running scripts (at least those with a visible tray icon) into a single icon. It is getting kinda ridiculous. Depending on workload this can rise to maybe 30-35 scripts, and then it's really just silly.

The script AHK Startup does a lot of what you describe:
viewtopic.php?t=788

I am running an updated version of the script that has some more functionality. I will see about posting this update soon.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks

lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: Consilidate custom script tray menus into a single one to declutter the tray

Post by lexikos » 27 Sep 2022, 04:05

Rather than consolidating the tray icons of dozens of running processes into a single icon, it would be much more efficient to consolidate the dozens of scripts into one script process, and/or start and stop (programmatically) scripts as needed rather than having them always running. Surely you do not need dozens of script processes running at any given moment.
I would have to figure out how to collect custom tray-menu items and recreate them.
If you are writing scripts with the intention of centrally managing them with your own custom menu, building a standard tray menu within each script and then trying to replicate it externally doesn't make a lot of sense. Instead of adding items to the tray menu, each script can inform the master script of any menu items that it should have. Instead of renaming items, a script can inform the master script to do it.
I know you can technically get two scripts to talk to each other by posing a hidden GUI edit control.
GUI controls serve as a kind of host for inter-process communication only because you are able to read from and write into someone else's edit control via commands such as ControlGetText and ControlSetText. These commands are only able to work because of the messaging system that underlies all Win32 GUI applications. You don't actually need the GUI control to take advantage of it: you can just send messages to the script's main window with SendMessage, and monitor incoming messages with OnMessage.

There are examples in the documentation for using SendMessage and OnMessage to communicate between processes. You can do a lot with just a simple mechanism for sending or receiving a single string, and a bit of code for parsing the string and calling functions.

There are also higher-level methods of communicating between scripts, such as ObjRegisterActive.
1. This seems extremely time-consuming and complicated to do so, because you essentially have to manually create a message handler for every script, create your own distinct messages and then keep that uniform across a lot of scripts.
I see no reason that you would need to "manually" create something for each script. Whatever protocol you come with for communicating between scripts, it can be encapsulated into a function, set of functions or a class, and #included in each script. It is no effort to keep the messages uniform across all scripts, because the protocol would be defined within one library script.
2. Because the label of a menu-item does not have to correspond to the label's text, and because several menu-items can trigger the same subroutine, this becomes very complicated immediately.
It doesn't need to be any more complicated than the built-in Menu commands. Instead of calling the built-in commands, send a similar message to the central script.

If you wanted to replicate an existing menu that belongs to an external process, you don't need to know which "label" will be executed; you just need a way to instruct the external process to execute whatever action corresponds to the menu item. The OS itself knows nothing about AutoHotkey's labels - when you select a menu item, it posts a WM_COMMAND message with the menu item's ID. You can do the same. If you get a handle to the menu, you can retrieve all of the menu item names, icons and IDs via Win32 functions (with DllCall).

4. Handling nested tray menu's could become complicated as well.
Not very. If you get a handle to a menu, you can retrieve handles to the submenus. If you can copy one menu, you can copy a hierarchy of menus by using a reasonably simple recursive function.
5. We also would need to extract and add the icons for the respective scripts, but that is possible iirc.
I believe GetMenuItemInfo can be used to retrieve the icon (and text) of a menu item, if you have a handle to the menu.

In addition, this could in theory allow menus on scripts who have had #NoTrayIcon set,
That's already allowed. Menu Tray, Show will show the tray menu even if the script contains #NoTrayIcon (and hasn't called Menu Tray, Icon to re-enable it). You can also show a script's tray menu by posting it the AHK_NOTIFYICON message (1028) with lParam = WM_RBUTTONUP (0x205).

Code: Select all

DetectHiddenWindows on
PostMessage AHK_NOTIFYICON := 1028, 0, WM_RBUTTONUP := 0x205,, %A_ScriptFullPath% ahk_class AutoHotkey

; Only for this example, posting to itself:
Critical Off
Sleep -1
"Open" can be activated by replacing WM_RBUTTONUP with WM_LBUTTONDBLCLK (0x203).
*: not sure right now how to trigger those specific events
All of the standard tray menu items and the menu items of the main window can be triggered by posting WM_COMMAND with the appropriate parameters. The tray menu item IDs are defined in script.h as members of an enumeration (i.e. ID_TRAY_OPEN = 65299, ID_TRAY_HELP = 65300, etc.). Main menu items are defined in resource.h.
1. Get all running scripts paths - repeated on timer,
Why a timer? If you aren't showing the menu, there's no need to keep it up to date. If you show a standard menu (of the type created by the Menu command), you can't update it while it is visible. So really, you only need to update the menu immediately before showing it.
Includes pose a problem because they are a nightmare, or even impossible to track down - can be located in a variety of places, and because they integrate into the code it's painfully complicated to track down properly
#Includes are incredibly simple. Suppose that you process each line of a script with the function ProcessScriptLine(text). When you encounter the text #Include file.ahk, just open file.ahk and pass each line to ProcessScriptLine(text). That's basically how the program handles #Include (aside from tracking the filename and line number for error reporting).

Reading a file line by line and trying to reconstruct actions that it might take (or menus that it might build) could be very complex. Doing this to reconstruct a script's tray menu would not be feasible unless you set severe limits on how Menu Tray, Add is used. For instance, a script that reads a list of items from a configuration file and dynamically constructs tray menu items from them might only have a single call to Menu Tray, Add, passing variables for its parameters.

Post Reply

Return to “Ask for Help (v1)”