Merge pull request #1385 from Armored-Dragon/feature/SettingsRebase2
Some checks are pending
Master API-docs CI Build and Deploy / Build and deploy API-docs (push) Waiting to run
Master Doxygen CI Build and Deploy / Build and deploy Doxygen documentation (push) Waiting to run

New Graphics SettingsUI
This commit is contained in:
ksuprynowicz 2025-05-01 16:24:06 +02:00 committed by GitHub
commit 42e54369d3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 1852 additions and 1013 deletions

View file

@ -1,898 +0,0 @@
//
// GraphicsSettings.qml
// qml\hifi\dialogs\graphics
//
// Created by Zach Fox on 2019-07-10
// Copyright 2019 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.12
import stylesUit 1.0 as HifiStylesUit
import controlsUit 1.0 as HifiControlsUit
import "qrc:////qml//controls" as HifiControls
import PerformanceEnums 1.0
Flickable {
HifiStylesUit.HifiConstants { id: hifi; }
contentHeight: graphicsSettingsColumnLayout.height;
ScrollBar.vertical : ScrollBar {
policy: ScrollBar.AlwaysOn
visible: true
width: 20
background: Rectangle {
color: hifi.colors.tableScrollBackgroundDark
}
}
id: root;
anchors.fill: parent
ColumnLayout {
id: graphicsSettingsColumnLayout
anchors.left: parent.left
anchors.leftMargin: 26
anchors.right: parent.right
anchors.rightMargin: 26
anchors.top: parent.top
anchors.topMargin: HMD.active ? 80 : 0
spacing: 8
ColumnLayout {
Layout.preferredWidth: parent.width
Layout.topMargin: 18
spacing: 0
HifiStylesUit.RalewayRegular {
text: "GRAPHICS SETTINGS"
Layout.maximumWidth: parent.width
height: 30
size: 16
color: "#FFFFFF"
}
ColumnLayout {
Layout.topMargin: 10
Layout.preferredWidth: parent.width
spacing: 0
HifiControlsUit.RadioButton {
id: performanceLowPower
colorScheme: hifi.colorSchemes.dark
height: 18
fontSize: 16
leftPadding: 0
text: "Low Power"
checked: Performance.getPerformancePreset() === PerformanceEnums.LOW_POWER
onClicked: {
Performance.setPerformancePreset(PerformanceEnums.LOW_POWER);
root.refreshAllDropdowns();
}
}
HifiControlsUit.RadioButton {
id: performanceLow
colorScheme: hifi.colorSchemes.dark
height: 18
fontSize: 16
leftPadding: 0
text: "Low"
checked: Performance.getPerformancePreset() === PerformanceEnums.LOW
onClicked: {
Performance.setPerformancePreset(PerformanceEnums.LOW);
root.refreshAllDropdowns();
}
}
HifiControlsUit.RadioButton {
id: performanceMedium
colorScheme: hifi.colorSchemes.dark
height: 18
fontSize: 16
leftPadding: 0
text: "Medium"
checked: Performance.getPerformancePreset() === PerformanceEnums.MID
onClicked: {
Performance.setPerformancePreset(PerformanceEnums.MID);
root.refreshAllDropdowns();
}
}
HifiControlsUit.RadioButton {
id: performanceHigh
colorScheme: hifi.colorSchemes.dark
height: 18
fontSize: 16
leftPadding: 0
text: "High"
checked: Performance.getPerformancePreset() === PerformanceEnums.HIGH
onClicked: {
Performance.setPerformancePreset(PerformanceEnums.HIGH);
root.refreshAllDropdowns();
}
}
HifiControlsUit.RadioButton {
id: performanceCustom
colorScheme: hifi.colorSchemes.dark
height: 18
fontSize: 16
leftPadding: 0
text: "Custom"
checked: Performance.getPerformancePreset() === PerformanceEnums.CUSTOM
onClicked: {
Performance.setPerformancePreset(PerformanceEnums.CUSTOM);
}
}
}
ColumnLayout {
Layout.topMargin: 10
Layout.preferredWidth: parent.width
spacing: 0
Item {
Layout.preferredWidth: parent.width
Layout.preferredHeight: 35
HifiStylesUit.RalewayRegular {
id: worldDetailHeader
text: "Target frame rate"
anchors.left: parent.left
anchors.top: parent.top
width: 130
height: parent.height
size: 16
color: "#FFFFFF"
}
ListModel {
id: worldDetailModel
ListElement {
text: "High Frame Rate/Low Detail"
}
ListElement {
text: "Medium Frame Rate/Medium Detail"
}
ListElement {
text: "Low Frame Rate/High Detail"
}
}
HifiControlsUit.ComboBox {
id: worldDetailDropdown
enabled: performanceCustom.checked
anchors.left: worldDetailHeader.right
anchors.leftMargin: 20
anchors.top: parent.top
width: 280
height: parent.height
colorScheme: hifi.colorSchemes.dark
model: worldDetailModel
currentIndex: -1
function refreshWorldDetailDropdown() {
worldDetailDropdown.currentIndex = LODManager.worldDetailQuality;
}
Component.onCompleted: {
worldDetailDropdown.refreshWorldDetailDropdown();
}
onCurrentIndexChanged: {
LODManager.worldDetailQuality = currentIndex;
worldDetailDropdown.displayText = model.get(currentIndex).text;
}
}
}
ColumnLayout {
Layout.preferredWidth: parent.width
Layout.topMargin: 20
HifiStylesUit.RalewayRegular {
id: renderingEffectsHeader
text: "Rendering Effects"
anchors.left: parent.left
anchors.top: parent.top
width: 130
height: parent.height
size: 16
color: "#FFFFFF"
}
ColumnLayout {
anchors.left: renderingEffectsHeader.right
anchors.leftMargin: 20
Layout.preferredWidth: parent.width
spacing: 0
enabled: performanceCustom.checked
HifiControlsUit.RadioButton {
id: renderingEffectsDisabled
colorScheme: hifi.colorSchemes.dark
height: 18
fontSize: 16
leftPadding: 0
text: "Disabled"
checked: Render.renderMethod === 1
onClicked: {
Render.renderMethod = 1; // "FORWARD"
//refreshRenderingEffectCheckboxes();
}
}
HifiControlsUit.RadioButton {
id: renderingEffectsEnabled
enabled: PlatformInfo.isRenderMethodDeferredCapable()
colorScheme: hifi.colorSchemes.dark
height: 18
fontSize: 16
leftPadding: 0
text: "Enabled"
checked: Render.renderMethod === 0
onClicked: {
Render.renderMethod = 0; // "DEFERRED"
}
}
ColumnLayout {
id: renderingEffectCheckboxes
Layout.preferredWidth: parent.width
anchors.left: parent.left
anchors.leftMargin: 24
anchors.topMargin: 8
anchors.bottom: parent.bottom
anchors.bottomMargin: Layout.topMargin
enabled: performanceCustom.checked && renderingEffectsEnabled.checked
HifiControlsUit.CheckBox {
id: renderingEffectShadows
checked: Render.shadowsEnabled
boxSize: 16
text: "Shadows"
spacing: -1
colorScheme: hifi.colorSchemes.dark
anchors.left: parent.left
anchors.top: parent.top
onCheckedChanged: {
Render.shadowsEnabled = renderingEffectShadows.checked;
}
}
HifiControlsUit.CheckBox {
id: renderingEffectHaze
checked: Render.hazeEnabled
boxSize: 16
text: "Haze"
spacing: -1
colorScheme: hifi.colorSchemes.dark
anchors.left: parent.left
anchors.top: renderingEffectShadows.bottom
onCheckedChanged: {
Render.hazeEnabled = renderingEffectHaze.checked;
}
}
HifiControlsUit.CheckBox {
id: renderingEffectBloom
checked: Render.bloomEnabled
boxSize: 16
text: "Bloom"
spacing: -1
colorScheme: hifi.colorSchemes.dark
anchors.left: parent.left
anchors.top: renderingEffectHaze.bottom
onCheckedChanged: {
Render.bloomEnabled = renderingEffectBloom.checked;
}
}
HifiControlsUit.CheckBox {
id: renderingEffectAO
checked: Render.ambientOcclusionEnabled
boxSize: 16
text: "AO"
spacing: -1
colorScheme: hifi.colorSchemes.dark
anchors.left: parent.left
anchors.top: renderingEffectBloom.bottom
onCheckedChanged: {
Render.ambientOcclusionEnabled = renderingEffectAO.checked;
}
}
HifiControlsUit.CheckBox {
id: renderingEffectLocalLights
enabled: false
//checked: Render.localLightsEnabled
checked: renderingEffectsEnabled.checked
boxSize: 16
text: "Local lights"
spacing: -1
colorScheme: hifi.colorSchemes.dark
anchors.left: parent.left
anchors.top: renderingEffectAO.bottom
//onCheckedChanged: {
// Render.localLightsEnabled = renderingEffectLocalLightsEnabled.checked;
//}
}
}
}
}
Item {
Layout.preferredWidth: parent.width
Layout.preferredHeight: 35
Layout.topMargin: 10
HifiStylesUit.RalewayRegular {
id: refreshRateHeader
text: "Refresh Rate"
anchors.left: parent.left
anchors.top: parent.top
width: 130
height: parent.height
size: 16
color: "#FFFFFF"
}
ListModel {
id: refreshRateModel
ListElement {
text: "Economical"
refreshRatePreset: 0 // RefreshRateProfile::ECO
}
ListElement {
text: "Interactive"
refreshRatePreset: 1 // RefreshRateProfile::INTERACTIVE
}
ListElement {
text: "Real-Time"
refreshRatePreset: 2 // RefreshRateProfile::REALTIME
}
ListElement {
text: "Custom"
refreshRatePreset: 3 // RefreshRateProfile::CUSTOM
}
}
HifiControlsUit.ComboBox {
id: refreshRateDropdown
enabled: performanceCustom.checked
anchors.left: refreshRateHeader.right
anchors.leftMargin: 20
anchors.top: parent.top
width: 280
height: parent.height
colorScheme: hifi.colorSchemes.dark
model: refreshRateModel
currentIndex: -1
function refreshRefreshRateDropdownDisplay() {
refreshRateDropdown.currentIndex = Performance.getRefreshRateProfile();
}
Component.onCompleted: {
refreshRateDropdown.refreshRefreshRateDropdownDisplay();
}
onCurrentIndexChanged: {
Performance.setRefreshRateProfile(model.get(currentIndex).refreshRatePreset);
refreshRateDropdown.displayText = model.get(currentIndex).text;
}
}
}
ColumnLayout {
width: parent.width
Layout.topMargin: 32
visible: refreshRateDropdown.currentIndex == 3
RowLayout {
Layout.margins: 8
HifiControlsUit.SpinBox {
id: refreshRateCustomFocusActive
decimals: 0
width: 160
height: 32
suffix: " FPS"
label: "Focus Active"
realFrom: 1
realTo: 1000
realStepSize: 15
realValue: 60
colorScheme: hifi.colorSchemes.dark
property var loaded: false
Component.onCompleted: {
realValue = Performance.getCustomRefreshRate(0)
loaded = true
}
onRealValueChanged: {
if (loaded) {
Performance.setCustomRefreshRate(0, realValue)
}
}
}
HifiControlsUit.SpinBox {
id: refreshRateCustomFocusInactive
decimals: 0
width: 160
height: 32
suffix: " FPS"
label: "Focus Inactive"
realFrom: 1
realTo: 1000
realStepSize: 15
realValue: 60
colorScheme: hifi.colorSchemes.dark
property var loaded: false
Component.onCompleted: {
realValue = Performance.getCustomRefreshRate(1)
loaded = true
}
onRealValueChanged: {
if (loaded) {
Performance.setCustomRefreshRate(1, realValue)
}
}
}
}
RowLayout {
Layout.margins: 8
HifiControlsUit.SpinBox {
id: refreshRateCustomUnfocus
decimals: 0
width: 160
height: 32
suffix: " FPS"
label: "Unfocus"
realFrom: 1
realTo: 1000
realStepSize: 15
realValue: 60
colorScheme: hifi.colorSchemes.dark
property var loaded: false
Component.onCompleted: {
realValue = Performance.getCustomRefreshRate(2)
loaded = true
}
onRealValueChanged: {
if (loaded) {
Performance.setCustomRefreshRate(2, realValue);
}
}
}
HifiControlsUit.SpinBox {
id: refreshRateCustomMinimized
decimals: 0
width: 160
height: 32
suffix: " FPS"
label: "Minimized"
realFrom: 1
realTo: 1000
realStepSize: 1
realValue: 60
colorScheme: hifi.colorSchemes.dark
property var loaded: false
Component.onCompleted: {
realValue = Performance.getCustomRefreshRate(3)
loaded = true
}
onRealValueChanged: {
if (loaded) {
Performance.setCustomRefreshRate(3, realValue)
}
}
}
}
RowLayout {
Layout.margins: 8
HifiControlsUit.SpinBox {
id: refreshRateCustomStartup
decimals: 0
width: 160
height: 32
suffix: " FPS"
label: "Startup"
realFrom: 1
realTo: 1000
realStepSize: 15
realValue: 60
colorScheme: hifi.colorSchemes.dark
property var loaded: false
Component.onCompleted: {
realValue = Performance.getCustomRefreshRate(4)
loaded = true
}
onRealValueChanged: {
if (loaded) {
Performance.setCustomRefreshRate(4, realValue)
}
}
}
HifiControlsUit.SpinBox {
id: refreshRateCustomShutdown
decimals: 0
width: 160
height: 32
suffix: " FPS"
label: "Shutdown"
realFrom: 1
realTo: 1000
realStepSize: 15
realValue: 60
colorScheme: hifi.colorSchemes.dark
property var loaded: false
Component.onCompleted: {
realValue = Performance.getCustomRefreshRate(5)
loaded = true
}
onRealValueChanged: {
if (loaded) {
Performance.setCustomRefreshRate(5, realValue)
}
}
}
}
}
Item {
Layout.preferredWidth: parent.width
Layout.preferredHeight: 35
Layout.topMargin: 16
HifiStylesUit.RalewayRegular {
id: resolutionHeader
text: "Resolution Scale (" + Number.parseFloat(Render.viewportResolutionScale).toPrecision(3) + ")"
anchors.left: parent.left
anchors.top: parent.top
width: 130
height: parent.height
size: 16
color: "#FFFFFF"
}
HifiControlsUit.Slider {
id: resolutionScaleSlider
enabled: performanceCustom.checked
anchors.left: resolutionHeader.right
anchors.leftMargin: 57
anchors.top: parent.top
width: 150
height: parent.height
colorScheme: hifi.colorSchemes.dark
minimumValue: 0.25
maximumValue: 2.0
stepSize: 0.05
value: Render.viewportResolutionScale
live: true
function updateResolutionScale(sliderValue) {
if (Render.viewportResolutionScale !== sliderValue) {
Render.viewportResolutionScale = sliderValue;
}
}
onValueChanged: {
updateResolutionScale(value);
}
onPressedChanged: {
if (!pressed) {
updateResolutionScale(value);
}
}
}
}
Item {
Layout.preferredWidth: parent.width
Layout.preferredHeight: 35
Layout.topMargin: 16
HifiStylesUit.RalewayRegular {
id: fieldOfViewHeader
text: "Vertical FOV (" + Number(Math.round(Render.verticalFieldOfView)) + ")"
anchors.left: parent.left
anchors.top: parent.top
width: 130
height: parent.height
size: 16
color: "#FFFFFF"
}
HifiControlsUit.Slider {
id: fieldOfViewSlider
enabled: true
anchors.left: fieldOfViewHeader.right
anchors.leftMargin: 57
anchors.top: parent.top
width: 150
height: parent.height
colorScheme: hifi.colorSchemes.dark
minimumValue: 20
maximumValue: 130
stepSize: 0.05
value: Render.verticalFieldOfView
live: true
function updateFieldOfView(sliderValue) {
if (Render.verticalFieldOfView !== sliderValue) {
Render.verticalFieldOfView = sliderValue;
}
}
onValueChanged: {
updateFieldOfView(value);
}
onPressedChanged: {
if (!pressed) {
updateFieldOfView(value);
}
}
}
}
Item {
Layout.preferredWidth: parent.width
Layout.preferredHeight: 35
Layout.topMargin: 16
HifiStylesUit.RalewayRegular {
id: enableCameraClippingHeader
text: "3rd Person Camera Clipping"
anchors.left: parent.left
anchors.top: parent.top
width: 200
height: parent.height
size: 16
color: "#FFFFFF"
}
HifiControlsUit.CheckBox {
id: enableCameraClipping
checked: Render.cameraClippingEnabled
boxSize: 16
spacing: -1
colorScheme: hifi.colorSchemes.dark
anchors.left: enableCameraClippingHeader.right
anchors.leftMargin: 20
anchors.top: parent.top
onCheckedChanged: {
Render.cameraClippingEnabled = enableCameraClipping.checked;
}
}
}
}
ColumnLayout {
Layout.topMargin: 20
Layout.preferredWidth: parent.width
spacing: 0
Item {
Layout.preferredWidth: parent.width
Layout.preferredHeight: 35
HifiStylesUit.RalewayRegular {
id: antialiasingHeader
text: "Anti-aliasing"
anchors.left: parent.left
anchors.top: parent.top
width: 130
height: parent.height
size: 16
color: "#FFFFFF"
}
ListModel {
id: antialiasingModel
// Maintain same order as "AntialiasingSetupConfig::Mode".
ListElement {
text: "None"
}
ListElement {
text: "TAA"
}
ListElement {
text: "FXAA"
}
}
HifiControlsUit.ComboBox {
id: antialiasingDropdown
anchors.left: antialiasingHeader.right
anchors.leftMargin: 20
anchors.top: parent.top
width: 280
height: parent.height
colorScheme: hifi.colorSchemes.dark
model: antialiasingModel
currentIndex: -1
function refreshAntialiasingDropdown() {
antialiasingDropdown.currentIndex = Render.antialiasingMode;
}
Component.onCompleted: {
antialiasingDropdown.refreshAntialiasingDropdown();
}
onCurrentIndexChanged: {
Render.antialiasingMode = currentIndex;
antialiasingDropdown.displayText = model.get(currentIndex).text;
}
}
}
}
ColumnLayout {
Layout.topMargin: 20
Layout.preferredWidth: parent.width
spacing: 0
Item {
Layout.preferredWidth: parent.width
Layout.preferredHeight: 35
HifiStylesUit.RalewayRegular {
id: fullScreenDisplayHeader
text: "Full screen display"
anchors.left: parent.left
anchors.top: parent.top
width: 130
height: parent.height
size: 16
color: "#FFFFFF"
}
ListModel {
id: fullScreenDisplayModel
ListElement {
text: "Screen 1"
}
function refreshScreens() {
fullScreenDisplayModel.clear();
Render.getScreens().forEach(function(screen) {
fullScreenDisplayModel.append({"text" : screen});
});
}
Component.onCompleted: {
fullScreenDisplayModel.refreshScreens();
}
}
HifiControlsUit.ComboBox {
id: fullScreenDisplayDropdown
anchors.left: fullScreenDisplayHeader.right
anchors.leftMargin: 20
anchors.top: parent.top
width: 280
height: parent.height
colorScheme: hifi.colorSchemes.dark
model: fullScreenDisplayModel
currentIndex: 0
function refreshFullScreenDisplayDropdown() {
var screens = Render.getScreens();
var selected = Render.getFullScreenScreen();
for(let idx = 0; idx < screens.length; idx++) {
if (screens[idx] == selected) {
fullScreenDisplayDropdown.currentIndex = idx;
return;
}
}
console.log("Selected full screen screen", selected, "not found, falling back to primary screen");
console.log("List of screens is:", screens);
fullScreenDisplayDropdown.currentIndex = 0;
}
Component.onCompleted: {
model.refreshScreens();
fullScreenDisplayDropdown.refreshFullScreenDisplayDropdown();
fullScreenDisplayDropdown.displayText = model.get(currentIndex).text;
}
onCurrentIndexChanged: {
if (currentIndex >= 0) {
// Somehow, we end up going through here twice on every change of the combo box.
// The first one is with the new selected index, and the second one is with the
// index at -1.
//
// The first one comes from a sensible stack of:
// onCurrentIndexChanged (qrc:/qml/hifi/dialogs/graphics/GraphicsSettings.qml:559)
// refreshScreens (qrc:/qml/hifi/dialogs/graphics/GraphicsSettings.qml:514)
// onCompleted (qrc:/qml/hifi/dialogs/graphics/GraphicsSettings.qml:553)
// load (qrc:/qml/hifi/tablet/WindowRoot.qml:170)
// loadSource (qrc:/qml/hifi/tablet/WindowRoot.qml:63)
//
// The second seems to be called out of nowhere. This likely indicates some sort of bug.
// Might be related to Wayland?
Render.setFullScreenScreen(model.get(currentIndex).text);
fullScreenDisplayDropdown.displayText = model.get(currentIndex).text;
} else {
console.log("Called with currentIndex =", currentIndex);
console.trace();
}
}
}
}
}
ColumnLayout {
Layout.topMargin: 20
Layout.preferredWidth: parent.width
spacing: 0
Item {
Layout.preferredWidth: parent.width
Layout.preferredHeight: 35
HifiStylesUit.RalewayRegular {
id: proceduralMaterialsHeader
text: "Procedural Materials"
anchors.left: parent.left
anchors.top: parent.top
width: 150
height: parent.height
size: 16
color: "#FFFFFF"
}
HifiControlsUit.CheckBox {
id: renderingEffectProceduralMaterials
checked: Render.proceduralMaterialsEnabled
boxSize: 16
spacing: -1
colorScheme: hifi.colorSchemes.dark
anchors.left: proceduralMaterialsHeader.right
anchors.leftMargin: 20
anchors.top: parent.top
onCheckedChanged: {
Render.proceduralMaterialsEnabled = renderingEffectProceduralMaterials.checked;
}
}
}
}
}
}
function refreshAllDropdowns() {
worldDetailDropdown.refreshWorldDetailDropdown();
refreshRateDropdown.refreshRefreshRateDropdownDisplay();
antialiasingDropdown.refreshAntialiasingDropdown();
}
}

