Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Property Browser window stays on top after switching focus #412

Closed
nabijaczleweli opened this issue May 18, 2019 · 14 comments
Closed

Property Browser window stays on top after switching focus #412

nabijaczleweli opened this issue May 18, 2019 · 14 comments
Milestone

Comments

@nabijaczleweli
Copy link
Contributor

System information

SolveSpace version: 3.0~62aba398

Operating system: Windows 10

Expected behavior

The Property Browser window is obscured alongside the sketch window after changing focus to a different window.

Actual behavior

The Property Browser window stays on top of whatever new window was selected. Clicking on any part of the window focuses it and brings back the sketch window.

Additional information

Works as expected on 2.3.

Screenshot

@whitequark
Copy link
Contributor

I believe the cause is somewhere along these lines:

DWORD style = WS_SIZEBOX|WS_CLIPCHILDREN;
switch(kind) {
case Window::Kind::TOPLEVEL:
style |= WS_OVERLAPPEDWINDOW|WS_CLIPSIBLINGS;
break;
case Window::Kind::TOOL:
style |= WS_POPUPWINDOW|WS_CAPTION;
break;
}
sscheck(hWindow = CreateWindowExW(0, L"SolveSpace", L"", style,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
hParentWindow, NULL, NULL, NULL));
sscheck(SetWindowLongPtr(hWindow, 0, (LONG_PTR)this));
if(hParentWindow != NULL) {
sscheck(SetWindowPos(hWindow, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE));
}

The relevant code in 2.3 is here:

static void CreateMainWindows(void)
{
WNDCLASSEX wc = {};
wc.cbSize = sizeof(wc);
// The graphics window, where the sketch is drawn and shown.
wc.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_OWNDC |
CS_DBLCLKS;
wc.lpfnWndProc = (WNDPROC)GraphicsWndProc;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszClassName = L"GraphicsWnd";
wc.lpszMenuName = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
IMAGE_ICON, 32, 32, 0);
wc.hIconSm = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
IMAGE_ICON, 16, 16, 0);
if(!RegisterClassEx(&wc)) oops();
HMENU top = CreateGraphicsWindowMenus();
GraphicsWnd = CreateWindowExW(0, L"GraphicsWnd",
L"SolveSpace (not yet saved)",
WS_OVERLAPPED | WS_THICKFRAME | WS_CLIPCHILDREN | WS_MAXIMIZEBOX |
WS_MINIMIZEBOX | WS_SYSMENU | WS_SIZEBOX | WS_CLIPSIBLINGS,
50, 50, 900, 600, NULL, top, Instance, NULL);
if(!GraphicsWnd) oops();
GraphicsEditControl = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDIT, L"",
WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS,
50, 50, 100, 21, GraphicsWnd, NULL, Instance, NULL);
// The text window, with a comand line and some textual information
// about the sketch.
wc.style &= ~CS_DBLCLKS;
wc.lpfnWndProc = (WNDPROC)TextWndProc;
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszClassName = L"TextWnd";
wc.hCursor = NULL;
if(!RegisterClassEx(&wc)) oops();
// We get the desired Alt+Tab behaviour by specifying that the text
// window is a child of the graphics window.
TextWnd = CreateWindowExW(0,
L"TextWnd", L"SolveSpace - Property Browser", WS_THICKFRAME | WS_CLIPCHILDREN,
650, 500, 420, 300, GraphicsWnd, (HMENU)NULL, Instance, NULL);
if(!TextWnd) oops();
TextWndScrollBar = CreateWindowExW(0, WC_SCROLLBAR, L"", WS_CHILD |
SBS_VERT | SBS_LEFTALIGN | WS_VISIBLE | WS_CLIPSIBLINGS,
200, 100, 100, 100, TextWnd, NULL, Instance, NULL);
// Force the scrollbar to get resized to the window,
TextWndProc(TextWnd, WM_SIZE, 0, 0);
TextEditControl = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDIT, L"",
WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS,
50, 50, 100, 21, TextWnd, NULL, Instance, NULL);
// Now that all our windows exist, set up gl contexts.
CreateGlContext(TextWnd, &TextGl);
CreateGlContext(GraphicsWnd, &GraphicsGl);
RECT r, rc;
GetWindowRect(TextWnd, &r);
GetClientRect(TextWnd, &rc);
ClientIsSmallerBy = (r.bottom - r.top) - (rc.bottom - rc.top);
}

There's a fair bit of difference between them. Do you think you could try playing with the WS_* bitfields and see if it helps? There's a bunch of different cases to be handled here with regards to focus handling, and evidently I missed some. Note that some of the WS_* values include the other, like WS_OVERLAPPEDWINDOW.

Here is the fairly confusing Microsoft doc for window styles.

@nabijaczleweli
Copy link
Contributor Author

nabijaczleweli commented May 20, 2019

I managed to arrive at the correct behaviour by applying the following:

diff --git a/src/platform/guiwin.cpp b/src/platform/guiwin.cpp
index 73da2da..ec6bfca 100644
--- a/src/platform/guiwin.cpp
+++ b/src/platform/guiwin.cpp
@@ -545,7 +545,7 @@ public:
                 break;

             case Window::Kind::TOOL:
