Skip to content

Commit 20c78ef

Browse files
feat: finished the advanced tutorials, but still waiting refactorize
1 parent 9dafb3d commit 20c78ef

458 files changed

Lines changed: 39124 additions & 110 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ cd Tutorial_AwesomeQt
5454
## 当前进度
5555

5656
```
57-
入门层 ██████████ 134 / 134 篇(代码示例) · 137 篇教程文章
58-
进阶层 ░░░░░░░░░░ 0 / 137 篇
59-
专家层 ░░░░░░░░░░ 0 / 142 篇
57+
入门层 ██████████ 137 / 137 篇教程 · 134 个代码示例
58+
进阶层 ██████████ 134 / 134 篇教程 · 134 个代码示例
59+
专家层 ░░░░░░░░░░ 0 / 142 篇(规划中)
60+
合计 ██████░░░░ 271 / 413 篇
6061
```
6162

6263
🚀🚀🚀 更加详细的进度:[tutorial/index.md](tutorial/index.md)

TODO.md

Lines changed: 52 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,17 @@
99
## 🔢 总体进度
1010

1111
```
12-
入门层 ██████████ 118 / 118 篇(代码示例 134 个全部验证通过)
13-
进阶层 ████████░░ 96 / 134 篇
12+
入门层 ██████████ 137 / 137 篇(代码示例 134 个全部验证通过)✅ 全部完成
13+
进阶层 ██████████ 134 / 134 篇 ✅ 全部完成
1414
专家层 ░░░░░░░░░░ 0 / 142 篇
15-
合计 ██████░░░ 214 / 394
15+
合计 ██████░░░░ 271 / 413
1616
```
1717

1818
---
1919

2020
## 🟢 入门层
2121

22-
全部完成(118 篇),归档于 [todo/archive/beginner-completed.md](todo/archive/beginner-completed.md)
22+
全部完成(137 篇),归档于 [todo/archive/beginner-completed.md](todo/archive/beginner-completed.md)
2323

2424
> **代码示例状态**:134 个示例全部构建通过,验证通过率 120/134(90%)。14 个未通过均为 WSL2 环境限制(QtWebEngine EGL、TTS 引擎缺失等),代码本身无问题。
2525
@@ -128,39 +128,39 @@
128128
</details>
129129

130130
<details>
131-
<summary><strong>04 · QtNetwork(进阶)</strong>(6 篇)</summary>
131+
<summary><strong>04 · QtNetwork(进阶)</strong>(6 篇 · ✅ 全部完成)</summary>
132132

133-
- [ ] 🔴 `01-tcp-advanced.md` — TCP 进阶:多客户端、粘包处理、心跳机制
133+
- [x] 🔴 `01-tcp-advanced.md` — TCP 进阶:多客户端、粘包处理、心跳机制 ✅ 2026-06-11
134134
- 多客户端连接管理(连接表 / ID 映射)
135135
- 自定义协议帧:包头长度字段解决粘包/拆包
136136
- 心跳包超时检测与断线重连自动机
137137
- `QTcpServer::setMaxPendingConnections` 控制连接队列
138138

139-
- [ ] 🟡 `02-udp-advanced.md` — UDP 进阶:多播、大数据分片重组
139+
- [x] 🟡 `02-udp-advanced.md` — UDP 进阶:多播、大数据分片重组 ✅ 2026-06-11
140140
- `QUdpSocket::joinMulticastGroup()` 加入多播组
141141
- UDP 大数据手动分片与序号重组
142142
- `QNetworkDatagram` 携带发送方信息
143143
- UDP 可靠传输的简单 ARQ 机制实现
144144

145-
- [ ] 🔴 `03-http-advanced.md` — HTTP 进阶:请求队列、拦截器、Cookie
145+
- [x] 🔴 `03-http-advanced.md` — HTTP 进阶:请求队列、拦截器、Cookie ✅ 2026-06-11
146146
- 并发请求限流与优先级队列
147147
- `QNetworkAccessManager` 全局请求拦截(鉴权头注入)
148148
- `QNetworkCookieJar` 管理 Session Cookie
149149
- 断点续传:`Range` 头 + 本地进度记录
150150

