xref: /MusicPlayer2/MusicPlayer2/BaseDialog.cpp (revision 6c7c104acdae990ed2acfcc80776176faf337a71)
1 // BaseDialog.cpp : 实现文件
2 //
3 
4 #include "stdafx.h"
5 #include "BaseDialog.h"
6 #include "IniHelper.h"
7 #include "MusicPlayer2.h"
8 
9 // CBaseDialog 对话框
10 std::map<CString, HWND> CBaseDialog::m_unique_hwnd;
11 
IMPLEMENT_DYNAMIC(CBaseDialog,CDialog)12 IMPLEMENT_DYNAMIC(CBaseDialog, CDialog)
13 
14 CBaseDialog::CBaseDialog(UINT nIDTemplate, CWnd* pParent /*=NULL*/)
15     : CDialog(nIDTemplate, pParent)
16 {
17     m_nDialogID = nIDTemplate;
18 }
19 
~CBaseDialog()20 CBaseDialog::~CBaseDialog()
21 {
22 }
23 
SetBackgroundColor(COLORREF color,BOOL bRepaint)24 void CBaseDialog::SetBackgroundColor(COLORREF color, BOOL bRepaint)
25 {
26     if (m_brBkgr.GetSafeHandle() != NULL)
27     {
28         m_brBkgr.DeleteObject();
29     }
30 
31     if (color != (COLORREF)-1)
32     {
33         m_brBkgr.CreateSolidBrush(color);
34     }
35 
36     if (bRepaint && GetSafeHwnd() != NULL)
37     {
38         Invalidate();
39         UpdateWindow();
40     }
41 }
42 
GetUniqueHandel(LPCTSTR dlg_name)43 HWND CBaseDialog::GetUniqueHandel(LPCTSTR dlg_name)
44 {
45     return m_unique_hwnd[dlg_name];
46 }
47 
AllUniqueHandels()48 const std::map<CString, HWND>& CBaseDialog::AllUniqueHandels()
49 {
50     return m_unique_hwnd;
51 }
52 
CloseAllWindow()53 void CBaseDialog::CloseAllWindow()
54 {
55     //确保在退出前关闭所有窗口
56     for (const auto& item : AllUniqueHandels())
57     {
58         ::SendMessage(item.second, WM_COMMAND, IDCANCEL, 0);
59     }
60 }
61 
ShowModelessDialog(UINT id)62 void CBaseDialog::ShowModelessDialog(UINT id)
63 {
64     m_is_modeless_dialog = true;
65     if (!IsWindow(m_hWnd))
66     {
67         Create(id);
68         ShowWindow(SW_SHOW);
69     }
70     else
71     {
72         ShowWindow(SW_SHOWNORMAL);
73     }
74 }
75 
LoadConfig()76 void CBaseDialog::LoadConfig()
77 {
78     ASSERT(!GetDialogName().IsEmpty());
79     ASSERT(IsRememberDialogSizeEnable());
80     CIniHelper ini{ theApp.m_config_path };
81     //载入窗口大小设置
82     m_window_size.cx = ini.GetInt(_T("window_size"), GetDialogName() + _T("_width"), -1);
83     m_window_size.cy = ini.GetInt(_T("window_size"), GetDialogName() + _T("_height"), -1);
84 }
85 
SaveConfig() const86 void CBaseDialog::SaveConfig() const
87 {
88     ASSERT(!GetDialogName().IsEmpty());
89     ASSERT(IsRememberDialogSizeEnable());
90     CIniHelper ini{ theApp.m_config_path };
91     //保存窗口大小设置
92     ini.WriteInt(_T("window_size"), GetDialogName() + _T("_width"), m_window_size.cx);
93     ini.WriteInt(_T("window_size"), GetDialogName() + _T("_height"), m_window_size.cy);
94     ini.Save();
95 }
96 
ReLoadLayoutResource()97 void CBaseDialog::ReLoadLayoutResource()
98 {
99     ASSERT(m_nDialogID);
100     CMFCDynamicLayout* pDynamicLayout = GetDynamicLayout();
101     if (pDynamicLayout)
102     {
103         HRSRC layoutRes = ::FindResourceW(NULL, MAKEINTRESOURCEW(m_nDialogID), RT_DIALOG_LAYOUT);
104         if (layoutRes)
105         {
106             HGLOBAL hResData = ::LoadResource(NULL, layoutRes);
107             if (hResData)
108             {
109                 LPVOID layoutResData = ::LockResource(hResData);
110                 DWORD layoutResSize = ::SizeofResource(NULL, layoutRes);
111                 // std::wstring data(static_cast<wchar_t*>(layoutResData), layoutResSize / sizeof(wchar_t));
112                 pDynamicLayout->LoadResource(this, layoutResData, layoutResSize);
113             }
114         }
115     }
116 }
117 
SetIcon(IconMgr::IconType type,BOOL bBigIcon)118 void CBaseDialog::SetIcon(IconMgr::IconType type, BOOL bBigIcon)
119 {
120     if (bBigIcon)
121         CDialog::SetIcon(theApp.m_icon_mgr.GetHICON(type, IconMgr::IconStyle::IS_OutlinedDark, IconMgr::IconSize::IS_ALL), TRUE);
122     else
123         CDialog::SetIcon(theApp.m_icon_mgr.GetHICON(type, IconMgr::IconStyle::IS_OutlinedDark, IconMgr::IconSize::IS_DPI_16), FALSE);
124 }
125 
SetButtonIcon(UINT id,IconMgr::IconType type)126 void CBaseDialog::SetButtonIcon(UINT id, IconMgr::IconType type)
127 {
128     CWnd* dlgItem = GetDlgItem(id);
129     CButton* close_btn = static_cast<CButton*>(dlgItem);
130     if (close_btn != nullptr)
131         close_btn->SetIcon(theApp.m_icon_mgr.GetHICON(type, IconMgr::IconStyle::IS_OutlinedDark, IconMgr::IconSize::IS_DPI_16));
132 }
133 
ShowDlgCtrl(UINT id,bool show)134 void CBaseDialog::ShowDlgCtrl(UINT id, bool show)
135 {
136     CWnd* pCtrl = GetDlgItem(id);
137     if (pCtrl != nullptr)
138         pCtrl->ShowWindow(show);
139 }
140 
EnableDlgCtrl(UINT id,bool enable)141 void CBaseDialog::EnableDlgCtrl(UINT id, bool enable)
142 {
143     CWnd* pCtrl = GetDlgItem(id);
144     if (pCtrl != nullptr)
145         pCtrl->EnableWindow(enable);
146 }
147 
GetTextExtent(const CString & text)148 CRect CBaseDialog::GetTextExtent(const CString& text)
149 {
150     ASSERT(m_pDC != nullptr);   // m_pDC由OnInitDialog负责申请释放
151     if (m_pDC == nullptr)
152         return CRect();
153     if (text.IsEmpty())
154         return CRect();
155     CRect text_size;
156     m_pDC->DrawTextW(text, &text_size, DT_CALCRECT);    // 使用CDC::DrawTextW测量文本宽度(CDC::GetTextExtent是理论宽度,不准确)
157     return text_size;
158 }
159 
RepositionTextBasedControls(const vector<CtrlTextInfo> & items,CtrlTextInfo::Width center_min_width)160 void CBaseDialog::RepositionTextBasedControls(const vector<CtrlTextInfo>& items, CtrlTextInfo::Width center_min_width)
161 {
162     ASSERT(m_pDC != nullptr);   // 此方法仅在InitializeControls期间可用
163     if (m_pDC == nullptr)
164         return;
165     int center_width = theApp.DPI(center_min_width);
166     std::map<int, pair<int, int>> col_info;
167     struct itemInfo
168     {
169         int col_index;
170         CWnd* p;
171         CRect rect;
172     };
173     vector<itemInfo> items_info;
174     int center_left{ 0 }, center_right{ INT_MAX };
175 
176     for (const auto& item : items)
177     {
178         ASSERT(item.col_index != CtrlTextInfo::UN_USE);
179         ASSERT(item.id != 0);
180         // 获取所有列所需dx,以及左贴靠元素的右边缘center_left,右贴靠元素的左边缘center_right
181         CWnd* pItem = GetDlgItem(item.id);
182         if (pItem == nullptr)
183             continue;
184         CRect rect{};
185         pItem->GetWindowRect(&rect);
186         ScreenToClient(&rect);
187         CString text;
188         pItem->GetWindowTextW(text);
189         int dx = GetTextExtent(text).Width() + theApp.DPI(item.ext_width) - rect.Width();
190         if (dx < 0) dx = 0;                                     // 文字只增加控件宽度
191         if (col_info[item.col_index].first < dx)                // 取此列元素中宽度增长最多的
192             col_info[item.col_index].first = dx;
193         if (item.col_index < 0 && center_left < rect.right)
194             center_left = rect.right;
195         if (item.col_index > 0 && center_right > rect.left)
196             center_right = rect.left;
197         if (item.col_index == 0)    // col_index为0的控件可能有多个
198         {
199             if(center_left < rect.left)
200                 center_left = rect.left;
201             if(center_right > rect.right)
202                 center_right = rect.right;
203         }
204         items_info.emplace_back(itemInfo{ item.col_index, pItem, std::move(rect) });
205     }
206 
207     if (center_right == INT_MAX)    // 如果控件全部都是左贴靠的那么以窗口右边缘作为剩余空间的右边缘
208     {
209         CRect dlg_rect{};
210         GetClientRect(&dlg_rect);
211         center_right = dlg_rect.Width();
212     }
213     // 此断言触发说明资源文件中的原始布局没有给中间控件/空闲空间留够宽度
214     ASSERT(center_right - center_left >= center_width);
215     int dx_sum_left{}, dx_sum_right{};
216     // 因为同一col_index可以有多个控件&没有要求顺序所以控件的最终位置必须可以无状态的计算出来
217     for (auto& a : col_info)
218     {
219         if (a.first < 0)
220         {
221             a.second.second = dx_sum_left;    // 存储此列之前控件的总dx,即此列控件的右移距离
222             dx_sum_left += a.second.first;
223         }
224         else if (a.first > 0)
225         {
226             a.second.second = dx_sum_right;
227             dx_sum_right += a.second.first;
228         }
229     }
230     float scale{ 1.0f };
231     if (center_right - center_left - dx_sum_left - dx_sum_right < center_width)
232     {
233         // ASSERT(false);
234         // 现在加载的文本使此行的中间控件/空闲空间被挤压的太小
235         // 这需要重新设计窗口控件排布以适应当前翻译长度,这里先简单的把缺少的空间分摊给各dx
236         scale = static_cast<float>(center_right - center_left - center_width) / (dx_sum_left + dx_sum_right);
237         dx_sum_left = static_cast<int>(dx_sum_left * scale + 0.5f);
238         dx_sum_right = static_cast<int>(dx_sum_right * scale + 0.5f);
239     }
240     for (const auto& item : items_info)
241     {
242         const auto& rect = item.rect;
243         int dx = static_cast<int>(col_info[item.col_index].first * scale + 0.5f);
244         int sum_dx = static_cast<int>(col_info[item.col_index].second * scale + 0.5f);
245         if (item.col_index < 0)
246             item.p->SetWindowPos(nullptr, rect.left + sum_dx, rect.top, rect.Width() + dx, rect.Height(), SWP_NOZORDER);
247         else if (item.col_index > 0)
248             item.p->SetWindowPos(nullptr, rect.left + sum_dx - dx_sum_right, rect.top, rect.Width() + dx, rect.Height(), SWP_NOZORDER);
249         else if (item.col_index == 0)
250             item.p->SetWindowPos(nullptr, rect.left + dx_sum_left, rect.top, rect.Width() - dx_sum_left - dx_sum_right, rect.Height(), SWP_NOZORDER);
251     }
252 }
253 
SetMinSize(int cx,int cy)254 void CBaseDialog::SetMinSize(int cx, int cy)
255 {
256     m_min_size.cx = cx;
257     m_min_size.cy = cy;
258 }
259 
SetDlgControlText(int id,const wchar_t * key)260 void CBaseDialog::SetDlgControlText(int id, const wchar_t* key)
261 {
262     const wstring& temp = theApp.m_str_table.LoadText(key);
263     SetDlgItemTextW(id, temp.c_str());
264 }
265 
DoDataExchange(CDataExchange * pDX)266 void CBaseDialog::DoDataExchange(CDataExchange* pDX)
267 {
268     CDialog::DoDataExchange(pDX);
269 }
270 
271 
BEGIN_MESSAGE_MAP(CBaseDialog,CDialog)272 BEGIN_MESSAGE_MAP(CBaseDialog, CDialog)
273     ON_WM_DESTROY()
274     ON_WM_GETMINMAXINFO()
275     ON_WM_SIZE()
276     ON_WM_ERASEBKGND()
277     ON_WM_CTLCOLOR()
278     ON_WM_CLOSE()
279 END_MESSAGE_MAP()
280 
281 
282 // CBaseDialog 消息处理程序
283 
284 
285 BOOL CBaseDialog::OnInitDialog()
286 {
287     if (!GetDialogName().IsEmpty())
288     {
289         m_unique_hwnd[GetDialogName()] = m_hWnd;
290         if (IsRememberDialogSizeEnable())
291             CBaseDialog::LoadConfig();          // 载入设置
292     }
293 
294     CDialog::OnInitDialog();
295 
296     // TODO:  在此添加额外的初始化
297 
298     //获取初始时窗口的大小
299     if (m_min_size.cx <= 0 || m_min_size.cy <= 0)
300     {
301         CRect rect;
302         GetWindowRect(rect);
303         m_min_size.cx = rect.Width();
304         m_min_size.cy = rect.Height();
305     }
306 
307     // 通用窗口/控件初始化操作
308 
309     // 设置窗口字体
310     CCommon::SetDialogFont(this, &theApp.m_font_set.dlg.GetFont());
311     //为按钮添加图标
312     SetButtonIcon(IDCANCEL, IconMgr::IconType::IT_Cancel);
313     SetButtonIcon(IDOK, IconMgr::IconType::IT_Ok);
314     // 设置按钮文本
315     wstring temp;
316     temp = theApp.m_str_table.LoadText(L"TXT_OK");
317     SetDlgItemTextW(IDOK, temp.c_str());
318     temp = theApp.m_str_table.LoadText(L"TXT_CANCEL");
319     SetDlgItemTextW(IDCANCEL, temp.c_str());
320 
321     // 在还原窗口大小之前(当前窗口状态与资源一致),派生类执行控件文本初始化及调整控件排布
322     // 与实际窗口大小相关的初始化(比如表格列宽)应在派生类的OnInitDialog进行
323     m_pDC = GetDC();
324     m_pDC->SelectObject(&theApp.m_font_set.dlg.GetFont());
325     bool rtn = InitializeControls();
326     ReleaseDC(m_pDC);
327     m_pDC = nullptr;
328     // 如果更改了控件排布那么应当返回true以向布局管理器应用控件调整(重新加载动态布局设置)
329     if (rtn)
330         ReLoadLayoutResource();
331 
332     //初始化窗口大小
333     if (m_window_size.cx > 0 && m_window_size.cy > 0)
334     {
335         SetWindowPos(nullptr, 0, 0, m_window_size.cx, m_window_size.cy, SWP_NOZORDER | SWP_NOMOVE);
336     }
337 
338     return TRUE;    // return TRUE unless you set the focus to a control
339                     // 异常: OCX 属性页应返回 FALSE
340 }
341 
342 
OnDestroy()343 void CBaseDialog::OnDestroy()
344 {
345     CDialog::OnDestroy();
346 
347     // TODO: 在此处添加消息处理程序代码
348     if (!GetDialogName().IsEmpty())
349     {
350         m_unique_hwnd[GetDialogName()] = NULL;
351         if (IsRememberDialogSizeEnable())
352             CBaseDialog::SaveConfig();
353     }
354 }
355 
356 
OnGetMinMaxInfo(MINMAXINFO * lpMMI)357 void CBaseDialog::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
358 {
359     // TODO: 在此添加消息处理程序代码和/或调用默认值
360     //限制窗口最小大小
361     lpMMI->ptMinTrackSize.x = m_min_size.cx;        //设置最小宽度
362     lpMMI->ptMinTrackSize.y = m_min_size.cy;        //设置最小高度
363 
364     CDialog::OnGetMinMaxInfo(lpMMI);
365 }
366 
367 
OnSize(UINT nType,int cx,int cy)368 void CBaseDialog::OnSize(UINT nType, int cx, int cy)
369 {
370     CDialog::OnSize(nType, cx, cy);
371 
372     // TODO: 在此处添加消息处理程序代码
373     if (nType != SIZE_MAXIMIZED && nType != SIZE_MINIMIZED)
374     {
375         CRect rect;
376         GetWindowRect(&rect);
377         m_window_size.cx = rect.Width();
378         m_window_size.cy = rect.Height();
379     }
380 
381 }
382 
383 
DoModal()384 INT_PTR CBaseDialog::DoModal()
385 {
386     if (!GetDialogName().IsEmpty())
387     {
388         HWND unique_hwnd{ m_unique_hwnd[GetDialogName()] };
389         if (unique_hwnd != NULL)      ///如果对话框已存在,则显示已存在的对话框
390         {
391             ::ShowWindow(unique_hwnd, SW_RESTORE);
392             ::SetForegroundWindow(unique_hwnd);
393             return 0;
394         }
395     }
396     return CDialog::DoModal();
397 }
398 
399 
OnEraseBkgnd(CDC * pDC)400 BOOL CBaseDialog::OnEraseBkgnd(CDC* pDC)
401 {
402     // 修改窗口背景(CDialogEx)
403     if (m_brBkgr.GetSafeHandle() != NULL)
404     {
405         ASSERT_VALID(pDC);
406         CRect rectClient;
407         GetClientRect(rectClient);
408         pDC->FillRect(rectClient, &m_brBkgr);
409         return TRUE;
410     }
411     return CDialog::OnEraseBkgnd(pDC);
412 }
413 
414 
OnCtlColor(CDC * pDC,CWnd * pWnd,UINT nCtlColor)415 HBRUSH CBaseDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
416 {
417     // 修改窗口背景(CDialogEx)
418     if (m_brBkgr.GetSafeHandle() != NULL)
419     {
420 #define AFX_MAX_CLASS_NAME 255
421 #define AFX_STATIC_CLASS _T("Static")
422 #define AFX_BUTTON_CLASS _T("Button")
423 // 上面两个是CDialogEx原有的(因为这两种比较原生,类名不在rc文件中直接出现?),下面是我自己加的
424 #define AFX_SLIDER_CLASS _T("msctls_trackbar32")    // 滑动条控件CSliderCtrl及其派生类
425 #define AFX_SYSLINK_CLASS _T("SysLink")             // 超链接控件CSysLink及其派生类
426 
427         if (nCtlColor == CTLCOLOR_STATIC)
428         {
429             TCHAR lpszClassName[AFX_MAX_CLASS_NAME + 1];
430 
431             ::GetClassName(pWnd->GetSafeHwnd(), lpszClassName, AFX_MAX_CLASS_NAME);
432             CString strClass = lpszClassName;
433 
434             if (strClass == AFX_BUTTON_CLASS || strClass == AFX_STATIC_CLASS || strClass == AFX_SLIDER_CLASS || strClass == AFX_SYSLINK_CLASS)
435             {
436                 pDC->SetBkMode(TRANSPARENT);
437 
438                 if (m_brBkgr.GetSafeHandle() != NULL && IsAppThemed())
439                 {
440                     return (HBRUSH)m_brBkgr.GetSafeHandle();
441                 }
442                 else
443                 {
444                     return (HBRUSH)::GetStockObject(HOLLOW_BRUSH);
445                 }
446             }
447         }
448     }
449 
450     return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
451 }
452 
453 
OnClose()454 void CBaseDialog::OnClose()
455 {
456     CDialog::OnClose();
457 
458     if (m_is_modeless_dialog)
459         DestroyWindow();
460 }
461 
462 
OnOK()463 void CBaseDialog::OnOK()
464 {
465     CDialog::OnOK();
466     if (m_is_modeless_dialog)
467         DestroyWindow();
468 }
469 
470 
OnCancel()471 void CBaseDialog::OnCancel()
472 {
473     CDialog::OnCancel();
474     if (m_is_modeless_dialog)
475         DestroyWindow();
476 }
477