소스 검색

Add modern scrollbars with theme support

New modern scrollbars (vertical and horizontal) control with rounded corners and themeable colors.
Schmurtz 2 주 전
부모
커밋
118e7107bf
13개의 변경된 파일875개의 추가작업 그리고 17개의 파일을 삭제
  1. 2 0
      CP_Main.vcxproj
  2. 9 0
      src/AdvGeneral.cpp
  3. 583 0
      src/ModernScrollBar.cpp
  4. 99 0
      src/ModernScrollBar.h
  5. 13 0
      src/Options.cpp
  6. 4 0
      src/Options.h
  7. 56 11
      src/QListCtrl.cpp
  8. 1 0
      src/QListCtrl.h
  9. 75 4
      src/QPasteWnd.cpp
  10. 9 2
      src/QPasteWnd.h
  11. 4 0
      src/QuickPaste.cpp
  12. 10 0
      src/Theme.cpp
  13. 10 0
      src/Theme.h

+ 2 - 0
CP_Main.vcxproj

@@ -608,6 +608,7 @@
     <ClCompile Include="src\Md5.cpp" />
     <ClCompile Include="src\MessagePumpThread.cpp" />
     <ClCompile Include="src\Misc.cpp" />
+    <ClCompile Include="src\ModernScrollBar.cpp" />
     <ClCompile Include="src\MoveToGroupDlg.cpp" />
     <ClCompile Include="src\MultiLanguage.cpp" />
     <ClCompile Include="src\MyDropTarget.cpp" />
@@ -875,6 +876,7 @@
     <ClInclude Include="src\memdc.h" />
     <ClInclude Include="src\MessagePumpThread.h" />
     <ClInclude Include="src\Misc.h" />
+    <ClInclude Include="src\ModernScrollBar.h" />
     <ClInclude Include="src\MoveToGroupDlg.h" />
     <ClInclude Include="src\MultiLanguage.h" />
     <ClInclude Include="src\MyDropTarget.h" />

+ 9 - 0
src/AdvGeneral.cpp

@@ -161,6 +161,7 @@ END_MESSAGE_MAP()
 #define SETTING_WEB_SEARCH_URL 107
 #define SETTING_DO_NOT_HIDE_ON_DEACTIVATE 108
 #define SETTING_HIDE_TASKBAR_ICON_ON_CLOSE 109
+#define SETTING_USE_MODERN_SCROLLBAR 110
 
 BOOL CAdvGeneral::OnInitDialog()
 {
@@ -200,6 +201,7 @@ BOOL CAdvGeneral::OnInitDialog()
 	AddTrueFalse(pGroupTest, _T("Allow back to back duplicates (if allowing duplicates)"), CGetSetOptions::GetAllowBackToBackDuplicates(), SETTING_ALOW_BACK_TO_BACK_DUPLICATES);
 
 	AddTrueFalse(pGroupTest, _T("Always show scroll bar"), CGetSetOptions::GetShowScrollBar(), SETTING_ALWAYS_SHOW_SCROLL_BAR);
+	AddTrueFalse(pGroupTest, _T("Use modern scroll bar"), CGetSetOptions::GetUseModernScrollBar(), SETTING_USE_MODERN_SCROLLBAR);
 	AddTrueFalse(pGroupTest, _T("Append Computer Name and IP when receiving clips"), CGetSetOptions::GetAppendRemoveComputerNameAndIPToDescription(), SETTING_APPEND_NAME_IP);
 
 	pGroupTest->AddSubItem(new CMFCPropertyGridProperty(_T("Amount of text to save for description"), CGetSetOptions::m_bDescTextSize, _T(""), SETTING_DESC_SIZE));
@@ -576,6 +578,13 @@ void CAdvGeneral::OnBnClickedOk()
 					CGetSetOptions::SetShowScrollBar(val);
 				}
 				break;
