Managing Window Z-Order Like Photoshop CS - c++

So I've got an application whose window behavior I would like to behave more like Photoshop CS. In Photoshop CS, the document windows always stay behind the tool windows, but are still top level windows. For MDI child windows, since the document window is actually a child, you can't move it outside of the main window. In CS, though, you can move your image to a different monitor fine, which is a big advantage over docked applications like Visual Studio, and over regular MDI applications.
Anyway, here's my research so far. I've tried to intercept the WM_MOUSEACTIVATE message, and use the DeferWindowPos commands to do my own ordering of the window, and then return MA_NOACTIVATEANDEAT, but that causes the window to not be activated properly, and I believe there are other commands that "activate" a window without calling WM_MOUSEACTIVATE (like SetFocus() I think), so that method probably won't work anyway.
I believe Windows' procedure for "activating" a window is
1. notify the unactivated window with the WM_NCACTIVATE and WM_ACTIVATE messages
2. move the window to the top of the z-order (sending WM_POSCHANGING, WM_POSCHANGED and repaint messages)
3. notify the newly activated window with WM_NCACTIVATE and WM_ACTIVATE messages.
It seems the cleanest way to do it would be to intercept the first WM_ACTIVATE message, and somehow notify Windows that you're going to override their way of doing the z-ordering, and then use the DeferWindowPos commands, but I can't figure out how to do it that way. It seems once Windows sends the WM_ACTIVATE message, it's already going to do the reordering its own way, so any DeferWindowPos commands I use are overridden.
Right now I've got a basic implementation quasy-working that makes the tool windows topmost when the app is activated, but then makes them non-topmost when it's not, but it's very quirky (it sometimes gets on top of other windows like the task manager, whereas Photoshop CS doesn't do that, so I think Photoshop somehow does it differently) and it just seems like there would be a more intuitive way of doing it.
Anyway, does anyone know how Photoshop CS does it, or a better way than using topmost?

I havn't seen anything remarkable about Photoshop CS that requries anything close to this level of hacking that can't instead be done simply by specifying the correct owner window relationships when creating windows. i.e. any window that must be shown above some other window specifies that window as its owner when being created - if you have multiple document windows, each one gets its own set of owned child windows that you can dynamically show and hide as the document window gains and looses activation.

You can try handle WM_WINDOWPOSCHANGING event to prevent overlaping another windows (with pseudo-topmost flag). So you are avoiding all problems with setting/clearing TopMost flag.
public class BaseForm : Form
{
public virtual int TopMostLevel
{
get { return 0; }
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumThreadWindows(uint dwThreadId, Win32Callback lpEnumFunc, IntPtr lParam);
/// <summary>
/// Get process window handles sorted by z order from top to bottom.
/// </summary>
public static IEnumerable<IntPtr> GetWindowsSortedByZOrder()
{
List<IntPtr> handles = new List<IntPtr>();
EnumThreadWindows(GetCurrentThreadId(),
(hWnd, lparam) =>
{
handles.Add(hWnd);
return true;
}, IntPtr.Zero);
return handles;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == (int)WindowsMessages.WM_WINDOWPOSCHANGING)
{
//Looking for Window at the bottom of Z-order, but with TopMostLevel > this.TopMostLevel
foreach (IntPtr handle in GetWindowsSortedByZOrder().Reverse())
{
var window = FromHandle(handle) as BaseForm;
if (window != null && this.TopMostLevel < window.TopMostLevel)
{
//changing hwndInsertAfter field in WindowPos structure
if (IntPtr.Size == 4)
{
Marshal.WriteInt32(m.LParam, IntPtr.Size, window.Handle.ToInt32());
}
else if (IntPtr.Size == 8)
{
Marshal.WriteInt64(m.LParam, IntPtr.Size, window.Handle.ToInt64());
}
break;
}
}
}
base.WndProc(ref m);
}
}
public class FormWithLevel1 : BaseForm
{
public override int TopMostLevel
{
get { return 1; }
}
}
So, FormWithLevel1 will be always over any BaseForm. You can add any number of Z-order Levels. Windows on the same level behave as usual, but will be always under Windows with level Current+1 and over Windows with level Current-1.

