//// Application_UI.cpp// interface/src//// Split from Application.cpp by HifiExperiments on 3/30/24// Created by Andrzej Kapolka on 5/10/13.// Copyright 2013 High Fidelity, Inc.// Copyright 2020 Vircadia contributors.// Copyright 2022-2025 Overte e.V.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// SPDX-License-Identifier: Apache-2.0// #include "Application.h" #include <QtQml/QQmlContext>#include <QStyle>#include <QStyleFactory>#if defined(Q_OS_WIN)#include <windows.h>#endif #include <AddressManager.h>#include <AnimationCacheScriptingInterface.h>#include <audio/AudioScope.h>#include <AudioScriptingInterface.h>#include <AvatarBookmarks.h>#include <display-plugins/CompositorHelper.h>#include <FileDialogHelper.h>#include <FSTReader.h>#include <MainWindow.h>#include <material-networking/TextureCacheScriptingInterface.h>#include <Menu.h>#include <MessagesClient.h>#include <model-networking/ModelCacheScriptingInterface.h>#include <OffscreenUi.h>#include <plugins/InputConfiguration.h>#include <plugins/PluginManager.h>#include <plugins/PluginUtils.h>#include <plugins/SteamClientPlugin.h>#include <Preferences.h>#include <procedural/MaterialCacheScriptingInterface.h>#include <raypick/PointerScriptingInterface.h>#include <recording/RecordingScriptingInterface.h>#include <SandboxUtils.h>#include <SceneScriptingInterface.h>#include <ScriptEngines.h>#include <scripting/AccountServicesScriptingInterface.h>#include <scripting/AssetMappingsScriptingInterface.h>#include <scripting/ControllerScriptingInterface.h>#include <scripting/DesktopScriptingInterface.h>#include <scripting/HMDScriptingInterface.h>#include <scripting/KeyboardScriptingInterface.h>#include <scripting/MenuScriptingInterface.h>#include <scripting/PerformanceScriptingInterface.h>#include <scripting/PlatformInfoScriptingInterface.h>#include <scripting/RatesScriptingInterface.h>#include <scripting/RenderScriptingInterface.h>#include <scripting/SelectionScriptingInterface.h>#include <scripting/SettingsScriptingInterface.h>#include <scripting/WindowScriptingInterface.h>#ifndef Q_OS_ANDROID#include <shared/FileLogger.h>#endif#include <shared/GlobalAppProperties.h>#include <shared/StringHelpers.h>#include <SoundCacheScriptingInterface.h>#include <ui/AnimStats.h>#include <ui/AvatarInputs.h>#include <ui/DialogsManager.h>#include <ui/DomainConnectionModel.h>#include <ui/EntityScriptServerLogDialog.h>#include <ui/Keyboard.h>#include <ui/LogDialog.h>#include <ui/LoginDialog.h>#include <ui/OctreeStatsDialog.h>#include <ui/OctreeStatsProvider.h>#include <ui/Snapshot.h>#include <ui/Stats.h>#include <ui/TabletScriptingInterface.h>#include <ui/ToolbarScriptingInterface.h>#include <UserActivityLogger.h>#include <UserActivityLoggerScriptingInterface.h>#include <UsersScriptingInterface.h> #include "AboutUtil.h"#include "ArchiveDownloadInterface.h"#include "AudioClient.h"#include "GLCanvas.h"#include "LocationBookmarks.h"#include "LODManager.h"#include "ResourceRequestObserver.h"#if defined(Q_OS_MAC) || defined(Q_OS_WIN)#include "SpeechRecognizer.h"#endif static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; static const QString STANDARD_TO_ACTION_MAPPING_NAME = "Standard to Action";static const QString NO_MOVEMENT_MAPPING_NAME = "Standard to Action (No Movement)";static const QString NO_MOVEMENT_MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/standard_nomovement.json"; const QString DEFAULT_CURSOR_NAME = "SYSTEM"; static const QUrl AVATAR_INPUTS_BAR_QML = PathUtils::qmlUrl("AvatarInputsBar.qml");static const QString INFO_HELP_PATH = "html/tabletHelp.html"; ui::Menu* Application::getPrimaryMenu() { auto appMenu = _window->menuBar(); auto uiMenu = dynamic_cast<ui::Menu*>(appMenu); return uiMenu;} void Application::showDisplayPluginsTools(bool show) { DependencyManager::get<DialogsManager>()->hmdTools(show);} #ifdef USE_GLGLWidget* Application::getPrimaryWidget() { return _primaryWidget;}#elseVKWidget* Application::getPrimaryWidget() { return _primaryWidget;}#endif MainWindow* Application::getPrimaryWindow() { return getWindow();} QOpenGLContext* Application::getPrimaryContext() { return _primaryWidget->qglContext();} bool Application::isForeground() const { return _isForeground && !_window->isMinimized();} bool Application::hasFocus() const { bool result = (QApplication::activeWindow() != nullptr); #if defined(Q_OS_WIN) // On Windows, QWidget::activateWindow() - as called in setFocus() - makes the application's taskbar icon flash but doesn't // take user focus away from their current window. So also check whether the application is the user's current foreground // window. result = result && (HWND)QApplication::activeWindow()->winId() == GetForegroundWindow();#endif return result;} void Application::setFocus() { // Note: Windows doesn't allow a user focus to be taken away from another application. Instead, it changes the color of and // flashes the taskbar icon.#ifdef USE_GL auto window = qApp->getWindow(); window->activateWindow(); //VKTODO#endif} void Application::raise() { auto windowState = qApp->getWindow()->windowState(); if (windowState & Qt::WindowMinimized) { if (windowState & Qt::WindowMaximized) { qApp->getWindow()->showMaximized(); } else if (windowState & Qt::WindowFullScreen) { qApp->getWindow()->showFullScreen(); } else { qApp->getWindow()->showNormal(); } } qApp->getWindow()->raise();} void Application::showCursor(const Cursor::Icon& cursor) { QMutexLocker locker(&_changeCursorLock); auto managedCursor = Cursor::Manager::instance().getCursor(); auto curIcon = managedCursor->getIcon(); if (curIcon != cursor) { managedCursor->setIcon(cursor); curIcon = cursor; } _desiredCursor = cursor == Cursor::Icon::SYSTEM ? Qt::ArrowCursor : Qt::BlankCursor; _cursorNeedsChanging = true;} CompositorHelper& Application::getApplicationCompositor() const { return *DependencyManager::get<CompositorHelper>();} PickRay Application::computePickRay(float x, float y) const { vec2 pickPoint { x, y }; PickRay result; if (isHMDMode()) { getApplicationCompositor().computeHmdPickRay(pickPoint, result.origin, result.direction); } else { pickPoint /= getCanvasSize(); if (_myCamera.getMode() == CameraMode::CAMERA_MODE_MIRROR) { pickPoint.x = 1.0f - pickPoint.x; } QMutexLocker viewLocker(&_viewMutex); _viewFrustum.computePickRay(pickPoint.x, pickPoint.y, result.origin, result.direction); } return result;} void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties) { surfaceContext->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data()); surfaceContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data()); surfaceContext->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data()); surfaceContext->setContextProperty("Preferences", DependencyManager::get<Preferences>().data()); surfaceContext->setContextProperty("Vec3", new Vec3()); surfaceContext->setContextProperty("Quat", new Quat()); surfaceContext->setContextProperty("MyAvatar", DependencyManager::get<AvatarManager>()->getMyAvatar().get()); surfaceContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data()); surfaceContext->setContextProperty("Snapshot", DependencyManager::get<Snapshot>().data()); surfaceContext->setContextProperty("KeyboardScriptingInterface", DependencyManager::get<KeyboardScriptingInterface>().data()); if (setAdditionalContextProperties) { qDebug() << "setting additional context properties!"; auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>(); auto flags = tabletScriptingInterface->getFlags(); surfaceContext->setContextProperty("offscreenFlags", flags); surfaceContext->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data()); surfaceContext->setContextProperty("Settings", new QMLSettingsScriptingInterface(surfaceContext)); surfaceContext->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance()); surfaceContext->setContextProperty("Performance", new PerformanceScriptingInterface()); surfaceContext->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED surfaceContext->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED surfaceContext->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance()); // in Qt 5.10.0 there is already an "Audio" object in the QML context // though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface" surfaceContext->setContextProperty("AudioScriptingInterface", DependencyManager::get<AudioScriptingInterface>().data()); surfaceContext->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data()); surfaceContext->setContextProperty("fileDialogHelper", new FileDialogHelper()); surfaceContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data()); surfaceContext->setContextProperty("Assets", DependencyManager::get<AssetMappingsScriptingInterface>().data()); surfaceContext->setContextProperty("LODManager", DependencyManager::get<LODManager>().data()); surfaceContext->setContextProperty("OctreeStats", DependencyManager::get<OctreeStatsProvider>().data()); surfaceContext->setContextProperty("DCModel", DependencyManager::get<DomainConnectionModel>().data()); surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); surfaceContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data()); surfaceContext->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance()); surfaceContext->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data()); surfaceContext->setContextProperty("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data()); surfaceContext->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data()); surfaceContext->setContextProperty("Render", RenderScriptingInterface::getInstance()); surfaceContext->setContextProperty("Workload", qApp->getGameWorkload()._engine->getConfiguration().get()); surfaceContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data()); surfaceContext->setContextProperty("Pointers", DependencyManager::get<PointerScriptingInterface>().data()); surfaceContext->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data()); surfaceContext->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface()); surfaceContext->setContextProperty("About", AboutUtil::getInstance()); surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance()); // Deprecated. surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data()); surfaceContext->setContextProperty("PlatformInfo", PlatformInfoScriptingInterface::getInstance()); surfaceContext->setContextProperty("ExternalResource", ExternalResource::getInstance()); // This `module` context property is blank for the QML scripting interface so that we don't get log errors when importing // certain JS files from both scripts (in the JS context) and QML (in the QML context). surfaceContext->setContextProperty("module", ""); }} void Application::setDesktopTabletBecomesToolbarSetting(bool value) { _desktopTabletBecomesToolbarSetting.set(value); updateSystemTabletMode();} void Application::setHmdTabletBecomesToolbarSetting(bool value) { _hmdTabletBecomesToolbarSetting.set(value); updateSystemTabletMode();} void Application::setMouseCaptureVR(bool value) { _defaultMouseCaptureVR.set(value); getApplicationCompositor().setEnableMouseCaptureVR(value);}bool Application::getMouseCaptureVR() { return _defaultMouseCaptureVR.get();} void Application::setMiniTabletEnabled(bool enabled) { _miniTabletEnabledSetting.set(enabled); emit miniTabletEnabledChanged(enabled);} void Application::setSettingConstrainToolbarPosition(bool setting) { _constrainToolbarPosition.set(setting); getOffscreenUI()->setConstrainToolbarToCenterX(setting);} void Application::setAwayStateWhenFocusLostInVREnabled(bool enabled) { _awayStateWhenFocusLostInVREnabled.set(enabled); emit awayStateWhenFocusLostInVRChanged(enabled);} QUuid Application::getTabletScreenID() const { auto HMD = DependencyManager::get<HMDScriptingInterface>(); return HMD->getCurrentTabletScreenID();} QUuid Application::getTabletHomeButtonID() const { auto HMD = DependencyManager::get<HMDScriptingInterface>(); return HMD->getCurrentHomeButtonID();} QUuid Application::getTabletFrameID() const { auto HMD = DependencyManager::get<HMDScriptingInterface>(); return HMD->getCurrentTabletFrameID();} QVector<QUuid> Application::getTabletIDs() const { // Most important first. QVector<QUuid> result; auto HMD = DependencyManager::get<HMDScriptingInterface>(); result << HMD->getCurrentTabletScreenID(); result << HMD->getCurrentHomeButtonID(); result << HMD->getCurrentTabletFrameID(); return result;} void Application::confirmConnectWithoutAvatarEntities() { if (_confirmConnectWithoutAvatarEntitiesDialog) { // Dialog is already displayed. return; } if (!getMyAvatar()->hasAvatarEntities()) { // No avatar entities so continue with login. DependencyManager::get<NodeList>()->getDomainHandler().setCanConnectWithoutAvatarEntities(true); return; } QString continueMessage = "Your wearables will not display on this domain. Continue?"; _confirmConnectWithoutAvatarEntitiesDialog = OffscreenUi::asyncQuestion("Continue Without Wearables", continueMessage, QMessageBox::Yes | QMessageBox::No); if (_confirmConnectWithoutAvatarEntitiesDialog->getDialogItem()) { QObject::connect(_confirmConnectWithoutAvatarEntitiesDialog, &ModalDialogListener::response, this, [=, this](QVariant answer) { QObject::disconnect(_confirmConnectWithoutAvatarEntitiesDialog, &ModalDialogListener::response, this, nullptr); _confirmConnectWithoutAvatarEntitiesDialog = nullptr; bool shouldConnect = (static_cast<QMessageBox::StandardButton>(answer.toInt()) == QMessageBox::Yes); DependencyManager::get<NodeList>()->getDomainHandler().setCanConnectWithoutAvatarEntities(shouldConnect); }); }} void Application::createLoginDialog() { const glm::vec3 LOGIN_DIMENSIONS { 0.89f, 0.5f, 0.01f }; const auto OFFSET = glm::vec2(0.7f, -0.1f); auto cameraPosition = _myCamera.getPosition(); auto cameraOrientation = _myCamera.getOrientation(); auto upVec = getMyAvatar()->getWorldOrientation() * Vectors::UNIT_Y; auto headLookVec = (cameraOrientation * Vectors::FRONT); // DEFAULT_DPI / tablet scale percentage const float DPI = 31.0f / (75.0f / 100.0f); auto offset = headLookVec * OFFSET.x; auto position = (cameraPosition + offset) + (upVec * OFFSET.y); EntityItemProperties properties; properties.setType(EntityTypes::Web); properties.setName("LoginDialogEntity"); properties.setSourceUrl(LOGIN_DIALOG.toString()); properties.setPosition(position); properties.setRotation(cameraOrientation); properties.setDimensions(LOGIN_DIMENSIONS); properties.setPrimitiveMode(PrimitiveMode::SOLID); properties.getGrab().setGrabbable(false); properties.setIgnorePickIntersection(false); properties.setAlpha(1.0f); properties.setDpi(DPI); properties.setVisible(true); auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); _loginDialogID = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL); auto keyboard = DependencyManager::get<Keyboard>().data(); if (!keyboard->getAnchorID().isNull() && !_loginDialogID.isNull()) { auto keyboardLocalOffset = cameraOrientation * glm::vec3(-0.4f * getMyAvatar()->getSensorToWorldScale(), -0.3f, 0.2f); EntityItemProperties properties; properties.setPosition(position + keyboardLocalOffset); properties.setRotation(cameraOrientation * Quaternions::Y_180); entityScriptingInterface->editEntity(keyboard->getAnchorID(), properties); keyboard->setResetKeyboardPositionOnRaise(false); } setKeyboardFocusEntity(_loginDialogID); emit loginDialogFocusEnabled(); getApplicationCompositor().getReticleInterface()->setAllowMouseCapture(false); getApplicationCompositor().getReticleInterface()->setVisible(false); if (!_loginStateManager.isSetUp()) { _loginStateManager.setUp(); }} void Application::updateLoginDialogPosition() { const float LOOK_AWAY_THRESHOLD_ANGLE = 70.0f; const auto OFFSET = glm::vec2(0.7f, -0.1f); auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); EntityPropertyFlags desiredProperties; desiredProperties += PROP_POSITION; auto properties = entityScriptingInterface->getEntityPropertiesInternal(_loginDialogID, desiredProperties, false); auto positionVec = properties.getPosition(); auto cameraPositionVec = _myCamera.getPosition(); auto cameraOrientation = cancelOutRollAndPitch(_myCamera.getOrientation()); auto headLookVec = (cameraOrientation * Vectors::FRONT); auto entityToHeadVec = positionVec - cameraPositionVec; auto pointAngle = (glm::acos(glm::dot(glm::normalize(entityToHeadVec), glm::normalize(headLookVec))) * 180.0f / PI); auto upVec = getMyAvatar()->getWorldOrientation() * Vectors::UNIT_Y; auto offset = headLookVec * OFFSET.x; auto newPositionVec = (cameraPositionVec + offset) + (upVec * OFFSET.y); bool outOfBounds = glm::distance(positionVec, cameraPositionVec) > 1.0f; if (pointAngle > LOOK_AWAY_THRESHOLD_ANGLE || outOfBounds) { { EntityItemProperties properties; properties.setPosition(newPositionVec); properties.setRotation(cameraOrientation); entityScriptingInterface->editEntity(_loginDialogID, properties); } { glm::vec3 keyboardLocalOffset = cameraOrientation * glm::vec3(-0.4f * getMyAvatar()->getSensorToWorldScale(), -0.3f, 0.2f); glm::quat keyboardOrientation = cameraOrientation * glm::quat(glm::radians(glm::vec3(-30.0f, 180.0f, 0.0f))); EntityItemProperties properties; properties.setPosition(newPositionVec + keyboardLocalOffset); properties.setRotation(keyboardOrientation); entityScriptingInterface->editEntity(DependencyManager::get<Keyboard>()->getAnchorID(), properties); } }} void Application::createAvatarInputsBar() { const glm::vec3 LOCAL_POSITION { 0.0, 0.0, -1.0 }; // DEFAULT_DPI / tablet scale percentage const float DPI = 31.0f / (75.0f / 100.0f); EntityItemProperties properties; properties.setType(EntityTypes::Web); properties.setName("AvatarInputsBarEntity"); properties.setSourceUrl(AVATAR_INPUTS_BAR_QML.toString()); properties.setParentID(getMyAvatar()->getSelfID()); properties.setParentJointIndex(getMyAvatar()->getJointIndex("_CAMERA_MATRIX")); properties.setPosition(LOCAL_POSITION); properties.setLocalRotation(Quaternions::IDENTITY); //properties.setDimensions(LOGIN_DIMENSIONS); properties.setPrimitiveMode(PrimitiveMode::SOLID); properties.getGrab().setGrabbable(false); properties.setIgnorePickIntersection(false); properties.setAlpha(1.0f); properties.setDpi(DPI); properties.setVisible(true); auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); _avatarInputsBarID = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL);} void Application::destroyAvatarInputsBar() { auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); if (!_avatarInputsBarID.isNull()) { entityScriptingInterface->deleteEntity(_avatarInputsBarID); }} void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const { auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet(SYSTEM_TABLET); auto hmd = DependencyManager::get<HMDScriptingInterface>(); bool onTablet = false; if (!tablet->getToolbarMode()) { onTablet = tablet->pushOntoStack(tabletUrl); if (onTablet) { toggleTabletUI(true); } } else {#if !defined(DISABLE_QML) getOffscreenUI()->show(widgetUrl, name);#endif }} void Application::loadDialog() { ModalDialogListener* dlg = OffscreenUi::getOpenFileNameAsync(_primaryWidget, tr("Open Script"), getPreviousScriptLocation(), tr("JavaScript Files (*.js)"));Lines 1–500 of 1477. Load more
// // Application_UI.cpp // interface/src // // Split from Application.cpp by HifiExperiments on 3/30/24 // Created by Andrzej Kapolka on 5/10/13. // Copyright 2013 High Fidelity, Inc. // Copyright 2020 Vircadia contributors. // Copyright 2022-2025 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // SPDX-License-Identifier: Apache-2.0 // #include "Application.h" #include <QtQml/QQmlContext> #include <QStyle> #include <QStyleFactory> #if defined(Q_OS_WIN) #include <windows.h> #endif #include <AddressManager.h> #include <AnimationCacheScriptingInterface.h> #include <audio/AudioScope.h> #include <AudioScriptingInterface.h> #include <AvatarBookmarks.h> #include <display-plugins/CompositorHelper.h> #include <FileDialogHelper.h> #include <FSTReader.h> #include <MainWindow.h> #include <material-networking/TextureCacheScriptingInterface.h> #include <Menu.h> #include <MessagesClient.h> #include <model-networking/ModelCacheScriptingInterface.h> #include <OffscreenUi.h> #include <plugins/InputConfiguration.h> #include <plugins/PluginManager.h> #include <plugins/PluginUtils.h> #include <plugins/SteamClientPlugin.h> #include <Preferences.h> #include <procedural/MaterialCacheScriptingInterface.h> #include <raypick/PointerScriptingInterface.h> #include <recording/RecordingScriptingInterface.h> #include <SandboxUtils.h> #include <SceneScriptingInterface.h> #include <ScriptEngines.h> #include <scripting/AccountServicesScriptingInterface.h> #include <scripting/AssetMappingsScriptingInterface.h> #include <scripting/ControllerScriptingInterface.h> #include <scripting/DesktopScriptingInterface.h> #include <scripting/HMDScriptingInterface.h> #include <scripting/KeyboardScriptingInterface.h> #include <scripting/MenuScriptingInterface.h> #include <scripting/PerformanceScriptingInterface.h> #include <scripting/PlatformInfoScriptingInterface.h> #include <scripting/RatesScriptingInterface.h> #include <scripting/RenderScriptingInterface.h> #include <scripting/SelectionScriptingInterface.h> #include <scripting/SettingsScriptingInterface.h> #include <scripting/WindowScriptingInterface.h> #ifndef Q_OS_ANDROID #include <shared/FileLogger.h> #endif #include <shared/GlobalAppProperties.h> #include <shared/StringHelpers.h> #include <SoundCacheScriptingInterface.h> #include <ui/AnimStats.h> #include <ui/AvatarInputs.h> #include <ui/DialogsManager.h> #include <ui/DomainConnectionModel.h> #include <ui/EntityScriptServerLogDialog.h> #include <ui/Keyboard.h> #include <ui/LogDialog.h> #include <ui/LoginDialog.h> #include <ui/OctreeStatsDialog.h> #include <ui/OctreeStatsProvider.h> #include <ui/Snapshot.h> #include <ui/Stats.h> #include <ui/TabletScriptingInterface.h> #include <ui/ToolbarScriptingInterface.h> #include <UserActivityLogger.h> #include <UserActivityLoggerScriptingInterface.h> #include <UsersScriptingInterface.h> #include "AboutUtil.h" #include "ArchiveDownloadInterface.h" #include "AudioClient.h" #include "GLCanvas.h" #include "LocationBookmarks.h" #include "LODManager.h" #include "ResourceRequestObserver.h" #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" #endif static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; static const QString STANDARD_TO_ACTION_MAPPING_NAME = "Standard to Action"; static const QString NO_MOVEMENT_MAPPING_NAME = "Standard to Action (No Movement)"; static const QString NO_MOVEMENT_MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/standard_nomovement.json"; const QString DEFAULT_CURSOR_NAME = "SYSTEM"; static const QUrl AVATAR_INPUTS_BAR_QML = PathUtils::qmlUrl("AvatarInputsBar.qml"); static const QString INFO_HELP_PATH = "html/tabletHelp.html"; ui::Menu* Application::getPrimaryMenu() { auto appMenu = _window->menuBar(); auto uiMenu = dynamic_cast<ui::Menu*>(appMenu); return uiMenu; } void Application::showDisplayPluginsTools(bool show) { DependencyManager::get<DialogsManager>()->hmdTools(show); } #ifdef USE_GL GLWidget* Application::getPrimaryWidget() { return _primaryWidget; } #else VKWidget* Application::getPrimaryWidget() { return _primaryWidget; } #endif MainWindow* Application::getPrimaryWindow() { return getWindow(); } QOpenGLContext* Application::getPrimaryContext() { return _primaryWidget->qglContext(); } bool Application::isForeground() const { return _isForeground && !_window->isMinimized(); } bool Application::hasFocus() const { bool result = (QApplication::activeWindow() != nullptr); #if defined(Q_OS_WIN) // On Windows, QWidget::activateWindow() - as called in setFocus() - makes the application's taskbar icon flash but doesn't // take user focus away from their current window. So also check whether the application is the user's current foreground // window. result = result && (HWND)QApplication::activeWindow()->winId() == GetForegroundWindow(); #endif return result; } void Application::setFocus() { // Note: Windows doesn't allow a user focus to be taken away from another application. Instead, it changes the color of and // flashes the taskbar icon. #ifdef USE_GL auto window = qApp->getWindow(); window->activateWindow(); //VKTODO #endif } void Application::raise() { auto windowState = qApp->getWindow()->windowState(); if (windowState & Qt::WindowMinimized) { if (windowState & Qt::WindowMaximized) { qApp->getWindow()->showMaximized(); } else if (windowState & Qt::WindowFullScreen) { qApp->getWindow()->showFullScreen(); } else { qApp->getWindow()->showNormal(); } } qApp->getWindow()->raise(); } void Application::showCursor(const Cursor::Icon& cursor) { QMutexLocker locker(&_changeCursorLock); auto managedCursor = Cursor::Manager::instance().getCursor(); auto curIcon = managedCursor->getIcon(); if (curIcon != cursor) { managedCursor->setIcon(cursor); curIcon = cursor; } _desiredCursor = cursor == Cursor::Icon::SYSTEM ? Qt::ArrowCursor : Qt::BlankCursor; _cursorNeedsChanging = true; } CompositorHelper& Application::getApplicationCompositor() const { return *DependencyManager::get<CompositorHelper>(); } PickRay Application::computePickRay(float x, float y) const { vec2 pickPoint { x, y }; PickRay result; if (isHMDMode()) { getApplicationCompositor().computeHmdPickRay(pickPoint, result.origin, result.direction); } else { pickPoint /= getCanvasSize(); if (_myCamera.getMode() == CameraMode::CAMERA_MODE_MIRROR) { pickPoint.x = 1.0f - pickPoint.x; } QMutexLocker viewLocker(&_viewMutex); _viewFrustum.computePickRay(pickPoint.x, pickPoint.y, result.origin, result.direction); } return result; } void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties) { surfaceContext->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data()); surfaceContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data()); surfaceContext->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data()); surfaceContext->setContextProperty("Preferences", DependencyManager::get<Preferences>().data()); surfaceContext->setContextProperty("Vec3", new Vec3()); surfaceContext->setContextProperty("Quat", new Quat()); surfaceContext->setContextProperty("MyAvatar", DependencyManager::get<AvatarManager>()->getMyAvatar().get()); surfaceContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data()); surfaceContext->setContextProperty("Snapshot", DependencyManager::get<Snapshot>().data()); surfaceContext->setContextProperty("KeyboardScriptingInterface", DependencyManager::get<KeyboardScriptingInterface>().data()); if (setAdditionalContextProperties) { qDebug() << "setting additional context properties!"; auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>(); auto flags = tabletScriptingInterface->getFlags(); surfaceContext->setContextProperty("offscreenFlags", flags); surfaceContext->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data()); surfaceContext->setContextProperty("Settings", new QMLSettingsScriptingInterface(surfaceContext)); surfaceContext->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance()); surfaceContext->setContextProperty("Performance", new PerformanceScriptingInterface()); surfaceContext->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED surfaceContext->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED surfaceContext->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance()); // in Qt 5.10.0 there is already an "Audio" object in the QML context // though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface" surfaceContext->setContextProperty("AudioScriptingInterface", DependencyManager::get<AudioScriptingInterface>().data()); surfaceContext->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data()); surfaceContext->setContextProperty("fileDialogHelper", new FileDialogHelper()); surfaceContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data()); surfaceContext->setContextProperty("Assets", DependencyManager::get<AssetMappingsScriptingInterface>().data()); surfaceContext->setContextProperty("LODManager", DependencyManager::get<LODManager>().data()); surfaceContext->setContextProperty("OctreeStats", DependencyManager::get<OctreeStatsProvider>().data()); surfaceContext->setContextProperty("DCModel", DependencyManager::get<DomainConnectionModel>().data()); surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); surfaceContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data()); surfaceContext->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance()); surfaceContext->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data()); surfaceContext->setContextProperty("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data()); surfaceContext->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data()); surfaceContext->setContextProperty("Render", RenderScriptingInterface::getInstance()); surfaceContext->setContextProperty("Workload", qApp->getGameWorkload()._engine->getConfiguration().get()); surfaceContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data()); surfaceContext->setContextProperty("Pointers", DependencyManager::get<PointerScriptingInterface>().data()); surfaceContext->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data()); surfaceContext->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface()); surfaceContext->setContextProperty("About", AboutUtil::getInstance()); surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance()); // Deprecated. surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data()); surfaceContext->setContextProperty("PlatformInfo", PlatformInfoScriptingInterface::getInstance()); surfaceContext->setContextProperty("ExternalResource", ExternalResource::getInstance()); // This `module` context property is blank for the QML scripting interface so that we don't get log errors when importing // certain JS files from both scripts (in the JS context) and QML (in the QML context). surfaceContext->setContextProperty("module", ""); } } void Application::setDesktopTabletBecomesToolbarSetting(bool value) { _desktopTabletBecomesToolbarSetting.set(value); updateSystemTabletMode(); } void Application::setHmdTabletBecomesToolbarSetting(bool value) { _hmdTabletBecomesToolbarSetting.set(value); updateSystemTabletMode(); } void Application::setMouseCaptureVR(bool value) { _defaultMouseCaptureVR.set(value); getApplicationCompositor().setEnableMouseCaptureVR(value); } bool Application::getMouseCaptureVR() { return _defaultMouseCaptureVR.get(); } void Application::setMiniTabletEnabled(bool enabled) { _miniTabletEnabledSetting.set(enabled); emit miniTabletEnabledChanged(enabled); } void Application::setSettingConstrainToolbarPosition(bool setting) { _constrainToolbarPosition.set(setting); getOffscreenUI()->setConstrainToolbarToCenterX(setting); } void Application::setAwayStateWhenFocusLostInVREnabled(bool enabled) { _awayStateWhenFocusLostInVREnabled.set(enabled); emit awayStateWhenFocusLostInVRChanged(enabled); } QUuid Application::getTabletScreenID() const { auto HMD = DependencyManager::get<HMDScriptingInterface>(); return HMD->getCurrentTabletScreenID(); } QUuid Application::getTabletHomeButtonID() const { auto HMD = DependencyManager::get<HMDScriptingInterface>(); return HMD->getCurrentHomeButtonID(); } QUuid Application::getTabletFrameID() const { auto HMD = DependencyManager::get<HMDScriptingInterface>(); return HMD->getCurrentTabletFrameID(); } QVector<QUuid> Application::getTabletIDs() const { // Most important first. QVector<QUuid> result; auto HMD = DependencyManager::get<HMDScriptingInterface>(); result << HMD->getCurrentTabletScreenID(); result << HMD->getCurrentHomeButtonID(); result << HMD->getCurrentTabletFrameID(); return result; } void Application::confirmConnectWithoutAvatarEntities() { if (_confirmConnectWithoutAvatarEntitiesDialog) { // Dialog is already displayed. return; } if (!getMyAvatar()->hasAvatarEntities()) { // No avatar entities so continue with login. DependencyManager::get<NodeList>()->getDomainHandler().setCanConnectWithoutAvatarEntities(true); return; } QString continueMessage = "Your wearables will not display on this domain. Continue?"; _confirmConnectWithoutAvatarEntitiesDialog = OffscreenUi::asyncQuestion("Continue Without Wearables", continueMessage, QMessageBox::Yes | QMessageBox::No); if (_confirmConnectWithoutAvatarEntitiesDialog->getDialogItem()) { QObject::connect(_confirmConnectWithoutAvatarEntitiesDialog, &ModalDialogListener::response, this, [=, this](QVariant answer) { QObject::disconnect(_confirmConnectWithoutAvatarEntitiesDialog, &ModalDialogListener::response, this, nullptr); _confirmConnectWithoutAvatarEntitiesDialog = nullptr; bool shouldConnect = (static_cast<QMessageBox::StandardButton>(answer.toInt()) == QMessageBox::Yes); DependencyManager::get<NodeList>()->getDomainHandler().setCanConnectWithoutAvatarEntities(shouldConnect); }); } } void Application::createLoginDialog() { const glm::vec3 LOGIN_DIMENSIONS { 0.89f, 0.5f, 0.01f }; const auto OFFSET = glm::vec2(0.7f, -0.1f); auto cameraPosition = _myCamera.getPosition(); auto cameraOrientation = _myCamera.getOrientation(); auto upVec = getMyAvatar()->getWorldOrientation() * Vectors::UNIT_Y; auto headLookVec = (cameraOrientation * Vectors::FRONT); // DEFAULT_DPI / tablet scale percentage const float DPI = 31.0f / (75.0f / 100.0f); auto offset = headLookVec * OFFSET.x; auto position = (cameraPosition + offset) + (upVec * OFFSET.y); EntityItemProperties properties; properties.setType(EntityTypes::Web); properties.setName("LoginDialogEntity"); properties.setSourceUrl(LOGIN_DIALOG.toString()); properties.setPosition(position); properties.setRotation(cameraOrientation); properties.setDimensions(LOGIN_DIMENSIONS); properties.setPrimitiveMode(PrimitiveMode::SOLID); properties.getGrab().setGrabbable(false); properties.setIgnorePickIntersection(false); properties.setAlpha(1.0f); properties.setDpi(DPI); properties.setVisible(true); auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); _loginDialogID = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL); auto keyboard = DependencyManager::get<Keyboard>().data(); if (!keyboard->getAnchorID().isNull() && !_loginDialogID.isNull()) { auto keyboardLocalOffset = cameraOrientation * glm::vec3(-0.4f * getMyAvatar()->getSensorToWorldScale(), -0.3f, 0.2f); EntityItemProperties properties; properties.setPosition(position + keyboardLocalOffset); properties.setRotation(cameraOrientation * Quaternions::Y_180); entityScriptingInterface->editEntity(keyboard->getAnchorID(), properties); keyboard->setResetKeyboardPositionOnRaise(false); } setKeyboardFocusEntity(_loginDialogID); emit loginDialogFocusEnabled(); getApplicationCompositor().getReticleInterface()->setAllowMouseCapture(false); getApplicationCompositor().getReticleInterface()->setVisible(false); if (!_loginStateManager.isSetUp()) { _loginStateManager.setUp(); } } void Application::updateLoginDialogPosition() { const float LOOK_AWAY_THRESHOLD_ANGLE = 70.0f; const auto OFFSET = glm::vec2(0.7f, -0.1f); auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); EntityPropertyFlags desiredProperties; desiredProperties += PROP_POSITION; auto properties = entityScriptingInterface->getEntityPropertiesInternal(_loginDialogID, desiredProperties, false); auto positionVec = properties.getPosition(); auto cameraPositionVec = _myCamera.getPosition(); auto cameraOrientation = cancelOutRollAndPitch(_myCamera.getOrientation()); auto headLookVec = (cameraOrientation * Vectors::FRONT); auto entityToHeadVec = positionVec - cameraPositionVec; auto pointAngle = (glm::acos(glm::dot(glm::normalize(entityToHeadVec), glm::normalize(headLookVec))) * 180.0f / PI); auto upVec = getMyAvatar()->getWorldOrientation() * Vectors::UNIT_Y; auto offset = headLookVec * OFFSET.x; auto newPositionVec = (cameraPositionVec + offset) + (upVec * OFFSET.y); bool outOfBounds = glm::distance(positionVec, cameraPositionVec) > 1.0f; if (pointAngle > LOOK_AWAY_THRESHOLD_ANGLE || outOfBounds) { { EntityItemProperties properties; properties.setPosition(newPositionVec); properties.setRotation(cameraOrientation); entityScriptingInterface->editEntity(_loginDialogID, properties); } { glm::vec3 keyboardLocalOffset = cameraOrientation * glm::vec3(-0.4f * getMyAvatar()->getSensorToWorldScale(), -0.3f, 0.2f); glm::quat keyboardOrientation = cameraOrientation * glm::quat(glm::radians(glm::vec3(-30.0f, 180.0f, 0.0f))); EntityItemProperties properties; properties.setPosition(newPositionVec + keyboardLocalOffset); properties.setRotation(keyboardOrientation); entityScriptingInterface->editEntity(DependencyManager::get<Keyboard>()->getAnchorID(), properties); } } } void Application::createAvatarInputsBar() { const glm::vec3 LOCAL_POSITION { 0.0, 0.0, -1.0 }; // DEFAULT_DPI / tablet scale percentage const float DPI = 31.0f / (75.0f / 100.0f); EntityItemProperties properties; properties.setType(EntityTypes::Web); properties.setName("AvatarInputsBarEntity"); properties.setSourceUrl(AVATAR_INPUTS_BAR_QML.toString()); properties.setParentID(getMyAvatar()->getSelfID()); properties.setParentJointIndex(getMyAvatar()->getJointIndex("_CAMERA_MATRIX")); properties.setPosition(LOCAL_POSITION); properties.setLocalRotation(Quaternions::IDENTITY); //properties.setDimensions(LOGIN_DIMENSIONS); properties.setPrimitiveMode(PrimitiveMode::SOLID); properties.getGrab().setGrabbable(false); properties.setIgnorePickIntersection(false); properties.setAlpha(1.0f); properties.setDpi(DPI); properties.setVisible(true); auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); _avatarInputsBarID = entityScriptingInterface->addEntityInternal(properties, entity::HostType::LOCAL); } void Application::destroyAvatarInputsBar() { auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); if (!_avatarInputsBarID.isNull()) { entityScriptingInterface->deleteEntity(_avatarInputsBarID); } } void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const { auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet(SYSTEM_TABLET); auto hmd = DependencyManager::get<HMDScriptingInterface>(); bool onTablet = false; if (!tablet->getToolbarMode()) { onTablet = tablet->pushOntoStack(tabletUrl); if (onTablet) { toggleTabletUI(true); } } else { #if !defined(DISABLE_QML) getOffscreenUI()->show(widgetUrl, name); #endif } } void Application::loadDialog() { ModalDialogListener* dlg = OffscreenUi::getOpenFileNameAsync(_primaryWidget, tr("Open Script"), getPreviousScriptLocation(), tr("JavaScript Files (*.js)"));