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

Feature Request: Relative window focus movement shortcuts #465

Closed
lpke opened this issue Sep 16, 2023 · 25 comments · Fixed by #469 or #476
Closed

Feature Request: Relative window focus movement shortcuts #465

lpke opened this issue Sep 16, 2023 · 25 comments · Fixed by #469 or #476
Labels
enhancement New feature or request

Comments

@lpke
Copy link

lpke commented Sep 16, 2023

A feature I sorely miss from many linux distros is the ability to move window focus in a direction relative to the currently focused window with keyboard shortcuts. The following image illustrates what I mean (shortcuts shown are just an example):

alt-focus

I haven't been able to find anything out there for windows that does it, and I think it would be an amazing addition to the (already fantastic) AltSnap feature set - especially when combined with snap layouts.

@lpke
Copy link
Author

lpke commented Sep 22, 2023

In case anyone else really wants this, and is as frustrated as I was about the lack of options out there, I've written an Autohotkey script that can do it.

#Requires AutoHotkey v2.0

calc_padding := 20 ; minimum direction/alignment overlap for target windows

#h::FocusWin("left")
#l::FocusWin("right")
#k::FocusWin("up")
#j::FocusWin("down")

; ==== DEVELOPMENT SHORTCUTS ====
; Reload this script
; #+r:: {
;   Reload
; }
; Get active window position data
; #+t:: {
;   aID := WinGetID("A")
;   WinGetFullPos(&aXL, &aXR, &aYT, &aYB, &aW, &aH, aID)
;   discounted := WinIsDiscounted(aID, &visible, &desktop, &taskbar, &startmenu)

;   MsgBox(
;     "XL:  " aXL "`n"
;     "XR:  " aXR "`n"
;     "YT:  " aYT "`n"
;     "YB:  " aYB "`n"
;     "W:  "  aW  "`n"
;     "H:  "  aH  "`n"
;     "`n"
;     "ID:  " aID "`n"
;     "`n"
;     "Visible:  " visible "`n"
;     "Desktop:  " desktop "`n"
;     "Taskbar:  " taskbar "`n"
;     "Startmenu:  " startmenu "`n"
;     "`n"
;     "Discounted:  " discounted
;   )
; }

WinGetFullPos(&xl, &xr, &yt, &yb, &w, &h, id) {
  WinGetPos(&xl, &yt, &w, &h, id)
  xr := xl + w
  yb := yt + h
  return
}

OverlapAxis(axis, padding, aXL, aXR, aYT, aYB, tXL, tXR, tYT, tYB) {
  if (axis = "Y" && (tYB >= (aYT + padding) && (tYT + padding) <= aYB))
    return true
  if (axis = "X" && (tXR >= (aXL + padding) && (tXL + padding) <= aXR))
    return true
  return false
}

WinIsDiscounted(id, &visible, &desktop, &taskbar, &startmenu) {
  wclass := WinGetClass(id)
  wstyle := WinGetStyle("ahk_id" id)
  WS_VISIBLE := 0x10000000

  visible := (wstyle & WS_VISIBLE) ? true : false
  desktop := (wclass = "Progman" || wclass = "WorkerW")
  taskbar := (wclass = "Shell_TrayWnd" || wclass = "Shell_SecondaryTrayWnd")
  startmenu := (wclass = "DV2ControlHost" || wclass = "Windows.UI.Core.CoreWindow")

  return !visible || desktop || taskbar || startmenu
}

