Fix status bar and tab bar flicker when updated
Add double buffering for status bar and tab bar to avoid flickering. Fix #15260, close #15296pull/15303/head
parent
6102687faf
commit
5b36e097c2
|
@ -0,0 +1,101 @@
|
||||||
|
// This file is part of Notepad++ project
|
||||||
|
// Copyright (C) 2024 Jiri Hruska <jirka@fud.cz>
|
||||||
|
|
||||||
|
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <windows.h>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
class DoubleBuffer final
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SIZE _size{};
|
||||||
|
HDC _hMemoryDC = nullptr;
|
||||||
|
HBITMAP _hDefaultBitmap = nullptr;
|
||||||
|
HBITMAP _hBufferBitmap = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DoubleBuffer() {}
|
||||||
|
DoubleBuffer(const DoubleBuffer&) = delete;
|
||||||
|
DoubleBuffer& operator=(const DoubleBuffer&) = delete;
|
||||||
|
|
||||||
|
~DoubleBuffer()
|
||||||
|
{
|
||||||
|
if (_hBufferBitmap)
|
||||||
|
{
|
||||||
|
::SelectObject(_hMemoryDC, _hDefaultBitmap);
|
||||||
|
::DeleteObject(_hBufferBitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_hMemoryDC)
|
||||||
|
{
|
||||||
|
::DeleteDC(_hMemoryDC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HDC beginPaint(HWND hWnd, PAINTSTRUCT* ps)
|
||||||
|
{
|
||||||
|
if (!::BeginPaint(hWnd, ps))
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_hMemoryDC)
|
||||||
|
{
|
||||||
|
_hMemoryDC = ::CreateCompatibleDC(ps->hdc);
|
||||||
|
}
|
||||||
|
|
||||||
|
RECT clientRc{};
|
||||||
|
::GetClientRect(hWnd, &clientRc);
|
||||||
|
if (clientRc.right != _size.cx || clientRc.bottom != _size.cy || !_hBufferBitmap)
|
||||||
|
{
|
||||||
|
_size = { clientRc.right, clientRc.bottom };
|
||||||
|
|
||||||
|
if (_hBufferBitmap)
|
||||||
|
{
|
||||||
|
::SelectObject(_hMemoryDC, _hDefaultBitmap);
|
||||||
|
::DeleteObject(_hBufferBitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
_hBufferBitmap = ::CreateCompatibleBitmap(ps->hdc, _size.cx, _size.cy);
|
||||||
|
_hDefaultBitmap = static_cast<HBITMAP>(::SelectObject(_hMemoryDC, _hBufferBitmap));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(ps->rcPaint.left < _size.cx && ps->rcPaint.top < _size.cy);
|
||||||
|
assert(ps->rcPaint.right <= _size.cx && ps->rcPaint.bottom <= _size.cy);
|
||||||
|
|
||||||
|
return _hMemoryDC;
|
||||||
|
}
|
||||||
|
|
||||||
|
void endPaint(HWND hWnd, PAINTSTRUCT* ps)
|
||||||
|
{
|
||||||
|
::BitBlt(
|
||||||
|
ps->hdc, ps->rcPaint.left, ps->rcPaint.top, ps->rcPaint.right - ps->rcPaint.left, ps->rcPaint.bottom - ps->rcPaint.top,
|
||||||
|
_hMemoryDC, ps->rcPaint.left, ps->rcPaint.top,
|
||||||
|
SRCCOPY);
|
||||||
|
|
||||||
|
::EndPaint(hWnd, ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getWidth() const
|
||||||
|
{
|
||||||
|
return _size.cx;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getHeight() const
|
||||||
|
{
|
||||||
|
return _size.cy;
|
||||||
|
}
|
||||||
|
};
|
|
@ -24,6 +24,7 @@
|
||||||
#include "NppDarkMode.h"
|
#include "NppDarkMode.h"
|
||||||
#include <uxtheme.h>
|
#include <uxtheme.h>
|
||||||
#include <vssym32.h>
|
#include <vssym32.h>
|
||||||
|
#include "DoubleBuffer.h"
|
||||||
|
|
||||||
//#define IDC_STATUSBAR 789
|
//#define IDC_STATUSBAR 789
|
||||||
|
|
||||||
|
@ -50,6 +51,7 @@ struct StatusBarSubclassInfo
|
||||||
{
|
{
|
||||||
HTHEME hTheme = nullptr;
|
HTHEME hTheme = nullptr;
|
||||||
HFONT _hFont = nullptr;
|
HFONT _hFont = nullptr;
|
||||||
|
DoubleBuffer _dblBuf;
|
||||||
|
|
||||||
StatusBarSubclassInfo() = default;
|
StatusBarSubclassInfo() = default;
|
||||||
StatusBarSubclassInfo(const HFONT& hFont)
|
StatusBarSubclassInfo(const HFONT& hFont)
|
||||||
|
@ -107,22 +109,24 @@ static LRESULT CALLBACK StatusBarSubclass(HWND hWnd, UINT uMsg, WPARAM wParam, L
|
||||||
{
|
{
|
||||||
case WM_ERASEBKGND:
|
case WM_ERASEBKGND:
|
||||||
{
|
{
|
||||||
if (!NppDarkMode::isEnabled())
|
// Skip background erasing and set fErase in PAINTSTRUCT instead,
|
||||||
{
|
// WM_PAINT does all the painting in all cases
|
||||||
break;
|
return FALSE;
|
||||||
}
|
|
||||||
|
|
||||||
RECT rc{};
|
|
||||||
GetClientRect(hWnd, &rc);
|
|
||||||
FillRect((HDC)wParam, &rc, NppDarkMode::getBackgroundBrush());
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case WM_PAINT:
|
case WM_PAINT:
|
||||||
{
|
{
|
||||||
|
PAINTSTRUCT ps{};
|
||||||
|
HDC hdc = pStatusBarInfo->_dblBuf.beginPaint(hWnd, &ps);
|
||||||
|
|
||||||
if (!NppDarkMode::isEnabled())
|
if (!NppDarkMode::isEnabled())
|
||||||
{
|
{
|
||||||
break;
|
// Even if the standard status bar common control is used directly in non-dark mode,
|
||||||
|
// it suffers from flickering during updates, so let it paint into a back buffer
|
||||||
|
DefSubclassProc(hWnd, WM_ERASEBKGND, reinterpret_cast<WPARAM>(hdc), 0);
|
||||||
|
DefSubclassProc(hWnd, WM_PRINTCLIENT, reinterpret_cast<WPARAM>(hdc), PRF_NONCLIENT | PRF_CLIENT);
|
||||||
|
pStatusBarInfo->_dblBuf.endPaint(hWnd, &ps);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -136,16 +140,10 @@ static LRESULT CALLBACK StatusBarSubclass(HWND hWnd, UINT uMsg, WPARAM wParam, L
|
||||||
const auto style = ::GetWindowLongPtr(hWnd, GWL_STYLE);
|
const auto style = ::GetWindowLongPtr(hWnd, GWL_STYLE);
|
||||||
bool isSizeGrip = style & SBARS_SIZEGRIP;
|
bool isSizeGrip = style & SBARS_SIZEGRIP;
|
||||||
|
|
||||||
PAINTSTRUCT ps{};
|
|
||||||
HDC hdc = BeginPaint(hWnd, &ps);
|
|
||||||
|
|
||||||
auto holdPen = static_cast<HPEN>(::SelectObject(hdc, NppDarkMode::getEdgePen()));
|
auto holdPen = static_cast<HPEN>(::SelectObject(hdc, NppDarkMode::getEdgePen()));
|
||||||
|
|
||||||
auto holdFont = static_cast<HFONT>(::SelectObject(hdc, pStatusBarInfo->_hFont));
|
auto holdFont = static_cast<HFONT>(::SelectObject(hdc, pStatusBarInfo->_hFont));
|
||||||
|
|
||||||
RECT rcClient{};
|
|
||||||
GetClientRect(hWnd, &rcClient);
|
|
||||||
|
|
||||||
FillRect(hdc, &ps.rcPaint, NppDarkMode::getBackgroundBrush());
|
FillRect(hdc, &ps.rcPaint, NppDarkMode::getBackgroundBrush());
|
||||||
|
|
||||||
int nParts = static_cast<int>(SendMessage(hWnd, SB_GETPARTS, 0, 0));
|
int nParts = static_cast<int>(SendMessage(hWnd, SB_GETPARTS, 0, 0));
|
||||||
|
@ -220,8 +218,8 @@ static LRESULT CALLBACK StatusBarSubclass(HWND hWnd, UINT uMsg, WPARAM wParam, L
|
||||||
{
|
{
|
||||||
pStatusBarInfo->ensureTheme(hWnd);
|
pStatusBarInfo->ensureTheme(hWnd);
|
||||||
SIZE gripSize{};
|
SIZE gripSize{};
|
||||||
GetThemePartSize(pStatusBarInfo->hTheme, hdc, SP_GRIPPER, 0, &rcClient, TS_DRAW, &gripSize);
|
RECT rc = { 0, 0, pStatusBarInfo->_dblBuf.getWidth(), pStatusBarInfo->_dblBuf.getHeight() };
|
||||||
RECT rc = rcClient;
|
GetThemePartSize(pStatusBarInfo->hTheme, hdc, SP_GRIPPER, 0, &rc, TS_DRAW, &gripSize);
|
||||||
rc.left = rc.right - gripSize.cx;
|
rc.left = rc.right - gripSize.cx;
|
||||||
rc.top = rc.bottom - gripSize.cy;
|
rc.top = rc.bottom - gripSize.cy;
|
||||||
DrawThemeBackground(pStatusBarInfo->hTheme, hdc, SP_GRIPPER, 0, &rc, nullptr);
|
DrawThemeBackground(pStatusBarInfo->hTheme, hdc, SP_GRIPPER, 0, &rc, nullptr);
|
||||||
|
@ -230,7 +228,7 @@ static LRESULT CALLBACK StatusBarSubclass(HWND hWnd, UINT uMsg, WPARAM wParam, L
|
||||||
::SelectObject(hdc, holdFont);
|
::SelectObject(hdc, holdFont);
|
||||||
::SelectObject(hdc, holdPen);
|
::SelectObject(hdc, holdPen);
|
||||||
|
|
||||||
EndPaint(hWnd, &ps);
|
pStatusBarInfo->_dblBuf.endPaint(hWnd, &ps);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -915,35 +915,29 @@ LRESULT TabBarPlus::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lPara
|
||||||
|
|
||||||
case WM_ERASEBKGND:
|
case WM_ERASEBKGND:
|
||||||
{
|
{
|
||||||
if (!NppDarkMode::isEnabled())
|
// Skip background erasing and set fErase in PAINTSTRUCT instead,
|
||||||
{
|
// WM_PAINT does all the painting in all cases
|
||||||
break;
|
return FALSE;
|
||||||
}
|
|
||||||
|
|
||||||
RECT rc{};
|
|
||||||
GetClientRect(hwnd, &rc);
|
|
||||||
FillRect((HDC)wParam, &rc, NppDarkMode::getDarkerBackgroundBrush());
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case WM_PAINT:
|
case WM_PAINT:
|
||||||
{
|
{
|
||||||
if (!NppDarkMode::isEnabled())
|
PAINTSTRUCT ps{};
|
||||||
{
|
HDC hdc = _dblBuf.beginPaint(hwnd, &ps);
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
LONG_PTR dwStyle = GetWindowLongPtr(hwnd, GWL_STYLE);
|
LONG_PTR dwStyle = GetWindowLongPtr(hwnd, GWL_STYLE);
|
||||||
if (!(dwStyle & TCS_OWNERDRAWFIXED))
|
if (!NppDarkMode::isEnabled() || !(dwStyle & TCS_OWNERDRAWFIXED))
|
||||||
{
|
{
|
||||||
break;
|
// Even if the tab bar common control is used directly, e.g. in non-dark mode,
|
||||||
|
// it suffers from flickering during updates, so let it paint into a back buffer
|
||||||
|
::CallWindowProc(_tabBarDefaultProc, hwnd, WM_ERASEBKGND, reinterpret_cast<WPARAM>(hdc), 0);
|
||||||
|
::CallWindowProc(_tabBarDefaultProc, hwnd, WM_PRINTCLIENT, reinterpret_cast<WPARAM>(hdc), PRF_NONCLIENT | PRF_CLIENT);
|
||||||
|
_dblBuf.endPaint(hwnd, &ps);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool hasMultipleLines = ((dwStyle & TCS_BUTTONS) == TCS_BUTTONS);
|
const bool hasMultipleLines = ((dwStyle & TCS_BUTTONS) == TCS_BUTTONS);
|
||||||
|
|
||||||
PAINTSTRUCT ps{};
|
|
||||||
HDC hdc = BeginPaint(hwnd, &ps);
|
|
||||||
FillRect(hdc, &ps.rcPaint, NppDarkMode::getDarkerBackgroundBrush());
|
FillRect(hdc, &ps.rcPaint, NppDarkMode::getDarkerBackgroundBrush());
|
||||||
|
|
||||||
UINT id = ::GetDlgCtrlID(hwnd);
|
UINT id = ::GetDlgCtrlID(hwnd);
|
||||||
|
@ -1072,7 +1066,7 @@ LRESULT TabBarPlus::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lPara
|
||||||
|
|
||||||
SelectObject(hdc, holdPen);
|
SelectObject(hdc, holdPen);
|
||||||
|
|
||||||
EndPaint(hwnd, &ps);
|
_dblBuf.endPaint(hwnd, &ps);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include <commctrl.h>
|
#include <commctrl.h>
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
#include "dpiManagerV2.h"
|
#include "dpiManagerV2.h"
|
||||||
|
#include "DoubleBuffer.h"
|
||||||
|
|
||||||
//Notification message
|
//Notification message
|
||||||
#define TCN_TABDROPPED (TCN_FIRST - 10)
|
#define TCN_TABDROPPED (TCN_FIRST - 10)
|
||||||
|
@ -241,6 +242,7 @@ protected:
|
||||||
int _previousTabSwapped = -1;
|
int _previousTabSwapped = -1;
|
||||||
POINT _draggingPoint{}; // coordinate of Screen
|
POINT _draggingPoint{}; // coordinate of Screen
|
||||||
WNDPROC _tabBarDefaultProc = nullptr;
|
WNDPROC _tabBarDefaultProc = nullptr;
|
||||||
|
DoubleBuffer _dblBuf;
|
||||||
|
|
||||||
RECT _currentHoverTabRect{};
|
RECT _currentHoverTabRect{};
|
||||||
int _currentHoverTabItem = -1; // -1 : no mouse on any tab
|
int _currentHoverTabItem = -1; // -1 : no mouse on any tab
|
||||||
|
|
|
@ -298,6 +298,7 @@
|
||||||
<ClInclude Include="..\src\WinControls\DockingWnd\DockingSplitter.h" />
|
<ClInclude Include="..\src\WinControls\DockingWnd\DockingSplitter.h" />
|
||||||
<ClInclude Include="..\src\ScintillaComponent\DocTabView.h" />
|
<ClInclude Include="..\src\ScintillaComponent\DocTabView.h" />
|
||||||
<ClInclude Include="..\src\WinControls\DocumentMap\documentMap.h" />
|
<ClInclude Include="..\src\WinControls\DocumentMap\documentMap.h" />
|
||||||
|
<ClInclude Include="..\src\WinControls\DoubleBuffer.h" />
|
||||||
<ClInclude Include="..\src\EncodingMapper.h" />
|
<ClInclude Include="..\src\EncodingMapper.h" />
|
||||||
<ClInclude Include="..\src\MISC\FileNameStringSplitter.h" />
|
<ClInclude Include="..\src\MISC\FileNameStringSplitter.h" />
|
||||||
<ClInclude Include="..\src\WinControls\FindCharsInRange\FindCharsInRange.h" />
|
<ClInclude Include="..\src\WinControls\FindCharsInRange\FindCharsInRange.h" />
|
||||||
|
|
Loading…
Reference in New Issue