View file

@ -286,18 +286,6 @@ Menu::Menu() {
}
});
// Settings > Graphics...
action = addActionToQMenuAndActionHash(settingsMenu, "Graphics...");
connect(action, &QAction::triggered, [] {
auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
auto hmd = DependencyManager::get<HMDScriptingInterface>();
tablet->pushOntoStack("hifi/dialogs/graphics/GraphicsSettings.qml");
if (!hmd->getShouldShowTablet()) {
hmd->toggleShouldShowTablet();
}
});
// Settings > Security...
action = addActionToQMenuAndActionHash(settingsMenu, "Security...");
connect(action, &QAction::triggered, [] {

View file

@ -24,7 +24,7 @@ var DEFAULT_SCRIPTS_COMBINED = [
"system/snapshot.js",
"system/pal.js", // "system/mod.js", // older UX, if you prefer
"system/avatarapp.js",
"system/graphicsSettings.js",
"system/settings/settings.js",
"system/makeUserConnection.js",
"system/notifications.js",
"system/create/edit.js",

View file

@ -1,102 +0,0 @@
//
// graphicsSettings.js
//
// Created by Kalila L. on August 5th, 2020
// Copyright 2020 Vircadia contributors.
// Copyright 2024 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() { // BEGIN LOCAL_SCOPE
var channelComm = "Overte-ShowGraphicsIconChanged";
var appStatus = false;
var GRAPHICS_HIDE_AND_SHOW_SETTING_KEY = "showGraphicsIcon";
var GRAPHICS_HIDE_AND_SHOW_DEFAULT_VALUE = true;
var AppUi = Script.require('appUi');
// cellphone-cog MDI
// var customIcon = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24"><path fill="white" d="M9.82,12.5C9.84,12.33 9.86,12.17 9.86,12C9.86,11.83 9.84,11.67 9.82,11.5L10.9,10.69C11,10.62 11,10.5 10.96,10.37L9.93,8.64C9.87,8.53 9.73,8.5 9.62,8.53L8.34,9.03C8.07,8.83 7.78,8.67 7.47,8.54L7.27,7.21C7.27,7.09 7.16,7 7.03,7H5C4.85,7 4.74,7.09 4.72,7.21L4.5,8.53C4.21,8.65 3.92,8.83 3.65,9L2.37,8.5C2.25,8.47 2.12,8.5 2.06,8.63L1.03,10.36C0.97,10.5 1,10.61 1.1,10.69L2.18,11.5C2.16,11.67 2.15,11.84 2.15,12C2.15,12.17 2.17,12.33 2.19,12.5L1.1,13.32C1,13.39 1,13.53 1.04,13.64L2.07,15.37C2.13,15.5 2.27,15.5 2.38,15.5L3.66,15C3.93,15.18 4.22,15.34 4.53,15.47L4.73,16.79C4.74,16.91 4.85,17 5,17H7.04C7.17,17 7.28,16.91 7.29,16.79L7.5,15.47C7.8,15.35 8.09,15.17 8.36,15L9.64,15.5C9.76,15.53 9.89,15.5 9.95,15.37L11,13.64C11.04,13.53 11,13.4 10.92,13.32L9.82,12.5M6,13.75C5,13.75 4.2,12.97 4.2,12C4.2,11.03 5,10.25 6,10.25C7,10.25 7.8,11.03 7.8,12C7.8,12.97 7,13.75 6,13.75M17,1H7A2,2 0 0,0 5,3V6H7V4H17V20H7V18H5V21A2,2 0 0,0 7,23H17A2,2 0 0,0 19,21V3A2,2 0 0,0 17,1Z" /></svg>'
// application-cog MDI
var customIcon = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24"><path fill="white" d="M21.7 18.6V17.6L22.8 16.8C22.9 16.7 23 16.6 22.9 16.5L21.9 14.8C21.9 14.7 21.7 14.7 21.6 14.7L20.4 15.2C20.1 15 19.8 14.8 19.5 14.7L19.3 13.4C19.3 13.3 19.2 13.2 19.1 13.2H17.1C16.9 13.2 16.8 13.3 16.8 13.4L16.6 14.7C16.3 14.9 16.1 15 15.8 15.2L14.6 14.7C14.5 14.7 14.4 14.7 14.3 14.8L13.3 16.5C13.3 16.6 13.3 16.7 13.4 16.8L14.5 17.6V18.6L13.4 19.4C13.3 19.5 13.2 19.6 13.3 19.7L14.3 21.4C14.4 21.5 14.5 21.5 14.6 21.5L15.8 21C16 21.2 16.3 21.4 16.6 21.5L16.8 22.8C16.9 22.9 17 23 17.1 23H19.1C19.2 23 19.3 22.9 19.3 22.8L19.5 21.5C19.8 21.3 20 21.2 20.3 21L21.5 21.4C21.6 21.4 21.7 21.4 21.8 21.3L22.8 19.6C22.9 19.5 22.9 19.4 22.8 19.4L21.7 18.6M18 19.5C17.2 19.5 16.5 18.8 16.5 18S17.2 16.5 18 16.5 19.5 17.2 19.5 18 18.8 19.5 18 19.5M11.29 20H5C3.89 20 3 19.1 3 18V6C3 4.89 3.9 4 5 4H19C20.11 4 21 4.9 21 6V11.68C20.38 11.39 19.71 11.18 19 11.08V8H5V18H11C11 18.7 11.11 19.37 11.29 20Z" /></svg>'
var lqIcon = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24"><path fill="white" d="M14.5,13.5H16.5V10.5H14.5M18,14C18,14.6 17.6,15 17,15H16.25V16.5H14.75V15H14C13.4,15 13,14.6 13,14V10C13,9.4 13.4,9 14,9H17C17.6,9 18,9.4 18,10M19,4H5A2,2 0 0,0 3,6V18A2,2 0 0,0 5,20H19A2,2 0 0,0 21,18V6A2,2 0 0,0 19,4M11,13.5V15H6V9H7.5V13.5H11Z" /></svg>';
var mqIcon = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24"><path fill="white" d="M21,6V18A2,2 0 0,1 19,20H5A2,2 0 0,1 3,18V6A2,2 0 0,1 5,4H19A2,2 0 0,1 21,6M12,10C12,9.5 11.5,9 11,9H6.5C6,9 5.5,9.5 5.5,10V15H7V10.5H8V14H9.5V10.5H10.5V15H12V10M14.5,9A1,1 0 0,0 13.5,10V14A1,1 0 0,0 14.5,15H15.5V16.5H16.75V15H17.5A1,1 0 0,0 18.5,14V10A1,1 0 0,0 17.5,9H14.5M15,10.5H17V13.5H15V10.5Z" /></svg>';
var hqIcon = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24"><path fill="white" d="M14.5,13.5H16.5V10.5H14.5M18,14A1,1 0 0,1 17,15H16.25V16.5H14.75V15H14A1,1 0 0,1 13,14V10A1,1 0 0,1 14,9H17A1,1 0 0,1 18,10M11,15H9.5V13H7.5V15H6V9H7.5V11.5H9.5V9H11M19,4H5C3.89,4 3,4.89 3,6V18A2,2 0 0,0 5,20H19A2,2 0 0,0 21,18V6C21,4.89 20.1,4 19,4Z" /></svg>';
var BUTTON_NAME = "GRAPHICS";
var GRAPHICS_QML_SOURCE = "hifi/dialogs/graphics/GraphicsSettings.qml";
var ui;
function getIcon() {
// TODO: Implement only once AppUi can be told to constantly retrieve / reset icons...
// var performanceProfile = Performance.getPerformancePreset();
//
// switch (performanceProfile) {
// case 0:
// return customIcon;
// break;
// case 1:
// return lqIcon;
// break;
// case 2:
// return mqIcon;
// break;
// case 3:
// return hqIcon;
// break;
// default:
// return customIcon;
// }
return customIcon;
}
function startup() {
if (!appStatus) {
ui = new AppUi({
buttonName: BUTTON_NAME,
sortOrder: 8,
normalButton: getIcon(),
activeButton: getIcon().replace('white', 'black'),
home: GRAPHICS_QML_SOURCE
});
}
appStatus = true;
}
function shutdown() {
if (appStatus) {
ui.onScriptEnding();
appStatus = false;
}
}
function cleanup() {
Messages.messageReceived.disconnect(onMessageReceived);
Messages.unsubscribe(channelComm);
}
function onMessageReceived(channel, message, sender, localOnly) {
if (channel === channelComm && localOnly) {
if (Settings.getValue(GRAPHICS_HIDE_AND_SHOW_SETTING_KEY, GRAPHICS_HIDE_AND_SHOW_DEFAULT_VALUE)) {
startup();
} else {
shutdown();
}
}
}
//
// Run the functions.
//
if (Settings.getValue(GRAPHICS_HIDE_AND_SHOW_SETTING_KEY, GRAPHICS_HIDE_AND_SHOW_DEFAULT_VALUE)) {
startup();
}
Messages.subscribe(channelComm);
Messages.messageReceived.connect(onMessageReceived);
Script.scriptEnding.connect(cleanup);
}()); // END LOCAL_SCOPE

View file

@ -0,0 +1,116 @@
# Settings
This application allows users to adjust multiple aspects of the Overte application.
## Developing
### QML Widgets
This application provides several QML widgets to make adding additional settings easy.
The important settings related widgets are as follows:
| Name | Description |
|---------------------|------------------------------------------------------------------------|
| SettingBoolean.qml | An enable/disable toggle for a provided setting |
| SettingComboBox.qml | A drop down element which provides a list of options |
| SettingNumber.qml | An input which only allows numerical data entry |
| SettingSlider.qml | An input which only allows numerical data entry, presented as a slider |
#### Examples
SettingBoolean.qml:
```qml
SettingBoolean {
// Provide the label for the setting (String).
settingText: "Rendering effects";
// Pass a function that is executed and the resulting value is set to the internal variable "settingEnabled"
// This function is executed when the component is loaded, setting the initial state of the boolean.
// When setting a graphics preset, all SettingBoolean elements have their "update()" function executed.
// The "update()" function executes the function provided here as the "settingEnabledCondition".
settingEnabledCondition: function () { return Render.renderMethod === 0; }
// When the value is changed, execute...
onSettingEnabledChanged: {
// Adjust the application setting to the current value of this boolean.
Render.renderMethod = settingEnabled ? 0 : 1;
}
}
```
SettingComboBox.qml:
```qml
SettingComboBox {
// Provide the label for the setting (String).
settingText: "Refresh rate";
// Options for the setting (Array of strings)
options: ["Economical", "Interactive", "Real-Time", "Custom"];
// Set the index of the combobox based on the current setting (int).
optionIndex: Performance.getRefreshRateProfile();
// When the value is changed, execute...
onValueChanged: {
// Adjust the application setting to the current value of the combobox.
// Note: the "index" variable provides the index of the provided options which is selected.
Performance.setRefreshRateProfile(index);
// If the index is 3 (Custom), show advanced settings, otherwise hide advanced settings.
customFPSVaulesContainer.visible = index == 3;
}
}
```
SettingNumber.qml:
```qml
SettingNumber {
// Provide the label for the setting (String).
settingText: "Focus Active";
// Set the minimum value allowed for this input (real).
minValue: 5;
// Set the maximum value allowed (real).
maxValue: 9999;
// Extra text to add at the far right of the element.
suffixText: "fps";
// Set the initial value of the number based on the current setting (var).
settingValue: Performance.getCustomRefreshRate(0);
// When the value is changed, execute...
onValueChanged: {
// Adjust the application setting to the current value of this number.
// Note: the "value" variable provides the current value of this element.
Performance.setCustomRefreshRate(0, value);
}
}
```
SettingSlider.qml:
```qml
SettingSlider {
// Provide the label for the setting (String).
settingText: "Resolution scale";
// Set the step size for the slider (real).
sliderStepSize: 0.1;
// Set the minimum value allowed by the slider (real).
minValue: 0.1;
// Set the maximum value allowed by the slider (real).
maxValue: 2;
// Set the initial value based on the current setting (var).
settingValue: Render.viewportResolutionScale.toFixed(1)
// When the value is changed, execute...
onSliderValueChanged: {
// Adjust the application setting to the current value of this slider.
// Note: the "value" variable provides the current value of this element.
Render.viewportResolutionScale = value.toFixed(1)
}
}
```

View file

@ -0,0 +1,71 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3
import "./qml"
import "./qml/pages"
Rectangle {
signal sendToScript(var message);
color: Qt.rgba(0.1,0.1,0.1,1);
width: parent.width;
height: parent.height;
anchors.centerIn: parent;
anchors.horizontalCenter: parent.horizontalCenter
property var pages: [
{name: "General", icon: "../img/overte.svg", targetPage: "hifi/tablet/TabletGeneralPreferences.qml" },
{name: "Graphics", icon: "../img/computer.svg", targetPage: "" },
{name: "Audio", icon: "../img/volume.svg", targetPage: "hifi/audio/Audio.qml" },
{name: "Controls", icon: "../img/dpad.svg", targetPage: "hifi/tablet/ControllerSettings.qml" },
{name: "Security", icon: "../img/badge.svg", targetPage: "hifi/dialogs/security/Security.qml" },
{name: "QML Allowlist", icon: "../img/lock.svg", targetPage: "hifi/dialogs/security/EntityScriptQMLAllowlist.qml" },
{name: "Script Security", icon: "../img/shield.svg", targetPage: "hifi/dialogs/security/ScriptSecurity.qml" },
];
property string currentPage: "Settings"
ColumnLayout {
width: parent.width
height: parent.height
anchors.horizontalCenter: parent.horizontalCenter
id: root
// Navigation Header
HeaderElement {
id: header
}
// Home page
SettingCenterContainer {
id: homePage
visible: currentPage == "Settings"
Layout.fillHeight: true
Repeater {
model: pages.length;
delegate: SettingSubviewListElement {
property string pageName: pages[index].name;
property string pageIcon: pages[index].icon;
property string targetPage: pages[index].targetPage;
}
}
}
// Graphics
GraphicsSettings {}
// Templates
}
// Messages from script
function fromScript(message) {
switch (message.type){
case "loadPage":
currentPage = message.page;
break;
}
}
// Send message to script
function toScript(packet){
sendToScript(packet)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#5f6368"
version="1.1"
id="svg1"
sodipodi:docname="back_arrow.svg"
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="48.541667"
inkscape:cx="11.9897"
inkscape:cy="12"
inkscape:window-width="2560"
inkscape:window-height="1363"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<path
d="m313-440 224 224-57 56-320-320 320-320 57 56-224 224h487v80H313Z"
id="path1"
style="fill:#ffffff" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="20"
viewBox="0 -960 800 800"
width="20"
fill="#ffffff"
version="1.1"
id="svg1"
sodipodi:docname="badge.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="50.375"
inkscape:cx="9.9950372"
inkscape:cy="9.9950372"
inkscape:window-width="2560"
inkscape:window-height="1368"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<path
d="M 80,-160 Q 47,-160 23.5,-183.5 0,-207 0,-240 V -680 Q 0,-713 23.5,-736.5 47,-760 80,-760 h 200 v -120 q 0,-33 23.5,-56.5 Q 327,-960 360,-960 h 80 q 33,0 56.5,23.5 23.5,23.5 23.5,56.5 v 120 h 200 q 33,0 56.5,23.5 23.5,23.5 23.5,56.5 v 440 q 0,33 -23.5,56.5 Q 753,-160 720,-160 Z m 0,-80 H 720 V -680 H 520 q 0,33 -23.5,56.5 Q 473,-600 440,-600 h -80 q -33,0 -56.5,-23.5 Q 280,-647 280,-680 H 80 Z m 80,-80 h 240 v -18 q 0,-17 -9.5,-31.5 Q 381,-384 364,-392 344,-401 323.5,-405.5 303,-410 280,-410 q -23,0 -43.5,4.5 -20.5,4.5 -40.5,13.5 -17,8 -26.5,22.5 -9.5,14.5 -9.5,31.5 z m 320,-60 h 160 v -60 H 480 Z m -200,-60 q 25,0 42.5,-17.5 Q 340,-475 340,-500 340,-525 322.5,-542.5 305,-560 280,-560 q -25,0 -42.5,17.5 -17.5,17.5 -17.5,42.5 0,25 17.5,42.5 17.5,17.5 42.5,17.5 z m 200,-60 h 160 v -60 H 480 Z M 360,-680 h 80 v -200 h -80 z m 40,220 z"
id="path1" />
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="18"
viewBox="0 -960 880 720"
width="22"
fill="#5f6368"
version="1.1"
id="svg1"
sodipodi:docname="computer.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="50.166667"
inkscape:cx="11.013289"
inkscape:cy="8.9900332"
inkscape:window-width="2560"
inkscape:window-height="1407"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<path
d="m 0,-240 v -80 h 880 v 80 z M 120,-360 Q 87,-360 63.5,-383.5 40,-407 40,-440 v -440 q 0,-33 23.5,-56.5 Q 87,-960 120,-960 h 640 q 33,0 56.5,23.5 23.5,23.5 23.5,56.5 v 440 q 0,33 -23.5,56.5 Q 793,-360 760,-360 Z m 0,-80 H 760 V -880 H 120 Z m 0,0 v -440 z"
id="path1"
style="fill:#ffffff" />
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="20"
viewBox="0 -960 800 800"
width="20"
fill="#ffffff"
version="1.1"
id="svg1"
sodipodi:docname="dpad.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="50.375"
inkscape:cx="9.9950372"
inkscape:cy="9.9950372"
inkscape:window-width="2560"
inkscape:window-height="1368"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<path
d="m 400,-734 z m 174,174 z m -348,0 z m 174,174 z m 0,-234 -120,-120 v -220 h 240 v 220 z m 180,180 -120,-120 120,-120 h 220 v 240 z m -580,0 v -240 h 220 l 120,120 -120,120 z m 280,280 v -220 l 120,-120 120,120 v 220 z m 120,-574 40,-40 v -106 h -80 v 106 z M 80,-520 h 106 l 40,-40 -40,-40 H 80 Z m 280,280 h 80 v -106 l -40,-40 -40,40 z m 254,-280 h 106 v -80 H 614 l -40,40 z"
id="path1" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="21"
viewBox="0 -960 640 840"
width="16"
fill="#ffffff"
version="1.1"
id="svg1"
sodipodi:docname="lock.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="48.541667"
inkscape:cx="8.0034335"
inkscape:cy="10.990558"
inkscape:window-width="2560"
inkscape:window-height="1368"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<path
d="M 80,-120 Q 47,-120 23.5,-143.5 0,-167 0,-200 V -600 Q 0,-633 23.5,-656.5 47,-680 80,-680 h 40 v -80 q 0,-83 58.5,-141.5 Q 237,-960 320,-960 q 83,0 141.5,58.5 Q 520,-843 520,-760 v 80 h 40 q 33,0 56.5,23.5 23.5,23.5 23.5,56.5 v 400 q 0,33 -23.5,56.5 Q 593,-120 560,-120 Z m 0,-80 H 560 V -600 H 80 Z m 240,-120 q 33,0 56.5,-23.5 Q 400,-367 400,-400 400,-433 376.5,-456.5 353,-480 320,-480 q -33,0 -56.5,23.5 -23.5,23.5 -23.5,56.5 0,33 23.5,56.5 Q 287,-320 320,-320 Z M 200,-680 h 240 v -80 q 0,-50 -35,-85 -35,-35 -85,-35 -50,0 -85,35 -35,35 -35,85 z M 80,-200 v -400 z"
id="path1" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="11.276092mm"
height="11.15854mm"
viewBox="0 0 11.276092 11.15854"
version="1.1"
id="svg5"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
sodipodi:docname="overte.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<title
id="title715">LOGO_Overte_Org_Black</title>
<sodipodi:namedview
id="namedview7"
pagecolor="#d1d1d1"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="1.2057243"
inkscape:cx="157.99632"
inkscape:cy="-143.89691"
inkscape:window-width="2560"
inkscape:window-height="1368"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
inkscape:showpageshadow="2"
inkscape:deskcolor="#d1d1d1" />
<defs
id="defs2" />
<g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="pastille"
style="display:inline"
transform="translate(-66.674462,-49.002321)" />
<g
inkscape:label="Letters"
inkscape:groupmode="layer"
id="layer1"
style="display:inline"
transform="translate(-66.674462,-49.002321)">
<path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.0240699"
d="m 71.752748,60.147164 c -1.377172,-0.14605 -2.574378,-0.732869 -3.530213,-1.730362 -0.825342,-0.861313 -1.341717,-1.931985 -1.516444,-3.144257 -0.04744,-0.329165 -0.04014,-1.234694 0.01269,-1.573208 0.220327,-1.411764 0.906991,-2.625395 2.001937,-3.538291 0.406864,-0.339217 0.998514,-0.688227 1.513503,-0.892803 0.204169,-0.0811 0.718969,-0.241851 0.774539,-0.241851 0.02985,0 0.03471,0.208002 0.03471,1.487877 v 1.48788 l -0.09026,0.04291 c -0.261261,0.124198 -0.69358,0.506545 -0.926976,0.819823 -0.7253,0.973539 -0.719063,2.366351 0.01495,3.335577 0.675906,0.892517 1.728786,1.300709 2.82169,1.093946 1.110321,-0.210056 2.027379,-1.147031 2.226244,-2.274592 0.04085,-0.23159 0.04094,-0.754422 2.06e-4,-0.986869 -0.07876,-0.449196 -0.279384,-0.901684 -0.549607,-1.239604 -0.23905,-0.298937 -0.685088,-0.667484 -0.963399,-0.796026 L 73.475802,51.95089 73.4751,50.476605 c -4.67e-4,-1.042841 0.007,-1.474284 0.02546,-1.474284 0.05635,0 0.413281,0.100024 0.646028,0.181038 2.077867,0.723258 3.543285,2.558867 3.779523,4.734302 0.03259,0.300057 0.03259,0.917373 0,1.21743 -0.116007,1.068259 -0.594065,2.195254 -1.273315,3.001776 -0.907409,1.077435 -2.114515,1.75767 -3.504262,1.974746 -0.280638,0.04384 -1.116884,0.06513 -1.395838,0.03555 z"
id="path339"
sodipodi:nodetypes="cssssssscsscsscssccscsssscc" />
</g>
<metadata
id="metadata713">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>LOGO_Overte_Org_Black</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="20"
viewBox="0 -960 640 800"
width="16"
fill="#ffffff"
version="1.1"
id="svg1"
sodipodi:docname="shield.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="48.541667"
inkscape:cx="8.0034335"
inkscape:cy="10.001717"
inkscape:window-width="2560"
inkscape:window-height="1368"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<path
d="M 320,-160 Q 181,-195 90.5,-319.5 0,-444 0,-596 v -244 l 320,-120 320,120 v 244 q 0,152 -90.5,276.5 Q 459,-195 320,-160 Z m 0,-84 q 104,-33 172,-132 68,-99 68,-220 v -189 l -240,-90 -240,90 v 189 q 0,121 68,220 68,99 172,132 z m 0,-316 z"
id="path1" />
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="17.5"
viewBox="0 -960 720 700"
width="18"
fill="#ffffff"
version="1.1"
id="svg1"
sodipodi:docname="volume2.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="50.375"
inkscape:cx="9.0024814"
inkscape:cy="8.7841191"
inkscape:window-width="2560"
inkscape:window-height="1368"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<path
d="m 440,-260 v -82 q 90,-26 145,-100 55,-74 55,-168 0,-94 -55,-168 -55,-74 -145,-100 v -82 q 124,28 202,125.5 78,97.5 78,224.5 0,127 -78,224.5 Q 564,-288 440,-260 Z M 0,-489 v -240 h 160 l 200,-200 v 640 L 160,-489 Z m 440,40 v -322 q 47,22 73.5,66 26.5,44 26.5,96 0,51 -26.5,94.5 Q 487,-471 440,-449 Z m -160,-286 -86,86 H 80 v 80 h 114 l 86,86 z m -100,126 z"
id="path1" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,63 @@
import QtQuick 2.7
import QtQuick.Controls 2.5
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3
Item {
property var isEnabled: false;
height: 0;
width: parent.width;
id: advancedOptionsRoot;
clip: true;
// Despite my best efforts, a timer is needed here to check to see if we need to expand on start.
// After the children are all moved to the advancedOptionsList column, this timer should start and correctly set
// the desired state.
Timer {
id: initExpandTimer;
interval: 100;
repeat: false;
running: false;
onTriggered: {
if (isEnabled) advancedOptionsRoot.height = advancedOptionsList.height;
}
}
// Expand Vertically on enabled animation.
Behavior on height {
NumberAnimation {
duration: 200;
easing.type: Easing.InOutCubic;
}
}
onIsEnabledChanged: {
if (isEnabled) advancedOptionsRoot.height = advancedOptionsList.height;
else advancedOptionsRoot.height = 0;
}
// Content Container.
Rectangle {
color: "#222222";
width: parent.width;
height: advancedOptionsList.height;
radius: 10;
// Content List.
ColumnLayout {
width: parent.width - 10;
id: advancedOptionsList;
}
}
// Append all children created to this widget to the correct element.
Component.onCompleted: {
while (advancedOptionsRoot.children.length > 1){
advancedOptionsRoot.children[1].parent = advancedOptionsList;
// On the last child moved, run a timer to check to see if we need to expand
initExpandTimer.running = true;
}
}
}

View file

@ -0,0 +1,44 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
Item {
height: 60;
width: parent.width;
id: root;
Rectangle {
anchors.fill: parent;
color: "black";
}
Image {
source: "../img/back_arrow.png";
anchors.verticalCenter: parent.verticalCenter;
height: 40;
width: 40;
x: currentPage == "Settings" ? -40 : 10;
Behavior on x {
NumberAnimation {
duration: 200;
easing.type: Easing.InOutCubic;
}
}
MouseArea {
anchors.fill: parent;
onClicked: {
currentPage = "Settings";
}
}
}
Text {
text: currentPage;
color: "white";
font.pixelSize: 26;
anchors.horizontalCenter: parent.horizontalCenter;
anchors.verticalCenter: parent.verticalCenter;
horizontalAlignment: Text.AlignHCenter;
}
}

View file

@ -0,0 +1,129 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3
import TabletScriptingInterface 1.0
Item {
id: root;
property string settingText: "";
property bool settingEnabled: false;
property var settingEnabledCondition;
height: 50;
width: parent.width;
Rectangle {
id: backgroundElement;
width: parent.width;
height: parent.height;
color: "transparent";
radius: 15;
RowLayout {
width: parent.width - 10;
height: parent.height;
anchors.horizontalCenter: parent.horizontalCenter;
TextEdit {
id: settingTextElem
height: parent.height;
text: settingText;
color: "white";
font.pixelSize: 22;
selectByMouse: true;
readOnly: true;
}
Switch {
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight;
checked: settingEnabled;
implicitHeight: 20;
indicator: Item {
implicitWidth: 70;
implicitHeight: parent.implicitHeight;
Rectangle {
anchors.fill: parent
radius: height / 2
color: parent.parent.checked ? "#5153bd" : "gray";
Behavior on color {
ColorAnimation {
duration: 200
easing.type: Easing.InOutCubic
}
}
}
Rectangle {
width: 30
height: 30
radius: height;
color: "white"
x: parent.parent.checked ? parent.width - width : 0;
y: (parent.implicitHeight - height) / 2
// Movement animation
Behavior on x {
NumberAnimation {
duration: 100;
easing.type: Easing.InOutCubic;
}
}
}
}
onCheckedChanged: {
Tablet.playSound(TabletEnums.ButtonClicked);
settingEnabled = checked;
}
}
}
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
propagateComposedEvents: true;
onPressed: {
mouse.accepted = false
}
onEntered: {
backgroundElement.color = "#333";
}
onExited: {
backgroundElement.color = "transparent";
}
}
Behavior on color {
ColorAnimation {
duration: 50
easing.type: Easing.InOutCubic
}
}
}
Component.onCompleted: {
update();
}
onSettingEnabledChanged: {
if (isChangingPreset === false) {
// We don't want to update this variable if we are changing to a preset.
hasPresetBeenModified = true;
}
}
function update(){
if (settingEnabledCondition && typeof settingEnabledCondition === "function") {
settingEnabled = settingEnabledCondition();
}
}
}

View file

@ -0,0 +1,23 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3
ScrollView {
width: parent.width
height: parent.height
y: header.height
id: root
ColumnLayout {
width: parent.width
anchors.horizontalCenter: parent.horizontalCenter
spacing: 0
}
// Append children made using this custom element to the ColumnLayout.
Component.onCompleted: {
while (root.contentChildren.length > 1){
root.contentChildren[2].parent = root.contentChildren[0]
}
}
}

View file

@ -0,0 +1,196 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3
import TabletScriptingInterface 1.0
Item {
id: root;
property string settingText: "";
property int optionIndex: 0;
property var _optionText: "";
readonly property string optionText: _optionText;
property var options: [""];
signal valueChanged(int index);
height: 50;
width: parent.width;
Rectangle {
id: backgroundElement;
width: parent.width;
height: parent.height;
color: "transparent";
radius: 15;
RowLayout {
width: parent.width - 10;
height: parent.height;
anchors.horizontalCenter: parent.horizontalCenter;
Layout.alignment: Qt.AlignTop;
TextEdit {
id: settingTextElem
height: parent.height;
text: settingText;
color: "white";
font.pixelSize: 22;
selectByMouse: true;
readOnly: true;
}
ComboBox {
id: control
Layout.alignment: Qt.AlignRight;
implicitWidth: 225;
implicitHeight: parent.height - 15;
model: options;
currentIndex: optionIndex;
onCurrentIndexChanged: {
valueChanged(currentIndex);
_optionText = options[currentIndex];
}
delegate: ItemDelegate {
width: control.width
contentItem: Text {
text: options[index]
color: "white"
font: control.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
background: Rectangle {
color: highlighted ? "gray" : "transparent";
radius: 10
}
highlighted: control.highlightedIndex === index
}
contentItem: RowLayout {
width: parent.width - 20;
height: parent.height;
Item {
width: parent.width - 10;
height: parent.height;
Text {
anchors.centerIn: parent;
width: parent.width;
text: control.displayText;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
elide: Text.ElideRight;
font.pixelSize: 22;
color: "white";
}
}
}
background: Rectangle {
id: comboBoxBackground;
color: "#333";
radius: 10;
width: parent.width;
}
popup: Popup {
width: control.width
padding: 1
contentItem: ListView {
clip: true
implicitHeight: contentHeight
model: control.popup.visible ? control.delegateModel : null
currentIndex: control.highlightedIndex
ScrollIndicator.vertical: ScrollIndicator { }
}
background: Rectangle {
color: Qt.rgba(0,0,0,0.9)
radius: 10
}
onVisibleChanged: {
Tablet.playSound(TabletEnums.ButtonClicked);
}
}
indicator: Canvas {
id: canvas
x: control.width - width - control.rightPadding
y: control.topPadding + (control.availableHeight - height) / 2
width: 12
height: 8
contextType: "2d"
Connections {
target: control
function onPressedChanged() { canvas.requestPaint(); }
}
onPaint: {
context.reset();
context.moveTo(0, 0);
context.lineTo(width, 0);
context.lineTo(width / 2, height);
context.closePath();
context.fillStyle = "white";
context.fill();
}
}
}
}
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
propagateComposedEvents: true;
onPressed: {
mouse.accepted = false
}
onEntered: {
backgroundElement.color = "#333";
comboBoxBackground.color = "#444";
}
onExited: {
backgroundElement.color = "transparent";
comboBoxBackground.color = "#333";
}
}
Behavior on color {
ColorAnimation {
duration: 50
easing.type: Easing.InOutCubic
}
}
}
// Updates the contents of a combobox.
// This is only required if the desired contents needs to be gathered from a javascript function and then set after the fact.
// Ideally, this would not be used, but sometimes you gotta do what you gotta do.
function setOptions(newOptions) {
// Clear the model
options = [];
// Add the new options to the model
for (var opt of newOptions){
options.push(opt);
}
// Whack it with a hammer
control.model = options;
}
function setOptionIndex(index) {
control.currentIndex = index;
}
}

View file

@ -0,0 +1,187 @@
import QtQuick 2.7
import QtQuick.Controls 2.5
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3
Item {
id: root;
property string settingText: "";
property var settingValue: 0;
property real minValue: 0;
property real maxValue: 9;
property string suffixText: "";
property real stepSize: 1;
property color buttonColor: "#333";
signal valueChanged(int value);
height: 50;
width: parent.width;
Rectangle {
id: backgroundElement;
width: parent.width;
height: parent.height;
color: "transparent";
radius: 15;
RowLayout {
width: parent.width - 10;
height: parent.height;
anchors.horizontalCenter: parent.horizontalCenter;
Layout.alignment: Qt.AlignTop;
TextEdit {
id: settingTextElem
height: parent.height;
text: settingText;
color: "white";
font.pixelSize: 22;
width: parent.width - 200;
selectByMouse: true;
readOnly: true;
}
Item {
Layout.alignment: Qt.AlignRight;
width: 225;
height: parent.height;
SpinBox {
id: spinbox;
value: settingValue;
from: minValue;
to: maxValue;
stepSize: stepSize;
Layout.alignment: Qt.AlignRight;
implicitWidth: 200;
implicitHeight: parent.height;
contentItem: TextField {
id: spinboxText;
color: "white";
text: parent.value;
verticalAlignment: Qt.AlignVCenter
horizontalAlignment: TextInput.AlignHCenter
width: parent.width;
clip: true;
font.pixelSize: 22
validator: RegExpValidator { regExp: /[0-9]*/ }
background: Rectangle {
color: "#111";
border.width: 0;
radius: 10;
}
onTextChanged: {
valueChanged(spinboxText.text);
settingValue = spinboxText.text;
}
Keys.onPressed: {
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
valueChanged(spinboxText.text);
settingValue = spinboxText.text;
}
}
}
up.indicator: Button {
height: parent.height - 15;
width: parent.height;
x: parent.width - width;
anchors.verticalCenter: parent.verticalCenter;
background: Rectangle {
color: buttonColor;
border.width: 0;
radius: 10;
}
Text {
text: "+";
color: "white";
font.pixelSize: 28;
verticalAlignment: Qt.AlignVCenter
anchors.horizontalCenter: parent.horizontalCenter;
anchors.verticalCenter: parent.verticalCenter;
}
onClicked: {
spinbox.value += stepSize;
valueChanged(spinbox.value);
}
}
down.indicator: Button {
height: parent.height - 15;
width: parent.height;
anchors.verticalCenter: parent.verticalCenter;
background: Rectangle {
color: buttonColor;
border.width: 0;
radius: 10;
}
Text {
text: "-";
color: "white";
font.pixelSize: 28;
anchors.horizontalCenter: parent.horizontalCenter;
anchors.verticalCenter: parent.verticalCenter;
}
onClicked: {
spinbox.value -= stepSize;
valueChanged(spinbox.value);
}
}
background: Rectangle {
color: "transparent";
}
}
Text {
visible: suffixText != "";
text: suffixText;
color: "white";
height: parent.height;
verticalAlignment: Qt.AlignVCenter
x: spinbox.width + 10;
}
}
}
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
propagateComposedEvents: true;
onPressed: {
mouse.accepted = false
}
onEntered: {
backgroundElement.color = "#333";
buttonColor = "#444";
}
onExited: {
backgroundElement.color = "transparent";
buttonColor = "#333";
}
}
Behavior on color {
ColorAnimation {
duration: 50
easing.type: Easing.InOutCubic
}
}
}
}

