From 3132cca482cb13c6f7d56b83fb88723779367e1b Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 14 Jul 2016 14:49:03 -0700 Subject: [PATCH] prototype --- interface/resources/qml/AddressBarDialog.qml | 162 +++++++++++++++++- .../qml/controls-uit/HorizontalSpacer.qml | 19 ++ interface/resources/qml/hifi/Card.qml | 64 +++++++ 3 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 interface/resources/qml/controls-uit/HorizontalSpacer.qml create mode 100644 interface/resources/qml/hifi/Card.qml diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index dc060b70e1..8ac199b326 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -11,8 +11,13 @@ import Hifi 1.0 import QtQuick 2.4 import "controls" +import "controls-uit" as HifiControls import "styles" import "windows" +import "hifi" + +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 Window { id: root @@ -43,12 +48,52 @@ Window { // Explicitly center in order to avoid warnings at shutdown anchors.centerIn = parent; } - + + function goCard(card) { + addressLine.text = card.userStory.name; + toggleOrGo(); + } + AddressBarDialog { id: addressBarDialog implicitWidth: backgroundImage.width implicitHeight: backgroundImage.height + Column { + width: backgroundImage.width; + anchors { + left: backgroundImage.left; + leftMargin: backgroundImage.height + 2 * hifi.layout.spacing + bottom: backgroundImage.top; + bottomMargin: 2 * hifi.layout.spacing; + } + Text { + text: "Suggestions" + } + Row { + Card { + id: s0; + width: 200; + height: 200; + goFunction: goCard + } + HifiControls.HorizontalSpacer { } + Card { + id: s1; + width: 200; + height: 200; + goFunction: goCard + } + HifiControls.HorizontalSpacer { } + Card { + id: s2; + width: 200; + height: 200; + goFunction: goCard + } + } + } + Image { id: backgroundImage source: "../images/address-bar.svg" @@ -135,9 +180,124 @@ Window { } } + function getRequest(url, cb) { // cb(error, responseOfCorrectContentType) of url. General for 'get' text/html/json, but without redirects. + // TODO: make available to other .qml. + var request = new XMLHttpRequest(); + // QT bug: apparently doesn't handle onload. Workaround using readyState. + request.onreadystatechange = function () { + var READY_STATE_DONE = 4; + var HTTP_OK = 200; + if (request.readyState >= READY_STATE_DONE) { + var error = (request.status !== HTTP_OK) && request.status.toString() + ':' + request.statusText, + response = !error && request.responseText, + contentType = !error && request.getResponseHeader('content-type'); + if (!error && contentType.indexOf('application/json') === 0) { + try { + response = JSON.parse(response); + } catch (e) { + error = e; + } + } + cb(error, response); + } + }; + request.open("GET", url, true); + request.send(); + } + // call iterator(element, icb) once for each element of array, and then cb(error) when icb(error) has been called by each iterator. + // short-circuits if error. Note that iterator MUST be an asynchronous function. (Use setTimeout if necessary.) + function asyncEach(array, iterator, cb) { + var count = array.length; + function icb(error) { + if (!--count || error) { + count = -1; // don't cb multiple times (e.g., if error) + cb(error); + } + } + if (!count) { + return cb(); + } + array.forEach(function (element) { + iterator(element, icb); + }); + } + + function addPictureToDomain(domainInfo, cb) { // asynchronously add thumbnail and lobby to domainInfo, if available, and cb(error) + asyncEach([domainInfo.name].concat(domainInfo.names || null).filter(function (x) { return x; }), function (name, icb) { + var url = "https://metaverse.highfidelity.com/api/v1/places/" + name; + getRequest(url, function (error, json) { + var previews = !error && json.data.place.previews; + if (previews) { + if (!domainInfo.thumbnail) { // just grab tghe first one + domainInfo.thumbnail = previews.thumbnail; + } + if (!domainInfo.lobby) { + domainInfo.lobby = previews.lobby; + } + } + icb(error); + }); + }, cb); + } + + function getDomains(options, cb) { // cb(error, arrayOfData) + if (!options.page) { + options.page = 1; + } + // FIXME: really want places I'm allowed in, not just open ones + var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&active&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; + getRequest(url, function (error, json) { + if (!error && (json.status !== 'success')) { + error = new Error("Bad response: " + JSON.stringify(json)); + } + if (error) { + error.message += ' for ' + url; + return cb(error); + } + var domains = json.data.domains; + asyncEach(domains, addPictureToDomain, function (error) { + if (json.current_page < json.total_pages) { + options.page++; + return getDomains(options, function (error, others) { + cb(error, domains.concat(others)); + }); + } + cb(null, domains); + }); + }); + } + + function fillDestinations () { + function fill1(target, data) { + if (!data) { + return; + } + console.log('suggestion:', JSON.stringify(data)); + target.userStory = data; + if (data.lobby) { + console.log('target', target); + console.log('target.image', target.image); + console.log('url', data.lobby); + target.image.source = data.lobby; + } + target.placeText = data.name; + target.usersText = data.online_users + ((data.online_users === 1) ? ' user' : ' users'); + } + getDomains({minUsers: 0, maxUsers: 20}, function (e, d) { + var withLobby = !e && d.filter(function (d1) { return d1.lobby; }); + console.log(e, d.length, withLobby.length); + withLobby.sort(function (a, b) { return b.online_users - a.online_users; }); + console.log(withLobby.map(function (d) { return d.online_users; })); + fill1(s0, withLobby[0]); + fill1(s1, withLobby[1]); + fill1(s2, withLobby[2]); + }); + } + onVisibleChanged: { if (visible) { addressLine.forceActiveFocus() + fillDestinations(); } else { addressLine.text = "" } diff --git a/interface/resources/qml/controls-uit/HorizontalSpacer.qml b/interface/resources/qml/controls-uit/HorizontalSpacer.qml new file mode 100644 index 0000000000..a9fac2bcf7 --- /dev/null +++ b/interface/resources/qml/controls-uit/HorizontalSpacer.qml @@ -0,0 +1,19 @@ +// +// HorizontalSpacer.qml +// +// Created by Howard Stearns on 12 July 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 "../styles-uit" + +Item { + HifiConstants { id: hifi } + height: 1 // Must be non-zero + width: hifi.dimensions.controlInterlineHeight +} diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml new file mode 100644 index 0000000000..342c469303 --- /dev/null +++ b/interface/resources/qml/hifi/Card.qml @@ -0,0 +1,64 @@ +// +// Card.qml +// qml/hifi +// +// Displays a clickable card representing a user story or destination. +// +// Created by Howard Stearns on 7/13/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 Hifi 1.0 +import QtQuick 2.5 +import "../styles-uit" + +Rectangle { + property var goFunction: null; + property var userStory: null; + property alias image: lobby; + property alias placeText: place.text; + property alias usersText: users.text; + HifiConstants { id: hifi } + Image { + id: lobby; + width: parent.width; + height: parent.height; + fillMode: Image.PreserveAspectCrop; + // source gets filled in later + anchors.verticalCenter: parent.verticalCenter; + anchors.left: parent.left; + onStatusChanged: { + if (status == Image.Error) { + console.log("source: " + source + ": failed to load " + JSON.stringify(userStory)); + // FIXME: let's get our own + source = "http://www.davidluke.com/wp-content/themes/david-luke/media/ims/placeholder720.gif" + } + } + } + RalewaySemiBold { + id: place; + color: hifi.colors.lightGrayText; + anchors { + leftMargin: 2 * hifi.layout.spacing; + topMargin: 2 * hifi.layout.spacing; + } + } + RalewayRegular { + id: users; + color: hifi.colors.lightGrayText; + anchors { + bottom: parent.bottom; + right: parent.right; + bottomMargin: 2 * hifi.layout.spacing; + rightMargin: 2 * hifi.layout.spacing; + } + } + MouseArea { + anchors.fill: parent; + acceptedButtons: Qt.LeftButton; + onClicked: goFunction(parent); + } +}