From 9e64d7080753d49574282b40175b9ec3fab61f58 Mon Sep 17 00:00:00 2001 From: Takashi Sawanaka Date: Fri, 4 Sep 2026 08:31:20 +0900 Subject: [PATCH 1/6] Change the WinIMergeLib splitter position API to use splitter ratios instead of absolute positions. --- src/WinIMergeLib/ImgMergeWindow.hpp | 260 +++++++++++++++++----------- src/WinIMergeLib/WinIMergeLib.h | 5 +- 2 files changed, 163 insertions(+), 102 deletions(-) diff --git a/src/WinIMergeLib/ImgMergeWindow.hpp b/src/WinIMergeLib/ImgMergeWindow.hpp index 9754ba0..66fd005 100644 --- a/src/WinIMergeLib/ImgMergeWindow.hpp +++ b/src/WinIMergeLib/ImgMergeWindow.hpp @@ -64,7 +64,6 @@ class CImgMergeWindow : public IImgMergeWindow , m_hInstance(NULL) , m_nDraggingSplitter(-1) , m_bHorizontalSplit(false) - , m_splitterPosition(0) , m_oldSplitPosX(-4) , m_oldSplitPosY(-4) , m_bDragging(false) @@ -79,6 +78,9 @@ class CImgMergeWindow : public IImgMergeWindow for (int i = 0; i < 3; ++i) m_ChildWndProc[i] = NULL; + m_splitterRatios[0] = -1.0; + m_splitterRatios[1] = -1.0; + Gdiplus::GdiplusStartupInput gdiplusStartupInput; Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, nullptr); } @@ -215,9 +217,6 @@ class CImgMergeWindow : public IImgMergeWindow { if (!m_hWnd) return; - // A stored width cannot be reused as a height. - if (m_bHorizontalSplit != horizontalSplit) - m_splitterPosition = 0; m_bHorizontalSplit = horizontalSplit; std::vector rects = CalcChildImgWindowRect(m_hWnd, m_nImages, m_bHorizontalSplit); for (int i = 0; i < m_nImages; ++i) @@ -228,22 +227,32 @@ class CImgMergeWindow : public IImgMergeWindow } } - int GetSplitterPosition() const override + double GetSplitterRatio(int pane) const override { - return m_splitterPosition; + if (pane < 0 || pane >= std::size(m_splitterRatios)) + return -1.0; + return m_splitterRatios[pane]; } - void SetSplitterPosition(int position) override + void SetSplitterRatios(const double* ratios, int count) override { - // Stored before the window check: unlike SetHorizontalSplit(), a value - // set before the panes exist must survive until they are created. - m_splitterPosition = position; - if (m_hWnd && m_nImages == 2) + if (count > std::size(m_splitterRatios) || !m_hWnd || !ratios || count < 1) + return; + for (int i = 0; i < count; ++i) { - std::vector rects = CalcChildImgWindowRect(m_hWnd, m_nImages, m_bHorizontalSplit); - for (int i = 0; i < m_nImages; ++i) - m_imgWindow[i].SetWindowRect(rects[i]); + if (ratios[i] >= 0.0 && ratios[i] <= 1.0) + m_splitterRatios[i] = ratios[i]; } + ApplySplitterRatio(); + } + + void ResetSplitterRatios() override + { + if (!m_hWnd || m_nImages < 2) + return; + for (int i = 0; i < m_nImages - 1; ++i) + m_splitterRatios[i] = 1.0 / m_nImages; + ApplySplitterRatio(); } COLORREF GetDiffColor() const override @@ -880,6 +889,11 @@ class CImgMergeWindow : public IImgMergeWindow { CloseImages(); m_nImages = nImages; + if (m_splitterRatios[0] < 0.0) + { + for (int i = 0; i < nImages - 1; ++i) + m_splitterRatios[i] = 1.0 / nImages; + } bool bSucceeded = m_buffer.NewImages(nImages, nPages, width, height); if (m_hWnd) { @@ -913,6 +927,11 @@ class CImgMergeWindow : public IImgMergeWindow { CloseImages(); m_nImages = nImages; + if (m_splitterRatios[0] < 0.0) + { + for (int i = 0; i < nImages - 1; ++i) + m_splitterRatios[i] = 1.0 / nImages; + } bool bSucceeded = m_buffer.OpenImages(nImages, filename); if (m_hWnd) { @@ -1283,27 +1302,6 @@ class CImgMergeWindow : public IImgMergeWindow return RegisterClassExW(&wcex); } - // First pane size to lay out, or 0 for the equal split. A positive - // m_splitterPosition sizes the first pane from the near edge, a negative one - // sizes the second pane from the far edge; whichever pane is smaller is the - // anchored one, so a narrow pane stays narrow and the wide pane absorbs the - // window change. Bounds match the ones MoveSplitter() enforces, so a - // restored layout matches the dragged one. The clamp is returned, never - // stored back: the window is laid out once before it has its final size, and - // clamping in place would let that transient rect shrink the value for good. - int CalcSplitterPosition(int nImages, LONG splitSize) const - { - const int minSize = 32; - const int total = static_cast(splitSize); - const int maxPos = total - minSize; - if (nImages != 2 || m_splitterPosition == 0 || maxPos < minSize) - return 0; - const int pos = (m_splitterPosition > 0) - ? m_splitterPosition - : total - 2 * 2 + m_splitterPosition; - return (std::min)((std::max)(pos, minSize), maxPos); - } - std::vector CalcChildImgWindowRect(HWND hWnd, int nImages, bool bHorizontalSplit) { std::vector childrects; @@ -1314,67 +1312,111 @@ class CImgMergeWindow : public IImgMergeWindow { if (!bHorizontalSplit) { - const int splitPos = CalcSplitterPosition(nImages, rcParent.right); - if (splitPos > 0) + int cx = GetSystemMetrics(SM_CXVSCROLL); + int width = (rcParent.left + rcParent.right - cx) / nImages - 2; + rc.left = 0; + rc.right = rc.left + width; + for (int i = 0; i < nImages - 1; ++i) { - rc.left = 0; - rc.right = splitPos; childrects.push_back(rc); rc.left = rc.right + 2 * 2; - rc.right = rcParent.right; - childrects.push_back(rc); - } - else - { - int cx = GetSystemMetrics(SM_CXVSCROLL); - int width = (rcParent.left + rcParent.right - cx) / nImages - 2; - rc.left = 0; rc.right = rc.left + width; - for (int i = 0; i < nImages - 1; ++i) - { - childrects.push_back(rc); - rc.left = rc.right + 2 * 2; - rc.right = rc.left + width; - } - rc.right = rcParent.right; - rc.left = rc.right - width - cx; - childrects.push_back(rc); } + rc.right = rcParent.right; + rc.left = rc.right - width - cx; + childrects.push_back(rc); } else { - const int splitPos = CalcSplitterPosition(nImages, rcParent.bottom); - if (splitPos > 0) + int cy = GetSystemMetrics(SM_CXVSCROLL); + int height = (rcParent.top + rcParent.bottom - cy) / nImages - 2; + rc.top = 0; + rc.bottom = rc.top + height; + for (int i = 0; i < nImages - 1; ++i) { - rc.top = 0; - rc.bottom = splitPos; childrects.push_back(rc); rc.top = rc.bottom + 2 * 2; - rc.bottom = rcParent.bottom; - childrects.push_back(rc); + rc.bottom = rc.top + height; + } + rc.bottom = rcParent.bottom; + rc.top = rc.bottom - height - cy; + childrects.push_back(rc); + } + } + return childrects; + } + + void ApplySplitterRatio() + { + if (!m_hWnd || m_nImages < 2) + return; + + RECT rcParent; + GetClientRect(m_hWnd, &rcParent); + + std::vector rects; + rects.resize(m_nImages); + + if (!m_bHorizontalSplit) + { + // Vertical split: distribute width based on ratios + int cx = GetSystemMetrics(SM_CXVSCROLL); + int totalWidth = rcParent.right - rcParent.left - cx; + int accumulatedWidth = 0; + + for (int i = 0; i < m_nImages; ++i) + { + rects[i].top = rcParent.top; + rects[i].bottom = rcParent.bottom; + + if (i < m_nImages - 1) + { + int paneWidth = static_cast(totalWidth * m_splitterRatios[i]); + rects[i].left = accumulatedWidth; + rects[i].right = accumulatedWidth + paneWidth; + accumulatedWidth = rects[i].right + 4; // 4 pixels for splitter + } + else + { + // Last pane takes remaining width + rects[i].left = accumulatedWidth; + rects[i].right = rcParent.right; + } + } } else { + // Horizontal split: distribute height based on ratios int cy = GetSystemMetrics(SM_CXVSCROLL); - int height = (rcParent.top + rcParent.bottom - cy) / nImages - 2; - rc.top = 0; - rc.bottom = rc.top + height; - for (int i = 0; i < nImages - 1; ++i) + int totalHeight = rcParent.bottom - rcParent.top - cy; + int accumulatedHeight = 0; + + for (int i = 0; i < m_nImages; ++i) { - childrects.push_back(rc); - rc.top = rc.bottom + 2 * 2; - rc.bottom = rc.top + height; + rects[i].left = rcParent.left; + rects[i].right = rcParent.right; + + if (i < m_nImages - 1) + { + int paneHeight = static_cast(totalHeight * m_splitterRatios[i]); + rects[i].top = accumulatedHeight; + rects[i].bottom = accumulatedHeight + paneHeight; + accumulatedHeight = rects[i].bottom + 4; // 4 pixels for splitter + } + else + { + // Last pane takes remaining height + rects[i].top = accumulatedHeight; + rects[i].bottom = rcParent.bottom; + } } - rc.bottom = rcParent.bottom; - rc.top = rc.bottom - height - cy; - childrects.push_back(rc); } + + for (int i = 0; i < m_nImages; ++i) + m_imgWindow[i].SetWindowRect(rects[i]); } - } - return childrects; - } - void MoveSplitter(int x, int y) + void MoveSplitter(int x, int y) { RECT rcParent; GetClientRect(m_hWnd, &rcParent); @@ -1419,28 +1461,50 @@ class CImgMergeWindow : public IImgMergeWindow rc[i].bottom = rc[i].top + height; } rc[m_nImages - 1].bottom = rcParent.bottom; - } + } + + for (int i = 0; i < m_nImages; ++i) + m_imgWindow[i].SetWindowRect(rc[i]); - // Recorded after the clamping above, so the stored value is always a - // position the layout can reproduce. OnLButtonUp() clears - // m_nDraggingSplitter only once this returns. The smaller pane is the one - // pinned to its edge, negated when that is the second pane. - if (m_nImages == 2 && m_nDraggingSplitter == 0) + // Update ratios based on new positions + UpdateSplitterRatios(); + } + + void UpdateSplitterRatios() { - const int first = m_bHorizontalSplit - ? rc[0].bottom - rc[0].top - : rc[0].right - rc[0].left; - const int second = m_bHorizontalSplit - ? rc[1].bottom - rc[1].top - : rc[1].right - rc[1].left; - m_splitterPosition = (second < first) ? -second : first; - } + if (m_nImages < 2) + return; - for (int i = 0; i < m_nImages; ++i) - m_imgWindow[i].SetWindowRect(rc[i]); - } + RECT rcParent; + GetClientRect(m_hWnd, &rcParent); + + if (!m_bHorizontalSplit) + { + // Vertical split: calculate width ratios + int cx = GetSystemMetrics(SM_CXVSCROLL); + int totalWidth = rcParent.right - rcParent.left - cx; + + for (int i = 0; i < m_nImages - 1; ++i) + { + int paneWidth = m_imgWindow[i].GetWindowRect().right - m_imgWindow[i].GetWindowRect().left; + m_splitterRatios[i] = static_cast(paneWidth) / totalWidth; + } + } + else + { + // Horizontal split: calculate height ratios + int cy = GetSystemMetrics(SM_CXVSCROLL); + int totalHeight = rcParent.bottom - rcParent.top - cy; - void DrawXorBar(HDC hdc, int x1, int y1, int width, int height) + for (int i = 0; i < m_nImages - 1; ++i) + { + int paneHeight = m_imgWindow[i].GetWindowRect().bottom - m_imgWindow[i].GetWindowRect().top; + m_splitterRatios[i] = static_cast(paneHeight) / totalHeight; + } + } + } + + void DrawXorBar(HDC hdc, int x1, int y1, int width, int height) { static const WORD _dotPatternBmp[8] = { @@ -1489,9 +1553,7 @@ class CImgMergeWindow : public IImgMergeWindow void OnSize(UINT nType, int cx, int cy) { - std::vector rects = CalcChildImgWindowRect(m_hWnd, m_nImages, m_bHorizontalSplit); - for (int i = 0; i < m_nImages; ++i) - m_imgWindow[i].SetWindowRect(rects[i]); + ApplySplitterRatio(); } void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) @@ -2126,9 +2188,6 @@ class CImgMergeWindow : public IImgMergeWindow std::vector m_listener; int m_nDraggingSplitter; bool m_bHorizontalSplit; - // Size along the split axis of whichever pane is smaller, in pixels: - // positive for the first pane, negative for the second. 0 = equal split. - int m_splitterPosition; int m_oldSplitPosX; int m_oldSplitPosY; bool m_bDragging; @@ -2141,5 +2200,6 @@ class CImgMergeWindow : public IImgMergeWindow std::unique_ptr m_pOcr; std::chrono::milliseconds m_timerPrev; std::chrono::milliseconds m_timerNext; + double m_splitterRatios[2]; inline static bool s_bDarkBackgroundEnabled = false; }; diff --git a/src/WinIMergeLib/WinIMergeLib.h b/src/WinIMergeLib/WinIMergeLib.h index 8c682c0..d5c1a0c 100644 --- a/src/WinIMergeLib/WinIMergeLib.h +++ b/src/WinIMergeLib/WinIMergeLib.h @@ -192,8 +192,9 @@ struct IImgMergeWindow virtual int GetLastErrorCode() const = 0; virtual bool GetPreferWICDecoder() const = 0; virtual void SetPreferWICDecoder(bool preferWICDecoder) = 0; - virtual int GetSplitterPosition() const = 0; - virtual void SetSplitterPosition(int position) = 0; + virtual double GetSplitterRatio(int pane) const = 0; + virtual void SetSplitterRatios(const double* ratios, int count) = 0; + virtual void ResetSplitterRatios() = 0; }; struct IImgToolWindow From 5b8a6f4bc3117529b4d3bc561fb3b6444f33e715 Mon Sep 17 00:00:00 2001 From: Takashi Sawanaka Date: Fri, 4 Sep 2026 18:41:29 +0900 Subject: [PATCH 2/6] Change the WinIMergeLib splitter position API to use splitter ratios instead of absolute positions. --- src/WinIMergeLib/ImgMergeWindow.hpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/WinIMergeLib/ImgMergeWindow.hpp b/src/WinIMergeLib/ImgMergeWindow.hpp index 66fd005..3d13950 100644 --- a/src/WinIMergeLib/ImgMergeWindow.hpp +++ b/src/WinIMergeLib/ImgMergeWindow.hpp @@ -236,7 +236,7 @@ class CImgMergeWindow : public IImgMergeWindow void SetSplitterRatios(const double* ratios, int count) override { - if (count > std::size(m_splitterRatios) || !m_hWnd || !ratios || count < 1) + if (count > std::size(m_splitterRatios) || !ratios || count < 1) return; for (int i = 0; i < count; ++i) { @@ -248,7 +248,7 @@ class CImgMergeWindow : public IImgMergeWindow void ResetSplitterRatios() override { - if (!m_hWnd || m_nImages < 2) + if (m_nImages < 2) return; for (int i = 0; i < m_nImages - 1; ++i) m_splitterRatios[i] = 1.0 / m_nImages; @@ -889,9 +889,9 @@ class CImgMergeWindow : public IImgMergeWindow { CloseImages(); m_nImages = nImages; - if (m_splitterRatios[0] < 0.0) + for (int i = 0; i < nImages - 1; ++i) { - for (int i = 0; i < nImages - 1; ++i) + if (m_splitterRatios[0] < 0.0) m_splitterRatios[i] = 1.0 / nImages; } bool bSucceeded = m_buffer.NewImages(nImages, nPages, width, height); @@ -927,9 +927,9 @@ class CImgMergeWindow : public IImgMergeWindow { CloseImages(); m_nImages = nImages; - if (m_splitterRatios[0] < 0.0) + for (int i = 0; i < nImages - 1; ++i) { - for (int i = 0; i < nImages - 1; ++i) + if (m_splitterRatios[0] < 0.0) m_splitterRatios[i] = 1.0 / nImages; } bool bSucceeded = m_buffer.OpenImages(nImages, filename); @@ -1486,7 +1486,8 @@ class CImgMergeWindow : public IImgMergeWindow for (int i = 0; i < m_nImages - 1; ++i) { - int paneWidth = m_imgWindow[i].GetWindowRect().right - m_imgWindow[i].GetWindowRect().left; + RECT paneRc = m_imgWindow[i].GetWindowRect(); + int paneWidth = paneRc.right - paneRc.left; m_splitterRatios[i] = static_cast(paneWidth) / totalWidth; } } @@ -1498,7 +1499,8 @@ class CImgMergeWindow : public IImgMergeWindow for (int i = 0; i < m_nImages - 1; ++i) { - int paneHeight = m_imgWindow[i].GetWindowRect().bottom - m_imgWindow[i].GetWindowRect().top; + RECT paneRc = m_imgWindow[i].GetWindowRect(); + int paneHeight = paneRc.bottom - paneRc.top; m_splitterRatios[i] = static_cast(paneHeight) / totalHeight; } } From 627a5e9cec9e0a3b817b1f6e6a8585b33438d1ce Mon Sep 17 00:00:00 2001 From: Takashi Sawanaka Date: Fri, 4 Sep 2026 18:55:30 +0900 Subject: [PATCH 3/6] Change the WinIMergeLib splitter position API to use splitter ratios instead of absolute positions. --- src/WinIMergeLib/ImgMergeWindow.hpp | 196 ++++++++++++++-------------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/src/WinIMergeLib/ImgMergeWindow.hpp b/src/WinIMergeLib/ImgMergeWindow.hpp index 3d13950..44ed09d 100644 --- a/src/WinIMergeLib/ImgMergeWindow.hpp +++ b/src/WinIMergeLib/ImgMergeWindow.hpp @@ -1341,82 +1341,82 @@ class CImgMergeWindow : public IImgMergeWindow rc.bottom = rcParent.bottom; rc.top = rc.bottom - height - cy; childrects.push_back(rc); - } - } - return childrects; } + } + return childrects; + } - void ApplySplitterRatio() - { - if (!m_hWnd || m_nImages < 2) - return; + void ApplySplitterRatio() + { + if (!m_hWnd || m_nImages < 2) + return; + + RECT rcParent; + GetClientRect(m_hWnd, &rcParent); - RECT rcParent; - GetClientRect(m_hWnd, &rcParent); + std::vector rects; + rects.resize(m_nImages); - std::vector rects; - rects.resize(m_nImages); + if (!m_bHorizontalSplit) + { + // Vertical split: distribute width based on ratios + int cx = GetSystemMetrics(SM_CXVSCROLL); + int totalWidth = rcParent.right - rcParent.left - cx; + int accumulatedWidth = 0; - if (!m_bHorizontalSplit) - { - // Vertical split: distribute width based on ratios - int cx = GetSystemMetrics(SM_CXVSCROLL); - int totalWidth = rcParent.right - rcParent.left - cx; - int accumulatedWidth = 0; + for (int i = 0; i < m_nImages; ++i) + { + rects[i].top = rcParent.top; + rects[i].bottom = rcParent.bottom; - for (int i = 0; i < m_nImages; ++i) - { - rects[i].top = rcParent.top; - rects[i].bottom = rcParent.bottom; - - if (i < m_nImages - 1) - { - int paneWidth = static_cast(totalWidth * m_splitterRatios[i]); - rects[i].left = accumulatedWidth; - rects[i].right = accumulatedWidth + paneWidth; - accumulatedWidth = rects[i].right + 4; // 4 pixels for splitter - } - else - { - // Last pane takes remaining width - rects[i].left = accumulatedWidth; - rects[i].right = rcParent.right; - } - } + if (i < m_nImages - 1) + { + int paneWidth = static_cast(totalWidth * m_splitterRatios[i]); + rects[i].left = accumulatedWidth; + rects[i].right = accumulatedWidth + paneWidth; + accumulatedWidth = rects[i].right + 4; // 4 pixels for splitter } else { - // Horizontal split: distribute height based on ratios - int cy = GetSystemMetrics(SM_CXVSCROLL); - int totalHeight = rcParent.bottom - rcParent.top - cy; - int accumulatedHeight = 0; - - for (int i = 0; i < m_nImages; ++i) - { - rects[i].left = rcParent.left; - rects[i].right = rcParent.right; - - if (i < m_nImages - 1) - { - int paneHeight = static_cast(totalHeight * m_splitterRatios[i]); - rects[i].top = accumulatedHeight; - rects[i].bottom = accumulatedHeight + paneHeight; - accumulatedHeight = rects[i].bottom + 4; // 4 pixels for splitter - } - else - { - // Last pane takes remaining height - rects[i].top = accumulatedHeight; - rects[i].bottom = rcParent.bottom; - } - } + // Last pane takes remaining width + rects[i].left = accumulatedWidth; + rects[i].right = rcParent.right; } + } + } + else + { + // Horizontal split: distribute height based on ratios + int cy = GetSystemMetrics(SM_CXVSCROLL); + int totalHeight = rcParent.bottom - rcParent.top - cy; + int accumulatedHeight = 0; - for (int i = 0; i < m_nImages; ++i) - m_imgWindow[i].SetWindowRect(rects[i]); + for (int i = 0; i < m_nImages; ++i) + { + rects[i].left = rcParent.left; + rects[i].right = rcParent.right; + + if (i < m_nImages - 1) + { + int paneHeight = static_cast(totalHeight * m_splitterRatios[i]); + rects[i].top = accumulatedHeight; + rects[i].bottom = accumulatedHeight + paneHeight; + accumulatedHeight = rects[i].bottom + 4; // 4 pixels for splitter + } + else + { + // Last pane takes remaining height + rects[i].top = accumulatedHeight; + rects[i].bottom = rcParent.bottom; + } } + } + + for (int i = 0; i < m_nImages; ++i) + m_imgWindow[i].SetWindowRect(rects[i]); + } - void MoveSplitter(int x, int y) + void MoveSplitter(int x, int y) { RECT rcParent; GetClientRect(m_hWnd, &rcParent); @@ -1461,52 +1461,52 @@ class CImgMergeWindow : public IImgMergeWindow rc[i].bottom = rc[i].top + height; } rc[m_nImages - 1].bottom = rcParent.bottom; - } + } - for (int i = 0; i < m_nImages; ++i) - m_imgWindow[i].SetWindowRect(rc[i]); + for (int i = 0; i < m_nImages; ++i) + m_imgWindow[i].SetWindowRect(rc[i]); - // Update ratios based on new positions - UpdateSplitterRatios(); - } + // Update ratios based on new positions + UpdateSplitterRatios(); + } - void UpdateSplitterRatios() - { - if (m_nImages < 2) - return; + void UpdateSplitterRatios() + { + if (m_nImages < 2) + return; - RECT rcParent; - GetClientRect(m_hWnd, &rcParent); + RECT rcParent; + GetClientRect(m_hWnd, &rcParent); - if (!m_bHorizontalSplit) - { - // Vertical split: calculate width ratios - int cx = GetSystemMetrics(SM_CXVSCROLL); - int totalWidth = rcParent.right - rcParent.left - cx; + if (!m_bHorizontalSplit) + { + // Vertical split: calculate width ratios + int cx = GetSystemMetrics(SM_CXVSCROLL); + int totalWidth = rcParent.right - rcParent.left - cx; - for (int i = 0; i < m_nImages - 1; ++i) - { - RECT paneRc = m_imgWindow[i].GetWindowRect(); - int paneWidth = paneRc.right - paneRc.left; - m_splitterRatios[i] = static_cast(paneWidth) / totalWidth; - } - } - else + for (int i = 0; i < m_nImages - 1; ++i) { - // Horizontal split: calculate height ratios - int cy = GetSystemMetrics(SM_CXVSCROLL); - int totalHeight = rcParent.bottom - rcParent.top - cy; + RECT paneRc = m_imgWindow[i].GetWindowRect(); + int paneWidth = paneRc.right - paneRc.left; + m_splitterRatios[i] = static_cast(paneWidth) / totalWidth; + } + } + else + { + // Horizontal split: calculate height ratios + int cy = GetSystemMetrics(SM_CXVSCROLL); + int totalHeight = rcParent.bottom - rcParent.top - cy; - for (int i = 0; i < m_nImages - 1; ++i) - { - RECT paneRc = m_imgWindow[i].GetWindowRect(); - int paneHeight = paneRc.bottom - paneRc.top; - m_splitterRatios[i] = static_cast(paneHeight) / totalHeight; - } + for (int i = 0; i < m_nImages - 1; ++i) + { + RECT paneRc = m_imgWindow[i].GetWindowRect(); + int paneHeight = paneRc.bottom - paneRc.top; + m_splitterRatios[i] = static_cast(paneHeight) / totalHeight; } } + } - void DrawXorBar(HDC hdc, int x1, int y1, int width, int height) + void DrawXorBar(HDC hdc, int x1, int y1, int width, int height) { static const WORD _dotPatternBmp[8] = { From 97b096176f3342536edc9b219f11d804c03af240 Mon Sep 17 00:00:00 2001 From: Takashi Sawanaka Date: Fri, 4 Sep 2026 19:11:19 +0900 Subject: [PATCH 4/6] Change the WinIMergeLib splitter position API to use splitter ratios instead of absolute positions. --- src/WinIMergeLib/ImgMergeWindow.hpp | 186 ++++++++++++++++------------ 1 file changed, 104 insertions(+), 82 deletions(-) diff --git a/src/WinIMergeLib/ImgMergeWindow.hpp b/src/WinIMergeLib/ImgMergeWindow.hpp index 44ed09d..eb38e1b 100644 --- a/src/WinIMergeLib/ImgMergeWindow.hpp +++ b/src/WinIMergeLib/ImgMergeWindow.hpp @@ -891,7 +891,7 @@ class CImgMergeWindow : public IImgMergeWindow m_nImages = nImages; for (int i = 0; i < nImages - 1; ++i) { - if (m_splitterRatios[0] < 0.0) + if (m_splitterRatios[i] < 0.0) m_splitterRatios[i] = 1.0 / nImages; } bool bSucceeded = m_buffer.NewImages(nImages, nPages, width, height); @@ -906,7 +906,7 @@ class CImgMergeWindow : public IImgMergeWindow m_buffer.CompareImages(); if (m_hWnd) { - std::vector rects = CalcChildImgWindowRect(m_hWnd, nImages, m_bHorizontalSplit); + std::vector rects = CalcChildImgWindowRectInternal(m_hWnd, nImages, m_bHorizontalSplit, m_splitterRatios); for (int i = 0; i < nImages; ++i) { if (i < nImages - 1) @@ -929,7 +929,7 @@ class CImgMergeWindow : public IImgMergeWindow m_nImages = nImages; for (int i = 0; i < nImages - 1; ++i) { - if (m_splitterRatios[0] < 0.0) + if (m_splitterRatios[i] < 0.0) m_splitterRatios[i] = 1.0 / nImages; } bool bSucceeded = m_buffer.OpenImages(nImages, filename); @@ -944,7 +944,7 @@ class CImgMergeWindow : public IImgMergeWindow m_buffer.CompareImages(); if (m_hWnd) { - std::vector rects = CalcChildImgWindowRect(m_hWnd, nImages, m_bHorizontalSplit); + std::vector rects = CalcChildImgWindowRectInternal(m_hWnd, nImages, m_bHorizontalSplit, m_splitterRatios); for (int i = 0; i < nImages; ++i) { if (i < nImages - 1) @@ -1302,116 +1302,138 @@ class CImgMergeWindow : public IImgMergeWindow return RegisterClassExW(&wcex); } - std::vector CalcChildImgWindowRect(HWND hWnd, int nImages, bool bHorizontalSplit) + std::vector CalcChildImgWindowRectInternal(HWND hWnd, int nImages, bool bHorizontalSplit, const double* ratios = nullptr) { - std::vector childrects; + std::vector rects; RECT rcParent; GetClientRect(hWnd, &rcParent); - RECT rc = rcParent; - if (nImages > 0) - { - if (!bHorizontalSplit) - { - int cx = GetSystemMetrics(SM_CXVSCROLL); - int width = (rcParent.left + rcParent.right - cx) / nImages - 2; - rc.left = 0; - rc.right = rc.left + width; - for (int i = 0; i < nImages - 1; ++i) - { - childrects.push_back(rc); - rc.left = rc.right + 2 * 2; - rc.right = rc.left + width; - } - rc.right = rcParent.right; - rc.left = rc.right - width - cx; - childrects.push_back(rc); - } - else - { - int cy = GetSystemMetrics(SM_CXVSCROLL); - int height = (rcParent.top + rcParent.bottom - cy) / nImages - 2; - rc.top = 0; - rc.bottom = rc.top + height; - for (int i = 0; i < nImages - 1; ++i) - { - childrects.push_back(rc); - rc.top = rc.bottom + 2 * 2; - rc.bottom = rc.top + height; - } - rc.bottom = rcParent.bottom; - rc.top = rc.bottom - height - cy; - childrects.push_back(rc); - } - } - return childrects; - } - void ApplySplitterRatio() - { - if (!m_hWnd || m_nImages < 2) - return; + if (nImages < 1) + return rects; - RECT rcParent; - GetClientRect(m_hWnd, &rcParent); + rects.resize(nImages); - std::vector rects; - rects.resize(m_nImages); - - if (!m_bHorizontalSplit) + if (!bHorizontalSplit) { - // Vertical split: distribute width based on ratios + // Vertical split int cx = GetSystemMetrics(SM_CXVSCROLL); int totalWidth = rcParent.right - rcParent.left - cx; int accumulatedWidth = 0; - for (int i = 0; i < m_nImages; ++i) + if (ratios == nullptr) { - rects[i].top = rcParent.top; - rects[i].bottom = rcParent.bottom; - - if (i < m_nImages - 1) + // Equal division + int width = totalWidth / nImages - 2; + for (int i = 0; i < nImages; ++i) { - int paneWidth = static_cast(totalWidth * m_splitterRatios[i]); - rects[i].left = accumulatedWidth; - rects[i].right = accumulatedWidth + paneWidth; - accumulatedWidth = rects[i].right + 4; // 4 pixels for splitter + rects[i].top = rcParent.top; + rects[i].bottom = rcParent.bottom; + + if (i < nImages - 1) + { + rects[i].left = accumulatedWidth; + rects[i].right = accumulatedWidth + width; + accumulatedWidth = rects[i].right + 4; // 4 pixels for splitter + } + else + { + rects[i].left = accumulatedWidth; + rects[i].right = rcParent.right; + } } - else + } + else + { + // Ratio-based division + for (int i = 0; i < nImages; ++i) { - // Last pane takes remaining width - rects[i].left = accumulatedWidth; - rects[i].right = rcParent.right; + rects[i].top = rcParent.top; + rects[i].bottom = rcParent.bottom; + + if (i < nImages - 1) + { + int paneWidth = static_cast(totalWidth * ratios[i]); + rects[i].left = accumulatedWidth; + rects[i].right = accumulatedWidth + paneWidth; + accumulatedWidth = rects[i].right + 4; // 4 pixels for splitter + } + else + { + // Last pane takes remaining width + rects[i].left = accumulatedWidth; + rects[i].right = rcParent.right; + } } } } else { - // Horizontal split: distribute height based on ratios + // Horizontal split int cy = GetSystemMetrics(SM_CXVSCROLL); int totalHeight = rcParent.bottom - rcParent.top - cy; int accumulatedHeight = 0; - for (int i = 0; i < m_nImages; ++i) + if (ratios == nullptr) { - rects[i].left = rcParent.left; - rects[i].right = rcParent.right; - - if (i < m_nImages - 1) + // Equal division + int height = totalHeight / nImages - 2; + for (int i = 0; i < nImages; ++i) { - int paneHeight = static_cast(totalHeight * m_splitterRatios[i]); - rects[i].top = accumulatedHeight; - rects[i].bottom = accumulatedHeight + paneHeight; - accumulatedHeight = rects[i].bottom + 4; // 4 pixels for splitter + rects[i].left = rcParent.left; + rects[i].right = rcParent.right; + + if (i < nImages - 1) + { + rects[i].top = accumulatedHeight; + rects[i].bottom = accumulatedHeight + height; + accumulatedHeight = rects[i].bottom + 4; // 4 pixels for splitter + } + else + { + rects[i].top = accumulatedHeight; + rects[i].bottom = rcParent.bottom; + } } - else + } + else + { + // Ratio-based division + for (int i = 0; i < nImages; ++i) { - // Last pane takes remaining height - rects[i].top = accumulatedHeight; - rects[i].bottom = rcParent.bottom; + rects[i].left = rcParent.left; + rects[i].right = rcParent.right; + + if (i < nImages - 1) + { + int paneHeight = static_cast(totalHeight * ratios[i]); + rects[i].top = accumulatedHeight; + rects[i].bottom = accumulatedHeight + paneHeight; + accumulatedHeight = rects[i].bottom + 4; // 4 pixels for splitter + } + else + { + // Last pane takes remaining height + rects[i].top = accumulatedHeight; + rects[i].bottom = rcParent.bottom; + } } } } + return rects; + } + + std::vector CalcChildImgWindowRect(HWND hWnd, int nImages, bool bHorizontalSplit) + { + return CalcChildImgWindowRectInternal(hWnd, nImages, bHorizontalSplit, nullptr); + } + + void ApplySplitterRatio() + { + if (!m_hWnd || m_nImages < 2) + return; + + std::vector rects = CalcChildImgWindowRectInternal(m_hWnd, m_nImages, m_bHorizontalSplit, m_splitterRatios); for (int i = 0; i < m_nImages; ++i) m_imgWindow[i].SetWindowRect(rects[i]); } From 10bfbe1a7b51db32da8b3c455a02c33d3899d40b Mon Sep 17 00:00:00 2001 From: Takashi Sawanaka Date: Fri, 4 Sep 2026 20:29:45 +0900 Subject: [PATCH 5/6] Change the WinIMergeLib splitter position API to use splitter ratios instead of absolute positions. --- src/WinIMergeLib/ImgMergeWindow.hpp | 145 +++++++++------------------- 1 file changed, 47 insertions(+), 98 deletions(-) diff --git a/src/WinIMergeLib/ImgMergeWindow.hpp b/src/WinIMergeLib/ImgMergeWindow.hpp index eb38e1b..75a4de6 100644 --- a/src/WinIMergeLib/ImgMergeWindow.hpp +++ b/src/WinIMergeLib/ImgMergeWindow.hpp @@ -74,13 +74,10 @@ class CImgMergeWindow : public IImgMergeWindow , m_gdiplusToken(0) , m_timerPrev() , m_timerNext() + , m_splitterRatios{ -1.0, -1.0 } + , m_requestedSplitterRatios{ -1.0, -1.0 } + , m_ChildWndProc{ nullptr, nullptr, nullptr } { - for (int i = 0; i < 3; ++i) - m_ChildWndProc[i] = NULL; - - m_splitterRatios[0] = -1.0; - m_splitterRatios[1] = -1.0; - Gdiplus::GdiplusStartupInput gdiplusStartupInput; Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, nullptr); } @@ -218,7 +215,7 @@ class CImgMergeWindow : public IImgMergeWindow if (!m_hWnd) return; m_bHorizontalSplit = horizontalSplit; - std::vector rects = CalcChildImgWindowRect(m_hWnd, m_nImages, m_bHorizontalSplit); + std::vector rects = CalcChildImgWindowRect(m_hWnd, m_nImages, m_bHorizontalSplit, m_splitterRatios); for (int i = 0; i < m_nImages; ++i) { if (i < m_nImages - 1) @@ -241,7 +238,10 @@ class CImgMergeWindow : public IImgMergeWindow for (int i = 0; i < count; ++i) { if (ratios[i] >= 0.0 && ratios[i] <= 1.0) + { m_splitterRatios[i] = ratios[i]; + m_requestedSplitterRatios[i] = ratios[i]; + } } ApplySplitterRatio(); } @@ -251,7 +251,10 @@ class CImgMergeWindow : public IImgMergeWindow if (m_nImages < 2) return; for (int i = 0; i < m_nImages - 1; ++i) + { m_splitterRatios[i] = 1.0 / m_nImages; + m_requestedSplitterRatios[i] = -1.0; + } ApplySplitterRatio(); } @@ -891,8 +894,8 @@ class CImgMergeWindow : public IImgMergeWindow m_nImages = nImages; for (int i = 0; i < nImages - 1; ++i) { - if (m_splitterRatios[i] < 0.0) - m_splitterRatios[i] = 1.0 / nImages; + m_splitterRatios[i] = (m_requestedSplitterRatios[i] < 0.0) ? 1.0 / nImages : m_requestedSplitterRatios[i]; + m_requestedSplitterRatios[i] = -1.0; } bool bSucceeded = m_buffer.NewImages(nImages, nPages, width, height); if (m_hWnd) @@ -906,7 +909,7 @@ class CImgMergeWindow : public IImgMergeWindow m_buffer.CompareImages(); if (m_hWnd) { - std::vector rects = CalcChildImgWindowRectInternal(m_hWnd, nImages, m_bHorizontalSplit, m_splitterRatios); + std::vector rects = CalcChildImgWindowRect(m_hWnd, nImages, m_bHorizontalSplit, m_splitterRatios); for (int i = 0; i < nImages; ++i) { if (i < nImages - 1) @@ -929,8 +932,8 @@ class CImgMergeWindow : public IImgMergeWindow m_nImages = nImages; for (int i = 0; i < nImages - 1; ++i) { - if (m_splitterRatios[i] < 0.0) - m_splitterRatios[i] = 1.0 / nImages; + m_splitterRatios[i] = (m_requestedSplitterRatios[i] < 0.0) ? 1.0 / nImages : m_requestedSplitterRatios[i]; + m_requestedSplitterRatios[i] = -1.0; } bool bSucceeded = m_buffer.OpenImages(nImages, filename); if (m_hWnd) @@ -944,7 +947,7 @@ class CImgMergeWindow : public IImgMergeWindow m_buffer.CompareImages(); if (m_hWnd) { - std::vector rects = CalcChildImgWindowRectInternal(m_hWnd, nImages, m_bHorizontalSplit, m_splitterRatios); + std::vector rects = CalcChildImgWindowRect(m_hWnd, nImages, m_bHorizontalSplit, m_splitterRatios); for (int i = 0; i < nImages; ++i) { if (i < nImages - 1) @@ -1302,7 +1305,7 @@ class CImgMergeWindow : public IImgMergeWindow return RegisterClassExW(&wcex); } - std::vector CalcChildImgWindowRectInternal(HWND hWnd, int nImages, bool bHorizontalSplit, const double* ratios = nullptr) + std::vector CalcChildImgWindowRect(HWND hWnd, int nImages, bool bHorizontalSplit, const double* ratios = nullptr) { std::vector rects; RECT rcParent; @@ -1320,49 +1323,24 @@ class CImgMergeWindow : public IImgMergeWindow int totalWidth = rcParent.right - rcParent.left - cx; int accumulatedWidth = 0; - if (ratios == nullptr) + // Ratio-based division + for (int i = 0; i < nImages; ++i) { - // Equal division - int width = totalWidth / nImages - 2; - for (int i = 0; i < nImages; ++i) - { - rects[i].top = rcParent.top; - rects[i].bottom = rcParent.bottom; + rects[i].top = rcParent.top; + rects[i].bottom = rcParent.bottom; - if (i < nImages - 1) - { - rects[i].left = accumulatedWidth; - rects[i].right = accumulatedWidth + width; - accumulatedWidth = rects[i].right + 4; // 4 pixels for splitter - } - else - { - rects[i].left = accumulatedWidth; - rects[i].right = rcParent.right; - } + if (i < nImages - 1) + { + int paneWidth = static_cast(totalWidth * ratios[i]); + rects[i].left = accumulatedWidth; + rects[i].right = accumulatedWidth + paneWidth; + accumulatedWidth = rects[i].right + 4; // 4 pixels for splitter } - } - else - { - // Ratio-based division - for (int i = 0; i < nImages; ++i) + else { - rects[i].top = rcParent.top; - rects[i].bottom = rcParent.bottom; - - if (i < nImages - 1) - { - int paneWidth = static_cast(totalWidth * ratios[i]); - rects[i].left = accumulatedWidth; - rects[i].right = accumulatedWidth + paneWidth; - accumulatedWidth = rects[i].right + 4; // 4 pixels for splitter - } - else - { - // Last pane takes remaining width - rects[i].left = accumulatedWidth; - rects[i].right = rcParent.right; - } + // Last pane takes remaining width + rects[i].left = accumulatedWidth; + rects[i].right = rcParent.right; } } } @@ -1373,49 +1351,24 @@ class CImgMergeWindow : public IImgMergeWindow int totalHeight = rcParent.bottom - rcParent.top - cy; int accumulatedHeight = 0; - if (ratios == nullptr) + // Ratio-based division + for (int i = 0; i < nImages; ++i) { - // Equal division - int height = totalHeight / nImages - 2; - for (int i = 0; i < nImages; ++i) - { - rects[i].left = rcParent.left; - rects[i].right = rcParent.right; + rects[i].left = rcParent.left; + rects[i].right = rcParent.right; - if (i < nImages - 1) - { - rects[i].top = accumulatedHeight; - rects[i].bottom = accumulatedHeight + height; - accumulatedHeight = rects[i].bottom + 4; // 4 pixels for splitter - } - else - { - rects[i].top = accumulatedHeight; - rects[i].bottom = rcParent.bottom; - } + if (i < nImages - 1) + { + int paneHeight = static_cast(totalHeight * ratios[i]); + rects[i].top = accumulatedHeight; + rects[i].bottom = accumulatedHeight + paneHeight; + accumulatedHeight = rects[i].bottom + 4; // 4 pixels for splitter } - } - else - { - // Ratio-based division - for (int i = 0; i < nImages; ++i) + else { - rects[i].left = rcParent.left; - rects[i].right = rcParent.right; - - if (i < nImages - 1) - { - int paneHeight = static_cast(totalHeight * ratios[i]); - rects[i].top = accumulatedHeight; - rects[i].bottom = accumulatedHeight + paneHeight; - accumulatedHeight = rects[i].bottom + 4; // 4 pixels for splitter - } - else - { - // Last pane takes remaining height - rects[i].top = accumulatedHeight; - rects[i].bottom = rcParent.bottom; - } + // Last pane takes remaining height + rects[i].top = accumulatedHeight; + rects[i].bottom = rcParent.bottom; } } } @@ -1423,17 +1376,12 @@ class CImgMergeWindow : public IImgMergeWindow return rects; } - std::vector CalcChildImgWindowRect(HWND hWnd, int nImages, bool bHorizontalSplit) - { - return CalcChildImgWindowRectInternal(hWnd, nImages, bHorizontalSplit, nullptr); - } - void ApplySplitterRatio() { if (!m_hWnd || m_nImages < 2) return; - std::vector rects = CalcChildImgWindowRectInternal(m_hWnd, m_nImages, m_bHorizontalSplit, m_splitterRatios); + std::vector rects = CalcChildImgWindowRect(m_hWnd, m_nImages, m_bHorizontalSplit, m_splitterRatios); for (int i = 0; i < m_nImages; ++i) m_imgWindow[i].SetWindowRect(rects[i]); } @@ -2224,6 +2172,7 @@ class CImgMergeWindow : public IImgMergeWindow std::unique_ptr m_pOcr; std::chrono::milliseconds m_timerPrev; std::chrono::milliseconds m_timerNext; + double m_requestedSplitterRatios[2]; double m_splitterRatios[2]; inline static bool s_bDarkBackgroundEnabled = false; }; From 0c7ccb71ce4e583af69576a7e4c7abce5eaded71 Mon Sep 17 00:00:00 2001 From: Takashi Sawanaka Date: Fri, 4 Sep 2026 20:42:51 +0900 Subject: [PATCH 6/6] Change the WinIMergeLib splitter position API to use splitter ratios instead of absolute positions. --- src/WinIMergeLib/ImgMergeWindow.hpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/WinIMergeLib/ImgMergeWindow.hpp b/src/WinIMergeLib/ImgMergeWindow.hpp index 75a4de6..593378e 100644 --- a/src/WinIMergeLib/ImgMergeWindow.hpp +++ b/src/WinIMergeLib/ImgMergeWindow.hpp @@ -893,10 +893,7 @@ class CImgMergeWindow : public IImgMergeWindow CloseImages(); m_nImages = nImages; for (int i = 0; i < nImages - 1; ++i) - { m_splitterRatios[i] = (m_requestedSplitterRatios[i] < 0.0) ? 1.0 / nImages : m_requestedSplitterRatios[i]; - m_requestedSplitterRatios[i] = -1.0; - } bool bSucceeded = m_buffer.NewImages(nImages, nPages, width, height); if (m_hWnd) { @@ -931,10 +928,7 @@ class CImgMergeWindow : public IImgMergeWindow CloseImages(); m_nImages = nImages; for (int i = 0; i < nImages - 1; ++i) - { m_splitterRatios[i] = (m_requestedSplitterRatios[i] < 0.0) ? 1.0 / nImages : m_requestedSplitterRatios[i]; - m_requestedSplitterRatios[i] = -1.0; - } bool bSucceeded = m_buffer.OpenImages(nImages, filename); if (m_hWnd) {