Feature/UI launcher accessibility (#3417)

* update background and foreground colors in launcher

* Change tab order and preliminary focus visuals

* update focus color

Improves on #3091 but does not fixes it, the contrast ratio is 2.4/2.9-3.3 versus the white border/the gray background on each button/dropdown

* update focus border width

* Update color of error messages fixes #3103

* Enable enter key interaction on ui launcher buttons fixes #3092

* Update accessibility names on buttons and fields where the current readout does not make sense, fixes #3089

* Fixed issue where you could not tab out of multiline textfields

* Add overlay to the launcher image to increase contrast

* Slightly improves readability of Start button fixes #3096

We decided to keep the all caps to indicate call to action

* Add start button to have default focus which enables quick start by pressing enter or space on startup

* Removed overriden object name

* Update color label text for better clarity fixes #3094

The actual fix is in #9a7e8e1

* Add `esc` keypress to exit the launcher fixes #3097

* add placeholder text for profile name fixes #3101

* Fixed issue with properties, assets, and keybindings multiline readout, fixes #3098

* Change tab order of properties, assets, and keybindings: text area -> edit button,  fixes #3099

* Add prompt to user warning of unsaved changes in profile editor

* Add error message dialog fixes #3100

* Removed unused code

* Remove hardcoded color for information text in cameradialog

* Don't compare profile files but profiles instead

* Add same focus outline to all buttons in launcher

---------

Co-authored-by: Alexander Bock <mail@alexanderbock.eu>
Co-authored-by: Alexander Bock <alexander.bock@liu.se>
This commit is contained in:
Andreas Engberg
2024-10-11 10:13:32 +02:00
committed by GitHub
parent 0fc9265837
commit 6dd3dd8ab8
27 changed files with 390 additions and 468 deletions

View File

@@ -88,6 +88,13 @@ public:
*/
bool isUserConfigSelected() const;
/**
* Handles keypresses while the Qt launcher window is open.
*
* \param evt QKeyEevent object of the key press event
*/
void keyPressEvent(QKeyEvent* evt) override;
private:
QWidget* createCentralWidget();
void setBackgroundImage(const std::filesystem::path& syncPath);

View File

@@ -32,6 +32,7 @@
class QCheckBox;
class QComboBox;
class QDialogButtonBox;
class QErrorMessage;
class QGridLayout;
class QLabel;
class QLineEdit;
@@ -78,7 +79,7 @@ private:
struct {
QListWidget* list = nullptr;
QLineEdit* identifier = nullptr;
QLabel* infoText = nullptr;
QErrorMessage* infoText = nullptr;
QLineEdit* name = nullptr;
QLineEdit* guiPath = nullptr;
QLineEdit* documentation = nullptr;

View File

@@ -48,8 +48,6 @@ private:
QBoxLayout* _layout = nullptr;
QLineEdit* _nameEdit = nullptr;
QLabel* _errorMsg = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___ASSETEDIT___H__

View File

@@ -32,6 +32,7 @@
class QLabel;
class QLineEdit;
class QMessageBox;
class QTabWidget;
class CameraDialog final : public QDialog {
@@ -88,7 +89,7 @@ private:
QLineEdit* altitude = nullptr;
} _geoState;
QLabel* _errorMsg = nullptr;
QMessageBox* _errorMsg = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___CAMERA___H__

View File

@@ -97,8 +97,6 @@ private:
QPushButton* _saveButton = nullptr;
QPushButton* _discardButton = nullptr;
QDialogButtonBox* _buttonBox = nullptr;
QLabel* _errorMsg = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___DELTATIMESDIALOG___H__

View File

@@ -121,7 +121,6 @@ private:
QLabel* _downloadLabel = nullptr;
QPlainTextEdit* _log = nullptr;
QLabel* _errorMsg = nullptr;
std::string _latestHorizonsError;
};

View File

@@ -86,8 +86,6 @@ private:
QPushButton* _buttonSave = nullptr;
QPushButton* _buttonCancel = nullptr;
QDialogButtonBox* _buttonBox = nullptr;
QLabel* _errorMsg = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___MODULESDIALOG___H__

View File

@@ -79,6 +79,13 @@ public:
*/
virtual void keyPressEvent(QKeyEvent* evt) override;
void reject() override;
void closeWithoutSaving();
void promptUserOfUnsavedChanges();
signals:
void raiseExitWindow();
private slots:
void duplicateProfile();
void openMeta();
@@ -91,7 +98,6 @@ private slots:
void openDeltaTimes();
void openCamera();
void openMarkNodes();
void cancel();
void approved();
private:
@@ -119,8 +125,6 @@ private:
QLabel* _timeLabel = nullptr;
QLabel* _metaLabel = nullptr;
QLabel* _additionalScriptsLabel = nullptr;
QLabel* _errorMsg = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___PROFILEEDIT___H__

View File

@@ -34,6 +34,7 @@ class QDialogButtonBox;
class QLabel;
class QLineEdit;
class QListWidget;
class QMessageBox;
class QPushButton;
class PropertiesDialog final : public QDialog {
@@ -93,7 +94,7 @@ private:
QPushButton* _cancelButton = nullptr;
QDialogButtonBox* _buttonBox = nullptr;
QLabel* _errorMsg = nullptr;
QMessageBox* _errorMsg = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___PROPERTIESDIALOG___H__

View File

@@ -9,8 +9,13 @@ QLabel#heading {
font-size: 12pt;
}
QLabel#error-message {
color: rgb(221, 17, 17);
QPushButton:focus,
QTextEdit:focus,
QTabWidget:focus,
QCheckBox:focus,
QComboBox:focus {
outline: none;
border: 2px solid rgb(61, 189, 238);
}
/*
@@ -22,7 +27,7 @@ LauncherWindow QLabel {
}
LauncherWindow QLabel#label_choose, QLabel#label_options {
color: #dddddd;
color: rgb(255, 255, 255);
font-size: 10pt;
}
@@ -36,10 +41,9 @@ LauncherWindow QLabel#version-info {
}
LauncherWindow QComboBox#config {
background: rgb(96, 96, 96);
border: 1px solid rgb(128, 128, 128);
border-radius: 3px;
border-color: rgb(225, 225, 225);
background: rgb(86, 86, 86);
border: 1px solid rgb(225, 225, 225);
border-radius: 2px;
padding: 1px 18px 1px 3px;
min-width: 14em;
font-size: 10pt;
@@ -49,7 +53,7 @@ LauncherWindow QComboBox#config {
}
LauncherWindow QComboBox#config:hover {
background: rgb(120, 120, 120);
background: rgb(110, 110, 110);
}
LauncherWindow QComboBox#config:disabled {
@@ -57,26 +61,25 @@ LauncherWindow QComboBox#config:disabled {
color: rgb(225, 225, 225);
}
LauncherWindow QPushButton#large {
background: rgb(128, 128, 128);
LauncherWindow QPushButton#start {
background: rgb(96, 96, 96);
border: 2px solid rgb(225, 225, 225);
border-radius: 2px;
border-style: outset;
border-width: 2px;
border-color: rgb(225, 225, 225);
font-size: 16pt;
font-weight: bold;
color: rgb(239, 239, 239);
letter-spacing: 1px;
color: rgb(255, 255, 255);
}
LauncherWindow QPushButton#large:hover {
background: rgb(150, 150, 150);
LauncherWindow QPushButton#start:hover {
background: rgb(120, 120, 120);
}
LauncherWindow QPushButton#small {
background: rgb(96, 96, 96);
background: rgb(86, 86, 86);
border: 1px solid rgb(225, 225, 225);
border-radius: 2px;
border-style: outset;
border-width: 1px;
border-color: rgb(225, 225, 225);
min-height: 1em;
font-size: 10pt;
font-weight: bold;
@@ -84,12 +87,12 @@ LauncherWindow QPushButton#small {
}
LauncherWindow QPushButton#small:hover {
background: rgb(120, 120, 120);
background: rgb(110, 110, 110);
}
LauncherWindow QPushButton#small:disabled {
color: rgb(180, 180, 180);
background: rgb(160, 160, 160);
background: rgb(204, 204, 204);
color: rgb(86, 86, 86);
}
LauncherWindow QPushButton#settings {
@@ -101,7 +104,17 @@ LauncherWindow QPushButton#settings:hover {
border-image: url(:/images/cogwheel-highlight);
}
LauncherWindow QPushButton#small:focus,
LauncherWindow QPushButton#start:focus,
LauncherWindow QComboBox#config:focus
{
outline: none;
border: 2px solid rgb(61, 189, 238);
}
LauncherWindow QPushButton#settings:focus {
outline: 2px solid rgb(61, 189, 238);
}
/*
* ProfileEdit
*/
@@ -165,12 +178,7 @@ DeltaTimesDialog QListWidget {
/*
* Camera
*/
CameraDialog QLabel#error-message {
min-width: 10em;
}
CameraDialog QLabel#camera-description {
color:rgb(96, 96, 96);
margin: 1em;
margin-top: 0.2em;
margin-bottom: 0.4em;
@@ -198,10 +206,6 @@ HorizonsDialog QLabel#thin {
font-weight: normal;
}
HorizonsDialog QLabel#error {
color: rgb(221, 17, 17);
}
HorizonsDialog QLabel#normal {
color: rgb(0, 0, 0);
}
@@ -223,8 +227,13 @@ WindowControl QLabel#notice {
/*
* Settings
*/
SettingsWidget QLabel#information {
SettingsDialog QLabel#information {
font-style: italic;
font-weight: normal;
font-size: 8pt;
}
SettingsDialog QComboBox#dropdown:focus {
outline: none;
border: 2px solid rgb(61, 189, 238);
}

