taooceros / Flow.Plugin.WindowWalker

Port of WindowsWalker from Powertoy for Flow Launcher
MIT License
23 stars 6 forks source link

Idea: fast switching using vim-boomark-like approach: e.g. `ga` to open the first window of browser #6

Closed pc223 closed 3 years ago

pc223 commented 3 years ago

What do you think about this workflow? :

Instead of type chro + Enter to open Google Chrome, we just need to type ga + Enter. Or gb + Enter -> open the second window of the same process, and so on. With this, you can even close your eyes and still able to switch to the exact window you want 😄 Like bookmark in vim. That means ga, gb will be "bind" to specific windows, and only change when user change in the settings.

Though I'm not sure how to implement this in Flow for real, like how users gonna config this in settings? Drag drog crosshair? Conflict with other plugins?... Right now I'm using the old Switcheroo.Plugin, with a little dirty config file like this and I have to put in the window ids by hand 😹 But it works, the workflow feel much more convenient than normal workflow.

ea, 3671758 # Explorer Window 1 
eb, 856130 # Window 2
ec, 462162 #...
ed, 265762

sa, 268694 # Sublime
sb, 203130

ga, 198080 # Google Chrome
gb, 527058
gc, 132524
gd, 1050786
taooceros commented 3 years ago

Seems a quite interesting workflow🤔 I have no experience on vim so I am not sure whether my understanding it right.

So basically you mean by configing some most frequently used program with a character, and the second character indicates which window insided the program should be opened?

I have a few questions toward that. How do we know which one is the first or second or third window for chrome? And in your case, won't the ID of windows change? I am not quite familiar with the this.....😂

Would you please provides a link toward your modified version of Switcheroo so that I can see how you implement it?

pc223 commented 3 years ago

Ok, so, for example: I have 3 Google Chrome windows, 1 window for chat stuff, 1 window for reference, 1 window for something else. So I will get id for these 3 windows, and put in my config file like this:

ga, <id window 1>
gb, <id window 2>
gc, <id window 3>

Then, when I type ga, it will open the first window (chat stuff), gb -> window 2 (reference) and so on. So users will have to decide what is ga window, what is gb window, like that, manually 😄

Would you please provides a link toward your modified version of Switcheroo so that I can see how you implement it?

Well, I'll try to put it up if needed, I don't even remember where I put that code, but it's really simple and ...dirty hacks hahahahah 😹

taooceros commented 3 years ago

Oh so you don't close windows frequently? 😂 Everytime opening a new window means storing the ID into config?

taooceros commented 3 years ago

That seems really dirty hahahhaha

pc223 commented 3 years ago

Oh so you don't close windows frequently? 😂

No, I never turn off my computer ✌️ only sleep, because I tends to open a lot of windows and tabs 😄

Everytime opening a new window means storing the ID into config?

Yes, but like I have to do it manually, no C# magic

That seems really dirty hahahhaha

Hahahahaha

pc223 commented 3 years ago

Maybe users can shift+enter to set up a special key for a window? Like this:

  1. alt+space to open Flow
  2. lp (list programs/window)
  3. Scroll thru list programs/window
  4. shift+enter (right-click mouse)
  5. Choose "Set fast-switching key"
  6. ->Setting page of plugin (pre-populated with id of the window that user choosed)
taooceros commented 3 years ago

Maybe users can shift+enter to set up a special key for a window? Like this:

  1. alt+space to open Flow
  2. lp (list programs/window)
  3. Scroll thru list programs/window
  4. shift+enter (right-click mouse)
  5. Choose "Set fast-switching key"
  6. ->Setting page of plugin (pre-populated with id of the window that user choosed)

Seems much more feasible. We don't need to list it, we can just add a context menu for the result, so that you can search and use that feature.

pc223 commented 3 years ago

Seems much more feasible. We don't need to list it, we can just add a context menu for the result, so that you can search and use that feature.

Yeah, that'll works too.

Although, I still think a list programs is usefull too, like with Switcheroo (the original), when alt+space, it opens up whole list of windows rightaway, reminds users of what windows in the systems (sometimes a lot). Some users they even replace alt+tab with Switcheroo because Switcheroo support bind alt+tab.

pc223 commented 3 years ago

If you need inspiration for new features, the original Switcheroo will be good place to start, a lot cool features there for windows switching :smile:

https://github.com/elig0n/Switcheroo (Active) https://github.com/kvakulo/Switcheroo

taooceros commented 3 years ago

Seems much more feasible. We don't need to list it, we can just add a context menu for the result, so that you can search and use that feature.

Yeah, that'll works too.

Although, I still think a list programs is usefull too, like with Switcheroo (the original), when alt+space, it opens up whole list of windows rightaway, reminds users of what windows in the systems (sometimes a lot). Some users they even replace alt+tab with Switcheroo because Switcheroo support bind alt+tab.

Yeah the new version of window walker do provide that feature. It is only unavailable for global wildcard action keyword due to flow's inner logic.

pc223 commented 3 years ago

Yeah the new version of window walker do provide that feature. It is only unavailable for global wildcard action keyword due to flow's inner logic.

Yeah, I understand It's hard to integrate with Flow/Wox architecture and avoid plugins conflict and so on. I did messing around with that before, have to do a bit of hacks to have a lp -> list programs ; alt+space -> lp 😂

taooceros commented 3 years ago

If you need inspiration for new features, the original Switcheroo will be good place to start, a lot cool features there for windows switching :smile:

https://github.com/elig0n/Switcheroo (Active) https://github.com/kvakulo/Switcheroo

Will do. I will still be free for about one week (will need to worry about stuff from school after that). I will try my best to migrate functions from Switcheroo😉

taooceros commented 3 years ago

Yeah the new version of window walker do provide that feature. It is only unavailable for global wildcard action keyword due to flow's inner logic.

Yeah, I understand It's hard to integrate with Flow/Wox architecture and avoid plugins conflict and so on. I did messing around with that before, have to do a bit of hacks to have a lp -> list programs, alt+space -> lp 😂

Yeah I mean for now, you don't need to use lp to list all windows. On default, < is the action keyword, so when you type <, all available windows will be listed.

pc223 commented 3 years ago

Yeah the new version of window walker do provide that feature. It is only unavailable for global wildcard action keyword due to flow's inner logic.

Yeah, I understand It's hard to integrate with Flow/Wox architecture and avoid plugins conflict and so on. I did messing around with that before, have to do a bit of hacks to have a lp -> list programs, alt+space -> lp 😂

Yeah I mean for now, you don't need to use lp to list all windows. On default, < is the action keyword, so when you type <, all available windows will be listed.

Cool, I'm still on the 1.0.0 plugin version and don't see the action keyword in settings, will upgrade to 1.2.0 soon 😄

taooceros commented 3 years ago

Yeah the new version of window walker do provide that feature. It is only unavailable for global wildcard action keyword due to flow's inner logic.

Yeah, I understand It's hard to integrate with Flow/Wox architecture and avoid plugins conflict and so on. I did messing around with that before, have to do a bit of hacks to have a lp -> list programs, alt+space -> lp 😂

Yeah I mean for now, you don't need to use lp to list all windows. On default, < is the action keyword, so when you type <, all available windows will be listed.

Cool, I'm still on the 1.0.0 plugin version and don't see the action keyword in settings, will upgrade to 1.2.0 soon 😄

Why not try pm update😃 Also, due to the setting saved, you may need to delete the extra global wildcard keyword in stored setting to see the action keyword, because flow block the action keyword from showing if there are more than one. It will lie in %appdata%/FlowLauncher/Settings/Settings.json

taooceros commented 3 years ago

Found the one with action keyword < and remove the global wildcard will allow you to see it in setting.

pc223 commented 3 years ago

Found the one with action keyword < and remove the global wildcard will allow you to see it in setting.

Will do! 👍

taooceros commented 3 years ago

@pc223 I have finished. Would you like to take a try? Microsoft.Plugin.WindowWalker.zip Shift enter to register the keyword you want.

pc223 commented 3 years ago

@pc223 I have finished. Would you like to take a try? Microsoft.Plugin.WindowWalker.zip Shift enter to register the keyword you want.

Nice! I'll try it soon 😄

pc223 commented 3 years ago

Work as expected 👍

Although I still think we need a better way to set quick-access-key the first time, alt+space, shift+enter 10-20 times is pretty awkward.

I'm thinking of something like a list of programs (windows) in setting page of the plugin and you can change quick-access-key in bulk there.

But this is good first step 😄

taooceros commented 3 years ago

Work as expected 👍

Although I still think we need a better way to set quick-access-key the first time, alt+space, shift+enter 10-20 times is pretty awkward.

I'm thinking of something like a list of programs (windows) in setting page of the plugin and you can change quick-access-key in bulk there.

But this is good first step 😄

Sounds great. What about add an option to bulk added the windows. By typing the action keyword you want for a window, a seperator, and a string used to query the window?

pc223 commented 3 years ago

I'm thinking maybe in bulk is not what we need, but a more "natural" way to set quick-access-keyword.

With current setup of WW, it goes like this:

  1. alt+space
  2. Typing, search for the window
  3. Use arrows to choose window
  4. shift+enter
  5. enter (to choose quick-access)
  6. ga (quick-access-keyword)
  7. enter

A bit clunky 😅

Maybe WW can imitate how Vim does, so the new sequence can be like this:

  1. User will focus on a window (either manual or by WW), most of the time user don't need to do this step because it already focused.
  2. alt+space
  3. mga (meaning: mark the current focused window with quick-access-keyword ga)
  4. enter

This feels more natural, fit with the "Flow" 😄

Note: In Vim, user only use ma, mb, mA, mB, but with WW, I think we should use at least 2 chars, hence mga, mgb.

taooceros commented 3 years ago

Let me check if that's possible, because awaking Flow will catch the focus from the application we want to set the keyword. Maybe that can be done with an shortcut key.

------------------ 原始邮件 ------------------ 发件人: "pc223"<notifications@github.com>; 发送时间: 2021年1月9日(星期六) 晚上6:26 收件人: "taooceros/Microsoft.Plugin.WindowWalker"<Microsoft.Plugin.WindowWalker@noreply.github.com>; 抄送: "天煞"<1569418337@qq.com>; "Comment"<comment@noreply.github.com>; 主题: Re: [taooceros/Microsoft.Plugin.WindowWalker] Idea: fast switching using vim-boomark-like approach: e.g. ga to open the first window of browser (#6)

I'm thinking maybe in bulk is not what we need, but a more "natural" way to set quick-access-keyword.

With current setup of WW, it goes like this:

alt+space

Typing, search for the window

Use arrows to choose window

shift+enter

enter (to choose quick-access)

ga (quick-access-keyword)

enter

A bit clunky 😅

Maybe WW can imitate how Vim does, so the new sequence can be like this:

User will focus on a window (either manual or by WW), most of the time user don't need to do this step because it already focused.

alt+space

mga (meaning: mark the current focused window with quick-access-keyword ga)

enter

Note: In Vim, user only use ma, mb, mA, mB, but with WW, I think we should use at least 2 chars, hence mga, mgb.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

pc223 commented 3 years ago

Let me check if that's possible, because awaking Flow will catch the focus from the application we want to set the keyword. Maybe that can be done with an shortcut key.

Hm...Yeah I feared that too, It'll be cool if we have something like "last-focused-window" or something 🤔

taooceros commented 3 years ago

hmmm seems that there isn't some method that can do that......Or we may need to update the last window every time focus change, which is a kind of waste. Maybe using a shortcut keyword may be better.

------------------ 原始邮件 ------------------ 发件人: "pc223"<notifications@github.com>; 发送时间: 2021年1月9日(星期六) 晚上6:33 收件人: "taooceros/Microsoft.Plugin.WindowWalker"<Microsoft.Plugin.WindowWalker@noreply.github.com>; 抄送: "天煞"<1569418337@qq.com>; "Comment"<comment@noreply.github.com>; 主题: Re: [taooceros/Microsoft.Plugin.WindowWalker] Idea: fast switching using vim-boomark-like approach: e.g. ga to open the first window of browser (#6)

Let me check if that's possible, because awaking Flow will catch the focus from the application we want to set the keyword. Maybe that can be done with an shortcut key.

Hm...Yeah I fear that too, It'll be cool if we have something like "last-focused-window" or something 🤔

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

pc223 commented 3 years ago

update the last window every time focus change

Yeah, that's a bit problematic. (If I do this for myself, I'll accept this like a dirty hack ahahhaha).

Maybe using a shortcut keyword may be better.

So a shortcut key only for WW? Does Flow support that? I'm under the impression that Flow will always change focus and plugin-level-code can't change that.

taooceros commented 3 years ago

I mean plugins can register hotkey (Not sure about the detailed implementation, but it is possible as I see in the IPbulicAPI, even if it doesn't support that, we can still register one in Init method). If we use a hotkey, which means that the focus won't change, we can get the foreground window handle and do the job. However, this seems a little bit beyond what a plugin should do hhh. Anyway, I will take a try later.

------------------ 原始邮件 ------------------ 发件人: "pc223"<notifications@github.com>; 发送时间: 2021年1月9日(星期六) 晚上7:02 收件人: "taooceros/Microsoft.Plugin.WindowWalker"<Microsoft.Plugin.WindowWalker@noreply.github.com>; 抄送: "天煞"<1569418337@qq.com>; "Comment"<comment@noreply.github.com>; 主题: Re: [taooceros/Microsoft.Plugin.WindowWalker] Idea: fast switching using vim-boomark-like approach: e.g. ga to open the first window of browser (#6)

update the last window every time focus change

Yeah, that's a bit problematic. (If I do this for myself, I'll accept this like a dirty hack ahahhaha).

Maybe using a shortcut keyword may be better.

So a shortcut key only for WW? Does Flow support that? I'm under the impression that Flow will always change focus and plugin-level-code can't change that.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

pc223 commented 3 years ago

I mean plugins can register hotkey (Not sure about the detailed implementation, but it is possible as I see in the IPbulicAPI, even if it doesn't support that, we can still register one in Init method). If we use a hotkey, which means that the focus won't change, we can get the foreground window handle and do the job. However, this seems a little bit beyond what a plugin should do hhh. Anyway, I will take a try later. ------------------ 原始邮件 ------------------ 发件人: "pc223"<notifications@github.com>; 发送时间: 2021年1月9日(星期六) 晚上7:02 收件人: "taooceros/Microsoft.Plugin.WindowWalker"<Microsoft.Plugin.WindowWalker@noreply.github.com>; 抄送: "天煞"<1569418337@qq.com>; "Comment"<comment@noreply.github.com>; 主题: Re: [taooceros/Microsoft.Plugin.WindowWalker] Idea: fast switching using vim-boomark-like approach: e.g. ga to open the first window of browser (#6) update the last window every time focus change Yeah, that's a bit problematic. (If I do this for myself, I'll accept this like a dirty hack ahahhaha). Maybe using a shortcut keyword may be better. So a shortcut key only for WW? Does Flow support that? I'm under the impression that Flow will always change focus and plugin-level-code can't change that. — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

Hm...I thinking, when we return back from Flow, it comebacks to the last-focused-window, obviously something have to save the last-focused-window ID (maybe the OS). Maybe we can tap into that data and pull out the ID, like a Windows API / P/Invoke thingy 😄

pc223 commented 3 years ago

This looks promising:

https://www.whitebyte.info/programming/how-to-get-main-window-handle-of-the-last-active-window

... For fixing this side-effect, i needed to find out, which window was active before my programm was “born” ... But in my case, i wanted to avoid, running the programm permanently, or run anything in the background.. ... After some more research, i found out that Windows API has a list of all windows which is called: the z-order. Next, i found the WinAPI Method GetWindow (see MSDN for documentation). This method allows you to get a window handle from the list mentoned above. Here how i used it:

IntPtr lastWindowHandle = GetWindow(Process.GetCurrentProcess().MainWindowHandle, (uint)GetWindow_Cmd.GW_HWNDNEXT);

But when trying to set the window with this handle to foreground, nothing came to foreground.. So i found out that the handle returned by this method was not neccessaryly the Main-Window-Handle. Therefore i wrote the following code finding out the parent handle:

while (true)
{
IntPtr temp = GetParent(lastWindowHandle);
if (temp.Equals(IntPtr.Zero)) break;
lastWindowHandle = temp;
}

These links look promising too

https://stackoverflow.com/a/9469057/4230390 https://stackoverflow.com/a/210519/4230390 https://devblogs.microsoft.com/oldnewthing/20071008-00/?p=24863

pc223 commented 3 years ago

Ahh yes, the original Switcheroo have alt-tab-replacement feature, that means it has to know about last-focus-window

You can base on this: https://github.com/elig0n/Switcheroo/blob/d21d477b1182feb0605fa5fc94e9e2c63f099dea/Core/AppWindow.cs#L94

Key part:

        private IntPtr GetLastActiveVisiblePopup()
        {
            // Which windows appear in the Alt+Tab list? -Raymond Chen
            // http://blogs.msdn.com/b/oldnewthing/archive/2007/10/08/5351207.aspx

            // Start at the root owner
            var hwndWalk = WinApi.GetAncestor(HWnd, WinApi.GetAncestorFlags.GetRootOwner);

            // See if we are the last active visible popup
            var hwndTry = IntPtr.Zero;
            while (hwndWalk != hwndTry)
            {
                hwndTry = hwndWalk;
                hwndWalk = WinApi.GetLastActivePopup(hwndTry);
                if (WinApi.IsWindowVisible(hwndWalk))
                {
                    return hwndWalk;
                }
            }
            return hwndWalk;
        }
taooceros commented 3 years ago

Ahh yes, the original Switcheroo have alt-tab-replacement feature, that means it has to know about last-focus-window

You can base on this: https://github.com/elig0n/Switcheroo/blob/d21d477b1182feb0605fa5fc94e9e2c63f099dea/Core/AppWindow.cs#L94

Key part:

        private IntPtr GetLastActiveVisiblePopup()
        {
            // Which windows appear in the Alt+Tab list? -Raymond Chen
            // http://blogs.msdn.com/b/oldnewthing/archive/2007/10/08/5351207.aspx

            // Start at the root owner
            var hwndWalk = WinApi.GetAncestor(HWnd, WinApi.GetAncestorFlags.GetRootOwner);

            // See if we are the last active visible popup
            var hwndTry = IntPtr.Zero;
            while (hwndWalk != hwndTry)
            {
                hwndTry = hwndWalk;
                hwndWalk = WinApi.GetLastActivePopup(hwndTry);
                if (WinApi.IsWindowVisible(hwndWalk))
                {
                    return hwndWalk;
                }
            }
            return hwndWalk;
        }

Wow......thank you! Will get that down soon!

taooceros commented 3 years ago

Well, Unfortunately, this doesn't serve the job we want. It is used to get the most active sub window of a certain window. The one for z-index should work, but I don't understand why it doesn't...... I will give more test later...

------------------ 原始邮件 ------------------ 发件人: "pc223"<notifications@github.com>; 发送时间: 2021年1月9日(星期六) 晚上7:55 收件人: "taooceros/Microsoft.Plugin.WindowWalker"<Microsoft.Plugin.WindowWalker@noreply.github.com>; 抄送: "天煞"<1569418337@qq.com>; "Comment"<comment@noreply.github.com>; 主题: Re: [taooceros/Microsoft.Plugin.WindowWalker] Idea: fast switching using vim-boomark-like approach: e.g. ga to open the first window of browser (#6)

Ahh yes, the original Switcheroo have alt-tab-replacement feature, that means it have to know about last-focus-window

You can base on this: https://github.com/kvakulo/Switcheroo/blob/21b07dffe7e42e91cede9d8e06cdcb32b768f149/Core/AppWindow.cs#L97

Key part: private IntPtr GetLastActiveVisiblePopup() { // Which windows appear in the Alt+Tab list? -Raymond Chen // http://blogs.msdn.com/b/oldnewthing/archive/2007/10/08/5351207.aspx // Start at the root owner var hwndWalk = WinApi.GetAncestor(HWnd, WinApi.GetAncestorFlags.GetRootOwner); // See if we are the last active visible popup var hwndTry = IntPtr.Zero; while (hwndWalk != hwndTry) { hwndTry = hwndWalk; hwndWalk = WinApi.GetLastActivePopup(hwndTry); if (WinApi.IsWindowVisible(hwndWalk)) { return hwndWalk; } } return hwndWalk; }

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

pc223 commented 3 years ago

Scratch all that! I think I begin to understand this, so GetLastActivePopup() actually serve as a "alt-tab-able-checker", not "the-first-window-in-alt-tab". So we should use GetWindow(). In GoToWindow, they use EnumWindows() but I guess it returns ordered windows too.

I think you should "steal" from GoToWindow, they have good support for Windows 10, UWP stuff, a lot of weird edge cases of "alt-tab-able-checker".

https://github.com/christianrondeau/GoToWindow/blob/master/GoToWindow.Api/WindowsListFactory.cs

taooceros commented 3 years ago

Scratch all that! I think I begin to understand this, so GetLastActivePopup() actually serve as a "alt-tab-able-checker", not "the-first-window-in-alt-tab". So we should use GetWindow(). In GoToWindow, they use EnumWindows() but I guess it returns ordered windows too.

I think you should "steal" from GoToWindow, they have good support for Windows 10, UWP stuff, a lot of weird edge cases of "alt-tab-able-checker".

https://github.com/christianrondeau/GoToWindow/blob/master/GoToWindow.Api/WindowsListFactory.cs

You are right! I will take a look on that later. Thank you so much for all these helps!

taooceros commented 3 years ago

@pc223 the debug process toward getting window like that is annoying…… I will leave it as a TODO until future that flow can be debugged easier. Currently, let me implement this by using a hotkey and GetForegroundWindow, which is feasible and quite natural to release this feature soon.

taooceros commented 3 years ago

@pc223 Please take a try. Currently the steps will look like.

  1. User will focus on a window
  2. ctrl+alt+d
  3. type the actionkeyword you like
  4. enter

Microsoft.Plugin.WindowWalker.zip

pc223 commented 3 years ago

@pc223 Please take a try. Currently the steps will look like.

1. User will focus on a window

2. `ctrl+alt+d`

3. type the actionkeyword you like

4. `enter`

Microsoft.Plugin.WindowWalker.zip

Tested, it's working well on my computer 👍

taooceros commented 3 years ago

Great! I will soon merge that to master and release a new version.