UI updates

- add import dialog
  this is more complex than I thought
- added scope exit
  grammar sugar
- main dialog is now linked to import dialog
- importer citra file path is fixed
- importer now reports maximum size
- file util is improved with GetDirectoryTreeSize
This commit is contained in:
zhupengfei
2019-08-28 23:02:30 +08:00
parent 7df0b63a1e
commit 8acfe9f304
13 changed files with 448 additions and 27 deletions
+6
View File
@@ -7,6 +7,9 @@ if (POLICY CMP0071)
endif()
add_executable(threeSD
import_dialog.cpp
import_dialog.h
import_dialog.ui
main.cpp
main.h
main.ui
@@ -47,6 +50,9 @@ target_compile_definitions(threeSD PRIVATE
# Disable implicit QString->QUrl conversions to enforce use of proper resolving functions.
-DQT_NO_URL_CAST_FROM_STRING
# Disable automatic conversions from 8-bit strings (char *) to unicode QStrings
-DQT_NO_CAST_FROM_ASCII
)
if (MSVC)
+174
View File
@@ -0,0 +1,174 @@
// Copyright 2019 threeSD Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cmath>
#include <QCheckBox>
#include <QMessageBox>
#include <QPushButton>
#include <QStorageInfo>
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "frontend/import_dialog.h"
#include "ui_import_dialog.h"
QString ReadableByteSize(qulonglong size) {
static const std::array<const char*, 6> units = {QT_TR_NOOP("B"), QT_TR_NOOP("KiB"),
QT_TR_NOOP("MiB"), QT_TR_NOOP("GiB"),
QT_TR_NOOP("TiB"), QT_TR_NOOP("PiB")};
if (size == 0)
return QStringLiteral("0");
int digit_groups = std::min<int>(static_cast<int>(std::log10(size) / std::log10(1024)),
static_cast<int>(units.size()));
return QStringLiteral("%L1 %2")
.arg(size / std::pow(1024, digit_groups), 0, 'f', 1)
.arg(QObject::tr(units[digit_groups], "ImportDialog"));
}
ImportDialog::ImportDialog(QWidget* parent, const Core::Config& config)
: QDialog(parent), ui(std::make_unique<Ui::ImportDialog>()), user_path(config.user_path),
importer(config) {
ui->setupUi(this);
if (!importer.IsGood()) {
QMessageBox::critical(
this, tr("Importer Error"),
tr("Failed to initalize the importer.\nRefer to the log for details."));
reject();
}
PopulateContent();
UpdateSizeDisplay();
}
ImportDialog::~ImportDialog() = default;
void ImportDialog::PopulateContent() {
contents = importer.ListContent();
ui->main->clear();
ui->main->setSortingEnabled(false);
const std::map<Core::ContentType, QString> content_type_map{
{Core::ContentType::Application, QStringLiteral("Application")},
{Core::ContentType::Update, QStringLiteral("Update")},
{Core::ContentType::DLC, QStringLiteral("DLC (Add-on Content)")},
{Core::ContentType::Savegame, QStringLiteral("Save Data")},
{Core::ContentType::Extdata, QStringLiteral("Extra Data")},
{Core::ContentType::Sysdata, QStringLiteral("System Data")},
};
for (const auto& [type, name] : content_type_map) {
auto* checkBox = new QCheckBox();
checkBox->setText(name);
checkBox->setStyleSheet(QStringLiteral("margin-left:7px"));
checkBox->setTristate(true);
checkBox->setProperty("previousState", static_cast<int>(Qt::Unchecked));
auto* item = new QTreeWidgetItem;
item->setFirstColumnSpanned(true);
ui->main->invisibleRootItem()->addChild(item);
connect(checkBox, &QCheckBox::stateChanged, [this, checkBox, item](int state) {
SCOPE_EXIT({ checkBox->setProperty("previousState", state); });
if (program_trigger) {
program_trigger = false;
return;
}
if (state == Qt::PartiallyChecked) {
if (checkBox->property("previousState").toInt() == Qt::Unchecked) {
checkBox->setCheckState(static_cast<Qt::CheckState>(state = Qt::Checked));
} else {
checkBox->setCheckState(static_cast<Qt::CheckState>(state = Qt::Unchecked));
}
return;
}
program_trigger = true;
for (int i = 0; i < item->childCount(); ++i) {
static_cast<QCheckBox*>(ui->main->itemWidget(item->child(i), 0))
->setCheckState(static_cast<Qt::CheckState>(state));
}
program_trigger = false;
});
ui->main->setItemWidget(item, 0, checkBox);
}
for (const auto& content : contents) {
auto* checkBox = new QCheckBox();
checkBox->setStyleSheet(QStringLiteral("margin-left:7px"));
auto* item = new QTreeWidgetItem{
{QString{},
content.name.empty() ? QStringLiteral("0x%1").arg(content.id, 16, 16, QLatin1Char('0'))
: QString::fromStdString(content.name),
ReadableByteSize(content.maximum_size),
content.already_exists ? QStringLiteral("Yes") : QStringLiteral("No")}};
ui->main->invisibleRootItem()->child(static_cast<int>(content.type))->addChild(item);
ui->main->setItemWidget(item, 0, checkBox);
connect(checkBox, &QCheckBox::stateChanged,
[this, item, size = content.maximum_size](int state) {
if (state == Qt::Checked) {
total_size += size;
} else {
total_size -= size;
}
UpdateSizeDisplay();
if (!program_trigger) {
UpdateItemCheckState(item->parent());
}
});
}
ui->main->setSortingEnabled(true);
}
void ImportDialog::UpdateSizeDisplay() {
QStorageInfo storage(QString::fromStdString(user_path));
if (!storage.isValid() || !storage.isReady()) {
LOG_ERROR(Frontend, "Storage {} is not good", user_path);
QMessageBox::critical(
this, tr("Bad Storage"),
tr("An error occured while trying to get available space for the storage.\nPlease "
"ensure that your SD card is well connected and try again."));
reject();
}
ui->availableSpace->setText(
tr("Available Space: %1").arg(ReadableByteSize(storage.bytesAvailable())));
ui->totalSize->setText(tr("Total Size: %1").arg(ReadableByteSize(total_size)));
ui->buttonBox->button(QDialogButtonBox::StandardButton::Ok)
->setEnabled(total_size <= static_cast<u64>(storage.bytesAvailable()));
}
void ImportDialog::UpdateItemCheckState(QTreeWidgetItem* item) {
bool has_checked = false, has_unchecked = false;
auto* item_checkBox = static_cast<QCheckBox*>(ui->main->itemWidget(item, 0));
for (int i = 0; i < item->childCount(); ++i) {
auto* checkBox = static_cast<QCheckBox*>(ui->main->itemWidget(item->child(i), 0));
if (checkBox->isChecked()) {
has_checked = true;
} else {
has_unchecked = true;
}
if (has_checked && has_unchecked) {
program_trigger = true;
item_checkBox->setCheckState(Qt::PartiallyChecked);
program_trigger = false;
return;
}
}
program_trigger = true;
if (has_checked) {
item_checkBox->setCheckState(Qt::Checked);
} else {
item_checkBox->setCheckState(Qt::Unchecked);
}
program_trigger = false;
}
+41
View File
@@ -0,0 +1,41 @@
// Copyright 2019 threeSD Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include <vector>
#include <QDialog>
#include "core/importer.h"
class QTreeWidgetItem;
namespace Ui {
class ImportDialog;
}
class ImportDialog : public QDialog {
Q_OBJECT;
public:
explicit ImportDialog(QWidget* parent, const Core::Config& config);
~ImportDialog() override;
private:
void PopulateContent();
void UpdateSizeDisplay();
void UpdateItemCheckState(QTreeWidgetItem* item);
std::unique_ptr<Ui::ImportDialog> ui;
std::string user_path;
Core::SDMCImporter importer;
std::vector<Core::ContentSpecifier> contents;
u64 total_size = 0;
// HACK: To tell whether the checkbox state change is a programmatic trigger
// TODO: Is there a more elegant way of doing the same?
bool program_trigger = false;
};
+77
View File
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ImportDialog</class>
<widget class="QDialog" name="ImportDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>Select Contents</string>
</property>
<layout class="QVBoxLayout">
<item>
<widget class="QTreeWidget" name="main">
<column>
<property name="text">
<string/>
</property>
</column>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Size</string>
</property>
</column>
<column>
<property name="text">
<string>Imported</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="availableSpace">
<property name="text">
<string>Available Space:</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="totalSize">
<property name="text">
<string>Total Size:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Ok|QDialogButtonBox::Cancel</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+23 -2
View File
@@ -7,6 +7,7 @@
#include <QStorageInfo>
#include <qdevicewatcher.h>
#include "common/file_util.h"
#include "frontend/import_dialog.h"
#include "frontend/main.h"
#include "ui_main.h"
@@ -34,6 +35,9 @@ MainDialog::MainDialog(QWidget* parent) : QDialog(parent), ui(std::make_unique<U
connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button) {
if (button == ui->buttonBox->button(QDialogButtonBox::StandardButton::Reset)) {
LoadPresetConfig();
} else if (button == ui->buttonBox->button(QDialogButtonBox::StandardButton::Ok)) {
ImportDialog dialog(this, GetCurrentConfig());
dialog.exec();
}
});
@@ -124,10 +128,27 @@ void MainDialog::HideAdvanced() {
adjustSize();
}
Core::Config MainDialog::GetCurrentConfig() {
if (ui->customGroupBox->isVisible()) {
Core::Config config{
/*sdmc_path*/ ui->sdmcPath->text().toStdString(),
/*user_path*/ ui->userPath->text().toStdString(),
/*movable_sed_path*/ ui->movableSedPath->text().toStdString(),
/*bootrom_path*/ ui->bootrom9Path->text().toStdString(),
/*safe_mode_firm_path*/ ui->safeModeFirmPath->text().toStdString(),
/*seed_db_path*/ ui->seeddbPath->text().toStdString(),
/*secret_sector_path*/ ui->secretSectorPath->text().toStdString(),
};
return config;
} else {
return preset_config_list[ui->configSelect->currentIndex()];
}
}
int main(int argc, char* argv[]) {
// Init settings params
QCoreApplication::setOrganizationName("zhaowenlan1779");
QCoreApplication::setApplicationName("threeSD");
QCoreApplication::setOrganizationName(QStringLiteral("zhaowenlan1779"));
QCoreApplication::setApplicationName(QStringLiteral("threeSD"));
#ifdef __APPLE__
std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + "..";
+1
View File
@@ -23,6 +23,7 @@ private:
void LoadPresetConfig();
void ShowAdvanced();
void HideAdvanced();
Core::Config GetCurrentConfig();
std::vector<Core::Config> preset_config_list;
std::unique_ptr<Ui::MainDialog> ui;