First pass on preferences dialog

Conflicts:
	tests/ui/qmlscratch.pro
This commit is contained in:
Brad Davis 2016-01-18 15:54:20 -08:00
parent 6cd6c3038f
commit 205ab05506
14 changed files with 991 additions and 3 deletions

View file

@ -0,0 +1,82 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2 as OriginalDialogs
import Qt.labs.settings 1.0
import "../../controls" as HifiControls
import "../../windows"
import "./preferences"
Window {
id: root
objectName: "Preferences"
title: "Preferences"
resizable: true
destroyOnInvisible: true
width: 500
height: 577
property var sections: []
function saveAll() {
for (var i = 0; i < sections.length; ++i) {
var section = sections[i];
section.saveAll();
}
destroy();
}
Rectangle {
anchors.fill: parent
clip: true
color: "white"
Settings {
category: "Overlay.Preferences"
property alias x: root.x
property alias y: root.y
}
Component {
id: sectionBuilder
Section { }
}
Component.onCompleted: {
Preferences.loadAll();
var categories = Preferences.categories;
for (var i = 0; i < categories.length; ++i) {
var category = categories[i];
sections.push(sectionBuilder.createObject(prefControls, { name: category }));
}
}
Flickable {
id: flickable
clip: true
interactive: true
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: dialogButtons.top
anchors.bottomMargin: 8
contentHeight: prefControls.height
contentWidth: parent.width
Column {
id: prefControls
anchors.left: parent.left
anchors.right: parent.right
}
}
Row {
id: dialogButtons
anchors { bottom: parent.bottom; right: parent.right; margins: 8 }
Button { text: "Cancel"; onClicked: root.destroy(); }
Button {
text: "Save all changes"
onClicked: root.saveAll();
}
}
}
}

View file

@ -0,0 +1,49 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import "."
Preference {
id: root
property alias buttonText: button.text
property alias text: dataTextField.text
property alias placeholderText: dataTextField.placeholderText
property real spacing: 8
height: labelText.height + Math.max(dataTextField.height, button.height) + spacing
Component.onCompleted: {
dataTextField.text = preference.value;
}
function save() {
preference.value = dataTextField.text;
preference.save();
}
Text {
id: labelText
text: root.label
}
TextField {
id: dataTextField
placeholderText: root.placeholderText
text: preference.value
anchors {
top: labelText.bottom
left: parent.left
right: button.left
topMargin: root.spacing
rightMargin: root.spacing
}
}
Button {
id: button
anchors {
right: parent.right;
verticalCenter: dataTextField.verticalCenter
}
text: "Browse"
}
}

View file

@ -0,0 +1,23 @@
import QtQuick 2.5
import QtQuick.Controls 1.4 as Original
import "."
Preference {
id: root
height: checkBox.implicitHeight
Component.onCompleted: {
checkBox.checked = preference.value;
}
function save() {
preference.value = checkBox.checked;
preference.save();
}
Original.CheckBox {
id: checkBox
anchors.fill: parent
text: root.label
}
}

View file

@ -0,0 +1,34 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
Preference {
id: root
property real spacing: 8
height: labelText.height + dataTextField.height + spacing
Component.onCompleted: {
dataTextField.text = preference.value;
}
function save() {
preference.value = dataTextField.text;
preference.save();
}
Text {
id: labelText
text: root.label
}
TextField {
id: dataTextField
placeholderText: preference.placeholderText
anchors {
top: labelText.bottom
left: parent.left
right: parent.right
topMargin: root.spacing
rightMargin: root.spacing
}
}
}

View file

@ -0,0 +1,9 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
Item {
id: root
anchors { left: parent.left; right: parent.right }
property var preference;
property string label: preference ? preference.name : "";
}

View file

