Working on new-UI implementations of address bar and login

This commit is contained in:
Brad Davis 2015-04-14 15:55:42 -07:00
parent fe482b35ff
commit d14d5f3d29
45 changed files with 1844 additions and 728 deletions

View file

@ -92,8 +92,17 @@ else ()
if (NOT QT_CMAKE_PREFIX_PATH)
set(QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH})
endif ()
if (NOT QT_CMAKE_PREFIX_PATH)
get_filename_component(QT_CMAKE_PREFIX_PATH "${Qt5_DIR}/.." REALPATH)
endif ()
endif ()
if (WIN32)
if (NOT EXISTS ${QT_CMAKE_PREFIX_PATH})
message(FATAL_ERROR "Could not determine QT_CMAKE_PREFIX_PATH.")
endif ()
endif()
# figure out where the qt dir is
get_filename_component(QT_DIR "${QT_CMAKE_PREFIX_PATH}/../../" ABSOLUTE)

View file

@ -54,7 +54,7 @@ else ()
list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP})
endif ()
find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Script Svg WebKitWidgets)
find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Qml Quick Script Svg WebKitWidgets)
# grab the ui files in resources/ui
file (GLOB_RECURSE QT_UI_FILES ui/*.ui)

View file

@ -0,0 +1,84 @@
import Hifi 1.0
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Window 2.2
import QtQuick.Controls.Styles 1.3
AddressBarDialog {
id: addressBarDialog
objectName: "AddressBarDialog"
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
height: 128
width: 512
onVisibleChanged: {
if (!visible) {
reset();
} else {
addressLine.focus = true
addressLine.forceActiveFocus()
}
}
Component.onCompleted: {
addressLine.focus = true
addressLine.forceActiveFocus()
}
function reset() {
addressLine.text = ""
goButton.source = "../images/address-bar-submit.svg"
}
CustomDialog {
id: dialog
anchors.fill: parent
title: "Go to..."
// The client area
Item {
id: item1
anchors.fill: parent
anchors.margins: parent.margins
anchors.topMargin: parent.topMargin
CustomBorder {
height: 64
anchors.left: parent.left
anchors.leftMargin: 0
anchors.right: goButton.left
anchors.rightMargin: 8
anchors.verticalCenter: parent.verticalCenter
CustomTextInput {
id: addressLine
anchors.fill: parent
helperText: "domain, location, @user, /x,y,z"
anchors.margins: 8
onAccepted: {
addressBarDialog.loadAddress(addressLine.text)
}
}
}
Image {
id: goButton
width: 32
height: 32
anchors.right: parent.right
anchors.rightMargin: 8
source: "../images/address-bar-submit.svg"
anchors.verticalCenter: parent.verticalCenter
MouseArea {
anchors.fill: parent
onClicked: {
parent.source = "../images/address-bar-submit-active.svg"
addressBarDialog.loadAddress(addressLine.text)
}
}
}
}
}
}

View file

@ -0,0 +1,12 @@
import QtQuick 2.3
Rectangle {
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
property int margin: 5
color: myPalette.window
border.color: myPalette.dark
border.width: 5
radius: border.width * 2
}

View file

@ -0,0 +1,23 @@
import QtQuick 2.3
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Controls.Styles 1.3
Button {
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
text: "Text"
width: 128
height: 64
style: ButtonStyle {
background: CustomBorder {
anchors.fill: parent
}
label: CustomText {
renderType: Text.NativeRendering
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: control.text
color: control.enabled ? myPalette.text : myPalette.dark
}
}
}

View file

@ -0,0 +1,98 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.3
import "hifiConstants.js" as HifiConstants
Item {
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
id: dialog
width: 256
height: 256
property rect clientArea: clientBorder
property int topMargin: dialog.height - clientBorder.height + 8
property int margins: 8
property string title
property int titleSize: titleBorder.height + 12
property string frameColor: HifiConstants.color
property string backgroundColor: myPalette.window
property string headerBackgroundColor: myPalette.dark
CustomBorder {
id: windowBorder
anchors.fill: parent
border.color: dialog.frameColor
color: dialog.backgroundColor
CustomBorder {
id: titleBorder
height: 48
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
border.color: dialog.frameColor
color: dialog.headerBackgroundColor
CustomText {
id: titleText
color: "white"
text: dialog.title
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
}
MouseArea {
id: titleDrag
property int startX
property int startY
anchors.right: closeButton.left
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
anchors.rightMargin: 4
drag {
target: dialog.parent
minimumX: 0
minimumY: 0
maximumX: dialog.parent.parent.width - dialog.parent.width
maximumY: dialog.parent.parent.height - dialog.parent.height
}
}
Image {
id: closeButton
x: 360
height: 16
anchors.verticalCenter: parent.verticalCenter
width: 16
anchors.right: parent.right
anchors.rightMargin: 12
source: "../styles/close.svg"
MouseArea {
anchors.fill: parent
onClicked: {
dialog.parent.destroy()
}
}
}
} // header border
CustomBorder {
id: clientBorder
border.color: dialog.frameColor
color: "#00000000"
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.top: titleBorder.bottom
anchors.topMargin: -titleBorder.border.width
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
} // client border
} // window border
}

View file

@ -0,0 +1,7 @@
import QtQuick 2.3
Text {
font.family: "Helvetica"
font.pointSize: 18
}

View file

@ -0,0 +1,10 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
TextArea {
font.family: "Helvetica"
font.pointSize: 18
backgroundVisible: false
readOnly: true
}

View file

@ -0,0 +1,7 @@
import QtQuick 2.3
TextEdit {
font.family: "Helvetica"
font.pointSize: 18
}

View file

@ -0,0 +1,34 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
TextInput {
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
property string helperText: ""
font.family: "Helvetica"
font.pointSize: 18
width: 256
height: 64
color: myPalette.text
clip: true
verticalAlignment: TextInput.AlignVCenter
onTextChanged: {
if (text == "") {
helperText.visible = true;
} else {
helperText.visible = false;
}
}
Text {
id: helperText
anchors.fill: parent
font.pointSize: parent.font.pointSize
font.family: "Helvetica"
verticalAlignment: TextInput.AlignVCenter
text: parent.helperText
color: myPalette.dark
clip: true
}
}

View file

@ -0,0 +1,8 @@
import QtQuick 1.0
Image {
id: icon
width: 64
height: 64
source: "file.svg"
}

View file

@ -0,0 +1,24 @@
import QtQuick 2.3
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Controls.Styles 1.3
Button {
text: "Text"
style: ButtonStyle {
background: Item { anchors.fill: parent }
label: Text {
id: icon
width: height
verticalAlignment: Text.AlignVCenter
renderType: Text.NativeRendering
font.family: iconFont.name
font.pointSize: 18
property alias unicode: icon.text
FontLoader { id: iconFont; source: "/fonts/fontawesome-webfont.ttf"; }
text: control.text
color: control.enabled ? "white" : "dimgray"
}
}
}

View file

@ -0,0 +1,191 @@
import Hifi 1.0
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Window 2.2
import QtQuick.Controls.Styles 1.3
import "hifiConstants.js" as HifiConstants
LoginDialog {
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
id: loginDialog
objectName: "LoginDialog"
height: 512
width: 384
onVisibleChanged: {
if (!visible) {
reset()
} else {
username.forceActiveFocus()
}
}
function reset() {
username.text = ""
password.text = ""
loginDialog.statusText = ""
}
CustomDialog {
anchors.fill: parent
title: "Login"
Item {
id: item1
anchors.fill: parent
anchors.margins: parent.margins
anchors.topMargin: parent.topMargin
Column {
anchors.topMargin: 8
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.top: parent.top
spacing: 8
Image {
height: 64
anchors.horizontalCenter: parent.horizontalCenter
width: 64
source: "../images/hifi-logo.svg"
}
CustomBorder {
width: 304
height: 64
anchors.horizontalCenter: parent.horizontalCenter
CustomTextInput {
id: username
anchors.fill: parent
helperText: "Username or Email"
anchors.margins: 8
KeyNavigation.tab: password
KeyNavigation.backtab: password
onAccepted: {
password.forceActiveFocus()
}
}
}
CustomBorder {
width: 304
height: 64
anchors.horizontalCenter: parent.horizontalCenter
CustomTextInput {
id: password
anchors.fill: parent
echoMode: TextInput.Password
helperText: "Password"
anchors.margins: 8
KeyNavigation.tab: username
KeyNavigation.backtab: username
onAccepted: {
if (username.text == "") {
username.forceActiveFocus()
} else {
loginDialog.login(username.text, password.text)
}
}
onFocusChanged: {
if (password.focus) {
password.selectAll()
}
}
}
}
CustomText {
anchors.horizontalCenter: parent.horizontalCenter
textFormat: Text.StyledText
width: parent.width
height: 96
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: loginDialog.statusText
}
}
Column {
anchors.bottomMargin: 5
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.bottom: parent.bottom
Rectangle {
width: 192
height: 64
anchors.horizontalCenter: parent.horizontalCenter
color: HifiConstants.color
border.width: 0
radius: 10
MouseArea {
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.top: parent.top
anchors.right: parent.right
anchors.left: parent.left
onClicked: {
loginDialog.login(username.text, password.text)
}
}
Row {
anchors.centerIn: parent
anchors.verticalCenter: parent.verticalCenter
spacing: 8
Image {
id: loginIcon
height: 32
width: 32
source: "../images/login.svg"
}
CustomText {
text: "Login"
color: "white"
width: 64
height: parent.height
}
}
}
CustomText {
width: parent.width
height: 24
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text:"Create Account"
font.pointSize: 12
font.bold: true
color: HifiConstants.color
MouseArea {
anchors.fill: parent
onClicked: {
loginDialog.openUrl(loginDialog.rootUrl + "/signup")
}
}
}
CustomText {
width: parent.width
height: 24
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pointSize: 12
text: "Recover Password"
color: HifiConstants.color
MouseArea {
anchors.fill: parent
onClicked: {
loginDialog.openUrl(loginDialog.rootUrl + "/users/password/new")
}
}
}
}
}
}
}

View file

@ -0,0 +1,14 @@
import QtQuick 2.3
import "componentCreation.js" as Creator
Item {
id: root
width: 1280
height: 720
function loadChild(url) {
Creator.createObject(root, url)
}
}

View file

@ -0,0 +1,27 @@
var component;
var instance;
var parent;
function createObject(parentObject, url) {
parent = parentObject;
component = Qt.createComponent(url);
if (component.status == Component.Ready)
finishCreation();
else
component.statusChanged.connect(finishCreation);
}
function finishCreation() {
if (component.status == Component.Ready) {
instance = component.createObject(parent, {"x": 100, "y": 100});
if (instance == null) {
// Error Handling
console.log("Error creating object");
} else {
instance.focus = true;
}
} else if (component.status == Component.Error) {
// Error Handling
console.log("Error loading component:", component.errorString());
}
}

View file

@ -0,0 +1,4 @@
var color = "#0e7077"
var Colors = {
hifiBlue: "#0e7077"
}

View file

@ -137,6 +137,7 @@
#include "ui/Snapshot.h"
#include "ui/StandAloneJSConsole.h"
#include "ui/Stats.h"
#include "ui/AddressBarDialog.h"
// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
#if defined(Q_OS_WIN)
@ -209,8 +210,12 @@ public:
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message);
if (!logMessage.isEmpty()) {
#ifdef Q_OS_WIN
OutputDebugStringA(logMessage.toLocal8Bit().constData());
OutputDebugStringA("\n");
#endif
Application::getInstance()->getLogger()->addMessage(qPrintable(logMessage + "\n"));
}
}
@ -260,6 +265,7 @@ bool setupEssentials(int& argc, char** argv) {
#endif
auto discoverabilityManager = DependencyManager::set<DiscoverabilityManager>();
auto sceneScriptingInterface = DependencyManager::set<SceneScriptingInterface>();
auto offscreenUi = DependencyManager::set<OffscreenUi>();
return true;
}
@ -316,8 +322,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
#ifdef Q_OS_WIN
installNativeEventFilter(&MyNativeEventFilter::getInstance());
#endif
_logger = new FileLogger(this); // After setting organization name in order to get correct directory
qInstallMessageHandler(messageHandler);
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "styles/Inconsolata.otf");
@ -562,8 +570,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
#endif
this->installEventFilter(this);
// The offscreen UI needs to intercept the mouse and keyboard
// events coming from the onscreen window
_glWidget->installEventFilter(
DependencyManager::get<OffscreenUi>().data());
}
void Application::aboutToQuit() {
emit beforeAboutToQuit();
@ -720,10 +733,35 @@ void Application::initializeGL() {
// update before the first render
update(1.0f / _fps);
// The UI can't be created until the primary OpenGL
// context is created, because it needs to share
// texture resources
initializeUi();
InfoView::showFirstTime(INFO_HELP_PATH);
}
void Application::initializeUi() {
AddressBarDialog::registerType();
LoginDialog::registerType();
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->create(_glWidget->context()->contextHandle());
offscreenUi->resize(_glWidget->size());
offscreenUi->setProxyWindow(_window->windowHandle());
auto rootQml = PathUtils::resourcesPath() + "qml/Root.qml";
offscreenUi->loadQml(QUrl::fromLocalFile(rootQml));
offscreenUi->setMouseTranslator([this](const QPointF& p){
if (OculusManager::isConnected()) {
glm::vec2 pos = _applicationOverlay.screenToOverlay(toGlm(p));
return QPointF(pos.x, pos.y);
}
return QPointF(p);
});
offscreenUi->resume();
}
void Application::paintGL() {
PROFILE_RANGE(__FUNCTION__);
PerformanceTimer perfTimer("paintGL");
@ -818,7 +856,7 @@ void Application::paintGL() {
{
PerformanceTimer perfTimer("renderOverlay");
_applicationOverlay.renderOverlay(true);
_applicationOverlay.renderOverlay();
_applicationOverlay.displayOverlayTexture();
}
}
@ -863,6 +901,9 @@ void Application::resizeGL(int width, int height) {
updateProjectionMatrix();
glLoadIdentity();
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->resize(QSize(width, height));
// update Stats width
// let's set horizontal offset to give stats some margin to mirror
int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
@ -911,6 +952,44 @@ bool Application::importSVOFromURL(const QString& urlString) {
}
bool Application::event(QEvent* event) {
switch (event->type()) {
case QEvent::MouseMove:
mouseMoveEvent((QMouseEvent*)event);
return true;
case QEvent::MouseButtonPress:
mousePressEvent((QMouseEvent*)event);
return true;
case QEvent::MouseButtonRelease:
mouseReleaseEvent((QMouseEvent*)event);
return true;
case QEvent::KeyPress:
keyPressEvent((QKeyEvent*)event);
return true;
case QEvent::KeyRelease:
keyReleaseEvent((QKeyEvent*)event);
return true;
case QEvent::FocusOut:
focusOutEvent((QFocusEvent*)event);
return true;
case QEvent::TouchBegin:
touchBeginEvent(static_cast<QTouchEvent*>(event));
event->accept();
return true;
case QEvent::TouchEnd:
touchEndEvent(static_cast<QTouchEvent*>(event));
return true;
case QEvent::TouchUpdate:
touchUpdateEvent(static_cast<QTouchEvent*>(event));
return true;
case QEvent::Wheel:
wheelEvent(static_cast<QWheelEvent*>(event));
return true;
case QEvent::Drop:
dropEvent(static_cast<QDropEvent*>(event));
return true;
default:
break;
}
// handle custom URL
if (event->type() == QEvent::FileOpen) {
@ -931,7 +1010,7 @@ bool Application::event(QEvent* event) {
if (HFActionEvent::types().contains(event->type())) {
_controllerScriptingInterface.handleMetaEvent(static_cast<HFMetaEvent*>(event));
}
return QApplication::event(event);
}
@ -963,14 +1042,17 @@ void Application::keyPressEvent(QKeyEvent* event) {
bool isShifted = event->modifiers().testFlag(Qt::ShiftModifier);
bool isMeta = event->modifiers().testFlag(Qt::ControlModifier);
bool isOption = event->modifiers().testFlag(Qt::AltModifier);
bool isKeypad = event->modifiers().testFlag(Qt::KeypadModifier);
switch (event->key()) {
break;
case Qt::Key_L:
if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::LodTools);
} else if (isMeta) {
if (isShifted && isMeta) {
Menu::getInstance()->triggerOption(MenuOption::Log);
}
} else if (isMeta) {
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
} else if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::LodTools);
}
break;
case Qt::Key_E:
@ -1030,11 +1112,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
break;
case Qt::Key_Return:
case Qt::Key_Enter:
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
break;
case Qt::Key_Backslash:
Menu::getInstance()->triggerOption(MenuOption::Chat);
break;
@ -1494,6 +1571,17 @@ void Application::dropEvent(QDropEvent *event) {
}
}
void Application::dragEnterEvent(QDragEnterEvent* event) {
const QMimeData* mimeData = event->mimeData();
foreach(QUrl url, mimeData->urls()) {
auto urlString = url.toString();
if (canAcceptURL(urlString)) {
event->acceptProposedAction();
break;
}
}
}
bool Application::acceptSnapshot(const QString& urlString) {
QUrl url(urlString);
QString snapshotPath = url.toLocalFile();

View file

@ -30,6 +30,7 @@
#include <NetworkPacket.h>
#include <NodeList.h>
#include <OctreeQuery.h>
#include <OffscreenUi.h>
#include <PacketHeaders.h>
#include <PhysicsEngine.h>
#include <ScriptEngine.h>
@ -151,6 +152,7 @@ public:
void setPreviousScriptLocation(const QString& previousScriptLocation);
void clearScriptsBeforeRunning();
void initializeGL();
void initializeUi();
void paintGL();
void resizeGL(int width, int height);
@ -170,6 +172,7 @@ public:
void wheelEvent(QWheelEvent* event);
void dropEvent(QDropEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
bool event(QEvent* event);
bool eventFilter(QObject* object, QEvent* event);

View file

@ -82,30 +82,6 @@ void GLCanvas::resizeGL(int width, int height) {
Application::getInstance()->resizeGL(width, height);
}
void GLCanvas::keyPressEvent(QKeyEvent* event) {
Application::getInstance()->keyPressEvent(event);
}
void GLCanvas::keyReleaseEvent(QKeyEvent* event) {
Application::getInstance()->keyReleaseEvent(event);
}
void GLCanvas::focusOutEvent(QFocusEvent* event) {
Application::getInstance()->focusOutEvent(event);
}
void GLCanvas::mouseMoveEvent(QMouseEvent* event) {
Application::getInstance()->mouseMoveEvent(event);
}
void GLCanvas::mousePressEvent(QMouseEvent* event) {
Application::getInstance()->mousePressEvent(event);
}
void GLCanvas::mouseReleaseEvent(QMouseEvent* event) {
Application::getInstance()->mouseReleaseEvent(event);
}
void GLCanvas::activeChanged(Qt::ApplicationState state) {
switch (state) {
case Qt::ApplicationActive:
@ -151,40 +127,37 @@ void GLCanvas::throttleRender() {
int updateTime = 0;
bool GLCanvas::event(QEvent* event) {
switch (event->type()) {
case QEvent::MouseMove:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::FocusIn:
case QEvent::FocusOut:
case QEvent::Resize:
case QEvent::TouchBegin:
Application::getInstance()->touchBeginEvent(static_cast<QTouchEvent*>(event));
event->accept();
return true;
case QEvent::TouchEnd:
Application::getInstance()->touchEndEvent(static_cast<QTouchEvent*>(event));
return true;
case QEvent::TouchUpdate:
Application::getInstance()->touchUpdateEvent(static_cast<QTouchEvent*>(event));
return true;
case QEvent::Wheel:
case QEvent::DragEnter:
case QEvent::Drop:
if (QCoreApplication::sendEvent(QCoreApplication::instance(), event)) {
return true;
}
break;
case QEvent::Paint:
// Ignore paint events that occur after we've decided to quit
if (Application::getInstance()->isAboutToQuit()) {
return true;
}
break;
default:
break;
}
return QGLWidget::event(event);
}
void GLCanvas::wheelEvent(QWheelEvent* event) {
Application::getInstance()->wheelEvent(event);
}
void GLCanvas::dragEnterEvent(QDragEnterEvent* event) {
const QMimeData* mimeData = event->mimeData();
foreach (QUrl url, mimeData->urls()) {
auto urlString = url.toString();
if (Application::getInstance()->canAcceptURL(urlString)) {
event->acceptProposedAction();
break;
}
}
}
void GLCanvas::dropEvent(QDropEvent* event) {
Application::getInstance()->dropEvent(event);
}
// Pressing Alt (and Meta) key alone activates the menubar because its style inherits the
// SHMenuBarAltKeyNavigation from QWindowsStyle. This makes it impossible for a scripts to

View file

@ -40,23 +40,8 @@ protected:
virtual void initializeGL();
virtual void paintGL();
virtual void resizeGL(int width, int height);
virtual void keyPressEvent(QKeyEvent* event);
virtual void keyReleaseEvent(QKeyEvent* event);
virtual void focusOutEvent(QFocusEvent* event);
virtual void mouseMoveEvent(QMouseEvent* event);
virtual void mousePressEvent(QMouseEvent* event);
virtual void mouseReleaseEvent(QMouseEvent* event);
virtual bool event(QEvent* event);
virtual void wheelEvent(QWheelEvent* event);
virtual void dragEnterEvent(QDragEnterEvent *event);
virtual void dropEvent(QDropEvent* event);
private slots:
void activeChanged(Qt::ApplicationState state);
void throttleRender();

View file

@ -97,7 +97,7 @@ Menu::Menu() {
addActionToQMenuAndActionHash(fileMenu,
MenuOption::AddressBar,
Qt::Key_Enter,
Qt::CTRL | Qt::Key_L,
dialogsManager.data(),
SLOT(toggleAddressBar()));
auto addressManager = DependencyManager::get<AddressManager>();
@ -151,7 +151,8 @@ Menu::Menu() {
connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool)));
#endif
addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, Qt::Key_Backslash,
addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat,
0, // QML Qt::Key_Backslash,
dialogsManager.data(), SLOT(showIRCLink()));
addActionToQMenuAndActionHash(toolsMenu, MenuOption::AddRemoveFriends, 0,
qApp, SLOT(showFriendsWindow()));
@ -194,7 +195,7 @@ Menu::Menu() {
addActionToQMenuAndActionHash(toolsMenu,
MenuOption::ResetSensors,
Qt::Key_Apostrophe,
0, // QML Qt::Key_Apostrophe,
qApp,
SLOT(resetSensors()));
@ -207,17 +208,17 @@ Menu::Menu() {
QMenu* avatarSizeMenu = avatarMenu->addMenu("Size");
addActionToQMenuAndActionHash(avatarSizeMenu,
MenuOption::IncreaseAvatarSize,
Qt::Key_Plus,
0, // QML Qt::Key_Plus,
avatar,
SLOT(increaseSize()));
addActionToQMenuAndActionHash(avatarSizeMenu,
MenuOption::DecreaseAvatarSize,
Qt::Key_Minus,
0, // QML Qt::Key_Minus,
avatar,
SLOT(decreaseSize()));
addActionToQMenuAndActionHash(avatarSizeMenu,
MenuOption::ResetAvatarSize,
Qt::Key_Equal,
0, // QML Qt::Key_Equal,
avatar,
SLOT(resetSize()));
@ -250,13 +251,17 @@ Menu::Menu() {
qApp,
SLOT(setFullscreen(bool)));
#endif
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true,
qApp,SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H, true);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false,
qApp, SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson,
0, // QML Qt::Key_P,
true, qApp, SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror,
0, //QML Qt::SHIFT | Qt::Key_H,
true);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror,
0, // QML Qt::Key_H,
false, qApp, SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, Qt::META | Qt::Key_H,
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, Qt::CTRL | Qt::SHIFT | Qt::Key_H,
false,
dialogsManager.data(),
SLOT(hmdTools(bool)));
@ -283,8 +288,12 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash);
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, qApp, SLOT(toggleLogDialog()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats,
0); // QML Qt::Key_Slash);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats);
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log,
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
qApp, SLOT(toggleLogDialog()));
addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0,
dialogsManager.data(), SLOT(bandwidthDetails()));
addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0,
@ -294,7 +303,9 @@ Menu::Menu() {
QMenu* developerMenu = addMenu("Developer");
QMenu* renderOptionsMenu = developerMenu->addMenu("Render");
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, Qt::SHIFT | Qt::Key_A, true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere,
0, // QML Qt::SHIFT | Qt::Key_A,
true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DontFadeOnOctreeServerChanges);
@ -346,13 +357,16 @@ Menu::Menu() {
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false));
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false));
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, Qt::Key_Asterisk, true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars,
0, // QML Qt::Key_Asterisk,
true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true,
DependencyManager::get<GlowEffect>().data(), SLOT(toggleGlowEffect(bool)));
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Wireframe, Qt::ALT | Qt::Key_W, false);
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L,
dialogsManager.data(), SLOT(lodTools()));
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools,
0, // QML Qt::SHIFT | Qt::Key_L,
dialogsManager.data(), SLOT(lodTools()));
QMenu* avatarDebugMenu = developerMenu->addMenu("Avatar");

View file

@ -114,8 +114,11 @@ void OculusManager::initSdk() {
}
void OculusManager::shutdownSdk() {
ovrHmd_Destroy(_ovrHmd);
ovr_Shutdown();
if (_ovrHmd) {
ovrHmd_Destroy(_ovrHmd);
_ovrHmd = nullptr;
ovr_Shutdown();
}
}
void OculusManager::init() {
@ -124,6 +127,12 @@ void OculusManager::init() {
#endif
}
void OculusManager::deinit() {
#ifdef OVR_DIRECT_MODE
shutdownSdk();
#endif
}
void OculusManager::connect() {
#ifndef OVR_DIRECT_MODE
initSdk();
@ -515,7 +524,7 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
// We only need to render the overlays to a texture once, then we just render the texture on the hemisphere
// PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()
applicationOverlay.renderOverlay(true);
applicationOverlay.renderOverlay();
//Bind our framebuffer object. If we are rendering the glow effect, we let the glow effect shader take care of it
if (Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect)) {

View file

@ -51,6 +51,7 @@ class Text3DOverlay;
class OculusManager {
public:
static void init();
static void deinit();
static void connect();
static void disconnect();
static bool isConnected();

View file

@ -103,7 +103,7 @@ void TV3DManager::display(Camera& whichCamera) {
// We only need to render the overlays to a texture once, then we just render the texture as a quad
// PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()
applicationOverlay.renderOverlay(true);
applicationOverlay.renderOverlay();
DependencyManager::get<GlowEffect>()->prepare();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

View file

@ -112,6 +112,7 @@ int main(int argc, const char* argv[]) {
exitCode = app.exec();
}
OculusManager::deinit();
#ifdef Q_OS_WIN
ReleaseMutex(mutex);
#endif

View file

@ -1,148 +1,50 @@
//
// AddressBarDialog.cpp
// interface/src/ui
//
// Created by Stojce Slavkovski on 9/22/14.
// Copyright 2014 High Fidelity, Inc.
// Created by Bradley Austin Davis on 2015/04/14
// 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
//
#include "AddressBarDialog.h"
#include <QMessageBox>
#include <PathUtils.h>
#include "AddressBarDialog.h"
#include "DependencyManager.h"
#include "AddressManager.h"
#include "Application.h"
#include "MainWindow.h"
const QString ADDRESSBAR_GO_BUTTON_ICON = "images/address-bar-submit.svg";
const QString ADDRESSBAR_GO_BUTTON_ACTIVE_ICON = "images/address-bar-submit-active.svg";
QML_DIALOG_DEF(AddressBarDialog)
AddressBarDialog::AddressBarDialog(QWidget* parent) :
FramelessDialog(parent, 0, FramelessDialog::POSITION_TOP)
{
setAttribute(Qt::WA_DeleteOnClose, false);
setupUI();
AddressBarDialog::AddressBarDialog(QQuickItem *parent) : QQuickItem(parent) {
auto addressManager = DependencyManager::get<AddressManager>();
connect(addressManager.data(), &AddressManager::lookupResultIsOffline, this, &AddressBarDialog::displayAddressOfflineMessage);
connect(addressManager.data(), &AddressManager::lookupResultIsNotFound, this, &AddressBarDialog::displayAddressNotFoundMessage);
connect(addressManager.data(), &AddressManager::lookupResultsFinished, this, &AddressBarDialog::hide);
}
void AddressBarDialog::setupUI() {
const QString DIALOG_STYLESHEET = "font-family: Helvetica, Arial, sans-serif;";
const QString ADDRESSBAR_PLACEHOLDER = "Go to: domain, location, @user, /x,y,z";
const QString ADDRESSBAR_STYLESHEET = "padding: 5px 10px; font-size: 20px;";
const int ADDRESSBAR_MIN_WIDTH = 200;
const int ADDRESSBAR_MAX_WIDTH = 615;
const int ADDRESSBAR_HEIGHT = 42;
const int ADDRESSBAR_STRETCH = 60;
const int BUTTON_SPACER_SIZE = 5;
const int DEFAULT_SPACER_SIZE = 20;
const int ADDRESS_LAYOUT_RIGHT_MARGIN = 10;
const int GO_BUTTON_SIZE = 42;
const int CLOSE_BUTTON_SIZE = 16;
const QString CLOSE_BUTTON_ICON = "styles/close.svg";
const int DIALOG_HEIGHT = 62;
const int DIALOG_INITIAL_WIDTH = 560;
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
setSizePolicy(sizePolicy);
setMinimumSize(QSize(DIALOG_INITIAL_WIDTH, DIALOG_HEIGHT));
setMaximumHeight(DIALOG_HEIGHT);
setStyleSheet(DIALOG_STYLESHEET);
_verticalLayout = new QVBoxLayout(this);
_verticalLayout->setContentsMargins(0, 0, 0, 0);
_addressLayout = new QHBoxLayout();
_addressLayout->setContentsMargins(0, 0, ADDRESS_LAYOUT_RIGHT_MARGIN, 0);
_leftSpacer = new QSpacerItem(DEFAULT_SPACER_SIZE,
DEFAULT_SPACER_SIZE,
QSizePolicy::MinimumExpanding,
QSizePolicy::Minimum);
_addressLayout->addItem(_leftSpacer);
_addressLineEdit = new QLineEdit(this);
_addressLineEdit->setAttribute(Qt::WA_MacShowFocusRect, 0);
_addressLineEdit->setPlaceholderText(ADDRESSBAR_PLACEHOLDER);
QSizePolicy sizePolicyLineEdit(QSizePolicy::Preferred, QSizePolicy::Fixed);
sizePolicyLineEdit.setHorizontalStretch(ADDRESSBAR_STRETCH);
_addressLineEdit->setSizePolicy(sizePolicyLineEdit);
_addressLineEdit->setMinimumSize(QSize(ADDRESSBAR_MIN_WIDTH, ADDRESSBAR_HEIGHT));
_addressLineEdit->setMaximumSize(QSize(ADDRESSBAR_MAX_WIDTH, ADDRESSBAR_HEIGHT));
_addressLineEdit->setStyleSheet(ADDRESSBAR_STYLESHEET);
_addressLayout->addWidget(_addressLineEdit);
_buttonSpacer = new QSpacerItem(BUTTON_SPACER_SIZE, BUTTON_SPACER_SIZE, QSizePolicy::Fixed, QSizePolicy::Minimum);
_addressLayout->addItem(_buttonSpacer);
_goButton = new QPushButton(this);
_goButton->setSizePolicy(sizePolicy);
_goButton->setMinimumSize(QSize(GO_BUTTON_SIZE, GO_BUTTON_SIZE));
_goButton->setMaximumSize(QSize(GO_BUTTON_SIZE, GO_BUTTON_SIZE));
_goButton->setIcon(QIcon(PathUtils::resourcesPath() + ADDRESSBAR_GO_BUTTON_ICON));
_goButton->setIconSize(QSize(GO_BUTTON_SIZE, GO_BUTTON_SIZE));
_goButton->setDefault(true);
_goButton->setFlat(true);
_addressLayout->addWidget(_goButton);
_rightSpacer = new QSpacerItem(DEFAULT_SPACER_SIZE,
DEFAULT_SPACER_SIZE,
QSizePolicy::MinimumExpanding,
QSizePolicy::Minimum);
_addressLayout->addItem(_rightSpacer);
_closeButton = new QPushButton(this);
_closeButton->setSizePolicy(sizePolicy);
_closeButton->setMinimumSize(QSize(CLOSE_BUTTON_SIZE, CLOSE_BUTTON_SIZE));
_closeButton->setMaximumSize(QSize(CLOSE_BUTTON_SIZE, CLOSE_BUTTON_SIZE));
QIcon icon(PathUtils::resourcesPath() + CLOSE_BUTTON_ICON);
_closeButton->setIcon(icon);
_closeButton->setIconSize(QSize(CLOSE_BUTTON_SIZE, CLOSE_BUTTON_SIZE));
_closeButton->setFlat(true);
_addressLayout->addWidget(_closeButton, 0, Qt::AlignRight);
_verticalLayout->addLayout(_addressLayout);
connect(_goButton, &QPushButton::clicked, this, &AddressBarDialog::accept);
connect(_closeButton, &QPushButton::clicked, this, &QDialog::close);
void AddressBarDialog::hide() {
setEnabled(false);
setVisible(false);
}
void AddressBarDialog::showEvent(QShowEvent* event) {
_goButton->setIcon(QIcon(PathUtils::resourcesPath() + ADDRESSBAR_GO_BUTTON_ICON));
_addressLineEdit->setText(QString());
_addressLineEdit->setFocus();
FramelessDialog::showEvent(event);
}
void AddressBarDialog::accept() {
if (!_addressLineEdit->text().isEmpty()) {
_goButton->setIcon(QIcon(PathUtils::resourcesPath() + ADDRESSBAR_GO_BUTTON_ACTIVE_ICON));
auto addressManager = DependencyManager::get<AddressManager>();
connect(addressManager.data(), &AddressManager::lookupResultsFinished, this, &QDialog::hide);
addressManager->handleLookupString(_addressLineEdit->text());
void AddressBarDialog::loadAddress(const QString & address) {
qDebug() << "Called LoadAddress with address " << address;
if (!address.isEmpty()) {
DependencyManager::get<AddressManager>()->handleLookupString(address);
}
}
// TODO port to a QML based message box
void AddressBarDialog::displayAddressOfflineMessage() {
QMessageBox::information(Application::getInstance()->getWindow(), "Address offline",
"That user or place is currently offline.");
QMessageBox::information(nullptr, "Address offline",
"That user or place is currently offline.");
}
// TODO port to a QML based message box
void AddressBarDialog::displayAddressNotFoundMessage() {
QMessageBox::information(Application::getInstance()->getWindow(), "Address not found",
"There is no address information for that user or place.");
}
QMessageBox::information(nullptr, "Address not found",
"There is no address information for that user or place.");
}

View file

@ -1,9 +1,8 @@
//
// AddressBarDialog.h
// interface/src/ui
//
// Created by Stojce Slavkovski on 9/22/14.
// Copyright 2014 High Fidelity, Inc.
// Created by Bradley Austin Davis on 2015/04/14
// 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
@ -12,36 +11,25 @@
#ifndef hifi_AddressBarDialog_h
#define hifi_AddressBarDialog_h
#include "FramelessDialog.h"
#pragma once
#include <QQuickItem>
#include <QLineEdit>
#include <QHBoxLayout>
#include <QSpacerItem>
#include <QVBoxLayout>
#include "OffscreenUi.h"
class AddressBarDialog : public FramelessDialog {
class AddressBarDialog : public QQuickItem
{
Q_OBJECT
QML_DIALOG_DECL
public:
AddressBarDialog(QWidget* parent);
private:
void setupUI();
void showEvent(QShowEvent* event);
QVBoxLayout *_verticalLayout;
QHBoxLayout *_addressLayout;
QSpacerItem *_leftSpacer;
QSpacerItem *_rightSpacer;
QSpacerItem *_buttonSpacer;
QPushButton *_goButton;
QPushButton *_closeButton;
QLineEdit *_addressLineEdit;
private slots:
void accept();
AddressBarDialog(QQuickItem *parent = 0);
protected:
void displayAddressOfflineMessage();
void displayAddressNotFoundMessage();
void hide();
Q_INVOKABLE void loadAddress(const QString & address);
};
#endif // hifi_AddressBarDialog_h
#endif

View file

@ -17,6 +17,7 @@
#include <GLMHelpers.h>
#include <PathUtils.h>
#include <PerfStat.h>
#include <OffscreenUi.h>
#include "AudioClient.h"
#include "audio/AudioIOStatsRenderer.h"
@ -161,13 +162,27 @@ ApplicationOverlay::ApplicationOverlay() :
_domainStatusBorder = geometryCache->allocateID();
_magnifierBorder = geometryCache->allocateID();
// Once we move UI rendering and screen rendering to different
// threads, we need to use a sync object to deteremine when
// the current UI texture is no longer being read from, and only
// then release it back to the UI for re-use
auto offscreenUi = DependencyManager::get<OffscreenUi>();
connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [&](GLuint textureId) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->lockTexture(textureId);
assert(!glGetError());
std::swap(_newUiTexture, textureId);
if (textureId) {
offscreenUi->releaseTexture(textureId);
}
});
}
ApplicationOverlay::~ApplicationOverlay() {
}
// Renders the overlays either to a texture or to the screen
void ApplicationOverlay::renderOverlay(bool renderToTexture) {
void ApplicationOverlay::renderOverlay() {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()");
Overlays& overlays = qApp->getOverlays();
auto glCanvas = Application::getInstance()->getGLWidget();
@ -183,11 +198,9 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (renderToTexture) {
_overlays.buildFramebufferObject();
_overlays.bind();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
_overlays.buildFramebufferObject();
_overlays.bind();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix(); {
const float NEAR_CLIP = -10000;
@ -218,9 +231,25 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
glEnable(GL_LIGHTING);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
if (renderToTexture) {
_overlays.release();
_overlays.release();
}
// A quick and dirty solution for compositing the old overlay
// texture with the new one
template <typename F>
void with_each_texture(GLuint a, GLuint b, F f) {
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
if (a) {
glBindTexture(GL_TEXTURE_2D, a);
f();
}
if (b) {
glBindTexture(GL_TEXTURE_2D, b);
f();
}
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
// Draws the FBO texture for the screen
@ -229,30 +258,24 @@ void ApplicationOverlay::displayOverlayTexture() {
return;
}
auto glCanvas = Application::getInstance()->getGLWidget();
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
_overlays.bindTexture();
glMatrixMode(GL_PROJECTION);
glPushMatrix(); {
glLoadIdentity();
glOrtho(0, glCanvas->getDeviceWidth(), glCanvas->getDeviceHeight(), 0, -1.0, 1.0);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
glm::vec2 topLeft(0.0f, 0.0f);
glm::vec2 bottomRight(glCanvas->getDeviceWidth(), glCanvas->getDeviceHeight());
glm::vec2 texCoordTopLeft(0.0f, 1.0f);
glm::vec2 texCoordBottomRight(1.0f, 0.0f);
if (_alpha < 1.0) {
glEnable(GL_BLEND);
}
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(1.0f, 1.0f, 1.0f, _alpha));
with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
static const glm::vec2 topLeft(-1, 1);
static const glm::vec2 bottomRight(1, -1);
static const glm::vec2 texCoordTopLeft(0.0f, 1.0f);
static const glm::vec2 texCoordBottomRight(1.0f, 0.0f);
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(1.0f, 1.0f, 1.0f, _alpha));
});
} glPopMatrix();
glDisable(GL_TEXTURE_2D);
}
// Draws the FBO texture for Oculus rift.
@ -260,10 +283,7 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
if (_alpha == 0.0f) {
return;
}
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
_overlays.bindTexture();
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
glEnable(GL_DEPTH_TEST);
@ -271,8 +291,8 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
glDisable(GL_LIGHTING);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.01f);
//Update and draw the magnifiers
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
const glm::quat& orientation = myAvatar->getOrientation();
@ -303,8 +323,9 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
//Render magnifier, but dont show border for mouse magnifier
glm::vec2 projection = screenToOverlay(glm::vec2(_reticlePosition[MOUSE].x(),
_reticlePosition[MOUSE].y()));
renderMagnifier(projection, _magSizeMult[i], i != MOUSE);
with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
renderMagnifier(projection, _magSizeMult[i], i != MOUSE);
});
}
}
@ -319,12 +340,15 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
_overlays.buildVBO(_textureFov, _textureAspectRatio, 80, 80);
}
_overlays.render();
with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
_overlays.render();
});
if (!Application::getInstance()->isMouseHidden()) {
renderPointersOculus(myAvatar->getDefaultEyePosition());
}
glDepthMask(GL_TRUE);
_overlays.releaseTexture();
glDisable(GL_TEXTURE_2D);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
@ -341,14 +365,10 @@ void ApplicationOverlay::displayOverlayTexture3DTV(Camera& whichCamera, float as
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
const glm::vec3& viewMatrixTranslation = qApp->getViewMatrixTranslation();
glActiveTexture(GL_TEXTURE0);
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
_overlays.bindTexture();
glEnable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D);
glMatrixMode(GL_MODELVIEW);
@ -382,13 +402,15 @@ void ApplicationOverlay::displayOverlayTexture3DTV(Camera& whichCamera, float as
GLfloat y = -halfQuadHeight;
glDisable(GL_DEPTH_TEST);
DependencyManager::get<GeometryCache>()->renderQuad(glm::vec3(x, y + quadHeight, -distance),
with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
DependencyManager::get<GeometryCache>()->renderQuad(glm::vec3(x, y + quadHeight, -distance),
glm::vec3(x + quadWidth, y + quadHeight, -distance),
glm::vec3(x + quadWidth, y, -distance),
glm::vec3(x, y, -distance),
glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f),
glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f),
overlayColor);
});
auto glCanvas = Application::getInstance()->getGLWidget();
if (_crosshairTexture == 0) {
@ -993,14 +1015,6 @@ void ApplicationOverlay::TexturedHemisphere::release() {
_framebufferObject->release();
}
void ApplicationOverlay::TexturedHemisphere::bindTexture() {
glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture());
}
void ApplicationOverlay::TexturedHemisphere::releaseTexture() {
glBindTexture(GL_TEXTURE_2D, 0);
}
void ApplicationOverlay::TexturedHemisphere::buildVBO(const float fov,
const float aspectRatio,
const int slices,
@ -1099,14 +1113,14 @@ void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() {
}
_framebufferObject = new QOpenGLFramebufferObject(size, QOpenGLFramebufferObject::Depth);
bindTexture();
glBindTexture(GL_TEXTURE_2D, getTexture());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
GLfloat borderColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
releaseTexture();
glBindTexture(GL_TEXTURE_2D, 0);
}
//Renders a hemisphere with texture coordinates.
@ -1137,6 +1151,10 @@ void ApplicationOverlay::TexturedHemisphere::render() {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
GLuint ApplicationOverlay::TexturedHemisphere::getTexture() {
return _framebufferObject->texture();
}
glm::vec2 ApplicationOverlay::directionToSpherical(glm::vec3 direction) const {
glm::vec2 result;
// Compute yaw

View file

@ -28,7 +28,7 @@ public:
ApplicationOverlay();
~ApplicationOverlay();
void renderOverlay(bool renderToTexture = false);
void renderOverlay();
void displayOverlayTexture();
void displayOverlayTextureOculus(Camera& whichCamera);
void displayOverlayTexture3DTV(Camera& whichCamera, float aspectRatio, float fov);
@ -75,8 +75,7 @@ private:
void bind();
void release();
void bindTexture();
void releaseTexture();
GLuint getTexture();
void buildFramebufferObject();
void buildVBO(const float fov, const float aspectRatio, const int slices, const int stacks);
@ -122,6 +121,9 @@ private:
float _trailingAudioLoudness;
GLuint _crosshairTexture;
// TODO, move divide up the rendering, displaying and input handling
// facilities of this class
GLuint _newUiTexture{ 0 };
int _reticleQuad;
int _magnifierQuad;

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "DialogsManager.h"
#include <QMessageBox>
#include <AccountManager.h>
@ -28,14 +30,9 @@
#include "PreferencesDialog.h"
#include "ScriptEditorWindow.h"
#include "DialogsManager.h"
void DialogsManager::toggleAddressBar() {
maybeCreateDialog(_addressBarDialog);
if (!_addressBarDialog->isVisible()) {
_addressBarDialog->show();
}
AddressBarDialog::toggle();
}
void DialogsManager::toggleDiskCacheEditor() {
@ -44,13 +41,11 @@ void DialogsManager::toggleDiskCacheEditor() {
}
void DialogsManager::toggleLoginDialog() {
maybeCreateDialog(_loginDialog);
_loginDialog->toggleQAction();
LoginDialog::toggleAction();
}
void DialogsManager::showLoginDialog() {
maybeCreateDialog(_loginDialog);
_loginDialog->showLoginForCurrentDomain();
LoginDialog::show();
}
void DialogsManager::octreeStatsDetails() {
@ -170,3 +165,4 @@ void DialogsManager::showIRCLink() {
_ircInfoBox->raise();
}

View file

@ -1,166 +0,0 @@
//
// FramelessDialog.cpp
// interface/src/ui
//
// Created by Stojce Slavkovski on 2/20/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QScreen>
#include <QWindow>
#include <PathUtils.h>
#include "Application.h"
#include "FramelessDialog.h"
#include "Menu.h"
const int RESIZE_HANDLE_WIDTH = 7;
FramelessDialog::FramelessDialog(QWidget *parent, Qt::WindowFlags flags, Position position) :
QDialog(parent, flags | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint),
_allowResize(true),
_isResizing(false),
_resizeInitialWidth(0),
_selfHidden(false),
_position(position),
_hideOnBlur(true) {
setAttribute(Qt::WA_DeleteOnClose);
// handle rezize and move events
parentWidget()->installEventFilter(this);
// handle minimize, restore and focus events
Application::getInstance()->installEventFilter(this);
}
bool FramelessDialog::eventFilter(QObject* sender, QEvent* event) {
switch (event->type()) {
case QEvent::Move:
if (sender == parentWidget()) {
resizeAndPosition(false);
}
break;
case QEvent::Resize:
if (sender == parentWidget()) {
resizeAndPosition(false);
}
break;
case QEvent::WindowStateChange:
if (_hideOnBlur && parentWidget()->isMinimized()) {
if (isVisible()) {
_selfHidden = true;
setHidden(true);
}
} else if (_selfHidden) {
_selfHidden = false;
setHidden(false);
}
break;
case QEvent::ApplicationDeactivate:
// hide on minimize and focus lost
if (_hideOnBlur && isVisible()) {
_selfHidden = true;
setHidden(true);
}
break;
case QEvent::ApplicationActivate:
if (_selfHidden) {
_selfHidden = false;
setHidden(false);
}
break;
default:
break;
}
return false;
}
void FramelessDialog::setStyleSheetFile(const QString& fileName) {
QFile globalStyleSheet(PathUtils::resourcesPath() + "styles/global.qss");
QFile styleSheet(PathUtils::resourcesPath() + fileName);
if (styleSheet.open(QIODevice::ReadOnly) && globalStyleSheet.open(QIODevice::ReadOnly) ) {
QDir::setCurrent(PathUtils::resourcesPath());
setStyleSheet(globalStyleSheet.readAll() + styleSheet.readAll());
}
}
void FramelessDialog::showEvent(QShowEvent* event) {
resizeAndPosition();
QDialog::showEvent(event);
}
void FramelessDialog::resizeAndPosition(bool resizeParent) {
QRect parentGeometry = Application::getInstance()->getDesirableApplicationGeometry();
QSize parentSize = parentGeometry.size();
// keep full app height or width depending on position
if (_position == POSITION_LEFT || _position == POSITION_RIGHT) {
setFixedHeight(parentSize.height());
} else {
setFixedWidth(parentSize.width());
}
// resize parrent if width is smaller than this dialog
if (resizeParent && parentSize.width() < size().width()) {
parentWidget()->resize(size().width(), parentSize.height());
}
if (_position == POSITION_LEFT || _position == POSITION_TOP) {
// move to upper left corner
move(parentGeometry.topLeft());
} else if (_position == POSITION_RIGHT) {
// move to upper right corner
QPoint pos = parentGeometry.topRight();
pos.setX(pos.x() - size().width());
move(pos);
}
repaint();
}
void FramelessDialog::mousePressEvent(QMouseEvent* mouseEvent) {
if (_allowResize && mouseEvent->button() == Qt::LeftButton) {
if (_position == POSITION_LEFT || _position == POSITION_RIGHT) {
bool hitLeft = (_position == POSITION_LEFT) && (abs(mouseEvent->pos().x() - size().width()) < RESIZE_HANDLE_WIDTH);
bool hitRight = (_position == POSITION_RIGHT) && (mouseEvent->pos().x() < RESIZE_HANDLE_WIDTH);
if (hitLeft || hitRight) {
_isResizing = true;
_resizeInitialWidth = size().width();
setCursor(Qt::SizeHorCursor);
}
} else {
bool hitTop = (_position == POSITION_TOP) && (abs(mouseEvent->pos().y() - size().height()) < RESIZE_HANDLE_WIDTH);
if (hitTop) {
_isResizing = true;
_resizeInitialWidth = size().height();
setCursor(Qt::SizeHorCursor);
}
}
}
}
void FramelessDialog::mouseReleaseEvent(QMouseEvent* mouseEvent) {
unsetCursor();
_isResizing = false;
}
void FramelessDialog::mouseMoveEvent(QMouseEvent* mouseEvent) {
if (_isResizing) {
if (_position == POSITION_LEFT) {
resize(mouseEvent->pos().x(), size().height());
} else if (_position == POSITION_RIGHT) {
setUpdatesEnabled(false);
resize(_resizeInitialWidth - mouseEvent->pos().x(), size().height());
resizeAndPosition();
_resizeInitialWidth = size().width();
setUpdatesEnabled(true);
} else if (_position == POSITION_TOP) {
resize(size().width(), mouseEvent->pos().y());
}
}
}

View file

@ -1,50 +0,0 @@
//
// FramelessDialog.h
// interface/src/ui
//
// Created by Stojce Slavkovski on 2/20/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_FramelessDialog_h
#define hifi_FramelessDialog_h
#include <QDialog>
class FramelessDialog : public QDialog {
Q_OBJECT
public:
enum Position { POSITION_LEFT, POSITION_RIGHT, POSITION_TOP };
FramelessDialog(QWidget* parent, Qt::WindowFlags flags = 0, Position position = POSITION_LEFT);
void setStyleSheetFile(const QString& fileName);
void setAllowResize(bool allowResize) { _allowResize = allowResize; }
bool getAllowResize() { return _allowResize; }
void setHideOnBlur(bool hideOnBlur) { _hideOnBlur = hideOnBlur; }
bool getHideOnBlur() { return _hideOnBlur; }
void resizeAndPosition(bool resizeParent = true);
protected:
virtual void mouseMoveEvent(QMouseEvent* mouseEvent);
virtual void mousePressEvent(QMouseEvent* mouseEvent);
virtual void mouseReleaseEvent(QMouseEvent* mouseEvent);
virtual void showEvent(QShowEvent* event);
bool eventFilter(QObject* sender, QEvent* event);
private:
bool _allowResize;
bool _isResizing;
int _resizeInitialWidth;
bool _selfHidden; ///< true when the dialog itself because of a window event (deactivation or minimization)
Position _position;
bool _hideOnBlur;
};
#endif // hifi_FramelessDialog_h

View file

@ -1,120 +1,35 @@
//
//
// LoginDialog.cpp
// interface/src/ui
//
// Created by Ryan Huffman on 4/23/14.
// Copyright 2014 High Fidelity, Inc.
// Created by Bradley Austin Davis on 2015/04/14
// 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
//
#include <QWidget>
#include <QPushButton>
#include <QPixmap>
#include <NetworkingConstants.h>
#include <PathUtils.h>
#include "Application.h"
#include "Menu.h"
#include "AccountManager.h"
#include "ui_loginDialog.h"
#include "LoginDialog.h"
#include "UIUtil.h"
const QString CREATE_ACCOUNT_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/signup";
const QString FORGOT_PASSWORD_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/users/password/new";
#include "DependencyManager.h"
#include "AccountManager.h"
#include "Menu.h"
#include <NetworkingConstants.h>
LoginDialog::LoginDialog(QWidget* parent) :
FramelessDialog(parent, 0, FramelessDialog::POSITION_TOP),
_ui(new Ui::LoginDialog) {
_ui->setupUi(this);
reset();
setAttribute(Qt::WA_DeleteOnClose, false);
QML_DIALOG_DEF(LoginDialog)
LoginDialog::LoginDialog(QQuickItem *parent) : QQuickItem(parent), _rootUrl(NetworkingConstants::METAVERSE_SERVER_URL.toString()) {
connect(&AccountManager::getInstance(), &AccountManager::loginComplete,
this, &LoginDialog::handleLoginCompleted);
this, &LoginDialog::handleLoginCompleted);
connect(&AccountManager::getInstance(), &AccountManager::loginFailed,
this, &LoginDialog::handleLoginFailed);
connect(_ui->loginButton, &QPushButton::clicked,
this, &LoginDialog::handleLoginClicked);
connect(_ui->closeButton, &QPushButton::clicked,
this, &LoginDialog::close);
UIUtil::scaleWidgetFontSizes(this);
_ui->accountLabel->setText(_ui->accountLabel->text().arg(CREATE_ACCOUNT_URL, FORGOT_PASSWORD_URL));
// Initialize toggle connection
toggleQAction();
};
LoginDialog::~LoginDialog() {
delete _ui;
};
void LoginDialog::reset() {
_ui->errorLabel->hide();
_ui->emailLineEdit->setFocus();
_ui->logoLabel->setPixmap(QPixmap(PathUtils::resourcesPath() + "images/hifi-logo.svg"));
_ui->loginButton->setIcon(QIcon(PathUtils::resourcesPath() + "images/login.svg"));
_ui->closeButton->setIcon(QIcon(PathUtils::resourcesPath() + "images/close.svg"));
_ui->infoLabel->setVisible(false);
_ui->errorLabel->setVisible(false);
_ui->emailLineEdit->setText("");
_ui->passwordLineEdit->setText("");
_ui->loginArea->setDisabled(false);
this, &LoginDialog::handleLoginFailed);
}
void LoginDialog::handleLoginCompleted(const QUrl& authURL) {
reset();
close();
};
void LoginDialog::handleLoginFailed() {
_ui->infoLabel->setVisible(false);
_ui->errorLabel->setVisible(true);
_ui->errorLabel->show();
_ui->loginArea->setDisabled(false);
// Move focus to password and select the entire line
_ui->passwordLineEdit->setFocus();
_ui->passwordLineEdit->setSelection(0, _ui->emailLineEdit->maxLength());
};
void LoginDialog::handleLoginClicked() {
// If the email or password inputs are empty, move focus to them, otherwise attempt to login.
if (_ui->emailLineEdit->text().isEmpty()) {
_ui->emailLineEdit->setFocus();
} else if (_ui->passwordLineEdit->text().isEmpty()) {
_ui->passwordLineEdit->setFocus();
} else {
_ui->infoLabel->setVisible(true);
_ui->errorLabel->setVisible(false);
_ui->loginArea->setDisabled(true);
AccountManager::getInstance().requestAccessToken(_ui->emailLineEdit->text(), _ui->passwordLineEdit->text());
}
};
void LoginDialog::moveEvent(QMoveEvent* event) {
// Modal dialogs seemed to get repositioned automatically. Combat this by moving the window if needed.
resizeAndPosition();
};
void LoginDialog::toggleQAction() {
void LoginDialog::toggleAction() {
AccountManager& accountManager = AccountManager::getInstance();
QAction* loginAction = Menu::getInstance()->getActionForOption(MenuOption::Login);
Q_CHECK_PTR(loginAction);
disconnect(loginAction, 0, 0, 0);
if (accountManager.isLoggedIn()) {
// change the menu item to logout
loginAction->setText("Logout " + accountManager.getAccountInfo().getUsername());
@ -122,11 +37,40 @@ void LoginDialog::toggleQAction() {
} else {
// change the menu item to login
loginAction->setText("Login");
connect(loginAction, &QAction::triggered, this, &LoginDialog::showLoginForCurrentDomain);
connect(loginAction, &QAction::triggered, &LoginDialog::show);
}
}
void LoginDialog::showLoginForCurrentDomain() {
show();
resizeAndPosition(false);
void LoginDialog::handleLoginCompleted(const QUrl& authURL) {
setEnabled(false);
setVisible(false);
}
void LoginDialog::handleLoginFailed() {
setStatusText("<font color = \"#267077\">Invalid username or password.< / font>");
}
void LoginDialog::setStatusText(const QString &a) {
if (a != _statusText) {
_statusText = a;
emit statusTextChanged();
}
}
QString LoginDialog::statusText() const {
return _statusText;
}
QString LoginDialog::rootUrl() const {
return _rootUrl;
}
void LoginDialog::login(const QString & username, const QString & password) {
qDebug() << "Attempting to login " << username;
setStatusText("Authenticating...");
AccountManager::getInstance().requestAccessToken(username, password);
}
void LoginDialog::openUrl(const QString & url) {
qDebug() << url;
}

View file

@ -1,9 +1,8 @@
//
// LoginDialog.h
// interface/src/ui
//
// Created by Ryan Huffman on 4/23/14.
// Copyright 2014 High Fidelity, Inc.
// Created by Bradley Austin Davis on 2015/04/14
// 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
@ -12,35 +11,41 @@
#ifndef hifi_LoginDialog_h
#define hifi_LoginDialog_h
#include <QObject>
#include "FramelessDialog.h"
#pragma once
#include <QQuickItem>
namespace Ui {
class LoginDialog;
}
#include "OffscreenUi.h"
class LoginDialog : public FramelessDialog {
class LoginDialog : public QQuickItem
{
Q_OBJECT
QML_DIALOG_DECL
Q_PROPERTY(QString statusText READ statusText WRITE setStatusText NOTIFY statusTextChanged)
Q_PROPERTY(QString rootUrl READ rootUrl)
public:
LoginDialog(QWidget* parent);
~LoginDialog();
static void toggleAction();
public slots:
void toggleQAction();
void showLoginForCurrentDomain();
protected slots:
void reset();
void handleLoginClicked();
LoginDialog(QQuickItem *parent = 0);
void setStatusText(const QString & a);
QString statusText() const;
QString rootUrl() const;
signals:
void statusTextChanged();
protected:
void handleLoginCompleted(const QUrl& authURL);
void handleLoginFailed();
protected:
void moveEvent(QMoveEvent* event);
Q_INVOKABLE void login(const QString & username, const QString & password);
Q_INVOKABLE void openUrl(const QString & url);
private:
Ui::LoginDialog* _ui = nullptr;
QString _statusText;
const QString _rootUrl;
};
#endif // hifi_LoginDialog_h

View file

@ -6,7 +6,7 @@ AUTOSCRIBE_SHADER_LIB(gpu model)
qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc")
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library(Widgets OpenGL Network Script)
setup_hifi_library(Widgets OpenGL Network Qml Quick Script)
add_dependency_external_projects(glm)
find_package(GLM REQUIRED)

View file

@ -0,0 +1,98 @@
//
// OffscreenGlCanvas.cpp
// interface/src/renderer
//
// Created by Bradley Austin Davis on 2014/04/09.
// 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
//
#include "FboCache.h"
#include <QOpenGLFramebufferObject>
#include <QDebug>
#include "ThreadHelpers.h"
FboCache::FboCache() {
// Why do we even HAVE that lever?
}
void FboCache::lockTexture(int texture) {
withLock(_lock, [&] {
Q_ASSERT(_fboMap.count(texture));
if (!_fboLocks.count(texture)) {
Q_ASSERT(_readyFboQueue.front()->texture() == texture);
_readyFboQueue.pop_front();
_fboLocks[texture] = 1;
} else {
_fboLocks[texture]++;
}
});
}
void FboCache::releaseTexture(int texture) {
withLock(_lock, [&] {
Q_ASSERT(_fboMap.count(texture));
Q_ASSERT(_fboLocks.count(texture));
int newLockCount = --_fboLocks[texture];
if (!newLockCount) {
auto fbo = _fboMap[texture].data();
if (fbo->size() != _size) {
// Move the old FBO to the destruction queue.
// We can't destroy the FBO here because we might
// not be on the right thread or have the context active
_destroyFboQueue.push_back(_fboMap[texture]);
_fboMap.remove(texture);
} else {
_readyFboQueue.push_back(fbo);
}
_fboLocks.remove(texture);
}
});
}
QOpenGLFramebufferObject* FboCache::getReadyFbo() {
QOpenGLFramebufferObject* result = nullptr;
withLock(_lock, [&] {
// Delete any FBOs queued for deletion
_destroyFboQueue.clear();
if (_readyFboQueue.empty()) {
qDebug() << "Building new offscreen FBO number " << _fboMap.size() + 1;
result = new QOpenGLFramebufferObject(_size, QOpenGLFramebufferObject::CombinedDepthStencil);
_fboMap[result->texture()] = QSharedPointer<QOpenGLFramebufferObject>(result);
_readyFboQueue.push_back(result);
} else {
result = _readyFboQueue.front();
}
});
return result;
}
void FboCache::setSize(const QSize & newSize) {
if (_size == newSize) {
return;
}
_size = newSize;
withLock(_lock, [&] {
// Clear out any fbos with the old id
_readyFboQueue.clear();
QSet<int> outdatedFbos;
// FBOs that are locked will be removed as they are unlocked
foreach(int texture, _fboMap.keys()) {
if (!_fboLocks.count(texture)) {
outdatedFbos.insert(texture);
}
}
// Implicitly deletes the FBO via the shared pointer destruction mechanism
foreach(int texture, outdatedFbos) {
_fboMap.remove(texture);
}
});
}

View file

@ -0,0 +1,50 @@
//
// OffscreenGlCanvas.h
// interface/src/renderer
//
// Created by Bradley Austin Davis on 2014/04/09.
// 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
//
#pragma once
#ifndef hifi_FboCache_h
#define hifi_FboCache_h
#include <QOpenGLContext>
#include <QOffscreenSurface>
#include <QQueue>
#include <QMap>
#include <QSharedPointer>
class QOpenGLFramebufferObject;
class FboCache : public QObject {
public:
FboCache();
// setSize() and getReadyFbo() must consitently be called from only a single
// thread. Additionally, it is the caller's responsibility to ensure that
// the appropriate OpenGL context is active when doing so.
// Important.... textures are sharable resources, but FBOs ARE NOT.
void setSize(const QSize & newSize);
QOpenGLFramebufferObject* getReadyFbo();
// These operations are thread safe and require no OpenGL context. They manipulate the
// internal locks and pointers but execute no OpenGL opreations.
void lockTexture(int texture);
void releaseTexture(int texture);
protected:
QMap<int, QSharedPointer<QOpenGLFramebufferObject>> _fboMap;
QMap<int, int> _fboLocks;
QQueue<QOpenGLFramebufferObject*> _readyFboQueue;
QQueue<QSharedPointer<QOpenGLFramebufferObject>> _destroyFboQueue;
QMutex _lock;
QSize _size;
};
#endif // hifi_FboCache_h

View file

@ -0,0 +1,43 @@
//
// OffscreenGlCanvas.cpp
// interface/src/renderer
//
// Created by Bradley Austin Davis on 2014/04/09.
// 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
//
#include "OffscreenGlCanvas.h"
OffscreenGlCanvas::OffscreenGlCanvas() {
}
void OffscreenGlCanvas::create(QOpenGLContext * sharedContext) {
QSurfaceFormat format;
format.setDepthBufferSize(16);
format.setStencilBufferSize(8);
format.setMajorVersion(4);
format.setMinorVersion(1);
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile);
_context.setFormat(format);
if (nullptr != sharedContext) {
_context.setShareContext(sharedContext);
}
_context.create();
_offscreenSurface.setFormat(_context.format());
_offscreenSurface.create();
}
bool OffscreenGlCanvas::makeCurrent() {
return _context.makeCurrent(&_offscreenSurface);
}
void OffscreenGlCanvas::doneCurrent() {
_context.doneCurrent();
}

View file

@ -0,0 +1,31 @@
//
// OffscreenGlCanvas.h
// interface/src/renderer
//
// Created by Bradley Austin Davis on 2014/04/09.
// 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
//
#pragma once
#ifndef hifi_OffscreenGlCanvas_h
#define hifi_OffscreenGlCanvas_h
#include <QOpenGLContext>
#include <QOffscreenSurface>
class OffscreenGlCanvas : public QObject {
public:
OffscreenGlCanvas();
void create(QOpenGLContext * sharedContext = nullptr);
bool makeCurrent();
void doneCurrent();
protected:
QOpenGLContext _context;
QOffscreenSurface _offscreenSurface;
};
#endif // hifi_OffscreenGlCanvas_h

View file

@ -0,0 +1,325 @@
#include "OffscreenUi.h"
#include <QOpenGLFramebufferObject>
#include <QOpenGLDebugLogger>
#include <QGLWidget>
OffscreenUi::OffscreenUi() {
}
OffscreenUi::~OffscreenUi() {
// Make sure the context is current while doing cleanup. Note that we use the
// offscreen surface here because passing 'this' at this point is not safe: the
// underlying platform window may already be destroyed. To avoid all the trouble, use
// another surface that is valid for sure.
makeCurrent();
// Delete the render control first since it will free the scenegraph resources.
// Destroy the QQuickWindow only afterwards.
delete _renderControl;
delete _qmlComponent;
delete _quickWindow;
delete _qmlEngine;
doneCurrent();
}
void OffscreenUi::create(QOpenGLContext * shareContext) {
OffscreenGlCanvas::create(shareContext);
makeCurrent();
// Create a QQuickWindow that is associated with out render control. Note that this
// window never gets created or shown, meaning that it will never get an underlying
// native (platform) window.
QQuickWindow::setDefaultAlphaBuffer(true);
_quickWindow = new QQuickWindow(_renderControl);
_quickWindow->setColor(QColor(255, 255, 255, 0));
_quickWindow->setFlags(_quickWindow->flags() | static_cast<Qt::WindowFlags>(Qt::WA_TranslucentBackground));
// Create a QML engine.
_qmlEngine = new QQmlEngine;
if (!_qmlEngine->incubationController())
_qmlEngine->setIncubationController(_quickWindow->incubationController());
// When Quick says there is a need to render, we will not render immediately. Instead,
// a timer with a small interval is used to get better performance.
_updateTimer.setSingleShot(true);
_updateTimer.setInterval(5);
connect(&_updateTimer, &QTimer::timeout, this, &OffscreenUi::updateQuick);
// Now hook up the signals. For simplicy we don't differentiate between
// renderRequested (only render is needed, no sync) and sceneChanged (polish and sync
// is needed too).
connect(_renderControl, &QQuickRenderControl::renderRequested, this, &OffscreenUi::requestRender);
connect(_renderControl, &QQuickRenderControl::sceneChanged, this, &OffscreenUi::requestUpdate);
connect(_quickWindow, &QQuickWindow::focusObjectChanged, this, [this](QObject *object){
OffscreenUi * p = this;
qDebug() << "Focus changed to " << object;
});
_quickWindow->focusObject();
_qmlComponent = new QQmlComponent(_qmlEngine);
// Initialize the render control and our OpenGL resources.
makeCurrent();
_renderControl->initialize(&_context);
}
void OffscreenUi::resize(const QSize & newSize) {
makeCurrent();
// Clear out any fbos with the old size
_fboCache.setSize(newSize);
// Update our members
if (_rootItem) {
_rootItem->setSize(newSize);
}
if (_quickWindow) {
_quickWindow->setGeometry(QRect(QPoint(), newSize));
}
doneCurrent();
}
QQmlContext * OffscreenUi::qmlContext() {
if (nullptr == _rootItem) {
return _qmlComponent->creationContext();
}
return QQmlEngine::contextForObject(_rootItem);
}
void OffscreenUi::loadQml(const QUrl & qmlSource, std::function<void(QQmlContext*)> f) {
_qmlComponent->loadUrl(qmlSource);
if (_qmlComponent->isLoading())
connect(_qmlComponent, &QQmlComponent::statusChanged, this, &OffscreenUi::finishQmlLoad);
else
finishQmlLoad();
}
void OffscreenUi::requestUpdate() {
_polish = true;
if (!_updateTimer.isActive())
_updateTimer.start();
}
void OffscreenUi::requestRender() {
if (!_updateTimer.isActive())
_updateTimer.start();
}
void OffscreenUi::finishQmlLoad() {
disconnect(_qmlComponent, &QQmlComponent::statusChanged, this, &OffscreenUi::finishQmlLoad);
if (_qmlComponent->isError()) {
QList<QQmlError> errorList = _qmlComponent->errors();
foreach(const QQmlError &error, errorList) {
qWarning() << error.url() << error.line() << error;
}
return;
}
QObject *rootObject = _qmlComponent->create();
if (_qmlComponent->isError()) {
QList<QQmlError> errorList = _qmlComponent->errors();
foreach(const QQmlError &error, errorList)
qWarning() << error.url() << error.line() << error;
qFatal("Unable to finish loading QML");
return;
}
_rootItem = qobject_cast<QQuickItem *>(rootObject);
if (!_rootItem) {
qWarning("run: Not a QQuickItem");
delete rootObject;
qFatal("Unable to find root QQuickItem");
return;
}
// Make sure we can assign focus to the root item (critical for
// supporting keyboard shortcuts)
_rootItem->setFlag(QQuickItem::ItemIsFocusScope, true);
// The root item is ready. Associate it with the window.
_rootItem->setParentItem(_quickWindow->contentItem());
_rootItem->setSize(_quickWindow->renderTargetSize());
qDebug() << "Finished setting up QML provider";
}
void OffscreenUi::updateQuick() {
if (_paused) {
return;
}
if (!makeCurrent())
return;
// Polish, synchronize and render the next frame (into our fbo). In this example
// everything happens on the same thread and therefore all three steps are performed
// in succession from here. In a threaded setup the render() call would happen on a
// separate thread.
if (_polish) {
_renderControl->polishItems();
_renderControl->sync();
_polish = false;
}
QOpenGLFramebufferObject* fbo = _fboCache.getReadyFbo();
_quickWindow->setRenderTarget(fbo);
fbo->bind();
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
_renderControl->render();
Q_ASSERT(!glGetError());
_quickWindow->resetOpenGLState();
QOpenGLFramebufferObject::bindDefault();
// Force completion of all the operations before we emit the texture as being ready for use
glFinish();
emit textureUpdated(fbo->texture());
}
QPointF OffscreenUi::mapWindowToUi(const QPointF & p, QObject * dest) {
vec2 sourceSize;
if (dynamic_cast<QWidget*>(dest)) {
sourceSize = toGlm(((QWidget*)dest)->size());
} else if (dynamic_cast<QWindow*>(dest)) {
sourceSize = toGlm(((QWindow*)dest)->size());
}
vec2 pos = toGlm(p);
pos /= sourceSize;
pos *= vec2(toGlm(_quickWindow->renderTargetSize()));
return QPointF(pos.x, pos.y);
}
///////////////////////////////////////////////////////
//
// Event handling customization
//
bool OffscreenUi::eventFilter(QObject * dest, QEvent * e) {
// Only intercept events while we're in an active state
if (_paused) {
return false;
}
// Don't intercept our own events, or we enter an infinite recursion
if (dest == _quickWindow) {
return false;
}
switch (e->type()) {
case QEvent::Resize:
{
QResizeEvent * re = (QResizeEvent *)e;
QGLWidget * widget = dynamic_cast<QGLWidget*>(dest);
if (widget) {
this->resize(re->size());
}
return false;
}
case QEvent::KeyPress:
case QEvent::KeyRelease:
{
e->ignore();
if (QApplication::sendEvent(_quickWindow, e)) {
return e->isAccepted();
}
}
break;
case QEvent::Wheel:
{
QWheelEvent * we = (QWheelEvent*)e;
QWheelEvent mappedEvent(mapWindowToUi(we->pos(), dest), we->delta(), we->buttons(), we->modifiers(), we->orientation());
QCoreApplication::sendEvent(_quickWindow, &mappedEvent);
return true;
}
break;
// Fall through
case QEvent::MouseButtonDblClick:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseMove:
{
QMouseEvent * me = (QMouseEvent *)e;
QPointF originalPos = me->localPos();
QPointF transformedPos = _mouseTranslator(originalPos);
QMouseEvent mappedEvent(e->type(), mapWindowToUi(transformedPos, dest), me->screenPos(), me->button(), me->buttons(), me->modifiers());
QCoreApplication::sendEvent(_quickWindow, &mappedEvent);
return QObject::event(e);
}
default: break;
}
return false;
}
void OffscreenUi::lockTexture(int texture) {
_fboCache.lockTexture(texture);
}
void OffscreenUi::releaseTexture(int texture) {
_fboCache.releaseTexture(texture);
}
void OffscreenUi::pause() {
_paused = true;
}
void OffscreenUi::resume() {
_paused = false;
requestRender();
}
bool OffscreenUi::isPaused() const {
return _paused;
}
void OffscreenUi::setProxyWindow(QWindow * window) {
_renderControl->_renderWindow = window;
}
void OffscreenUi::show(const QUrl & url, const QString & name) {
QQuickItem * item = _rootItem->findChild<QQuickItem*>(name);
if (nullptr != item) {
item->setEnabled(true);
item->setVisible(true);
} else {
load(url);
}
}
void OffscreenUi::toggle(const QUrl & url, const QString & name) {
QQuickItem * item = _rootItem->findChild<QQuickItem*>(name);
// First load?
if (nullptr == item) {
load(url);
return;
}
// Toggle the visibity AND the enabled flag (otherwise invisible
// dialogs can still swallow keyboard input)
bool newFlag = !item->isVisible();
item->setVisible(newFlag);
item->setEnabled(newFlag);
}
void OffscreenUi::load(const QUrl & url) {
QVariant returnedValue;
QVariant msg = url;
QMetaObject::invokeMethod(_rootItem, "loadChild",
Q_RETURN_ARG(QVariant, returnedValue),
Q_ARG(QVariant, msg));
qDebug() << "QML function returned:" << returnedValue.toString();
}

View file

@ -0,0 +1,136 @@
//
// OffscreenUi.h
// interface/src/entities
//
// Created by Bradley Austin Davis on 2015-04-04
// 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
//
#pragma once
#ifndef hifi_OffscreenUi_h
#define hifi_OffscreenUi_h
#include <QOpenGLContext>
#include <QOffscreenSurface>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQuickItem>
#include <QQuickWindow>
#include <QQuickRenderControl>
#include <QQuickImageProvider>
#include <GLMHelpers.h>
#include <ThreadHelpers.h>
#include <QQueue>
#include <QTimer>
#include <QApplication>
#include <atomic>
#include <functional>
#include <DependencyManager.h>
#include "FboCache.h"
#include "OffscreenGlCanvas.h"
#define QML_DIALOG_DECL \
private: \
static const QString NAME; \
static const QUrl QML; \
public: \
static void registerType(); \
static void show(); \
static void toggle(); \
private:
#define QML_DIALOG_DEF(x) \
const QUrl x::QML = #x ".qml"; \
const QString x::NAME = #x; \
\
void x::registerType() { \
qmlRegisterType<x>("Hifi", 1, 0, NAME.toLocal8Bit().constData()); \
} \
\
void x::show() { \
auto offscreenUi = DependencyManager::get<OffscreenUi>(); \
offscreenUi->show(QML, NAME); \
} \
\
void x::toggle() { \
auto offscreenUi = DependencyManager::get<OffscreenUi>(); \
offscreenUi->toggle(QML, NAME); \
}
class OffscreenUi : public OffscreenGlCanvas, public Dependency {
Q_OBJECT
class QMyQuickRenderControl : public QQuickRenderControl {
protected:
QWindow * renderWindow(QPoint * offset) Q_DECL_OVERRIDE{
if (nullptr == _renderWindow) {
return QQuickRenderControl::renderWindow(offset);
}
if (nullptr != offset) {
offset->rx() = offset->ry() = 0;
}
return _renderWindow;
}
private:
QWindow * _renderWindow{ nullptr };
friend class OffscreenUi;
};
public:
using MouseTranslator = std::function < QPointF(const QPointF &) > ;
OffscreenUi();
virtual ~OffscreenUi();
void create(QOpenGLContext * context);
void resize(const QSize & size);
void loadQml(const QUrl & qmlSource, std::function<void(QQmlContext*)> f = [](QQmlContext*) {});
void load(const QUrl & url);
void show(const QUrl & url, const QString & name);
void toggle(const QUrl & url, const QString & name);
QQmlContext * qmlContext();
void pause();
void resume();
bool isPaused() const;
void setProxyWindow(QWindow * window);
QPointF mapWindowToUi(const QPointF & p, QObject * dest);
virtual bool eventFilter(QObject * dest, QEvent * e);
void setMouseTranslator(MouseTranslator mt) {
_mouseTranslator = mt;
}
protected:
private slots:
void updateQuick();
void finishQmlLoad();
public slots:
void requestUpdate();
void requestRender();
void lockTexture(int texture);
void releaseTexture(int texture);
signals:
void textureUpdated(GLuint texture);
private:
QMyQuickRenderControl *_renderControl{ new QMyQuickRenderControl };
QQuickWindow *_quickWindow{ nullptr };
QQmlEngine *_qmlEngine{ nullptr };
QQmlComponent *_qmlComponent{ nullptr };
QQuickItem * _rootItem{ nullptr };
QTimer _updateTimer;
FboCache _fboCache;
bool _polish{ true };
bool _paused{ true };
MouseTranslator _mouseTranslator{ [](const QPointF & p) { return p; } };
};
#endif

View file

@ -15,4 +15,54 @@
/// Renders a quad from (-1, -1, 0) to (1, 1, 0) with texture coordinates from (sMin, tMin) to (sMax, tMax).
void renderFullscreenQuad(float sMin = 0.0f, float sMax = 1.0f, float tMin = 0.0f, float tMax = 1.0f);
template <typename F, GLenum matrix>
void withMatrixPush(F f) {
glMatrixMode(matrix);
glPushMatrix();
f();
glPopMatrix();
}
template <typename F>
void withProjectionPush(F f) {
withMatrixPush<GL_PROJECTION>(f);
}
template <typename F>
void withProjectionIdentity(F f) {
withProjectionPush([&] {
glLoadIdentity();
f();
});
}
template <typename F>
void withProjectionMatrix(GLfloat * matrix, F f) {
withProjectionPush([&] {
glLoadMatrixf(matrix);
f();
});
}
template <typename F>
void withModelviewPush(F f) {
withMatrixPush<GL_MODELVIEW>(f);
}
template <typename F>
void withModelviewIdentity(F f) {
withModelviewPush([&] {
glLoadIdentity();
f();
});
}
template <typename F>
void withModelviewMatrix(GLfloat * matrix, F f) {
withModelviewPush([&] {
glLoadMatrixf(matrix);
f();
});
}
#endif // hifi_RenderUtil_h

View file

@ -17,6 +17,17 @@
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
// Bring the most commonly used GLM types into the default namespace
using glm::ivec3;
using glm::ivec2;
using glm::uvec2;
using glm::mat3;
using glm::mat4;
using glm::vec2;
using glm::vec3;
using glm::vec4;
using glm::quat;
#include <QtCore/QByteArray>
#include <QtGui/QMatrix4x4>
#include <QtGui/QColor>

View file

@ -0,0 +1,13 @@
#pragma once
#include <exception>
template <typename L, typename F>
void withLock(L lock, F function) {
throw std::exception();
}
template <typename F>
void withLock(QMutex & lock, F function) {
QMutexLocker locker(&lock);
function();
}

View file

@ -10,6 +10,7 @@
#include "TextRenderer.h"
#include "MatrixStack.h"
#include "OffscreenUi.h"
#include <QWindow>
#include <QFile>
@ -21,9 +22,11 @@
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QResizeEvent>
#include <QLoggingCategory>
#include <QOpenGLTexture>
#include <QOpenGLVertexArrayObject>
#include <QApplication>
#include <QOpenGLDebugLogger>
#include <unordered_map>
#include <memory>
#include <glm/glm.hpp>
@ -66,99 +69,143 @@ public:
};
// Create a simple OpenGL window that renders text in various ways
class QTestWindow: public QWindow {
class QTestWindow : public QWindow {
Q_OBJECT
QOpenGLContext * _context;
QOpenGLContext * _context{ nullptr };
QSize _size;
TextRenderer* _textRenderer[4];
RateCounter fps;
OffscreenUi _offscreenUi;
int testQmlTexture{ 0 };
//ProgramPtr _planeProgam;
//ShapeWrapperPtr _planeShape;
protected:
void resizeEvent(QResizeEvent * ev) override {
QWindow::resizeEvent(ev);
_size = ev->size();
resizeGl();
}
void resizeGl() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _size.width(), _size.height(), 0, 1, -1);
glMatrixMode(GL_MODELVIEW);
glViewport(0, 0, _size.width(), _size.height());
void renderText();
void renderQml();
private:
void resizeWindow(const QSize & size) {
_size = size;
_offscreenUi.resize(_size);
}
public:
QTestWindow();
virtual ~QTestWindow() {
QTestWindow() {
setSurfaceType(QSurface::OpenGLSurface);
QSurfaceFormat format;
// Qt Quick may need a depth and stencil buffer. Always make sure these are available.
format.setDepthBufferSize(16);
format.setStencilBufferSize(8);
format.setVersion(4, 5);
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile);
format.setOption(QSurfaceFormat::DebugContext);
setFormat(format);
_context = new QOpenGLContext;
_context->setFormat(format);
_context->create();
show();
makeCurrent();
{
QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this);
logger->initialize(); // initializes in the current context, i.e. ctx
logger->enableMessages();
connect(logger, &QOpenGLDebugLogger::messageLogged, this, [&](const QOpenGLDebugMessage & debugMessage) {
qDebug() << debugMessage;
});
// logger->startLogging(QOpenGLDebugLogger::SynchronousLogging);
}
qDebug() << (const char*)glGetString(GL_VERSION);
#ifdef WIN32
glewExperimental = true;
GLenum err = glewInit();
if (GLEW_OK != err) {
/* Problem: glewInit failed, something is seriously wrong. */
const GLubyte * errStr = glewGetErrorString(err);
qDebug("Error: %s\n", errStr);
}
qDebug("Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
if (wglewGetExtension("WGL_EXT_swap_control")) {
int swapInterval = wglGetSwapIntervalEXT();
qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF"));
}
glGetError();
#endif
_textRenderer[0] = TextRenderer::getInstance(SANS_FONT_FAMILY, 12, false);
_textRenderer[1] = TextRenderer::getInstance(SERIF_FONT_FAMILY, 12, false,
TextRenderer::SHADOW_EFFECT);
_textRenderer[2] = TextRenderer::getInstance(MONO_FONT_FAMILY, 48, -1,
false, TextRenderer::OUTLINE_EFFECT);
_textRenderer[3] = TextRenderer::getInstance(INCONSOLATA_FONT_FAMILY, 24);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0.2f, 0.2f, 0.2f, 1);
glDisable(GL_DEPTH_TEST);
_offscreenUi.create(_context);
// FIXME, need to switch to a QWindow for mouse and keyboard input to work
_offscreenUi.setProxyWindow(this);
// "#0e7077"
setFramePosition(QPoint(-1000, 0));
resize(QSize(800, 600));
static const QString f("/Users/bdavis/Git/hifi/interface/resources/qml/Root.qml");
_offscreenUi.loadQml(QUrl::fromLocalFile(f));
connect(&_offscreenUi, &OffscreenUi::textureUpdated, this, [&](int textureId) {
_offscreenUi.lockTexture(textureId);
assert(!glGetError());
GLuint oldTexture = testQmlTexture;
testQmlTexture = textureId;
if (oldTexture) {
_offscreenUi.releaseTexture(oldTexture);
}
});
installEventFilter(&_offscreenUi);
_offscreenUi.resume();
}
virtual ~QTestWindow() {
}
void draw();
void makeCurrent() {
_context->makeCurrent(this);
}
void draw();
protected:
void resizeEvent(QResizeEvent * ev) override {
resizeWindow(ev->size());
}
void keyPressEvent(QKeyEvent *event) {
switch (event->key()) {
case Qt::Key_Slash:
qDebug() << "Foo";
_offscreenUi.load(QString("Login.qml"));
break;
}
QWindow::keyPressEvent(event);
}
};
#ifndef SERIF_FONT_FAMILY
#define SERIF_FONT_FAMILY "Times New Roman"
#endif
QTestWindow::QTestWindow() {
setSurfaceType(QSurface::OpenGLSurface);
QSurfaceFormat format;
// Qt Quick may need a depth and stencil buffer. Always make sure these are available.
format.setDepthBufferSize(16);
format.setStencilBufferSize(8);
format.setVersion(3, 2);
format.setProfile(
QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile);
setFormat(format);
_context = new QOpenGLContext;
_context->setFormat(format);
_context->create();
show();
makeCurrent();
qDebug() << (const char*) glGetString(GL_VERSION);
#ifdef WIN32
glewExperimental = true;
GLenum err = glewInit();
if (GLEW_OK != err) {
/* Problem: glewInit failed, something is seriously wrong. */
const GLubyte * errStr = glewGetErrorString(err);
qDebug("Error: %s\n", errStr);
}
qDebug("Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
if (wglewGetExtension("WGL_EXT_swap_control")) {
int swapInterval = wglGetSwapIntervalEXT();
qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF"));
}
glGetError();
#endif
setFramePosition(QPoint(100, -900));
resize(QSize(800, 600));
_size = QSize(800, 600);
_textRenderer[0] = TextRenderer::getInstance(SANS_FONT_FAMILY, 12, false);
_textRenderer[1] = TextRenderer::getInstance(SERIF_FONT_FAMILY, 12, false,
TextRenderer::SHADOW_EFFECT);
_textRenderer[2] = TextRenderer::getInstance(MONO_FONT_FAMILY, 48, -1,
false, TextRenderer::OUTLINE_EFFECT);
_textRenderer[3] = TextRenderer::getInstance(INCONSOLATA_FONT_FAMILY, 24);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0.2f, 0.2f, 0.2f, 1);
glDisable(GL_DEPTH_TEST);
resizeGl();
}
static const wchar_t * EXAMPLE_TEXT = L"Hello";
//static const wchar_t * EXAMPLE_TEXT = L"\xC1y Hello 1.0\ny\xC1 line 2\n\xC1y";
static const glm::uvec2 QUAD_OFFSET(10, 10);
@ -166,14 +213,21 @@ static const glm::uvec2 QUAD_OFFSET(10, 10);
static const glm::vec3 COLORS[4] = { { 1.0, 1.0, 1.0 }, { 0.5, 1.0, 0.5 }, {
1.0, 0.5, 0.5 }, { 0.5, 0.5, 1.0 } };
void QTestWindow::draw() {
makeCurrent();
glClear(GL_COLOR_BUFFER_BIT);
void QTestWindow::renderText() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _size.width(), _size.height(), 0, 1, -1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
const glm::uvec2 size = glm::uvec2(_size.width() / 2, _size.height() / 2);
const glm::uvec2 offsets[4] = { { QUAD_OFFSET.x, QUAD_OFFSET.y }, { size.x
+ QUAD_OFFSET.x, QUAD_OFFSET.y }, { size.x + QUAD_OFFSET.x, size.y
+ QUAD_OFFSET.y }, { QUAD_OFFSET.x, size.y + QUAD_OFFSET.y }, };
const glm::uvec2 offsets[4] = {
{ QUAD_OFFSET.x, QUAD_OFFSET.y },
{ size.x + QUAD_OFFSET.x, QUAD_OFFSET.y },
{ size.x + QUAD_OFFSET.x, size.y + QUAD_OFFSET.y },
{ QUAD_OFFSET.x, size.y + QUAD_OFFSET.y },
};
QString str = QString::fromWCharArray(EXAMPLE_TEXT);
for (int i = 0; i < 4; ++i) {
@ -200,17 +254,57 @@ void QTestWindow::draw() {
glm::vec4(COLORS[i], 1.0f));
}
}
}
void QTestWindow::renderQml() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if (testQmlTexture > 0) {
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, testQmlTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
glBegin(GL_QUADS);
{
glTexCoord2f(0, 0);
glVertex2f(-1, -1);
glTexCoord2f(0, 1);
glVertex2f(-1, 1);
glTexCoord2f(1, 1);
glVertex2f(1, 1);
glTexCoord2f(1, 0);
glVertex2f(1, -1);
}
glEnd();
}
void QTestWindow::draw() {
makeCurrent();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, _size.width(), _size.height());
renderText();
//renderQml();
_context->swapBuffers(this);
glFinish();
fps.increment();
if (fps.elapsed() >= 2.0f) {
qDebug() << "FPS: " << fps.rate();
//qDebug() << "FPS: " << fps.rate();
fps.reset();
}
}
int main(int argc, char** argv) {
QApplication app(argc, argv);
//QLoggingCategory::setFilterRules("qt.quick.mouse.debug = true");
QTestWindow window;
QTimer timer;
timer.setInterval(1);