diff --git a/src/WinIMergeLib/ImgMergeWindow.hpp b/src/WinIMergeLib/ImgMergeWindow.hpp index c0ce514..9754ba0 100644 --- a/src/WinIMergeLib/ImgMergeWindow.hpp +++ b/src/WinIMergeLib/ImgMergeWindow.hpp @@ -64,6 +64,7 @@ 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) @@ -214,6 +215,9 @@ 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) @@ -224,6 +228,24 @@ class CImgMergeWindow : public IImgMergeWindow } } + int GetSplitterPosition() const override + { + return m_splitterPosition; + } + + void SetSplitterPosition(int position) 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) + { + std::vector rects = CalcChildImgWindowRect(m_hWnd, m_nImages, m_bHorizontalSplit); + for (int i = 0; i < m_nImages; ++i) + m_imgWindow[i].SetWindowRect(rects[i]); + } + } + COLORREF GetDiffColor() const override { return RGBQUADtoCOLORREF(m_buffer.GetDiffColor()); @@ -1261,6 +1283,27 @@ 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; @@ -1271,35 +1314,61 @@ class CImgMergeWindow : public IImgMergeWindow { 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) + const int splitPos = CalcSplitterPosition(nImages, rcParent.right); + if (splitPos > 0) { + 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 { - 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) + const int splitPos = CalcSplitterPosition(nImages, rcParent.bottom); + if (splitPos > 0) { + rc.top = 0; + rc.bottom = splitPos; childrects.push_back(rc); rc.top = rc.bottom + 2 * 2; + rc.bottom = rcParent.bottom; + 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); } - rc.bottom = rcParent.bottom; - rc.top = rc.bottom - height - cy; - childrects.push_back(rc); } } return childrects; @@ -1352,6 +1421,21 @@ class CImgMergeWindow : public IImgMergeWindow rc[m_nImages - 1].bottom = rcParent.bottom; } + // 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) + { + 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; + } + for (int i = 0; i < m_nImages; ++i) m_imgWindow[i].SetWindowRect(rc[i]); } @@ -2042,6 +2126,9 @@ 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; diff --git a/src/WinIMergeLib/WinIMergeLib.h b/src/WinIMergeLib/WinIMergeLib.h index 8ccccc3..8c682c0 100644 --- a/src/WinIMergeLib/WinIMergeLib.h +++ b/src/WinIMergeLib/WinIMergeLib.h @@ -192,6 +192,8 @@ 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; }; struct IImgToolWindow