Not being familiar to Photoshop CS it is bit hard to know exactly what look and feel you are trying to achieve.
But I would have thought if you created a modeless dialog window as you tool window and you made sure it had the WS_POPUP style then the resulting tool window would not be clipped to the main parent window and Windows would automatically manage the z-Order making sure that the tool window stayed on top of the parent window.
And as the tool window dialog is modeless it would not interfere with the main window.

Managing Window Z-Order Like Photoshop CS
You should create the toolwindow with the image as the parent so that windows manage the zorder. There is no need to set WS_POPUP or WS_EX_TOOLWINDOW. Those flags only control the rendering of the window.
Call CreateWindowEx with the hwnd of the image window as the parent.

In reply to Chris and Emmanuel, the problem with using the owner window feature is that a window can only be owned by one other window, and you can't change who owns a window. So if tool windows A and B always need to be on top of document windows C and D, then when doc window C is active, I want it to own windows A and B so that A and B will always be on top of it. But when I activate doc window D, I would have to change ownership of tool windows A and B to D, or else they will go behind window D (since they're owned by window C). However, Windows doesn't allow you to change ownership of a window, so that option isn't available.
For now I've got it working with the topmost feature, but it is a hack at best. I do get some consolation in the fact that GIMP has tried themselves to emulate Photoshop with their version 2.6, but even their implementation occasionally acts quirky, which leads me to believe that their implementation was a hack as well.

Have you tried to make the tool windows topmost when the main window receives focus, and non-topmost when it loses focus? It sounds like you've already started to look at this sort of solution... but much more elaborate.
As a note, it seems to be quite well documented that tool windows exhibit unexpected behavior when it comes to z-ordering. I haven't found anything on MSDN to confirm it, but it could be that Windows manages them specially.

I would imagine they've, since they're not using .NET, rolled their own windowing code over the many years of its existence and it is now, like Amazon's original OBIDOS, so custom to their product that off-the-shelf (aka .NET's MDI support) just aren't going to come close.
I don't like answering without a real answer, but likely you'd have to spend a lot of time and effort to get something similar if Photoshop-like is truly your goal. Is it worth your time? Just remember many programmers over many years and versions have come together to get Photoshop's simple-seeming windowing behavior to work just right and to feel natural to you.
It looks like you're already having to delve pretty deep into Win32 API functions and values to even glimpse at a "solution" and that should be your first red flag. Is it eventually possible? Probably. But depending on your needs and your time and a lot of other factors only you could decide, it may not be practical.

Related

which window is on top on the other