+			case SETTING_USE_MODERN_SCROLLBAR:
+				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
+				{
+					BOOL val = wcscmp(pNewValue->bstrVal, L"True") == 0;
+					CGetSetOptions::SetUseModernScrollBar(val);
+				}
+				break;
 			case SETTING_PASTE_AS_ADMIN:
 				if (wcscmp(pNewValue->bstrVal, pOrigValue->bstrVal) != 0)
 				{

+ 583 - 0
src/ModernScrollBar.cpp

@@ -0,0 +1,583 @@
+#include "stdafx.h"
+#include "ModernScrollBar.h"
+#include "CP_Main.h"
+#include "QListCtrl.h"
+#include <gdiplus.h>
+
+#pragma comment(lib, "gdiplus.lib")
+
+using namespace Gdiplus;
+
+IMPLEMENT_DYNAMIC(CModernScrollBar, CWnd)
+
+CModernScrollBar::CModernScrollBar()
+	: m_pListCtrl(NULL)
+	, m_pParentWnd(NULL)
+	, m_pDPI(NULL)
+	, m_orientation(ScrollBarOrientation::Vertical)
+	, m_trackColor(RGB(240, 240, 240))
+	, m_thumbColor(RGB(180, 180, 180))
+	, m_thumbHoverColor(RGB(140, 140, 140))
+	, m_scrollBarWidth(8)
+	, m_scrollBarHoverWidth(12)
+	, m_cornerRadius(4)
+	, m_minThumbSize(30)
+	, m_isMouseOver(false)
+	, m_isDragging(false)
+	, m_dragStartPos(0)
+	, m_dragStartScrollPos(0)
+	, m_isVisible(false)
+	, m_trackingMouse(false)
+{
+}
+
+CModernScrollBar::~CModernScrollBar()
+{
+}
+
+BEGIN_MESSAGE_MAP(CModernScrollBar, CWnd)
+	ON_WM_PAINT()
+	ON_WM_ERASEBKGND()
+	ON_WM_MOUSEMOVE()
+	ON_WM_MOUSELEAVE()
+	ON_WM_LBUTTONDOWN()
+	ON_WM_LBUTTONUP()
+	ON_WM_TIMER()
+	ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
+END_MESSAGE_MAP()
+
+BOOL CModernScrollBar::Create(CWnd* pParentWnd, CListCtrl* pListCtrl, ScrollBarOrientation orientation)
+{
+	m_pParentWnd = pParentWnd;
+	m_pListCtrl = pListCtrl;
+	m_orientation = orientation;
+
+	// Create as a child window with no border
+	CString className = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW, 
+		::LoadCursor(NULL, IDC_ARROW), NULL, NULL);
+	
+	DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
+	DWORD dwExStyle = 0;
+
+	CRect rect(0, 0, 10, 100);
+	
+	if (!CWnd::CreateEx(dwExStyle, className, _T(""), dwStyle, rect, pParentWnd, 0))
+		return FALSE;
+
+	return TRUE;
+}
+
+void CModernScrollBar::SetColors(COLORREF trackColor, COLORREF thumbColor, COLORREF thumbHoverColor)
+{
+	m_trackColor = trackColor;
+	m_thumbColor = thumbColor;
+	m_thumbHoverColor = thumbHoverColor;
+	
+	if (m_hWnd && IsWindowVisible())
+		Invalidate();
+}
+
+void CModernScrollBar::UpdateScrollBar()
+{
+	if (!m_pListCtrl || !m_pListCtrl->m_hWnd)
+		return;
+
+	// Get scroll info for the appropriate orientation
+	int scrollBarType = (m_orientation == ScrollBarOrientation::Vertical) ? SB_VERT : SB_HORZ;
+	
+	SCROLLINFO si = { sizeof(SCROLLINFO) };
+	si.fMask = SIF_ALL;
+	m_pListCtrl->GetScrollInfo(scrollBarType, &si);
+
+	// Check if scrollbar is needed
+	bool needsScrollBar = (si.nMax > 0 && si.nPage > 0 && (int)si.nPage <= si.nMax);
+	
+	if (!needsScrollBar)
+	{
+		if (IsWindowVisible())
+			ShowWindow(SW_HIDE);
+		return;
+	}
+
+	// Get the list control position relative to parent
+	CRect listRectInParent;
+	m_pListCtrl->GetWindowRect(&listRectInParent);
+	m_pParentWnd->ScreenToClient(&listRectInParent);
+
+	// Get parent client rect to ensure we stay within visible area
+	CRect parentClientRect;
+	m_pParentWnd->GetClientRect(&parentClientRect);
+
+	// Calculate scrollbar size based on DPI and hover state
+	int scrollSize = (m_isMouseOver || m_isDragging) ? m_scrollBarHoverWidth : m_scrollBarWidth;
+	if (m_pDPI)
+	{
+		scrollSize = m_pDPI->Scale(scrollSize);
+	}
+
+	// The list control is clipped by a region to hide the native scrollbar.
+	// searchRowStart is 33 (the height reserved for search bar and options button).
+	int searchRowStart = 33;
+	if (m_pDPI)
+		searchRowStart = m_pDPI->Scale(33);
+	
+	int visibleBottom = parentClientRect.bottom - searchRowStart;
+
+	CRect scrollRect;
+	
+	if (m_orientation == ScrollBarOrientation::Vertical)
+	{
+		// Position the scrollbar on the right side
+		int rightEdge = parentClientRect.right;
+		if (listRectInParent.right < rightEdge)
+			rightEdge = listRectInParent.right;
+
+		scrollRect.left = rightEdge - scrollSize;
+		scrollRect.top = listRectInParent.top;
+		scrollRect.right = rightEdge;
+		scrollRect.bottom = visibleBottom;
+	}
+	else
+	{
+		// Position the scrollbar on the bottom
+		// Leave space for the vertical scrollbar on the right
+		int vertScrollWidth = m_scrollBarHoverWidth;  // Use hover width to ensure no overlap
+		if (m_pDPI)
+			vertScrollWidth = m_pDPI->Scale(vertScrollWidth);
+		
+		scrollRect.left = listRectInParent.left;
+		scrollRect.top = visibleBottom - scrollSize;
+		scrollRect.right = parentClientRect.right - vertScrollWidth;  // Stop before vertical scrollbar
+		scrollRect.bottom = visibleBottom;
+	}
+
+	// Only move if position changed
+	CRect currentRect;
+	GetWindowRect(&currentRect);
+	m_pParentWnd->ScreenToClient(&currentRect);
+
+	if (currentRect != scrollRect)
+	{
+		MoveWindow(&scrollRect);
+	}
+	
+	// Ensure visible
+	if (!IsWindowVisible())
+		ShowWindow(SW_SHOWNA);
+
+	// Redraw
+	Invalidate();
+	
+	// Bring to front
+	SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+}
+
+CRect CModernScrollBar::GetThumbRect()
+{
+	CRect thumbRect(0, 0, 0, 0);
+	
+	if (!m_pListCtrl || !m_pListCtrl->m_hWnd)
+		return thumbRect;
+
+	CRect clientRect;
+	GetClientRect(&clientRect);
+
+	// Get scroll info for appropriate orientation
+	int scrollBarType = (m_orientation == ScrollBarOrientation::Vertical) ? SB_VERT : SB_HORZ;
+	
+	SCROLLINFO si = { sizeof(SCROLLINFO) };
+	si.fMask = SIF_ALL;
+	m_pListCtrl->GetScrollInfo(scrollBarType, &si);
+
+	if (si.nMax <= 0 || si.nPage <= 0)
+		return thumbRect;
+
+	int trackSize = (m_orientation == ScrollBarOrientation::Vertical) ? clientRect.Height() : clientRect.Width();
+	int totalRange = si.nMax - si.nMin + 1;
+	
+	// Calculate thumb size proportionally
+	int thumbSize = (int)((double)si.nPage / totalRange * trackSize);
+	
+	// Apply minimum thumb size
+	int minSize = m_minThumbSize;
+	if (m_pDPI)
+		minSize = m_pDPI->Scale(m_minThumbSize);
+	
+	if (thumbSize < minSize)
+		thumbSize = minSize;
+	
+	if (thumbSize > trackSize)
+		thumbSize = trackSize;
+
+	// Calculate thumb position
+	int scrollableRange = totalRange - si.nPage;
+	double scrollRatio = 0;
+	if (scrollableRange > 0)
+		scrollRatio = (double)si.nPos / scrollableRange;
+	
+	int thumbPos = (int)(scrollRatio * (trackSize - thumbSize));
+	
+	// Clamp to valid range
+	if (thumbPos < 0) thumbPos = 0;
+	if (thumbPos + thumbSize > trackSize)
+		thumbPos = trackSize - thumbSize;
+
+	if (m_orientation == ScrollBarOrientation::Vertical)
+	{
+		thumbRect.left = -1;
+		thumbRect.right = clientRect.Width();
+		thumbRect.top = thumbPos;
+		thumbRect.bottom = thumbPos + thumbSize;
+	}
+	else
+	{
+		thumbRect.left = thumbPos;
+		thumbRect.right = thumbPos + thumbSize;
+		thumbRect.top = -1;
+		thumbRect.bottom = clientRect.Height();
+	}
+
+	return thumbRect;
+}
+
+void CModernScrollBar::DrawRoundedRect(CDC* pDC, CRect rect, int radius, COLORREF color)
+{
+	// Use GDI+ for anti-aliased rounded rectangles
+	Graphics graphics(pDC->GetSafeHdc());
+	graphics.SetSmoothingMode(SmoothingModeAntiAlias);
+	
+	// Create solid color (no transparency)
+	Color gdipColor(255, GetRValue(color), GetGValue(color), GetBValue(color));
+	SolidBrush brush(gdipColor);
+	
+	// Draw rounded rectangle path
+	GraphicsPath path;
+	
+	int diameter = radius * 2;
+	
+	// Handle very small rectangles
+	if (rect.Width() < diameter || rect.Height() < diameter)
+	{
+		// Just draw an ellipse or simple rect
+		if (rect.Width() <= 0 || rect.Height() <= 0)
+			return;
+		path.AddEllipse(rect.left, rect.top, rect.Width(), rect.Height());
+	}
+	else
+	{
+		// Top-left corner
+		path.AddArc(rect.left, rect.top, diameter, diameter, 180, 90);
+		// Top-right corner
+		path.AddArc(rect.right - diameter, rect.top, diameter, diameter, 270, 90);
+		// Bottom-right corner
+		path.AddArc(rect.right - diameter, rect.bottom - diameter, diameter, diameter, 0, 90);
+		// Bottom-left corner
+		path.AddArc(rect.left, rect.bottom - diameter, diameter, diameter, 90, 90);
+		path.CloseFigure();
+	}
+	
+	graphics.FillPath(&brush, &path);
+}
+
+void CModernScrollBar::OnPaint()
+{
+	CPaintDC dc(this);
+	
+	CRect clientRect;
+	GetClientRect(&clientRect);
+	
+	// Create memory DC for double buffering
+	CDC memDC;
+	memDC.CreateCompatibleDC(&dc);
+	
+	CBitmap memBitmap;
+	memBitmap.CreateCompatibleBitmap(&dc, clientRect.Width(), clientRect.Height());
+	CBitmap* pOldBitmap = memDC.SelectObject(&memBitmap);
+	
+	// Fill with track color (solid background)
+	memDC.FillSolidRect(&clientRect, m_trackColor);
+	
+	// Get thumb rect
+	CRect thumbRect = GetThumbRect();
+	
+	if (!thumbRect.IsRectEmpty())
+	{
+		int radius = m_cornerRadius;
+		if (m_pDPI)
+			radius = m_pDPI->Scale(m_cornerRadius);
+		
+		// Determine thumb color based on state
+		COLORREF thumbColor = m_isMouseOver || m_isDragging ? m_thumbHoverColor : m_thumbColor;
+		
+		// Draw the thumb
+		DrawRoundedRect(&memDC, thumbRect, radius, thumbColor);
+	}
+	
+	// Copy to screen
+	dc.BitBlt(0, 0, clientRect.Width(), clientRect.Height(), &memDC, 0, 0, SRCCOPY);
+	
+	memDC.SelectObject(pOldBitmap);
+}
+
+BOOL CModernScrollBar::OnEraseBkgnd(CDC* pDC)
+{
+	// Don't erase - we handle it in OnPaint
+	return TRUE;
+}
+
+void CModernScrollBar::OnMouseMove(UINT nFlags, CPoint point)
+{
+	if (!m_trackingMouse)
+	{
+		TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT) };
+		tme.dwFlags = TME_LEAVE | TME_HOVER;
+		tme.hwndTrack = m_hWnd;
+		tme.dwHoverTime = 1; // Immediate hover detection
+		TrackMouseEvent(&tme);
+		m_trackingMouse = true;
+	}
+
+	CRect clientRect;
+	GetClientRect(&clientRect);
+	bool wasMouseOver = m_isMouseOver;
+	m_isMouseOver = clientRect.PtInRect(point);
+	
+	if (wasMouseOver != m_isMouseOver)
+	{
+		// Update scrollbar size when hover state changes
+		UpdateScrollBar();
+	}
+
+	if (m_isDragging && m_pListCtrl)
+	{
+		if (m_orientation == ScrollBarOrientation::Vertical)
+		{
+			int deltaY = point.y - m_dragStartPos;
+			ScrollToPosition(m_dragStartScrollPos + deltaY);
+		}
+		else
+		{
+			int deltaX = point.x - m_dragStartPos;
+			ScrollToPosition(m_dragStartScrollPos + deltaX);
+		}
+	}
+
+	CWnd::OnMouseMove(nFlags, point);
+}
+
+void CModernScrollBar::OnMouseLeave()
+{
+	m_trackingMouse = false;
+	
+	if (m_isMouseOver)
+	{
+		m_isMouseOver = false;
+		// Update scrollbar size when hover state changes (shrink back)
+		UpdateScrollBar();
+
+		// If we leave the scrollbar area and we are in auto-hide mode, 
+		// restart the timer to hide it eventually
+		if (m_isVisible && !CGetSetOptions::m_showScrollBar && !m_isDragging)
+		{
+			SetTimer(TIMER_AUTO_HIDE, 800, NULL);
+		}
+	}
+
+	CWnd::OnMouseLeave();
+}
+
+LRESULT CModernScrollBar::OnMouseHover(WPARAM wParam, LPARAM lParam)
+{
+	return 0;
+}
+
+void CModernScrollBar::OnLButtonDown(UINT nFlags, CPoint point)
+{
+	CRect thumbRect = GetThumbRect();
+	
+	if (thumbRect.PtInRect(point))
+	{
+		// Start dragging the thumb
+		m_isDragging = true;
+		m_dragStartPos = (m_orientation == ScrollBarOrientation::Vertical) ? point.y : point.x;
+		
+		// Get current scroll position in thumb coordinates
+		m_dragStartScrollPos = (m_orientation == ScrollBarOrientation::Vertical) ? thumbRect.top : thumbRect.left;
+		
+		SetCapture();
+		Invalidate();
+	}
+	else
+	{
+		// Click on track - page scroll
+		if (m_orientation == ScrollBarOrientation::Vertical)
+		{
+			if (point.y < thumbRect.top)
+			{
+				m_pListCtrl->SendMessage(WM_VSCROLL, SB_PAGEUP, 0);
+			}
+			else if (point.y > thumbRect.bottom)
+			{
+				m_pListCtrl->SendMessage(WM_VSCROLL, SB_PAGEDOWN, 0);
+			}
+		}
+		else
+		{
+			// For horizontal, use Scroll with page width
+			CRect clientRect;
+			m_pListCtrl->GetClientRect(&clientRect);
+			int pageWidth = clientRect.Width();
+			
+			if (point.x < thumbRect.left)
+			{
+				m_pListCtrl->Scroll(CSize(-pageWidth, 0));
+			}
+			else if (point.x > thumbRect.right)
+			{
+				m_pListCtrl->Scroll(CSize(pageWidth, 0));
+			}
+		}
+		
+		UpdateScrollBar();
+	}
+
+	CWnd::OnLButtonDown(nFlags, point);
+}
+
+void CModernScrollBar::OnLButtonUp(UINT nFlags, CPoint point)
+{
+	if (m_isDragging)
+	{
+		m_isDragging = false;
+		ReleaseCapture();
+		Invalidate();
+	}
+
+	CWnd::OnLButtonUp(nFlags, point);
+}
+
+void CModernScrollBar::ScrollToPosition(int thumbPos)
+{
+	if (!m_pListCtrl)
+		return;
+
+	CRect clientRect;
+	GetClientRect(&clientRect);
+
+	int scrollBarType = (m_orientation == ScrollBarOrientation::Vertical) ? SB_VERT : SB_HORZ;
+	
+	SCROLLINFO si = { sizeof(SCROLLINFO) };
+	si.fMask = SIF_ALL;
+	m_pListCtrl->GetScrollInfo(scrollBarType, &si);
+
+	if (si.nMax <= 0 || si.nPage <= 0)
+		return;
+
+	int trackSize = (m_orientation == ScrollBarOrientation::Vertical) ? clientRect.Height() : clientRect.Width();
+	int totalRange = si.nMax - si.nMin + 1;
+	
+	// Calculate thumb size
+	int thumbSize = (int)((double)si.nPage / totalRange * trackSize);
+	int minSize = m_minThumbSize;
+	if (m_pDPI)
+		minSize = m_pDPI->Scale(m_minThumbSize);
+	if (thumbSize < minSize)
+		thumbSize = minSize;
+
+	// Clamp thumb position
+	if (thumbPos < 0) thumbPos = 0;
+	if (thumbPos > trackSize - thumbSize)
+		thumbPos = trackSize - thumbSize;
+
+	// Calculate new scroll position
+	int scrollableTrack = trackSize - thumbSize;
+	int scrollableRange = totalRange - si.nPage;
+	
+	int newPos = 0;
+	if (scrollableTrack > 0)
+		newPos = (int)((double)thumbPos / scrollableTrack * scrollableRange);
+
+	if (m_orientation == ScrollBarOrientation::Vertical)
+	{
+		// Use Scroll method for smoother scrolling with virtual lists
+		CQListCtrl* pQListCtrl = (CQListCtrl*)m_pListCtrl;
+		int rowHeight = pQListCtrl->GetRowHeight();
+		int currentTop = m_pListCtrl->GetTopIndex();
+		
+		if (rowHeight > 0)
+		{
+			int deltaRows = newPos - currentTop;
+			int deltaPixels = deltaRows * rowHeight;
+			
+			if (deltaPixels != 0)
+			{
+				m_pListCtrl->Scroll(CSize(0, deltaPixels));
+			}
+		}
+		else
+		{
+			// Fallback if row height is not available
+			si.fMask = SIF_POS;
+			si.nPos = newPos;
+			m_pListCtrl->SetScrollInfo(SB_VERT, &si);
+			m_pListCtrl->SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBPOSITION, newPos), 0);
+		}
+	}
+	else
+	{
+		// Horizontal scrolling - use Scroll method for more reliable scrolling
+		int currentScrollPos = m_pListCtrl->GetScrollPos(SB_HORZ);
+		int deltaPixels = newPos - currentScrollPos;
+		
+		if (deltaPixels != 0)
+		{
+			m_pListCtrl->Scroll(CSize(deltaPixels, 0));
+		}
+	}
+	
+	Invalidate();
+}
+
+void CModernScrollBar::Show(bool animate)
+{
+	m_isVisible = true;
+	ShowWindow(SW_SHOWNA);
+	
+	// Kill any pending hide timer
+	KillTimer(TIMER_AUTO_HIDE);
+	
+	// Start auto-hide timer (hide after 800ms of inactivity)
+	// Only if the option to always show scrollbar is NOT enabled
+	if (!CGetSetOptions::m_showScrollBar)
+	{
+		SetTimer(TIMER_AUTO_HIDE, 800, NULL);
+	}
+	
+	UpdateScrollBar();
+}
+
+void CModernScrollBar::Hide(bool animate)
+{
+	m_isVisible = false;
+	ShowWindow(SW_HIDE);
+	KillTimer(TIMER_AUTO_HIDE);
+}
+
+void CModernScrollBar::OnTimer(UINT_PTR nIDEvent)
+{
+	if (nIDEvent == TIMER_AUTO_HIDE)
+	{
+		// Don't hide if mouse is over or dragging
+		if (!m_isMouseOver && !m_isDragging)
+		{
+			// Don't auto-hide if the option to always show scrollbar is enabled
+			if (!CGetSetOptions::m_showScrollBar)
+			{
+				Hide(true);
+			}
+		}
+		KillTimer(TIMER_AUTO_HIDE);
+	}
+
+	CWnd::OnTimer(nIDEvent);
+}

