Refactor using Android fragments. Complete drawer menu. Add Privacy Policy.

This commit is contained in:
Gabriel Calero 2018-04-23 17:01:34 -03:00
commit 74e38882f7
288 changed files with 6284 additions and 2256 deletions

View file

@ -1,7 +1,7 @@
### Dependencies
- [cmake](https://cmake.org/download/): 3.9
- [Qt](https://www.qt.io/download-open-source): 5.9.1
- [Qt](https://www.qt.io/download-open-source): 5.10.1
- [OpenSSL](https://www.openssl.org/): Use the latest available 1.0 version (**NOT** 1.1) of OpenSSL to avoid security vulnerabilities.
- [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
@ -46,8 +46,8 @@ This can either be entered directly into your shell session before you build or
The path it needs to be set to will depend on where and how Qt5 was installed. e.g.
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.6.2/clang_64/lib/cmake/
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.6.2/lib/cmake
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.10.1/clang_64/lib/cmake/
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.10.1/lib/cmake
export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
#### Generating build files
@ -66,7 +66,7 @@ Any variables that need to be set for CMake to find dependencies can be set as E
For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generation:
cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.6.2/lib/cmake
cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.10.1/lib/cmake
#### Finding Dependencies

View file

@ -14,8 +14,8 @@ Should you choose not to install Qt5 via a package manager that handles dependen
Install qt:
```bash
wget http://debian.highfidelity.com/pool/h/hi/hifi-qt5.6.1_5.6.1_amd64.deb
sudo dpkg -i hifi-qt5.6.1_5.6.1_amd64.deb
wget http://debian.highfidelity.com/pool/h/hi/hifi-qt5.10.1_5.10.1_amd64.deb
sudo dpkg -i hifi-qt5.10.1_5.10.1_amd64.deb
```
Install build dependencies:
@ -66,7 +66,7 @@ cd hifi/build
Prepare makefiles:
```bash
cmake -DQT_CMAKE_PREFIX_PATH=/usr/local/Qt5.6.1/5.6/gcc_64/lib/cmake ..
cmake -DQT_CMAKE_PREFIX_PATH=/usr/local/Qt5.10.1/5.10/gcc_64/lib/cmake ..
```
Start compilation and get a cup of coffee:
@ -74,7 +74,7 @@ Start compilation and get a cup of coffee:
make domain-server assignment-client interface
```
In a server does not make sense to compile interface
In a server does not make sense to compile interface
### Running the software

View file

@ -20,7 +20,7 @@ Note that this uses the version from the homebrew formula at the time of this wr
Assuming you've installed Qt using the homebrew instructions above, you'll need to set QT_CMAKE_PREFIX_PATH so CMake can find your installations.
For Qt installed via homebrew, set QT_CMAKE_PREFIX_PATH:
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt/5.9.1/lib/cmake
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt/5.10.1/lib/cmake
Note that this uses the version from the homebrew formula at the time of this writing, and the version in the path will likely change.

View file

@ -1,13 +1,13 @@
This is a stand-alone guide for creating your first High Fidelity build for Windows 64-bit.
## Building High Fidelity
Note: We are now using Visual Studio 2017 and Qt 5.9.1. If you are upgrading from Visual Studio 2013 and Qt 5.6.2, do a clean uninstall of those versions before going through this guide.
Note: We are now using Visual Studio 2017 and Qt 5.10.1. If you are upgrading from Visual Studio 2013 and Qt 5.6.2, do a clean uninstall of those versions before going through this guide.
Note: The prerequisites will require about 10 GB of space on your drive. You will also need a system with at least 8GB of main memory.
### Step 1. Visual Studio 2017
If you dont have Community or Professional edition of Visual Studio 2017, download [Visual Studio Community 2017](https://www.visualstudio.com/downloads/).
If you dont have Community or Professional edition of Visual Studio 2017, download [Visual Studio Community 2017](https://www.visualstudio.com/downloads/).
When selecting components, check "Desktop development with C++." Also check "Windows 8.1 SDK and UCRT SDK" and "VC++ 2015.3 v140 toolset (x86,x64)" on the Summary toolbar on the right.
@ -17,15 +17,15 @@ Download and install the latest version of CMake 3.9. Download the file named w
### Step 3. Installing Qt
Download and install the [Qt Online Installer](https://www.qt.io/download-open-source/?hsCtaTracking=f977210e-de67-475f-a32b-65cec207fd03%7Cd62710cd-e1db-46aa-8d4d-2f1c1ffdacea). While installing, you only need to have the following components checked under Qt 5.9.1: "msvc2017 64-bit", "Qt WebEngine", and "Qt Script (Deprecated)".
Download and install the [Qt Online Installer](https://www.qt.io/download-open-source/?hsCtaTracking=f977210e-de67-475f-a32b-65cec207fd03%7Cd62710cd-e1db-46aa-8d4d-2f1c1ffdacea). While installing, you only need to have the following components checked under Qt 5.10.1: "msvc2017 64-bit", "Qt WebEngine", and "Qt Script (Deprecated)".
Note: Installing the Sources is optional but recommended if you have room for them (~2GB).
Note: Installing the Sources is optional but recommended if you have room for them (~2GB).
### Step 4. Setting Qt Environment Variable
Go to `Control Panel > System > Advanced System Settings > Environment Variables > New...` (or search “Environment Variables” in Start Search).
* Set "Variable name": `QT_CMAKE_PREFIX_PATH`
* Set "Variable value": `C:\Qt\5.9.1\msvc2017_64\lib\cmake`
* Set "Variable value": `C:\Qt\5.10.1\msvc2017_64\lib\cmake`
### Step 5. Installing [vcpkg](https://github.com/Microsoft/vcpkg)
@ -39,7 +39,7 @@ Go to `Control Panel > System > Advanced System Settings > Environment Variables
* In the vcpkg directory, install the 64 bit OpenSSL package with the command `vcpkg install openssl:x64-windows`
* Once the build completes you should have a file `ssl.h` in `${VCPKG_ROOT}/installed/x64-windows/include/openssl`
### Step 7. Running CMake to Generate Build Files
Run Command Prompt from Start and run the following commands:
@ -49,7 +49,7 @@ mkdir build
cd build
cmake .. -G "Visual Studio 15 Win64"
```
Where `%HIFI_DIR%` is the directory for the highfidelity repository.
### Step 8. Making a Build
@ -74,10 +74,10 @@ Note: You can also run Interface by launching it from command line or File Explo
## Troubleshooting
For any problems after Step #7, first try this:
For any problems after Step #7, first try this:
* Delete your locally cloned copy of the highfidelity repository
* Restart your computer
* Redownload the [repository](https://github.com/highfidelity/hifi)
* Redownload the [repository](https://github.com/highfidelity/hifi)
* Restart directions from Step #7
#### CMake gives you the same error message repeatedly after the build fails
@ -90,4 +90,4 @@ Remove `CMakeCache.txt` found in the `%HIFI_DIR%\build` directory. Verify that
#### Qt is throwing an error
Make sure you have the correct version (5.9.1) installed and `QT_CMAKE_PREFIX_PATH` environment variable is set correctly.
Make sure you have the correct version (5.10.1) installed and `QT_CMAKE_PREFIX_PATH` environment variable is set correctly.

View file

@ -105,6 +105,10 @@ dependencies {
implementation 'com.android.support:appcompat-v7:26.1.0'
compile 'com.android.support:recyclerview-v7:26.1.0'
compile 'com.squareup.retrofit2:retrofit:2.4.0'
compile 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation fileTree(include: ['*.jar'], dir: 'libs')
}

View file

@ -39,17 +39,10 @@
</activity>
-->
<activity
android:name=".HomeActivity"
android:label="@string/home"
android:name=".MainActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.NoActionBar">
android:theme="@style/AppTheme">
</activity>
<activity
android:name=".GotoActivity"
android:label="@string/go_to"
android:theme="@style/AppTheme" />
<activity android:name=".LoginActivity"
android:theme="@style/AppTheme"/>
<activity
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation"
android:name=".InterfaceActivity"

View file

@ -1,7 +1,7 @@
{
"hifi_domains" : [
{
"name": "You last location",
"name": "Your last location",
"url": "",
"thumbnail": ""
},

View file

@ -23,9 +23,9 @@
#include "AndroidHelper.h"
QAndroidJniObject __interfaceActivity;
QAndroidJniObject __loginActivity;
QAndroidJniObject __loginCompletedListener;
QAndroidJniObject __loadCompleteListener;
QAndroidJniObject __usernameChangedListener;
void tempMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
if (!message.isEmpty()) {
const char * local=message.toStdString().c_str();
@ -204,8 +204,9 @@ JNIEXPORT jstring JNICALL Java_io_highfidelity_hifiinterface_HifiUtils_getCurren
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_LoginActivity_nativeLogin(JNIEnv *env, jobject instance,
jstring username_, jstring password_) {
Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *env, jobject instance,
jstring username_, jstring password_,
jobject usernameChangedListener) {
const char *c_username = env->GetStringUTFChars(username_, 0);
const char *c_password = env->GetStringUTFChars(password_, 0);
QString username = QString(c_username);
@ -215,9 +216,11 @@ Java_io_highfidelity_hifiinterface_LoginActivity_nativeLogin(JNIEnv *env, jobjec
QSharedPointer<AccountManager> accountManager = AndroidHelper::instance().getAccountManager();
__loginActivity = QAndroidJniObject(instance);
__loginCompletedListener = QAndroidJniObject(instance);
__usernameChangedListener = QAndroidJniObject(usernameChangedListener);
QObject::connect(accountManager.data(), &AccountManager::loginComplete, [](const QUrl& authURL) {
QString username = AndroidHelper::instance().getAccountManager()->getAccountInfo().getUsername();
AndroidHelper::instance().notifyLoginComplete(true);
});
@ -225,12 +228,18 @@ Java_io_highfidelity_hifiinterface_LoginActivity_nativeLogin(JNIEnv *env, jobjec
AndroidHelper::instance().notifyLoginComplete(false);
});
QObject::connect(&AndroidHelper::instance(), &AndroidHelper::loginComplete, [](bool success) {
jboolean jSuccess = (jboolean) success;
__loginActivity.callMethod<void>("handleLoginCompleted", "(Z)V", jSuccess);
QObject::connect(accountManager.data(), &AccountManager::usernameChanged, [](const QString& username) {
QAndroidJniObject string = QAndroidJniObject::fromString(username);
__usernameChangedListener.callMethod<void>("handleUsernameChanged", "(Ljava/lang/String;)V", string.object<jstring>());
});
QMetaObject::invokeMethod(accountManager.data(), "requestAccessToken", Q_ARG(const QString&, username), Q_ARG(const QString&, password));
QObject::connect(&AndroidHelper::instance(), &AndroidHelper::loginComplete, [](bool success) {
jboolean jSuccess = (jboolean) success;
__loginCompletedListener.callMethod<void>("handleLoginCompleted", "(Z)V", jSuccess);
});
QMetaObject::invokeMethod(accountManager.data(), "requestAccessToken",
Q_ARG(const QString&, username), Q_ARG(const QString&, password));
}
JNIEXPORT void JNICALL
@ -250,20 +259,20 @@ Java_io_highfidelity_hifiinterface_SplashActivity_registerLoadCompleteListener(J
}
JNIEXPORT jboolean JNICALL
Java_io_highfidelity_hifiinterface_HomeActivity_nativeIsLoggedIn(JNIEnv *env, jobject instance) {
Java_io_highfidelity_hifiinterface_MainActivity_nativeIsLoggedIn(JNIEnv *env, jobject instance) {
return AndroidHelper::instance().getAccountManager()->isLoggedIn();
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_HomeActivity_nativeLogout(JNIEnv *env, jobject instance) {
Java_io_highfidelity_hifiinterface_MainActivity_nativeLogout(JNIEnv *env, jobject instance) {
AndroidHelper::instance().getAccountManager()->logout();
}
JNIEXPORT jstring JNICALL
Java_io_highfidelity_hifiinterface_HomeActivity_nativeGetDisplayName(JNIEnv *env,
Java_io_highfidelity_hifiinterface_MainActivity_nativeGetDisplayName(JNIEnv *env,
jobject instance) {
QString username = AndroidHelper::instance().getAccountManager()->getAccountInfo().getUsername();
return env->NewStringUTF(username.toLatin1().data());
}
}
}

View file

@ -1,93 +0,0 @@
package io.highfidelity.hifiinterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.AppCompatButton;
import android.support.v7.widget.Toolbar;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import java.net.URI;
import java.net.URISyntaxException;
public class GotoActivity extends AppCompatActivity {
public static final String PARAM_DOMAIN_URL = "domain_url";
private EditText mUrlEditText;
private AppCompatButton mGoBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_goto);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitleTextAppearance(this, R.style.HomeActionBarTitleStyle);
setSupportActionBar(toolbar);
ActionBar actionbar = getSupportActionBar();
actionbar.setDisplayHomeAsUpEnabled(true);
mUrlEditText = (EditText) findViewById(R.id.url_text);
mUrlEditText.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View view, int i, KeyEvent keyEvent) {
if (i == KeyEvent.KEYCODE_ENTER) {
actionGo();
return true;
}
return false;
}
});
mUrlEditText.setText(HifiUtils.getInstance().getCurrentAddress());
mGoBtn = (AppCompatButton) findViewById(R.id.go_btn);
mGoBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
actionGo();
}
});
}
private void actionGo() {
String urlString = mUrlEditText.getText().toString();
if (!urlString.trim().isEmpty()) {
URI uri;
try {
uri = new URI(urlString);
} catch (URISyntaxException e) {
return;
}
if (uri.getScheme()==null || uri.getScheme().isEmpty()) {
urlString = "hifi://" + urlString;
}
Intent intent = new Intent();
intent.putExtra(GotoActivity.PARAM_DOMAIN_URL, urlString);
setResult(RESULT_OK, intent);
finish();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
switch (id) {
case android.R.id.home:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -1,195 +0,0 @@
package io.highfidelity.hifiinterface;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.SearchView;
import android.widget.TabHost;
import android.widget.TabWidget;
import android.widget.TextView;
import io.highfidelity.hifiinterface.view.DomainAdapter;
public class HomeActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
public native boolean nativeIsLoggedIn();
public native void nativeLogout();
public native String nativeGetDisplayName();
/**
* Set this intent extra param to NOT start a new InterfaceActivity after a domain is selected"
*/
//public static final String PARAM_NOT_START_INTERFACE_ACTIVITY = "not_start_interface_activity";
public static final int ENTER_DOMAIN_URL = 1;
private DomainAdapter domainAdapter;
private DrawerLayout mDrawerLayout;
private NavigationView mNavigationView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitleTextAppearance(this, R.style.HomeActionBarTitleStyle);
setSupportActionBar(toolbar);
ActionBar actionbar = getSupportActionBar();
actionbar.setDisplayHomeAsUpEnabled(true);
actionbar.setHomeAsUpIndicator(R.drawable.ic_menu);
mDrawerLayout = findViewById(R.id.drawer_layout);
mNavigationView = (NavigationView) findViewById(R.id.nav_view);
mNavigationView.setNavigationItemSelectedListener(this);
TabHost tabs=(TabHost)findViewById(R.id.tabhost);
tabs.setup();
TabHost.TabSpec spec=tabs.newTabSpec("featured");
spec.setContent(R.id.featured);
spec.setIndicator(getString(R.string.featured));
tabs.addTab(spec);
spec=tabs.newTabSpec("popular");
spec.setContent(R.id.popular);
spec.setIndicator(getString(R.string.popular));
tabs.addTab(spec);
spec=tabs.newTabSpec("bookmarks");
spec.setContent(R.id.bookmarks);
spec.setIndicator(getString(R.string.bookmarks));
tabs.addTab(spec);
tabs.setCurrentTab(0);
TabWidget tabwidget=tabs.getTabWidget();
for(int i=0;i<tabwidget.getChildCount();i++){
TextView tv=(TextView) tabwidget.getChildAt(i).findViewById(android.R.id.title);
tv.setTextAppearance(R.style.TabText);
}
RecyclerView domainsView = findViewById(R.id.rvDomains);
int numberOfColumns = 1;
GridLayoutManager gridLayoutMgr = new GridLayoutManager(this, numberOfColumns);
domainsView.setLayoutManager(gridLayoutMgr);
domainAdapter = new DomainAdapter(this);
domainAdapter.setClickListener(new DomainAdapter.ItemClickListener() {
@Override
public void onItemClick(View view, int position, DomainAdapter.Domain domain) {
gotoDomain(domain.url);
}
});
domainsView.setAdapter(domainAdapter);
SearchView searchView = findViewById(R.id.searchView);
int searchPlateId = searchView.getContext().getResources().getIdentifier("android:id/search_plate", null, null);
View searchPlate = searchView.findViewById(searchPlateId);
if (searchPlate!=null) {
searchPlate.setBackgroundColor (Color.TRANSPARENT);
int searchTextId = searchPlate.getContext ().getResources ().getIdentifier ("android:id/search_src_text", null, null);
TextView searchTextView = searchView.findViewById(searchTextId);
searchTextView.setTextAppearance(R.style.SearchText);
}
updateLoginMenu();
}
private void updateLoginMenu() {
TextView loginOption = findViewById(R.id.login);
TextView logoutOption = findViewById(R.id.logout);
if (nativeIsLoggedIn()) {
loginOption.setVisibility(View.GONE);
logoutOption.setVisibility(View.VISIBLE);
} else {
loginOption.setVisibility(View.VISIBLE);
logoutOption.setVisibility(View.GONE);
}
}
private void gotoDomain(String domainUrl) {
Intent intent = new Intent(HomeActivity.this, InterfaceActivity.class);
intent.putExtra(InterfaceActivity.DOMAIN_URL, domainUrl);
HomeActivity.this.finish();
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
//getMenuInflater().inflate(R.menu.menu_home, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
switch (id) {
case android.R.id.home:
mDrawerLayout.openDrawer(GravityCompat.START);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch(item.getItemId()) {
case R.id.action_goto:
Intent i = new Intent(this, GotoActivity.class);
startActivityForResult(i, ENTER_DOMAIN_URL);
return true;
}
return false;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ENTER_DOMAIN_URL && resultCode == RESULT_OK) {
gotoDomain(data.getStringExtra(GotoActivity.PARAM_DOMAIN_URL));
}
}
@Override
protected void onStart() {
super.onStart();
updateLoginMenu();
}
public void onLoginClicked(View view) {
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
}
public void onLogoutClicked(View view) {
nativeLogout();
updateLoginMenu();
}
}

View file

@ -66,7 +66,7 @@ public class InterfaceActivity extends QtActivity {
super.isLoading = true;
Intent intent = getIntent();
if (intent.hasExtra(DOMAIN_URL) && !intent.getStringExtra(DOMAIN_URL).isEmpty()) {
intent.putExtra("applicationArguments", "--url "+intent.getStringExtra(DOMAIN_URL));
intent.putExtra("applicationArguments", "--url " + intent.getStringExtra(DOMAIN_URL));
}
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
@ -203,7 +203,7 @@ public class InterfaceActivity extends QtActivity {
public void openGotoActivity(String activityName) {
switch (activityName) {
case "Goto": {
Intent intent = new Intent(this, HomeActivity.class);
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
break;
}

View file

@ -1,106 +0,0 @@
package io.highfidelity.hifiinterface;
import android.app.ProgressDialog;
import android.support.annotation.MainThread;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class LoginActivity extends AppCompatActivity {
public native void nativeLogin(String username, String password);
private EditText mUsername;
private EditText mPassword;
private TextView mError;
private Button mLoginButton;
private ProgressDialog mDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mUsername = findViewById(R.id.username);
mPassword = findViewById(R.id.password);
mError = findViewById(R.id.error);
mLoginButton = findViewById(R.id.loginButton);
mPassword.setOnEditorActionListener(
new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
mLoginButton.performClick();
return true;
}
return false;
}
});
}
@Override
protected void onStop() {
super.onStop();
cancelActivityIndicator();
}
public void login(View view) {
String username = mUsername.getText().toString();
String password = mPassword.getText().toString();
if (username.isEmpty() || password.isEmpty()) {
showError(getString(R.string.login_username_or_password_incorrect));
} else {
mLoginButton.setEnabled(false);
hideError();
showActivityIndicator();
nativeLogin(username, password);
}
}
private void showActivityIndicator() {
if (mDialog == null) {
mDialog = new ProgressDialog(this);
}
mDialog.setMessage(getString(R.string.logging_in));
mDialog.setCancelable(false);
mDialog.show();
}
private void cancelActivityIndicator() {
if (mDialog != null) {
mDialog.cancel();
}
}
private void showError(String error) {
mError.setText(error);
mError.setVisibility(View.VISIBLE);
}
private void hideError() {
mError.setText("");
mError.setVisibility(View.INVISIBLE);
}
public void handleLoginCompleted(boolean success) {
runOnUiThread(() -> {
mLoginButton.setEnabled(true);
cancelActivityIndicator();
if (success) {
finish();
} else {
showError(getString(R.string.login_username_or_password_incorrect));
}
});
}
}

View file

@ -0,0 +1,289 @@
package io.highfidelity.hifiinterface;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.squareup.picasso.Callback;
import com.squareup.picasso.Picasso;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import io.highfidelity.hifiinterface.fragment.GotoFragment;
import io.highfidelity.hifiinterface.fragment.HomeFragment;
import io.highfidelity.hifiinterface.fragment.LoginFragment;
import io.highfidelity.hifiinterface.fragment.PolicyFragment;
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener,
LoginFragment.OnLoginInteractionListener,
HomeFragment.OnHomeInteractionListener,
GotoFragment.OnGotoInteractionListener {
private static final int PROFILE_PICTURE_PLACEHOLDER = R.drawable.ic_person;
private String TAG = "HighFidelity";
public native boolean nativeIsLoggedIn();
public native void nativeLogout();
public native String nativeGetDisplayName();
private DrawerLayout mDrawerLayout;
private NavigationView mNavigationView;
private ImageView mProfilePicture;
private TextView mDisplayName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNavigationView = findViewById(R.id.nav_view);
mNavigationView.setNavigationItemSelectedListener(this);
mDisplayName = mNavigationView.getHeaderView(0).findViewById(R.id.displayName);
mProfilePicture = mNavigationView.getHeaderView(0).findViewById(R.id.profilePicture);
Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.setTitleTextAppearance(this, R.style.HomeActionBarTitleStyle);
setSupportActionBar(toolbar);
ActionBar actionbar = getSupportActionBar();
actionbar.setDisplayHomeAsUpEnabled(true);
actionbar.setHomeAsUpIndicator(R.drawable.ic_menu);
mDrawerLayout = findViewById(R.id.drawer_layout);
loadHomeFragment();
}
private void loadHomeFragment() {
Fragment fragment = HomeFragment.newInstance();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.commit();
setTitle(getString(R.string.home));
mDrawerLayout.closeDrawer(mNavigationView);
}
private void loadLoginFragment() {
Fragment fragment = LoginFragment.newInstance();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.replace(R.id.content_frame, fragment).addToBackStack(null);
ft.commit();
// update selected item title, then close the drawer
setTitle(getString(R.string.login));
mDrawerLayout.closeDrawer(mNavigationView);
}
private void loadGotoFragment() {
Fragment fragment = GotoFragment.newInstance();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.replace(R.id.content_frame, fragment).addToBackStack(null);
ft.commit();
// update selected item title, then close the drawer
setTitle(getString(R.string.go_to));
mDrawerLayout.closeDrawer(mNavigationView);
}
private void loadPrivacyPolicyFragment() {
Fragment fragment = PolicyFragment.newInstance();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.replace(R.id.content_frame, fragment).addToBackStack(null);
ft.commit();
// update selected item title, then close the drawer
setTitle(getString(R.string.privacyPolicy));
mDrawerLayout.closeDrawer(mNavigationView);
}
private void updateLoginMenu() {
TextView loginOption = findViewById(R.id.login);
TextView logoutOption = findViewById(R.id.logout);
if (nativeIsLoggedIn()) {
loginOption.setVisibility(View.GONE);
logoutOption.setVisibility(View.VISIBLE);
updateProfileHeader();
} else {
loginOption.setVisibility(View.VISIBLE);
logoutOption.setVisibility(View.GONE);
mDisplayName.setText("");
mNavigationView.getHeaderView(0).setVisibility(View.INVISIBLE);
}
}
private void updateProfileHeader() {
updateProfileHeader(nativeGetDisplayName());
}
private void updateProfileHeader(String username) {
if (!username.isEmpty()) {
mDisplayName.setText(username);
mNavigationView.getHeaderView(0).setVisibility(View.VISIBLE);
updateProfilePicture(username);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
//getMenuInflater().inflate(R.menu.menu_navigation, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
switch (id) {
case android.R.id.home:
mDrawerLayout.openDrawer(GravityCompat.START);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch(item.getItemId()) {
case R.id.action_home:
loadHomeFragment();
return true;
case R.id.action_goto:
loadGotoFragment();
return true;
}
return false;
}
@Override
protected void onStart() {
super.onStart();
updateLoginMenu();
}
public void onLoginClicked(View view) {
loadLoginFragment();
}
public void onLogoutClicked(View view) {
nativeLogout();
updateLoginMenu();
}
public void onEnteredDomain(String domainUrl) {
goToDomain(domainUrl);
}
public void onSelectedDomain(String domainUrl) {
goToDomain(domainUrl);
}
private void goToDomain(String domainUrl) {
Intent intent = new Intent(this, InterfaceActivity.class);
intent.putExtra(InterfaceActivity.DOMAIN_URL, domainUrl);
finish();
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
}
@Override
public void onLoginCompleted() {
loadHomeFragment();
updateLoginMenu();
}
public void handleUsernameChanged(String username) {
runOnUiThread(() -> updateProfileHeader(username));
}
/**
* This is a temporary way to get the profile picture URL
* TODO: this should be get from an API (at the moment there is no one for this)
*/
private void updateProfilePicture(String username) {
Picasso.get().load(PROFILE_PICTURE_PLACEHOLDER).into(mProfilePicture);
new Thread(() -> {
try {
URL userPage = new URL("https://highfidelity.com/users/" + username);
BufferedReader in = new BufferedReader(
new InputStreamReader(
userPage.openStream()));
StringBuffer strBuff = new StringBuffer();
String inputLine;
while ((inputLine = in.readLine()) != null) {
strBuff.append(inputLine);
}
in.close();
String substr = "img class=\"users-img\" src=\"";
int indexBegin = strBuff.indexOf(substr) + substr.length();
if (indexBegin >= substr.length()) {
int indexEnd = strBuff.indexOf("\"", indexBegin);
runOnUiThread(() -> {
if (indexEnd > 0) {
Picasso.get().load(strBuff.substring(indexBegin, indexEnd)).into(mProfilePicture, new RoundProfilePictureCallback());
}
});
}
} catch (IOException e) {
Log.e(TAG, "Error getting profile picture for username " + username);
}
}).start();
}
public void onPrivacyPolicyClicked(View view) {
loadPrivacyPolicyFragment();
}
private class RoundProfilePictureCallback implements Callback {
@Override
public void onSuccess() {
Bitmap imageBitmap = ((BitmapDrawable) mProfilePicture.getDrawable()).getBitmap();
RoundedBitmapDrawable imageDrawable = RoundedBitmapDrawableFactory.create(getResources(), imageBitmap);
imageDrawable.setCircular(true);
imageDrawable.setCornerRadius(Math.max(imageBitmap.getWidth(), imageBitmap.getHeight()) / 2.0f);
mProfilePicture.setImageDrawable(imageDrawable);
}
@Override
public void onError(Exception e) {
mProfilePicture.setImageResource(PROFILE_PICTURE_PLACEHOLDER);
}
}
}

View file

@ -26,7 +26,7 @@ public class SplashActivity extends Activity {
}
public void onAppLoadedComplete() {
startActivity(new Intent(this, HomeActivity.class));
startActivity(new Intent(this, MainActivity.class));
finish();
}
}

View file

@ -0,0 +1,96 @@
package io.highfidelity.hifiinterface.fragment;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.widget.AppCompatButton;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import java.net.URI;
import java.net.URISyntaxException;
import io.highfidelity.hifiinterface.HifiUtils;
import io.highfidelity.hifiinterface.R;
public class GotoFragment extends Fragment {
private EditText mUrlEditText;
private AppCompatButton mGoBtn;
private OnGotoInteractionListener mListener;
public GotoFragment() {
// Required empty public constructor
}
public static GotoFragment newInstance() {
GotoFragment fragment = new GotoFragment();
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_goto, container, false);
mUrlEditText = rootView.findViewById(R.id.url_text);
mUrlEditText.setOnKeyListener((view, i, keyEvent) -> {
if (i == KeyEvent.KEYCODE_ENTER) {
actionGo();
return true;
}
return false;
});
mUrlEditText.setText(HifiUtils.getInstance().getCurrentAddress());
mGoBtn = rootView.findViewById(R.id.go_btn);
mGoBtn.setOnClickListener(view -> actionGo());
return rootView;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnGotoInteractionListener) {
mListener = (OnGotoInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnGotoInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
private void actionGo() {
String urlString = mUrlEditText.getText().toString();
if (!urlString.trim().isEmpty()) {
URI uri;
try {
uri = new URI(urlString);
} catch (URISyntaxException e) {
return;
}
if (uri.getScheme() == null || uri.getScheme().isEmpty()) {
urlString = "hifi://" + urlString;
}
mListener.onEnteredDomain(urlString);
}
}
public interface OnGotoInteractionListener {
void onEnteredDomain(String domainUrl);
}
}

View file

@ -0,0 +1,117 @@
package io.highfidelity.hifiinterface.fragment;
import android.app.Fragment;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SearchView;
import android.widget.TabHost;
import android.widget.TabWidget;
import android.widget.TextView;
import io.highfidelity.hifiinterface.R;
import io.highfidelity.hifiinterface.view.DomainAdapter;
public class HomeFragment extends Fragment {
public static final int ENTER_DOMAIN_URL = 1;
private DomainAdapter domainAdapter;
private OnHomeInteractionListener mListener;
public HomeFragment() {
// Required empty public constructor
}
public static HomeFragment newInstance() {
HomeFragment fragment = new HomeFragment();
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
TabHost tabs = rootView.findViewById(R.id.tabhost);
tabs.setup();
TabHost.TabSpec spec=tabs.newTabSpec("featured");
spec.setContent(R.id.featured);
spec.setIndicator(getString(R.string.featured));
tabs.addTab(spec);
spec = tabs.newTabSpec("popular");
spec.setContent(R.id.popular);
spec.setIndicator(getString(R.string.popular));
tabs.addTab(spec);
spec = tabs.newTabSpec("bookmarks");
spec.setContent(R.id.bookmarks);
spec.setIndicator(getString(R.string.bookmarks));
tabs.addTab(spec);
tabs.setCurrentTab(0);
TabWidget tabwidget=tabs.getTabWidget();
for(int i=0; i<tabwidget.getChildCount(); i++){
TextView tv= tabwidget.getChildAt(i).findViewById(android.R.id.title);
tv.setTextAppearance(R.style.TabText);
}
RecyclerView domainsView = rootView.findViewById(R.id.rvDomains);
int numberOfColumns = 1;
GridLayoutManager gridLayoutMgr = new GridLayoutManager(getContext(), numberOfColumns);
domainsView.setLayoutManager(gridLayoutMgr);
domainAdapter = new DomainAdapter(getContext());
domainAdapter.setClickListener((view, position, domain) -> mListener.onSelectedDomain(domain.url));
domainsView.setAdapter(domainAdapter);
SearchView searchView = rootView.findViewById(R.id.searchView);
int searchPlateId = searchView.getContext().getResources().getIdentifier("android:id/search_plate", null, null);
View searchPlate = searchView.findViewById(searchPlateId);
if (searchPlate != null) {
searchPlate.setBackgroundColor (Color.TRANSPARENT);
int searchTextId = searchPlate.getContext ().getResources ().getIdentifier ("android:id/search_src_text", null, null);
TextView searchTextView = searchView.findViewById(searchTextId);
searchTextView.setTextAppearance(R.style.SearchText);
}
return rootView;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnHomeInteractionListener) {
mListener = (OnHomeInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnHomeInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public interface OnHomeInteractionListener {
void onSelectedDomain(String domainUrl);
}
}

View file

@ -0,0 +1,165 @@
package io.highfidelity.hifiinterface.fragment;
import android.app.Activity;
import android.app.Fragment;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import io.highfidelity.hifiinterface.R;
public class LoginFragment extends Fragment {
private EditText mUsername;
private EditText mPassword;
private TextView mError;
private TextView mForgotPassword;
private Button mLoginButton;
private ProgressDialog mDialog;
public native void nativeLogin(String username, String password, Activity usernameChangedListener);
private LoginFragment.OnLoginInteractionListener mListener;
public LoginFragment() {
// Required empty public constructor
}
public static LoginFragment newInstance() {
LoginFragment fragment = new LoginFragment();
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_login, container, false);
mUsername = rootView.findViewById(R.id.username);
mPassword = rootView.findViewById(R.id.password);
mError = rootView.findViewById(R.id.error);
mLoginButton = rootView.findViewById(R.id.loginButton);
mForgotPassword = rootView.findViewById(R.id.forgotPassword);
mLoginButton.setOnClickListener(view -> login());
mForgotPassword.setOnClickListener(view -> forgotPassword());
mPassword.setOnEditorActionListener(
(textView, actionId, keyEvent) -> {
if (actionId == EditorInfo.IME_ACTION_DONE) {
mLoginButton.performClick();
return true;
}
return false;
});
return rootView;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnLoginInteractionListener) {
mListener = (OnLoginInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnLoginInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
@Override
public void onStop() {
super.onStop();
cancelActivityIndicator();
hideKeyboard();
}
public void login() {
String username = mUsername.getText().toString();
String password = mPassword.getText().toString();
hideKeyboard();
if (username.isEmpty() || password.isEmpty()) {
showError(getString(R.string.login_username_or_password_incorrect));
} else {
mLoginButton.setEnabled(false);
hideError();
showActivityIndicator();
nativeLogin(username, password, getActivity());
}
}
private void hideKeyboard() {
View view = getActivity().getCurrentFocus();
if (view != null) {
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
private void forgotPassword() {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://highfidelity.com/users/password/new"));
startActivity(intent);
}
private void showActivityIndicator() {
if (mDialog == null) {
mDialog = new ProgressDialog(getContext());
}
mDialog.setMessage(getString(R.string.logging_in));
mDialog.setCancelable(false);
mDialog.show();
}
private void cancelActivityIndicator() {
if (mDialog != null) {
mDialog.cancel();
}
}
private void showError(String error) {
mError.setText(error);
mError.setVisibility(View.VISIBLE);
}
private void hideError() {
mError.setText("");
mError.setVisibility(View.INVISIBLE);
}
public void handleLoginCompleted(boolean success) {
Log.d("[LOGIN]", "handleLoginCompleted " + success);
getActivity().runOnUiThread(() -> {
mLoginButton.setEnabled(true);
cancelActivityIndicator();
if (success) {
if (mListener != null) {
mListener.onLoginCompleted();
}
} else {
showError(getString(R.string.login_username_or_password_incorrect));
}
});
}
public interface OnLoginInteractionListener {
void onLoginCompleted();
}
}

View file

@ -0,0 +1,60 @@
package io.highfidelity.hifiinterface.fragment;
import android.app.Fragment;
import android.os.Bundle;
import android.text.Html;
import android.text.Spanned;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.io.IOException;
import java.io.InputStream;
import io.highfidelity.hifiinterface.R;
public class PolicyFragment extends Fragment {
private static final String POLICY_FILE = "privacy_policy.html";
public PolicyFragment() {
// Required empty public constructor
}
public static PolicyFragment newInstance() {
PolicyFragment fragment = new PolicyFragment();
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_policy, container, false);
TextView policy = rootView.findViewById(R.id.policyText);
Spanned myStringSpanned = null;
try {
myStringSpanned = Html.fromHtml(loadHTMLFromAsset(), null, null);
policy.setText(myStringSpanned, TextView.BufferType.SPANNABLE);
} catch (IOException e) {
policy.setText("N/A");
}
return rootView;
}
public String loadHTMLFromAsset() throws IOException {
String html = null;
InputStream is = getContext().getAssets().open(POLICY_FILE);
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
html = new String(buffer, "UTF-8");
return html;
}
}

View file

@ -7,13 +7,11 @@
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay"
>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
@ -21,8 +19,10 @@
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
<include layout="@layout/content_home" />
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.design.widget.AppBarLayout>
<!-- Container for contents of drawer - use NavigationView to make configuration easier -->
@ -31,9 +31,9 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/navigation_header"
android:fitsSystemWindows="true"
app:menu="@menu/menu_home"
>
app:menu="@menu/menu_navigation">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -47,14 +47,27 @@
android:onClick="onLoginClicked"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/menuOption" />
android:textColor="@color/menuOption"
android:paddingTop="15dp"
android:paddingBottom="15dp"/>
<TextView
android:id="@+id/logout"
android:text="@string/logout"
android:onClick="onLogoutClicked"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/menuOption" />
android:textColor="@color/menuOption"
android:paddingTop="15dp"
android:paddingBottom="15dp"/>
<TextView
android:id="@+id/policy"
android:text="@string/privacyPolicy"
android:onClick="onPrivacyPolicyClicked"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/menuOption"
android:paddingTop="15dp"
android:paddingBottom="30dp"/>
</LinearLayout>
</android.support.design.widget.NavigationView>

View file

@ -19,6 +19,9 @@
android:id="@+id/url_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/HifiEditText"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:hint="@string/goto_url_hint"
android:inputType="textUri"
android:imeOptions="actionGo"
@ -28,7 +31,9 @@
android:id="@+id/go_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/button_horizontal_margin"
android:text="@string/go"
app:layout_constraintTop_toBottomOf="@id/url_text"/>
app:layout_constraintTop_toBottomOf="@id/url_text"
app:layout_constraintEnd_toEndOf="@id/root_activity_goto"/>
</android.support.constraint.ConstraintLayout>

View file

@ -5,8 +5,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="io.highfidelity.hifiinterface.HomeActivity"
tools:showIn="@layout/activity_home">
tools:context="io.highfidelity.hifiinterface.MainActivity"
tools:showIn="@layout/activity_main">
<TabHost
android:id="@+id/tabhost"
@ -35,7 +35,7 @@
android:background="@drawable/search_bg"
android:gravity="right"
android:layout_gravity="right"
/>
<FrameLayout
android:id="@android:id/tabcontent"

View file

@ -4,19 +4,17 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/backgroundLight"
tools:context="io.highfidelity.hifiinterface.LoginActivity">
android:background="@color/backgroundLight">
<ImageView
android:id="@+id/imageView3"
android:id="@+id/header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/hifi_header"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toTopOf="@id/username"
android:layout_marginBottom="75dp"
/>
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="35dp"/>
<TextView
android:id="@+id/error"
@ -46,8 +44,8 @@
android:textColor="@color/editTextColor"
android:textColorHint="@color/editTextColor"
android:gravity="right"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/header"
android:layout_marginTop="35dp"
android:hint="@string/username_or_email" />
<EditText
@ -84,11 +82,10 @@
android:paddingLeft="30dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:layout_marginTop="16dp"
android:onClick="login"/>
android:layout_marginTop="16dp"/>
<TextView
android:id="@+id/textView"
android:id="@+id/forgotPassword"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_horizontal_margin"

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="io.highfidelity.hifiinterface.MainActivity"
tools:showIn="@layout/activity_main">
<ImageView
android:id="@+id/header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/hifi_header"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="35dp"
/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:layout_marginTop="170dp"
app:layout_constraintTop_toBottomOf="@id/header"
app:layout_constraintBottom_toBottomOf="parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
>
<TextView
android:id="@+id/policyText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/raleway"
android:textSize="14sp" />
</ScrollView>
</android.support.constraint.ConstraintLayout>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="144dp"
android:minHeight="144dp">
<ImageView
android:id="@+id/profilePicture"
android:layout_width="64dp"
android:layout_height="64dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginTop="40dp" />
<TextView
android:id="@+id/displayName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/raleway_semibold"
android:text=""
android:textSize="18sp"
app:layout_constraintLeft_toRightOf="@id/profilePicture"
android:layout_marginLeft="22dp"
app:layout_constraintTop_toTopOf="@id/profilePicture"
app:layout_constraintBottom_toBottomOf="@id/profilePicture"/>
</android.support.constraint.ConstraintLayout>

View file

@ -1,15 +0,0 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="io.highfidelity.hifiinterface.HomeActivity">
<item
android:id="@+id/action_goto"
android:orderInCategory="90"
android:title="@string/action_goto"
app:showAsAction="never" />
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="@string/action_settings"
app:showAsAction="never" />
</menu>

View file

@ -0,0 +1,12 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="io.highfidelity.hifiinterface.MainActivity">
<item
android:id="@+id/action_home"
android:title="@string/home"
/>
<item
android:id="@+id/action_goto"
android:title="@string/action_goto"
/>
</menu>

View file

@ -11,6 +11,8 @@
<color name="backgroundDark">#333333</color>
<color name="backgroundLight">#4F4F4F</color>
<color name="backgroundSearch">#33999999</color>
<color name="editText">#212121</color>
<color name="editTextHint">#9e9e9e</color>
<color name="menuOption">#F2F2F2</color>
<color name="colorLoginError">#FF7171</color>
</resources>

View file

@ -9,4 +9,6 @@
<dimen name="text_size_title_material_toolbar">14dp</dimen>
<!-- Default text size for action bar subtitle.-->
<dimen name="text_size_subtitle_material_toolbar">12dp</dimen>
<dimen name="button_horizontal_margin">12dp</dimen>
<dimen name="edit_text_padding">8dp</dimen>
</resources>

View file

@ -9,7 +9,6 @@
<string name="featured">FEATURED</string>
<string name="popular">POPULAR</string>
<string name="bookmarks">BOOKMARKS</string>
<string name="action_settings">Settings</string>
<string name="action_goto">Go To</string>
<string name="goto_url_hint">Type a domain url</string>
<string name="go">Go</string>
@ -20,5 +19,6 @@
<string name="forgot_password">Forgot password?</string>
<string name="login_username_or_password_incorrect">Username or password incorrect.</string>
<string name="logging_in">Logging into High Fidelity</string>
<string name="privacyPolicy">Privacy Policy</string>
</resources>

View file

@ -55,5 +55,11 @@
<item name="android:textSize">@dimen/text_size_subtitle_material_toolbar</item>
</style>
<style name="HifiEditText" parent="@android:style/Widget.Material.EditText">
<item name="android:padding">@dimen/edit_text_padding</item>
<item name="android:textColor">@color/editText</item>
<item name="android:textColorHint">@color/editTextHint</item>
<item name="android:background">@color/white_opaque</item>
</style>
</resources>

View file

@ -322,8 +322,16 @@ void AudioMixerSlave::addStream(AudioMixerClientData& listenerNodeData, const QU
// stereo sources are not passed through HRTF
if (streamToAdd.isStereo()) {
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; ++i) {
_mixSamples[i] += float(streamPopOutput[i] * gain / AudioConstants::MAX_SAMPLE_VALUE);
// apply the avatar gain adjustment
auto& hrtf = listenerNodeData.hrtfForStream(sourceNodeID, streamToAdd.getStreamIdentifier());
gain *= hrtf.getGainAdjustment();
const float scale = 1/32768.0f; // int16_t to float
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; i++) {
_mixSamples[2*i+0] += (float)streamPopOutput[2*i+0] * gain * scale;
_mixSamples[2*i+1] += (float)streamPopOutput[2*i+1] * gain * scale;
}
++stats.manualStereoMixes;
@ -332,10 +340,13 @@ void AudioMixerSlave::addStream(AudioMixerClientData& listenerNodeData, const QU
// echo sources are not passed through HRTF
if (isEcho) {
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; i += 2) {
auto monoSample = float(streamPopOutput[i / 2] * gain / AudioConstants::MAX_SAMPLE_VALUE);
_mixSamples[i] += monoSample;
_mixSamples[i + 1] += monoSample;
const float scale = 1/32768.0f; // int16_t to float
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; i++) {
float sample = (float)streamPopOutput[i] * gain * scale;
_mixSamples[2*i+0] += sample;
_mixSamples[2*i+1] += sample;
}
++stats.manualEchoMixes;

View file

@ -363,7 +363,9 @@ void EntityServer::nodeAdded(SharedNodePointer node) {
void EntityServer::nodeKilled(SharedNodePointer node) {
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
tree->deleteDescendantsOfAvatar(node->getUUID());
tree->withWriteLock([&] {
tree->deleteDescendantsOfAvatar(node->getUUID());
});
tree->forgetAvatarID(node->getUUID());
OctreeServer::nodeKilled(node);
}
@ -451,8 +453,6 @@ void EntityServer::domainSettingsRequestFailed() {
void EntityServer::startDynamicDomainVerification() {
qCDebug(entities) << "Starting Dynamic Domain Verification...";
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainID().remove(QRegExp("\\{|\\}"));
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
QHash<QString, EntityItemID> localMap(tree->getEntityCertificateIDMap());
@ -460,15 +460,19 @@ void EntityServer::startDynamicDomainVerification() {
qCDebug(entities) << localMap.size() << "entities in _entityCertificateIDMap";
while (i.hasNext()) {
i.next();
const auto& certificateID = i.key();
const auto& entityID = i.value();
EntityItemPointer entity = tree->findEntityByEntityItemID(i.value());
EntityItemPointer entity = tree->findEntityByEntityItemID(entityID);
if (entity) {
if (!entity->getProperties().verifyStaticCertificateProperties()) {
qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << i.value() << "failed"
qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << entityID << "failed"
<< "static certificate verification.";
// Delete the entity if it doesn't pass static certificate verification
tree->deleteEntity(i.value(), true);
tree->withWriteLock([&] {
tree->deleteEntity(entityID, true);
});
} else {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest;
@ -477,39 +481,46 @@ void EntityServer::startDynamicDomainVerification() {
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location");
QJsonObject request;
request["certificate_id"] = i.key();
request["certificate_id"] = certificateID;
networkRequest.setUrl(requestURL);
QNetworkReply* networkReply = NULL;
networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply] {
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
connect(networkReply, &QNetworkReply::finished, [=]() {
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
jsonObject = jsonObject["data"].toObject();
if (networkReply->error() == QNetworkReply::NoError) {
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainID().remove(QRegExp("\\{|\\}"));
if (jsonObject["domain_id"].toString() != thisDomainID) {
EntityItemPointer entity = tree->findEntityByEntityItemID(entityID);
if (entity->getAge() > (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS/MSECS_PER_SECOND)) {
qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << i.value();
tree->deleteEntity(i.value(), true);
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID;
tree->withWriteLock([&] {
tree->deleteEntity(entityID, true);
});
} else {
qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << i.value();
qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID;
}
} else {
qCDebug(entities) << "Entity passed dynamic domain verification:" << i.value();
qCDebug(entities) << "Entity passed dynamic domain verification:" << entityID;
}
} else {
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << i.value()
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << entityID
<< "More info:" << jsonObject;
tree->deleteEntity(i.value(), true);
tree->withWriteLock([&] {
tree->deleteEntity(entityID, true);
});
}
networkReply->deleteLater();
});
}
} else {
qCWarning(entities) << "During DDV, an entity with ID" << i.value() << "was NOT found in the Entity Tree!";
qCWarning(entities) << "During DDV, an entity with ID" << entityID << "was NOT found in the Entity Tree!";
}
}

View file

@ -311,7 +311,8 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
_entitiesInQueue.insert(entity.get());
}
} else if (entity->getLastEdited() > knownTimestamp->second) {
} else if (entity->getLastEdited() > knownTimestamp->second
|| entity->getLastChangedOnServer() > knownTimestamp->second) {
// it is known and it changed --> put it on the queue with any priority
// TODO: sort these correctly
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
@ -330,7 +331,9 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
return;
}
auto knownTimestamp = _knownState.find(entity.get());
if (knownTimestamp == _knownState.end() || entity->getLastEdited() > knownTimestamp->second) {
if (knownTimestamp == _knownState.end()
|| entity->getLastEdited() > knownTimestamp->second
|| entity->getLastChangedOnServer() > knownTimestamp->second) {
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
_entitiesInQueue.insert(entity.get());
}
@ -382,7 +385,8 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
_entitiesInQueue.insert(entity.get());
}
} else if (entity->getLastEdited() > knownTimestamp->second) {
} else if (entity->getLastEdited() > knownTimestamp->second
|| entity->getLastChangedOnServer() > knownTimestamp->second) {
// it is known and it changed --> put it on the queue with any priority
// TODO: sort these correctly
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));

View file

@ -400,7 +400,9 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
if (shouldTraverseAndSend(nodeData)) {
quint64 start = usecTimestampNow();
traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene);
_myServer->getOctree()->withReadLock([&]{
traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene);
});
// Here's where we can/should allow the server to send other data...
// send the environment packet

View file

@ -6,8 +6,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
if (WIN32)
ExternalProject_Add(
${EXTERNAL_NAME}
URL https://backtrace.io/download/crashpad_062317.zip
URL_MD5 65817e564b3628492abfc1dbd2a1e98b
URL http://public.highfidelity.com/dependencies/crashpad_062317.1.zip
URL_MD5 9c84b77f5f23daf939da1371825ed2b1
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""

View file

@ -4,8 +4,8 @@ set(EXTERNAL_NAME serverless-content)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC66-v2.zip
URL_MD5 d76bdb3e2bf7ae5d20115bd97b0c44a8
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC66-v4.zip
URL_MD5 d4f42f630986c83427ff39e1fe9908c6
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""

View file

@ -1,10 +1,10 @@
#
#
# Created by Bradley Austin Davis on 2017/09/02
# Copyright 2013-2017 High Fidelity, Inc.
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
#
# Construct a default QT location from a root path, a version and an architecture
function(calculate_default_qt_dir _RESULT_NAME)
@ -27,7 +27,7 @@ function(calculate_default_qt_dir _RESULT_NAME)
endif()
set_from_env(QT_ROOT QT_ROOT ${QT_DEFAULT_ROOT})
set_from_env(QT_VERSION QT_VERSION "5.9.1")
set_from_env(QT_VERSION QT_VERSION "5.10.1")
set_from_env(QT_ARCH QT_ARCH ${QT_DEFAULT_ARCH})
set(${_RESULT_NAME} "${QT_ROOT}/${QT_VERSION}/${QT_ARCH}" PARENT_SCOPE)
@ -60,11 +60,11 @@ macro(setup_qt)
#if (NOT EXISTS "${QT_DIR}/include/QtCore/QtGlobal")
# message(FATAL_ERROR "Unable to locate Qt includes in ${QT_DIR}")
#endif()
if (NOT EXISTS "${QT_CMAKE_PREFIX_PATH}/Qt5Core/Qt5CoreConfig.cmake")
message(FATAL_ERROR "Unable to locate Qt cmake config in ${QT_CMAKE_PREFIX_PATH}")
endif()
message(STATUS "The Qt build in use is: \"${QT_DIR}\"")
# Instruct CMake to run moc automatically when needed.
@ -72,7 +72,7 @@ macro(setup_qt)
# Instruct CMake to run rcc automatically when needed
set(CMAKE_AUTORCC ON)
if (WIN32)
add_paths_to_fixup_libs("${QT_DIR}/bin")
endif ()

View file

@ -1,25 +0,0 @@
# FindGVerb.cmake
#
# Try to find the Gverb library.
#
# You must provide a GVERB_ROOT_DIR which contains src and include directories
#
# Once done this will define
#
# GVERB_FOUND - system found Gverb
# GVERB_INCLUDE_DIRS - the Gverb include directory
#
# Copyright 2014 High Fidelity, Inc.
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("gverb")
find_path(GVERB_INCLUDE_DIRS gverb.h PATH_SUFFIXES include HINTS ${GVERB_SEARCH_DIRS})
find_library(GVERB_LIBRARIES gverb PATH_SUFFIXES lib HINTS ${GVERB_SEARCH_DIRS})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Gverb DEFAULT_MSG GVERB_INCLUDE_DIRS GVERB_LIBRARIES)

View file

@ -1234,6 +1234,9 @@ Section "Uninstall"
@CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS@
;Remove the installer information file
Delete "$INSTDIR\installer.ini"
;Remove files we installed.
;Keep the list of directories here in sync with the File commands above.
@CPACK_NSIS_DELETE_FILES@

View file

@ -1,6 +1,4 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.0
import "../../qml/dialogs"

View file

@ -1,6 +1,4 @@
import QtQuick 2.4
import QtQuick.Dialogs 1.1
import QtQuick.Controls 1.4
import "../../qml/dialogs"

View file

@ -1,5 +1,4 @@
import QtQuick 2.5
import QtQuick.Controls 1.4 as Controls
import "../../qml/controls-uit"
import "../../qml/styles-uit"

View file

@ -1,6 +1,4 @@
import QtQuick 2.5
import QtQuick.Controls 1.4 as Controls
import "../../qml/controls-uit"
import "../../qml/styles-uit"

View file

@ -1,6 +1,4 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.0
import "../../qml/controls-uit"
import "../../qml/styles-uit"

View file

@ -8,9 +8,6 @@
import Hifi 1.0 as Hifi
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtGraphicalEffects 1.0
import Qt.labs.settings 1.0
import "./hifi/audio" as HifiAudio

View file

@ -1,5 +1,4 @@
import QtQuick 2.5
import QtQuick.Controls 1.2
import QtWebChannel 1.0
import QtWebEngine 1.5

View file

@ -174,7 +174,7 @@ Item {
}
}
CheckBoxQQC2 {
CheckBox {
id: showPassword
text: "Show password"
}

View file

@ -11,7 +11,6 @@
import Hifi 1.0
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 as OriginalStyles
import "../controls-uit"
import "../styles-uit"

View file

@ -11,7 +11,6 @@
import Hifi 1.0
import QtQuick 2.4
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 as OriginalStyles
import "../controls-uit"
import "../styles-uit"

View file

@ -9,7 +9,6 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtWebEngine 1.1
import QtWebChannel 1.0

View file

@ -1,9 +1,4 @@
import QtQuick 2.3
import QtQuick.Controls 1.4
import QtWebChannel 1.0
import QtWebEngine 1.2
import QtWebSockets 1.0
import "windows" as Windows
import "controls"

View file

@ -1,5 +1,4 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
Text {
color: "white";

View file

@ -1,6 +1,5 @@
import Hifi 1.0 as Hifi
import QtQuick 2.3
import QtQuick.Controls 1.2
import '.'
Item {

View file

@ -1,5 +1,4 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtWebChannel 1.0
import QtWebEngine 1.5

View file

@ -9,7 +9,6 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "controls" as Controls

View file

@ -1,7 +1,4 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
Item {
id: root

View file

@ -1,7 +1,4 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
Item {
id: root

View file

@ -1,7 +1,4 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "hydra"

View file

@ -1,7 +1,4 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "xbox"

View file

@ -1,7 +1,4 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
Item {
id: root

View file

@ -1,7 +1,4 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "xbox"

View file

@ -1,7 +1,4 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import ".."

View file

@ -1,7 +1,4 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import ".."

View file

@ -1,7 +1,4 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import ".."

View file

@ -1,7 +1,4 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import ".."

View file

@ -1,7 +1,4 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import ".."

View file

@ -1,7 +1,4 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import ".."

View file

@ -0,0 +1,125 @@
//
// Button.qml
//
// Created by David Rowe on 16 Feb 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4 as Original
import QtQuick.Controls.Styles 1.4
import TabletScriptingInterface 1.0
import "../styles-uit"
Original.Button {
id: root;
property int color: 0
property int colorScheme: hifi.colorSchemes.light
property string buttonGlyph: "";
width: hifi.dimensions.buttonWidth
height: hifi.dimensions.controlLineHeight
HifiConstants { id: hifi }
onHoveredChanged: {
if (hovered) {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
onFocusChanged: {
if (focus) {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
}
style: ButtonStyle {
background: Rectangle {
radius: hifi.buttons.radius
border.width: (control.color === hifi.buttons.none ||
(control.color === hifi.buttons.noneBorderless && control.hovered) ||
(control.color === hifi.buttons.noneBorderlessWhite && control.hovered) ||
(control.color === hifi.buttons.noneBorderlessGray && control.hovered)) ? 1 : 0;
border.color: control.color === hifi.buttons.noneBorderless ? hifi.colors.blueHighlight :
(control.color === hifi.buttons.noneBorderlessGray ? hifi.colors.baseGray : hifi.colors.white);
gradient: Gradient {
GradientStop {
position: 0.2
color: {
if (!control.enabled) {
hifi.buttons.disabledColorStart[control.colorScheme]
} else if (control.pressed) {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else if (!control.hovered && control.focus) {
hifi.buttons.focusedColor[control.color]
} else {
hifi.buttons.colorStart[control.color]
}
}
}
GradientStop {
position: 1.0
color: {
if (!control.enabled) {
hifi.buttons.disabledColorFinish[control.colorScheme]
} else if (control.pressed) {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else if (!control.hovered && control.focus) {
hifi.buttons.focusedColor[control.color]
} else {
hifi.buttons.colorFinish[control.color]
}
}
}
}
}
label: Item {
HiFiGlyphs {
id: buttonGlyph;
visible: root.buttonGlyph !== "";
text: root.buttonGlyph === "" ? hifi.glyphs.question : root.buttonGlyph;
// Size
size: 34;
// Anchors
anchors.right: buttonText.left;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
// Style
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme];
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
RalewayBold {
id: buttonText;
anchors.centerIn: parent;
font.capitalization: Font.AllUppercase
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme]
size: hifi.fontSizes.buttonLabel
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: control.text
}
}
}
}

View file

@ -0,0 +1,165 @@
//
// Table.qml
//
// Created by David Rowe on 18 Feb 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Controls 2.2 as QQC2
import "../styles-uit"
TableView {
id: tableView
property int colorScheme: hifi.colorSchemes.light
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
property bool expandSelectedRow: false
property bool centerHeaderText: false
readonly property real headerSpacing: 3 //spacing between sort indicator and table header title
property var titlePaintedPos: [] // storing extra data position behind painted
// title text and sort indicatorin table's header
signal titlePaintedPosSignal(int column) //signal that extradata position gets changed
model: ListModel { }
Component.onCompleted: {
if (flickableItem !== null && flickableItem !== undefined) {
tableView.flickableItem.QQC2.ScrollBar.vertical = scrollbar
}
}
QQC2.ScrollBar {
id: scrollbar
parent: tableView.flickableItem
policy: QQC2.ScrollBar.AsNeeded
orientation: Qt.Vertical
visible: size < 1.0
topPadding: tableView.headerVisible ? hifi.dimensions.tableHeaderHeight + 1 : 1
anchors.top: tableView.top
anchors.left: tableView.right
anchors.bottom: tableView.bottom
background: Item {
implicitWidth: hifi.dimensions.scrollbarBackgroundWidth
Rectangle {
anchors {
fill: parent;
topMargin: tableView.headerVisible ? hifi.dimensions.tableHeaderHeight : 0
}
color: isLightColorScheme ? hifi.colors.tableScrollBackgroundLight
: hifi.colors.tableScrollBackgroundDark
}
}
contentItem: Item {
implicitWidth: hifi.dimensions.scrollbarHandleWidth
Rectangle {
anchors.fill: parent
radius: (width - 4)/2
color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark
}
}
}
headerVisible: false
headerDelegate: Rectangle {
height: hifi.dimensions.tableHeaderHeight
color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark
RalewayRegular {
id: titleText
x: centerHeaderText ? (parent.width - paintedWidth -
((sortIndicatorVisible &&
sortIndicatorColumn === styleData.column) ?
(titleSort.paintedWidth / 5 + tableView.headerSpacing) : 0)) / 2 :
hifi.dimensions.tablePadding
text: styleData.value
size: hifi.fontSizes.tableHeading
font.capitalization: Font.AllUppercase
color: hifi.colors.baseGrayHighlight
horizontalAlignment: (centerHeaderText ? Text.AlignHCenter : Text.AlignLeft)
anchors.verticalCenter: parent.verticalCenter
}
//actual image of sort indicator in glyph font only 20% of real font size
//i.e. if the charachter size set to 60 pixels, actual image is 12 pixels
HiFiGlyphs {
id: titleSort
text: sortIndicatorOrder == Qt.AscendingOrder ? hifi.glyphs.caratUp : hifi.glyphs.caratDn
color: hifi.colors.darkGray
opacity: 0.6;
size: hifi.fontSizes.tableHeadingIcon
anchors.verticalCenter: titleText.verticalCenter
anchors.left: titleText.right
anchors.leftMargin: -(hifi.fontSizes.tableHeadingIcon / 2.5) + tableView.headerSpacing
visible: sortIndicatorVisible && sortIndicatorColumn === styleData.column
onXChanged: {
titlePaintedPos[styleData.column] = titleText.x + titleText.paintedWidth +
paintedWidth / 5 + tableView.headerSpacing*2
titlePaintedPosSignal(styleData.column)
}
}
Rectangle {
width: 1
anchors {
left: parent.left
top: parent.top
topMargin: 1
bottom: parent.bottom
bottomMargin: 2
}
color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight
visible: styleData.column > 0
}
Rectangle {
height: 1
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight
}
}
// Use rectangle to draw border with rounded corners.
frameVisible: false
Rectangle {
color: "#00000000"
anchors { fill: parent; margins: -2 }
border.color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight
border.width: 2
}
anchors.margins: 2 // Shrink TableView to lie within border.
backgroundVisible: true
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff
style: TableViewStyle {
// Needed in order for rows to keep displaying rows after end of table entries.
backgroundColor: tableView.isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark
alternateBackgroundColor: tableView.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd
padding.top: headerVisible ? hifi.dimensions.tableHeaderHeight: 0
}
rowDelegate: Rectangle {
height: (styleData.selected && expandSelectedRow ? 1.8 : 1) * hifi.dimensions.tableRowHeight
color: styleData.selected
? hifi.colors.primaryHighlight
: tableView.isLightColorScheme
? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd)
: (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd)
}
}

View file

@ -8,15 +8,14 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4 as Original
import QtQuick.Controls.Styles 1.4
import QtQuick 2.7
import QtQuick.Controls 2.3 as Original
import TabletScriptingInterface 1.0
import "../styles-uit"
Original.Button {
id: root;
id: control;
property int color: 0
property int colorScheme: hifi.colorSchemes.light
@ -38,88 +37,86 @@ Original.Button {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
}
style: ButtonStyle {
background: Rectangle {
radius: hifi.buttons.radius
background: Rectangle {
radius: hifi.buttons.radius
border.width: (control.color === hifi.buttons.none ||
(control.color === hifi.buttons.noneBorderless && control.hovered) ||
(control.color === hifi.buttons.noneBorderlessWhite && control.hovered) ||
(control.color === hifi.buttons.noneBorderlessGray && control.hovered)) ? 1 : 0;
border.color: control.color === hifi.buttons.noneBorderless ? hifi.colors.blueHighlight :
(control.color === hifi.buttons.noneBorderlessGray ? hifi.colors.baseGray : hifi.colors.white);
border.width: (control.color === hifi.buttons.none ||
(control.color === hifi.buttons.noneBorderless && control.hovered) ||
(control.color === hifi.buttons.noneBorderlessWhite && control.hovered) ||
(control.color === hifi.buttons.noneBorderlessGray && control.hovered)) ? 1 : 0;
border.color: control.color === hifi.buttons.noneBorderless ? hifi.colors.blueHighlight :
(control.color === hifi.buttons.noneBorderlessGray ? hifi.colors.baseGray : hifi.colors.white);
gradient: Gradient {
GradientStop {
position: 0.2
color: {
if (!control.enabled) {
hifi.buttons.disabledColorStart[control.colorScheme]
} else if (control.pressed) {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
gradient: Gradient {
GradientStop {
position: 0.2
color: {
if (!control.enabled) {
hifi.buttons.disabledColorStart[control.colorScheme]
} else if (control.pressed) {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else if (!control.hovered && control.focus) {
hifi.buttons.focusedColor[control.color]
} else {
hifi.buttons.colorStart[control.color]
}
} else {
hifi.buttons.colorStart[control.color]
}
}
GradientStop {
position: 1.0
color: {
if (!control.enabled) {
hifi.buttons.disabledColorFinish[control.colorScheme]
} else if (control.pressed) {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
}
GradientStop {
position: 1.0
color: {
if (!control.enabled) {
hifi.buttons.disabledColorFinish[control.colorScheme]
} else if (control.pressed) {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else if (!control.hovered && control.focus) {
hifi.buttons.focusedColor[control.color]
} else {
hifi.buttons.colorFinish[control.color]
}
} else {
hifi.buttons.colorFinish[control.color]
}
}
}
}
label: Item {
HiFiGlyphs {
id: buttonGlyph;
visible: root.buttonGlyph !== "";
text: root.buttonGlyph === "" ? hifi.glyphs.question : root.buttonGlyph;
// Size
size: 34;
// Anchors
anchors.right: buttonText.left;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
// Style
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme];
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
RalewayBold {
id: buttonText;
anchors.centerIn: parent;
font.capitalization: Font.AllUppercase
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme]
size: hifi.fontSizes.buttonLabel
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: control.text
}
}
}
contentItem: Item {
HiFiGlyphs {
id: buttonGlyph;
visible: control.buttonGlyph !== "";
text: control.buttonGlyph === "" ? hifi.glyphs.question : control.buttonGlyph;
// Size
size: 34;
// Anchors
anchors.right: buttonText.left;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
// Style
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme];
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
RalewayBold {
id: buttonText;
anchors.centerIn: parent;
font.capitalization: Font.AllUppercase
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme]
size: hifi.fontSizes.buttonLabel
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: control.text
}
}
}

View file

@ -8,9 +8,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4 as Original
import QtQuick.Controls.Styles 1.4
import QtQuick 2.2
import QtQuick.Controls 2.2 as Original
import "../styles-uit"
@ -28,85 +27,89 @@ Original.CheckBox {
property bool wrap: true;
readonly property int checkSize: Math.max(boxSize - 8, 10)
readonly property int checkRadius: 2
activeFocusOnPress: true
focusPolicy: Qt.ClickFocus
hoverEnabled: true
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
}
// TODO: doesnt works for QQC1. check with QQC2
// onHovered: {
// Tablet.playSound(TabletEnums.ButtonHover);
// }
style: CheckBoxStyle {
indicator: Rectangle {
id: box
implicitWidth: boxSize
implicitHeight: boxSize
radius: boxRadius
border.width: 1
border.color: pressed || hovered
? hifi.colors.checkboxCheckedBorder
: (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish)
gradient: Gradient {
GradientStop {
position: 0.2
color: pressed || hovered
? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightStart)
: (checkBox.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart)
}
GradientStop {
position: 1.0
color: pressed || hovered
? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightFinish)
: (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish)
}
}
Rectangle {
visible: pressed || hovered
anchors.centerIn: parent
id: innerBox
width: checkSize - 4
height: width
radius: checkRadius
color: hifi.colors.checkboxCheckedBorder
}
Rectangle {
id: check
width: checkSize
height: checkSize
radius: checkRadius
anchors.centerIn: parent
color: isRedCheck ? hifi.colors.checkboxCheckedRed : hifi.colors.checkboxChecked
border.width: 2
border.color: isRedCheck? hifi.colors.checkboxCheckedBorderRed : hifi.colors.checkboxCheckedBorder
visible: checked && !pressed || !checked && pressed
}
Rectangle {
id: disabledOverlay
visible: !enabled
width: boxSize
height: boxSize
radius: boxRadius
border.width: 1
border.color: hifi.colors.baseGrayHighlight
color: hifi.colors.baseGrayHighlight
opacity: 0.5
}
}
label: Label {
text: checkBox.text
color: checkBox.color
x: 2
wrapMode: checkBox.wrap ? Text.Wrap : Text.NoWrap
elide: checkBox.wrap ? Text.ElideNone : Text.ElideRight
enabled: checkBox.enabled
onHoveredChanged: {
if (hovered) {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
indicator: Rectangle {
id: box
implicitWidth: boxSize
implicitHeight: boxSize
radius: boxRadius
y: parent.height / 2 - height / 2
border.width: 1
border.color: pressed || hovered
? hifi.colors.checkboxCheckedBorder
: (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish)
gradient: Gradient {
GradientStop {
position: 0.2
color: pressed || hovered
? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightStart)
: (checkBox.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart)
}
GradientStop {
position: 1.0
color: pressed || hovered
? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightFinish)
: (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish)
}
}
Rectangle {
visible: pressed || hovered
anchors.centerIn: parent
id: innerBox
width: checkSize - 4
height: width
radius: checkRadius
color: hifi.colors.checkboxCheckedBorder
}
Rectangle {
id: check
width: checkSize
height: checkSize
radius: checkRadius
anchors.centerIn: parent
color: isRedCheck ? hifi.colors.checkboxCheckedRed : hifi.colors.checkboxChecked
border.width: 2
border.color: isRedCheck? hifi.colors.checkboxCheckedBorderRed : hifi.colors.checkboxCheckedBorder
visible: checked && !pressed || !checked && pressed
}
Rectangle {
id: disabledOverlay
visible: !enabled
width: boxSize
height: boxSize
radius: boxRadius
border.width: 1
border.color: hifi.colors.baseGrayHighlight
color: hifi.colors.baseGrayHighlight
opacity: 0.5
}
}
contentItem: Label {
text: checkBox.text
color: checkBox.color
x: 2
verticalAlignment: Text.AlignVCenter
wrapMode: checkBox.wrap ? Text.Wrap : Text.NoWrap
elide: checkBox.wrap ? Text.ElideNone : Text.ElideRight
enabled: checkBox.enabled
leftPadding: checkBox.indicator.width + checkBox.spacing
}
}

View file

@ -8,9 +8,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick 2.7
import QtQuick.Controls 2.2
import "../styles-uit"
import "../controls-uit" as HifiControls
@ -24,6 +23,7 @@ FocusScope {
property alias comboBox: comboBox
readonly property alias currentText: comboBox.currentText;
property alias currentIndex: comboBox.currentIndex;
property int currentHighLightedIndex: comboBox.currentIndex;
property int dropdownHeight: 480
property int colorScheme: hifi.colorSchemes.light
@ -40,196 +40,134 @@ FocusScope {
implicitHeight: comboBox.height;
focus: true
Rectangle {
id: background
gradient: Gradient {
GradientStop {
position: 0.2
color: popup.visible
? (isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark)
: (isLightColorScheme ? hifi.colors.dropDownLightStart : hifi.colors.dropDownDarkStart)
}
GradientStop {
position: 1.0
color: popup.visible
? (isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark)
: (isLightColorScheme ? hifi.colors.dropDownLightFinish : hifi.colors.dropDownDarkFinish)
}
}
anchors.fill: parent
}
SystemPalette { id: palette }
ComboBox {
id: comboBox
anchors.fill: parent
visible: false
height: hifi.fontSizes.textFieldInput + 13 // Match height of TextField control.
}
FiraSansSemiBold {
id: textField
anchors {
left: parent.left
leftMargin: hifi.dimensions.textPadding
right: dropIcon.left
verticalCenter: parent.verticalCenter
}
size: hifi.fontSizes.textFieldInput
text: comboBox.currentText
elide: Text.ElideRight
color: controlHover.containsMouse || popup.visible ? hifi.colors.baseGray : (isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText )
}
Item {
id: dropIcon
anchors { right: parent.right; verticalCenter: parent.verticalCenter }
height: background.height
width: height
Rectangle {
width: 1
height: parent.height
anchors.top: parent.top
anchors.left: parent.left
color: isLightColorScheme ? hifi.colors.faintGray : hifi.colors.baseGray
}
HiFiGlyphs {
anchors {
top: parent.top
topMargin: -11
horizontalCenter: parent.horizontalCenter
}
size: hifi.dimensions.spinnerSize
text: hifi.glyphs.caratDn
color: controlHover.containsMouse || popup.visible ? hifi.colors.baseGray : (isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText)
}
}
MouseArea {
id: controlHover
hoverEnabled: true
anchors.fill: parent
onClicked: toggleList();
}
visible: true
height: hifi.fontSizes.textFieldInput + 13 // Match height of TextField control.
function toggleList() {
if (popup.visible) {
hideList();
} else {
showList();
}
}
function showList() {
var r;
if (isDesktop) {
r = desktop.mapFromItem(root, 0, 0, root.width, root.height);
} else {
r = mapFromItem(root, 0, 0, root.width, root.height);
}
var y = r.y + r.height;
var bottom = y + scrollView.height;
var height = isDesktop ? desktop.height : tabletRoot.height;
if (bottom > height) {
y -= bottom - height + 8;
}
scrollView.x = r.x;
scrollView.y = y;
popup.visible = true;
popup.forceActiveFocus();
listView.currentIndex = root.currentIndex;
scrollView.hoverEnabled = true;
}
function hideList() {
popup.visible = false;
scrollView.hoverEnabled = false;
root.accepted();
}
FocusScope {
id: popup
parent: isDesktop ? desktop : root
anchors.fill: parent
z: isDesktop ? desktop.zLevels.menu : 12
visible: false
focus: true
MouseArea {
anchors.fill: parent
onClicked: hideList();
}
function previousItem() { listView.currentIndex = (listView.currentIndex + listView.count - 1) % listView.count; }
function nextItem() { listView.currentIndex = (listView.currentIndex + listView.count + 1) % listView.count; }
function selectCurrentItem() { root.currentIndex = listView.currentIndex; hideList(); }
function selectSpecificItem(index) { root.currentIndex = index; hideList(); }
function previousItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count - 1) % comboBox.count; }
function nextItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count + 1) % comboBox.count; }
function selectCurrentItem() { root.currentIndex = root.currentHighLightedIndex; close(); /*hideList();*/ }
function selectSpecificItem(index) { root.currentIndex = index; close();/*hideList();*/ }
Keys.onUpPressed: previousItem();
Keys.onDownPressed: nextItem();
Keys.onSpacePressed: selectCurrentItem();
Keys.onRightPressed: selectCurrentItem();
Keys.onReturnPressed: selectCurrentItem();
Keys.onEscapePressed: hideList();
ScrollView {
id: scrollView
height: root.dropdownHeight
background: Rectangle {
gradient: Gradient {
GradientStop {
position: 0.2
color: comboBox.popup.visible
? (isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark)
: (isLightColorScheme ? hifi.colors.dropDownLightStart : hifi.colors.dropDownDarkStart)
}
GradientStop {
position: 1.0
color: comboBox.popup.visible
? (isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark)
: (isLightColorScheme ? hifi.colors.dropDownLightFinish : hifi.colors.dropDownDarkFinish)
}
}
}
indicator: Item {
id: dropIcon
anchors { right: parent.right; verticalCenter: parent.verticalCenter }
height: root.height
width: height
Rectangle {
width: 1
height: parent.height
anchors.top: parent.top
anchors.left: parent.left
color: isLightColorScheme ? hifi.colors.faintGray : hifi.colors.baseGray
}
HiFiGlyphs {
anchors { top: parent.top; topMargin: -11; horizontalCenter: parent.horizontalCenter }
size: hifi.dimensions.spinnerSize
text: hifi.glyphs.caratDn
color: comboBox.hovered || comboBox.popup.visible ? hifi.colors.baseGray : (isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText)
}
}
contentItem: FiraSansSemiBold {
id: textField
anchors {
left: parent.left
leftMargin: hifi.dimensions.textPadding
verticalCenter: parent.verticalCenter
}
size: hifi.fontSizes.textFieldInput
text: comboBox.currentText
elide: Text.ElideRight
color: comboBox.hovered || comboBox.popup.visible ? hifi.colors.baseGray : (isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText )
}
delegate: ItemDelegate {
id: itemDelegate
hoverEnabled: true
width: root.width + 4
property bool hoverEnabled: false;
height: popupText.implicitHeight * 1.4
highlighted: root.currentHighLightedIndex == index
style: ScrollViewStyle {
decrementControl: Item {
visible: false
}
incrementControl: Item {
visible: false
}
scrollBarBackground: Rectangle{
implicitWidth: 20
color: hifi.colors.baseGray
}
handle:
Rectangle {
implicitWidth: 16
anchors.left: parent.left
anchors.leftMargin: 3
anchors.top: parent.top
anchors.bottom: parent.bottom
radius: 6
color: hifi.colors.lightGrayText
onHoveredChanged: {
if (hovered) {
root.currentHighLightedIndex = index
}
}
ListView {
background: Rectangle {
color: itemDelegate.highlighted ? hifi.colors.primaryHighlight
: (isLightColorScheme ? hifi.colors.dropDownPressedLight
: hifi.colors.dropDownPressedDark)
}
contentItem: FiraSansSemiBold {
id: popupText
anchors.left: parent.left
anchors.leftMargin: hifi.dimensions.textPadding
anchors.verticalCenter: parent.verticalCenter
text: comboBox.model[index] ? comboBox.model[index]
: (comboBox.model.get && comboBox.model.get(index).text ?
comboBox.model.get(index).text : "")
size: hifi.fontSizes.textFieldInput
color: hifi.colors.baseGray
}
}
popup: Popup {
y: comboBox.height - 1
width: comboBox.width
implicitHeight: listView.contentHeight > dropdownHeight ? dropdownHeight
: listView.contentHeight
padding: 0
topPadding: 1
onClosed: {
root.accepted()
}
contentItem: ListView {
id: listView
height: textField.height * count * 1.4
model: root.model
delegate: Rectangle {
width: root.width + 4
height: popupText.implicitHeight * 1.4
color: (listView.currentIndex === index) ? hifi.colors.primaryHighlight :
(isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark)
FiraSansSemiBold {
anchors.left: parent.left
anchors.leftMargin: hifi.dimensions.textPadding
anchors.verticalCenter: parent.verticalCenter
id: popupText
text: listView.model[index] ? listView.model[index] : (listView.model.get && listView.model.get(index).text ? listView.model.get(index).text : "")
size: hifi.fontSizes.textFieldInput
color: hifi.colors.baseGray
}
MouseArea {
id: popupHover
anchors.fill: parent;
hoverEnabled: scrollView.hoverEnabled;
onEntered: listView.currentIndex = index;
onClicked: popup.selectSpecificItem(index);
}
clip: true
model: comboBox.popup.visible ? comboBox.delegateModel : null
currentIndex: root.currentHighLightedIndex
delegate: comboBox.delegate
ScrollBar.vertical: HifiControls.ScrollBar {
id: scrollbar
parent: listView
policy: ScrollBar.AsNeeded
visible: size < 1.0
}
}
background: Rectangle {
color: hifi.colors.baseGray
}
}
}

View file

@ -8,15 +8,14 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4 as Original
import QtQuick.Controls.Styles 1.4
import QtQuick 2.7
import QtQuick.Controls 2.2 as Original
import TabletScriptingInterface 1.0
import "../styles-uit"
Original.Button {
id: control
property int color: 0
property int colorScheme: hifi.colorSchemes.light
property string glyph: ""
@ -41,59 +40,52 @@ Original.Button {
Tablet.playSound(TabletEnums.ButtonClick);
}
style: ButtonStyle {
background: Rectangle {
radius: hifi.buttons.radius
background: Rectangle {
radius: hifi.buttons.radius
gradient: Gradient {
GradientStop {
position: 0.2
color: {
if (!control.enabled) {
hifi.buttons.disabledColorStart[control.colorScheme]
} else if (control.pressed) {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
gradient: Gradient {
GradientStop {
position: 0.2
color: {
if (!control.enabled) {
hifi.buttons.disabledColorStart[control.colorScheme]
} else if (control.pressed) {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else if (!control.hovered && control.focus) {
hifi.buttons.focusedColor[control.color]
} else {
hifi.buttons.colorStart[control.color]
}
}
}
GradientStop {
position: 1.0
color: {
if (!control.enabled) {
hifi.buttons.disabledColorFinish[control.colorScheme]
} else if (control.pressed) {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else if (!control.hovered && control.focus) {
hifi.buttons.focusedColor[control.color]
} else {
hifi.buttons.colorFinish[control.color]
}
} else {
hifi.buttons.colorStart[control.color]
}
}
}
}
label: HiFiGlyphs {
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme]
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
anchors {
// Tweak horizontal alignment so that it looks right.
left: parent.left
leftMargin: -0.5
GradientStop {
position: 1.0
color: {
if (!control.enabled) {
hifi.buttons.disabledColorFinish[control.colorScheme]
} else if (control.pressed) {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else if (!control.hovered && control.focus) {
hifi.buttons.focusedColor[control.color]
} else {
hifi.buttons.colorFinish[control.color]
}
}
}
text: control.glyph
size: control.size
}
}
contentItem: HiFiGlyphs {
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme]
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: control.glyph
size: control.size
}
}

View file

@ -9,8 +9,6 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
Rectangle {
anchors.left: parent.left

View file

@ -9,7 +9,6 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../styles-uit"
Item {

View file

@ -26,15 +26,17 @@ Rectangle {
readonly property int keyboardRowHeight: 50
readonly property int keyboardWidth: 480
readonly property int keyboardHeight: 200
readonly property int mirrorTextHeight: keyboardRowHeight
property bool password: false
property alias mirroredText: mirrorText.text
property bool showMirrorText: true
readonly property int raisedHeight: 200
height: enabled && raised ? raisedHeight + (showMirrorText ? keyboardRowHeight : 0) : 0
readonly property int raisedHeight: keyboardHeight + (showMirrorText ? keyboardRowHeight : 0)
height: enabled && raised ? raisedHeight : 0
visible: enabled && raised
property bool shiftMode: false
@ -158,7 +160,7 @@ Rectangle {
id: keyboardRect
y: showMirrorText ? mirrorTextHeight : 0
width: keyboardWidth
height: raisedHeight
height: keyboardHeight
color: "#252525"
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
@ -167,7 +169,7 @@ Rectangle {
Column {
id: columnAlpha
width: keyboardWidth
height: raisedHeight
height: keyboardHeight
visible: !numeric
Row {
@ -259,7 +261,7 @@ Rectangle {
Column {
id: columnNumeric
width: keyboardWidth
height: raisedHeight
height: keyboardHeight
visible: numeric
Row {

View file

@ -8,7 +8,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick 2.7
import "../styles-uit"

View file

@ -9,8 +9,6 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4 as Original
import QtQuick.Controls.Styles 1.4
import "../styles-uit"
import "." as HifiControls

View file

@ -9,8 +9,7 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4 as Original
import QtQuick.Controls.Styles 1.4
import QtQuick.Controls 2.2 as Original
import "../styles-uit"
import "../controls-uit" as HifiControls
@ -21,6 +20,8 @@ Original.RadioButton {
id: radioButton
HifiConstants { id: hifi }
hoverEnabled: true
property int colorScheme: hifi.colorSchemes.light
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
@ -33,50 +34,53 @@ Original.RadioButton {
Tablet.playSound(TabletEnums.ButtonClick);
}
// TODO: doesnt works for QQC1. check with QQC2
// onHovered: {
// Tablet.playSound(TabletEnums.ButtonHover);
// }
style: RadioButtonStyle {
indicator: Rectangle {
id: box
width: boxSize
height: boxSize
radius: 7
gradient: Gradient {
GradientStop {
position: 0.2
color: pressed || hovered
? (radioButton.isLightColorScheme ? hifi.colors.checkboxDarkStart : hifi.colors.checkboxLightStart)
: (radioButton.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart)
}
GradientStop {
position: 1.0
color: pressed || hovered
? (radioButton.isLightColorScheme ? hifi.colors.checkboxDarkFinish : hifi.colors.checkboxLightFinish)
: (radioButton.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish)
}
}
Rectangle {
id: check
width: checkSize
height: checkSize
radius: 7
anchors.centerIn: parent
color: "#00B4EF"
border.width: 1
border.color: "#36CDFF"
visible: checked && !pressed || !checked && pressed
}
}
label: RalewaySemiBold {
text: control.text
size: hifi.fontSizes.inputLabel
color: isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText
x: radioButton.boxSize / 2
onHoveredChanged: {
if (hovered) {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
indicator: Rectangle {
id: box
width: boxSize
height: boxSize
radius: 7
x: radioButton.leftPadding
y: parent.height / 2 - height / 2
gradient: Gradient {
GradientStop {
position: 0.2
color: pressed || hovered
? (radioButton.isLightColorScheme ? hifi.colors.checkboxDarkStart : hifi.colors.checkboxLightStart)
: (radioButton.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart)
}
GradientStop {
position: 1.0
color: pressed || hovered
? (radioButton.isLightColorScheme ? hifi.colors.checkboxDarkFinish : hifi.colors.checkboxLightFinish)
: (radioButton.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish)
}
}
Rectangle {
id: check
width: checkSize
height: checkSize
radius: 7
anchors.centerIn: parent
color: "#00B4EF"
border.width: 1
border.color: "#36CDFF"
visible: checked && !pressed || !checked && pressed
}
}
contentItem: RalewaySemiBold {
text: radioButton.text
size: hifi.fontSizes.inputLabel
color: isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
leftPadding: radioButton.indicator.width + radioButton.spacing
}
}

View file

@ -0,0 +1,41 @@
//
// ScrollBar.qml
//
// Created by Vlad Stelmahovsky on 27 Nov 2017
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
import QtQuick.Controls 2.2
import "../styles-uit"
ScrollBar {
visible: size < 1.0
HifiConstants { id: hifi }
property int colorScheme: hifi.colorSchemes.light
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
background: Item {
implicitWidth: hifi.dimensions.scrollbarBackgroundWidth
Rectangle {
anchors { fill: parent; topMargin: 3; bottomMargin: 3 }
radius: hifi.dimensions.scrollbarHandleWidth/2
color: isLightColorScheme ? hifi.colors.tableScrollBackgroundLight
: hifi.colors.tableScrollBackgroundDark
}
}
contentItem: Item {
implicitWidth: hifi.dimensions.scrollbarHandleWidth
Rectangle {
anchors { fill: parent; topMargin: 1; bottomMargin: 1 }
radius: hifi.dimensions.scrollbarHandleWidth/2
color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark
}
}
}

View file

@ -8,9 +8,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick 2.7
import QtQuick.Controls 2.2
import "../styles-uit"
import "../controls-uit" as HifiControls
@ -23,66 +22,65 @@ Slider {
property string label: ""
property real controlHeight: height + (sliderLabel.visible ? sliderLabel.height + sliderLabel.anchors.bottomMargin : 0)
property alias minimumValue: slider.from
property alias maximumValue: slider.to
height: hifi.fontSizes.textFieldInput + 14 // Match height of TextField control.
y: sliderLabel.visible ? sliderLabel.height + sliderLabel.anchors.bottomMargin : 0
style: SliderStyle {
background: Rectangle {
x: slider.leftPadding
y: slider.topPadding + slider.availableHeight / 2 - height / 2
groove: Rectangle {
implicitWidth: 50
implicitHeight: hifi.dimensions.sliderGrooveHeight
implicitWidth: 50
implicitHeight: hifi.dimensions.sliderGrooveHeight
width: slider.availableWidth
height: implicitHeight
radius: height / 2
color: isLightColorScheme ? hifi.colors.sliderGutterLight : hifi.colors.sliderGutterDark
Rectangle {
width: slider.visualPosition * parent.width
height: parent.height
radius: height / 2
color: isLightColorScheme ? hifi.colors.sliderGutterLight : hifi.colors.sliderGutterDark
gradient: Gradient {
GradientStop { position: 0.0; color: hifi.colors.blueAccent }
GradientStop { position: 1.0; color: hifi.colors.primaryHighlight }
}
}
}
Rectangle {
width: parent.height - 2
height: slider.width * (slider.value - slider.minimumValue) / (slider.maximumValue - slider.minimumValue) - 1
radius: height / 2
anchors {
top: parent.top
topMargin: width + 1
left: parent.left
leftMargin: 1
}
transformOrigin: Item.TopLeft
rotation: -90
gradient: Gradient {
GradientStop { position: 0.0; color: hifi.colors.blueAccent }
GradientStop { position: 1.0; color: hifi.colors.primaryHighlight }
}
handle: Rectangle {
x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width)
y: slider.topPadding + slider.availableHeight / 2 - height / 2
implicitWidth: hifi.dimensions.sliderHandleSize
implicitHeight: hifi.dimensions.sliderHandleSize
radius: height / 2
border.width: 1
border.color: isLightColorScheme ? hifi.colors.sliderBorderLight : hifi.colors.sliderBorderDark
gradient: Gradient {
GradientStop {
position: 0.0
color: pressed || hovered
? (isLightColorScheme ? hifi.colors.sliderDarkStart : hifi.colors.sliderLightStart )
: (isLightColorScheme ? hifi.colors.sliderLightStart : hifi.colors.sliderDarkStart )
}
GradientStop {
position: 1.0
color: pressed || hovered
? (isLightColorScheme ? hifi.colors.sliderDarkFinish : hifi.colors.sliderLightFinish )
: (isLightColorScheme ? hifi.colors.sliderLightFinish : hifi.colors.sliderDarkFinish )
}
}
handle: Rectangle {
implicitWidth: hifi.dimensions.sliderHandleSize
implicitHeight: hifi.dimensions.sliderHandleSize
Rectangle {
height: parent.height - 2
width: height
radius: height / 2
anchors.centerIn: parent
color: hifi.colors.transparent
border.width: 1
border.color: isLightColorScheme ? hifi.colors.sliderBorderLight : hifi.colors.sliderBorderDark
gradient: Gradient {
GradientStop {
position: 0.0
color: pressed || hovered
? (isLightColorScheme ? hifi.colors.sliderDarkStart : hifi.colors.sliderLightStart )
: (isLightColorScheme ? hifi.colors.sliderLightStart : hifi.colors.sliderDarkStart )
}
GradientStop {
position: 1.0
color: pressed || hovered
? (isLightColorScheme ? hifi.colors.sliderDarkFinish : hifi.colors.sliderLightFinish )
: (isLightColorScheme ? hifi.colors.sliderLightFinish : hifi.colors.sliderDarkFinish )
}
}
Rectangle {
height: parent.height - 2
width: height
radius: height / 2
anchors.centerIn: parent
color: hifi.colors.transparent
border.width: 1
border.color: hifi.colors.black
}
border.color: hifi.colors.black
}
}

View file

@ -8,9 +8,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick 2.7
import QtQuick.Controls 2.2
import "../styles-uit"
import "../controls-uit" as HifiControls
@ -19,11 +18,40 @@ SpinBox {
id: spinBox
property int colorScheme: hifi.colorSchemes.light
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light
property string label: ""
property string labelInside: ""
property color colorLabelInside: hifi.colors.white
property real controlHeight: height + (spinBoxLabel.visible ? spinBoxLabel.height + spinBoxLabel.anchors.bottomMargin : 0)
property int decimals: 2;
property real factor: Math.pow(10, decimals)
property real minimumValue: 0.0
property real maximumValue: 0.0
property real realValue: 0.0
property real realFrom: 0.0
property real realTo: 100.0
property real realStepSize: 1.0
implicitHeight: height
implicitWidth: width
padding: 0
leftPadding: 0
rightPadding: padding + (up.indicator ? up.indicator.width : 0)
topPadding: 0
bottomPadding: 0
locale: Qt.locale("en_US")
onValueModified: realValue = value/factor
onValueChanged: realValue = value/factor
stepSize: realStepSize*factor
value: realValue*factor
to : realTo*factor
from : realFrom*factor
font.family: "Fira Sans SemiBold"
font.pixelSize: hifi.fontSizes.textFieldInput
@ -31,43 +59,67 @@ SpinBox {
y: spinBoxLabel.visible ? spinBoxLabel.height + spinBoxLabel.anchors.bottomMargin : 0
style: SpinBoxStyle {
id: spinStyle
background: Rectangle {
color: isLightColorScheme
? (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGray)
: (spinBox.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow)
border.color: spinBoxLabelInside.visible ? spinBoxLabelInside.color : hifi.colors.primaryHighlight
border.width: spinBox.activeFocus ? spinBoxLabelInside.visible ? 2 : 1 : 0
}
background: Rectangle {
color: isLightColorScheme
? (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGray)
: (spinBox.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow)
border.color: spinBoxLabelInside.visible ? spinBoxLabelInside.color : hifi.colors.primaryHighlight
border.width: spinBox.activeFocus ? spinBoxLabelInside.visible ? 2 : 1 : 0
}
textColor: isLightColorScheme
? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.lightGray)
: (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText)
validator: DoubleValidator {
bottom: Math.min(spinBox.from, spinBox.to)*spinBox.factor
top: Math.max(spinBox.from, spinBox.to)*spinBox.factor
}
textFromValue: function(value, locale) {
return parseFloat(value*1.0/factor).toFixed(decimals);
}
valueFromText: function(text, locale) {
return Number.fromLocaleString(locale, text);
}
contentItem: TextInput {
z: 2
color: isLightColorScheme
? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.lightGray)
: (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText)
selectedTextColor: hifi.colors.black
selectionColor: hifi.colors.primaryHighlight
horizontalAlignment: Qt.AlignLeft
padding.left: spinBoxLabelInside.visible ? 30 : hifi.dimensions.textPadding
padding.right: hifi.dimensions.spinnerSize
padding.top: 0
incrementControl: HiFiGlyphs {
id: incrementButton
text: spinBox.textFromValue(spinBox.value, spinBox.locale)
verticalAlignment: Qt.AlignVCenter
leftPadding: spinBoxLabelInside.visible ? 30 : hifi.dimensions.textPadding
//rightPadding: hifi.dimensions.spinnerSize
width: spinBox.width - hifi.dimensions.spinnerSize
}
up.indicator: Item {
x: spinBox.width - implicitWidth - 5
y: 1
clip: true
implicitHeight: spinBox.implicitHeight/2
implicitWidth: spinBox.implicitHeight/2
HiFiGlyphs {
anchors.centerIn: parent
text: hifi.glyphs.caratUp
x: 10
y: 1
size: hifi.dimensions.spinnerSize
color: styleData.upPressed ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
color: spinBox.down.pressed || spinBox.up.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
}
}
decrementControl: HiFiGlyphs {
text: hifi.glyphs.caratDn
x: 10
y: -1
size: hifi.dimensions.spinnerSize
color: styleData.downPressed ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
}
down.indicator: Item {
x: spinBox.width - implicitWidth - 5
y: spinBox.implicitHeight/2
clip: true
implicitHeight: spinBox.implicitHeight/2
implicitWidth: spinBox.implicitHeight/2
HiFiGlyphs {
anchors.centerIn: parent
text: hifi.glyphs.caratDn
size: hifi.dimensions.spinnerSize
color: spinBox.down.pressed || spinBox.down.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
}
}
HifiControls.Label {
@ -91,26 +143,26 @@ SpinBox {
visible: spinBox.labelInside != ""
}
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
onWheel: {
if(spinBox.activeFocus)
wheel.accepted = false
else
wheel.accepted = true
}
onPressed: {
mouse.accepted = false
}
onReleased: {
mouse.accepted = false
}
onClicked: {
mouse.accepted = false
}
onDoubleClicked: {
mouse.accepted = false
}
}
// MouseArea {
// anchors.fill: parent
// propagateComposedEvents: true
// onWheel: {
// if(spinBox.activeFocus)
// wheel.accepted = false
// else
// wheel.accepted = true
// }
// onPressed: {
// mouse.accepted = false
// }
// onReleased: {
// mouse.accepted = false
// }
// onClicked: {
// mouse.accepted = false
// }
// onDoubleClicked: {
// mouse.accepted = false
// }
// }
}

View file

@ -8,9 +8,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4 as Original
import QtQuick.Controls.Styles 1.4
import QtQuick 2.7
import QtQuick.Controls 2.2 as Original
import "../styles-uit"
@ -33,44 +32,49 @@ Item {
Original.Switch {
id: originalSwitch;
activeFocusOnPress: true;
focusPolicy: Qt.ClickFocus
anchors.top: rootSwitch.top;
anchors.left: rootSwitch.left;
anchors.leftMargin: rootSwitch.width/2 - rootSwitch.switchWidth/2;
onCheckedChanged: rootSwitch.onCheckedChanged();
onClicked: rootSwitch.clicked();
hoverEnabled: true
style: SwitchStyle {
topPadding: 3;
leftPadding: 3;
rightPadding: 3;
bottomPadding: 3;
padding {
top: 3;
left: 3;
right: 3;
bottom: 3;
onHoveredChanged: {
if (hovered) {
switchHandle.color = hifi.colors.blueHighlight;
} else {
switchHandle.color = hifi.colors.lightGray;
}
}
background: Rectangle {
color: "#252525";
implicitWidth: rootSwitch.switchWidth;
implicitHeight: rootSwitch.height;
radius: rootSwitch.switchRadius;
}
indicator: Rectangle {
id: switchHandle;
implicitWidth: rootSwitch.height - originalSwitch.topPadding - originalSwitch.bottomPadding;
implicitHeight: implicitWidth;
radius: implicitWidth/2;
border.color: hifi.colors.lightGrayText;
color: hifi.colors.lightGray;
//x: originalSwitch.leftPadding
x: Math.max(0, Math.min(parent.width - width, originalSwitch.visualPosition * parent.width - (width / 2)))
y: parent.height / 2 - height / 2
Behavior on x {
enabled: !originalSwitch.down
SmoothedAnimation { velocity: 200 }
}
groove: Rectangle {
color: "#252525";
implicitWidth: rootSwitch.switchWidth;
implicitHeight: rootSwitch.height;
radius: rootSwitch.switchRadius;
}
handle: Rectangle {
id: switchHandle;
implicitWidth: rootSwitch.height - padding.top - padding.bottom;
implicitHeight: implicitWidth;
radius: implicitWidth/2;
border.color: hifi.colors.lightGrayText;
color: hifi.colors.lightGray;
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
onEntered: parent.color = hifi.colors.blueHighlight;
onExited: parent.color = hifi.colors.lightGray;
}
}
}
}

View file

@ -11,7 +11,7 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Controls 2.2 as QQC2
import QtQuick.Controls 2.3 as QQC2
import "../styles-uit"

View file

@ -9,7 +9,6 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../styles-uit"

View file

@ -9,8 +9,6 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "../styles-uit"
import "../controls-uit" as HifiControls

View file

@ -9,8 +9,6 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "../styles-uit"
TextEdit {

View file

@ -8,9 +8,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick 2.7
import QtQuick.Controls 2.2
import "../styles-uit"
import "../controls-uit" as HifiControls
@ -29,11 +28,17 @@ TextField {
property int roundedBorderRadius: 4
property bool error: false;
property bool hasClearButton: false;
property alias textColor: textField.color
property string leftPermanentGlyph: "";
property string centerPlaceholderGlyph: "";
placeholderText: textField.placeholderText
property bool rightAnchorSet: false;
anchors.onRightChanged: {
rightAnchorSet = true;
}
font.family: "Fira Sans"
font.pixelSize: hifi.fontSizes.textFieldInput
height: implicitHeight + 3 // Make surrounding box higher so that highlight is vertically centered.
@ -44,42 +49,56 @@ TextField {
// workaround for https://bugreports.qt.io/browse/QTBUG-49297
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Return:
case Qt.Key_Enter:
event.accepted = true;
case Qt.Key_Return:
case Qt.Key_Enter:
event.accepted = true;
// emit accepted signal manually
if (acceptableInput) {
accepted();
}
// emit accepted signal manually
if (acceptableInput) {
accepted();
}
}
}
style: TextFieldStyle {
id: style;
textColor: {
if (isLightColorScheme) {
if (textField.activeFocus) {
hifi.colors.black
} else {
hifi.colors.lightGray
}
} else if (isFaintGrayColorScheme) {
if (textField.activeFocus) {
hifi.colors.black
} else {
hifi.colors.lightGray
}
Text {
id: placeholder
x: textField.leftPadding
y: textField.topPadding
width: textField.width - (textField.leftPadding + textField.rightPadding)
height: textField.height - (textField.topPadding + textField.bottomPadding)
text: textField.placeholderText
font: textField.font
color: textField.placeholderTextColor
verticalAlignment: textField.verticalAlignment
visible: !textField.length && !textField.preeditText && (!textField.activeFocus || textField.horizontalAlignment !== Qt.AlignHCenter)
elide: Text.ElideRight
}
color: {
if (isLightColorScheme) {
if (textField.activeFocus) {
hifi.colors.black
} else {
if (textField.activeFocus) {
hifi.colors.white
} else {
hifi.colors.lightGrayText
}
hifi.colors.lightGray
}
} else if (isFaintGrayColorScheme) {
if (textField.activeFocus) {
hifi.colors.black
} else {
hifi.colors.lightGray
}
} else {
if (textField.activeFocus) {
hifi.colors.white
} else {
hifi.colors.lightGrayText
}
}
background: Rectangle {
color: {
}
background: Rectangle {
color: {
if (isLightColorScheme) {
if (textField.activeFocus) {
hifi.colors.white
@ -100,22 +119,22 @@ TextField {
}
}
}
border.color: textField.error ? hifi.colors.redHighlight :
border.color: textField.error ? hifi.colors.redHighlight :
(textField.activeFocus ? hifi.colors.primaryHighlight : (hasDefocusedBorder ? (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray) : color))
border.width: textField.activeFocus || hasRoundedBorder || textField.error ? 1 : 0
border.width: textField.activeFocus || hasRoundedBorder || textField.error ? 1 : 0
radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? roundedBorderRadius : 0)
HiFiGlyphs {
HiFiGlyphs {
text: textField.leftPermanentGlyph;
color: textColor;
size: hifi.fontSizes.textFieldSearchIcon;
anchors.left: parent.left;
anchors.verticalCenter: parent.verticalCenter;
anchors.leftMargin: hifi.dimensions.textPadding - 2;
visible: text;
}
color: textColor;
size: hifi.fontSizes.textFieldSearchIcon;
anchors.left: parent.left;
anchors.verticalCenter: parent.verticalCenter;
anchors.leftMargin: hifi.dimensions.textPadding - 2;
visible: text;
}
HiFiGlyphs {
HiFiGlyphs {
text: textField.centerPlaceholderGlyph;
color: textColor;
size: parent.height;
@ -125,39 +144,40 @@ TextField {
}
HiFiGlyphs {
text: hifi.glyphs.search
color: textColor
size: hifi.fontSizes.textFieldSearchIcon
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: hifi.dimensions.textPadding - 2
visible: isSearchField
}
text: hifi.glyphs.search
color: textColor
size: hifi.fontSizes.textFieldSearchIcon
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: hifi.dimensions.textPadding - 2
visible: isSearchField
}
HiFiGlyphs {
text: hifi.glyphs.error
color: textColor
size: 40
anchors.right: parent.right
anchors.rightMargin: hifi.dimensions.textPadding - 2
anchors.verticalCenter: parent.verticalCenter
visible: hasClearButton && textField.text !== "";
HiFiGlyphs {
text: hifi.glyphs.error
color: textColor
size: 40
anchors.right: parent.right
anchors.rightMargin: hifi.dimensions.textPadding - 2
anchors.verticalCenter: parent.verticalCenter
visible: hasClearButton && textField.text !== "";
MouseArea {
anchors.fill: parent;
onClicked: {
textField.text = "";
}
MouseArea {
anchors.fill: parent;
onClicked: {
textField.text = "";
}
}
}
placeholderTextColor: isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray
selectedTextColor: hifi.colors.black
selectionColor: hifi.colors.primaryHighlight
padding.left: hasRoundedBorder ? textField.height / 2 : ((isSearchField || textField.leftPermanentGlyph !== "") ? textField.height - 2 : 0) + hifi.dimensions.textPadding
padding.right: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding
}
property color placeholderTextColor: isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray
selectedTextColor: hifi.colors.black
selectionColor: hifi.colors.primaryHighlight
leftPadding: hasRoundedBorder ? textField.height / 2 : ((isSearchField || textField.leftPermanentGlyph !== "") ? textField.height - 2 : 0) + hifi.dimensions.textPadding
rightPadding: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding
HifiControls.Label {
id: textFieldLabel
text: textField.label
@ -165,11 +185,11 @@ TextField {
anchors.left: parent.left
Binding on anchors.right {
when: parent.right
value: parent.right
when: rightAnchorSet
value: textField.right
}
Binding on wrapMode {
when: parent.right
when: rightAnchorSet
value: Text.WordWrap
}

View file

@ -9,8 +9,7 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4 as Original
import QtQuick.Controls.Styles 1.4
import QtQuick.Controls 2.2 as Original
import "../styles-uit"
@ -26,23 +25,16 @@ Original.Button {
readonly property color clickedColor: "#FFFFFF"
readonly property color disabledColor: "#575757"
style: ButtonStyle {
background: Item {}
background: Item {}
label: HiFiGlyphs {
color: control.enabled ? (control.pressed ? control.clickedColor :
(control.hovered ? control.hoverColor : control.normalColor)) :
control.disabledColor
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
anchors {
// Tweak horizontal alignment so that it looks right.
left: parent.left
leftMargin: -0.5
}
text: control.glyph
size: control.size
}
contentItem: HiFiGlyphs {
color: control.enabled ? (control.pressed ? control.clickedColor :
(control.hovered ? control.hoverColor : control.normalColor)) :
control.disabledColor
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: control.glyph
size: control.size
}
}

View file

@ -1,27 +1,35 @@
import QtQuick 2.3
import QtQuick.Controls 1.3 as Original
import QtQuick.Controls.Styles 1.3
import QtQuick.Controls 2.2 as Original
import "."
import "../styles"
import "../controls-uit"
Original.Button {
style: ButtonStyle {
HifiConstants { id: hifi }
padding {
top: 8
left: 12
right: 12
bottom: 8
}
background: Border {
anchors.fill: parent
}
label: Text {
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: control.text
color: control.enabled ? hifi.colors.text : hifi.colors.disabledText
id: control
HifiConstants { id: hifi }
property Action action: null
onActionChanged: {
if (action !== null && action.text !== "") {
control.text = action.text
}
}
padding {
top: 8
left: 12
right: 12
bottom: 8
}
background: Border {
anchors.fill: parent
}
contentItem: Text {
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: control.text
color: control.enabled ? hifi.colors.text : hifi.colors.disabledText
}
}

View file

@ -1,6 +1,4 @@
import QtQuick 2.3
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
Text {
id: root

View file

@ -0,0 +1,575 @@
//
// Desktop.qml
//
// Created by Bradley Austin Davis on 15 Apr 2015
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
import QtQuick.Controls 1.4
import "../dialogs"
import "../js/Utils.js" as Utils
// This is our primary 'desktop' object to which all VR dialogs and windows are childed.
FocusScope {
id: desktop
objectName: "desktop"
anchors.fill: parent
readonly property int invalid_position: -9999;
property rect recommendedRect: Qt.rect(0,0,0,0);
property var expectedChildren;
property bool repositionLocked: true
property bool hmdHandMouseActive: false
onRepositionLockedChanged: {
if (!repositionLocked) {
d.handleSizeChanged();
}
}
onHeightChanged: d.handleSizeChanged();
onWidthChanged: d.handleSizeChanged();
// Controls and windows can trigger this signal to ensure the desktop becomes visible
// when they're opened.
signal showDesktop();
// This is for JS/QML communication, which is unused in the Desktop,
// but not having this here results in spurious warnings about a
// missing signal
signal sendToScript(var message);
// Allows QML/JS to find the desktop through the parent chain
property bool desktopRoot: true
// The VR version of the primary menu
property var rootMenu: Menu {
id: rootMenuId
objectName: "rootMenu"
property var exclusionGroups: ({});
property Component exclusiveGroupMaker: Component {
ExclusiveGroup {
}
}
function addExclusionGroup(qmlAction, exclusionGroup) {
var exclusionGroupId = exclusionGroup.toString();
if(!exclusionGroups[exclusionGroupId]) {
exclusionGroups[exclusionGroupId] = exclusiveGroupMaker.createObject(rootMenuId);
}
qmlAction.exclusiveGroup = exclusionGroups[exclusionGroupId]
}
}
// FIXME: Alpha gradients display as fuschia under QtQuick 2.5 on OSX/AMD
// because shaders are 4.2, and do not include #version declarations.
property bool gradientsSupported: Qt.platform.os != "osx" && !~GL.vendor.indexOf("ATI")
readonly property alias zLevels: zLevels
QtObject {
id: zLevels;
readonly property real normal: 1 // make windows always appear higher than QML overlays and other non-window controls.
readonly property real top: 2000
readonly property real modal: 4000
readonly property real menu: 8000
}
QtObject {
id: d
function handleSizeChanged() {
if (desktop.repositionLocked) {
return;
}
var oldRecommendedRect = recommendedRect;
var newRecommendedRectJS = (typeof Controller === "undefined") ? Qt.rect(0,0,0,0) : Controller.getRecommendedHUDRect();
var newRecommendedRect = Qt.rect(newRecommendedRectJS.x, newRecommendedRectJS.y,
newRecommendedRectJS.width,
newRecommendedRectJS.height);
var oldChildren = expectedChildren;
var newChildren = d.getRepositionChildren();
if (oldRecommendedRect != Qt.rect(0,0,0,0) && oldRecommendedRect != Qt.rect(0,0,1,1)
&& (oldRecommendedRect != newRecommendedRect
|| oldChildren != newChildren)
) {
expectedChildren = newChildren;
d.repositionAll();
}
recommendedRect = newRecommendedRect;
}
function findChild(item, name) {
for (var i = 0; i < item.children.length; ++i) {
if (item.children[i].objectName === name) {
return item.children[i];
}
}
return null;
}
function findParentMatching(item, predicate) {
while (item) {
if (predicate(item)) {
break;
}
item = item.parent;
}
return item;
}
function findMatchingChildren(item, predicate) {
var results = [];
for (var i in item.children) {
var child = item.children[i];
if (predicate(child)) {
results.push(child);
}
}
return results;
}
function isTopLevelWindow(item) {
return item.topLevelWindow;
}
function isAlwaysOnTopWindow(window) {
return window.alwaysOnTop;
}
function isModalWindow(window) {
return window.modality !== Qt.NonModal;
}
function getTopLevelWindows(predicate) {
return findMatchingChildren(desktop, function(child) {
return (isTopLevelWindow(child) && (!predicate || predicate(child)));
});
}
function getDesktopWindow(item) {
return findParentMatching(item, isTopLevelWindow)
}
function fixupZOrder(windows, basis, topWindow) {
windows.sort(function(a, b){ return a.z - b.z; });
if ((topWindow.z >= basis) && (windows[windows.length - 1] === topWindow)) {
return;
}
var lastZ = -1;
var lastTargetZ = basis - 1;
for (var i = 0; i < windows.length; ++i) {
var window = windows[i];
if (!window.visible) {
continue
}
if (topWindow && (topWindow === window)) {
continue
}
if (window.z > lastZ) {
lastZ = window.z;
++lastTargetZ;
}
if (DebugQML) {
console.log("Assigning z order " + lastTargetZ + " to " + window)
}
window.z = lastTargetZ;
}
if (topWindow) {
++lastTargetZ;
if (DebugQML) {
console.log("Assigning z order " + lastTargetZ + " to " + topWindow)
}
topWindow.z = lastTargetZ;
}
return lastTargetZ;
}
function raiseWindow(targetWindow) {
var predicate;
var zBasis;
if (isModalWindow(targetWindow)) {
predicate = isModalWindow;
zBasis = zLevels.modal
} else if (isAlwaysOnTopWindow(targetWindow)) {
predicate = function(window) {
return (isAlwaysOnTopWindow(window) && !isModalWindow(window));
}
zBasis = zLevels.top
} else {
predicate = function(window) {
return (!isAlwaysOnTopWindow(window) && !isModalWindow(window));
}
zBasis = zLevels.normal
}
var windows = getTopLevelWindows(predicate);
fixupZOrder(windows, zBasis, targetWindow);
}
Component.onCompleted: {
//offscreenWindow.activeFocusItemChanged.connect(onWindowFocusChanged);
focusHack.start();
}
function onWindowFocusChanged() {
//console.log("Focus item is " + offscreenWindow.activeFocusItem);
// FIXME this needs more testing before it can go into production
// and I already cant produce any way to have a modal dialog lose focus
// to a non-modal one.
/*
var focusedWindow = getDesktopWindow(offscreenWindow.activeFocusItem);
if (isModalWindow(focusedWindow)) {
return;
}
// new focused window is not modal... check if there are any modal windows
var windows = getTopLevelWindows(isModalWindow);
if (0 === windows.length) {
return;
}
// There are modal windows present, force focus back to the top-most modal window
windows.sort(function(a, b){ return a.z - b.z; });
windows[windows.length - 1].focus = true;
*/
// var focusedItem = offscreenWindow.activeFocusItem ;
// if (DebugQML && focusedItem) {
// var rect = desktop.mapFromItem(focusedItem, 0, 0, focusedItem.width, focusedItem.height);
// focusDebugger.x = rect.x;
// focusDebugger.y = rect.y;
// focusDebugger.width = rect.width
// focusDebugger.height = rect.height
// }
}
function getRepositionChildren(predicate) {
return findMatchingChildren(desktop, function(child) {
return (child.shouldReposition === true && (!predicate || predicate(child)));
});
}
function repositionAll() {
if (desktop.repositionLocked) {
return;
}
var oldRecommendedRect = recommendedRect;
var oldRecommendedDimmensions = { x: oldRecommendedRect.width, y: oldRecommendedRect.height };
var newRecommendedRect = Controller.getRecommendedHUDRect();
var newRecommendedDimmensions = { x: newRecommendedRect.width, y: newRecommendedRect.height };
var windows = d.getTopLevelWindows();
for (var i = 0; i < windows.length; ++i) {
var targetWindow = windows[i];
if (targetWindow.visible) {
repositionWindow(targetWindow, true, oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions);
}
}
// also reposition the other children that aren't top level windows but want to be repositioned
var otherChildren = d.getRepositionChildren();
for (var i = 0; i < otherChildren.length; ++i) {
var child = otherChildren[i];
repositionWindow(child, true, oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions);
}
}
}
property bool pinned: false
property var hiddenChildren: []
function togglePinned() {
pinned = !pinned
}
function isPointOnWindow(point) {
for (var i = 0; i < desktop.visibleChildren.length; i++) {
var child = desktop.visibleChildren[i];
if (child.hasOwnProperty("modality")) {
var mappedPoint = mapToItem(child, point.x, point.y);
if (child.hasOwnProperty("frame")) {
var outLine = child.frame.children[2];
var framePoint = outLine.mapFromGlobal(point.x, point.y);
if (outLine.contains(framePoint)) {
return true;
}
}
if (child.contains(mappedPoint)) {
return true;
}
}
}
return false;
}
function setPinned(newPinned) {
pinned = newPinned
}
property real unpinnedAlpha: 1.0;
Behavior on unpinnedAlpha {
NumberAnimation {
easing.type: Easing.Linear;
duration: 300
}
}
state: "NORMAL"
states: [
State {
name: "NORMAL"
PropertyChanges { target: desktop; unpinnedAlpha: 1.0 }
},
State {
name: "PINNED"
PropertyChanges { target: desktop; unpinnedAlpha: 0.0 }
}
]
transitions: [
Transition {
NumberAnimation { properties: "unpinnedAlpha"; duration: 300 }
}
]
onPinnedChanged: {
if (pinned) {
d.raiseWindow(desktop);
desktop.focus = true;
desktop.forceActiveFocus();
// recalculate our non-pinned children
hiddenChildren = d.findMatchingChildren(desktop, function(child){
return !d.isTopLevelWindow(child) && child.visible && !child.pinned;
});
hiddenChildren.forEach(function(child){
child.opacity = Qt.binding(function(){ return desktop.unpinnedAlpha });
});
}
state = pinned ? "PINNED" : "NORMAL"
}
onShowDesktop: pinned = false
function raise(item) {
var targetWindow = d.getDesktopWindow(item);
if (!targetWindow) {
console.warn("Could not find top level window for " + item);
return;
}
// Fix up the Z-order (takes into account if this is a modal window)
d.raiseWindow(targetWindow);
var setFocus = true;
if (!d.isModalWindow(targetWindow)) {
var modalWindows = d.getTopLevelWindows(d.isModalWindow);
if (modalWindows.length) {
setFocus = false;
}
}
if (setFocus) {
targetWindow.focus = true;
}
showDesktop();
}
function ensureTitleBarVisible(targetWindow) {
// Reposition window to ensure that title bar is vertically inside window.
if (targetWindow.frame && targetWindow.frame.decoration) {
var topMargin = -targetWindow.frame.decoration.anchors.topMargin; // Frame's topMargin is a negative value.
targetWindow.y = Math.max(targetWindow.y, topMargin);
}
}
function centerOnVisible(item) {
var targetWindow = d.getDesktopWindow(item);
if (!targetWindow) {
console.warn("Could not find top level window for " + item);
return;
}
if (typeof Controller === "undefined") {
console.warn("Controller not yet available... can't center");
return;
}
var newRecommendedRectJS = (typeof Controller === "undefined") ? Qt.rect(0,0,0,0) : Controller.getRecommendedHUDRect();
var newRecommendedRect = Qt.rect(newRecommendedRectJS.x, newRecommendedRectJS.y,
newRecommendedRectJS.width,
newRecommendedRectJS.height);
var newRecommendedDimmensions = { x: newRecommendedRect.width, y: newRecommendedRect.height };
var newX = newRecommendedRect.x + ((newRecommendedRect.width - targetWindow.width) / 2);
var newY = newRecommendedRect.y + ((newRecommendedRect.height - targetWindow.height) / 2);
targetWindow.x = newX;
targetWindow.y = newY;
ensureTitleBarVisible(targetWindow);
// If we've noticed that our recommended desktop rect has changed, record that change here.
if (recommendedRect != newRecommendedRect) {
recommendedRect = newRecommendedRect;
}
}
function repositionOnVisible(item) {
var targetWindow = d.getDesktopWindow(item);
if (!targetWindow) {
console.warn("Could not find top level window for " + item);
return;
}
if (typeof Controller === "undefined") {
console.warn("Controller not yet available... can't reposition targetWindow:" + targetWindow);
return;
}
var oldRecommendedRect = recommendedRect;
var oldRecommendedDimmensions = { x: oldRecommendedRect.width, y: oldRecommendedRect.height };
var newRecommendedRect = Controller.getRecommendedHUDRect();
var newRecommendedDimmensions = { x: newRecommendedRect.width, y: newRecommendedRect.height };
repositionWindow(targetWindow, false, oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions);
}
function repositionWindow(targetWindow, forceReposition,
oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions) {
if (desktop.width === 0 || desktop.height === 0) {
return;
}
if (!targetWindow) {
console.warn("Could not find top level window for " + item);
return;
}
var recommended = Controller.getRecommendedHUDRect();
var maxX = recommended.x + recommended.width;
var maxY = recommended.y + recommended.height;
var newPosition = Qt.vector2d(targetWindow.x, targetWindow.y);
// if we asked to force reposition, or if the window is completely outside of the recommended rectangle, reposition it
if (forceReposition || (targetWindow.x > maxX || (targetWindow.x + targetWindow.width) < recommended.x) ||
(targetWindow.y > maxY || (targetWindow.y + targetWindow.height) < recommended.y)) {
newPosition.x = -1
newPosition.y = -1
}
if (newPosition.x === -1 && newPosition.y === -1) {
var originRelativeX = (targetWindow.x - oldRecommendedRect.x);
var originRelativeY = (targetWindow.y - oldRecommendedRect.y);
if (isNaN(originRelativeX)) {
originRelativeX = 0;
}
if (isNaN(originRelativeY)) {
originRelativeY = 0;
}
var fractionX = Utils.clamp(originRelativeX / oldRecommendedDimmensions.x, 0, 1);
var fractionY = Utils.clamp(originRelativeY / oldRecommendedDimmensions.y, 0, 1);
var newX = (fractionX * newRecommendedDimmensions.x) + newRecommendedRect.x;
var newY = (fractionY * newRecommendedDimmensions.y) + newRecommendedRect.y;
newPosition = Qt.vector2d(newX, newY);
}
targetWindow.x = newPosition.x;
targetWindow.y = newPosition.y;
ensureTitleBarVisible(targetWindow);
}
Component { id: messageDialogBuilder; MessageDialog { } }
function messageBox(properties) {
return messageDialogBuilder.createObject(desktop, properties);
}
Component { id: inputDialogBuilder; QueryDialog { } }
function inputDialog(properties) {
return inputDialogBuilder.createObject(desktop, properties);
}
Component { id: customInputDialogBuilder; CustomQueryDialog { } }
function customInputDialog(properties) {
return customInputDialogBuilder.createObject(desktop, properties);
}
Component { id: fileDialogBuilder; FileDialog { } }
function fileDialog(properties) {
return fileDialogBuilder.createObject(desktop, properties);
}
Component { id: assetDialogBuilder; AssetDialog { } }
function assetDialog(properties) {
return assetDialogBuilder.createObject(desktop, properties);
}
function unfocusWindows() {
// First find the active focus item, and unfocus it, all the way
// up the parent chain to the window
var currentFocus = offscreenWindow.activeFocusItem;
var targetWindow = d.getDesktopWindow(currentFocus);
while (currentFocus) {
if (currentFocus === targetWindow) {
break;
}
currentFocus.focus = false;
currentFocus = currentFocus.parent;
}
// Unfocus all windows
var windows = d.getTopLevelWindows();
for (var i = 0; i < windows.length; ++i) {
windows[i].focus = false;
}
// For the desktop to have active focus
desktop.focus = true;
desktop.forceActiveFocus();
}
function openBrowserWindow(request, profile) {
var component = Qt.createComponent("../Browser.qml");
var newWindow = component.createObject(desktop);
newWindow.webView.profile = profile;
request.openIn(newWindow.webView);
}
FocusHack { id: focusHack; }
Rectangle {
id: focusDebugger;
objectName: "focusDebugger"
z: 9999; visible: false; color: "red"
ColorAnimation on color { from: "#7fffff00"; to: "#7f0000ff"; duration: 1000; loops: 9999 }
}
Action {
text: "Toggle Focus Debugger"
shortcut: "Ctrl+Shift+F"
enabled: DebugQML
onTriggered: focusDebugger.visible = !focusDebugger.visible
}
}

View file

@ -8,8 +8,9 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls 2.3 as QQC2
import "../dialogs"
import "../js/Utils.js" as Utils
@ -323,6 +324,18 @@ FocusScope {
return false;
}
function hideDesktopWindows() {
for (var index = 0; index < desktop.visibleChildren.length; index++) {
var child = desktop.visibleChildren[index];
if (child.topLevelWindow && child.hasOwnProperty("modality")) {
var TOOLBAR_NAME = "com.highfidelity.interface.toolbar.system"
if (child.objectName !== TOOLBAR_NAME) {
child.setShown(false);
}
}
}
}
function setPinned(newPinned) {
pinned = newPinned
}
@ -565,7 +578,7 @@ FocusScope {
ColorAnimation on color { from: "#7fffff00"; to: "#7f0000ff"; duration: 1000; loops: 9999 }
}
Action {
QQC2.Action {
text: "Toggle Focus Debugger"
shortcut: "Ctrl+Shift+F"
enabled: DebugQML

View file

@ -0,0 +1,338 @@
//
// CustomQueryDialog.qml
//
// Created by Zander Otavka on 7/14/16
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7;
import QtQuick.Dialogs 1.2 as OriginalDialogs;
import QtQuick.Controls 1.4;
import "../controls-uit";
import "../styles-uit";
import "../windows";
ModalWindow {
id: root;
HifiConstants { id: hifi; }
implicitWidth: 640;
implicitHeight: 320;
visible: true;
keyboardOverride: true // Disable ModalWindow's keyboard.
signal selected(var result);
signal canceled();
property int icon: hifi.icons.none;
property string iconText: "";
property int iconSize: 35;
onIconChanged: updateIcon();
property var textInput;
property var comboBox;
property var checkBox;
onTextInputChanged: {
if (textInput && textInput.text !== undefined) {
textField.text = textInput.text;
}
}
onComboBoxChanged: {
if (comboBox && comboBox.index !== undefined) {
comboBoxField.currentIndex = comboBox.index;
}
}
onCheckBoxChanged: {
if (checkBox && checkBox.checked !== undefined) {
checkBoxField.checked = checkBox.checked;
}
}
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
onKeyboardRaisedChanged: d.resize();
property var warning: "";
property var result;
property var implicitCheckState: null;
property int titleWidth: 0;
onTitleWidthChanged: d.resize();
function updateIcon() {
if (!root) {
return;
}
iconText = hifi.glyphForIcon(root.icon);
}
function updateCheckbox() {
if (checkBox.disableForItems) {
var currentItemInDisableList = false;
for (var i in checkBox.disableForItems) {
if (comboBoxField.currentIndex === checkBox.disableForItems[i]) {
currentItemInDisableList = true;
break;
}
}
if (currentItemInDisableList) {
checkBoxField.enabled = false;
if (checkBox.checkStateOnDisable !== null && checkBox.checkStateOnDisable !== undefined) {
root.implicitCheckState = checkBoxField.checked;
checkBoxField.checked = checkBox.checkStateOnDisable;
}
root.warning = checkBox.warningOnDisable;
} else {
checkBoxField.enabled = true;
if (root.implicitCheckState !== null) {
checkBoxField.checked = root.implicitCheckState;
root.implicitCheckState = null;
}
root.warning = "";
}
}
}
Item {
clip: true;
width: pane.width;
height: pane.height;
anchors.margins: 0;
QtObject {
id: d;
readonly property int minWidth: 480
readonly property int maxWdith: 1280
readonly property int minHeight: 120
readonly property int maxHeight: 720
function resize() {
var targetWidth = Math.max(titleWidth, pane.width);
var targetHeight = (textField.visible ? textField.controlHeight + hifi.dimensions.contentSpacing.y : 0) +
(extraInputs.visible ? extraInputs.height + hifi.dimensions.contentSpacing.y : 0) +
(buttons.height + 3 * hifi.dimensions.contentSpacing.y) +
((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + hifi.dimensions.contentSpacing.y) : 0);
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth);
root.height = (targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ?
d.maxHeight : targetHeight);
}
}
Item {
anchors {
top: parent.top;
bottom: extraInputs.visible ? extraInputs.top : buttons.top;
left: parent.left;
right: parent.right;
margins: 0;
}
// FIXME make a text field type that can be bound to a history for autocompletion
TextField {
id: textField;
label: root.textInput.label;
focus: root.textInput ? true : false;
visible: root.textInput ? true : false;
anchors {
left: parent.left;
right: parent.right;
bottom: keyboard.top;
bottomMargin: hifi.dimensions.contentSpacing.y;
}
}
Keyboard {
id: keyboard
raised: keyboardEnabled && keyboardRaised
numeric: punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
bottomMargin: raised ? hifi.dimensions.contentSpacing.y : 0
}
}
}
Item {
id: extraInputs;
visible: Boolean(root.checkBox || root.comboBox);
anchors {
left: parent.left;
right: parent.right;
bottom: buttons.top;
bottomMargin: hifi.dimensions.contentSpacing.y;
}
height: comboBoxField.controlHeight;
onHeightChanged: d.resize();
onWidthChanged: d.resize();
CheckBox {
id: checkBoxField;
text: root.checkBox.label;
focus: Boolean(root.checkBox);
visible: Boolean(root.checkBox);
anchors {
left: parent.left;
bottom: parent.bottom;
leftMargin: 6; // Magic number to align with warning icon
bottomMargin: 6;
}
}
ComboBox {
id: comboBoxField;
label: root.comboBox.label;
focus: Boolean(root.comboBox);
visible: Boolean(root.comboBox);
Binding on x {
when: comboBoxField.visible
value: !checkBoxField.visible ? buttons.x : acceptButton.x
}
Binding on width {
when: comboBoxField.visible
value: !checkBoxField.visible ? buttons.width : buttons.width - acceptButton.x
}
anchors {
right: parent.right;
bottom: parent.bottom;
}
model: root.comboBox ? root.comboBox.items : [];
onAccepted: {
updateCheckbox();
focus = true;
}
}
}
Row {
id: buttons;
focus: true;
spacing: hifi.dimensions.contentSpacing.x;
layoutDirection: Qt.RightToLeft;
onHeightChanged: d.resize();
onWidthChanged: {
d.resize();
resizeWarningText();
}
anchors {
bottom: parent.bottom;
left: parent.left;
right: parent.right;
bottomMargin: hifi.dimensions.contentSpacing.y;
}
function resizeWarningText() {
var rowWidth = buttons.width;
var buttonsWidth = acceptButton.width + cancelButton.width + hifi.dimensions.contentSpacing.x * 2;
var warningIconWidth = warningIcon.width + hifi.dimensions.contentSpacing.x;
warningText.width = rowWidth - buttonsWidth - warningIconWidth;
}
Button {
id: cancelButton;
action: cancelAction;
}
Button {
id: acceptButton;
action: acceptAction;
}
Text {
id: warningText;
visible: Boolean(root.warning);
text: root.warning;
wrapMode: Text.WordWrap;
font.italic: true;
maximumLineCount: 2;
}
HiFiGlyphs {
id: warningIcon;
visible: Boolean(root.warning);
text: hifi.glyphs.alert;
size: hifi.dimensions.controlLineHeight;
width: 20 // Line up with checkbox.
}
}
Action {
id: cancelAction;
text: qsTr("Cancel");
shortcut: "Esc";
onTriggered: {
root.result = null;
root.canceled();
// FIXME we are leaking memory to avoid a crash
// root.destroy();
root.disableFade = true
visible = false;
}
}
Action {
id: acceptAction;
text: qsTr("Add");
shortcut: "Return"
onTriggered: {
var result = {};
if (textInput) {
result.textInput = textField.text;
}
if (comboBox) {
result.comboBox = comboBoxField.currentIndex;
result.comboBoxText = comboBoxField.currentText;
}
if (checkBox) {
result.checkBox = checkBoxField.enabled ? checkBoxField.checked : null;
}
root.result = JSON.stringify(result);
root.selected(root.result);
// FIXME we are leaking memory to avoid a crash
// root.destroy();
root.disableFade = true
visible = false;
}
}
}
Keys.onPressed: {
if (!visible) {
return;
}
switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
cancelAction.trigger();
event.accepted = true;
break;
case Qt.Key_Return:
case Qt.Key_Enter:
acceptAction.trigger();
event.accepted = true;
break;
}
}
Component.onCompleted: {
keyboardEnabled = HMD.active;
updateIcon();
updateCheckbox();
d.resize();
textField.forceActiveFocus();
}
}

View file

@ -0,0 +1,840 @@
//
// FileDialog.qml
//
// Created by Bradley Austin Davis on 14 Jan 2016
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
import Qt.labs.folderlistmodel 2.1
import Qt.labs.settings 1.0
import QtQuick.Dialogs 1.2 as OriginalDialogs
import QtQuick.Controls 1.4
import ".."
import "../controls-uit"
import "../styles-uit"
import "../windows"
import "fileDialog"
//FIXME implement shortcuts for favorite location
ModalWindow {
id: root
resizable: true
implicitWidth: 480
implicitHeight: 360 + (fileDialogItem.keyboardEnabled && fileDialogItem.keyboardRaised ? keyboard.raisedHeight + hifi.dimensions.contentSpacing.y : 0)
minSize: Qt.vector2d(360, 240)
draggable: true
HifiConstants { id: hifi }
property var filesModel: ListModel { }
Settings {
category: "FileDialog"
property alias width: root.width
property alias height: root.height
property alias x: root.x
property alias y: root.y
}
// Set from OffscreenUi::getOpenFile()
property alias caption: root.title;
// Set from OffscreenUi::getOpenFile()
property alias dir: fileTableModel.folder;
// Set from OffscreenUi::getOpenFile()
property alias filter: selectionType.filtersString;
// Set from OffscreenUi::getOpenFile()
property int options; // <-- FIXME unused
property string iconText: root.title !== "" ? hifi.glyphs.scriptUpload : ""
property int iconSize: 40
property bool selectDirectory: false;
property bool showHidden: false;
// FIXME implement
property bool multiSelect: false;
property bool saveDialog: false;
property var helper: fileDialogHelper
property alias model: fileTableView.model
property var drives: helper.drives()
property int titleWidth: 0
signal selectedFile(var file);
signal canceled();
signal selected(int button);
function click(button) {
clickedButton = button;
selected(button);
destroy();
}
property int clickedButton: OriginalDialogs.StandardButton.NoButton;
Component.onCompleted: {
console.log("Helper " + helper + " drives " + drives);
fileDialogItem.keyboardEnabled = HMD.active;
// HACK: The following lines force the model to initialize properly such that the go-up button
// works properly from the initial screen.
var initialFolder = folderListModel.folder;
fileTableModel.folder = helper.pathToUrl(drives[0]);
fileTableModel.folder = initialFolder;
iconText = root.title !== "" ? hifi.glyphs.scriptUpload : "";
// Clear selection when click on external frame.
frameClicked.connect(function() { d.clearSelection(); });
if (selectDirectory) {
currentSelection.text = d.capitalizeDrive(helper.urlToPath(initialFolder));
d.currentSelectionIsFolder = true;
d.currentSelectionUrl = initialFolder;
}
helper.contentsChanged.connect(function() {
if (folderListModel) {
// Make folderListModel refresh.
var save = folderListModel.folder;
folderListModel.folder = "";
folderListModel.folder = save;
}
});
focusTimer.start();
}
Timer {
id: focusTimer
interval: 10
running: false
repeat: false
onTriggered: {
fileTableView.contentItem.forceActiveFocus();
}
}
Item {
id: fileDialogItem
clip: true
width: pane.width
height: pane.height
anchors.margins: 0
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
MouseArea {
// Clear selection when click on internal unused area.
anchors.fill: parent
drag.target: root
onClicked: {
d.clearSelection();
// Defocus text field so that the keyboard gets hidden.
// Clicking also breaks keyboard navigation apart from backtabbing to cancel
frame.forceActiveFocus();
}
}
Row {
id: navControls
anchors {
top: parent.top
topMargin: hifi.dimensions.contentMargin.y
left: parent.left
}
spacing: hifi.dimensions.contentSpacing.x
GlyphButton {
id: upButton
glyph: hifi.glyphs.levelUp
width: height
size: 30
enabled: fileTableModel.parentFolder && fileTableModel.parentFolder !== ""
onClicked: d.navigateUp();
Keys.onReturnPressed: { d.navigateUp(); }
KeyNavigation.tab: homeButton
KeyNavigation.backtab: upButton
KeyNavigation.left: upButton
KeyNavigation.right: homeButton
}
GlyphButton {
id: homeButton
property var destination: helper.home();
glyph: hifi.glyphs.home
size: 28
width: height
enabled: d.homeDestination ? true : false
onClicked: d.navigateHome();
Keys.onReturnPressed: { d.navigateHome(); }
KeyNavigation.tab: fileTableView.contentItem
KeyNavigation.backtab: upButton
KeyNavigation.left: upButton
}
}
ComboBox {
id: pathSelector
anchors {
top: parent.top
topMargin: hifi.dimensions.contentMargin.y
left: navControls.right
leftMargin: hifi.dimensions.contentSpacing.x
right: parent.right
}
property var lastValidFolder: helper.urlToPath(fileTableModel.folder)
function calculatePathChoices(folder) {
var folders = folder.split("/"),
choices = [],
i, length;
if (folders[folders.length - 1] === "") {
folders.pop();
}
choices.push(folders[0]);
for (i = 1, length = folders.length; i < length; i++) {
choices.push(choices[i - 1] + "/" + folders[i]);
}
if (folders[0] === "") {
// Special handling for OSX root dir.
choices[0] = "/";
}
choices.reverse();
if (drives && drives.length > 1) {
choices.push("This PC");
}
if (choices.length > 0) {
pathSelector.model = choices;
}
}
onLastValidFolderChanged: {
var folder = d.capitalizeDrive(lastValidFolder);
calculatePathChoices(folder);
}
onCurrentTextChanged: {
var folder = currentText;
if (/^[a-zA-z]:$/.test(folder)) {
folder = "file:///" + folder + "/";
} else if (folder === "This PC") {
folder = "file:///";
} else {
folder = helper.pathToUrl(folder);
}
if (helper.urlToPath(folder).toLowerCase() !== helper.urlToPath(fileTableModel.folder).toLowerCase()) {
if (root.selectDirectory) {
currentSelection.text = currentText !== "This PC" ? currentText : "";
d.currentSelectionUrl = helper.pathToUrl(currentText);
}
fileTableModel.folder = folder;
}
}
KeyNavigation.up: fileTableView.contentItem
KeyNavigation.down: fileTableView.contentItem
KeyNavigation.tab: fileTableView.contentItem
KeyNavigation.backtab: fileTableView.contentItem
KeyNavigation.left: fileTableView.contentItem
KeyNavigation.right: fileTableView.contentItem
}
QtObject {
id: d
property var currentSelectionUrl;
readonly property string currentSelectionPath: helper.urlToPath(currentSelectionUrl);
property bool currentSelectionIsFolder;
property var backStack: []
property var tableViewConnection: Connections { target: fileTableView; onCurrentRowChanged: d.update(); }
property var modelConnection: Connections { target: fileTableModel; onFolderChanged: d.update(); }
property var homeDestination: helper.home();
function capitalizeDrive(path) {
// Consistently capitalize drive letter for Windows.
if (/[a-zA-Z]:/.test(path)) {
return path.charAt(0).toUpperCase() + path.slice(1);
}
return path;
}
function update() {
var row = fileTableView.currentRow;
if (row === -1) {
if (!root.selectDirectory) {
currentSelection.text = "";
currentSelectionIsFolder = false;
}
return;
}
currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath);
currentSelectionIsFolder = fileTableView.model !== filesModel ?
fileTableView.model.isFolder(row) :
fileTableModel.isFolder(row);
if (root.selectDirectory || !currentSelectionIsFolder) {
currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl));
} else {
currentSelection.text = "";
}
}
function navigateUp() {
if (fileTableModel.parentFolder && fileTableModel.parentFolder !== "") {
fileTableModel.folder = fileTableModel.parentFolder;
return true;
}
}
function navigateHome() {
fileTableModel.folder = homeDestination;
return true;
}
function clearSelection() {
fileTableView.selection.clear();
fileTableView.currentRow = -1;
update();
}
}
FolderListModel {
id: folderListModel
nameFilters: selectionType.currentFilter
showDirsFirst: true
showDotAndDotDot: false
showFiles: !root.selectDirectory
Component.onCompleted: {
showFiles = !root.selectDirectory
}
onFolderChanged: {
fileTableModel.update(); // Update once the data from the folder change is available.
}
function getItem(index, field) {
return get(index, field);
}
}
ListModel {
// Emulates FolderListModel but contains drive data.
id: driveListModel
property int count: 1
Component.onCompleted: initialize();
function initialize() {
var drive,
i;
count = drives.length;
for (i = 0; i < count; i++) {
drive = drives[i].slice(0, -1); // Remove trailing "/".
append({
fileName: drive,
fileModified: new Date(0),
fileSize: 0,
filePath: drive + "/",
fileIsDir: true,
fileNameSort: drive.toLowerCase()
});
}
}
function getItem(index, field) {
return get(index)[field];
}
}
Component {
id: filesModelBuilder
ListModel { }
}
QtObject {
id: fileTableModel
// FolderListModel has a couple of problems:
// 1) Files and directories sort case-sensitively: https://bugreports.qt.io/browse/QTBUG-48757
// 2) Cannot browse up to the "computer" level to view Windows drives: https://bugreports.qt.io/browse/QTBUG-42901
//
// To solve these problems an intermediary ListModel is used that implements proper sorting and can be populated with
// drive information when viewing at the computer level.
property var folder
property int sortOrder: Qt.AscendingOrder
property int sortColumn: 0
property var model: folderListModel
property string parentFolder: calculateParentFolder();
readonly property string rootFolder: "file:///"
function calculateParentFolder() {
if (model === folderListModel) {
if (folderListModel.parentFolder.toString() === "" && driveListModel.count > 1) {
return rootFolder;
} else {
return folderListModel.parentFolder;
}
} else {
return "";
}
}
onFolderChanged: {
if (folder === rootFolder) {
model = driveListModel;
helper.monitorDirectory("");
update();
} else {
var needsUpdate = model === driveListModel && folder === folderListModel.folder;
model = folderListModel;
folderListModel.folder = folder;
helper.monitorDirectory(helper.urlToPath(folder));
if (needsUpdate) {
update();
}
}
}
function isFolder(row) {
if (row === -1) {
return false;
}
return filesModel.get(row).fileIsDir;
}
function get(row) {
return filesModel.get(row)
}
function update() {
var dataFields = ["fileName", "fileModified", "fileSize"],
sortFields = ["fileNameSort", "fileModified", "fileSize"],
dataField = dataFields[sortColumn],
sortField = sortFields[sortColumn],
sortValue,
fileName,
fileIsDir,
comparisonFunction,
lower,
middle,
upper,
rows = 0,
i;
var newFilesModel = filesModelBuilder.createObject(root);
comparisonFunction = sortOrder === Qt.AscendingOrder
? function(a, b) { return a < b; }
: function(a, b) { return a > b; }
for (i = 0; i < model.count; i++) {
fileName = model.getItem(i, "fileName");
fileIsDir = model.getItem(i, "fileIsDir");
sortValue = model.getItem(i, dataField);
if (dataField === "fileName") {
// Directories first by prefixing a "*".
// Case-insensitive.
sortValue = (fileIsDir ? "*" : "") + sortValue.toLowerCase();
}
lower = 0;
upper = rows;
while (lower < upper) {
middle = Math.floor((lower + upper) / 2);
var lessThan;
if (comparisonFunction(sortValue, newFilesModel.get(middle)[sortField])) {
lessThan = true;
upper = middle;
} else {
lessThan = false;
lower = middle + 1;
}
}
newFilesModel.insert(lower, {
fileName: fileName,
fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")),
fileSize: model.getItem(i, "fileSize"),
filePath: model.getItem(i, "filePath"),
fileIsDir: fileIsDir,
fileNameSort: (fileIsDir ? "*" : "") + fileName.toLowerCase()
});
rows++;
}
filesModel = newFilesModel;
d.clearSelection();
}
}
Table {
id: fileTableView
colorScheme: hifi.colorSchemes.light
anchors {
top: navControls.bottom
topMargin: hifi.dimensions.contentSpacing.y
left: parent.left
right: parent.right
bottom: currentSelection.top
bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height
}
headerVisible: !selectDirectory
onDoubleClicked: navigateToRow(row);
Keys.onReturnPressed: navigateToCurrentRow();
Keys.onEnterPressed: navigateToCurrentRow();
sortIndicatorColumn: 0
sortIndicatorOrder: Qt.AscendingOrder
sortIndicatorVisible: true
model: filesModel
function updateSort() {
fileTableModel.sortOrder = sortIndicatorOrder;
fileTableModel.sortColumn = sortIndicatorColumn;
fileTableModel.update();
}
onSortIndicatorColumnChanged: { updateSort(); }
onSortIndicatorOrderChanged: { updateSort(); }
itemDelegate: Item {
clip: true
FiraSansSemiBold {
text: getText();
elide: styleData.elideMode
anchors {
left: parent.left
leftMargin: hifi.dimensions.tablePadding
right: parent.right
rightMargin: hifi.dimensions.tablePadding
verticalCenter: parent.verticalCenter
}
size: hifi.fontSizes.tableText
color: hifi.colors.baseGrayHighlight
font.family: (styleData.row !== -1 && fileTableView.model.get(styleData.row).fileIsDir)
? "Fira Sans SemiBold" : "Fira Sans"
function getText() {
if (styleData.row === -1) {
return styleData.value;
}
switch (styleData.column) {
case 1: return fileTableView.model.get(styleData.row).fileIsDir ? "" : styleData.value;
case 2: return fileTableView.model.get(styleData.row).fileIsDir ? "" : formatSize(styleData.value);
default: return styleData.value;
}
}
function formatSize(size) {
var suffixes = [ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" ];
var suffixIndex = 0
while ((size / 1024.0) > 1.1) {
size /= 1024.0;
++suffixIndex;
}
size = Math.round(size*1000)/1000;
size = size.toLocaleString()
return size + " " + suffixes[suffixIndex];
}
}
}
TableViewColumn {
id: fileNameColumn
role: "fileName"
title: "Name"
width: (selectDirectory ? 1.0 : 0.5) * fileTableView.width
movable: false
resizable: true
}
TableViewColumn {
id: fileModifiedColumn
role: "fileModified"
title: "Date"
width: 0.3 * fileTableView.width
movable: false
resizable: true
visible: !selectDirectory
}
TableViewColumn {
role: "fileSize"
title: "Size"
width: fileTableView.width - fileNameColumn.width - fileModifiedColumn.width
movable: false
resizable: true
visible: !selectDirectory
}
function navigateToRow(row) {
currentRow = row;
navigateToCurrentRow();
}
function navigateToCurrentRow() {
var currentModel = fileTableView.model !== filesModel ? fileTableView.model : fileTableModel
var row = fileTableView.currentRow
var isFolder = currentModel.isFolder(row);
var file = currentModel.get(row).filePath;
if (isFolder) {
currentModel.folder = helper.pathToUrl(file);
} else {
okAction.trigger();
}
}
property string prefix: ""
function addToPrefix(event) {
if (!event.text || event.text === "") {
return false;
}
var newPrefix = prefix + event.text.toLowerCase();
var matchedIndex = -1;
for (var i = 0; i < model.count; ++i) {
var name = model !== filesModel ? model.get(i).fileName.toLowerCase() :
filesModel.get(i).fileName.toLowerCase();
if (0 === name.indexOf(newPrefix)) {
matchedIndex = i;
break;
}
}
if (matchedIndex !== -1) {
fileTableView.selection.clear();
fileTableView.selection.select(matchedIndex);
fileTableView.currentRow = matchedIndex;
fileTableView.prefix = newPrefix;
}
prefixClearTimer.restart();
return true;
}
Timer {
id: prefixClearTimer
interval: 1000
repeat: false
running: false
onTriggered: fileTableView.prefix = "";
}
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Backspace:
case Qt.Key_Tab:
case Qt.Key_Backtab:
event.accepted = false;
break;
case Qt.Key_Escape:
event.accepted = true;
root.click(OriginalDialogs.StandardButton.Cancel);
break;
default:
if (addToPrefix(event)) {
event.accepted = true
} else {
event.accepted = false;
}
break;
}
}
KeyNavigation.tab: root.saveDialog ? currentSelection : openButton
}
TextField {
id: currentSelection
label: selectDirectory ? "Directory:" : "File name:"
anchors {
left: parent.left
right: selectionType.visible ? selectionType.left: parent.right
rightMargin: selectionType.visible ? hifi.dimensions.contentSpacing.x : 0
bottom: keyboard.top
bottomMargin: hifi.dimensions.contentSpacing.y
}
readOnly: !root.saveDialog
activeFocusOnTab: !readOnly
onActiveFocusChanged: if (activeFocus) { selectAll(); }
onAccepted: okAction.trigger();
KeyNavigation.up: fileTableView.contentItem
KeyNavigation.down: openButton
KeyNavigation.tab: openButton
KeyNavigation.backtab: fileTableView.contentItem
}
FileTypeSelection {
id: selectionType
anchors {
top: currentSelection.top
left: buttonRow.left
right: parent.right
}
visible: !selectDirectory && filtersCount > 1
}
Keyboard {
id: keyboard
raised: parent.keyboardEnabled && parent.keyboardRaised
numeric: parent.punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: buttonRow.top
bottomMargin: visible ? hifi.dimensions.contentSpacing.y : 0
}
}
Row {
id: buttonRow
anchors {
right: parent.right
bottom: parent.bottom
}
spacing: hifi.dimensions.contentSpacing.y
Button {
id: openButton
color: hifi.buttons.blue
action: okAction
Keys.onReturnPressed: okAction.trigger()
KeyNavigation.right: cancelButton
KeyNavigation.up: root.saveDialog ? currentSelection : fileTableView.contentItem
KeyNavigation.tab: cancelButton
}
Button {
id: cancelButton
action: cancelAction
Keys.onReturnPressed: { cancelAction.trigger() }
KeyNavigation.left: openButton
KeyNavigation.up: root.saveDialog ? currentSelection : fileTableView.contentItem
KeyNavigation.backtab: openButton
}
}
Action {
id: okAction
text: currentSelection.text ? (root.selectDirectory && fileTableView.currentRow === -1 ? "Choose" : (root.saveDialog ? "Save" : "Open")) : "Open"
enabled: currentSelection.text || !root.selectDirectory && d.currentSelectionIsFolder ? true : false
onTriggered: {
if (!root.selectDirectory && !d.currentSelectionIsFolder
|| root.selectDirectory && fileTableView.currentRow === -1) {
okActionTimer.start();
} else {
fileTableView.navigateToCurrentRow();
}
}
}
Timer {
id: okActionTimer
interval: 50
running: false
repeat: false
onTriggered: {
if (!root.saveDialog) {
selectedFile(d.currentSelectionUrl);
root.destroy()
return;
}
// Handle the ambiguity between different cases
// * typed name (with or without extension)
// * full path vs relative vs filename only
var selection = helper.saveHelper(currentSelection.text, root.dir, selectionType.currentFilter);
if (!selection) {
desktop.messageBox({ icon: OriginalDialogs.StandardIcon.Warning, text: "Unable to parse selection" })
return;
}
if (helper.urlIsDir(selection)) {
root.dir = selection;
currentSelection.text = "";
return;
}
// Check if the file is a valid target
if (!helper.urlIsWritable(selection)) {
desktop.messageBox({
icon: OriginalDialogs.StandardIcon.Warning,
text: "Unable to write to location " + selection
})
return;
}
if (helper.urlExists(selection)) {
var messageBox = desktop.messageBox({
icon: OriginalDialogs.StandardIcon.Question,
buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No,
text: "Do you wish to overwrite " + selection + "?",
});
var result = messageBox.exec();
if (OriginalDialogs.StandardButton.Yes !== result) {
return;
}
}
console.log("Selecting " + selection)
selectedFile(selection);
root.destroy();
}
}
Action {
id: cancelAction
text: "Cancel"
onTriggered: { canceled(); root.shown = false; }
}
}
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Backspace:
event.accepted = d.navigateUp();
break;
case Qt.Key_Home:
event.accepted = d.navigateHome();
break;
case Qt.Key_Escape:
event.accepted = true;
root.click(OriginalDialogs.StandardButton.Cancel);
break;
}
}
}

View file

@ -0,0 +1,231 @@
//
// QueryDialog.qml
//
// Created by Bradley Austin Davis on 22 Jan 2016
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
import QtQuick.Controls 1.4
import "../controls-uit"
import "../styles-uit"
import "../windows"
ModalWindow {
id: root
HifiConstants { id: hifi }
implicitWidth: 640
implicitHeight: 320
visible: true
keyboardOverride: true // Disable ModalWindow's keyboard.
signal selected(var result);
signal canceled();
property int icon: hifi.icons.none
property string iconText: ""
property int iconSize: 35
onIconChanged: updateIcon();
property var items;
property string label
property var result;
property alias current: textResult.text
// For text boxes
property alias placeholderText: textResult.placeholderText
// For combo boxes
property bool editable: true;
property int titleWidth: 0
onTitleWidthChanged: d.resize();
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
onKeyboardRaisedChanged: d.resize();
function updateIcon() {
if (!root) {
return;
}
iconText = hifi.glyphForIcon(root.icon);
}
Item {
id: modalWindowItem
clip: true
width: pane.width
height: pane.height
anchors.margins: 0
QtObject {
id: d
readonly property int minWidth: 480
readonly property int maxWdith: 1280
readonly property int minHeight: 120
readonly property int maxHeight: 720
function resize() {
var targetWidth = Math.max(titleWidth, pane.width)
var targetHeight = (items ? comboBox.controlHeight : textResult.controlHeight) + 5 * hifi.dimensions.contentSpacing.y + buttons.height
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth);
root.height = ((targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)) + ((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + 2 * hifi.dimensions.contentSpacing.y) : 0)
}
}
Item {
anchors {
top: parent.top
bottom: keyboard.top;
left: parent.left;
right: parent.right;
margins: 0
bottomMargin: 2 * hifi.dimensions.contentSpacing.y
}
// FIXME make a text field type that can be bound to a history for autocompletion
TextField {
id: textResult
label: root.label
visible: items ? false : true
anchors {
left: parent.left;
right: parent.right;
bottom: parent.bottom
}
KeyNavigation.down: acceptButton
KeyNavigation.tab: acceptButton
}
ComboBox {
id: comboBox
label: root.label
visible: items ? true : false
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
model: items ? items : []
KeyNavigation.down: acceptButton
KeyNavigation.tab: acceptButton
}
}
property alias keyboardOverride: root.keyboardOverride
property alias keyboardRaised: root.keyboardRaised
property alias punctuationMode: root.punctuationMode
Keyboard {
id: keyboard
raised: keyboardEnabled && keyboardRaised
numeric: punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: buttons.top
bottomMargin: raised ? 2 * hifi.dimensions.contentSpacing.y : 0
}
}
Flow {
id: buttons
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
layoutDirection: Qt.RightToLeft
anchors {
bottom: parent.bottom
right: parent.right
margins: 0
bottomMargin: hifi.dimensions.contentSpacing.y
}
Button {
id: cancelButton
action: cancelAction
KeyNavigation.left: acceptButton
KeyNavigation.up: items ? comboBox : textResult
KeyNavigation.backtab: acceptButton
}
Button {
id: acceptButton
action: acceptAction
KeyNavigation.right: cancelButton
KeyNavigation.up: items ? comboBox : textResult
KeyNavigation.tab: cancelButton
KeyNavigation.backtab: items ? comboBox : textResult
}
}
Action {
id: cancelAction
text: qsTr("Cancel");
shortcut: "Esc"
onTriggered: {
root.canceled();
// FIXME we are leaking memory to avoid a crash
// root.destroy();
root.disableFade = true
visible = false;
}
}
Action {
id: acceptAction
text: qsTr("OK");
shortcut: "Return"
onTriggered: {
root.result = items ? comboBox.currentText : textResult.text
root.selected(root.result);
// FIXME we are leaking memory to avoid a crash
// root.destroy();
root.disableFade = true
visible = false;
}
}
}
Keys.onPressed: {
if (!visible) {
return
}
switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
cancelAction.trigger()
event.accepted = true;
break;
case Qt.Key_Return:
case Qt.Key_Enter:
if (acceptButton.focus) {
acceptAction.trigger()
} else if (cancelButton.focus) {
cancelAction.trigger()
} else if (comboBox.focus || comboBox.popup.focus) {
comboBox.showList()
}
event.accepted = true;
break;
}
}
Component.onCompleted: {
keyboardEnabled = HMD.active;
updateIcon();
d.resize();
if (items) {
comboBox.forceActiveFocus()
} else {
textResult.forceActiveFocus()
}
}
}

View file

@ -9,7 +9,6 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import Qt.labs.settings 1.0
import "../styles-uit"

View file

@ -8,9 +8,9 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5;
import QtQuick.Controls 1.4;
import QtQuick 2.7;
import QtQuick.Dialogs 1.2 as OriginalDialogs;
import QtQuick.Controls 2.3
import "../controls-uit";
import "../styles-uit";
@ -122,12 +122,6 @@ ModalWindow {
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth);
root.height = (targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ?
d.maxHeight : targetHeight);
if (checkBoxField.visible && comboBoxField.visible) {
checkBoxField.width = extraInputs.width / 2;
comboBoxField.width = extraInputs.width / 2;
} else if (!checkBoxField.visible && comboBoxField.visible) {
comboBoxField.width = extraInputs.width;
}
}
}
@ -198,6 +192,15 @@ ModalWindow {
label: root.comboBox.label;
focus: Boolean(root.comboBox);
visible: Boolean(root.comboBox);
Binding on x {
when: comboBoxField.visible
value: !checkBoxField.visible ? buttons.x : acceptButton.x
}
Binding on width {
when: comboBoxField.visible
value: !checkBoxField.visible ? buttons.width : buttons.width - acceptButton.x
}
anchors {
right: parent.right;
bottom: parent.bottom;
@ -266,7 +269,7 @@ ModalWindow {
Action {
id: cancelAction;
text: qsTr("Cancel");
shortcut: Qt.Key_Escape;
shortcut: "Esc";
onTriggered: {
root.result = null;
root.canceled();
@ -281,7 +284,7 @@ ModalWindow {
Action {
id: acceptAction;
text: qsTr("Add");
shortcut: Qt.Key_Return;
shortcut: "Return"
onTriggered: {
var result = {};
if (textInput) {

View file

@ -8,12 +8,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick 2.7
import Qt.labs.folderlistmodel 2.1
import Qt.labs.settings 1.0
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2 as OriginalDialogs
import QtQuick.Controls 1.4 as QQC1
import QtQuick.Controls 2.3
import ".."
import "../controls-uit"
@ -574,7 +574,7 @@ ModalWindow {
}
}
TableViewColumn {
QQC1.TableViewColumn {
id: fileNameColumn
role: "fileName"
title: "Name"
@ -582,7 +582,7 @@ ModalWindow {
movable: false
resizable: true
}
TableViewColumn {
QQC1.TableViewColumn {
id: fileModifiedColumn
role: "fileModified"
title: "Date"
@ -591,7 +591,7 @@ ModalWindow {
resizable: true
visible: !selectDirectory
}
TableViewColumn {
QQC1.TableViewColumn {
role: "fileSize"
title: "Size"
width: fileTableView.width - fileNameColumn.width - fileModifiedColumn.width

View file

@ -9,7 +9,6 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2 as OriginalDialogs
import "../controls-uit"

Some files were not shown because too many files have changed in this diff Show more