-                style |= WS_POPUPWINDOW|WS_CAPTION;
+                style |= (/*WS_POPUP | */WS_BORDER | WS_SYSMENU)|WS_CAPTION|WS_CHILD;
                 break;
         }
         sscheck(hWindow = CreateWindowExW(0, L"SolveSpace", L"", style,

However, the window now looks like absolute garbage pretty bad (and, perhaps more importantly, appears permanently disabled):

Screenshot

Unfortunately, I haven't managed to make it look platable nor as-was in 2.3 (not for lack of slamming my head against a proverbial wall) with any combination of more or less sensible options.

Also, a few questions that popped up while I was making this:

  1. Do you consider warnings bugs? Because this file has two, so dunno what I should do about them, and
  2. Scrolling in the Property Browser selects the face that's below it (didn't happen in 2.3), is this intended?

@whitequark
Copy link
Contributor

However, the window now looks like absolute garbage pretty bad

Have you tried this? I think the property browser isn't intended to have a sysmenu, and that might make it look better:

style |= WS_BORDER|WS_CAPTION|WS_CHILD;

and, perhaps more importantly, appears permanently disabled

That's actually intended, or at least that was the 2.x behavior, IIRC. This should have the effect of forwarding key bindings to the parent window.

Can you try disabling the following code and seeing if key bindings (e.g. d) are still forwarded?

} else {
HWND hParent;
sscheck(hParent = GetParent(h));
if(hParent != NULL) {
sscheck(SetForegroundWindow(hParent));
sscheck(SendMessageW(hParent, msg, wParam, lParam));
}
}

Scrolling in the Property Browser selects the face that's below it (didn't happen in 2.3), is this intended?

Here is the part of the code that deals with scroll events:

case WM_MOUSEWHEEL:
// Make the mousewheel work according to which window the mouse is
// over, not according to which window is active.
POINT pt;
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
HWND hWindowUnderMouse;
sscheck(hWindowUnderMouse = WindowFromPoint(pt));
if(hWindowUnderMouse && hWindowUnderMouse != h) {
SendMessageW(hWindowUnderMouse, msg, wParam, lParam);
break;
}
event.type = MouseEvent::Type::SCROLL_VERT;
event.scrollDelta = GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? 1 : -1;
break;

I'm not entirely sure from your description what went wrong here.

Do you consider warnings bugs? Because this file has two, so dunno what I should do about them

Depends. Some warnings are useful, some are not, and compilers constantly add new warnings. You could post them here if you wanted.

@nabijaczleweli
Copy link
Contributor Author

The WS_BORDER|WS_CAPTION|WS_CHILD combo does look marginally better:
fresh combo SS

In both 2.3 as well as unpatched master, the Property Browser window can be focused, which provides a visual indicator, that it's interactable:
2.3 property browser SS

Commenting out the else block has no effect on d being forwarded in both WS_POPUPWINDOW|WS_CAPTION and WS_BORDER|WS_CAPTION|WS_CHILD window styles.

Another thing I saw in taking those screenshots is that in the WS_CHILD-containing variant the Property Browser window is constrained to the sketch window and cannot extend beyond it:
constraint SS

Here's a video illustrating the issue: the scrolling code works fine, but the as the mouse is over the Property Browser window and scrolling, if there's any selectable face in the sketch window at the mouse's position, it will be highlighted, until the mouse is moved.

And here's the warnings
[1/2] Building CXX object src/CMakeFiles/solvespace.dir/platform/guiwin.cpp.obj
../src/platform/guiwin.cpp: In function 'UINT SolveSpace::Platform::ssGetDpiForWindow(HWND)':
../src/platform/guiwin.cpp:70:78: warning: cast between incompatible function types from 'FARPROC' {aka 'long long int (*)()'} to 'SolveSpace::Platform::LPFNGETDPIFORWINDOW' {aka 'unsigned int (*)(HWND__*)'} [-Wcast-function-type]
             GetProcAddress(GetModuleHandleW(L"user32.dll"), "GetDpiForWindow");
                                                                              ^
../src/platform/guiwin.cpp: In function 'BOOL SolveSpace::Platform::ssAdjustWindowRectExForDpi(LPRECT, DWORD, BOOL, DWORD, UINT)':
../src/platform/guiwin.cpp:93:87: warning: cast between incompatible function types from 'FARPROC' {aka 'long long int (*)()'} to 'SolveSpace::Platform::LPFNADJUSTWINDOWRECTEXFORDPI' {aka 'int (*)(tagRECT*, long unsigned int, int, long unsigned int, unsigned int)'} [-Wcast-function-type]
             GetProcAddress(GetModuleHandleW(L"user32.dll"), "AdjustWindowRectExForDpi");

@whitequark
Copy link
Contributor

I fixed the warning in be0dc7e.

@whitequark
Copy link
Contributor

Now that I look closer at the window staying on top, well, there's code that does that:

if(hParentWindow != NULL) {
sscheck(SetWindowPos(hWindow, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE));
}

What if you comment it out and rely on parent-child relationship?

@nabijaczleweli
Copy link
Contributor Author

That did it, the window abides as expected!

@whitequark
Copy link
Contributor

As a Windows user, which set of WS_* flags do you think SolveSpace should use?

@whitequark
Copy link
Contributor

Here's a video illustrating the issue: the scrolling code works fine, but the as the mouse is over the Property Browser window and scrolling, if there's any selectable face in the sketch window at the mouse's position, it will be highlighted, until the mouse is moved.

Regarding this bug, I think I know what causes it, as well as why I can't reproduce it. Can you open another issue so it doesn't get lost?

@nabijaczleweli
Copy link
Contributor Author

WS_POPUPWINDOW|WS_CAPTION are actually perfect, now that it doesn't linger!

@whitequark
Copy link
Contributor

To recheck, do I understand it correctly that without that code, the property browser window is still on top of the main window at all times, but now it is no longer on top of other windows when the main window is not in focus?

@nabijaczleweli
Copy link
Contributor Author

Yes

@whitequark
Copy link
Contributor

Fixed in 5dbe090.

@nabijaczleweli
Copy link
Contributor Author

Superb, thank you!

@whitequark whitequark added this to the 3.0 milestone May 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants