expose windows switcher capability to create float menu

I am impressed when I found this show window switcher action, it is amazing.
I heavily rely on opening multiple windows in same application, vs code, edge browser, terminals etc. I am very keen to explore implementing a "windows 10" type of task bar, that each opened application window will show as an icon and its document title, I still prefer windows 10 way to list opened same app documents in task bar rather than macbook docker.

I can achieve it if you expose the current windows switcher action as a function api call that can return icon, document title and an action for clicking, just like your current BTTActions.copyLaunchedApplicationsInFrontToBackOrder does, which fetches the latest apps. A similar style api function but to fetch your windows switcher content will be nice that I can combine it with float menu to create a windows 10 style task bar in macbook. Is it a valid request? Thanks.

yeah good idea, I just need to make the function that retrieves the windows and the function that activates a window available to Java Script. Will do that with the next alpha, maybe this evening!

EXACTLY! That will be too perfect to me to think you can do it so fast. Really look forward to it

I just exposed it without modification in v 4.769 alpha (uploading now), might already do the trick but might be a bit hard to understand the output:

async function someJavaScriptFunction() {
		return JSON.stringify(await BTTActions.getAllStandardWindows());
}

It returns an array like this:

[

  {
    "kCGWindowOwnerPID": 1665,
    "kCGWindowOwnerName": "Spotify",
    "kCGWindowAlpha": 1,
    "kCGWindowSharingState": 1,
    "kCGWindowStoreType": 1,
    "Space": -1,
    "kCGWindowNumber": 181,
    "kCGWindowName": "Spotify Premium",
    "AppIconPath": "/Applications/Spotify.app/Contents/Resources/Icon.icns",
    "RelativeScreen": 0,
    "kCGWindowMemoryUsage": 2272,
    "kCGWindowBounds": { "X": 455, "Height": 848, "Y": 201, "Width": 1189 },
    "kCGWindowLayer": 0,
    "Minimized": 1,
    "Order": 100000
  },
  {
    "kCGWindowOwnerPID": 1538,
    "kCGWindowOwnerName": "Sublime Text",
    "Order": 26,
    "kCGWindowAlpha": 1,
    "kCGWindowSharingState": 1,
    "kCGWindowStoreType": 1,
    "Space": -1,
    "kCGWindowNumber": 119,
    "AppIconPath": "/Applications/Sublime Text.app/Contents/Resources/Sublime Text.icns",
    "kCGWindowName": "[",
    "RelativeScreen": 0,
    "kCGWindowMemoryUsage": 2272,
    "kCGWindowBounds": { "X": 297, "Height": 873, "Y": 195, "Width": 1212 },
    "kCGWindowLayer": 0,
    "kCGWindowIsOnscreen": true,
    "Minimized": 2
  }
]

To activate a window use this:

async function someJavaScriptFunction() {
// replace kCGWindowNumber and kCGWindowOwnerPID with what you got above
	return await BTTActions.activateWindowWithNumberRaiseAppID(kCGWindowNumber, true, kCGWindowOwnerPID)
}

In the middle of implementation, it looks promising. I follow below logic:

  • group same app together
  • if it is the only window, show only app icon
  • if there are more than 1 window, show title, and shorten its length if it is too long
  • active window(based upon smallest order) is shown with lightgreenbackground (need enhancement)

before I proceed to tune it better, i have three questions:

  1. right now I am relying on the script executing every 5 seconds to scan the current opening windows and refresh the taskbar. is there a better event triggering way based upon activating different window, opening new window and closing exist window?
  2. is there a way to know which window is the current active window? I am right now based upon Order number, but it seems it is only working for the macbook builtin screen, i mean if i activate a window in my external monitor, its order is not change to smallest, why?
  3. Is there a way to know the position of the window is in which screen? if it is possible, i can achieve that show different content taskbar in different monitor and only show the windows in that monitor

Anyway, I already get it working to my expectation. It is super like a windows 10 taskbar now, I can trigger which window to open by clicking. I share my script for your scrutiny, please note those button template uuid are the few invisible buttons I put inside the floating menu to show different status: title/no title, activate/inactivate.

Finally I can completely hide the native mac docker bar and use instead this BTT float menu, I am happy to get rid of the dock bar!