151-
- [ ] 🟡 `04-websocket-advanced.md` — WebSocket 进阶:断线重连与心跳保活
151+
- [x] 🟡 `04-websocket-advanced.md` — WebSocket 进阶:断线重连与心跳保活 ✅ 2026-06-11
152152
- 指数退避重连策略实现
153153
- Ping/Pong 心跳帧发送与超时检测
154154
- 大消息分帧发送(`sendBinaryMessage` 超大 payload)
155155
- WSS 证书配置与自签名证书信任
156156

157-
- [ ] 🟡 `05-ssl-advanced.md` — SSL 进阶:双向认证与证书链验证
157+
- [x] 🟡 `05-ssl-advanced.md` — SSL 进阶:双向认证与证书链验证 ✅ 2026-06-11
158158
- 客户端证书(mTLS)配置流程
159159
- `QSslCertificate` 证书解析与有效期检查
160160
- `QSslError` 白名单忽略特定错误(仅开发调试)
161161
- Let's Encrypt 证书在 Qt 应用中的信任配置
162162

163-
- [ ] 🔴 `06-serialport-advanced.md` — 串口进阶:自定义协议封装与超时处理
163+
- [x] 🔴 `06-serialport-advanced.md` — 串口进阶:自定义协议封装与超时处理 ✅ 2026-06-11
164164
- 自定义帧格式:帧头/长度/数据/校验和解析状态机
165165
- 接收缓冲区管理:不完整帧的暂存策略
166166
- `QSerialPort::WaitForReadyRead` 同步等待 vs 异步 `readyRead`
@@ -169,76 +169,76 @@
169169
</details>
170170