I have 2 windows and I want to know which window is on the top of the other?
I tried to test using GetWindowLong and comparing the results but no chance.
LONG wndState1 = ::GetWindowLong(handler1, GWL_EXSTYLE);
LONG wndState2 = ::GetWindowLong(handler2, GWL_EXSTYLE);
both results is equal to 256.
Edited: In the picture below I have the dialog of notepad++ is on top of the FileZilla, How do I Get That by Code.
Is there a trick for that ?
THank you
GetWindowLong is used to retrieve style information for a particular window.
In order to get the top-most window, use
HWND WINAPI GetForegroundWindow(void);
You will still need to know the window handles (HWND) for the processes you're interested in so that you can find out which process owns the foreground window.
Note that this API can only return the window that the user is interacting with (or has interacted with most recently).
UPDATE:
I agree with Remy that there isn't any API to do that. The only way I can think of to actually do that is to install a global hook and intercept certain messages (e.g. WM_ACTIVATE, WM_SETFOCUS and so on). Since you will also retrieve the timestamps for the messages, it should be simple to infer which window is on top of any other window. This will require you to write a dll but this is relatively simple to do. I can't guarantee this will work either though I think it will (I've written a global hook but never used it to find out the z-order of windows)

Create a child window inside another application

I'm trying to write a bot in C++ for a PC game using the windows API. I've managed to find the process HANDLE, its ID and finally its HWNDwith EnumWindows. I've managed also to send messages to its window (using SendMessage) in order to simulate keystrokes. Since the game runs full screen I'd like to create a child window inside it with custom controls to switch on/off the bot easily but i'm having problems with it. I've tried to register a window class and create a window with the game's HWND as a parent but I can't see anything. This is the code I've used to create the child window (not working):
// ... Window class registering etc...
HWND hChild = CreateWindowEx(0,
"MyWindowClass",
"Title",
WS_CHILD | WS_VISIBLE,
0, 0, 100, 100,
hParent,
NULL,
AHInstance,
NULL); // Parent is a valid window handle from another process
Honestly I'm not a skilled windows API programmer, this whole thing is new to me. I'm not even sure if this is the right way to do such thing.
[Edit.] When i minimize/maximize the game my window shows correctly for a second or two. It seems that the game's rendering is happening after the rendering of my window. Any idea? And what about messages? Should i manage them or not? Is the game going to forward messages to my window?
You are not going to have much success by running the code from a different process. Cross process parent/child relationships seldom work. Especially if the other process is not expecting it. This is not something you take on lightly. The capability only exists as a compatibility sop to 16 bit Windows. No place for that technique today.
It seems plausible to me that you don't need to parent the window in this way. If you don't need it to be a child of a window in the other process, don't do that.
If you do need it to be a child of the other process then you might be in trouble. Perhaps you could inject a DLL into the other process and create the window there. Also not without difficulties.
Windows have thread affinity. This means that the thread that creates the window will be processing the messages for that window. Furthermore, all windows in a parent-child chain should be processed by the same thread. Otherwise, bad things happen.
So - how is your code being run? If it's a separate executable, you're going to be much better off leaving your window as a top-level window, and simply monitoring the target window for move events (using UI Automation, for example) and having your window move appropriately.
Otherwise, you're going to need to inject your code into the game executable, and have it create the windows on the appropriate thread. I wouldn't recommend that.

Windows.h - Notification when focus enters a text input

I'm trying to come up with a solution for setting up a notification when focus enters a text field. The end goal in mind is to recreate the type of functionality you see on mobile devices with on screen keyboards.
So far I've been exploring SetWinEventHook with EVENT_OBJECT_FOCUS and GetGUIThreadInfo with GUI_CARETBLINKING.
From the docs:
EVENT_OBJECT_FOCUS
An object has received the keyboard focus. The system sends this event
for the following user interface elements: list-view control, menu
bar, pop-up menu, switch window, tab control, tree view control, and
window object.
GUI_CARETBLINKING The caret's blink state. This bit is set if the
caret is visible.
Using these methods I've come up with this solution:
void TextInputHelper::setupEventHook(FREContext iCtx)
{
ctx = iCtx;
CoInitialize(NULL);
evHook = SetWinEventHook(EVENT_OBJECT_FOCUS, EVENT_OBJECT_END, NULL,
handleEventObjectFocus, 0, 0,
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
}
void CALLBACK handleEventObjectFocus(HWINEVENTHOOK hook, DWORD evt, HWND hwnd,
LONG idObj, LONG idChild, DWORD thread, DWORD time)
{
GUITHREADINFO threadInfo;
threadInfo.cbSize = sizeof(GUITHREADINFO);
BOOL result = GetGUIThreadInfo(thread, &threadInfo);
if(threadInfo.flags & GUI_CARETBLINKING)
{
//text field focus
}
}
This does seem to work in some cases but its definitely not reliable. Programs like Notepad an IE seem to work fine but others like Firefox do not. This also will not work for things like text fields on websites because it doesn't look like handleEventObjectFocus will get called.
Does anyone know of another way to approach this problem? I've been searching around and it seems like I might be looking for something in the Accessibility APIs but I haven't been able to dig up to much on it.
Thanks!
edit
To clarify, I'm looking to receive a notification when focus enters any text field. This application is a win32 dll and will never have focus its self.
If you're using standard Windows controls WM_SETFOCUS should do the trick. No need to get fancy with hooking etc.
EDIT: For system wide behavior you can check out SetWindowsHookEx. To catch events system-wide you need to use it from within a DLL. You can use a combination of hooks including one that catches WM_SETFOCUS.
If you're trying to supply an alternative text input method, you should look into "IME" - "Input Method Editor". These are directly supported by the OS.
You can catch the entrance into a text field using the EN_SETFOCUS notification.
WM_FOCUS is for the window itself, not for controls that are in it. Otherwise said, if you want to use WM_FOCUS, you'll have to subclass your EDIT field. It's not necessary here.
EDIT: it wasn't completely clear that you wanted a system-wide behavior. IN that case you have to use an hook (cf. SetWindowsHookEx) as explained in the answer above. Sorry.

How to add custom item to system menu in C++?

I need to enumerate all running applications. In particular, all top windows. And for every window I need to add my custom item to the system menu of that window.
How can I accomplish that in C++?
Update.
I would be more than happy to have a solution for Windows, MacOS, and Ubuntu (though, I'm not sure if MacOS and Ubuntu have such thing as 'system menu').
For Windows, another way to get the top-level windows (besides EnumWindows, which uses a callback) is to get the first child of the desktop and then retrieve all its siblings:
HWND wnd = GetWindow(GetDesktopWindow(), GW_CHILD);
while (wnd) {
// handle 'wnd' here
// ...
wnd = GetNextWindow(wnd, GW_HWNDNEXT);
}
As for getting the system menu, use the GetSystemMenu function, with FALSE as the second argument. The GetMenu mentioned in the other answers returns the normal window menu.
Note, however, that while adding a custom menu item to a foreign process's window is easy, responding to the selection of that item is a bit tricky. You'll either have to inject some code to the process in order to be able to subclass the window, or install a global hook (probably a WH_GETMESSAGE or WH_CBT type) to monitor WM_SYSCOMMAND messages.
Once you have another window's top level handle, you may be able to call GetMenu() to retrieve the Window's system menu and then modify it, eg:
HMENU hMenu = GetMenu(hwndNext);
You can use EnumWindows() to enumerate top level Windows.
I don't have a specific answer for the second part of your question, but if you subclass the window, I imagine you can modify the system menu.
EDIT: or do what Chris said: call GetMenu()
Re: the update - please note that not even Microsoft Windows requires windows to have a sytem menu. GetMenu( ) may return 0. You'll need to intercept window creation as well, because each new top window presumably needs it too.
Also, what you propose is rather intrusive to other applications. How are you going to ensure they don't break when you modify their menus? And how are you going to ensure you suppress the messages? In particular, how will you ensure you intercept them before anyone else sees them? To quote Raymond Chen, imagine what happens if two programs would try that.

How to create a non-interactive window in MFC

In my application I have a window which I popup with small messages on it (think similar to tooltip). This window uses the layered attributes to draw alpha backgrounds etc.
If I have several of these windows open at once, and I click one with my mouse, when they disappear they cause my application to lose focus (it switches focus to the app behind the current one).
How do I stop any interaction in my window?
It works because OnSetFocus (like many of the On* methods) gives you a chance to pre-empt an action before it actually occurs. The focus never actually switches to your non-interactive window.
After playing with the WM_NCACTIVATE message with no luck, I overrode the WM_SETFOCUS message:
void CMyWindow::OnSetFocus(CWnd* pOldWnd)
{
if (pOldWnd != NULL)
{
pOldWnd->SetFocus();
}
}
That seems to do the trick. No idea why it works though! Comments welcome on that issue.