View file

@ -0,0 +1,142 @@
import QtQuick 2.7
import QtQuick.Controls 2.5
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3
Item {
id: root;
property string settingText: "";
property var settingValue: 0;
property real minValue: 0;
property real maxValue: 9;
property real sliderStepSize: 0.1;
property int roundDisplay: 1;
signal sliderValueChanged(real value);
height: 50;
width: parent.width;
Rectangle {
id: backgroundElement;
width: parent.width;
height: parent.height;
color: "transparent";
radius: 15;
RowLayout {
width: parent.width - 10;
height: parent.height;
anchors.horizontalCenter: parent.horizontalCenter;
Layout.alignment: Qt.AlignTop;
TextEdit {
id: settingTextElem
height: parent.height;
text: settingText;
color: "white";
font.pixelSize: 22;
Layout.fillWidth: true;
selectByMouse: true;
readOnly: true;
}
RowLayout {
Layout.alignment: Qt.AlignRight;
width: 225;
implicitWidth: 225;
height: parent.height;
Layout.fillWidth: false;
Text {
id: sliderValueDisplay
text: slider.value.toFixed(roundDisplay);
color: "white";
height: parent.height;
verticalAlignment: Qt.AlignVCenter
width: 25;
font.pixelSize: 22;
}
Slider {
Layout.fillWidth: true;
height: parent.height;
id: slider;
from: minValue;
to: maxValue;
stepSize: sliderStepSize;
snapMode: Slider.SnapOnRelease;
value: settingValue;
handle: Rectangle {
x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width)
y: slider.topPadding + slider.availableHeight / 2 - height / 2
implicitWidth: 20
implicitHeight: 40
color: "black"
Rectangle {
width: 16
height: 36
color: "gray"
anchors.horizontalCenter: parent.horizontalCenter;
anchors.verticalCenter: parent.verticalCenter;
}
}
background: Rectangle {
x: slider.leftPadding;
y: slider.topPadding + slider.availableHeight / 2 - height / 2;
implicitWidth: 200;
implicitHeight: 20;
width: slider.availableWidth;
height: implicitHeight;
radius: 10;
color: "#ffffff";
clip: true;
Rectangle {
width: slider.visualPosition * parent.width + 1;
height: parent.height + 1;
color: "#5153bd";
radius: parent.radius;
antialiasing: false;
}
}
onValueChanged: {
sliderValueChanged(value)
}
}
}
}
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
propagateComposedEvents: true;
onPressed: {
mouse.accepted = false
}
onEntered: {
backgroundElement.color = "#333";
}
onExited: {
backgroundElement.color = "transparent";
}
}
Behavior on color {
ColorAnimation {
duration: 50
easing.type: Easing.InOutCubic
}
}
}
}

