Refactor docking panel tab bar to improve dpi compatibility

- reduce flickering when resizing panels

Ref #14959

Close #15282
pull/15285/head^2
ozone10 2024-06-12 18:31:15 +02:00 committed by Don Ho
parent 7a6768b029
commit f2fbeadfea
5 changed files with 67 additions and 128 deletions

View File

@ -768,10 +768,8 @@ LRESULT DockingCont::runProcTab(HWND hwnd, UINT Message, WPARAM wParam, LPARAM l
RECT rcIntersect {};
if (IntersectRect(&rcIntersect, &ps.rcPaint, &dis.rcItem))
{
dis.rcItem.top += NppParameters::getInstance()._dpiManager.scaleY(1);
dis.rcItem.right -= 1;
dis.rcItem.bottom += 2;
::OffsetRect(&dis.rcItem, 0, _dpiManager.scale(CAPTION_GAP * 2));
if (i == 0)
{
POINT edges[] = {
@ -1012,13 +1010,13 @@ LRESULT DockingCont::runProcTab(HWND hwnd, UINT Message, WPARAM wParam, LPARAM l
return ::CallWindowProc(_hDefaultTabProc, hwnd, Message, wParam, lParam);
}
void DockingCont::drawTabItem(DRAWITEMSTRUCT *pDrawItemStruct)
void DockingCont::drawTabItem(DRAWITEMSTRUCT* pDrawItemStruct)
{
TCITEM tcItem {};
TCITEM tcItem{};
RECT rc = pDrawItemStruct->rcItem;
int nTab = pDrawItemStruct->itemID;
bool isSelected = (nTab == getActiveTb());
bool isSelected = (nTab == getActiveTb());
// get current selected item
tcItem.mask = TCIF_PARAM;
@ -1026,41 +1024,29 @@ void DockingCont::drawTabItem(DRAWITEMSTRUCT *pDrawItemStruct)
if (!tcItem.lParam)
return;
const TCHAR *text = reinterpret_cast<tTbData*>(tcItem.lParam)->pszName;
int length = lstrlen(reinterpret_cast<tTbData*>(tcItem.lParam)->pszName);
auto tbData = reinterpret_cast<tTbData*>(tcItem.lParam);
const TCHAR* text = tbData->pszName;
int length = lstrlen(tbData->pszName);
// get drawing context
HDC hDc = pDrawItemStruct->hDC;
int nSavedDC = ::SaveDC(hDc);
// For some bizarre reason the rcItem you get extends above the actual
// drawing area. We have to workaround this "feature".
rc.top += ::GetSystemMetrics(SM_CYEDGE);
::SetBkMode(hDc, TRANSPARENT);
const int onePadding = _dpiManager.scale(1);
if (NppDarkMode::isEnabled())
{
RECT selectedRect = rc;
selectedRect.top -= 2;
selectedRect.bottom += 2;
if (isSelected)
{
::FillRect(hDc, &selectedRect, NppDarkMode::getSofterBackgroundBrush());
}
else
{
::FillRect(hDc, &selectedRect, NppDarkMode::getBackgroundBrush());
}
::FillRect(hDc, &rc, isSelected ? NppDarkMode::getSofterBackgroundBrush() : NppDarkMode::getBackgroundBrush());
::OffsetRect(&rc, 0, -onePadding);
}
// draw orange bar
if (!NppDarkMode::isEnabled() && _bDrawOgLine && isSelected)
else if (isSelected) // draw orange bar
{
RECT barRect = rc;
barRect.top += rc.bottom - 4;
barRect.top = rc.bottom - _dpiManager.scale(4);
HBRUSH hBrush = ::CreateSolidBrush(RGB(250, 170, 60));
::FillRect(hDc, &barRect, hBrush);
@ -1068,42 +1054,34 @@ void DockingCont::drawTabItem(DRAWITEMSTRUCT *pDrawItemStruct)
}
// draw icon if enabled
if (((tTbData*)tcItem.lParam)->uMask & DWS_ICONTAB)
if ((tbData->uMask & DWS_ICONTAB) == DWS_ICONTAB)
{
HIMAGELIST hImageList = (HIMAGELIST)::SendMessage(_hParent, DMM_GETIMAGELIST, 0, 0);
int iPosImage = static_cast<int32_t>(::SendMessage(_hParent, DMM_GETICONPOS, 0, reinterpret_cast<LPARAM>(reinterpret_cast<tTbData*>(tcItem.lParam)->hClient)));
const int wPadding = _dpiManager.scale(g_dockingContTabIconPadding);
const int iconSize = _dpiManager.scale(g_dockingContTabIconSize);
if ((hImageList != NULL) && (iPosImage >= 0))
const int iconDpiDynamicalX = rc.left + (isSelected ? wPadding : (rc.right - rc.left - iconSize + 1) / 2);
const int iconDpiDynamicalY = rc.top + (rc.bottom - rc.top - iconSize - onePadding) / 2;
::DrawIconEx(hDc, iconDpiDynamicalX, iconDpiDynamicalY, tbData->hIconTab, 0, 0, 0, nullptr, DI_NORMAL);
if (isSelected)
{
// Get height of image so we
IMAGEINFO info {};
const RECT& imageRect = info.rcImage;
ImageList_GetImageInfo(hImageList, iPosImage, &info);
int darkPaddingX = NppDarkMode::isEnabled() ? 1 : 0;
int darkPaddingY = NppDarkMode::isEnabled() ? 2 : (isSelected ? 1 : 0);
int iconDpiDynamicalX = isSelected ? rc.left + 3
: rc.left + (rc.right - rc.left - imageRect.right + imageRect.left) / 2 + darkPaddingX;
int iconDpiDynamicalY = NppParameters::getInstance()._dpiManager.scaleY(5) + darkPaddingY;
ImageList_Draw(hImageList, iPosImage, hDc, iconDpiDynamicalX, iconDpiDynamicalY, ILD_NORMAL);
if (isSelected)
{
rc.left += imageRect.right - imageRect.left + 5;
}
rc.left += iconSize + wPadding * 2;
}
}
if (isSelected)
{
if (NppDarkMode::isEnabled())
{
const int textOffset = 3 * onePadding / 2 - 1;
::OffsetRect(&rc, 0, -textOffset);
}
COLORREF _unselectedColor = RGB(0, 0, 0);
::SetTextColor(hDc, NppDarkMode::isEnabled() ? NppDarkMode::getTextColor() : _unselectedColor);
// draw text
rc.top -= ::GetSystemMetrics(SM_CYEDGE);
::SelectObject(hDc, _hFont);
::DrawText(hDc, text, length, &rc, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX);
}
@ -1142,8 +1120,10 @@ intptr_t CALLBACK DockingCont::run_dlgProc(UINT Message, WPARAM wParam, LPARAM l
_hDefaultTabProc = reinterpret_cast<WNDPROC>(::SetWindowLongPtr(_hContTab, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(wndTabProc)));
// set min tab width
int tabDpiDynamicalMinWidth = NppParameters::getInstance()._dpiManager.scaleX(24);
::SendMessage(_hContTab, TCM_SETMINTABWIDTH, 0, tabDpiDynamicalMinWidth);
const int tabDpiPadding = _dpiManager.scale(g_dockingContTabIconSize + g_dockingContTabIconPadding * 2);
::SendMessage(_hContTab, TCM_SETMINTABWIDTH, 0, tabDpiPadding);
TabCtrl_SetPadding(_hContTab, tabDpiPadding / 2, 0);
TabCtrl_SetItemSize(_hContTab, 2 * tabDpiPadding, tabDpiPadding);
return TRUE;
}
@ -1165,15 +1145,28 @@ intptr_t CALLBACK DockingCont::run_dlgProc(UINT Message, WPARAM wParam, LPARAM l
}
break;
}
case WM_ERASEBKGND:
{
if (!NppDarkMode::isEnabled())
{
break;
}
RECT rc {};
HDC hDC = reinterpret_cast<HDC>(wParam);
RECT rc{};
getClientRect(rc);
::FillRect(reinterpret_cast<HDC>(wParam), &rc, NppDarkMode::getDarkerBackgroundBrush());
RECT rcTab{};
getMappedChildRect(_hContTab, rcTab);
RECT rcClientTab{};
getMappedChildRect(IDC_CLIENT_TAB, rcClientTab);
RECT rcCap{};
getMappedChildRect(_hCaption, rcCap);
::ExcludeClipRect(hDC, rcTab.left, rcTab.top, rcTab.right, rcTab.bottom);
::ExcludeClipRect(hDC, rcClientTab.left, rcClientTab.top, rcClientTab.right, rcClientTab.bottom);
::ExcludeClipRect(hDC, rcCap.left, rcCap.top, rcCap.right, rcCap.bottom);
::FillRect(hDC, &rc, NppDarkMode::isEnabled() ? NppDarkMode::getDarkerBackgroundBrush() : ::GetSysColorBrush(COLOR_3DFACE));
return TRUE;
}
@ -1266,7 +1259,7 @@ void DockingCont::onSize()
if (iItemCnt >= 1)
{
// resize to docked window
int tabDpiDynamicalHeight = NppParameters::getInstance()._dpiManager.scaleY(16) + 8;
const int tabDpiDynamicalHeight = _dpiManager.scale(g_dockingContTabIconSize + (g_dockingContTabIconPadding) * 2 + CAPTION_GAP);
if (_isFloating == false)
{
// draw caption
@ -1293,7 +1286,7 @@ void DockingCont::onSize()
iTabOff = tabDpiDynamicalHeight;
::SetWindowPos(_hContTab, NULL,
rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom,
rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom,
SWP_NOZORDER | SWP_SHOWWINDOW | SWP_NOACTIVATE);
if (_hTabUpdown != nullptr)
@ -1319,7 +1312,7 @@ void DockingCont::onSize()
// set position of client area
::SetWindowPos(::GetDlgItem(_hSelf, IDC_CLIENT_TAB), NULL,
rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom,
rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom,
SWP_NOZORDER | SWP_NOACTIVATE);
}
// resize to float window
@ -1340,7 +1333,7 @@ void DockingCont::onSize()
rcTemp.bottom = tabDpiDynamicalHeight;
::SetWindowPos(_hContTab, NULL,
rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom,
rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom,
SWP_NOZORDER | SWP_SHOWWINDOW);
if (_hTabUpdown != nullptr)
@ -1355,10 +1348,9 @@ void DockingCont::onSize()
rcTemp.bottom -= ((iItemCnt == 1)?0:tabDpiDynamicalHeight);
::SetWindowPos(::GetDlgItem(_hSelf, IDC_CLIENT_TAB), NULL,
rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom,
rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom,
SWP_NOZORDER | SWP_NOACTIVATE);
}
// get active item data
size_t iItemCnt2 = static_cast<size_t>(::SendMessage(_hContTab, TCM_GETITEMCOUNT, 0, 0));

View File

@ -43,6 +43,7 @@ enum eMousePos {
#define CLOSEBTN_POS_TOP 3
constexpr int g_dockingContTabIconSize = 14;
constexpr int g_dockingContTabIconPadding = 3;
class DockingCont : public StaticDialog
{
@ -118,15 +119,11 @@ public:
updateCaption();
};
void setTabStyle(const BOOL & bDrawOgLine) {
_bDrawOgLine = bDrawOgLine;
::RedrawWindow(_hContTab, nullptr, nullptr, RDW_INVALIDATE);
};
void destroy() override{
for (int iTb = static_cast<int>(_vTbData.size()); iTb > 0; iTb--)
for (auto& tTbData : _vTbData)
{
delete _vTbData[iTb-1];
::DestroyIcon(tTbData->hIconTab);
delete tTbData;
}
::DestroyWindow(_hSelf);
};
@ -191,9 +188,6 @@ private:
BOOL _isMouseClose = FALSE;
BOOL _isMouseOver = FALSE;
RECT _rcCaption{};
// tab style
BOOL _bDrawOgLine = TRUE;
// Important value for DlgMoving class
BOOL _dragFromTab = FALSE;

View File

@ -260,12 +260,6 @@ LRESULT DockingManager::runProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM l
hWndServer = NULL;
}
// destroy imagelist if it exists
if (_hImageList != NULL)
{
::ImageList_Destroy(_hImageList);
}
// destroy containers
for (int32_t i = static_cast<int32_t>(_vContainer.size()); i > 0; i--)
{
@ -395,21 +389,7 @@ LRESULT DockingManager::runProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM l
toggleVisTb(reinterpret_cast<DockingCont*>(lParam), DMM_DOCK);
return FALSE;
}
case DMM_GETIMAGELIST:
{
return reinterpret_cast<LPARAM>(_hImageList);
}
case DMM_GETICONPOS:
{
for (size_t uImageCnt = 0, len = _vImageList.size(); uImageCnt < len; ++uImageCnt)
{
if (reinterpret_cast<HWND>(lParam) == _vImageList[uImageCnt])
{
return uImageCnt;
}
}
return -1;
}
default :
break;
}
@ -585,23 +565,6 @@ void DockingManager::reSizeTo(RECT & rc)
void DockingManager::createDockableDlg(tTbData data, int iCont, bool isVisible)
{
// add icons
if ((data.uMask & DWS_ICONTAB) && data.hIconTab != NULL)
{
// create image list if not exist
if (_hImageList == NULL)
{
const int iconDpiDynamicalSize = DPIManagerV2::scale(g_dockingContTabIconSize, data.hClient);
_hImageList = ::ImageList_Create(iconDpiDynamicalSize, iconDpiDynamicalSize, ILC_COLOR32 | ILC_MASK, 0, 0);
}
// add icon
::ImageList_AddIcon(_hImageList, data.hIconTab);
// do the reference here to find later the correct position
_vImageList.push_back(data.hClient);
}
if ((data.uMask & DWS_USEOWNDARKMODE) != DWS_USEOWNDARKMODE && NppDarkMode::isEnabledForPlugins())
{
NppDarkMode::autoSubclassAndThemePluginDockWindow(data.hClient);

View File

@ -35,7 +35,7 @@ public :
~DockingManager();
void init(HINSTANCE hInst, HWND hWnd, Window ** ppWin);
virtual void reSizeTo(RECT & rc);
void reSizeTo(RECT & rc) override;
void setClientWnd(Window ** ppWin) {
_ppWindow = ppWin;
@ -73,14 +73,9 @@ public :
_vContainer[CONT_BOTTOM]->setCaptionTop(captionOnTop);
};
void setTabStyle(BOOL orangeLine) {
for (size_t i = 0; i < _vContainer.size(); ++i)
_vContainer[i]->setTabStyle(orangeLine);
};
int getDockedContSize(int iCont);
void setDockedContSize(int iCont, int iSize);
virtual void destroy();
void destroy() override;
void resize();
private :
@ -88,8 +83,6 @@ private :
RECT _rcWork = {};
RECT _rect = {};
Window **_ppMainWindow = nullptr;
std::vector<HWND> _vImageList;
HIMAGELIST _hImageList = nullptr;
std::vector<DockingCont*> _vContainer;
tDockMgr _dockData;
static BOOL _isRegistered;
@ -107,7 +100,4 @@ private :
BOOL ContExists(size_t iCont);
int FindEmptyContainer();
LRESULT SendNotify(HWND hWnd, UINT message);
};

View File

@ -39,8 +39,8 @@
#define DMM_FLOATALL (DMM_MSG + 5)
#define DMM_MOVE (DMM_MSG + 6)
#define DMM_UPDATEDISPINFO (DMM_MSG + 7)
#define DMM_GETIMAGELIST (DMM_MSG + 8)
#define DMM_GETICONPOS (DMM_MSG + 9)
//#define DMM_GETIMAGELIST (DMM_MSG + 8)
//#define DMM_GETICONPOS (DMM_MSG + 9)
#define DMM_DROPDATA (DMM_MSG + 10)
#define DMM_MOVE_SPLITTER (DMM_MSG + 11)
#define DMM_CANCEL_MOVE (DMM_MSG + 12)