FocusWin(direction) {
  aID := WinGetID("A") ; get ID of active window
  WinGetFullPos(&aXL, &aXR, &aYT, &aYB, &aW, &aH, aID) ; get pos/size of active window
  
  ids_arr := WinGetList() ; get all window IDs (returns array of id strings)

  ; values to update during the loop
  closest_id := ""
  closest_distance := 999999999

  ; loop through all windows
  ; validity checks are made early as possible for performance
  for (tID in ids_arr) {
    is_active_win := tID == aID
    if (is_active_win)
      continue
    is_discounted_win := WinIsDiscounted(tID, &visible, &desktop, &taskbar, &startmenu)
    if (is_discounted_win)
      continue

    WinGetFullPos(&tXL, &tXR, &tYT, &tYB, &tW, &tH, tID) ; get pos/size of target window

    switch direction
    {
      case "left": is_direction := aXL - tXL >= calc_padding
      case "right": is_direction := tXR - aXR >= calc_padding
      case "up": is_direction := aYT - tYT >= calc_padding
      case "down": is_direction := tYB - aYB >= calc_padding
    }
    if (!is_direction)
      continue
    switch direction
    {
      case "left": is_aligned := OverlapAxis("Y", calc_padding, aXL, aXR, aYT, aYB, tXL, tXR, tYT, tYB)
      case "right": is_aligned := OverlapAxis("Y", calc_padding, aXL, aXR, aYT, aYB, tXL, tXR, tYT, tYB)
      case "up": is_aligned := OverlapAxis("X", calc_padding, aXL, aXR, aYT, aYB, tXL, tXR, tYT, tYB)
      case "down": is_aligned := OverlapAxis("X", calc_padding, aXL, aXR, aYT, aYB, tXL, tXR, tYT, tYB)
    }
    if (!is_aligned)
      continue

    switch direction
    {
      case "left": distance := aXL - tXR
      case "right": distance := tXL - aXR
      case "up": distance := aYT - tYB
      case "down": distance := tYT - aYB
    }

    ; update closest values if window is closer and to the left
    if (distance < closest_distance) {
        closest_id := tID
        closest_distance := distance
    }
  }
  
  ; activate closest window
  if (closest_id)
    WinActivate("ahk_id" closest_id)

  return
}

@RamonUnch
Copy link
Owner

This would be a nice addition indeed, I will investigate This...

@RamonUnch
Copy link
Owner

Here is a first test build I wrote in an hour this morning.
I use a conical region to determine what is right/left/top/bottom of a window, For me it seems to be a good algorithm. Might use some more work. I did not try your ahk script, I directly wrote something from scratch.

AltSnap1.62test1_i386.zip
AltSnap1.62test1_x64.zip

Of course as usual replace AltSnap.exe and hooks.dll with the versions above and in the Keyboard tab you will have the option to configure the Focus left/right/top/bottom window in the Shortcut for action: drop list.

@lpke
Copy link
Author

lpke commented Oct 6, 2023

I'll give this a test later, thank you!

@ZhichaoHong
Copy link

@RamonUnch, I was trying to play with using command line L/R direction change the focus fine.
AltSnap.exe -afFocusR and AltSnap.exe -afFocusL

However, when I try to move focus up/down -afFocusT and -afFocusB, it does not change focus. Actually, it is hard to tell where the focus is after these commands in up/down direction.

@RamonUnch
Copy link
Owner

For me it works relyably.
Maybe try with the latest test build:
AltSnap1.62test5_x64.zip
AltSnap1.62test5_i386.zip

Also here is a visual description on how it should work:
ImageFocusLTRB
Same logic applies to right and top.

@RamonUnch
Copy link
Owner

also those actions were designed for keyboard shortcuts in mind. You can setup those shortcuts directly in AltSnap.

@ZhichaoHong
Copy link

I tried with keyboard and also with the latest release (test5) and still cannot navigate in T/B direction. I attached my config in the comment. Can you please check?

By the way, I am using external monitor with 150% DPI and grid 2x2 layout. I tried with 4 notepad window laid out neatly using Altsnap. There is no other layout manager such as FancyZone in the play.

AltSnap.ini.txt

@ZhichaoHong
Copy link

ZhichaoHong commented Oct 12, 2023

image
Here are how these 4 notepad laid out for testing. There is not any overlap. LR direction always works. Except sometime, the focus changes between RB and LT intead of LB and RB. So there are some strange things going on.

@RamonUnch
Copy link
Owner

I still cannot reproduce but I see some problems with the current system.
I will make a logging version to test on your machine.
Also I will work on improving the window selection algorithm.

@RamonUnch RamonUnch reopened this Oct 12, 2023
@RamonUnch
Copy link
Owner

AltSnap1.62test5_LOG_x64.zip
See what you get in the ad.log file that will be created when you use the FocusL/T/R/B actions...
Each line you should see the window that AltSnap tries to focus.

@Ichisich
Copy link

Currently, seems, pretty unreliable in which window gets focused.
Needs probably more cones, TL/TR/BL/BR

@RamonUnch
Copy link
Owner

This version removes overlap between directions and prioritize the pure cardinal position over diagonals.
Like this you get more directional while still being able to move along all windows (in theory).

Also let me know if you see strange windows appear in the ad.log file. Normally only AltTabable windows should appear.
AltSnap1.62test6_LOG_x64.zip
AltSnap1.62test6_LOG_i386.zip

@ZhichaoHong
Copy link