View file

@ -0,0 +1,92 @@
import QtQuick 2.15
import QtQuick.Layouts 1.3
import TabletScriptingInterface 1.0
Item {
property color bgColor: index % 2 === 0 ? "transparent" : Qt.rgba(0.12,0.12,0.12,1);
property int initialTextXPosition;
width: parent.width;
height: 60;
Rectangle {
id: backgroundElement;
width: parent.width;
height: parent.height;
color: bgColor;
anchors.fill: parent;
Behavior on color {
ColorAnimation {
duration: 50
easing.type: Easing.InOutCubic
}
}
}
Row {
width: parent.width - 20;
height: parent.height;
anchors.centerIn: parent;
// Image/Icon container
Item {
width: 45;
height: parent.height;
Image {
sourceSize.height: 25;
source: pageIcon;
anchors.centerIn: parent;
}
}
// Page name
Text {
id: pageNameElement
text: pageName;
color: "white";
font.pixelSize: 24;
anchors.verticalCenter: parent.verticalCenter;
// Set a variable to the initial X position, used for animating it on hover.
Component.onCompleted: {
initialTextXPosition = x;
}
Behavior on x {
NumberAnimation {
duration: 50
easing.type: Easing.InOutCubic
}
}
}
}
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
onClicked: {
Tablet.playSound(TabletEnums.ButtonClicked);
if (targetPage !== "") {
toScript({type:"switchApp", appUrl: targetPage});
return;
}
currentPage = pageName;
}
onEntered: {
backgroundElement.color = "#333";
pageNameElement.x = initialTextXPosition + 20;
Tablet.playSound(TabletEnums.ButtonHover);
}
onExited: {
backgroundElement.color = bgColor;
pageNameElement.x = initialTextXPosition;
}
}
}

