Merge branch 'master' into tell-accountmanager-url-changed

This commit is contained in:
ksuprynowicz 2022-01-23 14:08:10 +01:00
commit e2d7e55e0b
16 changed files with 292 additions and 269 deletions

View file

@ -1,75 +1,81 @@
# Build Linux # Build Linux
*Last Updated on December 1, 2020* *Last Updated on January 6, 2022*
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Linux specific instructions are found in this file. Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Linux specific instructions are found in this file.
You can use the [Vircadia Builder](https://github.com/vircadia/vircadia-builder) to build on Linux more easily. Alternatively, you can follow the manual steps below. You can use the [Vircadia Builder](https://github.com/vircadia/vircadia-builder) to build on Linux more easily. Alternatively, you can follow the manual steps below.
## Ubuntu 16.04/18.04 specific build guide ## Ubuntu 18.04
### Ubuntu 16.04 only
Add the following line to *.bash_profile* ### Ubuntu 18.04 Server only
`export QT_QPA_FONTDIR=/usr/share/fonts/truetype/dejavu/`
### Ubuntu 18.04 server only
Add the universe repository: Add the universe repository:
_(This is not enabled by default on the server edition)_ _(This is not enabled by default on the server edition.)_
```bash ```bash
sudo add-apt-repository universe sudo add-apt-repository universe
sudo apt-get update sudo apt-get update
``` ```
#### Install build tools:
### Install build tools:
- First update the repositories: - First update the repositories:
```bash ```bash
sudo apt-get update -y sudo apt-get update -y
sudo apt-get upgrade -y sudo apt-get upgrade -y
``` ```
- git
- Install git
```bash ```bash
sudo apt-get install git -y sudo apt-get install git -y
``` ```
Verify by git --version Verify git was installed by running `git --version`.
- g++
- Install g++
```bash ```bash
sudo apt-get install g++ -y sudo apt-get install g++ -y
``` ```
Verify by g++ --version Verify g++ was installed by running `g++ --version`.
- *Ubuntu 18.04* cmake
- **Ubuntu 18.04** CMake
```bash ```bash
sudo apt-get install cmake -y sudo apt-get install cmake -y
``` ```
Verify by cmake --version Verify CMake was installed by running `cmake --version`.
- *Ubuntu 16.04* cmake
```bash ### Install build dependencies:
wget https://cmake.org/files/v3.14/cmake-3.14.2-Linux-x86_64.sh
sudo sh cmake-3.14.2-Linux-x86_64.sh --prefix=/usr/local --exclude-subdir
```
#### Install build dependencies:
- OpenSSL: - OpenSSL:
```bash ```bash
sudo apt-get install libssl-dev sudo apt-get install libssl-dev
``` ```
Verify with `openssl version` Verify OpenSSL was installed by running `openssl version`.
- OpenGL: - OpenGL:
```bash ```bash
sudo apt-get install libgl1-mesa-dev -y sudo apt-get install libgl1-mesa-dev -y
sudo ln -s /usr/lib/x86_64-linux-gnu/libGL.so.346.35 /usr/lib/x86_64-linux-gnu/libGL.so.1.2.0
``` ```
- Verify OpenGL: Verify OpenGL:
- First install mesa-utils with the command `sudo apt install mesa-utils -y` - First install mesa-utils with the command `sudo apt install mesa-utils -y`.
- Then run `glxinfo | grep "OpenGL version"` - Then run `glxinfo | grep "OpenGL version"`.
#### To compile interface in a server you must install:
### Extra dependencies to compile Interface on a server
- Install the following:
```bash ```bash
sudo apt-get -y install libpulse0 libnss3 libnspr4 libfontconfig1 libxcursor1 libxcomposite1 libxtst6 libxslt1.1 sudo apt-get -y install libpulse0 libnss3 libnspr4 libfontconfig1 libxcursor1 libxcomposite1 libxtst6 libxslt1.1
``` ```
- Misc dependencies: - Misc dependencies:
```bash ```bash
sudo apt-get install libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev zlib1g-dev sudo apt-get install libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev zlib1g-dev
``` ```
- Install Python 3 and required packages: - Install Python 3 and required packages:
```bash ```bash
sudo apt-get install python python3 python3-distro sudo apt-get install python python3 python3-distro
``` ```
- Install node, required to build the jsdoc documentation:
- Install Node.js as it is required to build the jsdoc documentation:
```bash ```bash
sudo apt-get install nodejs sudo apt-get install nodejs
``` ```
@ -81,16 +87,17 @@ Clone this repository:
git clone https://github.com/vircadia/vircadia.git git clone https://github.com/vircadia/vircadia.git
``` ```
To compile a DEV version checkout the branch you need. To get a list of all tags: Then checkout the master branch with:
```bash
git fetch -a
```
Then checkout the main branch with:
```bash ```bash
git checkout master git checkout master
``` ```
If you need a different branch, you can get a list of all tags with:
```bash
git fetch --tags
git tag
```
### Using a custom Qt build ### Using a custom Qt build
Qt binaries are only provided for Ubuntu. In order to build on other distributions, a Qt5 install Qt binaries are only provided for Ubuntu. In order to build on other distributions, a Qt5 install
@ -104,6 +111,18 @@ The system's Qt can be used, if the development packages are installed, by setti
also the last version available in the Qt 5 branch. It is expected that Linux distributions will have also the last version available in the Qt 5 branch. It is expected that Linux distributions will have
Qt 5.15.2 available for a long time. Qt 5.15.2 available for a long time.
### Architecture support
If the build is intended to be packaged for distribution, the `VIRCADIA_CPU_ARCHITECTURE`
CMake variable needs to be set to an architecture specific value.
By default, it is set to `-march=native -mtune=native`, which yields builds optimized for a particular
machine, but these builds will not work on machines lacking same CPU instructions.
For packaging, it is recommended to set it to a different value, for example `-msse3`. This will help ensure that the build will run on all reasonably modern CPUs.
Setting `VIRCADIA_CPU_ARCHITECTURE` to an empty string will use the default compiler settings and yield maximum compatibility.
### Compiling ### Compiling
Create the build directory: Create the build directory:
@ -118,33 +137,34 @@ Prepare makefiles:
cmake .. cmake ..
``` ```
- If cmake fails with a vcpkg error - delete /tmp/hifi/vcpkg. If cmake fails with a vcpkg error, then delete `~/vircadia-files/vcpkg/`.
Start compilation of the server and get a cup of coffee: #### Server
To compile the Domain server:
```bash ```bash
make domain-server assignment-client make domain-server assignment-client
``` ```
To compile interface: *Note: For a server, it is not necessary to compile the Interface.*
#### Interface
To compile the Interface client:
```bash ```bash
make interface make interface
``` ```
The commands above will compile with a single thread. If you have enough memory, The commands above will compile with a single thread. If you have enough memory, you can decrease your build time using the `-j` flag. Since most x64 CPUs support two threads per core, this works out to CPU_COUNT*2. As an example, if you have a 2 core machine, you could use:
you can decrease your build time using the `-j` flag. Since most x64 CPUs ```bash
support two threads per core, this works out to CPU_COUNT*2. As an example, if
you have a 2 core machine, you could use:
```
make -j4 interface make -j4 interface
``` ```
In a server, it does not make sense to compile interface.
### Running the software ### Running the software
#### Domain server #### Domain server
Running domain server: Running Domain server:
```bash ```bash
./domain-server/domain-server ./domain-server/domain-server
``` ```
@ -158,24 +178,16 @@ Running assignment client:
#### Interface #### Interface
Running interface: Running Interface:
```bash ```bash
./interface/interface ./interface/interface
``` ```
Go to localhost in the running interface. Go to "localhost" in the running Interface to visit your newly launched Domain server.
#### Notes ### Notes
If your goal is to set up a development environment, it is desirable to set the If your goal is to set up a development environment, it is desirable to set the directory that vcpkg builds into with the `HIFI_VCPKG_BASE` environment variable.
directory that vcpkg builds into with the `HIFI_VCPKG_BASE` environment variable.
For example, you might set `HIFI_VCPKG_BASE` to `/home/$USER/vcpkg`. For example, you might set `HIFI_VCPKG_BASE` to `/home/$USER/vcpkg`.
By default, vcpkg will build in the system `/tmp` directory.
If build is intended for packaging or creation of AppImage, `VIRCADIA_CPU_ARCHITECTURE` By default, vcpkg will build in the `~/vircadia-files/vcpkg/` directory.
CMake variable needs to be set to architecture specific value.
It defaults to `-march=native -mtune=native`, which yields builds optimized for particular
machine, but builds will not work on machines lacking same CPU instructions.
For packaging and AppImage it is recommended to set it to different value, for example `-msse3`.
Setting `VIRCADIA_CPU_ARCHITECTURE` to empty string will use default compiler settings and yield
maximum compatibility.

View file

@ -1,6 +1,6 @@
# Build MacOS # Build macOS
*Last Updated on October 19, 2021* *Last Updated on December 1, 2021*
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. This will include the necessary environment variables to customize your build. Only macOS specific instructions are found in this document. Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. This will include the necessary environment variables to customize your build. Only macOS specific instructions are found in this document.
@ -21,9 +21,9 @@ brew install cmake openssl npm
Download an install Python 3.6.6 or higher from [here](https://www.python.org/downloads/). Download an install Python 3.6.6 or higher from [here](https://www.python.org/downloads/).
Execute the `Update Shell Profile.command` script that is provided with the installer. Execute the `Update Shell Profile.command` script that is provided with the installer.
### MacOS SDK ### macOS SDK
You will need version `10.12` of the macOS SDK for building, otherwise you may have crashing or other unintended issues due to the deprecation of OpenGL on macOS. You can get that SDK from [here](https://github.com/phracker/MacOSX-SDKs). You must copy it in to your Xcode SDK directory, e.g. You will need version `10.12` of the macOS SDK for building, otherwise you may experience crashing or other unintended issues due to the deprecation of OpenGL on macOS. You can get that SDK from [here](https://github.com/phracker/MacOSX-SDKs). You must copy it in to your Xcode SDK directory, e.g.
```bash ```bash
cp -rp ~/Downloads/MacOSX10.12.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ cp -rp ~/Downloads/MacOSX10.12.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/
@ -64,14 +64,17 @@ You can append `-j4` to assign more threads to build with. The number indicates
To package the installation, you can simply run `make package` afterwards. To package the installation, you can simply run `make package` afterwards.
## Notes ## Architecture Support
If build is intended for packaging or creation of AppImage, `VIRCADIA_CPU_ARCHITECTURE` If the build is intended to be packaged for distribution, the `VIRCADIA_CPU_ARCHITECTURE`
CMake variable needs to be set to architecture specific value. CMake variable needs to be set to an architecture specific value.
It defaults to `-march=native -mtune=native`, which yields builds optimized for particular
machine, but builds will not work on machines lacking same CPU instructions. By default, it is set to `-march=native -mtune=native`, which yields builds optimized for a particular
For packaging and AppImage it is recommended to set it to different value, for example `-msse3`. machine, but these builds will not work on machines lacking same CPU instructions.
Setting `VIRCADIA_CPU_ARCHITECTURE` to empty string will use default compiler settings and yield
For packaging, it is recommended to set it to a different value, for example `-msse3`. This will help ensure that the build will run on all reasonably modern CPUs.
Setting `VIRCADIA_CPU_ARCHITECTURE` to an empty string will use the default compiler settings and yield
maximum compatibility. maximum compatibility.
## FAQ ## FAQ

View file

@ -54,7 +54,6 @@ Vircadia™ is a 3D social software project seeking to incrementally bring about
- [For Windows - Interface & Server](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md) - [For Windows - Interface & Server](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md)
- [For Mac - Interface](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md#os-x) - [For Mac - Interface](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md#os-x)
- [For Linux - Server .deb - Vircadia Builder](INSTALLER.md#ubuntu-1804--deb) - [For Linux - Server .deb - Vircadia Builder](INSTALLER.md#ubuntu-1804--deb)
- [For Linux - Server .rpm - Vircadia Builder](INSTALLER.md#amazon-linux-2--rpm)
- [For Linux - Interface AppImage - Vircadia Builder](https://github.com/vircadia/vircadia-builder/blob/master/README.md#building-appimages) - [For Linux - Interface AppImage - Vircadia Builder](https://github.com/vircadia/vircadia-builder/blob/master/README.md#building-appimages)
### Boot to Metaverse: [The Goal](https://vircadia.com/vision/) ### Boot to Metaverse: [The Goal](https://vircadia.com/vision/)
@ -94,10 +93,8 @@ Keep in mind that Vircadia consists of multiple smaller projects that might have
#### Supporters of the Vircadia Project #### Supporters of the Vircadia Project
| [ksuprynowicz (74hc595)](https://github.com/ksuprynowicz) | One (1) anonymous, three (3) total sponsors through GitHub. ❤️
| [Daichi Shimabukuro](https://github.com/mshlomd) |
| --- | | --- |
| <p align="center">[<img src="https://vircadia.com/wp-content/uploads/2021/07/74hc595_profile_2-1.png" width="80" alt="ksuprynowicz" />](https://github.com/ksuprynowicz)</p> | <p align="center">[<img src="https://avatars.githubusercontent.com/u/4787162?v=4" width="80" alt="Daichi Shimabukuro" />](https://github.com/mshlomd)</p>
#### Sponsors of Open Source
<img src="https://vircadia.com/wp-content/uploads/2021/11/CBT_OS-logo_White-V.png" width="250" alt="CrossBrowserTesting" />

View file

@ -144,12 +144,16 @@ endif()
if 'x86_64' == cpu_architecture: if 'x86_64' == cpu_architecture:
u_major = int( distro.major_version() ) u_major = int( distro.major_version() )
u_minor = int( distro.minor_version() ) u_minor = int( distro.minor_version() )
if (distro.id() == 'ubuntu' and u_major == 18) or distro.id() == 'linuxmint' and u_major == 19: if distro.id() == 'ubuntu' or distro.id() == 'linuxmint':
self.qtUrl = self.assets_url + '/dependencies/vcpkg/qt5-install-5.15.2-ubuntu-18.04-amd64.tar.xz' if (distro.id() == 'ubuntu' and u_major == 18) or distro.id() == 'linuxmint' and u_major == 19:
elif (distro.id() == 'ubuntu' and u_major > 18) or (distro.id() == 'linuxmint' and u_major > 19): self.qtUrl = self.assets_url + '/dependencies/vcpkg/qt5-install-5.15.2-ubuntu-18.04-amd64.tar.xz'
self.__no_qt_package_error() elif (distro.id() == 'ubuntu' and u_major > 18) or (distro.id() == 'linuxmint' and u_major > 19):
self.__no_qt_package_error()
else:
self.__unsupported_error()
else: else:
self.__unsupported_error() self.__no_qt_package_error()
elif 'aarch64' == cpu_architecture: elif 'aarch64' == cpu_architecture:
if distro.id() == 'ubuntu': if distro.id() == 'ubuntu':

View file

@ -182,7 +182,7 @@ void runTimingTests() {
const int EXTRA_JUNK_SIZE = 200; const int EXTRA_JUNK_SIZE = 200;
extraJunk.append((unsigned char)255); extraJunk.append((unsigned char)255);
for (int i = 0; i < EXTRA_JUNK_SIZE; i++) { for (int i = 0; i < EXTRA_JUNK_SIZE; i++) {
extraJunk.append(QString("junk")); extraJunk.append(QString("junk").toUtf8());
} }
{ {
@ -257,17 +257,17 @@ void runUnitTests() {
quint64 LAST_TEST = 10; quint64 LAST_TEST = 10;
quint64 SKIP_BY = 1; quint64 SKIP_BY = 1;
for (quint64 value = 0; value <= LAST_TEST; value += SKIP_BY) { for (quint64 value = 0; value <= LAST_TEST; value += SKIP_BY) {
qDebug() << "value:" << value; qDebug() << "value:" << value;
ByteCountCoded<quint64> codedValue = value; ByteCountCoded<quint64> codedValue = value;
QByteArray codedValueBuffer = codedValue; QByteArray codedValueBuffer = codedValue;
codedValueBuffer.append((unsigned char)255); codedValueBuffer.append((unsigned char)255);
codedValueBuffer.append(QString("junk")); codedValueBuffer.append(QString("junk").toUtf8());
qDebug() << "codedValueBuffer:"; qDebug() << "codedValueBuffer:";
outputBufferBits((const unsigned char*)codedValueBuffer.constData(), codedValueBuffer.size()); outputBufferBits((const unsigned char*)codedValueBuffer.constData(), codedValueBuffer.size());
@ -276,7 +276,7 @@ void runUnitTests() {
quint64 valueDecoded = valueDecoder; quint64 valueDecoded = valueDecoder;
qDebug() << "valueDecoded:" << valueDecoded; qDebug() << "valueDecoded:" << valueDecoded;
qDebug() << "bytesConsumed:" << bytesConsumed; qDebug() << "bytesConsumed:" << bytesConsumed;
if (value == valueDecoded) { if (value == valueDecoded) {
qDebug() << "SUCCESS!"; qDebug() << "SUCCESS!";

View file

@ -525,7 +525,7 @@ bool Wallet::readSecurityImage(const QString& inputFilePath, unsigned char** out
} else { } else {
foundFooter = (line == IMAGE_FOOTER); foundFooter = (line == IMAGE_FOOTER);
if (!foundFooter) { if (!foundFooter) {
base64EncryptedBuffer.append(line); base64EncryptedBuffer.append(line.toUtf8());
} }
} }
} }

View file

@ -844,7 +844,7 @@ glm::uvec2 OpenGLDisplayPlugin::getSurfacePixels() const {
uvec2 result; uvec2 result;
auto window = _container->getPrimaryWidget(); auto window = _container->getPrimaryWidget();
if (window) { if (window) {
result = toGlm(window->geometry().size() * window->devicePixelRatio()); result = toGlm(window->geometry().size() * window->devicePixelRatioF());
} }
return result; return result;
} }

View file

@ -239,7 +239,7 @@ QNetworkRequest AccountManager::createRequest(QString path, AccountManagerAuth::
} else { } else {
requestURL.setPath(getMetaverseServerURLPath(true) + path.left(queryStringLocation)); requestURL.setPath(getMetaverseServerURLPath(true) + path.left(queryStringLocation));
} }
// qCDebug(networking) << "Creating request path" << requestURL; // qCDebug(networking) << "Creating request path" << requestURL;
// qCDebug(networking) << "requestURL.isValid()" << requestURL.isValid(); // qCDebug(networking) << "requestURL.isValid()" << requestURL.isValid();
// qCDebug(networking) << "requestURL.errorString()" << requestURL.errorString(); // qCDebug(networking) << "requestURL.errorString()" << requestURL.errorString();
@ -573,7 +573,7 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas
postData.append("grant_type=password&"); postData.append("grant_type=password&");
postData.append("username=" + QUrl::toPercentEncoding(login) + "&"); postData.append("username=" + QUrl::toPercentEncoding(login) + "&");
postData.append("password=" + QUrl::toPercentEncoding(password) + "&"); postData.append("password=" + QUrl::toPercentEncoding(password) + "&");
postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE); postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE.toUtf8());
request.setUrl(grantURL); request.setUrl(grantURL);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
@ -594,9 +594,9 @@ void AccountManager::requestAccessTokenWithAuthCode(const QString& authCode, con
QByteArray postData; QByteArray postData;
postData.append("grant_type=authorization_code&"); postData.append("grant_type=authorization_code&");
postData.append("client_id=" + clientId + "&"); postData.append("client_id=" + clientId.toUtf8() + "&");
postData.append("client_secret=" + clientSecret + "&"); postData.append("client_secret=" + clientSecret.toUtf8() + "&");
postData.append("code=" + authCode + "&"); postData.append("code=" + authCode.toUtf8() + "&");
postData.append("redirect_uri=" + QUrl::toPercentEncoding(redirectUri)); postData.append("redirect_uri=" + QUrl::toPercentEncoding(redirectUri));
request.setUrl(grantURL); request.setUrl(grantURL);
@ -618,7 +618,7 @@ void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) {
QByteArray postData; QByteArray postData;
postData.append("grant_type=password&"); postData.append("grant_type=password&");
postData.append("steam_auth_ticket=" + QUrl::toPercentEncoding(authSessionTicket) + "&"); postData.append("steam_auth_ticket=" + QUrl::toPercentEncoding(authSessionTicket) + "&");
postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE); postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE.toUtf8());
request.setUrl(grantURL); request.setUrl(grantURL);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
@ -639,9 +639,9 @@ void AccountManager::requestAccessTokenWithOculus(const QString& nonce, const QS
QByteArray postData; QByteArray postData;
postData.append("grant_type=password&"); postData.append("grant_type=password&");
postData.append("oculus_nonce=" + nonce + "&"); postData.append("oculus_nonce=" + nonce.toUtf8() + "&");
postData.append("oculus_id=" + oculusID + "&"); postData.append("oculus_id=" + oculusID.toUtf8() + "&");
postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE); postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE.toUtf8());
request.setUrl(grantURL); request.setUrl(grantURL);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
@ -671,7 +671,7 @@ void AccountManager::refreshAccessToken() {
QByteArray postData; QByteArray postData;
postData.append("grant_type=refresh_token&"); postData.append("grant_type=refresh_token&");
postData.append("refresh_token=" + QUrl::toPercentEncoding(_accountInfo.getAccessToken().refreshToken) + "&"); postData.append("refresh_token=" + QUrl::toPercentEncoding(_accountInfo.getAccessToken().refreshToken) + "&");
postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE); postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE.toUtf8());
request.setUrl(grantURL); request.setUrl(grantURL);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");

View file

@ -57,7 +57,7 @@ void DomainAccountManager::setAuthURL(const QUrl& authURL) {
} }
_currentAuth.authURL = authURL; _currentAuth.authURL = authURL;
qCDebug(networking) << "DomainAccountManager URL for authenticated requests has been changed to" qCDebug(networking) << "DomainAccountManager URL for authenticated requests has been changed to"
<< qPrintable(_currentAuth.authURL.toString()); << qPrintable(_currentAuth.authURL.toString());
_currentAuth.accessToken = ""; _currentAuth.accessToken = "";
@ -70,7 +70,7 @@ bool DomainAccountManager::hasLogIn() {
return !_currentAuth.authURL.isEmpty(); return !_currentAuth.authURL.isEmpty();
} }
bool DomainAccountManager::isLoggedIn() { bool DomainAccountManager::isLoggedIn() {
return !_currentAuth.authURL.isEmpty() && hasValidAccessToken(); return !_currentAuth.authURL.isEmpty() && hasValidAccessToken();
} }
@ -92,7 +92,7 @@ void DomainAccountManager::requestAccessToken(const QString& username, const QSt
formData.append("grant_type=password&"); formData.append("grant_type=password&");
formData.append("username=" + QUrl::toPercentEncoding(username) + "&"); formData.append("username=" + QUrl::toPercentEncoding(username) + "&");
formData.append("password=" + QUrl::toPercentEncoding(password) + "&"); formData.append("password=" + QUrl::toPercentEncoding(password) + "&");
formData.append("client_id=" + _currentAuth.clientID); formData.append("client_id=" + _currentAuth.clientID.toUtf8());
request.setUrl(_currentAuth.authURL); request.setUrl(_currentAuth.authURL);
@ -168,7 +168,7 @@ bool DomainAccountManager::hasValidAccessToken() {
} }
// ####### TODO // ####### TODO
// if (!_isWaitingForTokenRefresh && needsToRefreshToken()) { // if (!_isWaitingForTokenRefresh && needsToRefreshToken()) {
// refreshAccessToken(); // refreshAccessToken();
// } // }
@ -184,18 +184,18 @@ void DomainAccountManager::setTokensFromJSON(const QJsonObject& jsonObject, cons
bool DomainAccountManager::checkAndSignalForAccessToken() { bool DomainAccountManager::checkAndSignalForAccessToken() {
bool hasToken = hasValidAccessToken(); bool hasToken = hasValidAccessToken();
// ####### TODO: Handle hasToken == true. // ####### TODO: Handle hasToken == true.
// It causes the login dialog not to display (OK) but somewhere the domain server needs to be sent it (and if domain server // It causes the login dialog not to display (OK) but somewhere the domain server needs to be sent it (and if domain server
// gets error when trying to use it then user should be prompted to login). // gets error when trying to use it then user should be prompted to login).
hasToken = false; hasToken = false;
if (!hasToken) { if (!hasToken) {
// Emit a signal so somebody can call back to us and request an access token given a user name and password. // Emit a signal so somebody can call back to us and request an access token given a user name and password.
// Dialog can be hidden immediately after showing if we've just teleported to the domain, unless the signal is delayed. // Dialog can be hidden immediately after showing if we've just teleported to the domain, unless the signal is delayed.
auto domain = _currentAuth.authURL.host(); auto domain = _currentAuth.authURL.host();
QTimer::singleShot(500, this, [this, domain] { QTimer::singleShot(500, this, [this, domain] {
emit this->authRequired(domain); emit this->authRequired(domain);
}); });
} }

View file

@ -52,7 +52,7 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall
// Adding the action name // Adding the action name
QHttpPart actionPart; QHttpPart actionPart;
actionPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"action_name\""); actionPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"action_name\"");
actionPart.setBody(QByteArray().append(action)); actionPart.setBody(QByteArray().append(action.toUtf8()));
multipart->append(actionPart); multipart->append(actionPart);
// Log the local-time that this event was logged // Log the local-time that this event was logged

View file

@ -63,11 +63,11 @@ bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromFile(QString path) {
QByteArray OctreeUtils::RawOctreeData::toByteArray() { QByteArray OctreeUtils::RawOctreeData::toByteArray() {
QByteArray jsonString; QByteArray jsonString;
jsonString += QString("{\n \"DataVersion\": %1,\n").arg(dataVersion); jsonString += QString("{\n \"DataVersion\": %1,\n").arg(dataVersion).toUtf8();
writeSubclassData(jsonString); writeSubclassData(jsonString);
jsonString += QString(",\n \"Id\": \"%1\",\n \"Version\": %2\n}").arg(id.toString()).arg(version); jsonString += QString(",\n \"Id\": \"%1\",\n \"Version\": %2\n}").arg(id.toString()).arg(version).toUtf8();
return jsonString; return jsonString;
} }

View file

@ -38,14 +38,14 @@
* <p><em>This API currently has no effect and is not used.</em></p> * <p><em>This API currently has no effect and is not used.</em></p>
* *
* @namespace OffscreenFlags * @namespace OffscreenFlags
* *
* @hifi-interface * @hifi-interface
* @hifi-client-entity * @hifi-client-entity
* @hifi-avatar * @hifi-avatar
* *
* @property {boolean} navigationFocused - <code>true</code> if UI has joystick navigation focus, <code>false</code> if it * @property {boolean} navigationFocused - <code>true</code> if UI has joystick navigation focus, <code>false</code> if it
* doesn't. * doesn't.
* @property {boolean} navigationFocusDisabled - <code>true</code> if UI joystick navigation focus is disabled, * @property {boolean} navigationFocusDisabled - <code>true</code> if UI joystick navigation focus is disabled,
* <code>false</code> if it isn't. * <code>false</code> if it isn't.
*/ */
@ -75,7 +75,7 @@ public:
emit navigationFocusDisabledChanged(); emit navigationFocusDisabledChanged();
} }
} }
signals: signals:
/*@jsdoc /*@jsdoc
@ -99,9 +99,9 @@ private:
static OffscreenFlags* offscreenFlags { nullptr }; static OffscreenFlags* offscreenFlags { nullptr };
// This hack allows the QML UI to work with keys that are also bound as // This hack allows the QML UI to work with keys that are also bound as
// shortcuts at the application level. However, it seems as though the // shortcuts at the application level. However, it seems as though the
// bound actions are still getting triggered. At least for backspace. // bound actions are still getting triggered. At least for backspace.
// Not sure why. // Not sure why.
// //
// However, the problem may go away once we switch to the new menu system, // However, the problem may go away once we switch to the new menu system,
@ -168,7 +168,7 @@ void OffscreenUi::onRootContextCreated(QQmlContext* qmlContext) {
qmlContext->setContextProperty("fileDialogHelper", new FileDialogHelper()); qmlContext->setContextProperty("fileDialogHelper", new FileDialogHelper());
#ifdef DEBUG #ifdef DEBUG
qmlContext->setContextProperty("DebugQML", QVariant(true)); qmlContext->setContextProperty("DebugQML", QVariant(true));
#else #else
qmlContext->setContextProperty("DebugQML", QVariant(false)); qmlContext->setContextProperty("DebugQML", QVariant(false));
#endif #endif
@ -284,7 +284,7 @@ QQuickItem* OffscreenUi::createMessageBox(Icon icon, const QString& title, const
map.insert("buttons", buttons.operator int()); map.insert("buttons", buttons.operator int());
map.insert("defaultButton", defaultButton); map.insert("defaultButton", defaultButton);
QVariant result; QVariant result;
bool invokeResult; bool invokeResult = false;
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>(); auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
if (tablet->getToolbarMode() && _desktop) { if (tablet->getToolbarMode() && _desktop) {
@ -309,7 +309,7 @@ int OffscreenUi::waitForMessageBoxResult(QQuickItem* messageBox) {
if (!messageBox) { if (!messageBox) {
return QMessageBox::NoButton; return QMessageBox::NoButton;
} }
return MessageBoxListener(messageBox).waitForButtonResult(); return MessageBoxListener(messageBox).waitForButtonResult();
} }
@ -424,8 +424,8 @@ QString OffscreenUi::getText(const Icon icon, const QString& title, const QStrin
QString OffscreenUi::getItem(const Icon icon, const QString& title, const QString& label, const QStringList& items, QString OffscreenUi::getItem(const Icon icon, const QString& title, const QString& label, const QStringList& items,
int current, bool editable, bool* ok) { int current, bool editable, bool* ok) {
if (ok) { if (ok) {
*ok = false; *ok = false;
} }
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
@ -580,7 +580,7 @@ QQuickItem* OffscreenUi::createInputDialog(const Icon icon, const QString& title
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>(); auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
bool invokeResult; bool invokeResult = false;
if (tablet->getToolbarMode() && _desktop) { if (tablet->getToolbarMode() && _desktop) {
invokeResult = QMetaObject::invokeMethod(_desktop, "inputDialog", invokeResult = QMetaObject::invokeMethod(_desktop, "inputDialog",
Q_RETURN_ARG(QVariant, result), Q_RETURN_ARG(QVariant, result),
@ -608,7 +608,7 @@ QQuickItem* OffscreenUi::createCustomInputDialog(const Icon icon, const QString&
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>(); auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
bool invokeResult; bool invokeResult = false;
if (tablet->getToolbarMode() && _desktop) { if (tablet->getToolbarMode() && _desktop) {
invokeResult = QMetaObject::invokeMethod(_desktop, "inputDialog", invokeResult = QMetaObject::invokeMethod(_desktop, "inputDialog",
Q_RETURN_ARG(QVariant, result), Q_RETURN_ARG(QVariant, result),
@ -619,7 +619,7 @@ QQuickItem* OffscreenUi::createCustomInputDialog(const Icon icon, const QString&
Q_ARG(QVariant, QVariant::fromValue(map))); Q_ARG(QVariant, QVariant::fromValue(map)));
emit tabletScriptingInterface->tabletNotification(); emit tabletScriptingInterface->tabletNotification();
} }
if (!invokeResult) { if (!invokeResult) {
qWarning() << "Failed to create custom message box"; qWarning() << "Failed to create custom message box";
return nullptr; return nullptr;
@ -648,13 +648,13 @@ void OffscreenUi::setNavigationFocused(bool focused) {
// FIXME HACK.... // FIXME HACK....
// This hack is an attempt to work around the 'offscreen UI can't gain keyboard focus' bug // This hack is an attempt to work around the 'offscreen UI can't gain keyboard focus' bug
// https://app.asana.com/0/27650181942747/83176475832393 // https://app.asana.com/0/27650181942747/83176475832393
// The problem seems related to https://bugreports.qt.io/browse/QTBUG-50309 // The problem seems related to https://bugreports.qt.io/browse/QTBUG-50309
// //
// The workaround seems to be to give some other window (same process or another process doesn't seem to matter) // The workaround seems to be to give some other window (same process or another process doesn't seem to matter)
// focus and then put focus back on the interface main window. // focus and then put focus back on the interface main window.
// //
// If I could reliably reproduce this bug I could eventually track down what state change is occuring // If I could reliably reproduce this bug I could eventually track down what state change is occuring
// during the process of the main window losing and then gaining focus, but failing that, here's a // during the process of the main window losing and then gaining focus, but failing that, here's a
// brute force way of triggering that state change at application start in a way that should be nearly // brute force way of triggering that state change at application start in a way that should be nearly
// imperceptible to the user. // imperceptible to the user.
class KeyboardFocusHack : public QObject { class KeyboardFocusHack : public QObject {
@ -754,7 +754,7 @@ private slots:
QString OffscreenUi::fileDialog(const QVariantMap& properties) { QString OffscreenUi::fileDialog(const QVariantMap& properties) {
QVariant buildDialogResult; QVariant buildDialogResult;
bool invokeResult; bool invokeResult = false;
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>(); auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
if (tablet->getToolbarMode() && _desktop) { if (tablet->getToolbarMode() && _desktop) {
@ -783,7 +783,7 @@ QString OffscreenUi::fileDialog(const QVariantMap& properties) {
ModalDialogListener* OffscreenUi::fileDialogAsync(const QVariantMap& properties) { ModalDialogListener* OffscreenUi::fileDialogAsync(const QVariantMap& properties) {
QVariant buildDialogResult; QVariant buildDialogResult;
bool invokeResult; bool invokeResult = false;
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>(); auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
if (tablet->getToolbarMode() && _desktop) { if (tablet->getToolbarMode() && _desktop) {
@ -1003,7 +1003,7 @@ class AssetDialogListener : public ModalDialogListener {
QString OffscreenUi::assetDialog(const QVariantMap& properties) { QString OffscreenUi::assetDialog(const QVariantMap& properties) {
// ATP equivalent of fileDialog(). // ATP equivalent of fileDialog().
QVariant buildDialogResult; QVariant buildDialogResult;
bool invokeResult; bool invokeResult = false;
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>(); auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
if (tablet->getToolbarMode() && _desktop) { if (tablet->getToolbarMode() && _desktop) {
@ -1033,7 +1033,7 @@ QString OffscreenUi::assetDialog(const QVariantMap& properties) {
ModalDialogListener *OffscreenUi::assetDialogAsync(const QVariantMap& properties) { ModalDialogListener *OffscreenUi::assetDialogAsync(const QVariantMap& properties) {
// ATP equivalent of fileDialog(). // ATP equivalent of fileDialog().
QVariant buildDialogResult; QVariant buildDialogResult;
bool invokeResult; bool invokeResult = false;
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>(); auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
if (tablet->getToolbarMode() && _desktop) { if (tablet->getToolbarMode() && _desktop) {
@ -1165,7 +1165,7 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) {
} }
// QML input elements absorb key press, but apparently not key release. // QML input elements absorb key press, but apparently not key release.
// therefore we want to ensure that key release events for key presses that were // therefore we want to ensure that key release events for key presses that were
// accepted by the QML layer are suppressed // accepted by the QML layer are suppressed
if (type == QEvent::KeyRelease && pressed) { if (type == QEvent::KeyRelease && pressed) {
pressed = false; pressed = false;
@ -1175,10 +1175,6 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) {
return result; return result;
} }
unsigned int OffscreenUi::getMenuUserDataId() const {
return _vrMenu->_userDataId;
}
ModalDialogListener::ModalDialogListener(QQuickItem *dialog) : _dialog(dialog) { ModalDialogListener::ModalDialogListener(QQuickItem *dialog) : _dialog(dialog) {
if (!dialog) { if (!dialog) {
_finished = true; _finished = true;

View file

@ -74,7 +74,7 @@ public:
// Setting pinned to true will hide all overlay elements on the desktop that don't have a pinned flag // Setting pinned to true will hide all overlay elements on the desktop that don't have a pinned flag
void setPinned(bool pinned = true); void setPinned(bool pinned = true);
void togglePinned(); void togglePinned();
void setConstrainToolbarToCenterX(bool constrained); void setConstrainToolbarToCenterX(bool constrained);
@ -237,7 +237,6 @@ public:
static ModalDialogListener* getTextAsync(const Icon icon, const QString & title, const QString & label, const QString & text = QString()); static ModalDialogListener* getTextAsync(const Icon icon, const QString & title, const QString & label, const QString & text = QString());
static ModalDialogListener* getItemAsync(const Icon icon, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true); static ModalDialogListener* getItemAsync(const Icon icon, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true);
unsigned int getMenuUserDataId() const;
QList<QObject *> &getModalDialogListeners(); QList<QObject *> &getModalDialogListeners();
signals: signals:
@ -270,7 +269,7 @@ private:
QList<QObject*> _modalDialogListeners; QList<QObject*> _modalDialogListeners;
std::unordered_map<int, bool> _pressedKeys; std::unordered_map<int, bool> _pressedKeys;
VrMenu* _vrMenu { nullptr }; VrMenu* _vrMenu { nullptr };
QQueue<std::function<void(VrMenu*)>> _queuedMenuInitializers; QQueue<std::function<void(VrMenu*)>> _queuedMenuInitializers;
}; };
#endif #endif

View file

@ -17,139 +17,116 @@
#include "OffscreenUi.h" #include "OffscreenUi.h"
#include "ui/Logging.h" #include "ui/Logging.h"
static unsigned int USER_DATA_ID = 0;
// Binds together a Qt Action or Menu with the QML Menu or MenuItem
//
// TODO On reflection, it may be pointless to use the UUID. Perhaps
// simply creating the bidirectional link pointing to both the widget
// and qml object and inject the pointer into both objects
class MenuUserData : public QObjectUserData {
public:
MenuUserData(QAction* action, QObject* qmlObject, QObject* qmlParent) {
if (!USER_DATA_ID) {
USER_DATA_ID = DependencyManager::get<OffscreenUi>()->getMenuUserDataId();
}
_action = action;
_qml = qmlObject;
_qmlParent = qmlParent;
action->setUserData(USER_DATA_ID, this); MenuUserData::MenuUserData(QAction* action, QObject* qmlObject, QObject* qmlParent) {
qmlObject->setUserData(USER_DATA_ID, this); _action = action;
qmlObject->setObjectName(uuid.toString()); _qml = qmlObject;
// Make sure we can find it again in the future _qmlParent = qmlParent;
action->setProperty(USER_DATA, QVariant::fromValue(this));
qmlObject->setProperty(USER_DATA, QVariant::fromValue(this));
qmlObject->setObjectName(uuid.toString());
// Make sure we can find it again in the future
updateQmlItemFromAction();
_changedConnection = QObject::connect(action, &QAction::changed, [=] {
updateQmlItemFromAction(); updateQmlItemFromAction();
_changedConnection = QObject::connect(action, &QAction::changed, [=] { });
updateQmlItemFromAction(); _shutdownConnection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [=] {
});
_shutdownConnection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [=] {
QObject::disconnect(_changedConnection);
});
class ExclusionGroupSetter : public QObject {
public:
ExclusionGroupSetter(QObject* from, QObject* to, QObject* qmlParent) : QObject(from), _from(from), _to(to), _qmlParent(qmlParent) {
_from->installEventFilter(this);
}
~ExclusionGroupSetter() {
_from->removeEventFilter(this);
}
protected:
virtual bool eventFilter(QObject* o, QEvent* e) override {
if (e->type() == QEvent::DynamicPropertyChange) {
QDynamicPropertyChangeEvent* dpc = static_cast<QDynamicPropertyChangeEvent*>(e);
if (dpc->propertyName() == "exclusionGroup")
{
// unfortunately Qt doesn't support passing dynamic properties between C++ / QML, so we have to use this ugly helper function
QMetaObject::invokeMethod(_qmlParent,
"addExclusionGroup",
Qt::DirectConnection,
Q_ARG(QVariant, QVariant::fromValue(_to)),
Q_ARG(QVariant, _from->property(dpc->propertyName())));
}
}
return QObject::eventFilter(o, e);
}
private:
QObject* _from;
QObject* _to;
QObject* _qmlParent;
};
new ExclusionGroupSetter(action, qmlObject, qmlParent);
}
~MenuUserData() {
QObject::disconnect(_changedConnection); QObject::disconnect(_changedConnection);
QObject::disconnect(_shutdownConnection); });
_action->setUserData(USER_DATA_ID, nullptr);
_qml->setUserData(USER_DATA_ID, nullptr);
}
void updateQmlItemFromAction() { class ExclusionGroupSetter : public QObject {
_qml->setProperty("checkable", _action->isCheckable()); public:
_qml->setProperty("enabled", _action->isEnabled()); ExclusionGroupSetter(QObject* from, QObject* to, QObject* qmlParent) : QObject(from), _from(from), _to(to), _qmlParent(qmlParent) {
QString text = _action->text(); _from->installEventFilter(this);
_qml->setProperty("text", text);
_qml->setProperty("shortcut", _action->shortcut().toString());
_qml->setProperty("checked", _action->isChecked());
_qml->setProperty("visible", _action->isVisible());
}
void clear() {
_qml->setProperty("checkable", 0);
_qml->setProperty("enabled", 0);
_qml->setProperty("text", 0);
_qml->setProperty("shortcut", 0);
_qml->setProperty("checked", 0);
_qml->setProperty("visible", 0);
_action->setUserData(USER_DATA_ID, nullptr);
_qml->setUserData(USER_DATA_ID, nullptr);
}
const QUuid uuid{ QUuid::createUuid() };
static bool hasData(QAction* object) {
if (!object) {
qWarning() << "Attempted to fetch MenuUserData for null object";
return false;
} }
return (nullptr != static_cast<MenuUserData*>(object->userData(USER_DATA_ID)));
}
static MenuUserData* forObject(QAction* object) { ~ExclusionGroupSetter() {
if (!object) { _from->removeEventFilter(this);
qWarning() << "Attempted to fetch MenuUserData for null object";
return nullptr;
} }
auto result = static_cast<MenuUserData*>(object->userData(USER_DATA_ID)); protected:
if (!result) { virtual bool eventFilter(QObject* o, QEvent* e) override {
qWarning() << "Unable to find MenuUserData for object " << object; if (e->type() == QEvent::DynamicPropertyChange) {
if (auto action = dynamic_cast<QAction*>(object)) { QDynamicPropertyChangeEvent* dpc = static_cast<QDynamicPropertyChangeEvent*>(e);
qWarning() << action->text(); if (dpc->propertyName() == "exclusionGroup") {
} else if (auto menu = dynamic_cast<QMenu*>(object)) { // unfortunately Qt doesn't support passing dynamic properties between C++ / QML, so we have to use this ugly helper function
qWarning() << menu->title(); QMetaObject::invokeMethod(_qmlParent,
"addExclusionGroup",
Qt::DirectConnection,
Q_ARG(QVariant, QVariant::fromValue(_to)),
Q_ARG(QVariant, _from->property(dpc->propertyName())));
}
} }
return nullptr;
return QObject::eventFilter(o, e);
} }
return result;
private:
QObject* _from;
QObject* _to;
QObject* _qmlParent;
};
new ExclusionGroupSetter(action, qmlObject, qmlParent);
}
MenuUserData::~MenuUserData() {
QObject::disconnect(_changedConnection);
QObject::disconnect(_shutdownConnection);
_action->setProperty(USER_DATA, QVariant());
_qml->setProperty(USER_DATA, QVariant());
}
void MenuUserData::updateQmlItemFromAction() {
_qml->setProperty("checkable", _action->isCheckable());
_qml->setProperty("enabled", _action->isEnabled());
QString text = _action->text();
_qml->setProperty("text", text);
_qml->setProperty("shortcut", _action->shortcut().toString());
_qml->setProperty("checked", _action->isChecked());
_qml->setProperty("visible", _action->isVisible());
}
void MenuUserData::clear() {
_qml->setProperty("checkable", 0);
_qml->setProperty("enabled", 0);
_qml->setProperty("text", 0);
_qml->setProperty("shortcut", 0);
_qml->setProperty("checked", 0);
_qml->setProperty("visible", 0);
_action->setProperty(USER_DATA, QVariant());
_qml->setProperty(USER_DATA, QVariant());
}
bool MenuUserData::hasData(QAction* object) {
if (!object) {
qWarning() << "Attempted to fetch MenuUserData for null object";
return false;
} }
return (nullptr != object->property(USER_DATA).value<MenuUserData*>());
}
private: MenuUserData* MenuUserData::forObject(QAction* object) {
Q_DISABLE_COPY(MenuUserData); if (!object) {
qWarning() << "Attempted to fetch MenuUserData for null object";
QMetaObject::Connection _shutdownConnection; return nullptr;
QMetaObject::Connection _changedConnection; }
QAction* _action { nullptr }; auto result = object->property(USER_DATA).value<MenuUserData*>();
QObject* _qml { nullptr }; if (!result) {
QObject* _qmlParent{ nullptr }; qWarning() << "Unable to find MenuUserData for object " << object;
}; if (auto action = dynamic_cast<QAction*>(object)) {
qWarning() << action->text();
} else if (auto menu = dynamic_cast<QMenu*>(object)) {
qWarning() << menu->title();
}
return nullptr;
}
return result;
}
VrMenu::VrMenu(OffscreenUi* parent) : QObject(parent) { VrMenu::VrMenu(OffscreenUi* parent) : QObject(parent) {
_rootMenu = parent->getRootItem()->findChild<QObject*>("rootMenu"); _rootMenu = parent->getRootItem()->findChild<QObject*>("rootMenu");

View file

@ -18,8 +18,44 @@
#include <QSignalMapper> #include <QSignalMapper>
#include <QAction> #include <QAction>
#include <QMenu> #include <QMenu>
#include <QUuid>
#include "OffscreenUi.h" #include "OffscreenUi.h"
// Binds together a Qt Action or Menu with the QML Menu or MenuItem
//
// TODO On reflection, it may be pointless to use the UUID. Perhaps
// simply creating the bidirectional link pointing to both the widget
// and qml object and inject the pointer into both objects
class MenuUserData : public QObject {
Q_OBJECT
public:
MenuUserData(QAction* action, QObject* qmlObject, QObject* qmlParent);
~MenuUserData();
void updateQmlItemFromAction();
void clear();
const QUuid uuid{ QUuid::createUuid() };
static bool hasData(QAction* object);
static MenuUserData* forObject(QAction* object);
private:
Q_DISABLE_COPY(MenuUserData);
static constexpr const char *USER_DATA{"user_data"};
QMetaObject::Connection _shutdownConnection;
QMetaObject::Connection _changedConnection;
QAction* _action { nullptr };
QObject* _qml { nullptr };
QObject* _qmlParent{ nullptr };
};
// FIXME break up the rendering code (VrMenu) and the code for mirroring a Widget based menu in QML // FIXME break up the rendering code (VrMenu) and the code for mirroring a Widget based menu in QML
class VrMenu : public QObject { class VrMenu : public QObject {
Q_OBJECT Q_OBJECT
@ -37,7 +73,6 @@ protected:
friend class MenuUserData; friend class MenuUserData;
friend class OffscreenUi; friend class OffscreenUi;
const unsigned int _userDataId { QObject::registerUserData() };
}; };
#endif // hifi_VrMenu_h #endif // hifi_VrMenu_h

View file

@ -6488,9 +6488,9 @@
} }
}, },
"follow-redirects": { "follow-redirects": {
"version": "1.12.1", "version": "1.14.7",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
"integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
"dev": true "dev": true
}, },
"for-in": { "for-in": {
@ -10662,9 +10662,9 @@
"dev": true "dev": true
}, },
"shelljs": { "shelljs": {
"version": "0.8.4", "version": "0.8.5",
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
"integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
"dev": true, "dev": true,
"requires": { "requires": {
"glob": "^7.0.0", "glob": "^7.0.0",