status-im / status-desktop

Status Desktop client made in Nim & QML
https://status.app
Mozilla Public License 2.0
271 stars 76 forks source link

[Deep links] Windows - App window stays in the background after a deep link is opened #11027

Open lukaszso opened 1 year ago

lukaszso commented 1 year ago

Clicking any deep link on Windows when the app window is in the background does not bring it to the foreground.

Steps to reproduce

  1. Open the app
  2. Open a browser and paste a deep link
  3. Hit Enter

Expected behavior

App window is brought to the foreground.

Actual behavior

The link works but the app stays in the background. The user has to bring it manually.

Additional Information

https://github.com/status-im/status-desktop/assets/52439419/23c24f93-8e6c-4633-b2a7-b0172cdf5c91

anastasiyaig commented 1 year ago

@jrainville please take a look, how important it is for us?

caybro commented 1 year ago

It should be a matter of calling (from main.qml) this at the right spot:

    function makeStatusAppActive() {
        applicationWindow.show()
        applicationWindow.raise()
        applicationWindow.requestActivate()
    }
jrainville commented 1 year ago

Indeed, the fix should be quite simple. I'll try to land this in 0.13

jrainville commented 1 year ago

I can reproduce.

We already call

applicationWindow.show()
applicationWindow.raise()
applicationWindow.requestActivate()

like mentioned by Lukas.

Seems like there is an issue on Windows with making the app go to the foreground https://stackoverflow.com/questions/6087887/bring-window-to-front-raise-show-activatewindow-don-t-work

Do you know how to call setWindowState from QML @caybro ?

Weirdly, if the app is hidden or minimized, the code works fine. However, I did try the hack of minimizing first and then showing, but it's still the same. Seems like we really need a function to tell Windows to put the app to the top of the stack, but it doesn't seem like the functions we have in QML do it.

caybro commented 1 year ago

Do you know how to call setWindowState from QML @caybro ?

Weirdly, if the app is hidden or minimized, the code works fine. However, I did try the hack of minimizing first and then showing, but it's still the same. Seems like we really need a function to tell Windows to put the app to the top of the stack, but it doesn't seem like the functions we have in QML do it.

Indeed weird... this should work but we can still do this from C++ since we have a custom StatusWindow class (which is a QQuickWindow, inheriting a regular QWindow)

caybro commented 1 year ago

Weirdly, if the app is hidden or minimized, the code works fine. However, I did try the hack of minimizing first and then showing, but it's still the same. Seems like we really need a function to tell Windows to put the app to the top of the stack, but it doesn't seem like the functions we have in QML do it.

@jrainville so I played around with it a bit and it's simple - it's not a bug but a feature (tm) :) to simply prevent apps from stealing focus, Qt removes the Active flag when setting the windowState. (see https://codebrowser.dev/qt5/qtbase/src/gui/kernel/qwindow.cpp.html#1361). The only way a window can become active is via user interaction. To work around it, I simply first hid the window, as such:

    function makeStatusAppActive() {
        applicationWindow.hide()
        applicationWindow.show()
        applicationWindow.raise()
        applicationWindow.requestActivate()
    }

and this works fine, at least on Linux. You can give it a try on Windows too

jrainville commented 1 year ago

You can give it a try on Windows too

I'm pretty sure I tried it and it didn't work, but I'll try again. Thanks for trying

jrainville commented 1 year ago

Yeah, it doesn't work. I even tried with a timer where I hide, wait a second, then do show.

In this video, you can see it. I call make run again to call the onSecondInstanceDetected signal. From there, I call your modified makeStatusAppActive.

https://github.com/status-im/status-desktop/assets/11926403/a8549de4-e97d-45df-84a7-5a673aeb2b94

@caybro do you recommend I try to do it in DOtherside? The signal is already coming from DOtherSide. See SingleInstance::handleNewConnection()

caybro commented 1 year ago

@caybro do you recommend I try to do it in DOtherside? The signal is already coming from DOtherSide. See SingleInstance::handleNewConnection()

Yeah, that's worth trying out

jrainville commented 1 year ago

Turns out this is a Windows "feature".

https://doc.qt.io/qt-5/qwidget.html#activateWindow

On Windows, if you are calling this when the application is not currently the active one then it will not make it the active window. It will change the color of the taskbar entry to indicate that the window has changed in some way. This is because Microsoft does not allow an application to interrupt what the user is currently doing in another application.

So Windows does not let apps make themselves active. I guess it makes sense to make sure apps don't just pop up everywhere like viruses.

It's still annoying. I swear other apps were able to move to the foreground, but maybe they are not in QT or they use the ugly hack here: https://wiki.qt.io/Technical_FAQ#QWidget_::activateWindow.28.29_-_behavior_under_windows Though, I wouldn't even try, even if it could work, as it changes the behaviour for the whole system, and not just the app.

jrainville commented 1 year ago

I moved this to a later milestone since there is no easy way to do it for now