View file

@ -0,0 +1,353 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3
import "../"
Flickable {
property var verticalScrollBarWidth: 20;
property bool hasPresetBeenModified: false;
property bool isChangingPreset: false;
id: graphicsPage;
visible: currentPage == "Graphics";
width: parent.width;
Layout.fillHeight: true;
y: header.height + 10;
contentWidth: parent.width;
contentHeight: graphicsPageColumn.height;
clip: true;
flickDeceleration: 4000;
Timer {
id: verticalScrollBarInitialVisibilityTimer;
interval: 200;
running: false;
repeat: false;
onTriggered: {
verticalScrollBarWidth = 15;
}
}
onVisibleChanged: {
// Set the initial values for the variables.
verticalScrollBarWidth = 20;
// We are leaving the page, don't animate.
if (!visible) return;
// We have opened the page
// Start the visibility effect timers.
verticalScrollBarInitialVisibilityTimer.running = true;
}
ScrollBar.vertical: ScrollBar {
id: scrollBar;
policy: Qt.ScrollBarAlwaysOn;
background: Rectangle {
implicitWidth: verticalScrollBarWidth;
color: "transparent";
radius: 5;
visible: scrollBar.visible;
Behavior on implicitWidth {
NumberAnimation {
duration: 300;
easing.type: Easing.InOutCubic;
}
}
}
}
Column {
id: graphicsPageColumn;
width: parent.width - 20;
anchors.horizontalCenterOffset: -5
anchors.horizontalCenter: parent.horizontalCenter;
spacing: 10;
// Graphics Presets
SettingComboBox {
id: graphicsPresetCombobox;
settingText: "Graphics preset";
optionIndex: Performance.getPerformancePreset() - 1;
options: ["Low Power", "Low", "Medium", "High", "Custom"];
onValueChanged: {
Performance.setPerformancePreset(index + 1);
if (index !== 4) switchToAGraphicsPreset();
}
}
// Rendering Effects
SettingBoolean {
settingText: "Rendering effects";
settingEnabledCondition: function () { return Render.renderMethod === 0; }
onSettingEnabledChanged: {
Render.renderMethod = settingEnabled ? 0 : 1;
}
}
// Rendering Effects sub options
AdvancedOptions {
id: renderingEffectsAdvancedOptions;
isEnabled: Render.renderMethod === 0;
SettingBoolean {
settingText: "Shadows";
settingEnabledCondition: () => { return Render.shadowsEnabled }
onSettingEnabledChanged: {
Render.shadowsEnabled = settingEnabled;
}
}
SettingBoolean {
settingText: "Local Lights";
settingEnabledCondition: () => { return Render.renderMethod === 0 }
}
// NOTE: Once local lights have a proper toggle, the SettingBoolean above can be replaced with this one below.
// SettingBoolean {
// settingText: "Local Lights";
// settingEnabledCondition: () => { return Render.localLightsEnabled }
// onSettingEnabledChanged: {
// Render.localLightsEnabled = settingEnabled;
// }
// }
SettingBoolean {
settingText: "Ambient Occlusion";
settingEnabledCondition: () => { return Render.ambientOcclusionEnabled }
onSettingEnabledChanged: {
Render.ambientOcclusionEnabled = settingEnabled;
}
}
SettingBoolean {
settingText: "Haze";
settingEnabledCondition: () => { return Render.hazeEnabled }
onSettingEnabledChanged: {
Render.hazeEnabled = settingEnabled;
}
}
SettingBoolean {
settingText: "Bloom";
settingEnabledCondition: () => { return Render.bloomEnabled }
onSettingEnabledChanged: {
Render.bloomEnabled = settingEnabled;
}
}
}
// Procedural Materials
SettingBoolean {
settingText: "Procedural Materials";
settingEnabledCondition: () => { return Render.proceduralMaterialsEnabled}
onSettingEnabledChanged: {
Render.proceduralMaterialsEnabled = settingEnabled;
}
}
// FPS
SettingComboBox {
settingText: "Refresh rate";
options: ["Economical", "Interactive", "Real-Time", "Custom"];
optionIndex: Performance.getRefreshRateProfile();
onValueChanged: {
Performance.setRefreshRateProfile(index);
fpsAdvancedOptions.isEnabled = index == 3;
}
}
// Custom FPS
AdvancedOptions {
id: fpsAdvancedOptions;
isEnabled: Performance.getRefreshRateProfile() === 3;
SettingNumber {
settingText: "Focus Active";
minValue: 5;
maxValue: 9999;
suffixText: "fps";
settingValue: Performance.getCustomRefreshRate(0)
onValueChanged: {
Performance.setCustomRefreshRate(0, value);
}
}
SettingNumber {
settingText: "Focus Inactive";
minValue: 1;
maxValue: 9999;
suffixText: "fps";
settingValue: Performance.getCustomRefreshRate(1)
onValueChanged: {
Performance.setCustomRefreshRate(1, value);
}
}
SettingNumber {
settingText: "Unfocused";
minValue: 1;
maxValue: 9999;
suffixText: "fps";
settingValue: Performance.getCustomRefreshRate(2)
onValueChanged: {
Performance.setCustomRefreshRate(2, value);
}
}
SettingNumber {
settingText: "Minimized";
minValue: 1;
maxValue: 9999;
suffixText: "fps";
settingValue: Performance.getCustomRefreshRate(3)
onValueChanged: {
Performance.setCustomRefreshRate(3, value);
}
}
SettingNumber {
settingText: "Startup";
minValue: 1;
maxValue: 9999;
suffixText: "fps";
settingValue: Performance.getCustomRefreshRate(4)
onValueChanged: {
Performance.setCustomRefreshRate(4, value);
}
}
SettingNumber {
settingText: "Shutdown";
minValue: 1;
maxValue: 9999;
suffixText: "fps";
settingValue: Performance.getCustomRefreshRate(5)
onValueChanged: {
Performance.setCustomRefreshRate(5, value);
}
}
}
// Resolution Scale
SettingSlider {
settingText: "Resolution scale";
sliderStepSize: 0.1;
minValue: 0.1;
maxValue: 2;
settingValue: Render.viewportResolutionScale.toFixed(1)
onSliderValueChanged: {
Render.viewportResolutionScale = value.toFixed(1)
}
}
// Fullscreen Display
SettingComboBox {
settingText: "Fullscreen Display";
Component.onCompleted: {
var screens = Render.getScreens();
var selected = Render.getFullScreenScreen();
setOptions(screens);
for (let i = 0; screens.length > i; i++) {
if (screens[i] == selected) {
optionIndex = i;
return;
}
}
}
onValueChanged: {
Render.setFullScreenScreen(optionText);
}
}
// FOV
SettingSlider {
settingText: "Field of View";
sliderStepSize: 1;
minValue: 20;
maxValue: 130;
settingValue: Render.verticalFieldOfView.toFixed(1);
roundDisplay: 0;
onSliderValueChanged: {
Render.verticalFieldOfView = value.toFixed(1);
}
}
// Camera clipping
SettingBoolean {
settingText: "Allow camera clipping";
settingEnabledCondition: () => { return !Render.cameraClippingEnabled }
onSettingEnabledChanged: {
Render.cameraClippingEnabled = settingEnabled ? 0 : 1;
}
}
// Anti Aliasing
SettingComboBox {
settingText: "Anti-aliasing";
optionIndex: Render.antialiasingMode;
options: ["None", "TAA", "FXAA"];
onValueChanged: {
Render.antialiasingMode = index;
}
}
}
onHasPresetBeenModifiedChanged: {
if (hasPresetBeenModified === true && isChangingPreset === false){
graphicsPresetCombobox.setOptionIndex(4);
}
}
function switchToAGraphicsPreset(){
// We need to disable the event updates from settings to detect if we have changed a preset.
isChangingPreset = true;
// Change all of the settings to match the preset
recursivelyUpdateAllSettings(graphicsPageColumn);
hasPresetBeenModified = false;
// "Unmute" the events listening for a preset change.
isChangingPreset = false;
}
function recursivelyUpdateAllSettings(item){
// In order to update all settings based on current values,
// we need to go through all children elements and re-evaluate their settingEnabled value
// Update all settings options visually to reflect settings
for (let i = 0; item.children.length > i; i++) {
var child = item.children[i];
child.update();
// Run this function on all of this elements children.
recursivelyUpdateAllSettings(child);
}
}
}

