From 05c1128956fe180307a40ce19e3929c1e18b768f Mon Sep 17 00:00:00 2001 From: leoshone Date: Thu, 3 Sep 2026 18:10:20 +0800 Subject: [PATCH 1/2] Persist the tree view zoom level across Notepad++ sessions The plugin already ships a zoom slider for the JSON tree (80%..250%), but the chosen level only lived in the slider control: closing Notepad++ and starting it again always fell back to 100%. Store the zoom percentage in JSONViewer.ini under [Others] TREE_ZOOM and re-apply it when the dialog is initialised. The value is written only when it actually changes, and while the slider thumb is being dragged (TB_THUMBTRACK) nothing is written, so a drag gesture produces a single write at the end instead of one per pixel. Purely additive: the existing ini keys and the default behaviour are untouched. --- src/NppJsonViewer/Define.h | 6 ++++++ src/NppJsonViewer/JsonViewDlg.cpp | 25 +++++++++++++++++++++++++ src/NppJsonViewer/JsonViewDlg.h | 1 + src/NppJsonViewer/NppJsonPlugin.cpp | 3 ++- src/NppJsonViewer/Profile.cpp | 5 +++++ tests/UnitTest/ProfileTest.cpp | 24 ++++++++++++++++++++++++ 6 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/NppJsonViewer/Define.h b/src/NppJsonViewer/Define.h index 5ee80f1..3834ddb 100644 --- a/src/NppJsonViewer/Define.h +++ b/src/NppJsonViewer/Define.h @@ -1,4 +1,6 @@ #pragma once +#include + #include "PluginInterface.h" // Define the number of plugin commands here @@ -64,6 +66,7 @@ const TCHAR STR_INI_FORMATTING_INDENTCOUNT[] = TEXT("INDENTATION_COUNT"); const TCHAR STR_INI_OTHER_SEC[] = TEXT("Others"); const TCHAR STR_INI_OTHER_FOLLOW_TAB[] = TEXT("FOLLOW_TAB"); +const TCHAR STR_INI_OTHER_TREE_ZOOM[] = TEXT("TREE_ZOOM"); const TCHAR STR_INI_OTHER_AUTO_FORMAT[] = TEXT("AUTO_FORMAT"); const TCHAR STR_INI_OTHER_USE_HIGHLIGHT[] = TEXT("USE_JSON_HIGHLIGHT"); const TCHAR STR_INI_OTHER_IGNORE_COMMENT[] = TEXT("IGNORE_COMMENT"); @@ -117,4 +120,7 @@ struct Setting bool bAutoFormat = false; bool bUseJsonHighlight = true; ParseOptions parseOptions {}; + int nTreeZoom = 100; // Tree view font zoom in percent (80..250) + + std::wstring configPath; // Full path of JSONViewer.ini (not persisted) }; diff --git a/src/NppJsonViewer/JsonViewDlg.cpp b/src/NppJsonViewer/JsonViewDlg.cpp index 4080af0..ac9e10c 100644 --- a/src/NppJsonViewer/JsonViewDlg.cpp +++ b/src/NppJsonViewer/JsonViewDlg.cpp @@ -936,6 +936,19 @@ void JsonViewDlg::UpdateUIOnZoom(int zoomPercentage) const SetTreeViewZoom(zoomFactor); } +void JsonViewDlg::PersistZoom(int zoomPercentage) +{ + const auto& zoomRange = m_pTreeViewZoom->GetRange(); + if (zoomPercentage < zoomRange.m_nMinZoom || zoomPercentage > zoomRange.m_nMaxZoom) + return; + + if (m_pSetting->nTreeZoom != zoomPercentage) + { + m_pSetting->nTreeZoom = zoomPercentage; + ProfileSetting(m_pSetting->configPath).SetSettings(*m_pSetting); + } +} + void JsonViewDlg::HandleZoomOnScroll(WPARAM wParam) const { int pos = GetZoomLevel(); // Current zoom level @@ -1101,6 +1114,10 @@ INT_PTR JsonViewDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) m_pTreeView->OnInit(getHSelf(), IDC_TREE); m_pTreeViewZoom->OnInit(getHSelf(), IDC_ZOOM_SLIDER, IDC_ZOOM_PERCENT); + // Apply the zoom level restored from JSONViewer.ini + const auto& zoomRange = m_pTreeViewZoom->GetRange(); + UpdateUIOnZoom(std::clamp(m_pSetting->nTreeZoom, zoomRange.m_nMinZoom, zoomRange.m_nMaxZoom)); + PrepareButtons(); // Set default node path as JSON @@ -1182,6 +1199,7 @@ INT_PTR JsonViewDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) if (GetKeyState(VK_CONTROL) & 0x8000) { HandleZoomOnScroll(wParam); + PersistZoom(GetZoomLevel()); return TRUE; } return FALSE; @@ -1193,9 +1211,16 @@ INT_PTR JsonViewDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) if (reinterpret_cast(lParam) == hSlider) { + // While the thumb is being dragged (TB_THUMBTRACK) the position + // changes continuously, so only persist once the gesture is over. + const bool bDragging = (HIWORD(wParam) == TB_THUMBTRACK); + int pos = m_pTreeViewZoom->GetPosition(); UpdateUIOnZoom(pos); + if (!bDragging) + PersistZoom(pos); + return TRUE; } return FALSE; diff --git a/src/NppJsonViewer/JsonViewDlg.h b/src/NppJsonViewer/JsonViewDlg.h index 00cc599..605db34 100644 --- a/src/NppJsonViewer/JsonViewDlg.h +++ b/src/NppJsonViewer/JsonViewDlg.h @@ -95,6 +95,7 @@ class JsonViewDlg void SetTreeViewZoom(double dwZoomFactor) const; void UpdateUIOnZoom(int zoomPercentage) const; void HandleZoomOnScroll(WPARAM wParam) const; + void PersistZoom(int zoomPercentage); void HandleTreeEvents(LPARAM lParam) const; diff --git a/src/NppJsonViewer/NppJsonPlugin.cpp b/src/NppJsonViewer/NppJsonPlugin.cpp index f251481..e7224ff 100644 --- a/src/NppJsonViewer/NppJsonPlugin.cpp +++ b/src/NppJsonViewer/NppJsonPlugin.cpp @@ -169,7 +169,8 @@ void NppJsonPlugin::ConstructSetting() { if (!m_pSetting) { - m_pSetting = std::make_shared(); + m_pSetting = std::make_shared(); + m_pSetting->configPath = m_configPath; ProfileSetting(m_configPath).GetSettings(*m_pSetting); } } diff --git a/src/NppJsonViewer/Profile.cpp b/src/NppJsonViewer/Profile.cpp index 7f13a18..cf6c9c6 100644 --- a/src/NppJsonViewer/Profile.cpp +++ b/src/NppJsonViewer/Profile.cpp @@ -94,6 +94,10 @@ bool ProfileSetting::GetSettings(Setting& info) const if (bRetVal) info.bFollowCurrentTab = static_cast(nVal); + bRetVal = bRetVal && ReadValue(STR_INI_OTHER_SEC, STR_INI_OTHER_TREE_ZOOM, nVal, info.nTreeZoom); + if (bRetVal) + info.nTreeZoom = nVal; + bRetVal = bRetVal && ReadValue(STR_INI_OTHER_SEC, STR_INI_OTHER_AUTO_FORMAT, nVal, info.bAutoFormat); if (bRetVal) info.bAutoFormat = static_cast(nVal); @@ -127,6 +131,7 @@ bool ProfileSetting::SetSettings(const Setting& info) const bRetVal = bRetVal && WriteValue(STR_INI_FORMATTING_SEC, STR_INI_FORMATTING_INDENTCOUNT, info.indent.len); bRetVal = bRetVal && WriteValue(STR_INI_OTHER_SEC, STR_INI_OTHER_FOLLOW_TAB, info.bFollowCurrentTab); + bRetVal = bRetVal && WriteValue(STR_INI_OTHER_SEC, STR_INI_OTHER_TREE_ZOOM, info.nTreeZoom); bRetVal = bRetVal && WriteValue(STR_INI_OTHER_SEC, STR_INI_OTHER_AUTO_FORMAT, info.bAutoFormat); bRetVal = bRetVal && WriteValue(STR_INI_OTHER_SEC, STR_INI_OTHER_USE_HIGHLIGHT, info.bUseJsonHighlight); bRetVal = bRetVal && WriteValue(STR_INI_OTHER_SEC, STR_INI_OTHER_IGNORE_COMMENT, info.parseOptions.bIgnoreComment); diff --git a/tests/UnitTest/ProfileTest.cpp b/tests/UnitTest/ProfileTest.cpp index 10ab846..873d9f6 100644 --- a/tests/UnitTest/ProfileTest.cpp +++ b/tests/UnitTest/ProfileTest.cpp @@ -137,6 +137,7 @@ namespace ProfileSettingTests EXPECT_EQ(setting.bFollowCurrentTab, false); EXPECT_EQ(setting.bAutoFormat, false); EXPECT_EQ(setting.bUseJsonHighlight, true); + EXPECT_EQ(setting.nTreeZoom, 100); EXPECT_EQ(setting.parseOptions.bIgnoreComment, true); EXPECT_EQ(setting.parseOptions.bIgnoreTrailingComma, true); @@ -173,9 +174,32 @@ namespace ProfileSettingTests EXPECT_EQ(actual.bFollowCurrentTab, expected.bFollowCurrentTab); EXPECT_EQ(actual.bAutoFormat, expected.bAutoFormat); EXPECT_EQ(actual.bUseJsonHighlight, expected.bUseJsonHighlight); + EXPECT_EQ(actual.nTreeZoom, expected.nTreeZoom); EXPECT_EQ(actual.parseOptions.bIgnoreComment, expected.parseOptions.bIgnoreComment); EXPECT_EQ(actual.parseOptions.bIgnoreTrailingComma, expected.parseOptions.bIgnoreTrailingComma); EXPECT_EQ(actual.parseOptions.bReplaceUndefined, expected.parseOptions.bReplaceUndefined); } + + TEST_F(ProfileTest, TreeZoom_RoundTrip) + { + // a profile without the TREE_ZOOM key falls back to 100% + { + Setting setting {}; + EXPECT_TRUE(m_pProfile->GetSettings(setting)); + EXPECT_EQ(setting.nTreeZoom, 100); + } + + // every value inside the slider range must survive a write/read cycle + for (int zoom : { 80, 100, 150, 200, 250 }) + { + Setting expected {}; + expected.nTreeZoom = zoom; + ASSERT_TRUE(m_pProfile->SetSettings(expected)) << zoom; + + Setting actual {}; + ASSERT_TRUE(m_pProfile->GetSettings(actual)) << zoom; + EXPECT_EQ(actual.nTreeZoom, zoom); + } + } } // namespace ProfileSettingTests From 7771ca00d7775ab49ca87c26735a6d00d9b89ce0 Mon Sep 17 00:00:00 2001 From: leoshone Date: Fri, 4 Sep 2026 10:23:02 +0800 Subject: [PATCH 2/2] Fix the drag detection of the zoom slider WM_HSCROLL carries the notification code in LOWORD(wParam), not HIWORD: HIWORD holds the thumb position itself (80..250 here), so comparing it against TB_THUMBTRACK never matched and the zoom was written to the ini file continuously while the thumb was being dragged, instead of once when the gesture ended. Found by an independent review of the integration branch; the end-to-end harness never caught it because it only sends TB_ENDTRACK and never simulates the dragging itself. --- src/NppJsonViewer/JsonViewDlg.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/NppJsonViewer/JsonViewDlg.cpp b/src/NppJsonViewer/JsonViewDlg.cpp index ac9e10c..331d2d4 100644 --- a/src/NppJsonViewer/JsonViewDlg.cpp +++ b/src/NppJsonViewer/JsonViewDlg.cpp @@ -1213,7 +1213,9 @@ INT_PTR JsonViewDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) { // While the thumb is being dragged (TB_THUMBTRACK) the position // changes continuously, so only persist once the gesture is over. - const bool bDragging = (HIWORD(wParam) == TB_THUMBTRACK); + // WM_HSCROLL carries the notification code in LOWORD(wParam); + // HIWORD is the thumb position itself. + const bool bDragging = (LOWORD(wParam) == TB_THUMBTRACK); int pos = m_pTreeViewZoom->GetPosition(); UpdateUIOnZoom(pos);