diff --git a/include/ignition/gui/Helpers.hh b/include/ignition/gui/Helpers.hh index d9ec7b979..b99b1822d 100644 --- a/include/ignition/gui/Helpers.hh +++ b/include/ignition/gui/Helpers.hh @@ -89,6 +89,15 @@ namespace ignition IGNITION_GUI_VISIBLE std::string renderEngineName(); + /// \brief Import path for ign-gui QML modules added to the Qt resource + /// system. This helper function returns the QRC resource path where custom + /// ignition QML modules can be imported from. To import an ignition QML + /// module, add this path to the QML engine's import path list before + /// attempting to load a QML file that imports ignition QML modules. + /// \return Resousrce path prefix as a string + IGNITION_GUI_VISIBLE + const QString qmlQrcImportPath(); + /// \brief Returns the first element on a QList which matches the given /// property. /// \param[in] _list The list to search through. diff --git a/include/ignition/gui/qml/qmldir b/include/ignition/gui/qml/qmldir new file mode 100644 index 000000000..f4f66ebc1 --- /dev/null +++ b/include/ignition/gui/qml/qmldir @@ -0,0 +1,3 @@ +module ignition.gui + +IgnSpinBox 1.0 IgnSpinBox.qml \ No newline at end of file diff --git a/include/ignition/gui/resources.qrc b/include/ignition/gui/resources.qrc index 6d54c9f0c..7ec777bf9 100644 --- a/include/ignition/gui/resources.qrc +++ b/include/ignition/gui/resources.qrc @@ -23,4 +23,10 @@ qml/images/export_icon.png qml/images/search.svg + + + qml/qmldir + + qml/IgnSpinBox.qml + diff --git a/src/Application.cc b/src/Application.cc index 57104b09b..9b8d0c79b 100644 --- a/src/Application.cc +++ b/src/Application.cc @@ -28,6 +28,7 @@ #include "ignition/gui/Application.hh" #include "ignition/gui/config.hh" #include "ignition/gui/Dialog.hh" +#include "ignition/gui/Helpers.hh" #include "ignition/gui/MainWindow.hh" #include "ignition/gui/Plugin.hh" @@ -91,6 +92,7 @@ Application::Application(int &_argc, char **_argv, const WindowType _type) // QML engine this->dataPtr->engine = new QQmlApplicationEngine(); + this->dataPtr->engine->addImportPath(qmlQrcImportPath()); // Install signal handler for graceful shutdown this->dataPtr->signalHandler.AddCallback( @@ -387,6 +389,9 @@ bool Application::LoadPlugin(const std::string &_filename, else plugin->Load(_pluginElem); + if (nullptr == plugin->CardItem()) + return false; + // Store plugin in queue to be added to the window this->dataPtr->pluginsToAdd.push(plugin); diff --git a/src/Application_TEST.cc b/src/Application_TEST.cc index 265a86220..8d26c6936 100644 --- a/src/Application_TEST.cc +++ b/src/Application_TEST.cc @@ -136,6 +136,14 @@ TEST(ApplicationTest, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(LoadPlugin)) EXPECT_FALSE(app.LoadPlugin("TestNotRegisteredPlugin")); } + + // Plugin with invalid QML + { + Application app(g_argc, g_argv); + app.AddPluginPath(std::string(PROJECT_BINARY_PATH) + "/lib"); + + EXPECT_FALSE(app.LoadPlugin("TestInvalidQmlPlugin")); + } } ////////////////////////////////////////////////// diff --git a/src/Helpers.cc b/src/Helpers.cc index 37ef34649..286b45f56 100644 --- a/src/Helpers.cc +++ b/src/Helpers.cc @@ -194,3 +194,9 @@ std::string ignition::gui::renderEngineName() return renderEngineNameVariant.toString().toStdString(); } + +///////////////////////////////////////////////// +const QString ignition::gui::qmlQrcImportPath() +{ + return "qrc:/ign-gui-qml/"; +} diff --git a/src/ign.cc b/src/ign.cc index 12ef07713..faba5dcc3 100644 --- a/src/ign.cc +++ b/src/ign.cc @@ -33,6 +33,18 @@ char* g_argv[] = reinterpret_cast(const_cast("./ignition")), }; +////////////////////////////////////////////////// +void startConsoleLog() +{ + std::string home; + ignition::common::env(IGN_HOMEDIR, home); + + std::string logPathMod = ignition::common::joinPaths(home, + ".ignition", "gui", "log", + ignition::common::timeToIso(IGN_SYSTEM_TIME())); + ignLogInit(logPathMod, "console.log"); +} + ////////////////////////////////////////////////// extern "C" IGNITION_GUI_VISIBLE char *ignitionVersion() { @@ -42,6 +54,8 @@ extern "C" IGNITION_GUI_VISIBLE char *ignitionVersion() ////////////////////////////////////////////////// extern "C" IGNITION_GUI_VISIBLE void cmdPluginList() { + startConsoleLog(); + ignition::gui::Application app(g_argc, g_argv); auto pluginsList = app.PluginList(); @@ -65,6 +79,8 @@ extern "C" IGNITION_GUI_VISIBLE void cmdPluginList() ////////////////////////////////////////////////// extern "C" IGNITION_GUI_VISIBLE void cmdStandalone(const char *_filename) { + startConsoleLog(); + ignition::gui::Application app(g_argc, g_argv, ignition::gui::WindowType::kDialog); @@ -79,6 +95,8 @@ extern "C" IGNITION_GUI_VISIBLE void cmdStandalone(const char *_filename) ////////////////////////////////////////////////// extern "C" IGNITION_GUI_VISIBLE void cmdConfig(const char *_config) { + startConsoleLog(); + ignition::gui::Application app(g_argc, g_argv); if (!app.findChild()) @@ -103,6 +121,8 @@ extern "C" IGNITION_GUI_VISIBLE void cmdVerbose(const char *_verbosity) ////////////////////////////////////////////////// extern "C" IGNITION_GUI_VISIBLE void cmdEmptyWindow() { + startConsoleLog(); + ignition::gui::Application app(g_argc, g_argv); if (!app.findChild()) diff --git a/src/ign_TEST.cc b/src/ign_TEST.cc index 80a240ac8..cf3ba5ea7 100644 --- a/src/ign_TEST.cc +++ b/src/ign_TEST.cc @@ -21,6 +21,8 @@ #include +#include +#include #include #include "test_config.h" // NOLINT(build/include) @@ -52,10 +54,45 @@ std::string custom_exec_str(std::string _cmd) return result; } +using namespace ignition; + +class CmdLine : public ::testing::Test +{ + // Documentation inherited + protected: void SetUp() override + { + // Change environment variable so that test files aren't written to $HOME + common::env(IGN_HOMEDIR, this->realHome); + EXPECT_TRUE(common::setenv(IGN_HOMEDIR, this->kFakeHome.c_str())); + } + + // Documentation inherited + protected: void TearDown() override + { + // Restore $HOME + EXPECT_TRUE(common::setenv(IGN_HOMEDIR, this->realHome.c_str())); + } + + /// \brief Directory to act as $HOME for tests + public: const std::string kFakeHome = common::joinPaths(PROJECT_BINARY_PATH, + "test", "fake_home"); + + /// \brief Store user's real $HOME to set it back at the end of tests. + public: std::string realHome; +}; + // See https://github.com/ignitionrobotics/ign-gui/issues/75 -TEST(CmdLine, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(list)) +TEST_F(CmdLine, IGN_UTILS_TEST_ENABLED_ONLY_ON_LINUX(list)) { + // Clear home if it exists + common::removeAll(this->kFakeHome); + + EXPECT_FALSE(common::exists(this->kFakeHome)); + std::string output = custom_exec_str("ign gui -l"); EXPECT_NE(output.find("TopicEcho"), std::string::npos) << output; EXPECT_NE(output.find("Publisher"), std::string::npos) << output; + + EXPECT_TRUE(common::exists(common::joinPaths(this->kFakeHome, ".ignition", + "gui"))); } diff --git a/src/plugins/teleop/Teleop.qml b/src/plugins/teleop/Teleop.qml index 74f9eed9d..f4001a578 100644 --- a/src/plugins/teleop/Teleop.qml +++ b/src/plugins/teleop/Teleop.qml @@ -20,7 +20,7 @@ import QtQuick.Controls 2.2 import QtQuick.Controls.Material 2.1 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 -import "qrc:/qml" +import ignition.gui 1.0 Rectangle { color:"transparent" diff --git a/src/plugins/topic_echo/TopicEcho.qml b/src/plugins/topic_echo/TopicEcho.qml index e1b91543c..aef2cd3a5 100644 --- a/src/plugins/topic_echo/TopicEcho.qml +++ b/src/plugins/topic_echo/TopicEcho.qml @@ -97,8 +97,8 @@ Rectangle { currentIndex: -1 delegate: ItemDelegate { - width: parent.width - text: display + width: listView.width + text: model.display } model: TopicEchoMsgList diff --git a/test/plugins/CMakeLists.txt b/test/plugins/CMakeLists.txt index 7451a7329..5381233cc 100644 --- a/test/plugins/CMakeLists.txt +++ b/test/plugins/CMakeLists.txt @@ -11,6 +11,7 @@ link_directories( set (plugins TestBadInheritancePlugin + TestInvalidQmlPlugin TestNotRegisteredPlugin TestPlugin ) diff --git a/test/plugins/TestInvalidQmlPlugin.cc b/test/plugins/TestInvalidQmlPlugin.cc new file mode 100644 index 000000000..c4b604799 --- /dev/null +++ b/test/plugins/TestInvalidQmlPlugin.cc @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#include + +#include "TestInvalidQmlPlugin.hh" + +using namespace ignition; +using namespace gui; + +///////////////////////////////////////////////// +TestInvalidQmlPlugin::TestInvalidQmlPlugin() + : Plugin() +{ +} + +///////////////////////////////////////////////// +TestInvalidQmlPlugin::~TestInvalidQmlPlugin() +{ +} + +// Register this plugin +IGNITION_ADD_PLUGIN(ignition::gui::TestInvalidQmlPlugin, + ignition::gui::Plugin) diff --git a/test/plugins/TestInvalidQmlPlugin.hh b/test/plugins/TestInvalidQmlPlugin.hh new file mode 100644 index 000000000..412dbd1e6 --- /dev/null +++ b/test/plugins/TestInvalidQmlPlugin.hh @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#ifndef IGNITION_GUI_TEST_MALFORMEDPLUGIN_HH_ +#define IGNITION_GUI_TEST_MALFORMEDPLUGIN_HH_ + +#include + +namespace ignition +{ + namespace gui + { + class TestInvalidQmlPlugin : public Plugin + { + Q_OBJECT + + /// \brief Constructor + public: TestInvalidQmlPlugin(); + + /// \brief Destructor + public: virtual ~TestInvalidQmlPlugin(); + }; + } +} + +#endif diff --git a/test/plugins/TestInvalidQmlPlugin.qml b/test/plugins/TestInvalidQmlPlugin.qml new file mode 100644 index 000000000..17cdc890e --- /dev/null +++ b/test/plugins/TestInvalidQmlPlugin.qml @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ +import QtQuick 2.9 + +Rectangle { + banana: fail +} diff --git a/test/plugins/TestInvalidQmlPlugin.qrc b/test/plugins/TestInvalidQmlPlugin.qrc new file mode 100644 index 000000000..c48ea9d64 --- /dev/null +++ b/test/plugins/TestInvalidQmlPlugin.qrc @@ -0,0 +1,5 @@ + + + TestInvalidQmlPlugin.qml + + diff --git a/tutorials/02_commandline.md b/tutorials/02_commandline.md index daa19ab4e..978021331 100644 --- a/tutorials/02_commandline.md +++ b/tutorials/02_commandline.md @@ -32,3 +32,6 @@ If you have Ignition Tools installed, you can use the `ign gui` command line too --force-version Use a specific library version. --versions Show the available versions. + +When using the command line tool, all console messages are logged to +`$HOME/.ignition/gui/log/`.