Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

moving/resizing electron based apps (slack, vscode) #2316

Open
Madd0g opened this issue Mar 9, 2020 · 28 comments
Open

moving/resizing electron based apps (slack, vscode) #2316

Madd0g opened this issue Mar 9, 2020 · 28 comments

Comments

@Madd0g
Copy link

Madd0g commented Mar 9, 2020

All the electron applications I use (actually slack and vscode, but not spotify) have the same problem moving/resizing within a screen and between screens.

I have to press the shortcut key multiple times and every time it does part of the movement (resize only, then on next press it'll put the window in the right place). Also, only those apps are animating the resize, even though I have hs.window.animationDuration = 0.

All the shortcuts work in Finder/Spotify, but as soon as I switch to vscode, it starts animating and not fully working.

I thought it was just hs.grid based code, but seems like it happens with win:maximize() and win:moveToScreen().

Thanks

@ogirginc
Copy link

This might not be exclusive to Electron apps. I have a similar problem with Firefox Developer Edition when using it with or without an external display.

The example above is me trying to move FF from external to MBP's screen:

resizing-with-external-monitor

@Madd0g
Copy link
Author

Madd0g commented Mar 10, 2020

You're right, it happens with other apps as well. It's happening with iTerm for me.

The animation is one weird symptom and another is that my logic for moving the app between screens doesn't work properly, like it loses the screen, or the window/screen relationship.

@ogirginc
Copy link

Are you using a spoon or the API itself? I am using WindowGrid spoon which does depend on http://www.hammerspoon.org/docs/hs.grid.html

@cmsj
Copy link
Member

cmsj commented Mar 11, 2020

Sometimes this kind of thing can be caused by apps having a different name in their Info.plist from the name of their .app bundle. If that’s the case here, maybe we can figure out where we’re using the wrong one, and try to fix it.

@Madd0g
Copy link
Author

Madd0g commented Mar 11, 2020

I'm using both hs.grid and simpler window methods like win:maximize() and both have the same problem

@ogirginc
Copy link

ogirginc commented Mar 12, 2020

@cmsj do you need anything from me? Console log or any other info? I am not familiar with macOS internals & Lua but would love to help. :)

@cmsj
Copy link
Member

cmsj commented Mar 12, 2020

Are there any errors in the Hammerspoon Console when the movements fail to trigger?

@Madd0g
Copy link
Author

Madd0g commented Mar 12, 2020

I get no errors.

In addition to simple win:maximize() having this problem, I have a big pile of custom code for moving windows between screens, so within a screen there's one problem (unwanted animation, need to call the function multiple times to get the desired result), but my own code is also failing to move the window to the next screen (maybe failing to detect which screen it's on, or maybe the total number of screens).

I'll dig more over the weekend, I'm sure I can figure out exactly where my code is failing, maybe that will provide some additional hint.

@Madd0g
Copy link
Author

Madd0g commented Mar 12, 2020

Also, yesterday, vscode was having this problem, today it doesn't have the problem, but now firefox does. I don't think it's related to specific apps.

@maxandersen
Copy link

im hittingthis at the moment.

with vscode focusedWindow() is nil but also happens when doing hs.application.frontmostApplication():focusedWindow() or :allWindows()

to hammerspoon its like vscode has no windows at all.

@ogirginc
Copy link

I am not sure, if this is only an Hammerspoon issue. I have tried using Mosaic and had similar problems.

However, Magnet seems to work just fine. 🤷

@maxandersen
Copy link

I also found that ecamm live's customziation docks are not detectable/filterable by hammerspoon. which is shame as their auto layout is bad and i would love to fix it.

@ciamac
Copy link

ciamac commented Jun 11, 2020

I'm having a similar issue with slack. Hammerspoon does not detect any slack windows:

> hs.application.find("Slack"):mainWindow()
nil

This seems to have happened recently.

@codelahoma
Copy link

codelahoma commented Aug 10, 2020

I've been seeing this issue as well when using hs.grid.

My Emacs would sometimes require three calls, which in no particular order would resize height, resize width, and set origin.

Another datapoint that bolsters the theory that the issue is with untitled windows: After changing my Emacs window title to begin with Emacs |, it works on the first try every time.

@sfdc-slumos
Copy link

Hopefully not just pouring confusion on the fire, but I just had this happen to me with Slack, and found that restarting Slack fixed the issue. (For now...)

@stpddream
Copy link

i am having a similar issue with VScode that Hammerspoon does not detect any VSCode windows. After I changed my VSCode window title to start with VSCode -, it works without a problem. So it probably has something to do with untitled windows.

