Hammerspoon / hammerspoon

Staggeringly powerful macOS desktop automation with Lua

Home Page:http://www.hammerspoon.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Alerts from hs.dialog do not get keyboard focus when launched from hs.hotkey

jdv85 opened this issue · comments

When using hs.dialog.blockAlert() and hs.dialog.textPrompt() from a hs.hotkey.bind() callback, the alerts do not get keyboard focus. This means I have to reach for my mouse to enter text, which defeats the purpose of the hotkey.

Steps to reproduce

  1. Put this code in init.lua:
ctrlCmdOpt = { 'ctrl', 'cmd', 'alt' }
hs.hotkey.bind(ctrlCmdOpt, "a", function() 
	hs.dialog.blockAlert("Test alert", "alert ...", "Button 1", "Button 2")
end)
hs.hotkey.bind(ctrlCmdOpt, "t", function() 
	hs.dialog.textPrompt("Test prompt", "Enter something", "", "OK", "Cancel")
end)
  1. Restart Hammerspoon
  2. Disable "Show Dock icon" in the Hammerspoon preferences
  3. Make sure the Hammerspoon is not the frontmost app
  4. Use one of the hotkeys (ctrl + cmd + alt + a / t) to bring up a dialog

Expected result

The appearing dialog should have keyboard focus:

Screenshot 2022-05-23 at 10 30 50

Actual result

The first time a dialog is launched after Hammerspoon starts, the dialog does not get keyboard focus:

Screenshot 2022-05-23 at 10 30 40

With "Show Dock icon" enabled, the dialogs do get focus, but they don't appear above the currently active window.

I thought this might be fixed by simply adding [[alert window] makeKeyAndOrderFront:nil]; in textPrompt() in libdialog.m but it does not work. It reports the following exception from Cocoa:

[General] ERROR: Setting <NSTextField: 0x61800001b880> as the first responder for window <NSPanel: 0x614000076c40>, but it is in a different window ((null))! This would eventually crash when the view is freed. The first responder will be set to nil.

Another approach that did work for me is telling the NSApplication instance to activate:

From b090a8b41a00c999e1d3361c7c341932e781965f Mon Sep 17 00:00:00 2001
From: Jonas Due Vesterheden <jonasduevesterheden@gmail.com>
Date: Mon, 23 May 2022 11:01:54 +0200
Subject: [PATCH] #3218 possible fix

---
 extensions/dialog/libdialog.m | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/extensions/dialog/libdialog.m b/extensions/dialog/libdialog.m
index 5abac1be..f7e9a14a 100755
--- a/extensions/dialog/libdialog.m
+++ b/extensions/dialog/libdialog.m
@@ -749,8 +749,9 @@ static int textPrompt(lua_State *L) {
     [alert setAccessoryView:input];
 
     // Focus on text input:
-    [[alert window] setInitialFirstResponder: input];
-
+    [[alert window] setInitialFirstResponder:input];
+    [NSApp activateIgnoringOtherApps:YES];
+    
     // Show alert:
     NSInteger result = [alert runModal];
 
-- 
2.35.1

I am not sure if this would be an acceptable fix?

May be this is a workaround/solution:

local ctrlCmdOpt = { 'ctrl', 'cmd', 'alt' }
hs.hotkey.bind(ctrlCmdOpt, "t", function()
  local lastApplication = hs.application.frontmostApplication()
  hs.application.get("Hammerspoon"):activate()
  hs.dialog.textPrompt("Test prompt", "Enter something", "", "OK", "Cancel")
  if lastApplication then
    lastApplication:activate()
  end
end)

I use this sometimes for the inputs in Hammerspoon.

Here's a utility function for doing something for the above workaround (generalized for any application):

--- @param app string|table
--- @param fn function
--- @return any
function doWithActivated(app, fn)
  local app_obj = app
  if type(app) == "string" then
    app_obj = hs.application.get(app)
  end
  local last_app = hs.application.frontmostApplication()
  app_obj:activate()
  local retval = {fn()}
  if last_app then 
    last_app:activate()
  end
  return table.unpack(retval)
end

(yes, the type annotations should use generics, but they're broken in the lua-language-server in vscode)