xref: /MusicPlayer2/MusicPlayer2/CPlayerUIBase.h (revision fc77c71b2623add2756a54b7bf5cd93b7619bbd0)
1 #pragma once
2 #include "MusicPlayer2.h"
3 #include "IPlayerUI.h"
4 #include "CPlayerUIHelper.h"
5 #include "CUIDrawer.h"
6 #include "IconMgr.h"
7 
8 #define WM_MAIN_MENU_POPEDUP (WM_USER+117)      //显示弹出式主菜单的消息,wPara为表示菜单显示位置的CPoint的指针
9 
10 namespace UiElement
11 {
12     class Element;
13     class Rectangle;
14     class Button;
15     class Text;
16     class AlbumCover;
17     class Spectrum;
18     class TrackInfo;
19     class Toolbar;
20     class ProgressBar;
21     class Lyrics;
22     class Volume;
23     class BeatIndicator;
24     class StackElement;
25     class ListElement;
26     class Playlist;
27     class PlaylistIndicator;
28     class ClassicalControlBar;
29     class MediaLibItemList;
30     class MediaLibPlaylist;
31     class MediaLibFolder;
32     class RecentPlayedList;
33     class NavigationBar;
34     class MyFavouriteList;
35     class AllTracksList;
36     class MiniSpectrum;
37     class FolderExploreTree;
38     class SearchBox;
39 }
40 
41 struct SLayoutData
42 {
43     const int margin = theApp.DPI(4);                           //边缘的余量
44     const int width_threshold = theApp.DPI(600);                //界面从普通界面模式切换到窄界面模式时界面宽度的阈值
45     const int info_height = theApp.DPI(216);                    //窄界面模式时显示信息区域的高度
46     const int path_edit_height = theApp.DPI(24);                //当前路径Edit控件的高度
47     const int search_edit_height = theApp.DPI(26);              //歌曲搜索框Edit控件区域的高度
48     const CSize spectral_size{ theApp.DPI(120), theApp.DPI(90) };   //频谱分析区域的大小
49     const int toolbar_height = theApp.DPI(24);                  //播放列表工具栏的高度
50     const int titlabar_height = theApp.DPI(28);                 //标题栏的高度
51     const int menubar_height = theApp.DPI(24);                  //菜单栏的高度
52 };
53 
54 namespace PlayerUiConstVal
55 {
56     const int BTN_MAX_NUM = 1000;
57 }
58 
59 class CPlayerUIBase : public IPlayerUI
60 {
61 public:
62     CPlayerUIBase(UIData& ui_data, CWnd* pMainWnd);
63     ~CPlayerUIBase();
64 
GetToolTipCtrl()65     virtual CToolTipCtrl& GetToolTipCtrl() override { return m_tool_tip; }
66 
67     friend class UiElement::Element;
68     friend class UiElement::Rectangle;
69     friend class UiElement::Button;
70     friend class UiElement::Text;
71     friend class UiElement::AlbumCover;
72     friend class UiElement::Spectrum;
73     friend class UiElement::TrackInfo;
74     friend class UiElement::Toolbar;
75     friend class UiElement::ProgressBar;
76     friend class UiElement::Lyrics;
77     friend class UiElement::Volume;
78     friend class UiElement::BeatIndicator;
79     friend class UiElement::StackElement;
80     friend class UiElement::ListElement;
81     friend class UiElement::Playlist;
82     friend class UiElement::PlaylistIndicator;
83     friend class UiElement::ClassicalControlBar;
84     friend class UiElement::MediaLibItemList;
85     friend class UiElement::MediaLibPlaylist;
86     friend class UiElement::MediaLibFolder;
87     friend class UiElement::RecentPlayedList;
88     friend class UiElement::NavigationBar;
89     friend class UiElement::MyFavouriteList;
90     friend class UiElement::AllTracksList;
91     friend class UiElement::MiniSpectrum;
92     friend class UiElement::FolderExploreTree;
93     friend class UiElement::SearchBox;
94 
95     friend class UiFontGuard;
96 
97 public:
98     void Init(CDC* pDC) override;
99     virtual void DrawInfo(bool reset = false) override final;
100     virtual void ClearInfo() override;
101 
102     virtual bool LButtonDown(CPoint point) override;
103     virtual void RButtonUp(CPoint point) override;
104     virtual void MouseMove(CPoint point) override;
105     virtual bool LButtonUp(CPoint point) override;
106     virtual void RButtonDown(CPoint point) override;
107     virtual bool MouseWheel(int delta, CPoint point) override;
108     virtual bool DoubleClick(CPoint point) override;
109 
110     virtual CRect GetThumbnailClipArea() override;
111     void UpdateRepeatModeToolTip();
112     void UpdateSongInfoToolTip();
113     void UpdatePlayPauseButtonTip() override;
114     virtual void UpdateFullScreenTip() override;
115     void UpdateTitlebarBtnToolTip();       //更新标题栏上的最大化/还原按钮的鼠标提示
116     virtual void UpdateVolumeToolTip();
117     void UpdatePlaylistBtnToolTip();
118     void UpdateDarkLightModeBtnToolTip();
119     void UpdateToolTipPositionLater();
120 
121     virtual bool SetCursor() override;
122     virtual void MouseLeave() override;
123 
124     void ClearBtnRect();
125 
126     int Margin() const;
127     int EdgeMargin(bool x = true) const;
128     int WidthThreshold() const;
129     int DrawAreaHeight() const;     //窄界面模式下显示播放列表时绘图区的高度
130     int TopRightButtonsWidth() const;
131 
132     virtual bool PointInControlArea(CPoint point) const;        //判断一个点的位置是否在控件区域
133     bool PointInTitlebarArea(CPoint point) const;
134     bool PointInAppIconArea(CPoint point) const;
135     bool PointInMenubarArea(CPoint point) const;
136 
137     //获取界面的名称
GetUIName()138     virtual wstring GetUIName() { return wstring(); }
139 
140     enum class UiSize
141     {
142         BIG,
143         NARROW,
144         SMALL
145     };
146 
147     UiSize GetUiSize() const;
148 
UiSizeChanged()149     virtual void UiSizeChanged() {}
150 
151     static CString GetCmdShortcutKeyForTooltips(UINT id);      //获取用于显示在鼠标提示中的键盘快捷键
152 
153     CRect GetVolumeRect() const;    //获取音量图标的矩形区域
154 
155 protected:
156     // 将字符串形如“%(KEY_STR)”格式的字符替换成当前<language>.ini中对应id的字符串
157     static void ReplaceUiStringRes(wstring& str);
158 
159 public:
160     enum BtnKey     //标识按钮的类型
161     {
162         BTN_REPETEMODE,         //“循环模式”按钮
163         BTN_VOLUME,             //音量按钮
164         BTN_VOLUME_UP,
165         BTN_VOLUME_DOWN,
166         BTN_TRANSLATE,          //歌词翻译按钮
167         BTN_SKIN,               //切换界面按钮
168         BTN_SKIN_TITLEBAR,      //标题栏上的切换界面按钮
169         BTN_EQ,                 //音效设定按钮
170         BTN_SETTING,            //设置按钮
171         BTN_SETTING_TITLEBAR,   //标题栏上的设置按钮
172         BTN_MINI,               //迷你模式按钮
173         BTN_MINI_TITLEBAR,      //标题栏上的迷你模式按钮
174         BTN_INFO,               //曲目信息按钮
175         BTN_FIND,               //查找歌曲按钮
176         BTN_LRYIC,              //桌面歌词按钮
177         BTN_AB_REPEAT,          //AB重复按钮
178         BTN_STOP,               //停止
179         BTN_PREVIOUS,           //上一曲
180         BTN_PLAY_PAUSE,         //播放/暂停
181         BTN_NEXT,               //下一曲
182         BTN_SHOW_PLAYLIST,      //显示/隐藏播放列表
183         BTN_MEDIA_LIB,          //媒体库
184         BTN_PROGRESS,           //进度条
185         BTN_COVER,              //专辑封面
186         BTN_FULL_SCREEN_TITLEBAR, //标题栏上的全屏显示按钮
187         BTN_FULL_SCREEN,        //全屏显示按钮
188         BTN_MENU_TITLEBAR,      //标题栏上的主菜单按钮
189         BTN_MENU,               //主菜单按钮
190         BTN_FAVOURITE,          //“我喜欢”按钮
191         BTN_CLOSE,              //关闭按钮(迷你模式)
192         BTN_MINIMIZE,           //最小化按钮
193         BTN_MAXIMIZE,           //最大化按钮
194         BTN_APP_CLOSE,          //关闭按钮
195         BTN_ADD_TO_PLAYLIST,    //添加到播放列表按钮
196         BTN_SWITCH_DISPLAY,     //切换界面中的stackElement
197         BTN_DARK_LIGHT,         //切换深色/浅色模式
198         BTN_DARK_LIGHT_TITLE_BAR, //标题栏中的切换深色/浅色模式
199         BTN_LOCATE_TO_CURRENT,  //播放列表定位到当前播放
200         BTN_OPEN_FOLDER,        //打开文件夹
201         BTN_NEW_PLAYLIST,       //新建播放列表
202         BTN_PLAY_MY_FAVOURITE,  //播放“我喜欢的音乐”
203         BTN_MEDIALIB_FOLDER_SORT, //媒体库“文件夹”排序方式
204         BTN_MEDIALIB_PLAYLIST_SORT, //媒体库“播放列表”排序方式
205         BTN_KARAOKE,            //歌词卡拉OK模式显示
206 
207         //菜单栏
208         MENU_FILE,
209         MENU_PLAY_CONTROL,
210         MENU_PLAYLIST,
211         MENU_LYRICS,
212         MENU_VIEW,
213         MENU_TOOLS,
214         MENU_HELP,
215 
216         BTN_INVALID,            //无效的按钮
217     };
218 
219     enum ColorMode
220     {
221         RCM_AUTO,
222         RCM_DARK,
223         RCM_LIGHT
224     };
225 
226     // 获取参数按钮当前应当使用的图标类型
227     // 将BtnKey枚举和当前状态组合映射为IconMgr::IconType枚举
228     IconMgr::IconType GetBtnIconType(BtnKey key);
229 
230     //获取按钮的文本
231     std::wstring GetButtonText(BtnKey key_type);
232 
233 protected:
234     struct DrawData
235     {
236         //CRect cover_rect;
237         CRect lyric_rect;
238         CRect thumbnail_rect;
239     };
240 
241 protected:
242     virtual void _DrawInfo(CRect draw_rect, bool reset = false) = 0;
243     virtual void PreDrawInfo();
244     void SetDrawRect();
245     void DrawBackground();
246     void DrawSongInfo(CRect rect, int font_size = 9, bool reset = false);
247     void DrawRectangle(const CRect& rect, bool no_corner_radius = false, bool theme_color = true, ColorMode color_mode = RCM_AUTO);       //绘制矩形。如果no_corner_radius为true,则总是绘制直角矩形,忽略“使用圆角风格按钮”的设置;theme_color:是否使用主题彦颜色
248     void DrawToolBar(CRect rect, bool draw_translate_button);
249     void DrawToolBarWithoutBackground(CRect rect, bool draw_translate_button);
250     void DrawBeatIndicator(CRect rect);
251     void DrawVolumnAdjBtn();
252     void DrawControlBar(CRect rect, bool draw_switch_display_btn = false);
253     void DrawProgressBar(CRect rect, bool play_time_both_side = false);               //绘制进度条(包含时间)。play_time_both_side如果为true,则播放时间显示的进度条的两侧,否则显示在进度条的右侧
254     void DrawProgess(CRect rect);                   //绘制进度条
255     void DrawTranslateButton(CRect rect);
256     void DrawDesktopLyricButton(CRect rect);
257     void DrawKaraokeButton(CRect rect);
258     void DrawTopRightIcons();           //绘制右上角的图标
259     void DrawCurrentTime();             //在右上角绘制当前系统时间
260     void DrawAlbumCover(CRect rect);                //绘制专辑封面
261     void DrawAlbumCoverWithInfo(CRect rect);        //绘制专辑封面,并在上面绘制歌曲的标题和艺术家
262     void DrawVolumeButton(CRect rect, bool adj_btn_top = false, bool show_text = true);     //adj_btn_top:点击后弹出的音量调整按钮是否在上方;show_text:是否显示文本
263     void DrawABRepeatButton(CRect rect);
264     void DrawLyrics(CRect rect, CFont* lyric_font, CFont* lyric_tr_font, bool with_background, bool show_song_info = false);        //绘制歌词 rect:歌曲区域;with_background是否绘制背景;show_song_info:是否总是在没有歌词时显示歌曲信息
265     void DrawList(CRect rect, UiElement::ListElement* list_element, int item_height);                  //绘制播放列表
266     void DrawCurrentPlaylistIndicator(CRect rect, UiElement::PlaylistIndicator* playlist_indicator);      //绘制当前播放列表指示
267     /**
268      * @brief   绘制stackElement的指示器
269      * @param   UIButton indicator 指示器信息
270      * @param   int num 指示器中元素的数量
271      * @param   int index 指示器当前索引
272      */
273     void DrawStackIndicator(UIButton indicator, int num, int index);
274     void DrawUiMenuBar(CRect rect);
275     void DrawNavigationBar(CRect rect, UiElement::NavigationBar* tab_element);
276     void DrawMiniSpectrum(CRect rect);      //绘制图标大小的迷你频谱
277     void DrawSearchBox(CRect rect, UiElement::SearchBox* search_box);
278 
279     // 实际绘制一个图标
280     void DrawUiIcon(const CRect& rect, IconMgr::IconType icon_type, IconMgr::IconStyle icon_style = IconMgr::IconStyle::IS_Auto, IconMgr::IconSize icon_size = IconMgr::IconSize::IS_DPI_16);
281     // 绘制一个UI按钮 (使用GetBtnIconType取得的图标)
282     void DrawUIButton(const CRect& rect, BtnKey key_type, bool big_icon = false, bool show_text = false, int font_size = 9, bool checked = false);
283     void DrawUIButton(const CRect& rect, UIButton& btn, IconMgr::IconType icon_type, bool big_icon = false, const std::wstring& text = std::wstring(), int font_size = 9, bool checked = false);
284     // 绘制一个工具条按钮(将rect四面缩小 DPI(2) 后调用DrawUIButton)
285     void DrawControlBarBtn(CRect rect, BtnKey btn_type);
286     // 绘制一个UI按钮,以text文本作为图标
287     void DrawTextButton(CRect rect, BtnKey btn_type, LPCTSTR text, bool checked = false);
288 
289     virtual void AddMouseToolTip(BtnKey btn, LPCTSTR str);      //为一个按钮添加鼠标提示
290     virtual void UpdateMouseToolTip(BtnKey btn, LPCTSTR str);
UpdateMouseToolTip(int btn,LPCTSTR str)291     virtual void UpdateMouseToolTip(int btn, LPCTSTR str) override { UpdateMouseToolTip(static_cast<BtnKey>(btn), str); }
292     virtual void UpdateMouseToolTipPosition(int btn, CRect rect);
293 
294     virtual void UpdateToolTipPosition() override;
295 
296     virtual void AddToolTips();         //为每一个按钮添加鼠标提示(由于按钮的矩形区域只有在第一次绘图之后才能确定,所以此函数必须在第一次绘图之后调用)
297 
298     //当绘图区域的原点不是窗口的原点的时候需要使用这两个函数在绘图区域坐标和窗口坐标中转换
299     //现在这两个函数已弃用
300     static CRect DrawAreaToClient(CRect rect, CRect draw_area);
301     static CRect ClientAreaToDraw(CRect rect, CRect draw_area);
302 
303     bool IsDrawNarrowMode() const;            //是否使用窄界面模式绘图
304     bool IsDrawBackgroundAlpha() const; //是否需要绘制透明背景
305     virtual bool IsDrawStatusBar() const;       //是否需要绘制状态栏
306     virtual bool IsDrawTitleBar() const;        //是否需要绘制标题栏
307     virtual bool IsDrawMenuBar() const;         //是否需要绘制菜单栏
308 
309     static wstring GetDisplayFormatString();       //获取显示格式的字符串
310     CString GetVolumeTooltipString();       //获取音量鼠标提示字符串
311 
312     int DPI(int pixel) const;
313     int DPI(double pixel) const;
314     double DPIDouble(double pixel);
315     double GetScrollTextPixel(bool slower = false);       //计算滚动文本一次滚动的像素值,如果slower为true,则滚动得稍微慢一点
316     int CalculateRoundRectRadius(const CRect& rect);        //计算绘制圆角矩形的半径
317 
318     virtual bool IsDrawLargeIcon() const;        //是否绘制大图标
319 
SwitchStackElement()320     virtual void SwitchStackElement() {}
321 
322     bool IsMiniMode() const;
323 
324 public:
GetUiIndex()325     virtual int GetUiIndex() { return 1; }  //UI的序号,用于区分每个界面,不会为0
326 
327     void ShowUiTipInfo(const std::wstring& info);       //在界面的中央显示一个提示信息,几秒钟后自动消失
328 
329 private:
330     void SetRepeatModeToolTipText();
331     void SetSongInfoToolTipText();
332     void SetCoverToolTipText();
333     void DrawPlayTag(CRect rect, LPCTSTR str_text);
334 
335     void DrawStatusBar(CRect rect, bool reset = false);
336     void DrawTitleBar(CRect rect);
337 
338     int GetToolTipIdOffset();
339 
340 protected:
341     CWnd* m_pMainWnd = nullptr;
342     CDC* m_pDC;
343     UIColors m_colors;
344     CUIDrawer m_draw{ m_colors };       //用于绘制文本的对象
345     SLayoutData m_layout;
346     //CFont m_font_time;
347     DrawData m_draw_data;
348 
349 
350     CToolTipCtrl m_tool_tip;
351 
352     wstring m_repeat_mode_tip;
353     wstring m_info_tip;
354     wstring m_cover_tip;
355 
356     UIData& m_ui_data;
357 
358     //UI 数据
359     CRect m_draw_rect;                      //绘图区域
360     bool m_show_volume_adj{ false };        //显示音量调整按钮
361     bool m_show_volume_text{};        //是否显示音量文本
362 
363     std::map<BtnKey, UIButton> m_buttons;
364 
365     const int m_progress_on_top_threshold = theApp.DPI(350);        //当控制条的宽度小于此值,将进度条显示在播放控制按钮的上方
366 
367     bool m_first_draw{ true };
368 
369 private:
370     CBitmap m_mem_bitmap_static;
371 
372     bool m_need_update_tooltip_pos{ false };   //是否需要更新鼠标提示
373 
374     enum { UI_TIP_INFO_TIMER_ID = 1728 };
375 
376     static bool m_show_ui_tip_info;
377     wstring m_ui_tip_info;
378     int m_top_right_buttons_width{};
379 };
380 
381 //用于在UI中设置字体。
382 //在需要设置字体时,创建此类的一个局部对象,它会在构造时设置字体,并在析构时恢复原来的字体
383 class UiFontGuard
384 {
385 public:
UiFontGuard(CPlayerUIBase * _ui,int font_size)386     UiFontGuard(CPlayerUIBase* _ui, int font_size)
387         : ui(_ui)
388     {
389         if (ui != nullptr)
390         {
391             bool big_font{ ui->m_ui_data.full_screen && ui->IsDrawLargeIcon() };
392             //设置字体
393             old_font = ui->m_draw.SetFont(&theApp.m_font_set.GetFontBySize(font_size).GetFont(big_font));
394         }
395     }
~UiFontGuard()396     ~UiFontGuard()
397     {
398         if (ui != nullptr)
399         {
400             //恢复原来的字体
401             if (old_font != nullptr)
402                 ui->m_draw.SetFont(old_font);
403         }
404     }
405 
406 private:
407     CFont* old_font{};
408     CPlayerUIBase* ui{};
409 };
410