From 0c13c2b6e2176d6861680409f934f8379259db42 Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 13 Nov 2017 16:37:28 +0100 Subject: [PATCH 001/438] Remove unused QQC1 imports --- .../QtWebEngine/UIDelegates/AlertDialog.qml | 2 -- .../resources/QtWebEngine/UIDelegates/FilePicker.qml | 2 -- interface/resources/QtWebEngine/UIDelegates/Menu.qml | 1 - .../resources/QtWebEngine/UIDelegates/MenuItem.qml | 2 -- .../QtWebEngine/UIDelegates/PromptDialog.qml | 2 -- interface/resources/qml/AvatarInputs.qml | 3 --- interface/resources/qml/Browser.qml | 1 - interface/resources/qml/LoginDialog/SignUpBody.qml | 1 - .../qml/LoginDialog/UsernameCollisionBody.qml | 1 - interface/resources/qml/QmlWebWindow.qml | 1 - interface/resources/qml/QmlWindow.qml | 5 ----- interface/resources/qml/StatText.qml | 1 - interface/resources/qml/Stats.qml | 1 - interface/resources/qml/TabletBrowser.qml | 1 - interface/resources/qml/Web3DOverlay.qml | 1 - interface/resources/qml/controller/AnalogButton.qml | 3 --- interface/resources/qml/controller/AnalogStick.qml | 3 --- interface/resources/qml/controller/Hydra.qml | 3 --- interface/resources/qml/controller/Standard.qml | 3 --- interface/resources/qml/controller/ToggleButton.qml | 3 --- interface/resources/qml/controller/Xbox.qml | 3 --- .../resources/qml/controller/hydra/HydraButtons.qml | 3 --- .../resources/qml/controller/hydra/HydraStick.qml | 3 --- interface/resources/qml/controller/xbox/DPad.qml | 3 --- .../qml/controller/xbox/LeftAnalogStick.qml | 3 --- .../qml/controller/xbox/RightAnalogStick.qml | 3 --- .../resources/qml/controller/xbox/XboxButtons.qml | 3 --- .../resources/qml/controls-uit/HorizontalRule.qml | 2 -- .../resources/qml/controls-uit/ImageMessageBox.qml | 1 - .../resources/qml/controls-uit/QueuedButton.qml | 2 -- .../resources/qml/controls-uit/TabletHeader.qml | 1 - interface/resources/qml/controls-uit/TextAction.qml | 2 -- interface/resources/qml/controls-uit/TextEdit.qml | 2 -- interface/resources/qml/controls/FontAwesome.qml | 2 -- interface/resources/qml/dialogs/AssetDialog.qml | 1 - interface/resources/qml/dialogs/MessageDialog.qml | 1 - .../resources/qml/dialogs/PreferencesDialog.qml | 1 - .../resources/qml/dialogs/TabletAssetDialog.qml | 1 - .../qml/dialogs/TabletConnectionFailureDialog.qml | 1 - .../resources/qml/dialogs/TabletLoginDialog.qml | 1 - interface/resources/qml/dialogs/TabletMessageBox.qml | 1 - .../dialogs/messageDialog/MessageDialogButton.qml | 1 - .../qml/dialogs/preferences/AvatarBrowser.qml | 1 - .../qml/dialogs/preferences/ComboBoxPreference.qml | 2 -- .../resources/qml/dialogs/preferences/Preference.qml | 1 - interface/resources/qml/hifi/ComboDialog.qml | 1 - .../resources/qml/hifi/DesktopLetterboxMessage.qml | 1 - interface/resources/qml/hifi/LetterboxMessage.qml | 1 - interface/resources/qml/hifi/audio/InputPeak.qml | 2 -- interface/resources/qml/hifi/audio/MicBar.qml | 2 -- .../qml/hifi/commerce/common/CommerceLightbox.qml | 1 - .../commerce/common/EmulatedMarketplaceHeader.qml | 1 - .../qml/hifi/commerce/common/FirstUseTutorial.qml | 1 - .../inspectionCertificate/InspectionCertificate.qml | 1 - .../qml/hifi/commerce/purchases/Purchases.qml | 1 - .../qml/hifi/commerce/wallet/NeedsLogIn.qml | 1 - .../qml/hifi/commerce/wallet/PassphraseChange.qml | 1 - .../qml/hifi/commerce/wallet/PassphraseModal.qml | 1 - .../qml/hifi/commerce/wallet/PassphraseSelection.qml | 1 - .../resources/qml/hifi/commerce/wallet/Security.qml | 1 - .../qml/hifi/commerce/wallet/SecurityImageChange.qml | 1 - .../hifi/commerce/wallet/SecurityImageSelection.qml | 1 - .../resources/qml/hifi/commerce/wallet/SendMoney.qml | 1 - .../resources/qml/hifi/commerce/wallet/Wallet.qml | 1 - .../qml/hifi/commerce/wallet/WalletSetup.qml | 1 - .../resources/qml/hifi/dialogs/AttachmentsDialog.qml | 1 - .../qml/hifi/dialogs/ModelBrowserDialog.qml | 1 - .../resources/qml/hifi/dialogs/TabletDCDialog.qml | 1 - .../qml/hifi/dialogs/TabletEntityStatistics.qml | 1 - .../qml/hifi/dialogs/TabletEntityStatisticsItem.qml | 2 -- .../resources/qml/hifi/dialogs/TabletLODTools.qml | 2 -- .../qml/hifi/dialogs/attachments/Attachment.qml | 4 ---- .../qml/hifi/dialogs/attachments/Vector3.qml | 1 - .../resources/qml/hifi/overlays/ImageOverlay.qml | 1 - interface/resources/qml/hifi/overlays/Overlay.qml | 1 - .../resources/qml/hifi/overlays/RectangleOverlay.qml | 1 - .../resources/qml/hifi/overlays/TextOverlay.qml | 1 - .../resources/qml/hifi/tablet/CalibratingScreen.qml | 5 +---- .../resources/qml/hifi/tablet/InputRecorder.qml | 2 -- .../resources/qml/hifi/tablet/NewModelDialog.qml | 1 - .../qml/hifi/tablet/OpenVrConfiguration.qml | 12 +----------- .../qml/hifi/tablet/TabletAttachmentsDialog.qml | 1 - .../resources/qml/hifi/tablet/TabletMenuItem.qml | 1 - .../resources/qml/hifi/tablet/TabletMenuView.qml | 2 -- .../qml/hifi/tablet/TabletModelBrowserDialog.qml | 1 - interface/resources/qml/hifi/tablet/TabletRoot.qml | 1 - .../tablet/tabletWindows/TabletPreferencesDialog.qml | 4 ---- .../tablet/tabletWindows/preferences/Preference.qml | 1 - .../tablet/tabletWindows/preferences/Section.qml | 1 - .../preferences/TabletAvatarBrowser.qml | 1 - interface/resources/qml/hifi/toolbars/StateImage.qml | 1 - interface/resources/qml/hifi/toolbars/Toolbar.qml | 1 - .../resources/qml/hifi/toolbars/ToolbarButton.qml | 1 - .../resources/qml/styles-uit/AnonymousProRegular.qml | 2 -- interface/resources/qml/styles-uit/ButtonLabel.qml | 2 -- .../resources/qml/styles-uit/FiraSansRegular.qml | 2 -- .../resources/qml/styles-uit/FiraSansSemiBold.qml | 2 -- interface/resources/qml/styles-uit/IconButton.qml | 2 -- interface/resources/qml/styles-uit/InfoItem.qml | 2 -- interface/resources/qml/styles-uit/InputLabel.qml | 2 -- interface/resources/qml/styles-uit/ListItem.qml | 2 -- interface/resources/qml/styles-uit/Logs.qml | 2 -- interface/resources/qml/styles-uit/OverlayTitle.qml | 2 -- interface/resources/qml/styles-uit/RalewayBold.qml | 2 -- interface/resources/qml/styles-uit/RalewayLight.qml | 2 -- .../resources/qml/styles-uit/RalewayRegular.qml | 2 -- .../resources/qml/styles-uit/RalewaySemiBold.qml | 2 -- interface/resources/qml/styles-uit/SectionName.qml | 2 -- interface/resources/qml/styles-uit/ShortcutText.qml | 2 -- interface/resources/qml/styles-uit/TabName.qml | 2 -- .../resources/qml/styles-uit/TextFieldInput.qml | 2 -- interface/resources/qml/windows/DefaultFrame.qml | 1 - interface/resources/qml/windows/Fadable.qml | 2 -- .../resources/qml/windows/TabletModalWindow.qml | 3 --- interface/resources/qml/windows/Window.qml | 2 -- 115 files changed, 2 insertions(+), 202 deletions(-) diff --git a/interface/resources/QtWebEngine/UIDelegates/AlertDialog.qml b/interface/resources/QtWebEngine/UIDelegates/AlertDialog.qml index e6dc03fa55..bf72869752 100644 --- a/interface/resources/QtWebEngine/UIDelegates/AlertDialog.qml +++ b/interface/resources/QtWebEngine/UIDelegates/AlertDialog.qml @@ -1,6 +1,4 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.0 import "../../qml/dialogs" diff --git a/interface/resources/QtWebEngine/UIDelegates/FilePicker.qml b/interface/resources/QtWebEngine/UIDelegates/FilePicker.qml index cb6552b075..55f7d27534 100644 --- a/interface/resources/QtWebEngine/UIDelegates/FilePicker.qml +++ b/interface/resources/QtWebEngine/UIDelegates/FilePicker.qml @@ -1,6 +1,4 @@ import QtQuick 2.4 -import QtQuick.Dialogs 1.1 -import QtQuick.Controls 1.4 import "../../qml/dialogs" diff --git a/interface/resources/QtWebEngine/UIDelegates/Menu.qml b/interface/resources/QtWebEngine/UIDelegates/Menu.qml index 1bbbbd6cbe..46c00e758e 100644 --- a/interface/resources/QtWebEngine/UIDelegates/Menu.qml +++ b/interface/resources/QtWebEngine/UIDelegates/Menu.qml @@ -1,5 +1,4 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 as Controls import "../../qml/controls-uit" import "../../qml/styles-uit" diff --git a/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml b/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml index 1890fcb81d..6014b6834b 100644 --- a/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml +++ b/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml @@ -1,6 +1,4 @@ - import QtQuick 2.5 -import QtQuick.Controls 1.4 as Controls import "../../qml/controls-uit" import "../../qml/styles-uit" diff --git a/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml b/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml index 01d3262bc0..e4ab3037ef 100644 --- a/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml +++ b/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml @@ -1,6 +1,4 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.0 import "../../qml/controls-uit" import "../../qml/styles-uit" diff --git a/interface/resources/qml/AvatarInputs.qml b/interface/resources/qml/AvatarInputs.qml index be4bf03465..5001cb79e5 100644 --- a/interface/resources/qml/AvatarInputs.qml +++ b/interface/resources/qml/AvatarInputs.qml @@ -8,9 +8,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtGraphicalEffects 1.0 -import Qt.labs.settings 1.0 import "./hifi/audio" as HifiAudio diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 55927fda24..aa01e6e5a0 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -1,5 +1,4 @@ import QtQuick 2.5 -import QtQuick.Controls 1.2 import QtWebChannel 1.0 import QtWebEngine 1.2 diff --git a/interface/resources/qml/LoginDialog/SignUpBody.qml b/interface/resources/qml/LoginDialog/SignUpBody.qml index 9d55998b40..5eb99e0ece 100644 --- a/interface/resources/qml/LoginDialog/SignUpBody.qml +++ b/interface/resources/qml/LoginDialog/SignUpBody.qml @@ -11,7 +11,6 @@ import Hifi 1.0 import QtQuick 2.7 import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 as OriginalStyles import "../controls-uit" import "../styles-uit" diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml index 5c212578b8..bf05a36ce1 100644 --- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -11,7 +11,6 @@ import Hifi 1.0 import QtQuick 2.4 import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 as OriginalStyles import "../controls-uit" import "../styles-uit" diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml index d2daf0fa1d..d73a574081 100644 --- a/interface/resources/qml/QmlWebWindow.qml +++ b/interface/resources/qml/QmlWebWindow.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import QtWebEngine 1.1 import QtWebChannel 1.0 diff --git a/interface/resources/qml/QmlWindow.qml b/interface/resources/qml/QmlWindow.qml index 9a84418b3a..f1ef1464d3 100644 --- a/interface/resources/qml/QmlWindow.qml +++ b/interface/resources/qml/QmlWindow.qml @@ -1,9 +1,4 @@ - import QtQuick 2.3 -import QtQuick.Controls 1.4 -import QtWebChannel 1.0 -import QtWebEngine 1.2 -import QtWebSockets 1.0 import "windows" as Windows import "controls" diff --git a/interface/resources/qml/StatText.qml b/interface/resources/qml/StatText.qml index 69963c1373..9fefbd28b8 100644 --- a/interface/resources/qml/StatText.qml +++ b/interface/resources/qml/StatText.qml @@ -1,5 +1,4 @@ import QtQuick 2.3 -import QtQuick.Controls 1.2 Text { color: "white"; diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 4626d9bcda..d961285a46 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -1,6 +1,5 @@ import Hifi 1.0 as Hifi import QtQuick 2.3 -import QtQuick.Controls 1.2 import '.' Item { diff --git a/interface/resources/qml/TabletBrowser.qml b/interface/resources/qml/TabletBrowser.qml index 8dbcc8f4f8..141c1f25a7 100644 --- a/interface/resources/qml/TabletBrowser.qml +++ b/interface/resources/qml/TabletBrowser.qml @@ -1,5 +1,4 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 import QtWebChannel 1.0 import QtWebEngine 1.5 diff --git a/interface/resources/qml/Web3DOverlay.qml b/interface/resources/qml/Web3DOverlay.qml index a1fa2d2641..fdd5d8a7c6 100644 --- a/interface/resources/qml/Web3DOverlay.qml +++ b/interface/resources/qml/Web3DOverlay.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import "controls" as Controls diff --git a/interface/resources/qml/controller/AnalogButton.qml b/interface/resources/qml/controller/AnalogButton.qml index 82beb818ab..6539bc793d 100644 --- a/interface/resources/qml/controller/AnalogButton.qml +++ b/interface/resources/qml/controller/AnalogButton.qml @@ -1,7 +1,4 @@ import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 Item { id: root diff --git a/interface/resources/qml/controller/AnalogStick.qml b/interface/resources/qml/controller/AnalogStick.qml index c0d10bac59..d7b52a6319 100644 --- a/interface/resources/qml/controller/AnalogStick.qml +++ b/interface/resources/qml/controller/AnalogStick.qml @@ -1,7 +1,4 @@ import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 Item { id: root diff --git a/interface/resources/qml/controller/Hydra.qml b/interface/resources/qml/controller/Hydra.qml index 19f3b4c193..e5c918cc77 100644 --- a/interface/resources/qml/controller/Hydra.qml +++ b/interface/resources/qml/controller/Hydra.qml @@ -1,7 +1,4 @@ import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 import "hydra" diff --git a/interface/resources/qml/controller/Standard.qml b/interface/resources/qml/controller/Standard.qml index 45e4febfa2..1c51a527cd 100644 --- a/interface/resources/qml/controller/Standard.qml +++ b/interface/resources/qml/controller/Standard.qml @@ -1,7 +1,4 @@ import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 import "xbox" diff --git a/interface/resources/qml/controller/ToggleButton.qml b/interface/resources/qml/controller/ToggleButton.qml index ee8bd380e2..567191bd25 100644 --- a/interface/resources/qml/controller/ToggleButton.qml +++ b/interface/resources/qml/controller/ToggleButton.qml @@ -1,7 +1,4 @@ import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 Item { id: root diff --git a/interface/resources/qml/controller/Xbox.qml b/interface/resources/qml/controller/Xbox.qml index 4ff2959129..71f99c2081 100644 --- a/interface/resources/qml/controller/Xbox.qml +++ b/interface/resources/qml/controller/Xbox.qml @@ -1,7 +1,4 @@ import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 import "xbox" diff --git a/interface/resources/qml/controller/hydra/HydraButtons.qml b/interface/resources/qml/controller/hydra/HydraButtons.qml index aa8927f5b6..f579527a1f 100644 --- a/interface/resources/qml/controller/hydra/HydraButtons.qml +++ b/interface/resources/qml/controller/hydra/HydraButtons.qml @@ -1,7 +1,4 @@ import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 import ".." diff --git a/interface/resources/qml/controller/hydra/HydraStick.qml b/interface/resources/qml/controller/hydra/HydraStick.qml index d082a20b10..7b5ce41c76 100644 --- a/interface/resources/qml/controller/hydra/HydraStick.qml +++ b/interface/resources/qml/controller/hydra/HydraStick.qml @@ -1,7 +1,4 @@ import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 import ".." diff --git a/interface/resources/qml/controller/xbox/DPad.qml b/interface/resources/qml/controller/xbox/DPad.qml index 2cfb6412e7..20eda19648 100644 --- a/interface/resources/qml/controller/xbox/DPad.qml +++ b/interface/resources/qml/controller/xbox/DPad.qml @@ -1,7 +1,4 @@ import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 import ".." diff --git a/interface/resources/qml/controller/xbox/LeftAnalogStick.qml b/interface/resources/qml/controller/xbox/LeftAnalogStick.qml index 8e2de1eb36..430d6f39a4 100644 --- a/interface/resources/qml/controller/xbox/LeftAnalogStick.qml +++ b/interface/resources/qml/controller/xbox/LeftAnalogStick.qml @@ -1,7 +1,4 @@ import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 import ".." diff --git a/interface/resources/qml/controller/xbox/RightAnalogStick.qml b/interface/resources/qml/controller/xbox/RightAnalogStick.qml index 0cdfeda2cf..89e93aa8bc 100644 --- a/interface/resources/qml/controller/xbox/RightAnalogStick.qml +++ b/interface/resources/qml/controller/xbox/RightAnalogStick.qml @@ -1,7 +1,4 @@ import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 import ".." diff --git a/interface/resources/qml/controller/xbox/XboxButtons.qml b/interface/resources/qml/controller/xbox/XboxButtons.qml index e26a4a0b98..5c68fcff72 100644 --- a/interface/resources/qml/controller/xbox/XboxButtons.qml +++ b/interface/resources/qml/controller/xbox/XboxButtons.qml @@ -1,7 +1,4 @@ import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 import ".." diff --git a/interface/resources/qml/controls-uit/HorizontalRule.qml b/interface/resources/qml/controls-uit/HorizontalRule.qml index 425500f1d4..0609cc451d 100644 --- a/interface/resources/qml/controls-uit/HorizontalRule.qml +++ b/interface/resources/qml/controls-uit/HorizontalRule.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 Rectangle { anchors.left: parent.left diff --git a/interface/resources/qml/controls-uit/ImageMessageBox.qml b/interface/resources/qml/controls-uit/ImageMessageBox.qml index 95c753aab4..74313f7ffe 100644 --- a/interface/resources/qml/controls-uit/ImageMessageBox.qml +++ b/interface/resources/qml/controls-uit/ImageMessageBox.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../styles-uit" Item { diff --git a/interface/resources/qml/controls-uit/QueuedButton.qml b/interface/resources/qml/controls-uit/QueuedButton.qml index 36ffbe582f..6612d582df 100644 --- a/interface/resources/qml/controls-uit/QueuedButton.qml +++ b/interface/resources/qml/controls-uit/QueuedButton.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 as Original -import QtQuick.Controls.Styles 1.4 import "../styles-uit" import "." as HifiControls diff --git a/interface/resources/qml/controls-uit/TabletHeader.qml b/interface/resources/qml/controls-uit/TabletHeader.qml index 17530f81ea..56203de286 100644 --- a/interface/resources/qml/controls-uit/TabletHeader.qml +++ b/interface/resources/qml/controls-uit/TabletHeader.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../styles-uit" diff --git a/interface/resources/qml/controls-uit/TextAction.qml b/interface/resources/qml/controls-uit/TextAction.qml index 1c66c3630a..1745a6c273 100644 --- a/interface/resources/qml/controls-uit/TextAction.qml +++ b/interface/resources/qml/controls-uit/TextAction.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import "../styles-uit" import "../controls-uit" as HifiControls diff --git a/interface/resources/qml/controls-uit/TextEdit.qml b/interface/resources/qml/controls-uit/TextEdit.qml index 5ee9ce94ba..d03dcbbac6 100644 --- a/interface/resources/qml/controls-uit/TextEdit.qml +++ b/interface/resources/qml/controls-uit/TextEdit.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import "../styles-uit" TextEdit { diff --git a/interface/resources/qml/controls/FontAwesome.qml b/interface/resources/qml/controls/FontAwesome.qml index 50d7e96fb5..c0f7fbb5fe 100644 --- a/interface/resources/qml/controls/FontAwesome.qml +++ b/interface/resources/qml/controls/FontAwesome.qml @@ -1,6 +1,4 @@ import QtQuick 2.3 -import QtQuick.Controls 1.3 -import QtQuick.Controls.Styles 1.3 Text { id: root diff --git a/interface/resources/qml/dialogs/AssetDialog.qml b/interface/resources/qml/dialogs/AssetDialog.qml index 8d19d38efb..e8d28e9b37 100644 --- a/interface/resources/qml/dialogs/AssetDialog.qml +++ b/interface/resources/qml/dialogs/AssetDialog.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import Qt.labs.settings 1.0 import "../styles-uit" diff --git a/interface/resources/qml/dialogs/MessageDialog.qml b/interface/resources/qml/dialogs/MessageDialog.qml index 40c5a01e15..d7549f664b 100644 --- a/interface/resources/qml/dialogs/MessageDialog.qml +++ b/interface/resources/qml/dialogs/MessageDialog.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs import "../controls-uit" diff --git a/interface/resources/qml/dialogs/PreferencesDialog.qml b/interface/resources/qml/dialogs/PreferencesDialog.qml index e16f3aa12d..601f350ed5 100644 --- a/interface/resources/qml/dialogs/PreferencesDialog.qml +++ b/interface/resources/qml/dialogs/PreferencesDialog.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../controls-uit" as HifiControls import "../styles-uit" diff --git a/interface/resources/qml/dialogs/TabletAssetDialog.qml b/interface/resources/qml/dialogs/TabletAssetDialog.qml index 016deec094..897378e40c 100644 --- a/interface/resources/qml/dialogs/TabletAssetDialog.qml +++ b/interface/resources/qml/dialogs/TabletAssetDialog.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../styles-uit" import "../windows" diff --git a/interface/resources/qml/dialogs/TabletConnectionFailureDialog.qml b/interface/resources/qml/dialogs/TabletConnectionFailureDialog.qml index 6d2ff36550..544824135e 100644 --- a/interface/resources/qml/dialogs/TabletConnectionFailureDialog.qml +++ b/interface/resources/qml/dialogs/TabletConnectionFailureDialog.qml @@ -10,7 +10,6 @@ import Hifi 1.0 import QtQuick 2.5 -import QtQuick.Controls 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs Item { diff --git a/interface/resources/qml/dialogs/TabletLoginDialog.qml b/interface/resources/qml/dialogs/TabletLoginDialog.qml index 269788a808..c85b2b2ba0 100644 --- a/interface/resources/qml/dialogs/TabletLoginDialog.qml +++ b/interface/resources/qml/dialogs/TabletLoginDialog.qml @@ -10,7 +10,6 @@ import Hifi 1.0 import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../controls-uit" import "../styles-uit" diff --git a/interface/resources/qml/dialogs/TabletMessageBox.qml b/interface/resources/qml/dialogs/TabletMessageBox.qml index f8876b1ec8..fabe0dd247 100644 --- a/interface/resources/qml/dialogs/TabletMessageBox.qml +++ b/interface/resources/qml/dialogs/TabletMessageBox.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs import "../controls-uit" diff --git a/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml b/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml index b7ff9354eb..4a4d741619 100644 --- a/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml +++ b/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.2 import QtQuick.Dialogs 1.2 import "../../controls-uit" diff --git a/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml b/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml index 5949adffca..d03fc4d880 100644 --- a/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml +++ b/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import QtWebChannel 1.0 import QtWebEngine 1.2 diff --git a/interface/resources/qml/dialogs/preferences/ComboBoxPreference.qml b/interface/resources/qml/dialogs/preferences/ComboBoxPreference.qml index 860cbcb5a8..3b3efaf520 100644 --- a/interface/resources/qml/dialogs/preferences/ComboBoxPreference.qml +++ b/interface/resources/qml/dialogs/preferences/ComboBoxPreference.qml @@ -10,8 +10,6 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import "../../controls-uit" as HiFiControls import "../../styles-uit" diff --git a/interface/resources/qml/dialogs/preferences/Preference.qml b/interface/resources/qml/dialogs/preferences/Preference.qml index 1d72197382..6956147950 100644 --- a/interface/resources/qml/dialogs/preferences/Preference.qml +++ b/interface/resources/qml/dialogs/preferences/Preference.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 Item { id: root diff --git a/interface/resources/qml/hifi/ComboDialog.qml b/interface/resources/qml/hifi/ComboDialog.qml index 83fcad18c7..a0cee109a8 100644 --- a/interface/resources/qml/hifi/ComboDialog.qml +++ b/interface/resources/qml/hifi/ComboDialog.qml @@ -10,7 +10,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../styles-uit" import "../controls-uit" diff --git a/interface/resources/qml/hifi/DesktopLetterboxMessage.qml b/interface/resources/qml/hifi/DesktopLetterboxMessage.qml index bafa518eb9..1804177fd7 100644 --- a/interface/resources/qml/hifi/DesktopLetterboxMessage.qml +++ b/interface/resources/qml/hifi/DesktopLetterboxMessage.qml @@ -10,7 +10,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../styles-uit" Item { diff --git a/interface/resources/qml/hifi/LetterboxMessage.qml b/interface/resources/qml/hifi/LetterboxMessage.qml index 7ce66adf11..d5b5dc9d5e 100644 --- a/interface/resources/qml/hifi/LetterboxMessage.qml +++ b/interface/resources/qml/hifi/LetterboxMessage.qml @@ -10,7 +10,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../styles-uit" Item { diff --git a/interface/resources/qml/hifi/audio/InputPeak.qml b/interface/resources/qml/hifi/audio/InputPeak.qml index 4ff49091b1..ca65bc699e 100644 --- a/interface/resources/qml/hifi/audio/InputPeak.qml +++ b/interface/resources/qml/hifi/audio/InputPeak.qml @@ -10,8 +10,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 Rectangle { diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index b6699d6ceb..0d6f938e5d 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -10,8 +10,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import TabletScriptingInterface 1.0 diff --git a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml index 145b78bdc4..1dde0373c8 100644 --- a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml +++ b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml @@ -14,7 +14,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 -import QtQuick.Controls 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls diff --git a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml index 8b3f017fd2..e8559b3019 100644 --- a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml +++ b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml @@ -14,7 +14,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.7 import QtGraphicalEffects 1.0 -import QtQuick.Controls 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls diff --git a/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml b/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml index 0d3f67ef7a..0f9db0a51e 100644 --- a/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml +++ b/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml @@ -14,7 +14,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 -import QtQuick.Controls 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index b6c29a1fad..47aacc80a3 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -13,7 +13,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index f292f9603e..767cae82a5 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -13,7 +13,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls diff --git a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml index 7ce0cf3853..ae95b3d80a 100644 --- a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml +++ b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml @@ -13,7 +13,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseChange.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseChange.qml index a75d511793..8adb78cc53 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseChange.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseChange.qml @@ -13,7 +13,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml index d967a36b68..3eed3f7bf9 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -13,7 +13,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml index ffeedde8f0..b132e76d67 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml @@ -13,7 +13,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls diff --git a/interface/resources/qml/hifi/commerce/wallet/Security.qml b/interface/resources/qml/hifi/commerce/wallet/Security.qml index 485b0ec086..d5696c2c56 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Security.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Security.qml @@ -14,7 +14,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 -import QtQuick.Controls 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml b/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml index 7f767060f6..cc644ed457 100644 --- a/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml +++ b/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml @@ -13,7 +13,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml b/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml index e12332cd0c..548e4c1777 100644 --- a/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml +++ b/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml @@ -13,7 +13,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls diff --git a/interface/resources/qml/hifi/commerce/wallet/SendMoney.qml b/interface/resources/qml/hifi/commerce/wallet/SendMoney.qml index 75334b1686..438d343230 100644 --- a/interface/resources/qml/hifi/commerce/wallet/SendMoney.qml +++ b/interface/resources/qml/hifi/commerce/wallet/SendMoney.qml @@ -13,7 +13,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 759d7a37eb..aa032d9ebd 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -14,7 +14,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 -import QtQuick.Controls 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml index 99fe933bd6..a54a2ba373 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml @@ -14,7 +14,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 -import QtQuick.Controls 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls diff --git a/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml index 76484cf8c7..006a4b7158 100644 --- a/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml +++ b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import Qt.labs.settings 1.0 import "../../styles-uit" diff --git a/interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml b/interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml index c427052904..55b4e98bf5 100644 --- a/interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml +++ b/interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../../windows" import "content" diff --git a/interface/resources/qml/hifi/dialogs/TabletDCDialog.qml b/interface/resources/qml/hifi/dialogs/TabletDCDialog.qml index 0f363d1be9..1ec7ea3e0e 100644 --- a/interface/resources/qml/hifi/dialogs/TabletDCDialog.qml +++ b/interface/resources/qml/hifi/dialogs/TabletDCDialog.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import Qt.labs.settings 1.0 import "../../styles-uit" diff --git a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml index da295917a0..7bce460283 100644 --- a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml +++ b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import Qt.labs.settings 1.0 import "../../styles-uit" diff --git a/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml b/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml index 894a4c1813..d5c5a5ee02 100644 --- a/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml +++ b/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import Qt.labs.settings 1.0 import "../../styles-uit" import "../../controls-uit" as HifiControls diff --git a/interface/resources/qml/hifi/dialogs/TabletLODTools.qml b/interface/resources/qml/hifi/dialogs/TabletLODTools.qml index 2291a42bf6..ab53f03477 100644 --- a/interface/resources/qml/hifi/dialogs/TabletLODTools.qml +++ b/interface/resources/qml/hifi/dialogs/TabletLODTools.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import Qt.labs.settings 1.0 import "../../styles-uit" import "../../controls-uit" as HifiControls diff --git a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml index b6f906ffc2..89ad327a71 100644 --- a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml +++ b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml @@ -1,8 +1,4 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Dialogs 1.2 as OriginalDialogs -import Qt.labs.settings 1.0 import "." import ".." diff --git a/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml b/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml index 29f2c0ebf4..eb6172ec78 100644 --- a/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml +++ b/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml @@ -1,5 +1,4 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControls diff --git a/interface/resources/qml/hifi/overlays/ImageOverlay.qml b/interface/resources/qml/hifi/overlays/ImageOverlay.qml index b509f0ce3a..b111596db7 100644 --- a/interface/resources/qml/hifi/overlays/ImageOverlay.qml +++ b/interface/resources/qml/hifi/overlays/ImageOverlay.qml @@ -1,5 +1,4 @@ import QtQuick 2.3 -import QtQuick.Controls 1.2 import QtGraphicalEffects 1.0 import "." diff --git a/interface/resources/qml/hifi/overlays/Overlay.qml b/interface/resources/qml/hifi/overlays/Overlay.qml index 80f3233b69..188833bf88 100644 --- a/interface/resources/qml/hifi/overlays/Overlay.qml +++ b/interface/resources/qml/hifi/overlays/Overlay.qml @@ -1,6 +1,5 @@ import Hifi 1.0 import QtQuick 2.3 -import QtQuick.Controls 1.2 Item { id: root diff --git a/interface/resources/qml/hifi/overlays/RectangleOverlay.qml b/interface/resources/qml/hifi/overlays/RectangleOverlay.qml index cba3b560d2..4d33d73f85 100644 --- a/interface/resources/qml/hifi/overlays/RectangleOverlay.qml +++ b/interface/resources/qml/hifi/overlays/RectangleOverlay.qml @@ -1,5 +1,4 @@ import QtQuick 2.3 -import QtQuick.Controls 1.2 import "." diff --git a/interface/resources/qml/hifi/overlays/TextOverlay.qml b/interface/resources/qml/hifi/overlays/TextOverlay.qml index 20336fdde6..a162fc01f5 100644 --- a/interface/resources/qml/hifi/overlays/TextOverlay.qml +++ b/interface/resources/qml/hifi/overlays/TextOverlay.qml @@ -1,5 +1,4 @@ import QtQuick 2.3 -import QtQuick.Controls 1.2 import "." diff --git a/interface/resources/qml/hifi/tablet/CalibratingScreen.qml b/interface/resources/qml/hifi/tablet/CalibratingScreen.qml index dd56bc96ab..6c26bd87c5 100644 --- a/interface/resources/qml/hifi/tablet/CalibratingScreen.qml +++ b/interface/resources/qml/hifi/tablet/CalibratingScreen.qml @@ -8,11 +8,8 @@ import QtQuick 2.5 - -import QtQuick 2.5 -import QtQuick.Controls 1.4 import QtGraphicalEffects 1.0 -import QtQuick.Controls.Styles 1.4 + import "../../styles-uit" import "../../controls" import "../../controls-uit" as HifiControls diff --git a/interface/resources/qml/hifi/tablet/InputRecorder.qml b/interface/resources/qml/hifi/tablet/InputRecorder.qml index 292deb751e..527a6cacb4 100644 --- a/interface/resources/qml/hifi/tablet/InputRecorder.qml +++ b/interface/resources/qml/hifi/tablet/InputRecorder.qml @@ -8,8 +8,6 @@ import QtQuick 2.5 import Hifi 1.0 -import QtQuick.Controls 1.4 -import QtQuick.Dialogs 1.2 as OriginalDialogs import "../../styles-uit" import "../../controls-uit" as HifiControls diff --git a/interface/resources/qml/hifi/tablet/NewModelDialog.qml b/interface/resources/qml/hifi/tablet/NewModelDialog.qml index 47d28486a9..be0e5fc77a 100644 --- a/interface/resources/qml/hifi/tablet/NewModelDialog.qml +++ b/interface/resources/qml/hifi/tablet/NewModelDialog.qml @@ -10,7 +10,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../../styles-uit" import "../../controls-uit" diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index 78c10e2ffa..25abbb994a 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -7,10 +7,8 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import QtGraphicalEffects 1.0 -import QtQuick.Controls 1.4 as Original -import QtQuick.Controls.Styles 1.4 + import "../../styles-uit" import "../../controls" import "../../controls-uit" as HifiControls @@ -53,10 +51,6 @@ Rectangle { } - - - - MouseArea { id: mouseArea @@ -593,9 +587,6 @@ Rectangle { } } - - - HiFiGlyphs { id: glyphButton color: enabled ? hifi.buttons.textColor[calibrationButton.color] @@ -625,7 +616,6 @@ Rectangle { } } - MouseArea { anchors.fill: parent hoverEnabled: true diff --git a/interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml b/interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml index 19548365aa..7e1cdea0db 100644 --- a/interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../../controls-uit" as HifiControls import "../../styles-uit" diff --git a/interface/resources/qml/hifi/tablet/TabletMenuItem.qml b/interface/resources/qml/hifi/tablet/TabletMenuItem.qml index 11d3cab35e..5612e4d629 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuItem.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuItem.qml @@ -10,7 +10,6 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import "../../controls-uit" import "../../styles-uit" diff --git a/interface/resources/qml/hifi/tablet/TabletMenuView.qml b/interface/resources/qml/hifi/tablet/TabletMenuView.qml index 4a4a6b7f87..67eeb5b010 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuView.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuView.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import TabletScriptingInterface 1.0 import "../../styles-uit" diff --git a/interface/resources/qml/hifi/tablet/TabletModelBrowserDialog.qml b/interface/resources/qml/hifi/tablet/TabletModelBrowserDialog.qml index 60bd7a88e0..d69d760b95 100644 --- a/interface/resources/qml/hifi/tablet/TabletModelBrowserDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletModelBrowserDialog.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../../controls-uit" as HifiControls import "../../styles-uit" diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index a161741049..0d2ea6bda0 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -1,6 +1,5 @@ import QtQuick 2.0 import Hifi 1.0 -import QtQuick.Controls 1.4 import "../../dialogs" import "../../controls" diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml index fcb3e9ff92..a5ef3d2686 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml @@ -9,10 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtGraphicalEffects 1.0 - import "." import "./preferences" diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Preference.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Preference.qml index 9986c85445..fcf022c8ef 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Preference.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Preference.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 Item { id: root diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml index 8cf254809d..5036569031 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import Hifi 1.0 import "../../../../dialogs/preferences" diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml index 2ea12f1d3d..25b08b2b5a 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 import QtWebChannel 1.0 import QtWebEngine 1.2 diff --git a/interface/resources/qml/hifi/toolbars/StateImage.qml b/interface/resources/qml/hifi/toolbars/StateImage.qml index ebf1544f2b..9cd311060d 100644 --- a/interface/resources/qml/hifi/toolbars/StateImage.qml +++ b/interface/resources/qml/hifi/toolbars/StateImage.qml @@ -1,5 +1,4 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 Item { property alias imageURL: image.source diff --git a/interface/resources/qml/hifi/toolbars/Toolbar.qml b/interface/resources/qml/hifi/toolbars/Toolbar.qml index 01aa29f665..c3477baaaa 100644 --- a/interface/resources/qml/hifi/toolbars/Toolbar.qml +++ b/interface/resources/qml/hifi/toolbars/Toolbar.qml @@ -1,5 +1,4 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 import Qt.labs.settings 1.0 import "../../windows" diff --git a/interface/resources/qml/hifi/toolbars/ToolbarButton.qml b/interface/resources/qml/hifi/toolbars/ToolbarButton.qml index 63149ad23b..14a3537332 100644 --- a/interface/resources/qml/hifi/toolbars/ToolbarButton.qml +++ b/interface/resources/qml/hifi/toolbars/ToolbarButton.qml @@ -1,5 +1,4 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 StateImage { id: button diff --git a/interface/resources/qml/styles-uit/AnonymousProRegular.qml b/interface/resources/qml/styles-uit/AnonymousProRegular.qml index 789689973b..83e1de8d02 100644 --- a/interface/resources/qml/styles-uit/AnonymousProRegular.qml +++ b/interface/resources/qml/styles-uit/AnonymousProRegular.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 Text { id: root diff --git a/interface/resources/qml/styles-uit/ButtonLabel.qml b/interface/resources/qml/styles-uit/ButtonLabel.qml index aade5fb439..d227cb4869 100644 --- a/interface/resources/qml/styles-uit/ButtonLabel.qml +++ b/interface/resources/qml/styles-uit/ButtonLabel.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import "." RalewayBold { diff --git a/interface/resources/qml/styles-uit/FiraSansRegular.qml b/interface/resources/qml/styles-uit/FiraSansRegular.qml index 4ae0826772..3413361de1 100644 --- a/interface/resources/qml/styles-uit/FiraSansRegular.qml +++ b/interface/resources/qml/styles-uit/FiraSansRegular.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 Text { id: root diff --git a/interface/resources/qml/styles-uit/FiraSansSemiBold.qml b/interface/resources/qml/styles-uit/FiraSansSemiBold.qml index b3f3324090..1ac7048bc0 100644 --- a/interface/resources/qml/styles-uit/FiraSansSemiBold.qml +++ b/interface/resources/qml/styles-uit/FiraSansSemiBold.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 Text { id: root diff --git a/interface/resources/qml/styles-uit/IconButton.qml b/interface/resources/qml/styles-uit/IconButton.qml index 84c1ef14c1..e5a18e2ae7 100644 --- a/interface/resources/qml/styles-uit/IconButton.qml +++ b/interface/resources/qml/styles-uit/IconButton.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import "." RalewayRegular { diff --git a/interface/resources/qml/styles-uit/InfoItem.qml b/interface/resources/qml/styles-uit/InfoItem.qml index 83781a4ef5..fa7684e8e7 100644 --- a/interface/resources/qml/styles-uit/InfoItem.qml +++ b/interface/resources/qml/styles-uit/InfoItem.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import "." RalewaySemiBold { diff --git a/interface/resources/qml/styles-uit/InputLabel.qml b/interface/resources/qml/styles-uit/InputLabel.qml index 59657a554d..3853dd5b19 100644 --- a/interface/resources/qml/styles-uit/InputLabel.qml +++ b/interface/resources/qml/styles-uit/InputLabel.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import "." RalewaySemiBold { diff --git a/interface/resources/qml/styles-uit/ListItem.qml b/interface/resources/qml/styles-uit/ListItem.qml index f707686edc..a69c4b48c2 100644 --- a/interface/resources/qml/styles-uit/ListItem.qml +++ b/interface/resources/qml/styles-uit/ListItem.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import "." RalewayRegular { diff --git a/interface/resources/qml/styles-uit/Logs.qml b/interface/resources/qml/styles-uit/Logs.qml index 577fe2f8d8..45d4436fbf 100644 --- a/interface/resources/qml/styles-uit/Logs.qml +++ b/interface/resources/qml/styles-uit/Logs.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import "." AnonymousProRegular { diff --git a/interface/resources/qml/styles-uit/OverlayTitle.qml b/interface/resources/qml/styles-uit/OverlayTitle.qml index e23b9eca14..0fb423baab 100644 --- a/interface/resources/qml/styles-uit/OverlayTitle.qml +++ b/interface/resources/qml/styles-uit/OverlayTitle.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import "." RalewayRegular { diff --git a/interface/resources/qml/styles-uit/RalewayBold.qml b/interface/resources/qml/styles-uit/RalewayBold.qml index 433fdb7ae6..2d529cff8a 100644 --- a/interface/resources/qml/styles-uit/RalewayBold.qml +++ b/interface/resources/qml/styles-uit/RalewayBold.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 Text { id: root diff --git a/interface/resources/qml/styles-uit/RalewayLight.qml b/interface/resources/qml/styles-uit/RalewayLight.qml index 913918afd7..7785bc29d0 100644 --- a/interface/resources/qml/styles-uit/RalewayLight.qml +++ b/interface/resources/qml/styles-uit/RalewayLight.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 Text { id: root diff --git a/interface/resources/qml/styles-uit/RalewayRegular.qml b/interface/resources/qml/styles-uit/RalewayRegular.qml index aab31ecf33..2b1c17e49c 100644 --- a/interface/resources/qml/styles-uit/RalewayRegular.qml +++ b/interface/resources/qml/styles-uit/RalewayRegular.qml @@ -9,8 +9,6 @@ // import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 Text { id: root diff --git a/interface/resources/qml/styles-uit/RalewaySemiBold.qml b/interface/resources/qml/styles-uit/RalewaySemiBold.qml index b6c79e02a4..fe3a42dd6c 100644 --- a/interface/resources/qml/styles-uit/RalewaySemiBold.qml +++ b/interface/resources/qml/styles-uit/RalewaySemiBold.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 Text { id: root diff --git a/interface/resources/qml/styles-uit/SectionName.qml b/interface/resources/qml/styles-uit/SectionName.qml index 5438fec7bc..20f8e1e116 100644 --- a/interface/resources/qml/styles-uit/SectionName.qml +++ b/interface/resources/qml/styles-uit/SectionName.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import "." RalewayRegular { diff --git a/interface/resources/qml/styles-uit/ShortcutText.qml b/interface/resources/qml/styles-uit/ShortcutText.qml index a3ab351870..8504ffa2b8 100644 --- a/interface/resources/qml/styles-uit/ShortcutText.qml +++ b/interface/resources/qml/styles-uit/ShortcutText.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import "." RalewayLight { diff --git a/interface/resources/qml/styles-uit/TabName.qml b/interface/resources/qml/styles-uit/TabName.qml index eb4e790e7e..0f620fe8c2 100644 --- a/interface/resources/qml/styles-uit/TabName.qml +++ b/interface/resources/qml/styles-uit/TabName.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import "." RalewayRegular { diff --git a/interface/resources/qml/styles-uit/TextFieldInput.qml b/interface/resources/qml/styles-uit/TextFieldInput.qml index 010b4d03ad..f2a57e57fc 100644 --- a/interface/resources/qml/styles-uit/TextFieldInput.qml +++ b/interface/resources/qml/styles-uit/TextFieldInput.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import "." FiraSansSemiBold { diff --git a/interface/resources/qml/windows/DefaultFrame.qml b/interface/resources/qml/windows/DefaultFrame.qml index 33c2818849..60e744bec3 100644 --- a/interface/resources/qml/windows/DefaultFrame.qml +++ b/interface/resources/qml/windows/DefaultFrame.qml @@ -9,7 +9,6 @@ // import QtQuick 2.5 -import QtGraphicalEffects 1.0 import "." import "../styles-uit" diff --git a/interface/resources/qml/windows/Fadable.qml b/interface/resources/qml/windows/Fadable.qml index 4d506755f2..0945c6e47e 100644 --- a/interface/resources/qml/windows/Fadable.qml +++ b/interface/resources/qml/windows/Fadable.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtGraphicalEffects 1.0 import "../styles-uit" diff --git a/interface/resources/qml/windows/TabletModalWindow.qml b/interface/resources/qml/windows/TabletModalWindow.qml index e21cb6b224..0dbf4689d6 100644 --- a/interface/resources/qml/windows/TabletModalWindow.qml +++ b/interface/resources/qml/windows/TabletModalWindow.qml @@ -8,9 +8,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Dialogs 1.2 as OriginalDialogs -import "." Rectangle { id: modalWindow diff --git a/interface/resources/qml/windows/Window.qml b/interface/resources/qml/windows/Window.qml index a0ef73290a..ea68fd8783 100644 --- a/interface/resources/qml/windows/Window.qml +++ b/interface/resources/qml/windows/Window.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 import QtGraphicalEffects 1.0 import "." From 6b732804eb279ed720013c7fdd82e9c9000695c5 Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 13 Nov 2017 21:15:31 +0100 Subject: [PATCH 002/438] Button as QQC2 --- .../resources/qml/controls-uit/Button.qml | 140 +++++++++--------- interface/resources/qml/controls/Button.qml | 35 ++--- 2 files changed, 86 insertions(+), 89 deletions(-) diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controls-uit/Button.qml index c068fdcfaf..c6bac84510 100644 --- a/interface/resources/qml/controls-uit/Button.qml +++ b/interface/resources/qml/controls-uit/Button.qml @@ -9,19 +9,21 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 as Original -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.2 as Original import TabletScriptingInterface 1.0 import "../styles-uit" Original.Button { - id: root; + id: control; property int color: 0 property int colorScheme: hifi.colorSchemes.light property string buttonGlyph: ""; + //TODO: add real Action item. Backport from Qt 5.10 + property QtObject action: null + width: 120 height: hifi.dimensions.controlLineHeight @@ -37,79 +39,77 @@ Original.Button { tabletInterface.playSound(TabletEnums.ButtonClick); } - style: ButtonStyle { + background: Rectangle { + radius: hifi.buttons.radius - background: Rectangle { - radius: hifi.buttons.radius + border.width: (control.color === hifi.buttons.none || + (control.color === hifi.buttons.noneBorderless && control.hovered) || + (control.color === hifi.buttons.noneBorderlessWhite && control.hovered) || + (control.color === hifi.buttons.noneBorderlessGray && control.hovered)) ? 1 : 0; + border.color: control.color === hifi.buttons.noneBorderless ? hifi.colors.blueHighlight : + (control.color === hifi.buttons.noneBorderlessGray ? hifi.colors.baseGray : hifi.colors.white); - border.width: (control.color === hifi.buttons.none || - (control.color === hifi.buttons.noneBorderless && control.hovered) || - (control.color === hifi.buttons.noneBorderlessWhite && control.hovered) || - (control.color === hifi.buttons.noneBorderlessGray && control.hovered)) ? 1 : 0; - border.color: control.color === hifi.buttons.noneBorderless ? hifi.colors.blueHighlight : - (control.color === hifi.buttons.noneBorderlessGray ? hifi.colors.baseGray : hifi.colors.white); - - gradient: Gradient { - GradientStop { - position: 0.2 - color: { - if (!control.enabled) { - hifi.buttons.disabledColorStart[control.colorScheme] - } else if (control.pressed) { - hifi.buttons.pressedColor[control.color] - } else if (control.hovered) { - hifi.buttons.hoveredColor[control.color] - } else { - hifi.buttons.colorStart[control.color] - } - } - } - GradientStop { - position: 1.0 - color: { - if (!control.enabled) { - hifi.buttons.disabledColorFinish[control.colorScheme] - } else if (control.pressed) { - hifi.buttons.pressedColor[control.color] - } else if (control.hovered) { - hifi.buttons.hoveredColor[control.color] - } else { - hifi.buttons.colorFinish[control.color] - } + gradient: Gradient { + GradientStop { + position: 0.2 + color: { + if (!control.enabled) { + hifi.buttons.disabledColorStart[control.colorScheme] + } else if (control.pressed) { + hifi.buttons.pressedColor[control.color] + } else if (control.hovered) { + hifi.buttons.hoveredColor[control.color] + } else { + hifi.buttons.colorStart[control.color] } } } - } - - label: Item { - HiFiGlyphs { - id: buttonGlyph; - visible: root.buttonGlyph !== ""; - text: root.buttonGlyph === "" ? hifi.glyphs.question : root.buttonGlyph; - // Size - size: 34; - // Anchors - anchors.right: buttonText.left; - anchors.top: parent.top; - anchors.bottom: parent.bottom; - // Style - color: enabled ? hifi.buttons.textColor[control.color] - : hifi.buttons.disabledTextColor[control.colorScheme]; - // Alignment - horizontalAlignment: Text.AlignHCenter; - verticalAlignment: Text.AlignVCenter; - } - RalewayBold { - id: buttonText; - anchors.centerIn: parent; - font.capitalization: Font.AllUppercase - color: enabled ? hifi.buttons.textColor[control.color] - : hifi.buttons.disabledTextColor[control.colorScheme] - size: hifi.fontSizes.buttonLabel - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - text: control.text + GradientStop { + position: 1.0 + color: { + if (!control.enabled) { + hifi.buttons.disabledColorFinish[control.colorScheme] + } else if (control.pressed) { + hifi.buttons.pressedColor[control.color] + } else if (control.hovered) { + hifi.buttons.hoveredColor[control.color] + } else { + hifi.buttons.colorFinish[control.color] + } + } } } } + + contentItem: Item { + HiFiGlyphs { + id: buttonGlyph; + visible: control.buttonGlyph !== ""; + text: control.buttonGlyph === "" ? hifi.glyphs.question : control.buttonGlyph; + // Size + size: 34; + // Anchors + anchors.right: buttonText.left; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + // Style + color: enabled ? hifi.buttons.textColor[control.color] + : hifi.buttons.disabledTextColor[control.colorScheme]; + // Alignment + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + } + RalewayBold { + id: buttonText; + anchors.centerIn: parent; + font.capitalization: Font.AllUppercase + color: enabled ? hifi.buttons.textColor[control.color] + : hifi.buttons.disabledTextColor[control.colorScheme] + size: hifi.fontSizes.buttonLabel + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: control.text + } + } } + diff --git a/interface/resources/qml/controls/Button.qml b/interface/resources/qml/controls/Button.qml index c5a9f99343..ac3c2d32d4 100644 --- a/interface/resources/qml/controls/Button.qml +++ b/interface/resources/qml/controls/Button.qml @@ -1,27 +1,24 @@ import QtQuick 2.3 -import QtQuick.Controls 1.3 as Original -import QtQuick.Controls.Styles 1.3 +import QtQuick.Controls 2.2 as Original import "." import "../styles" Original.Button { - style: ButtonStyle { - HifiConstants { id: hifi } - padding { - top: 8 - left: 12 - right: 12 - bottom: 8 - } - background: Border { - anchors.fill: parent - } - label: Text { - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - text: control.text - color: control.enabled ? hifi.colors.text : hifi.colors.disabledText - } + HifiConstants { id: hifi } + padding { + top: 8 + left: 12 + right: 12 + bottom: 8 + } + background: Border { + anchors.fill: parent + } + contentItem: Text { + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: control.text + color: control.enabled ? hifi.colors.text : hifi.colors.disabledText } } From 28523752853e8bb1ae908bd42d4043410ecca19c Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 15 Nov 2017 20:32:28 +0100 Subject: [PATCH 003/438] Make QueryDioalogs buttons works --- .../resources/qml/controls-uit/Button.qml | 7 +++-- .../resources/qml/dialogs/QueryDialog.qml | 28 +++++++++++-------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controls-uit/Button.qml index c6bac84510..165de98878 100644 --- a/interface/resources/qml/controls-uit/Button.qml +++ b/interface/resources/qml/controls-uit/Button.qml @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 +import QtQuick 2.7 import QtQuick.Controls 2.2 as Original import TabletScriptingInterface 1.0 @@ -22,7 +22,7 @@ Original.Button { property string buttonGlyph: ""; //TODO: add real Action item. Backport from Qt 5.10 - property QtObject action: null + property Shortcut action: null width: 120 height: hifi.dimensions.controlLineHeight @@ -37,6 +37,9 @@ Original.Button { onClicked: { tabletInterface.playSound(TabletEnums.ButtonClick); + if (action !== null) { + action.activated() + } } background: Rectangle { diff --git a/interface/resources/qml/dialogs/QueryDialog.qml b/interface/resources/qml/dialogs/QueryDialog.qml index 9a38c3f0d6..159e5a1801 100644 --- a/interface/resources/qml/dialogs/QueryDialog.qml +++ b/interface/resources/qml/dialogs/QueryDialog.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.2 import QtQuick.Dialogs 1.2 as OriginalDialogs import "../controls-uit" @@ -145,24 +145,30 @@ ModalWindow { margins: 0 bottomMargin: hifi.dimensions.contentSpacing.y } - Button { action: cancelAction } - Button { action: acceptAction } + Button { + action: cancelAction; + text: qsTr("Cancel"); + } + + Button { + action: acceptAction + text: qsTr("OK"); + } } - Action { + Shortcut { id: cancelAction - text: qsTr("Cancel") - shortcut: Qt.Key_Escape - onTriggered: { + sequence: Qt.Key_Escape + onActivated: { root.canceled(); root.destroy(); } } - Action { + + Shortcut { id: acceptAction - text: qsTr("OK") - shortcut: Qt.Key_Return - onTriggered: { + sequence: Qt.Key_Return + onActivated: { root.result = items ? comboBox.currentText : textResult.text root.selected(root.result); root.destroy(); From b4f6c25f4d21e77b670349abaf1c5c41bb0bb571 Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 16 Nov 2017 19:04:35 +0100 Subject: [PATCH 004/438] Implement QQC1-compatible Action component --- .../resources/qml/controls-uit/Action.qml | 8 ++ .../resources/qml/controls-uit/Button.qml | 11 ++- .../qml/controls-uit/GlyphButton.qml | 94 ++++++++++--------- interface/resources/qml/controls/Button.qml | 11 +++ .../resources/qml/dialogs/FileDialog.qml | 11 +-- .../resources/qml/dialogs/QueryDialog.qml | 14 ++- .../qml/dialogs/TabletFileDialog.qml | 11 +-- 7 files changed, 91 insertions(+), 69 deletions(-) create mode 100644 interface/resources/qml/controls-uit/Action.qml diff --git a/interface/resources/qml/controls-uit/Action.qml b/interface/resources/qml/controls-uit/Action.qml new file mode 100644 index 0000000000..5dbfa3914b --- /dev/null +++ b/interface/resources/qml/controls-uit/Action.qml @@ -0,0 +1,8 @@ +import QtQuick 2.7 + +Shortcut { + id: root + property string text + property alias shortcut: root.sequence + signal triggered() +} diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controls-uit/Button.qml index 165de98878..7d1607caa9 100644 --- a/interface/resources/qml/controls-uit/Button.qml +++ b/interface/resources/qml/controls-uit/Button.qml @@ -21,8 +21,7 @@ Original.Button { property int colorScheme: hifi.colorSchemes.light property string buttonGlyph: ""; - //TODO: add real Action item. Backport from Qt 5.10 - property Shortcut action: null + property Action action: null width: 120 height: hifi.dimensions.controlLineHeight @@ -35,10 +34,16 @@ Original.Button { } } + onActionChanged: { + if (action !== null && action.text !== "") { + control.text = action.text + } + } + onClicked: { tabletInterface.playSound(TabletEnums.ButtonClick); if (action !== null) { - action.activated() + action.triggered() } } diff --git a/interface/resources/qml/controls-uit/GlyphButton.qml b/interface/resources/qml/controls-uit/GlyphButton.qml index bc7bc636fe..2c715ea29c 100644 --- a/interface/resources/qml/controls-uit/GlyphButton.qml +++ b/interface/resources/qml/controls-uit/GlyphButton.qml @@ -8,15 +8,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - -import QtQuick 2.5 -import QtQuick.Controls 1.4 as Original -import QtQuick.Controls.Styles 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 as Original import TabletScriptingInterface 1.0 import "../styles-uit" Original.Button { + id: control property int color: 0 property int colorScheme: hifi.colorSchemes.light property string glyph: "" @@ -25,65 +24,68 @@ Original.Button { width: 120 height: 28 + property Action action: null + onHoveredChanged: { if (hovered) { tabletInterface.playSound(TabletEnums.ButtonHover); } } + onActionChanged: { + if (action !== null && action.text !== "") { + control.text = action.text + } + } onClicked: { tabletInterface.playSound(TabletEnums.ButtonClick); + if (action !== null) { + action.triggered() + } } - style: ButtonStyle { + background: Rectangle { + radius: hifi.buttons.radius - background: Rectangle { - radius: hifi.buttons.radius - - gradient: Gradient { - GradientStop { - position: 0.2 - color: { - if (!control.enabled) { - hifi.buttons.disabledColorStart[control.colorScheme] - } else if (control.pressed) { - hifi.buttons.pressedColor[control.color] - } else if (control.hovered) { - hifi.buttons.hoveredColor[control.color] - } else { - hifi.buttons.colorStart[control.color] - } + gradient: Gradient { + GradientStop { + position: 0.2 + color: { + if (!control.enabled) { + hifi.buttons.disabledColorStart[control.colorScheme] + } else if (control.pressed) { + hifi.buttons.pressedColor[control.color] + } else if (control.hovered) { + hifi.buttons.hoveredColor[control.color] + } else { + hifi.buttons.colorStart[control.color] } } - GradientStop { - position: 1.0 - color: { - if (!control.enabled) { - hifi.buttons.disabledColorFinish[control.colorScheme] - } else if (control.pressed) { - hifi.buttons.pressedColor[control.color] - } else if (control.hovered) { - hifi.buttons.hoveredColor[control.color] - } else { - hifi.buttons.colorFinish[control.color] - } + } + GradientStop { + position: 1.0 + color: { + if (!control.enabled) { + hifi.buttons.disabledColorFinish[control.colorScheme] + } else if (control.pressed) { + hifi.buttons.pressedColor[control.color] + } else if (control.hovered) { + hifi.buttons.hoveredColor[control.color] + } else { + hifi.buttons.colorFinish[control.color] } } } } + } - label: HiFiGlyphs { - color: enabled ? hifi.buttons.textColor[control.color] - : hifi.buttons.disabledTextColor[control.colorScheme] - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - anchors { - // Tweak horizontal alignment so that it looks right. - left: parent.left - leftMargin: -0.5 - } - text: control.glyph - size: control.size - } + contentItem: HiFiGlyphs { + color: enabled ? hifi.buttons.textColor[control.color] + : hifi.buttons.disabledTextColor[control.colorScheme] + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: control.glyph + size: control.size } } + diff --git a/interface/resources/qml/controls/Button.qml b/interface/resources/qml/controls/Button.qml index ac3c2d32d4..6cbdec5644 100644 --- a/interface/resources/qml/controls/Button.qml +++ b/interface/resources/qml/controls/Button.qml @@ -3,9 +3,20 @@ import QtQuick.Controls 2.2 as Original import "." import "../styles" +import "../controls-uit" Original.Button { + id: control + HifiConstants { id: hifi } + property Action action: null + + onActionChanged: { + if (action !== null && action.text !== "") { + control.text = action.text + } + } + padding { top: 8 left: 12 diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index b9633104d5..26530bf633 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -8,12 +8,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 import Qt.labs.folderlistmodel 2.1 import Qt.labs.settings 1.0 -import QtQuick.Controls.Styles 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Controls 1.4 as QQC1 import ".." import "../controls-uit" @@ -543,7 +542,7 @@ ModalWindow { } } - TableViewColumn { + QQC1.TableViewColumn { id: fileNameColumn role: "fileName" title: "Name" @@ -551,7 +550,7 @@ ModalWindow { movable: false resizable: true } - TableViewColumn { + QQC1.TableViewColumn { id: fileMofifiedColumn role: "fileModified" title: "Date" @@ -560,7 +559,7 @@ ModalWindow { resizable: true visible: !selectDirectory } - TableViewColumn { + QQC1.TableViewColumn { role: "fileSize" title: "Size" width: fileTableView.width - fileNameColumn.width - fileMofifiedColumn.width diff --git a/interface/resources/qml/dialogs/QueryDialog.qml b/interface/resources/qml/dialogs/QueryDialog.qml index 159e5a1801..889bcda768 100644 --- a/interface/resources/qml/dialogs/QueryDialog.qml +++ b/interface/resources/qml/dialogs/QueryDialog.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 2.2 -import QtQuick.Dialogs 1.2 as OriginalDialogs import "../controls-uit" import "../styles-uit" @@ -147,28 +145,28 @@ ModalWindow { } Button { action: cancelAction; - text: qsTr("Cancel"); } Button { action: acceptAction - text: qsTr("OK"); } } - Shortcut { + Action { id: cancelAction + text: qsTr("Cancel"); sequence: Qt.Key_Escape - onActivated: { + onTriggered: { root.canceled(); root.destroy(); } } - Shortcut { + Action { id: acceptAction + text: qsTr("OK"); sequence: Qt.Key_Return - onActivated: { + onTriggered: { root.result = items ? comboBox.currentText : textResult.text root.selected(root.result); root.destroy(); diff --git a/interface/resources/qml/dialogs/TabletFileDialog.qml b/interface/resources/qml/dialogs/TabletFileDialog.qml index d3b738469e..a03e0d02f3 100644 --- a/interface/resources/qml/dialogs/TabletFileDialog.qml +++ b/interface/resources/qml/dialogs/TabletFileDialog.qml @@ -8,12 +8,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 import Qt.labs.folderlistmodel 2.1 import Qt.labs.settings 1.0 -import QtQuick.Controls.Styles 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Controls 1.4 as QQC1 import ".." import "../controls-uit" @@ -541,7 +540,7 @@ TabletModalWindow { } } - TableViewColumn { + QQC1.TableViewColumn { id: fileNameColumn role: "fileName" title: "Name" @@ -549,7 +548,7 @@ TabletModalWindow { movable: false resizable: true } - TableViewColumn { + QQC1.TableViewColumn { id: fileMofifiedColumn role: "fileModified" title: "Date" @@ -558,7 +557,7 @@ TabletModalWindow { resizable: true visible: !selectDirectory } - TableViewColumn { + QQC1.TableViewColumn { role: "fileSize" title: "Size" width: fileTableView.width - fileNameColumn.width - fileMofifiedColumn.width From cfacf3b47dacbf446ecf6aed47f51ff8e96eed59 Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 16 Nov 2017 21:58:09 +0100 Subject: [PATCH 005/438] Port web glyph button to QQC2 --- .../qml/controls-uit/WebGlyphButton.qml | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/interface/resources/qml/controls-uit/WebGlyphButton.qml b/interface/resources/qml/controls-uit/WebGlyphButton.qml index 15524e4188..fd7cd001b2 100644 --- a/interface/resources/qml/controls-uit/WebGlyphButton.qml +++ b/interface/resources/qml/controls-uit/WebGlyphButton.qml @@ -9,8 +9,7 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 as Original -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.2 as Original import "../styles-uit" @@ -26,23 +25,16 @@ Original.Button { readonly property color clickedColor: "#FFFFFF" readonly property color disabledColor: "#575757" - style: ButtonStyle { - background: Item {} + background: Item {} - - label: HiFiGlyphs { - color: control.enabled ? (control.pressed ? control.clickedColor : - (control.hovered ? control.hoverColor : control.normalColor)) : - control.disabledColor - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - anchors { - // Tweak horizontal alignment so that it looks right. - left: parent.left - leftMargin: -0.5 - } - text: control.glyph - size: control.size - } + contentItem: HiFiGlyphs { + color: control.enabled ? (control.pressed ? control.clickedColor : + (control.hovered ? control.hoverColor : control.normalColor)) : + control.disabledColor + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: control.glyph + size: control.size } } + From dbd6ec0dff5f64abd18f2fa02f74c01bad49e9d6 Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 17 Nov 2017 20:46:21 +0100 Subject: [PATCH 006/438] Port Edit.qml to QQC2 as well as some tablet preferences compinents --- .../qml/dialogs/CustomQueryDialog.qml | 3 +- .../qml/dialogs/TabletCustomQueryDialog.qml | 3 +- .../qml/dialogs/TabletQueryDialog.qml | 3 +- .../assetDialog/AssetDialogContent.qml | 2 +- interface/resources/qml/hifi/Desktop.qml | 6 +- .../resources/qml/hifi/SpectatorCamera.qml | 3 +- interface/resources/qml/hifi/tablet/Edit.qml | 292 +++++++++++++++++- .../qml/hifi/tablet/EditTabButton.qml | 52 ++++ .../qml/hifi/tablet/TabletAddressDialog.qml | 4 +- .../qml/hifi/tablet/TabletAudioBuffers.qml | 4 +- .../hifi/tablet/TabletGeneralPreferences.qml | 4 +- .../hifi/tablet/TabletGraphicsPreferences.qml | 4 +- .../qml/hifi/tablet/TabletLodPreferences.qml | 4 +- .../tablet/TabletNetworkingPreferences.qml | 4 +- 14 files changed, 351 insertions(+), 37 deletions(-) create mode 100644 interface/resources/qml/hifi/tablet/EditTabButton.qml diff --git a/interface/resources/qml/dialogs/CustomQueryDialog.qml b/interface/resources/qml/dialogs/CustomQueryDialog.qml index 4d6fe74bca..403cd526ce 100644 --- a/interface/resources/qml/dialogs/CustomQueryDialog.qml +++ b/interface/resources/qml/dialogs/CustomQueryDialog.qml @@ -8,8 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5; -import QtQuick.Controls 1.4; +import QtQuick 2.7; import QtQuick.Dialogs 1.2 as OriginalDialogs; import "../controls-uit"; diff --git a/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml b/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml index 7965006b8b..8652e37571 100644 --- a/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml +++ b/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml @@ -8,8 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 import QtQuick.Dialogs 1.2 as OriginalDialogs import "../controls-uit" diff --git a/interface/resources/qml/dialogs/TabletQueryDialog.qml b/interface/resources/qml/dialogs/TabletQueryDialog.qml index e21677c12c..e9830ca3ba 100644 --- a/interface/resources/qml/dialogs/TabletQueryDialog.qml +++ b/interface/resources/qml/dialogs/TabletQueryDialog.qml @@ -8,8 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 import QtQuick.Dialogs 1.2 as OriginalDialogs import "../controls-uit" diff --git a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml index 8c0501e3b4..8da12c246f 100644 --- a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml +++ b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 +import QtQuick 2.7 import QtQuick.Controls 1.4 import "../../controls-uit" diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index ea9ec2f6c9..77568428d2 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -1,12 +1,12 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtWebEngine 1.1; +import QtQuick 2.7 +import QtWebEngine 1.5; import Qt.labs.settings 1.0 import "../desktop" as OriginalDesktop import ".." import "." import "./toolbars" +import "../controls-uit" OriginalDesktop.Desktop { id: desktop diff --git a/interface/resources/qml/hifi/SpectatorCamera.qml b/interface/resources/qml/hifi/SpectatorCamera.qml index 3a8559ab1e..4bf80e410b 100644 --- a/interface/resources/qml/hifi/SpectatorCamera.qml +++ b/interface/resources/qml/hifi/SpectatorCamera.qml @@ -12,8 +12,7 @@ // import Hifi 1.0 as Hifi -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 import "../styles-uit" import "../controls-uit" as HifiControlsUit import "../controls" as HifiControls diff --git a/interface/resources/qml/hifi/tablet/Edit.qml b/interface/resources/qml/hifi/tablet/Edit.qml index e2e8c4362e..8df461561a 100644 --- a/interface/resources/qml/hifi/tablet/Edit.qml +++ b/interface/resources/qml/hifi/tablet/Edit.qml @@ -1,29 +1,295 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 import "../../styles-uit" +import "../../controls-uit" as HifiControls +import "../../controls" +import "../toolbars" -StackView { +ColumnLayout { id: editRoot objectName: "stack" - initialItem: Qt.resolvedUrl('EditTabView.qml') + anchors.fill: parent + + readonly property var webTabsLinks: [ + "", + "/system/html/entityList.html", + "/system/html/entityProperties.html", + "/system/html/gridControls.html", + "/system/particle_explorer/particleExplorer.html" + ] signal sendToScript(var message); - HifiConstants { id: hifi } - function pushSource(path) { - editRoot.push(Qt.resolvedUrl(path)); - editRoot.currentItem.sendToScript.connect(editRoot.sendToScript); + console.error("Edit. push source", path, Qt.resolvedUrl(path)) + editStack.push(Qt.resolvedUrl(path)); + editStack.currentItem.sendToScript.connect(editRoot.sendToScript); } function popSource() { - editRoot.pop(); + editStack.pop(); } - // Passes script messages to the item on the top of the stack function fromScript(message) { - var currentItem = editRoot.currentItem; - if (currentItem && currentItem.fromScript) - currentItem.fromScript(message); + console.error("from script", JSON.stringify(message)) + switch (message.method) { + case 'selectTab': + selectTab(message.params.id); + break; + default: + var currentItem = editStack.currentItem; + if (currentItem && currentItem.fromScript) { + currentItem.fromScript(message); + } else { + console.warn('Unrecognized message:', JSON.stringify(message)); + } + } + } + + // Changes the current tab based on tab index or title as input + function selectTab(id) { + console.error("selecting tab", id) + if (typeof id === 'number') { + if (id >= 0 && id <= 4) { + editTabView.currentIndex = id; + } else { + console.warn('Attempt to switch to invalid tab:', id); + } + } else if (typeof id === 'string'){ + switch (id.toLowerCase()) { + case 'create': + editTabView.currentIndex = 0; + break; + case 'list': + editTabView.currentIndex = 1; + break; + case 'properties': + editTabView.currentIndex = 2; + break; + case 'grid': + editTabView.currentIndex = 3; + break; + case 'particle': + editTabView.currentIndex = 4; + break; + default: + console.warn('Attempt to switch to invalid tab:', id); + } + } else { + console.warn('Attempt to switch tabs with invalid input:', JSON.stringify(id)); + } + } + spacing: 0 + + HifiConstants { id: hifi } + + TabBar { + id: editTabView + height: 60 + width: parent.width + contentWidth: parent.width + currentIndex: 0 + padding: 0 + spacing: 0 + + onCurrentIndexChanged: { + if (currentIndex === 0) { + editStack.replace(null, mainTab, {}, StackView.Immediate) + } else { + editStack.replace(null, webViewTab, + {url: Paths.defaultScripts + webTabsLinks[currentIndex]}, + StackView.Immediate) + } + } + + EditTabButton { + text: "CREATE" + } + + EditTabButton { + text: "LIST" + } + + EditTabButton { + text: "PROPERTIES" + } + + EditTabButton { + text: "GRID" + } + + EditTabButton { + text: "P" + } + } + + StackView { + id: editStack + + Layout.fillHeight: true + Layout.fillWidth: true + initialItem: mainTab//Qt.resolvedUrl('EditTabView.qml') + } + + Component { + id: mainTab + + + Rectangle { //1st tab + color: "#404040" + + Text { + color: "#ffffff" + text: "Choose an Entity Type to Create:" + font.pixelSize: 14 + font.bold: true + anchors.top: parent.top + anchors.topMargin: 28 + anchors.left: parent.left + anchors.leftMargin: 28 + } + + Flow { + id: createEntitiesFlow + spacing: 35 + anchors.right: parent.right + anchors.rightMargin: 55 + anchors.left: parent.left + anchors.leftMargin: 55 + anchors.top: parent.top + anchors.topMargin: 70 + + + NewEntityButton { + icon: "icons/create-icons/94-model-01.svg" + text: "MODEL" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newModelButton" } + }); + editTabView.currentIndex = 2 + } + } + + NewEntityButton { + icon: "icons/create-icons/21-cube-01.svg" + text: "CUBE" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newCubeButton" } + }); + editTabView.currentIndex = 2 + } + } + + NewEntityButton { + icon: "icons/create-icons/22-sphere-01.svg" + text: "SPHERE" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newSphereButton" } + }); + editTabView.currentIndex = 2 + } + } + + NewEntityButton { + icon: "icons/create-icons/24-light-01.svg" + text: "LIGHT" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newLightButton" } + }); + editTabView.currentIndex = 2 + } + } + + NewEntityButton { + icon: "icons/create-icons/20-text-01.svg" + text: "TEXT" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newTextButton" } + }); + editTabView.currentIndex = 2 + } + } + + NewEntityButton { + icon: "icons/create-icons/25-web-1-01.svg" + text: "WEB" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newWebButton" } + }); + editTabView.currentIndex = 2 + } + } + + NewEntityButton { + icon: "icons/create-icons/23-zone-01.svg" + text: "ZONE" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newZoneButton" } + }); + editTabView.currentIndex = 2 + } + } + + NewEntityButton { + icon: "icons/create-icons/90-particles-01.svg" + text: "PARTICLE" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newParticleButton" } + }); + editTabView.currentIndex = 4 + } + } + } + + HifiControls.Button { + id: assetServerButton + text: "Open This Domain's Asset Server" + color: hifi.buttons.black + colorScheme: hifi.colorSchemes.dark + anchors.right: parent.right + anchors.rightMargin: 55 + anchors.left: parent.left + anchors.leftMargin: 55 + anchors.top: createEntitiesFlow.bottom + anchors.topMargin: 35 + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "openAssetBrowserButton" } + }); + } + } + + HifiControls.Button { + text: "Import Entities (.json)" + color: hifi.buttons.black + colorScheme: hifi.colorSchemes.dark + anchors.right: parent.right + anchors.rightMargin: 55 + anchors.left: parent.left + anchors.leftMargin: 55 + anchors.top: assetServerButton.bottom + anchors.topMargin: 20 + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "importEntitiesButton" } + }); + } + } + } + } + + Component { + id: webViewTab + WebView {} } } + diff --git a/interface/resources/qml/hifi/tablet/EditTabButton.qml b/interface/resources/qml/hifi/tablet/EditTabButton.qml new file mode 100644 index 0000000000..761ab4d8cd --- /dev/null +++ b/interface/resources/qml/hifi/tablet/EditTabButton.qml @@ -0,0 +1,52 @@ +// +// AudioTabButton.qml +// qml/hifi/audio +// +// Created by Vlad Stelmahovsky on 8/16/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import "../../controls-uit" as HifiControls +import "../../styles-uit" + +TabButton { + id: control + font.pixelSize: height / 2 + padding: 0 + spacing: 0 + HifiConstants { id: hifi; } + + contentItem: Text { + id: text + text: control.text + font.pixelSize: 16 + font.bold: true + color: control.checked ? "white" : "white" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + property string glyphtext: "" + HiFiGlyphs { + anchors.centerIn: parent + size: 30 + color: "#ffffff" + text: text.glyphtext + } + Component.onCompleted: { + if (control.text === "P") { + text.text = " "; + text.glyphtext = "\ue004"; + } + } + } + + background: Rectangle { + color: control.checked ? "#404040" :"black" + implicitWidth: control.contentItem.width + 42 + implicitHeight: 40 + } +} diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml index 649a8e6259..ac33eaf5fa 100644 --- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml @@ -9,8 +9,8 @@ // import Hifi 1.0 -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import QtGraphicalEffects 1.0 import "../../controls" import "../../styles" diff --git a/interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml b/interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml index 1b4d0feaca..239c2452d4 100644 --- a/interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml +++ b/interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml @@ -8,8 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import "tabletWindows" import "../../dialogs" diff --git a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml index dee2eed9c3..810f5bb43f 100644 --- a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml @@ -8,8 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import "tabletWindows" import "../../dialogs" diff --git a/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml b/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml index 25b5be05f2..3114c79bfe 100644 --- a/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml @@ -8,8 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import "tabletWindows" import "../../dialogs" diff --git a/interface/resources/qml/hifi/tablet/TabletLodPreferences.qml b/interface/resources/qml/hifi/tablet/TabletLodPreferences.qml index b502c26245..ddc116371d 100644 --- a/interface/resources/qml/hifi/tablet/TabletLodPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletLodPreferences.qml @@ -8,8 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import "tabletWindows" import "../../dialogs" diff --git a/interface/resources/qml/hifi/tablet/TabletNetworkingPreferences.qml b/interface/resources/qml/hifi/tablet/TabletNetworkingPreferences.qml index 91d6140fc3..bad546a39c 100644 --- a/interface/resources/qml/hifi/tablet/TabletNetworkingPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletNetworkingPreferences.qml @@ -8,8 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import "tabletWindows" import "../../dialogs" From 41f338e6adcf6d93fe6ed55670ea5410856d83e4 Mon Sep 17 00:00:00 2001 From: vladest Date: Sat, 18 Nov 2017 20:18:17 +0100 Subject: [PATCH 007/438] Port ScrollingWindow and TabletDebugWindow --- .../qml/hifi/dialogs/TabletDebugWindow.qml | 9 ++-- .../resources/qml/windows/ScrollingWindow.qml | 51 +++++++++---------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml b/interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml index 22e9dc07a2..50df4dedbc 100644 --- a/interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml +++ b/interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml @@ -8,8 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import Hifi 1.0 as Hifi import "../../styles-uit" @@ -69,9 +69,8 @@ Rectangle { id: textArea width: parent.width height: parent.height - backgroundVisible: false - textColor: hifi.colors.white + background: Item {} + color: hifi.colors.white text:"" } - } diff --git a/interface/resources/qml/windows/ScrollingWindow.qml b/interface/resources/qml/windows/ScrollingWindow.qml index 1f9b59d2b4..cf44927941 100644 --- a/interface/resources/qml/windows/ScrollingWindow.qml +++ b/interface/resources/qml/windows/ScrollingWindow.qml @@ -10,8 +10,7 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.2 import QtGraphicalEffects 1.0 import "." @@ -78,17 +77,21 @@ Window { ScrollView { id: scrollView contentItem: content - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - verticalScrollBarPolicy: Qt.ScrollBarAsNeeded + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff anchors.fill: parent anchors.rightMargin: parent.isScrolling ? 1 : 0 anchors.bottomMargin: footerPane.height - style: ScrollViewStyle { - - padding.right: -7 // Move to right away from content. - - handle: Item { + ScrollBar.vertical: ScrollBar { + parent: scrollView + policy: ScrollBar.AsNeeded + orientation: Qt.Vertical + x: scrollView.mirrored ? 0 : scrollView.width - width + y: scrollView.topPadding + height: scrollView.availableHeight + active: scrollView.ScrollBar.horizontal.active + contentItem: Item { implicitWidth: 8 Rectangle { radius: 4 @@ -101,30 +104,24 @@ Window { } } } + } - scrollBarBackground: Item { - implicitWidth: 10 - Rectangle { - color: hifi.colors.darkGray30 - radius: 4 - anchors { - fill: parent - topMargin: -1 // Finesse size - bottomMargin: -2 - } + background: Item { + implicitWidth: 10 + Rectangle { + color: hifi.colors.darkGray30 + radius: 4 + anchors { + fill: parent + topMargin: -1 // Finesse size + bottomMargin: -2 } } - - incrementControl: Item { - visible: false - } - - decrementControl: Item { - visible: false - } } + } + function scrollBy(delta) { scrollView.flickableItem.contentY += delta; } From d92f461f1244fc5cccf2dfb8032471f6d125da7b Mon Sep 17 00:00:00 2001 From: vladest Date: Sat, 18 Nov 2017 20:44:52 +0100 Subject: [PATCH 008/438] Port ControllerSettings --- interface/resources/qml/hifi/tablet/ControllerSettings.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/ControllerSettings.qml b/interface/resources/qml/hifi/tablet/ControllerSettings.qml index 4814eaf01c..3e1a7bf139 100644 --- a/interface/resources/qml/hifi/tablet/ControllerSettings.qml +++ b/interface/resources/qml/hifi/tablet/ControllerSettings.qml @@ -6,8 +6,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import QtGraphicalEffects 1.0 import "../../styles-uit" import "../../controls" From 68b08f429c4586370f2ebca59fb8f3c7ffc667df Mon Sep 17 00:00:00 2001 From: vladest Date: Sat, 18 Nov 2017 22:49:16 +0100 Subject: [PATCH 009/438] Port Attachments to QQC2 --- .../qml/hifi/dialogs/content/AttachmentsContent.qml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml b/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml index 5c9d6822c8..8a89cd5b42 100644 --- a/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml +++ b/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml @@ -1,7 +1,6 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import QtQuick.Dialogs 1.2 as OriginalDialogs -import QtQuick.Controls.Styles 1.4 import "../../../styles-uit" import "../../../controls-uit" as HifiControls @@ -246,7 +245,7 @@ Item { } } - Action { + HifiControls.Action { id: cancelAction text: "Cancel" onTriggered: { @@ -255,7 +254,7 @@ Item { } } - Action { + HifiControls.Action { id: okAction text: "OK" onTriggered: { From 19f9af05769ad5c3061285c21870ed8bba42eb39 Mon Sep 17 00:00:00 2001 From: vladest Date: Sat, 18 Nov 2017 23:03:52 +0100 Subject: [PATCH 010/438] Port avatar preferences --- .../qml/hifi/dialogs/content/ModelBrowserContent.qml | 5 ++--- .../resources/qml/hifi/tablet/TabletAvatarPreferences.qml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/content/ModelBrowserContent.qml b/interface/resources/qml/hifi/dialogs/content/ModelBrowserContent.qml index 50fca94ff1..c9fe9840b8 100644 --- a/interface/resources/qml/hifi/dialogs/content/ModelBrowserContent.qml +++ b/interface/resources/qml/hifi/dialogs/content/ModelBrowserContent.qml @@ -1,5 +1,4 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 import "../../../controls-uit" as HifiControls @@ -40,7 +39,7 @@ Column { HifiControls.Button { action: cancelAction ; color: hifi.buttons.black; colorScheme: hifi.colorSchemes.dark } } - Action { + HifiControls.Action { id: acceptAction text: qsTr("OK") enabled: root.result ? true : false @@ -51,7 +50,7 @@ Column { } } - Action { + HifiControls.Action { id: cancelAction text: qsTr("Cancel") shortcut: Qt.Key_Escape diff --git a/interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml b/interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml index 94fb29c6a1..e824036587 100644 --- a/interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletAvatarPreferences.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.2 import "tabletWindows" import "../../dialogs" From b21f66f6660463ff478df28be9f1f7ee3cfe3674 Mon Sep 17 00:00:00 2001 From: vladest Date: Sun, 19 Nov 2017 20:09:07 +0100 Subject: [PATCH 011/438] Port PlaySampleSound to QQC2. Fix crash on press down key in scroll window --- interface/resources/qml/ToolWindow.qml | 4 +- .../qml/hifi/audio/PlaySampleSound.qml | 49 +++++++++---------- .../resources/qml/windows/ScrollingWindow.qml | 14 ++---- 3 files changed, 30 insertions(+), 37 deletions(-) diff --git a/interface/resources/qml/ToolWindow.qml b/interface/resources/qml/ToolWindow.qml index b1120058f9..bfc7758348 100644 --- a/interface/resources/qml/ToolWindow.qml +++ b/interface/resources/qml/ToolWindow.qml @@ -56,8 +56,8 @@ ScrollingWindow { id: toolWindowTabViewItem height: pane.scrollHeight width: pane.contentWidth - anchors.left: parent.left - anchors.top: parent.top + anchors.left: parent !== null ? parent.left : undefined + anchors.top: parent !== null ? parent.top : undefined TabView { id: tabView diff --git a/interface/resources/qml/hifi/audio/PlaySampleSound.qml b/interface/resources/qml/hifi/audio/PlaySampleSound.qml index dec2e9bfc9..4b0e32ee12 100644 --- a/interface/resources/qml/hifi/audio/PlaySampleSound.qml +++ b/interface/resources/qml/hifi/audio/PlaySampleSound.qml @@ -9,9 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import "../../styles-uit" @@ -57,31 +56,31 @@ RowLayout { HifiConstants { id: hifi; } Button { - style: ButtonStyle { - background: Rectangle { - implicitWidth: 20; - implicitHeight: 20; - radius: hifi.buttons.radius; - gradient: Gradient { - GradientStop { - position: 0.2; - color: isPlaying ? hifi.buttons.colorStart[hifi.buttons.blue] : hifi.buttons.colorStart[hifi.buttons.black]; - } - GradientStop { - position: 1.0; - color: isPlaying ? hifi.buttons.colorFinish[hifi.buttons.blue] : hifi.buttons.colorFinish[hifi.buttons.black]; - } + id: control + background: Rectangle { + implicitWidth: 20; + implicitHeight: 20; + radius: hifi.buttons.radius; + gradient: Gradient { + GradientStop { + position: 0.2; + color: isPlaying ? hifi.buttons.colorStart[hifi.buttons.blue] : hifi.buttons.colorStart[hifi.buttons.black]; + } + GradientStop { + position: 1.0; + color: isPlaying ? hifi.buttons.colorFinish[hifi.buttons.blue] : hifi.buttons.colorFinish[hifi.buttons.black]; } } - label: HiFiGlyphs { - // absolutely position due to asymmetry in glyph - x: isPlaying ? 0 : 1; - y: 1; - size: 14; - color: (control.pressed || control.hovered) ? (isPlaying ? "black" : hifi.colors.primaryHighlight) : "white"; - text: isPlaying ? hifi.glyphs.stop_square : hifi.glyphs.playback_play; - } } + contentItem: HiFiGlyphs { + // absolutely position due to asymmetry in glyph +// x: isPlaying ? 0 : 1; +// y: 1; + size: 14; + color: (control.pressed || control.hovered) ? (isPlaying ? "black" : hifi.colors.primaryHighlight) : "white"; + text: isPlaying ? hifi.glyphs.stop_square : hifi.glyphs.playback_play; + } + onClicked: isPlaying ? stopSound() : playSound(); } diff --git a/interface/resources/qml/windows/ScrollingWindow.qml b/interface/resources/qml/windows/ScrollingWindow.qml index cf44927941..840432f262 100644 --- a/interface/resources/qml/windows/ScrollingWindow.qml +++ b/interface/resources/qml/windows/ScrollingWindow.qml @@ -43,7 +43,9 @@ Window { // type should only consist of logic sized areas, with nothing drawn (although the // default value for the frame property does include visual decorations) property var pane: Item { - property bool isScrolling: scrollView.height < scrollView.contentItem.height + property bool isScrolling: scrollView.contentChildren.length > 0 ? + (scrollView.height < scrollView.contentChildren[0].height) : + false property int contentWidth: scrollView.width - (isScrolling ? 10 : 0) property int scrollHeight: scrollView.height @@ -76,7 +78,7 @@ Window { ScrollView { id: scrollView - contentItem: content + contentChildren: content clip: true ScrollBar.horizontal.policy: ScrollBar.AlwaysOff anchors.fill: parent @@ -84,13 +86,7 @@ Window { anchors.bottomMargin: footerPane.height ScrollBar.vertical: ScrollBar { - parent: scrollView policy: ScrollBar.AsNeeded - orientation: Qt.Vertical - x: scrollView.mirrored ? 0 : scrollView.width - width - y: scrollView.topPadding - height: scrollView.availableHeight - active: scrollView.ScrollBar.horizontal.active contentItem: Item { implicitWidth: 8 Rectangle { @@ -118,10 +114,8 @@ Window { } } } - } - function scrollBy(delta) { scrollView.flickableItem.contentY += delta; } From 50ed9e2b2a291ec6b7c57aa7283bb2d8d2c1eaac Mon Sep 17 00:00:00 2001 From: vladest Date: Sun, 19 Nov 2017 22:03:38 +0100 Subject: [PATCH 012/438] Port CheckBox to QQC2 --- .../qml/LoginDialog/LinkAccountBody.qml | 2 +- .../resources/qml/controls-uit/CheckBox.qml | 147 +++++++++--------- .../resources/qml/controls-uit/Label.qml | 2 +- .../qml/styles-uit/RalewaySemiBold.qml | 2 +- 4 files changed, 78 insertions(+), 75 deletions(-) diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 300bcd46f0..76b1e91066 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -170,7 +170,7 @@ Item { } } - CheckBoxQQC2 { + CheckBox { id: showPassword text: "Show password" } diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index 22b25671c3..86a7742d16 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -8,9 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 as Original -import QtQuick.Controls.Styles 1.4 +import QtQuick 2.2 +import QtQuick.Controls 2.2 as Original import "../styles-uit" @@ -28,85 +27,89 @@ Original.CheckBox { property bool wrap: true; readonly property int checkSize: Math.max(boxSize - 8, 10) readonly property int checkRadius: 2 - activeFocusOnPress: true + focusPolicy: Qt.ClickFocus + hoverEnabled: true onClicked: { tabletInterface.playSound(TabletEnums.ButtonClick); } -// TODO: doesnt works for QQC1. check with QQC2 -// onHovered: { -// tabletInterface.playSound(TabletEnums.ButtonHover); -// } + onHoveredChanged: { + if (hovered) { + tabletInterface.playSound(TabletEnums.ButtonHover); + } + } - style: CheckBoxStyle { - indicator: Rectangle { - id: box + + indicator: Rectangle { + id: box + width: boxSize + height: boxSize + radius: boxRadius + y: parent.height / 2 - height / 2 + border.width: 1 + border.color: pressed || hovered + ? hifi.colors.checkboxCheckedBorder + : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) + + gradient: Gradient { + GradientStop { + position: 0.2 + color: pressed || hovered + ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightStart) + : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart) + } + GradientStop { + position: 1.0 + color: pressed || hovered + ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightFinish) + : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) + } + } + + Rectangle { + visible: pressed || hovered + anchors.centerIn: parent + id: innerBox + width: checkSize - 4 + height: width + radius: checkRadius + color: hifi.colors.checkboxCheckedBorder + } + + Rectangle { + id: check + width: checkSize + height: checkSize + radius: checkRadius + anchors.centerIn: parent + color: isRedCheck ? hifi.colors.checkboxCheckedRed : hifi.colors.checkboxChecked + border.width: 2 + border.color: isRedCheck? hifi.colors.checkboxCheckedBorderRed : hifi.colors.checkboxCheckedBorder + visible: checked && !pressed || !checked && pressed + } + + Rectangle { + id: disabledOverlay + visible: !enabled width: boxSize height: boxSize radius: boxRadius border.width: 1 - border.color: pressed || hovered - ? hifi.colors.checkboxCheckedBorder - : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) - - gradient: Gradient { - GradientStop { - position: 0.2 - color: pressed || hovered - ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightStart) - : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart) - } - GradientStop { - position: 1.0 - color: pressed || hovered - ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightFinish) - : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) - } - } - - Rectangle { - visible: pressed || hovered - anchors.centerIn: parent - id: innerBox - width: checkSize - 4 - height: width - radius: checkRadius - color: hifi.colors.checkboxCheckedBorder - } - - Rectangle { - id: check - width: checkSize - height: checkSize - radius: checkRadius - anchors.centerIn: parent - color: isRedCheck ? hifi.colors.checkboxCheckedRed : hifi.colors.checkboxChecked - border.width: 2 - border.color: isRedCheck? hifi.colors.checkboxCheckedBorderRed : hifi.colors.checkboxCheckedBorder - visible: checked && !pressed || !checked && pressed - } - - Rectangle { - id: disabledOverlay - visible: !enabled - width: boxSize - height: boxSize - radius: boxRadius - border.width: 1 - border.color: hifi.colors.baseGrayHighlight - color: hifi.colors.baseGrayHighlight - opacity: 0.5 - } - } - - label: Label { - text: control.text - color: control.color - x: 2 - wrapMode: checkBox.wrap ? Text.Wrap : Text.NoWrap - elide: checkBox.wrap ? Text.ElideNone : Text.ElideRight - enabled: checkBox.enabled + border.color: hifi.colors.baseGrayHighlight + color: hifi.colors.baseGrayHighlight + opacity: 0.5 } } + + contentItem: Label { + text: checkBox.text + color: checkBox.color + x: 2 + verticalAlignment: Text.AlignVCenter + wrapMode: checkBox.wrap ? Text.Wrap : Text.NoWrap + elide: checkBox.wrap ? Text.ElideNone : Text.ElideRight + enabled: checkBox.enabled + leftPadding: checkBox.indicator.width + checkBox.spacing + } } diff --git a/interface/resources/qml/controls-uit/Label.qml b/interface/resources/qml/controls-uit/Label.qml index 1dc3aa0dd4..4c7051b495 100644 --- a/interface/resources/qml/controls-uit/Label.qml +++ b/interface/resources/qml/controls-uit/Label.qml @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 +import QtQuick 2.7 import "../styles-uit" diff --git a/interface/resources/qml/styles-uit/RalewaySemiBold.qml b/interface/resources/qml/styles-uit/RalewaySemiBold.qml index fe3a42dd6c..2d7a7f46a2 100644 --- a/interface/resources/qml/styles-uit/RalewaySemiBold.qml +++ b/interface/resources/qml/styles-uit/RalewaySemiBold.qml @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 +import QtQuick 2.7 Text { id: root From f991379c9c108b059d4a4673455c6a1be51e8484 Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 20 Nov 2017 17:02:34 +0100 Subject: [PATCH 013/438] Fix scroll bar --- interface/resources/qml/windows/ScrollingWindow.qml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/windows/ScrollingWindow.qml b/interface/resources/qml/windows/ScrollingWindow.qml index 840432f262..df15526039 100644 --- a/interface/resources/qml/windows/ScrollingWindow.qml +++ b/interface/resources/qml/windows/ScrollingWindow.qml @@ -43,8 +43,8 @@ Window { // type should only consist of logic sized areas, with nothing drawn (although the // default value for the frame property does include visual decorations) property var pane: Item { - property bool isScrolling: scrollView.contentChildren.length > 0 ? - (scrollView.height < scrollView.contentChildren[0].height) : + property bool isScrolling: /*scrollView.contentChildren.length > 0 ? + (scrollView.height < scrollView.contentChildren[0].height) :*/ false property int contentWidth: scrollView.width - (isScrolling ? 10 : 0) property int scrollHeight: scrollView.height @@ -86,7 +86,15 @@ Window { anchors.bottomMargin: footerPane.height ScrollBar.vertical: ScrollBar { + id: control policy: ScrollBar.AsNeeded + parent: scrollView + x: scrollView.width - width + y: scrollView.topPadding + height: scrollView.availableHeight + active: scrollView.ScrollBar.vertical.active + visible: control.size < 1.0 + contentItem: Item { implicitWidth: 8 Rectangle { From 4e38b3bace80715188ca915f0b1d7a9a189e21ab Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 20 Nov 2017 20:35:41 +0100 Subject: [PATCH 014/438] Started working on ComboBox. Add background for scroll bar --- .../resources/qml/controls-uit/ComboBox.qml | 28 ++++++++--------- .../resources/qml/windows/ScrollingWindow.qml | 30 +++++++++---------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml index d672fa6387..59be9134bc 100644 --- a/interface/resources/qml/controls-uit/ComboBox.qml +++ b/interface/resources/qml/controls-uit/ComboBox.qml @@ -8,9 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import "../styles-uit" import "../controls-uit" as HifiControls @@ -177,22 +176,21 @@ FocusScope { id: scrollView height: root.dropdownHeight width: root.width + 4 - property bool hoverEnabled: false; + hoverEnabled: false; - style: ScrollViewStyle { - decrementControl: Item { - visible: false - } - incrementControl: Item { - visible: false - } - scrollBarBackground: Rectangle{ +// style: ScrollViewStyle { +// decrementControl: Item { +// visible: false +// } +// incrementControl: Item { +// visible: false +// } + background: Rectangle{ implicitWidth: 20 color: hifi.colors.baseGray } - handle: - Rectangle { + contentItem: Rectangle { implicitWidth: 16 anchors.left: parent.left anchors.leftMargin: 3 @@ -201,7 +199,7 @@ FocusScope { radius: 6 color: hifi.colors.lightGrayText } - } +// } ListView { id: listView diff --git a/interface/resources/qml/windows/ScrollingWindow.qml b/interface/resources/qml/windows/ScrollingWindow.qml index df15526039..1966380709 100644 --- a/interface/resources/qml/windows/ScrollingWindow.qml +++ b/interface/resources/qml/windows/ScrollingWindow.qml @@ -43,8 +43,8 @@ Window { // type should only consist of logic sized areas, with nothing drawn (although the // default value for the frame property does include visual decorations) property var pane: Item { - property bool isScrolling: /*scrollView.contentChildren.length > 0 ? - (scrollView.height < scrollView.contentChildren[0].height) :*/ + property bool isScrolling: scrollView.contentChildren.length > 0 ? + (scrollView.height < scrollView.contentChildren[0].height) : false property int contentWidth: scrollView.width - (isScrolling ? 10 : 0) property int scrollHeight: scrollView.height @@ -94,7 +94,18 @@ Window { height: scrollView.availableHeight active: scrollView.ScrollBar.vertical.active visible: control.size < 1.0 - + background: Item { + implicitWidth: 10 + Rectangle { + color: hifi.colors.darkGray30 + radius: 4 + anchors { + fill: parent + topMargin: -1 // Finesse size + bottomMargin: -2 + } + } + } contentItem: Item { implicitWidth: 8 Rectangle { @@ -110,18 +121,7 @@ Window { } } - background: Item { - implicitWidth: 10 - Rectangle { - color: hifi.colors.darkGray30 - radius: 4 - anchors { - fill: parent - topMargin: -1 // Finesse size - bottomMargin: -2 - } - } - } + } function scrollBy(delta) { From da5090fd496220e5b8ee270c9afd40c49b4b0e6f Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 27 Nov 2017 19:43:17 +0100 Subject: [PATCH 015/438] Implemented more close to QQC2 ideology --- .../resources/qml/controls-uit/ComboBox.qml | 212 +++++++++++------- .../resources/qml/controls-uit/ScrollBar.qml | 41 ++++ 2 files changed, 173 insertions(+), 80 deletions(-) create mode 100644 interface/resources/qml/controls-uit/ScrollBar.qml diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml index 59be9134bc..04b1362202 100644 --- a/interface/resources/qml/controls-uit/ComboBox.qml +++ b/interface/resources/qml/controls-uit/ComboBox.qml @@ -39,72 +39,128 @@ FocusScope { implicitHeight: comboBox.height; focus: true - Rectangle { - id: background - gradient: Gradient { - GradientStop { - position: 0.2 - color: popup.visible - ? (isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark) - : (isLightColorScheme ? hifi.colors.dropDownLightStart : hifi.colors.dropDownDarkStart) - } - GradientStop { - position: 1.0 - color: popup.visible - ? (isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark) - : (isLightColorScheme ? hifi.colors.dropDownLightFinish : hifi.colors.dropDownDarkFinish) - } - } - anchors.fill: parent - } - - SystemPalette { id: palette } + //SystemPalette { id: palette } ComboBox { id: comboBox anchors.fill: parent - visible: false + hoverEnabled: true + visible: true + height: hifi.fontSizes.textFieldInput + 13 // Match height of TextField control. - } - - FiraSansSemiBold { - id: textField - anchors { - left: parent.left - leftMargin: hifi.dimensions.textPadding - right: dropIcon.left - verticalCenter: parent.verticalCenter - } - size: hifi.fontSizes.textFieldInput - text: comboBox.currentText - elide: Text.ElideRight - color: controlHover.containsMouse || popup.visible ? hifi.colors.baseGray : (isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText ) - } - - Item { - id: dropIcon - anchors { right: parent.right; verticalCenter: parent.verticalCenter } - height: background.height - width: height - Rectangle { - width: 1 - height: parent.height - anchors.top: parent.top - anchors.left: parent.left - color: isLightColorScheme ? hifi.colors.faintGray : hifi.colors.baseGray - } - HiFiGlyphs { - anchors { - top: parent.top - topMargin: -11 - horizontalCenter: parent.horizontalCenter + background: Rectangle { + id: background + gradient: Gradient { + GradientStop { + position: 0.2 + color: comboBox.popup.visible + ? (isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark) + : (isLightColorScheme ? hifi.colors.dropDownLightStart : hifi.colors.dropDownDarkStart) + } + GradientStop { + position: 1.0 + color: comboBox.popup.visible + ? (isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark) + : (isLightColorScheme ? hifi.colors.dropDownLightFinish : hifi.colors.dropDownDarkFinish) + } } - size: hifi.dimensions.spinnerSize - text: hifi.glyphs.caratDn - color: controlHover.containsMouse || popup.visible ? hifi.colors.baseGray : (isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText) } - } + indicator: Item { + id: dropIcon + anchors { right: parent.right; verticalCenter: parent.verticalCenter } + height: background.height + width: height + Rectangle { + width: 1 + height: parent.height + anchors.top: parent.top + anchors.left: parent.left + color: isLightColorScheme ? hifi.colors.faintGray : hifi.colors.baseGray + } + HiFiGlyphs { + anchors { top: parent.top; topMargin: -11; horizontalCenter: parent.horizontalCenter } + size: hifi.dimensions.spinnerSize + text: hifi.glyphs.caratDn + color: comboBox.hovered || comboBox.popup.visible ? hifi.colors.baseGray : (isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText) + } + } + + contentItem: FiraSansSemiBold { + id: textField + anchors { + left: parent.left + leftMargin: hifi.dimensions.textPadding + verticalCenter: parent.verticalCenter + } + size: hifi.fontSizes.textFieldInput + text: comboBox.currentText + elide: Text.ElideRight + color: comboBox.hovered || comboBox.popup.visible ? hifi.colors.baseGray : (isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText ) + } + + delegate: ItemDelegate { + id: itemDelegate + hoverEnabled: true + width: root.width + 4 + height: popupText.implicitHeight * 1.4 + + onHoveredChanged: { + itemDelegate.highlighted = hovered; + } + + background: Rectangle { + color: itemDelegate.highlighted ? hifi.colors.primaryHighlight + : (isLightColorScheme ? hifi.colors.dropDownPressedLight + : hifi.colors.dropDownPressedDark) + } + + contentItem: FiraSansSemiBold { + id: popupText + anchors.left: parent.left + anchors.leftMargin: hifi.dimensions.textPadding + anchors.verticalCenter: parent.verticalCenter + text: comboBox.model[index] ? comboBox.model[index] : (comboBox.model.get && comboBox.model.get(index).text ? comboBox.model.get(index).text : "") + size: hifi.fontSizes.textFieldInput + color: hifi.colors.baseGray + } + } + popup: Popup { + y: comboBox.height - 1 + width: comboBox.width + implicitHeight: dropdownHeight + padding: 1 + + contentItem: ListView { + id: listView + clip: true + implicitHeight: dropdownHeight + model: comboBox.popup.visible ? comboBox.delegateModel : null + currentIndex: comboBox.highlightedIndex + delegate: comboBox.delegate + ScrollBar.vertical: HifiControls.ScrollBar { + id: scrollbar + parent: listView + policy: ScrollBar.AsNeeded + visible: size < 1.0 + } + } + + background: Rectangle { + color: hifi.colors.baseGray + } + } +// MouseArea { +// id: popupHover +// anchors.fill: parent; +// hoverEnabled: scrollView.hoverEnabled; +// onEntered: listView.currentIndex = index; +// onClicked: popup.selectSpecificItem(index); +// } +// } +// } + } +/* MouseArea { id: controlHover hoverEnabled: true @@ -178,28 +234,20 @@ FocusScope { width: root.width + 4 hoverEnabled: false; -// style: ScrollViewStyle { -// decrementControl: Item { -// visible: false -// } -// incrementControl: Item { -// visible: false -// } - background: Rectangle{ - implicitWidth: 20 - color: hifi.colors.baseGray - } + background: Rectangle{ + implicitWidth: 20 + color: hifi.colors.baseGray + } - contentItem: Rectangle { - implicitWidth: 16 - anchors.left: parent.left - anchors.leftMargin: 3 - anchors.top: parent.top - anchors.bottom: parent.bottom - radius: 6 - color: hifi.colors.lightGrayText - } -// } + contentItem: Rectangle { + implicitWidth: 16 + anchors.left: parent.left + anchors.leftMargin: 3 + anchors.top: parent.top + anchors.bottom: parent.bottom + radius: 6 + color: hifi.colors.lightGrayText + } ListView { id: listView @@ -230,7 +278,7 @@ FocusScope { } } } - +*/ HifiControls.Label { id: comboBoxLabel text: root.label @@ -243,5 +291,9 @@ FocusScope { Component.onCompleted: { isDesktop = (typeof desktop !== "undefined"); + comboBox.popup.closePolicy = Popup.CloseOnPressOutside + comboBox.popup.height = dropdownHeight + //TODO: do we need this? + comboBox.popup.z = isDesktop ? desktop.zLevels.menu : 12 } } diff --git a/interface/resources/qml/controls-uit/ScrollBar.qml b/interface/resources/qml/controls-uit/ScrollBar.qml new file mode 100644 index 0000000000..125e84e585 --- /dev/null +++ b/interface/resources/qml/controls-uit/ScrollBar.qml @@ -0,0 +1,41 @@ +// +// ScrollBar.qml +// +// Created by Vlad Stelmahovsky on 27 Nov 2017 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +import "../styles-uit" + +ScrollBar { + visible: size < 1.0 + + HifiConstants { id: hifi } + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + + background: Item { + implicitWidth: hifi.dimensions.scrollbarBackgroundWidth + Rectangle { + anchors { fill: parent; topMargin: 3; bottomMargin: 3 } + radius: hifi.dimensions.scrollbarHandleWidth/2 + color: isLightColorScheme ? hifi.colors.tableScrollBackgroundLight + : hifi.colors.tableScrollBackgroundDark + } + } + + contentItem: Item { + implicitWidth: hifi.dimensions.scrollbarHandleWidth + Rectangle { + anchors { fill: parent; topMargin: 1; bottomMargin: 1 } + radius: hifi.dimensions.scrollbarHandleWidth/2 + color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark + } + } +} From 80eb203008f43508fe1b9828d01e1ae23cf485c3 Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 28 Nov 2017 20:20:58 +0100 Subject: [PATCH 016/438] More precise handle placing. some logging added --- .../resources/qml/controls-uit/ComboBox.qml | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml index 04b1362202..e6cf78601f 100644 --- a/interface/resources/qml/controls-uit/ComboBox.qml +++ b/interface/resources/qml/controls-uit/ComboBox.qml @@ -39,15 +39,21 @@ FocusScope { implicitHeight: comboBox.height; focus: true - //SystemPalette { id: palette } - ComboBox { id: comboBox anchors.fill: parent hoverEnabled: true visible: true - height: hifi.fontSizes.textFieldInput + 13 // Match height of TextField control. + + onPressedChanged: { + console.warn("on pressed", pressed, popup.visible) + if (!pressed && popup.visible) { + popup.visible = false + down = undefined + } + } + background: Rectangle { id: background gradient: Gradient { @@ -120,7 +126,9 @@ FocusScope { anchors.left: parent.left anchors.leftMargin: hifi.dimensions.textPadding anchors.verticalCenter: parent.verticalCenter - text: comboBox.model[index] ? comboBox.model[index] : (comboBox.model.get && comboBox.model.get(index).text ? comboBox.model.get(index).text : "") + text: comboBox.model[index] ? comboBox.model[index] + : (comboBox.model.get && comboBox.model.get(index).text ? + comboBox.model.get(index).text : "") size: hifi.fontSizes.textFieldInput color: hifi.colors.baseGray } @@ -128,13 +136,16 @@ FocusScope { popup: Popup { y: comboBox.height - 1 width: comboBox.width - implicitHeight: dropdownHeight - padding: 1 + implicitHeight: listView.contentHeight > dropdownHeight ? dropdownHeight + : listView.contentHeight + padding: 0 + topPadding: 1 + closePolicy: Popup.NoAutoClose + onVisibleChanged: console.warn("popup", visible) contentItem: ListView { id: listView clip: true - implicitHeight: dropdownHeight model: comboBox.popup.visible ? comboBox.delegateModel : null currentIndex: comboBox.highlightedIndex delegate: comboBox.delegate @@ -291,8 +302,6 @@ FocusScope { Component.onCompleted: { isDesktop = (typeof desktop !== "undefined"); - comboBox.popup.closePolicy = Popup.CloseOnPressOutside - comboBox.popup.height = dropdownHeight //TODO: do we need this? comboBox.popup.z = isDesktop ? desktop.zLevels.menu : 12 } From ac7a5ebc947b7720dca7f4be1aa64bab018d3af4 Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 30 Nov 2017 10:39:01 +0100 Subject: [PATCH 017/438] ComboBox: fix closing popup. Started to implement keyboard support --- .../resources/qml/controls-uit/ComboBox.qml | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml index e6cf78601f..1fc95de129 100644 --- a/interface/resources/qml/controls-uit/ComboBox.qml +++ b/interface/resources/qml/controls-uit/ComboBox.qml @@ -46,16 +46,18 @@ FocusScope { visible: true height: hifi.fontSizes.textFieldInput + 13 // Match height of TextField control. - onPressedChanged: { - console.warn("on pressed", pressed, popup.visible) - if (!pressed && popup.visible) { - popup.visible = false - down = undefined - } - } + function previousItem() { listView.currentIndex = (listView.currentIndex + listView.count - 1) % listView.count; } + function nextItem() { listView.currentIndex = (listView.currentIndex + listView.count + 1) % listView.count; } + function selectCurrentItem() { root.currentIndex = listView.currentIndex; close(); /*hideList();*/ } + function selectSpecificItem(index) { root.currentIndex = index; close();/*hideList();*/ } + + Keys.onUpPressed: previousItem(); + Keys.onDownPressed: nextItem(); + Keys.onSpacePressed: selectCurrentItem(); + Keys.onRightPressed: selectCurrentItem(); + Keys.onReturnPressed: selectCurrentItem(); background: Rectangle { - id: background gradient: Gradient { GradientStop { position: 0.2 @@ -75,7 +77,7 @@ FocusScope { indicator: Item { id: dropIcon anchors { right: parent.right; verticalCenter: parent.verticalCenter } - height: background.height + height: root.height width: height Rectangle { width: 1 @@ -140,8 +142,10 @@ FocusScope { : listView.contentHeight padding: 0 topPadding: 1 - closePolicy: Popup.NoAutoClose - onVisibleChanged: console.warn("popup", visible) + + onClosed: { + root.accepted() + } contentItem: ListView { id: listView @@ -303,6 +307,6 @@ FocusScope { Component.onCompleted: { isDesktop = (typeof desktop !== "undefined"); //TODO: do we need this? - comboBox.popup.z = isDesktop ? desktop.zLevels.menu : 12 + //comboBox.popup.z = isDesktop ? desktop.zLevels.menu : 12 } } From 0f4329be693e7422e42961f02a5ee5bab359a40c Mon Sep 17 00:00:00 2001 From: vladest Date: Sun, 3 Dec 2017 22:45:57 +0100 Subject: [PATCH 018/438] Added keyboard support. Cleanup --- .../resources/qml/controls-uit/ComboBox.qml | 143 ++---------------- 1 file changed, 9 insertions(+), 134 deletions(-) diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml index 1fc95de129..ab8a6c2344 100644 --- a/interface/resources/qml/controls-uit/ComboBox.qml +++ b/interface/resources/qml/controls-uit/ComboBox.qml @@ -23,6 +23,7 @@ FocusScope { property alias comboBox: comboBox readonly property alias currentText: comboBox.currentText; property alias currentIndex: comboBox.currentIndex; + property int currentHighLightedIndex: comboBox.currentIndex; property int dropdownHeight: 480 property int colorScheme: hifi.colorSchemes.light @@ -46,9 +47,9 @@ FocusScope { visible: true height: hifi.fontSizes.textFieldInput + 13 // Match height of TextField control. - function previousItem() { listView.currentIndex = (listView.currentIndex + listView.count - 1) % listView.count; } - function nextItem() { listView.currentIndex = (listView.currentIndex + listView.count + 1) % listView.count; } - function selectCurrentItem() { root.currentIndex = listView.currentIndex; close(); /*hideList();*/ } + function previousItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count - 1) % comboBox.count; } + function nextItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count + 1) % comboBox.count; } + function selectCurrentItem() { root.currentIndex = root.currentHighLightedIndex; close(); /*hideList();*/ } function selectSpecificItem(index) { root.currentIndex = index; close();/*hideList();*/ } Keys.onUpPressed: previousItem(); @@ -112,9 +113,12 @@ FocusScope { hoverEnabled: true width: root.width + 4 height: popupText.implicitHeight * 1.4 + highlighted: root.currentHighLightedIndex == index onHoveredChanged: { - itemDelegate.highlighted = hovered; + if (hovered) { + root.currentHighLightedIndex = index + } } background: Rectangle { @@ -151,7 +155,7 @@ FocusScope { id: listView clip: true model: comboBox.popup.visible ? comboBox.delegateModel : null - currentIndex: comboBox.highlightedIndex + currentIndex: root.currentHighLightedIndex delegate: comboBox.delegate ScrollBar.vertical: HifiControls.ScrollBar { id: scrollbar @@ -165,135 +169,8 @@ FocusScope { color: hifi.colors.baseGray } } -// MouseArea { -// id: popupHover -// anchors.fill: parent; -// hoverEnabled: scrollView.hoverEnabled; -// onEntered: listView.currentIndex = index; -// onClicked: popup.selectSpecificItem(index); -// } -// } -// } - } -/* - MouseArea { - id: controlHover - hoverEnabled: true - anchors.fill: parent - onClicked: toggleList(); } - function toggleList() { - if (popup.visible) { - hideList(); - } else { - showList(); - } - } - - function showList() { - var r; - if (isDesktop) { - r = desktop.mapFromItem(root, 0, 0, root.width, root.height); - } else { - r = mapFromItem(root, 0, 0, root.width, root.height); - } - var y = r.y + r.height; - var bottom = y + scrollView.height; - var height = isDesktop ? desktop.height : tabletRoot.height; - if (bottom > height) { - y -= bottom - height + 8; - } - scrollView.x = r.x; - scrollView.y = y; - popup.visible = true; - popup.forceActiveFocus(); - listView.currentIndex = root.currentIndex; - scrollView.hoverEnabled = true; - } - - function hideList() { - popup.visible = false; - scrollView.hoverEnabled = false; - root.accepted(); - } - - FocusScope { - id: popup - parent: isDesktop ? desktop : root - anchors.fill: parent - z: isDesktop ? desktop.zLevels.menu : 12 - visible: false - focus: true - - MouseArea { - anchors.fill: parent - onClicked: hideList(); - } - - function previousItem() { listView.currentIndex = (listView.currentIndex + listView.count - 1) % listView.count; } - function nextItem() { listView.currentIndex = (listView.currentIndex + listView.count + 1) % listView.count; } - function selectCurrentItem() { root.currentIndex = listView.currentIndex; hideList(); } - function selectSpecificItem(index) { root.currentIndex = index; hideList(); } - - Keys.onUpPressed: previousItem(); - Keys.onDownPressed: nextItem(); - Keys.onSpacePressed: selectCurrentItem(); - Keys.onRightPressed: selectCurrentItem(); - Keys.onReturnPressed: selectCurrentItem(); - Keys.onEscapePressed: hideList(); - - ScrollView { - id: scrollView - height: root.dropdownHeight - width: root.width + 4 - hoverEnabled: false; - - background: Rectangle{ - implicitWidth: 20 - color: hifi.colors.baseGray - } - - contentItem: Rectangle { - implicitWidth: 16 - anchors.left: parent.left - anchors.leftMargin: 3 - anchors.top: parent.top - anchors.bottom: parent.bottom - radius: 6 - color: hifi.colors.lightGrayText - } - - ListView { - id: listView - height: textField.height * count * 1.4 - model: root.model - delegate: Rectangle { - width: root.width + 4 - height: popupText.implicitHeight * 1.4 - color: (listView.currentIndex === index) ? hifi.colors.primaryHighlight : - (isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark) - FiraSansSemiBold { - anchors.left: parent.left - anchors.leftMargin: hifi.dimensions.textPadding - anchors.verticalCenter: parent.verticalCenter - id: popupText - text: listView.model[index] ? listView.model[index] : (listView.model.get && listView.model.get(index).text ? listView.model.get(index).text : "") - size: hifi.fontSizes.textFieldInput - color: hifi.colors.baseGray - } - MouseArea { - id: popupHover - anchors.fill: parent; - hoverEnabled: scrollView.hoverEnabled; - onEntered: listView.currentIndex = index; - onClicked: popup.selectSpecificItem(index); - } - } - } - } - } -*/ HifiControls.Label { id: comboBoxLabel text: root.label @@ -306,7 +183,5 @@ FocusScope { Component.onCompleted: { isDesktop = (typeof desktop !== "undefined"); - //TODO: do we need this? - //comboBox.popup.z = isDesktop ? desktop.zLevels.menu : 12 } } From 3e118c58a1392daa9f0754b0dc5f0355dbd32339 Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 4 Dec 2017 12:51:57 +0100 Subject: [PATCH 019/438] Moved RadiButton and Slider --- .../qml/controls-uit/RadioButton.qml | 92 ++++++++-------- .../resources/qml/controls-uit/Slider.qml | 103 +++++++++--------- 2 files changed, 97 insertions(+), 98 deletions(-) diff --git a/interface/resources/qml/controls-uit/RadioButton.qml b/interface/resources/qml/controls-uit/RadioButton.qml index 65d36d2dcb..17fa8e746e 100644 --- a/interface/resources/qml/controls-uit/RadioButton.qml +++ b/interface/resources/qml/controls-uit/RadioButton.qml @@ -9,8 +9,7 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 as Original -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.2 as Original import "../styles-uit" import "../controls-uit" as HifiControls @@ -21,6 +20,8 @@ Original.RadioButton { id: radioButton HifiConstants { id: hifi } + hoverEnabled: true + property int colorScheme: hifi.colorSchemes.light readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light @@ -33,50 +34,49 @@ Original.RadioButton { tabletInterface.playSound(TabletEnums.ButtonClick); } -// TODO: doesnt works for QQC1. check with QQC2 -// onHovered: { -// tabletInterface.playSound(TabletEnums.ButtonHover); -// } - - style: RadioButtonStyle { - indicator: Rectangle { - id: box - width: boxSize - height: boxSize - radius: 7 - gradient: Gradient { - GradientStop { - position: 0.2 - color: pressed || hovered - ? (radioButton.isLightColorScheme ? hifi.colors.checkboxDarkStart : hifi.colors.checkboxLightStart) - : (radioButton.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart) - } - GradientStop { - position: 1.0 - color: pressed || hovered - ? (radioButton.isLightColorScheme ? hifi.colors.checkboxDarkFinish : hifi.colors.checkboxLightFinish) - : (radioButton.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) - } - } - - Rectangle { - id: check - width: checkSize - height: checkSize - radius: 7 - anchors.centerIn: parent - color: "#00B4EF" - border.width: 1 - border.color: "#36CDFF" - visible: checked && !pressed || !checked && pressed - } - } - - label: RalewaySemiBold { - text: control.text - size: hifi.fontSizes.inputLabel - color: isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText - x: radioButton.boxSize / 2 + onHoveredChanged: { + if (hovered) { + tabletInterface.playSound(TabletEnums.ButtonHover); } } + + indicator: Rectangle { + id: box + width: boxSize + height: boxSize + radius: 7 + gradient: Gradient { + GradientStop { + position: 0.2 + color: pressed || hovered + ? (radioButton.isLightColorScheme ? hifi.colors.checkboxDarkStart : hifi.colors.checkboxLightStart) + : (radioButton.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart) + } + GradientStop { + position: 1.0 + color: pressed || hovered + ? (radioButton.isLightColorScheme ? hifi.colors.checkboxDarkFinish : hifi.colors.checkboxLightFinish) + : (radioButton.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) + } + } + + Rectangle { + id: check + width: checkSize + height: checkSize + radius: 7 + anchors.centerIn: parent + color: "#00B4EF" + border.width: 1 + border.color: "#36CDFF" + visible: checked && !pressed || !checked && pressed + } + } + + contentItem: RalewaySemiBold { + text: radioButton.text + size: hifi.fontSizes.inputLabel + color: isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText + x: radioButton.boxSize / 2 + } } diff --git a/interface/resources/qml/controls-uit/Slider.qml b/interface/resources/qml/controls-uit/Slider.qml index 89bae9bcde..f24785450f 100644 --- a/interface/resources/qml/controls-uit/Slider.qml +++ b/interface/resources/qml/controls-uit/Slider.qml @@ -8,9 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import "../styles-uit" import "../controls-uit" as HifiControls @@ -26,63 +25,63 @@ Slider { height: hifi.fontSizes.textFieldInput + 14 // Match height of TextField control. y: sliderLabel.visible ? sliderLabel.height + sliderLabel.anchors.bottomMargin : 0 - style: SliderStyle { - groove: Rectangle { - implicitWidth: 50 - implicitHeight: hifi.dimensions.sliderGrooveHeight + background: Rectangle { + implicitWidth: 50 + implicitHeight: hifi.dimensions.sliderGrooveHeight + radius: height / 2 + color: isLightColorScheme ? hifi.colors.sliderGutterLight : hifi.colors.sliderGutterDark + + Rectangle { + width: parent.height - 2 + height: slider.width * (slider.value - slider.minimumValue) / (slider.maximumValue - slider.minimumValue) - 1 radius: height / 2 - color: isLightColorScheme ? hifi.colors.sliderGutterLight : hifi.colors.sliderGutterDark + anchors { + top: parent.top + topMargin: width + 1 + left: parent.left + leftMargin: 1 + } + transformOrigin: Item.TopLeft + rotation: -90 + gradient: Gradient { + GradientStop { position: 0.0; color: hifi.colors.blueAccent } + GradientStop { position: 1.0; color: hifi.colors.primaryHighlight } + } + } + } - Rectangle { - width: parent.height - 2 - height: slider.width * (slider.value - slider.minimumValue) / (slider.maximumValue - slider.minimumValue) - 1 - radius: height / 2 - anchors { - top: parent.top - topMargin: width + 1 - left: parent.left - leftMargin: 1 - } - transformOrigin: Item.TopLeft - rotation: -90 - gradient: Gradient { - GradientStop { position: 0.0; color: hifi.colors.blueAccent } - GradientStop { position: 1.0; color: hifi.colors.primaryHighlight } - } + handle: Rectangle { + x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width) + y: slider.topPadding + slider.availableHeight / 2 - height / 2 + implicitWidth: hifi.dimensions.sliderHandleSize + implicitHeight: hifi.dimensions.sliderHandleSize + radius: height / 2 + border.width: 1 + border.color: isLightColorScheme ? hifi.colors.sliderBorderLight : hifi.colors.sliderBorderDark + gradient: Gradient { + GradientStop { + position: 0.0 + color: pressed || hovered + ? (isLightColorScheme ? hifi.colors.sliderDarkStart : hifi.colors.sliderLightStart ) + : (isLightColorScheme ? hifi.colors.sliderLightStart : hifi.colors.sliderDarkStart ) + } + GradientStop { + position: 1.0 + color: pressed || hovered + ? (isLightColorScheme ? hifi.colors.sliderDarkFinish : hifi.colors.sliderLightFinish ) + : (isLightColorScheme ? hifi.colors.sliderLightFinish : hifi.colors.sliderDarkFinish ) } } - handle: Rectangle { - implicitWidth: hifi.dimensions.sliderHandleSize - implicitHeight: hifi.dimensions.sliderHandleSize + Rectangle { + height: parent.height - 2 + width: height radius: height / 2 + anchors.centerIn: parent + color: hifi.colors.transparent border.width: 1 - border.color: isLightColorScheme ? hifi.colors.sliderBorderLight : hifi.colors.sliderBorderDark - gradient: Gradient { - GradientStop { - position: 0.0 - color: pressed || hovered - ? (isLightColorScheme ? hifi.colors.sliderDarkStart : hifi.colors.sliderLightStart ) - : (isLightColorScheme ? hifi.colors.sliderLightStart : hifi.colors.sliderDarkStart ) - } - GradientStop { - position: 1.0 - color: pressed || hovered - ? (isLightColorScheme ? hifi.colors.sliderDarkFinish : hifi.colors.sliderLightFinish ) - : (isLightColorScheme ? hifi.colors.sliderLightFinish : hifi.colors.sliderDarkFinish ) - } - } - - Rectangle { - height: parent.height - 2 - width: height - radius: height / 2 - anchors.centerIn: parent - color: hifi.colors.transparent - border.width: 1 - border.color: hifi.colors.black - } + border.color: hifi.colors.black } } From b52163cb24908c9ec3062384b718d42dd4f79564 Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 4 Dec 2017 20:54:35 +0100 Subject: [PATCH 020/438] Intitial SpinBox port --- .../resources/qml/controls-uit/SpinBox.qml | 159 ++++++++++++------ .../hifi/dialogs/attachments/Attachment.qml | 2 +- .../qml/hifi/dialogs/attachments/Vector3.qml | 8 +- 3 files changed, 108 insertions(+), 61 deletions(-) diff --git a/interface/resources/qml/controls-uit/SpinBox.qml b/interface/resources/qml/controls-uit/SpinBox.qml index a1237d4bc7..b4ad996554 100644 --- a/interface/resources/qml/controls-uit/SpinBox.qml +++ b/interface/resources/qml/controls-uit/SpinBox.qml @@ -8,9 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import "../styles-uit" import "../controls-uit" as HifiControls @@ -19,11 +18,42 @@ SpinBox { id: spinBox property int colorScheme: hifi.colorSchemes.light - readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light property string label: "" property string labelInside: "" property color colorLabelInside: hifi.colors.white property real controlHeight: height + (spinBoxLabel.visible ? spinBoxLabel.height + spinBoxLabel.anchors.bottomMargin : 0) + property int decimals: 2; + property real factor: Math.pow(10, decimals) + + property real minimumValue: 0.0 + property real maximumValue: 0.0 + + property real realValue: 0.0 + property real realFrom: 0.0 + property real realTo: 100.0 + property real realStepSize: 1.0 + + locale: Qt.locale("en_US") + + onValueModified: { + realValue = value/factor + console.warn("rv (value mod)", realValue) + } + + onValueChanged: { + realValue = value/factor + console.warn("rv (value)", realValue) + } + + padding: 0 + leftPadding: 0 + rightPadding: 0 + + stepSize: realStepSize*factor + value: realValue*factor + to : realTo*factor + from : realFrom*factor FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; } font.family: firaSansSemiBold.name @@ -32,43 +62,60 @@ SpinBox { y: spinBoxLabel.visible ? spinBoxLabel.height + spinBoxLabel.anchors.bottomMargin : 0 - style: SpinBoxStyle { - id: spinStyle - background: Rectangle { - color: isLightColorScheme - ? (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGray) - : (spinBox.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow) - border.color: spinBoxLabelInside.visible ? spinBoxLabelInside.color : hifi.colors.primaryHighlight - border.width: spinBox.activeFocus ? spinBoxLabelInside.visible ? 2 : 1 : 0 - } + background: Rectangle { + color: isLightColorScheme + ? (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGray) + : (spinBox.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow) + border.color: spinBoxLabelInside.visible ? spinBoxLabelInside.color : hifi.colors.primaryHighlight + border.width: spinBox.activeFocus ? spinBoxLabelInside.visible ? 2 : 1 : 0 + } - textColor: isLightColorScheme - ? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.lightGray) - : (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText) + validator: DoubleValidator { + bottom: Math.min(spinBox.from, spinBox.to)*spinBox.factor + top: Math.max(spinBox.from, spinBox.to)*spinBox.factor + } + + textFromValue: function(value, locale) { + return parseFloat(value*1.0/factor).toFixed(decimals); + } + + valueFromText: function(text, locale) { + console.warn("valueFromText", text) + return Number.fromLocaleString(locale, text); + } + + + contentItem: TextInput { + color: isLightColorScheme + ? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.lightGray) + : (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText) selectedTextColor: hifi.colors.black selectionColor: hifi.colors.primaryHighlight + text: spinBox.textFromValue(spinBox.value, spinBox.locale) + verticalAlignment: Qt.AlignVCenter + leftPadding: spinBoxLabelInside.visible ? 30 : hifi.dimensions.textPadding + //rightPadding: hifi.dimensions.spinnerSize + width: spinBox.width - hifi.dimensions.spinnerSize + } - horizontalAlignment: Qt.AlignLeft - padding.left: spinBoxLabelInside.visible ? 30 : hifi.dimensions.textPadding - padding.right: hifi.dimensions.spinnerSize - padding.top: 0 + up.indicator: HiFiGlyphs { + x: spinBox.mirrored ? 0 : spinBox.width - implicitWidth + 10 + y: 1 + height: parent.height/2 + width: height + text: hifi.glyphs.caratUp + size: hifi.dimensions.spinnerSize + color: spinBox.up.pressed || spinBox.up.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray + } - incrementControl: HiFiGlyphs { - id: incrementButton - text: hifi.glyphs.caratUp - x: 10 - y: 1 - size: hifi.dimensions.spinnerSize - color: styleData.upPressed ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray - } - - decrementControl: HiFiGlyphs { + down.indicator: HiFiGlyphs { + x: spinBox.mirrored ? 0 : spinBox.width - implicitWidth + 10 + height: parent.height/2 + width: height + y: height - 1 text: hifi.glyphs.caratDn - x: 10 - y: -1 size: hifi.dimensions.spinnerSize - color: styleData.downPressed ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray - } + color: spinBox.down.pressed || spinBox.up.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray } HifiControls.Label { @@ -92,26 +139,26 @@ SpinBox { visible: spinBox.labelInside != "" } - MouseArea { - anchors.fill: parent - propagateComposedEvents: true - onWheel: { - if(spinBox.activeFocus) - wheel.accepted = false - else - wheel.accepted = true - } - onPressed: { - mouse.accepted = false - } - onReleased: { - mouse.accepted = false - } - onClicked: { - mouse.accepted = false - } - onDoubleClicked: { - mouse.accepted = false - } - } +// MouseArea { +// anchors.fill: parent +// propagateComposedEvents: true +// onWheel: { +// if(spinBox.activeFocus) +// wheel.accepted = false +// else +// wheel.accepted = true +// } +// onPressed: { +// mouse.accepted = false +// } +// onReleased: { +// mouse.accepted = false +// } +// onClicked: { +// mouse.accepted = false +// } +// onDoubleClicked: { +// mouse.accepted = false +// } +// } } diff --git a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml index 89ad327a71..d93e077b5a 100644 --- a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml +++ b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml @@ -180,7 +180,7 @@ Item { decimals: 2; minimumValue: 0.01 maximumValue: 10 - stepSize: 0.05; + realStepSize: 0.05; value: attachment ? attachment.scale : 1.0 colorScheme: hifi.colorSchemes.dark onValueChanged: { diff --git a/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml b/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml index eb6172ec78..228d71fe6f 100644 --- a/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml +++ b/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml @@ -56,12 +56,12 @@ Item { colorScheme: hifi.colorSchemes.dark colorLabelInside: hifi.colors.redHighlight decimals: root.decimals - stepSize: root.stepSize + realStepSize: root.stepSize maximumValue: root.maximumValue minimumValue: root.minimumValue - onValueChanged: { - if (value !== vector.x) { - vector.x = value + onRealValueChanged: { + if (realValue !== vector.x) { + vector.x = realValue root.valueChanged(); } } From 1cf82b7725c1eecdfa38fa6f8296c476c456df79 Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 19 Dec 2017 21:28:28 +0100 Subject: [PATCH 021/438] SpinBox now works --- .../resources/qml/controls-uit/SpinBox.qml | 53 ++++++++++++------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/interface/resources/qml/controls-uit/SpinBox.qml b/interface/resources/qml/controls-uit/SpinBox.qml index b4ad996554..32da3a6df8 100644 --- a/interface/resources/qml/controls-uit/SpinBox.qml +++ b/interface/resources/qml/controls-uit/SpinBox.qml @@ -34,6 +34,15 @@ SpinBox { property real realTo: 100.0 property real realStepSize: 1.0 + implicitHeight: height + implicitWidth: width + + padding: 0 + leftPadding: 0 + rightPadding: padding + (up.indicator ? up.indicator.width : 0) + topPadding: 0 + bottomPadding: 0 + locale: Qt.locale("en_US") onValueModified: { @@ -46,10 +55,6 @@ SpinBox { console.warn("rv (value)", realValue) } - padding: 0 - leftPadding: 0 - rightPadding: 0 - stepSize: realStepSize*factor value: realValue*factor to : realTo*factor @@ -86,6 +91,7 @@ SpinBox { contentItem: TextInput { + z: 2 color: isLightColorScheme ? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.lightGray) : (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText) @@ -97,25 +103,32 @@ SpinBox { //rightPadding: hifi.dimensions.spinnerSize width: spinBox.width - hifi.dimensions.spinnerSize } - - up.indicator: HiFiGlyphs { - x: spinBox.mirrored ? 0 : spinBox.width - implicitWidth + 10 + up.indicator: Item { + x: spinBox.width - implicitWidth - 5 y: 1 - height: parent.height/2 - width: height - text: hifi.glyphs.caratUp - size: hifi.dimensions.spinnerSize - color: spinBox.up.pressed || spinBox.up.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray - } - - down.indicator: HiFiGlyphs { - x: spinBox.mirrored ? 0 : spinBox.width - implicitWidth + 10 - height: parent.height/2 - width: height - y: height - 1 - text: hifi.glyphs.caratDn + clip: true + implicitHeight: spinBox.implicitHeight/2 + implicitWidth: spinBox.implicitHeight/2 + HiFiGlyphs { + anchors.centerIn: parent + text: hifi.glyphs.caratUp size: hifi.dimensions.spinnerSize color: spinBox.down.pressed || spinBox.up.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray + } + } + + down.indicator: Item { + x: spinBox.width - implicitWidth - 5 + y: spinBox.implicitHeight/2 + clip: true + implicitHeight: spinBox.implicitHeight/2 + implicitWidth: spinBox.implicitHeight/2 + HiFiGlyphs { + anchors.centerIn: parent + text: hifi.glyphs.caratDn + size: hifi.dimensions.spinnerSize + color: spinBox.down.pressed || spinBox.down.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray + } } HifiControls.Label { From 5877eeea0b85a99ea9f402396006265da4bdbb18 Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 19 Dec 2017 21:35:11 +0100 Subject: [PATCH 022/438] cleanup --- interface/resources/qml/controls-uit/SpinBox.qml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/controls-uit/SpinBox.qml b/interface/resources/qml/controls-uit/SpinBox.qml index 32da3a6df8..a04441851f 100644 --- a/interface/resources/qml/controls-uit/SpinBox.qml +++ b/interface/resources/qml/controls-uit/SpinBox.qml @@ -45,15 +45,8 @@ SpinBox { locale: Qt.locale("en_US") - onValueModified: { - realValue = value/factor - console.warn("rv (value mod)", realValue) - } - - onValueChanged: { - realValue = value/factor - console.warn("rv (value)", realValue) - } + onValueModified: realValue = value/factor + onValueChanged: realValue = value/factor stepSize: realStepSize*factor value: realValue*factor @@ -85,7 +78,6 @@ SpinBox { } valueFromText: function(text, locale) { - console.warn("valueFromText", text) return Number.fromLocaleString(locale, text); } From f5f19ea67fb1a978be0bd3db7fe208ee939483e6 Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 28 Dec 2017 15:53:16 +0100 Subject: [PATCH 023/438] Move RadioButton --- .../qml/controls-uit/RadioButton.qml | 8 ++++++-- interface/resources/qml/js/Utils.jsc | Bin 6596 -> 6274 bytes 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/controls-uit/RadioButton.qml b/interface/resources/qml/controls-uit/RadioButton.qml index bc6426977d..ebfe1ff9a9 100644 --- a/interface/resources/qml/controls-uit/RadioButton.qml +++ b/interface/resources/qml/controls-uit/RadioButton.qml @@ -36,7 +36,7 @@ Original.RadioButton { onHoveredChanged: { if (hovered) { - tabletInterface.playSound(TabletEnums.ButtonHover); + Tablet.playSound(TabletEnums.ButtonHover); } } @@ -45,6 +45,8 @@ Original.RadioButton { width: boxSize height: boxSize radius: 7 + x: radioButton.leftPadding + y: parent.height / 2 - height / 2 gradient: Gradient { GradientStop { position: 0.2 @@ -77,6 +79,8 @@ Original.RadioButton { text: radioButton.text size: hifi.fontSizes.inputLabel color: isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText - x: radioButton.boxSize / 2 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + leftPadding: radioButton.indicator.width + radioButton.spacing } } diff --git a/interface/resources/qml/js/Utils.jsc b/interface/resources/qml/js/Utils.jsc index ab20e996b9469915ac6a89901da175143e6b5024..eded46b485e5e9b6b6bb8f6c731dee09de5b40b5 100644 GIT binary patch literal 6274 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 Date: Sat, 30 Dec 2017 19:55:52 +0100 Subject: [PATCH 024/438] TextField moved. Fixed sound on hovering check box --- .../resources/qml/controls-uit/CheckBox.qml | 2 +- .../resources/qml/controls-uit/TextField.qml | 142 ++++++++++-------- interface/resources/qml/js/Utils.jsc | Bin 6274 -> 0 bytes 3 files changed, 79 insertions(+), 65 deletions(-) delete mode 100644 interface/resources/qml/js/Utils.jsc diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index 208e5cc965..997f22bcbb 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -36,7 +36,7 @@ Original.CheckBox { onHoveredChanged: { if (hovered) { - tabletInterface.playSound(TabletEnums.ButtonHover); + Tablet.playSound(TabletEnums.ButtonHover); } } diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml index e636bfc27f..cf5bd40173 100644 --- a/interface/resources/qml/controls-uit/TextField.qml +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -8,9 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import "../styles-uit" import "../controls-uit" as HifiControls @@ -27,8 +26,7 @@ TextField { property bool hasRoundedBorder: false property bool error: false; property bool hasClearButton: false; - - placeholderText: textField.placeholderText + property alias textColor: textField.color FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; } font.family: firaSansSemiBold.name @@ -42,41 +40,56 @@ TextField { // workaround for https://bugreports.qt.io/browse/QTBUG-49297 Keys.onPressed: { switch (event.key) { - case Qt.Key_Return: - case Qt.Key_Enter: - event.accepted = true; + case Qt.Key_Return: + case Qt.Key_Enter: + event.accepted = true; - // emit accepted signal manually - if (acceptableInput) { - accepted(); - } + // emit accepted signal manually + if (acceptableInput) { + accepted(); + } } } - style: TextFieldStyle { - textColor: { - if (isLightColorScheme) { - if (textField.activeFocus) { - hifi.colors.black - } else { - hifi.colors.lightGray - } - } else if (isFaintGrayColorScheme) { - if (textField.activeFocus) { - hifi.colors.black - } else { - hifi.colors.lightGray - } + Text { + id: placeholder + x: textField.leftPadding + y: textField.topPadding + width: textField.width - (textField.leftPadding + textField.rightPadding) + height: textField.height - (textField.topPadding + textField.bottomPadding) + + text: textField.placeholderText + font: textField.font + color: textField.placeholderTextColor + verticalAlignment: textField.verticalAlignment + visible: !textField.length && !textField.preeditText && (!textField.activeFocus || textField.horizontalAlignment !== Qt.AlignHCenter) + elide: Text.ElideRight + } + + color: { + if (isLightColorScheme) { + if (textField.activeFocus) { + hifi.colors.black } else { - if (textField.activeFocus) { - hifi.colors.white - } else { - hifi.colors.lightGrayText - } + hifi.colors.lightGray + } + } else if (isFaintGrayColorScheme) { + if (textField.activeFocus) { + hifi.colors.black + } else { + hifi.colors.lightGray + } + } else { + if (textField.activeFocus) { + hifi.colors.white + } else { + hifi.colors.lightGrayText } } - background: Rectangle { - color: { + } + + background: Rectangle { + color: { if (isLightColorScheme) { if (textField.activeFocus) { hifi.colors.white @@ -97,45 +110,46 @@ TextField { } } } - border.color: textField.error ? hifi.colors.redHighlight : - (textField.activeFocus ? hifi.colors.primaryHighlight : (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray)) - border.width: textField.activeFocus || hasRoundedBorder || textField.error ? 1 : 0 - radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? 4 : 0) + border.color: textField.error ? hifi.colors.redHighlight : + (textField.activeFocus ? hifi.colors.primaryHighlight : (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray)) + border.width: textField.activeFocus || hasRoundedBorder || textField.error ? 1 : 0 + radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? 4 : 0) - HiFiGlyphs { - text: hifi.glyphs.search - color: textColor - size: hifi.fontSizes.textFieldSearchIcon - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: hifi.dimensions.textPadding - 2 - visible: isSearchField - } + HiFiGlyphs { + text: hifi.glyphs.search + color: textColor + size: hifi.fontSizes.textFieldSearchIcon + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: hifi.dimensions.textPadding - 2 + visible: isSearchField + } - HiFiGlyphs { - text: hifi.glyphs.error - color: textColor - size: 40 - anchors.right: parent.right - anchors.rightMargin: hifi.dimensions.textPadding - 2 - anchors.verticalCenter: parent.verticalCenter - visible: hasClearButton && textField.text !== ""; + HiFiGlyphs { + text: hifi.glyphs.error + color: textColor + size: 40 + anchors.right: parent.right + anchors.rightMargin: hifi.dimensions.textPadding - 2 + anchors.verticalCenter: parent.verticalCenter + visible: hasClearButton && textField.text !== ""; - MouseArea { - anchors.fill: parent; - onClicked: { - textField.text = ""; - } + MouseArea { + anchors.fill: parent; + onClicked: { + textField.text = ""; } } } - placeholderTextColor: isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray - selectedTextColor: hifi.colors.black - selectionColor: hifi.colors.primaryHighlight - padding.left: (isSearchField ? textField.height - 2 : 0) + hifi.dimensions.textPadding - padding.right: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding } + property color placeholderTextColor: isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray + selectedTextColor: hifi.colors.black + selectionColor: hifi.colors.primaryHighlight + leftPadding: (isSearchField ? textField.height - 2 : 0) + hifi.dimensions.textPadding + rightPadding: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding + + HifiControls.Label { id: textFieldLabel text: textField.label diff --git a/interface/resources/qml/js/Utils.jsc b/interface/resources/qml/js/Utils.jsc deleted file mode 100644 index eded46b485e5e9b6b6bb8f6c731dee09de5b40b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6274 zcmcIoUu;`f8UNYW85s<27+V>vj0!Ggsz$w`YMM}`#hEi~aHhbrsGUaFEOC~U{9EEy z=~N(H8WhV^_+hFH;=vD<=7&D`VL~PpD?mjj;Gx~R(N24qrcFww2xS(3sxfBtJLfys z_qx|fqli;gC-*zw{r>*G?|j$K9osiJG>{qi902RqHYDo#olk~<6K%k`zkINN;YSar zfAhw#FLv$z{O&ISxCb|`uLJlpfCFKm4M1L4&Nl63c{ckOTJtxKQKJ9TnVh6T8C`!sn&xm!7bL!vMVUrkukhWri9F-` zl`!yD82Bg*++hLxEFf+HeHJie0T~N8VF9Ns;Aa+a)&gF(fb$k`!236xPLJ&0mc38*UfH{4 ze^T~?=u+~gF^QupGCEH5FNH}AVgO@E<8fp#t!#c)*|$qD?TzPQ6~9r%|1Ct-zY!Jb zG@=-DrA5(?NAL(9LpS;m#WBn%U&d6FeuyZBkXD}aohghXjR|BB#Rx_*to}1g;w_PgKkcu_b~wnTe+mIhs;W2QDw0d}sExRuCl|ZqhFpCiFGH0U zYP;_D+O;=CpW;p>dAiT3R4G++Ey80xuZ4!PD*XtM^^S^x^Gk zL!-~GDZi|S^*&kO@ylvN!Y9k2EH~T9`L$kVHkCWNlFE&*r*i4*iTMk?Klo#VXZ3IO zdfnt4TgMZdWH6q3ayxo*-*2zQ?2fLETFm}z+^KkJcWGMLOWUPsrJmf5*u}2<8T~5T zmrg;+o6i48K#Hvx-V}yH>|)%xfL#{&$1<~!%*CAo5%UVIi#rvq_L6=o>!+(k({h#7 ztpe z0qO?+y&&3dq6Ykd6tz-b9F|nBJmtj?0#6N`T$=;1X&GE$Bil0mcPL#!_o25hN&hNp z+P%qKIFai-OdAV6(g;a;A8CrDMIULFq&Y8Xjvgknr<_?{#;S2@C39@=JeX=e;8zk4 zMw$-_a&Wl$fQ7|34(J0mSWfLa;gTaJrz38_zU%|LG|M?pS!jbjm7xul;^5obKuuWD z1RACZ>zY7=G~s$;$rPen%F-5ky*K_;gs#M$LgOMEPS_LDz>8W~emG(O_)_YD6>=84 zbA8T>v6CP2BCr?u`BRJ8d-|OGYA1l$??1d<%{aRmq|+yye5`(^9QKS4|FwYB11n0+ z>Zi$-i#vHkF?QrT$?!_eK6E)z`QhV8wV?VrA+n%nj)nc1o(w6rLeUNx4k zYqM(hfotA4;QYmRvdHcSQyt{tV#vie=$N+%hAuEZ{El z8Y)%U%bI)XqjGiln$jFcM23fbiAm;9GnBE&dn&K*&Sh`GaYW<`d2$uWRnT1g!NtAv zt_fFDvDpH8UZHzlT_vJSy`4J5SG<+P_@8y_)V#`)-t&(C)vwB{2HJ1e>;qT4z$mWj zY8H#dFaAX1T5PVGqNExfp2l>Oyd@E;RS*Ye5~d zV0yI|B8Bai`$Ui`_gWyTjNYp?yW`L7t~L89U(qWY8jx?eTMTx$*zE56LEY0-MY9bD zU-SBJ0HDy+;!7)MW^e%uAsh@A{Y}*xEHNX=onwQ*a<_0brrjX0{L@U%%;3ubU?~Sj zuiPBHb3qNe90Zm!Rh Date: Sun, 31 Dec 2017 15:17:44 +0100 Subject: [PATCH 025/438] Switch moved --- .../resources/qml/controls-uit/Switch.qml | 68 ++++++++++--------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/interface/resources/qml/controls-uit/Switch.qml b/interface/resources/qml/controls-uit/Switch.qml index d54f986717..bfe86b1420 100644 --- a/interface/resources/qml/controls-uit/Switch.qml +++ b/interface/resources/qml/controls-uit/Switch.qml @@ -8,9 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 as Original -import QtQuick.Controls.Styles 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 as Original import "../styles-uit" @@ -33,44 +32,49 @@ Item { Original.Switch { id: originalSwitch; - activeFocusOnPress: true; + focusPolicy: Qt.ClickFocus anchors.top: rootSwitch.top; anchors.left: rootSwitch.left; anchors.leftMargin: rootSwitch.width/2 - rootSwitch.switchWidth/2; onCheckedChanged: rootSwitch.onCheckedChanged(); onClicked: rootSwitch.clicked(); + hoverEnabled: true - style: SwitchStyle { + topPadding: 3; + leftPadding: 3; + rightPadding: 3; + bottomPadding: 3; - padding { - top: 3; - left: 3; - right: 3; - bottom: 3; + onHoveredChanged: { + if (hovered) { + switchHandle.color = hifi.colors.blueHighlight; + } else { + switchHandle.color = hifi.colors.lightGray; + } + } + + background: Rectangle { + color: "#252525"; + implicitWidth: rootSwitch.switchWidth; + implicitHeight: rootSwitch.height; + radius: rootSwitch.switchRadius; + } + + indicator: Rectangle { + id: switchHandle; + implicitWidth: rootSwitch.height - originalSwitch.topPadding - originalSwitch.bottomPadding; + implicitHeight: implicitWidth; + radius: implicitWidth/2; + border.color: hifi.colors.lightGrayText; + color: hifi.colors.lightGray; + //x: originalSwitch.leftPadding + x: Math.max(0, Math.min(parent.width - width, originalSwitch.visualPosition * parent.width - (width / 2))) + y: parent.height / 2 - height / 2 + Behavior on x { + enabled: !originalSwitch.down + SmoothedAnimation { velocity: 200 } } - groove: Rectangle { - color: "#252525"; - implicitWidth: rootSwitch.switchWidth; - implicitHeight: rootSwitch.height; - radius: rootSwitch.switchRadius; - } - - handle: Rectangle { - id: switchHandle; - implicitWidth: rootSwitch.height - padding.top - padding.bottom; - implicitHeight: implicitWidth; - radius: implicitWidth/2; - border.color: hifi.colors.lightGrayText; - color: hifi.colors.lightGray; - - MouseArea { - anchors.fill: parent; - hoverEnabled: true; - onEntered: parent.color = hifi.colors.blueHighlight; - onExited: parent.color = hifi.colors.lightGray; - } - } } } From 45144bece6d429a609b1ea0fc01884ef2328b5f5 Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 11 Jan 2018 22:05:11 +0100 Subject: [PATCH 026/438] Fix Properties does not fit in Create tab --- interface/resources/qml/hifi/tablet/EditTabButton.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/tablet/EditTabButton.qml b/interface/resources/qml/hifi/tablet/EditTabButton.qml index 761ab4d8cd..666ea16927 100644 --- a/interface/resources/qml/hifi/tablet/EditTabButton.qml +++ b/interface/resources/qml/hifi/tablet/EditTabButton.qml @@ -24,7 +24,7 @@ TabButton { contentItem: Text { id: text text: control.text - font.pixelSize: 16 + font.pixelSize: 14 font.bold: true color: control.checked ? "white" : "white" horizontalAlignment: Text.AlignHCenter From e7a035f954ed16f58583c086bea1005dd515b9eb Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 19 Jan 2018 23:01:41 +0100 Subject: [PATCH 027/438] Fix indent --- .../resources/qml/controls-uit/TextField.qml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml index ffc2995f01..814c07cb8a 100644 --- a/interface/resources/qml/controls-uit/TextField.qml +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -118,16 +118,16 @@ TextField { radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? 4 : 0) HiFiGlyphs { - text: textField.leftPlaceholderGlyph; - color: textColor; - size: hifi.fontSizes.textFieldSearchIcon; - anchors.left: parent.left; - anchors.verticalCenter: parent.verticalCenter; - anchors.leftMargin: hifi.dimensions.textPadding - 2; - visible: text; - } + text: textField.leftPlaceholderGlyph; + color: textColor; + size: hifi.fontSizes.textFieldSearchIcon; + anchors.left: parent.left; + anchors.verticalCenter: parent.verticalCenter; + anchors.leftMargin: hifi.dimensions.textPadding - 2; + visible: text; + } - HiFiGlyphs { + HiFiGlyphs { text: hifi.glyphs.search color: textColor size: hifi.fontSizes.textFieldSearchIcon From ea23d8283ff5a0c80bdb2e499bf356dab36c29e4 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 25 Jan 2017 16:02:58 -0800 Subject: [PATCH 028/438] Introducing the TextureTable in the gpu shader system --- libraries/gpu/src/gpu/TextureTable.slh | 29 ++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 libraries/gpu/src/gpu/TextureTable.slh diff --git a/libraries/gpu/src/gpu/TextureTable.slh b/libraries/gpu/src/gpu/TextureTable.slh new file mode 100644 index 0000000000..1aa727c99d --- /dev/null +++ b/libraries/gpu/src/gpu/TextureTable.slh @@ -0,0 +1,29 @@ + +<@if not GPU_TEXTURE_TABLE_SLH@> +<@def GPU_TEXTURE_TABLE_SLH@> + +#ifdef GPU_TEXTURE_TABLE_BINDLESS +#define GPU_TEXTURE_TABLE_MAX_NUM_TEXTURES 8 + +struct TextureTable { + uvec4 _textures[GPU_TEXTURE_TABLE_MAX_NUM_TEXTURES]; +}; + +#define TextureTable(index) layout (std140) uniform gpu_textureTableBuffer##index { TextureTable _table##index; }; + +#else + +#endif + + + + +<@endif@> From 536ada39735112199080376a9e729d1c2081003a Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 25 Jan 2017 18:38:05 -0800 Subject: [PATCH 029/438] Introducing the TextureTable in the gpu shader system --- .../gpu-gl/src/gpu/gl/GLBackendShader.cpp | 15 +++- libraries/gpu/src/gpu/TextureTable.slh | 8 ++- .../render-utils/src/MaterialTextures.slh | 72 +++++++++++++++++++ 3 files changed, 90 insertions(+), 5 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp index 93c9b0d2ff..557591ffb6 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp @@ -50,6 +50,12 @@ static const std::string stereoVersion { #endif }; +// TextureTable specific defines +static const std::string textureTableVersion { + "#extension GL_ARB_bindless_texture : require\n#define GPU_TEXTURE_TABLE_BINDLESS\n" +}; + + // Versions specific of the shader static const std::array VERSION_DEFINES { { "", @@ -64,10 +70,15 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co Shader::CompilationLogs compilationLogs(GLShader::NumVersions); shader.incrementCompilationAttempt(); + bool supportTextureTableBindless = true; + for (int version = 0; version < GLShader::NumVersions; version++) { auto& shaderObject = shaderObjects[version]; - std::string shaderDefines = getBackendShaderHeader() + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version]; + std::string shaderDefines = getBackendShaderHeader() + "\n" + + (supportTextureTableBindless ? textureTableVersion : "\n") + + DOMAIN_DEFINES[shader.getType()] + "\n" + + VERSION_DEFINES[version]; if (handler) { bool retest = true; std::string currentSrc = shaderSource; @@ -89,7 +100,7 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co compilationLogs[version].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, compilationLogs[version].message); } - if (!compilationLogs[version].compiled) { + if (!compilationLogs[version].compiled) { qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << compilationLogs[version].message.c_str(); shader.setCompilationLogs(compilationLogs); return nullptr; diff --git a/libraries/gpu/src/gpu/TextureTable.slh b/libraries/gpu/src/gpu/TextureTable.slh index 1aa727c99d..7859057bdb 100644 --- a/libraries/gpu/src/gpu/TextureTable.slh +++ b/libraries/gpu/src/gpu/TextureTable.slh @@ -13,11 +13,13 @@ #ifdef GPU_TEXTURE_TABLE_BINDLESS #define GPU_TEXTURE_TABLE_MAX_NUM_TEXTURES 8 -struct TextureTable { - uvec4 _textures[GPU_TEXTURE_TABLE_MAX_NUM_TEXTURES]; +struct GPUTextureTable { + uvec2 _textures[GPU_TEXTURE_TABLE_MAX_NUM_TEXTURES]; }; -#define TextureTable(index) layout (std140) uniform gpu_textureTableBuffer##index { TextureTable _table##index; }; +#define TextureTable(index, name) layout (std140) uniform gpu_textureTableBuffer { GPUTextureTable name; } + +#define tableTex(name, slot) sampler2D(name._textures[slot]) #else diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh index 40709b3668..ef77954fad 100644 --- a/libraries/render-utils/src/MaterialTextures.slh +++ b/libraries/render-utils/src/MaterialTextures.slh @@ -48,6 +48,76 @@ TexMapArray getTexMapArray() { <@func declareMaterialTextures(withAlbedo, withRoughness, withNormal, withMetallic, withEmissive, withOcclusion, withScattering)@> + + +<@include gpu/TextureTable.slh@> + +TextureTable(0, matTex); + + +<@if withAlbedo@> +#define albedoMap 1 +vec4 fetchAlbedoMap(vec2 uv) { + return texture(tableTex(matTex, albedoMap), uv); +} +<@endif@> + +<@if withRoughness@> +#define roughnessMap 3 +float fetchRoughnessMap(vec2 uv) { + return (texture(tableTex(matTex, roughnessMap), uv).r); +} +<@endif@> + +<@if withNormal@> +#define normalMap 4 +vec3 fetchNormalMap(vec2 uv) { + return texture(tableTex(matTex, normalMap), uv).xyz; +} +<@endif@> + +<@if withMetallic@> +#define metallicMap 2 +float fetchMetallicMap(vec2 uv) { + return (texture(tableTex(matTex, metallicMap), uv).r); +} +<@endif@> + +<@if withEmissive@> +#define emissiveMap 0 +vec3 fetchEmissiveMap(vec2 uv) { + return texture(tableTex(matTex, emissiveMap), uv).rgb; +} +<@endif@> + +<@if withOcclusion@> +#define occlusionMap 5 +float fetchOcclusionMap(vec2 uv) { + return texture(tableTex(matTex, occlusionMap), uv).r; +} +<@endif@> + +<@if withScattering@> +#define scatteringMap 7 +float fetchScatteringMap(vec2 uv) { + float scattering = texture(tableTex(matTex, scatteringMap), uv).r; // boolean scattering for now + return max(((scattering - 0.1) / 0.9), 0.0); + return texture(tableTex(matTex, scatteringMap), uv).r; // boolean scattering for now +} +<@endif@> + + + <@if withAlbedo@> uniform sampler2D albedoMap; vec4 fetchAlbedoMap(vec2 uv) { @@ -101,6 +171,8 @@ float fetchScatteringMap(vec2 uv) { return texture(scatteringMap, uv).r; // boolean scattering for now } <@endif@> +<@endif@> +!> <@endfunc@> From 03aeb7addaa865a2448edd1a361a8e90d7b0b4a4 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 26 Jan 2017 11:04:46 -0800 Subject: [PATCH 030/438] Adding the ubo slot assignment for the resource texture table --- libraries/gpu-gl/src/gpu/gl/GLBackend.h | 6 + libraries/gpu-gl/src/gpu/gl/GLShader.cpp | 444 +++++++++++++++++- libraries/gpu-gl/src/gpu/gl/GLShader.h | 11 + libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 2 +- .../src/gpu/gl45/GL45BackendTexture.cpp | 82 ++++ libraries/gpu/src/gpu/Batch.h | 5 + libraries/gpu/src/gpu/TextureTable.slh | 6 +- .../render-utils/src/MeshPartPayload.cpp | 52 +- 8 files changed, 573 insertions(+), 35 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 18916ac18c..e13bfe8166 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -98,6 +98,12 @@ public: static const int MAX_NUM_RESOURCE_TEXTURES = 16; size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; } + // Texture Tables offers 2 dedicated slot (taken from the ubo slots) + static const int MAX_NUM_RESOURCE_TABLE_TEXTURES = 2; + static const int RESOURCE_TABLE_TEXTURE_SLOT_OFFSET = TRANSFORM_CAMERA_SLOT + 1; + size_t getMaxNumResourceTextureTables() const { return MAX_NUM_RESOURCE_TABLE_TEXTURES; } + + // Draw Stage virtual void do_draw(const Batch& batch, size_t paramOffset) = 0; virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0; diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.cpp b/libraries/gpu-gl/src/gpu/gl/GLShader.cpp index 010a7c479c..9c3de94957 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLShader.cpp @@ -10,6 +10,447 @@ #include "GLBackend.h" +namespace gpu { + namespace gl { + + class ElementResource { + public: + gpu::Element _element; + uint16 _resource; + + ElementResource(Element&& elem, uint16 resource) : _element(elem), _resource(resource) {} + }; + + ElementResource getFormatFromGLUniform(GLenum gltype) { + switch (gltype) { + case GL_FLOAT: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); + /* + case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); + */ + case GL_INT: return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC2: return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC3: return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC4: return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER); + + case GL_UNSIGNED_INT: return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER); +#if defined(Q_OS_WIN) + case GL_UNSIGNED_INT_VEC2: return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC3: return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC4: return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER); +#endif + + case GL_BOOL: return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC2: return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC3: return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC4: return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER); + + + case GL_FLOAT_MAT2: return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_MAT3: return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_MAT4: return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER); + + /* {GL_FLOAT_MAT2x3 mat2x3}, + {GL_FLOAT_MAT2x4 mat2x4}, + {GL_FLOAT_MAT3x2 mat3x2}, + {GL_FLOAT_MAT3x4 mat3x4}, + {GL_FLOAT_MAT4x2 mat4x2}, + {GL_FLOAT_MAT4x3 mat4x3}, + {GL_DOUBLE_MAT2 dmat2}, + {GL_DOUBLE_MAT3 dmat3}, + {GL_DOUBLE_MAT4 dmat4}, + {GL_DOUBLE_MAT2x3 dmat2x3}, + {GL_DOUBLE_MAT2x4 dmat2x4}, + {GL_DOUBLE_MAT3x2 dmat3x2}, + {GL_DOUBLE_MAT3x4 dmat3x4}, + {GL_DOUBLE_MAT4x2 dmat4x2}, + {GL_DOUBLE_MAT4x3 dmat4x3}, + */ + + case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D); + case GL_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D); + + case GL_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D); + case GL_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE); + +#if defined(Q_OS_WIN) + case GL_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); +#endif + + case GL_SAMPLER_2D_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D); +#if defined(Q_OS_WIN) + case GL_SAMPLER_CUBE_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE); + + case GL_SAMPLER_2D_ARRAY_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY); +#endif + + // {GL_SAMPLER_1D_SHADOW sampler1DShadow}, + // {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, + + // {GL_SAMPLER_BUFFER samplerBuffer}, + // {GL_SAMPLER_2D_RECT sampler2DRect}, + // {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, + +#if defined(Q_OS_WIN) + case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D); + case GL_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D); + case GL_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D); + case GL_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE); + + case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); + + // {GL_INT_SAMPLER_BUFFER isamplerBuffer}, + // {GL_INT_SAMPLER_2D_RECT isampler2DRect}, + + case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D); + case GL_UNSIGNED_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_UNSIGNED_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D); + case GL_UNSIGNED_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE); + + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); +#endif + // {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, + // {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, + /* + {GL_IMAGE_1D image1D}, + {GL_IMAGE_2D image2D}, + {GL_IMAGE_3D image3D}, + {GL_IMAGE_2D_RECT image2DRect}, + {GL_IMAGE_CUBE imageCube}, + {GL_IMAGE_BUFFER imageBuffer}, + {GL_IMAGE_1D_ARRAY image1DArray}, + {GL_IMAGE_2D_ARRAY image2DArray}, + {GL_IMAGE_2D_MULTISAMPLE image2DMS}, + {GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray}, + {GL_INT_IMAGE_1D iimage1D}, + {GL_INT_IMAGE_2D iimage2D}, + {GL_INT_IMAGE_3D iimage3D}, + {GL_INT_IMAGE_2D_RECT iimage2DRect}, + {GL_INT_IMAGE_CUBE iimageCube}, + {GL_INT_IMAGE_BUFFER iimageBuffer}, + {GL_INT_IMAGE_1D_ARRAY iimage1DArray}, + {GL_INT_IMAGE_2D_ARRAY iimage2DArray}, + {GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS}, + {GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray}, + {GL_UNSIGNED_INT_IMAGE_1D uimage1D}, + {GL_UNSIGNED_INT_IMAGE_2D uimage2D}, + {GL_UNSIGNED_INT_IMAGE_3D uimage3D}, + {GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect}, + {GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot + + {GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer}, + {GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray}, + {GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray}, + {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS}, + {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray}, + {GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint} + */ + default: + return ElementResource(Element(), Resource::BUFFER); + } + + }; + + int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, + Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& textureTables, Shader::SlotSet& samplers) { + GLint uniformsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); + + for (int i = 0; i < uniformsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + GLint location = glGetUniformLocation(glprogram, name); + const GLint INVALID_UNIFORM_LOCATION = -1; + + // Try to make sense of the gltype + auto elementResource = getFormatFromGLUniform(type); + + // The uniform as a standard var type + if (location != INVALID_UNIFORM_LOCATION) { + // Let's make sure the name doesn't contains an array element + std::string sname(name); + auto foundBracket = sname.find_first_of('['); + if (foundBracket != std::string::npos) { + // std::string arrayname = sname.substr(0, foundBracket); + + if (sname[foundBracket + 1] == '0') { + sname = sname.substr(0, foundBracket); + } else { + // skip this uniform since it's not the first element of an array + continue; + } + } + + if (elementResource._resource == Resource::BUFFER) { + uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource)); + } else { + // For texture/Sampler, the location is the actual binding value + GLint binding = -1; + glGetUniformiv(glprogram, location, &binding); + + auto requestedBinding = slotBindings.find(std::string(sname)); + if (requestedBinding != slotBindings.end()) { + if (binding != (*requestedBinding)._location) { + binding = (*requestedBinding)._location; + glProgramUniform1i(glprogram, location, binding); + } + } + + textures.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); + samplers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); + } + } + } + + return uniformsCount; + } + + const GLint UNUSED_SLOT = -1; + bool isUnusedSlot(GLint binding) { + return (binding == UNUSED_SLOT); + } + + int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) { + GLint buffersCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount); + + // fast exit + if (buffersCount == 0) { + return 0; + } + + GLint maxNumUniformBufferSlots = 0; + glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots); + std::vector uniformBufferSlotMap(maxNumUniformBufferSlots, -1); + + struct UniformBlockInfo { + using Vector = std::vector; + const GLuint index { 0 }; + const std::string name; + GLint binding { -1 }; + GLint size { 0 }; + + static std::string getName(GLuint glprogram, GLuint i) { + static const GLint NAME_LENGTH = 256; + GLint length = 0; + GLchar nameBuffer[NAME_LENGTH]; + glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); + glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, nameBuffer); + return std::string(nameBuffer); + } + + UniformBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) { + glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_BINDING, &binding); + glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size); + } + }; + + UniformBlockInfo::Vector uniformBlocks; + uniformBlocks.reserve(buffersCount); + for (int i = 0; i < buffersCount; i++) { + uniformBlocks.push_back(UniformBlockInfo(glprogram, i)); + } + + for (auto& info : uniformBlocks) { + auto requestedBinding = slotBindings.find(info.name); + if (requestedBinding != slotBindings.end()) { + info.binding = (*requestedBinding)._location; + glUniformBlockBinding(glprogram, info.index, info.binding); + uniformBufferSlotMap[info.binding] = info.index; + } + } + + for (auto& info : uniformBlocks) { + if (slotBindings.count(info.name)) { + continue; + } + + // If the binding is 0, or the binding maps to an already used binding + if (info.binding == 0 || uniformBufferSlotMap[info.binding] != UNUSED_SLOT) { + // If no binding was assigned then just do it finding a free slot + auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), isUnusedSlot); + if (slotIt != uniformBufferSlotMap.end()) { + info.binding = slotIt - uniformBufferSlotMap.begin(); + glUniformBlockBinding(glprogram, info.index, info.binding); + } else { + // This should neve happen, an active ubo cannot find an available slot among the max available?! + info.binding = -1; + } + } + + uniformBufferSlotMap[info.binding] = info.index; + } + + for (auto& info : uniformBlocks) { + static const Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER); + buffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size)); + } + return buffersCount; + } + + int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) { + GLint inputsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount); + + for (int i = 0; i < inputsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + + GLint binding = glGetAttribLocation(glprogram, name); + + auto elementResource = getFormatFromGLUniform(type); + inputs.insert(Shader::Slot(name, binding, elementResource._element, -1)); + } + + return inputsCount; + } + + int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) { + /* GLint outputsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount); + + for (int i = 0; i < inputsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + + auto element = getFormatFromGLUniform(type); + outputs.insert(Shader::Slot(name, i, element)); + } + */ + return 0; //inputsCount; + } + + void makeProgramBindings(ShaderObject& shaderObject) { + if (!shaderObject.glprogram) { + return; + } + GLuint glprogram = shaderObject.glprogram; + GLint loc = -1; + + //Check for gpu specific attribute slotBindings + loc = glGetAttribLocation(glprogram, "inPosition"); + if (loc >= 0 && loc != gpu::Stream::POSITION) { + glBindAttribLocation(glprogram, gpu::Stream::POSITION, "inPosition"); + } + + loc = glGetAttribLocation(glprogram, "inNormal"); + if (loc >= 0 && loc != gpu::Stream::NORMAL) { + glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "inNormal"); + } + + loc = glGetAttribLocation(glprogram, "inColor"); + if (loc >= 0 && loc != gpu::Stream::COLOR) { + glBindAttribLocation(glprogram, gpu::Stream::COLOR, "inColor"); + } + + loc = glGetAttribLocation(glprogram, "inTexCoord0"); + if (loc >= 0 && loc != gpu::Stream::TEXCOORD) { + glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "inTexCoord0"); + } + + loc = glGetAttribLocation(glprogram, "inTangent"); + if (loc >= 0 && loc != gpu::Stream::TANGENT) { + glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "inTangent"); + } + + loc = glGetAttribLocation(glprogram, "inTexCoord1"); + if (loc >= 0 && loc != gpu::Stream::TEXCOORD1) { + glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD1, "inTexCoord1"); + } + + loc = glGetAttribLocation(glprogram, "inSkinClusterIndex"); + if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_INDEX) { + glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "inSkinClusterIndex"); + } + + loc = glGetAttribLocation(glprogram, "inSkinClusterWeight"); + if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_WEIGHT) { + glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight"); + } + + loc = glGetAttribLocation(glprogram, "_drawCallInfo"); + if (loc >= 0 && loc != gpu::Stream::DRAW_CALL_INFO) { + glBindAttribLocation(glprogram, gpu::Stream::DRAW_CALL_INFO, "_drawCallInfo"); + } + + // Link again to take into account the assigned attrib location + glLinkProgram(glprogram); + + GLint linked = 0; + glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); + if (!linked) { + qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?"; + } + + // now assign the ubo binding, then DON't relink! + + //Check for gpu specific uniform slotBindings +#ifdef GPU_SSBO_DRAW_CALL_INFO + loc = glGetProgramResourceIndex(glprogram, GL_SHADER_STORAGE_BLOCK, "transformObjectBuffer"); + if (loc >= 0) { + glShaderStorageBlockBinding(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT); + shaderObject.transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT; + } +#else + loc = glGetUniformLocation(glprogram, "transformObjectBuffer"); + if (loc >= 0) { + glProgramUniform1i(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT); + shaderObject.transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT; + } +#endif + + loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer"); + if (loc >= 0) { + glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT); + shaderObject.transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT; + } + + loc = glGetUniformBlockIndex(glprogram, "gpu_resourceTextureTable0"); + if (loc >= 0) { + glUniformBlockBinding(glprogram, loc, GLBackend::RESOURCE_TABLE_TEXTURE_SLOT_OFFSET); + shaderObject.resourceTextureTableSlot0 = GLBackend::RESOURCE_TABLE_TEXTURE_SLOT_OFFSET; + } + loc = glGetUniformBlockIndex(glprogram, "gpu_resourceTextureTable1"); + if (loc >= 0) { + glUniformBlockBinding(glprogram, loc, GLBackend::RESOURCE_TABLE_TEXTURE_SLOT_OFFSET + 1); + shaderObject.resourceTextureTableSlot1 = GLBackend::RESOURCE_TABLE_TEXTURE_SLOT_OFFSET + 1; + } + (void)CHECK_GL_ERROR(); + } + } +} + + using namespace gpu; using namespace gpu::gl; @@ -73,8 +514,9 @@ bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::Bin Shader::SlotSet uniforms; Shader::SlotSet textures; + Shader::SlotSet textureTables; Shader::SlotSet samplers; - backend.makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers); + backend.makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, textureTables, samplers); Shader::SlotSet resourceBuffers; backend.makeResourceBufferSlots(shaderObject.glprogram, slotBindings, resourceBuffers); diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.h b/libraries/gpu-gl/src/gpu/gl/GLShader.h index 3259982e93..a5a6a6e72c 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShader.h +++ b/libraries/gpu-gl/src/gpu/gl/GLShader.h @@ -17,8 +17,19 @@ struct ShaderObject { GLuint glprogram { 0 }; GLint transformCameraSlot { -1 }; GLint transformObjectSlot { -1 }; + + GLint resourceTextureTableSlot0 { -1 }; + GLint resourceTextureTableSlot1 { -1 }; }; +int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, + Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& textureTables, Shader::SlotSet& samplers); +int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers); +int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs); +int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs); +void makeProgramBindings(ShaderObject& shaderObject); + + class GLShader : public GPUObject { public: static GLShader* sync(GLBackend& backend, const Shader& shader, const Shader::CompilationHandler& handler = nullptr); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 1a4b63d35f..098ac11763 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -103,7 +103,7 @@ public: friend class GL45Backend; using PromoteLambda = std::function; - + const uvec4& getHandle(); protected: GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); ~GL45VariableAllocationTexture(); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 1d415c7ca3..45900a62b6 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -212,6 +212,7 @@ void GL45Texture::syncSampler() const { glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, sampler.getMinMip()); glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); + (void)CHECK_GL_ERROR(); } using GL45FixedAllocationTexture = GL45Backend::GL45FixedAllocationTexture; @@ -275,6 +276,10 @@ GL45StrictResourceTexture::GL45StrictResourceTexture(const std::weak_ptr& backend, const TextureTable& textureTable, const HandlesArray& handles, bool handlesComplete) + : Parent(backend, textureTable, allocate()), _stamp(textureTable.getStamp()), _handles(handles), _complete(handlesComplete) { + Backend::setGPUObject(textureTable, this); + // FIXME include these in overall buffer storage reporting + glNamedBufferStorage(_id, sizeof(uvec4) * TextureTable::COUNT, &_handles[0], 0); +} + + +GL45TextureTable::~GL45TextureTable() { + if (_id) { + auto backend = _backend.lock(); + if (backend) { + // FIXME include these in overall buffer storage reporting + backend->releaseBuffer(_id, 0); + } + } +} + + +GL45TextureTable* GL45Backend::syncGPUObject(const TextureTablePointer& textureTablePointer) { + const auto& textureTable = *textureTablePointer; + + // Find the target handles + auto textures = textureTable.getTextures(); + bool handlesComplete = true; + GL45TextureTable::HandlesArray handles{}; + for (size_t i = 0; i < textures.size(); ++i) { + auto texture = textures[i]; + if (!texture) { + continue; + } + // FIXME what if we have a non-transferrable texture here? + auto gltexture = (GL45Texture*)syncGPUObject(texture, true); + if (!gltexture) { + handlesComplete = false; + continue; + } + auto handle = gltexture->getHandle(); + memcpy(&handles[i], &handle, sizeof(handle)); + } + + // If the object hasn't been created, or the object definition is out of date, drop and re-create + GL45TextureTable* object = Backend::getGPUObject(textureTable); + + if (!object || object->_stamp != textureTable.getStamp() || !object->_complete || handles != object->_handles) { + object = new GL45TextureTable(shared_from_this(), textureTable, handles, handlesComplete); + } + + return object; +} + +void GL45Backend::do_setResourceTextureTable(const Batch& batch, size_t paramOffset) { + auto textureTable = batch._textureTables.get(batch._params[paramOffset]._uint); + auto slot = batch._params[paramOffset + 1]._uint; + GL45TextureTable* glTextureTable = syncGPUObject(textureTable); + if (glTextureTable) { + glBindBufferBase(GL_UNIFORM_BUFFER, slot + GLBackend::RESOURCE_TABLE_TEXTURE_SLOT_OFFSET, glTextureTable->_id); + } +} diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 79f08ea5cc..a57ee94747 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -30,6 +30,11 @@ class QDebug; namespace gpu { enum ReservedSlot { +#ifdef GPU_SSBO_DRAW_CALL_INFO + TRANSFORM_OBJECT_SLOT = 14, +#else + TRANSFORM_OBJECT_SLOT = 31, +#endif TRANSFORM_CAMERA_SLOT = 15, }; diff --git a/libraries/gpu/src/gpu/TextureTable.slh b/libraries/gpu/src/gpu/TextureTable.slh index 7859057bdb..d45c8f819b 100644 --- a/libraries/gpu/src/gpu/TextureTable.slh +++ b/libraries/gpu/src/gpu/TextureTable.slh @@ -14,12 +14,12 @@ #define GPU_TEXTURE_TABLE_MAX_NUM_TEXTURES 8 struct GPUTextureTable { - uvec2 _textures[GPU_TEXTURE_TABLE_MAX_NUM_TEXTURES]; + uvec4 _textures[GPU_TEXTURE_TABLE_MAX_NUM_TEXTURES]; }; -#define TextureTable(index, name) layout (std140) uniform gpu_textureTableBuffer { GPUTextureTable name; } +#define TextureTable(index, name) layout (std140) uniform gpu_resourceTextureTable##index { GPUTextureTable name; } -#define tableTex(name, slot) sampler2D(name._textures[slot]) +#define tableTex(name, slot) sampler2D(name._textures[slot].xy) #else diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 9655b60a78..2428ec95aa 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -145,6 +145,17 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat } auto textureCache = DependencyManager::get(); + if (!_drawMaterialTextures) { + _drawMaterialTextures = std::make_shared(); + _drawMaterialTextures->setTexture(model::MaterialKey::ALBEDO_MAP, textureCache->getWhiteTexture()); + _drawMaterialTextures->setTexture(model::MaterialKey::ROUGHNESS_MAP, textureCache->getWhiteTexture()); + _drawMaterialTextures->setTexture(model::MaterialKey::NORMAL_MAP, textureCache->getBlueTexture()); + _drawMaterialTextures->setTexture(model::MaterialKey::METALLIC_MAP, textureCache->getBlackTexture()); + _drawMaterialTextures->setTexture(model::MaterialKey::OCCLUSION_MAP, textureCache->getWhiteTexture()); + _drawMaterialTextures->setTexture(model::MaterialKey::SCATTERING_MAP, textureCache->getWhiteTexture()); + _drawMaterialTextures->setTexture(model::MaterialKey::EMISSIVE_MAP, textureCache->getBlackTexture()); + _drawMaterialTextures->setTexture(model::MaterialKey::LIGHTMAP_MAP, textureCache->getGrayTexture()); + } batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::MATERIAL, _drawMaterial->getSchemaBuffer()); batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::TEXMAPARRAY, _drawMaterial->getTexMapArrayBuffer()); @@ -172,9 +183,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat if (materialKey.isAlbedoMap()) { auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, itr->second->getTextureView()); - } else { - batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, textureCache->getGrayTexture()); + _drawMaterialTextures->setTexture(model::MaterialKey::ALBEDO_MAP, itr->second->getTextureView()); } } @@ -182,11 +191,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat if (materialKey.isRoughnessMap()) { auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, itr->second->getTextureView()); - - // texcoord are assumed to be the same has albedo - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, textureCache->getWhiteTexture()); + _drawMaterialTextures->setTexture(model::MaterialKey::ROUGHNESS_MAP, itr->second->getTextureView()); } } @@ -194,11 +199,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat if (materialKey.isNormalMap()) { auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, itr->second->getTextureView()); - - // texcoord are assumed to be the same has albedo - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, textureCache->getBlueTexture()); + _drawMaterialTextures->setTexture(model::MaterialKey::NORMAL_MAP, itr->second->getTextureView()); } } @@ -206,11 +207,11 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat if (materialKey.isMetallicMap()) { auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, itr->second->getTextureView()); + _drawMaterialTextures->setTexture(model::MaterialKey::METALLIC_MAP, itr->second->getTextureView()); // texcoord are assumed to be the same has albedo } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, textureCache->getBlackTexture()); + _drawMaterialTextures->setTexture(model::MaterialKey::METALLIC_MAP, textureCache->getBlackTexture()); } } @@ -218,11 +219,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat if (materialKey.isOcclusionMap()) { auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, itr->second->getTextureView()); - - // texcoord are assumed to be the same has albedo - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, textureCache->getWhiteTexture()); + _drawMaterialTextures->setTexture(model::MaterialKey::OCCLUSION_MAP, itr->second->getTextureView()); } } @@ -230,11 +227,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat if (materialKey.isScatteringMap()) { auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, itr->second->getTextureView()); - - // texcoord are assumed to be the same has albedo - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, textureCache->getWhiteTexture()); + _drawMaterialTextures->setTexture(model::MaterialKey::SCATTERING_MAP, itr->second->getTextureView()); } } @@ -243,17 +236,16 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat auto itr = textureMaps.find(graphics::MaterialKey::LIGHTMAP_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, itr->second->getTextureView()); + _drawMaterialTextures->setTexture(model::MaterialKey::LIGHTMAP_MAP, itr->second->getTextureView()); } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, textureCache->getGrayTexture()); + _drawMaterialTextures->setTexture(model::MaterialKey::LIGHTMAP_MAP, textureCache->getGrayTexture()); } } else if (materialKey.isEmissiveMap()) { auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); - if (itr != textureMaps.end() && itr->second->isDefined()) { - batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, itr->second->getTextureView()); + _drawMaterialTextures->setTexture(model::MaterialKey::EMISSIVE_MAP, itr->second->getTextureView()); } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, textureCache->getBlackTexture()); + _drawMaterialTextures->setTexture(model::MaterialKey::EMISSIVE_MAP, textureCache->getBlackTexture()); } } } From ab5355dbdd91ad8e8e0c5e2c8a803fb79819e610 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Wed, 25 Jan 2017 17:47:33 -0800 Subject: [PATCH 031/438] First pass at bindless textures --- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 1 + libraries/gpu-gl/src/gpu/gl/GLBackend.h | 1 + libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 3 ++ .../src/gpu/gl41/GL41BackendTexture.cpp | 3 ++ libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 21 ++++++++ .../src/gpu/gl45/GL45BackendTexture.cpp | 3 +- libraries/gpu/src/gpu/Batch.cpp | 8 +++ libraries/gpu/src/gpu/Batch.h | 4 ++ libraries/gpu/src/gpu/Forward.h | 2 + libraries/gpu/src/gpu/TextureTable.cpp | 48 ++++++++++++++++++ libraries/gpu/src/gpu/TextureTable.h | 42 ++++++++++++++++ .../render-utils/src/MeshPartPayload.cpp | 49 +++++++++---------- libraries/render-utils/src/MeshPartPayload.h | 1 + 13 files changed, 160 insertions(+), 26 deletions(-) create mode 100644 libraries/gpu/src/gpu/TextureTable.cpp create mode 100644 libraries/gpu/src/gpu/TextureTable.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 08bd20be66..3a4c7b4f47 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -99,6 +99,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_setUniformBuffer), (&::gpu::gl::GLBackend::do_setResourceBuffer), (&::gpu::gl::GLBackend::do_setResourceTexture), + (&::gpu::gl::GLBackend::do_setResourceTextureTable), (&::gpu::gl::GLBackend::do_setFramebuffer), (&::gpu::gl::GLBackend::do_clearFramebuffer), diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index e13bfe8166..35aabef48f 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -132,6 +132,7 @@ public: // Resource Stage virtual void do_setResourceBuffer(const Batch& batch, size_t paramOffset) final; virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final; + virtual void do_setResourceTextureTable(const Batch& batch, size_t paramOffset) = 0; // Pipeline Stage virtual void do_setPipeline(const Batch& batch, size_t paramOffset) final; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 42926fdb1c..1b7882518d 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -157,6 +157,9 @@ protected: void resetInputStage() override; void updateInput() override; + // Resource stage + void do_setResourceTextureTable(const Batch& batch, size_t paramOffset) override; + // Synchronize the state cache of this Backend with the actual real state of the GL Context void transferTransformState(const Batch& batch) const override; void initTransform() override; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 925e83bb77..b70730fd98 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -611,3 +611,6 @@ GL41ResourceTexture::GL41ResourceTexture(const std::weak_ptr& backend GL41ResourceTexture::~GL41ResourceTexture() { } +void GL41Backend::do_setResourceTextureTable(const Batch& batch, size_t paramOffset) { + +} diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 098ac11763..4fb12fbdb1 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -15,6 +15,7 @@ #include "../gl/GLBackend.h" #include "../gl/GLTexture.h" #include +#include #define INCREMENTAL_TRANSFER 0 #define GPU_SSBO_TRANSFORM_OBJECT 1 @@ -48,6 +49,21 @@ public: static const std::string GL45_VERSION; const std::string& getVersion() const override { return GL45_VERSION; } + class GL45TextureTable : public GLObject { + static GLuint allocate(); + using Parent = GLObject; + public: + using HandlesArray = std::array; + GL45TextureTable(const std::weak_ptr& backend, const TextureTable& texture, const HandlesArray& newHandles, bool complete); + ~GL45TextureTable(); + + // FIXME instead of making a buffer for each table, there should be a global buffer of all materials + // and we should store an offset into that buffer + const uint32_t _stamp { 0 }; + const HandlesArray _handles; + const bool _complete { false }; + }; + class GL45Texture : public GLTexture { using Parent = GLTexture; friend class GL45Backend; @@ -181,6 +197,8 @@ protected: GLuint getQueryID(const QueryPointer& query) override; GLQuery* syncGPUObject(const Query& query) override; + GL45TextureTable* syncGPUObject(const TextureTablePointer& textureTable); + // Draw Stage void do_draw(const Batch& batch, size_t paramOffset) override; void do_drawIndexed(const Batch& batch, size_t paramOffset) override; @@ -193,6 +211,9 @@ protected: void resetInputStage() override; void updateInput() override; + // Resource stage + void do_setResourceTextureTable(const Batch& batch, size_t paramOffset) override; + // Synchronize the state cache of this Backend with the actual real state of the GL Context void transferTransformState(const Batch& batch) const override; void initTransform() override; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 45900a62b6..e3c10928b4 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -20,15 +20,16 @@ #include #include +#include #include "../gl/GLTexelFormat.h" using namespace gpu; using namespace gpu::gl; using namespace gpu::gl45; -#define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f #define MAX_RESOURCE_TEXTURES_PER_FRAME 2 + GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { if (!texturePointer) { return nullptr; diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index c432e19368..3764a94494 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -59,6 +59,7 @@ Batch::Batch(const Batch& batch_) { _buffers._items.swap(batch._buffers._items); _textures._items.swap(batch._textures._items); + _textureTables._items.swap(batch._textureTables._items); _streamFormats._items.swap(batch._streamFormats._items); _transforms._items.swap(batch._transforms._items); _pipelines._items.swap(batch._pipelines._items); @@ -96,6 +97,7 @@ void Batch::clear() { _data.clear(); _buffers.clear(); _textures.clear(); + _textureTables.clear(); _streamFormats.clear(); _transforms.clear(); _pipelines.clear(); @@ -306,6 +308,12 @@ void Batch::setResourceTexture(uint32 slot, const TexturePointer& texture) { _params.emplace_back(slot); } +void Batch::setResourceTextureTable(const TextureTablePointer& textureTable, uint32 slot) { + ADD_COMMAND(setResourceTextureTable); + _params.emplace_back(_textureTables.cache(textureTable)); + _params.emplace_back(slot); +} + void Batch::setResourceTexture(uint32 slot, const TextureView& view) { setResourceTexture(slot, view._texture); } diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index a57ee94747..8eb769a6fe 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -193,6 +193,7 @@ public: void setResourceTexture(uint32 slot, const TexturePointer& texture); void setResourceTexture(uint32 slot, const TextureView& view); // not a command, just a shortcut from a TextureView + void setResourceTextureTable(const TextureTablePointer& table, uint32 slot = 0); // Ouput Stage void setFramebuffer(const FramebufferPointer& framebuffer); @@ -304,6 +305,7 @@ public: COMMAND_setUniformBuffer, COMMAND_setResourceBuffer, COMMAND_setResourceTexture, + COMMAND_setResourceTextureTable, COMMAND_setFramebuffer, COMMAND_clearFramebuffer, @@ -422,6 +424,7 @@ public: typedef Cache::Vector BufferCaches; typedef Cache::Vector TextureCaches; + typedef Cache::Vector TextureTableCaches; typedef Cache::Vector StreamFormatCaches; typedef Cache::Vector TransformCaches; typedef Cache::Vector PipelineCaches; @@ -476,6 +479,7 @@ public: BufferCaches _buffers; TextureCaches _textures; + TextureTableCaches _textureTables; StreamFormatCaches _streamFormats; TransformCaches _transforms; PipelineCaches _pipelines; diff --git a/libraries/gpu/src/gpu/Forward.h b/libraries/gpu/src/gpu/Forward.h index 8100efafaf..987788c29b 100644 --- a/libraries/gpu/src/gpu/Forward.h +++ b/libraries/gpu/src/gpu/Forward.h @@ -91,6 +91,8 @@ namespace gpu { using Textures = std::vector; class TextureView; using TextureViews = std::vector; + class TextureTable; + using TextureTablePointer = std::shared_ptr; struct StereoState { bool isStereo() const { diff --git a/libraries/gpu/src/gpu/TextureTable.cpp b/libraries/gpu/src/gpu/TextureTable.cpp new file mode 100644 index 0000000000..17a9e87f3e --- /dev/null +++ b/libraries/gpu/src/gpu/TextureTable.cpp @@ -0,0 +1,48 @@ +// +// Created by Bradley Austin Davis on 2017/01/25 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "TextureTable.h" +#include "Texture.h" + +using namespace gpu; + +TextureTable::TextureTable() { } + +TextureTable::TextureTable(const std::initializer_list& textures) { + auto max = std::min(COUNT, textures.size()); + auto itr = textures.begin(); + size_t index = 0; + while (itr != textures.end() && index < max) { + setTexture(index, *itr); + ++index; + } +} + +TextureTable::TextureTable(const std::array& textures) : _stamp(1), _textures(textures) { +} + +void TextureTable::setTexture(size_t index, const TexturePointer& texturePointer) { + if (index >= COUNT || _textures[index] == texturePointer) { + return; + } + { + Lock lock(_mutex); + ++_stamp; + _textures[index] = texturePointer; + } +} + +void TextureTable::setTexture(size_t index, const TextureView& textureView) { + setTexture(index, textureView._texture); +} + +TextureTable::Array TextureTable::getTextures() const { + Array result; + Lock lock(_mutex); + result = _textures; + return result; +} diff --git a/libraries/gpu/src/gpu/TextureTable.h b/libraries/gpu/src/gpu/TextureTable.h new file mode 100644 index 0000000000..7b278bf304 --- /dev/null +++ b/libraries/gpu/src/gpu/TextureTable.h @@ -0,0 +1,42 @@ +// +// Created by Bradley Austin Davis on 2017/01/25 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_TextureTable_h +#define hifi_gpu_TextureTable_h + +#include "Forward.h" + +#include + +namespace gpu { + +class TextureTable { +public: + static const size_t COUNT = 8; + using Array = std::array; + using Array = std::array; + TextureTable(); + TextureTable(const std::initializer_list& textures); + TextureTable(const std::array& textures); + + // Only for gpu::Context + const GPUObjectPointer gpuObject{}; + + void setTexture(size_t index, const TexturePointer& texturePointer); + void setTexture(size_t index, const TextureView& texturePointer); + + Array getTextures() const; + Stamp getStamp() const { return _stamp; } +private: + mutable Mutex _mutex; + Array _textures; + Stamp _stamp{ 0 }; +}; + +} + +#endif diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 2428ec95aa..181eb19c8a 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -14,6 +14,8 @@ #include #include +#include + #include "DeferredLightingEffect.h" using namespace render; @@ -144,24 +146,25 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat return; } + + const auto& textureMaps = _drawMaterial->getTextureMaps(); + const auto& materialKey = _drawMaterial->getKey(); auto textureCache = DependencyManager::get(); if (!_drawMaterialTextures) { _drawMaterialTextures = std::make_shared(); - _drawMaterialTextures->setTexture(model::MaterialKey::ALBEDO_MAP, textureCache->getWhiteTexture()); - _drawMaterialTextures->setTexture(model::MaterialKey::ROUGHNESS_MAP, textureCache->getWhiteTexture()); - _drawMaterialTextures->setTexture(model::MaterialKey::NORMAL_MAP, textureCache->getBlueTexture()); - _drawMaterialTextures->setTexture(model::MaterialKey::METALLIC_MAP, textureCache->getBlackTexture()); - _drawMaterialTextures->setTexture(model::MaterialKey::OCCLUSION_MAP, textureCache->getWhiteTexture()); - _drawMaterialTextures->setTexture(model::MaterialKey::SCATTERING_MAP, textureCache->getWhiteTexture()); - _drawMaterialTextures->setTexture(model::MaterialKey::EMISSIVE_MAP, textureCache->getBlackTexture()); - _drawMaterialTextures->setTexture(model::MaterialKey::LIGHTMAP_MAP, textureCache->getGrayTexture()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::ALBEDO_MAP, textureCache->getWhiteTexture()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::ROUGHNESS_MAP, textureCache->getWhiteTexture()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::NORMAL_MAP, textureCache->getBlueTexture()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::METALLIC_MAP, textureCache->getBlackTexture()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::OCCLUSION_MAP, textureCache->getWhiteTexture()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::SCATTERING_MAP, textureCache->getWhiteTexture()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::EMISSIVE_MAP, textureCache->getBlackTexture()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::LIGHTMAP_MAP, textureCache->getGrayTexture()); } batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::MATERIAL, _drawMaterial->getSchemaBuffer()); batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::TEXMAPARRAY, _drawMaterial->getTexMapArrayBuffer()); - - const auto& materialKey = _drawMaterial->getKey(); - const auto& textureMaps = _drawMaterial->getTextureMaps(); + batch.setResourceTextureTable(_drawMaterialTextures); int numUnlit = 0; if (materialKey.isUnlit()) { @@ -183,7 +186,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat if (materialKey.isAlbedoMap()) { auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { - _drawMaterialTextures->setTexture(model::MaterialKey::ALBEDO_MAP, itr->second->getTextureView()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::ALBEDO_MAP, itr->second->getTextureView()); } } @@ -191,7 +194,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat if (materialKey.isRoughnessMap()) { auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { - _drawMaterialTextures->setTexture(model::MaterialKey::ROUGHNESS_MAP, itr->second->getTextureView()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::ROUGHNESS_MAP, itr->second->getTextureView()); } } @@ -199,7 +202,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat if (materialKey.isNormalMap()) { auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { - _drawMaterialTextures->setTexture(model::MaterialKey::NORMAL_MAP, itr->second->getTextureView()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::NORMAL_MAP, itr->second->getTextureView()); } } @@ -207,11 +210,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat if (materialKey.isMetallicMap()) { auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { - _drawMaterialTextures->setTexture(model::MaterialKey::METALLIC_MAP, itr->second->getTextureView()); - - // texcoord are assumed to be the same has albedo - } else { - _drawMaterialTextures->setTexture(model::MaterialKey::METALLIC_MAP, textureCache->getBlackTexture()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::METALLIC_MAP, itr->second->getTextureView()); } } @@ -219,7 +218,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat if (materialKey.isOcclusionMap()) { auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { - _drawMaterialTextures->setTexture(model::MaterialKey::OCCLUSION_MAP, itr->second->getTextureView()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::OCCLUSION_MAP, itr->second->getTextureView()); } } @@ -227,7 +226,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat if (materialKey.isScatteringMap()) { auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { - _drawMaterialTextures->setTexture(model::MaterialKey::SCATTERING_MAP, itr->second->getTextureView()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::SCATTERING_MAP, itr->second->getTextureView()); } } @@ -236,16 +235,16 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat auto itr = textureMaps.find(graphics::MaterialKey::LIGHTMAP_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { - _drawMaterialTextures->setTexture(model::MaterialKey::LIGHTMAP_MAP, itr->second->getTextureView()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::LIGHTMAP_MAP, itr->second->getTextureView()); } else { - _drawMaterialTextures->setTexture(model::MaterialKey::LIGHTMAP_MAP, textureCache->getGrayTexture()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::LIGHTMAP_MAP, textureCache->getGrayTexture()); } } else if (materialKey.isEmissiveMap()) { auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { - _drawMaterialTextures->setTexture(model::MaterialKey::EMISSIVE_MAP, itr->second->getTextureView()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::EMISSIVE_MAP, itr->second->getTextureView()); } else { - _drawMaterialTextures->setTexture(model::MaterialKey::EMISSIVE_MAP, textureCache->getBlackTexture()); + _drawMaterialTextures->setTexture(graphics::MaterialKey::EMISSIVE_MAP, textureCache->getBlackTexture()); } } } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 21f9dc2e68..51bc7f39e0 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -67,6 +67,7 @@ public: std::shared_ptr _drawMaterial; graphics::Mesh::Part _drawPart; + mutable gpu::TextureTablePointer _drawMaterialTextures; size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; } size_t getMaterialTextureSize() { return _drawMaterial ? _drawMaterial->getTextureSize() : 0; } From 7d7c3bb1f57480acfbb7cd8ec6cf80332caa891f Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 22 Feb 2018 10:30:42 +0100 Subject: [PATCH 032/438] Change new material dialog to QQC2 --- interface/resources/qml/hifi/tablet/NewMaterialDialog.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml b/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml index 226c17e8e2..e49d78193b 100644 --- a/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml +++ b/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import QtQuick.Dialogs 1.2 as OriginalDialogs import "../../styles-uit" From 931696322f192080ecdc92fc0c90bf6523caf7b7 Mon Sep 17 00:00:00 2001 From: vladest Date: Sun, 25 Feb 2018 08:58:05 +0100 Subject: [PATCH 033/438] Fixed stucked mouse pointer in QQC2 toolbar mode --- interface/resources/qml/windows/ScrollingWindow.qml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/windows/ScrollingWindow.qml b/interface/resources/qml/windows/ScrollingWindow.qml index 1966380709..8e71064195 100644 --- a/interface/resources/qml/windows/ScrollingWindow.qml +++ b/interface/resources/qml/windows/ScrollingWindow.qml @@ -38,14 +38,14 @@ Window { property bool keyboardRaised: false property bool punctuationMode: false + // Scrollable window content. // FIXME this should not define any visual content in this type. The base window // type should only consist of logic sized areas, with nothing drawn (although the // default value for the frame property does include visual decorations) property var pane: Item { - property bool isScrolling: scrollView.contentChildren.length > 0 ? - (scrollView.height < scrollView.contentChildren[0].height) : - false + property bool isScrolling: scrollView.height < scrollView.contentItem.height + property int contentWidth: scrollView.width - (isScrolling ? 10 : 0) property int scrollHeight: scrollView.height @@ -60,7 +60,6 @@ Window { visible: !window.hideBackground && modality != Qt.ApplicationModal } - LinearGradient { visible: !window.hideBackground && gradientsSupported && modality != Qt.ApplicationModal anchors.top: contentBackground.bottom @@ -78,9 +77,9 @@ Window { ScrollView { id: scrollView - contentChildren: content - clip: true + contentItem: content ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + anchors.fill: parent anchors.rightMargin: parent.isScrolling ? 1 : 0 anchors.bottomMargin: footerPane.height @@ -120,8 +119,6 @@ Window { } } } - - } function scrollBy(delta) { From b7f146fd473363507ab36c33c1f2c28d42b1ba19 Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 27 Feb 2018 16:27:08 +0100 Subject: [PATCH 034/438] Fix clipping issues in scrolling window --- interface/resources/qml/windows/ScrollingWindow.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/windows/ScrollingWindow.qml b/interface/resources/qml/windows/ScrollingWindow.qml index 8e71064195..94cab7fd29 100644 --- a/interface/resources/qml/windows/ScrollingWindow.qml +++ b/interface/resources/qml/windows/ScrollingWindow.qml @@ -79,6 +79,7 @@ Window { id: scrollView contentItem: content ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + clip: true anchors.fill: parent anchors.rightMargin: parent.isScrolling ? 1 : 0 From 90b891dbc3d4db70ba5950f39b56100310a6ae06 Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 27 Feb 2018 16:42:30 +0100 Subject: [PATCH 035/438] Fix push issue due to merge errors --- interface/resources/qml/hifi/tablet/Edit.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/Edit.qml b/interface/resources/qml/hifi/tablet/Edit.qml index 3b440e9e22..8e362b8b78 100644 --- a/interface/resources/qml/hifi/tablet/Edit.qml +++ b/interface/resources/qml/hifi/tablet/Edit.qml @@ -22,8 +22,7 @@ ColumnLayout { signal sendToScript(var message); function pushSource(path) { - editRoot.push(Qt.resolvedUrl("../../" + path)); - editStack.push(Qt.resolvedUrl(path)); + editStack.push(Qt.resolvedUrl("../../" + path)); editStack.currentItem.sendToScript.connect(editRoot.sendToScript); } From 7cbdbe9ab01b98a1a674bee1c439ef5b56accd8e Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 1 Mar 2018 11:02:05 +0100 Subject: [PATCH 036/438] Make Edit view more consistant with original one --- interface/resources/qml/hifi/tablet/Edit.qml | 299 +---------- .../qml/hifi/tablet/EditTabButton.qml | 9 +- .../resources/qml/hifi/tablet/EditTabView.qml | 480 ++++++++---------- .../hifi/tablet/TabletGraphicsPreferences.qml | 4 +- 4 files changed, 259 insertions(+), 533 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/Edit.qml b/interface/resources/qml/hifi/tablet/Edit.qml index 8e362b8b78..28d6094194 100644 --- a/interface/resources/qml/hifi/tablet/Edit.qml +++ b/interface/resources/qml/hifi/tablet/Edit.qml @@ -6,289 +6,42 @@ import "../../controls-uit" as HifiControls import "../../controls" import "../toolbars" -ColumnLayout { +StackView { id: editRoot objectName: "stack" - anchors.fill: parent + //anchors.fill: parent + topPadding: 40 - readonly property var webTabsLinks: [ - "", - "/system/html/entityList.html", - "/system/html/entityProperties.html", - "/system/html/gridControls.html", - "/system/particle_explorer/particleExplorer.html" - ] + property var itemProperties: {"y": editRoot.topPadding, + "width": editRoot.availableWidth, + "height": editRoot.availableHeight } + Component.onCompleted: { + tab.currentIndex = 0 + } + + background: Rectangle { + color: "#404040" //default background color + EditTabView { + id: tab + currentIndex: -1 + onCurrentIndexChanged: { + editRoot.replace(null, tab.itemAt(currentIndex).visualItem, + itemProperties, + StackView.Immediate) + } + } + } signal sendToScript(var message); function pushSource(path) { - editStack.push(Qt.resolvedUrl("../../" + path)); - editStack.currentItem.sendToScript.connect(editRoot.sendToScript); + editRoot.push(Qt.resolvedUrl("../../" + path), itemProperties, + StackView.Immediate); + editRoot.currentItem.sendToScript.connect(editRoot.sendToScript); } function popSource() { - editStack.pop(); - } - - function fromScript(message) { - console.error("from script", JSON.stringify(message)) - switch (message.method) { - case 'selectTab': - selectTab(message.params.id); - break; - default: - var currentItem = editStack.currentItem; - if (currentItem && currentItem.fromScript) { - currentItem.fromScript(message); - } else { - console.warn('Unrecognized message:', JSON.stringify(message)); - } - } - } - - // Changes the current tab based on tab index or title as input - function selectTab(id) { - console.error("selecting tab", id) - if (typeof id === 'number') { - if (id >= 0 && id <= 4) { - editTabView.currentIndex = id; - } else { - console.warn('Attempt to switch to invalid tab:', id); - } - } else if (typeof id === 'string'){ - switch (id.toLowerCase()) { - case 'create': - editTabView.currentIndex = 0; - break; - case 'list': - editTabView.currentIndex = 1; - break; - case 'properties': - editTabView.currentIndex = 2; - break; - case 'grid': - editTabView.currentIndex = 3; - break; - case 'particle': - editTabView.currentIndex = 4; - break; - default: - console.warn('Attempt to switch to invalid tab:', id); - } - } else { - console.warn('Attempt to switch tabs with invalid input:', JSON.stringify(id)); - } - } - spacing: 0 - - HifiConstants { id: hifi } - - TabBar { - id: editTabView - height: 60 - width: parent.width - contentWidth: parent.width - currentIndex: 0 - padding: 0 - spacing: 0 - - onCurrentIndexChanged: { - if (currentIndex === 0) { - editStack.replace(null, mainTab, {}, StackView.Immediate) - } else { - editStack.replace(null, webViewTab, - {url: Paths.defaultScripts + webTabsLinks[currentIndex]}, - StackView.Immediate) - } - } - - EditTabButton { - text: "CREATE" - } - - EditTabButton { - text: "LIST" - } - - EditTabButton { - text: "PROPERTIES" - } - - EditTabButton { - text: "GRID" - } - - EditTabButton { - text: "P" - } - } - - StackView { - id: editStack - - Layout.fillHeight: true - Layout.fillWidth: true - initialItem: mainTab//Qt.resolvedUrl('EditTabView.qml') - } - - Component { - id: mainTab - - - Rectangle { //1st tab - color: "#404040" - - Text { - color: "#ffffff" - text: "Choose an Entity Type to Create:" - font.pixelSize: 14 - font.bold: true - anchors.top: parent.top - anchors.topMargin: 28 - anchors.left: parent.left - anchors.leftMargin: 28 - } - - Flow { - id: createEntitiesFlow - spacing: 35 - anchors.right: parent.right - anchors.rightMargin: 55 - anchors.left: parent.left - anchors.leftMargin: 55 - anchors.top: parent.top - anchors.topMargin: 70 - - - NewEntityButton { - icon: "icons/create-icons/94-model-01.svg" - text: "MODEL" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newModelButton" } - }); - editTabView.currentIndex = 2 - } - } - - NewEntityButton { - icon: "icons/create-icons/21-cube-01.svg" - text: "CUBE" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newCubeButton" } - }); - editTabView.currentIndex = 2 - } - } - - NewEntityButton { - icon: "icons/create-icons/22-sphere-01.svg" - text: "SPHERE" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newSphereButton" } - }); - editTabView.currentIndex = 2 - } - } - - NewEntityButton { - icon: "icons/create-icons/24-light-01.svg" - text: "LIGHT" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newLightButton" } - }); - editTabView.currentIndex = 2 - } - } - - NewEntityButton { - icon: "icons/create-icons/20-text-01.svg" - text: "TEXT" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newTextButton" } - }); - editTabView.currentIndex = 2 - } - } - - NewEntityButton { - icon: "icons/create-icons/25-web-1-01.svg" - text: "WEB" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newWebButton" } - }); - editTabView.currentIndex = 2 - } - } - - NewEntityButton { - icon: "icons/create-icons/23-zone-01.svg" - text: "ZONE" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newZoneButton" } - }); - editTabView.currentIndex = 2 - } - } - - NewEntityButton { - icon: "icons/create-icons/90-particles-01.svg" - text: "PARTICLE" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newParticleButton" } - }); - editTabView.currentIndex = 4 - } - } - } - - HifiControls.Button { - id: assetServerButton - text: "Open This Domain's Asset Server" - color: hifi.buttons.black - colorScheme: hifi.colorSchemes.dark - anchors.right: parent.right - anchors.rightMargin: 55 - anchors.left: parent.left - anchors.leftMargin: 55 - anchors.top: createEntitiesFlow.bottom - anchors.topMargin: 35 - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "openAssetBrowserButton" } - }); - } - } - - HifiControls.Button { - text: "Import Entities (.json)" - color: hifi.buttons.black - colorScheme: hifi.colorSchemes.dark - anchors.right: parent.right - anchors.rightMargin: 55 - anchors.left: parent.left - anchors.leftMargin: 55 - anchors.top: assetServerButton.bottom - anchors.topMargin: 20 - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "importEntitiesButton" } - }); - } - } - } - } - - Component { - id: webViewTab - WebView {} + editRoot.pop(StackView.Immediate); } } diff --git a/interface/resources/qml/hifi/tablet/EditTabButton.qml b/interface/resources/qml/hifi/tablet/EditTabButton.qml index 666ea16927..232c2ca792 100644 --- a/interface/resources/qml/hifi/tablet/EditTabButton.qml +++ b/interface/resources/qml/hifi/tablet/EditTabButton.qml @@ -1,6 +1,6 @@ // -// AudioTabButton.qml -// qml/hifi/audio +// EditTabButton.qml +// qml/hifi/tablet // // Created by Vlad Stelmahovsky on 8/16/2017 // Copyright 2017 High Fidelity, Inc. @@ -16,6 +16,9 @@ import "../../styles-uit" TabButton { id: control + property alias title: control.text + property alias active: control.checkable + height: 40 font.pixelSize: height / 2 padding: 0 spacing: 0 @@ -26,7 +29,7 @@ TabButton { text: control.text font.pixelSize: 14 font.bold: true - color: control.checked ? "white" : "white" + color: "white" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter property string glyphtext: "" diff --git a/interface/resources/qml/hifi/tablet/EditTabView.qml b/interface/resources/qml/hifi/tablet/EditTabView.qml index e419b04848..9a7958f95c 100644 --- a/interface/resources/qml/hifi/tablet/EditTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditTabView.qml @@ -1,316 +1,286 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls 2.2 // Need both for short-term fix -import QtWebEngine 1.1 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import QtWebChannel 1.0 -import QtQuick.Controls.Styles 1.4 import "../../controls" import "../toolbars" import QtGraphicalEffects 1.0 import "../../controls-uit" as HifiControls import "../../styles-uit" - - -TabView { +TabBar { id: editTabView // anchors.fill: parent - height: 60 + width: parent.width + contentWidth: parent.width + padding: 0 + spacing: 0 - Tab { + readonly property HifiConstants hifi: HifiConstants {} + + EditTabButton { title: "CREATE" active: true enabled: true property string originalUrl: "" - Rectangle { - color: "#404040" - id: container + property Component visualItem: Component { - Flickable { - height: parent.height - width: parent.width + Rectangle { + color: "#404040" + id: container - contentHeight: createEntitiesFlow.height + importButton.height + assetServerButton.height + - header.anchors.topMargin + createEntitiesFlow.anchors.topMargin + - assetServerButton.anchors.topMargin + importButton.anchors.topMargin - contentWidth: width + Flickable { + height: parent.height + width: parent.width + clip: true - ScrollBar.vertical : ScrollBar { - visible: parent.contentHeight > parent.height - width: 20 - background: Rectangle { - color: hifi.colors.tableScrollBackgroundDark - } - } + contentHeight: createEntitiesFlow.height + importButton.height + assetServerButton.height + + header.anchors.topMargin + createEntitiesFlow.anchors.topMargin + + assetServerButton.anchors.topMargin + importButton.anchors.topMargin + + header.paintedHeight - Text { - id: header - color: "#ffffff" - text: "Choose an Entity Type to Create:" - font.pixelSize: 14 - font.bold: true - anchors.top: parent.top - anchors.topMargin: 28 - anchors.left: parent.left - anchors.leftMargin: 28 - } + contentWidth: width - Flow { - id: createEntitiesFlow - spacing: 35 - anchors.right: parent.right - anchors.rightMargin: 55 - anchors.left: parent.left - anchors.leftMargin: 55 - anchors.top: parent.top - anchors.topMargin: 70 + ScrollBar.vertical : ScrollBar { + visible: parent.contentHeight > parent.height + width: 20 + background: Rectangle { + color: hifi.colors.tableScrollBackgroundDark + } + } + + Text { + id: header + color: "#ffffff" + text: "Choose an Entity Type to Create:" + font.pixelSize: 14 + font.bold: true + anchors.top: parent.top + anchors.topMargin: 28 + anchors.left: parent.left + anchors.leftMargin: 28 + } + + Flow { + id: createEntitiesFlow + spacing: 35 + anchors.right: parent.right + anchors.rightMargin: 55 + anchors.left: parent.left + anchors.leftMargin: 55 + anchors.top: parent.top + anchors.topMargin: 70 - NewEntityButton { - icon: "icons/create-icons/94-model-01.svg" - text: "MODEL" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newModelButton" } - }); - editTabView.currentIndex = 2 + NewEntityButton { + icon: "icons/create-icons/94-model-01.svg" + text: "MODEL" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newModelButton" } + }); + editTabView.currentIndex = 2 + } + } + + NewEntityButton { + icon: "icons/create-icons/21-cube-01.svg" + text: "CUBE" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newCubeButton" } + }); + editTabView.currentIndex = 2 + } + } + + NewEntityButton { + icon: "icons/create-icons/22-sphere-01.svg" + text: "SPHERE" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newSphereButton" } + }); + editTabView.currentIndex = 2 + } + } + + NewEntityButton { + icon: "icons/create-icons/24-light-01.svg" + text: "LIGHT" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newLightButton" } + }); + editTabView.currentIndex = 2 + } + } + + NewEntityButton { + icon: "icons/create-icons/20-text-01.svg" + text: "TEXT" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newTextButton" } + }); + editTabView.currentIndex = 2 + } + } + + NewEntityButton { + icon: "icons/create-icons/image.svg" + text: "IMAGE" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newImageButton" } + }); + editTabView.currentIndex = 2 + } + } + + NewEntityButton { + icon: "icons/create-icons/25-web-1-01.svg" + text: "WEB" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newWebButton" } + }); + editTabView.currentIndex = 2 + } + } + + NewEntityButton { + icon: "icons/create-icons/23-zone-01.svg" + text: "ZONE" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newZoneButton" } + }); + editTabView.currentIndex = 2 + } + } + + NewEntityButton { + icon: "icons/create-icons/90-particles-01.svg" + text: "PARTICLE" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newParticleButton" } + }); + editTabView.currentIndex = 4 + } + } + + NewEntityButton { + icon: "icons/create-icons/126-material-01.svg" + text: "MATERIAL" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "newMaterialButton" } + }); + editTabView.currentIndex = 2 + } + } + } + + HifiControls.Button { + id: assetServerButton + text: "Open This Domain's Asset Server" + color: hifi.buttons.black + colorScheme: hifi.colorSchemes.dark + anchors.right: parent.right + anchors.rightMargin: 55 + anchors.left: parent.left + anchors.leftMargin: 55 + anchors.top: createEntitiesFlow.bottom + anchors.topMargin: 35 + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "openAssetBrowserButton" } + }); + } + } + + HifiControls.Button { + id: importButton + text: "Import Entities (.json)" + color: hifi.buttons.black + colorScheme: hifi.colorSchemes.dark + anchors.right: parent.right + anchors.rightMargin: 55 + anchors.left: parent.left + anchors.leftMargin: 55 + anchors.top: assetServerButton.bottom + anchors.topMargin: 20 + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", params: { buttonName: "importEntitiesButton" } + }); + } } } - - NewEntityButton { - icon: "icons/create-icons/21-cube-01.svg" - text: "CUBE" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newCubeButton" } - }); - editTabView.currentIndex = 2 - } - } - - NewEntityButton { - icon: "icons/create-icons/22-sphere-01.svg" - text: "SPHERE" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newSphereButton" } - }); - editTabView.currentIndex = 2 - } - } - - NewEntityButton { - icon: "icons/create-icons/24-light-01.svg" - text: "LIGHT" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newLightButton" } - }); - editTabView.currentIndex = 2 - } - } - - NewEntityButton { - icon: "icons/create-icons/20-text-01.svg" - text: "TEXT" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newTextButton" } - }); - editTabView.currentIndex = 2 - } - } - - NewEntityButton { - icon: "icons/create-icons/image.svg" - text: "IMAGE" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newImageButton" } - }); - editTabView.currentIndex = 2 - } - } - - NewEntityButton { - icon: "icons/create-icons/25-web-1-01.svg" - text: "WEB" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newWebButton" } - }); - editTabView.currentIndex = 2 - } - } - - NewEntityButton { - icon: "icons/create-icons/23-zone-01.svg" - text: "ZONE" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newZoneButton" } - }); - editTabView.currentIndex = 2 - } - } - - NewEntityButton { - icon: "icons/create-icons/90-particles-01.svg" - text: "PARTICLE" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newParticleButton" } - }); - editTabView.currentIndex = 4 - } - } - - NewEntityButton { - icon: "icons/create-icons/126-material-01.svg" - text: "MATERIAL" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "newMaterialButton" } - }); - editTabView.currentIndex = 2 - } - } - } - - HifiControls.Button { - id: assetServerButton - text: "Open This Domain's Asset Server" - color: hifi.buttons.black - colorScheme: hifi.colorSchemes.dark - anchors.right: parent.right - anchors.rightMargin: 55 - anchors.left: parent.left - anchors.leftMargin: 55 - anchors.top: createEntitiesFlow.bottom - anchors.topMargin: 35 - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "openAssetBrowserButton" } - }); - } - } - - HifiControls.Button { - id: importButton - text: "Import Entities (.json)" - color: hifi.buttons.black - colorScheme: hifi.colorSchemes.dark - anchors.right: parent.right - anchors.rightMargin: 55 - anchors.left: parent.left - anchors.leftMargin: 55 - anchors.top: assetServerButton.bottom - anchors.topMargin: 20 - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", params: { buttonName: "importEntitiesButton" } - }); - } - } + } // Flickable } - } // Flickable } - Tab { + EditTabButton { title: "LIST" active: true enabled: true property string originalUrl: "" - WebView { - id: entityListToolWebView - url: Paths.defaultScripts + "/system/html/entityList.html" - anchors.fill: parent - enabled: true + property Component visualItem: Component { + WebView { + id: entityListToolWebView + url: Paths.defaultScripts + "/system/html/entityList.html" + enabled: true + } } } - Tab { + EditTabButton { title: "PROPERTIES" active: true enabled: true property string originalUrl: "" - WebView { - id: entityPropertiesWebView - url: Paths.defaultScripts + "/system/html/entityProperties.html" - anchors.fill: parent - enabled: true + property Component visualItem: Component { + WebView { + id: entityPropertiesWebView + url: Paths.defaultScripts + "/system/html/entityProperties.html" + enabled: true + } } } - Tab { + EditTabButton { title: "GRID" active: true enabled: true property string originalUrl: "" - WebView { - id: gridControlsWebView - url: Paths.defaultScripts + "/system/html/gridControls.html" - anchors.fill: parent - enabled: true + property Component visualItem: Component { + WebView { + id: gridControlsWebView + url: Paths.defaultScripts + "/system/html/gridControls.html" + enabled: true + } } } - Tab { + EditTabButton { title: "P" active: true enabled: true property string originalUrl: "" - WebView { - id: particleExplorerWebView - url: Paths.defaultScripts + "/system/particle_explorer/particleExplorer.html" - anchors.fill: parent - enabled: true - } - } - - - style: TabViewStyle { - frameOverlap: 1 - tab: Rectangle { - color: styleData.selected ? "#404040" :"black" - implicitWidth: text.width + 42 - implicitHeight: 40 - Text { - id: text - anchors.centerIn: parent - text: styleData.title - font.pixelSize: 16 - font.bold: true - color: styleData.selected ? "white" : "white" - property string glyphtext: "" - HiFiGlyphs { - anchors.centerIn: parent - size: 30 - color: "#ffffff" - text: text.glyphtext - } - Component.onCompleted: if (styleData.title == "P") { - text.text = " "; - text.glyphtext = "\ue004"; - } + property Component visualItem: Component { + WebView { + id: particleExplorerWebView + url: Paths.defaultScripts + "/system/particle_explorer/particleExplorer.html" + enabled: true } } - tabBar: Rectangle { - color: "black" - anchors.right: parent.right - anchors.rightMargin: 0 - anchors.left: parent.left - anchors.leftMargin: 0 - anchors.bottom: parent.bottom - anchors.bottomMargin: 0 - anchors.top: parent.top - anchors.topMargin: 0 - } } function fromScript(message) { diff --git a/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml b/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml index 25b5be05f2..3114c79bfe 100644 --- a/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletGraphicsPreferences.qml @@ -8,8 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import "tabletWindows" import "../../dialogs" From f51025061ca1de30fa41b6ce9a1f2881ea799867 Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 1 Mar 2018 11:03:56 +0100 Subject: [PATCH 037/438] Make Edit view more consistant with original one #2 --- interface/resources/qml/hifi/tablet/Edit.qml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/Edit.qml b/interface/resources/qml/hifi/tablet/Edit.qml index 28d6094194..d5edf4c5af 100644 --- a/interface/resources/qml/hifi/tablet/Edit.qml +++ b/interface/resources/qml/hifi/tablet/Edit.qml @@ -9,7 +9,9 @@ import "../toolbars" StackView { id: editRoot objectName: "stack" - //anchors.fill: parent + + signal sendToScript(var message); + topPadding: 40 property var itemProperties: {"y": editRoot.topPadding, @@ -32,8 +34,6 @@ StackView { } } - signal sendToScript(var message); - function pushSource(path) { editRoot.push(Qt.resolvedUrl("../../" + path), itemProperties, StackView.Immediate); @@ -43,5 +43,12 @@ StackView { function popSource() { editRoot.pop(StackView.Immediate); } + + // Passes script messages to the item on the top of the stack + function fromScript(message) { + var currentItem = editRoot.currentItem; + if (currentItem && currentItem.fromScript) + currentItem.fromScript(message); + } } From 02612e04650e19325e13de7074c3933b8539a1da Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 1 Mar 2018 11:18:18 +0100 Subject: [PATCH 038/438] cleanup and fits text to buttons --- interface/resources/qml/hifi/tablet/Edit.qml | 5 ----- interface/resources/qml/hifi/tablet/EditTabButton.qml | 5 +++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/Edit.qml b/interface/resources/qml/hifi/tablet/Edit.qml index d5edf4c5af..ef38cdeb51 100644 --- a/interface/resources/qml/hifi/tablet/Edit.qml +++ b/interface/resources/qml/hifi/tablet/Edit.qml @@ -1,10 +1,5 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.3 -import "../../styles-uit" -import "../../controls-uit" as HifiControls -import "../../controls" -import "../toolbars" StackView { id: editRoot diff --git a/interface/resources/qml/hifi/tablet/EditTabButton.qml b/interface/resources/qml/hifi/tablet/EditTabButton.qml index 232c2ca792..13894f4d15 100644 --- a/interface/resources/qml/hifi/tablet/EditTabButton.qml +++ b/interface/resources/qml/hifi/tablet/EditTabButton.qml @@ -19,7 +19,6 @@ TabButton { property alias title: control.text property alias active: control.checkable height: 40 - font.pixelSize: height / 2 padding: 0 spacing: 0 HifiConstants { id: hifi; } @@ -32,6 +31,7 @@ TabButton { color: "white" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter + fontSizeMode: Text.HorizontalFit property string glyphtext: "" HiFiGlyphs { anchors.centerIn: parent @@ -49,7 +49,8 @@ TabButton { background: Rectangle { color: control.checked ? "#404040" :"black" - implicitWidth: control.contentItem.width + 42 + implicitWidth: control.contentItem.width + 42 > text.paintedWidth ? control.contentItem.width + 42 : + text.paintedWidth + 10 implicitHeight: 40 } } From 2a9bbfa8088767c066fef119f145d0040d2f05e2 Mon Sep 17 00:00:00 2001 From: vladest Date: Sun, 4 Mar 2018 13:19:56 +0100 Subject: [PATCH 039/438] Wrap text in query dialogs. Add right margins for input field and buttons --- interface/resources/qml/controls-uit/TextField.qml | 2 ++ interface/resources/qml/dialogs/TabletQueryDialog.qml | 8 +++++--- .../src/scripting/AssetMappingsScriptingInterface.cpp | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml index ee646b2575..729fcd44a6 100644 --- a/interface/resources/qml/controls-uit/TextField.qml +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -165,8 +165,10 @@ TextField { text: textField.label colorScheme: textField.colorScheme anchors.left: parent.left + anchors.right: parent.right anchors.bottom: parent.top anchors.bottomMargin: 3 + wrapMode: Text.WordWrap visible: label != "" } } diff --git a/interface/resources/qml/dialogs/TabletQueryDialog.qml b/interface/resources/qml/dialogs/TabletQueryDialog.qml index e21677c12c..182cc12c9f 100644 --- a/interface/resources/qml/dialogs/TabletQueryDialog.qml +++ b/interface/resources/qml/dialogs/TabletQueryDialog.qml @@ -72,13 +72,13 @@ TabletModalWindow { QtObject { id: d - readonly property int minWidth: 470 - readonly property int maxWidth: 470 + readonly property int minWidth: modalWindowItem.width + readonly property int maxWidth: modalWindowItem.width readonly property int minHeight: 120 readonly property int maxHeight: 720 function resize() { - var targetWidth = Math.max(titleWidth, 470) + var targetWidth = Math.max(titleWidth, modalWindowItem.width) var targetHeight = (items ? comboBox.controlHeight : textResult.controlHeight) + 5 * hifi.dimensions.contentSpacing.y + buttons.height modalWindowItem.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth); modalWindowItem.height = ((targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)) + ((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + 2 * hifi.dimensions.contentSpacing.y) : 0) + modalWindowItem.frameMarginTop @@ -106,6 +106,7 @@ TabletModalWindow { right: parent.right; bottom: parent.bottom leftMargin: 5 + rightMargin: 5 } } @@ -150,6 +151,7 @@ TabletModalWindow { bottom: parent.bottom right: parent.right margins: 0 + rightMargin: hifi.dimensions.borderRadius bottomMargin: hifi.dimensions.contentSpacing.y } Button { action: cancelAction } diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index dc227c21ba..ecaa5f2f6e 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -69,11 +69,11 @@ void AssetMappingsScriptingInterface::getMapping(QString path, QJSValue callback void AssetMappingsScriptingInterface::uploadFile(QString path, QString mapping, QJSValue startedCallback, QJSValue completedCallback, bool dropEvent) { static const QString helpText = - "Upload your asset to a specific folder by entering the full path. Specifying\n" + "Upload your asset to a specific folder by entering the full path. Specifying" "a new folder name will automatically create that folder for you."; static const QString dropHelpText = "This file will be added to your Asset Server.\n" - "Use the field below to place your file in a specific folder or to rename it.\n" + "Use the field below to place your file in a specific folder or to rename it. " "Specifying a new folder name will automatically create that folder for you."; auto offscreenUi = DependencyManager::get(); From 126f05f8bd1bb78a808661b361fe6b736d08a86a Mon Sep 17 00:00:00 2001 From: vladest Date: Sun, 4 Mar 2018 13:28:26 +0100 Subject: [PATCH 040/438] Added space --- interface/src/scripting/AssetMappingsScriptingInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index ecaa5f2f6e..e99cab8e96 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -69,7 +69,7 @@ void AssetMappingsScriptingInterface::getMapping(QString path, QJSValue callback void AssetMappingsScriptingInterface::uploadFile(QString path, QString mapping, QJSValue startedCallback, QJSValue completedCallback, bool dropEvent) { static const QString helpText = - "Upload your asset to a specific folder by entering the full path. Specifying" + "Upload your asset to a specific folder by entering the full path. Specifying " "a new folder name will automatically create that folder for you."; static const QString dropHelpText = "This file will be added to your Asset Server.\n" From 94e070eae25089e2a59498ba01414d99855607f7 Mon Sep 17 00:00:00 2001 From: vladest Date: Sun, 4 Mar 2018 15:14:49 +0100 Subject: [PATCH 041/438] Try to fix incorrectly visible scrollbars on Mac --- interface/resources/qml/windows/ScrollingWindow.qml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/windows/ScrollingWindow.qml b/interface/resources/qml/windows/ScrollingWindow.qml index 94cab7fd29..5f0780a748 100644 --- a/interface/resources/qml/windows/ScrollingWindow.qml +++ b/interface/resources/qml/windows/ScrollingWindow.qml @@ -44,7 +44,7 @@ Window { // type should only consist of logic sized areas, with nothing drawn (although the // default value for the frame property does include visual decorations) property var pane: Item { - property bool isScrolling: scrollView.height < scrollView.contentItem.height + property bool isScrolling: scrollView.contentHeight > scrollView.availableHeight property int contentWidth: scrollView.width - (isScrolling ? 10 : 0) property int scrollHeight: scrollView.height @@ -86,14 +86,11 @@ Window { anchors.bottomMargin: footerPane.height ScrollBar.vertical: ScrollBar { - id: control policy: ScrollBar.AsNeeded - parent: scrollView x: scrollView.width - width y: scrollView.topPadding height: scrollView.availableHeight - active: scrollView.ScrollBar.vertical.active - visible: control.size < 1.0 + visible: scrollView.contentHeight > scrollView.availableHeight background: Item { implicitWidth: 10 Rectangle { From 5139f3f50328c0299801906ba435d5e1528c72f8 Mon Sep 17 00:00:00 2001 From: Alexia Mandeville Date: Mon, 5 Mar 2018 14:15:50 -0800 Subject: [PATCH 042/438] Update repo script with marketplace script --- scripts/tutorials/entity_scripts/sit.js | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/scripts/tutorials/entity_scripts/sit.js b/scripts/tutorials/entity_scripts/sit.js index 70456ea493..9de65d7037 100644 --- a/scripts/tutorials/entity_scripts/sit.js +++ b/scripts/tutorials/entity_scripts/sit.js @@ -12,9 +12,9 @@ Script.include("/~/system/libraries/utils.js"); if (!String.prototype.startsWith) { String.prototype.startsWith = function(searchString, position){ - position = position || 0; - return this.substr(position, searchString.length) === searchString; - }; + position = position || 0; + return this.substr(position, searchString.length) === searchString; + }; } var SETTING_KEY = "com.highfidelity.avatar.isSitting"; @@ -122,20 +122,10 @@ this.rolesToOverride = function() { return MyAvatar.getAnimationRoles().filter(function(role) { - return !(role.startsWith("right") || role.startsWith("left")); + return !(role.startsWith("right") || role.startsWith("left")); }); } - // Handler for user changing the avatar model while sitting. There's currently an issue with changing avatar models while override role animations are applied, - // so to avoid that problem, re-apply the role overrides once the model has finished changing. - this.modelURLChangeFinished = function () { - print("Sitter's model has FINISHED changing. Reapply anim role overrides."); - var roles = this.rolesToOverride(); - for (i in roles) { - MyAvatar.overrideRoleAnimation(roles[i], ANIMATION_URL, ANIMATION_FPS, true, ANIMATION_FIRST_FRAME, ANIMATION_LAST_FRAME); - } - } - this.sitDown = function() { if (this.checkSeatForAvatar()) { print("Someone is already sitting in that chair."); @@ -174,14 +164,12 @@ return { headType: 0 }; }, ["headType"]); Script.update.connect(this, this.update); - MyAvatar.onLoadComplete.connect(this, this.modelURLChangeFinished); } this.standUp = function() { print("Standing up (" + this.entityID + ")"); MyAvatar.removeAnimationStateHandler(this.animStateHandlerID); Script.update.disconnect(this, this.update); - MyAvatar.onLoadComplete.disconnect(this, this.modelURLChangeFinished); if (MyAvatar.sessionUUID === this.getSeatUser()) { this.setSeatUser(null); @@ -343,7 +331,7 @@ } this.cleanupOverlay(); } - + this.clickDownOnEntity = function (id, event) { if (isInEditMode()) { return; @@ -352,4 +340,4 @@ this.sitDown(); } } -}); +}); \ No newline at end of file From 8723b317c907bbab98d6ca4ae61ed4afb713b8b0 Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 6 Mar 2018 13:09:08 +0100 Subject: [PATCH 043/438] Move Keyboard under input dialog makes sure it will no shift dialogs content --- .../qml/dialogs/TabletQueryDialog.qml | 39 ++++++++----------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/interface/resources/qml/dialogs/TabletQueryDialog.qml b/interface/resources/qml/dialogs/TabletQueryDialog.qml index 182cc12c9f..60dbc106dc 100644 --- a/interface/resources/qml/dialogs/TabletQueryDialog.qml +++ b/interface/resources/qml/dialogs/TabletQueryDialog.qml @@ -65,10 +65,7 @@ TabletModalWindow { id: modalWindowItem width: parent.width - 12 height: 240 - anchors { - verticalCenter: parent.verticalCenter - horizontalCenter: parent.horizontalCenter - } + anchors.horizontalCenter: parent.horizontalCenter QtObject { id: d @@ -81,18 +78,20 @@ TabletModalWindow { var targetWidth = Math.max(titleWidth, modalWindowItem.width) var targetHeight = (items ? comboBox.controlHeight : textResult.controlHeight) + 5 * hifi.dimensions.contentSpacing.y + buttons.height modalWindowItem.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth); - modalWindowItem.height = ((targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)) + ((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + 2 * hifi.dimensions.contentSpacing.y) : 0) + modalWindowItem.frameMarginTop + modalWindowItem.height = ((targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)) + modalWindowItem.frameMarginTop + modalWindowItem.y = (root.height - (modalWindowItem.height + ((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + 2 * hifi.dimensions.contentSpacing.y) : 0))) / 2 } } Item { anchors { top: parent.top - bottom: keyboard.top; + bottom: buttons.top; left: parent.left; right: parent.right; margins: 0 bottomMargin: 2 * hifi.dimensions.contentSpacing.y + topMargin: modalWindowItem.frameMarginTop } // FIXME make a text field type that can be bound to a history for autocompletion @@ -125,22 +124,6 @@ TabletModalWindow { } } - property alias keyboardOverride: root.keyboardOverride - property alias keyboardRaised: root.keyboardRaised - property alias punctuationMode: root.punctuationMode - - Keyboard { - id: keyboard - raised: keyboardEnabled && keyboardRaised - numeric: punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: buttons.top - bottomMargin: raised ? 2 * hifi.dimensions.contentSpacing.y : 0 - } - } - Flow { id: buttons focus: true @@ -179,7 +162,17 @@ TabletModalWindow { } } - Keys.onPressed: { + Keyboard { + id: keyboard + raised: keyboardEnabled && keyboardRaised + numeric: punctuationMode + anchors { + left: parent.left + right: parent.right + top: modalWindowItem.bottom + } + } + Keys.onPressed: { if (!visible) { return } From 698a224bb0e17f54eab0f2042d90dc60d84dd165 Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 6 Mar 2018 16:13:46 +0100 Subject: [PATCH 044/438] Next attempt to fix ghosted scroll bars --- .../resources/qml/windows/ScrollingWindow.qml | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/interface/resources/qml/windows/ScrollingWindow.qml b/interface/resources/qml/windows/ScrollingWindow.qml index 5f0780a748..2346522172 100644 --- a/interface/resources/qml/windows/ScrollingWindow.qml +++ b/interface/resources/qml/windows/ScrollingWindow.qml @@ -38,24 +38,26 @@ Window { property bool keyboardRaised: false property bool punctuationMode: false + readonly property real verticalScrollWidth: 10 + readonly property real verticalScrollShaft: 8 + // Scrollable window content. // FIXME this should not define any visual content in this type. The base window // type should only consist of logic sized areas, with nothing drawn (although the // default value for the frame property does include visual decorations) property var pane: Item { - property bool isScrolling: scrollView.contentHeight > scrollView.availableHeight + property bool isScrolling: Qt.binding(function() { return scrollView.contentHeight > scrollView.availableHeight; }); - property int contentWidth: scrollView.width - (isScrolling ? 10 : 0) - property int scrollHeight: scrollView.height + property int contentWidth: scrollView.availableWidth + property int scrollHeight: scrollView.availableHeight anchors.fill: parent - anchors.rightMargin: isScrolling ? 11 : 0 Rectangle { id: contentBackground anchors.fill: parent - anchors.rightMargin: parent.isScrolling ? 11 : 0 + //anchors.rightMargin: parent.isScrolling ? verticalScrollWidth + 1 : 0 color: hifi.colors.baseGray visible: !window.hideBackground && modality != Qt.ApplicationModal } @@ -77,22 +79,23 @@ Window { ScrollView { id: scrollView - contentItem: content + contentChildren: content ScrollBar.horizontal.policy: ScrollBar.AlwaysOff clip: true + anchors.rightMargin: parent.isScrolling ? verticalScrollWidth : 0 anchors.fill: parent - anchors.rightMargin: parent.isScrolling ? 1 : 0 anchors.bottomMargin: footerPane.height ScrollBar.vertical: ScrollBar { - policy: ScrollBar.AsNeeded - x: scrollView.width - width - y: scrollView.topPadding - height: scrollView.availableHeight - visible: scrollView.contentHeight > scrollView.availableHeight + policy: scrollView.contentHeight > scrollView.availableHeight ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff + parent: scrollView.parent + anchors.top: scrollView.top + anchors.right: scrollView.right + anchors.bottom: scrollView.bottom + anchors.rightMargin: -verticalScrollWidth //compensate scrollview's right margin background: Item { - implicitWidth: 10 + implicitWidth: verticalScrollWidth Rectangle { color: hifi.colors.darkGray30 radius: 4 @@ -104,9 +107,9 @@ Window { } } contentItem: Item { - implicitWidth: 8 + implicitWidth: verticalScrollShaft Rectangle { - radius: 4 + radius: verticalScrollShaft/2 color: hifi.colors.white30 anchors { fill: parent From b7d0260a17654dd79854e5b65058b2cd74c6115f Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Thu, 8 Feb 2018 14:05:21 -0800 Subject: [PATCH 045/438] Fix the uniform target for bindless --- libraries/gpu-gl/src/gpu/gl/GLShader.cpp | 444 +----------------- libraries/gpu-gl/src/gpu/gl/GLShader.h | 9 - .../src/gpu/gl41/GL41BackendTexture.cpp | 4 - libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 84 +++- .../gpu-gl/src/gpu/gl45/GL45BackendShader.cpp | 5 + .../src/gpu/gl45/GL45BackendTexture.cpp | 183 ++++++-- .../gpu/gl45/GL45BackendVariableTexture.cpp | 19 +- libraries/gpu/src/gpu/Texture.h | 34 +- libraries/gpu/src/gpu/TextureTable.h | 1 - libraries/gpu/src/gpu/TextureTable.slh | 3 - 10 files changed, 259 insertions(+), 527 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.cpp b/libraries/gpu-gl/src/gpu/gl/GLShader.cpp index 9c3de94957..7a26642024 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLShader.cpp @@ -10,447 +10,6 @@ #include "GLBackend.h" -namespace gpu { - namespace gl { - - class ElementResource { - public: - gpu::Element _element; - uint16 _resource; - - ElementResource(Element&& elem, uint16 resource) : _element(elem), _resource(resource) {} - }; - - ElementResource getFormatFromGLUniform(GLenum gltype) { - switch (gltype) { - case GL_FLOAT: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - /* - case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - */ - case GL_INT: return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC2: return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC3: return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC4: return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER); - - case GL_UNSIGNED_INT: return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER); -#if defined(Q_OS_WIN) - case GL_UNSIGNED_INT_VEC2: return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER); - case GL_UNSIGNED_INT_VEC3: return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER); - case GL_UNSIGNED_INT_VEC4: return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER); -#endif - - case GL_BOOL: return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC2: return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC3: return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC4: return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER); - - - case GL_FLOAT_MAT2: return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_MAT3: return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_MAT4: return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - - /* {GL_FLOAT_MAT2x3 mat2x3}, - {GL_FLOAT_MAT2x4 mat2x4}, - {GL_FLOAT_MAT3x2 mat3x2}, - {GL_FLOAT_MAT3x4 mat3x4}, - {GL_FLOAT_MAT4x2 mat4x2}, - {GL_FLOAT_MAT4x3 mat4x3}, - {GL_DOUBLE_MAT2 dmat2}, - {GL_DOUBLE_MAT3 dmat3}, - {GL_DOUBLE_MAT4 dmat4}, - {GL_DOUBLE_MAT2x3 dmat2x3}, - {GL_DOUBLE_MAT2x4 dmat2x4}, - {GL_DOUBLE_MAT3x2 dmat3x2}, - {GL_DOUBLE_MAT3x4 dmat3x4}, - {GL_DOUBLE_MAT4x2 dmat4x2}, - {GL_DOUBLE_MAT4x3 dmat4x3}, - */ - - case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D); - case GL_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D); - - case GL_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D); - case GL_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE); - -#if defined(Q_OS_WIN) - case GL_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); -#endif - - case GL_SAMPLER_2D_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D); -#if defined(Q_OS_WIN) - case GL_SAMPLER_CUBE_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE); - - case GL_SAMPLER_2D_ARRAY_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY); -#endif - - // {GL_SAMPLER_1D_SHADOW sampler1DShadow}, - // {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, - - // {GL_SAMPLER_BUFFER samplerBuffer}, - // {GL_SAMPLER_2D_RECT sampler2DRect}, - // {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, - -#if defined(Q_OS_WIN) - case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D); - case GL_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D); - case GL_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D); - case GL_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE); - - case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); - - // {GL_INT_SAMPLER_BUFFER isamplerBuffer}, - // {GL_INT_SAMPLER_2D_RECT isampler2DRect}, - - case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D); - case GL_UNSIGNED_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D); - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_UNSIGNED_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D); - case GL_UNSIGNED_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE); - - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); -#endif - // {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, - // {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, - /* - {GL_IMAGE_1D image1D}, - {GL_IMAGE_2D image2D}, - {GL_IMAGE_3D image3D}, - {GL_IMAGE_2D_RECT image2DRect}, - {GL_IMAGE_CUBE imageCube}, - {GL_IMAGE_BUFFER imageBuffer}, - {GL_IMAGE_1D_ARRAY image1DArray}, - {GL_IMAGE_2D_ARRAY image2DArray}, - {GL_IMAGE_2D_MULTISAMPLE image2DMS}, - {GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray}, - {GL_INT_IMAGE_1D iimage1D}, - {GL_INT_IMAGE_2D iimage2D}, - {GL_INT_IMAGE_3D iimage3D}, - {GL_INT_IMAGE_2D_RECT iimage2DRect}, - {GL_INT_IMAGE_CUBE iimageCube}, - {GL_INT_IMAGE_BUFFER iimageBuffer}, - {GL_INT_IMAGE_1D_ARRAY iimage1DArray}, - {GL_INT_IMAGE_2D_ARRAY iimage2DArray}, - {GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS}, - {GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray}, - {GL_UNSIGNED_INT_IMAGE_1D uimage1D}, - {GL_UNSIGNED_INT_IMAGE_2D uimage2D}, - {GL_UNSIGNED_INT_IMAGE_3D uimage3D}, - {GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect}, - {GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot - - {GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer}, - {GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray}, - {GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray}, - {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS}, - {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray}, - {GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint} - */ - default: - return ElementResource(Element(), Resource::BUFFER); - } - - }; - - int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, - Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& textureTables, Shader::SlotSet& samplers) { - GLint uniformsCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); - - for (int i = 0; i < uniformsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - GLint location = glGetUniformLocation(glprogram, name); - const GLint INVALID_UNIFORM_LOCATION = -1; - - // Try to make sense of the gltype - auto elementResource = getFormatFromGLUniform(type); - - // The uniform as a standard var type - if (location != INVALID_UNIFORM_LOCATION) { - // Let's make sure the name doesn't contains an array element - std::string sname(name); - auto foundBracket = sname.find_first_of('['); - if (foundBracket != std::string::npos) { - // std::string arrayname = sname.substr(0, foundBracket); - - if (sname[foundBracket + 1] == '0') { - sname = sname.substr(0, foundBracket); - } else { - // skip this uniform since it's not the first element of an array - continue; - } - } - - if (elementResource._resource == Resource::BUFFER) { - uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource)); - } else { - // For texture/Sampler, the location is the actual binding value - GLint binding = -1; - glGetUniformiv(glprogram, location, &binding); - - auto requestedBinding = slotBindings.find(std::string(sname)); - if (requestedBinding != slotBindings.end()) { - if (binding != (*requestedBinding)._location) { - binding = (*requestedBinding)._location; - glProgramUniform1i(glprogram, location, binding); - } - } - - textures.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); - samplers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); - } - } - } - - return uniformsCount; - } - - const GLint UNUSED_SLOT = -1; - bool isUnusedSlot(GLint binding) { - return (binding == UNUSED_SLOT); - } - - int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) { - GLint buffersCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount); - - // fast exit - if (buffersCount == 0) { - return 0; - } - - GLint maxNumUniformBufferSlots = 0; - glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots); - std::vector uniformBufferSlotMap(maxNumUniformBufferSlots, -1); - - struct UniformBlockInfo { - using Vector = std::vector; - const GLuint index { 0 }; - const std::string name; - GLint binding { -1 }; - GLint size { 0 }; - - static std::string getName(GLuint glprogram, GLuint i) { - static const GLint NAME_LENGTH = 256; - GLint length = 0; - GLchar nameBuffer[NAME_LENGTH]; - glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); - glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, nameBuffer); - return std::string(nameBuffer); - } - - UniformBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) { - glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_BINDING, &binding); - glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size); - } - }; - - UniformBlockInfo::Vector uniformBlocks; - uniformBlocks.reserve(buffersCount); - for (int i = 0; i < buffersCount; i++) { - uniformBlocks.push_back(UniformBlockInfo(glprogram, i)); - } - - for (auto& info : uniformBlocks) { - auto requestedBinding = slotBindings.find(info.name); - if (requestedBinding != slotBindings.end()) { - info.binding = (*requestedBinding)._location; - glUniformBlockBinding(glprogram, info.index, info.binding); - uniformBufferSlotMap[info.binding] = info.index; - } - } - - for (auto& info : uniformBlocks) { - if (slotBindings.count(info.name)) { - continue; - } - - // If the binding is 0, or the binding maps to an already used binding - if (info.binding == 0 || uniformBufferSlotMap[info.binding] != UNUSED_SLOT) { - // If no binding was assigned then just do it finding a free slot - auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), isUnusedSlot); - if (slotIt != uniformBufferSlotMap.end()) { - info.binding = slotIt - uniformBufferSlotMap.begin(); - glUniformBlockBinding(glprogram, info.index, info.binding); - } else { - // This should neve happen, an active ubo cannot find an available slot among the max available?! - info.binding = -1; - } - } - - uniformBufferSlotMap[info.binding] = info.index; - } - - for (auto& info : uniformBlocks) { - static const Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER); - buffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size)); - } - return buffersCount; - } - - int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) { - GLint inputsCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount); - - for (int i = 0; i < inputsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - - GLint binding = glGetAttribLocation(glprogram, name); - - auto elementResource = getFormatFromGLUniform(type); - inputs.insert(Shader::Slot(name, binding, elementResource._element, -1)); - } - - return inputsCount; - } - - int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) { - /* GLint outputsCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount); - - for (int i = 0; i < inputsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - - auto element = getFormatFromGLUniform(type); - outputs.insert(Shader::Slot(name, i, element)); - } - */ - return 0; //inputsCount; - } - - void makeProgramBindings(ShaderObject& shaderObject) { - if (!shaderObject.glprogram) { - return; - } - GLuint glprogram = shaderObject.glprogram; - GLint loc = -1; - - //Check for gpu specific attribute slotBindings - loc = glGetAttribLocation(glprogram, "inPosition"); - if (loc >= 0 && loc != gpu::Stream::POSITION) { - glBindAttribLocation(glprogram, gpu::Stream::POSITION, "inPosition"); - } - - loc = glGetAttribLocation(glprogram, "inNormal"); - if (loc >= 0 && loc != gpu::Stream::NORMAL) { - glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "inNormal"); - } - - loc = glGetAttribLocation(glprogram, "inColor"); - if (loc >= 0 && loc != gpu::Stream::COLOR) { - glBindAttribLocation(glprogram, gpu::Stream::COLOR, "inColor"); - } - - loc = glGetAttribLocation(glprogram, "inTexCoord0"); - if (loc >= 0 && loc != gpu::Stream::TEXCOORD) { - glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "inTexCoord0"); - } - - loc = glGetAttribLocation(glprogram, "inTangent"); - if (loc >= 0 && loc != gpu::Stream::TANGENT) { - glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "inTangent"); - } - - loc = glGetAttribLocation(glprogram, "inTexCoord1"); - if (loc >= 0 && loc != gpu::Stream::TEXCOORD1) { - glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD1, "inTexCoord1"); - } - - loc = glGetAttribLocation(glprogram, "inSkinClusterIndex"); - if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_INDEX) { - glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "inSkinClusterIndex"); - } - - loc = glGetAttribLocation(glprogram, "inSkinClusterWeight"); - if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_WEIGHT) { - glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight"); - } - - loc = glGetAttribLocation(glprogram, "_drawCallInfo"); - if (loc >= 0 && loc != gpu::Stream::DRAW_CALL_INFO) { - glBindAttribLocation(glprogram, gpu::Stream::DRAW_CALL_INFO, "_drawCallInfo"); - } - - // Link again to take into account the assigned attrib location - glLinkProgram(glprogram); - - GLint linked = 0; - glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); - if (!linked) { - qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?"; - } - - // now assign the ubo binding, then DON't relink! - - //Check for gpu specific uniform slotBindings -#ifdef GPU_SSBO_DRAW_CALL_INFO - loc = glGetProgramResourceIndex(glprogram, GL_SHADER_STORAGE_BLOCK, "transformObjectBuffer"); - if (loc >= 0) { - glShaderStorageBlockBinding(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT); - shaderObject.transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT; - } -#else - loc = glGetUniformLocation(glprogram, "transformObjectBuffer"); - if (loc >= 0) { - glProgramUniform1i(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT); - shaderObject.transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT; - } -#endif - - loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer"); - if (loc >= 0) { - glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT); - shaderObject.transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT; - } - - loc = glGetUniformBlockIndex(glprogram, "gpu_resourceTextureTable0"); - if (loc >= 0) { - glUniformBlockBinding(glprogram, loc, GLBackend::RESOURCE_TABLE_TEXTURE_SLOT_OFFSET); - shaderObject.resourceTextureTableSlot0 = GLBackend::RESOURCE_TABLE_TEXTURE_SLOT_OFFSET; - } - loc = glGetUniformBlockIndex(glprogram, "gpu_resourceTextureTable1"); - if (loc >= 0) { - glUniformBlockBinding(glprogram, loc, GLBackend::RESOURCE_TABLE_TEXTURE_SLOT_OFFSET + 1); - shaderObject.resourceTextureTableSlot1 = GLBackend::RESOURCE_TABLE_TEXTURE_SLOT_OFFSET + 1; - } - (void)CHECK_GL_ERROR(); - } - } -} - - using namespace gpu; using namespace gpu::gl; @@ -516,7 +75,8 @@ bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::Bin Shader::SlotSet textures; Shader::SlotSet textureTables; Shader::SlotSet samplers; - backend.makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, textureTables, samplers); + //backend.makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, textureTables, samplers); + backend.makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers); Shader::SlotSet resourceBuffers; backend.makeResourceBufferSlots(shaderObject.glprogram, slotBindings, resourceBuffers); diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.h b/libraries/gpu-gl/src/gpu/gl/GLShader.h index a5a6a6e72c..b2a1f189a2 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShader.h +++ b/libraries/gpu-gl/src/gpu/gl/GLShader.h @@ -17,19 +17,10 @@ struct ShaderObject { GLuint glprogram { 0 }; GLint transformCameraSlot { -1 }; GLint transformObjectSlot { -1 }; - GLint resourceTextureTableSlot0 { -1 }; GLint resourceTextureTableSlot1 { -1 }; }; -int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, - Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& textureTables, Shader::SlotSet& samplers); -int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers); -int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs); -int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs); -void makeProgramBindings(ShaderObject& shaderObject); - - class GLShader : public GPUObject { public: static GLShader* sync(GLBackend& backend, const Shader& shader, const Shader::CompilationHandler& handler = nullptr); diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index b70730fd98..495f11e4a8 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -167,7 +167,6 @@ void GL41Texture::syncSampler() const { glTexParameteri(_target, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]); glTexParameterfv(_target, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); - glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, (uint16)sampler.getMipOffset()); glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); @@ -206,9 +205,6 @@ void GL41FixedAllocationTexture::allocateStorage() const { void GL41FixedAllocationTexture::syncSampler() const { Parent::syncSampler(); const Sampler& sampler = _gpuObject.getSampler(); - auto baseMip = std::max(sampler.getMipOffset(), sampler.getMinMip()); - - glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, baseMip); glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.0f : sampler.getMaxMip())); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 4fb12fbdb1..d733b38a2f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -19,6 +19,7 @@ #define INCREMENTAL_TRANSFER 0 #define GPU_SSBO_TRANSFORM_OBJECT 1 +#define GPU_BINDLESS_TEXTURES 1 namespace gpu { namespace gl45 { @@ -49,21 +50,6 @@ public: static const std::string GL45_VERSION; const std::string& getVersion() const override { return GL45_VERSION; } - class GL45TextureTable : public GLObject { - static GLuint allocate(); - using Parent = GLObject; - public: - using HandlesArray = std::array; - GL45TextureTable(const std::weak_ptr& backend, const TextureTable& texture, const HandlesArray& newHandles, bool complete); - ~GL45TextureTable(); - - // FIXME instead of making a buffer for each table, there should be a global buffer of all materials - // and we should store an offset into that buffer - const uint32_t _stamp { 0 }; - const HandlesArray _handles; - const bool _complete { false }; - }; - class GL45Texture : public GLTexture { using Parent = GLTexture; friend class GL45Backend; @@ -73,8 +59,72 @@ public: void generateMips() const override; Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override; void syncSampler() const override; + + bool isBindless() const { +#if GPU_BINDLESS_TEXTURES + return _bindless.operator bool(); +#else + return false; +#endif + } + +#if GPU_BINDLESS_TEXTURES + struct Bindless { + uint64_t handle{ 0 }; + uint32_t minMip{ 0 }; + uint32_t sampler{ 0 }; + + operator bool() const { + return handle != 0; + } + }; + + virtual const Bindless& getBindless() const; + void releaseBindless() const; + void recreateBindless() const; + virtual uint16 getMinMip() const = 0; + + + private: + class InvalidSampler : public Sampler { + public: + InvalidSampler() { + _desc._borderColor = vec4(-1.0f); + } + + operator const Sampler&() const { + return *this; + } + }; + + static const Sampler INVALID_SAMPLER; + // This stores the texture handle (64 bits) in xy, the min mip available in z, and the sampler ID in w + mutable Sampler _cachedSampler{ INVALID_SAMPLER }; + + mutable Bindless _bindless; + +#endif }; +#if GPU_BINDLESS_TEXTURES + class GL45TextureTable : public GLObject { + static GLuint allocate(); + using Parent = GLObject; + public: + using BindlessArray = std::array; + + GL45TextureTable(const std::weak_ptr& backend, const TextureTable& texture); + ~GL45TextureTable(); + + void update(const BindlessArray& newHandles); + + // FIXME instead of making a buffer for each table, there should be a global buffer of all materials + // and we should store an offset into that buffer + BindlessArray _handles; + }; +#endif + + // // Textures that have fixed allocation sizes and cannot be managed at runtime // @@ -89,6 +139,8 @@ public: protected: Size size() const override { return _size; } + uint16 getMinMip() const override { return 0; } + void allocateStorage() const; void syncSampler() const override; const Size _size { 0 }; @@ -125,6 +177,8 @@ public: ~GL45VariableAllocationTexture(); Size size() const override { return _size; } + uint16 getMinMip() const override { return _populatedMip; } + virtual const Bindless& getBindless() const override; Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override; void copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) override; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp index c2490524ae..9c973c9a6a 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp @@ -165,6 +165,11 @@ void GL45Backend::makeProgramBindings(ShaderObject& shaderObject) { shaderObject.transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT; } + loc = glGetUniformBlockIndex(glprogram, "gpu_resourceTextureTable0"); + if (loc >= 0) { + glUniformBlockBinding(glprogram, loc, RESOURCE_TABLE_TEXTURE_SLOT_OFFSET); + } + (void)CHECK_GL_ERROR(); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index e3c10928b4..d33afc0e67 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include "../gl/GLTexelFormat.h" @@ -29,6 +30,10 @@ using namespace gpu::gl45; #define MAX_RESOURCE_TEXTURES_PER_FRAME 2 +#pragma optimize("", off) + +#define FORCE_STRICT_TEXTURE 0 +#define ENABLE_SPARSE_TEXTURE 0 GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { if (!texturePointer) { @@ -52,14 +57,18 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { object = new GL45AttachmentTexture(shared_from_this(), texture); break; +#if FORCE_STRICT_TEXTURE + case TextureUsageType::RESOURCE: +#endif case TextureUsageType::STRICT_RESOURCE: qCDebug(gpugllogging) << "Strict texture " << texture.source().c_str(); object = new GL45StrictResourceTexture(shared_from_this(), texture); break; +#if !FORCE_STRICT_TEXTURE case TextureUsageType::RESOURCE: { if (GL45VariableAllocationTexture::_frameTexturesCreated < MAX_RESOURCE_TEXTURES_PER_FRAME) { -#if 0 +#if ENABLE_SPARSE_TEXTURE if (isTextureManagementSparseEnabled() && GL45Texture::isSparseEligible(texture)) { object = new GL45SparseResourceTexture(shared_from_this(), texture); } else { @@ -77,7 +86,7 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { } break; } - +#endif default: Q_UNREACHABLE(); } @@ -115,6 +124,50 @@ void GL45Backend::initTextureManagementStage() { using GL45Texture = GL45Backend::GL45Texture; + +class GLSamplerCache { +public: + GLuint getGLSampler(const Sampler& sampler) { + if (0 == _samplerCache.count(sampler)) { + GLuint result = 0; + glGenSamplers(1, &result); + const auto& fm = GLTexture::FILTER_MODES[sampler.getFilter()]; + glSamplerParameteri(result, GL_TEXTURE_MIN_FILTER, fm.minFilter); + glSamplerParameteri(result, GL_TEXTURE_MAG_FILTER, fm.magFilter); + if (sampler.doComparison()) { + glSamplerParameteri(result, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE_ARB); + glSamplerParameteri(result, GL_TEXTURE_COMPARE_FUNC, COMPARISON_TO_GL[sampler.getComparisonFunction()]); + } else { + glSamplerParameteri(result, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } + + glSamplerParameteri(result, GL_TEXTURE_WRAP_S, GLTexture::WRAP_MODES[sampler.getWrapModeU()]); + glSamplerParameteri(result, GL_TEXTURE_WRAP_T, GLTexture::WRAP_MODES[sampler.getWrapModeV()]); + glSamplerParameteri(result, GL_TEXTURE_WRAP_R, GLTexture::WRAP_MODES[sampler.getWrapModeW()]); + + glSamplerParameterf(result, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); + glSamplerParameterfv(result, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); + + glSamplerParameterf(result, GL_TEXTURE_MIN_LOD, sampler.getMinMip()); + glSamplerParameterf(result, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); + _samplerCache[sampler] = result; + return result; + } + + return _samplerCache[sampler]; + } + + void releaseGLSampler(GLuint sampler) { + // NO OP + } + +private: + std::unordered_map _samplerCache; +}; + +static GLSamplerCache SAMPLER_CACHE; +const Sampler GL45Texture::INVALID_SAMPLER = GL45Texture::InvalidSampler(); + GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture) : GLTexture(backend, texture, allocate(texture)) { } @@ -122,10 +175,10 @@ GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& GLuint GL45Texture::allocate(const Texture& texture) { GLuint result; glCreateTextures(getGLTextureType(texture), 1, &result); -#ifdef DEBUG - auto source = texture.source(); - glObjectLabel(GL_TEXTURE, result, (GLsizei)source.length(), source.data()); -#endif + if (::gl::Context::enableDebugLogger()) { + auto source = texture.source(); + glObjectLabel(GL_TEXTURE, result, (GLsizei)source.length(), source.data()); + } return result; } @@ -190,32 +243,69 @@ Size GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const return amountCopied; } -void GL45Texture::syncSampler() const { - const Sampler& sampler = _gpuObject.getSampler(); - - const auto& fm = FILTER_MODES[sampler.getFilter()]; - glTextureParameteri(_id, GL_TEXTURE_MIN_FILTER, fm.minFilter); - glTextureParameteri(_id, GL_TEXTURE_MAG_FILTER, fm.magFilter); - - if (sampler.doComparison()) { - glTextureParameteri(_id, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE_ARB); - glTextureParameteri(_id, GL_TEXTURE_COMPARE_FUNC, COMPARISON_TO_GL[sampler.getComparisonFunction()]); - } else { - glTextureParameteri(_id, GL_TEXTURE_COMPARE_MODE, GL_NONE); +void GL45Texture::releaseBindless() const { + // Release the old handler + SAMPLER_CACHE.releaseGLSampler(_bindless.sampler); + glMakeTextureHandleNonResidentARB(_bindless.handle); + _bindless = Bindless(); +} + +void GL45Texture::recreateBindless() const { + if (isBindless()) { + releaseBindless(); } - glTextureParameteri(_id, GL_TEXTURE_WRAP_S, WRAP_MODES[sampler.getWrapModeU()]); - glTextureParameteri(_id, GL_TEXTURE_WRAP_T, WRAP_MODES[sampler.getWrapModeV()]); - glTextureParameteri(_id, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]); + _bindless.sampler = SAMPLER_CACHE.getGLSampler(_cachedSampler); + _bindless.handle = glGetTextureSamplerHandleARB(_id, _bindless.sampler); + glMakeTextureHandleResidentARB(_bindless.handle); +} - glTextureParameterf(_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); - glTextureParameterfv(_id, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); +const GL45Texture::Bindless& GL45Texture::getBindless() const { + if (!_bindless) { + recreateBindless(); + } + _bindless.minMip = getMinMip(); + return _bindless; +} - glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, sampler.getMinMip()); - glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); +void GL45Texture::syncSampler() const { + const Sampler& sampler = _gpuObject.getSampler(); + bool samplerChanged = _cachedSampler != sampler; + if (samplerChanged) { + _cachedSampler = sampler; + } + + if (isBindless()) { + if (samplerChanged) { + recreateBindless(); + } + } else if (samplerChanged) { + const auto& fm = FILTER_MODES[sampler.getFilter()]; + glTextureParameteri(_id, GL_TEXTURE_MIN_FILTER, fm.minFilter); + glTextureParameteri(_id, GL_TEXTURE_MAG_FILTER, fm.magFilter); + + if (sampler.doComparison()) { + glTextureParameteri(_id, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE_ARB); + glTextureParameteri(_id, GL_TEXTURE_COMPARE_FUNC, COMPARISON_TO_GL[sampler.getComparisonFunction()]); + } else { + glTextureParameteri(_id, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } + + glTextureParameteri(_id, GL_TEXTURE_WRAP_S, WRAP_MODES[sampler.getWrapModeU()]); + glTextureParameteri(_id, GL_TEXTURE_WRAP_T, WRAP_MODES[sampler.getWrapModeV()]); + glTextureParameteri(_id, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]); + + glTextureParameterf(_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); + glTextureParameterfv(_id, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); + + glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, sampler.getMinMip()); + glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); + } (void)CHECK_GL_ERROR(); } +// Fixed allocation textures, used for strict resources & framebuffer attachments + using GL45FixedAllocationTexture = GL45Backend::GL45FixedAllocationTexture; GL45FixedAllocationTexture::GL45FixedAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture), _size(texture.evalTotalSize()) { @@ -240,8 +330,6 @@ void GL45FixedAllocationTexture::allocateStorage() const { void GL45FixedAllocationTexture::syncSampler() const { Parent::syncSampler(); const Sampler& sampler = _gpuObject.getSampler(); - auto baseMip = std::max(sampler.getMipOffset(), sampler.getMinMip()); - glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, baseMip); glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); } @@ -280,7 +368,6 @@ GL45StrictResourceTexture::GL45StrictResourceTexture(const std::weak_ptr& backend, const TextureTable& textureTable, const HandlesArray& handles, bool handlesComplete) - : Parent(backend, textureTable, allocate()), _stamp(textureTable.getStamp()), _handles(handles), _complete(handlesComplete) { +GL45TextureTable::GL45TextureTable(const std::weak_ptr& backend, const TextureTable& textureTable) + : Parent(backend, textureTable, allocate()){ Backend::setGPUObject(textureTable, this); // FIXME include these in overall buffer storage reporting - glNamedBufferStorage(_id, sizeof(uvec4) * TextureTable::COUNT, &_handles[0], 0); + glNamedBufferStorage(_id, sizeof(uvec4) * TextureTable::COUNT, nullptr, GL_DYNAMIC_STORAGE_BIT); } +void GL45TextureTable::update(const BindlessArray& handles) { + if (_handles != handles) { + _handles = handles; + // FIXME include these in overall buffer storage reporting + // FIXME use a single shared buffer for bindless data + glNamedBufferSubData(_id, 0, sizeof(GL45Texture::Bindless) * TextureTable::COUNT, &_handles[0]); + } +} GL45TextureTable::~GL45TextureTable() { if (_id) { @@ -324,36 +410,33 @@ GL45TextureTable::~GL45TextureTable() { } } - GL45TextureTable* GL45Backend::syncGPUObject(const TextureTablePointer& textureTablePointer) { const auto& textureTable = *textureTablePointer; // Find the target handles auto textures = textureTable.getTextures(); - bool handlesComplete = true; - GL45TextureTable::HandlesArray handles{}; + GL45TextureTable::BindlessArray handles{}; for (size_t i = 0; i < textures.size(); ++i) { auto texture = textures[i]; if (!texture) { continue; } // FIXME what if we have a non-transferrable texture here? - auto gltexture = (GL45Texture*)syncGPUObject(texture, true); + auto gltexture = (GL45Texture*)syncGPUObject(texture); if (!gltexture) { - handlesComplete = false; continue; } - auto handle = gltexture->getHandle(); - memcpy(&handles[i], &handle, sizeof(handle)); + handles[i] = gltexture->getBindless(); } // If the object hasn't been created, or the object definition is out of date, drop and re-create GL45TextureTable* object = Backend::getGPUObject(textureTable); - if (!object || object->_stamp != textureTable.getStamp() || !object->_complete || handles != object->_handles) { - object = new GL45TextureTable(shared_from_this(), textureTable, handles, handlesComplete); + if (!object) { + object = new GL45TextureTable(shared_from_this(), textureTable); } + object->update(handles); return object; } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 0f1de0f868..ab5577940c 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -27,6 +27,7 @@ using namespace gpu; using namespace gpu::gl; using namespace gpu::gl45; +using GL45Texture = GL45Backend::GL45Texture; using GL45VariableAllocationTexture = GL45Backend::GL45VariableAllocationTexture; GL45VariableAllocationTexture::GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture) { @@ -40,6 +41,12 @@ GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { Backend::textureResourcePopulatedGPUMemSize.update(_populatedSize, 0); } +const GL45Texture::Bindless& GL45VariableAllocationTexture::getBindless() const { + auto& result = Parent::getBindless(); + _bindless.minMip = _populatedMip - _allocatedMip; + return result; +} + Size GL45VariableAllocationTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const { Size amountCopied = 0; amountCopied = Parent::copyMipFaceLinesFromTexture(mip, face, size, yOffset, internalFormat, format, type, sourceSize, sourcePointer); @@ -127,7 +134,9 @@ Size GL45ResourceTexture::copyMipsFromTexture() { void GL45ResourceTexture::syncSampler() const { Parent::syncSampler(); - glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); + if (!isBindless()) { + glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); + } } void GL45ResourceTexture::promote() { @@ -137,6 +146,10 @@ void GL45ResourceTexture::promote() { uint16_t targetAllocatedMip = _allocatedMip - std::min(_allocatedMip, 2); targetAllocatedMip = std::max(_minAllocatedMip, targetAllocatedMip); + if (isBindless()) { + releaseBindless(); + } + GLuint oldId = _id; auto oldSize = _size; uint16_t oldAllocatedMip = _allocatedMip; @@ -170,6 +183,10 @@ void GL45ResourceTexture::demote() { auto oldSize = _size; auto oldPopulatedMip = _populatedMip; + if (isBindless()) { + releaseBindless(); + } + // allocate new texture const_cast(_id) = allocate(_gpuObject); uint16_t oldAllocatedMip = _allocatedMip; diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index ad3dc5fada..096d6368f8 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -17,8 +17,10 @@ #include #include +#include #include #include +#include #include "Forward.h" #include "Resource.h" #include "Metric.h" @@ -126,12 +128,23 @@ public: uint8 _wrapModeV = WRAP_REPEAT; uint8 _wrapModeW = WRAP_REPEAT; - uint8 _mipOffset = 0; uint8 _minMip = 0; uint8 _maxMip = MAX_MIP_LEVEL; Desc() {} Desc(const Filter filter, const WrapMode wrap = WRAP_REPEAT) : _filter(filter), _wrapModeU(wrap), _wrapModeV(wrap), _wrapModeW(wrap) {} + + bool operator==(const Desc& other) const { + return _borderColor == other._borderColor && + _maxAnisotropy == other._maxAnisotropy && + _filter == other._filter && + _comparisonFunc == other._comparisonFunc && + _wrapModeU == other._wrapModeU && + _wrapModeV == other._wrapModeV && + _wrapModeW == other._wrapModeW && + _minMip == other._minMip && + _maxMip == other._maxMip; + } }; Sampler() {} @@ -151,11 +164,17 @@ public: ComparisonFunction getComparisonFunction() const { return ComparisonFunction(_desc._comparisonFunc); } bool doComparison() const { return getComparisonFunction() != ALWAYS; } - uint8 getMipOffset() const { return _desc._mipOffset; } uint8 getMinMip() const { return _desc._minMip; } uint8 getMaxMip() const { return _desc._maxMip; } const Desc& getDesc() const { return _desc; } + + bool operator==(const Sampler& other) const { + return _desc == other._desc; + } + bool operator!=(const Sampler& other) const { + return !(*this == other); + } protected: Desc _desc; }; @@ -667,6 +686,17 @@ typedef std::shared_ptr< TextureSource > TextureSourcePointer; }; +namespace std { + template<> struct hash { + size_t operator()(const gpu::Sampler& sampler) const noexcept { + size_t result = 0; + const auto& desc = sampler.getDesc(); + hash_combine(result, desc._comparisonFunc, desc._filter, desc._maxAnisotropy, desc._maxMip, desc._minMip, desc._wrapModeU, desc._wrapModeV, desc._wrapModeW); + return result; + } + }; +} + Q_DECLARE_METATYPE(gpu::TexturePointer) #endif diff --git a/libraries/gpu/src/gpu/TextureTable.h b/libraries/gpu/src/gpu/TextureTable.h index 7b278bf304..4307e60adb 100644 --- a/libraries/gpu/src/gpu/TextureTable.h +++ b/libraries/gpu/src/gpu/TextureTable.h @@ -18,7 +18,6 @@ class TextureTable { public: static const size_t COUNT = 8; using Array = std::array; - using Array = std::array; TextureTable(); TextureTable(const std::initializer_list& textures); TextureTable(const std::array& textures); diff --git a/libraries/gpu/src/gpu/TextureTable.slh b/libraries/gpu/src/gpu/TextureTable.slh index d45c8f819b..1b83f4d622 100644 --- a/libraries/gpu/src/gpu/TextureTable.slh +++ b/libraries/gpu/src/gpu/TextureTable.slh @@ -25,7 +25,4 @@ struct GPUTextureTable { #endif - - - <@endif@> From f9605cffb127cc1870c132ec17907dbcc1770c1d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 6 Mar 2018 11:20:54 -0800 Subject: [PATCH 046/438] Target available mip --- .../gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 18 ++++++++++-------- libraries/gpu/src/gpu/TextureTable.slh | 10 ++++++++++ libraries/gpu/src/gpu/Texture_ktx.cpp | 4 +++- .../render-utils/src/MaterialTextures.slh | 14 +++++++------- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index d33afc0e67..f02615c5c7 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -253,6 +253,9 @@ void GL45Texture::releaseBindless() const { void GL45Texture::recreateBindless() const { if (isBindless()) { releaseBindless(); + } else { + // Once a texture is about to become bindless, it's base mip level MUST be set to 0 + glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); } _bindless.sampler = SAMPLER_CACHE.getGLSampler(_cachedSampler); @@ -270,16 +273,15 @@ const GL45Texture::Bindless& GL45Texture::getBindless() const { void GL45Texture::syncSampler() const { const Sampler& sampler = _gpuObject.getSampler(); - bool samplerChanged = _cachedSampler != sampler; - if (samplerChanged) { - _cachedSampler = sampler; - } + if (_cachedSampler == sampler) { + return; + } + + _cachedSampler = sampler; if (isBindless()) { - if (samplerChanged) { - recreateBindless(); - } - } else if (samplerChanged) { + recreateBindless(); + } else { const auto& fm = FILTER_MODES[sampler.getFilter()]; glTextureParameteri(_id, GL_TEXTURE_MIN_FILTER, fm.minFilter); glTextureParameteri(_id, GL_TEXTURE_MAG_FILTER, fm.magFilter); diff --git a/libraries/gpu/src/gpu/TextureTable.slh b/libraries/gpu/src/gpu/TextureTable.slh index 1b83f4d622..f2d89e753b 100644 --- a/libraries/gpu/src/gpu/TextureTable.slh +++ b/libraries/gpu/src/gpu/TextureTable.slh @@ -20,7 +20,17 @@ struct GPUTextureTable { #define TextureTable(index, name) layout (std140) uniform gpu_resourceTextureTable##index { GPUTextureTable name; } #define tableTex(name, slot) sampler2D(name._textures[slot].xy) +#define tableTexMinLod(name, slot) float(name._textures[slot].z) +#define tableTexValue(name, slot, uv) \ + tableTexValueLod(tableTex(matTex, albedoMap), tableTexMinLod(matTex, albedoMap), uv) + +vec4 tableTexValueLod(sampler2D sampler, float minLod, vec2 uv) { + float queryLod = textureQueryLod(sampler, uv).x; + queryLod = max(minLod, queryLod); + return textureLod(sampler, uv, queryLod); +} + #else #endif diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 754bdca445..803acbd961 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -504,6 +504,8 @@ TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) { gpuktxKeyValue._usage = Texture::Usage::Builder().withColor().withAlpha().build(); } + auto samplerDesc = gpuktxKeyValue._samplerDesc; + samplerDesc._maxMip = gpu::Sampler::MAX_MIP_LEVEL; auto texture = create(gpuktxKeyValue._usageType, type, texelFormat, @@ -513,7 +515,7 @@ TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) { 1, // num Samples header.getNumberOfSlices(), header.getNumberOfLevels(), - gpuktxKeyValue._samplerDesc); + samplerDesc); texture->setUsage(gpuktxKeyValue._usage); // Assing the mips availables diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh index ef77954fad..72db15aea9 100644 --- a/libraries/render-utils/src/MaterialTextures.slh +++ b/libraries/render-utils/src/MaterialTextures.slh @@ -67,42 +67,42 @@ SCATTERING_MAP, <@if withAlbedo@> #define albedoMap 1 vec4 fetchAlbedoMap(vec2 uv) { - return texture(tableTex(matTex, albedoMap), uv); + return tableTexValue(matTex, albedoMap, uv); } <@endif@> <@if withRoughness@> #define roughnessMap 3 float fetchRoughnessMap(vec2 uv) { - return (texture(tableTex(matTex, roughnessMap), uv).r); + return tableTexValue(matTex, roughnessMap, uv).r; } <@endif@> <@if withNormal@> #define normalMap 4 vec3 fetchNormalMap(vec2 uv) { - return texture(tableTex(matTex, normalMap), uv).xyz; + return tableTexValue(matTex, normalMap, uv).xyz; } <@endif@> <@if withMetallic@> #define metallicMap 2 float fetchMetallicMap(vec2 uv) { - return (texture(tableTex(matTex, metallicMap), uv).r); + return tableTexValue(matTex, metallicMap, uv).r; } <@endif@> <@if withEmissive@> #define emissiveMap 0 vec3 fetchEmissiveMap(vec2 uv) { - return texture(tableTex(matTex, emissiveMap), uv).rgb; + return tableTexValue(matTex, emissiveMap, uv).rgb; } <@endif@> <@if withOcclusion@> #define occlusionMap 5 float fetchOcclusionMap(vec2 uv) { - return texture(tableTex(matTex, occlusionMap), uv).r; + return tableTexValue(matTex, occlusionMap, uv).r; } <@endif@> @@ -111,7 +111,7 @@ float fetchOcclusionMap(vec2 uv) { float fetchScatteringMap(vec2 uv) { float scattering = texture(tableTex(matTex, scatteringMap), uv).r; // boolean scattering for now return max(((scattering - 0.1) / 0.9), 0.0); - return texture(tableTex(matTex, scatteringMap), uv).r; // boolean scattering for now + return tableTexValue(matTex, scatteringMap, uv).r; // boolean scattering for now } <@endif@> From fe363e7f2a9434725ee9e97b45f3c5b5292bcbca Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 6 Mar 2018 14:16:25 -0800 Subject: [PATCH 047/438] changing keybaord mapping --- .../resources/controllers/keyboardMouse.json | 8 +++---- interface/src/Application.cpp | 23 +++++++++---------- interface/src/Menu.cpp | 6 ++--- interface/src/avatar/MyAvatar.cpp | 7 ++++++ interface/src/avatar/MyAvatar.h | 2 ++ .../controllers/src/controllers/Actions.cpp | 1 + .../controllers/src/controllers/Actions.h | 1 + 7 files changed, 28 insertions(+), 20 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index b3f16a115e..f82f6276ae 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -8,8 +8,6 @@ { "from": "Keyboard.D", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.E", "when": "Keyboard.Shift", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.05 } ] }, { "from": "Keyboard.C", "when": "Keyboard.Shift", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.05 } ] }, - { "from": "Keyboard.S", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" }, - { "from": "Keyboard.W", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" }, { "comment" : "Mouse turn need to be small continuous increments", @@ -90,8 +88,8 @@ { "from": "Keyboard.W", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.S", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_BACKWARD" }, - { "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" }, - { "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" }, + { "from": "Keyboard.Shift", "to": "Actions.SPRINT" }, + { "from": "Keyboard.Control", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.Left", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.Right", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, @@ -128,7 +126,7 @@ { "from": "Keyboard.MouseWheelLeft", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.02 } ]}, { "from": "Keyboard.MouseWheelRight", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.02 } ]}, - { "from": "Keyboard.Space", "to": "Actions.SHIFT" }, + { "from": "Keyboard.Space", "to": "Actions.VERTICAL_UP" }, { "from": "Keyboard.R", "to": "Actions.ACTION1" }, { "from": "Keyboard.T", "to": "Actions.ACTION2" }, { "from": "Keyboard.Tab", "to": "Actions.ContextMenu" } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 35b7de7284..acdd77c2f2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -352,7 +352,7 @@ static const QString AVA_JSON_EXTENSION = ".ava.json"; static const QString WEB_VIEW_TAG = "noDownload=true"; static const QString ZIP_EXTENSION = ".zip"; -static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; +static const float MIRROR_FULLSCREEN_DISTANCE = 0.789f; static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; @@ -2812,12 +2812,13 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { + mirrorBodyOrientation * hmdOffset); } else { + auto boomZOffset = boomOffset * Vectors::UNIT_Z; _myCamera.setOrientation(myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); _myCamera.setPosition(myAvatar->getDefaultEyePosition() + glm::vec3(0, _raiseMirror * myAvatar->getModelScale(), 0) + (myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * - glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); + glm::vec3(0.0f, 0.0f, -1.0f) * myAvatar->getBoomLength() * _scaleMirror); } renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; } @@ -3324,8 +3325,6 @@ void Application::keyPressEvent(QKeyEvent* event) { } else { setFullscreen(nullptr); } - } else { - Menu::getInstance()->triggerOption(MenuOption::AddressBar); } break; @@ -3387,13 +3386,6 @@ void Application::keyPressEvent(QKeyEvent* event) { } break; - case Qt::Key_F: { - if (isOption) { - _physicsEngine->dumpNextStats(); - } - break; - } - case Qt::Key_Asterisk: Menu::getInstance()->triggerOption(MenuOption::DefaultSkybox); break; @@ -3413,7 +3405,11 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_S: if (isShifted && isMeta && !isOption) { Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings); - } else if (!isOption && !isShifted && isMeta) { + } + break; + + case Qt::Key_P: + if (!isOption && !isShifted && isMeta) { AudioInjectorOptions options; options.localOnly = true; options.stereo = true; @@ -4853,6 +4849,7 @@ void Application::cameraMenuChanged() { if (!isHMDMode() && _myCamera.getMode() != CAMERA_MODE_MIRROR) { _myCamera.setMode(CAMERA_MODE_MIRROR); getMyAvatar()->reset(false, false, false); // to reset any active MyAvatar::FollowHelpers + getMyAvatar()->setBoomLength(MyAvatar::ZOOM_DEFAULT); } } else if (menu->isOptionChecked(MenuOption::FirstPerson)) { if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) { @@ -5145,6 +5142,8 @@ void Application::update(float deltaTime) { myAvatar->setDriveKey(MyAvatar::ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); } + myAvatar->setSprintMode((bool)userInputMapper->getActionState(controller::Action::SPRINT)); + static const std::vector avatarControllerActions = { controller::Action::LEFT_HAND, controller::Action::RIGHT_HAND, diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8c0ac584c5..d9d9f6b124 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -225,21 +225,21 @@ Menu::Menu() { // View > First Person auto firstPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::FirstPerson, Qt::CTRL | Qt::Key_F, + viewMenu, MenuOption::FirstPerson, Qt::SHIFT | Qt::Key_F, true, qApp, SLOT(cameraMenuChanged()))); firstPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); // View > Third Person auto thirdPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::ThirdPerson, Qt::CTRL | Qt::Key_G, + viewMenu, MenuOption::ThirdPerson, Qt::SHIFT | Qt::Key_G, false, qApp, SLOT(cameraMenuChanged()))); thirdPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); // View > Mirror auto viewMirrorAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::FullscreenMirror, Qt::CTRL | Qt::Key_H, + viewMenu, MenuOption::FullscreenMirror, Qt::SHIFT | Qt::Key_H, false, qApp, SLOT(cameraMenuChanged()))); viewMirrorAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b6fa3fde96..edb70692d9 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -71,6 +71,7 @@ const float YAW_SPEED_DEFAULT = 100.0f; // degrees/sec const float PITCH_SPEED_DEFAULT = 75.0f; // degrees/sec const float MAX_BOOST_SPEED = 0.5f * DEFAULT_AVATAR_MAX_WALKING_SPEED; // action motor gets additive boost below this speed +const float AVATAR_RUN_SPEED = DEFAULT_AVATAR_MAX_WALKING_SPEED * 2.0f; const float MIN_AVATAR_SPEED = 0.05f; const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // speed is set to zero below this @@ -2821,6 +2822,12 @@ float MyAvatar::getWalkSpeed() const { return _walkSpeed.get(); } +void MyAvatar::setSprintMode(bool sprint) { + _sprint = sprint; + float sprintSpeed = _sprint ? AVATAR_RUN_SPEED : DEFAULT_AVATAR_MAX_WALKING_SPEED; + _walkSpeed.set(sprintSpeed); +} + void MyAvatar::setWalkSpeed(float value) { _walkSpeed.set(value); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index fa5206e128..e0792fc77e 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -395,6 +395,7 @@ public: // Set what driving keys are being pressed to control thrust levels void clearDriveKeys(); void setDriveKey(DriveKeys key, float val); + void setSprintMode(bool sprint); float getDriveKey(DriveKeys key) const; Q_INVOKABLE float getRawDriveKey(DriveKeys key) const; void relayDriveKeysToCharacterController(); @@ -839,6 +840,7 @@ private: mutable std::mutex _controllerPoseMapMutex; bool _hmdLeanRecenterEnabled = true; + bool _sprint = false; AnimPose _prePhysicsRoomPose; std::mutex _holdActionsMutex; std::vector _holdActions; diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index d8dd7f5e35..359ff6b33a 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -183,6 +183,7 @@ namespace controller { makeButtonPair(Action::ACTION2, "ACTION2"), makeButtonPair(Action::CONTEXT_MENU, "CONTEXT_MENU"), makeButtonPair(Action::TOGGLE_MUTE, "TOGGLE_MUTE"), + makeButtonPair(Action::SPRINT, "SPRINT") }; return availableInputs; } diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index a133d62c9f..0c77d63863 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -174,6 +174,7 @@ enum class Action { TRACKED_OBJECT_13, TRACKED_OBJECT_14, TRACKED_OBJECT_15, + SPRINT, NUM_ACTIONS }; From 6fefd1355aa53dc0fb545cb8148ac151740102f8 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 6 Mar 2018 14:19:26 -0800 Subject: [PATCH 048/438] minimize diff --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index acdd77c2f2..25a24006ae 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2818,7 +2818,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _myCamera.setPosition(myAvatar->getDefaultEyePosition() + glm::vec3(0, _raiseMirror * myAvatar->getModelScale(), 0) + (myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * - glm::vec3(0.0f, 0.0f, -1.0f) * myAvatar->getBoomLength() * _scaleMirror); + glm::vec3(0.0f, 0.0f, -1.0f) * myAvatar->getBoomLength() * _scaleMirror); } renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; } From 69af12fbc7648ab1de36034907b40c986dd02105 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 6 Mar 2018 15:47:01 -0800 Subject: [PATCH 049/438] chaning speeds --- interface/resources/controllers/keyboardMouse.json | 4 ++-- interface/src/avatar/MyAvatar.cpp | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index f82f6276ae..f0cb926167 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -6,8 +6,8 @@ { "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.A", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.D", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.E", "when": "Keyboard.Shift", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.05 } ] }, - { "from": "Keyboard.C", "when": "Keyboard.Shift", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.05 } ] }, + { "from": "Keyboard.E", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.Q", "to": "Actions.LATERAL_LEFT" }, { "comment" : "Mouse turn need to be small continuous increments", diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index edb70692d9..070f4716c5 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2190,7 +2190,8 @@ void MyAvatar::updateActionMotor(float deltaTime) { if (state == CharacterController::State::Hover || _characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { // we can fly --> support vertical motion - glm::vec3 up = (getDriveKey(TRANSLATE_Y)) * IDENTITY_UP; + float impulse = _sprint ? 4.0f : 1.0f; + glm::vec3 up = (getDriveKey(TRANSLATE_Y) * impulse) * IDENTITY_UP; direction += up; } @@ -2210,7 +2211,7 @@ void MyAvatar::updateActionMotor(float deltaTime) { float motorSpeed = glm::length(_actionMotorVelocity); float finalMaxMotorSpeed = getSensorToWorldScale() * DEFAULT_AVATAR_MAX_FLYING_SPEED; float speedGrowthTimescale = 2.0f; - float speedIncreaseFactor = 1.8f; + float speedIncreaseFactor = _sprint ? 5.0f : 1.8f; motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale, 0.0f, 1.0f) * speedIncreaseFactor; const float maxBoostSpeed = getSensorToWorldScale() * MAX_BOOST_SPEED; From a70509233e22bb3e70748d3716601e99a808c30a Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 7 Mar 2018 08:25:27 -0800 Subject: [PATCH 050/438] making small changes --- interface/src/Application.cpp | 1 - interface/src/avatar/MyAvatar.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 25a24006ae..33db441ea3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2812,7 +2812,6 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { + mirrorBodyOrientation * hmdOffset); } else { - auto boomZOffset = boomOffset * Vectors::UNIT_Z; _myCamera.setOrientation(myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); _myCamera.setPosition(myAvatar->getDefaultEyePosition() diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 070f4716c5..a72580288d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -71,7 +71,7 @@ const float YAW_SPEED_DEFAULT = 100.0f; // degrees/sec const float PITCH_SPEED_DEFAULT = 75.0f; // degrees/sec const float MAX_BOOST_SPEED = 0.5f * DEFAULT_AVATAR_MAX_WALKING_SPEED; // action motor gets additive boost below this speed -const float AVATAR_RUN_SPEED = DEFAULT_AVATAR_MAX_WALKING_SPEED * 2.0f; +const float AVATAR_RUN_SPEED = DEFAULT_AVATAR_MAX_WALKING_SPEED * 3.0f; const float MIN_AVATAR_SPEED = 0.05f; const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // speed is set to zero below this From 21781c0c40dc170446bb9b29211fc4f68b6b1ba7 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 7 Mar 2018 13:54:50 -0800 Subject: [PATCH 051/438] can keyboard help page --- .../html/img/tablet-help-keyboard.jpg | Bin 220264 -> 246957 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/interface/resources/html/img/tablet-help-keyboard.jpg b/interface/resources/html/img/tablet-help-keyboard.jpg index a62fbe945036335728d5cdd287b9125e375fb220..d0f84c17c785b7fc5cd009d141c48db7db28264f 100644 GIT binary patch literal 246957 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 z�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

gk4-WJ``%YDGz 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 G5!pnS%e1B1 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�WZ*Gu{!wJ@fHo%{DgF|4vx`gcu(E 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 X{wHUMe+T~?FaO5N|92Z)5U~CaEk9gh literal 220264 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