From fcbf4f421af7d39f2229f667e5bbbc3916d86e78 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 12 Nov 2016 10:04:15 -0800 Subject: [PATCH 01/11] no scroll margins --- interface/resources/qml/AddressBarDialog.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 941099b7cc..c341a9ec32 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -120,8 +120,6 @@ Window { highlightMoveDuration: -1; highlightMoveVelocity: -1; highlight: Rectangle { color: "transparent"; border.width: 4; border.color: "#1DB5ED"; z: 1; } - leftMargin: 50; // Start the first item over by about the same amount as the last item peeks through on the other side. - rightMargin: 50; } Image { // Just a visual indicator that the user can swipe the cards over to see more. source: "../images/Swipe-Icon-single.svg" From 17428d7ecbf11a953d893b7703ad08b4c2602658 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 12 Nov 2016 15:23:11 -0800 Subject: [PATCH 02/11] tab-like filtering of suggestions by type --- interface/resources/qml/AddressBarDialog.qml | 61 +++++++++++++++----- interface/resources/qml/hifi/TextButton.qml | 43 ++++++++++++++ 2 files changed, 90 insertions(+), 14 deletions(-) create mode 100644 interface/resources/qml/hifi/TextButton.qml diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index c341a9ec32..96492c2cec 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -24,7 +24,7 @@ Window { HifiStyles.HifiConstants { id: hifiStyleConstants } objectName: "AddressBarDialog" - title: "Go To" + title: "Go To:" shown: false destroyOnHidden: false @@ -131,6 +131,38 @@ Window { } } + Row { + spacing: 2 * hifi.layout.spacing; + anchors { + top: parent.top; + left: parent.left; + leftMargin: 75; + topMargin: -35; + } + property var selected: allTab; + TextButton { + id: allTab; + text: "All"; + property string includeActions: 'snapshot,concurrency'; + selected: allTab === selectedTab; + action: tabSelect; + } + TextButton { + id: placeTab; + text: "Places"; + property string includeActions: 'concurrency'; + selected: placeTab === selectedTab; + action: tabSelect; + } + TextButton { + id: snapsTab; + text: "Snaps"; + property string includeActions: 'snapshot'; + selected: snapsTab === selectedTab; + action: tabSelect; + } + } + Image { id: backgroundImage source: "../images/address-bar.svg" @@ -369,11 +401,15 @@ Window { return true; } return (place.place_name !== AddressManager.hostname); // Not our entry, but do show other entry points to current domain. - // could also require right protocolVersion + } + property var selectedTab: allTab; + function tabSelect(textButton) { + selectedTab = textButton; + fillDestinations(); } function getUserStoryPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model var options = [ - 'include_actions=snapshot,concurrency', + 'include_actions=' + selectedTab.includeActions, 'protocol=' + encodeURIComponent(AddressManager.protocolVersion()), 'page=' + pageNumber ]; @@ -386,21 +422,14 @@ Window { return makeModelData(story, url); }); allStories = allStories.concat(stories); - if (!addressLine.text) { // Don't add if the user is already filtering - stories.forEach(function (story) { - if (suggestable(story)) { - suggestions.append(story); - } - }); - } + stories.forEach(makeFilteredPlaceProcessor()); if ((data.current_page < data.total_pages) && (data.current_page <= 10)) { // just 10 pages = 100 stories for now return getUserStoryPage(pageNumber + 1, cb); } cb(); }); } - function filterChoicesByText() { - suggestions.clear(); + function makeFilteredPlaceProcessor() { // answer a function(placeData) that adds it to suggestions if it matches var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity), data = allStories; function matches(place) { @@ -411,11 +440,15 @@ Window { return place.searchText.indexOf(word) >= 0; }); } - data.forEach(function (place) { + return function (place) { if (matches(place)) { suggestions.append(place); } - }); + }; + } + function filterChoicesByText() { + suggestions.clear(); + allStories.forEach(makeFilteredPlaceProcessor()); } function fillDestinations() { diff --git a/interface/resources/qml/hifi/TextButton.qml b/interface/resources/qml/hifi/TextButton.qml new file mode 100644 index 0000000000..99fcffa8f7 --- /dev/null +++ b/interface/resources/qml/hifi/TextButton.qml @@ -0,0 +1,43 @@ +// +// TextButton.qml +// +// Created by Howard Stearns 11/12/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 Hifi 1.0 +import QtQuick 2.4 +import "../styles-uit" + +Rectangle { + property alias text: label.text; + property alias pixelSize: label.font.pixelSize; + property bool selected: false; + property int spacing: 2; + property var action: function () { }; + width: label.width + (4 * spacing); + height: label.height + spacing; + radius: 2 * spacing; + color: selected ? "grey" : "transparent"; + HifiConstants { id: hifi; } + RalewaySemiBold { + id: label; + color: hifi.colors.white; + font.pixelSize: 25; + anchors { + horizontalCenter: parent.horizontalCenter; + verticalCenter: parent.verticalCenter; + } + } + MouseArea { + anchors.fill: parent; + acceptedButtons: Qt.LeftButton; + onClicked: action(parent); + hoverEnabled: true; + onEntered: label.color = "#1DB5ED"; + onExited: label.color = hifi.colors.white; + } + +} From fac5d1e12eaa22283a1a11956fe450a4c2975ed8 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 12 Nov 2016 17:00:54 -0800 Subject: [PATCH 03/11] group snapshots by place --- interface/resources/qml/AddressBarDialog.qml | 24 ++++++++++++++++++-- interface/resources/qml/hifi/Card.qml | 5 ++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 941099b7cc..572b36967c 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -114,6 +114,7 @@ Window { timestamp: model.created_at; onlineUsers: model.online_users; storyId: model.metaverseId; + drillDownToPlace: model.drillDownToPlace; hoverThunk: function () { ListView.view.currentIndex = index; } unhoverThunk: function () { ListView.view.currentIndex = -1; } } @@ -362,6 +363,7 @@ Window { tags: tags, description: description, online_users: data.details.concurrency || 0, + drillDownToPlace: false, searchText: [name].concat(tags, description || []).join(' ').toUpperCase() } @@ -373,6 +375,22 @@ Window { return (place.place_name !== AddressManager.hostname); // Not our entry, but do show other entry points to current domain. // could also require right protocolVersion } + property var placeMap: ({}); + function addToSuggestions(place) { + // This version always collapses. But, if we have a tabbed interface as in #9061, we might only collapse on all. + var collapse = place.action !== 'concurrency'; + if (collapse) { + var existing = placeMap[place.place_name]; + if (existing) { + existing.drillDownToPlace = true; + return; + } + } + suggestions.append(place); + if (collapse) { + placeMap[place.place_name] = suggestions.get(suggestions.count - 1); + } + } function getUserStoryPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model var options = [ 'include_actions=snapshot,concurrency', @@ -391,7 +409,7 @@ Window { if (!addressLine.text) { // Don't add if the user is already filtering stories.forEach(function (story) { if (suggestable(story)) { - suggestions.append(story); + addToSuggestions(story); } }); } @@ -403,6 +421,7 @@ Window { } function filterChoicesByText() { suggestions.clear(); + placeMap = {}; var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity), data = allStories; function matches(place) { @@ -415,7 +434,7 @@ Window { } data.forEach(function (place) { if (matches(place)) { - suggestions.append(place); + addToSuggestions(place); } }); } @@ -423,6 +442,7 @@ Window { function fillDestinations() { allStories = []; suggestions.clear(); + placeMap = {}; getUserStoryPage(1, function (error) { console.log('user stories query', error || 'ok', allStories.length); }); diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 70eab82910..51765fe2c2 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -27,6 +27,7 @@ Rectangle { property var goFunction: null; property string storyId: ""; + property bool drillDownToPlace: false; property string timePhrase: pastTime(timestamp); property int onlineUsers: 0; @@ -113,7 +114,7 @@ Rectangle { } FiraSansRegular { id: users; - text: (action === 'concurrency') ? onlineUsers : 'snapshot'; + text: (action === 'concurrency') ? onlineUsers : ('snapshot' + (drillDownToPlace ? 's' : '')); size: (action === 'concurrency') ? textSize : textSizeSmall; color: hifi.colors.white; anchors { @@ -140,7 +141,7 @@ Rectangle { id: usersImage; imageURL: "../../images/" + action + ".svg"; size: 32; - onClicked: goFunction("/user_stories/" + storyId); + onClicked: goFunction(drillDownToPlace ? ("/places/" + placeName) : ("/user_stories/" + storyId)); buttonState: 0; defaultState: 0; hoverState: 1; From eb401c125a2d92290cb397cca7fb1e9f2ca65515 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 14 Nov 2016 17:52:18 -0800 Subject: [PATCH 04/11] Bug fix for blank webEntities during long sessions In prolonged sessions _currentWebCount would reach MAX_CONCURRENT_WEB_VIEWS and prevent new webEntities from being properly created and initialized. The _currentWebCount inside of RenderableWebEntityItem would become inflated when buildWebSurface is called without a currentContext. The early return did not properly take this into account. Instead we move the increment after the early return and right before the _webSurface is actually created. --- .../entities-renderer/src/RenderableWebEntityItem.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index f426f4a816..e9015e265a 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -69,8 +69,6 @@ bool RenderableWebEntityItem::buildWebSurface(QSharedPointer qWarning() << "Too many concurrent web views to create new view"; return false; } - qDebug() << "Building web surface"; - QString javaScriptToInject; QFile webChannelFile(":qtwebchannel/qwebchannel.js"); QFile createGlobalEventBridgeFile(PathUtils::resourcesPath() + "/html/createGlobalEventBridge.js"); @@ -85,12 +83,15 @@ bool RenderableWebEntityItem::buildWebSurface(QSharedPointer qCWarning(entitiesrenderer) << "unable to find qwebchannel.js or createGlobalEventBridge.js"; } - ++_currentWebCount; // Save the original GL context, because creating a QML surface will create a new context QOpenGLContext * currentContext = QOpenGLContext::currentContext(); if (!currentContext) { return false; } + + ++_currentWebCount; + qDebug() << "Building web surface: " << getID() << ", #" << _currentWebCount << ", url = " << _sourceUrl; + QSurface * currentSurface = currentContext->surface(); auto deleter = [](OffscreenQmlSurface* webSurface) { @@ -356,6 +357,8 @@ void RenderableWebEntityItem::destroyWebSurface() { QObject::disconnect(_hoverLeaveConnection); _hoverLeaveConnection = QMetaObject::Connection(); _webSurface.reset(); + + qDebug() << "Delete web surface: " << getID() << ", #" << _currentWebCount << ", url = " << _sourceUrl; } } From 9809515853d1c49ee494aa57c740c0f4ecebf915 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 15 Nov 2016 14:12:27 -0800 Subject: [PATCH 05/11] styling --- interface/resources/qml/AddressBarDialog.qml | 13 ++- interface/resources/qml/hifi/Card.qml | 115 +++++++++++++------ interface/resources/qml/hifi/TextButton.qml | 16 +-- 3 files changed, 100 insertions(+), 44 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 8474ea1e09..088b34432a 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -33,6 +33,7 @@ Window { width: addressBarDialog.implicitWidth height: addressBarDialog.implicitHeight + property int gap: 14 onShownChanged: { addressBarDialog.keyboardEnabled = HMD.active; @@ -65,7 +66,7 @@ Window { clearAddressLineTimer.start(); } property var allStories: []; - property int cardWidth: 200; + property int cardWidth: 212; property int cardHeight: 152; property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/"; property bool isCursorVisible: false // Override default cursor visibility. @@ -78,7 +79,7 @@ Window { property bool punctuationMode: false implicitWidth: backgroundImage.width - implicitHeight: backgroundImage.height + (keyboardEnabled ? keyboard.height : 0) + cardHeight; + implicitHeight: scroll.height + backgroundImage.height + (keyboardEnabled ? keyboard.height : 0); // The buttons have their button state changed on hover, so we have to manually fix them up here onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0; @@ -93,8 +94,9 @@ Window { ListView { id: scroll width: backgroundImage.width; - height: cardHeight; - spacing: hifi.layout.spacing; + height: cardHeight + scroll.stackedCardShadowHeight + property int stackedCardShadowHeight: 20; + spacing: gap; clip: true; anchors { bottom: backgroundImage.top @@ -115,6 +117,7 @@ Window { onlineUsers: model.online_users; storyId: model.metaverseId; drillDownToPlace: model.drillDownToPlace; + shadowHeight: scroll.stackedCardShadowHeight; hoverThunk: function () { ListView.view.currentIndex = index; } unhoverThunk: function () { ListView.view.currentIndex = -1; } } @@ -137,7 +140,7 @@ Window { anchors { top: parent.top; left: parent.left; - leftMargin: 75; + leftMargin: 150; topMargin: -35; } property var selected: allTab; diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 51765fe2c2..11a2901661 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -28,13 +28,19 @@ Rectangle { property string storyId: ""; property bool drillDownToPlace: false; + property bool showPlace: isConcurrency; + property string messageColor: "#1DB5ED"; property string timePhrase: pastTime(timestamp); property int onlineUsers: 0; + property bool isConcurrency: action === 'concurrency'; + property bool isStacked: !isConcurrency && drillDownToPlace; property int textPadding: 10; + property int messageHeight: 40; property int textSize: 24; property int textSizeSmall: 18; property string defaultThumbnail: Qt.resolvedUrl("../../images/default-domain.gif"); + property int shadowHeight: 20; HifiConstants { id: hifi } function pastTime(timestamp) { // Answer a descriptive string @@ -60,13 +66,16 @@ Rectangle { Image { id: lobby; - width: parent.width; - height: parent.height; + width: parent.width - (isConcurrency ? 0 : 8); + height: parent.height - messageHeight - (isConcurrency ? 0 : 4); source: thumbnail || defaultThumbnail; fillMode: Image.PreserveAspectCrop; // source gets filled in later - anchors.verticalCenter: parent.verticalCenter; - anchors.left: parent.left; + anchors { + horizontalCenter: parent.horizontalCenter; + top: parent.top; + topMargin: isConcurrency ? 0 : 4; + } onStatusChanged: { if (status == Image.Error) { console.log("source: " + source + ": failed to load " + hifiUrl); @@ -74,13 +83,41 @@ Rectangle { } } } + Rectangle { + id: shadow1; + visible: isStacked; + width: parent.width - 5; + height: shadowHeight / 2; + anchors { + top: parent.bottom; + horizontalCenter: parent.horizontalCenter; + } + gradient: Gradient { + GradientStop { position: 0.0; color: "gray" } + GradientStop { position: 1.0; color: "white" } + } + } + Rectangle { + id: shadow2; + visible: isStacked; + width: shadow1.width - 5; + height: shadowHeight / 2; + anchors { + top: shadow1.bottom; + horizontalCenter: parent.horizontalCenter; + } + gradient: Gradient { + GradientStop { position: 0.0; color: "gray" } + GradientStop { position: 1.0; color: "white" } + } + } property int dropHorizontalOffset: 0; property int dropVerticalOffset: 1; property int dropRadius: 2; property int dropSamples: 9; property int dropSpread: 0; DropShadow { - visible: desktop.gradientsSupported; + visible: showPlace && desktop.gradientsSupported; source: place; anchors.fill: place; horizontalOffset: dropHorizontalOffset; @@ -90,19 +127,9 @@ Rectangle { color: hifi.colors.black; spread: dropSpread; } - DropShadow { - visible: users.visible && desktop.gradientsSupported; - source: users; - anchors.fill: users; - horizontalOffset: dropHorizontalOffset; - verticalOffset: dropVerticalOffset; - radius: dropRadius; - samples: dropSamples; - color: hifi.colors.black; - spread: dropSpread; - } RalewaySemiBold { id: place; + visible: showPlace; text: placeName; color: hifi.colors.white; size: textSize; @@ -112,15 +139,31 @@ Rectangle { margins: textPadding; } } - FiraSansRegular { - id: users; - text: (action === 'concurrency') ? onlineUsers : ('snapshot' + (drillDownToPlace ? 's' : '')); - size: (action === 'concurrency') ? textSize : textSizeSmall; - color: hifi.colors.white; + Row { + FiraSansRegular { + id: users; + visible: isConcurrency; + text: onlineUsers; + size: textSize; + color: messageColor; + anchors.verticalCenter: message.verticalCenter; + } + RalewaySemiBold { + id: message; + text: isConcurrency ? ((onlineUsers === 1) ? "person" : "people") : (drillDownToPlace ? "snapshots" : ("by " + userName)); + size: textSizeSmall; + color: messageColor; + anchors { + bottom: parent.bottom; + bottomMargin: parent.spacing; + } + } + spacing: textPadding; + height: messageHeight; anchors { - verticalCenter: usersImage.verticalCenter; - right: usersImage.left; - margins: textPadding; + bottom: parent.bottom; + left: parent.left; + leftMargin: textPadding; } } // These two can be supplied to provide hover behavior. @@ -129,7 +172,6 @@ Rectangle { property var hoverThunk: function () { }; property var unhoverThunk: function () { }; MouseArea { - id: zmouseArea; anchors.fill: parent; acceptedButtons: Qt.LeftButton; onClicked: goFunction("hifi://" + hifiUrl); @@ -137,18 +179,27 @@ Rectangle { onEntered: hoverThunk(); onExited: unhoverThunk(); } - ToolbarButton { - id: usersImage; + StateImage { + id: actionIcon; imageURL: "../../images/" + action + ".svg"; size: 32; - onClicked: goFunction(drillDownToPlace ? ("/places/" + placeName) : ("/user_stories/" + storyId)); - buttonState: 0; - defaultState: 0; - hoverState: 1; + buttonState: 1; anchors { bottom: parent.bottom; right: parent.right; - margins: textPadding; + margins: 4; } } + MouseArea { + width: parent.width; + height: messageHeight; + anchors { + top: lobby.bottom; + } + acceptedButtons: Qt.LeftButton; + onClicked: goFunction(drillDownToPlace ? ("/places/" + placeName) : ("/user_stories/" + storyId)); + hoverEnabled: true; + onEntered: actionIcon.buttonState = 0; + onExited: actionIcon.buttonState = 1; + } } diff --git a/interface/resources/qml/hifi/TextButton.qml b/interface/resources/qml/hifi/TextButton.qml index 99fcffa8f7..613ff617e1 100644 --- a/interface/resources/qml/hifi/TextButton.qml +++ b/interface/resources/qml/hifi/TextButton.qml @@ -17,27 +17,29 @@ Rectangle { property bool selected: false; property int spacing: 2; property var action: function () { }; - width: label.width + (4 * spacing); - height: label.height + spacing; - radius: 2 * spacing; - color: selected ? "grey" : "transparent"; + property string highlightColor: "#1DB5ED"; + width: label.width + 64; + height: 32; + radius: height / 2; + border.width: (clickArea.containsMouse && !clickArea.containsPress) ? 4 : 0; + border.color: highlightColor; + color: clickArea.containsPress ? hifi.colors.darkGray : (selected ? highlightColor : "transparent"); HifiConstants { id: hifi; } RalewaySemiBold { id: label; color: hifi.colors.white; - font.pixelSize: 25; + font.pixelSize: 20; anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter; } } MouseArea { + id: clickArea; anchors.fill: parent; acceptedButtons: Qt.LeftButton; onClicked: action(parent); hoverEnabled: true; - onEntered: label.color = "#1DB5ED"; - onExited: label.color = hifi.colors.white; } } From 182050c4b94da7346c2e1a8c0b242573073a3979 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 15 Nov 2016 14:34:23 -0800 Subject: [PATCH 06/11] don't process stale query responses --- interface/resources/qml/AddressBarDialog.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 088b34432a..afbbea1eca 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -429,6 +429,7 @@ Window { suggestions.get(suggestions.count - 1).drillDownToPlace = true; // Don't change raw place object (in allStories). } } + property int requestId: 0; function getUserStoryPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model var options = [ 'include_actions=' + selectedTab.includeActions, @@ -436,8 +437,9 @@ Window { 'page=' + pageNumber ]; var url = metaverseBase + 'user_stories?' + options.join('&'); + var thisRequestId = ++requestId; getRequest(url, function (error, data) { - if (handleError(url, error, data, cb)) { + if ((thisRequestId !== requestId) || handleError(url, error, data, cb)) { return; } var stories = data.user_stories.map(function (story) { // explicit single-argument function From b0518feee42ae80440ae93b70fb80275067f9260 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 15 Nov 2016 14:59:27 -0800 Subject: [PATCH 07/11] split ToolbarButton.qml into a stateful image part --- .../qml/hifi/toolbars/StateImage.qml | 34 +++++++++++++++++++ .../qml/hifi/toolbars/ToolbarButton.qml | 30 +--------------- 2 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 interface/resources/qml/hifi/toolbars/StateImage.qml diff --git a/interface/resources/qml/hifi/toolbars/StateImage.qml b/interface/resources/qml/hifi/toolbars/StateImage.qml new file mode 100644 index 0000000000..44eaa6f7fd --- /dev/null +++ b/interface/resources/qml/hifi/toolbars/StateImage.qml @@ -0,0 +1,34 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +Item { + property alias imageURL: image.source + property alias alpha: image.opacity + property var subImage; + property int yOffset: 0 + property int buttonState: 0 + property real size: 50 + width: size; height: size + property bool pinned: false + clip: true + + function updateYOffset() { yOffset = size * buttonState; } + onButtonStateChanged: updateYOffset(); + + Component.onCompleted: { + if (subImage) { + if (subImage.y) { + yOffset = subImage.y; + return; + } + } + updateYOffset(); + } + + Image { + id: image + y: -parent.yOffset; + width: parent.width + } +} + diff --git a/interface/resources/qml/hifi/toolbars/ToolbarButton.qml b/interface/resources/qml/hifi/toolbars/ToolbarButton.qml index aed90cd433..bc035ca19c 100644 --- a/interface/resources/qml/hifi/toolbars/ToolbarButton.qml +++ b/interface/resources/qml/hifi/toolbars/ToolbarButton.qml @@ -1,41 +1,13 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 -Item { +StateImage { id: button - property alias imageURL: image.source - property alias alpha: image.opacity - property var subImage; - property int yOffset: 0 - property int buttonState: 0 property int hoverState: -1 property int defaultState: -1 - property var toolbar; - property real size: 50 // toolbar ? toolbar.buttonSize : 50 - width: size; height: size - property bool pinned: false - clip: true - - onButtonStateChanged: { - yOffset = size * buttonState; - } - - Component.onCompleted: { - if (subImage) { - if (subImage.y) { - yOffset = subImage.y; - } - } - } signal clicked() - Image { - id: image - y: -button.yOffset; - width: parent.width - } - Timer { id: asyncClickSender interval: 10 From 066ddad25a245236e0ca9d7993f19568d964fcb2 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 16 Nov 2016 09:39:55 -0800 Subject: [PATCH 08/11] Reduced MAX_CONCURRENT_WEB_VIEWS from 100 to 20. --- libraries/entities-renderer/src/RenderableWebEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index e9015e265a..5290f3df19 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -31,7 +31,7 @@ const float METERS_TO_INCHES = 39.3701f; static uint32_t _currentWebCount { 0 }; // Don't allow more than 100 concurrent web views -static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 100; +static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 20; // If a web-view hasn't been rendered for 30 seconds, de-allocate the framebuffer static uint64_t MAX_NO_RENDER_INTERVAL = 30 * USECS_PER_SECOND; From fef68ac53a200d0e97d6159f0d5fb4e5b316360f Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 16 Nov 2016 10:02:17 -0800 Subject: [PATCH 09/11] gap between top and scroll --- interface/resources/qml/AddressBarDialog.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index afbbea1eca..5d60821ce0 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -79,7 +79,7 @@ Window { property bool punctuationMode: false implicitWidth: backgroundImage.width - implicitHeight: scroll.height + backgroundImage.height + (keyboardEnabled ? keyboard.height : 0); + implicitHeight: scroll.height + gap + backgroundImage.height + (keyboardEnabled ? keyboard.height : 0); // The buttons have their button state changed on hover, so we have to manually fix them up here onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0; @@ -141,7 +141,7 @@ Window { top: parent.top; left: parent.left; leftMargin: 150; - topMargin: -35; + topMargin: -30; } property var selected: allTab; TextButton { From 050d3438493b6a5e6e37017a1ac6b23e2881d2c9 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 16 Nov 2016 15:54:11 -0800 Subject: [PATCH 10/11] styling changes --- .../{address-bar.svg => address-bar-856.svg} | 16 +++--- interface/resources/images/concurrency.svg | 53 ------------------- .../resources/images/info-icon-2-state.svg | 24 +++++++++ interface/resources/images/snap-icon.svg | 14 +++++ interface/resources/images/snapshot.svg | 33 ------------ interface/resources/images/swipe-chevron.svg | 39 ++++++++++++++ interface/resources/qml/AddressBarDialog.qml | 31 ++++++----- interface/resources/qml/hifi/Card.qml | 40 +++++++++----- interface/resources/qml/hifi/TextButton.qml | 4 +- 9 files changed, 133 insertions(+), 121 deletions(-) rename interface/resources/images/{address-bar.svg => address-bar-856.svg} (52%) delete mode 100644 interface/resources/images/concurrency.svg create mode 100644 interface/resources/images/info-icon-2-state.svg create mode 100644 interface/resources/images/snap-icon.svg delete mode 100644 interface/resources/images/snapshot.svg create mode 100644 interface/resources/images/swipe-chevron.svg diff --git a/interface/resources/images/address-bar.svg b/interface/resources/images/address-bar-856.svg similarity index 52% rename from interface/resources/images/address-bar.svg rename to interface/resources/images/address-bar-856.svg index 39926b017d..678e1aaf95 100644 --- a/interface/resources/images/address-bar.svg +++ b/interface/resources/images/address-bar-856.svg @@ -2,17 +2,17 @@ + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 856 100" + style="enable-background:new 0 0 856 100;" xml:space="preserve"> - - - + + + diff --git a/interface/resources/images/concurrency.svg b/interface/resources/images/concurrency.svg deleted file mode 100644 index b9e76e7d55..0000000000 --- a/interface/resources/images/concurrency.svg +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/interface/resources/images/info-icon-2-state.svg b/interface/resources/images/info-icon-2-state.svg new file mode 100644 index 0000000000..cb6c802315 --- /dev/null +++ b/interface/resources/images/info-icon-2-state.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + diff --git a/interface/resources/images/snap-icon.svg b/interface/resources/images/snap-icon.svg new file mode 100644 index 0000000000..8c6c042bec --- /dev/null +++ b/interface/resources/images/snap-icon.svg @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/interface/resources/images/snapshot.svg b/interface/resources/images/snapshot.svg deleted file mode 100644 index 7b3da80f3c..0000000000 --- a/interface/resources/images/snapshot.svg +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/interface/resources/images/swipe-chevron.svg b/interface/resources/images/swipe-chevron.svg new file mode 100644 index 0000000000..b70c8c31e2 --- /dev/null +++ b/interface/resources/images/swipe-chevron.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 5d60821ce0..bfb5295512 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -93,14 +93,14 @@ Window { ListView { id: scroll - width: backgroundImage.width; height: cardHeight + scroll.stackedCardShadowHeight - property int stackedCardShadowHeight: 20; + property int stackedCardShadowHeight: 10; spacing: gap; clip: true; anchors { + left: backgroundImage.left + right: swipe.left bottom: backgroundImage.top - horizontalCenter: backgroundImage.horizontalCenter } model: suggestions; orientation: ListView.Horizontal; @@ -123,15 +123,20 @@ Window { } highlightMoveDuration: -1; highlightMoveVelocity: -1; - highlight: Rectangle { color: "transparent"; border.width: 4; border.color: "#1DB5ED"; z: 1; } + highlight: Rectangle { color: "transparent"; border.width: 4; border.color: hifiStyleConstants.colors.blueHighlight; z: 1; } } Image { // Just a visual indicator that the user can swipe the cards over to see more. - source: "../images/Swipe-Icon-single.svg" - width: 50; + id: swipe; + source: "../images/swipe-chevron.svg"; + width: 72; visible: suggestions.count > 3; anchors { - right: scroll.right; - verticalCenter: scroll.verticalCenter; + right: backgroundImage.right; + top: scroll.top; + } + MouseArea { + anchors.fill: parent + onClicked: scroll.currentIndex = (scroll.currentIndex < 0) ? 3 : (scroll.currentIndex + 3) } } @@ -146,21 +151,21 @@ Window { property var selected: allTab; TextButton { id: allTab; - text: "All"; + text: "ALL"; property string includeActions: 'snapshot,concurrency'; selected: allTab === selectedTab; action: tabSelect; } TextButton { id: placeTab; - text: "Places"; + text: "PLACES"; property string includeActions: 'concurrency'; selected: placeTab === selectedTab; action: tabSelect; } TextButton { id: snapsTab; - text: "Snaps"; + text: "SNAPS"; property string includeActions: 'snapshot'; selected: snapsTab === selectedTab; action: tabSelect; @@ -169,8 +174,8 @@ Window { Image { id: backgroundImage - source: "../images/address-bar.svg" - width: 720 + source: "../images/address-bar-856.svg" + width: 856 height: 100 anchors { bottom: parent.keyboardEnabled ? keyboard.top : parent.bottom; diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 11a2901661..9e9b1dff51 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -18,6 +18,7 @@ import "toolbars" import "../styles-uit" Rectangle { + id: root; property string userName: ""; property string placeName: ""; property string action: ""; @@ -29,16 +30,18 @@ Rectangle { property bool drillDownToPlace: false; property bool showPlace: isConcurrency; - property string messageColor: "#1DB5ED"; + property string messageColor: hifi.colors.blueAccent; property string timePhrase: pastTime(timestamp); property int onlineUsers: 0; property bool isConcurrency: action === 'concurrency'; property bool isStacked: !isConcurrency && drillDownToPlace; property int textPadding: 10; + property int smallMargin: 4; property int messageHeight: 40; property int textSize: 24; property int textSizeSmall: 18; + property int stackShadowNarrowing: 5; property string defaultThumbnail: Qt.resolvedUrl("../../images/default-domain.gif"); property int shadowHeight: 20; HifiConstants { id: hifi } @@ -66,15 +69,15 @@ Rectangle { Image { id: lobby; - width: parent.width - (isConcurrency ? 0 : 8); - height: parent.height - messageHeight - (isConcurrency ? 0 : 4); + width: parent.width - (isConcurrency ? 0 : (2 * smallMargin)); + height: parent.height - messageHeight - (isConcurrency ? 0 : smallMargin); source: thumbnail || defaultThumbnail; fillMode: Image.PreserveAspectCrop; // source gets filled in later anchors { horizontalCenter: parent.horizontalCenter; top: parent.top; - topMargin: isConcurrency ? 0 : 4; + topMargin: isConcurrency ? 0 : smallMargin; } onStatusChanged: { if (status == Image.Error) { @@ -86,7 +89,7 @@ Rectangle { Rectangle { id: shadow1; visible: isStacked; - width: parent.width - 5; + width: parent.width - stackShadowNarrowing; height: shadowHeight / 2; anchors { top: parent.bottom; @@ -100,7 +103,7 @@ Rectangle { Rectangle { id: shadow2; visible: isStacked; - width: shadow1.width - 5; + width: shadow1.width - stackShadowNarrowing; height: shadowHeight / 2; anchors { top: shadow1.bottom; @@ -133,9 +136,11 @@ Rectangle { text: placeName; color: hifi.colors.white; size: textSize; + elide: Text.ElideRight; // requires constrained width anchors { top: parent.top; left: parent.left; + right: parent.right; margins: textPadding; } } @@ -148,11 +153,23 @@ Rectangle { color: messageColor; anchors.verticalCenter: message.verticalCenter; } - RalewaySemiBold { + Image { + id: icon; + source: "../../images/snap-icon.svg" + width: 40; + height: 40; + visible: action === 'snapshot'; + } + RalewayRegular { id: message; text: isConcurrency ? ((onlineUsers === 1) ? "person" : "people") : (drillDownToPlace ? "snapshots" : ("by " + userName)); size: textSizeSmall; color: messageColor; + elide: Text.ElideRight; // requires a width to be specified` + width: root.width - textPadding + - (users.visible ? users.width + parent.spacing : 0) + - (icon.visible ? icon.width + parent.spacing : 0) + - (actionIcon.width + (2 * smallMargin)); anchors { bottom: parent.bottom; bottomMargin: parent.spacing; @@ -181,16 +198,17 @@ Rectangle { } StateImage { id: actionIcon; - imageURL: "../../images/" + action + ".svg"; + imageURL: "../../images/info-icon-2-state.svg"; size: 32; - buttonState: 1; + buttonState: messageArea.containsMouse ? 1 : 0; anchors { bottom: parent.bottom; right: parent.right; - margins: 4; + margins: smallMargin; } } MouseArea { + id: messageArea; width: parent.width; height: messageHeight; anchors { @@ -199,7 +217,5 @@ Rectangle { acceptedButtons: Qt.LeftButton; onClicked: goFunction(drillDownToPlace ? ("/places/" + placeName) : ("/user_stories/" + storyId)); hoverEnabled: true; - onEntered: actionIcon.buttonState = 0; - onExited: actionIcon.buttonState = 1; } } diff --git a/interface/resources/qml/hifi/TextButton.qml b/interface/resources/qml/hifi/TextButton.qml index 613ff617e1..1000e7c588 100644 --- a/interface/resources/qml/hifi/TextButton.qml +++ b/interface/resources/qml/hifi/TextButton.qml @@ -17,11 +17,11 @@ Rectangle { property bool selected: false; property int spacing: 2; property var action: function () { }; - property string highlightColor: "#1DB5ED"; + property string highlightColor: hifi.colors.blueHighlight; width: label.width + 64; height: 32; radius: height / 2; - border.width: (clickArea.containsMouse && !clickArea.containsPress) ? 4 : 0; + border.width: (clickArea.containsMouse && !clickArea.containsPress) ? 3 : 0; border.color: highlightColor; color: clickArea.containsPress ? hifi.colors.darkGray : (selected ? highlightColor : "transparent"); HifiConstants { id: hifi; } From 5b57354624a9c25bea844f7fee1e772ba57d99e7 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 16 Nov 2016 17:03:24 -0800 Subject: [PATCH 11/11] crazy workaround for qt bug --- interface/resources/qml/hifi/TextButton.qml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/TextButton.qml b/interface/resources/qml/hifi/TextButton.qml index 1000e7c588..02e49d86e4 100644 --- a/interface/resources/qml/hifi/TextButton.qml +++ b/interface/resources/qml/hifi/TextButton.qml @@ -20,10 +20,7 @@ Rectangle { property string highlightColor: hifi.colors.blueHighlight; width: label.width + 64; height: 32; - radius: height / 2; - border.width: (clickArea.containsMouse && !clickArea.containsPress) ? 3 : 0; - border.color: highlightColor; - color: clickArea.containsPress ? hifi.colors.darkGray : (selected ? highlightColor : "transparent"); + color: "transparent"; HifiConstants { id: hifi; } RalewaySemiBold { id: label; @@ -34,6 +31,21 @@ Rectangle { verticalCenter: parent.verticalCenter; } } + Rectangle { + // This is crazy. All of this stuff (except the opacity) ought to be in the parent, with the label drawn on top. + // But there's a bug in QT such that if you select this TextButton, AND THEN enter the area of + // a TextButton created before this one, AND THEN enter a ListView with a highlight, then our label + // will draw as though it on the bottom. (If the phase of the moon is right, it will do this for a + // about half a second and then render normally. But if you're not lucky it just stays this way.) + // So.... here we deliberately put the rectangle on TOP of the text so that you can't tell when the bug + // is happening. + anchors.fill: parent; + radius: height / 2; + border.width: 4; + border.color: clickArea.containsMouse ? highlightColor : "transparent"; + color: clickArea.containsPress ? hifi.colors.darkGray : (selected ? hifi.colors.blueAccent : "transparent"); + opacity: (clickArea.containsMouse && !clickArea.containsPress) ? 0.8 : 0.5; + } MouseArea { id: clickArea; anchors.fill: parent; @@ -41,5 +53,4 @@ Rectangle { onClicked: action(parent); hoverEnabled: true; } - }