-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
2537 lines (2154 loc) · 108 KB
/
main.cpp
File metadata and controls
2537 lines (2154 loc) · 108 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include <windows.h>
#include <commctrl.h>
#include <math.h>
#include <string>
#include <vector>
#include <set>
#include <gdiplus.h>
#include <time.h>
// 如果定义了__RESOURCE_ICON_ICO__宏,则定义IDI_ICON1标识符
#ifdef __RESOURCE_ICON_ICO__
#define IDI_ICON1 101
#endif
#pragma comment(lib, "comdlg32.lib")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "msimg32.lib")
#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;
// Window class name
const wchar_t g_szClassName[] = L"TransparentWindowClass";
// Menu item IDs
#define IDM_EXIT 1001
#define IDM_SETTINGS 1002
#define IDM_ABOUT 1003
#define IDM_SHOW 1004
#define IDM_LOCK 1005
#define IDM_TOPMOST 1006
// Window size
const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
// Settings window control IDs
#define IDC_SLIDER_OPACITY 1100
#define IDC_SLIDER_TEXT_OPACITY 1125
#define IDC_CHECK_TOPMOST 1101
#define IDC_BUTTON_BG_COLOR 1102
#define IDC_BUTTON_TEXT_COLOR 1103
#define IDC_COMBO_FONT 1104
#define IDC_COMBO_FONT_SIZE 1105
#define IDC_EDIT_WIDTH 1106
#define IDC_EDIT_HEIGHT 1107
#define IDC_EDIT_DISPLAY_TEXT 1108
#define IDC_BUTTON_OK 1111
#define IDC_PANEL_LEFT 1112
#define IDC_PANEL_RIGHT 1113
#define IDC_BUTTON_GENERAL 1114
#define IDC_BUTTON_LOAD 1115
#define IDC_BUTTON_APPLY 1116
#define IDC_BUTTON_STANDBY_DISPLAY 1117
#define IDC_EDIT_STANDBY_TEXT 1118
#define IDC_RADIO_HORIZONTAL_SCROLL 1119
#define IDC_RADIO_VERTICAL_SCROLL 1120
#define IDC_CHECK_TRANSPARENT_MODE 1126
// 单词卡相关ID
#define IDC_BUTTON_WORD_CARDS 1127
#define IDC_BUTTON_LOAD_CSV 1128
#define IDC_EDIT_CSV_PATH 1129
#define IDC_RADIO_SEQUENTIAL 1130
#define IDC_RADIO_RANDOM 1131
#define IDC_EDIT_INTERVAL 1132
#define IDC_CHECK_WORD_CARDS_ENABLED 1133
// Taskbar icon message
#define WM_TRAYMESSAGE (WM_USER + 1)
// Function prototypes
void ApplySettings(HWND hDialogWnd); // 应用设置函数
bool LoadWordCardsFromCsv(const std::wstring& filePath);
void DrawWordCard(Graphics& graphics, HWND hWnd);
void SwitchToNextWordCard();
// Global variables
HMENU g_hPopupMenu = NULL;
NOTIFYICONDATAW g_notifyIconData;
HFONT g_hFont = NULL;
HWND hButtonOK = NULL; // 确定按钮句柄
// 滚动相关的全局变量
int scrollX = 0;
int scrollY = 0;
SIZE textSize = {0};
static bool needCenterInit = true; // 是否需要初始化居中位置
// 单词卡相关全局变量
std::vector<std::vector<std::wstring>> wordCards; // 单词卡列表,每行是一个文本块,每列是一个段落
int currentCardIndex = 0; // 当前显示的卡片索引
DWORD64 lastCardChangeTime = 0; // 上次切换卡片的时间
bool isWordCardsMode = false; // 是否处于单词卡模式
bool needReloadWordCards = false; // 是否需要重新加载单词卡
std::vector<int> shuffledIndices; // 洗牌后的索引列表
int shuffledIndex = 0; // 当前在洗牌列表中的位置
// Settings
// 待机显示滚动类型枚举
enum class StandbyScrollType {
HorizontalScroll = 0, // 横向滚动
VerticalScroll = 1, // 竖向滚动
Static = 2, // 静止显示
};
struct WindowSettings {
BYTE opacity; // 0-255
BYTE textOpacity; // 0-255 文本透明度
bool isTopmost;
COLORREF bgColor;
COLORREF textColor;
std::wstring fontName;
int fontSize;
bool isBold; // 粗体
bool isItalic; // 斜体
bool isUnderline; // 下划线
int width;
int height;
int xPos; // Window X position
int yPos; // Window Y position
bool isLocked;
// 透明模式控制
bool isTransparentBackground; // true: 使用可调节透明度的背景, false: 窗口底色完全透明(仅显示文本)
// 待机显示相关设置
std::wstring standbyDisplayText; // 待机显示文本
StandbyScrollType standbyScrollType; // 滚动类型
int scrollSpeed; // 滚动速度 (1-10)
// 单词卡相关设置
std::wstring wordCardsCsvPath; // CSV文件路径
bool isWordCardsEnabled; // 是否启用单词卡功能
bool isRandomOrder; // 是否随机播放
int playIntervalSeconds; // 播放间隔时间(秒)
};
WindowSettings g_settings = {
200, // opacity
255, // textOpacity - 默认完全不透明
true, // isTopmost
RGB(255, 255, 255), // bgColor
RGB(0, 0, 0), // textColor
L"KaiTi", // fontName - 楷体更加圆润
14, // fontSize
false, // isBold
false, // isItalic
false, // isUnderline
WINDOW_WIDTH, // width
WINDOW_HEIGHT, // height
CW_USEDEFAULT, // xPos (default position)
CW_USEDEFAULT, // yPos (default position)
false, // isLocked
false, // isTransparentBackground - 默认窗口底色完全透明(仅显示文本)
L"待机模式 - 透明窗口应用", // standbyDisplayText
StandbyScrollType::Static, // standbyScrollType - 默认静止显示
2, // scrollSpeed - 默认滚动速度
L"", // wordCardsCsvPath - 初始为空
false, // isWordCardsEnabled - 默认不启用单词卡功能
false, // isRandomOrder - 默认顺序播放
5, // playIntervalSeconds - 默认5秒
};
// Function declarations
void CreateAppPopupMenu();
void CreateTaskbarIcon(HWND hWnd);
void RemoveTaskbarIcon();
void ApplyWindowSettings(HWND hWnd);
HWND CreateSettingsWindow(HWND hWndParent);
void LoadSettingsFromIni();
void SaveSettingsToIni();
// 字体枚举参数结构体
struct EnumParams {
HWND comboBox;
const wchar_t* currentFont;
int* selectedIndex;
std::set<std::wstring>* fontNames; // 用于去重的字体名称集合
};
// 字体枚举回调函数
int CALLBACK EnumFontProc(const LOGFONTW* lplf, const TEXTMETRICW* lptm, DWORD dwType, LPARAM lParam) {
EnumParams* params = reinterpret_cast<EnumParams*>(lParam);
// 只添加非空字体名称
if (lstrlenW(lplf->lfFaceName) > 0) {
std::wstring fontName(lplf->lfFaceName);
// 检查是否已存在该字体(去重)
if (params->fontNames->find(fontName) == params->fontNames->end()) {
// 添加到集合中
params->fontNames->insert(fontName);
// 添加字体到下拉框
int index = SendMessageW(params->comboBox, CB_ADDSTRING, 0, (LPARAM)lplf->lfFaceName);
// 检查是否为当前字体,如果是则记录索引
if (*params->selectedIndex == -1 && params->currentFont &&
_wcsicmp(lplf->lfFaceName, params->currentFont) == 0) {
*params->selectedIndex = index;
}
}
}
return 1; // 继续枚举
}
// Window procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK SettingsWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
// Subwindow procedure callback function
LRESULT CALLBACK PanelProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
// WinMain function - 使用正确的函数声明格式
INT WINAPI WinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow
) {
// 初始化GDI+
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
// Initialize common controls
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_STANDARD_CLASSES | ICC_BAR_CLASSES;
InitCommonControlsEx(&icex);
// Load settings from INI file on program start
LoadSettingsFromIni();
// 初始化随机数生成器,使用当前时间作为种子,确保每次运行程序时随机顺序不同
srand(time(NULL));
// Register window class
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
// 加载图标,如果存在自定义图标则使用,否则使用默认图标
#ifdef __RESOURCE_ICON_ICO__
wc.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
if (!wc.hIcon) {
#endif
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
#ifdef __RESOURCE_ICON_ICO__
}
#endif
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
// 使用空背景刷以避免默认边框效果
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
// 加载小图标,如果存在自定义图标则使用,否则使用默认图标
#ifdef __RESOURCE_ICON_ICO__
wc.hIconSm = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
if (!wc.hIconSm) {
#endif
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
#ifdef __RESOURCE_ICON_ICO__
}
#endif
if (!RegisterClassEx(&wc)) {
MessageBoxW(NULL, L"Window class registration failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Create window without title bar using saved position or default
// 添加WS_EX_TOOLWINDOW样式使窗口不出现在Alt+Tab切换列表中
HWND hwnd = CreateWindowExW(
WS_EX_LAYERED | WS_EX_TOOLWINDOW | (g_settings.isTopmost ? WS_EX_TOPMOST : 0),
g_szClassName,
L"",
WS_POPUP | WS_VISIBLE,
g_settings.xPos, g_settings.yPos, g_settings.width, g_settings.height,
NULL, NULL, hInstance, NULL);
if (hwnd == NULL) {
MessageBoxW(NULL, L"Window creation failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Set initial window properties
ApplyWindowSettings(hwnd);
CreateAppPopupMenu();
CreateTaskbarIcon(hwnd);
// Message loop
MSG msg;
while (GetMessageW(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
// Save settings to INI file on program exit
SaveSettingsToIni();
RemoveTaskbarIcon();
if (g_hPopupMenu) DestroyMenu(g_hPopupMenu);
if (g_hFont) DeleteObject(g_hFont);
// 关闭GDI+
GdiplusShutdown(gdiplusToken);
return (int)msg.wParam;
}
// Create right-click menu
void CreateAppPopupMenu() {
g_hPopupMenu = CreatePopupMenu();
AppendMenuW(g_hPopupMenu, MF_STRING, IDM_SHOW, L"显示窗口");
AppendMenuW(g_hPopupMenu, MF_STRING, IDM_SETTINGS, L"设置");
AppendMenuW(g_hPopupMenu, MF_STRING, IDM_LOCK, L"锁定窗口");
// 添加窗口置顶菜单项
UINT topmostMenuFlags = MF_STRING | (g_settings.isTopmost ? MF_CHECKED : MF_UNCHECKED);
AppendMenuW(g_hPopupMenu, topmostMenuFlags, IDM_TOPMOST, L"窗口置顶");
AppendMenuW(g_hPopupMenu, MF_SEPARATOR, 0, NULL);
AppendMenuW(g_hPopupMenu, MF_STRING, IDM_ABOUT, L"关于");
AppendMenuW(g_hPopupMenu, MF_STRING, IDM_EXIT, L"退出");
}
// Create taskbar icon
void CreateTaskbarIcon(HWND hWnd) {
ZeroMemory(&g_notifyIconData, sizeof(NOTIFYICONDATAW));
g_notifyIconData.cbSize = sizeof(NOTIFYICONDATAW);
g_notifyIconData.hWnd = hWnd;
g_notifyIconData.uID = 100;
g_notifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
g_notifyIconData.uCallbackMessage = WM_TRAYMESSAGE;
// 加载任务栏图标,如果存在自定义图标则使用,否则使用默认图标
#ifdef __RESOURCE_ICON_ICO__
g_notifyIconData.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
if (!g_notifyIconData.hIcon) {
#endif
g_notifyIconData.hIcon = LoadIcon(NULL, IDI_APPLICATION);
#ifdef __RESOURCE_ICON_ICO__
}
#endif
lstrcpyW(g_notifyIconData.szTip, L"Transparent Window App");
Shell_NotifyIconW(NIM_ADD, &g_notifyIconData);
}
// Remove taskbar icon
void RemoveTaskbarIcon() {
Shell_NotifyIconW(NIM_DELETE, &g_notifyIconData);
if (g_notifyIconData.hIcon) DestroyIcon(g_notifyIconData.hIcon);
}
// Load settings from INI file
void LoadSettingsFromIni() {
wchar_t iniPath[MAX_PATH];
GetModuleFileNameW(NULL, iniPath, MAX_PATH);
wcscpy_s(wcsrchr(iniPath, L'.'), MAX_PATH - wcslen(iniPath), L".ini");
// Read settings from INI file
g_settings.opacity = (BYTE)GetPrivateProfileIntW(L"Settings", L"Opacity", g_settings.opacity, iniPath);
g_settings.textOpacity = (BYTE)GetPrivateProfileIntW(L"Settings", L"TextOpacity", g_settings.textOpacity, iniPath);
g_settings.isTopmost = GetPrivateProfileIntW(L"Settings", L"IsTopmost", g_settings.isTopmost, iniPath) != 0;
g_settings.bgColor = GetPrivateProfileIntW(L"Settings", L"BgColor", g_settings.bgColor, iniPath);
g_settings.textColor = GetPrivateProfileIntW(L"Settings", L"TextColor", g_settings.textColor, iniPath);
// Read font name
wchar_t fontName[256];
GetPrivateProfileStringW(L"Settings", L"FontName", g_settings.fontName.c_str(), fontName, 256, iniPath);
g_settings.fontName = fontName;
g_settings.fontSize = GetPrivateProfileIntW(L"Settings", L"FontSize", g_settings.fontSize, iniPath);
g_settings.width = GetPrivateProfileIntW(L"Settings", L"Width", g_settings.width, iniPath);
g_settings.height = GetPrivateProfileIntW(L"Settings", L"Height", g_settings.height, iniPath);
// Read window position
g_settings.xPos = GetPrivateProfileIntW(L"Settings", L"XPos", g_settings.xPos, iniPath);
g_settings.yPos = GetPrivateProfileIntW(L"Settings", L"YPos", g_settings.yPos, iniPath);
// Read font style settings
g_settings.isBold = GetPrivateProfileIntW(L"Settings", L"IsBold", g_settings.isBold, iniPath) != 0;
g_settings.isItalic = GetPrivateProfileIntW(L"Settings", L"IsItalic", g_settings.isItalic, iniPath) != 0;
g_settings.isUnderline = GetPrivateProfileIntW(L"Settings", L"IsUnderline", g_settings.isUnderline, iniPath) != 0;
g_settings.isLocked = GetPrivateProfileIntW(L"Settings", L"IsLocked", g_settings.isLocked, iniPath) != 0;
// 读取透明模式设置
g_settings.isTransparentBackground = GetPrivateProfileIntW(L"Settings", L"IsTransparentBackground", g_settings.isTransparentBackground, iniPath) != 0;
// Load standby display settings - 使用直接文件读取方法以支持多行文本
// 尝试使用GetPrivateProfileStringW读取(向后兼容)
wchar_t standbyText[1024];
GetPrivateProfileStringW(L"Settings", L"StandbyDisplayText", g_settings.standbyDisplayText.c_str(), standbyText, 1024, iniPath);
// 检查是否包含转义的换行符,如果没有,尝试直接从文件读取
std::wstring tempText = standbyText;
if (tempText.find(L"\\n") != std::wstring::npos) {
// 包含转义的换行符,进行替换
size_t pos = 0;
while ((pos = tempText.find(L"\\n", pos)) != std::wstring::npos) {
tempText.replace(pos, 2, L"\n");
pos += 1; // 跳过替换的字符
}
g_settings.standbyDisplayText = tempText;
} else {
// 尝试直接从文件读取整个INI内容,手动解析
HANDLE hFile = CreateFileW(iniPath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile != INVALID_HANDLE_VALUE) {
DWORD fileSize = GetFileSize(hFile, nullptr);
std::vector<char> fileData(fileSize + 1);
DWORD bytesRead;
if (ReadFile(hFile, fileData.data(), fileSize, &bytesRead, nullptr)) {
fileData[bytesRead] = '\0';
// 转换为宽字符串
int wideSize = MultiByteToWideChar(CP_ACP, 0, fileData.data(), -1, nullptr, 0);
std::vector<wchar_t> wideBuffer(wideSize);
MultiByteToWideChar(CP_ACP, 0, fileData.data(), -1, wideBuffer.data(), wideSize);
std::wstring fileContent(wideBuffer.data());
// 手动解析StandbyDisplayText部分
std::wstring sectionStart = L"[Settings]";
std::wstring keyStart = L"StandbyDisplayText=";
size_t sectionPos = fileContent.find(sectionStart);
if (sectionPos != std::wstring::npos) {
size_t keyPos = fileContent.find(keyStart, sectionPos);
if (keyPos != std::wstring::npos) {
keyPos += keyStart.length();
// 查找值的结束位置(下一个节或文件结束)
size_t endPos = fileContent.find(L"\n", keyPos);
if (endPos == std::wstring::npos) {
endPos = fileContent.length();
}
// 提取值并处理多行情况
std::wstring value = fileContent.substr(keyPos, endPos - keyPos);
// 检查并移除开头和结尾的引号(如果存在)
if (value.length() >= 2 && value[0] == '"' && value[value.length() - 1] == '"') {
value = value.substr(1, value.length() - 2);
}
// 替换转义的换行符
size_t pos = 0;
while ((pos = value.find(L"\\n", pos)) != std::wstring::npos) {
value.replace(pos, 2, L"\n");
pos += 1;
}
g_settings.standbyDisplayText = value;
}
}
}
CloseHandle(hFile);
}
}
int scrollType = GetPrivateProfileIntW(L"Settings", L"StandbyScrollType", (int)g_settings.standbyScrollType, iniPath);
// Validate scroll type value
if (scrollType >= (int)StandbyScrollType::HorizontalScroll && scrollType <= (int)StandbyScrollType::Static) {
g_settings.standbyScrollType = (StandbyScrollType)scrollType;
}
// Load scroll speed
g_settings.scrollSpeed = GetPrivateProfileIntW(L"Settings", L"ScrollSpeed", g_settings.scrollSpeed, iniPath);
// Validate speed value (1-10)
if (g_settings.scrollSpeed < 1) g_settings.scrollSpeed = 1;
if (g_settings.scrollSpeed > 10) g_settings.scrollSpeed = 10;
// Load word cards settings
wchar_t csvPath[1024];
GetPrivateProfileStringW(L"Settings", L"WordCardsCsvPath", g_settings.wordCardsCsvPath.c_str(), csvPath, 1024, iniPath);
g_settings.wordCardsCsvPath = csvPath;
g_settings.isRandomOrder = GetPrivateProfileIntW(L"Settings", L"IsRandomOrder", g_settings.isRandomOrder, iniPath) != 0;
// 从INI加载间隔设置
int loadedInterval = GetPrivateProfileIntW(L"Settings", L"PlayIntervalSeconds", g_settings.playIntervalSeconds, iniPath);
g_settings.playIntervalSeconds = loadedInterval;
g_settings.isWordCardsEnabled = GetPrivateProfileIntW(L"Settings", L"IsWordCardsEnabled", g_settings.isWordCardsEnabled, iniPath) != 0;
// Validate interval value (1-300 seconds)
if (g_settings.playIntervalSeconds < 1) g_settings.playIntervalSeconds = 1;
if (g_settings.playIntervalSeconds > 300) g_settings.playIntervalSeconds = 300;
}
// Save settings to INI file
void SaveSettingsToIni() {
wchar_t iniPath[MAX_PATH];
GetModuleFileNameW(NULL, iniPath, MAX_PATH);
wcscpy_s(wcsrchr(iniPath, L'.'), MAX_PATH - wcslen(iniPath), L".ini");
// Write settings to INI file
wchar_t buffer[32];
swprintf_s(buffer, 32, L"%d", g_settings.opacity);
WritePrivateProfileStringW(L"Settings", L"Opacity", buffer, iniPath);
swprintf_s(buffer, 32, L"%d", g_settings.textOpacity);
WritePrivateProfileStringW(L"Settings", L"TextOpacity", buffer, iniPath);
swprintf_s(buffer, 32, L"%d", g_settings.isTopmost ? 1 : 0);
WritePrivateProfileStringW(L"Settings", L"IsTopmost", buffer, iniPath);
swprintf_s(buffer, 32, L"%d", g_settings.bgColor);
WritePrivateProfileStringW(L"Settings", L"BgColor", buffer, iniPath);
swprintf_s(buffer, 32, L"%d", g_settings.textColor);
WritePrivateProfileStringW(L"Settings", L"TextColor", buffer, iniPath);
WritePrivateProfileStringW(L"Settings", L"FontName", g_settings.fontName.c_str(), iniPath);
swprintf_s(buffer, 32, L"%d", g_settings.fontSize);
WritePrivateProfileStringW(L"Settings", L"FontSize", buffer, iniPath);
swprintf_s(buffer, 32, L"%d", g_settings.width);
WritePrivateProfileStringW(L"Settings", L"Width", buffer, iniPath);
swprintf_s(buffer, 32, L"%d", g_settings.height);
WritePrivateProfileStringW(L"Settings", L"Height", buffer, iniPath);
// Save word cards settings
WritePrivateProfileStringW(L"Settings", L"WordCardsCsvPath", g_settings.wordCardsCsvPath.c_str(), iniPath);
swprintf_s(buffer, 32, L"%d", g_settings.isRandomOrder ? 1 : 0);
WritePrivateProfileStringW(L"Settings", L"IsRandomOrder", buffer, iniPath);
swprintf_s(buffer, 32, L"%d", g_settings.playIntervalSeconds);
WritePrivateProfileStringW(L"Settings", L"PlayIntervalSeconds", buffer, iniPath);
swprintf_s(buffer, 32, L"%d", g_settings.isWordCardsEnabled ? 1 : 0);
WritePrivateProfileStringW(L"Settings", L"IsWordCardsEnabled", buffer, iniPath);
// Save window position
swprintf_s(buffer, 32, L"%d", g_settings.xPos);
WritePrivateProfileStringW(L"Settings", L"XPos", buffer, iniPath);
swprintf_s(buffer, 32, L"%d", g_settings.yPos);
WritePrivateProfileStringW(L"Settings", L"YPos", buffer, iniPath);
// Save font style settings
swprintf_s(buffer, 32, L"%d", g_settings.isBold ? 1 : 0);
WritePrivateProfileStringW(L"Settings", L"IsBold", buffer, iniPath);
swprintf_s(buffer, 32, L"%d", g_settings.isItalic ? 1 : 0);
WritePrivateProfileStringW(L"Settings", L"IsItalic", buffer, iniPath);
swprintf_s(buffer, 32, L"%d", g_settings.isUnderline ? 1 : 0);
WritePrivateProfileStringW(L"Settings", L"IsUnderline", buffer, iniPath);
swprintf_s(buffer, 32, L"%d", g_settings.isLocked ? 1 : 0);
WritePrivateProfileStringW(L"Settings", L"IsLocked", buffer, iniPath);
// 保存透明模式设置
swprintf_s(buffer, 32, L"%d", g_settings.isTransparentBackground ? 1 : 0);
WritePrivateProfileStringW(L"Settings", L"IsTransparentBackground", buffer, iniPath);
// Save standby display settings - 使用更可靠的方式保存多行文本
// 对换行符进行转义
std::wstring escapedText = g_settings.standbyDisplayText;
size_t pos = 0;
while ((pos = escapedText.find(L"\n", pos)) != std::wstring::npos) {
escapedText.replace(pos, 1, L"\\n");
pos += 2; // 跳过替换的字符
}
// 使用WritePrivateProfileStringW写入(基本支持)
WritePrivateProfileStringW(L"Settings", L"StandbyDisplayText", escapedText.c_str(), iniPath);
// 为了确保多行文本正确保存,我们使用直接文件操作进行修复
HANDLE hFile = CreateFileW(iniPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile != INVALID_HANDLE_VALUE) {
DWORD fileSize = GetFileSize(hFile, nullptr);
std::vector<char> fileData(fileSize + 1);
DWORD bytesRead;
if (ReadFile(hFile, fileData.data(), fileSize, &bytesRead, nullptr)) {
fileData[bytesRead] = '\0';
// 转换为宽字符串进行处理
int wideSize = MultiByteToWideChar(CP_ACP, 0, fileData.data(), -1, nullptr, 0);
std::vector<wchar_t> wideBuffer(wideSize);
MultiByteToWideChar(CP_ACP, 0, fileData.data(), -1, wideBuffer.data(), wideSize);
std::wstring fileContent(wideBuffer.data());
// 查找并替换StandbyDisplayText行
std::wstring sectionStart = L"[Settings]";
std::wstring keyStart = L"StandbyDisplayText=";
size_t sectionPos = fileContent.find(sectionStart);
if (sectionPos != std::wstring::npos) {
size_t keyPos = fileContent.find(keyStart, sectionPos);
if (keyPos != std::wstring::npos) {
// 查找当前值的结束位置
size_t endPos = fileContent.find(L"\n", keyPos);
if (endPos == std::wstring::npos) {
endPos = fileContent.length();
}
// 构建新的键值对行,用引号包裹值以支持特殊字符
std::wstring newValue = keyStart + L"\"" + escapedText + L"\"";
// 替换旧的键值对
fileContent.replace(keyPos, endPos - keyPos, newValue);
// 转换回多字节字符串并写回文件
int multiSize = WideCharToMultiByte(CP_ACP, 0, fileContent.c_str(), -1, nullptr, 0, nullptr, nullptr);
std::vector<char> multiBuffer(multiSize);
WideCharToMultiByte(CP_ACP, 0, fileContent.c_str(), -1, multiBuffer.data(), multiSize, nullptr, nullptr);
// 写回文件
SetFilePointer(hFile, 0, nullptr, FILE_BEGIN);
SetEndOfFile(hFile);
DWORD bytesWritten;
WriteFile(hFile, multiBuffer.data(), multiSize - 1, &bytesWritten, nullptr);
}
}
}
CloseHandle(hFile);
}
swprintf_s(buffer, 32, L"%d", (int)g_settings.standbyScrollType);
WritePrivateProfileStringW(L"Settings", L"StandbyScrollType", buffer, iniPath);
// Save scroll speed
swprintf_s(buffer, 32, L"%d", g_settings.scrollSpeed);
WritePrivateProfileStringW(L"Settings", L"ScrollSpeed", buffer, iniPath);
// Save float speed
// 移除漂移速度相关设置
}
// Clear all controls in the right panel
void ClearRightPanel(HWND hPanelRight) {
EnumChildWindows(hPanelRight, [](HWND hwndChild, LPARAM) -> BOOL {
// 不删除确定和应用按钮,但这些按钮现在是设置窗口的直接子窗口,而不是面板的子窗口
// 因此这个检查可能不再必要,但保留以防万一
DWORD_PTR id = GetWindowLongPtr(hwndChild, GWLP_ID);
if (id != IDC_BUTTON_OK && id != IDC_BUTTON_APPLY) {
DestroyWindow(hwndChild);
}
return TRUE;
}, 0);
// 刷新面板,确保旧控件被完全清除
InvalidateRect(hPanelRight, NULL, TRUE);
UpdateWindow(hPanelRight);
}
// Show general settings panel
void ShowGeneralSettingsPanel(HWND hWnd, HWND hPanelRight) {
ClearRightPanel(hPanelRight);
// 确保在添加面板内容前,应用和确定按钮总是可见的
// Create SimSun font for all labels
HFONT hSimSunFontEarly = CreateFontW(
11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, L"SimSun");
// Store the font handle in window properties for cleanup later
SetPropW(hWnd, L"SimSunFontEarly", (HANDLE)hSimSunFontEarly);
// Create width label and edit box - 调整位置避免超出面板范围
HWND hLabelWidth = CreateWindowW(L"STATIC", L"窗口宽度:",
WS_CHILD | WS_VISIBLE | SS_CENTER, 10, 20, 80, 20,
hPanelRight, NULL, GetModuleHandle(NULL), NULL);
SendMessageW(hLabelWidth, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
HWND hEditWidth = CreateWindowW(L"EDIT", NULL,
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NUMBER,
100, 20, 80, 20, hPanelRight, (HMENU)IDC_EDIT_WIDTH,
GetModuleHandle(NULL), NULL);
SendMessageW(hEditWidth, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
// 设置初始宽度值
wchar_t widthText[10];
swprintf_s(widthText, 10, L"%d", g_settings.width);
SetWindowTextW(hEditWidth, widthText);
// Create height label and edit box - 调整位置避免超出面板范围
HWND hLabelHeight = CreateWindowW(L"STATIC", L"窗口高度:",
WS_CHILD | WS_VISIBLE | SS_CENTER, 190, 20, 70, 20,
hPanelRight, NULL, GetModuleHandle(NULL), NULL);
SendMessageW(hLabelHeight, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
HWND hEditHeight = CreateWindowW(L"EDIT", NULL,
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NUMBER,
270, 20, 70, 20, hPanelRight, (HMENU)IDC_EDIT_HEIGHT,
GetModuleHandle(NULL), NULL);
SendMessageW(hEditHeight, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
// 设置初始高度值
wchar_t heightText[10];
swprintf_s(heightText, 10, L"%d", g_settings.height);
SetWindowTextW(hEditHeight, heightText);
// Create font name label and combo box - 调整大小避免超出面板范围
HWND hLabelFont = CreateWindowW(L"STATIC", L"字体名称:",
WS_CHILD | WS_VISIBLE | SS_CENTER, 10, 60, 80, 20,
hPanelRight, NULL, GetModuleHandle(NULL), NULL);
SendMessageW(hLabelFont, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
HWND hComboFont = CreateWindowW(L"COMBOBOX", NULL,
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | WS_BORDER | WS_VSCROLL,
100, 60, 150, 100, hPanelRight, (HMENU)IDC_COMBO_FONT,
GetModuleHandle(NULL), NULL);
// 设置下拉列表的最大显示高度,允许显示更多选项
SendMessageW(hComboFont, CB_SETMINVISIBLE, (WPARAM)15, 0); // 设置最多可见15个选项
SendMessageW(hComboFont, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
// 创建枚举参数和字体名称集合(用于去重)
int selectedIndex = -1;
std::set<std::wstring> fontNames;
EnumParams params;
params.comboBox = hComboFont;
params.currentFont = g_settings.fontName.c_str();
params.selectedIndex = &selectedIndex;
params.fontNames = &fontNames;
// 设置LOGFONT结构以枚举所有字体
LOGFONTW logFont;
ZeroMemory(&logFont, sizeof(LOGFONTW));
// 使用DEFAULT_CHARSET并设置lfPitchAndFamily为0以枚举所有字体
logFont.lfCharSet = DEFAULT_CHARSET;
logFont.lfPitchAndFamily = 0; // 不限制字体的间距和字体系列
// 调用枚举函数,使用0标志确保枚举所有字体
// 通过logFont.lfPitchAndFamily = 0确保枚举所有字体类型
EnumFontFamiliesExW(GetDC(NULL), &logFont, EnumFontProc, (LPARAM)¶ms, 0);
// 如果找到了匹配的字体,设置选中状态;否则默认选择第一个字体
if (selectedIndex >= 0) {
SendMessageW(hComboFont, CB_SETCURSEL, selectedIndex, 0);
} else {
// 检查下拉框中是否有项目,如果有则选择第一个
int count = SendMessageW(hComboFont, CB_GETCOUNT, 0, 0);
if (count > 0) {
SendMessageW(hComboFont, CB_SETCURSEL, 0, 0);
}
}
// Create font size label and combo box - 调整位置避免超出面板范围
HWND hLabelFontSize = CreateWindowW(L"STATIC", L"字体大小:",
WS_CHILD | WS_VISIBLE | SS_CENTER, 260, 60, 60, 20,
hPanelRight, NULL, GetModuleHandle(NULL), NULL);
SendMessageW(hLabelFontSize, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
HWND hComboFontSize = CreateWindowW(L"COMBOBOX", NULL,
WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | WS_BORDER,
330, 60, 50, 100, hPanelRight, (HMENU)IDC_COMBO_FONT_SIZE,
GetModuleHandle(NULL), NULL);
SendMessageW(hComboFontSize, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
// Add font size options
const wchar_t* fontSizeOptions[] = { L"12", L"14", L"16", L"18", L"20", L"24", L"28", L"32" };
for (int i = 0; i < 8; i++) {
SendMessage(hComboFontSize, CB_ADDSTRING, 0, (LPARAM)fontSizeOptions[i]);
}
// Set current font size
wchar_t fontSizeText[10];
swprintf_s(fontSizeText, 10, L"%d", g_settings.fontSize);
SetWindowTextW(hComboFontSize, fontSizeText);
// Create opacity label and slider
HWND hLabelOpacity = CreateWindowW(L"STATIC", L"窗口透明度:",
WS_CHILD | WS_VISIBLE | SS_CENTER, 10, 100, 80, 20,
hPanelRight, NULL, GetModuleHandle(NULL), NULL);
SendMessageW(hLabelOpacity, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
HWND hSliderOpacity = CreateWindowW(TRACKBAR_CLASSW, NULL,
WS_CHILD | WS_VISIBLE | TBS_HORZ | TBS_AUTOTICKS | TBS_ENABLESELRANGE,
100, 100, 200, 30, hPanelRight, (HMENU)IDC_SLIDER_OPACITY,
GetModuleHandle(NULL), NULL);
SendMessage(hSliderOpacity, TBM_SETRANGE, TRUE, MAKELPARAM(0, 255));
SendMessage(hSliderOpacity, TBM_SETPOS, TRUE, (LPARAM)g_settings.opacity);
// Create text opacity label and slider
HWND hLabelTextOpacity = CreateWindowW(L"STATIC", L"文本透明度:",
WS_CHILD | WS_VISIBLE | SS_CENTER, 10, 140, 80, 20,
hPanelRight, NULL, GetModuleHandle(NULL), NULL);
SendMessageW(hLabelTextOpacity, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
HWND hSliderTextOpacity = CreateWindowW(TRACKBAR_CLASSW, NULL,
WS_CHILD | WS_VISIBLE | TBS_HORZ | TBS_AUTOTICKS | TBS_ENABLESELRANGE,
100, 140, 200, 30, hPanelRight, (HMENU)IDC_SLIDER_TEXT_OPACITY,
GetModuleHandle(NULL), NULL);
SendMessage(hSliderTextOpacity, TBM_SETRANGE, TRUE, MAKELPARAM(0, 255));
SendMessage(hSliderTextOpacity, TBM_SETPOS, TRUE, (LPARAM)g_settings.textOpacity);
// Create transparent mode checkbox
HWND hCheckTransparentMode = CreateWindowW(L"BUTTON", L"显示背景色",
WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
100, 180, 200, 20, hPanelRight, (HMENU)IDC_CHECK_TRANSPARENT_MODE,
GetModuleHandle(NULL), NULL);
SendMessageW(hCheckTransparentMode, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
if (g_settings.isTransparentBackground) SendMessage(hCheckTransparentMode, BM_SETCHECK, BST_CHECKED, 0);
// Create word cards enable checkbox
// Create background color label and button
HWND hLabelBgColor = CreateWindowW(L"STATIC", L"背景颜色:",
WS_CHILD | WS_VISIBLE | SS_CENTER, 10, 220, 80, 20,
hPanelRight, NULL, GetModuleHandle(NULL), NULL);
SendMessageW(hLabelBgColor, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
HWND hButtonBgColor = CreateWindowW(L"BUTTON", L"选择颜色",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
100, 220, 100, 25, hPanelRight, (HMENU)IDC_BUTTON_BG_COLOR,
GetModuleHandle(NULL), NULL);
SendMessageW(hButtonBgColor, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
// Create text color label and button - 调整位置避免超出面板范围
HWND hLabelTextColor = CreateWindowW(L"STATIC", L"文本颜色:",
WS_CHILD | WS_VISIBLE | SS_CENTER, 10, 260, 80, 20,
hPanelRight, NULL, GetModuleHandle(NULL), NULL);
SendMessageW(hLabelTextColor, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
HWND hButtonTextColor = CreateWindowW(L"BUTTON", L"选择颜色",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
100, 260, 100, 25, hPanelRight, (HMENU)IDC_BUTTON_TEXT_COLOR,
GetModuleHandle(NULL), NULL);
SendMessageW(hButtonTextColor, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
// Create font style options - 调整位置避免超出面板范围
HWND hLabelFontStyle = CreateWindowW(L"STATIC", L"字体样式:",
WS_CHILD | WS_VISIBLE | SS_CENTER, 10, 300, 100, 20,
hPanelRight, NULL, GetModuleHandle(NULL), NULL);
SendMessageW(hLabelFontStyle, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
HWND hCheckBold = CreateWindowW(L"BUTTON", L"粗体",
WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
90, 300, 60, 20, hPanelRight, (HMENU)1120,
GetModuleHandle(NULL), NULL);
SendMessageW(hCheckBold, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
if (g_settings.isBold) SendMessage(hCheckBold, BM_SETCHECK, BST_CHECKED, 0);
HWND hCheckItalic = CreateWindowW(L"BUTTON", L"斜体",
WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
160, 300, 60, 20, hPanelRight, (HMENU)1121,
GetModuleHandle(NULL), NULL);
SendMessageW(hCheckItalic, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
if (g_settings.isItalic) SendMessage(hCheckItalic, BM_SETCHECK, BST_CHECKED, 0);
HWND hCheckUnderline = CreateWindowW(L"BUTTON", L"下划线",
WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
230, 300, 70, 20, hPanelRight, (HMENU)1122,
GetModuleHandle(NULL), NULL);
SendMessageW(hCheckUnderline, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
if (g_settings.isUnderline) SendMessage(hCheckUnderline, BM_SETCHECK, BST_CHECKED, 0);
// 按钮已移至SettingsWndProc的WM_CREATE中创建,此处不再重复创建
// 保存控件句柄到全局变量 - 直接使用CreateWindowW返回值
// 这里不需要再调用GetDlgItem,因为我们已经有了句柄
// 将应用和确定按钮置于顶层,确保它们不会被面板内容覆盖
// 使用局部变量避免命名冲突
HWND btnApply = GetDlgItem(hWnd, IDC_BUTTON_APPLY);
HWND btnOK = GetDlgItem(hWnd, IDC_BUTTON_OK);
if (btnApply) SetWindowPos(btnApply, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
if (btnOK) SetWindowPos(btnOK, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
// 单词卡设置面板函数
void ShowWordCardsPanel(HWND hWnd, HWND hPanelRight) {
ClearRightPanel(hPanelRight);
// 确保在添加面板内容前,应用和确定按钮总是可见的
// Create SimSun font for all labels
HFONT hSimSunFontEarly = CreateFontW(
11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, L"SimSun");
// Store the font handle in window properties for cleanup later
SetPropW(hWnd, L"SimSunFontWordCards", (HANDLE)hSimSunFontEarly);
// 创建CSV文件路径标签
HWND hLabelCsvPath = CreateWindowW(L"STATIC", L"单词卡CSV文件:",
WS_CHILD | WS_VISIBLE | SS_CENTER, 10, 20, 90, 20,
hPanelRight, NULL, GetModuleHandle(NULL), NULL);
SendMessageW(hLabelCsvPath, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
// 创建CSV文件路径显示框
HWND hEditCsvPath = CreateWindowW(L"EDIT", g_settings.wordCardsCsvPath.c_str(),
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_READONLY | ES_AUTOHSCROLL,
110, 20, 200, 20, hPanelRight, (HMENU)IDC_EDIT_CSV_PATH,
GetModuleHandle(NULL), NULL);
SendMessageW(hEditCsvPath, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
// 创建加载CSV按钮
HWND hButtonLoadCsv = CreateWindowW(L"BUTTON", L"浏览...", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
320, 20, 60, 20, hPanelRight, (HMENU)IDC_BUTTON_LOAD_CSV, GetModuleHandle(NULL), NULL);
SendMessageW(hButtonLoadCsv, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
// 创建播放顺序标签
HWND hLabelPlayOrder = CreateWindowW(L"STATIC", L"播放顺序:",
WS_CHILD | WS_VISIBLE | SS_CENTER, 10, 60, 80, 20,
hPanelRight, NULL, GetModuleHandle(NULL), NULL);
SendMessageW(hLabelPlayOrder, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
// 创建顺序播放单选按钮
HWND hRadioSequential = CreateWindowW(L"BUTTON", L"顺序播放",
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
100, 60, 100, 20, hPanelRight, (HMENU)IDC_RADIO_SEQUENTIAL,
GetModuleHandle(NULL), NULL);
SendMessageW(hRadioSequential, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
// 创建随机播放单选按钮
HWND hRadioRandom = CreateWindowW(L"BUTTON", L"随机播放",
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
210, 60, 100, 20, hPanelRight, (HMENU)IDC_RADIO_RANDOM,
GetModuleHandle(NULL), NULL);
SendMessageW(hRadioRandom, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
// 根据当前设置选择单选按钮
if (g_settings.isRandomOrder) {
SendMessage(hRadioRandom, BM_SETCHECK, BST_CHECKED, 0);
} else {
SendMessage(hRadioSequential, BM_SETCHECK, BST_CHECKED, 0);
}
// 创建播放间隔标签
HWND hLabelInterval = CreateWindowW(L"STATIC", L"播放间隔(秒):",
WS_CHILD | WS_VISIBLE | SS_CENTER, 10, 100, 100, 20,
hPanelRight, NULL, GetModuleHandle(NULL), NULL);
SendMessageW(hLabelInterval, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
// 创建播放间隔输入框
wchar_t intervalStr[10];
swprintf_s(intervalStr, 10, L"%d", g_settings.playIntervalSeconds);
HWND hEditInterval = CreateWindowW(L"EDIT", intervalStr,
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NUMBER,
100, 100, 60, 20, hPanelRight, (HMENU)IDC_EDIT_INTERVAL,
GetModuleHandle(NULL), NULL);
SendMessageW(hEditInterval, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
HWND hCheckWordCardsEnabled = CreateWindowW(L"BUTTON", L"启用单词卡功能",
WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
100, 200, 200, 20, hPanelRight, (HMENU)IDC_CHECK_WORD_CARDS_ENABLED,
GetModuleHandle(NULL), NULL);
SendMessageW(hCheckWordCardsEnabled, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
if (g_settings.isWordCardsEnabled) SendMessage(hCheckWordCardsEnabled, BM_SETCHECK, BST_CHECKED, 0);
// 将应用和确定按钮置于顶层,确保它们不会被面板内容覆盖
HWND btnApply = GetDlgItem(hWnd, IDC_BUTTON_APPLY);
HWND btnOK = GetDlgItem(hWnd, IDC_BUTTON_OK);
if (btnApply) SetWindowPos(btnApply, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
if (btnOK) SetWindowPos(btnOK, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
// Show standby display settings panel
void ShowStandbyDisplayPanel(HWND hWnd, HWND hPanelRight) {
ClearRightPanel(hPanelRight);
// 确保在添加面板内容前,应用和确定按钮总是可见的
// Create SimSun font for all labels
HFONT hSimSunFontEarly = CreateFontW(
11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, L"SimSun");
// Store the font handle in window properties for cleanup later
SetPropW(hWnd, L"SimSunFontStandby", (HANDLE)hSimSunFontEarly);
// Create standby display text label
HWND hLabelStandbyText = CreateWindowW(L"STATIC", L"待机显示文本:",
WS_CHILD | WS_VISIBLE | SS_CENTER, 10, 20, 100, 20,
hPanelRight, NULL, GetModuleHandle(NULL), NULL);
SendMessageW(hLabelStandbyText, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
// Create multiline text box for standby display text
HWND hEditStandbyText = CreateWindowW(L"EDIT", g_settings.standbyDisplayText.c_str(),
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN,
120, 20, 260, 120, hPanelRight, (HMENU)IDC_EDIT_STANDBY_TEXT,
GetModuleHandle(NULL), NULL);
SendMessageW(hEditStandbyText, WM_SETFONT, (WPARAM)hSimSunFontEarly, TRUE);
// Create scrolling type label
HWND hLabelScrollType = CreateWindowW(L"STATIC", L"滚动方式:",
WS_CHILD | WS_VISIBLE | SS_CENTER, 10, 160, 100, 20,
hPanelRight, NULL, GetModuleHandle(NULL), NULL);