Toggle window visibility on tray icon click using WinAPI - winapi

How can I bring a window to the top if not there, and close it otherwise, when the tray icon is clicked, using the Windows API? That is, very similar to what the taskbar icon does. By bring to the top I mean fully visible to the user.
I cannot find a way to tell whether the window is on top or not, because when the tray icon is clicked, the taskbar is always on top. I tried inspecting the Z-order of the windows but it looks inconsistent. For example, when the application window is on top and tray icon is clicked, I would expect a constant number of taskbar/tray windows to be brought to top, and the window immediately following them being the one that was on top. But that's not the case, it seems.
I could mostly implement it, the problem is that the toggle doesn't care whether the window is open and fully visible to the user (on top). When the tray icon is clicked and the window is currently closed, it is opened and brought to top correctly. When the window is open and on top, it is also closed correctly. However, when the window is open but not on top, it will be closed instead of being brought to foreground.

To expand upon what Hans Passant said:
"On top" is not important, since some windows can be always on top; it's whether the window is the foreground window or not that you're concerned with.
Use GetForegroundWindow() to see if your window is the foreground window and if not, SetForegroundWindow() to make it so.

Thanks to Mike Gelfand who solved the problem! The following function will return:
-1 when window is closed
0 when window is fully visible
1 or above when window is open but not fully visible
int GetMyWindowLevel(HWND myWindow) {
WINDOWINFO myWindowInfo;
myWindowInfo.cbSize = sizeof(myWindowInfo);
if (!GetWindowInfo(myWindow, &myWindowInfo))
return -1;
int myWindowLevel = -1;
HWND currentWindow = GetTopWindow(NULL);
for (; currentWindow != NULL;
currentWindow = GetNextWindow(currentWindow, GW_HWNDNEXT)) {
WINDOWINFO currentWindowInfo;
currentWindowInfo.cbSize = sizeof(currentWindowInfo);
if (!GetWindowInfo(currentWindow, &currentWindowInfo))
continue;
if ((currentWindowInfo.dwExStyle & WS_EX_TOPMOST) !=
(myWindowInfo.dwExStyle & WS_EX_TOPMOST) ||
(currentWindowInfo.dwStyle & WS_POPUP) != 0)
continue;
++myWindowLevel;
if (currentWindow == myWindow)
break;
}
return currentWindow == myWindow ? myWindowLevel : -1;
}

Related

Stop being topmost window

My window should be on top of a specific "target" window that I don't have control over.
When the target window is activated, I call SetWindowPos with HWND_TOPMOST to place my window on top of it while the target can still be the active window.
When the target window is no longer the foreground window, I want my window to still be on top of the target window, but no longer topmost, so other windows are not covered by it.
Two ideas I had:
Call SetWindowPos with hWndInsertAfter to be the just activated window. This fails when the just activated window is topmost, because my window then does not lose the topmost status. Another issue with this: If the just activated window is the desktop, then my window is placed below the target window.
Call SetWindowPos with HWND_NOTOPMOST to lose the topmost status. However, this brings my window to the top of all non-topmost windows, so it covers the just activated window. To fix this I have to bring the just activated window on top again with another SetWindowPos with HWND_TOP. This feels like the wrong way to do it and may cause flicker.
Is it possible to have a window just stop being topmost and placing it below the current foreground window?
The only automatic method to make a window permanently on top of another one whether the target window is top-most or not is an owner/owned relationship. You could try using SetParent to create this relationship but note that Raymond Chen does say it's not recommended.
Assuming you're tracking window activations somehow, I think your SetWindowPos idea (the first one) is the way to do it, with the following modification:
When the target window is active, set your window to HWND_TOPMOST
When the target loses activation, insert your window after the target window's immediate predecessor in the z-order (i.e. effectively still on top of the target window, but not top-most)
Something like this psuedo-code:
if (foregroundwindow == targetwindow)
SetWindowPos(my_window, HWND_TOPMOST, ...);
else
{
HWND hwndPred = GetWindow(targetwindow, GW_HWNDPREV);
if (!hwndPred)
{
// no predecessor so my_window will still be on top, just not top-most any more
if (GetWindowLong(targetwindow, GWL_EXSTYLE) & WS_EX_TOPMOST)
hwndPred = HWND_NOTOPMOST;
}
SetWindowPos(my_window, hwndPred, ...);
}

