Skip to content

Commit b09fb2b

Browse files
committed
feat: Refactor QML delegates and add unsaved changes tracking
This commit introduces several major improvements to the application: - Refactors the QML delegates in `controlsView` to centralize control logic in `Main.qml`, simplifying the `ControlDelegate` and improving maintainability. - Implements a robust "unsaved changes" tracking system with a "dirty" flag in `RegimeManager`, an indicator in the application title, and a confirmation dialog on close or import. - Corrects the `CMakeLists.txt` file and QML module definition to follow standard practices.
1 parent 9249f6e commit b09fb2b

13 files changed

Lines changed: 383 additions & 224 deletions

CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ target_link_libraries(${EXECUTABLE_NAME}
3232
prototablemodel
3333
)
3434

35-
add_library(prototablemodel STATIC prototablemodel.cpp regime.cpp)
35+
add_library(prototablemodel STATIC prototablemodel.cpp regime.cpp regimemanager.cpp)
3636

3737
target_link_libraries(prototablemodel PRIVATE Qt6::Core)
3838

@@ -41,10 +41,11 @@ qt6_add_qml_module(${EXECUTABLE_NAME}
4141
VERSION 1.0
4242
QML_FILES
4343
ConditionCell.qml
44-
CycleCell.qml
44+
ControlDelegate.qml
4545
SOURCES
4646
prototablemodel.h
4747
regime.h
48+
regimemanager.h
4849
RESOURCE_PREFIX /
4950
)
5051

ConditionCell.qml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,4 @@ Row {
6565
container.model.condition = newCond
6666
}
6767
}
68-
}
68+
}

ControlDelegate.qml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import QtQuick
2+
import QtQuick.Controls
3+
import QtQuick.Layouts
4+
5+
import com.grams.prototable
6+
7+
Rectangle {
8+
id: root
9+
width: 250
10+
border.color: "black"
11+
border.width: 1
12+
color: isSelected ? "lightblue" : "white"
13+
required property int index
14+
required property var model
15+
property bool isSelected: false
16+
Row {
17+
height: parent.height
18+
width: parent.width
19+
spacing: 5
20+
SpinBox {
21+
y: parent.height/2 - 20
22+
height: 40
23+
width: 100
24+
value: model.cycle_repeat
25+
onValueModified: model.cycle_repeat = value
26+
}
27+
CheckBox {
28+
y: parent.height/2 - 20
29+
height: 40
30+
checked: isSelected
31+
onClicked: {
32+
root.isSelected = checked
33+
}
34+
}
35+
Button {
36+
y: parent.height/2 - 20
37+
height: 40
38+
id: upButton
39+
text: "Up"
40+
visible: RegimeManager.model.isMoveUpEnabled([index])
41+
onClicked: {
42+
RegimeManager.model.moveSelection([index], true)
43+
}
44+
}
45+
46+
Button {
47+
y: parent.height/2 - 20
48+
height: 40
49+
id: downButton
50+
text: "Down"
51+
visible: RegimeManager.model.isMoveDownEnabled([index])
52+
onClicked: {
53+
RegimeManager.model.moveSelection([index], false)
54+
}
55+
}
56+
57+
Button {
58+
y: parent.height/2 - 20
59+
height: 40
60+
id: deleteButton
61+
text: "Delete"
62+
visible: root.isSelected
63+
onClicked: {
64+
RegimeManager.model.deleteRows([index])
65+
}
66+
}
67+
}
68+
}

CycleCell.qml

Lines changed: 0 additions & 116 deletions
This file was deleted.

README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,62 @@
1+
2+
### TODO for Thursday, August 7, 2025
3+
4+
1. **Create Unit Tests for `RegimeManager`:**
5+
* Write tests for `loadDefaultRegimes`, `importRegimes`, `exportRegimes`, and `saveRegimes`.
6+
* This will likely involve creating temporary JSON files within the test suite to verify that files are read and written correctly.
7+
2. **Implement Regime "State" Property:**
8+
* Add a `state` property to the `Regime` struct in C++.
9+
* Update JSON serialization/deserialization to handle the new `state` field.
10+
* Expose the `state` property to QML and add a UI control (e.g., a ComboBox) in the `TableView` delegate to allow viewing and editing it.
11+
3. **Define and Implement Table Module API:**
12+
* Once the above tasks are complete, design and implement a more formal API for the table module (`RegimeManager` and `ProtoTableModel`) to improve modularity and prepare for future external interactions.
13+
14+
### План работ на четверг, 7 августа 2025 г.
15+
16+
1. **Создать юнит-тесты для `RegimeManager`:**
17+
* Написать тесты для `loadDefaultRegimes`, `importRegimes`, `exportRegimes` и `saveRegimes`.
18+
* Вероятно, для этого потребуется создавать временные файлы JSON в наборе тестов для проверки правильности чтения и записи файлов.
19+
2. **Реализовать свойство "state" для режимов:**
20+
* Добавить свойство `state` в структуру `Regime` в C++.
21+
* Обновить сериализацию/десериализацию JSON для обработки нового поля `state`.
22+
* Предоставить свойство `state` в QML и добавить элемент управления (например, ComboBox) в делегат `TableView` для его просмотра и редактирования.
23+
3. **Спроектировать и реализовать API для модуля таблицы:**
24+
* После завершения вышеуказанных задач, спроектировать и реализовать более формальный API для модуля таблицы (`RegimeManager` и `ProtoTableModel`), чтобы улучшить модульность и подготовиться к будущим внешним взаимодействиям.
25+
26+
## Daily Report for 2025-08-06
27+
28+
Today's session focused on completing the refactoring of the QML delegates and implementing a robust "unsaved changes" tracking system.
29+
30+
**1. QML Delegate Refactoring (`ControlDelegate.qml` and `Main.qml`):**
31+
32+
* **Centralized Control Logic**: The `controlsView` was successfully refactored by moving all the action buttons ("Up", "Down", "Delete", "Group", "Ungroup") out of the `ControlDelegate` and into `Main.qml`. This centralizes the application's logic, making it easier to manage and debug. The `ControlDelegate` is now a much simpler and more reusable component, responsible only for displaying the selection state and the `cycle_repeat` value.
33+
* **Dynamic Button Visibility**: The visibility of the action buttons is now dynamically controlled by the selection state, which provides a much cleaner and more intuitive user experience.
34+
* **Simplified `ControlDelegate`**: The `ControlDelegate` is now a simple component that only contains a `CheckBox` and a `SpinBox`. This makes it much easier to understand and maintain.
35+
36+
**2. "Unsaved Changes" Tracking (`RegimeManager`):**
37+
38+
* **"Dirty" Flag**: A "dirty" flag was implemented in the `RegimeManager` to track whether there are unsaved changes. This is a crucial feature for any application that allows users to edit data.
39+
* **Application Title Indicator**: The application title now displays an asterisk (`*`) when there are unsaved changes, which provides a clear visual cue to the user.
40+
* **Confirmation Dialog**: A confirmation dialog is now displayed when the user tries to close the application or import a new file with unsaved changes. This prevents accidental data loss and is a standard feature in most data-driven applications.
41+
42+
---
43+
44+
## Ежедневный отчет за 2025-08-06
45+
46+
Сегодняшняя сессия была посвящена завершению рефакторинга делегатов QML и реализации надежной системы отслеживания несохраненных изменений.
47+
48+
**1. Рефакторинг делегатов QML (`ControlDelegate.qml` и `Main.qml`):**
49+
50+
* **Централизованная логика управления**: `controlsView` был успешно отрефакторен путем перемещения всех кнопок действий («Вверх», «Вниз», «Удалить», «Сгруппировать», «Разгруппировать») из `ControlDelegate` в `Main.qml`. Это централизует логику приложения, облегчая управление и отладку. `ControlDelegate` теперь является гораздо более простым и переиспользуемым компонентом, отвечающим только за отображение состояния выбора и значения `cycle_repeat`.
51+
* **Динамическая видимость кнопок**: Видимость кнопок действий теперь динамически контролируется состоянием выбора, что обеспечивает более чистый и интуитивно понятный пользовательский интерфейс.
52+
* **Упрощенный `ControlDelegate`**: `ControlDelegate` теперь представляет собой простой компонент, содержащий только `CheckBox` и `SpinBox`. Это значительно упрощает его понимание и обслуживание.
53+
54+
**2. Отслеживание несохраненных изменений (`RegimeManager`):**
55+
56+
* **Флаг "dirty"**: В `RegimeManager` был реализован флаг «dirty» для отслеживания наличия несохраненных изменений. Это важная функция для любого приложения, позволяющего пользователям редактировать данные.
57+
* **Индикатор в заголовке приложения**: В заголовке приложения теперь отображается звездочка (`*`), когда есть несохраненные изменения, что служит четким визуальным сигналом для пользователя.
58+
* **Диалог подтверждения**: Теперь при попытке закрыть приложение или импортировать новый файл с несохраненными изменениями отображается диалоговое окно подтверждения. Это предотвращает случайную потерю данных и является стандартной функцией в большинстве приложений, работающих с данными.
59+
160
### TODO for Wednesday, August 6, 2025
261

362
1. **Create Unit Tests for `RegimeManager`:**

prototablemodel.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ QVariant ProtoTableModel::data(const QModelIndex &index, int role) const
112112
return regime.m_cycleRepeat;
113113
}
114114

115+
if (role == StatusRole) {
116+
return regime.m_status;
117+
}
118+
115119
return QVariant();
116120
}
117121

@@ -166,6 +170,12 @@ bool ProtoTableModel::setData(const QModelIndex &index, const QVariant &value, i
166170
return true;
167171
}
168172

173+
if (role == StatusRole) {
174+
regime.m_status = value.toInt();
175+
emit dataChanged(index, index, {role});
176+
return true;
177+
}
178+
169179
return false;
170180
}
171181

@@ -215,7 +225,8 @@ QHash<int, QByteArray> ProtoTableModel::roleNames() const
215225
{ MaxTimeRole, "max_time" },
216226
{ SpanRole, "span" },
217227
{ CycleStatusRole, "cycle_status" },
218-
{ CycleRepeatRole, "cycle_repeat" }
228+
{ CycleRepeatRole, "cycle_repeat" },
229+
{ StatusRole, "status" }
219230
};
220231
}
221232

0 commit comments

Comments
 (0)