+ 99 - 0
src/ModernScrollBar.h

@@ -0,0 +1,99 @@
+#pragma once
+
+#include <afxwin.h>
+#include "DPI.h"
+
+// Scrollbar orientation
+enum class ScrollBarOrientation
+{
+	Vertical,
+	Horizontal
+};
+
+// Modern scrollbar overlay control with rounded corners
+// Similar to Discord, Teams, GitHub Desktop style
+class CModernScrollBar : public CWnd
+{
+	DECLARE_DYNAMIC(CModernScrollBar)
+
+public:
+	CModernScrollBar();
+	virtual ~CModernScrollBar();
+
+	// Create the scrollbar overlay
+	BOOL Create(CWnd* pParentWnd, CListCtrl* pListCtrl, ScrollBarOrientation orientation = ScrollBarOrientation::Vertical);
+	
+	// Update scrollbar position and visibility based on list state
+	void UpdateScrollBar();
+	
+	// Set scrollbar colors from theme
+	void SetColors(COLORREF trackColor, COLORREF thumbColor, COLORREF thumbHoverColor);
+	
+	// Set rounded corner radius
+	void SetCornerRadius(int radius) { m_cornerRadius = radius; }
+	
+	// Set scrollbar width/height (depending on orientation)
+	void SetWidth(int width) { m_scrollBarWidth = width; }
+	
+	// Show/hide with fade animation
+	void Show(bool animate = true);
+	void Hide(bool animate = true);
+	
+	// Check if mouse is over scrollbar
+	bool IsMouseOver() const { return m_isMouseOver; }
+	
+	// DPI awareness
+	void SetDPI(CDPI* pDPI) { m_pDPI = pDPI; }
+	
+	// Get orientation
+	ScrollBarOrientation GetOrientation() const { return m_orientation; }
+
+protected:
+	DECLARE_MESSAGE_MAP()
+	
+	afx_msg void OnPaint();
+	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
+	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
+	afx_msg void OnMouseLeave();
+	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
+	afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
+	afx_msg void OnTimer(UINT_PTR nIDEvent);
+	afx_msg LRESULT OnMouseHover(WPARAM wParam, LPARAM lParam);
+
+	// Calculate thumb rectangle based on list scroll position
+	CRect GetThumbRect();
+	
+	// Draw rounded rectangle with GDI+
+	void DrawRoundedRect(CDC* pDC, CRect rect, int radius, COLORREF color);
+	
+	// Scroll list to position based on thumb drag
+	void ScrollToPosition(int thumbPos);
+
+private:
+	CListCtrl* m_pListCtrl;
+	CWnd* m_pParentWnd;
+	CDPI* m_pDPI;
+	ScrollBarOrientation m_orientation;
+	
+	// Colors
+	COLORREF m_trackColor;
+	COLORREF m_thumbColor;
+	COLORREF m_thumbHoverColor;
+	
+	// Dimensions
+	int m_scrollBarWidth;
+	int m_scrollBarHoverWidth;
+	int m_cornerRadius;
+	int m_minThumbSize;  // Min thumb width or height depending on orientation
+	
+	// State
+	bool m_isMouseOver;
+	bool m_isDragging;
+	int m_dragStartPos;  // X or Y depending on orientation
+	int m_dragStartScrollPos;
+	bool m_isVisible;
+	bool m_trackingMouse;
+	
+	// Timer
+	enum { TIMER_AUTO_HIDE = 1 };
+};