@ -0,0 +1,114 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import Hifi 1.0
import "../../../controls" as VrControls
import "."
Preference {
id: root
property bool expanded: false
property string name: "Header"
property real spacing: 8
readonly property alias toggle: toggle
readonly property alias header: header
default property alias preferences: contentContainer.children
function saveAll() {
for (var i = 0; i < d.preferences.length; ++i) {
var preference = d.preferences[i];
preference.save();
}
}
clip: true
children: [ toggle, header, contentContainer ]
height: expanded ? header.height + contentContainer.height + root.spacing * 3
: Math.max(toggle.height, header.height) + root.spacing * 2
Behavior on height { PropertyAnimation {} }
Component.onCompleted: d.buildPreferences();
function toggleExpanded() {
root.expanded = !root.expanded;
}
VrControls.FontAwesome {
id: toggle
anchors { left: parent.left; top: parent.top; margins: root.spacing }
rotation: root.expanded ? 0 : -90
text: "\uf078"
Behavior on rotation { PropertyAnimation {} }
MouseArea { anchors.fill: parent; onClicked: root.toggleExpanded() }
}
Text {
id: header
anchors { left: toggle.right; top: parent.top; leftMargin: root.spacing * 2; margins: root.spacing }
font.bold: true
font.pointSize: 16
color: "#0e7077"
text: root.name
}
Column {
id: contentContainer
spacing: root.spacing
anchors { left: toggle.right; top: header.bottom; topMargin: root.spacing; right: parent.right; margins: root.spacing }
enabled: root.expanded
visible: root.expanded
clip: true
}
QtObject {
id: d
property var editableBuilder: Component { Editable { } }
property var browsableBuilder: Component { Browsable { } }
property var spinnerBuilder: Component { SpinBox { } }
property var checkboxBuilder: Component { CheckBox { } }
property var sliderBuilder: Component { Slider { } }
property var preferences: []
function buildPreferences() {
var categoryPreferences = Preferences.preferencesByCategory[root.name];
if (categoryPreferences) {
console.log("Category " + root.name + " with " + categoryPreferences.length + " preferences");
for (var j = 0; j < categoryPreferences.length; ++j) {
buildPreference(categoryPreferences[j]);
}
}
}
function buildPreference(preference) {
console.log("\tPreference type " + preference.type + " name " + preference.name)
var builder;
switch (preference.type) {
case Preference.Editable:
builder = editableBuilder;
break;
case Preference.Browsable:
builder = browsableBuilder;
break;
case Preference.Spinner:
builder = spinnerBuilder;
break;
case Preference.Slider:
builder = sliderBuilder;
break;
case Preference.Checkbox:
builder = checkboxBuilder;
break;
};
if (builder) {
preferences.push(builder.createObject(contentContainer, { preference: preference }));
}
}
}
}

View file

@ -0,0 +1,32 @@
import QtQuick 2.5
import QtQuick.Controls 1.4 as Original
import "."
Preference {
id: root
property alias slider: slider
height: slider.height
Component.onCompleted: {
slider.value = preference.value;
}
function save() {
preference.value = slider.value;
preference.save();
}
Text {
text: root.label
anchors.verticalCenter: slider.verticalCenter
}
Original.Slider {
id: slider
value: preference.value
width: 130
anchors { right: parent.right }
}
}

View file

@ -0,0 +1,33 @@
import QtQuick 2.5
import QtQuick.Controls 1.4 as Original
import "."
Preference {
id: root
property alias spinner: spinner
height: spinner.height
Component.onCompleted: {
spinner.value = preference.value;
}
function save() {
preference.value = spinner.value;
preference.save();
}
Text {
text: root.label
anchors.verticalCenter: spinner.verticalCenter
}
Original.SpinBox {
id: spinner
decimals: preference.decimals
minimumValue: preference.min
maximumValue: preference.max
width: 100
anchors { right: parent.right }
}
}

View file