171171
<details>
172-
<summary><strong>05 · 其他模块(进阶,与入门层一一对应)</strong>(25 篇)</summary>
173-
174-
- [ ] 🔴 `01-qtsql-advanced.md` — QtSql 进阶:事务、连接池、ORM 封装
175-
- [ ] 🟡 `02-qtsql-tablemodel-advanced.md` — QSqlRelationalTableModel 关联表视图
176-
- [ ] 🔴 `03-qtcharts-advanced.md` — QtCharts/QtGraphs 进阶:实时数据更新与自定义 Axis
177-
- [ ] 🔴 `04-qtmultimedia-player-advanced.md` — 媒体播放进阶:播放列表、媒体元数据、字幕
178-
- [ ] 🟡 `05-qtmultimedia-camera-advanced.md` — 摄像头进阶:视频录制、帧处理、滤镜
179-
- [ ] 🟡 `06-qtsvg-advanced.md` — QtSvg 进阶:动态修改 SVG 元素属性
180-
- [ ] 🟡 `07-qtprintsupport-advanced.md` — 打印进阶:复杂报表生成与多页面布局
181-
- [ ] 🔴 `08-modbus-advanced.md` — Modbus 进阶:RTU/TCP 切换、寄存器映射表管理
182-
- [ ] 🟡 `09-mqtt-advanced.md` — MQTT 进阶:QoS 1/2、遗嘱消息、TLS 加密连接
183-
- [ ] 🟡 `10-qtbluetooth-advanced.md` — 蓝牙进阶:GATT Profile 读写 Characteristic
184-
- [ ]`11-qtnfc-advanced.md` — NFC 进阶:NDEF 记录类型详解与写入标签
185-
- [ ] 🟡 `12-qtstatemachine-advanced.md` — 状态机进阶:层次状态机、历史状态、并行状态
186-
- [ ]`13-qtscxml-advanced.md` — SCXML 进阶:数据模型与延迟事件
187-
- [ ] 🟡 `14-qt3d-advanced.md` — Qt3D 进阶:自定义 Material、Framegraph 配置
188-
- [ ] 🟡 `15-qtquick3d-advanced.md` — QtQuick3D 进阶:PBR 材质、环境光遮蔽、阴影
189-
- [ ]`16-qtquick3d-physics-advanced.md` — 物理进阶:关节约束、力与冲量、射线检测
190-
- [ ] 🟡 `17-qtpdf-advanced.md` — QtPdf 进阶:文本搜索、选中复制、书签导航
191-
- [ ] 🟡 `18-qthttpserver-advanced.md` — HttpServer 进阶:中间件链、静态文件服务、身份验证
192-
- [ ] 🟡 `19-qtwebsockets-advanced.md` — WebSocket 服务端进阶:房间广播、消息队列
193-
- [ ]`20-qtwebchannel-advanced.md` — WebChannel 进阶:自定义传输层(非 WebEngine)
194-
- [ ]`21-qtwebengine-advanced.md` — WebEngine 进阶:自定义 URL Scheme、安全策略
195-
- [ ]`22-qtremoteobjects-advanced.md` — Remote Objects 进阶:自定义序列化与网络传输
196-
- [ ]`23-qtspatial-audio-advanced.md` — 空间音频进阶:混响、距离衰减、头部追踪
197-
- [ ]`24-qttexttospeech-advanced.md` — TTS 进阶:SSML 标记语言与语音合成控制
198-
- [ ] 🟡 `25-qt5compat-advanced.md` — Qt5Compat 进阶:批量迁移策略与自动化检测工具
172+
<summary><strong>05 · 其他模块(进阶,与入门层一一对应)</strong>(25 篇 · ✅ 全部完成)</summary>
173+
174+
- [x] 🔴 `01-qtsql-advanced.md` — QtSql 进阶:事务、连接池、ORM 封装 ✅ 2026-06-11
175+
- [x] 🟡 `02-qtsql-tablemodel-advanced.md` — QSqlRelationalTableModel 关联表视图 ✅ 2026-06-11
176+
- [x] 🔴 `03-qtcharts-advanced.md` — QtCharts/QtGraphs 进阶:实时数据更新与自定义 Axis ✅ 2026-06-11
177+
- [x] 🔴 `04-qtmultimedia-player-advanced.md` — 媒体播放进阶:播放列表、媒体元数据、字幕 ✅ 2026-06-11
178+
- [x] 🟡 `05-qtmultimedia-camera-advanced.md` — 摄像头进阶:视频录制、帧处理、滤镜 ✅ 2026-06-11
179+
- [x] 🟡 `06-qtsvg-advanced.md` — QtSvg 进阶:动态修改 SVG 元素属性 ✅ 2026-06-11
180+
- [x] 🟡 `07-qtprintsupport-advanced.md` — 打印进阶:复杂报表生成与多页面布局 ✅ 2026-06-11
181+
- [x] 🔴 `08-modbus-advanced.md` — Modbus 进阶:RTU/TCP 切换、寄存器映射表管理 ✅ 2026-06-11
182+
- [x] 🟡 `09-mqtt-advanced.md` — MQTT 进阶:QoS 1/2、遗嘱消息、TLS 加密连接 ✅ 2026-06-11
183+
- [x] 🟡 `10-qtbluetooth-advanced.md` — 蓝牙进阶:GATT Profile 读写 Characteristic ✅ 2026-06-11
184+
- [x]`11-qtnfc-advanced.md` — NFC 进阶:NDEF 记录类型详解与写入标签 ✅ 2026-06-11
185+
- [x] 🟡 `12-qtstatemachine-advanced.md` — 状态机进阶:层次状态机、历史状态、并行状态 ✅ 2026-06-11
186+
- [x]`13-qtscxml-advanced.md` — SCXML 进阶:数据模型与延迟事件 ✅ 2026-06-11
187+
- [x] 🟡 `14-qt3d-advanced.md` — Qt3D 进阶:自定义 Material、Framegraph 配置 ✅ 2026-06-11
188+
- [x] 🟡 `15-qtquick3d-advanced.md` — QtQuick3D 进阶:PBR 材质、环境光遮蔽、阴影 ✅ 2026-06-11
189+
- [x]`16-qtquick3d-physics-advanced.md` — 物理进阶:关节约束、力与冲量、射线检测 ✅ 2026-06-11
190+
- [x] 🟡 `17-qtpdf-advanced.md` — QtPdf 进阶:文本搜索、选中复制、书签导航 ✅ 2026-06-11
191+
- [x] 🟡 `18-qthttpserver-advanced.md` — HttpServer 进阶:中间件链、静态文件服务、身份验证 ✅ 2026-06-11
192+
- [x] 🟡 `19-qtwebsockets-advanced.md` — WebSocket 服务端进阶:房间广播、消息队列 ✅ 2026-06-11
193+
- [x]`20-qtwebchannel-advanced.md` — WebChannel 进阶:自定义传输层(非 WebEngine) ✅ 2026-06-11
194+
- [x]`21-qtwebengine-advanced.md` — WebEngine 进阶:自定义 URL Scheme、安全策略 ✅ 2026-06-11
195+
- [x]`22-qtremoteobjects-advanced.md` — Remote Objects 进阶:自定义序列化与网络传输 ✅ 2026-06-11
196+
- [x]`23-qtspatial-audio-advanced.md` — 空间音频进阶:混响、距离衰减、头部追踪 ✅ 2026-06-11
197+
- [x]`24-qttexttospeech-advanced.md` — TTS 进阶:SSML 标记语言与语音合成控制 ✅ 2026-06-11
198+
- [x] 🟡 `25-qt5compat-advanced.md` — Qt5Compat 进阶:批量迁移策略与自动化检测工具 ✅ 2026-06-11
199199

