mirror of
https://github.com/rizonesoft/Notepad3.git
synced 2026-06-14 21:09:05 +08:00
219 lines
6.7 KiB
C++
219 lines
6.7 KiB
C++
// sktoolslib - common files for SK tools
|
|
|
|
// Copyright (C) 2012-2013, 2016, 2020 - Stefan Kueng
|
|
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software Foundation,
|
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "DlgResizer.h"
|
|
#include <cassert>
|
|
#include <type_traits>
|
|
|
|
#ifndef ComboBox_GetEditSel
|
|
#include <windowsx.h>
|
|
#endif
|
|
|
|
CDlgResizer::CDlgResizer(void)
|
|
: m_hDlg(nullptr)
|
|
, m_wndGrip(nullptr)
|
|
, m_useSizeGrip(true)
|
|
{
|
|
m_controls.clear();
|
|
m_dlgRect = {};
|
|
m_dlgRectScreen = {};
|
|
m_sizeGrip = {};
|
|
}
|
|
|
|
CDlgResizer::~CDlgResizer(void)
|
|
{
|
|
m_controls.clear();
|
|
}
|
|
|
|
|
|
void CDlgResizer::Init(HWND hWndDlg)
|
|
{
|
|
m_hDlg = hWndDlg;
|
|
GetClientRect(hWndDlg, &m_dlgRect);
|
|
GetWindowRect(hWndDlg, &m_dlgRectScreen);
|
|
OffsetRect(&m_dlgRectScreen, -m_dlgRectScreen.left, -m_dlgRectScreen.top);
|
|
|
|
m_sizeGrip.cx = GetSystemMetrics(SM_CXVSCROLL);
|
|
m_sizeGrip.cy = GetSystemMetrics(SM_CYHSCROLL);
|
|
|
|
RECT rect = { 0 , 0, m_sizeGrip.cx, m_sizeGrip.cy };
|
|
|
|
m_wndGrip = ::CreateWindowEx(0, _T("SCROLLBAR"),
|
|
(LPCTSTR)nullptr,
|
|
WS_CHILD | WS_CLIPSIBLINGS | SBS_SIZEGRIP,
|
|
rect.left, rect.top,
|
|
rect.right-rect.left,
|
|
rect.bottom-rect.top,
|
|
m_hDlg,
|
|
(HMENU)0,
|
|
nullptr,
|
|
nullptr);
|
|
|
|
if (m_wndGrip)
|
|
{
|
|
// set a triangular window region
|
|
HRGN rgnGrip, rgn;
|
|
rgn = ::CreateRectRgn(0,0,1,1);
|
|
rgnGrip = ::CreateRectRgnIndirect(&rect);
|
|
|
|
for (int y=0; y<m_sizeGrip.cy; y++)
|
|
{
|
|
::SetRectRgn(rgn, 0, y, m_sizeGrip.cx-y, y+1);
|
|
::CombineRgn(rgnGrip, rgnGrip, rgn, RGN_DIFF);
|
|
}
|
|
::SetWindowRgn(m_wndGrip, rgnGrip, FALSE);
|
|
|
|
// update pos
|
|
UpdateGripPos();
|
|
ShowSizeGrip();
|
|
}
|
|
}
|
|
|
|
void CDlgResizer::AdjustMinMaxSize()
|
|
{
|
|
GetWindowRect(m_hDlg, &m_dlgRectScreen);
|
|
OffsetRect(&m_dlgRectScreen, -m_dlgRectScreen.left, -m_dlgRectScreen.top);
|
|
}
|
|
|
|
void CDlgResizer::AddControl(HWND hWndDlg, UINT ctrlId, UINT resizeType)
|
|
{
|
|
ResizeCtrls ctrlInfo;
|
|
|
|
ctrlInfo.hWnd = GetDlgItem(hWndDlg, ctrlId);
|
|
if (!ctrlInfo.hWnd)
|
|
{
|
|
assert(false);
|
|
return;
|
|
}
|
|
ctrlInfo.resizeType = resizeType;
|
|
|
|
GetWindowRect(ctrlInfo.hWnd, &ctrlInfo.origSize);
|
|
OffsetRect(&ctrlInfo.origSize, -ctrlInfo.origSize.left, -ctrlInfo.origSize.top);
|
|
MapWindowPoints(ctrlInfo.hWnd, hWndDlg, (LPPOINT)&ctrlInfo.origSize, 2);
|
|
|
|
m_controls.push_back(ctrlInfo);
|
|
}
|
|
|
|
void CDlgResizer::DoResize(int width, int height)
|
|
{
|
|
UpdateGripPos();
|
|
if (m_controls.empty())
|
|
return;
|
|
|
|
InvalidateRect(m_hDlg, nullptr, true);
|
|
HDWP hdwp = BeginDeferWindowPos((int)m_controls.size());
|
|
|
|
TCHAR className[257]; // WNDCLASS docs say 256 is the longest class name possible.
|
|
std::vector<std::pair<size_t, DWORD>> savedSelections;
|
|
for (size_t i=0; i<m_controls.size(); ++i)
|
|
{
|
|
const auto& ctrlInfo = m_controls[i];
|
|
// Work around a bug in the standard combo box control that causes it to
|
|
// incorrectly change the selection status after resizing. Without this
|
|
// fix sometimes the combo box will show selected text after a WM_SIZE
|
|
// resize type event even if there was no text selected before the size event.
|
|
// The workaround is to save the current selection state before the resize and
|
|
// to restore that state after the resize.
|
|
int status = GetClassName(ctrlInfo.hWnd, className, (int)std::size(className));
|
|
bool isComboBox = status > 0 &&_tcsicmp(className, _T("COMBOBOX")) == 0;
|
|
if (isComboBox)
|
|
{
|
|
DWORD sel = ComboBox_GetEditSel(ctrlInfo.hWnd);
|
|
savedSelections.push_back({ i, sel });
|
|
}
|
|
RECT newpos = ctrlInfo.origSize;
|
|
switch (ctrlInfo.resizeType)
|
|
{
|
|
case RESIZER_TOPLEFT:
|
|
break; // do nothing - the original position is fine
|
|
case RESIZER_TOPRIGHT:
|
|
newpos.left += (width - m_dlgRect.right);
|
|
newpos.right += (width - m_dlgRect.right);
|
|
break;
|
|
case RESIZER_TOPLEFTRIGHT:
|
|
newpos.right += (width - m_dlgRect.right);
|
|
break;
|
|
case RESIZER_TOPLEFTBOTTOMRIGHT:
|
|
newpos.right += (width - m_dlgRect.right);
|
|
newpos.bottom += (height - m_dlgRect.bottom);
|
|
break;
|
|
case RESIZER_BOTTOMLEFT:
|
|
newpos.top += (height - m_dlgRect.bottom);
|
|
newpos.bottom += (height - m_dlgRect.bottom);
|
|
break;
|
|
case RESIZER_BOTTOMRIGHT:
|
|
newpos.top += (height - m_dlgRect.bottom);
|
|
newpos.bottom += (height - m_dlgRect.bottom);
|
|
newpos.left += (width - m_dlgRect.right);
|
|
newpos.right += (width - m_dlgRect.right);
|
|
break;
|
|
case RESIZER_BOTTOMLEFTRIGHT:
|
|
newpos.top += (height - m_dlgRect.bottom);
|
|
newpos.bottom += (height - m_dlgRect.bottom);
|
|
newpos.right += (width - m_dlgRect.right);
|
|
break;
|
|
}
|
|
hdwp = DeferWindowPos(hdwp, ctrlInfo.hWnd, nullptr, newpos.left, newpos.top,
|
|
newpos.right-newpos.left, newpos.bottom-newpos.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
}
|
|
EndDeferWindowPos(hdwp);
|
|
for (const auto& selInfo : savedSelections)
|
|
{
|
|
size_t index = selInfo.first;
|
|
DWORD sel = selInfo.second;
|
|
int startSel = LOWORD(sel);
|
|
int endSel = HIWORD(sel);
|
|
ComboBox_SetEditSel(m_controls[index].hWnd, startSel, endSel);
|
|
}
|
|
UpdateGripPos();
|
|
}
|
|
|
|
void CDlgResizer::ShowSizeGrip(bool bShow)
|
|
{
|
|
::ShowWindow(m_wndGrip, (bShow && m_useSizeGrip) ? SW_SHOW : SW_HIDE);
|
|
}
|
|
|
|
void CDlgResizer::UpdateGripPos()
|
|
{
|
|
RECT rect;
|
|
::GetClientRect(m_hDlg, &rect);
|
|
|
|
rect.left = rect.right - m_sizeGrip.cx;
|
|
rect.top = rect.bottom - m_sizeGrip.cy;
|
|
|
|
// must stay below other children
|
|
::SetWindowPos(m_wndGrip,HWND_BOTTOM, rect.left, rect.top, 0, 0,
|
|
SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREPOSITION);
|
|
|
|
// maximized windows cannot be resized
|
|
|
|
if (::IsZoomed(m_hDlg))
|
|
{
|
|
::EnableWindow(m_wndGrip, FALSE);
|
|
ShowSizeGrip(false);
|
|
}
|
|
else
|
|
{
|
|
::EnableWindow(m_wndGrip, TRUE);
|
|
ShowSizeGrip(true);
|
|
}
|
|
}
|