@ -1175,7 +1175,7 @@ void Application::initializeGL() {
InfoView::show(INFO_HELP_PATH, true);
}
extern void setupPreferences();
void Application::initializeUi() {
AddressBarDialog::registerType();
ErrorDialog::registerType();
@ -1201,6 +1201,8 @@ void Application::initializeUi() {
qApp->quit();
});
setupPreferences();
// For some reason there is already an "Application" object in the QML context,
// though I can't find it. Hence, "ApplicationInterface"
rootContext->setContextProperty("ApplicationInterface", this);
@ -1833,6 +1835,14 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
break;
case Qt::Key_X:
if (isShifted && isMeta) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->getRootContext()->engine()->clearComponentCache();
offscreenUi->show(QString("hifi/dialogs/PreferencesDialog.qml"), "PreferencesDialog");
}
break;
case Qt::Key_B:
if (isMeta) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();

View file

@ -18,6 +18,9 @@
#include <devices/Faceshift.h>
#include <NetworkingConstants.h>
#include <ScriptEngines.h>
#include <OffscreenUi.h>
#include <QtQml/QQmlContext>
#include <Preferences.h>
#include "Application.h"
#include "DialogsManager.h"
@ -30,6 +33,291 @@
#include "UIUtil.h"
#include "scripting/WebWindowClass.h"
void setupPreferences() {
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
Preferences* preferences = new Preferences();
static const QString AVATAR_BASICS { "Avatar Basics" };
{
auto getter = [=]()->QString {return myAvatar->getDisplayName(); };
auto setter = [=](const QString& value) { myAvatar->setDisplayName(value); };
const QString label = "Avatar display name <font color=\"#909090\">(optional)</font>";
auto preference = new EditPreference(AVATAR_BASICS, label, getter, setter, preferences);
preference->setPlaceholderText("Not showing a name");
preferences->addPreference(preference);
}
{
auto getter = [=]()->QString {return myAvatar->getCollisionSoundURL(); };
auto setter = [=](const QString& value) { myAvatar->setCollisionSoundURL(value); };
const QString label = "Avatar collision sound URL <font color=\"#909090\">(optional)</font>";
auto preference = new EditPreference(AVATAR_BASICS, label, getter, setter, preferences);
preference->setPlaceholderText("Enter the URL of a sound to play when you bump into something");
preferences->addPreference(preference);
}
{
auto getter = [=]()->QString { return myAvatar->getFullAvatarURLFromPreferences().toString(); };
auto setter = [=](const QString& value) { /* FIXME */ };
auto preference = new BrowsePreference(AVATAR_BASICS, "Appearance: ", getter, setter, preferences);
preferences->addPreference(preference);
}
{
auto getter = []()->QString { return Snapshot::snapshotsLocation.get(); };
auto setter = [](const QString& value) { Snapshot::snapshotsLocation.set(value); };
auto preference = new BrowsePreference("Snapshots", "Place my Snapshots here:", getter, setter, preferences);
preferences->addPreference(preference);
}
// Scripts
{
auto getter = []()->QString { return DependencyManager::get<ScriptEngines>()->getScriptsLocation(); };
auto setter = [](const QString& value) { DependencyManager::get<ScriptEngines>()->setScriptsLocation(value); };
preferences->addPreference(new BrowsePreference("Scripts", "Load scripts from this directory:", getter, setter, preferences));
}
preferences->addPreference(new ButtonPreference("Scripts", "Load Default Scripts", preferences));
{
auto getter = []()->bool {return !Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger); };
auto setter = [](bool value) { Menu::getInstance()->setIsOptionChecked(MenuOption::DisableActivityLogger, !value); };
preferences->addPreference(new CheckPreference("Privacy", "Send Data", getter, setter, preferences));
}
static const QString LOD_TUNING("Level of Detail Tuning");
{
auto getter = []()->bool { return DependencyManager::get<LODManager>()->getUseAcuity(); };
auto setter = [](bool value) { DependencyManager::get<LODManager>()->setUseAcuity(value); };
preferences->addPreference(new CheckPreference(LOD_TUNING, "Render based on visual acuity", getter, setter, preferences));
}
{
auto getter = []()->float { return DependencyManager::get<LODManager>()->getDesktopLODDecreaseFPS(); };
auto setter = [](float value) { DependencyManager::get<LODManager>()->setDesktopLODDecreaseFPS(value); };
auto preference = new SpinnerPreference(LOD_TUNING, "Minimum Desktop FPS", getter, setter, preferences);
preference->setMin(0);
preference->setMax(120);
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return DependencyManager::get<LODManager>()->getHMDLODDecreaseFPS(); };
auto setter = [](float value) { DependencyManager::get<LODManager>()->setHMDLODDecreaseFPS(value); };
auto preference = new SpinnerPreference(LOD_TUNING, "Minimum HMD FPS", getter, setter, preferences);
preference->setMin(0);
preference->setMax(120);
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return 1.0f / DependencyManager::get<LODManager>()->getRenderDistanceInverseHighLimit(); };
auto setter = [](float value) { DependencyManager::get<LODManager>()->setRenderDistanceInverseHighLimit(1.0f / value); };
auto preference = new SpinnerPreference(LOD_TUNING, "Minimum Display Distance", getter, setter, preferences);
preference->setMin(5);
preference->setMax(32768);
preference->setStep(1);
preferences->addPreference(preference);
}
static const QString AVATAR_TUNING { "Avatar Tuning" };
{
auto getter = [=]()->float { return myAvatar->getRealWorldFieldOfView(); };
auto setter = [=](float value) { myAvatar->setRealWorldFieldOfView(value); };
auto preference = new SpinnerPreference(AVATAR_TUNING, "Real world vertical field of view (angular size of monitor)", getter, setter, preferences);
preference->setMin(1);
preference->setMax(180);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return qApp->getFieldOfView(); };
auto setter = [](float value) { qApp->setFieldOfView(value); };
auto preference = new SpinnerPreference(AVATAR_TUNING, "Vertical field of view", getter, setter, preferences);
preference->setMin(1);
preference->setMax(180);
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = [=]()->float { return myAvatar->getLeanScale(); };
auto setter = [=](float value) { myAvatar->setLeanScale(value); };
auto preference = new SpinnerPreference(AVATAR_TUNING, "Lean scale (applies to Faceshift users)", getter, setter, preferences);
preference->setMin(0);
preference->setMax(99.9f);
preference->setDecimals(2);
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = [=]()->float { return myAvatar->getUniformScale(); };
auto setter = [=](float value) { myAvatar->setTargetScaleVerbose(value); }; // The hell?
auto preference = new SpinnerPreference(AVATAR_TUNING, "Avatar scale <font color=\"#909090\">(default is 1.0)</font>", getter, setter, preferences);
preference->setMin(0.01f);
preference->setMax(99.9f);
preference->setDecimals(2);
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = [=]()->float { return myAvatar->getHead()->getPupilDilation(); };
auto setter = [=](float value) { myAvatar->getHead()->setPupilDilation(value); };
preferences->addPreference(new SliderPreference(AVATAR_TUNING, "Pupil dilation", getter, setter, preferences));
}
{
auto getter = []()->float { return DependencyManager::get<DdeFaceTracker>()->getEyeClosingThreshold(); };
auto setter = [](float value) { DependencyManager::get<DdeFaceTracker>()->setEyeClosingThreshold(value); };
preferences->addPreference(new SliderPreference(AVATAR_TUNING, "Camera binary eyelid threshold", getter, setter, preferences));
}
{
auto getter = []()->float { return FaceTracker::getEyeDeflection(); };
auto setter = [](float value) { FaceTracker::setEyeDeflection(value); };
preferences->addPreference(new SliderPreference(AVATAR_TUNING, "Face tracker eye deflection", getter, setter, preferences));
}
{
auto getter = []()->QString { return DependencyManager::get<Faceshift>()->getHostname(); };
auto setter = [](const QString& value) { DependencyManager::get<Faceshift>()->setHostname(value); };
auto preference = new EditPreference(AVATAR_TUNING, "Faceshift hostname", getter, setter, preferences);
preference->setPlaceholderText("localhost");
preferences->addPreference(preference);
}
{
auto getter = [=]()->QString { return myAvatar->getAnimGraphUrl().toString(); };
auto setter = [=](const QString& value) { myAvatar->setAnimGraphUrl(value); };
auto preference = new EditPreference(AVATAR_TUNING, "Avatar Animation JSON", getter, setter, preferences);
preference->setPlaceholderText("default");
preferences->addPreference(preference);
}
static const QString AUDIO("Audio");
{
auto getter = []()->bool {return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getDynamicJitterBuffers(); };
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setDynamicJitterBuffers(value); };
preferences->addPreference(new CheckPreference(AUDIO, "Enable Dynamic Jitter Buffers", getter, setter, preferences));
}
{
auto getter = []()->float { return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getDesiredJitterBufferFrames(); };
auto setter = [](float value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setStaticDesiredJitterBufferFrames(value); };
auto preference = new SpinnerPreference(AUDIO, "Static Jitter Buffer Frames", getter, setter, preferences);
preference->setMin(0);
preference->setMax(10000);
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getMaxFramesOverDesired(); };
auto setter = [](float value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setMaxFramesOverDesired(value); };
auto preference = new SpinnerPreference(AUDIO, "Max Frames Over Desired", getter, setter, preferences);
preference->setMax(10000);
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = []()->bool {return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getUseStDevForJitterCalc(); };
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setUseStDevForJitterCalc(value); };
preferences->addPreference(new CheckPreference(AUDIO, "Use Stddev for Dynamic Jitter Calc", getter, setter, preferences));
}
{
auto getter = []()->float { return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getWindowStarveThreshold(); };
auto setter = [](float value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setWindowStarveThreshold(value); };
auto preference = new SpinnerPreference(AUDIO, "Window A Starve Threshold", getter, setter, preferences);
preference->setMax(10000);
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getWindowSecondsForDesiredCalcOnTooManyStarves(); };
auto setter = [](float value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setWindowSecondsForDesiredCalcOnTooManyStarves(value); };
auto preference = new SpinnerPreference(AUDIO, "Window A (raise desired on N starves) Seconds)", getter, setter, preferences);
preference->setMax(10000);
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getWindowSecondsForDesiredReduction(); };
auto setter = [](float value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setWindowSecondsForDesiredReduction(value); };
auto preference = new SpinnerPreference(AUDIO, "Window B (desired ceiling) Seconds", getter, setter, preferences);
preference->setMax(10000);
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = []()->bool {return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getRepetitionWithFade(); };
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setRepetitionWithFade(value); };
preferences->addPreference(new CheckPreference(AUDIO, "Repetition with Fade", getter, setter, preferences));
}
{
auto getter = []()->float { return DependencyManager::get<AudioClient>()->getOutputBufferSize(); };
auto setter = [](float value) { DependencyManager::get<AudioClient>()->setOutputBufferSize(value); };
auto preference = new SpinnerPreference(AUDIO, "Output Buffer Size (frames)", getter, setter, preferences);
preference->setMin(1);
preference->setMax(20);
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = []()->bool {return DependencyManager::get<AudioClient>()->getOutputStarveDetectionEnabled(); };
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->setOutputStarveDetectionEnabled(value); };
auto preference = new CheckPreference(AUDIO, "Output Starve Detection (Automatic Buffer Size Increase)", getter, setter, preferences);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return DependencyManager::get<AudioClient>()->getOutputStarveDetectionThreshold(); };
auto setter = [](float value) { DependencyManager::get<AudioClient>()->setOutputStarveDetectionThreshold(value); };
auto preference = new SpinnerPreference(AUDIO, "Output Starve Detection Threshold", getter, setter, preferences);
preference->setMin(1);
preference->setMax(500);
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return DependencyManager::get<AudioClient>()->getOutputStarveDetectionPeriod(); };
auto setter = [](float value) { DependencyManager::get<AudioClient>()->setOutputStarveDetectionPeriod(value); };
auto preference = new SpinnerPreference(AUDIO, "Output Starve Detection Period (ms)", getter, setter, preferences);
preference->setMin(1);
preference->setMax(999999999);
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return qApp->getMaxOctreePacketsPerSecond(); };
auto setter = [](float value) { qApp->setMaxOctreePacketsPerSecond(value); };
auto preference = new SpinnerPreference("Octree", "Max packets sent each second", getter, setter, preferences);
preference->setMin(60);
preference->setMax(6000);
preference->setStep(10);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return qApp->getApplicationCompositor().getHmdUIAngularSize(); };
auto setter = [](float value) { qApp->getApplicationCompositor().setHmdUIAngularSize(value); };
auto preference = new SpinnerPreference("HMD", "User Interface Horizontal Angular Size (degrees)", getter, setter, preferences);
preference->setMin(30);
preference->setMax(160);
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return controller::InputDevice::getReticleMoveSpeed(); };
auto setter = [](float value) { controller::InputDevice::setReticleMoveSpeed(value); };
auto preference = new SpinnerPreference("Sixense Controllers", "Reticle Movement Speed", getter, setter, preferences);
preference->setMin(0);
preference->setMax(100);
preference->setStep(1);
preferences->addPreference(preference);
}
DependencyManager::get<OffscreenUi>()->getRootContext()->setContextProperty("Preferences", preferences);
}
const int PREFERENCES_HEIGHT_PADDING = 20;

View file

@ -0,0 +1,25 @@
//
// Created by Bradley Austin Davis 2016/01/20
// 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
//
#include "Preferences.h"
void Preferences::addPreference(Preference* preference) {
const QString& category = preference->getCategory();
QVariantList categoryPreferences;
// FIXME is there an easier way to do this with less copying?
if (_preferencesByCategory.contains(category)) {
categoryPreferences = qvariant_cast<QVariantList>(_preferencesByCategory[category]);
} else {
// Use this property to maintain the order of the categories
_categories.append(category);
}
categoryPreferences.append(QVariant::fromValue(preference));
_preferencesByCategory[category] = categoryPreferences;
}

View file

@ -0,0 +1,248 @@
//
// Created by Bradley Austin Davis 2016/01/20
// 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
//
#pragma once
#ifndef hifi_Shared_Preferences_h
#define hifi_Shared_Preferences_h
#include <functional>
#include <QtCore/QObject>
#include <QtCore/QVariant>
#include <QtCore/QList>
#include <QtCore/QString>
class Preference;
class Preferences : public QObject {
Q_OBJECT
Q_PROPERTY(QVariantMap preferencesByCategory READ getPreferencesByCategory CONSTANT)
Q_PROPERTY(QList<QString> categories READ getCategories CONSTANT)
public:
void addPreference(Preference* preference);
const QVariantMap& getPreferencesByCategory() { return _preferencesByCategory; }
const QList<QString>& getCategories() { return _categories; }
private:
QVariantMap _preferencesByCategory;
QList<QString> _categories;
};
class Preference : public QObject {
Q_OBJECT
Q_PROPERTY(QString category READ getCategory CONSTANT)
Q_PROPERTY(QString name READ getName CONSTANT)
Q_PROPERTY(Type type READ getType CONSTANT)
Q_PROPERTY(bool enabled READ isEnabled NOTIFY enabledChanged)
Q_ENUMS(Type)
public:
enum Type {
Invalid,
Editable,
Browsable,
Slider,
Spinner,
Checkbox,
Button,
};
explicit Preference(QObject* parent = nullptr) : QObject(parent) {}
Preference(const QString& category, const QString& name, QObject* parent = nullptr)
: QObject(parent), _category(category), _name(name) { }
const QString& getCategory() const { return _category; }
const QString& getName() const { return _name; }
bool isEnabled() const {
return _enabled;
}
void setEnabled(bool enabled) {
if (enabled != _enabled) {
_enabled = enabled;
emit enabledChanged();
}
}
virtual Type getType() { return Invalid; };
Q_INVOKABLE virtual void load() {};
Q_INVOKABLE virtual void save() const {}
signals:
void enabledChanged();
protected:
virtual void emitValueChanged() {};
const QString _category;
const QString _name;
bool _enabled { true };
};
class ButtonPreference : public Preference {
Q_OBJECT
public:
ButtonPreference(const QString& category, const QString& name, Preferences* parent = nullptr)
: Preference(category, name, parent) { }
Type getType() { return Button; }
};
template <typename T>
class TypedPreference : public Preference {
public:
using Getter = std::function<T()>;
using Setter = std::function<void(const T&)>;
TypedPreference(const QString& category, const QString& name, Getter getter, Setter setter, Preferences* parent = nullptr)
: Preference(category, name, parent), _getter(getter), _setter(setter) { }
T getValue() const { return _getter(); }
void setValue(const T& value) { if (_value != value) { _value = value; emitValueChanged(); } }
void load() override { _value = _getter(); }
void save() const override {
T oldValue = _getter();
if (_value != oldValue) {
_setter(_value);
}
}
protected:
T _value;
const Getter _getter;
const Setter _setter;
};
class BoolPreference : public TypedPreference<bool> {
Q_OBJECT
Q_PROPERTY(bool value READ getValue WRITE setValue NOTIFY valueChanged)
public:
BoolPreference(const QString& category, const QString& name, Getter getter, Setter setter, Preferences* parent = nullptr)
: TypedPreference(category, name, getter, setter, parent) { }
signals:
void valueChanged();
protected:
void emitValueChanged() override { emit valueChanged(); }
};
class FloatPreference : public TypedPreference<float> {
Q_OBJECT
Q_PROPERTY(float value READ getValue WRITE setValue NOTIFY valueChanged)
Q_PROPERTY(float min READ getMin CONSTANT)
Q_PROPERTY(float max READ getMax CONSTANT)
Q_PROPERTY(float step READ getStep CONSTANT)
Q_PROPERTY(float decimals READ getDecimals CONSTANT)
public:
FloatPreference(const QString& category, const QString& name, Getter getter, Setter setter, Preferences* parent = nullptr)
: TypedPreference(category, name, getter, setter, parent) { }
float getMin() const { return _min; }
void setMin(float min) { _min = min; };
float getMax() const { return _max; }
void setMax(float max) { _max = max; };
float getStep() const { return _step; }
void setStep(float step) { _step = step; };
float getDecimals() const { return _decimals; }
void setDecimals(float decimals) { _decimals = decimals; };
signals:
void valueChanged();
protected:
void emitValueChanged() override { emit valueChanged(); }
float _decimals { 0 };
float _min { 0 };
float _max { 1 };
float _step { 0.1f };
};
class StringPreference : public TypedPreference<QString> {
Q_OBJECT
Q_PROPERTY(QString value READ getValue WRITE setValue NOTIFY valueChanged)
public:
StringPreference(const QString& category, const QString& name, Getter getter, Setter setter, Preferences* parent = nullptr)
: TypedPreference(category, name, getter, setter, parent) { }
signals:
void valueChanged();
protected:
void emitValueChanged() override { emit valueChanged(); }
};
class SliderPreference : public FloatPreference {
Q_OBJECT
public:
SliderPreference(const QString& category, const QString& name, Getter getter, Setter setter, Preferences* parent = nullptr)
: FloatPreference(category, name, getter, setter, parent) { }
Type getType() { return Slider; }
};
class SpinnerPreference : public FloatPreference {
Q_OBJECT
public:
SpinnerPreference(const QString& category, const QString& name, Getter getter, Setter setter, Preferences* parent = nullptr)
: FloatPreference(category, name, getter, setter, parent) { }
Type getType() { return Spinner; }
};
class EditPreference : public StringPreference {
Q_OBJECT
Q_PROPERTY(QString placeholderText READ getPlaceholderText CONSTANT)
public:
EditPreference(const QString& category, const QString& name, Getter getter, Setter setter, Preferences* parent = nullptr)
: StringPreference(category, name, getter, setter, parent) { }
Type getType() { return Editable; }
const QString& getPlaceholderText() const { return _placeholderText; }
void setPlaceholderText(const QString& placeholderText) { _placeholderText = placeholderText; }
protected:
QString _placeholderText;
};
class BrowsePreference : public EditPreference {
Q_OBJECT
Q_PROPERTY(QString browseLabel READ getBrowseLabel CONSTANT)
public:
BrowsePreference(const QString& category, const QString& name, Getter getter, Setter setter, Preferences* parent = nullptr)
: EditPreference(category, name, getter, setter, parent) { }
Type getType() { return Browsable; }
const QString& getBrowseLabel() { return _browseLabel; }
void setBrowseLabel(const QString& browseLabel) { _browseLabel = browseLabel; }
protected:
QString _browseLabel { "Browse" };
};
class CheckPreference : public BoolPreference {
Q_OBJECT
public:
CheckPreference(const QString& category, const QString& name, Getter getter, Setter setter, Preferences* parent = nullptr)
: BoolPreference(category, name, getter, setter, parent) { }
Type getType() { return Checkbox; }
};
#endif

View file

@ -87,5 +87,13 @@ DISTFILES += \
../../interface/resources/qml/menus/VrMenuItem.qml \
../../interface/resources/qml/menus/VrMenuView.qml \
../../interface/resources/qml/windows/ModalWindow.qml \
../../interface/resources/qml/desktop/FocusHack.qml
../../interface/resources/qml/desktop/FocusHack.qml \
../../interface/resources/qml/hifi/dialogs/PreferencesDialog.qml \
../../interface/resources/qml/hifi/dialogs/Section.qml \
../../interface/resources/qml/hifi/dialogs/preferences/Browsable.qml \
../../interface/resources/qml/hifi/dialogs/preferences/Section.qml \
../../interface/resources/qml/hifi/dialogs/preferences/Editable.qml \
../../interface/resources/qml/hifi/dialogs/preferences/Slider.qml \
../../interface/resources/qml/hifi/dialogs/preferences/Preference.qml \
../../interface/resources/qml/hifi/dialogs/preferences/SpinBox.qml \
../../interface/resources/qml/hifi/dialogs/preferences/CheckBox.qml

View file

@ -3,6 +3,36 @@
#include <QtWebEngine>
#include <QFileSystemModel>
class Preference : public QObject {
Q_OBJECT
Q_PROPERTY(QString category READ getCategory() CONSTANT)
Q_PROPERTY(QString name READ getName() CONSTANT)
Q_PROPERTY(Type type READ getType() CONSTANT)
Q_ENUMS(Type)
public:
enum Type {
Editable,
Browsable,
Spinner,
Checkbox,
};
Preference(QObject* parent = nullptr) : QObject(parent) {}
Preference(const QString& category, const QString& name, QObject* parent = nullptr)
: QObject(parent), _category(category), _name(name) { }
const QString& getCategory() const { return _category; }
const QString& getName() const { return _name; }
virtual Type getType() { return Editable; }
protected:
const QString _category;
const QString _name;
};
QString getRelativeDir(const QString& relativePath = ".") {
QDir path(__FILE__); path.cdUp();
auto result = path.absoluteFilePath(relativePath);
@ -45,6 +75,7 @@ int main(int argc, char *argv[]) {
QDir::setCurrent(getRelativeDir(".."));
QtWebEngine::initialize();
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
QQmlApplicationEngine engine;
addImportPath(engine, "../qml");
@ -63,3 +94,5 @@ int main(int argc, char *argv[]) {
engine.load(QUrl(QStringLiteral("qml/main.qml")));
return app.exec();
}
#include "main.moc"