async function someJavaScriptFunction() {
let openWindows = await BTTActions.getAllStandardWindows();
let menuItems = openWindows.map((eachWindow, index) => {
return {
"icon": path::${eachWindow.AppIconPath}::width@@40,
"title": eachWindow.kCGWindowName,
"appName": eachWindow.kCGWindowOwnerName,
"Order": eachWindow.Order,
"kCGWindowNumber": eachWindow.kCGWindowNumber,
"kCGWindowOwnerPID": eachWindow.kCGWindowOwnerPID,
"action": js::(async () => {BTTActions.activateWindowWithNumberRaiseAppID(${eachWindow.kCGWindowNumber}, true, ${eachWindow.kCGWindowOwnerPID})})(),
};
});

menuItems.sort((a, b) => a.appName.localeCompare(b.appName));

const appNameCounts = menuItems.reduce((acc, item) => {
    acc[item.appName] = (acc[item.appName] || 0) + 1;
    return acc;
}, {});

const smallestOrder = Math.min(...menuItems.map(item => item.Order));

const resultMenuItems = menuItems.map(item => {
    if (appNameCounts[item.appName] === 1) {
        return { ...item, title: "", templateItemUUID: (item.Order === smallestOrder) ? "49B1E707-AD35-485E-9C36-7CA5133B95F7" : "50322501-3495-4D48-97CC-1F1193D5F657" };
    } else {
        const newTitle = item.title.length > 8 ? item.title.slice(0, 8) + ".." : item.title;
        return { ...item, title: newTitle, templateItemUUID: (item.Order === smallestOrder) ? "45EB672D-91FB-4D8B-B04C-E9D24DCC6B97" : "A506E4C6-09CA-4490-8ADA-DFE408E29B25" };
    }
    return item;  // Keep title unchanged if there are duplicates
});
return JSON.stringify(resultMenuItems);

}

You can get the active window number by reading this variable:

let activeWin = await get_number_variable("BTTActiveWindowNum")

You could run the script e.g. with the "focused window changed" trigger in the "Automations, Named & Other Triggers" section. You can use the predefined action "Run Content Retrieval Script For Floating Menu"

I put my current work in preset sharing forum. right now i am using the window order and its windows boundary to pick the active window.

There are still a lot can be enhanced if you find any resolution for the previous asks.
Right now I have the main taskbar in builtin screen for showing all windows and differentiate their color based upon the screen they are in. In my own laptop i also duplicate and create one specifically for my external screen to only show windows in that screen.

If you have a chance to try the preset, let me know how to improve it. Thanks very much

I found it is BTT's limit right now it can not monitor mac os event and use the event as trigger, which is important to this small taskbar i am developing. I am forced to use polling to refresh its content.
I research and found that i can do it with hammerspoon, who is a very lite tool that can monitor mac os windows open/close/move/focus change. By using hammerspoon plus btt I have achieved a very responsive taskbar now.
I can not put hammerspoon config into btt preset. So I just share its script below. basically I use hammerspoon to listent to windows event then trigger a BTT named trigger, and that named trigger just run the floating menu content retrieval script. I am happy with current result. Below is the hammerspoon config file init.lua

-- Create a window filter to listen for window events
local wf = hs.window.filter.new()

-- Function to trigger the BTT named action via AppleScript
function triggerBTTNamedAction()
hs.osascript.applescript([[
tell application "BetterTouchTool"
trigger_named "reevaluate win10 taskbar"
end tell
]])
end

function isNotFromBTT(win)
local appName = win:application():name()
return appName ~= "BetterTouchTool" -- Only proceed if the app is not BTT
end

-- Function to show an alert when a window is opened
function windowOpened(win)
if isNotFromBTT(win) then
-- hs.alert.show("Window opened: " .. win:title())
triggerBTTNamedAction()
end
-- triggerBTTNamedAction()
end

-- Function to show an alert when a window is closed
function windowClosed(win)
if isNotFromBTT(win) then
-- hs.alert.show("Window closed: " .. win:title())
triggerBTTNamedAction()
end
-- triggerBTTNamedAction()
end

-- Function to show an alert when the active window changes
function windowFocused(win)
if isNotFromBTT(win) then
-- hs.alert.show("Window focused: " .. win:title())
triggerBTTNamedAction()
end

-- triggerBTTNamedAction()

end

-- Function to show an alert when a window is moved
function windowMoved(win)
if isNotFromBTT(win) then
-- hs.alert.show("Window moved: " .. win:title())
triggerBTTNamedAction()
end

-- triggerBTTNamedAction()

end

-- Subscribe to window creation (opened), destruction (closed), focus change, and movement events
wf:subscribe(hs.window.filter.windowCreated, windowOpened)
wf:subscribe(hs.window.filter.windowDestroyed, windowClosed)
wf:subscribe(hs.window.filter.windowFocused, windowFocused)
wf:subscribe(hs.window.filter.windowMoved, windowMoved)

did you see all the app change&window focus triggers in the „automations, named & other triggers section“?

oh, I really did not know that. I really can simply use "focused window did change" only to run content retrieval script. It basically covers window new/close/focus change

the only case is not covered is window move from one screen to another screen, since right now the taskbar shows where each window is where, I hope this event trigger is also possible in BTT itself.
But yeh, it is almost all can be done in BTT.