View file

@ -0,0 +1,96 @@
//
// settings.js
//
// App to configure Overte
//
// Created by Armored Dragon, 2024.
// Copyright 2024-2025 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/* global Script Tablet */
(() => {
"use strict";
var tablet;
var appButton;
var active = false;
const url = Script.resolvePath("./Settings.qml")
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
appButton = tablet.addButton({
icon: Script.resolvePath("./img/icon_white.png"),
activeIcon: Script.resolvePath("./img/icon_black.png"),
text: "SETTINGS",
isActive: active,
});
// When script ends, remove itself from tablet
Script.scriptEnding.connect(function () {
console.log("Shutting down Settings.js application");
tablet.removeButton(appButton);
Menu.removeMenuItem("Settings", "Graphics...");
});
// Event listeners
appButton.clicked.connect(toolbarButtonClicked);
tablet.fromQml.connect(fromQML);
tablet.screenChanged.connect(onTabletScreenChanged);
Menu.menuItemEvent.connect(onMenuItemEvent);
// Menu button
Menu.addMenuItem({
menuName: "Settings",
menuItemName: "Graphics...",
afterItem: "Audio...",
});
function onMenuItemEvent(menuItem) {
if (menuItem === 'Graphics...') {
toolbarButtonClicked();
toQML({ type: 'loadPage', page: 'Graphics' })
}
}
function toolbarButtonClicked() {
if (active) tablet.gotoHomeScreen();
else tablet.loadQMLSource(url);
active = !active;
appButton.editProperties({
isActive: active,
});
}
function onTabletScreenChanged(type, newUrl) {
if (url == newUrl) active = true;
else active = false;
appButton.editProperties({
isActive: active,
});
}
// Communication
function fromQML(event) {
console.log(`New QML event:\n${JSON.stringify(event)}`);
if (event.type === "switchApp") {
if (event.appUrl == "hifi/dialogs/GeneralPreferencesDialog.qml") {
// This page needs to be opened like this just because.
Desktop.show("hifi/dialogs/GeneralPreferencesDialog.qml", "GeneralPreferencesDialog");
return;
}
tablet.loadQMLSource(event.appUrl);
}
}
/**
* Emit a packet to the HTML front end. Easy communication!
* @param {Object} packet - The Object packet to emit to the HTML
* @param {("loadPage"|)} packet.type - The type of packet it is
*/
function toQML(packet = { type: "" }) {
tablet.sendToQml(packet);
}
})();