It is better than before. It can still get focused the wrong window. I initially focused on TL and issued to focusB. It moved down currectly. I issued focusT and it focused top window correctly. I then move the focus to the right top window. It got the TR (top right) window correctly. But now when I issued focusB, and tried to move the focus to BR (bottom right) it moves focus to the BL (bottom left). Now if I move the focus to T or B, it switches the focus between TR and BL (diagonally) all the time. ad.log is attached.
ad.log

@RamonUnch
Copy link
Owner

Thanks for the log file, so it seems the only problem comes from bad selection algorithm.
Here is a version with a slightly thinner beam, as well as two others versions with even smaller beam for testing.
So you can try first test7 then see if half beam fits you better then decide if maybe 2/3rd or 1/3 beam are better. You only need to replace the hooks.dll file
Square_beam

hooks1.62test7_2_3rd_beam_x64.zip
hooks1.62test7_3rd_beam_x64.zip
hooks1.62test7_half_beam_x64.zip
hooks1.62test7_x64.zip

It is a two step process, first AltSnap tries to find the closest window inside the beam, then if it was unable to find any window, it create 45 degree regions and selects the next closest window within those regions. thus covering the whole space.

@ZhichaoHong
Copy link

ZhichaoHong commented Oct 13, 2023

@RamonUnch, thank you for additional dlls. I found the hooks1.6test7_x64 version works better than 1/3, 1/2, and 2/3.

Actually, I have a very reliable way to reproduce this issue with the layout below. I found that I can focus on all 3 of them reliably, if I move focus from TL(left top) -> BL (left-bottom) -> TL (left top) -> TR (top right). But the problem happens when I try to move focus from TR to BL (focusB), it found the BL diagonally. After that, BL can only move focus to TR (focusT), it will ignore the TL window that is directly above. This is reproducible every time for me. I think the order of scanning diagonally versus straight can be improved.

image

@RamonUnch
Copy link
Owner

You mean that you would like to be able to go from BL to TR by using the FocusR action?
This would make some sense indeed I would just need to use the diagonals from the window itself instead of fixed 45° angle for the cones.

@RamonUnch
Copy link
Owner

Actually the above is already possible so it would be going from TR to BL with the FocusB action...

@RamonUnch
Copy link
Owner

RamonUnch commented Oct 13, 2023

However using the the window diagonals as cone is not necessary a very good idea because it means that each window would have a different cones based on its dimentions which would be confusing. Would be nice only in this specific case. Also monitor's diagonal angle could be used, not sure as well it it is a good idea.

Maybe I can add a third step that tries with an even larger cone if it fails in the 45° cone but I am not sure it is a good idea either. Because it would go back to the first problem that if you have overlap in the regions then you may have windows that become impossible to select in some cases.

@ZhichaoHong
Copy link

ZhichaoHong commented Oct 13, 2023

The current implementation with hooks1.62test7_x64.zip can move focus between BL and TR using focusT or focusB. However, after that, while in BL, I issued the focusT to try to focus the window TL that is directly above, it focused the TR instead. When the focus moved from TR to BL. The BL can only move focus to back TR instead of TL when issuing focusT from it. This is confusing.

@ZhichaoHong
Copy link

ZhichaoHong commented Oct 13, 2023

@RamonUnch , there is a demo of this issue. Keys about bound with Ctrl+Win+Alt+H, J, K, L for left, down, top, and right.
focus_moving_issue

@RamonUnch
Copy link
Owner

That is very strange, I am unable to reproduce: When I hit Ctrl+Win+Alt+K from BL, I only get TL selected and never TR.
Actually in theory it should be impossible to go from BL to TR with the FocusT action.

Try with opening in notepad files with different names so that we can check in the ad.log file which one AltSnap tries to focus.
Use this build to have a log:
hooks1.62test7_LOG_x64.zip

@ZhichaoHong
Copy link

ZhichaoHong commented Oct 14, 2023

Here are the screenshot and log file. To reproduce, I toggled the between TL and BL a few times. Then I move to TR from TL. Then focusB forces TR to BL as there is no other window directly under it. After that, FocusB/T will only move focus between BL and TR. I named the files in notepad with the windows location they represent. So the log file will be more helpful.

image

ad.log

@RamonUnch
Copy link
Owner

Try again with this build? At this point math seems to be non-reproducible because it SocusT should not be able to do BL->TR and FocusB should not even be able to do TR->BL.

hooks1.62test8_LOG_x64.zip
hooks1.62test8_x64.zip

@ZhichaoHong
Copy link

@RamonUnch, the test8 works as intended as I tried with no overlapping windows and windows overlapped a little bit. The algorithms is what I intended to have to be able to move the focus to. I don't find any odd behavior so far. Thank you so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
4 participants