Merge branch 'master' of https://github.com/highfidelity/hifi into entityListDeleteSpamFix

This commit is contained in:
unknown 2018-07-12 12:46:26 -07:00
commit 765dda4cb2
37 changed files with 308 additions and 2914 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 298 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 229 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 289 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

View file

@ -66,7 +66,7 @@
<script>
var handControllerImageURL = null;
var index = 0;
var count = 4;
var count = 5;
function showKbm() {
document.getElementById("main_image").setAttribute("src", "img/tablet-help-keyboard.jpg");
@ -102,9 +102,13 @@
showHandControllers();
break;
case 2:
showGamepad();
handControllerImageURL = "img/tablet-help-windowsMR.jpg";
showHandControllers();
break;
case 3:
showGamepad();
break;
case 4:
showKbm();
break;
@ -144,7 +148,10 @@
handControllerImageURL = "img/tablet-help-oculus.jpg";
index = 0;
break;
case "windowsMR":
handControllerImageURL = "img/tablet-help-windowsMR.jpg";
index = 2;
break;
case "vive":
default:
handControllerImageURL = "img/tablet-help-vive.jpg";
@ -154,7 +161,7 @@
switch (params.defaultTab) {
case "gamepad":
showGamepad();
index = 2;
index = 3;
break;
case "handControllers":
@ -164,7 +171,7 @@
case "kbm":
default:
showKbm();
index = 3;
index = 4;
}
}
</script>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,533 +0,0 @@
//
// AssetDialogContent.qml
//
// Created by David Rowe on 19 Apr 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 1.5
import "../../controls-uit"
import "../../styles-uit"
import "../fileDialog"
Item {
// Set from OffscreenUi::assetDialog()
property alias dir: assetTableModel.folder
property alias filter: selectionType.filtersString // FIXME: Currently only supports simple filters, "*.xxx".
property int options // Not used.
property bool selectDirectory: false
// Not implemented.
//property bool saveDialog: false;
//property bool multiSelect: false;
property bool singleClickNavigate: false
HifiConstants { id: hifi }
Component.onCompleted: {
homeButton.destination = dir;
if (selectDirectory) {
d.currentSelectionIsFolder = true;
d.currentSelectionPath = assetTableModel.folder;
}
assetTableView.forceActiveFocus();
}
Item {
id: assetDialogItem
anchors.fill: parent
clip: true
MouseArea {
// Clear selection when click on internal unused area.
anchors.fill: parent
drag.target: root
onClicked: {
d.clearSelection();
frame.forceActiveFocus();
assetTableView.forceActiveFocus();
}
}
Row {
id: navControls
anchors {
top: parent.top
topMargin: hifi.dimensions.contentMargin.y
left: parent.left
}
spacing: hifi.dimensions.contentSpacing.x
GlyphButton {
id: upButton
glyph: hifi.glyphs.levelUp
width: height
size: 30
enabled: assetTableModel.parentFolder !== ""
onClicked: d.navigateUp();
}
GlyphButton {
id: homeButton
property string destination: ""
glyph: hifi.glyphs.home
size: 28
width: height
enabled: destination !== ""
//onClicked: d.navigateHome();
onClicked: assetTableModel.folder = destination;
}
}
ComboBox {
id: pathSelector
anchors {
top: parent.top
topMargin: hifi.dimensions.contentMargin.y
left: navControls.right
leftMargin: hifi.dimensions.contentSpacing.x
right: parent.right
}
z: 10
property string lastValidFolder: assetTableModel.folder
function calculatePathChoices(folder) {
var folders = folder.split("/"),
choices = [],
i, length;
if (folders[folders.length - 1] === "") {
folders.pop();
}
choices.push(folders[0]);
for (i = 1, length = folders.length; i < length; i++) {
choices.push(choices[i - 1] + "/" + folders[i]);
}
if (folders[0] === "") {
choices[0] = "/";
}
choices.reverse();
if (choices.length > 0) {
pathSelector.model = choices;
}
}
onLastValidFolderChanged: {
var folder = lastValidFolder;
calculatePathChoices(folder);
}
onCurrentTextChanged: {
var folder = currentText;
if (folder !== "/") {
folder += "/";
}
if (folder !== assetTableModel.folder) {
if (root.selectDirectory) {
currentSelection.text = currentText;
d.currentSelectionPath = currentText;
}
assetTableModel.folder = folder;
assetTableView.forceActiveFocus();
}
}
}
QtObject {
id: d
property string currentSelectionPath
property bool currentSelectionIsFolder
property var tableViewConnection: Connections { target: assetTableView; onCurrentRowChanged: d.update(); }
function update() {
var row = assetTableView.currentRow;
if (row === -1) {
if (!root.selectDirectory) {
currentSelection.text = "";
currentSelectionIsFolder = false;
}
return;
}
var rowInfo = assetTableModel.get(row);
currentSelectionPath = rowInfo.filePath;
currentSelectionIsFolder = rowInfo.fileIsDir;
if (root.selectDirectory || !currentSelectionIsFolder) {
currentSelection.text = currentSelectionPath;
} else {
currentSelection.text = "";
}
}
function navigateUp() {
if (assetTableModel.parentFolder !== "") {
assetTableModel.folder = assetTableModel.parentFolder;
return true;
}
return false;
}
function navigateHome() {
assetTableModel.folder = homeButton.destination;
return true;
}
function clearSelection() {
assetTableView.selection.clear();
assetTableView.currentRow = -1;
update();
}
}
ListModel {
id: assetTableModel
property string folder
property string parentFolder: ""
readonly property string rootFolder: "/"
onFolderChanged: {
parentFolder = calculateParentFolder();
update();
}
function calculateParentFolder() {
if (folder !== "/") {
return folder.slice(0, folder.slice(0, -1).lastIndexOf("/") + 1);
}
return "";
}
function isFolder(row) {
if (row === -1) {
return false;
}
return get(row).fileIsDir;
}
function onGetAllMappings(error, map) {
var mappings,
fileTypeFilter,
index,
path,
fileName,
fileType,
fileIsDir,
isValid,
subDirectory,
subDirectories = [],
fileNameSort,
rows = 0,
lower,
middle,
upper,
i,
length;
clear();
if (error === "") {
mappings = Object.keys(map);
fileTypeFilter = filter.replace("*", "").toLowerCase();
for (i = 0, length = mappings.length; i < length; i++) {
index = mappings[i].lastIndexOf("/");
path = mappings[i].slice(0, mappings[i].lastIndexOf("/") + 1);
fileName = mappings[i].slice(path.length);
fileType = fileName.slice(fileName.lastIndexOf("."));
fileIsDir = false;
isValid = false;
if (fileType.toLowerCase() === fileTypeFilter) {
if (path === folder) {
isValid = !selectDirectory;
} else if (path.length > folder.length) {
subDirectory = path.slice(folder.length);
index = subDirectory.indexOf("/");
if (index === subDirectory.lastIndexOf("/")) {
fileName = subDirectory.slice(0, index);
if (subDirectories.indexOf(fileName) === -1) {
fileIsDir = true;
isValid = true;
subDirectories.push(fileName);
}
}
}
}
if (isValid) {
fileNameSort = (fileIsDir ? "*" : "") + fileName.toLowerCase();
lower = 0;
upper = rows;
while (lower < upper) {
middle = Math.floor((lower + upper) / 2);
var lessThan;
if (fileNameSort < get(middle)["fileNameSort"]) {
lessThan = true;
upper = middle;
} else {
lessThan = false;
lower = middle + 1;
}
}
insert(lower, {
fileName: fileName,
filePath: path + (fileIsDir ? "" : fileName),
fileIsDir: fileIsDir,
fileNameSort: fileNameSort
});
rows++;
}
}
} else {
console.log("Error getting mappings from Asset Server");
}
}
function update() {
d.clearSelection();
clear();
Assets.getAllMappings(onGetAllMappings);
}
}
Table {
id: assetTableView
colorScheme: hifi.colorSchemes.light
anchors {
top: navControls.bottom
topMargin: hifi.dimensions.contentSpacing.y
left: parent.left
right: parent.right
bottom: currentSelection.top
bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height
}
model: assetTableModel
focus: true
onClicked: {
if (singleClickNavigate) {
navigateToRow(row);
}
}
onDoubleClicked: navigateToRow(row);
Keys.onReturnPressed: navigateToCurrentRow();
Keys.onEnterPressed: navigateToCurrentRow();
itemDelegate: Item {
clip: true
FiraSansSemiBold {
text: styleData.value
elide: styleData.elideMode
anchors {
left: parent.left
leftMargin: hifi.dimensions.tablePadding
right: parent.right
rightMargin: hifi.dimensions.tablePadding
verticalCenter: parent.verticalCenter
}
size: hifi.fontSizes.tableText
color: hifi.colors.baseGrayHighlight
font.family: (styleData.row !== -1 && assetTableView.model.get(styleData.row).fileIsDir)
? "Fira Sans SemiBold" : "Fira Sans"
}
}
TableViewColumn {
id: fileNameColumn
role: "fileName"
title: "Name"
width: assetTableView.width
movable: false
resizable: false
}
function navigateToRow(row) {
currentRow = row;
navigateToCurrentRow();
}
function navigateToCurrentRow() {
if (model.isFolder(currentRow)) {
model.folder = model.get(currentRow).filePath;
} else {
okAction.trigger();
}
}
Timer {
id: prefixClearTimer
interval: 1000
repeat: false
running: false
onTriggered: assetTableView.prefix = "";
}
property string prefix: ""
function addToPrefix(event) {
if (!event.text || event.text === "") {
return false;
}
var newPrefix = prefix + event.text.toLowerCase();
var matchedIndex = -1;
for (var i = 0; i < model.count; ++i) {
var name = model.get(i).fileName.toLowerCase();
if (0 === name.indexOf(newPrefix)) {
matchedIndex = i;
break;
}
}
if (matchedIndex !== -1) {
assetTableView.selection.clear();
assetTableView.selection.select(matchedIndex);
assetTableView.currentRow = matchedIndex;
assetTableView.prefix = newPrefix;
}
prefixClearTimer.restart();
return true;
}
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Backspace:
case Qt.Key_Tab:
case Qt.Key_Backtab:
event.accepted = false;
break;
default:
if (addToPrefix(event)) {
event.accepted = true
} else {
event.accepted = false;
}
break;
}
}
}
TextField {
id: currentSelection
label: selectDirectory ? "Directory:" : "File name:"
anchors {
left: parent.left
right: selectionType.visible ? selectionType.left: parent.right
rightMargin: selectionType.visible ? hifi.dimensions.contentSpacing.x : 0
bottom: buttonRow.top
bottomMargin: hifi.dimensions.contentSpacing.y
}
readOnly: true
activeFocusOnTab: !readOnly
onActiveFocusChanged: if (activeFocus) { selectAll(); }
onAccepted: okAction.trigger();
}
FileTypeSelection {
id: selectionType
anchors {
top: currentSelection.top
left: buttonRow.left
right: parent.right
}
visible: !selectDirectory && filtersCount > 1
KeyNavigation.left: assetTableView
KeyNavigation.right: openButton
}
Action {
id: okAction
text: currentSelection.text && root.selectDirectory && assetTableView.currentRow === -1 ? "Choose" : "Open"
enabled: currentSelection.text || !root.selectDirectory && d.currentSelectionIsFolder ? true : false
onTriggered: {
if (!root.selectDirectory && !d.currentSelectionIsFolder
|| root.selectDirectory && assetTableView.currentRow === -1) {
selectedAsset(d.currentSelectionPath);
root.destroy();
} else {
assetTableView.navigateToCurrentRow();
}
}
}
Action {
id: cancelAction
text: "Cancel"
onTriggered: {
canceled();
root.destroy();
}
}
Row {
id: buttonRow
anchors {
right: parent.right
bottom: parent.bottom
}
spacing: hifi.dimensions.contentSpacing.y
Button {
id: openButton
color: hifi.buttons.blue
action: okAction
Keys.onReturnPressed: okAction.trigger()
KeyNavigation.up: selectionType
KeyNavigation.left: selectionType
KeyNavigation.right: cancelButton
}
Button {
id: cancelButton
action: cancelAction
KeyNavigation.up: selectionType
KeyNavigation.left: openButton
KeyNavigation.right: assetTableView.contentItem
Keys.onReturnPressed: { canceled(); root.enabled = false }
}
}
}
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Backspace:
event.accepted = d.navigateUp();
break;
case Qt.Key_Home:
event.accepted = d.navigateHome();
break;
}
}
}