+ 13 - 0
src/Options.cpp

@@ -72,6 +72,7 @@ CString CGetSetOptions::m_csIniFileName;
 __int64 CGetSetOptions::nLastDbWriteTime = 0;
 CTheme CGetSetOptions::m_Theme;
 BOOL CGetSetOptions::m_showScrollBar = false;
+BOOL CGetSetOptions::m_useModernScrollBar = TRUE;
 BOOL CGetSetOptions::m_bShowAlwaysOnTopWarning = TRUE;
 CRegExFilterHelper CGetSetOptions::m_regexHelper;
 CString CGetSetOptions::m_ignoreAnnoyingCFDIB = "";
@@ -287,6 +288,7 @@ void CGetSetOptions::LoadSettings()
 	m_outputDebugStringLogging = GetEnableOutputDebugStringLogging();
 	m_bEnsureConnectToClipboard = GetEnsureConnectToClipboard();
 	m_showScrollBar = GetShowScrollBar();
+	m_useModernScrollBar = GetUseModernScrollBar();
 	m_bShowAlwaysOnTopWarning = GetShowAlwaysOnTopWarning();
 	m_ignoreAnnoyingCFDIB = GetIgnoreAnnoyingCFDIB();
 	m_doubleKeyStrokeTimeout = GetDoubleKeyStrokeTimeout();
@@ -2299,6 +2301,17 @@ BOOL CGetSetOptions::GetShowScrollBar()
 	return GetProfileLong(_T("ShowScrollBar"), 0);
 }
 
+void CGetSetOptions::SetUseModernScrollBar(BOOL val)
+{
+	m_useModernScrollBar = val;
+	SetProfileLong(_T("UseModernScrollBar"), val);
+}
+
+BOOL CGetSetOptions::GetUseModernScrollBar()
+{
+	return GetProfileLong(_T("UseModernScrollBar"), TRUE);
+}
+
 void CGetSetOptions::SetPasteAsAdmin(BOOL val)
 {
 	SetProfileLong(_T("PasteAsAdmin"), val);

+ 4 - 0
src/Options.h

@@ -464,6 +464,10 @@ public:
 	static BOOL		GetShowScrollBar();
 	static BOOL		m_showScrollBar;
 
+	static void		SetUseModernScrollBar(BOOL val);
+	static BOOL		GetUseModernScrollBar();
+	static BOOL		m_useModernScrollBar;
+
 	static void		SetPasteAsAdmin(BOOL val);
 	static BOOL		GetPasteAsAdmin();
 

+ 56 - 11
src/QListCtrl.cpp

@@ -1410,7 +1410,16 @@ BOOL CQListCtrl::PreTranslateMessage(MSG* pMsg)
 			return TRUE;
 		break; // end case WM_KEYDOWN
 	case WM_MOUSEWHEEL:
-		break;
+		// Will be handled by default, but ensure scrollbar updates after
+		{
+			BOOL result = CListCtrl::PreTranslateMessage(pMsg);
+			CWnd* pParent = GetParent();
+			if (pParent && pParent->GetSafeHwnd())
+			{
+				pParent->PostMessage(NM_UPDATE_SCROLLBAR, 0, 0);
+			}
+			return result;
+		}
 
 	case WM_VSCROLL:
 		ASSERT(FALSE);
@@ -1925,6 +1934,13 @@ void CQListCtrl::OnSelectionChange(NMHDR* pNMHDR, LRESULT* pResult)
 	if ((pnmv->uNewState == 3) ||
 		(pnmv->uNewState == 1))
 	{
+		// Notify parent to update modern scrollbar when selection changes (keyboard navigation)
+		CWnd* pParent = GetParent();
+		if (pParent && pParent->GetSafeHwnd())
+		{
+			pParent->PostMessage(NM_UPDATE_SCROLLBAR, 0, 0);
+		}
+
 		if (VALID_TOOLTIP &&
 			::IsWindowVisible(m_pToolTip->m_hWnd))
 		{
@@ -2045,6 +2061,13 @@ void CQListCtrl::SetLogFont(LOGFONT& font)
 void CQListCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
 {
 	CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
+	
+	// Notify parent to update modern scrollbar
+	CWnd* pParent = GetParent();
+	if (pParent && pParent->GetSafeHwnd())
+	{
+		pParent->PostMessage(NM_UPDATE_SCROLLBAR, 0, 0);
+	}
 }
 
 BOOL CQListCtrl::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult)
@@ -2079,27 +2102,37 @@ void CQListCtrl::OnMouseMove(UINT nFlags, CPoint point)
 {
 	if (CGetSetOptions::m_showScrollBar == FALSE)
 	{
-		CPoint cursorPos;
-		GetCursorPos(&cursorPos);
-
 		CRect crWindow;
 		this->GetWindowRect(&crWindow);
 		ScreenToClient(&crWindow);
 
-		crWindow.right -= m_windowDpi->Scale(::GetSystemMetrics(SM_CXVSCROLL));
-		crWindow.bottom -= m_windowDpi->Scale(::GetSystemMetrics(SM_CXHSCROLL));
+		// Don't subtract scrollbar size - detect in the full window area
+		// This prevents flickering when scrollbar appears/disappears
 
 		if (MouseInScrollBarArea(crWindow, point))
 		{
-			if ((GetTickCount() - m_mouseOverScrollAreaStart) > 500)
+			// Show scrollbar immediately when mouse enters scrollbar area
+			if (m_mouseOverScrollAreaStart == 0)
 			{
-				SetTimer(TIMER_SHOW_SCROLL, 500, NULL);
-
 				m_mouseOverScrollAreaStart = GetTickCount();
+				
+				// For modern scrollbar, notify parent
+				if (CGetSetOptions::m_useModernScrollBar)
+				{
+					GetParent()->PostMessage(NM_UPDATE_SCROLLBAR, 0, 0);
+				}
+				else
+				{
+					// For native scrollbar, show immediately and start hide timer
+					m_timerToHideScrollAreaSet = true;
+					GetParent()->SendMessage(NM_SHOW_HIDE_SCROLLBARS, 1, 0);
+					SetTimer(TIMER_HIDE_SCROL, 1000, NULL);
+				}
 			}
 		}
 		else
 		{
+			m_mouseOverScrollAreaStart = 0;
 			if (m_timerToHideScrollAreaSet)
 			{
 				StopHideScrollBarTimer();
@@ -2113,11 +2146,16 @@ void CQListCtrl::OnMouseMove(UINT nFlags, CPoint point)
 
 bool CQListCtrl::MouseInScrollBarArea(CRect crWindow, CPoint point)
 {
+	int scrollBarWidth = m_windowDpi->Scale(::GetSystemMetrics(SM_CXVSCROLL));
+	int scrollBarHeight = m_windowDpi->Scale(::GetSystemMetrics(SM_CYHSCROLL));
+	int extraMargin = m_windowDpi->Scale(6); // Small extra margin for easier detection
+
 	CRect crRight(crWindow);
 	CRect crBottom(crWindow);
 
-	crRight.left = crRight.right - m_windowDpi->Scale(::GetSystemMetrics(SM_CXVSCROLL));
-	crBottom.top = crBottom.bottom - m_windowDpi->Scale(::GetSystemMetrics(SM_CYHSCROLL));
+	// Detect from the right edge of the window (includes scrollbar area when visible)
+	crRight.left = crRight.right - scrollBarWidth - extraMargin;
+	crBottom.top = crBottom.bottom - scrollBarHeight - extraMargin;
 
 	/*CString cs;
 	cs.Format(_T("point.x: %d, Width: %d, Height: %d\n"), point.x, crWindow.Width(), crWindow.Height());
@@ -2285,5 +2323,12 @@ void CQListCtrl::OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)
 		this->SendMessage(WM_HSCROLL, SB_LINELEFT, NULL);
 	}
 
+	// Notify parent to update modern scrollbar
+	CWnd* pParent = GetParent();
+	if (pParent && pParent->GetSafeHwnd())
+	{
+		pParent->PostMessage(NM_UPDATE_SCROLLBAR, 0, 0);
+	}
+
 	//CListCtrl::OnMouseHWheel(nFlags, zDelta, pt);
 }

+ 1 - 0
src/QListCtrl.h

@@ -44,6 +44,7 @@
 #define NM_MOVE_TO_GROUP			WM_USER+0x128
 #define NM_FOCUS_ON_SEARCH			WM_USER+0x129
 #define NM_COPY_CLIP				WM_USER+0x130
+#define NM_UPDATE_SCROLLBAR			WM_USER+0x131
 
 
 

+ 75 - 4
src/QPasteWnd.cpp

@@ -223,6 +223,7 @@ BEGIN_MESSAGE_MAP(CQPasteWnd, CWndEx)
 	ON_COMMAND_RANGE(3000, 4000, OnAddinSelect)
 	ON_MESSAGE(NM_ALL_SELECTED, OnSelectAll)
 	ON_MESSAGE(NM_SHOW_HIDE_SCROLLBARS, OnShowHideScrollBar)
+	ON_MESSAGE(NM_UPDATE_SCROLLBAR, OnUpdateScrollBar)
 	ON_MESSAGE(NM_CANCEL_SEARCH, OnCancelFilter)
 	ON_MESSAGE(NM_POST_OPTIONS_WINDOW, OnPostOptions)
 	ON_COMMAND(ID_MENU_SEARCHDESCRIPTION, OnMenuSearchDescription)
@@ -418,7 +419,7 @@ int CQPasteWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
 	//m_search.SetButtonArea(rcCloseArea);
 
 	// Create the header control
-	if (!m_lstHeader.Create(WS_TABSTOP | WS_CHILD | WS_VISIBLE | LVS_NOCOLUMNHEADER | LVS_REPORT | LVS_SHOWSELALWAYS | LVS_OWNERDATA | LVS_OWNERDRAWFIXED, CRect(0, 0, 0, 0), this, ID_LIST_HEADER))
+	if (!m_lstHeader.Create(WS_TABSTOP | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | LVS_NOCOLUMNHEADER | LVS_REPORT | LVS_SHOWSELALWAYS | LVS_OWNERDATA | LVS_OWNERDRAWFIXED, CRect(0, 0, 0, 0), this, ID_LIST_HEADER))
 	{
 		ASSERT(FALSE);
 		return -1;
@@ -426,6 +427,24 @@ int CQPasteWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
 	m_lstHeader.SetDpiInfo(&m_DittoWindow.m_dpi);
 	m_lstHeader.ShowWindow(SW_SHOW);
 
+	// Create modern scrollbar overlay (vertical)
+	m_modernScrollBar.Create(this, &m_lstHeader, ScrollBarOrientation::Vertical);
+	m_modernScrollBar.SetDPI(&m_DittoWindow.m_dpi);
+	m_modernScrollBar.SetColors(
+		CGetSetOptions::m_Theme.ScrollBarTrack(),
+		CGetSetOptions::m_Theme.ScrollBarThumb(),
+		CGetSetOptions::m_Theme.ScrollBarThumbHover()
+	);
+
+	// Create modern scrollbar overlay (horizontal)
+	m_modernScrollBarHorz.Create(this, &m_lstHeader, ScrollBarOrientation::Horizontal);
+	m_modernScrollBarHorz.SetDPI(&m_DittoWindow.m_dpi);
+	m_modernScrollBarHorz.SetColors(
+		CGetSetOptions::m_Theme.ScrollBarTrack(),
+		CGetSetOptions::m_Theme.ScrollBarThumb(),
+		CGetSetOptions::m_Theme.ScrollBarThumbHover()
+	);
+
 	((CWnd*)&m_GroupTree)->CreateEx(NULL, _T("SysTreeView32"), NULL, TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS, CRect(0, 0, 100, 100), this, 0);
 	m_GroupTree.ModifyStyle(WS_CAPTION | WS_TABSTOP, 0);
 
@@ -675,8 +694,11 @@ void CQPasteWnd::MoveControls()
 
 	int extraSize = 0;
 
-	if (m_showScrollBars == false &&
-		CGetSetOptions::m_showScrollBar == false)
+	// Hide native scrollbar if using modern scrollbar OR if scrollbar is set to not always show
+	bool hideNativeScrollbar = CGetSetOptions::m_useModernScrollBar || 
+		(m_showScrollBars == false && CGetSetOptions::m_showScrollBar == false);
+
+	if (hideNativeScrollbar)
 	{
 		extraSize = m_DittoWindow.m_dpi.Scale(::GetSystemMetrics(SM_CXVSCROLL));
 
@@ -684,10 +706,15 @@ void CQPasteWnd::MoveControls()
 		CRect r;
 		m_lstHeader.GetWindowRect(&r);
 
-		rgnRect.CreateRectRgn(0, 0, cx, (cy - listBoxBottomOffset - topOfListBox) + 1);
+		rgnRect.CreateRectRgn(0, 0, cx, (cy - listBoxBottomOffset - topOfListBox) );
 
 		m_lstHeader.SetWindowRgn(rgnRect, TRUE);
 	}
+	else
+	{
+		// Clear region to show native scrollbar
+		m_lstHeader.SetWindowRgn(NULL, TRUE);
+	}
 
 
 	if (m_noSearchResults &&
@@ -695,6 +722,8 @@ void CQPasteWnd::MoveControls()
 	{
 		m_lstHeader.ShowWindow(SW_HIDE);
 		m_noSearchResultsStatic.ShowWindow(SW_SHOW);
+		m_modernScrollBar.ShowWindow(SW_HIDE);
+		m_modernScrollBarHorz.ShowWindow(SW_HIDE);
 
 		auto border = m_DittoWindow.m_dpi.Scale(10);
 		m_noSearchResultsStatic.MoveWindow(border, topOfListBox + border, cx - border, cy - listBoxBottomOffset - topOfListBox + 1 - border);
@@ -705,6 +734,20 @@ void CQPasteWnd::MoveControls()
 		m_noSearchResultsStatic.ShowWindow(SW_HIDE);
 
 		m_lstHeader.MoveWindow(0, topOfListBox, cx + extraSize, cy - listBoxBottomOffset - topOfListBox + extraSize + 1);
+		
+		// Update modern scrollbar position and visibility (only if enabled)
+		if (CGetSetOptions::m_useModernScrollBar)
+		{
+			m_modernScrollBar.UpdateScrollBar();
+			m_modernScrollBar.Show(false);
+			m_modernScrollBarHorz.UpdateScrollBar();
+			m_modernScrollBarHorz.Show(false);
+		}
+		else
+		{
+			m_modernScrollBar.Hide(false);
+			m_modernScrollBarHorz.Hide(false);
+		}
 	}
 	m_search.MoveWindow(m_DittoWindow.m_dpi.Scale(34), cy - m_DittoWindow.m_dpi.Scale(searchRowStart - 5), cx - m_DittoWindow.m_dpi.Scale(70), m_DittoWindow.m_dpi.Scale(25));
 
@@ -6472,6 +6515,17 @@ LRESULT CQPasteWnd::OnShowHideScrollBar(WPARAM wParam, LPARAM lParam)
 	return 1;
 }
 
+LRESULT CQPasteWnd::OnUpdateScrollBar(WPARAM wParam, LPARAM lParam)
+{
+	// Update modern scrollbar position when list scrolls (only if enabled)
+	if (CGetSetOptions::m_useModernScrollBar)
+	{
+		m_modernScrollBar.Show(false);
+		m_modernScrollBarHorz.Show(false);
+	}
+	return 0;
+}
+
 //HBRUSH CQPasteWnd::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
 //{
 //	// Call the base class implementation first! Otherwise, it may 
@@ -7741,12 +7795,29 @@ bool CQPasteWnd::DoActionGmail()
 	return true;
 }
 
+void CQPasteWnd::RefreshScrollBarColors()
+{
+	m_modernScrollBar.SetColors(
+		CGetSetOptions::m_Theme.ScrollBarTrack(),
+		CGetSetOptions::m_Theme.ScrollBarThumb(),
+		CGetSetOptions::m_Theme.ScrollBarThumbHover()
+	);
+	m_modernScrollBarHorz.SetColors(
+		CGetSetOptions::m_Theme.ScrollBarTrack(),
+		CGetSetOptions::m_Theme.ScrollBarThumb(),
+		CGetSetOptions::m_Theme.ScrollBarThumbHover()
+	);
+}
+
 void CQPasteWnd::RefreshThemeColors()
 {
 	// Refresh caption bar colors
 	SetCaptionColorActive(CGetSetOptions::m_bShowPersistent, theApp.GetConnectCV());
 	SetCaptionOn(CGetSetOptions::GetCaptionPos(), true, CGetSetOptions::m_Theme.GetCaptionSize(), CGetSetOptions::m_Theme.GetCaptionFontSize());
 	
+	// Refresh scrollbar colors
+	RefreshScrollBarColors();
+	
 	// Force repaint of the entire window including non-client area
 	SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
 	RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_FRAME);

+ 9 - 2
src/QPasteWnd.h

@@ -20,6 +20,7 @@
 #include "SymbolEdit.h"
 #include "Popup.h"
 #include "CustomFriendsHelper.h"
+#include "ModernScrollBar.h"
 
 class CMainTable
 {
@@ -165,6 +166,8 @@ public:
 	CAccels m_toolTipActions;
 	CAccels m_modifierKeyActions;
 	bool m_showScrollBars;
+	CModernScrollBar m_modernScrollBar;       // Vertical scrollbar
+	CModernScrollBar m_modernScrollBarHorz;   // Horizontal scrollbar
 	int m_leftSelectedCompareId;
 	INT64 m_extraDataCounter;
 	CPopup m_popupMsg;
@@ -310,6 +313,11 @@ public:
 	bool DoActionEmailTo();
 	bool DoActionGmail();
 	bool DoActionEmailToAttachExport();
+	
+	// Refresh scrollbar colors from current theme
+	void RefreshScrollBarColors();
+	// Refresh all theme colors (caption, scrollbars, etc.)
+	void RefreshThemeColors();
 	bool DoActionEmailToAttachContent();
 	bool DoActionSlugify();
 	bool DoCopySelection();
@@ -322,8 +330,6 @@ public:
 	bool OnGlobalHotkyes();
 	
 	void UpdateMenuShortCut(CCmdUI *pCmdUI, DWORD action);
-	// Refresh all theme colors (caption, scrollbars, etc.)
-	void RefreshThemeColors();
 
 	bool ShowProperties(int id, int row);
 	bool DeleteClips(CClipIDs &IDs, ARRAY &Indexs);
@@ -466,6 +472,7 @@ protected:
 	afx_msg void OnChaiScriptPaste(UINT idIn);
     afx_msg LRESULT OnSelectAll(WPARAM wParam, LPARAM lParam);
 	afx_msg LRESULT OnShowHideScrollBar(WPARAM wParam, LPARAM lParam);
+	afx_msg LRESULT OnUpdateScrollBar(WPARAM wParam, LPARAM lParam);
 	afx_msg void OnMenuSearchDescription();
 	afx_msg void OnMenuSearchFullText();
 	afx_msg void OnMenuSearchQuickPaste();

+ 4 - 0
src/QuickPaste.cpp

@@ -292,6 +292,10 @@ void CQuickPaste::ShowQPasteWnd(CWnd *pParent, bool bAtPrevPos, bool bFromKeyboa
 	{
 		m_pwndPaste->ShowQPasteWindow(bReFillList);
 	}
+	
+	// Refresh scrollbar colors to match current theme
+	m_pwndPaste->RefreshScrollBarColors();
+	
 	m_pwndPaste->SetForegroundWindow();
 
 	Log(StrF(_T("END of ShowQPasteWnd, AtPrevPos: %d, FromKeyboard: %d, RefillList: %d, Position, %d %d %d %d"), bAtPrevPos, bFromKeyboard, bReFillList, crRect.left, crRect.top, crRect.right, crRect.bottom));

+ 10 - 0
src/Theme.cpp

@@ -62,6 +62,11 @@ void CTheme::LoadDefaults()
 
 	m_descriptionWindowText = RGB(0, 0, 0);
 
+	// Modern scrollbar defaults - rounded look
+	m_scrollBarThumb = RGB(180, 180, 180);
+	m_scrollBarThumbHover = RGB(140, 140, 140);
+	m_scrollBarTrack = RGB(240, 240, 240);
+
 	m_captionSize = 25;
 	m_captionFontSize = 19;
 }
@@ -181,6 +186,11 @@ bool CTheme::Load(CString csTheme, bool bHeaderOnly, bool bCheckLastWriteTime)
 	LoadColor(ItemHeader, "DescriptionWindowBG", m_descriptionWindowBG);
 	LoadColor(ItemHeader, "DescriptionWindowText", m_descriptionWindowText);
 
+	// Modern scrollbar colors
+	LoadColor(ItemHeader, "ScrollBarThumb", m_scrollBarThumb);
+	LoadColor(ItemHeader, "ScrollBarThumbHover", m_scrollBarThumbHover);
+	LoadColor(ItemHeader, "ScrollBarTrack", m_scrollBarTrack);
+
 	if (followWindows10Theme)
 	{
 		LoadWindowsAccentColor();

+ 10 - 0
src/Theme.h

@@ -48,6 +48,11 @@ public:
 	COLORREF DescriptionWindowBG() const { return m_descriptionWindowBG; }
 	COLORREF DescriptionWindowText() const { return m_descriptionWindowText; }
 
+	// Modern scrollbar colors
+	COLORREF ScrollBarThumb() const { return m_scrollBarThumb; }
+	COLORREF ScrollBarThumbHover() const { return m_scrollBarThumbHover; }
+	COLORREF ScrollBarTrack() const { return m_scrollBarTrack; }
+
 	CString Notes() const { return m_csNotes; }
 	CString Author() const { return m_csAuthor; }
 	long FileVersion() const { return m_lFileVersion; }
@@ -95,6 +100,11 @@ protected:
 	COLORREF m_descriptionWindowBG;
 	COLORREF m_descriptionWindowText;
 
+	// Modern scrollbar colors
+	COLORREF m_scrollBarThumb;
+	COLORREF m_scrollBarThumbHover;
+	COLORREF m_scrollBarTrack;
+
 	int m_captionSize;
 	int m_captionFontSize;