View File

@@ -34,9 +34,12 @@
#include <ghoul/logging/logmanager.h>
#include <sgct/readconfig.h>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QFile>
#include <QKeyEvent>
#include <QLabel>
#include <QMessageBox>
#include <QPainter>
#include <QPushButton>
#include <QStandardItemModel>
#include <filesystem>
@@ -51,6 +54,8 @@
using namespace openspace;
namespace {
constexpr std::string_view _loggerCat = "LauncherWindow";
constexpr int ScreenWidth = 480;
constexpr int ScreenHeight = 640;
@@ -66,7 +71,7 @@ namespace {
namespace geometry {
constexpr QRect BackgroundImage(0, 0, ScreenWidth, ScreenHeight);
constexpr QRect LogoImage(LeftRuler, TopRuler, ItemWidth, ItemHeight);
constexpr QRect ChooseLabel(LeftRuler, TopRuler + 80, 151, 24);
constexpr QRect ChooseLabel(LeftRuler + 10, TopRuler + 80, 151, 24);
constexpr QRect ProfileBox(LeftRuler, TopRuler + 110, ItemWidth, ItemHeight);
constexpr QRect NewProfileButton(
LeftRuler + 160, TopRuler + 180, SmallItemWidth, SmallItemHeight
@@ -74,7 +79,7 @@ namespace {
constexpr QRect EditProfileButton(
LeftRuler, TopRuler + 180, SmallItemWidth, SmallItemHeight
);
constexpr QRect OptionsLabel(LeftRuler, TopRuler + 230, 151, 24);
constexpr QRect OptionsLabel(LeftRuler + 10, TopRuler + 230, 151, 24);
constexpr QRect WindowConfigBox(LeftRuler, TopRuler + 260, ItemWidth, ItemHeight);
constexpr QRect NewWindowButton(
LeftRuler + 160, TopRuler + 330, SmallItemWidth, SmallItemHeight
@@ -242,6 +247,10 @@ LauncherWindow::LauncherWindow(bool profileEnabled,
}
setCentralWidget(createCentralWidget());
QPushButton* startButton = centralWidget()->findChild<QPushButton*>("start");
if (startButton) {
startButton->setFocus(Qt::OtherFocusReason);
}
populateProfilesList(globalConfig.profile);
_profileBox->setEnabled(profileEnabled);
@@ -277,43 +286,29 @@ QWidget* LauncherWindow::createCentralWidget() {
logoImage->setPixmap(QPixmap(":/images/openspace-horiz-logo-small.png"));
QLabel* labelChoose = new QLabel("Choose Profile", centralWidget);
labelChoose->setObjectName("clear");
labelChoose->setGeometry(geometry::ChooseLabel);
labelChoose->setObjectName("label_choose");
_profileBox = new QComboBox(centralWidget);
_profileBox->setObjectName("config");
_profileBox->setGeometry(geometry::ProfileBox);
_profileBox->setAccessibleName("Choose profile");
QLabel* optionsLabel = new QLabel("Window Options", centralWidget);
optionsLabel->setObjectName("clear");
optionsLabel->setGeometry(geometry::OptionsLabel);
optionsLabel->setObjectName("label_options");
_windowConfigBox = new QComboBox(centralWidget);
_windowConfigBox->setObjectName("config");
_windowConfigBox->setGeometry(geometry::WindowConfigBox);
QPushButton* startButton = new QPushButton("START", centralWidget);
QPushButton* editProfileButton = new QPushButton("Edit", centralWidget);
connect(
startButton, &QPushButton::released,
editProfileButton, &QPushButton::released,
[this]() {
if (_profileBox->currentText().isEmpty()) {
QMessageBox::critical(
this,
"Empty Profile",
"Cannot launch with an empty profile"
);
}
else {
_shouldLaunch = true;
close();
}
}
const std::string selection = _profileBox->currentText().toStdString();
const int selectedIndex = _profileBox->currentIndex();
const bool isUserProfile = selectedIndex < _userAssetCount;
openProfileEditor(selection, isUserProfile);
}
);
startButton->setObjectName("large");
startButton->setGeometry(geometry::StartButton);
startButton->setCursor(Qt::PointingHandCursor);
editProfileButton->setObjectName("small");
editProfileButton->setGeometry(geometry::EditProfileButton);
editProfileButton->setCursor(Qt::PointingHandCursor);
editProfileButton->setAutoDefault(true);
editProfileButton->setAccessibleName("Edit profile");
QPushButton* newProfileButton = new QPushButton("New", centralWidget);
connect(
@@ -325,31 +320,17 @@ QWidget* LauncherWindow::createCentralWidget() {
newProfileButton->setObjectName("small");
newProfileButton->setGeometry(geometry::NewProfileButton);
newProfileButton->setCursor(Qt::PointingHandCursor);
newProfileButton->setAutoDefault(true);
newProfileButton->setAccessibleName("New profile");
QPushButton* editProfileButton = new QPushButton("Edit", centralWidget);
connect(
editProfileButton, &QPushButton::released,
[this]() {
const std::string selection = _profileBox->currentText().toStdString();
const int selectedIndex = _profileBox->currentIndex();
const bool isUserProfile = selectedIndex < _userAssetCount;
openProfileEditor(selection, isUserProfile);
}
);
editProfileButton->setObjectName("small");
editProfileButton->setGeometry(geometry::EditProfileButton);
editProfileButton->setCursor(Qt::PointingHandCursor);
QLabel* optionsLabel = new QLabel("Window Options", centralWidget);
optionsLabel->setGeometry(geometry::OptionsLabel);
optionsLabel->setObjectName("label_options");
QPushButton* newWindowButton = new QPushButton("New", centralWidget);
connect(
newWindowButton, &QPushButton::released,
[this]() {
openWindowEditor("", true);
}
);
newWindowButton->setObjectName("small");
newWindowButton->setGeometry(geometry::NewWindowButton);
newWindowButton->setCursor(Qt::PointingHandCursor);
_windowConfigBox = new QComboBox(centralWidget);
_windowConfigBox->setObjectName("config");
_windowConfigBox->setGeometry(geometry::WindowConfigBox);
_windowConfigBox->setAccessibleName("Select window configuration");
_editWindowButton = new QPushButton("Edit", centralWidget);
connect(
@@ -368,7 +349,44 @@ QWidget* LauncherWindow::createCentralWidget() {
_editWindowButton->setObjectName("small");
_editWindowButton->setGeometry(geometry::EditWindowButton);
_editWindowButton->setCursor(Qt::PointingHandCursor);
_editWindowButton->setAutoDefault(true);
_editWindowButton->setAccessibleName("Edit window configuration");
QPushButton* newWindowButton = new QPushButton("New", centralWidget);
connect(
newWindowButton, &QPushButton::released,
[this]() {
openWindowEditor("", true);
}
);
newWindowButton->setObjectName("small");
newWindowButton->setGeometry(geometry::NewWindowButton);
newWindowButton->setCursor(Qt::PointingHandCursor);
newWindowButton->setAutoDefault(true);
newWindowButton->setAccessibleName("New window configuration");
QPushButton* startButton = new QPushButton("START", centralWidget);
connect(
startButton, &QPushButton::released,
[this]() {
if (_profileBox->currentText().isEmpty()) {
QMessageBox::critical(
this,
"Empty Profile",
"Cannot launch with an empty profile"
);
}
else {
_shouldLaunch = true;
close();
}
}
);
startButton->setObjectName("start");
startButton->setGeometry(geometry::StartButton);
startButton->setCursor(Qt::PointingHandCursor);
startButton->setAutoDefault(true);
startButton->setAccessibleName("Start OpenSpace");
QLabel* versionLabel = new QLabel(centralWidget);
versionLabel->setVisible(true);
@@ -379,9 +397,6 @@ QWidget* LauncherWindow::createCentralWidget() {
versionLabel->setGeometry(geometry::VersionString);
QPushButton* settingsButton = new QPushButton(centralWidget);
settingsButton->setObjectName("settings");
settingsButton->setGeometry(geometry::SettingsButton);
settingsButton->setIconSize(QSize(SettingsIconSize, SettingsIconSize));
connect(
settingsButton,
&QPushButton::released,
@@ -410,6 +425,11 @@ QWidget* LauncherWindow::createCentralWidget() {
dialog.exec();
}
);
settingsButton->setObjectName("settings");
settingsButton->setGeometry(geometry::SettingsButton);
settingsButton->setIconSize(QSize(SettingsIconSize, SettingsIconSize));
settingsButton->setAutoDefault(true);
settingsButton->setAccessibleName("Settings");
return centralWidget;
}
@@ -454,27 +474,33 @@ void LauncherWindow::setBackgroundImage(const std::filesystem::path& syncPath) {
// We know there has to be at least one folder, so it's fine to just pick the first
while (!files.empty()) {
const std::filesystem::path& p = files.front();
if (p.extension() == ".png") {
// If the top path starts with the png extension, we have found our candidate
break;
if (p.extension() != ".png" || p.filename() == "overlay.png") {
files.erase(files.begin());
}
else {
// There shouldn't be any non-png images in here, but you never know. So we
// just remove non-image files here
files.erase(files.begin());
// If the top path starts with the png extension, we have found our candidate
break;
}
}
// There better be at least one file left, but just in in case
if (!files.empty()) {
// Take the selected image and overpaint the overlay increasing the contrast
std::string image = files.front().string();
_backgroundImage->setPixmap(QPixmap(QString::fromStdString(image)));
QPixmap pixmap = QPixmap(QString::fromStdString(image));
QPainter painter = QPainter(&pixmap);
painter.setOpacity(0.7);
QPixmap overlay = QPixmap(QString::fromStdString(
std::format("{}/overlay.png", latest.path.path())
));
painter.drawPixmap(pixmap.rect(), overlay);
_backgroundImage->setPixmap(pixmap);
}
}
void LauncherWindow::populateProfilesList(const std::string& preset) {
namespace fs = std::filesystem;
_profileBox->clear();
_userAssetCount = 0;
@@ -745,7 +771,7 @@ void LauncherWindow::onNewWindowConfigSelection(int newIndex) {
versionMin.versionString()
)));
return;
}
}
}
catch (const std::runtime_error&) {
// Ignore an exception here because clicking the edit button will
@@ -781,6 +807,29 @@ void LauncherWindow::openProfileEditor(const std::string& profile, bool isUserPr
savePath,
this
);
// Check whether there are unsaved changes from the profile editor
connect(
&editor,
&ProfileEdit::raiseExitWindow,
[this, &editor, &savePath, &p, &profile]() {
const std::string origPath = std::format("{}{}.profile", savePath, profile);
// If this is a new profile we want to prompt the user
if (!std::filesystem::exists(origPath)) {
editor.promptUserOfUnsavedChanges();
return;
}
// Check if the profile is the same as current existing file
if (*p != Profile(origPath)) {
editor.promptUserOfUnsavedChanges();
}
else {
editor.closeWithoutSaving();
}
}
);
editor.exec();
if (editor.wasSaved()) {
if (editor.specifiedFilename() != profile) {
@@ -932,3 +981,12 @@ bool LauncherWindow::isUserConfigSelected() const {
const int selectedIndex = _windowConfigBox->currentIndex();
return (selectedIndex <= _userConfigCount);
}
void LauncherWindow::keyPressEvent(QKeyEvent* evt) {
if (evt->key() == Qt::Key_Escape) {
_shouldLaunch = false;
close();
return;
}
QMainWindow::keyPressEvent(evt);
}

View File

@@ -32,6 +32,7 @@
#include <QCheckBox>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QErrorMessage>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QLabel>
@@ -103,7 +104,7 @@ void ActionDialog::createWidgets() {
// *----------------------*---------------*-----------------*
QGridLayout* layout = new QGridLayout(this);
createActionWidgets(layout);
clearActionFields();
@@ -161,25 +162,17 @@ void ActionDialog::createActionWidgets(QGridLayout* layout) {
[this]() {
// Check if the identifier is legal
const std::string id = _actionWidgets.identifier->text().toStdString();
const bool isLegal = id.find_first_of("\t\n. ") == std::string::npos;
if (isLegal) {
_actionWidgets.infoText->clear();
_actionWidgets.infoText->setHidden(true);
}
else {
_actionWidgets.infoText->setText(
"Identifier must not contain whitespace or ."
const bool isLegal = id.find_first_of("\t\n ") == std::string::npos;
if (!isLegal) {
_actionWidgets.infoText->showMessage(
"Identifier must not contain whitespace"
);
_actionWidgets.infoText->setHidden(false);
}
}
);
layout->addWidget(_actionWidgets.identifier, 1, 2);
_actionWidgets.infoText = new QLabel;
_actionWidgets.infoText->setHidden(true);
_actionWidgets.infoText->setObjectName("error-message");
layout->addWidget(_actionWidgets.infoText, 1, 3);
_actionWidgets.infoText = new QErrorMessage(this);
layout->addWidget(new QLabel("Name"), 2, 1);
_actionWidgets.name = new QLineEdit;
@@ -243,6 +236,7 @@ void ActionDialog::createActionWidgets(QGridLayout* layout) {
"`args` variable when this script executes. If no arguments are passed, this "
"variable does not exist"
);
_actionWidgets.script->setTabChangesFocus(true);
_actionWidgets.script->setEnabled(false);
layout->addWidget(_actionWidgets.script, 6, 2, 1, 2);
@@ -612,7 +606,6 @@ void ActionDialog::actionSaved() {
}
action->identifier = newIdentifier;
}
action->name = _actionWidgets.name->text().toStdString();
std::string guiPath = _actionWidgets.guiPath->text().toStdString();
@@ -643,8 +636,6 @@ void ActionDialog::clearActionFields() const {
_actionWidgets.list->setCurrentRow(-1);
_actionWidgets.identifier->clear();
_actionWidgets.identifier->setEnabled(false);
_actionWidgets.infoText->clear();
_actionWidgets.infoText->setHidden(true);
_actionWidgets.name->clear();
_actionWidgets.name->setEnabled(false);
_actionWidgets.guiPath->clear();
@@ -701,7 +692,7 @@ void ActionDialog::keybindingAdd() {
void ActionDialog::keybindingRemove() {
const Profile::Keybinding* keybinding = selectedKeybinding();
ghoul_assert(keybinding, "A keybinding must be selected at this point");
for (size_t i = 0; i < _keybindingsData.size(); i++) {
if (_keybindingsData[i].key == keybinding->key &&
_keybindingsData[i].action == keybinding->action)

View File

@@ -64,6 +64,7 @@ void AdditionalScriptsDialog::createWidgets() {
_textScripts = new QTextEdit;
_textScripts->setAcceptRichText(false);
_textScripts->setTabChangesFocus(true);
layout->addWidget(_textScripts, 1);
_chooseScriptsButton = new QPushButton("Choose Scripts");

View File

@@ -72,40 +72,10 @@ void AssetEdit::createWidgets() {
_layout->addWidget(generateButton);
}
//{
// QBoxLayout* container = new QHBoxLayout(this);
// QLabel* assetLabel = new QLabel("Asset Name:");
// assetLabel->setObjectName("profile");
// container->addWidget(assetLabel);
// _nameEdit = new QLineEdit();
// container->addWidget(_nameEdit);
// _layout->addLayout(container);
//}
//_layout->addWidget(new Line);
//{
// QBoxLayout* container = new QHBoxLayout(this);
// _components = new QComboBox(this);
// _components->addItems(_supportedComponents);
// _components->setCurrentIndex(0);
// container->addWidget(_components);
// QPushButton* addButton = new QPushButton("Add", this);
// connect(addButton, &QPushButton::clicked, this, &AssetEdit::openComponent);
// addButton->setCursor(Qt::PointingHandCursor);
// container->addWidget(addButton);
// _layout->addLayout(container);
//}
_layout->addWidget(new Line);
{
QBoxLayout* footer = new QHBoxLayout;
_errorMsg = new QLabel;
_errorMsg->setObjectName("error-message");
_errorMsg->setWordWrap(true);
footer->addWidget(_errorMsg);
QDialogButtonBox* buttons = new QDialogButtonBox;
buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
connect(buttons, &QDialogButtonBox::accepted, this, &AssetEdit::approved);
@@ -115,93 +85,14 @@ void AssetEdit::createWidgets() {
}
}
//void AssetEdit::openComponent() {
// switch (_components->currentIndex()) {
// case 0:
// _errorMsg->setText("Choose a component to add to the asset");
// break;
// case 1: {
// QBoxLayout* horizonsLayout = new QVBoxLayout(this);
// {
// QLabel* label = new QLabel("Horizons Translation:", this);
// label->setObjectName("heading");
// horizonsLayout->addWidget(label);
// }
// {
// QBoxLayout* container = new QHBoxLayout(this);
// QLabel* fileLabel = new QLabel("File path:", this);
// container->addWidget(fileLabel);
//
// _horizonsFileEdit = new QLineEdit(this);
// container->addWidget(_horizonsFileEdit);
//
// QPushButton* fileButton = new QPushButton("Browse", this);
// connect(
// fileButton,
// &QPushButton::released,
// this,
// &AssetEdit::openHorizonsFile
// );
// fileButton->setCursor(Qt::PointingHandCursor);
// container->addWidget(fileButton);
//
// QPushButton* generateButton = new QPushButton("Generate", this);
// connect(
// generateButton,
// &QPushButton::released,
// this,
// &AssetEdit::openHorizons
// );
//
// // In order to generate a Horizons File the Space module is required
// #ifndef OPENSPACE_MODULE_SPACE_ENABLED
// generateButton->setEnabled(false);
// generateButton->setToolTip(
// "Connot generate Horizons file without the space module enabled"
// );
// #else
// generateButton->setCursor(Qt::PointingHandCursor);
// #endif // OPENSPACE_MODULE_SPACE_ENABLED
//
// container->addWidget(generateButton);
// horizonsLayout->addLayout(container);
// }
// horizonsLayout->addWidget(new Line);
// _layout->insertLayout(_layout->count() - 3, horizonsLayout);
// break;
// }
// default:
// _errorMsg->setText("Unkown component");
// break;
// }
//}
//
//void AssetEdit::openHorizonsFile() {
// std::string filePath = QFileDialog::getOpenFileName(
// this,
// tr("Open Horizons file"),
// "",
// tr("Horiozons file (*.dat)")
// ).toStdString();
// _horizonsFile = std::filesystem::absolute(filePath);
// _horizonsFileEdit->setText(QString(_horizonsFile.string().c_str()));
//}
void AssetEdit::openHorizons() {
_errorMsg->clear();
#ifdef OPENSPACE_MODULE_SPACE_ENABLED
HorizonsDialog* horizonsDialog = new HorizonsDialog(this);
horizonsDialog->exec();
//_horizonsFile = horizonsDialog->file();
//_horizonsFileEdit->setText(QString(_horizonsFile.string().c_str()));
#endif // OPENSPACE_MODULE_SPACE_ENABLED
}
void AssetEdit::approved() {
// std::string assetName = _nameEdit->text().toStdString();
// if (assetName.empty()) {
// _errorMsg->setText("Asset name must be specified");
// return;
// }
accept();
}

View File

@@ -34,6 +34,7 @@
#include <QKeyEvent>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QTabWidget>
@@ -173,10 +174,9 @@ void CameraDialog::createWidgets() {
{
QBoxLayout* footerLayout = new QHBoxLayout;
_errorMsg = new QLabel;
_errorMsg->setObjectName("error-message");
_errorMsg->setWordWrap(true);
footerLayout->addWidget(_errorMsg);
_errorMsg = new QMessageBox(this);
_errorMsg->setIcon(QMessageBox::Critical);
_errorMsg->setText("Invalid input data");
QDialogButtonBox* buttons = new QDialogButtonBox;
buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
@@ -352,6 +352,9 @@ QWidget* CameraDialog::createNavStateWidget() {
this,
"Select navigate state file"
);
if (file.isEmpty()) {
return;
}
std::ifstream f = std::ifstream(file.toStdString());
const std::string contents = std::string(
@@ -441,7 +444,7 @@ QWidget* CameraDialog::createGeoWidget() {
bool CameraDialog::areRequiredFormsFilledAndValid() {
bool allFormsOk = true;
_errorMsg->clear();
_errorMsg->setInformativeText("");
if (_tabWidget->currentIndex() == CameraTypeNode) {
if (_nodeState.anchor->text().isEmpty()) {
@@ -525,16 +528,17 @@ bool CameraDialog::areRequiredFormsFilledAndValid() {
}
void CameraDialog::addErrorMsg(const QString& errorDescription) {
QString contents = _errorMsg->text();
QString contents = _errorMsg->informativeText();
if (!contents.isEmpty()) {
contents += ", ";
}
contents += errorDescription;
_errorMsg->setText(contents);
_errorMsg->setInformativeText(contents);
}
void CameraDialog::approved() {
if (!areRequiredFormsFilledAndValid()) {
_errorMsg->exec();
return;
}
@@ -597,7 +601,7 @@ void CameraDialog::approved() {
}
void CameraDialog::tabSelect(int tabIndex) {
_errorMsg->clear();
_errorMsg->setInformativeText("");
if (tabIndex == CameraTypeNode) {
_nodeState.anchor->setFocus(Qt::OtherFocusReason);

View File

@@ -35,6 +35,7 @@
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QMessageBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <array>
@@ -107,7 +108,7 @@ void DeltaTimesDialog::createWidgets() {
_listWidget->setAutoScroll(true);
_listWidget->setLayoutMode(QListView::SinglePass);
layout->addWidget(_listWidget);
{
QBoxLayout* buttonLayout = new QHBoxLayout;
_addButton = new QPushButton("Add Entry");
@@ -128,14 +129,15 @@ void DeltaTimesDialog::createWidgets() {
layout->addLayout(buttonLayout);
}
_adjustLabel = new QLabel("Set Simulation Time Increment for key");
_adjustLabel = new QLabel("Set Simulation Time Increment (in seconds) for key");
layout->addWidget(_adjustLabel);
{
QBoxLayout* box = new QHBoxLayout;
_seconds = new QLineEdit;
_seconds->setValidator(new QDoubleValidator);
connect(_seconds, &QLineEdit::textChanged, this, &DeltaTimesDialog::valueChanged);
_seconds->setAccessibleName("Set simulation time increment in seconds for key");
box->addWidget(_seconds);
_value = new QLabel;
@@ -165,11 +167,6 @@ void DeltaTimesDialog::createWidgets() {
layout->addWidget(new Line);
{
QBoxLayout* footer = new QHBoxLayout;
_errorMsg = new QLabel;
_errorMsg->setObjectName("error-message");
_errorMsg->setWordWrap(true);
footer->addWidget(_errorMsg);
_buttonBox = new QDialogButtonBox;
_buttonBox->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
connect(
@@ -230,7 +227,7 @@ void DeltaTimesDialog::listItemSelected() {
}
void DeltaTimesDialog::setLabelForKey(int index, bool editMode, std::string_view color) {
std::string labelS = "Set Simulation Time Increment for key";
std::string labelS = "Set Simulation Time Increment (in seconds) for key";
if (index >= static_cast<int>(_deltaTimesData.size())) {
index = static_cast<int>(_deltaTimesData.size()) - 1;
}
@@ -243,15 +240,9 @@ void DeltaTimesDialog::setLabelForKey(int index, bool editMode, std::string_view
}
void DeltaTimesDialog::valueChanged(const QString& text) {
if (text.isEmpty()) {
_errorMsg->setText("");
}
else {
const double value = text.toDouble();
if (value != 0.0) {
_value->setText(QString::fromStdString(timeDescription(value)));
_errorMsg->setText("");
}
const double value = text.toDouble();
if (value != 0.0) {
_value->setText(QString::fromStdString(timeDescription(value)));
}
}
@@ -282,7 +273,11 @@ void DeltaTimesDialog::addDeltaTimeValue() {
_listWidget->addItem(new QListWidgetItem(messageAddValue));
}
else {
_errorMsg->setText("Exceeded maximum amount of simulation time increments");
QMessageBox::critical(
this,
"Error",
"Exceeded maximum amount of simulation time increments"
);
}
_listWidget->setCurrentRow(_listWidget->count() - 1);
_seconds->setFocus(Qt::OtherFocusReason);
@@ -337,7 +332,6 @@ void DeltaTimesDialog::transitionEditMode(int index, bool state) {
_discardButton->setEnabled(state);
_adjustLabel->setEnabled(state);
_seconds->setEnabled(state);
_errorMsg->clear();
if (state) {
_seconds->setFocus(Qt::OtherFocusReason);
@@ -348,7 +342,6 @@ void DeltaTimesDialog::transitionEditMode(int index, bool state) {
setLabelForKey(index, false, "light gray");
_value->clear();
}
_errorMsg->clear();
}
void DeltaTimesDialog::parseSelections() {

View File

@@ -125,7 +125,7 @@ void HorizonsDialog::typeOnChange(int index) {
_timeTypeCombo->insertItem(0, TimeVarying.data());
}
else {
_errorMsg->setText("Invalid Horizons type");
QMessageBox::critical(this, "Error", "Invalid Horizons type");
styleLabel(_typeLabel, IsDirty::Yes);
}
}
@@ -162,12 +162,11 @@ void HorizonsDialog::importTimeRange() {
_validTimeRange = std::pair<std::string, std::string>();
return;
}
_errorMsg->setText("Could not import time range");
const std::string msg = std::format(
"Could not import time range '{}' to '{}'",
_validTimeRange.first, _validTimeRange.second
);
QMessageBox::critical(this, "Error", QString::fromStdString(msg));
appendLog(msg, LogLevel::Error);
return;
}
@@ -296,6 +295,7 @@ void HorizonsDialog::createWidgets() {
_startEdit = new QDateTimeEdit;
_startEdit->setDisplayFormat("yyyy-MM-dd T hh:mm:ss");
_startEdit->setDate(QDate::currentDate().addYears(-1));
_startEdit->setAccessibleName("Set start time");
_startEdit->setToolTip("Enter the start date and time for the data");
layout->addWidget(_startEdit, 8, 2);
}
@@ -306,6 +306,7 @@ void HorizonsDialog::createWidgets() {
_endEdit = new QDateTimeEdit(this);
_endEdit->setDisplayFormat("yyyy-MM-dd T hh:mm:ss");
_endEdit->setDate(QDate::currentDate());
_endEdit->setAccessibleName("Set end time");
_endEdit->setToolTip("Enter the end date and time for the data");
layout->addWidget(_endEdit, 9, 2);
}
@@ -375,11 +376,6 @@ void HorizonsDialog::createWidgets() {
}
{
QBoxLayout* footer = new QHBoxLayout;
_errorMsg = new QLabel;
_errorMsg->setObjectName("error-message");
_errorMsg->setWordWrap(true);
footer->addWidget(_errorMsg);
QDialogButtonBox* buttons = new QDialogButtonBox;
buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
connect(buttons, &QDialogButtonBox::accepted, this, &HorizonsDialog::approved);
@@ -392,7 +388,8 @@ void HorizonsDialog::createWidgets() {
bool HorizonsDialog::isValidInput() {
// File
if (_fileEdit->text().isEmpty()) {
_errorMsg->setText("File path not selected");
QMessageBox::critical(this, "Error", "File path not selected");
_fileEdit->setFocus();
styleLabel(_fileLabel, IsDirty::Yes);
return false;
}
@@ -401,13 +398,15 @@ bool HorizonsDialog::isValidInput() {
if (_targetEdit->text().isEmpty() && ((_chooseTargetCombo->count() > 0 &&
_chooseTargetCombo->currentIndex() == 0) || _chooseTargetCombo->count() == 0))
{
_errorMsg->setText("Target not selected");
QMessageBox::critical(this, "Error", "Target not selected");
_targetEdit->setFocus();
styleLabel(_targetLabel, IsDirty::Yes);
return false;
}
if (_targetEdit->text().toStdString().find_first_of("¤<>§£´¨€") != std::string::npos)
{
_errorMsg->setText("Target includes illegal characters");
QMessageBox::critical(this, "Error", "Target includes illegal characters");
_targetEdit->setFocus();
styleLabel(_targetLabel, IsDirty::Yes);
return false;
}
@@ -416,13 +415,15 @@ bool HorizonsDialog::isValidInput() {
if (_centerEdit->text().isEmpty() && ((_chooseObserverCombo->count() > 0 &&
_chooseObserverCombo->currentIndex() == 0) || _chooseObserverCombo->count() == 0))
{
_errorMsg->setText("Observer not selected");
QMessageBox::critical(this, "Error", "Observer not selected");
_centerEdit->setFocus();
styleLabel(_centerLabel, IsDirty::Yes);
return false;
}
if (_centerEdit->text().toStdString().find_first_of("¤<>§£´¨€") != std::string::npos)
{
_errorMsg->setText("Observer includes illegal characters");
QMessageBox::critical(this, "Error", "Observer includes illegal characters");
_centerEdit->setFocus();
styleLabel(_centerLabel, IsDirty::Yes);
return false;
}
@@ -430,7 +431,8 @@ bool HorizonsDialog::isValidInput() {
// Step size
// Empty
if (_stepEdit->text().isEmpty()) {
_errorMsg->setText("Step size not selected");
QMessageBox::critical(this, "Error", "Step size is not selected");
_stepEdit->setFocus();
styleLabel(_stepLabel, IsDirty::Yes);
return false;
}
@@ -438,17 +440,26 @@ bool HorizonsDialog::isValidInput() {
bool couldConvert = false;
const int32_t step = _stepEdit->text().toInt(&couldConvert);
if (!couldConvert) {
_errorMsg->setText(QString::fromStdString(std::format(
"Step size needs to be a number in range 1 to {}",
std::numeric_limits<int32_t>::max()
)));
QMessageBox::critical(
this, "Error",
QString::fromStdString(std::format(
"Step size needs to be a number in range 1 to {}",
std::numeric_limits<int32_t>::max()
))
);
_stepEdit->setFocus();
styleLabel(_stepLabel, IsDirty::Yes);
return false;
}
// In the case of arcseconds range is different
if (_timeTypeCombo->currentText().toStdString() == TimeVarying) {
if (step < 60 || step > 3600) {
_errorMsg->setText("Angular step size needs to be in range 60 to 3600");
QMessageBox::critical(
this,
"Error",
"Angular step size needs to be in range 60 to 3600"
);
_stepEdit->setFocus();
styleLabel(_stepLabel, IsDirty::Yes);
return false;
}
@@ -458,10 +469,11 @@ bool HorizonsDialog::isValidInput() {
// website as a uint32_t. If step size over 32 bit int is sent, this error message is
// received: Cannot read numeric value -- re-enter
if (step < 1) {
_errorMsg->setText(QString::fromStdString(std::format(
QMessageBox::critical(this, "Error", QString::fromStdString(std::format(
"Step size is outside valid range 1 to '{}'",
std::numeric_limits<int32_t>::max()
)));
_stepEdit->setFocus();
styleLabel(_stepLabel, IsDirty::Yes);
return false;
}
@@ -658,14 +670,14 @@ std::pair<std::string, std::string> HorizonsDialog::readTimeRange() {
if (!start.isValid() || !end.isValid()) {
if (timeRange.first.empty() || timeRange.second.empty()) {
_errorMsg->setText("Could not find time range");
QMessageBox::critical(this, "Error", "Could not find time range");
appendLog(
"Could not find time range in Horizons file",
LogLevel::Error
);
}
else {
_errorMsg->setText("Could not parse time range");
QMessageBox::critical(this, "Error", "Could not parse time range");
const std::string msg = std::format(
"Could not read time range '{}' to '{}'",
timeRange.first, timeRange.second
@@ -693,10 +705,6 @@ bool HorizonsDialog::handleRequest() {
return false;
}
// Reset
_errorMsg->clear();
//
// Clean all widgets
styleLabel(_typeLabel, IsDirty::No);
styleLabel(_fileLabel, IsDirty::No);
@@ -719,7 +727,7 @@ bool HorizonsDialog::handleRequest() {
nlohmann::json answer = sendRequest(url);
if (answer.empty()) {
_errorMsg->setText("Connection error");
QMessageBox::critical(this, "Errpr", "Connection error");
return false;
}
@@ -762,7 +770,7 @@ std::string HorizonsDialog::constructUrl() {
type = HorizonsType::Observer;
}
else {
_errorMsg->setText("Invalid Horizons type");
QMessageBox::critical(this, "Error", "Invalid Horizons type");
styleLabel(_typeLabel, IsDirty::Yes);
return "";
}
@@ -829,7 +837,7 @@ std::string HorizonsDialog::constructUrl() {
unit = "";
}
else {
_errorMsg->setText("Invalid time unit type");
QMessageBox::critical(this, "Error", "Invalid time unit type");
styleLabel(_stepLabel, IsDirty::Yes);
return "";
}
@@ -894,11 +902,15 @@ openspace::HorizonsFile HorizonsDialog::handleAnswer(nlohmann::json& answer) {
break;
case QMessageBox::No:
case QMessageBox::Cancel:
_errorMsg->setText("File already exist, try another file path");
QMessageBox::critical(
this,
"Error",
"File already exist, try another file path"
);
styleLabel(_fileLabel, IsDirty::Yes);
return openspace::HorizonsFile();
default:
_errorMsg->setText("Invalid answer");
QMessageBox::critical(this, "Error", "Invalid answer");
styleLabel(_fileLabel, IsDirty::Yes);
return openspace::HorizonsFile();
}
@@ -927,7 +939,7 @@ bool HorizonsDialog::handleResult(openspace::HorizonsResultCode& result) {
break;
}
case HorizonsResultCode::Empty: {
_errorMsg->setText("The horizons file is empty");
QMessageBox::critical(this, "Error", "The horizons file is empty");
if (!_latestHorizonsError.empty()) {
const std::string msg = std::format(
"Latest Horizons error: {}", _latestHorizonsError
@@ -1172,7 +1184,7 @@ bool HorizonsDialog::handleResult(openspace::HorizonsResultCode& result) {
);
appendLog(msg, LogLevel::Error);
}
_errorMsg->setText("An unknown error occured");
QMessageBox::critical(this, "Error", "An unknown error occured");
break;
}
default: {
@@ -1182,7 +1194,7 @@ bool HorizonsDialog::handleResult(openspace::HorizonsResultCode& result) {
);
appendLog(msg, LogLevel::Error);
}
_errorMsg->setText("Invalid result type");
QMessageBox::critical(this, "Error", "Invalid result type");
break;
}
}

View File

@@ -65,6 +65,7 @@ void MarkNodesDialog::createWidgets() {
_removeButton = new QPushButton("Remove");
connect(_removeButton, &QPushButton::clicked, this, &MarkNodesDialog::listItemRemove);
_removeButton->setAccessibleName("Remove node");
layout->addWidget(_removeButton);
{
@@ -78,6 +79,7 @@ void MarkNodesDialog::createWidgets() {
QPushButton* addButton = new QPushButton("Add new");
connect(addButton, &QPushButton::clicked, this, &MarkNodesDialog::listItemAdded);
addButton->setAccessibleName("Add new node");
box->addWidget(addButton);
layout->addLayout(box);
}

View File

@@ -68,28 +68,34 @@ void MetaDialog::createWidgets() {
QBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(new QLabel("Name"));
_nameEdit = new QLineEdit;
_nameEdit->setAccessibleName("Profile name");
layout->addWidget(_nameEdit);
layout->addWidget(new QLabel("Version"));
_versionEdit = new QLineEdit;
_versionEdit->setAccessibleName("Profile version number");
layout->addWidget(_versionEdit);
layout->addWidget(new QLabel("Description"));
_descriptionEdit = new QTextEdit;
_descriptionEdit->setAcceptRichText(false);
_descriptionEdit->setTabChangesFocus(true);
_descriptionEdit->setAccessibleName("Profile description");
layout->addWidget(_descriptionEdit);
layout->addWidget(new QLabel("Author"));
_authorEdit = new QLineEdit;
_authorEdit->setAccessibleName("Profile author name");
layout->addWidget(_authorEdit);
layout->addWidget(new QLabel("URL"));
_urlEdit = new QLineEdit;
_urlEdit->setAccessibleName("Profile url");
layout->addWidget(_urlEdit);
layout->addWidget(new QLabel("License"));
_licenseEdit = new QLineEdit;
_licenseEdit->setAccessibleName("Profile license");
layout->addWidget(_licenseEdit);
layout->addWidget(new Line);

View File

@@ -31,6 +31,7 @@
#include <QLabel>
#include <QListWidget>
#include <QLineEdit>
#include <QMessageBox>
#include <QPushButton>
#include <QVBoxLayout>
@@ -92,11 +93,12 @@ void ModulesDialog::createWidgets() {
_list->addItem(new QListWidgetItem(createOneLineSummary(m)));
}
layout->addWidget(_list);
{
QBoxLayout* box = new QHBoxLayout;
_buttonAdd = new QPushButton("Add new");
connect(_buttonAdd, &QPushButton::clicked, this, &ModulesDialog::listItemAdded);
_buttonAdd->setAccessibleName("Add new module");
box->addWidget(_buttonAdd);
_buttonRemove = new QPushButton("Remove");
@@ -104,6 +106,7 @@ void ModulesDialog::createWidgets() {
_buttonRemove, &QPushButton::clicked,
this, &ModulesDialog::listItemRemove
);
_buttonRemove->setAccessibleName("Remove module");
box->addWidget(_buttonRemove);
box->addStretch();
@@ -113,14 +116,14 @@ void ModulesDialog::createWidgets() {
{
_moduleLabel = new QLabel("Module");
layout->addWidget(_moduleLabel);
_moduleEdit = new QLineEdit;
_moduleEdit->setToolTip("Name of OpenSpace module related to this profile");
layout->addWidget(_moduleEdit);
_loadedLabel = new QLabel("Command if Module is Loaded");
layout->addWidget(_loadedLabel);
_loadedEdit = new QLineEdit;
_loadedEdit->setToolTip(
"Lua command(s) to execute if OpenSpace has been compiled with the module"
@@ -129,7 +132,7 @@ void ModulesDialog::createWidgets() {
_notLoadedLabel = new QLabel("Command if Module is NOT Loaded");
layout->addWidget(_notLoadedLabel);
_notLoadedEdit = new QLineEdit;
_notLoadedEdit->setToolTip(
"Lua command(s) to execute if the module is not present in the OpenSpace "
@@ -159,12 +162,6 @@ void ModulesDialog::createWidgets() {
layout->addWidget(new Line);
{
QBoxLayout* footerLayout = new QHBoxLayout;
_errorMsg = new QLabel;
_errorMsg->setObjectName("error-message");
_errorMsg->setWordWrap(true);
footerLayout->addWidget(_errorMsg);
_buttonBox = new QDialogButtonBox;
_buttonBox->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
QObject::connect(
@@ -193,7 +190,7 @@ void ModulesDialog::listItemSelected() {
else {
_loadedEdit->clear();
}
if (m.notLoadedInstruction.has_value()) {
_notLoadedEdit->setText(QString::fromStdString(*m.notLoadedInstruction));
}
@@ -227,7 +224,6 @@ void ModulesDialog::listItemAdded() {
_list->addItem(new QListWidgetItem(" (Enter details below & click 'Save')"));
//Scroll down to that blank line highlighted
_list->setCurrentRow(_list->count() - 1);
_errorMsg->clear();
}
// Blank-out the 2 text fields, set combo box to index 0
@@ -254,7 +250,7 @@ void ModulesDialog::listItemAdded() {
void ModulesDialog::listItemSave() {
if (_moduleEdit->text().isEmpty()) {
_errorMsg->setText("Missing module name");
QMessageBox::critical(this, "Error", "Missing module name");
return;
}
@@ -311,7 +307,6 @@ void ModulesDialog::transitionToEditMode() {
_loadedLabel->setText("<font color='black'>Command if Module is Loaded</font>");
_notLoadedLabel->setText("<font color='black'>Command if Module is NOT Loaded</font>");
editBoxDisabled(false);
_errorMsg->setText("");
}
void ModulesDialog::transitionFromEditMode() {
@@ -326,7 +321,6 @@ void ModulesDialog::transitionFromEditMode() {
_moduleLabel->setText("<font color='light gray'>Module</font>");
_loadedLabel->setText("<font color='light gray'>Command if Module is Loaded</font>");
_notLoadedLabel->setText("<font color='light gray'>Command if Module is NOT Loaded</font>");
_errorMsg->setText("");
}
void ModulesDialog::editBoxDisabled(bool disabled) {

View File

@@ -41,6 +41,7 @@
#include <QKeyEvent>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QPushButton>
#include <QTextEdit>
#include <QVBoxLayout>
@@ -65,7 +66,7 @@ namespace {
std::string summarizeAssets(const std::vector<std::string>& assets) {
std::string results;
for (const std::string& a : assets) {
results += a + '\n';
results += std::format("{}<br>", a);
}
return results;
}
@@ -81,7 +82,7 @@ namespace {
);
std::string name = it != actions.end() ? it->name : "Unknown action";
results += std::format("{} ({})\n", name, ghoul::to_string(k.key));
results += std::format("{} ({})<br>", name, ghoul::to_string(k.key));
}
return results;
}
@@ -89,13 +90,13 @@ namespace {
std::string summarizeProperties(const std::vector<Profile::Property>& properties) {
std::string results;
for (openspace::Profile::Property p : properties) {
results += std::format("{} = {}\n", p.name, p.value);
results += std::format("{} = {}<br>", p.name, p.value);
}
return results;
}
} // namespace
ProfileEdit::ProfileEdit(Profile& profile, const std::string& profileName,
ProfileEdit::ProfileEdit(Profile& profile, const std::string& profileName,
std::filesystem::path assetBasePath,
std::filesystem::path userAssetBasePath,
std::filesystem::path builtInProfileBasePath,
@@ -125,6 +126,7 @@ void ProfileEdit::createWidgets(const std::string& profileName) {
container->addWidget(profileLabel);
_profileEdit = new QLineEdit(QString::fromStdString(profileName));
_profileEdit->setPlaceholderText("required");
container->addWidget(_profileEdit);
QPushButton* duplicateButton = new QPushButton("Duplicate Profile");
@@ -146,17 +148,19 @@ void ProfileEdit::createWidgets(const std::string& profileName) {
_propertiesLabel->setWordWrap(true);
container->addWidget(_propertiesLabel, 0, 0);
_propertiesEdit = new QTextEdit;
_propertiesEdit->setReadOnly(true);
_propertiesEdit->setAccessibleName("Property value settings");
container->addWidget(_propertiesEdit, 1, 0, 1, 3);
QPushButton* editProperties = new QPushButton("Edit");
connect(
editProperties, &QPushButton::clicked,
this, &ProfileEdit::openProperties
);
editProperties->setAccessibleName("Edit properties");
container->addWidget(editProperties, 0, 2);
_propertiesEdit = new QTextEdit;
_propertiesEdit->setReadOnly(true);
container->addWidget(_propertiesEdit, 1, 0, 1, 3);
leftLayout->addLayout(container);
}
leftLayout->addWidget(new Line);
@@ -169,14 +173,16 @@ void ProfileEdit::createWidgets(const std::string& profileName) {
_assetsLabel->setWordWrap(true);
container->addWidget(_assetsLabel, 0, 0);
QPushButton* assetsProperties = new QPushButton("Edit");
connect(assetsProperties, &QPushButton::clicked, this, &ProfileEdit::openAssets);
container->addWidget(assetsProperties, 0, 2);
_assetsEdit = new QTextEdit;
_assetsEdit->setReadOnly(true);
_assetsEdit->setAccessibleName("Loaded assets");
container->addWidget(_assetsEdit, 1, 0, 1, 3);
QPushButton* assetsProperties = new QPushButton("Edit");
connect(assetsProperties, &QPushButton::clicked, this, &ProfileEdit::openAssets);
assetsProperties->setAccessibleName("Edit assets");
container->addWidget(assetsProperties, 0, 2);
leftLayout->addLayout(container);
}
leftLayout->addWidget(new Line);
@@ -188,17 +194,19 @@ void ProfileEdit::createWidgets(const std::string& profileName) {
_keybindingsLabel->setObjectName("heading");
container->addWidget(_keybindingsLabel, 0, 0);
_keybindingsEdit = new QTextEdit;
_keybindingsEdit->setReadOnly(true);
_keybindingsEdit->setAccessibleName("Loaded action and keybindings");
container->addWidget(_keybindingsEdit, 1, 0, 1, 3);
QPushButton* keybindingsProperties = new QPushButton("Edit");
connect(
keybindingsProperties, &QPushButton::clicked,
this, &ProfileEdit::openKeybindings
);
keybindingsProperties->setAccessibleName("Edit actions and keybindings");
container->addWidget(keybindingsProperties, 0, 2);
_keybindingsEdit = new QTextEdit;
_keybindingsEdit->setReadOnly(true);
container->addWidget(_keybindingsEdit, 1, 0, 1, 3);
leftLayout->addLayout(container);
}
topLayout->addLayout(leftLayout, 3);
@@ -216,6 +224,7 @@ void ProfileEdit::createWidgets(const std::string& profileName) {
QPushButton* metaEdit = new QPushButton("Edit");
connect(metaEdit, &QPushButton::clicked, this, &ProfileEdit::openMeta);
metaEdit->setLayoutDirection(Qt::RightToLeft);
metaEdit->setAccessibleName("Edit metadata");
container->addWidget(metaEdit);
rightLayout->addLayout(container);
}
@@ -233,6 +242,7 @@ void ProfileEdit::createWidgets(const std::string& profileName) {
this, &ProfileEdit::openMarkNodes
);
interestingNodesEdit->setLayoutDirection(Qt::RightToLeft);
interestingNodesEdit->setAccessibleName("Edit interesting nodes");
container->addWidget(interestingNodesEdit);
rightLayout->addLayout(container);
}
@@ -250,6 +260,7 @@ void ProfileEdit::createWidgets(const std::string& profileName) {
this, &ProfileEdit::openDeltaTimes
);
deltaTimesEdit->setLayoutDirection(Qt::RightToLeft);
deltaTimesEdit->setAccessibleName("Edit simulation time increments");
container->addWidget(deltaTimesEdit);
rightLayout->addLayout(container);
}
@@ -264,6 +275,7 @@ void ProfileEdit::createWidgets(const std::string& profileName) {
QPushButton* cameraEdit = new QPushButton("Edit");
connect(cameraEdit, &QPushButton::clicked, this, &ProfileEdit::openCamera);
cameraEdit->setLayoutDirection(Qt::RightToLeft);
cameraEdit->setAccessibleName("Edit camera");
container->addWidget(cameraEdit);
rightLayout->addLayout(container);
}
@@ -278,6 +290,7 @@ void ProfileEdit::createWidgets(const std::string& profileName) {
QPushButton* timeEdit = new QPushButton("Edit");
connect(timeEdit, &QPushButton::clicked, this, &ProfileEdit::openTime);
timeEdit->setLayoutDirection(Qt::RightToLeft);
timeEdit->setAccessibleName("Edit time");
container->addWidget(timeEdit);
rightLayout->addLayout(container);
}
@@ -292,6 +305,7 @@ void ProfileEdit::createWidgets(const std::string& profileName) {
QPushButton* modulesEdit = new QPushButton("Edit");
connect(modulesEdit, &QPushButton::clicked, this, &ProfileEdit::openModules);
modulesEdit->setLayoutDirection(Qt::RightToLeft);
modulesEdit->setAccessibleName("Edit modules");
container->addWidget(modulesEdit);
rightLayout->addLayout(container);
}
@@ -309,6 +323,7 @@ void ProfileEdit::createWidgets(const std::string& profileName) {
this, &ProfileEdit::openAddedScripts
);
additionalScriptsEdit->setLayoutDirection(Qt::RightToLeft);
additionalScriptsEdit->setAccessibleName("Edit additional scripts");
container->addWidget(additionalScriptsEdit);
rightLayout->addLayout(container);
}
@@ -319,11 +334,6 @@ void ProfileEdit::createWidgets(const std::string& profileName) {
{
QBoxLayout* footer = new QHBoxLayout;
_errorMsg = new QLabel;
_errorMsg->setObjectName("error-message");
_errorMsg->setWordWrap(true);
footer->addWidget(_errorMsg);
QDialogButtonBox* buttons = new QDialogButtonBox;
buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
connect(buttons, &QDialogButtonBox::accepted, this, &ProfileEdit::approved);
@@ -360,7 +370,6 @@ void ProfileEdit::initSummaryTextForEachCategory() {
}
void ProfileEdit::duplicateProfile() {
_errorMsg->clear();
std::string profile = _profileEdit->text().toStdString();
if (profile.empty()) {
return;
@@ -405,18 +414,15 @@ void ProfileEdit::duplicateProfile() {
}
void ProfileEdit::openMeta() {
_errorMsg->clear();
MetaDialog(this, &_profile.meta).exec();
}
void ProfileEdit::openModules() {
_errorMsg->clear();
ModulesDialog(this, &_profile.modules).exec();
_modulesLabel->setText(labelText(_profile.modules.size(), "Modules"));
}
void ProfileEdit::openProperties() {
_errorMsg->clear();
PropertiesDialog(this, &_profile.properties).exec();
_propertiesLabel->setText(labelText(_profile.properties.size(), "Properties"));
_propertiesEdit->setText(
@@ -425,7 +431,6 @@ void ProfileEdit::openProperties() {
}
void ProfileEdit::openKeybindings() {
_errorMsg->clear();
ActionDialog(this, &_profile.actions, &_profile.keybindings).exec();
_keybindingsLabel->setText(labelText(_profile.keybindings.size(), "Keybindings"));
_keybindingsEdit->setText(QString::fromStdString(
@@ -434,19 +439,16 @@ void ProfileEdit::openKeybindings() {
}
void ProfileEdit::openAssets() {
_errorMsg->clear();
AssetsDialog(this, &_profile, _assetBasePath, _userAssetBasePath).exec();
_assetsLabel->setText(labelText(_profile.assets.size(), "Assets"));
_assetsEdit->setText(QString::fromStdString(summarizeAssets(_profile.assets)));
}
void ProfileEdit::openTime() {
_errorMsg->clear();
TimeDialog(this, &_profile.time).exec();
}
void ProfileEdit::openDeltaTimes() {
_errorMsg->clear();
DeltaTimesDialog(this, &_profile.deltaTimes).exec();
_deltaTimesLabel->setText(
labelText(_profile.deltaTimes.size(), "Simulation Time Increments")
@@ -454,17 +456,14 @@ void ProfileEdit::openDeltaTimes() {
}
void ProfileEdit::openAddedScripts() {
_errorMsg->clear();
AdditionalScriptsDialog(this, &_profile.additionalScripts).exec();
}
void ProfileEdit::openCamera() {
_errorMsg->clear();
CameraDialog(this, &_profile.camera).exec();
}
void ProfileEdit::openMarkNodes() {
_errorMsg->clear();
MarkNodesDialog(this, &_profile.markNodes).exec();
_interestingNodesLabel->setText(
labelText(_profile.markNodes.size(), "Mark Interesting Nodes")
@@ -479,15 +478,11 @@ std::string ProfileEdit::specifiedFilename() const {
return _profileEdit->text().toStdString();
}
void ProfileEdit::cancel() {
_saveSelected = false;
reject();
}
void ProfileEdit::approved() {
std::string profileName = _profileEdit->text().toStdString();
if (profileName.empty()) {
_errorMsg->setText("Profile name must be specified");
QMessageBox::critical(this, "No profile name", "Profile name must be specified");
_profileEdit->setFocus();
return;
}
@@ -497,13 +492,15 @@ void ProfileEdit::approved() {
if (std::filesystem::exists(p)) {
// The filename exists in the OpenSpace-provided folder, so we don't want to allow
// a user to overwrite it
_errorMsg->setText(
"This is a read-only profile. Click 'Duplicate' or rename & save"
QMessageBox::critical(
this,
"Reserved profile name",
"This is a read-only profile. Click 'Duplicate' or rename profile and save"
);
_profileEdit->setFocus();
}
else {
_saveSelected = true;
_errorMsg->setText("");
accept();
}
}
@@ -515,3 +512,36 @@ void ProfileEdit::keyPressEvent(QKeyEvent* evt) {
QDialog::keyPressEvent(evt);
}
void ProfileEdit::reject() {
// We hijack the reject (i.e., exit window) and emit the signal. The actual shutdown
// of the window comes at a later stage.
emit raiseExitWindow();
}
void ProfileEdit::closeWithoutSaving() {
_saveSelected = false;
QDialog::reject();
}
void ProfileEdit::promptUserOfUnsavedChanges() {
QMessageBox msgBox;
msgBox.setText("There are unsaved changes");
msgBox.setInformativeText("Do you want to save your changes");
msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Save);
int ret = msgBox.exec();
switch (ret) {
case QMessageBox::Save:
approved();
break;
case QMessageBox::Discard:
closeWithoutSaving();
break;
case QMessageBox::Cancel:
break;
default:
break;
}
}

View File

@@ -35,6 +35,7 @@
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QMessageBox>
#include <QPushButton>
#include <QTextStream>
#include <QVBoxLayout>
@@ -157,13 +158,11 @@ void PropertiesDialog::createWidgets() {
}
layout->addWidget(new Line);
{
_errorMsg = new QMessageBox(this);
_errorMsg->setIcon(QMessageBox::Critical);
_errorMsg->setText("Invalid input data");
QBoxLayout* footerLayout = new QHBoxLayout;
_errorMsg = new QLabel;
_errorMsg->setObjectName("error-message");
_errorMsg->setWordWrap(true);
footerLayout->addWidget(_errorMsg);
_buttonBox = new QDialogButtonBox;
_buttonBox->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
@@ -238,6 +237,7 @@ void PropertiesDialog::listItemAdded() {
void PropertiesDialog::listItemSave() {
if (!areRequiredFormsFilled()) {
_errorMsg->exec();
return;
}
@@ -274,7 +274,7 @@ bool PropertiesDialog::areRequiredFormsFilled() {
errors += "Missing value";
requiredFormsFilled = false;
}
_errorMsg->setText("<font color='red'>" + errors + "</font>");
_errorMsg->setInformativeText(errors);
return requiredFormsFilled;
}
@@ -322,7 +322,6 @@ void PropertiesDialog::transitionToEditMode() {
_propertyLabel->setText("<font color='black'>Property</font>");
_valueLabel->setText("<font color='black'>Value to set</font>");
editBoxDisabled(false);
_errorMsg->setText("");
}
void PropertiesDialog::transitionFromEditMode() {
@@ -338,7 +337,6 @@ void PropertiesDialog::transitionFromEditMode() {
_propertyLabel->setText("<font color='light gray'>Property</font>");
_valueLabel->setText("<font color='light gray'>Value to set</font>");
editBoxDisabled(true);
_errorMsg->setText("");
}
void PropertiesDialog::editBoxDisabled(bool disabled) {

View File

@@ -75,6 +75,7 @@ void TimeDialog::createWidgets() {
{
layout->addWidget(new QLabel("Time Type"));
_typeCombo = new QComboBox;
_typeCombo->setAccessibleName("Time type");
_typeCombo->setToolTip("Types: Absolute defined time or Relative to actual time");
connect(
_typeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
@@ -85,17 +86,19 @@ void TimeDialog::createWidgets() {
{
_absoluteLabel = new QLabel("Absolute UTC:");
layout->addWidget(_absoluteLabel);
_absoluteEdit = new QDateTimeEdit;
_absoluteEdit->setDisplayFormat("yyyy-MM-dd T hh:mm:ss");
_absoluteEdit->setDateTime(QDateTime::currentDateTime());
_absoluteEdit->setAccessibleName("Set absolute time");
layout->addWidget(_absoluteEdit);
}
{
_relativeLabel = new QLabel("Relative Time:");
layout->addWidget(_relativeLabel);
_relativeEdit = new QLineEdit;
_relativeEdit->setAccessibleName("Set relative time");
_relativeEdit->setToolTip(
"String for relative time to actual (e.g. \"-1d\" for back 1 day)"
);

View File

@@ -230,6 +230,7 @@ void SettingsDialog::createWidgets() {
updateSaveButton();
}
);
_propertyVisibility->setObjectName("dropdown");
layout->addWidget(_propertyVisibility, 9, 1);
_bypassLauncher = new QCheckBox("Bypass Launcher");
@@ -299,6 +300,7 @@ void SettingsDialog::createWidgets() {
updateSaveButton();
}
);
_layerServer->setObjectName("dropdown");
layout->addWidget(_layerServer, 14, 1);
_mrf.isEnabled = new QCheckBox("Enable MRF Caching");

View File

@@ -2,6 +2,6 @@ local DataPath = asset.resource({
Name = "Launcher Images",
Type = "HttpSynchronization",
Identifier = "launcher_images",
Version = 3
Version = 4
})
asset.export("DataPath", DataPath)

View File

@@ -55,11 +55,15 @@ public:
struct Version {
int major = 0;
int minor = 0;
auto operator<=>(const Version&) const = default;
};
struct Module {
std::string name;
std::optional<std::string> loadedInstruction;
std::optional<std::string> notLoadedInstruction;
auto operator<=>(const Module&) const = default;
};
struct Meta {
std::optional<std::string> name;
@@ -68,6 +72,8 @@ public:
std::optional<std::string> author;
std::optional<std::string> url;
std::optional<std::string> license;
auto operator<=>(const Meta&) const = default;
};
struct Property {
@@ -79,6 +85,8 @@ public:
SetType setType = SetType::SetPropertyValue;
std::string name;
std::string value;
auto operator<=>(const Property&) const = default;
};
struct Action {
@@ -88,11 +96,15 @@ public:
std::string guiPath;
bool isLocal = false;
std::string script;
auto operator<=>(const Action&) const = default;
};
struct Keybinding {
KeyWithModifier key;
std::string action;
auto operator<=>(const Keybinding&) const = default;
};
struct Time {
@@ -104,6 +116,8 @@ public:
Type type = Type::Relative;
std::string value;
bool startPaused = false;
auto operator<=>(const Time&) const = default;
};
struct CameraGoToNode {
@@ -111,6 +125,8 @@ public:
std::string anchor;
std::optional<double> height;
auto operator<=>(const CameraGoToNode&) const = default;
};
struct CameraNavState {
@@ -123,6 +139,8 @@ public:
std::optional<glm::dvec3> up;
std::optional<double> yaw;
std::optional<double> pitch;
auto operator<=>(const CameraNavState&) const = default;
};
struct CameraGoToGeo {
@@ -132,6 +150,8 @@ public:
double latitude = 0.0;
double longitude = 0.0;
std::optional<double> altitude;
auto operator<=>(const CameraGoToGeo&) const = default;
};
using CameraType = std::variant<CameraGoToNode, CameraNavState, CameraGoToGeo>;
@@ -140,6 +160,8 @@ public:
explicit Profile(const std::filesystem::path& path);
std::string serialize() const;
auto operator<=>(const Profile&) const = default;
/**
* Saves all current settings, starting from the profile that was loaded at startup,
* and all of the property & asset changes that were made since startup.

View File

@@ -41,108 +41,7 @@
// compiler to agree
// NOLINTBEGIN(modernize-use-emplace)
namespace openspace {
bool operator==(const openspace::Profile::Version& lhs,
const openspace::Profile::Version& rhs) noexcept
{
return lhs.major == rhs.major && lhs.minor == rhs.minor;
}
bool operator==(const openspace::Profile::Module& lhs,
const openspace::Profile::Module& rhs) noexcept
{
return lhs.name == rhs.name &&
lhs.loadedInstruction == rhs.loadedInstruction &&
lhs.notLoadedInstruction == rhs.notLoadedInstruction;
}
bool operator==(const openspace::Profile::Meta& lhs,
const openspace::Profile::Meta& rhs) noexcept
{
return lhs.name == rhs.name &&
lhs.version == rhs.version &&
lhs.description == rhs.description &&
lhs.author == rhs.author &&
lhs.url == rhs.url &&
lhs.license == rhs.license;
}
bool operator==(const openspace::Profile::Property& lhs,
const openspace::Profile::Property& rhs) noexcept
{
return lhs.setType == rhs.setType &&
lhs.name == rhs.name &&
lhs.value == rhs.value;
}
bool operator==(const openspace::Profile::Action& lhs,
const openspace::Profile::Action& rhs) noexcept
{
return lhs.identifier == rhs.identifier &&
lhs.documentation == rhs.documentation &&
lhs.name == rhs.name &&
lhs.guiPath == rhs.guiPath &&
lhs.isLocal == rhs.isLocal &&
lhs.script == rhs.script;
}
bool operator==(const openspace::Profile::Keybinding& lhs,
const openspace::Profile::Keybinding& rhs) noexcept
{
return lhs.key == rhs.key && lhs.action == rhs.action;
}
bool operator==(const openspace::Profile::Time& lhs,
const openspace::Profile::Time& rhs) noexcept
{
return lhs.type == rhs.type && lhs.value == rhs.value;
}
bool operator==(const openspace::Profile::CameraGoToNode& lhs,
const openspace::Profile::CameraGoToNode& rhs) noexcept
{
return lhs.anchor == rhs.anchor && lhs.height == rhs.height;
}
bool operator==(const openspace::Profile::CameraNavState& lhs,
const openspace::Profile::CameraNavState& rhs) noexcept
{
return lhs.anchor == rhs.anchor &&
lhs.aim == rhs.aim &&
lhs.referenceFrame == rhs.referenceFrame &&
lhs.position == rhs.position &&
lhs.up == rhs.up &&
lhs.yaw == rhs.yaw &&
lhs.pitch == rhs.pitch;
}
bool operator==(const openspace::Profile::CameraGoToGeo& lhs,
const openspace::Profile::CameraGoToGeo& rhs) noexcept
{
return lhs.anchor == rhs.anchor &&
lhs.latitude == rhs.latitude &&
lhs.longitude == rhs.longitude &&
lhs.altitude == rhs.altitude;
}
bool operator==(const openspace::Profile& lhs,
const openspace::Profile& rhs) noexcept
{
return lhs.version == rhs.version &&
lhs.modules == rhs.modules &&
lhs.meta == rhs.meta &&
lhs.assets == rhs.assets &&
lhs.properties == rhs.properties &&
lhs.actions == rhs.actions &&
lhs.keybindings == rhs.keybindings &&
lhs.time == rhs.time &&
lhs.deltaTimes == rhs.deltaTimes &&
lhs.camera == rhs.camera &&
lhs.markNodes == rhs.markNodes &&
lhs.additionalScripts == rhs.additionalScripts &&
lhs.ignoreUpdates == rhs.ignoreUpdates;
}
namespace {
std::ostream& operator<<(std::ostream& os, const openspace::Profile& profile) {
os << profile.serialize();
return os;