-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWorldClockManager.cpp
More file actions
396 lines (328 loc) · 10.4 KB
/
Copy pathWorldClockManager.cpp
File metadata and controls
396 lines (328 loc) · 10.4 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
#include "WorldClockManager.h"
#include "SDF.h"
#include "TimeZone.h"
#include <sstream>
#include <iomanip>
#include <cmath>
using namespace std;
WorldClockManager::WorldClockManager()
{
card_width = 160; //4cm ≈ 160px
card_height = 212; //5.3cm ≈ 212px
cards_per_row = 3; //每行3个
card_margin = 10; //卡片间距10px
clock_radius = 56; //1.4cm ≈ 56px(修正回原尺寸)
max_cards = 20; //最多20个时钟
card_count = 0; //当前时钟数量
scroll_offset = 0; //滚动偏移量初始化为0
clock_cards = new ClockCard[max_cards];
//初始化数组
for (int i = 0; i < max_cards; i++)
{
clock_cards[i].clock_obj = nullptr;
}
//默认添加北京时间
add_city(L"北京");
}
WorldClockManager::~WorldClockManager()
{
//释放所有时钟对象
for (int i = 0; i < card_count; i++)
{
if (clock_cards[i].clock_obj != nullptr)
{
delete clock_cards[i].clock_obj;
clock_cards[i].clock_obj = nullptr;
}
}
//释放数组
delete[] clock_cards;
}
//计算卡片位置(考虑滚动偏移)
void WorldClockManager::calculate_card_position(int index, int& x, int& y)
{
int row = index / cards_per_row; //第几行
int col = index % cards_per_row; //第几列
x = card_margin + col * (card_width + card_margin);
y = 60 + row * (card_height + card_margin) + scroll_offset; //60是导航栏高度,加上滚动偏移
}
//添加城市时钟
bool WorldClockManager::add_city(const wstring& city_name)
{
if (card_count >= max_cards)
{
return false; //已达最大数量
}
//检查是否已存在
for (int i = 0; i < card_count; i++)
{
if (clock_cards[i].city_name == city_name)
{
return false; //城市已存在
}
}
//计算卡片位置
int card_x, card_y;
calculate_card_position(card_count, card_x, card_y);
//计算时钟中心位置(在卡片内居中偏上)
int clock_center_x = card_x + card_width / 2;
int clock_center_y = card_y + clock_radius + 15; //距卡片顶部15px
//创建Clock对象
Clock* new_clock = new Clock(clock_center_x, clock_center_y, clock_radius);
new_clock->set_timezone(city_name); //设置时区(关键!)
new_clock->set_antialiasing(true); //启用抗锯齿
//填充卡片信息
clock_cards[card_count].clock_obj = new_clock;
clock_cards[card_count].city_name = city_name;
clock_cards[card_count].card_x = card_x;
clock_cards[card_count].card_y = card_y;
clock_cards[card_count].card_width = card_width;
clock_cards[card_count].card_height = card_height;
clock_cards[card_count].show_delete_btn = (city_name != L"北京"); //北京不显示删除按钮
card_count++;
return true;
}
//删除城市时钟
bool WorldClockManager::remove_city(int index)
{
if (index < 0 || index >= card_count)
{
return false; //索引无效
}
//不允许删除北京(第一个)
if (index == 0)
{
return false;
}
//释放时钟对象
if (clock_cards[index].clock_obj != nullptr)
{
delete clock_cards[index].clock_obj;
clock_cards[index].clock_obj = nullptr;
}
//后面的卡片前移
for (int i = index; i < card_count - 1; i++)
{
clock_cards[i] = clock_cards[i + 1];
}
card_count--;
//重新计算所有卡片位置
for (int i = 0; i < card_count; i++)
{
int new_x, new_y;
calculate_card_position(i, new_x, new_y);
clock_cards[i].card_x = new_x;
clock_cards[i].card_y = new_y;
//更新时钟中心位置
int clock_center_x = new_x + card_width / 2;
int clock_center_y = new_y + clock_radius + 15;
//重新创建时钟(因为Clock没有set_position方法)
wstring city = clock_cards[i].city_name;
delete clock_cards[i].clock_obj;
Clock* new_clock = new Clock(clock_center_x, clock_center_y, clock_radius);
new_clock->set_timezone(city); //重新设置时区
new_clock->set_antialiasing(true);
clock_cards[i].clock_obj = new_clock;
}
return true;
}
//更新所有时钟
void WorldClockManager::update_all_clocks()
{
for (int i = 0; i < card_count; i++)
{
if (clock_cards[i].clock_obj != nullptr)
{
clock_cards[i].clock_obj->update_time();
}
}
}
//绘制时差信息("今天 +0小时")
void WorldClockManager::draw_time_offset_label(int x, int y, const wstring& city_name)
{
//获取北京和当前城市的时区信息
TimeZoneInfo beijing_tz = TimeZone::get_timezone_by_city(L"北京");
TimeZoneInfo current_tz = TimeZone::get_timezone_by_city(city_name);
//获取当前日期(用于判断夏令时)
time_t now = time(0);
struct tm t;
gmtime_s(&t, &now);
int month = t.tm_mon + 1;
int day = t.tm_mday;
//计算时差
int beijing_offset = TimeZone::get_utc_offset(beijing_tz, month, day);
int current_offset = TimeZone::get_utc_offset(current_tz, month, day);
int hour_diff = current_offset - beijing_offset;
//判断是今天、昨天还是明天
wstring day_label = L"今天";
if (hour_diff <= -12)
{
day_label = L"昨天";
}
else if (hour_diff >= 12)
{
day_label = L"明天";
}
//格式化显示文字
wstringstream woss;
woss << day_label << L" ";
if (hour_diff >= 0)
{
woss << L"+";
}
woss << hour_diff << L"小时";
wstring offset_text = woss.str();
//绘制文字(浅灰色)
settextcolor(RGB(162, 162, 162));
settextstyle(18, 0, L"微软雅黑");
setbkmode(TRANSPARENT);
int text_width = textwidth(offset_text.c_str());
outtextxy(x - text_width / 2, y, offset_text.c_str());
}
//绘制删除按钮(小叉叉)
void WorldClockManager::draw_delete_button(int x, int y)
{
//外圈:浅灰色圆圈
SDF::draw_aa_circle(x, y, 8, RGB(162, 162, 162));
//内部叉叉:深灰色
COLORREF cross_color = RGB(50, 50, 50);
int cross_size = 4;
//画两条交叉的线
SDF::draw_aa_line(x - cross_size, y - cross_size, x + cross_size, y + cross_size, 2, cross_color);
SDF::draw_aa_line(x - cross_size, y + cross_size, x + cross_size, y - cross_size, 2, cross_color);
}
//绘制单个时钟卡片
void WorldClockManager::draw_card(const ClockCard& card)
{
//绘制灰色背景卡片(4cm × 5.3cm)
setfillcolor(RGB(50, 50, 50));
solidroundrect(card.card_x, card.card_y,
card.card_x + card.card_width,
card.card_y + card.card_height, 10, 10); //圆角半径10px
//绘制时钟
if (card.clock_obj != nullptr)
{
//绘制表盘和指针
card.clock_obj->draw_clock_face();
card.clock_obj->draw_hands();
//绘制城市名和时间(白色)
time_t now = time(0);
struct tm t;
gmtime_s(&t, &now);
//根据城市名获取对应的时区信息
TimeZoneInfo tz = TimeZone::get_timezone_by_city(card.city_name);
int month = t.tm_mon + 1;
int day = t.tm_mday;
int utc_offset = TimeZone::get_utc_offset(tz, month, day);
now += utc_offset * 3600; //应用时区偏移
gmtime_s(&t, &now);
//格式化时间字符串
wstring period = (t.tm_hour < 12) ? L"上午" : L"下午";
int display_hour = t.tm_hour % 12;
if (display_hour == 0) display_hour = 12; //0点显示为12点
wstringstream woss;
woss << card.city_name << L" " << period << display_hour << L":"
<< setfill(L'0') << setw(2) << t.tm_min; //分钟数补零显示
wstring time_text = woss.str();
//绘制城市和时间文字
settextcolor(WHITE);
settextstyle(20, 0, L"微软雅黑"); //字号从15改为16(更清晰)
setbkmode(TRANSPARENT);
int text_x = card.card_x + card.card_width / 2;
int text_y = card.card_y + clock_radius * 2 + 30; //表盘下方30px
int text_width = textwidth(time_text.c_str());
outtextxy(text_x - text_width / 2, text_y, time_text.c_str());
//绘制时差信息(第二行)
draw_time_offset_label(text_x, text_y + 24, card.city_name); //行距24px
}
//绘制删除按钮(如果需要)
if (card.show_delete_btn)
{
int btn_x = card.card_x + card.card_width - 15;
int btn_y = card.card_y + 15;
draw_delete_button(btn_x, btn_y);
}
}
//绘制所有时钟
void WorldClockManager::draw_all_clocks()
{
//设置裁剪区域:只在导航栏下方绘制时钟
//这样时钟滚动时不会0"穿透"到导航栏上方
RECT clip_rect;
clip_rect.left = 0;
clip_rect.top = 60; //导航栏高度
clip_rect.right = 520; //窗口宽度
clip_rect.bottom = 680; //窗口高度
//保存当前绘图设备,启用裁剪区域
HDC hdc = GetImageHDC(NULL); //获取当前绘图设备
HRGN clip_region = CreateRectRgn(clip_rect.left, clip_rect.top, clip_rect.right, clip_rect.bottom);
SelectClipRgn(hdc, clip_region); //设置裁剪区域
//在裁剪区域内绘制所有时钟
for (int i = 0; i < card_count; i++)
{
draw_card(clock_cards[i]);
}
//恢复原始裁剪区域(取消限制)
SelectClipRgn(hdc, NULL);
DeleteObject(clip_region); //释放资源
}
//检查删除按钮点击
int WorldClockManager::check_delete_button_click(int mouse_x, int mouse_y)
{
for (int i = 0; i < card_count; i++)
{
if (!clock_cards[i].show_delete_btn)
{
continue; //跳过没有删除按钮的卡片
}
int btn_x = clock_cards[i].card_x + clock_cards[i].card_width - 15;
int btn_y = clock_cards[i].card_y + 15;
//检查鼠标是否在删除按钮范围内
int dx = mouse_x - btn_x;
int dy = mouse_y - btn_y;
if (dx * dx + dy * dy <= 8 * 8)
{
return i; //返回被点击的卡片索引
}
}
return -1; //没有点击删除按钮
}
//滚动功能(鼠标滚轮)
void WorldClockManager::scroll(int delta)
{
scroll_offset += delta;
//计算最大滚动距离(不能滚动到超出内容)
int total_rows = (card_count + cards_per_row - 1) / cards_per_row; //上取整
int total_height = total_rows * (card_height + card_margin);
int visible_height = 680 - 60; //窗口高度 - 导航栏高度
int max_scroll = 0; //不能向下滚动(顶部)
int min_scroll = -(total_height - visible_height); //不能向上滚动超过底部
if (min_scroll > 0) min_scroll = 0; //如果内容少于一屏,不允许滚动
//限制滚动范围
if (scroll_offset > max_scroll)
{
scroll_offset = max_scroll;
}
if (scroll_offset < min_scroll)
{
scroll_offset = min_scroll;
}
//滚动后重新计算所有卡片位置
for (int i = 0; i < card_count; i++)
{
int new_x, new_y;
calculate_card_position(i, new_x, new_y);
clock_cards[i].card_x = new_x;
clock_cards[i].card_y = new_y;
//更新时钟中心位置
int clock_center_x = new_x + card_width / 2;
int clock_center_y = new_y + clock_radius + 15;
//重新创建时钟
wstring city = clock_cards[i].city_name;
delete clock_cards[i].clock_obj;
Clock* new_clock = new Clock(clock_center_x, clock_center_y, clock_radius);
new_clock->set_timezone(city);
new_clock->set_antialiasing(true);
clock_cards[i].clock_obj = new_clock;
}
}