View file

@ -129,7 +129,7 @@ Rectangle {
}
onAppInstalled: {
if (appHref === root.itemHref) {
if (appID === root.itemId) {
root.isInstalled = true;
}
}

View file

@ -67,13 +67,13 @@ Item {
}
onAppInstalled: {
if (appHref === root.itemHref) {
if (appID === root.itemId) {
root.isInstalled = true;
}
}
onAppUninstalled: {
if (appHref === root.itemHref) {
if (appID === root.itemId) {
root.isInstalled = false;
}
}

View file

@ -98,7 +98,7 @@ Rectangle {
}
onAppInstalled: {
root.installedApps = Commerce.getInstalledApps();
root.installedApps = Commerce.getInstalledApps(appID);
}
onAppUninstalled: {

View file

@ -3220,6 +3220,7 @@ void Application::setSettingConstrainToolbarPosition(bool setting) {
void Application::showHelp() {
static const QString HAND_CONTROLLER_NAME_VIVE = "vive";
static const QString HAND_CONTROLLER_NAME_OCULUS_TOUCH = "oculus";
static const QString HAND_CONTROLLER_NAME_WINDOWS_MR = "windowsMR";
static const QString TAB_KEYBOARD_MOUSE = "kbm";
static const QString TAB_GAMEPAD = "gamepad";
@ -3234,9 +3235,13 @@ void Application::showHelp() {
} else if (PluginUtils::isOculusTouchControllerAvailable()) {
defaultTab = TAB_HAND_CONTROLLERS;
handControllerName = HAND_CONTROLLER_NAME_OCULUS_TOUCH;
} else if (qApp->getActiveDisplayPlugin()->getName() == "WindowMS") {
defaultTab = TAB_HAND_CONTROLLERS;
handControllerName = HAND_CONTROLLER_NAME_WINDOWS_MR;
} else if (PluginUtils::isXboxControllerAvailable()) {
defaultTab = TAB_GAMEPAD;
}
// TODO need some way to detect windowsMR to load controls reference default tab in Help > Controls Reference menu.
QUrlQuery queryString;
queryString.addQueryItem("handControllerName", handControllerName);
@ -3262,13 +3267,22 @@ void Application::resizeGL() {
// Set the desired FBO texture size. If it hasn't changed, this does nothing.
// Otherwise, it must rebuild the FBOs
uvec2 framebufferSize = displayPlugin->getRecommendedRenderSize();
float renderResolutionScale = getRenderResolutionScale();
uvec2 renderSize = uvec2(vec2(framebufferSize) * renderResolutionScale);
uvec2 renderSize = uvec2(framebufferSize);
if (_renderResolution != renderSize) {
_renderResolution = renderSize;
DependencyManager::get<FramebufferCache>()->setFrameBufferSize(fromGlm(renderSize));
}
auto renderResolutionScale = getRenderResolutionScale();
if (displayPlugin->getRenderResolutionScale() != renderResolutionScale) {
auto renderConfig = _renderEngine->getConfiguration();
assert(renderConfig);
auto mainView = renderConfig->getConfig("RenderMainView.RenderDeferredTask");
assert(mainView);
mainView->setProperty("resolutionScale", renderResolutionScale);
displayPlugin->setRenderResolutionScale(renderResolutionScale);
}
// FIXME the aspect ratio for stereo displays is incorrect based on this.
float aspectRatio = displayPlugin->getRecommendedAspectRatio();
_myCamera.setProjection(glm::perspective(glm::radians(_fieldOfView.get()), aspectRatio,
@ -3280,7 +3294,6 @@ void Application::resizeGL() {
}
DependencyManager::get<OffscreenUi>()->resize(fromGlm(displayPlugin->getRecommendedUiSize()));
displayPlugin->setRenderResolutionScale(renderResolutionScale);
}
void Application::handleSandboxStatus(QNetworkReply* reply) {

View file

@ -102,7 +102,7 @@ void Application::paintGL() {
PerformanceTimer perfTimer("renderOverlay");
// NOTE: There is no batch associated with this renderArgs
// the ApplicationOverlay class assumes it's viewport is setup to be the device size
renderArgs._viewport = glm::ivec4(0, 0, getDeviceSize() * getRenderResolutionScale());
renderArgs._viewport = glm::ivec4(0, 0, getDeviceSize());
_applicationOverlay.renderOverlay(&renderArgs);
}
@ -118,7 +118,7 @@ void Application::paintGL() {
// Primary rendering pass
auto framebufferCache = DependencyManager::get<FramebufferCache>();
finalFramebufferSize = framebufferCache->getFrameBufferSize();
// Final framebuffer that will be handled to the display-plugin
// Final framebuffer that will be handed to the display-plugin
finalFramebuffer = framebufferCache->getFramebuffer();
}

View file

@ -208,7 +208,7 @@ void QmlCommerce::alreadyOwned(const QString& marketplaceId) {
ledger->alreadyOwned(marketplaceId);
}
QString QmlCommerce::getInstalledApps() {
QString QmlCommerce::getInstalledApps(const QString& justInstalledAppID) {
QString installedAppsFromMarketplace;
QStringList runningScripts = DependencyManager::get<ScriptEngines>()->getRunningScripts();
@ -217,6 +217,18 @@ QString QmlCommerce::getInstalledApps() {
foreach(QString appFileName, apps) {
installedAppsFromMarketplace += appFileName;
installedAppsFromMarketplace += ",";
// If we were supplied a "justInstalledAppID" argument, that means we're entering this function
// to get the new list of installed apps immediately after installing an app.
// In that case, the app we installed may not yet have its associated script running -
// that task is asynchronous and takes a nonzero amount of time. This is especially true
// for apps that are not in Interface's script cache.
// Thus, we protect against deleting the .app.json from the user's disk (below)
// by skipping that check for the app we just installed.
if ((justInstalledAppID != "") && ((justInstalledAppID + ".app.json") == appFileName)) {
continue;
}
QFile appFile(_appsPath + appFileName);
if (appFile.open(QIODevice::ReadOnly)) {
QJsonDocument appFileJsonDocument = QJsonDocument::fromJson(appFile.readAll());
@ -291,7 +303,8 @@ bool QmlCommerce::installApp(const QString& itemHref) {
return false;
}
emit appInstalled(itemHref);
QFileInfo appFileInfo(appFile);
emit appInstalled(appFileInfo.baseName());
return true;
});
request->send();
@ -321,7 +334,8 @@ bool QmlCommerce::uninstallApp(const QString& itemHref) {
qCWarning(commerce) << "Couldn't delete local .app.json file during app uninstall. Continuing anyway. App filename is:" << appHref.fileName();
}
emit appUninstalled(itemHref);
QFileInfo appFileInfo(appFile);
emit appUninstalled(appFileInfo.baseName());
return true;
}

View file

@ -53,8 +53,8 @@ signals:
void contentSetChanged(const QString& contentSetHref);
void appInstalled(const QString& appHref);
void appUninstalled(const QString& appHref);
void appInstalled(const QString& appID);
void appUninstalled(const QString& appID);
protected:
Q_INVOKABLE void getWalletStatus();
@ -86,7 +86,7 @@ protected:
Q_INVOKABLE void replaceContentSet(const QString& itemHref, const QString& certificateID);
Q_INVOKABLE QString getInstalledApps();
Q_INVOKABLE QString getInstalledApps(const QString& justInstalledAppID = "");
Q_INVOKABLE bool installApp(const QString& appHref);
Q_INVOKABLE bool uninstallApp(const QString& appHref);
Q_INVOKABLE bool openApp(const QString& appHref);

View file

@ -179,7 +179,7 @@ static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPT
void ApplicationOverlay::buildFramebufferObject() {
PROFILE_RANGE(app, __FUNCTION__);
auto uiSize = glm::uvec2(glm::vec2(qApp->getUiSize()) * qApp->getRenderResolutionScale());
auto uiSize = glm::uvec2(qApp->getUiSize());
if (!_overlayFramebuffer || uiSize != _overlayFramebuffer->getSize()) {
_overlayFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("ApplicationOverlay"));
}

View file

@ -116,7 +116,7 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
auto geometryCache = DependencyManager::get<GeometryCache>();
auto textureCache = DependencyManager::get<TextureCache>();
auto size = glm::uvec2(glm::vec2(qApp->getUiSize()) * qApp->getRenderResolutionScale());
auto size = glm::uvec2(qApp->getUiSize());
int width = size.x;
int height = size.y;
mat4 legacyProjection = glm::ortho<float>(0, width, height, 0, -1000, 1000);

View file

@ -275,7 +275,7 @@ bool CompositorHelper::getReticleOverDesktop() const {
// as being over the desktop.
if (isHMD()) {
QMutexLocker locker(&_reticleLock);
glm::vec2 maxOverlayPosition = glm::vec2(_currentDisplayPlugin->getRecommendedUiSize()) * _currentDisplayPlugin->getRenderResolutionScale();
glm::vec2 maxOverlayPosition = glm::vec2(_currentDisplayPlugin->getRecommendedUiSize());
static const glm::vec2 minOverlayPosition;
if (glm::any(glm::lessThan(_reticlePositionInHMD, minOverlayPosition)) ||
glm::any(glm::greaterThan(_reticlePositionInHMD, maxOverlayPosition))) {
@ -317,7 +317,7 @@ void CompositorHelper::sendFakeMouseEvent() {
void CompositorHelper::setReticlePosition(const glm::vec2& position, bool sendFakeEvent) {
if (isHMD()) {
glm::vec2 maxOverlayPosition = glm::vec2(_currentDisplayPlugin->getRecommendedUiSize()) * _currentDisplayPlugin->getRenderResolutionScale();
glm::vec2 maxOverlayPosition = glm::vec2(_currentDisplayPlugin->getRecommendedUiSize());
// FIXME don't allow negative mouseExtra
glm::vec2 mouseExtra = (MOUSE_EXTENTS_PIXELS - maxOverlayPosition) / 2.0f;
glm::vec2 minMouse = vec2(0) - mouseExtra;

View file

@ -888,7 +888,7 @@ OpenGLDisplayPlugin::~OpenGLDisplayPlugin() {
}
void OpenGLDisplayPlugin::updateCompositeFramebuffer() {
auto renderSize = glm::uvec2(glm::vec2(getRecommendedRenderSize()) * getRenderResolutionScale());
auto renderSize = glm::uvec2(getRecommendedRenderSize());
if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) {
_compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y));
}

View file

@ -22,13 +22,14 @@ const float AnimationPropertyGroup::MAXIMUM_POSSIBLE_FRAME = 100000.0f;
bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) {
return
(a._currentFrame == b._currentFrame) &&
(a._running == b._running) &&
(a._loop == b._loop) &&
(a._hold == b._hold) &&
(a._firstFrame == b._firstFrame) &&
(a._lastFrame == b._lastFrame) &&
(a._fps == b._fps) &&
(a._allowTranslation == b._allowTranslation) &&
(a._url == b._url);
}
@ -40,6 +41,8 @@ bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b
(a._hold != b._hold) ||
(a._firstFrame != b._firstFrame) ||
(a._lastFrame != b._lastFrame) ||
(a._fps != b._fps) ||
(a._allowTranslation != b._allowTranslation) ||
(a._url != b._url);
}

View file

@ -393,34 +393,42 @@ graphics::MeshPointer DeferredLightingEffect::getSpotLightMesh() {
return _spotLightMesh;
}
void PreparePrimaryFramebuffer::run(const RenderContextPointer& renderContext, gpu::FramebufferPointer& primaryFramebuffer) {
gpu::FramebufferPointer PreparePrimaryFramebuffer::createFramebuffer(const char* name, const glm::uvec2& frameSize) {
gpu::FramebufferPointer framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(name));
auto colorFormat = gpu::Element::COLOR_SRGBA_32;
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
auto primaryColorTexture = gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
framebuffer->setRenderBuffer(0, primaryColorTexture);
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format
auto primaryDepthTexture = gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
framebuffer->setDepthStencilBuffer(primaryDepthTexture, depthFormat);
return framebuffer;
}
void PreparePrimaryFramebuffer::configure(const Config& config) {
_resolutionScale = config.resolutionScale;
}
void PreparePrimaryFramebuffer::run(const RenderContextPointer& renderContext, Output& primaryFramebuffer) {
glm::uvec2 frameSize(renderContext->args->_viewport.z, renderContext->args->_viewport.w);
glm::uvec2 scaledFrameSize(glm::vec2(frameSize) * _resolutionScale);
// Resizing framebuffers instead of re-building them seems to cause issues with threaded
// rendering
if (_primaryFramebuffer && _primaryFramebuffer->getSize() != frameSize) {
_primaryFramebuffer.reset();
if (!_primaryFramebuffer || _primaryFramebuffer->getSize() != scaledFrameSize) {
_primaryFramebuffer = createFramebuffer("deferredPrimary", scaledFrameSize);
}
if (!_primaryFramebuffer) {
_primaryFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("deferredPrimary"));
auto colorFormat = gpu::Element::COLOR_SRGBA_32;
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
auto primaryColorTexture = gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
_primaryFramebuffer->setRenderBuffer(0, primaryColorTexture);
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format
auto primaryDepthTexture = gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
_primaryFramebuffer->setDepthStencilBuffer(primaryDepthTexture, depthFormat);
}
primaryFramebuffer = _primaryFramebuffer;
// Set viewport for the rest of the scaled passes
renderContext->args->_viewport.z = scaledFrameSize.x;
renderContext->args->_viewport.w = scaledFrameSize.y;
}
void PrepareDeferred::run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {

View file

@ -93,13 +93,34 @@ private:
friend class RenderDeferredCleanup;
};
class PreparePrimaryFramebufferConfig : public render::Job::Config {
Q_OBJECT
Q_PROPERTY(float resolutionScale MEMBER resolutionScale NOTIFY dirty)
public:
float resolutionScale{ 1.0f };
signals:
void dirty();
};
class PreparePrimaryFramebuffer {
public:
using JobModel = render::Job::ModelO<PreparePrimaryFramebuffer, gpu::FramebufferPointer>;
void run(const render::RenderContextPointer& renderContext, gpu::FramebufferPointer& primaryFramebuffer);
using Output = gpu::FramebufferPointer;
using Config = PreparePrimaryFramebufferConfig;
using JobModel = render::Job::ModelO<PreparePrimaryFramebuffer, Output, Config>;
PreparePrimaryFramebuffer(float resolutionScale = 1.0f) : _resolutionScale{resolutionScale} {}
void configure(const Config& config);
void run(const render::RenderContextPointer& renderContext, Output& primaryFramebuffer);
gpu::FramebufferPointer _primaryFramebuffer;
float _resolutionScale{ 1.0f };
private:
static gpu::FramebufferPointer createFramebuffer(const char* name, const glm::uvec2& size);
};
class PrepareDeferred {

View file

@ -15,19 +15,20 @@
#define CATEGORY_COUNT 5
<@include Fade_shared.slh@>
<@include FadeObjectParams.shared.slh@>
layout(std140) uniform fadeParametersBuffer {
FadeParameters fadeParameters[CATEGORY_COUNT];
};
uniform sampler2D fadeMaskMap;
struct FadeObjectParams {
int category;
float threshold;
vec3 noiseOffset;
vec3 baseOffset;
vec3 baseInvSize;
};
vec3 getNoiseInverseSize(int category) {
return fadeParameters[category]._noiseInvSizeAndLevel.xyz;
}
float getNoiseLevel(int category) {
return fadeParameters[category]._noiseInvSizeAndLevel.w;
}
vec2 hash2D(vec3 position) {
return position.xy* vec2(0.1677, 0.221765) + position.z*0.561;
@ -40,7 +41,7 @@ float noise3D(vec3 position) {
float evalFadeNoiseGradient(FadeObjectParams params, vec3 position) {
// Do tri-linear interpolation
vec3 noisePosition = position * fadeParameters[params.category]._noiseInvSizeAndLevel.xyz + params.noiseOffset;
vec3 noisePosition = position * getNoiseInverseSize(params.category) + params.noiseOffset.xyz;
vec3 noisePositionFloored = floor(noisePosition);
vec3 noisePositionFraction = fract(noisePosition);
@ -61,11 +62,11 @@ float evalFadeNoiseGradient(FadeObjectParams params, vec3 position) {
float noise = mix(maskY.x, maskY.y, noisePositionFraction.y);
noise -= 0.5; // Center on value 0
return noise * fadeParameters[params.category]._noiseInvSizeAndLevel.w;
return noise * getNoiseLevel(params.category);
}
float evalFadeBaseGradient(FadeObjectParams params, vec3 position) {
float gradient = length((position - params.baseOffset) * params.baseInvSize.xyz);
float gradient = length((position - params.baseOffset.xyz) * params.baseInvSize.xyz);
gradient = gradient-0.5; // Center on value 0.5
gradient *= fadeParameters[params.category]._baseLevel;
return gradient;
@ -112,20 +113,14 @@ void applyFade(FadeObjectParams params, vec3 position, out vec3 emissive) {
<@func declareFadeFragmentUniform()@>
uniform int fadeCategory;
uniform vec3 fadeNoiseOffset;
uniform vec3 fadeBaseOffset;
uniform vec3 fadeBaseInvSize;
uniform float fadeThreshold;
layout(std140) uniform fadeObjectParametersBuffer {
FadeObjectParams fadeObjectParams;
};
<@endfunc@>
<@func fetchFadeObjectParams(fadeParams)@>
<$fadeParams$>.category = fadeCategory;
<$fadeParams$>.threshold = fadeThreshold;
<$fadeParams$>.noiseOffset = fadeNoiseOffset;
<$fadeParams$>.baseOffset = fadeBaseOffset;
<$fadeParams$>.baseInvSize = fadeBaseInvSize;
<$fadeParams$> = fadeObjectParams;
<@endfunc@>
<@func declareFadeFragmentVertexInput()@>
@ -139,9 +134,9 @@ in vec4 _fadeData3;
<@func fetchFadeObjectParamsInstanced(fadeParams)@>
<$fadeParams$>.category = int(_fadeData1.w);
<$fadeParams$>.threshold = _fadeData2.w;
<$fadeParams$>.noiseOffset = _fadeData1.xyz;
<$fadeParams$>.baseOffset = _fadeData2.xyz;
<$fadeParams$>.baseInvSize = _fadeData3.xyz;
<$fadeParams$>.noiseOffset = _fadeData1;
<$fadeParams$>.baseOffset = _fadeData2;
<$fadeParams$>.baseInvSize = _fadeData3;
<@endfunc@>
<@func declareFadeFragment()@>

View file

@ -13,6 +13,8 @@
#include "render/TransitionStage.h"
#include "FadeObjectParams.shared.slh"
#include <PathUtils.h>
FadeEffect::FadeEffect() {
@ -31,15 +33,8 @@ void FadeEffect::build(render::Task::TaskConcept& task, const task::Varying& edi
render::ShapePipeline::BatchSetter FadeEffect::getBatchSetter() const {
return [this](const render::ShapePipeline& shapePipeline, gpu::Batch& batch, render::Args*) {
auto program = shapePipeline.pipeline->getProgram();
auto maskMapLocation = program->getTextures().findLocation("fadeMaskMap");
auto bufferLocation = program->getUniformBuffers().findLocation("fadeParametersBuffer");
if (maskMapLocation != -1) {
batch.setResourceTexture(maskMapLocation, _maskMap);
}
if (bufferLocation != -1) {
batch.setUniformBuffer(bufferLocation, _configurations);
}
batch.setResourceTexture(render::ShapePipeline::Slot::FADE_MASK, _maskMap);
batch.setUniformBuffer(render::ShapePipeline::Slot::FADE_PARAMETERS, _configurations);
};
}
@ -50,23 +45,29 @@ render::ShapePipeline::ItemSetter FadeEffect::getItemUniformSetter() const {
auto batch = args->_batch;
auto transitionStage = scene->getStage<render::TransitionStage>(render::TransitionStage::getName());
auto& transitionState = transitionStage->getTransition(item.getTransitionId());
auto program = shapePipeline.pipeline->getProgram();
auto& uniforms = program->getUniforms();
auto fadeNoiseOffsetLocation = uniforms.findLocation("fadeNoiseOffset");
auto fadeBaseOffsetLocation = uniforms.findLocation("fadeBaseOffset");
auto fadeBaseInvSizeLocation = uniforms.findLocation("fadeBaseInvSize");
auto fadeThresholdLocation = uniforms.findLocation("fadeThreshold");
auto fadeCategoryLocation = uniforms.findLocation("fadeCategory");
if (fadeNoiseOffsetLocation >= 0 || fadeBaseInvSizeLocation >= 0 || fadeBaseOffsetLocation >= 0 || fadeThresholdLocation >= 0 || fadeCategoryLocation >= 0) {
const auto fadeCategory = FadeJob::transitionToCategory[transitionState.eventType];
batch->_glUniform1i(fadeCategoryLocation, fadeCategory);
batch->_glUniform1f(fadeThresholdLocation, transitionState.threshold);
batch->_glUniform3f(fadeNoiseOffsetLocation, transitionState.noiseOffset.x, transitionState.noiseOffset.y, transitionState.noiseOffset.z);
batch->_glUniform3f(fadeBaseOffsetLocation, transitionState.baseOffset.x, transitionState.baseOffset.y, transitionState.baseOffset.z);
batch->_glUniform3f(fadeBaseInvSizeLocation, transitionState.baseInvSize.x, transitionState.baseInvSize.y, transitionState.baseInvSize.z);
if (transitionState.paramsBuffer._size != sizeof(gpu::StructBuffer<FadeObjectParams>)) {
static_assert(sizeof(transitionState.paramsBuffer) == sizeof(gpu::StructBuffer<FadeObjectParams>), "Assuming gpu::StructBuffer is a helper class for gpu::BufferView");
transitionState.paramsBuffer = gpu::StructBuffer<FadeObjectParams>();
}
const auto fadeCategory = FadeJob::transitionToCategory[transitionState.eventType];
auto& paramsConst = static_cast<gpu::StructBuffer<FadeObjectParams>&>(transitionState.paramsBuffer).get();
if (paramsConst.category != fadeCategory
|| paramsConst.threshold != transitionState.threshold
|| glm::vec3(paramsConst.baseOffset) != transitionState.baseOffset
|| glm::vec3(paramsConst.noiseOffset) != transitionState.noiseOffset
|| glm::vec3(paramsConst.baseInvSize) != transitionState.baseInvSize) {
auto& params = static_cast<gpu::StructBuffer<FadeObjectParams>&>(transitionState.paramsBuffer).edit();
params.category = fadeCategory;
params.threshold = transitionState.threshold;
params.baseInvSize = glm::vec4(transitionState.baseInvSize, 0.0f);
params.noiseOffset = glm::vec4(transitionState.noiseOffset, 0.0f);
params.baseOffset = glm::vec4(transitionState.baseOffset, 0.0f);
}
batch->setUniformBuffer(render::ShapePipeline::Slot::FADE_OBJECT_PARAMETERS, transitionState.paramsBuffer);
}
};
}

View file

@ -0,0 +1,25 @@
// glsl / C++ compatible source as interface for FadeObjectParams
#ifdef __cplusplus
# define FOP_VEC4 glm::vec4
# define FOP_VEC2 glm::vec2
# define FOP_FLOAT32 glm::float32
# define FOP_INT32 glm::int32
#else
# define FOP_VEC4 vec4
# define FOP_VEC2 vec2
# define FOP_FLOAT32 float
# define FOP_INT32 int
#endif
struct FadeObjectParams {
FOP_VEC4 noiseOffset;
FOP_VEC4 baseOffset;
FOP_VEC4 baseInvSize;
FOP_INT32 category;
FOP_FLOAT32 threshold;
};
// <@if 1@>
// Trigger Scribe include
// <@endif@> <!def that !>
//

View file

@ -26,6 +26,7 @@
#include <render/DrawStatus.h>
#include <render/DrawSceneOctree.h>
#include <render/BlurTask.h>
#include <render/ResampleTask.h>
#include "RenderHifi.h"
#include "RenderCommonTask.h"
@ -59,8 +60,14 @@ RenderDeferredTask::RenderDeferredTask()
{
}
void RenderDeferredTask::configure(const Config& config)
{
void RenderDeferredTask::configure(const Config& config) {
// Propagate resolution scale to sub jobs who need it
auto preparePrimaryBufferConfig = config.getConfig<PreparePrimaryFramebuffer>("PreparePrimaryBuffer");
auto upsamplePrimaryBufferConfig = config.getConfig<Upsample>("PrimaryBufferUpscale");
assert(preparePrimaryBufferConfig);
assert(upsamplePrimaryBufferConfig);
preparePrimaryBufferConfig->setProperty("resolutionScale", config.resolutionScale);
upsamplePrimaryBufferConfig->setProperty("factor", 1.0f / config.resolutionScale);
}
const render::Varying RenderDeferredTask::addSelectItemJobs(JobModel& task, const char* selectionName,
@ -97,23 +104,22 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
const auto jitter = task.addJob<JitterSample>("JitterCam");
// Prepare deferred, generate the shared Deferred Frame Transform
// GPU jobs: Start preparing the primary, deferred and lighting buffer
const auto scaledPrimaryFramebuffer = task.addJob<PreparePrimaryFramebuffer>("PreparePrimaryBuffer");
// Prepare deferred, generate the shared Deferred Frame Transform. Only valid with the scaled frame buffer
const auto deferredFrameTransform = task.addJob<GenerateDeferredFrameTransform>("DeferredFrameTransform", jitter);
const auto lightingModel = task.addJob<MakeLightingModel>("LightingModel");
// GPU jobs: Start preparing the primary, deferred and lighting buffer
const auto primaryFramebuffer = task.addJob<PreparePrimaryFramebuffer>("PreparePrimaryBuffer");
const auto opaqueRangeTimer = task.addJob<BeginGPURangeTimer>("BeginOpaqueRangeTimer", "DrawOpaques");
const auto prepareDeferredInputs = PrepareDeferred::Inputs(primaryFramebuffer, lightingModel).asVarying();
const auto prepareDeferredInputs = PrepareDeferred::Inputs(scaledPrimaryFramebuffer, lightingModel).asVarying();
const auto prepareDeferredOutputs = task.addJob<PrepareDeferred>("PrepareDeferred", prepareDeferredInputs);
const auto deferredFramebuffer = prepareDeferredOutputs.getN<PrepareDeferred::Outputs>(0);
const auto lightingFramebuffer = prepareDeferredOutputs.getN<PrepareDeferred::Outputs>(1);
// draw a stencil mask in hidden regions of the framebuffer.
task.addJob<PrepareStencil>("PrepareStencil", primaryFramebuffer);
task.addJob<PrepareStencil>("PrepareStencil", scaledPrimaryFramebuffer);
// Render opaque objects in DeferredBuffer
const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel, jitter).asVarying();
@ -223,7 +229,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
task.addJob<Bloom>("Bloom", bloomInputs);
// Lighting Buffer ready for tone mapping
const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer).asVarying();
const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, scaledPrimaryFramebuffer).asVarying();
task.addJob<ToneMappingDeferred>("ToneMapping", toneMappingInputs);
{ // Debug the bounds of the rendered items, still look at the zbuffer
@ -284,6 +290,9 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
task.addJob<DebugZoneLighting>("DrawZoneStack", deferredFrameTransform);
}
// Upscale to finale resolution
const auto primaryFramebuffer = task.addJob<render::Upsample>("PrimaryBufferUpscale", scaledPrimaryFramebuffer);
// Composite the HUD and HUD overlays
task.addJob<CompositeHUD>("HUD");

View file

@ -105,11 +105,13 @@ class RenderDeferredTaskConfig : public render::Task::Config {
Q_OBJECT
Q_PROPERTY(float fadeScale MEMBER fadeScale NOTIFY dirty)
Q_PROPERTY(float fadeDuration MEMBER fadeDuration NOTIFY dirty)
Q_PROPERTY(float resolutionScale MEMBER resolutionScale NOTIFY dirty)
Q_PROPERTY(bool debugFade MEMBER debugFade NOTIFY dirty)
Q_PROPERTY(float debugFadePercent MEMBER debugFadePercent NOTIFY dirty)
public:
float fadeScale{ 0.5f };
float fadeDuration{ 3.0f };
float resolutionScale{ 1.f };
float debugFadePercent{ 0.f };
bool debugFade{ false };

View file

@ -81,3 +81,69 @@ void HalfDownsample::run(const RenderContextPointer& renderContext, const gpu::F
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
}
gpu::PipelinePointer Upsample::_pipeline;
void Upsample::configure(const Config& config) {
_factor = config.factor;
}
gpu::FramebufferPointer Upsample::getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer) {
if (_factor == 1.0f) {
return sourceFramebuffer;
}
auto resampledFramebufferSize = glm::uvec2(glm::vec2(sourceFramebuffer->getSize()) * _factor);
if (!_destinationFrameBuffer || resampledFramebufferSize != _destinationFrameBuffer->getSize()) {
_destinationFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("UpsampledOutput"));
auto sampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
auto target = gpu::Texture::createRenderBuffer(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), resampledFramebufferSize.x, resampledFramebufferSize.y, gpu::Texture::SINGLE_MIP, sampler);
_destinationFrameBuffer->setRenderBuffer(0, target);
}
return _destinationFrameBuffer;
}
void Upsample::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& resampledFrameBuffer) {
assert(renderContext->args);
assert(renderContext->args->hasViewFrustum());
RenderArgs* args = renderContext->args;
resampledFrameBuffer = getResampledFrameBuffer(sourceFramebuffer);
if (resampledFrameBuffer != sourceFramebuffer) {
if (!_pipeline) {
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS();
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
gpu::Shader::makeProgram(*program, slotBindings);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(gpu::State::DepthTest(false, false));
_pipeline = gpu::Pipeline::create(program, state);
}
const auto bufferSize = resampledFrameBuffer->getSize();
glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y };
gpu::doInBatch("Upsample::run", args->_context, [&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.setFramebuffer(resampledFrameBuffer);
batch.setViewportTransform(viewport);
batch.setProjectionTransform(glm::mat4());
batch.resetViewTransform();
batch.setPipeline(_pipeline);
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport));
batch.setResourceTexture(0, sourceFramebuffer->getRenderBuffer(0));
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
// Set full final viewport
args->_viewport = viewport;
}
}

View file

@ -36,6 +36,37 @@ namespace render {
gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer);
};
class UpsampleConfig : public render::Job::Config {
Q_OBJECT
Q_PROPERTY(float factor MEMBER factor NOTIFY dirty)
public:
float factor{ 1.0f };
signals:
void dirty();
};
class Upsample {
public:
using Config = UpsampleConfig;
using JobModel = Job::ModelIO<Upsample, gpu::FramebufferPointer, gpu::FramebufferPointer, Config>;
Upsample(float factor = 2.0f) : _factor{ factor } {}
void configure(const Config& config);
void run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& resampledFrameBuffer);
protected:
static gpu::PipelinePointer _pipeline;
gpu::FramebufferPointer _destinationFrameBuffer;
float _factor{ 2.0f };
gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer);
};
}
#endif // hifi_render_ResampleTask_h

View file

@ -95,6 +95,7 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p
slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), Slot::MAP::LIGHT_AMBIENT_MAP));
slotBindings.insert(gpu::Shader::Binding(std::string("fadeMaskMap"), Slot::MAP::FADE_MASK));
slotBindings.insert(gpu::Shader::Binding(std::string("fadeParametersBuffer"), Slot::BUFFER::FADE_PARAMETERS));
slotBindings.insert(gpu::Shader::Binding(std::string("fadeObjectParametersBuffer"), Slot::BUFFER::FADE_OBJECT_PARAMETERS));
slotBindings.insert(gpu::Shader::Binding(std::string("hazeBuffer"), Slot::BUFFER::HAZE_MODEL));
if (key.isTranslucent()) {
@ -124,6 +125,7 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p
locations->lightAmbientMapUnit = program->getTextures().findLocation("skyboxMap");
locations->fadeMaskTextureUnit = program->getTextures().findLocation("fadeMaskMap");
locations->fadeParameterBufferUnit = program->getUniformBuffers().findLocation("fadeParametersBuffer");
locations->fadeObjectParameterBufferUnit = program->getUniformBuffers().findLocation("fadeObjectParametersBuffer");
locations->hazeParameterBufferUnit = program->getUniformBuffers().findLocation("hazeBuffer");
if (key.isTranslucent()) {
locations->lightClusterGridBufferUnit = program->getUniformBuffers().findLocation("clusterGridBuffer");

View file

@ -240,6 +240,7 @@ public:
LIGHT_AMBIENT_BUFFER,
HAZE_MODEL,
FADE_PARAMETERS,
FADE_OBJECT_PARAMETERS,
LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT,
LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT,
LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT,
@ -254,9 +255,9 @@ public:
ROUGHNESS,
OCCLUSION,
SCATTERING,
FADE_MASK,
LIGHT_AMBIENT_MAP = 10,
FADE_MASK,
};
};
@ -278,6 +279,7 @@ public:
int lightAmbientMapUnit;
int fadeMaskTextureUnit;
int fadeParameterBufferUnit;
int fadeObjectParameterBufferUnit;
int hazeParameterBufferUnit;
int lightClusterGridBufferUnit;
int lightClusterContentBufferUnit;

View file

@ -42,6 +42,8 @@ namespace render {
glm::vec3 baseInvSize{ 1.f, 1.f, 1.f };
float threshold{ 0.f };
uint8_t isFinished{ 0 };
mutable gpu::BufferView paramsBuffer;
};
typedef std::shared_ptr<Transition> TransitionPointer;