Cocoa - go to foreground/background programmatically

I have an application with LSUIElement set to 1. It has a built-in editor, so I want the application to appear in Cmd+Tab cycle when the editor is open.
-(void)stepIntoForeground
{
if (NSAppKitVersionNumber < NSAppKitVersionNumber10_7) return;
if (counter == 0) {
ProcessSerialNumber psn = {0, kCurrentProcess};
OSStatus osstatus = TransformProcessType(&psn, kProcessTransformToForegroundApplication);
if (osstatus == 0) {
++counter;
} else {
//...
}
}
}
-(void)stepIntoBackground
{
if (NSAppKitVersionNumber < NSAppKitVersionNumber10_7) return;
if (counter == 0) return;
if (counter == 1) {
ProcessSerialNumber psn = {0, kCurrentProcess};
OSStatus osstatus = TransformProcessType(&psn, kProcessTransformToUIElementApplication);
if (osstatus == 0) {
--counter;
} else {
//..
}
}
}
The problems are:
there's also a Dock icon (not a big deal);
there's also Menu, that is not a big deal too, but they appear not always.
Is there any way to disable menu at all or to make it appear always in foreground? Thanks in advance.
This is how we do it.
(Works 10.7+)
DO NOT USE LSBackgroundOnly NOR LSUIElement in the app plist
Add and init your menu and NSStatusBar menu
After app initialized but not yet shown any window take a place where you might want to show the first window if any. We use applicationDidFinishLaunching.
If you do not want to show any window yet after app initialized use
[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
on 10.9 you can use at last the otherwise much correct
[NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
If you should open any window after app init finished than simply show the main window
Maintain your list of windows
If last window closed, call
[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
on 10.9 you can use at last the otherwise much correct
[NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
When your first window shown next time, call
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp activateIgnoringOtherApps:YES];
[[self window] makeKeyAndOrderFront:nil];
This should do the trick, if at least one app window is visible you will have menu, dock icon with state signaled, and cmd+tab element with your app, if last app window closed only your NSStatusBar element stays.
Known issues:
The first step is important because without that if a system modal dialog suspends your startup (f.e. your app is downloaded from the net and become quarantined a confirmation dialog might appear at first startup depending on your security settings) your menubar might not be owned by your app after your first app window shown.
Workaround: Starting as normal app (step 1.) would solve this problem, but will cause another small one, your app icon might appear for a moment in the dock at startup even if you would like to startup without any window shown. (but we can deal with this, not owning the menubar was a bigger problem for us, so we chose this instead)
Changing between NSApplicationActivationPolicyRegular and NSApplicationActivationPolicyAccessory (or NSApplicationActivationPolicyProhibited on OSes bellow 10.9) will kill your tooltip of status bar menu element, the tooltip will be shown initially but will not ever after the second call of NSApplicationActivationPolicyAccessory -> NSApplicationActivationPolicyProhibited
Workaround: We could not find a working workaround for this and reported to Apple as a bug.
Changing from NSApplicationActivationPolicyRegular to NSApplicationActivationPolicyAccessory has other problems on some OS versions like there might be no more mouse events in visible app windows sometimes
Workaround: switch first to NSApplicationActivationPolicyProhibited (take care this leads to unwanted app messages, like NSApplicationWillResignActiveNotification, NSWindowDidResignMainNotification, etc. !)
Changing from NSApplicationActivationPolicyAccessory to NSApplicationActivationPolicyRegular is bogus as on some OS versions
the app main menu is frozen till the first app front status change
the app activated after this policy not always get placed front in the application order
Workaround: switch first to NSApplicationActivationPolicyProhibited, take care the final switch to the desired NSApplicationActivationPolicyRegular should be made delayed, use f.e. dispatch_async or similar
With swift 4, in applicationDidfinishLaunching(_:Notification)
NSApplication.shared.setActivationPolicy(.regular)
did the trick for me, but I was only trying to get keyboard focus to my programmatically created window. Thanks.
You can set App "Application is agent (UIElement)" to YES in your plist file.
EDIT:
I think there are some hacks to do this.
But it's really not the way it's meant to be.
Cmd+tab is for getting an application to foreground, but if you don't have a menu bar, it doesn't look like foreground to the user.
I'd rather make a menu bar to access the app.

windows application showing behind the taskbar on vista

I have an MFC application. In my application if I run on Windows XP it's working fine. But if I run in Windows Vista the MFC dialog hides behind the taskbar.
bool bHide=true;
CRect rectWorkArea = CRect(0,0,0,0);
CRect rectTaskBar = CRect(0,0,0,0);
CWnd* pWnd = CWnd::FindWindow("Shell_TrayWnd", "");
pWnd->ShowWindow(SW_SHOW);
if( bHide )
{ // Code to Hide the System Task Bar
SystemParametersInfo(SPI_GETWORKAREA,0,(LPVOID)&rectWorkArea,0);
if( pWnd )
{
pWnd->GetWindowRect(rectTaskBar);
// rectWorkArea.bottom -= rectTaskBar.Height();
rectWorkArea.bottom += rectTaskBar.Height();//-----to hide taskbar
SystemParametersInfo(SPI_SETWORKAREA,0,(LPVOID)&rectWorkArea,0);
// pWnd->ShowWindow(SW_SHOW);
pWnd->ShowWindow(SW_HIDE); //--to hide taskbar
}
}
I used this code but it hides the taskbar. But I want to show the application above the task bar.
You don't own the taskbar, so you are not supposed to hide it. You have the option to auto-minimize it by the way. You have another option of using secondary monitor without taskbar there.
On the primary monitor your app is given work area, you are being able to locate (judging from the code snippet provided above). It is the best to position your window within this area without interfering with the taskbar, whether it is above or beyond.
If you still feel like making it more like a competition "who is on top" with the task bar, you might want to take a look at SetWindowPos API and window Z-Order.
finally i found the solution , what we want to do is we should add the below code in our oninitdialog,
SetWindowPos(&this->wndTopMost,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
the above line is enough to show the mfc dialog on above the taskbar . but sometimes the focus of the dialog get changed looks hanged(no response in dialog) the application.if it occurs put the below code.
SetWindowPos(&this->wndBottom,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);

How can I change the taskbar icon using XIconifyWindow?

I am programming in Linux and am using X11 for my desktop. I would like to change the images of a minimized window in the task bar.
I am using Fluxbox.
I am currently using XIconifyWindow when the user clicks to minimize a window.
How can I control the little square icon in the taskbar when the user minimized it?
Thanks.
Thank you for your help. The code I'm using to minimize looks something like this:
void minWin(Window window, bool yes) const
{
if(yes)
{
XIconifyWindow(display, window, DefaultScreen(display));
}else{
XMapWindow(display, window);
}
}
So how would I put in some code to set the icon depending on some property?
Like
if myProp == "green" set green icon
else set red icon
use _NET_WM_ICON property of toplevel window

Problem with switching application and focus

Sorry but my english is very bad.
I am writing a winapi program in c and I have a problem. The program has a main window and NO DIALOG child windows (controls). The controls are directly attached to the main window. When I switch the application to another application and back again, the focus is set to the main window and not to the control that owns the focus before switching.
My message loop is:
while ((rGetMessage = GetMessage(&msg, NULL, 0, 0)) != 0 && rGetMessage != -1)
{
if(!IsDialogMessage(hwnd_principal, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
What is my error?
I use the IsDialogMessage function for that various keys work on (like TAB key in the controls).
When you switch back to your application, Windows will by default set the keyboard focus to its main window, regardless of which window had the focus when it was deactivated. If you want to do something different you need to handle WM_ACTIVATE and use SetFocus() to restore the focus to the control.

Resources