200200
</details>
201201

202202
<details>
203-
<summary><strong>06 · QML 独立教程(进阶)</strong>(7 篇)</summary>
203+
<summary><strong>06 · QML 独立教程(进阶)</strong>(7 篇 · ✅ 全部完成)</summary>
204204

205-
- [ ] 🔴 `01-qml-syntax-advanced.md` — QML 语法进阶:绑定陷阱、`required` 属性、`readonly`
205+
- [x] 🔴 `01-qml-syntax-advanced.md` — QML 语法进阶:绑定陷阱、`required` 属性、`readonly` ✅ 2026-06-11
206206
- 绑定断裂(命令式赋值覆盖绑定)的排查与修复
207207
- `required property` 强制父级传值
208208
- `readonly property` 只读属性的正确用法
209209
- 延迟初始化与 `Component.onCompleted`
210210

211-
- [ ] 🔴 `02-property-binding-advanced.md` — 属性绑定进阶:`Binding` 元素与条件绑定
211+
- [x] 🔴 `02-property-binding-advanced.md` — 属性绑定进阶:`Binding` 元素与条件绑定 ✅ 2026-06-11
212212
- `Binding { target; property; value; when }` 条件绑定
213213
- `Qt.binding()` 在命令式代码中重建绑定
214214
- 双向绑定的正确实现(避免绑定循环)
215215
- 属性别名 `property alias` 与性能分析
216216

217-
- [ ] 🔴 `03-qtquick-controls-advanced.md` — Qt Quick Controls 进阶:自定义样式
217+
- [x] 🔴 `03-qtquick-controls-advanced.md` — Qt Quick Controls 进阶:自定义样式 ✅ 2026-06-11
218218
- `Material` / `Fusion` / `Universal` 样式切换
219219
- 自定义 Control 模板(`background` / `contentItem` / `indicator`
220220
- `Palette` 主题色统一管理
221221
- `ToolTip` / `ToolTipAttached` 全局提示配置
222222

223-
- [ ] 🔴 `04-cpp-qml-interop-advanced.md` — C++/QML 进阶:类型系统与 QML 模块注册
223+
- [x] 🔴 `04-cpp-qml-interop-advanced.md` — C++/QML 进阶:类型系统与 QML 模块注册 ✅ 2026-06-11
224224
- `QML_ELEMENT` / `QML_SINGLETON` 宏的区别与用法
225225
- `qmlRegisterUncreatableType` 只暴露枚举/常量
226226
- C++ `QAbstractListModel` 完整实现供 QML ListView 驱动
227227
- Q_INVOKABLE 方法的线程安全注意事项
228228

229-
- [ ] 🔴 `05-qml-animation-advanced.md` — QML 动画进阶:路径动画与 Animator
229+
- [x] 🔴 `05-qml-animation-advanced.md` — QML 动画进阶:路径动画与 Animator ✅ 2026-06-11
230230
- `PathAnimation` 沿路径运动
231231
- `Animator`(在渲染线程运行,比 Animation 更流畅)
232232
- `SmoothedAnimation` / `SpringAnimation` 物理感动画
233233
- 动画性能分析:避免 JavaScript 在动画帧中执行
234234

235-
- [ ] 🔴 `06-qml-model-view-advanced.md` — QML 模型视图进阶:DelegateModel 与 section
235+
- [x] 🔴 `06-qml-model-view-advanced.md` — QML 模型视图进阶:DelegateModel 与 section ✅ 2026-06-11
236236
- `DelegateModel` 分组与排序
237237
- `ListView::section` 分节标题
238238
- `QSortFilterProxyModel` 在 QML 中的使用
239239
- 大数据列表性能:`cacheBuffer``displayMarginBeginning`
240240

241-
- [ ] 🟡 `07-qml-async-workerscript-advanced.md` — QML 异步进阶:WorkerScript 线程模型
241+
- [x] 🟡 `07-qml-async-workerscript-advanced.md` — QML 异步进阶:WorkerScript 线程模型 ✅ 2026-06-11
242242
- `WorkerScript` 在后台线程执行 JS 计算
243243
- `sendMessage` / `onMessage` 线程间通信
244244
- `XMLHttpRequest` 在 QML 中的异步 HTTP 请求
@@ -709,12 +709,13 @@
709709

710710
| 层级 | 总篇数 | 已完成 | 进行中 | 未开始 |
711711
|------|--------|--------|--------|--------|
712-
| 入门 | 118 | 118 | 0 | 0 |
713-
| 进阶 | 134 | 96 | 0 | 38 |
712+
| 入门 | 137 | 137 | 0 | 0 |
713+
| 进阶 | 134 | 134 | 0 | 0 |
714714
| 专家 | 142 | 0 | 0 | 142 |
715-
| **合计** | **394** | **214** | **0** | **180** |
715+
| **合计** | **413** | **271** | **0** | **142** |
716716

717717
---
718718

719-
*TODO 版本:v2.0(百科全书版)· 生成于 2026-03-17*
720-
*每次完成一篇请立即更新对应条目(`[ ]``[x] 完成于 YYYY-MM-DD`)并更新上方统计表*
719+
*TODO 版本:v2.1(百科全书版)· 更新于 2026-06-11*
720+
*每次完成一篇请立即更新对应条目(`[ ]``[x] 完成于 YYYY-MM-DD`)并更新上方统计表*
721+
*注:入门层 118→137 系档案中 00/01 节使用「全部完成」批量标记未计入逐条统计,实际磁盘文档 137 篇已全部完成*
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
build/
2+
*.user
3+
.idea/
4+
.vscode/
5+
CMakeCache.txt
6+
CMakeFiles/
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
cmake_minimum_required(VERSION 3.26) # Qt6 required CMake version
2+
project(41-qstackedwidget-advanced LANGUAGES CXX) # Animated QStackedWidget transitions
3+
4+
set(CMAKE_CXX_STANDARD 17) # Unified C++17
5+
set(CMAKE_CXX_STANDARD_REQUIRED ON) # Do not fall back to older standards
6+
7+
set(CMAKE_AUTOMOC ON) # Auto-run MOC for Q_OBJECT macros
8+
set(CMAKE_AUTORCC ON) # Auto-handle .qrc resource files
9+
set(CMAKE_AUTOUIC ON) # Auto-handle .ui form files
10+
11+
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) # Core, Gui, Widgets modules
12+
13+
qt_add_executable(${PROJECT_NAME} # Build executable
14+
slide_stacked_widget.h # Custom QStackedWidget header
15+
slide_stacked_widget.cpp # Slide animation implementation
16+
main.cpp # Application entry point
17+
)
18+
19+
target_link_libraries(${PROJECT_NAME}
20+
PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets # Link required Qt modules
21+
)
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/// @file main.cpp
2+
/// @brief Entry point for the animated QStackedWidget demo.
3+
///
4+
/// Builds a window with a SlideStackedWidget containing three colored pages,
5+
/// plus forward/backward navigation buttons and a label showing the current
6+
/// page index.
7+
///
8+
/// 对应教程:进阶层 03-QtWidgets/41-QStackedWidget 滑动切换动画实现。
9+
10+
#include "slide_stacked_widget.h"
11+
12+
#include <QApplication>
13+
#include <QHBoxLayout>
14+
#include <QLabel>
15+
#include <QPushButton>
16+
#include <QVBoxLayout>
17+
#include <QWidget>
18+
19+
/// @brief Creates a colored page widget with a centered label.
20+
/// @param[in] color Background color for the page.
21+
/// @param[in] title Text displayed in the center of the page.
22+
/// @param[in] parent Parent widget for Qt ownership.
23+
/// @return Pointer to the newly created page widget.
24+
/// @note The widget is created on the heap with Qt parent ownership.
25+
static QWidget* createPage(const QColor& color, const QString& title,
26+
QWidget* parent = nullptr)
27+
{
28+
auto* page = new QWidget(parent);
29+
page->setAutoFillBackground(true);
30+
31+
QPalette palette;
32+
palette.setColor(QPalette::Window, color);
33+
page->setPalette(palette);
34+
35+
auto* label = new QLabel(title, page);
36+
label->setAlignment(Qt::AlignCenter);
37+
38+
// Use a stylesheet on the label for readable text on colored backgrounds
39+
label->setStyleSheet("font-size: 24px; font-weight: bold; color: white;");
40+
41+
auto* layout = new QVBoxLayout(page);
42+
layout->addWidget(label);
43+
44+
return page;
45+
}
46+
47+
int main(int argc, char* argv[])
48+
{
49+
QApplication app(argc, argv);
50+
51+
auto* window = new QWidget;
52+
window->setWindowTitle("QStackedWidget Slide Animation");
53+
window->resize(500, 350);
54+
55+
auto* mainLayout = new QVBoxLayout(window);
56+
57+
// Create the custom stacked widget with animated transitions
58+
auto* stacked = new SlideStackedWidget(window);
59+
stacked->addWidget(createPage(QColor("#2196F3"), "Page 1 - Blue"));
60+
stacked->addWidget(createPage(QColor("#4CAF50"), "Page 2 - Green"));
61+
stacked->addWidget(createPage(QColor("#FF9800"), "Page 3 - Orange"));
62+
stacked->setCurrentIndex(0);
63+
64+
mainLayout->addWidget(stacked, 1);
65+
66+
// Navigation controls at the bottom
67+
auto* navLayout = new QHBoxLayout();
68+
69+
auto* prevBtn = new QPushButton("<< Previous", window);
70+
auto* indexLabel = new QLabel("Page: 1 / 3", window);
71+
indexLabel->setAlignment(Qt::AlignCenter);
72+
auto* nextBtn = new QPushButton("Next >>", window);
73+
74+
navLayout->addWidget(prevBtn);
75+
navLayout->addStretch();
76+
navLayout->addWidget(indexLabel);
77+
navLayout->addStretch();
78+
navLayout->addWidget(nextBtn);
79+
80+
mainLayout->addLayout(navLayout);
81+
82+
// Update label text when the stacked widget's current page changes
83+
auto updateLabel = [stacked, indexLabel]() {
84+
int idx = stacked->currentIndex();
85+
int total = stacked->count();
86+
indexLabel->setText(
87+
QString("Page: %1 / %2").arg(idx + 1).arg(total));
88+
};
89+
90+
// Connect navigation buttons to slide animation
91+
QObject::connect(nextBtn, &QPushButton::clicked, stacked,
92+
[stacked]() {
93+
int next = stacked->currentIndex() + 1;
94+
if (next < stacked->count()) {
95+
stacked->setCurrentIndex(next);
96+
}
97+
});
98+
99+
QObject::connect(prevBtn, &QPushButton::clicked, stacked,
100+
[stacked]() {
101+
int prev = stacked->currentIndex() - 1;
102+
if (prev >= 0) {
103+
stacked->setCurrentIndex(prev);
104+
}
105+
});
106+
107+
// Update the label when the logical current index changes
108+
QObject::connect(stacked, &QStackedWidget::currentChanged, stacked,
109+
updateLabel);
110+
111+
window->show();
112+
return app.exec();
113+
}

0 commit comments

Comments
 (0)