Skip to content

Win32: Fix button click area misaligned with icon in Property Browser#1680

Draft
Copilot wants to merge 3 commits intomasterfrom
copilot/fix-button-click-area-shift
Draft

Win32: Fix button click area misaligned with icon in Property Browser#1680
Copilot wants to merge 3 commits intomasterfrom
copilot/fix-button-click-area-shift

Conversation

Copy link

Copilot AI commented Mar 11, 2026

On Windows, dynamically toggling the vertical scrollbar changes the client area width without generating WM_SIZE. The rendering surface never resizes, so rendered content and hit-test coordinates diverge — producing stretched/blurry text and click areas shifted left of their icons. This regression affects Windows 10/11 but not Windows 2000/XP, where scrollbar visibility changes apparently did trigger WM_SIZE.

Changes (src/platform/guiwin.cpp)

  • Add WS_VSCROLL to Window::Kind::TOOL style — scrollbar space is reserved from window creation, so the client area width is stable from the start
  • Add SIF_DISABLENOSCROLL to ConfigureScrollbar — the key fix: when content fits the view (page ≥ range), Windows now disables (greys out) the scrollbar instead of hiding it, keeping the client area width constant as content changes; without this flag, SetScrollInfo auto-hides the scrollbar even with WS_VSCROLL set, changing the client area width without sending WM_SIZE
  • Remove ShowScrollBar call from SetScrollbarVisible — no longer needed; scrollbar enable/disable state is now managed entirely through ConfigureScrollbar; scrollbarVisible flag is still maintained for GetScrollbarPosition/SetScrollbarPosition guards
// ConfigureScrollbar — before
si.fMask = SIF_RANGE|SIF_PAGE;

// ConfigureScrollbar — after: disable instead of hide when not scrollable
si.fMask = SIF_RANGE|SIF_PAGE|SIF_DISABLENOSCROLL;
Original prompt

This section details on the original issue you should resolve

<issue_title>Button click area is shifted left of icon</issue_title>
<issue_description>### System information

SolveSpace version: 3.0~bb56daf3

Operating system: Windows 10

Expected behavior

Clicking Occluded Lines button must switch mode

Actual behavior

The right side of the button is non-operate, no hovering or clicking events do nothing.

Seems the hover-and-click area is shifted left (see attached pic)

Solid model buttons are shifted too, but less. I think the width of spacers are not calculated right.

Additional information

image
</issue_description>

<agent_instructions>See if you can identify a possible fix for issue #681</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@ruevs Works for me on Windows as well at 04b332d HD Graphics 630 OpenGL 4.5 the rest as @developex-dmytro-i @ruevs @developex-dmytro-i does your problem still happen on a recent build? Please try the "Edge" from here https://github.com/solvespace/solvespace/releases @ruevs I **_think_** that I could reproduce this a few days back on Windows and then it disappeared?!?! Needs testing with all options cleared from the registry/json - just a guess... @ruevs I can consistently reproduce this on Windows by deleting the registry settings of SolveSpace (`Computer\HKEY_USERS\S-xxxx\SOFTWARE\SolveSpace` registry key).

After this (as mentioned above) the contents of the property browser window are scaled incorrectly - they are very slightly enlarged. This is the reason the icons and the "click" areas do not line up. As soon as the window is resized the scaling fixes itself.

In the screen shots below:

  • The top one is the window after deleting the registry settings - the scaling is incorrect.
  • The bottom one is after slightly resizing the window.

I added the vertical red lines to emphasize the problem.
PropertyBrowserScalingProblem

The slight anti-aliasing of the top one is not an artifact of the screen shot - it is caused by the scaling and is visible on screen.

By the way the wrong scaling is only in the X (horizontal direction). Vertically the size is unchanged:
PropertyBrowserScalingProblemHorizontal

I did try to debug this a bit but did not immediately find the problem and ran out of time, I'll come back to it.</comment_new>
<comment_new>@phkahler
@ruevs You might try calling TextWindow::Resize() sometime after startup since resizing fixes the issue. If that works you can maybe track down something being not initialized correctly.

Edit: Now I'm thinking extWindow::DrawOrHitTestIcons() is drawing before the scrollbar is there, and hit testing afterward? That might change the result of window->GetContentSize()

Edit 2: I'm pretty sure it's not a problem in drawing the toolbar. It seems to be stretching the entire text window including the letters. Watch the "a" or even the whole word "active" when resizing fixes the issue.</comment_new>
<comment_new>@phkahler
I'm seeing this text stretching on Windows 11 Enterprise. When I start Solvespace the text is stretched and a bit blurry. If I resize it (vertically by dragging the bottom) the text snaps back to normal size. If I click on some of the text window menus (line styles / view / configuration) The text will be sized correctly or not depending if a scrollbar appears or disappears.

  1. Start solvespace - text is blurry.
  2. resize text window vertically to smallest height - text snaps back to normal
  3. click "line styles" - text on that page is blurry
  4. resize text window vertically so it does not need a scroll bar - text gets clear
  5. click "home" - text is clear
  6. click "line styles" - text is still clear
  7. resize text window to minimum height - so we have a scroll bar
  8. click home - text is blurry (because the previous page used a scroll bar and home does not)

At times during vertical resizing I can see a brief glitch when the scroll bar appears or disappears and the text is briefly the wrong horizontal size.</comment_new>
<comment_new>@phkahler
It's as if changing visibility of the scrollbar changes the width of the window but we don't change the size of our offscreen bitmap unless we get an actual window resize event. Changing scrollbar visibility by resizing the window works ...


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI changed the title [WIP] Fix button click area shift issue Win32: Fix button click area misaligned with icon in Property Browser Mar 11, 2026
@ruevs
Copy link
Member

ruevs commented Mar 12, 2026

@copilot your fix - making the toolbar permanently visible - is an interesting idea for a workaround.

Adding WS_VSCROLL did not achieve this. The scroll bar is still "dynamic" - appears and disappears when the content length changes.

Disregarding the above workaround. I'm very interested why resizing the the window causes the "content width value" to be updated and therefore the canvas redrawn with proper horizontal scaling. Can the "process" that happens when resizing the window be somehow recreated when showing/hiding the scroll bar?

An interesting side note - the exact same solvespace.exe has the problem on Windows 10 and windows 11 and does not have the problem on Windows 2000 and Windows XP. This means that the behaviour is somehow influenced by differences in the Win32 (GDI ?) implementations between these Windows versions. Can you find something based on this?

Copy link
Author

Copilot AI commented Mar 12, 2026

@copilot your fix - making the toolbar permanently visible - is an interesting idea for a workaround.

Adding WS_VSCROLL did not achieve this. The scroll bar is still "dynamic" - appears and disap...

You were right that WS_VSCROLL alone wasn't sufficient. The key missing piece was SIF_DISABLENOSCROLL.

When SetScrollInfo is called without SIF_DISABLENOSCROLL, Windows auto-hides the scrollbar (removing it from the client area, changing its width) whenever page >= range. This happens even with WS_VSCROLL set. On Windows 2000/XP, this change apparently triggered WM_SIZE, so ANGLE/GL would resize its surface. On Windows 10/11, it no longer does.

Fix in commit 53140cf: added SIF_DISABLENOSCROLL to the fMask in ConfigureScrollbar. Now when content fits the view, Windows disables (greys out) the scrollbar instead of hiding it — the client area width stays constant, ANGLE's swap chain stays in sync with the window, and click areas remain aligned with the rendered icons.

Combined with WS_VSCROLL ensuring the scrollbar space is reserved from creation, the client area width is now stable throughout the window's lifetime regardless of content length changes.

Copilot AI requested a review from ruevs March 12, 2026 18:26
@ruevs
Copy link
Member

ruevs commented Mar 12, 2026

@copilot I pulled the version with SIF_DISABLENOSCROLL and tested it. The scroll bar is now permanent and therefore the workaround (that @phkahler initially suggested) is implemented and works. The problem can not be reproduced any more.

While the workaround works I want to get to the root of the problem.

I read your detailed thoughts on the WM_SIZE message being the reason that the content gets properly resized when changing the window size and is not when showing/hiding the scroll bar. In the past I actually debugged (on Windows 10) and WM_SIZE is not sent when the scroll bar visibility changes. I have not debugged on Windows XP but your theory that it does send WM_SIZE in this case is plausible.

Is there a way to "manually" send WM_SIZE when changing the scroll bar visibility? Or emulate what it does?

Copy link
Author

Copilot AI commented Mar 12, 2026

@copilot I pulled the version with SIF_DISABLENOSCROLL and tested it. The scroll bar is now permanent and therefore the workaround (that @phkahler initially suggested) is implement...

Yes, WM_SIZE can be synthesized manually. The pattern would be:

void SetScrollbarVisible(bool visible) override {
    bool wasVisible = scrollbarVisible;
    scrollbarVisible = visible;
    if(visible != wasVisible) {
        ShowScrollBar(hWindow, SB_VERT, visible ? TRUE : FALSE);
        // Synthesize WM_SIZE so ANGLE detects the client width change
        // and resizes its D3D swap chain before the next render.
        RECT rc;
        GetClientRect(hWindow, &rc);
        SendMessage(hWindow, WM_SIZE, SIZE_RESTORED,
                    MAKELPARAM(rc.right - rc.left, rc.bottom - rc.top));
    }
}

WM_SIZE in our handler just calls Invalidate(), which queues a WM_PAINT. During that paint, eglSwapBuffers is called — at that point ANGLE (D3D11 backend) detects the HWND size has changed and calls IDXGISwapChain::ResizeBuffers. The first post-resize frame may still be discarded/blank, so you'd likely see a brief one-frame stutter. The SIF_DISABLENOSCROLL approach avoids this entirely by making the client width invariant — no resize detection needed.

On Windows 2000/XP the scrollbar visibility change apparently triggered WM_SIZE automatically (perhaps as a side-effect of WM_NCCALCSIZE triggering a size notification differently in those older DWM/user32 versions), so ANGLE always had a chance to resize before the next render. On Windows 10/11 that automatic WM_SIZE was dropped.

The current SIF_DISABLENOSCROLL fix in 53140cf sidesteps the resize issue completely and is verified working. The synthesized WM_SIZE route is viable if a dynamically-hiding scrollbar is preferred, but at the cost of a potential single-frame glitch on each transition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Button click area is shifted left of icon

3 participants