fyne-io / fyne

Cross platform GUI toolkit in Go inspired by Material Design
https://fyne.io/
Other
25.15k stars 1.4k forks source link

Ability to create alway-on-top window #1129

Open mortzdk opened 4 years ago

mortzdk commented 4 years ago

Should be something as simple as setting window hint: GLFW_FLOATING.

Generally it would be nice if the user could set the window hint settings themselves.

andydotxyz commented 4 years ago

There is a discussion related to this on #476 - it's a complex topic as that is not something that will work across all platforms and also is discouraged by some platforms.

Can I ask what the user-facing requirement for this is, are you trying to do dialogs, kiosks - or just forcing user to interact with your app at some point? :)

hick commented 4 years ago

I want to make an alarm or some task notification application, just like an old desktop application named "alarm++" , the alert window should be always on top in case other operation & windows covered the alert. just like the following screenshot.

the tip on systray can only stay for a few seconds, and can not display many alerts at the same time. and can not operate like Snooze and Off like the following screenshot

It's ok if only some system like MS Windows & MacOS are supported. the QT and goqt can do like this but it's so heavy a project, I do not want to use goqt

image

mortzdk commented 4 years ago

I'm trying to build a "Deck Tracker" for the Hearthstone game. Most of the work is done reading log files but the actual presentation of the information I would like to use "fyne" for. Currently I have the following window:

Screenshot from 2020-07-02 08-08-49

The always-on-top part should be used when the game runs in fullscreen mode or when the game is using the whole screen. In these cases the user will not be able to see the tracker unless it is always on top.

mortzdk commented 4 years ago

On the glfw website: https://www.glfw.org/docs/3.1/news.html they say:

GLFW now supports floating windows, also called topmost or always on top, for easier debugging with the GLFW_FLOATING window hint.

Hence this could also be useful in general, for easier debugging of certain windows, while also supporting cases where it is absolute necessary for the application to either gain the users attention or provide extra services.

andydotxyz commented 4 years ago

@mortzdk Thanks for the input and great screenshot :) Unfortunately I don't know if we could support always-on-top over a fullscreen window. The way that fullscreen is handled on most OS is to not allow any other apps to be visible, and so even if on top you would not be able to see it. On macOS (and others have similar) you would need to use "split screen" so your fullscreen app is not the entire screen - with that you could load any app you like on the other portion, without needing "on top" set. I hope that helps?

andydotxyz commented 4 years ago

the tip on systray can only stay for a few seconds, and can not display many alerts at the same time

Which version of Windows is this @hick? I thought there was a notifications list section that could list many items?

The functionality you expand on is something that could become very annoying if too many apps decided to do it so I'm keen to find a better way. If the notifications did not automatically disappear would they become a viable solution? I think there may be a way we could expand the notification API to indicate that they should stay until dismissed...

dans-stuff commented 3 years ago

@andydotxyz I also need this for a game-specific version of LiveSplit, a game timing tool, which by necessity must float over the game being played.

Willing to do the user32.dll hackery to get it to work, but would need the window handle to do so.

Perfectly find if it doesn't work on MacOS, while I do support that OS, it can go without this one feature. Would love if it were included also.

Any advice?

andydotxyz commented 3 years ago

Sorry @dantoye we don’t expose the platform specific elements. If you want to add windows specific behaviour perhaps you can wrap the application launch in a windows script or additional binary that watches for the window load and manipulates its parameters directly?

Jacalz commented 2 years ago

I think this generally is something that the window manager is supposed to handle. I don't think it is something that we want all apps to be able to control.

anthonysharpy commented 2 years ago

For anyone stumbling across this thread like me here's code that will achieve this for Windows (tested on 64 bit). Code is not perfect (e.g. no error checking, unnecessary repeat loading of DLLs) but it works.

First you'll need to get a handle to the fyne window. You can use EnumWindows and GetWindowTextH from the Windows API to find the handle via the window name.

func GetWindowHandleByWindowName(window_name string) uintptr {
    user32dll := windows.MustLoadDLL("user32.dll")
    enumwindows := user32dll.MustFindProc("EnumWindows")

    var the_handle uintptr
    window_byte_name := []byte(window_name)

    // Windows will loop over this function for each window.
    wndenumproc_function := syscall.NewCallback(func(hwnd uintptr, lparam uintptr) uintptr {
        // Allocate 100 characters so that it has something to write to.
        var filename_data [100]uint16
        max_chars := uintptr(100)

        getwindowtextw := user32dll.MustFindProc("GetWindowTextW")
        getwindowtextw.Call(hwnd, uintptr(unsafe.Pointer(&filename_data)), max_chars)

        // If there's a match, save the value and return 0 to stop the iteration.
        if strings.Contains(string(windows.UTF16ToString([]uint16(filename_data[:]))), string(window_byte_name)) {
            the_handle = hwnd
            return 0
        }

        return 1
    })

    // Call the above looping function.
    enumwindows.Call(wndenumproc_function, uintptr(0))

    return the_handle
}

image

You can then set it to be on top.

const SWP_NOSIZE = uintptr(0x0001)
const SWP_NOMOVE = uintptr(0x0002)

// This is dumb but Go doesn't like the inline conversion (see above image).
func IntToUintptr(value int) uintptr {
    return uintptr(value)
}

func SetWindowAlwaysOnTop(hwnd uintptr) {
    user32dll := windows.MustLoadDLL("user32.dll")
    setwindowpos := user32dll.MustFindProc("SetWindowPos")
    setwindowpos.Call(hwnd, IntToUintptr(-1), 0, 0, 100, 100, SWP_NOSIZE|SWP_NOMOVE)
}

All of this and more is documented on the Microsoft website.

Edit: or if you wanna do it the boring way there's probably packages for it out there that do the same thing.

Jacalz commented 1 year ago

This seems to be achievable on desktop using the GLFW support per https://github.com/fyne-io/fyne/issues/3429#issuecomment-1697928145

andydotxyz commented 1 year ago

It's worth reading the GLFW docs relates to the feature they made available. They stress caution and suggest it should be used mostly for debugging IIRC.

phillvancejr commented 1 year ago

"GLFW_FLOATING specifies whether the windowed mode window will be floating above other regular windows, also called topmost or always-on-top. This is intended primarily for debugging purposes and cannot be used to implement proper full screen windows. Possible values are GLFW_TRUE and GLFW_FALSE. This hint is ignored for full screen windows." from https://www.glfw.org/docs/3.3/window_guide.html

Maybe there is another place where more detail is given but I wouldn't say this text stresses caution. The previous comment to me implies that this is almost dangerous, but all the docs say is that it is intended for debugging, and that it can't be used to implement full screen windows. Having a window always on top and full screen to me are more or less unrelated, of course a full screen window is "on top" of all the others in a sense, but the use case for a topmost window and a full screen window are completely different. I'm assuming GLFW implements this on windows using SetWindowPos and the HWND_TOPMOST and on mac using the NSWindow level property set to NSFloatingWindowLevel. I don't know anything about X11/Wayland on Linux which is what GLFW uses there (as far as I know) so I can't comment on that, but on the formerly mentioned windows and mac platforms neither microsoft nor apple give any sort of warning, caution or disclaimer about setting windows topmost, its just a normal thing that a developer might want to do. Perhaps on X11/Wayland there is some caution, again I know nothing about those systems, but I can't imagine what would be dangerous about such an operation. Given that at least in the Windows & Mac documentation there is no caution presented with topmost windows, I would guess that GLFW's "caution" concerning this is either due to something weird with X11/Wayland on linux or its just arbitrary or the GLFW devs opinion, rather than something that is the result of real cause for caution. Again I'm not presuming to know about all the issues this could potentially cause, I'm just not aware of any nor can I think of any other than maybe your app will be annoying to use in some instances, but here the solution is just don't set the window topmost as opposed to not supporting it at all because its possible that one application using it might annoy some people.

I think its probably worth repeating here what I mentioned to Jacalz in the other thread concerning window managers and users being able to decide for themselves whether something is topmost or not: as far as I'm aware the ability for a user to choose whether or not a window is topmost is OS specific. What I mean is that apparently systems using Gnome allow this (and I assume some other linux flavors?) but Mac certainly does not have this feature, and I don't think Windows supports this out of the box either (correct me if I'm wrong). As far as I know and have since yesterday done some additional researching on, in Mac and Windows you must use third party applications to make a window topmost if the developer of the app did not program the app with that functionality.

I definitely understand if this is a hard no for a good reason, but seeing as this thread as been open for 3 years I'm not sure there is much concrete evidence this would be harmful in anyway. I'd like to see this in Fyne as I find it useful personally and its easy to implement via GLFW and also on Windows/Mac natively. I'd prefer not to have to maintain my personal copy of Fyne that has this functionality but if thats the only option its not a huge deal I suppose.

andydotxyz commented 1 year ago

the docs say is that it is intended for debugging,

That is reason enough to stop and pause to consider if it's a good idea to be able for all GUI applications.

I would guess that GLFW's "caution" concerning this is either due to something weird with X11/Wayland on linux

Possibly so, it is not supported on Wayland: https://github.com/rust-windowing/winit/issues/899

What I mean is that apparently systems using Gnome allow this (and I assume some other linux flavors?) but Mac certainly does not have this feature, and I don't think Windows supports this out of the box either

This article implies macOS does: https://www.thetechedvocate.org/how-to-keep-your-application-window-always-on-top-in-mac/. However Windows doesn't, and a lot of folk are discussing - but there are solutions https://www.alphr.com/always-on-top-windows-10/

In general it is indeed OS specific, as are many other window handling features. The problem with baking it into the application is how do you manage multiple always-on-top windows? (answer: you can't). I guess we (Fyne devs) don't see this as positive UX.

There are many discussions online, not least because on-top launching will steal focus, such as https://answers.microsoft.com/en-us/windows/forum/all/disable-annoying-always-on-top-application-windows/50dc0605-b707-4f54-8494-e72724548376 or https://superuser.com/questions/176394/disable-window-always-on-top

Essentially this is really subjective and many folk are leaving it the end user and desktop/WM to decide.

I definitely understand if this is a hard no for a good reason

On the flip side, just because something is easy to implement does not mean it should be added to the API.

phillvancejr commented 1 year ago

This article implies macOS does: https://www.thetechedvocate.org/how-to-keep-your-application-window-always-on-top-in-mac/.

Came across this when I was researching after the initial post. Not sure if you actually read it, but only the first option is built in to mac, tried it, it does not work on my Apple M2 Pro Ventura laptop. All the other options require third party applications

However Windows doesn't, and a lot of folk are discussing - but there are solutions https://www.alphr.com/always-on-top-windows-10/

Also requires third party applications

The problem with baking it into the application is how do you manage multiple always-on-top windows? (answer: you can't). I guess we (Fyne devs) don't see this as positive UX.

Is this a real concern? In what real situation would you have multiple always on top windows? I don't think its a practical situation you'd actually encounter. When are you actually going to be using either a single multi window app where all windows are always on top or using multiple different applications that want to be always on top? I just don't see that being a real issue. I'd be really surprised if anyone here actually had experience with that, I've never experienced it on mac, maybe it is common on linux or windows, but I do doubt that is the case.

There are many discussions online, not least because on-top launching will steal focus, such as https://answers.microsoft.com/en-us/windows/forum/all/disable-annoying-always-on-top-application-windows/50dc0605-b707-4f54-8494-e72724548376 or https://superuser.com/questions/176394/disable-window-always-on-top

Ah here we have a case of someone actually experiencing windows always on top and it being an annoyance, but as I've said before, isn't the solution just to not use said apps? It seems pretty simple to just not use an app that is forcing itself on top of others if you find that annoying.

In general it is indeed OS specific, as are many other window handling features. The problem with baking it into the application is how do you manage multiple always-on-top windows? (answer: you can't). I guess we (Fyne devs) don't see this as positive UX.

Just tried it in Fyne with my custom topmost code added actually. I don't know what you mean by "manage" but all that happens is that all windows marked as topmost are topmost above all others that aren't and the topmost topmost window is whichever was focused last. I have this code:

func main() {
    a := app.New()
    w := a.NewWindow("Hello")
    w.Topmost(true)
    w.Resize(fyne.NewSize(500,500))

    w2 := a.NewWindow("other")
    w2.Topmost(true)
    w2.Resize(fyne.NewSize(200,200))
    w2.Show()

    w.ShowAndRun()
}

https://github.com/fyne-io/fyne/assets/13558411/35e24cbc-db85-40ec-be26-ba9448faaf6f

Additionally to "manage" windows you can still use the windowsHide and RequestFocus methods on topmost windows to manage them, and you can also use Topmost(false) to turn off topmost mode. This all seems easily manageable with multiple windows and I don't know exactly what windows does but Mac seems to handle it fine. The following code does exactly what you'd expect it to:

func main() {
    a := app.New()
    w := a.NewWindow("Hello")
    w.Topmost(true)
    w.Resize(fyne.NewSize(500,500))

    w2 := a.NewWindow("other")
    w2.Topmost(true)
    w2.Resize(fyne.NewSize(200,200))
    w2.Show()
    go func() {
        time.Sleep(time.Second * 1)
        w.RequestFocus()
        time.Sleep(time.Second * 3)
        w2.Hide()
    }()

    w.ShowAndRun()
}
andydotxyz commented 1 year ago

Not sure if you actually read it, but only the first option is built in to mac, tried it, it does not work on my Apple M2 Pro Ventura laptop. All the other options require third party applications

Let's just assume for a moment that I did read the posts that I linked (seems like a polite assumption). Yes I am aware that these link to 3rd party tools. But what you see here is people commenting on the lack of features in the OS, not lack of features in the apps...

Is this a real concern? In what real situation would you have multiple always on top windows?

The situation where app developers decide how my windows should be stacked and I/the OS lose control.

It's a bit like the deprecated LightTheme and DarkTheme APIs - developers were enforcing their preference on users so we are working to remove that. We want users to be in control.

isn't the solution just to not use said apps?

I may be misunderstanding, but it seems like you are championing a feature that you're happy may upset your users but that's OK they can go use different software?

Additionally to "manage" windows you can still use the windows Hide and RequestFocus methods on topmost windows to manage them

You're talking about app/API based management of windows - but I am trying to discuss the users perspective.

phillvancejr commented 1 year ago

Hmm there seems to be a lot of opposition to this, I guess it's a no which is totally fine just unexpected given how old this open issue is.

Let's just assume for a moment that I did read the posts that I linked (seems like a polite assumption). Yes I am aware that these link to 3rd party tools. But what you see here is people commenting on the lack of features in the OS, not lack of features in the apps...

I think you interpreted by genuine uncertainty that you read the article with rudeness or criticism for not doing so. This was not the intent, instead, and my apologies, but I just dont understand the point of linking those articles. This issue asked about giving the dev the ability to make a topmost window while the link proposes third party solutions. In the previous reply you've mentioned that people are complaining about the OS not the app

But what you see here is people commenting on the lack of features in the OS, not lack of features in the apps...

I think it's fair to say that this statement does not reflect the issue in its entirety because if you read through the comments on the super user post multiple posts mention that the issue is with the app itself, not the os, for example this one:

"Yes, when I am unable to bring another window to the top (I can focus it, but it'll remain in the background), I'm pretty sure that is because a windows was set to always-on-top. Now that is a conscious design flaw of the ones behind that program"

Notice the problem is "the ones behind the program", not "the ones behind the OS" as you've stated.

But the solution to this is actually trivial (and obvious) and a later super user comment has the answer:

"I think always on top is ok if the application provides an option to control this behaviour (WinAmp comes to mind, they've been doing just this). Sometimes the behaviour can be desired by the user, but the programmers shouldn't decide for every possible user that they should desire... "

These comments indicate that the problem is actually about a lack of features in the app, in this case the feature would be being able to toggle the always on top behavior when an app uses it. This is simple,  just expose a menu item to disable the topmost behavior in the app.

The situation where app developers decide how my windows should be stacked and I/the OS lose control.

Except you dont actually lose any control if the app that uses topmost also lets you disable it. If an app makes itself topmost, then you as the user decide you dont like it and disable topmost through whatever means the app exposes that whether via menu item, button, hotkey or some other means then what control have you or the OS lost?

I may be misunderstanding, but it seems like you are championing a feature that you're happy may upset your users but that's OK they can go use different software?

First, yes you've misunderstood because you wont find a place in this discussion where I've stated I'd be happy that a feature upset my users. You're presenting my previous statement as if I'm intentionally doing something to annoy users knowing it would be annoying but I never stated this. Second, even if that was the case ... why does that matter? Why does it matter if I as a developer choose to make a crappy app? Bad applications and ux are usually an issue of application design, not in most cases the framework used to build the application. That is to say, a  bad ux resulting from the misuse of some feature Fyne could implement reflects poorly on the application, not necessarily on Fyne. Rather than "championing a feature that you're happy may upset your users", I'm defending a feature that gives developers more control over the applications they can make with Fyne. Whereas the opposition to it seems to be presuming to know more about the user base of an application than the developer of that application.  Third, in my case the second point I've previously made is a devil's advocate argument and does not actually apply to my use of the topmost feature. I currently have an application  internal to my design company that has been in production for 4 years and which is universally used by the whole graphic design team. This app uses the topmost feature and it does not annoy my users and in fact is preferred to not having the option in this case. I know this for several reasons, one is because I am both the developer and a user (im a graphic designer) of the app and I designed the app specifically to solve the problems my team and I were faced with, and the second reason I know it doesnt annoy my users is because I know all of them personally and thus have communicated with them in person, via email and in meetings and have only received positive feedback on the apps behavior. And to reiterate, the topmost ability in this case is actually a desirable feature, because it allows us to work smoothly without continuously having to flip back and forth between windows. So the point of that is, I know how to use the feature and have first hand experience both as a user and direct positive feedback from my user base. So to conclude this part, yes I actually do think saying "if my app isnt for you then use another " is completely valid, but in my case is not actually a problem because I use the feature in a useful way, and this which has been corroborated by my users which includes myself

You're talking about app/API based management of windows - but I am trying to discuss the users perspective.

The user can still minimize, focus, and resize the window freely, and exposing a menu, button or hotkey which allows the user to disable topmost is an obvious and trivial solution that addresses the users perspective.  Most of the objections here and in the links posted are concerned with poor design of the application due to first of all not understanding the user bases desired features and second not putting in a way to opt out of the developers (in this case incorrect) assumptions about what is useful. That is to say, if a dev makes an app that has the topmost feature and their users dont like it, that means that dev is bad at designing apps and ux, it does not mean topmost as a feature is bad.

All that being said I have no hard feelings over this not being implemented because as can be seen in  the video from the previous post, I've already implemented it for myself in my own local version. As I mentioned before if you dont want to support the feature that's no problem but if that is the case I think it would be clearer to people if issues like this were closed with a direct answer that Fyne wont support this. Since this issue is 3 years old and open I thought Fyne was more open to it, if all the issues on this topic were closed with a no, I (and anyone else searching for this) would have had an answer earlier

But with that I want to say regardless of whether or not this is added, thank you all for Fyne, it is a great library! No hard feelings and regards to all.

andydotxyz commented 1 year ago

Hmm there seems to be a lot of opposition to this, I guess it's a no which is totally fine just unexpected given how old this open issue is.

We continue to discuss this, and the topic of other "won't work on all platforms but some folk really want it" issues.

The point of discussion and links attached here are that the inability to control windows in the ways users are requesting at an OS level is an oversight of some platforms. Allowing apps to make these decisions instead creates a poor user experience - which is why its not a clear answer, and the context keeps changing.

Why does it matter if I as a developer choose to make a crappy app?

As an opinionated toolkit it is our job to make the great things easy and the potentially negative outcomes difficult

Consider it like apps offering the light/dark mode switch. This was required before OS offered this as a general feature but now it has become standard having apps all control theme separately creates a worse experience. Apps controlling window stacking order feels similar to this.

We finally moved to a custom theme option whereby devs can override user preference if required. It is intentionally not simple - but possible. If we can find a similar approach here for power users without polluting our API with bad UX features it is more likely this will be made possible.

But with that I want to say regardless of whether or not this is added, thank you all for Fyne, it is a great library!

Many thanks, keep enjoying the toolkit :)

NicolasDenier commented 10 months ago

I am using i3 as window manager, and I found that setting window.SetFixedSize(true) makes the window starts in floating mode.