@Madd0g
Copy link
Author

Madd0g commented Nov 5, 2020

My Emacs would sometimes require three calls, which in no particular order would resize height, resize width, and set origin.

I'm actually amazed by this bug, some things work but require multiple "tries" and others simply don't work.

I have my own screen-moving logic and I also tried using the WinWin spoon, both using moveToScreen(), funnily mine never works with these windows (window:moveToScreen(previousScreen) just doesn't do anything) but WinWin stutters (making few wrong resizes in-place) but sometimes eventually does jump to the next screen.

just weird, man.

EDIT: one thing I noticed today is that the behavior is better when passing some more arguments to moveToScreen

-- works better!
window:moveToScreen(previousScreen, true, false)

@mengelbrecht
Copy link
Contributor

For me the win:maximize() function requires 3 tries for apps like Firefox Nightly or VSCode to lead to a maximized window (note: I have set hs.window.animationDuration = 0).

  1. The first one sets the size of the window so the window reaches to the end of the screen. However, the position of the window is not changed. So it is not really maximized.
  2. The second try sets the topleft position of the window but does not change the size. This leads to the window being positioned correctly but being too small.
  3. The third try sets the size correctly to reach the end of the screen and the window is finally maximized.

When looking at the code of the maximize function one can see that it only calls _setFrame with the appropriate frame for the screen:

return self:setFrame(self:screen():frame(), duration)

The _setFrame function performs 3 calls: setSize followed by setTopLeft and again setSize:
self:_setSize(f) self:_setTopLeft(f) return self:_setSize(f)

It seems that the _setFrame function aborts execution after setSize and/or setTopLeft?!

@nimatrueway
Copy link

This seems like a macOS or Chrome problem, because even macOS's built-in context menu for moving a window to other screen is broken.

image

@tanpengsccd
Copy link

tanpengsccd commented Jun 11, 2022

So if we still want this functions, this app https://github.com/rxhanson/Rectangle can do this at this moment.

Sorry ,Rectangle does not work too.

@tanpengsccd
Copy link

tanpengsccd commented Jun 11, 2022

So if we still want this functions, this app https://github.com/rxhanson/Rectangle can do this at this moment.

Sorry ,Rectangle does not work too.

oh I got U, they are both work when I restart vscode. It's weird !

@chrisgrieser
Copy link

chrisgrieser commented Jun 22, 2022

I got the same issue, but solely with Finder. Even with the example taken from the "Getting Started" page.

Before migrating my window management to Hammerspoon, I used pure AppleScript for Window Management, and here, there are two methods for resizing windows, one of which caused the same issue while the other worked fine. Maybe this helps with fixing this issue?

# using bounds, everything works fine
# however, setting window bounds is only available for apps with proper AppleScript support
use framework "AppKit"
set allFrames to (current application's NSScreen's screens()'s valueForKey:"frame") as list
set max_x to item 1 of item 2 of item 1 of allFrames
set max_y to item 2 of item 2 of item 1 of allFrames

set x to 0.2 * max_x
set y to 0.1 * max_y
set w to 0.6 * max_x
set h to 0.8 * max_y
tell application "Finder" to set bounds of window 1 to {x, y, x + w, y + h}
# using size and position works for apps without regular AppleScript support
# but causes issues with some apps that do have regular AppleScript support
use framework "AppKit"
set allFrames to (current application's NSScreen's screens()'s valueForKey:"frame") as list
set max_x to item 1 of item 2 of item 1 of allFrames
set max_y to item 2 of item 2 of item 1 of allFrames

set x to 0.2 * max_x
set y to 0.1 * max_y
set w to 0.6 * max_x
set h to 0.8 * max_y

tell application "System Events"
	tell process "Finder"
		tell window 1
			set position to {x, y}
			set size to {w, h}
		end tell
       end tell
end tell

@chrisgrieser
Copy link

chrisgrieser commented Jun 23, 2022

Okay, I looked into it and I am quite positive that the issue is caused by the difference between the position-size-method and the bounds-method I posted above.

After a bunch of testing, the issue 100% only occurs for apps with explicit AppleScript support. You can check whether an app has explicit AppleScript support by launching the Script Editor, and then selecting "open dictionary" in the file menu. You can also check it programmatically by looking whether a .sdef file exists somewhere in the app bundle (the .app).

  1. If the app has explicit AppleScript support, the bounds-method and the position-size-method both work to resize a window. However, the position-size-method will lead to the glitching bug, where you sometimes need multiple attempts. The bounds-method works without any issue.
  2. If the app does not have explicit AppleScript support, it can only be indirectly scripted via AppleScript by targeting process windows via tell application "System Events" .... Resizing windows only works via the position-size-method for these apps, but it works issue-free.

Consequently, you should use the bounds-method for AppleScript-capable apps, and the position-size-method for the others. From what I can tell, Hammerspoon seems to use purely the position-size-approach for all apps, resulting in the bug. Using the bounds-approach for the apps in question should also fix the problem for Hammerspoon. Unfortunately, I have zero knowledge of C and Objective-C, but maybe someone who does can fix this?

edit: there seems to be one exception for me: Sublime Text does not have explicit AppleScript support (i.e. the bounds-method does not work), but still has the issue when using the the position-size approach 😵‍💫


In the meantime, you can effectively use hs.applescript to create a workaround that fixes the window movement issue. Add this function to your init.lua and use resizingWorkaround instead of win:moveToUnit for moving windows. I have tested this and the issue with the windows not moving properly 100% stops for me with this workaround. (Sorry if the code isn't really clean, I only learned lua 2 days ago when I started using Hammerspoon.)

function resizingWorkaround(win, pos)
  local winApp = win:application():name()
  -- add Applescript-capable apps you are using to the if-condition below
  if (winApp == "Finder" or winApp == "Brave Browser" or winApp == "BusyCal" or winApp == "Safari") then
  	hs.applescript([[
  		use framework "AppKit"
  		set allFrames to (current application's NSScreen's screens()'s valueForKey:"frame") as list
  		set max_x to item 1 of item 2 of item 1 of allFrames
  		set max_y to item 2 of item 2 of item 1 of allFrames
  		]] ..

  		"set x to " .. pos.x .. " * max_x\n" ..
  		"set y to " .. pos.y .. " * max_y\n" ..
  		"set w to " .. pos.w .. " * max_x\n" ..
  		"set h to " .. pos.h .. " * max_y\n" ..
  		'tell application "' .. winApp .. '" to set bounds of front window to {x, y, x + w, y + h}'
  	)
  else
  	win:moveToUnit(pos)
  end
end

@francoiscote
Copy link

I got the same behavior with Chrome for a while (multisteps necessary to move and place windows, and animation is happening even if my animation duration setting is set to 0). My other electron based apps (slack, vscode) were always working fine.

The culprit on my end was the Gramarly Desktop app, which adds a little overlay on top of some apps. Just dropping this here in case this helps someone with a similar issue.

@sfdc-slumos
Copy link

Well after 2 years of no problems I'm back with Slack giving nil for every variation of focusedWindow I've tried. Restarting Slack doesn't work either. Is there no way to get a hs.window for Slack now?

@Rhys-T
Copy link

Rhys-T commented Oct 28, 2022

Looks like this is probably an issue with the AXEnhancedUserInterface attribute. Full explanation and workaround posted here: #3224 (comment)

@Madd0g
Copy link
Author

Madd0g commented Oct 28, 2022

I was so annoyed that positioning windows is not working, that for some features, I developed a timer based loop hack that keeps issuing the command until the window seems to be in the right spot.

I'm almost sad to see it go, I kinda like to see the window twitching 4-5 times until it settles down in the desired position :)

resizeloop.mp4

@cmpadden
Copy link

cmpadden commented Apr 7, 2024

I am still experiencing this issue for specific applications, notably Firefox (Librewolf). Closed my issue to instead track this one, but more details can be found there:

#3624

Quite hacky, but this is the workaround for moving windows with retries that I came up with; feedback is always welcome!

-- Convert relative `unitrect` to `rect` (couldn't get hs.window:fromUnitRect to work)
local function _unit_rect_to_rect(unit_rect)
    local screen_frame = hs.screen.mainScreen():frame()
    return hs.geometry.rect(
        screen_frame.x + (unit_rect.x * screen_frame.w),
        screen_frame.y + (unit_rect.y * screen_frame.h),
        unit_rect.w * screen_frame.w,
        unit_rect.h * screen_frame.h
    )
end

-- Temporary workaround: move windows until we confirm that they are at the frame that
-- we expect. Have a retry of 3 to prevent any unwanted infinite loops.
local function _move_to_unit_with_retries(geometry, window)
    window:moveToUnit(geometry)
    local retries = 3
    hs.timer.doUntil(function()
        return retries == 0 or window:frame():equals(_unit_rect_to_rect(geometry):floor())
    end, function()
        window:moveToUnit(geometry)
        retries = retries - 1
    end, 0.25)
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests