Skip to content

Fix bottom rows being clipped when the panel is resized at high DPI - #254

Open
leoshone wants to merge 3 commits into
NPP-JSONViewer:masterfrom
leoshone:fix/panel-dpi-clipping
Open

Fix bottom rows being clipped when the panel is resized at high DPI#254
leoshone wants to merge 3 commits into
NPP-JSONViewer:masterfrom
leoshone:fix/panel-dpi-clipping

Conversation

@leoshone

@leoshone leoshone commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Fix bottom rows being clipped when the panel is resized at high DPI

The bug

On a monitor whose DPI scale is not 100%, expanding a JSON tree fully and
scrolling to the bottom leaves the last few rows physically unreachable: the
vertical scrollbar reports that it is at the end, but the bottom rows are
simply outside the visible panel. The node path box at the bottom of the panel
disappears completely. The larger the panel (and the higher the DPI scale),
the more rows are lost.

Measured on a 150% DPI display (JSON Viewer docked, panel client 394x968):

what before the fix
tree control bottom, in panel client coordinates 1181 px (213 px below the panel)
unreachable rows at 100% zoom (item height 22 px) ~10 rows
node path edit box 115 px below the panel, fully invisible

Root cause

JsonViewDlg::AdjustDocPanelSize() multiplied the size delta between the
current panel size and the initially recorded one by CUtility::GetDesktopScale():

auto newDeltaHeight = nHeight - m_lfInitialClientHeight;
auto addHeight      = static_cast<int>((newDeltaHeight - m_lfDeltaHeight) * fDeskScale);

nHeight comes from WM_SIZE and m_lfInitialClientHeight from
GetClientRect - both are already in physical pixels. Multiplying the
delta by the DPI scale again makes the child controls grow s times faster
than the panel itself on any scaled display (s > 1). The tree control
computes its scroll range from its own (oversized) client height, so its
scrollbar is convinced it has reached the end while the overflow rows are
clipped by the parent dialog. At 100% scaling (s == 1) the error is exactly
zero, which is why the bug only shows up on scaled monitors.

The fix

Position the resizable controls from the current client size instead of
accumulating scaled deltas:

  • the template layout of the four controls (search box, search button, tree,
    node path box) is captured once when the dialog is created;
  • on every WM_SIZE each control is placed at template rect + unscaled
    delta
    , so its designed margins to the panel edge are preserved at any DPI;
  • the tree height is derived as "everything between the tool bar row and the
    node path box", with the node path box pinned to the bottom of the client
    area - the layout is now self-correcting instead of accumulated.

This also fixes two adjacent effects of the same arithmetic: the needless
horizontal scrollbar caused by the oversized tree width, and the drift
between repeated resizes caused by truncating scaled deltas.

Verification

Measured with live Win32 geometry probes against the real plugin inside
Notepad++, at 150% DPI, with the tree fully expanded (TVM_GETVISIBLECOUNT,
TVM_GETNEXTITEM(TVGN_LASTVISIBLE), control rects mapped into the panel's
client space):

  • before: tree bottom 213 px below the panel client area, last visible row
    reported as the final one while it was clipped;
  • after: tree bottom 36 px above the panel bottom (the space of the node path
    box), all 91 rows of the test document reachable after scrolling to the
    bottom, node path box visible again;
  • re-measured across five window resizes (+150 px, +300 px, -200 px, back):
    no drift, overflow stays at zero;
  • existing E2E suites (zoom persistence, per-tab snapshot, expansion on
    refresh, draw-on-open, jsonc recognition) all pass unchanged.

AdjustDocPanelSize multiplied the pixel delta between the new panel size
and the initial one by the desktop DPI scale. Both values are already in
physical pixels, so on any monitor whose scale is not 100% the tree (and
the node path box) grew s times faster than the panel itself and slid
below the panel's client area. The tree control computed its scroll
range from its own oversized height, so the scrollbar reported the end
while the last (s-1)*growth/itemHeight rows were physically outside the
visible panel: fully expanded long documents showed rows that could
never be scrolled into view.

Measurements at 150% scaling before the fix: tree bottom 213 px below
the panel client area (5+ unreachable rows at 100% zoom), node path box
115 px below it. Positioning is now absolute - template rect plus the
unscaled delta, with the tree ending above a node path box that is
pinned to the bottom of the client area - which also makes repeated
resizes idempotent instead of accumulated.
MSVC's windows.h defines max as a macro unless NOMINMAX is set, so
std::max(...) failed to compile as std::(...). Parenthesize the call -
the standard portable workaround - instead of touching the project's
include settings.
The tree height is now derived from the node path box position instead
of the raw height delta, so the variable is gone.
@SinghRajenM

Copy link
Copy Markdown
Collaborator

Please share the before after comparison.

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.

2 participants