xref: /MusicPlayer2/MusicPlayer2/TabDlg.cpp (revision 96b4ae5bc7d8473417c7b8f62228ed05d4b6c5a5)
1 #include "stdafx.h"
2 #include "TabDlg.h"
3 #include "MusicPlayer2.h"
4 #include "WinVersionHelper.h"
5 
IMPLEMENT_DYNAMIC(CTabDlg,CBaseDialog)6 IMPLEMENT_DYNAMIC(CTabDlg, CBaseDialog)
7 
8 CTabDlg::CTabDlg(UINT nIDTemplate, CWnd * pParent) : CBaseDialog(nIDTemplate, pParent)
9 {
10 }
11 
12 
~CTabDlg()13 CTabDlg::~CTabDlg()
14 {
15 }
16 
GetParentWindow()17 CWnd* CTabDlg::GetParentWindow()
18 {
19     CWnd* pParent = GetParent();
20     if (pParent != nullptr)
21     {
22         return pParent->GetParent();
23     }
24     return nullptr;
25 }
26 
SetScrollbarInfo(int nPage,int nMax)27 void CTabDlg::SetScrollbarInfo(int nPage, int nMax)
28 {
29     // 获取滚动条状态
30     SCROLLINFO scrollinfo;
31     GetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
32     if (m_scroll_enable)
33         m_last_pos = scrollinfo.nPos - scrollinfo.nMin;     // 如果已启用滚动则记录当前滚动位置
34     else
35         m_scroll_enable = true;                             // 否则启用滚动
36     int reset_pos = m_last_pos;
37     // 父窗口OnSize后的新滚动状态边界检查
38     if (nMax < 0) nMax = 0;
39     if (m_last_pos > nMax - nPage) m_last_pos = nMax - nPage;
40     if (m_last_pos < 0) m_last_pos = 0;
41     // 设置滚动条状态
42     scrollinfo.nMin = 0;                // 设置滚动内容起点在窗口中的pos
43     scrollinfo.nMax = nMax;             // 设置滚动内容终点在窗口中的pos
44     scrollinfo.nPage = nPage;           // 设置实际显示内容区域高度
45     scrollinfo.nPos = m_last_pos;       // 设置当前实际显示内容区域顶端pos
46     SetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
47     // 复位滚动
48     if (reset_pos != 0)
49         ScrollWindow(0, reset_pos);
50     // 代替框架做出OnSize的响应 (在SetScrollInfo决定并设置滚动条是否在新状态下显示之后)
51     CBaseDialog::ResizeDynamicLayout(); // 这个方法会无视滚动更新控件位置导致控件错位,在滚动为0时调用避开这个问题
52     // 执行滚动
53     if (m_last_pos != 0)
54         ScrollWindow(0, -m_last_pos);
55 }
56 
ScrollWindowSimple(int step,bool absolute)57 void CTabDlg::ScrollWindowSimple(int step, bool absolute)
58 {
59     SCROLLINFO scrollinfo;
60     GetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
61     if (step == INT_MAX)
62         step = scrollinfo.nPage;
63     if (step == INT_MIN)
64         step = -static_cast<int>(scrollinfo.nPage);
65     int offset_diff = scrollinfo.nMax - static_cast<int>(scrollinfo.nPage);
66     if (offset_diff < 0)    //nMax应该总是大于或等于nPage,当nMax等于nPage时没有滚动条
67         offset_diff = 0;
68     if (!absolute)
69         scrollinfo.nPos -= step;
70     else
71     {
72         if (step < 0)               // 绝对位置时step为-1表示滚动到底部
73             step = offset_diff;
74         scrollinfo.nPos = step;     // 此时的stpe为目标绝对位置
75         step = m_last_pos - step;   // 将参数的绝对位置转换为偏移量继续处理
76     }
77     if (scrollinfo.nPos < scrollinfo.nMin)                                          // 限制nPos不小于最小值nMin
78     {
79         step -= scrollinfo.nMin - scrollinfo.nPos;
80         scrollinfo.nPos = scrollinfo.nMin;
81     }
82     if (scrollinfo.nPos > offset_diff)     // 限制nPos不大于最大值nMax - nPage
83     {
84         step += scrollinfo.nPos - offset_diff;
85         scrollinfo.nPos = offset_diff;
86     }
87     SetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
88     m_last_pos = scrollinfo.nPos;
89     ScrollWindow(0, step);
90 }
91 
BEGIN_MESSAGE_MAP(CTabDlg,CBaseDialog)92 BEGIN_MESSAGE_MAP(CTabDlg, CBaseDialog)
93     ON_WM_VSCROLL()
94     ON_WM_MOUSEWHEEL()
95 END_MESSAGE_MAP()
96 
97 
98 void CTabDlg::ResizeDynamicLayout()
99 {
100     // m_pDynamicLayout->Adjust()会无视窗口滚动更新控件位置
101     // 之前在父窗口OnSize后重置不可靠(进入退出最大化和贴靠时没能正常工作),也没找到检出这两种特殊OnSize的方法
102     // 框架内部调用ResizeDynamicLayout太早,来不及先复位滚动,故覆写此虚方法介入
103 
104     // 如果已启用滚动那么此处不调用基类使框架的处理无效,改为在父窗口的OnSize后自行调用
105     if (!m_scroll_enable)
106         CBaseDialog::ResizeDynamicLayout();
107 }
108 
OnInitDialog()109 BOOL CTabDlg::OnInitDialog()
110 {
111     CBaseDialog::OnInitDialog();
112 
113     // TODO:  在此添加额外的初始化
114 
115     // 设置窗口背景色
116     SetBackgroundColor(CWinVersionHelper::IsWindows11OrLater() ? RGB(249, 249, 249) : RGB(255, 255, 255));
117     // SetBackgroundColor(RGB(255, 240, 244));   // 测试用
118 
119     return TRUE;  // return TRUE unless you set the focus to a control
120     // 异常: OCX 属性页应返回 FALSE
121 }
122 
123 
PreTranslateMessage(MSG * pMsg)124 BOOL CTabDlg::PreTranslateMessage(MSG* pMsg)
125 {
126     // TODO: 在此添加专用代码和/或调用基类
127     if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE)
128     {
129         return TRUE;
130     }
131     if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)
132     {
133         return TRUE;
134     }
135 
136     return CBaseDialog::PreTranslateMessage(pMsg);
137 }
138 
139 
OnOK()140 void CTabDlg::OnOK()
141 {
142     // 此处将CDialog原本protected的默认OnOK重写为Public
143     // 这是为了对外提供统一调用标签页(此类的派生类)OnOK的方式
144     // 而不必区分具体是哪个派生类(dynamic_cast<CTabDlg*>)
145     CBaseDialog::OnOK();
146 }
147 
148 
OnVScroll(UINT nSBCode,UINT nPos,CScrollBar * pScrollBar)149 void CTabDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
150 {
151     // TODO: 在此添加消息处理程序代码和/或调用默认值
152     //参考资料:https://www.cnblogs.com/ranjiewen/p/6013922.html
153 
154     if (m_scroll_enable)
155     {
156         int step = theApp.DPI(16);
157         switch (nSBCode)
158         {
159         case SB_LINEUP:                         //Scroll one line up
160             ScrollWindowSimple(step);
161             break;
162         case SB_LINEDOWN:                       //Scroll one line down
163             ScrollWindowSimple(-step);
164             break;
165         case SB_PAGEUP:                         //Scroll one page up.
166             ScrollWindowSimple(INT_MAX);
167             break;
168         case SB_PAGEDOWN:                       //Scroll one page down
169             ScrollWindowSimple(INT_MIN);
170             break;
171         case SB_THUMBPOSITION:                  //Scroll to the absolute position. The current position is provided in nPos
172             break;
173         case SB_THUMBTRACK:                     //Drag scroll box to specified position. The current position is provided in nPos
174             ScrollWindowSimple(nPos, true);
175             break;
176         case SB_TOP:                            // 顶部
177             ScrollWindowSimple(0, true);
178             break;
179         case SB_BOTTOM:                         // 底部
180             ScrollWindowSimple(-1, true);
181             break;
182         case SB_ENDSCROLL:                      //End scroll
183             break;
184         default:
185             break;
186         }
187     }
188     CBaseDialog::OnVScroll(nSBCode, nPos, pScrollBar);
189 }
190 
191 
OnMouseWheel(UINT nFlags,short zDelta,CPoint pt)192 BOOL CTabDlg::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
193 {
194     // TODO: 在此添加消息处理程序代码和/或调用默认值
195     if (m_scroll_enable)
196     {
197         int step = theApp.DPI(64) * zDelta / 120;
198         ScrollWindowSimple(step);
199     }
200 
201     return CBaseDialog::OnMouseWheel(nFlags, zDelta, pt);
202 }
203