();
+ setOpenRecentMenuActions();
setMainWindowIcons();
- resize(START_WIDTH, START_HEIGHT);
setWelcomingWidget();
+ resize(START_WIDTH, START_HEIGHT);
}
@@ -153,13 +156,23 @@ MainWindow::saveAs()
void
-MainWindow::openTable()
+MainWindow::openTable(const QString& recentDir)
{
- const auto fileName = QFileDialog::getOpenFileName(this, "Open Table", m_dirSettings.openDir, ("lcm File(*.lcm)"));
- // Return if this exact same file is already loaded or if the dialog has been cancelled
- if ((m_isTableActive && fileName == m_fileDir) || fileName.isEmpty()) {
+ QString fileName;
+ if (recentDir.isEmpty()) {
+ fileName = QFileDialog::getOpenFileName(this, "Open Table", m_dirSettings.openDir, ("lcm File(*.lcm)"));
+ if (fileName.isEmpty()) {
+ return;
+ }
+ } else {
+ fileName = recentDir;
+ }
+
+ // Return if this exact same file is already loaded
+ if ((m_isTableActive && fileName == m_fileDir)) {
return;
}
+
// Check if a table is active right now
if (m_isTableActive && isWindowModified() &&
createSaveMessageBox(tr("Do you want to save the current Combat before opening another existing Combat?"), false) == 0) {
@@ -174,8 +187,8 @@ MainWindow::openTable()
if (!checkStoredTableRules(m_tableFileHandler->getData())) {
const auto messageString = createRuleChangeMessageBoxText();
auto *const msgBox = new QMessageBox(QMessageBox::Warning, tr("Different Rulesets detected!"), messageString, QMessageBox::Cancel);
- auto* const applyButton = msgBox->addButton(tr("Apply Table Ruleset to Settings"), QMessageBox::ApplyRole);
- auto* const ignoreButton = msgBox->addButton(tr("Ignore stored Table Ruleset"), QMessageBox::AcceptRole);
+ auto* const ignoreButton = msgBox->addButton(tr("Use Settings Ruleset"), QMessageBox::AcceptRole);
+ auto* const applyButton = msgBox->addButton(tr("Use Table Ruleset (Apply to Settings)"), QMessageBox::ApplyRole);
msgBox->exec();
if (msgBox->clickedButton() == applyButton) {
@@ -195,6 +208,7 @@ MainWindow::openTable()
m_fileDir = fileName;
setTableWidget(true, false);
+ setOpenRecentMenuActions();
// If the settings rules are applied to the table, it is modified
setCombatTitle(rulesModified);
break;
@@ -229,7 +243,7 @@ MainWindow::about()
QMessageBox::about(this, tr("About Light Combat Manager"),
tr("Light Combat Manager. A small, lightweight combat manager for d20-based role playing games.
"
"Code available on Github. Uses GNU GPLv3 license.
"
- "Version 3.0.0.
"
+ "
Version 3.1.0.
"
"Changelog
"));
}
@@ -254,7 +268,7 @@ MainWindow::setWelcomingWidget()
m_welcomeWidget = new WelcomeWidget(this);
setCentralWidget(m_welcomeWidget);
- resize(START_WIDTH, START_HEIGHT);
+ callTimedResize(START_WIDTH, START_HEIGHT);
m_isTableSavedInFile = false;
emit setSaveAction(false);
@@ -268,17 +282,10 @@ MainWindow::setTableWidget(bool isDataStored, bool newCombatStarted)
setCentralWidget(m_combatWidget);
connect(m_combatWidget, &CombatWidget::exit, this, &MainWindow::exitCombat);
connect(m_combatWidget, &CombatWidget::tableHeightSet, this, [this] (unsigned int height) {
- if (height > START_HEIGHT && (int) height > this->height()) {
- resize(width(), height);
- }
+ callTimedResize(width(), height);
});
connect(m_combatWidget, &CombatWidget::tableWidthSet, this, [this] (int tableWidth) {
- // @note A single immediate call to resize() won't actually resize the window
- // So the function is called with a minimal delay of 1 ms, which will actually
- // resize the main window
- QTimer::singleShot(1, [this, tableWidth]() {
- resize(tableWidth, height());
- });
+ callTimedResize(tableWidth, height());
});
connect(m_combatWidget, &CombatWidget::changeOccured, this, [this] {
setCombatTitle(true);
@@ -286,20 +293,14 @@ MainWindow::setTableWidget(bool isDataStored, bool newCombatStarted)
setCombatTitle(false);
- const auto resizeWidget = [this] (int width, int height) {
- QTimer::singleShot(1, [this, width, height] {
- resize(width, height);
- });
- };
-
if (newCombatStarted) {
- resizeWidget(START_WIDTH, START_HEIGHT);
+ callTimedResize(START_WIDTH, START_HEIGHT);
m_combatWidget->openAddCharacterDialog();
} else {
m_combatWidget->generateTableFromTableData();
const auto width = m_combatWidget->isLoggingWidgetVisible() ? m_combatWidget->width() - 250 : m_combatWidget->width();
const auto height = m_combatWidget->getHeight();
- resizeWidget(std::max(width, START_WIDTH), std::max(height, START_HEIGHT));
+ callTimedResize(std::max(width, START_WIDTH), std::max(height, START_HEIGHT));
}
m_isTableActive = true;
@@ -359,12 +360,12 @@ MainWindow::createSaveMessageBox(const QString& tableMessage, bool isClosing)
QString
MainWindow::createRuleChangeMessageBoxText() const
{
- const auto message = tr("The Table you are trying to load uses another ruleset than you have stored in your rule settings!
"
- "Your ruleset: ") + Utils::General::getRulesetName(m_ruleSettings.ruleset) + ", " +
- "" + Utils::General::getAutoRollEnabled(m_ruleSettings.rollAutomatical) + "
" +
- tr("The stored table ruleset is: ") + Utils::General::getRulesetName(m_loadedTableRule) + ", " +
- "" + Utils::General::getAutoRollEnabled(m_loadedTableRollAutomatically) + "
" +
- tr("Do you want to apply the stored Table ruleset to your settings or ignore it?");
+ const auto message = tr("The loaded table uses a different ruleset from your settings!
"
+ "Settings ruleset: ") + Utils::General::getRulesetName(m_ruleSettings.ruleset) + ", " +
+ Utils::General::getAutoRollEnabled(m_ruleSettings.rollAutomatical) + "
" +
+ tr("Table ruleset: ") + Utils::General::getRulesetName(m_loadedTableRule) + ", " +
+ Utils::General::getAutoRollEnabled(m_loadedTableRollAutomatically) + "
" +
+ tr("Do you want to use your settings or the table ruleset?");
return message;
}
@@ -404,6 +405,33 @@ MainWindow::checkStoredTableRules(const QJsonObject& jsonObjectData)
}
+void
+MainWindow::setOpenRecentMenuActions()
+{
+ m_openRecentMenu->clear();
+
+ if (m_dirSettings.recentDirs.at(0).isEmpty()) {
+ m_openRecentMenu->addAction(new QAction(tr("No recent dirs")));
+ } else {
+ for (const auto& recentDir : m_dirSettings.recentDirs) {
+ if (!std::filesystem::exists(recentDir.toStdString())) {
+ continue;
+ }
+
+ auto trimmedName = recentDir;
+ if (trimmedName.length() > 50) {
+ trimmedName.replace(0, trimmedName.length() - 50, "...");
+ }
+ auto* const recentDirAction = new QAction(trimmedName);
+ m_openRecentMenu->addAction(recentDirAction);
+ connect(recentDirAction, &QAction::triggered, this, [this, recentDir] {
+ openTable(recentDir);
+ });
+ }
+ }
+}
+
+
void
MainWindow::setMainWindowIcons()
{
@@ -416,10 +444,22 @@ MainWindow::setMainWindowIcons()
m_openSettingsAction->setIcon(QIcon(isSystemInDarkMode ? ":/icons/menus/gear_white.svg" : ":/icons/menus/gear_black.svg"));
m_aboutLCMAction->setIcon(QIcon(isSystemInDarkMode ? ":/icons/logos/main_light.svg" : ":/icons/logos/main_dark.svg"));
+ m_openRecentMenu->setIcon(QIcon(isSystemInDarkMode ? ":/icons/menus/open_white.svg" : ":/icons/menus/open_black.svg"));
+
QApplication::setWindowIcon(QIcon(isSystemInDarkMode ? ":/icons/logos/main_light.svg" : ":/icons/logos/main_dark.svg"));
}
+void
+MainWindow::callTimedResize(int width, int height)
+{
+ // Sometimes it needs minimal delays to process events in the background before this can be called
+ QTimer::singleShot(1, [this, width, height] {
+ resize(width, height);
+ });
+}
+
+
bool
MainWindow::event(QEvent *event)
{
diff --git a/src/ui/MainWindow.hpp b/src/ui/MainWindow.hpp
index 568782a..8c1fcf3 100644
--- a/src/ui/MainWindow.hpp
+++ b/src/ui/MainWindow.hpp
@@ -9,6 +9,7 @@
#include
class QAction;
+class QMenu;
class CombatWidget;
class WelcomeWidget;
@@ -37,7 +38,7 @@ private slots:
saveAs();
void
- openTable();
+ openTable(const QString& recentDir = "");
void
openSettings();
@@ -72,9 +73,16 @@ private slots:
[[nodiscard]] bool
checkStoredTableRules(const QJsonObject& jsonObjectData);
+ void
+ setOpenRecentMenuActions();
+
void
setMainWindowIcons();
+ void
+ callTimedResize(int width,
+ int height);
+
bool
event(QEvent *event) override;
@@ -90,6 +98,8 @@ private slots:
QPointer m_openSettingsAction;
QPointer m_aboutLCMAction;
+ QPointer m_openRecentMenu;
+
std::shared_ptr m_tableFileHandler;
AdditionalSettings m_additionalSettings;
diff --git a/src/ui/WelcomeWidget.cpp b/src/ui/WelcomeWidget.cpp
index a38523d..6463d26 100644
--- a/src/ui/WelcomeWidget.cpp
+++ b/src/ui/WelcomeWidget.cpp
@@ -17,8 +17,8 @@ WelcomeWidget::WelcomeWidget(QWidget *parent)
"or open an already existing Combat ('File' -> 'Open...')."));
welcomeLabel->setAlignment(Qt::AlignCenter);
- auto* const versionLabel = new QLabel("v3.0.0");
- versionLabel->setToolTip(tr("Logging widget, major infrastructure updates and various UI improvements!"));
+ auto* const versionLabel = new QLabel("v3.1.0");
+ versionLabel->setToolTip(tr("Custom effects, an Open Recent Menu and minor icon rearrangement!"));
versionLabel->setAlignment(Qt::AlignRight);
auto *const layout = new QVBoxLayout(this);
diff --git a/src/ui/settings/DirSettings.cpp b/src/ui/settings/DirSettings.cpp
index d7bba8b..46cf8c4 100644
--- a/src/ui/settings/DirSettings.cpp
+++ b/src/ui/settings/DirSettings.cpp
@@ -20,6 +20,22 @@ DirSettings::write(const QString& fileName, bool setSaveDir)
writeParameter(settings, fileName, saveDir, "dir_save");
saveDir = fileName;
}
+
+ // Apply the filename to recent saved directories
+ if (std::find(std::begin(recentDirs), std::end(recentDirs), fileName) != recentDirs.end()) {
+ return;
+ }
+ // Place in front
+ std::shift_right(recentDirs.begin(), recentDirs.end(), 1);
+ recentDirs[0] = fileName;
+ // Resave recent dir values
+ for (std::array::size_type i = 0; i < recentDirs.size(); i++) {
+ if (recentDirs.at(i).isEmpty()) {
+ break;
+ }
+
+ settings.setValue("recent_dir_" + QString::number(i), recentDirs.at(i));
+ }
}
@@ -27,6 +43,21 @@ void
DirSettings::read()
{
QSettings settings;
+ for (std::array::size_type i = 0; i < recentDirs.size(); i++) {
+ const auto& recentKey = "recent_dir_" + QString::number(i);
+ if (!settings.value(recentKey).isValid()) {
+ break;
+ }
+
+ if (std::filesystem::exists(settings.value(recentKey).toString().toStdString())
+ && std::filesystem::path(settings.value(recentKey).toString().toStdString()).extension().string() == ".lcm") {
+ recentDirs[i] = settings.value("recent_dir_" + QString::number(i)).toString();
+ } else {
+ // Might have turned invalid in the meantime
+ settings.remove("recent_dir_" + QString::number(i));
+ }
+ }
+
saveDir = settings.value("dir_save").isValid() ? settings.value("dir_save").toString() : QString();
openDir = settings.value("dir_open").isValid() ? settings.value("dir_open").toString() : QString();
}
diff --git a/src/ui/settings/DirSettings.hpp b/src/ui/settings/DirSettings.hpp
index 5ff6f53..cb78a6d 100644
--- a/src/ui/settings/DirSettings.hpp
+++ b/src/ui/settings/DirSettings.hpp
@@ -4,6 +4,8 @@
#include
+#include
+
// Store data used for handling the opening and saving directories
class DirSettings : public BaseSettings {
public:
@@ -14,6 +16,8 @@ class DirSettings : public BaseSettings {
bool setSaveDir = false);
public:
+ std::array recentDirs;
+
QString openDir;
QString saveDir;
diff --git a/src/ui/settings/TableSettings.cpp b/src/ui/settings/TableSettings.cpp
index fb638a3..e23305b 100644
--- a/src/ui/settings/TableSettings.cpp
+++ b/src/ui/settings/TableSettings.cpp
@@ -27,6 +27,9 @@ TableSettings::write(ValueType valueType, bool valueToWrite)
break;
case SHOW_INI_TOOLTIPS:
writeParameter(settings, valueToWrite, showIniToolTips, "ini_tool_tips");
+ break;
+ case ADJUST_HEIGHT_AFTER_REMOVE:
+ writeParameter(settings, valueToWrite, adjustHeightAfterRemove, "adjust_height_remove");
default:
break;
}
@@ -45,5 +48,6 @@ TableSettings::read()
modifierShown = settings.value("modifier").isValid() ? settings.value("modifier").toBool() : true;
colorTableRows = settings.value("color_rows").isValid() ? settings.value("color_rows").toBool() : false;
showIniToolTips = settings.value("ini_tool_tips").isValid() ? settings.value("ini_tool_tips").toBool() : false;
+ adjustHeightAfterRemove = settings.value("adjust_height_remove").isValid() ? settings.value("adjust_height_remove").toBool() : false;
settings.endGroup();
}
diff --git a/src/ui/settings/TableSettings.hpp b/src/ui/settings/TableSettings.hpp
index 7043a8d..20fc2ea 100644
--- a/src/ui/settings/TableSettings.hpp
+++ b/src/ui/settings/TableSettings.hpp
@@ -8,10 +8,11 @@ class TableSettings : public BaseSettings {
TableSettings();
enum class ValueType {
- INI_SHOWN = 0,
- MOD_SHOWN = 1,
- COLOR_TABLE = 2,
- SHOW_INI_TOOLTIPS = 3
+ INI_SHOWN = 0,
+ MOD_SHOWN = 1,
+ COLOR_TABLE = 2,
+ SHOW_INI_TOOLTIPS = 3,
+ ADJUST_HEIGHT_AFTER_REMOVE = 4
};
void
@@ -23,6 +24,7 @@ class TableSettings : public BaseSettings {
bool modifierShown{ true };
bool colorTableRows{ false };
bool showIniToolTips{ false };
+ bool adjustHeightAfterRemove{ false };
private:
void
diff --git a/src/ui/table/CombatTableWidget.cpp b/src/ui/table/CombatTableWidget.cpp
index 4a478ee..b68f021 100644
--- a/src/ui/table/CombatTableWidget.cpp
+++ b/src/ui/table/CombatTableWidget.cpp
@@ -174,10 +174,10 @@ CombatTableWidget::tableDataFromWidget()
QVector rowValues;
for (auto j = 0; j < FIRST_FOUR_COLUMNS; j++) {
- rowValues.push_back(item(i, j)->text());
+ rowValues.emplace_back(item(i, j)->text());
}
- rowValues.push_back(item(i, Utils::Table::COL_ENEMY)->checkState() == Qt::Checked);
+ rowValues.emplace_back(item(i, Utils::Table::COL_ENEMY)->checkState() == Qt::Checked);
QVariant variant;
variant.setValue(cellWidget(i, Utils::Table::COL_ADDITIONAL)->findChild()->getAdditionalInformation());
@@ -216,7 +216,7 @@ CombatTableWidget::getHeight() const
for (int i = 0; i < rowCount(); i++) {
height += rowHeight(i);
}
- return height + HEIGHT_BUFFER;
+ return height + TABLE_HEIGHT_BUFFER;
}
diff --git a/src/ui/table/CombatTableWidget.hpp b/src/ui/table/CombatTableWidget.hpp
index 9dde0dd..c107b79 100644
--- a/src/ui/table/CombatTableWidget.hpp
+++ b/src/ui/table/CombatTableWidget.hpp
@@ -67,7 +67,7 @@ class CombatTableWidget : public QTableWidget {
static constexpr int FIRST_FIVE_COLUMNS = 5;
static constexpr int NMBR_COLUMNS = 6;
- static constexpr int HEIGHT_BUFFER = 140;
+ static constexpr int TABLE_HEIGHT_BUFFER = 140;
static constexpr float WIDTH_NAME = 0.20f;
static constexpr float WIDTH_INI = 0.05f;
diff --git a/src/ui/table/CombatWidget.cpp b/src/ui/table/CombatWidget.cpp
index 20d0ac4..8884dc7 100644
--- a/src/ui/table/CombatWidget.cpp
+++ b/src/ui/table/CombatWidget.cpp
@@ -44,11 +44,11 @@ CombatWidget::CombatWidget(std::shared_ptr tableFilerHandler,
m_undoStack = new QUndoStack(this);
+ m_removeCharacterAction = createAction(tr("Remove"), tr("Remove Character(s)"), QKeySequence(Qt::Key_Delete), false);
m_addCharacterAction = createAction(tr("Add new Character(s)..."), tr("Add new Character(s)"),
QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_N), true);
m_insertTableAction = createAction(tr("Insert other Table..."), tr("Insert another table without overwriting the current one"),
QKeySequence(tr("Ctrl+T")), false);
- m_removeAction = createAction(tr("Remove"), tr("Remove Character(s)"), QKeySequence(Qt::Key_Delete), false);
m_addEffectAction = createAction(tr("Add Status Effect(s)..."), tr("Add Status Effect(s)"), QKeySequence(tr("Ctrl+E")), false);
m_duplicateAction = createAction(tr("Duplicate"), tr("Duplicate Character"), QKeySequence(tr("Ctrl+D")), false);
m_rerollAction = createAction(tr("Reroll Initiative"), tr("Reroll Initiative"), QKeySequence(tr("Ctrl+I")), false);
@@ -75,9 +75,10 @@ CombatWidget::CombatWidget(std::shared_ptr tableFilerHandler,
setUndoRedoIcon(isSystemInDarkMode);
auto* const toolBar = new QToolBar("Actions");
+ toolBar->addAction(m_removeCharacterAction);
toolBar->addAction(m_addCharacterAction);
+ toolBar->addSeparator();
toolBar->addAction(m_insertTableAction);
- toolBar->addAction(m_removeAction);
toolBar->addAction(m_addEffectAction);
toolBar->addAction(m_resortAction);
toolBar->addSeparator();
@@ -152,9 +153,9 @@ CombatWidget::CombatWidget(std::shared_ptr tableFilerHandler,
m_roundCounterLabel->setText(tr("Round ") + QString::number(m_roundCounter));
});
+ connect(m_removeCharacterAction, &QAction::triggered, this, &CombatWidget::removeRow);
connect(m_addCharacterAction, &QAction::triggered, this, &CombatWidget::openAddCharacterDialog);
connect(m_insertTableAction, &QAction::triggered, this, &CombatWidget::insertTable);
- connect(m_removeAction, &QAction::triggered, this, &CombatWidget::removeRow);
connect(m_addEffectAction, &QAction::triggered, this, &CombatWidget::openStatusEffectDialog);
connect(m_duplicateAction, &QAction::triggered, this, &CombatWidget::duplicateRow);
connect(m_rerollAction, &QAction::triggered, this, &CombatWidget::rerollIni);
@@ -186,7 +187,7 @@ CombatWidget::CombatWidget(std::shared_ptr tableFilerHandler,
saveOldState();
});
connect(m_tableWidget, &QTableWidget::itemSelectionChanged, this, [this] {
- m_removeAction->setEnabled(m_tableWidget->selectionModel()->hasSelection());
+ m_removeCharacterAction->setEnabled(m_tableWidget->selectionModel()->hasSelection());
m_addEffectAction->setEnabled(m_tableWidget->selectionModel()->hasSelection());
m_duplicateAction->setEnabled(m_tableWidget->selectionModel()->selectedRows().size() == 1);
m_rerollAction->setEnabled(m_tableWidget->selectionModel()->selectedRows().size() == 1);
@@ -284,7 +285,8 @@ CombatWidget::pushOnUndoStack(bool resynchronize)
// We got everything, so push
m_undoStack->push(new Undo(this, m_logListWidget, m_roundCounterLabel, m_currentPlayerLabel,
oldData, newData, m_affectedRowIndices, &m_rowEntered, &m_roundCounter,
- m_tableSettings.colorTableRows, m_tableSettings.showIniToolTips));
+ m_tableSettings.colorTableRows, m_tableSettings.showIniToolTips,
+ m_tableSettings.adjustHeightAfterRemove));
m_affectedRowIndices.clear();
}
@@ -323,9 +325,9 @@ CombatWidget::resetNameAndInfoWidth(const int nameWidth, const int addInfoWidth)
void
CombatWidget::setUndoRedoIcon(bool isDarkMode)
{
+ m_removeCharacterAction->setIcon(isDarkMode ? QIcon(":/icons/table/remove_white.svg") : QIcon(":/icons/table/remove_black.svg"));
m_addCharacterAction->setIcon(isDarkMode ? QIcon(":/icons/table/add_white.svg") : QIcon(":/icons/table/add_black.svg"));
m_insertTableAction->setIcon(isDarkMode ? QIcon(":/icons/table/insert_table_white.svg") : QIcon(":/icons/table/insert_table_black.svg"));
- m_removeAction->setIcon(isDarkMode ? QIcon(":/icons/table/remove_white.svg") : QIcon(":/icons/table/remove_black.svg"));
m_addEffectAction->setIcon(isDarkMode ? QIcon(":/icons/table/effect_white.svg") : QIcon(":/icons/table/effect_black.svg"));
m_duplicateAction->setIcon(isDarkMode ? QIcon(":/icons/table/duplicate_white.svg") : QIcon(":/icons/table/duplicate_black.svg"));
m_rerollAction->setIcon(isDarkMode ? QIcon(":/icons/table/reroll_white.svg") : QIcon(":/icons/table/reroll_black.svg"));
@@ -334,6 +336,8 @@ CombatWidget::setUndoRedoIcon(bool isDarkMode)
m_resortAction->setIcon(isDarkMode ? QIcon(":/icons/table/sort_white.svg") : QIcon(":/icons/table/sort_black.svg"));
m_undoAction->setIcon(isDarkMode ? QIcon(":/icons/table/undo_white.svg") : QIcon(":/icons/table/undo_black.svg"));
m_redoAction->setIcon(isDarkMode ? QIcon(":/icons/table/redo_white.svg") : QIcon(":/icons/table/redo_black.svg"));
+ m_moveUpwardAction->setIcon(isDarkMode ? QIcon(":/icons/table/move_up_white.svg") : QIcon(":/icons/table/move_up_black.svg"));
+ m_moveDownwardAction->setIcon(isDarkMode ? QIcon(":/icons/table/move_down_white.svg") : QIcon(":/icons/table/move_down_black.svg"));
m_showLogAction->setIcon(isDarkMode ? QIcon(":/icons/table/log_white.svg") : QIcon(":/icons/table/log_black.svg"));
}
@@ -343,15 +347,14 @@ CombatWidget::openAddCharacterDialog()
{
// Resynchronize because the table could have been modified
m_tableWidget->resynchronizeCharacters();
- auto sizeBeforeDialog = m_characterHandler->getCharacters().size();
+ const auto sizeBeforeDialog = m_characterHandler->getCharacters().size();
auto *const dialog = new AddCharacterDialog(m_additionalSettings.modAddedToIni, this);
connect(dialog, &AddCharacterDialog::characterCreated, this, [this, &sizeBeforeDialog] (CharacterHandler::Character character, int instanceCount) {
addCharacter(character, instanceCount);
- emit tableHeightSet(m_tableWidget->getHeight() + 40);
+ emit tableHeightSet(m_tableWidget->getHeight() + Utils::Table::HEIGHT_BUFFER);
m_logListWidget->logConditionalValue(COUNT, m_characterHandler->getCharacters().size() - sizeBeforeDialog, true);
- sizeBeforeDialog = m_characterHandler->getCharacters().size();
});
if (dialog->exec() == QDialog::Accepted) {
@@ -415,7 +418,7 @@ CombatWidget::insertTable()
std::iota(m_affectedRowIndices.begin(), m_affectedRowIndices.end(), oldSize);
pushOnUndoStack();
- emit tableHeightSet(m_tableWidget->getHeight() + 40);
+ emit tableHeightSet(m_tableWidget->getHeight() + Utils::Table::HEIGHT_BUFFER);
auto const reply = QMessageBox::question(this, tr("Sort Table?"), tr("Do you want to resort the Table?"),
QMessageBox::Yes | QMessageBox::No);
@@ -615,6 +618,10 @@ CombatWidget::removeRow()
setRowAndPlayer();
pushOnUndoStack();
m_tableWidget->itemSelectionChanged();
+
+ if (m_tableSettings.adjustHeightAfterRemove) {
+ emit tableHeightSet(m_tableWidget->getHeight() + Utils::Table::HEIGHT_BUFFER);
+ }
}
@@ -800,6 +807,7 @@ CombatWidget::setTableOption(bool option, int valueType)
case 3:
m_tableWidget->setIniColumnTooltips(!option);
break;
+ case 4:
default:
break;
}
@@ -834,7 +842,7 @@ CombatWidget::loadCharactersFromTable(const QJsonObject& jsonObject)
additionalInfoData.statusEffects.push_back(effect);
}
- characters.push_back(CharacterHandler::Character {
+ characters.emplace_back(CharacterHandler::Character {
characterObject.value("name").toString(), characterObject.value("initiative").toInt(),
characterObject.value("modifier").toInt(), characterObject.value("hp").toInt(),
characterObject.value("is_enemy").toBool(), additionalInfoData });
@@ -860,15 +868,17 @@ CombatWidget::contextMenuEvent(QContextMenuEvent *event)
{
auto *const menu = new QMenu(this);
+ const auto currentRow = m_tableWidget->indexAt(m_tableWidget->viewport()->mapFrom(this, event->pos())).row();
+ if (currentRow >= 0) {
+ menu->addAction(m_removeCharacterAction);
+ }
menu->addAction(m_addCharacterAction);
+
if (m_tableWidget->rowCount() > 0) {
menu->addAction(m_insertTableAction);
}
-
- const auto currentRow = m_tableWidget->indexAt(m_tableWidget->viewport()->mapFrom(this, event->pos())).row();
// Map from MainWindow coordinates to Table Widget coordinates
if (currentRow >= 0) {
- menu->addAction(m_removeAction);
menu->addAction(m_addEffectAction);
if (m_tableWidget->rowCount() > 1) {
@@ -922,5 +932,11 @@ CombatWidget::contextMenuEvent(QContextMenuEvent *event)
showIniTooltipsAction->setCheckable(true);
showIniTooltipsAction->setChecked(m_tableSettings.showIniToolTips);
+ auto *const adjustHeightAfterRemoveAction = optionMenu->addAction(tr("Readjust Height after Character Removal"), this, [this] (bool show) {
+ setTableOption(show, 4);
+ });
+ adjustHeightAfterRemoveAction->setCheckable(true);
+ adjustHeightAfterRemoveAction->setChecked(m_tableSettings.adjustHeightAfterRemove);
+
menu->exec(event->globalPos());
}
diff --git a/src/ui/table/CombatWidget.hpp b/src/ui/table/CombatWidget.hpp
index 1e9bb05..7294d18 100644
--- a/src/ui/table/CombatWidget.hpp
+++ b/src/ui/table/CombatWidget.hpp
@@ -176,9 +176,9 @@ private slots:
QPointer m_timer;
+ QPointer m_removeCharacterAction;
QPointer m_addCharacterAction;
QPointer m_insertTableAction;
- QPointer m_removeAction;
QPointer m_addEffectAction;
QPointer m_duplicateAction;
QPointer m_rerollAction;
diff --git a/src/ui/table/Undo.cpp b/src/ui/table/Undo.cpp
index 1f547a7..2e73e39 100644
--- a/src/ui/table/Undo.cpp
+++ b/src/ui/table/Undo.cpp
@@ -12,12 +12,12 @@ Undo::Undo(CombatWidget *CombatWidget, LogListWidget* logListWidget,
QPointer roundCounterLabel, QPointer currentPlayerLabel,
const UndoData& oldData, const UndoData& newData, const std::vector affectedRows,
unsigned int* rowEntered, unsigned int* roundCounter,
- bool colorTableRows, bool showIniToolTips) :
+ bool colorTableRows, bool showIniToolTips, bool adjustTableHeight) :
m_combatWidget(CombatWidget), m_logListWidget(logListWidget),
m_roundCounterLabel(roundCounterLabel), m_currentPlayerLabel(currentPlayerLabel),
m_oldData(std::move(oldData)), m_newData(std::move(newData)), m_affectedRows(std::move(affectedRows)),
m_rowEntered(rowEntered), m_roundCounter(roundCounter),
- m_colorTableRows(colorTableRows), m_showIniToolTips(showIniToolTips)
+ m_colorTableRows(colorTableRows), m_showIniToolTips(showIniToolTips), m_adjustTableHeight(adjustTableHeight)
{
}
@@ -83,7 +83,13 @@ Undo::setCombatWidget(bool undo)
tableWidget->setTableRowColor(!m_colorTableRows);
tableWidget->setIniColumnTooltips(!m_showIniToolTips);
- emit m_combatWidget->tableHeightSet(tableWidget->getHeight());
+ // Only set table height if undoing and not removing
+ // (this is handled in the combat widget's remove row function)
+ const auto isRemovingRow = (oldTableData.size() < newTableData.size() && undo) ||
+ (oldTableData.size() > newTableData.size() && !undo);
+ if (undo && !isRemovingRow && m_adjustTableHeight) {
+ emit m_combatWidget->tableHeightSet(tableWidget->getHeight() + Utils::Table::HEIGHT_BUFFER);
+ }
emit m_combatWidget->changeOccured();
tableWidget->blockSignals(false);
diff --git a/src/ui/table/Undo.hpp b/src/ui/table/Undo.hpp
index 0c40c70..675d079 100644
--- a/src/ui/table/Undo.hpp
+++ b/src/ui/table/Undo.hpp
@@ -29,7 +29,8 @@ class Undo : public QUndoCommand {
unsigned int* rowEntered,
unsigned int* roundCounter,
bool colorTableRows,
- bool showIniToolTips);
+ bool showIniToolTips,
+ bool adjustTableHeight);
void
undo() override;
@@ -66,6 +67,7 @@ class Undo : public QUndoCommand {
const bool m_colorTableRows;
const bool m_showIniToolTips;
+ const bool m_adjustTableHeight;
static constexpr int COL_ENEMY = 4;
static constexpr int COL_ADDITIONAL = 5;
diff --git a/src/ui/table/dialog/AddCustomEffectDialog.cpp b/src/ui/table/dialog/AddCustomEffectDialog.cpp
new file mode 100644
index 0000000..9c9d6e2
--- /dev/null
+++ b/src/ui/table/dialog/AddCustomEffectDialog.cpp
@@ -0,0 +1,37 @@
+#include "AddCustomEffectDialog.hpp"
+
+#include "UtilsGeneral.hpp"
+
+#include
+#include
+#include
+#include
+#include
+
+AddCustomEffectDialog::AddCustomEffectDialog(const QList& otherEffects, QWidget *parent) :
+ QDialog(parent)
+{
+ setWindowTitle(tr("Add Custom Effect"));
+
+ auto* const lineEdit = new QLineEdit;
+
+ auto *const buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+
+ auto* const mainLayout = new QVBoxLayout;
+ mainLayout->addWidget(lineEdit);
+ mainLayout->addWidget(buttonBox);
+
+ setLayout(mainLayout);
+
+ connect(buttonBox, &QDialogButtonBox::accepted, this, [this, otherEffects, lineEdit] {
+ if (otherEffects.contains(lineEdit->text())) {
+ Utils::General::displayWarningMessageBox(this, tr("Effect already exists!"),
+ tr("The effect already exists. Please provide a different name!"));
+ return;
+ }
+
+ m_name = lineEdit->text();
+ QDialog::accept();
+ });
+ connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+}
diff --git a/src/ui/table/dialog/AddCustomEffectDialog.hpp b/src/ui/table/dialog/AddCustomEffectDialog.hpp
new file mode 100644
index 0000000..e07c78c
--- /dev/null
+++ b/src/ui/table/dialog/AddCustomEffectDialog.hpp
@@ -0,0 +1,22 @@
+#pragma once
+
+#include
+
+// Dialog used to add effects
+class AddCustomEffectDialog : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit
+ AddCustomEffectDialog(const QList& otherEffects,
+ QWidget* parent = 0);
+
+ [[nodiscard]] QString&
+ getName()
+ {
+ return m_name;
+ }
+
+private:
+ QString m_name;
+};
diff --git a/src/ui/table/dialog/CMakeLists.txt b/src/ui/table/dialog/CMakeLists.txt
index 2f76bdd..0e2cacc 100644
--- a/src/ui/table/dialog/CMakeLists.txt
+++ b/src/ui/table/dialog/CMakeLists.txt
@@ -9,6 +9,8 @@ target_include_directories (dialog
target_sources(dialog INTERFACE
${CMAKE_CURRENT_LIST_DIR}/AddCharacterDialog.hpp
${CMAKE_CURRENT_LIST_DIR}/AddCharacterDialog.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/AddCustomEffectDialog.hpp
+ ${CMAKE_CURRENT_LIST_DIR}/AddCustomEffectDialog.cpp
${CMAKE_CURRENT_LIST_DIR}/ChangeStatDialog.hpp
${CMAKE_CURRENT_LIST_DIR}/ChangeStatDialog.cpp
${CMAKE_CURRENT_LIST_DIR}/StatusEffectData.hpp
diff --git a/src/ui/table/dialog/StatusEffectDialog.cpp b/src/ui/table/dialog/StatusEffectDialog.cpp
index 2850a41..ef82420 100644
--- a/src/ui/table/dialog/StatusEffectDialog.cpp
+++ b/src/ui/table/dialog/StatusEffectDialog.cpp
@@ -1,18 +1,23 @@
#include "StatusEffectDialog.hpp"
+#include "AddCustomEffectDialog.hpp"
#include "RuleSettings.hpp"
#include "StatusEffectData.hpp"
+#include "UtilsFiles.hpp"
+#include "UtilsGeneral.hpp"
#include
-#include
#include
+#include
+#include
#include
#include
#include
-#include
#include
#include
#include
+#include
+#include
#include
StatusEffectDialog::StatusEffectDialog(const RuleSettings& RuleSettings, QWidget *parent) :
@@ -20,7 +25,7 @@ StatusEffectDialog::StatusEffectDialog(const RuleSettings& RuleSettings, QWidget
{
setWindowTitle(tr("Add Status Effect(s)"));
- m_lineEdit = new QLineEdit(this);
+ m_lineEdit = new QLineEdit;
auto *const shortcut = new QShortcut(QKeySequence::Find, this);
connect(shortcut, &QShortcut::activated, this, [this] () {
m_lineEdit->setFocus(Qt::ShortcutFocusReason);
@@ -30,11 +35,56 @@ StatusEffectDialog::StatusEffectDialog(const RuleSettings& RuleSettings, QWidget
m_lineEdit->setToolTip(tr("Selected list items are returned as effect.\n"
"If nothing is selected, the entered text will be returned."));
- m_listWidget = new QListWidget(this);
- m_listWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ m_effectFileHandler = std::make_unique();
+
+ m_treeWidget = new QTreeWidget;
+ m_treeWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ m_treeWidget->setHeaderHidden(true);
+
+ // Apply standard effects
+ auto* const standardItem = new QTreeWidgetItem;
+ standardItem->setText(COL_NAME, "Standard Effects:");
+ m_treeWidget->addTopLevelItem(standardItem);
+
+ QList items;
for (const auto effects = StatusEffectData::getEffectList(m_ruleSettings.ruleset); const auto& effect : effects) {
- m_listWidget->addItem(new QListWidgetItem(effect));
+ auto* const item = new QTreeWidgetItem;
+ item->setText(COL_NAME, effect);
+ items.append(item);
+ }
+ standardItem->addChildren(items);
+ items.clear();
+
+ // Apply custom effects
+ m_customHeaderItem = new QTreeWidgetItem;
+ m_customHeaderItem->setText(COL_NAME, "Custom Effects:");
+ m_treeWidget->addTopLevelItem(m_customHeaderItem);
+
+ QDirIterator it(m_effectFileHandler->getDirectoryString(), { "*.effect" }, QDir::Files);
+ while (it.hasNext()) {
+ it.next();
+
+ if (const auto code = m_effectFileHandler->getStatus(it.fileName()); code == 0) {
+ const auto effectObject = m_effectFileHandler->getData();
+ auto* const item = new QTreeWidgetItem;
+ item->setText(COL_NAME, effectObject["name"].toString());
+ items.append(item);
+ }
}
+ m_customHeaderItem->addChildren(items);
+ m_treeWidget->expandAll();
+
+ m_removeEffectButton = new QToolButton;
+ m_removeEffectButton->setEnabled(false);
+ m_removeEffectButton->setToolTip(tr("Remove a custom effect."));
+ m_addEffectButton = new QToolButton;
+ m_addEffectButton->setToolTip(tr("Add a custom effect."));
+ setButtonIcons();
+
+ auto* const effectButtonLayout = new QHBoxLayout;
+ effectButtonLayout->addStretch();
+ effectButtonLayout->addWidget(m_removeEffectButton);
+ effectButtonLayout->addWidget(m_addEffectButton);
m_checkBox = new QCheckBox(tr("Permanent"));
m_checkBox->setTristate(false);
@@ -59,7 +109,8 @@ StatusEffectDialog::StatusEffectDialog(const RuleSettings& RuleSettings, QWidget
auto *const layout = new QVBoxLayout(this);
layout->addWidget(m_lineEdit);
- layout->addWidget(m_listWidget);
+ layout->addWidget(m_treeWidget);
+ layout->addLayout(effectButtonLayout);
layout->addLayout(spinBoxLayout);
layout->addWidget(buttonBox);
setLayout(layout);
@@ -67,29 +118,105 @@ StatusEffectDialog::StatusEffectDialog(const RuleSettings& RuleSettings, QWidget
connect(m_lineEdit, &QLineEdit::textChanged, this, [this] () {
findEffect(m_lineEdit->text());
});
+ connect(m_treeWidget, &QTreeWidget::itemDoubleClicked, this, [this, standardItem] (QTreeWidgetItem *item, int /* column */) {
+ if (item == standardItem || item == m_customHeaderItem) {
+ return;
+ }
+
+ createEffect(item->text(COL_NAME));
+ QDialog::accept();
+ });
+ connect(m_treeWidget, &QTreeWidget::itemSelectionChanged, this, &StatusEffectDialog::setRemoveButtonEnabling);
+ connect(m_addEffectButton, &QPushButton::clicked, this, &StatusEffectDialog::addEffectButtonClicked);
+ connect(m_removeEffectButton, &QPushButton::clicked, this, &StatusEffectDialog::removeEffectButtonClicked);
connect(m_checkBox, &QCheckBox::stateChanged, this, [this, spinBoxLabel] {
spinBoxLabel->setEnabled(m_checkBox->checkState() != Qt::Checked);
m_spinBox->setEnabled(m_checkBox->checkState() != Qt::Checked);
});
- connect(m_listWidget, &QListWidget::itemDoubleClicked, this, [this] (QListWidgetItem *item) {
- createEffect(item->text());
- QDialog::accept();
- });
connect(okButton, &QPushButton::clicked, this, &StatusEffectDialog::okButtonClicked);
connect(cancelButton, &QPushButton::clicked, this, &QDialog::reject);
}
+void
+StatusEffectDialog::setRemoveButtonEnabling()
+{
+ const auto& selectedItems = m_treeWidget->selectedItems();
+ if (selectedItems.size() != 1) {
+ m_removeEffectButton->setEnabled(false);
+ return;
+ }
+
+ QList customChildrenItem;
+ for (auto i = 0; i < m_customHeaderItem->childCount(); i++) {
+ customChildrenItem.append(m_customHeaderItem->child(i));
+ }
+
+ for (auto i = 0; i < selectedItems.count(); i++) {
+ if (!customChildrenItem.contains(selectedItems.at(i))) {
+ m_removeEffectButton->setEnabled(false);
+ return;
+ }
+ }
+ m_removeEffectButton->setEnabled(true);
+}
+
+
+void
+StatusEffectDialog::removeEffectButtonClicked()
+{
+ const auto& selectedItems = m_treeWidget->selectedItems();
+ if (selectedItems.size() != 1) {
+ return;
+ }
+
+ const auto foundEffect = Utils::Files::findObject(m_effectFileHandler->getDirectoryString(), "*.effect", selectedItems.at(0)->text(COL_NAME));
+ if (!foundEffect.has_value()) {
+ Utils::General::displayWarningMessageBox(this, tr("Effect not found!"), tr("The Effect was not found on disc!"));
+ return;
+ }
+
+ if (const auto effectRemoved = Utils::Files::removeFile(foundEffect.value()); !effectRemoved) {
+ Utils::General::displayWarningMessageBox(this, tr("Action not possible!"), tr("Could not remove Effect!"));
+ return;
+ }
+ delete selectedItems.at(0);
+}
+
+
+void
+StatusEffectDialog::addEffectButtonClicked()
+{
+ QList otherEffects;
+ for (auto i = 0; i < m_customHeaderItem->childCount(); i++) {
+ auto* const customEffectItem = m_customHeaderItem->child(i);
+ otherEffects.append(customEffectItem->text(COL_NAME));
+ }
+
+ if (auto *const dialog = new AddCustomEffectDialog(otherEffects, this); dialog->exec() == QDialog::Accepted) {
+ const auto effectName = dialog->getName();
+ if (!m_effectFileHandler->writeToFile(effectName)) {
+ Utils::General::displayWarningMessageBox(this, tr("Action not possible!"), tr("The Effect could not be saved!"));
+ return;
+ }
+
+ auto* const newItem = new QTreeWidgetItem;
+ newItem->setText(COL_NAME, effectName);
+ m_customHeaderItem->addChild(newItem);
+ }
+}
+
+
void
StatusEffectDialog::okButtonClicked()
{
// If nothing is selected, add the line edit text as status effect
- if (m_listWidget->selectedItems().empty() && !m_lineEdit->text().isEmpty()) {
+ if (m_treeWidget->selectedItems().empty() && !m_lineEdit->text().isEmpty()) {
createEffect(m_lineEdit->text());
} else {
// Otherwise, add the effect in the list
- for (auto* const item : m_listWidget->selectedItems()) {
- createEffect(item->text());
+ for (auto* const item : m_treeWidget->selectedItems()) {
+ createEffect(item->text(COL_NAME));
}
}
@@ -112,8 +239,30 @@ void
StatusEffectDialog::findEffect(const QString& filter)
{
// Hide effects not containing the filter
- for (int i = 0; i < m_listWidget->count(); ++i) {
- auto *item = m_listWidget->item(i);
- item->setHidden(!item->text().contains(filter, Qt::CaseInsensitive));
+ QTreeWidgetItemIterator it(m_treeWidget);
+ while (*it) {
+ if ((*it)->childCount() == 0) {
+ (*it)->setHidden(!(*it)->text(COL_NAME).contains(filter, Qt::CaseInsensitive));
+ }
+ ++it;
+ }
+}
+
+
+void
+StatusEffectDialog::setButtonIcons()
+{
+ const auto isSystemInDarkMode = Utils::General::isSystemInDarkMode();
+ m_removeEffectButton->setIcon(isSystemInDarkMode ? QIcon(":/icons/table/remove_white.svg") : QIcon(":/icons/table/remove_black.svg"));
+ m_addEffectButton->setIcon(isSystemInDarkMode ? QIcon(":/icons/table/add_white.svg") : QIcon(":/icons/table/add_black.svg"));
+}
+
+
+bool
+StatusEffectDialog::event(QEvent *event)
+{
+ [[unlikely]] if (event->type() == QEvent::ApplicationPaletteChange || event->type() == QEvent::PaletteChange) {
+ setButtonIcons();
}
+ return QWidget::event(event);
}
diff --git a/src/ui/table/dialog/StatusEffectDialog.hpp b/src/ui/table/dialog/StatusEffectDialog.hpp
index 51de83d..f001af5 100644
--- a/src/ui/table/dialog/StatusEffectDialog.hpp
+++ b/src/ui/table/dialog/StatusEffectDialog.hpp
@@ -1,13 +1,16 @@
#pragma once
#include "AdditionalInfoData.hpp"
+#include "EffectFileHandler.hpp"
#include
#include
+#include
class QCheckBox;
class QLineEdit;
-class QListWidget;
+class QTreeWidget;
+class QToolButton;
class QSpinBox;
class RuleSettings;
@@ -28,6 +31,15 @@ class StatusEffectDialog : public QDialog {
}
private slots:
+ void
+ setRemoveButtonEnabling();
+
+ void
+ addEffectButtonClicked();
+
+ void
+ removeEffectButtonClicked();
+
void
okButtonClicked();
@@ -38,13 +50,26 @@ private slots:
void
findEffect(const QString& filter);
+ void
+ setButtonIcons();
+
+ bool
+ event(QEvent* event);
+
private:
- QPointer m_listWidget;
QPointer m_lineEdit;
+ QPointer m_treeWidget;
+ QPointer m_removeEffectButton;
+ QPointer m_addEffectButton;
QPointer m_checkBox;
QPointer m_spinBox;
+ QTreeWidgetItem* m_customHeaderItem;
+
+ std::unique_ptr m_effectFileHandler;
QVector m_effects;
const RuleSettings& m_ruleSettings;
+
+ static constexpr int COL_NAME = 0;
};
diff --git a/src/ui/table/dialog/template/TemplatesListWidget.cpp b/src/ui/table/dialog/template/TemplatesListWidget.cpp
index e3dc869..a1c4e0d 100644
--- a/src/ui/table/dialog/template/TemplatesListWidget.cpp
+++ b/src/ui/table/dialog/template/TemplatesListWidget.cpp
@@ -28,16 +28,14 @@ TemplatesListWidget::addCharacter(const CharacterHandler::Character& character)
}
-bool
-TemplatesListWidget::removeCharacter(const CharacterHandler::Character &character)
+void
+TemplatesListWidget::removeCharacter(const QString& characterName)
{
for (auto i = 0; i < count(); i++) {
auto const storedCharacter = item(i)->data(Qt::UserRole).value();
- if (storedCharacter.name == character.name) {
+ if (storedCharacter.name == characterName) {
QListWidgetItem *it = this->takeItem(i);
delete it;
- return true;
}
}
- return false;
}
diff --git a/src/ui/table/dialog/template/TemplatesListWidget.hpp b/src/ui/table/dialog/template/TemplatesListWidget.hpp
index aa97066..740271b 100644
--- a/src/ui/table/dialog/template/TemplatesListWidget.hpp
+++ b/src/ui/table/dialog/template/TemplatesListWidget.hpp
@@ -15,6 +15,6 @@ class TemplatesListWidget : public QListWidget {
bool
addCharacter(const CharacterHandler::Character& character);
- bool
- removeCharacter(const CharacterHandler::Character& character);
+ void
+ removeCharacter(const QString& characterName);
};
diff --git a/src/ui/table/dialog/template/TemplatesWidget.cpp b/src/ui/table/dialog/template/TemplatesWidget.cpp
index 897fd44..6869ed8 100644
--- a/src/ui/table/dialog/template/TemplatesWidget.cpp
+++ b/src/ui/table/dialog/template/TemplatesWidget.cpp
@@ -6,6 +6,7 @@
#include
#include
+#include "UtilsFiles.hpp"
#include "UtilsGeneral.hpp"
TemplatesWidget::TemplatesWidget(QWidget* parent) :
@@ -52,6 +53,7 @@ TemplatesWidget::loadTemplates()
}
}
+ m_templatesListWidget->sortItems();
if (m_templatesListWidget->count() > 0) {
m_templatesListWidget->item(0)->setSelected(true);
}
@@ -93,12 +95,16 @@ TemplatesWidget::removeButtonClicked()
return;
}
- const auto character = m_templatesListWidget->selectedItems().first()->data(Qt::UserRole).value();
- if (!m_templatesListWidget->removeCharacter(character)) {
- Utils::General::displayWarningMessageBox(this, tr("Action not possible!"), tr("Could not remove Character!"));
+ const auto characterName = m_templatesListWidget->selectedItems().first()->data(Qt::UserRole).value().name;
+ const auto foundCharacter = Utils::Files::findObject(m_charFileHandler->getDirectoryString(), "*.char", characterName);
+ if (!foundCharacter.has_value()) {
+ Utils::General::displayWarningMessageBox(this, tr("Character not found!"), tr("The Character was not found on disc!"));
return;
}
- if (!m_charFileHandler->removeCharacter(character.name + ".char")) {
- Utils::General::displayWarningMessageBox(this, tr("Action not possible!"), tr("The Character could not be removed!"));
+
+ if (const auto charRemoved = Utils::Files::removeFile(foundCharacter.value()); !charRemoved) {
+ Utils::General::displayWarningMessageBox(this, tr("Action not possible!"), tr("The Character could not be removed from disc!"));
+ return;
}
+ m_templatesListWidget->removeCharacter(characterName);
}
diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt
index bca92a3..c4dbcd8 100644
--- a/src/utils/CMakeLists.txt
+++ b/src/utils/CMakeLists.txt
@@ -5,6 +5,8 @@ target_include_directories (utils
)
target_sources(utils INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/UtilsFiles.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/UtilsFiles.hpp
${CMAKE_CURRENT_LIST_DIR}/UtilsGeneral.cpp
${CMAKE_CURRENT_LIST_DIR}/UtilsGeneral.hpp
${CMAKE_CURRENT_LIST_DIR}/UtilsTable.cpp
diff --git a/src/utils/UtilsFiles.cpp b/src/utils/UtilsFiles.cpp
new file mode 100644
index 0000000..d775d75
--- /dev/null
+++ b/src/utils/UtilsFiles.cpp
@@ -0,0 +1,41 @@
+#include "UtilsFiles.hpp"
+
+#include
+#include
+#include
+#include
+
+namespace Utils::Files
+{
+bool
+removeFile(const QString& fileName)
+{
+ QFile file(fileName);
+ if (!file.exists()) {
+ return false;
+ }
+
+ return file.remove();
+};
+
+
+std::optional
+findObject(const QString& directory, const QString& fileEnding, const QString& objectName)
+{
+ QDirIterator it(directory, { fileEnding }, QDir::Files);
+ while (it.hasNext()) {
+ it.next();
+
+ QFile file(it.filePath());
+ file.open(QIODevice::ReadOnly);
+ const auto jsonObject = QJsonDocument::fromJson(file.readAll()).object();
+ file.close();
+
+ if (jsonObject["name"] == objectName) {
+ return it.filePath();
+ }
+ }
+
+ return {};
+}
+}
diff --git a/src/utils/UtilsFiles.hpp b/src/utils/UtilsFiles.hpp
new file mode 100644
index 0000000..7a6a152
--- /dev/null
+++ b/src/utils/UtilsFiles.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#include
+
+#include
+
+// Utils for file handling
+namespace Utils::Files
+{
+// Remove a file
+[[nodiscard]] bool
+removeFile(const QString& fileName);
+
+// Find a json object inside a directory by a certain name
+std::optional
+findObject(const QString& directory,
+ const QString& fileEnding,
+ const QString& objectName);
+}
diff --git a/src/utils/UtilsGeneral.cpp b/src/utils/UtilsGeneral.cpp
index a408512..ef80d40 100644
--- a/src/utils/UtilsGeneral.cpp
+++ b/src/utils/UtilsGeneral.cpp
@@ -80,7 +80,7 @@ getRulesetName(unsigned int ruleset)
QString
getAutoRollEnabled(bool autoRollEnabled)
{
- return autoRollEnabled ? "automatic rolling enabled" : "automatic rolling disabled";
+ return autoRollEnabled ? "automatic rolling enabled" : "automatic rolling disabled";
}
diff --git a/src/utils/UtilsTable.hpp b/src/utils/UtilsTable.hpp
index f4b5411..034fb46 100644
--- a/src/utils/UtilsTable.hpp
+++ b/src/utils/UtilsTable.hpp
@@ -14,6 +14,8 @@ setTableAdditionalInfoWidget(CombatWidget* combatWidget,
unsigned int row,
const QVariant& additionalInfo);
+static constexpr int HEIGHT_BUFFER = 40;
+
static constexpr int COL_NAME = 0;
static constexpr int COL_INI = 1;
static constexpr int COL_MODIFIER = 2;
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index e8cc7bb..d864acf 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -12,6 +12,7 @@ add_executable(tests
${CMAKE_CURRENT_LIST_DIR}/handler/CharacterHandlerTest.cpp
${CMAKE_CURRENT_LIST_DIR}/handler/CharFileHandlerTest.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/handler/EffectFileHandlerTest.cpp
${CMAKE_CURRENT_LIST_DIR}/handler/TableFileHandlerTest.cpp
${CMAKE_CURRENT_LIST_DIR}/ui/settings/SettingsTest.cpp
@@ -20,7 +21,8 @@ add_executable(tests
${CMAKE_CURRENT_LIST_DIR}/ui/widget/LogListWidgetTest.cpp
${CMAKE_CURRENT_LIST_DIR}/ui/widget/TemplatesListWidgetTest.cpp
- ${CMAKE_CURRENT_LIST_DIR}/utils/GeneralUtilsTest.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/utils/UtilsGeneralTest.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/utils/UtilsFilesTest.cpp
)
target_link_libraries(tests
diff --git a/test/handler/CharFileHandlerTest.cpp b/test/handler/CharFileHandlerTest.cpp
index 3b5833c..6ca0a1d 100644
--- a/test/handler/CharFileHandlerTest.cpp
+++ b/test/handler/CharFileHandlerTest.cpp
@@ -9,8 +9,6 @@
#endif
#include
-#include
-#include
TEST_CASE("CharFileHandler Testing", "[CharFileHandler]") {
auto const charFileHandler = std::make_shared();
@@ -41,12 +39,8 @@ TEST_CASE("CharFileHandler Testing", "[CharFileHandler]") {
// Incomplete json object
QJsonObject jsonObject;
jsonObject["name"] = 2;
-
// Write to file
- auto byteArray = QJsonDocument(jsonObject).toJson();
- QFile fileOut(dir.currentPath() + "/chars/broken.char");
- fileOut.open(QIODevice::WriteOnly);
- fileOut.write(byteArray);
+ charFileHandler->writeJsonObjectToFile(jsonObject, dir.currentPath() + "/chars/broken.char");
REQUIRE(charFileHandler->getStatus("broken.char") == 1);
dir.remove(dir.currentPath() + "/chars/broken.char");
@@ -55,9 +49,6 @@ TEST_CASE("CharFileHandler Testing", "[CharFileHandler]") {
const auto codeCSVStatus = charFileHandler->getStatus("nonexisting.char");
REQUIRE(codeCSVStatus == 2);
}
- SECTION("Check file removal") {
- const auto fileRemoved = charFileHandler->removeCharacter("test.char");
- REQUIRE(fileRemoved == true);
- REQUIRE(!dir.exists(charPath));
- }
+
+ std::remove("./chars/test.char");
}
diff --git a/test/handler/EffectFileHandlerTest.cpp b/test/handler/EffectFileHandlerTest.cpp
new file mode 100644
index 0000000..3e0d7b6
--- /dev/null
+++ b/test/handler/EffectFileHandlerTest.cpp
@@ -0,0 +1,46 @@
+#include "EffectFileHandler.hpp"
+
+#ifdef CATCH2_V3
+#include
+#else
+#include
+#endif
+
+#include
+
+TEST_CASE("EffectFileHandler Testing", "[EffectFileHandler]") {
+ auto const effectFileHandler = std::make_unique();
+ const auto effectSaved = effectFileHandler->writeToFile("Test Effect");
+
+ QDir dir;
+ const auto effectPath = dir.currentPath() + "/effects/Test Effect.effect";
+
+ SECTION("Effect successfully saved") {
+ REQUIRE(effectSaved == true);
+ REQUIRE(dir.exists(effectPath));
+ }
+ SECTION("File format and content correct") {
+ const auto codeCSVStatus = effectFileHandler->getStatus("Test Effect.effect");
+ REQUIRE(codeCSVStatus == 0);
+
+ const auto& jsonObject = effectFileHandler->getData();
+ REQUIRE(jsonObject.value("name").toString() == "Test Effect");
+ }
+
+ SECTION("Broken table") {
+ // Incomplete json object
+ QJsonObject jsonObject;
+ jsonObject["broken"] = 2;
+ // Write to file
+ effectFileHandler->writeJsonObjectToFile(jsonObject, dir.currentPath() + "/effects/Broken Effect.effect");
+
+ REQUIRE(effectFileHandler->getStatus("Broken Effect.effect") == 1);
+ dir.remove(dir.currentPath() + "/effects/Broken Effect.effect");
+ }
+ SECTION("Non-existent file") {
+ const auto codeCSVStatus = effectFileHandler->getStatus("nonexisting.effect");
+ REQUIRE(codeCSVStatus == 2);
+ }
+
+ std::remove("./effects/Test Effect.effect");
+}
diff --git a/test/handler/TableFileHandlerTest.cpp b/test/handler/TableFileHandlerTest.cpp
index 5414481..d132d1e 100644
--- a/test/handler/TableFileHandlerTest.cpp
+++ b/test/handler/TableFileHandlerTest.cpp
@@ -11,9 +11,7 @@
#include
#endif
-#include
#include
-#include
#include
#include
@@ -151,12 +149,8 @@ TEST_CASE_METHOD(FileHandlerTestUtils, "TableFileHandler Testing", "[TableFileHa
QJsonObject jsonObject;
jsonObject["row_entered"] = 2;
jsonObject["round_counter"] = 3;
-
// Write to file
- auto byteArray = QJsonDocument(jsonObject).toJson();
- QFile fileOut("./broken.lcm");
- fileOut.open(QIODevice::WriteOnly);
- fileOut.write(byteArray);
+ tableFileHandler->writeJsonObjectToFile(jsonObject, "./broken.lcm");
REQUIRE(tableFileHandler->getStatus(resolvePath("./broken.lcm")) == 1);
}
diff --git a/test/ui/settings/SettingsTest.cpp b/test/ui/settings/SettingsTest.cpp
index a307011..2d7dcb4 100644
--- a/test/ui/settings/SettingsTest.cpp
+++ b/test/ui/settings/SettingsTest.cpp
@@ -11,6 +11,9 @@
#include
+#include
+#include
+
TEST_CASE("Settings Testing", "[Settings]") {
SECTION("Concepts test") {
enum TestEnum {};
@@ -60,18 +63,41 @@ TEST_CASE("Settings Testing", "[Settings]") {
QSettings settings;
settings.clear();
+ // Create file so that the settings entry won't be deleted
+ std::filesystem::create_directories(std::filesystem::current_path().string() + "/example");
+ std::ofstream file(std::filesystem::current_path().string() + "/example/test.lcm");
+ file << "Text";
+ file.close();
+ const auto lcmFilePath = QString::fromStdString(std::filesystem::current_path().string() + "/example/test.lcm");
+
REQUIRE(settings.value("dir_save").isValid() == false);
REQUIRE(settings.value("dir_open").isValid() == false);
+ REQUIRE(settings.value("recent_dir_0").isValid() == false);
+ REQUIRE(settings.value("recent_dir_1").isValid() == false);
- dirSettings.write("/example/path/dir_open_and_save", true);
+ dirSettings.write(lcmFilePath, true);
REQUIRE(settings.value("dir_save").isValid() == true);
REQUIRE(settings.value("dir_open").isValid() == true);
- REQUIRE(settings.value("dir_open").toString() == "/example/path/dir_open_and_save");
- REQUIRE(settings.value("dir_save").toString() == "/example/path/dir_open_and_save");
+ REQUIRE(settings.value("recent_dir_0").isValid() == true);
+ REQUIRE(settings.value("dir_open").toString() == lcmFilePath);
+ REQUIRE(settings.value("dir_save").toString() == lcmFilePath);
+ REQUIRE(settings.value("recent_dir_0").toString() == lcmFilePath);
+
+ dirSettings.write("/example/invalid.csv", false);
+ REQUIRE(settings.value("dir_open").toString() == "/example/invalid.csv");
+ REQUIRE(settings.value("dir_save").toString() == lcmFilePath);
+ REQUIRE(settings.value("recent_dir_1").isValid() == true);
+ REQUIRE(settings.value("recent_dir_0").toString() == "/example/invalid.csv");
+ REQUIRE(settings.value("recent_dir_1").toString() == lcmFilePath);
+
+ DirSettings newDirSettings;
+ REQUIRE(settings.value("dir_save").isValid() == true);
+ REQUIRE(settings.value("dir_open").isValid() == true);
+ // These files never really existed, therefore the settings key should have been deleted
+ REQUIRE(settings.value("recent_dir_0").isValid() == false);
+ REQUIRE(settings.value("recent_dir_1").isValid() == true);
- dirSettings.write("/example/path/new_path", false);
- REQUIRE(settings.value("dir_open").toString() == "/example/path/new_path");
- REQUIRE(settings.value("dir_save").toString() == "/example/path/dir_open_and_save");
+ std::filesystem::remove_all("example/test.lcm");
}
SECTION("Rule settings test") {
RuleSettings ruleSettings;
@@ -102,23 +128,28 @@ TEST_CASE("Settings Testing", "[Settings]") {
REQUIRE(settings.value("modifier").isValid() == false);
REQUIRE(settings.value("color_rows").isValid() == false);
REQUIRE(settings.value("ini_tool_tips").isValid() == false);
+ REQUIRE(settings.value("adjust_height_remove").isValid() == false);
settings.endGroup();
tableSettings.write(TableSettings::ValueType::INI_SHOWN, false);
tableSettings.write(TableSettings::ValueType::MOD_SHOWN, false);
tableSettings.write(TableSettings::ValueType::COLOR_TABLE, true);
tableSettings.write(TableSettings::ValueType::SHOW_INI_TOOLTIPS, true);
+ tableSettings.write(TableSettings::ValueType::ADJUST_HEIGHT_AFTER_REMOVE, true);
settings.beginGroup("table");
REQUIRE(settings.value("ini").isValid() == true);
REQUIRE(settings.value("modifier").isValid() == true);
REQUIRE(settings.value("color_rows").isValid() == true);
REQUIRE(settings.value("ini_tool_tips").isValid() == true);
+ REQUIRE(settings.value("adjust_height_remove").isValid() == true);
REQUIRE(settings.value("ini").toBool() == false);
REQUIRE(settings.value("modifier").toBool() == false);
REQUIRE(settings.value("color_rows").toBool() == true);
REQUIRE(settings.value("ini_tool_tips").toBool() == true);
+ REQUIRE(settings.value("adjust_height_remove").toBool() == true);
settings.endGroup();
+ settings.clear();
}
}
diff --git a/test/ui/widget/TemplatesListWidgetTest.cpp b/test/ui/widget/TemplatesListWidgetTest.cpp
index 354fc34..b6bd07a 100644
--- a/test/ui/widget/TemplatesListWidgetTest.cpp
+++ b/test/ui/widget/TemplatesListWidgetTest.cpp
@@ -32,13 +32,7 @@ TEST_CASE("Templates List Widget Testing", "[TableUtils]") {
REQUIRE(sameCharacterAddedAgain == false);
}
SECTION("Remove character test") {
- const auto characterRemoved = templatesListWidget->removeCharacter(character);
- REQUIRE(characterRemoved == true);
+ templatesListWidget->removeCharacter(character.name);
REQUIRE(templatesListWidget->count() == 0);
}
- SECTION("Remove another unadded character test") {
- const auto anotherCharacter = CharacterHandler::Character("test2", 0, -3, 10, false, AdditionalInfoData{ .mainInfoText = "Haste" });
- const auto characterRemoved = templatesListWidget->removeCharacter(anotherCharacter);
- REQUIRE(characterRemoved == false);
- }
}
diff --git a/test/utils/UtilsFilesTest.cpp b/test/utils/UtilsFilesTest.cpp
new file mode 100644
index 0000000..958bbce
--- /dev/null
+++ b/test/utils/UtilsFilesTest.cpp
@@ -0,0 +1,37 @@
+#include "UtilsFiles.hpp"
+
+#ifdef CATCH2_V3
+#include
+#else
+#include
+#endif
+
+#include
+
+TEST_CASE("Utils Files Testing", "[UtilsFiles]") {
+ SECTION("Remove file test") {
+ std::ofstream file;
+ file.open("existing_file.txt");
+ file.close();
+
+ auto fileRemoved = Utils::Files::removeFile("existing_file.txt");
+ REQUIRE(fileRemoved == true);
+
+ fileRemoved = Utils::Files::removeFile("nonexisting_file.txt");
+ REQUIRE(fileRemoved == false);
+ }
+ SECTION("Find object test") {
+ std::ofstream file;
+ file.open("test.file");
+ file << "{\"name\": \"a_random_name\"}";
+ file.close();
+
+ auto foundEffect = Utils::Files::findObject(".", "*.file", "a_random_name");
+ REQUIRE(foundEffect.has_value());
+
+ foundEffect = Utils::Files::findObject("", "*.txt", "a_random_name");
+ REQUIRE(!foundEffect.has_value());
+ foundEffect = Utils::Files::findObject("", "*.file", "another_name");
+ REQUIRE(!foundEffect.has_value());
+ }
+}
diff --git a/test/utils/GeneralUtilsTest.cpp b/test/utils/UtilsGeneralTest.cpp
similarity index 84%
rename from test/utils/GeneralUtilsTest.cpp
rename to test/utils/UtilsGeneralTest.cpp
index be852de..0d9113e 100644
--- a/test/utils/GeneralUtilsTest.cpp
+++ b/test/utils/UtilsGeneralTest.cpp
@@ -1,4 +1,3 @@
-#include "AdditionalInfoWidget.hpp"
#include "UtilsGeneral.hpp"
#ifdef CATCH2_V3
@@ -7,9 +6,7 @@
#include
#endif
-#include
-
-TEST_CASE("General Util Testing", "[GeneralUtils]") {
+TEST_CASE("Utils General Testing", "[UtilsGeneral]") {
SECTION("CSV file path test") {
SECTION("Example Latin") {
REQUIRE(Utils::General::getLCMName("a/path/to/an/exampleTable.csv") == "exampleTable.csv");