mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 06:44:06 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into hdr
This commit is contained in:
commit
3f4f10c38c
48 changed files with 2488 additions and 2204 deletions
|
@ -156,13 +156,6 @@ var STATE_CONTINUE_EQUIP = 14;
|
|||
var STATE_WAITING_FOR_BUMPER_RELEASE = 15;
|
||||
var STATE_EQUIP_SPRING = 16;
|
||||
|
||||
// Used by the HandAnimaitonBuddy to play hand animations
|
||||
var IDLE_HAND_STATES = [STATE_OFF, STATE_RELEASE];
|
||||
var OPEN_HAND_STATES = [STATE_SEARCHING, STATE_EQUIP_SEARCHING];
|
||||
var POINT_HAND_STATES = [STATE_NEAR_TRIGGER, STATE_CONTINUE_NEAR_TRIGGER, STATE_FAR_TRIGGER, STATE_CONTINUE_FAR_TRIGGER];
|
||||
var FAR_GRASP_HAND_STATES = [STATE_DISTANCE_HOLDING, STATE_CONTINUE_DISTANCE_HOLDING];
|
||||
// otherwise grasp
|
||||
|
||||
// collision masks are specified by comma-separated list of group names
|
||||
// the possible list of names is: static, dynamic, kinematic, myAvatar, otherAvatar
|
||||
var COLLISION_MASK_WHILE_GRABBED = "dynamic,otherAvatar";
|
||||
|
@ -274,94 +267,6 @@ function getSpatialOffsetRotation(hand, spatialKey) {
|
|||
return rotation;
|
||||
}
|
||||
|
||||
var HAND_IDLE_RAMP_ON_RATE = 0.1;
|
||||
var HAND_IDLE_RAMP_OFF_RATE = 0.02;
|
||||
|
||||
// ctor
|
||||
function HandAnimationBuddy(handController) {
|
||||
|
||||
this.handController = handController;
|
||||
this.hand = handController.hand;
|
||||
this.handIdleAlpha = 0;
|
||||
|
||||
var handPrefix = (this.hand === RIGHT_HAND) ? "right" : "left";
|
||||
this.animVarKeys = {
|
||||
idle: handPrefix + "HandIdle",
|
||||
overlayAlpha: handPrefix + "HandOverlayAlpha",
|
||||
open: handPrefix + "HandOpen",
|
||||
point: handPrefix + "HandPoint",
|
||||
farGrasp: handPrefix + "HandFarGrasp",
|
||||
grasp: handPrefix + "HandGrasp"
|
||||
};
|
||||
|
||||
// hook up anim var handler
|
||||
var self = this;
|
||||
this.animHandlerId = MyAvatar.addAnimationStateHandler(function (props) {
|
||||
return self.animStateHandler(props);
|
||||
}, []);
|
||||
}
|
||||
|
||||
HandAnimationBuddy.prototype.animStateHandler = function (props) {
|
||||
var foundState = false;
|
||||
var result = {};
|
||||
|
||||
var state = this.handController.state;
|
||||
var keys = this.animVarKeys;
|
||||
|
||||
// idle check & process
|
||||
if (IDLE_HAND_STATES.indexOf(state) != -1) {
|
||||
// ramp down handIdleAlpha
|
||||
this.handIdleAlpha = Math.max(0, this.handIdleAlpha - HAND_IDLE_RAMP_OFF_RATE);
|
||||
result[keys.idle] = true;
|
||||
foundState = true;
|
||||
} else {
|
||||
// ramp up handIdleAlpha
|
||||
this.handIdleAlpha = Math.min(1, this.handIdleAlpha + HAND_IDLE_RAMP_ON_RATE);
|
||||
result[keys.idle] = false;
|
||||
}
|
||||
result[keys.overlayAlpha] = this.handIdleAlpha;
|
||||
|
||||
// open check
|
||||
if (OPEN_HAND_STATES.indexOf(state) != -1) {
|
||||
result[keys.open] = true;
|
||||
foundState = true;
|
||||
} else {
|
||||
result[keys.open] = false;
|
||||
}
|
||||
|
||||
// point check
|
||||
if (POINT_HAND_STATES.indexOf(state) != -1) {
|
||||
result[keys.point] = true;
|
||||
foundState = true;
|
||||
} else {
|
||||
result[keys.point] = false;
|
||||
}
|
||||
|
||||
// far grasp check
|
||||
if (FAR_GRASP_HAND_STATES.indexOf(state) != -1) {
|
||||
result[keys.farGrasp] = true;
|
||||
foundState = true;
|
||||
} else {
|
||||
result[keys.farGrasp] = false;
|
||||
}
|
||||
|
||||
// grasp check
|
||||
if (!foundState) {
|
||||
result[keys.grasp] = true;
|
||||
} else {
|
||||
result[keys.grasp] = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
HandAnimationBuddy.prototype.cleanup = function () {
|
||||
if (this.animHandlerId) {
|
||||
MyAvatar.removeAnimationStateHandler(this.animHandlerId);
|
||||
this.animHandlerId = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
function MyController(hand) {
|
||||
this.hand = hand;
|
||||
if (this.hand === RIGHT_HAND) {
|
||||
|
@ -402,8 +307,6 @@ function MyController(hand) {
|
|||
this.offsetPosition = Vec3.ZERO;
|
||||
this.offsetRotation = Quat.IDENTITY;
|
||||
|
||||
this.handAnimationBuddy = new HandAnimationBuddy(this);
|
||||
|
||||
var _this = this;
|
||||
|
||||
this.update = function() {
|
||||
|
@ -1822,7 +1725,6 @@ function MyController(hand) {
|
|||
Entities.deleteEntity(this.particleBeam);
|
||||
Entities.deleteEntity(this.spotLight);
|
||||
Entities.deleteEntity(this.pointLight);
|
||||
this.handAnimationBuddy.cleanup();
|
||||
};
|
||||
|
||||
this.activateEntity = function(entityID, grabbedProperties) {
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
//
|
||||
// squeezeHands.js
|
||||
// examples
|
||||
//
|
||||
// Created by Philip Rosedale on June 4, 2014
|
||||
// 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
|
||||
//
|
||||
|
||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
|
||||
var rightHandAnimation = HIFI_PUBLIC_BUCKET + "animations/RightHandAnimPhilip.fbx";
|
||||
var leftHandAnimation = HIFI_PUBLIC_BUCKET + "animations/LeftHandAnimPhilip.fbx";
|
||||
|
||||
var LEFT = 0;
|
||||
var RIGHT = 1;
|
||||
|
||||
var lastLeftFrame = 0;
|
||||
var lastRightFrame = 0;
|
||||
|
||||
var leftDirection = true;
|
||||
var rightDirection = true;
|
||||
|
||||
var LAST_FRAME = 15.0; // What is the number of the last frame we want to use in the animation?
|
||||
var SMOOTH_FACTOR = 0.0;
|
||||
var MAX_FRAMES = 30.0;
|
||||
|
||||
Script.update.connect(function(deltaTime) {
|
||||
var leftTriggerValue = Controller.getTriggerValue(LEFT);
|
||||
var rightTriggerValue = Controller.getTriggerValue(RIGHT);
|
||||
|
||||
var leftFrame, rightFrame;
|
||||
|
||||
// Average last few trigger frames together for a bit of smoothing
|
||||
leftFrame = (leftTriggerValue * LAST_FRAME) * (1.0 - SMOOTH_FACTOR) + lastLeftFrame * SMOOTH_FACTOR;
|
||||
rightFrame = (rightTriggerValue * LAST_FRAME) * (1.0 - SMOOTH_FACTOR) + lastRightFrame * SMOOTH_FACTOR;
|
||||
|
||||
if (!leftDirection) {
|
||||
leftFrame = MAX_FRAMES - leftFrame;
|
||||
}
|
||||
if (!rightDirection) {
|
||||
rightFrame = MAX_FRAMES - rightFrame;
|
||||
}
|
||||
|
||||
if ((leftTriggerValue == 1.0) && (leftDirection == true)) {
|
||||
leftDirection = false;
|
||||
lastLeftFrame = MAX_FRAMES - leftFrame;
|
||||
} else if ((leftTriggerValue == 0.0) && (leftDirection == false)) {
|
||||
leftDirection = true;
|
||||
lastLeftFrame = leftFrame;
|
||||
}
|
||||
if ((rightTriggerValue == 1.0) && (rightDirection == true)) {
|
||||
rightDirection = false;
|
||||
lastRightFrame = MAX_FRAMES - rightFrame;
|
||||
} else if ((rightTriggerValue == 0.0) && (rightDirection == false)) {
|
||||
rightDirection = true;
|
||||
lastRightFrame = rightFrame;
|
||||
}
|
||||
|
||||
if ((leftFrame != lastLeftFrame) && leftHandAnimation.length){
|
||||
MyAvatar.startAnimation(leftHandAnimation, 30.0, 1.0, false, true, leftFrame, leftFrame);
|
||||
}
|
||||
if ((rightFrame != lastRightFrame) && rightHandAnimation.length) {
|
||||
MyAvatar.startAnimation(rightHandAnimation, 30.0, 1.0, false, true, rightFrame, rightFrame);
|
||||
}
|
||||
|
||||
lastLeftFrame = leftFrame;
|
||||
lastRightFrame = rightFrame;
|
||||
});
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
MyAvatar.stopAnimation(leftHandAnimation);
|
||||
MyAvatar.stopAnimation(rightHandAnimation);
|
||||
});
|
|
@ -1,86 +1,80 @@
|
|||
//
|
||||
// squeezeHands.js
|
||||
// examples
|
||||
// controllers/squeezeHands.js
|
||||
//
|
||||
// Created by Philip Rosedale on June 4, 2014
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
// Created by Anthony J. Thibault
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Default script to drive the animation of the hands based on hand controllers.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
var HIFI_OZAN_BUCKET = "http://hifi-public.s3.amazonaws.com/ozan/";
|
||||
|
||||
var rightHandAnimation = HIFI_OZAN_BUCKET + "anim/squeeze_hands/right_hand_anim.fbx";
|
||||
var leftHandAnimation = HIFI_OZAN_BUCKET + "anim/squeeze_hands/left_hand_anim.fbx";
|
||||
|
||||
var lastLeftTrigger = 0;
|
||||
var lastRightTrigger = 0;
|
||||
|
||||
var leftIsClosing = true;
|
||||
var rightIsClosing = true;
|
||||
|
||||
var LAST_FRAME = 15.0; // What is the number of the last frame we want to use in the animation?
|
||||
var SMOOTH_FACTOR = 0.75;
|
||||
var MAX_FRAMES = 30.0;
|
||||
|
||||
var leftHandOverlayAlpha = 0;
|
||||
var rightHandOverlayAlpha = 0;
|
||||
|
||||
var CONTROLLER_DEAD_SPOT = 0.25;
|
||||
var TRIGGER_SMOOTH_TIMESCALE = 0.1;
|
||||
var OVERLAY_RAMP_RATE = 8.0;
|
||||
|
||||
function clamp(val, min, max) {
|
||||
if (val < min) {
|
||||
return min;
|
||||
} else if (val > max) {
|
||||
return max;
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
return Math.min(Math.max(val, min), max);
|
||||
}
|
||||
|
||||
function normalizeControllerValue(val) {
|
||||
return clamp((val - CONTROLLER_DEAD_SPOT) / (1.0 - CONTROLLER_DEAD_SPOT), 0.0, 1.0);
|
||||
return clamp((val - CONTROLLER_DEAD_SPOT) / (1 - CONTROLLER_DEAD_SPOT), 0, 1);
|
||||
}
|
||||
|
||||
Script.update.connect(function(deltaTime) {
|
||||
function lerp(a, b, alpha) {
|
||||
return a * (1 - alpha) + b * alpha;
|
||||
}
|
||||
|
||||
function init() {
|
||||
Script.update.connect(update);
|
||||
MyAvatar.addAnimationStateHandler(animStateHandler, ["leftHandOverlayAlpha", "rightHandOverlayAlpha",
|
||||
"leftHandGraspAlpha", "rightHandGraspAlpha"]);
|
||||
}
|
||||
|
||||
function animStateHandler(props) {
|
||||
return { leftHandOverlayAlpha: leftHandOverlayAlpha,
|
||||
leftHandGraspAlpha: lastLeftTrigger,
|
||||
rightHandOverlayAlpha: rightHandOverlayAlpha,
|
||||
rightHandGraspAlpha: lastRightTrigger };
|
||||
}
|
||||
|
||||
function update(dt) {
|
||||
var leftTrigger = normalizeControllerValue(Controller.getValue(Controller.Standard.LT));
|
||||
var rightTrigger = normalizeControllerValue(Controller.getValue(Controller.Standard.RT));
|
||||
|
||||
// Average last few trigger values together for a bit of smoothing
|
||||
var smoothLeftTrigger = leftTrigger * (1.0 - SMOOTH_FACTOR) + lastLeftTrigger * SMOOTH_FACTOR;
|
||||
var smoothRightTrigger = rightTrigger * (1.0 - SMOOTH_FACTOR) + lastRightTrigger * SMOOTH_FACTOR;
|
||||
var tau = clamp(dt / TRIGGER_SMOOTH_TIMESCALE, 0, 1);
|
||||
lastLeftTrigger = lerp(leftTrigger, lastLeftTrigger, tau);
|
||||
lastRightTrigger = lerp(rightTrigger, lastRightTrigger, tau);
|
||||
|
||||
if (leftTrigger == 0.0) {
|
||||
leftIsClosing = true;
|
||||
} else if (leftTrigger == 1.0) {
|
||||
leftIsClosing = false;
|
||||
// ramp on/off left hand overlay
|
||||
var leftHandPose = Controller.getPoseValue(Controller.Standard.LeftHand);
|
||||
if (leftHandPose.valid) {
|
||||
leftHandOverlayAlpha = clamp(leftHandOverlayAlpha + OVERLAY_RAMP_RATE * dt, 0, 1);
|
||||
} else {
|
||||
leftHandOverlayAlpha = clamp(leftHandOverlayAlpha - OVERLAY_RAMP_RATE * dt, 0, 1);
|
||||
}
|
||||
|
||||
if (rightTrigger == 0.0) {
|
||||
rightIsClosing = true;
|
||||
} else if (rightTrigger == 1.0) {
|
||||
rightIsClosing = false;
|
||||
// ramp on/off right hand overlay
|
||||
var rightHandPose = Controller.getPoseValue(Controller.Standard.RightHand);
|
||||
if (rightHandPose.valid) {
|
||||
rightHandOverlayAlpha = clamp(rightHandOverlayAlpha + OVERLAY_RAMP_RATE * dt, 0, 1);
|
||||
} else {
|
||||
rightHandOverlayAlpha = clamp(rightHandOverlayAlpha - OVERLAY_RAMP_RATE * dt, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
lastLeftTrigger = smoothLeftTrigger;
|
||||
lastRightTrigger = smoothRightTrigger;
|
||||
function shutdown() {
|
||||
Script.update.disconnect(update);
|
||||
MyAvatar.removeAnimationStateHandler(animStateHandler);
|
||||
}
|
||||
|
||||
// 0..15
|
||||
var leftFrame = smoothLeftTrigger * LAST_FRAME;
|
||||
var rightFrame = smoothRightTrigger * LAST_FRAME;
|
||||
Script.scriptEnding.connect(shutdown);
|
||||
|
||||
var adjustedLeftFrame = (leftIsClosing) ? leftFrame : (MAX_FRAMES - leftFrame);
|
||||
if (leftHandAnimation.length) {
|
||||
MyAvatar.startAnimation(leftHandAnimation, 30.0, 1.0, false, true, adjustedLeftFrame, adjustedLeftFrame);
|
||||
}
|
||||
|
||||
var adjustedRightFrame = (rightIsClosing) ? rightFrame : (MAX_FRAMES - rightFrame);
|
||||
if (rightHandAnimation.length) {
|
||||
MyAvatar.startAnimation(rightHandAnimation, 30.0, 1.0, false, true, adjustedRightFrame, adjustedRightFrame);
|
||||
}
|
||||
});
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
MyAvatar.stopAnimation(leftHandAnimation);
|
||||
MyAvatar.stopAnimation(rightHandAnimation);
|
||||
});
|
||||
init();
|
||||
|
|
|
@ -17,6 +17,7 @@ Script.load("inspect.js");
|
|||
Script.load("notifications.js");
|
||||
Script.load("users.js");
|
||||
Script.load("controllers/handControllerGrab.js");
|
||||
Script.load("controllers/squeezeHands.js");
|
||||
Script.load("grab.js");
|
||||
Script.load("directory.js");
|
||||
Script.load("dialTone.js");
|
||||
|
|
|
@ -33,11 +33,11 @@ var BUTTON_SIZE = 32;
|
|||
var PADDING = 3;
|
||||
|
||||
Script.include(["libraries/toolBars.js"]);
|
||||
var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.dice.toolbar", function (screenSize) {
|
||||
return {
|
||||
x: (screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING),
|
||||
y: (screenSize.y - (BUTTON_SIZE + PADDING))
|
||||
};
|
||||
var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.dice.toolbar", function(screenSize) {
|
||||
return {
|
||||
x: (screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING),
|
||||
y: (screenSize.y - (BUTTON_SIZE + PADDING))
|
||||
};
|
||||
});
|
||||
var offButton = toolBar.addOverlay("image", {
|
||||
width: BUTTON_SIZE,
|
||||
|
@ -65,12 +65,13 @@ var deleteButton = toolBar.addOverlay("image", {
|
|||
alpha: 1
|
||||
});
|
||||
|
||||
var diceIconURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/images/dice.png"
|
||||
var diceButton = toolBar.addOverlay("image", {
|
||||
x: screenSize.x / 2 + PADDING,
|
||||
y: screenSize.y - (BUTTON_SIZE + PADDING),
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/die.png",
|
||||
imageURL: diceIconURL,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
|
@ -79,6 +80,7 @@ var diceButton = toolBar.addOverlay("image", {
|
|||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
var GRAVITY = -3.5;
|
||||
|
||||
// NOTE: angularVelocity is in radians/sec
|
||||
|
@ -90,28 +92,27 @@ function shootDice(position, velocity) {
|
|||
Window.alert(INSUFFICIENT_PERMISSIONS_ERROR_MSG);
|
||||
} else {
|
||||
for (var i = 0; i < NUMBER_OF_DICE; i++) {
|
||||
dice.push(Entities.addEntity(
|
||||
{
|
||||
type: "Model",
|
||||
modelURL: HIFI_PUBLIC_BUCKET + "models/props/Dice/goldDie.fbx",
|
||||
position: position,
|
||||
velocity: velocity,
|
||||
rotation: Quat.fromPitchYawRollDegrees(Math.random() * 360, Math.random() * 360, Math.random() * 360),
|
||||
angularVelocity: {
|
||||
x: Math.random() * MAX_ANGULAR_SPEED,
|
||||
y: Math.random() * MAX_ANGULAR_SPEED,
|
||||
z: Math.random() * MAX_ANGULAR_SPEED
|
||||
},
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: GRAVITY,
|
||||
z: 0
|
||||
},
|
||||
lifetime: LIFETIME,
|
||||
shapeType: "box",
|
||||
collisionsWillMove: true,
|
||||
collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav"
|
||||
}));
|
||||
dice.push(Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: HIFI_PUBLIC_BUCKET + "models/props/Dice/goldDie.fbx",
|
||||
position: position,
|
||||
velocity: velocity,
|
||||
rotation: Quat.fromPitchYawRollDegrees(Math.random() * 360, Math.random() * 360, Math.random() * 360),
|
||||
angularVelocity: {
|
||||
x: Math.random() * MAX_ANGULAR_SPEED,
|
||||
y: Math.random() * MAX_ANGULAR_SPEED,
|
||||
z: Math.random() * MAX_ANGULAR_SPEED
|
||||
},
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: GRAVITY,
|
||||
z: 0
|
||||
},
|
||||
lifetime: LIFETIME,
|
||||
shapeType: "box",
|
||||
collisionsWillMove: true,
|
||||
collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav"
|
||||
}));
|
||||
position = Vec3.sum(position, Vec3.multiply(DIE_SIZE, Vec3.normalize(Quat.getRight(Camera.getOrientation()))));
|
||||
}
|
||||
}
|
||||
|
@ -150,4 +151,4 @@ function scriptEnding() {
|
|||
}
|
||||
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
|
@ -1502,7 +1502,11 @@ PropertiesTool = function(opts) {
|
|||
var that = {};
|
||||
|
||||
var url = Script.resolvePath('html/entityProperties.html');
|
||||
var webView = new WebWindow('Entity Properties', url, 200, 280, true);
|
||||
var webView = new OverlayWebWindow({
|
||||
title: 'Entity Properties',
|
||||
source: url,
|
||||
toolWindow: true
|
||||
});
|
||||
|
||||
var visible = false;
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ SettingsWindow = function() {
|
|||
this.plankyStack = null;
|
||||
this.webWindow = null;
|
||||
this.init = function(plankyStack) {
|
||||
_this.webWindow = new WebWindow('Planky', Script.resolvePath('../../html/plankySettings.html'), 255, 500, true);
|
||||
_this.webWindow = new OverlayWebWindow('Planky', Script.resolvePath('../../html/plankySettings.html'), 255, 500, true);
|
||||
_this.webWindow.setVisible(false);
|
||||
_this.webWindow.eventBridge.webEventReceived.connect(_this.onWebEventReceived);
|
||||
_this.plankyStack = plankyStack;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<script src="list.min.js"></script>
|
||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||
<script type="text/javascript" src="eventBridgeLoader.js"></script>
|
||||
<script>
|
||||
var entities = {};
|
||||
var selectedEntities = [];
|
||||
|
@ -15,217 +17,219 @@
|
|||
const MAX_ITEMS = Number.MAX_VALUE; // Used to set the max length of the list of discovered entities.
|
||||
|
||||
function loaded() {
|
||||
entityList = new List('entity-list', { valueNames: ['name', 'type', 'url'], page: MAX_ITEMS});
|
||||
entityList.clear();
|
||||
elEntityTable = document.getElementById("entity-table");
|
||||
elEntityTableBody = document.getElementById("entity-table-body");
|
||||
elRefresh = document.getElementById("refresh");
|
||||
elDelete = document.getElementById("delete");
|
||||
elTeleport = document.getElementById("teleport");
|
||||
elRadius = document.getElementById("radius");
|
||||
elNoEntitiesMessage = document.getElementById("no-entities");
|
||||
elNoEntitiesRadius = document.getElementById("no-entities-radius");
|
||||
|
||||
document.getElementById("entity-name").onclick = function() {
|
||||
setSortColumn('name');
|
||||
};
|
||||
document.getElementById("entity-type").onclick = function() {
|
||||
setSortColumn('type');
|
||||
};
|
||||
document.getElementById("entity-url").onclick = function() {
|
||||
setSortColumn('url');
|
||||
};
|
||||
|
||||
function onRowClicked(clickEvent) {
|
||||
var id = this.dataset.entityId;
|
||||
var selection = [this.dataset.entityId];
|
||||
if (clickEvent.ctrlKey) {
|
||||
selection = selection.concat(selectedEntities);
|
||||
} else if (clickEvent.shiftKey && selectedEntities.length > 0) {
|
||||
var previousItemFound = -1;
|
||||
var clickedItemFound = -1;
|
||||
for (var entity in entityList.visibleItems) {
|
||||
if (clickedItemFound === -1 && this.dataset.entityId == entityList.visibleItems[entity].values().id) {
|
||||
clickedItemFound = entity;
|
||||
} else if(previousItemFound === -1 && selectedEntities[0] == entityList.visibleItems[entity].values().id) {
|
||||
previousItemFound = entity;
|
||||
}
|
||||
}
|
||||
if (previousItemFound !== -1 && clickedItemFound !== -1) {
|
||||
var betweenItems = [];
|
||||
var toItem = Math.max(previousItemFound, clickedItemFound);
|
||||
// skip first and last item in this loop, we add them to selection after the loop
|
||||
for (var i = (Math.min(previousItemFound, clickedItemFound) + 1); i < toItem; i++) {
|
||||
entityList.visibleItems[i].elm.className = 'selected';
|
||||
betweenItems.push(entityList.visibleItems[i].values().id);
|
||||
}
|
||||
if (previousItemFound > clickedItemFound) {
|
||||
// always make sure that we add the items in the right order
|
||||
betweenItems.reverse();
|
||||
}
|
||||
selection = selection.concat(betweenItems, selectedEntities);
|
||||
}
|
||||
}
|
||||
|
||||
selectedEntities = selection;
|
||||
|
||||
this.className = 'selected';
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "selectionUpdate",
|
||||
focus: false,
|
||||
entityIds: selection,
|
||||
}));
|
||||
}
|
||||
|
||||
function onRowDoubleClicked() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "selectionUpdate",
|
||||
focus: true,
|
||||
entityIds: [this.dataset.entityId],
|
||||
}));
|
||||
}
|
||||
|
||||
function addEntity(id, name, type, url) {
|
||||
if (entities[id] === undefined) {
|
||||
var urlParts = url.split('/');
|
||||
var filename = urlParts[urlParts.length - 1];
|
||||
|
||||
entityList.add([{ id: id, name: name, type: type, url: filename }], function(items) {
|
||||
var currentElement = items[0].elm;
|
||||
var id = items[0]._values.id;
|
||||
entities[id] = {
|
||||
id: id,
|
||||
name: name,
|
||||
el: currentElement,
|
||||
item: items[0],
|
||||
};
|
||||
currentElement.setAttribute('id', 'entity_' + id);
|
||||
currentElement.setAttribute('title', url);
|
||||
currentElement.dataset.entityId = id;
|
||||
currentElement.onclick = onRowClicked;
|
||||
currentElement.ondblclick = onRowDoubleClicked;
|
||||
});
|
||||
|
||||
if (refreshEntityListTimer) {
|
||||
clearTimeout(refreshEntityListTimer);
|
||||
}
|
||||
refreshEntityListTimer = setTimeout(refreshEntityListObject, 50);
|
||||
} else {
|
||||
var item = entities[id].item;
|
||||
item.values({ name: name, url: url });
|
||||
}
|
||||
}
|
||||
|
||||
function clearEntities() {
|
||||
entities = {};
|
||||
entityList.clear();
|
||||
}
|
||||
|
||||
var elSortOrder = {
|
||||
name: document.querySelector('#entity-name .sort-order'),
|
||||
type: document.querySelector('#entity-type .sort-order'),
|
||||
url: document.querySelector('#entity-url .sort-order'),
|
||||
}
|
||||
function setSortColumn(column) {
|
||||
if (currentSortColumn == column) {
|
||||
currentSortOrder = currentSortOrder == "asc" ? "desc" : "asc";
|
||||
} else {
|
||||
elSortOrder[currentSortColumn].style.display = 'none';
|
||||
elSortOrder[column].style.display = 'inline';
|
||||
currentSortColumn = column;
|
||||
currentSortOrder = "asc";
|
||||
}
|
||||
elSortOrder[column].innerHTML = currentSortOrder == "asc" ? ASCENDING_STRING : DESCENDING_STRING;
|
||||
entityList.sort(currentSortColumn, { order: currentSortOrder });
|
||||
}
|
||||
|
||||
function refreshEntities() {
|
||||
clearEntities();
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'refresh' }));
|
||||
}
|
||||
|
||||
function refreshEntityListObject() {
|
||||
refreshEntityListTimer = null;
|
||||
entityList.sort(currentSortColumn, { order: currentSortOrder });
|
||||
entityList.search(document.getElementById("filter").value);
|
||||
}
|
||||
|
||||
function updateSelectedEntities(selectedEntities) {
|
||||
var notFound = false;
|
||||
for (var id in entities) {
|
||||
entities[id].el.className = '';
|
||||
}
|
||||
for (var i = 0; i < selectedEntities.length; i++) {
|
||||
var id = selectedEntities[i];
|
||||
if (id in entities) {
|
||||
var entity = entities[id];
|
||||
entity.el.className = 'selected';
|
||||
} else {
|
||||
notFound = true;
|
||||
}
|
||||
}
|
||||
return notFound;
|
||||
}
|
||||
|
||||
elRefresh.onclick = function() {
|
||||
refreshEntities();
|
||||
}
|
||||
elTeleport.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'teleport' }));
|
||||
}
|
||||
elDelete.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
|
||||
refreshEntities();
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", function (keyDownEvent) {
|
||||
if (keyDownEvent.target.nodeName === "INPUT") {
|
||||
return;
|
||||
}
|
||||
var keyCode = keyDownEvent.keyCode;
|
||||
if (keyCode === DELETE) {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
|
||||
refreshEntities();
|
||||
}
|
||||
}, false);
|
||||
|
||||
elRadius.onchange = function () {
|
||||
elRadius.value = Math.max(elRadius.value, 0);
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elRadius.value }));
|
||||
refreshEntities();
|
||||
elNoEntitiesRadius.firstChild.nodeValue = elRadius.value;
|
||||
}
|
||||
|
||||
if (window.EventBridge !== undefined) {
|
||||
EventBridge.scriptEventReceived.connect(function(data) {
|
||||
data = JSON.parse(data);
|
||||
|
||||
if (data.type === "clearEntityList") {
|
||||
clearEntities();
|
||||
} else if (data.type == "selectionUpdate") {
|
||||
var notFound = updateSelectedEntities(data.selectedIDs);
|
||||
if (notFound) {
|
||||
refreshEntities();
|
||||
}
|
||||
} else if (data.type == "update") {
|
||||
var newEntities = data.entities;
|
||||
if (newEntities.length == 0) {
|
||||
elEntityTable.style.display = "none";
|
||||
elNoEntitiesMessage.style.display = "block";
|
||||
} else {
|
||||
elEntityTable.style.display = "table";
|
||||
elNoEntitiesMessage.style.display = "none";
|
||||
for (var i = 0; i < newEntities.length; i++) {
|
||||
var id = newEntities[i].id;
|
||||
addEntity(id, newEntities[i].name, newEntities[i].type, newEntities[i].url);
|
||||
}
|
||||
updateSelectedEntities(data.selectedIDs);
|
||||
}
|
||||
}
|
||||
});
|
||||
setTimeout(refreshEntities, 1000);
|
||||
}
|
||||
openEventBridge(function() {
|
||||
entityList = new List('entity-list', { valueNames: ['name', 'type', 'url'], page: MAX_ITEMS});
|
||||
entityList.clear();
|
||||
elEntityTable = document.getElementById("entity-table");
|
||||
elEntityTableBody = document.getElementById("entity-table-body");
|
||||
elRefresh = document.getElementById("refresh");
|
||||
elDelete = document.getElementById("delete");
|
||||
elTeleport = document.getElementById("teleport");
|
||||
elRadius = document.getElementById("radius");
|
||||
elNoEntitiesMessage = document.getElementById("no-entities");
|
||||
elNoEntitiesRadius = document.getElementById("no-entities-radius");
|
||||
|
||||
document.getElementById("entity-name").onclick = function() {
|
||||
setSortColumn('name');
|
||||
};
|
||||
document.getElementById("entity-type").onclick = function() {
|
||||
setSortColumn('type');
|
||||
};
|
||||
document.getElementById("entity-url").onclick = function() {
|
||||
setSortColumn('url');
|
||||
};
|
||||
|
||||
function onRowClicked(clickEvent) {
|
||||
var id = this.dataset.entityId;
|
||||
var selection = [this.dataset.entityId];
|
||||
if (clickEvent.ctrlKey) {
|
||||
selection = selection.concat(selectedEntities);
|
||||
} else if (clickEvent.shiftKey && selectedEntities.length > 0) {
|
||||
var previousItemFound = -1;
|
||||
var clickedItemFound = -1;
|
||||
for (var entity in entityList.visibleItems) {
|
||||
if (clickedItemFound === -1 && this.dataset.entityId == entityList.visibleItems[entity].values().id) {
|
||||
clickedItemFound = entity;
|
||||
} else if(previousItemFound === -1 && selectedEntities[0] == entityList.visibleItems[entity].values().id) {
|
||||
previousItemFound = entity;
|
||||
}
|
||||
}
|
||||
if (previousItemFound !== -1 && clickedItemFound !== -1) {
|
||||
var betweenItems = [];
|
||||
var toItem = Math.max(previousItemFound, clickedItemFound);
|
||||
// skip first and last item in this loop, we add them to selection after the loop
|
||||
for (var i = (Math.min(previousItemFound, clickedItemFound) + 1); i < toItem; i++) {
|
||||
entityList.visibleItems[i].elm.className = 'selected';
|
||||
betweenItems.push(entityList.visibleItems[i].values().id);
|
||||
}
|
||||
if (previousItemFound > clickedItemFound) {
|
||||
// always make sure that we add the items in the right order
|
||||
betweenItems.reverse();
|
||||
}
|
||||
selection = selection.concat(betweenItems, selectedEntities);
|
||||
}
|
||||
}
|
||||
|
||||
selectedEntities = selection;
|
||||
|
||||
this.className = 'selected';
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "selectionUpdate",
|
||||
focus: false,
|
||||
entityIds: selection,
|
||||
}));
|
||||
}
|
||||
|
||||
function onRowDoubleClicked() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "selectionUpdate",
|
||||
focus: true,
|
||||
entityIds: [this.dataset.entityId],
|
||||
}));
|
||||
}
|
||||
|
||||
function addEntity(id, name, type, url) {
|
||||
if (entities[id] === undefined) {
|
||||
var urlParts = url.split('/');
|
||||
var filename = urlParts[urlParts.length - 1];
|
||||
|
||||
entityList.add([{ id: id, name: name, type: type, url: filename }], function(items) {
|
||||
var currentElement = items[0].elm;
|
||||
var id = items[0]._values.id;
|
||||
entities[id] = {
|
||||
id: id,
|
||||
name: name,
|
||||
el: currentElement,
|
||||
item: items[0],
|
||||
};
|
||||
currentElement.setAttribute('id', 'entity_' + id);
|
||||
currentElement.setAttribute('title', url);
|
||||
currentElement.dataset.entityId = id;
|
||||
currentElement.onclick = onRowClicked;
|
||||
currentElement.ondblclick = onRowDoubleClicked;
|
||||
});
|
||||
|
||||
if (refreshEntityListTimer) {
|
||||
clearTimeout(refreshEntityListTimer);
|
||||
}
|
||||
refreshEntityListTimer = setTimeout(refreshEntityListObject, 50);
|
||||
} else {
|
||||
var item = entities[id].item;
|
||||
item.values({ name: name, url: url });
|
||||
}
|
||||
}
|
||||
|
||||
function clearEntities() {
|
||||
entities = {};
|
||||
entityList.clear();
|
||||
}
|
||||
|
||||
var elSortOrder = {
|
||||
name: document.querySelector('#entity-name .sort-order'),
|
||||
type: document.querySelector('#entity-type .sort-order'),
|
||||
url: document.querySelector('#entity-url .sort-order'),
|
||||
}
|
||||
function setSortColumn(column) {
|
||||
if (currentSortColumn == column) {
|
||||
currentSortOrder = currentSortOrder == "asc" ? "desc" : "asc";
|
||||
} else {
|
||||
elSortOrder[currentSortColumn].style.display = 'none';
|
||||
elSortOrder[column].style.display = 'inline';
|
||||
currentSortColumn = column;
|
||||
currentSortOrder = "asc";
|
||||
}
|
||||
elSortOrder[column].innerHTML = currentSortOrder == "asc" ? ASCENDING_STRING : DESCENDING_STRING;
|
||||
entityList.sort(currentSortColumn, { order: currentSortOrder });
|
||||
}
|
||||
|
||||
function refreshEntities() {
|
||||
clearEntities();
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'refresh' }));
|
||||
}
|
||||
|
||||
function refreshEntityListObject() {
|
||||
refreshEntityListTimer = null;
|
||||
entityList.sort(currentSortColumn, { order: currentSortOrder });
|
||||
entityList.search(document.getElementById("filter").value);
|
||||
}
|
||||
|
||||
function updateSelectedEntities(selectedEntities) {
|
||||
var notFound = false;
|
||||
for (var id in entities) {
|
||||
entities[id].el.className = '';
|
||||
}
|
||||
for (var i = 0; i < selectedEntities.length; i++) {
|
||||
var id = selectedEntities[i];
|
||||
if (id in entities) {
|
||||
var entity = entities[id];
|
||||
entity.el.className = 'selected';
|
||||
} else {
|
||||
notFound = true;
|
||||
}
|
||||
}
|
||||
return notFound;
|
||||
}
|
||||
|
||||
elRefresh.onclick = function() {
|
||||
refreshEntities();
|
||||
}
|
||||
elTeleport.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'teleport' }));
|
||||
}
|
||||
elDelete.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
|
||||
refreshEntities();
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", function (keyDownEvent) {
|
||||
if (keyDownEvent.target.nodeName === "INPUT") {
|
||||
return;
|
||||
}
|
||||
var keyCode = keyDownEvent.keyCode;
|
||||
if (keyCode === DELETE) {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
|
||||
refreshEntities();
|
||||
}
|
||||
}, false);
|
||||
|
||||
elRadius.onchange = function () {
|
||||
elRadius.value = Math.max(elRadius.value, 0);
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elRadius.value }));
|
||||
refreshEntities();
|
||||
elNoEntitiesRadius.firstChild.nodeValue = elRadius.value;
|
||||
}
|
||||
|
||||
if (window.EventBridge !== undefined) {
|
||||
EventBridge.scriptEventReceived.connect(function(data) {
|
||||
data = JSON.parse(data);
|
||||
|
||||
if (data.type === "clearEntityList") {
|
||||
clearEntities();
|
||||
} else if (data.type == "selectionUpdate") {
|
||||
var notFound = updateSelectedEntities(data.selectedIDs);
|
||||
if (notFound) {
|
||||
refreshEntities();
|
||||
}
|
||||
} else if (data.type == "update") {
|
||||
var newEntities = data.entities;
|
||||
if (newEntities.length == 0) {
|
||||
elEntityTable.style.display = "none";
|
||||
elNoEntitiesMessage.style.display = "block";
|
||||
} else {
|
||||
elEntityTable.style.display = "table";
|
||||
elNoEntitiesMessage.style.display = "none";
|
||||
for (var i = 0; i < newEntities.length; i++) {
|
||||
var id = newEntities[i].id;
|
||||
addEntity(id, newEntities[i].name, newEntities[i].type, newEntities[i].url);
|
||||
}
|
||||
updateSelectedEntities(data.selectedIDs);
|
||||
}
|
||||
}
|
||||
});
|
||||
setTimeout(refreshEntities, 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,6 +8,8 @@
|
|||
// void scriptEventReceived(const QString& data);
|
||||
//
|
||||
|
||||
var EventBridge;
|
||||
|
||||
EventBridgeConnectionProxy = function(parent) {
|
||||
this.parent = parent;
|
||||
this.realSignal = this.parent.realBridge.scriptEventReceived
|
||||
|
@ -46,12 +48,10 @@ openEventBridge = function(callback) {
|
|||
socket.onopen = function() {
|
||||
channel = new QWebChannel(socket, function(channel) {
|
||||
console.log("Document url is " + document.URL);
|
||||
for(var key in channel.objects){
|
||||
console.log("registered object: " + key);
|
||||
}
|
||||
var webWindow = channel.objects[document.URL.toLowerCase()];
|
||||
console.log("WebWindow is " + webWindow)
|
||||
eventBridgeProxy = new EventBridgeProxy(webWindow);
|
||||
EventBridge = eventBridgeProxy;
|
||||
if (callback) { callback(eventBridgeProxy); }
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,110 +1,114 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||
<script type="text/javascript" src="eventBridgeLoader.js"></script>
|
||||
<script>
|
||||
function loaded() {
|
||||
var gridColor = { red: 0, green: 0, blue: 0 };
|
||||
var gridColors = [
|
||||
{ red: 0, green: 0, blue: 0 },
|
||||
{ red: 255, green: 255, blue: 255 },
|
||||
{ red: 255, green: 0, blue: 0 },
|
||||
{ red: 0, green: 255, blue: 0},
|
||||
{ red: 0, green: 0, blue: 255 },
|
||||
];
|
||||
var gridColorIndex = 0;
|
||||
|
||||
elPosY = document.getElementById("horiz-y");
|
||||
elMinorSpacing = document.getElementById("minor-spacing");
|
||||
elMajorSpacing = document.getElementById("major-spacing");
|
||||
elSnapToGrid = document.getElementById("snap-to-grid");
|
||||
elHorizontalGridVisible = document.getElementById("horiz-grid-visible");
|
||||
elMoveToSelection = document.getElementById("move-to-selection");
|
||||
elMoveToAvatar = document.getElementById("move-to-avatar");
|
||||
|
||||
if (window.EventBridge !== undefined) {
|
||||
EventBridge.scriptEventReceived.connect(function(data) {
|
||||
data = JSON.parse(data);
|
||||
|
||||
if (data.origin) {
|
||||
var origin = data.origin;
|
||||
elPosY.value = origin.y.toFixed(2);
|
||||
openEventBridge(function() {
|
||||
var gridColor = { red: 0, green: 0, blue: 0 };
|
||||
var gridColors = [
|
||||
{ red: 0, green: 0, blue: 0 },
|
||||
{ red: 255, green: 255, blue: 255 },
|
||||
{ red: 255, green: 0, blue: 0 },
|
||||
{ red: 0, green: 255, blue: 0},
|
||||
{ red: 0, green: 0, blue: 255 },
|
||||
];
|
||||
var gridColorIndex = 0;
|
||||
|
||||
elPosY = document.getElementById("horiz-y");
|
||||
elMinorSpacing = document.getElementById("minor-spacing");
|
||||
elMajorSpacing = document.getElementById("major-spacing");
|
||||
elSnapToGrid = document.getElementById("snap-to-grid");
|
||||
elHorizontalGridVisible = document.getElementById("horiz-grid-visible");
|
||||
elMoveToSelection = document.getElementById("move-to-selection");
|
||||
elMoveToAvatar = document.getElementById("move-to-avatar");
|
||||
|
||||
if (window.EventBridge !== undefined) {
|
||||
EventBridge.scriptEventReceived.connect(function(data) {
|
||||
data = JSON.parse(data);
|
||||
|
||||
if (data.origin) {
|
||||
var origin = data.origin;
|
||||
elPosY.value = origin.y.toFixed(2);
|
||||
}
|
||||
|
||||
if (data.minorGridWidth !== undefined) {
|
||||
elMinorSpacing.value = data.minorGridWidth;
|
||||
}
|
||||
|
||||
if (data.majorGridEvery !== undefined) {
|
||||
elMajorSpacing.value = data.majorGridEvery;
|
||||
}
|
||||
|
||||
if (data.gridColor) {
|
||||
gridColor = data.gridColor;
|
||||
}
|
||||
|
||||
if (data.snapToGrid !== undefined) {
|
||||
elSnapToGrid.checked = data.snapToGrid == true;
|
||||
}
|
||||
|
||||
if (data.visible !== undefined) {
|
||||
elHorizontalGridVisible.checked = data.visible == true;
|
||||
}
|
||||
});
|
||||
|
||||
function emitUpdate() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "update",
|
||||
origin: {
|
||||
y: elPosY.value,
|
||||
},
|
||||
minorGridWidth: elMinorSpacing.value,
|
||||
majorGridEvery: elMajorSpacing.value,
|
||||
gridColor: gridColor,
|
||||
colorIndex: gridColorIndex,
|
||||
snapToGrid: elSnapToGrid.checked,
|
||||
visible: elHorizontalGridVisible.checked,
|
||||
}));
|
||||
}
|
||||
|
||||
if (data.minorGridWidth !== undefined) {
|
||||
elMinorSpacing.value = data.minorGridWidth;
|
||||
}
|
||||
|
||||
if (data.majorGridEvery !== undefined) {
|
||||
elMajorSpacing.value = data.majorGridEvery;
|
||||
}
|
||||
|
||||
if (data.gridColor) {
|
||||
gridColor = data.gridColor;
|
||||
}
|
||||
|
||||
if (data.snapToGrid !== undefined) {
|
||||
elSnapToGrid.checked = data.snapToGrid == true;
|
||||
}
|
||||
|
||||
if (data.visible !== undefined) {
|
||||
elHorizontalGridVisible.checked = data.visible == true;
|
||||
}
|
||||
});
|
||||
|
||||
function emitUpdate() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "update",
|
||||
origin: {
|
||||
y: elPosY.value,
|
||||
},
|
||||
minorGridWidth: elMinorSpacing.value,
|
||||
majorGridEvery: elMajorSpacing.value,
|
||||
gridColor: gridColor,
|
||||
colorIndex: gridColorIndex,
|
||||
snapToGrid: elSnapToGrid.checked,
|
||||
visible: elHorizontalGridVisible.checked,
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
elPosY.addEventListener("change", emitUpdate);
|
||||
elMinorSpacing.addEventListener("change", emitUpdate);
|
||||
elMajorSpacing.addEventListener("change", emitUpdate);
|
||||
elSnapToGrid.addEventListener("change", emitUpdate);
|
||||
elHorizontalGridVisible.addEventListener("change", emitUpdate);
|
||||
|
||||
elMoveToAvatar.addEventListener("click", function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "action",
|
||||
action: "moveToAvatar",
|
||||
}));
|
||||
|
||||
elPosY.addEventListener("change", emitUpdate);
|
||||
elMinorSpacing.addEventListener("change", emitUpdate);
|
||||
elMajorSpacing.addEventListener("change", emitUpdate);
|
||||
elSnapToGrid.addEventListener("change", emitUpdate);
|
||||
elHorizontalGridVisible.addEventListener("change", emitUpdate);
|
||||
|
||||
elMoveToAvatar.addEventListener("click", function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "action",
|
||||
action: "moveToAvatar",
|
||||
}));
|
||||
});
|
||||
elMoveToSelection.addEventListener("click", function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "action",
|
||||
action: "moveToSelection",
|
||||
}));
|
||||
});
|
||||
|
||||
var gridColorBox = document.getElementById('grid-color');
|
||||
|
||||
for (var i = 0; i < gridColors.length; i++) {
|
||||
var colors = gridColors[i];
|
||||
var box = document.createElement('div');
|
||||
box.setAttribute('class', 'color-box');
|
||||
box.style.background = 'rgb(' + colors.red + ', ' + colors.green + ', ' + colors.blue + ')';
|
||||
document.getElementById("grid-colors").appendChild(box);
|
||||
box.addEventListener("click", function(color, index) {
|
||||
return function() {
|
||||
gridColor = color;
|
||||
gridColorIndex = index;
|
||||
emitUpdate();
|
||||
}
|
||||
}({ red: colors.red, green: colors.green, blue: colors.blue }, i));
|
||||
}
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'init' }));
|
||||
});
|
||||
elMoveToSelection.addEventListener("click", function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "action",
|
||||
action: "moveToSelection",
|
||||
}));
|
||||
});
|
||||
|
||||
var gridColorBox = document.getElementById('grid-color');
|
||||
|
||||
for (var i = 0; i < gridColors.length; i++) {
|
||||
var colors = gridColors[i];
|
||||
var box = document.createElement('div');
|
||||
box.setAttribute('class', 'color-box');
|
||||
box.style.background = 'rgb(' + colors.red + ', ' + colors.green + ', ' + colors.blue + ')';
|
||||
document.getElementById("grid-colors").appendChild(box);
|
||||
box.addEventListener("click", function(color, index) {
|
||||
return function() {
|
||||
gridColor = color;
|
||||
gridColorIndex = index;
|
||||
emitUpdate();
|
||||
}
|
||||
}({ red: colors.red, green: colors.green, blue: colors.blue }, i));
|
||||
}
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'init' }));
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
|
|
@ -4,7 +4,9 @@ EntityListTool = function(opts) {
|
|||
var that = {};
|
||||
|
||||
var url = ENTITY_LIST_HTML_URL;
|
||||
var webView = new WebWindow('Entities', url, 200, 280, true);
|
||||
var webView = new OverlayWebWindow({
|
||||
title: 'Entities', source: url, toolWindow: true
|
||||
});
|
||||
|
||||
var searchRadius = 100;
|
||||
|
||||
|
|
|
@ -231,7 +231,9 @@ GridTool = function(opts) {
|
|||
var listeners = [];
|
||||
|
||||
var url = GRID_CONTROLS_HTML_URL;
|
||||
var webView = new WebWindow('Grid', url, 200, 280, true);
|
||||
var webView = new OverlayWebWindow({
|
||||
title: 'Grid', source: url, toolWindow: true
|
||||
});
|
||||
|
||||
horizontalGrid.addListener(function(data) {
|
||||
webView.eventBridge.emitScriptEvent(JSON.stringify(data));
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
"id": "rightHandOverlay",
|
||||
"type": "overlay",
|
||||
"data": {
|
||||
"alpha": 1.0,
|
||||
"alpha": 0.0,
|
||||
"boneSet": "rightHand",
|
||||
"alphaVar": "rightHandOverlayAlpha"
|
||||
},
|
||||
|
@ -122,125 +122,50 @@
|
|||
"id": "rightHandStateMachine",
|
||||
"type": "stateMachine",
|
||||
"data": {
|
||||
"currentState": "rightHandIdle",
|
||||
"currentState": "rightHandGrasp",
|
||||
"states": [
|
||||
{
|
||||
"id": "rightHandIdle",
|
||||
"interpTarget": 3,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "rightHandOpen", "state": "rightHandOpen" },
|
||||
{ "var": "rightHandGrasp", "state": "rightHandGrasp" },
|
||||
{ "var": "rightHandPoint", "state": "rightHandPoint" },
|
||||
{ "var": "rightHandFarGrasp", "state": "rightHandFarGrasp" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rightHandOpen",
|
||||
"interpTarget": 3,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "rightHandIdle", "state": "rightHandIdle" },
|
||||
{ "var": "rightHandGrasp", "state": "rightHandGrasp" },
|
||||
{ "var": "rightHandPoint", "state": "rightHandPoint" },
|
||||
{ "var": "rightHandFarGrasp", "state": "rightHandFarGrasp" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rightHandGrasp",
|
||||
"interpTarget": 3,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "rightHandOpen", "state": "rightHandOpen" },
|
||||
{ "var": "rightHandIdle", "state": "rightHandIdle" },
|
||||
{ "var": "rightHandPoint", "state": "rightHandPoint" },
|
||||
{ "var": "rightHandFarGrasp", "state": "rightHandFarGrasp" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rightHandPoint",
|
||||
"interpTarget": 3,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "rightHandOpen", "state": "rightHandOpen" },
|
||||
{ "var": "rightHandIdle", "state": "rightHandIdle" },
|
||||
{ "var": "rightHandGrasp", "state": "rightHandGrasp" },
|
||||
{ "var": "rightHandFarGrasp", "state": "rightHandFarGrasp" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rightHandFarGrasp",
|
||||
"interpTarget": 3,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "rightHandOpen", "state": "rightHandOpen" },
|
||||
{ "var": "rightHandIdle", "state": "rightHandIdle" },
|
||||
{ "var": "rightHandGrasp", "state": "rightHandGrasp" },
|
||||
{ "var": "rightHandPoint", "state": "rightHandPoint" }
|
||||
]
|
||||
"transitions": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "rightHandIdle",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_right_hand.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "rightHandOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/search_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "rightHandGrasp",
|
||||
"type": "clip",
|
||||
"type": "blendLinear",
|
||||
"data": {
|
||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/grasp_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
"alpha": 0.0,
|
||||
"alphaVar": "rightHandGraspAlpha"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "rightHandPoint",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_right_hand.fbx",
|
||||
"startFrame": 20.0,
|
||||
"endFrame": 20.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "rightHandFarGrasp",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/far_grasp_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
"children": [
|
||||
{
|
||||
"id": "rightHandGraspOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/hydra_pose_open_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "rightHandGraspClosed",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/hydra_pose_closed_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -248,7 +173,7 @@
|
|||
"id": "leftHandOverlay",
|
||||
"type": "overlay",
|
||||
"data": {
|
||||
"alpha": 1.0,
|
||||
"alpha": 0.0,
|
||||
"boneSet": "leftHand",
|
||||
"alphaVar" : "leftHandOverlayAlpha"
|
||||
},
|
||||
|
@ -257,127 +182,50 @@
|
|||
"id": "leftHandStateMachine",
|
||||
"type": "stateMachine",
|
||||
"data": {
|
||||
"currentState": "leftHandIdle",
|
||||
"currentState": "leftHandGrasp",
|
||||
"states": [
|
||||
{
|
||||
"id": "leftHandIdle",
|
||||
"interpTarget": 3,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "leftHandOpen", "state": "leftHandOpen" },
|
||||
{ "var": "leftHandGrasp", "state": "leftHandGrasp" },
|
||||
{ "var": "leftHandPoint", "state": "leftHandPoint" },
|
||||
{ "var": "leftHandFarGrasp", "state": "leftHandFarGrasp" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftHandOpen",
|
||||
"interpTarget": 3,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "leftHandIdle", "state": "leftHandIdle" },
|
||||
{ "var": "leftHandGrasp", "state": "leftHandGrasp" },
|
||||
{ "var": "leftHandPoint", "state": "leftHandPoint" },
|
||||
{ "var": "leftHandFarGrasp", "state": "leftHandFarGrasp" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftHandGrasp",
|
||||
"interpTarget": 3,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "leftHandOpen", "state": "leftHandOpen" },
|
||||
{ "var": "leftHandIdle", "state": "leftHandIdle" },
|
||||
{ "var": "leftHandPoint", "state": "leftHandPoint" },
|
||||
{ "var": "leftHandFarGrasp", "state": "leftHandFarGrasp" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftHandPoint",
|
||||
"interpTarget": 3,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "leftHandOpen", "state": "leftHandOpen" },
|
||||
{ "var": "leftHandIdle", "state": "leftHandIdle" },
|
||||
{ "var": "leftHandGrasp", "state": "leftHandGrasp" },
|
||||
{ "var": "leftHandFarGrasp", "state": "leftHandFarGrasp" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftHandFarGrasp",
|
||||
"interpTarget": 3,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "leftHandOpen", "state": "leftHandOpen" },
|
||||
{ "var": "leftHandIdle", "state": "leftHandIdle" },
|
||||
{ "var": "leftHandGrasp", "state": "leftHandGrasp" },
|
||||
{ "var": "leftHandPoint", "state": "leftHandPoint" }
|
||||
]
|
||||
"transitions": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "leftHandIdle",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_left_hand.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "leftHandOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
|
||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/search_left.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "leftHandGrasp",
|
||||
"type": "clip",
|
||||
"type": "blendLinear",
|
||||
"data": {
|
||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/grasp_left.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
"alpha": 0.0,
|
||||
"alphaVar": "leftHandGraspAlpha"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "leftHandPoint",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_left_hand.fbx",
|
||||
"startFrame": 12.0,
|
||||
"endFrame": 12.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "leftHandFarGrasp",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
|
||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/far_grasp_left.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
"children": [
|
||||
{
|
||||
"id": "leftHandGraspOpen",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/hydra_pose_open_left.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 0.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "leftHandGraspClosed",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/hand_anims/hydra_pose_closed_left.fbx",
|
||||
"startFrame": 10.0,
|
||||
"endFrame": 10.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -86,7 +86,7 @@ Hifi.AvatarInputs {
|
|||
id: controls
|
||||
width: root.mirrorWidth
|
||||
height: 44
|
||||
visible: !root.isHMD
|
||||
visible: root.showAudioTools
|
||||
anchors.top: mirror.bottom
|
||||
|
||||
Rectangle {
|
||||
|
|
|
@ -10,18 +10,22 @@ function findChild(item, name) {
|
|||
return null;
|
||||
}
|
||||
|
||||
function findParent(item, name) {
|
||||
function findParentMatching(item, predicate) {
|
||||
while (item) {
|
||||
if (item.objectName === name) {
|
||||
return item;
|
||||
if (predicate(item)) {
|
||||
break;
|
||||
}
|
||||
item = item.parent;
|
||||
}
|
||||
return null;
|
||||
return item;
|
||||
}
|
||||
|
||||
function getDesktop(item) {
|
||||
return findParent(item, OFFSCREEN_ROOT_OBJECT_NAME);
|
||||
function findParentByName(item, name) {
|
||||
return findParentMatching(item, function(item) {
|
||||
var testName = name;
|
||||
var result = (item.name === testName);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
function findRootMenu(item) {
|
||||
|
@ -29,6 +33,13 @@ function findRootMenu(item) {
|
|||
return item ? item.rootMenu : null;
|
||||
}
|
||||
|
||||
function isDesktop(item) {
|
||||
return item.desktopRoot;
|
||||
}
|
||||
|
||||
function isTopLevelWindow(item) {
|
||||
return item.topLevelWindow;
|
||||
}
|
||||
|
||||
function getTopLevelWindows(item) {
|
||||
var desktop = getDesktop(item);
|
||||
|
@ -40,8 +51,7 @@ function getTopLevelWindows(item) {
|
|||
|
||||
for (var i = 0; i < desktop.children.length; ++i) {
|
||||
var child = desktop.children[i];
|
||||
if ((Global.OFFSCREEN_WINDOW_OBJECT_NAME === child.objectName) ||
|
||||
child[Global.OFFSCREEN_WINDOW_OBJECT_NAME]) {
|
||||
if (isTopLevelWindow(child)) {
|
||||
var windowId = child.toString();
|
||||
currentWindows.push(child)
|
||||
}
|
||||
|
@ -50,9 +60,12 @@ function getTopLevelWindows(item) {
|
|||
}
|
||||
|
||||
|
||||
function getDesktop(item) {
|
||||
return findParentMatching(item, isDesktop);
|
||||
}
|
||||
|
||||
function getDesktopWindow(item) {
|
||||
item = findParent(item, OFFSCREEN_WINDOW_OBJECT_NAME);
|
||||
return item;
|
||||
return findParentMatching(item, isTopLevelWindow)
|
||||
}
|
||||
|
||||
function closeWindow(item) {
|
||||
|
@ -142,7 +155,7 @@ function raiseWindow(item) {
|
|||
|
||||
var desktop = getDesktop(targetWindow);
|
||||
if (!desktop) {
|
||||
//console.warn("Could not find desktop for window " + targetWindow);
|
||||
console.warn("Could not find desktop for window " + targetWindow);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,86 +2,26 @@ import QtQuick 2.3
|
|||
import QtQuick.Controls 1.2
|
||||
import QtWebEngine 1.1
|
||||
|
||||
import "controls"
|
||||
import "windows" as Windows
|
||||
import "controls" as Controls
|
||||
import "styles"
|
||||
|
||||
VrDialog {
|
||||
Windows.Window {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
title: "WebWindow"
|
||||
resizable: true
|
||||
enabled: false
|
||||
visible: false
|
||||
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
|
||||
destroyOnCloseButton: false
|
||||
contentImplicitWidth: clientArea.implicitWidth
|
||||
contentImplicitHeight: clientArea.implicitHeight
|
||||
backgroundColor: "#7f000000"
|
||||
property url source: "about:blank"
|
||||
property alias source: webview.url
|
||||
|
||||
signal navigating(string url)
|
||||
function stop() {
|
||||
webview.stop();
|
||||
function raiseWindow() { Desktop.raise(root) }
|
||||
|
||||
Controls.WebView {
|
||||
id: webview
|
||||
url: "about:blank"
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// Ensure the JS from the web-engine makes it to our logging
|
||||
webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
id: clientArea
|
||||
implicitHeight: 600
|
||||
implicitWidth: 800
|
||||
x: root.clientX
|
||||
y: root.clientY
|
||||
width: root.clientWidth
|
||||
height: root.clientHeight
|
||||
|
||||
WebEngineView {
|
||||
id: webview
|
||||
url: root.source
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
|
||||
property var originalUrl
|
||||
property var lastFixupTime: 0
|
||||
|
||||
onUrlChanged: {
|
||||
var currentUrl = url.toString();
|
||||
var newUrl = urlHandler.fixupUrl(currentUrl).toString();
|
||||
if (newUrl != currentUrl) {
|
||||
var now = new Date().valueOf();
|
||||
if (url === originalUrl && (now - lastFixupTime < 100)) {
|
||||
console.warn("URL fixup loop detected")
|
||||
return;
|
||||
}
|
||||
originalUrl = url
|
||||
lastFixupTime = now
|
||||
url = newUrl;
|
||||
}
|
||||
}
|
||||
|
||||
onLoadingChanged: {
|
||||
// Required to support clicking on "hifi://" links
|
||||
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
||||
var url = loadRequest.url.toString();
|
||||
if (urlHandler.canHandleUrl(url)) {
|
||||
if (urlHandler.handleUrl(url)) {
|
||||
webview.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
profile: WebEngineProfile {
|
||||
id: webviewProfile
|
||||
httpUserAgent: "Mozilla/5.0 (HighFidelityInterface)"
|
||||
storageName: "qmlWebEngine"
|
||||
}
|
||||
}
|
||||
} // item
|
||||
} // dialog
|
||||
|
|
|
@ -5,52 +5,32 @@ import QtWebChannel 1.0
|
|||
import QtWebSockets 1.0
|
||||
import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel
|
||||
|
||||
import "Global.js" as Global
|
||||
|
||||
import "windows" as Windows
|
||||
import "controls"
|
||||
import "styles"
|
||||
|
||||
VrDialog {
|
||||
Windows.Window {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
title: "QmlWindow"
|
||||
resizable: true
|
||||
enabled: false
|
||||
visible: false
|
||||
focus: true
|
||||
property var channel;
|
||||
|
||||
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
|
||||
destroyOnCloseButton: false
|
||||
contentImplicitWidth: clientArea.implicitWidth
|
||||
contentImplicitHeight: clientArea.implicitHeight
|
||||
property alias source: pageLoader.source
|
||||
|
||||
function raiseWindow() {
|
||||
Global.raiseWindow(root)
|
||||
}
|
||||
function raiseWindow() { Desktop.raise(root) }
|
||||
|
||||
Item {
|
||||
id: clientArea
|
||||
implicitHeight: 600
|
||||
implicitWidth: 800
|
||||
x: root.clientX
|
||||
y: root.clientY
|
||||
width: root.clientWidth
|
||||
height: root.clientHeight
|
||||
Loader {
|
||||
id: pageLoader
|
||||
objectName: "Loader"
|
||||
focus: true
|
||||
clip: true
|
||||
|
||||
Loader {
|
||||
id: pageLoader
|
||||
objectName: "Loader"
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
property var dialog: root
|
||||
property var dialog: root
|
||||
|
||||
Keys.onPressed: {
|
||||
console.log("QmlWindow pageLoader keypress")
|
||||
}
|
||||
Keys.onPressed: {
|
||||
console.log("QmlWindow pageLoader keypress")
|
||||
}
|
||||
} // item
|
||||
}
|
||||
} // dialog
|
||||
|
|
|
@ -7,22 +7,25 @@ import "Global.js" as Global
|
|||
// windows will be childed.
|
||||
Item {
|
||||
id: desktop
|
||||
objectName: Global.OFFSCREEN_ROOT_OBJECT_NAME
|
||||
anchors.fill: parent;
|
||||
onParentChanged: forceActiveFocus();
|
||||
|
||||
// Allows QML/JS to find the desktop through the parent chain
|
||||
property bool desktopRoot: true
|
||||
|
||||
// The VR version of the primary menu
|
||||
property var rootMenu: Menu { objectName: "rootMenu" }
|
||||
|
||||
// List of all top level windows
|
||||
property var windows: [];
|
||||
property var rootMenu: Menu {
|
||||
objectName: "rootMenu"
|
||||
}
|
||||
onChildrenChanged: windows = Global.getTopLevelWindows(desktop);
|
||||
|
||||
onChildrenChanged: {
|
||||
windows = Global.getTopLevelWindows(desktop);
|
||||
}
|
||||
|
||||
onParentChanged: {
|
||||
forceActiveFocus();
|
||||
}
|
||||
// The tool window, one instance
|
||||
property alias toolWindow: toolWindow
|
||||
ToolWindow { id: toolWindow }
|
||||
|
||||
function raise(item) {
|
||||
Global.raiseWindow(item);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
127
interface/resources/qml/ToolWindow.qml
Normal file
127
interface/resources/qml/ToolWindow.qml
Normal file
|
@ -0,0 +1,127 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtWebEngine 1.1
|
||||
|
||||
import Qt.labs.settings 1.0
|
||||
|
||||
import "windows" as Windows
|
||||
import "controls" as Controls
|
||||
|
||||
Windows.Window {
|
||||
id: toolWindow
|
||||
resizable: true
|
||||
objectName: "ToolWindow"
|
||||
destroyOnCloseButton: false
|
||||
destroyOnInvisible: false
|
||||
closable: false
|
||||
visible: false
|
||||
property string newTabSource
|
||||
property alias tabView: tabView
|
||||
onParentChanged: {
|
||||
x = desktop.width / 2 - width / 2;
|
||||
y = desktop.height / 2 - height / 2;
|
||||
}
|
||||
|
||||
Settings {
|
||||
category: "ToolWindow.Position"
|
||||
property alias x: toolWindow.x
|
||||
property alias y: toolWindow.y
|
||||
}
|
||||
|
||||
property var webTabCreator: Component {
|
||||
Controls.WebView {
|
||||
id: webView
|
||||
property string originalUrl;
|
||||
|
||||
// Both toolWindow.newTabSource and url can change, so we need
|
||||
// to store the original url here, without creating any bindings
|
||||
Component.onCompleted: {
|
||||
originalUrl = toolWindow.newTabSource;
|
||||
url = originalUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TabView {
|
||||
id: tabView; width: 384; height: 640;
|
||||
onCountChanged: {
|
||||
if (0 == count) {
|
||||
toolWindow.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateVisiblity() {
|
||||
var newVisible = false
|
||||
console.log("Checking " + tabView.count + " children")
|
||||
for (var i = 0; i < tabView.count; ++i) {
|
||||
if (tabView.getTab(i).enabled) {
|
||||
console.log("Tab " + i + " enabled");
|
||||
newVisible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
console.log("Setting toolWindow visible: " + newVisible);
|
||||
visible = newVisible
|
||||
}
|
||||
|
||||
function findIndexForUrl(source) {
|
||||
for (var i = 0; i < tabView.count; ++i) {
|
||||
var tab = tabView.getTab(i);
|
||||
if (tab && tab.item && tab.item.originalUrl &&
|
||||
tab.item.originalUrl === source) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function removeTabForUrl(source) {
|
||||
var index = findIndexForUrl(source);
|
||||
if (index < 0) {
|
||||
console.warn("Could not find tab for " + source);
|
||||
return;
|
||||
}
|
||||
tabView.removeTab(index);
|
||||
console.log("Updating visibility based on child tab removed");
|
||||
updateVisiblity();
|
||||
}
|
||||
|
||||
function addWebTab(properties) {
|
||||
if (!properties.source) {
|
||||
console.warn("Attempted to open Web Tool Pane without URl")
|
||||
return;
|
||||
}
|
||||
|
||||
var existingTabIndex = findIndexForUrl(properties.source);
|
||||
if (existingTabIndex >= 0) {
|
||||
console.log("Existing tab " + existingTabIndex + " found with URL " + properties.source)
|
||||
return tabView.getTab(existingTabIndex);
|
||||
}
|
||||
|
||||
var title = properties.title || "Unknown";
|
||||
newTabSource = properties.source;
|
||||
console.log(typeof(properties.source))
|
||||
var newTab = tabView.addTab(title, webTabCreator);
|
||||
newTab.active = true;
|
||||
newTab.enabled = false;
|
||||
|
||||
if (properties.width) {
|
||||
tabView.width = Math.min(Math.max(tabView.width, properties.width),
|
||||
toolWindow.maxSize.x);
|
||||
}
|
||||
|
||||
if (properties.height) {
|
||||
tabView.height = Math.min(Math.max(tabView.height, properties.height),
|
||||
toolWindow.maxSize.y);
|
||||
}
|
||||
|
||||
console.log("Updating visibility based on child tab added");
|
||||
newTab.enabledChanged.connect(function() {
|
||||
console.log("Updating visibility based on child tab enabled change");
|
||||
updateVisiblity();
|
||||
})
|
||||
updateVisiblity();
|
||||
return newTab
|
||||
}
|
||||
}
|
|
@ -3,8 +3,6 @@ import QtQuick.Controls 1.2
|
|||
import "."
|
||||
import "../styles"
|
||||
|
||||
import "../Global.js" as Global
|
||||
|
||||
/*
|
||||
* FIXME Need to create a client property here so that objects can be
|
||||
* placed in it without having to think about positioning within the outer
|
||||
|
@ -48,7 +46,7 @@ DialogBase {
|
|||
if (enabled) {
|
||||
visible = true;
|
||||
if (root.parent) {
|
||||
Global.raiseWindow(root);
|
||||
Desktop.raise(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
50
interface/resources/qml/controls/WebView.qml
Normal file
50
interface/resources/qml/controls/WebView.qml
Normal file
|
@ -0,0 +1,50 @@
|
|||
import QtQuick 2.5
|
||||
import QtWebEngine 1.1
|
||||
|
||||
WebEngineView {
|
||||
id: root
|
||||
property var originalUrl
|
||||
property int lastFixupTime: 0
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("Connecting JS messaging to Hifi Logging")
|
||||
// Ensure the JS from the web-engine makes it to our logging
|
||||
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
onUrlChanged: {
|
||||
var currentUrl = url.toString();
|
||||
var newUrl = urlHandler.fixupUrl(currentUrl).toString();
|
||||
if (newUrl != currentUrl) {
|
||||
var now = new Date().valueOf();
|
||||
if (url === originalUrl && (now - lastFixupTime < 100)) {
|
||||
console.warn("URL fixup loop detected")
|
||||
return;
|
||||
}
|
||||
originalUrl = url
|
||||
lastFixupTime = now
|
||||
url = newUrl;
|
||||
}
|
||||
}
|
||||
|
||||
onLoadingChanged: {
|
||||
// Required to support clicking on "hifi://" links
|
||||
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
||||
var url = loadRequest.url.toString();
|
||||
if (urlHandler.canHandleUrl(url)) {
|
||||
if (urlHandler.handleUrl(url)) {
|
||||
root.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
profile: WebEngineProfile {
|
||||
id: webviewProfile
|
||||
httpUserAgent: "Mozilla/5.0 (HighFidelityInterface)"
|
||||
storageName: "qmlWebEngine"
|
||||
}
|
||||
}
|
232
interface/resources/qml/dialogs/FileDialog.qml
Normal file
232
interface/resources/qml/dialogs/FileDialog.qml
Normal file
|
@ -0,0 +1,232 @@
|
|||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.4
|
||||
import Qt.labs.folderlistmodel 2.1
|
||||
import Qt.labs.settings 1.0
|
||||
|
||||
import ".."
|
||||
import "."
|
||||
|
||||
// Work in progress....
|
||||
DialogBase {
|
||||
id: root
|
||||
Constants { id: vr }
|
||||
property string settingsName: ""
|
||||
|
||||
signal selectedFile(var file);
|
||||
signal canceled();
|
||||
|
||||
function selectCurrentFile() {
|
||||
var row = tableView.currentRow
|
||||
console.log("Selecting row " + row)
|
||||
var fileName = folderModel.get(row, "fileName");
|
||||
if (fileName === "..") {
|
||||
folderModel.folder = folderModel.parentFolder
|
||||
} else if (folderModel.isFolder(row)) {
|
||||
folderModel.folder = folderModel.get(row, "fileURL");
|
||||
} else {
|
||||
selectedFile(folderModel.get(row, "fileURL"));
|
||||
enabled = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
property var folderModel: FolderListModel {
|
||||
id: folderModel
|
||||
showDotAndDotDot: true
|
||||
showDirsFirst: true
|
||||
folder: "file:///C:/";
|
||||
onFolderChanged: tableView.currentRow = 0
|
||||
}
|
||||
|
||||
property var filterModel: ListModel {
|
||||
ListElement {
|
||||
text: "All Files (*.*)"
|
||||
filter: "*"
|
||||
}
|
||||
ListElement {
|
||||
text: "Javascript Files (*.js)"
|
||||
filter: "*.js"
|
||||
}
|
||||
ListElement {
|
||||
text: "QML Files (*.qml)"
|
||||
filter: "*.qml"
|
||||
}
|
||||
}
|
||||
|
||||
Settings {
|
||||
// fixme, combine with a property to allow different saved locations
|
||||
category: "FileOpenLastFolder." + settingsName
|
||||
property alias folder: folderModel.folder
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: currentDirectoryWrapper
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 8
|
||||
height: currentDirectory.implicitHeight + 8
|
||||
radius: vr.styles.radius
|
||||
color: vr.controls.colors.inputBackground
|
||||
|
||||
TextEdit {
|
||||
enabled: false
|
||||
id: currentDirectory
|
||||
text: folderModel.folder
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: 8
|
||||
rightMargin: 8
|
||||
topMargin: 4
|
||||
bottomMargin: 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TableView {
|
||||
id: tableView
|
||||
focus: true
|
||||
model: folderModel
|
||||
anchors.top: currentDirectoryWrapper.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: selectionType.top
|
||||
anchors.margins: 8
|
||||
itemDelegate: Item {
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.family: vr.fonts.lightFontName
|
||||
font.pointSize: 12
|
||||
color: tableView.activeFocus && styleData.row === tableView.currentRow ? "yellow" : styleData.textColor
|
||||
elide: styleData.elideMode
|
||||
text: getText();
|
||||
function getText() {
|
||||
switch (styleData.column) {
|
||||
//case 1: return Date.fromLocaleString(locale, styleData.value, "yyyy-MM-dd hh:mm:ss");
|
||||
case 2: return folderModel.get(styleData.row, "fileIsDir") ? "" : formatSize(styleData.value);
|
||||
default: return styleData.value;
|
||||
}
|
||||
}
|
||||
|
||||
function formatSize(size) {
|
||||
var suffixes = [ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" ];
|
||||
var suffixIndex = 0
|
||||
while ((size / 1024.0) > 1.1) {
|
||||
size /= 1024.0;
|
||||
++suffixIndex;
|
||||
}
|
||||
|
||||
size = Math.round(size*1000)/1000;
|
||||
size = size.toLocaleString()
|
||||
|
||||
return size + " " + suffixes[suffixIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TableViewColumn {
|
||||
role: "fileName"
|
||||
title: "Name"
|
||||
width: 400
|
||||
}
|
||||
TableViewColumn {
|
||||
role: "fileModified"
|
||||
title: "Date Modified"
|
||||
width: 200
|
||||
}
|
||||
TableViewColumn {
|
||||
role: "fileSize"
|
||||
title: "Size"
|
||||
width: 200
|
||||
}
|
||||
|
||||
function selectItem(row) {
|
||||
selectCurrentFile()
|
||||
}
|
||||
|
||||
|
||||
Keys.onReturnPressed: selectCurrentFile();
|
||||
onDoubleClicked: { currentRow = row; selectCurrentFile(); }
|
||||
onCurrentRowChanged: currentSelection.text = model.get(currentRow, "fileName");
|
||||
KeyNavigation.left: cancelButton
|
||||
KeyNavigation.right: selectionType
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.right: selectionType.left
|
||||
anchors.rightMargin: 8
|
||||
anchors.top: selectionType.top
|
||||
anchors.bottom: selectionType.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
radius: 8
|
||||
color: "#eee"
|
||||
Text {
|
||||
id: currentSelection
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.family: vr.fonts.mainFontName
|
||||
font.pointSize: 18
|
||||
}
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: selectionType
|
||||
anchors.bottom: buttonRow.top
|
||||
anchors.bottomMargin: 8
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.left: buttonRow.left
|
||||
model: filterModel
|
||||
onCurrentIndexChanged: folderModel.nameFilters = [
|
||||
filterModel.get(currentIndex).filter
|
||||
]
|
||||
|
||||
KeyNavigation.left: tableView
|
||||
KeyNavigation.right: openButton
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttonRow
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 8
|
||||
layoutDirection: Qt.RightToLeft
|
||||
spacing: 8
|
||||
Button {
|
||||
id: cancelButton
|
||||
text: "Cancel"
|
||||
KeyNavigation.up: selectionType
|
||||
KeyNavigation.left: openButton
|
||||
KeyNavigation.right: tableView.contentItem
|
||||
Keys.onReturnPressed: { canceled(); root.enabled = false }
|
||||
onClicked: { canceled(); root.enabled = false }
|
||||
}
|
||||
Button {
|
||||
id: openButton
|
||||
text: "Open"
|
||||
enabled: tableView.currentRow != -1 && !folderModel.get(tableView.currentRow, "fileIsDir")
|
||||
KeyNavigation.up: selectionType
|
||||
KeyNavigation.left: selectionType
|
||||
KeyNavigation.right: cancelButton
|
||||
onClicked: selectCurrentFile();
|
||||
Keys.onReturnPressed: selectedFile(folderModel.get(tableView.currentRow, "fileURL"))
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (event.key === Qt.Key_Backspace && folderModel.parentFolder && folderModel.parentFolder != "") {
|
||||
console.log("Navigating to " + folderModel.parentFolder)
|
||||
folderModel.folder = folderModel.parentFolder
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
311
interface/resources/qml/test/Stubs.qml
Normal file
311
interface/resources/qml/test/Stubs.qml
Normal file
|
@ -0,0 +1,311 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
// Stubs for the global service objects set by Interface.cpp when creating the UI
|
||||
// This is useful for testing inside Qt creator where these services don't actually exist.
|
||||
Item {
|
||||
|
||||
Item {
|
||||
objectName: "urlHandler"
|
||||
function fixupUrl(url) { return url; }
|
||||
function canHandleUrl(url) { return false; }
|
||||
function handleUrl(url) { return true; }
|
||||
}
|
||||
|
||||
Item {
|
||||
objectName: "Account"
|
||||
function isLoggedIn() { return true; }
|
||||
function getUsername() { return "Jherico"; }
|
||||
}
|
||||
|
||||
Item {
|
||||
objectName: "ScriptDiscoveryService"
|
||||
property var scriptsModelFilter: scriptsModel
|
||||
signal scriptCountChanged()
|
||||
property var _runningScripts:[
|
||||
{ name: "wireFrameTest.js", url: "foo/wireframetest.js", path: "foo/wireframetest.js", local: true },
|
||||
{ name: "edit.js", url: "foo/edit.js", path: "foo/edit.js", local: false },
|
||||
{ name: "listAllScripts.js", url: "foo/listAllScripts.js", path: "foo/listAllScripts.js", local: false },
|
||||
{ name: "users.js", url: "foo/users.js", path: "foo/users.js", local: false },
|
||||
]
|
||||
|
||||
function getRunning() {
|
||||
return _runningScripts;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
id: menuHelper
|
||||
objectName: "MenuHelper"
|
||||
|
||||
Component {
|
||||
id: modelMaker
|
||||
ListModel { }
|
||||
}
|
||||
|
||||
function toModel(menu, parent) {
|
||||
if (!parent) { parent = menuHelper }
|
||||
var result = modelMaker.createObject(parent);
|
||||
if (menu.type !== MenuItemType.Menu) {
|
||||
console.warn("Not a menu: " + menu);
|
||||
return result;
|
||||
}
|
||||
|
||||
var items = menu.items;
|
||||
for (var i = 0; i < items.length; ++i) {
|
||||
var item = items[i];
|
||||
switch (item.type) {
|
||||
case 2:
|
||||
result.append({"name": item.title, "item": item})
|
||||
break;
|
||||
case 1:
|
||||
result.append({"name": item.text, "item": item})
|
||||
break;
|
||||
case 0:
|
||||
result.append({"name": "", "item": item})
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
objectName: "Desktop"
|
||||
|
||||
property string _OFFSCREEN_ROOT_OBJECT_NAME: "desktopRoot";
|
||||
property string _OFFSCREEN_DIALOG_OBJECT_NAME: "topLevelWindow";
|
||||
|
||||
|
||||
function findChild(item, name) {
|
||||
for (var i = 0; i < item.children.length; ++i) {
|
||||
if (item.children[i].objectName === name) {
|
||||
return item.children[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findParent(item, name) {
|
||||
while (item) {
|
||||
if (item.objectName === name) {
|
||||
return item;
|
||||
}
|
||||
item = item.parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findDialog(item) {
|
||||
item = findParent(item, _OFFSCREEN_DIALOG_OBJECT_NAME);
|
||||
return item;
|
||||
}
|
||||
|
||||
function closeDialog(item) {
|
||||
item = findDialog(item);
|
||||
if (item) {
|
||||
item.enabled = false
|
||||
} else {
|
||||
console.warn("Could not find top level dialog")
|
||||
}
|
||||
}
|
||||
|
||||
function getDesktop(item) {
|
||||
while (item) {
|
||||
if (item.desktopRoot) {
|
||||
break;
|
||||
}
|
||||
item = item.parent;
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
function raise(item) {
|
||||
var desktop = getDesktop(item);
|
||||
if (desktop) {
|
||||
desktop.raise(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
id: root
|
||||
objectName: "rootMenu"
|
||||
|
||||
Menu {
|
||||
title: "Audio"
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: "Avatar"
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: "Display"
|
||||
ExclusiveGroup { id: displayMode }
|
||||
Menu {
|
||||
title: "More Stuff"
|
||||
|
||||
Menu { title: "Empty" }
|
||||
|
||||
MenuItem {
|
||||
text: "Do Nothing"
|
||||
onTriggered: console.log("Nothing")
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: "Oculus"
|
||||
exclusiveGroup: displayMode
|
||||
checkable: true
|
||||
}
|
||||
MenuItem {
|
||||
text: "OpenVR"
|
||||
exclusiveGroup: displayMode
|
||||
checkable: true
|
||||
}
|
||||
MenuItem {
|
||||
text: "OSVR"
|
||||
exclusiveGroup: displayMode
|
||||
checkable: true
|
||||
}
|
||||
MenuItem {
|
||||
text: "2D Screen"
|
||||
exclusiveGroup: displayMode
|
||||
checkable: true
|
||||
checked: true
|
||||
}
|
||||
MenuItem {
|
||||
text: "3D Screen (Active)"
|
||||
exclusiveGroup: displayMode
|
||||
checkable: true
|
||||
}
|
||||
MenuItem {
|
||||
text: "3D Screen (Passive)"
|
||||
exclusiveGroup: displayMode
|
||||
checkable: true
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: "View"
|
||||
Menu {
|
||||
title: "Camera Mode"
|
||||
ExclusiveGroup { id: cameraMode }
|
||||
MenuItem {
|
||||
exclusiveGroup: cameraMode
|
||||
text: "First Person";
|
||||
onTriggered: console.log(text + " checked " + checked)
|
||||
checkable: true
|
||||
checked: true
|
||||
}
|
||||
MenuItem {
|
||||
exclusiveGroup: cameraMode
|
||||
text: "Third Person";
|
||||
onTriggered: console.log(text)
|
||||
checkable: true
|
||||
}
|
||||
MenuItem {
|
||||
exclusiveGroup: cameraMode
|
||||
text: "Independent Mode";
|
||||
onTriggered: console.log(text)
|
||||
checkable: true
|
||||
}
|
||||
MenuItem {
|
||||
exclusiveGroup: cameraMode
|
||||
text: "Entity Mode";
|
||||
onTriggered: console.log(text)
|
||||
enabled: false
|
||||
checkable: true
|
||||
}
|
||||
MenuItem {
|
||||
exclusiveGroup: cameraMode
|
||||
text: "Fullscreen Mirror";
|
||||
onTriggered: console.log(text)
|
||||
checkable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: "Edit"
|
||||
|
||||
MenuItem {
|
||||
text: "Undo"
|
||||
shortcut: "Ctrl+Z"
|
||||
enabled: false
|
||||
onTriggered: console.log(text)
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "Redo"
|
||||
shortcut: "Ctrl+Shift+Z"
|
||||
enabled: false
|
||||
onTriggered: console.log(text)
|
||||
}
|
||||
|
||||
MenuSeparator { }
|
||||
|
||||
MenuItem {
|
||||
text: "Cut"
|
||||
shortcut: "Ctrl+X"
|
||||
onTriggered: console.log(text)
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "Copy"
|
||||
shortcut: "Ctrl+C"
|
||||
onTriggered: console.log(text)
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "Paste"
|
||||
shortcut: "Ctrl+V"
|
||||
visible: false
|
||||
onTriggered: console.log("Paste")
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: "Navigate"
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: "Market"
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: "Settings"
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: "Developer"
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: "Quit"
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: "File"
|
||||
|
||||
Action {
|
||||
id: login
|
||||
text: "Login"
|
||||
}
|
||||
|
||||
Action {
|
||||
id: quit
|
||||
text: "Quit"
|
||||
shortcut: "Ctrl+Q"
|
||||
onTriggered: Qt.quit();
|
||||
}
|
||||
|
||||
MenuItem { action: quit }
|
||||
MenuItem { action: login }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,90 +4,134 @@ import "."
|
|||
import "../controls"
|
||||
|
||||
Frame {
|
||||
id: root
|
||||
anchors { margins: -16; topMargin: parent.closable ? -32 : -16; }
|
||||
id: frame
|
||||
// The frame fills the parent, which should be the size of the content.
|
||||
// The frame decorations use negative anchor margins to extend beyond
|
||||
anchors.fill: parent
|
||||
// Size of the controls
|
||||
readonly property real iconSize: 24;
|
||||
|
||||
// Convenience accessor for the window
|
||||
property alias window: frame.parent
|
||||
// FIXME needed?
|
||||
property alias decoration: decoration
|
||||
|
||||
MouseArea {
|
||||
id: controlsArea
|
||||
anchors.fill: desktopControls
|
||||
hoverEnabled: true
|
||||
drag.target: root.parent
|
||||
propagateComposedEvents: true
|
||||
onClicked: {
|
||||
root.raise()
|
||||
mouse.accepted = false;
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: sizeDrag
|
||||
enabled: root.parent.resizable
|
||||
property int startX
|
||||
property int startY
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
width: 16
|
||||
height: 16
|
||||
z: 1000
|
||||
hoverEnabled: true
|
||||
onPressed: {
|
||||
startX = mouseX
|
||||
startY = mouseY
|
||||
}
|
||||
onPositionChanged: {
|
||||
if (pressed) {
|
||||
root.deltaSize((mouseX - startX), (mouseY - startY))
|
||||
startX = mouseX
|
||||
startY = mouseY
|
||||
}
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
anchors { margins: -4 }
|
||||
visible: !decoration.visible
|
||||
anchors.fill: parent;
|
||||
color: "#7f7f7f7f";
|
||||
radius: 3;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: desktopControls
|
||||
// FIXME this doesn't work as expected
|
||||
visible: root.parent.showFrame
|
||||
id: decoration
|
||||
anchors { margins: -iconSize; topMargin: -iconSize * (window.closable ? 2 : 1); }
|
||||
// FIXME doesn't work
|
||||
// visible: window.activator.containsMouse
|
||||
anchors.fill: parent;
|
||||
color: "#7f7f7f7f";
|
||||
radius: 3;
|
||||
|
||||
// Allow dragging of the window
|
||||
MouseArea {
|
||||
id: dragMouseArea
|
||||
anchors.fill: parent
|
||||
drag {
|
||||
target: window
|
||||
// minimumX: (decoration.width - window.width) * -1
|
||||
// minimumY: 0
|
||||
// maximumX: (window.parent.width - window.width) - 2 * (decoration.width - window.width)
|
||||
// maximumY: (window.parent.height - window.height) - 2 * (decoration.height - window.height)
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: controlsRow
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.rightMargin: 4
|
||||
anchors.topMargin: 4
|
||||
spacing: 4
|
||||
anchors.rightMargin: iconSize
|
||||
anchors.topMargin: iconSize / 2
|
||||
spacing: iconSize / 4
|
||||
FontAwesome {
|
||||
visible: false
|
||||
text: "\uf08d"
|
||||
style: Text.Outline; styleColor: "white"
|
||||
size: 16
|
||||
rotation: !root.parent ? 90 : root.parent.pinned ? 0 : 90
|
||||
color: root.pinned ? "red" : "black"
|
||||
size: frame.iconSize
|
||||
rotation: !frame.parent ? 90 : frame.parent.pinned ? 0 : 90
|
||||
color: frame.pinned ? "red" : "black"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
propagateComposedEvents: true
|
||||
onClicked: { root.pin(); mouse.accepted = false; }
|
||||
onClicked: { frame.pin(); mouse.accepted = false; }
|
||||
}
|
||||
}
|
||||
|
||||
FontAwesome {
|
||||
visible: root.parent.closable
|
||||
visible: window.closable
|
||||
text: closeClickArea.containsMouse ? "\uf057" : "\uf05c"
|
||||
style: Text.Outline;
|
||||
styleColor: "white"
|
||||
color: closeClickArea.containsMouse ? "red" : "black"
|
||||
size: 16
|
||||
size: frame.iconSize
|
||||
MouseArea {
|
||||
id: closeClickArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: root.close();
|
||||
onClicked: frame.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allow sizing of the window
|
||||
// FIXME works in native QML, doesn't work in Interface
|
||||
MouseArea {
|
||||
id: sizeDrag
|
||||
width: iconSize
|
||||
height: iconSize
|
||||
|
||||
anchors {
|
||||
right: decoration.right;
|
||||
bottom: decoration.bottom
|
||||
bottomMargin: iconSize * 2
|
||||
}
|
||||
property vector2d pressOrigin
|
||||
property vector2d sizeOrigin
|
||||
property bool hid: false
|
||||
onPressed: {
|
||||
console.log("Pressed on size")
|
||||
pressOrigin = Qt.vector2d(mouseX, mouseY)
|
||||
sizeOrigin = Qt.vector2d(window.content.width, window.content.height)
|
||||
hid = false;
|
||||
}
|
||||
onReleased: {
|
||||
if (hid) {
|
||||
window.content.visible = true
|
||||
hid = false;
|
||||
}
|
||||
}
|
||||
|
||||
onPositionChanged: {
|
||||
if (pressed) {
|
||||
if (window.content.visible) {
|
||||
window.content.visible = false;
|
||||
hid = true;
|
||||
}
|
||||
var delta = Qt.vector2d(mouseX, mouseY).minus(pressOrigin);
|
||||
frame.deltaSize(delta.x, delta.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FontAwesome {
|
||||
visible: window.resizable
|
||||
rotation: -45
|
||||
anchors { centerIn: sizeDrag }
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: "\uf07d"
|
||||
size: iconSize / 3 * 2
|
||||
style: Text.Outline; styleColor: "white"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,78 +6,75 @@ import "../styles"
|
|||
|
||||
FocusScope {
|
||||
id: window
|
||||
objectName: "topLevelWindow"
|
||||
HifiConstants { id: hifi }
|
||||
implicitHeight: frame.height
|
||||
implicitWidth: frame.width
|
||||
// The Window size is the size of the content, while the frame
|
||||
// decorations can extend outside it. Windows should generally not be
|
||||
// given explicit height / width, but rather be allowed to conform to
|
||||
// their content
|
||||
implicitHeight: content.height
|
||||
implicitWidth: content.width
|
||||
|
||||
property bool topLevelWindow: true
|
||||
property string title
|
||||
property bool showFrame: true
|
||||
// Should the window include a close control?
|
||||
property bool closable: true
|
||||
property bool destroyOnInvisible: false
|
||||
// Should hitting the close button hide or destroy the window?
|
||||
property bool destroyOnCloseButton: true
|
||||
property bool pinned: false
|
||||
// Should hiding the window destroy it or just hide it?
|
||||
property bool destroyOnInvisible: false
|
||||
// FIXME support for pinned / unpinned pending full design
|
||||
// property bool pinnable: false
|
||||
// property bool pinned: false
|
||||
property bool resizable: false
|
||||
property real minX: 320
|
||||
property real minY: 240;
|
||||
property vector2d minSize: Qt.vector2d(100, 100)
|
||||
property vector2d maxSize: Qt.vector2d(1280, 720)
|
||||
|
||||
// The content to place inside the window, determined by the client
|
||||
default property var content
|
||||
property var frame: DefaultFrame { anchors.fill: content }
|
||||
property var blur: FastBlur { anchors.fill: content; source: content; visible: false; radius: 0}
|
||||
//property var hoverDetector: MouseArea { anchors.fill: frame; hoverEnabled: true; propagateComposedEvents: true; }
|
||||
//property bool mouseInWindow: hoverDetector.containsMouse
|
||||
children: [ frame, content, blur ]
|
||||
signal windowDestroyed();
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
property vector2d minPosition: Qt.vector2d(0, 0);
|
||||
property vector2d maxPosition: Qt.vector2d(100, 100);
|
||||
|
||||
function clamp(value, min, max) {
|
||||
return Math.min(Math.max(value, min), max);
|
||||
onContentChanged: {
|
||||
if (content) {
|
||||
content.anchors.fill = window
|
||||
}
|
||||
|
||||
function updateParentRect() {
|
||||
// if (!frame) { return; }
|
||||
// console.log(window.parent.width);
|
||||
// console.log(frame.width);
|
||||
// minPosition = Qt.vector2d(-frame.anchors.leftMargin, -frame.anchors.topMargin);
|
||||
// maxPosition = Qt.vector2d(
|
||||
// Math.max(minPosition.x, Desktop.width - frame.width + minPosition.x),
|
||||
// Math.max(minPosition.y, Desktop.height - frame.height + minPosition.y))
|
||||
// console.log(maxPosition);
|
||||
}
|
||||
|
||||
function keepOnScreen() {
|
||||
//window.x = clamp(x, minPosition.x, maxPosition.x);
|
||||
//window.y = clamp(y, minPosition.y, maxPosition.y);
|
||||
}
|
||||
|
||||
onMinPositionChanged: keepOnScreen();
|
||||
onMaxPositionChanged: keepOnScreen();
|
||||
}
|
||||
|
||||
// Default to a standard frame. Can be overriden to provide custom
|
||||
// frame styles, like a full desktop frame to simulate a modal window
|
||||
property var frame: DefaultFrame {
|
||||
z: -1
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
// This mouse area serves to raise the window. To function, it must live
|
||||
// in the window and have a higher Z-order than the content, but follow
|
||||
// the position and size of frame decoration
|
||||
property var activator: MouseArea {
|
||||
width: frame.decoration.width
|
||||
height: frame.decoration.height
|
||||
x: frame.decoration.anchors.margins
|
||||
y: frame.decoration.anchors.topMargin
|
||||
propagateComposedEvents: true
|
||||
hoverEnabled: true
|
||||
onPressed: { window.raise(); mouse.accepted = false; }
|
||||
// Debugging visualization
|
||||
// Rectangle { anchors.fill:parent; color: "#7f00ff00" }
|
||||
}
|
||||
|
||||
children: [ frame, content, activator ]
|
||||
signal windowDestroyed();
|
||||
|
||||
Component.onCompleted: {
|
||||
d.updateParentRect();
|
||||
fadeTargetProperty = visible ? 1.0 : 0.0
|
||||
raise();
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
content.destroy();
|
||||
console.log("Destroyed " + window);
|
||||
windowDestroyed();
|
||||
}
|
||||
|
||||
onParentChanged: {
|
||||
d.updateParentRect();
|
||||
raise();
|
||||
}
|
||||
|
||||
onFrameChanged: d.updateParentRect();
|
||||
onWidthChanged: d.updateParentRect();
|
||||
onHeightChanged: d.updateParentRect();
|
||||
onXChanged: d.keepOnScreen();
|
||||
onYChanged: d.keepOnScreen();
|
||||
onParentChanged: raise();
|
||||
|
||||
Connections {
|
||||
target: frame
|
||||
|
@ -85,15 +82,15 @@ FocusScope {
|
|||
onClose: window.close();
|
||||
onPin: window.pin();
|
||||
onDeltaSize: {
|
||||
console.log("deltaSize")
|
||||
content.width = Math.max(content.width + dx, minX)
|
||||
content.height = Math.max(content.height + dy, minY)
|
||||
var newSize = Qt.vector2d(content.width + dx, content.height + dy);
|
||||
newSize = clampVector(newSize, minSize, maxSize);
|
||||
window.width = newSize.x
|
||||
window.height = newSize.y
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function raise() {
|
||||
if (enabled && parent) {
|
||||
if (visible && parent) {
|
||||
Desktop.raise(window)
|
||||
if (!focus) {
|
||||
focus = true;
|
||||
|
@ -102,48 +99,58 @@ FocusScope {
|
|||
}
|
||||
|
||||
function pin() {
|
||||
pinned = ! pinned
|
||||
// pinned = ! pinned
|
||||
}
|
||||
|
||||
// our close function performs the same way as the OffscreenUI class:
|
||||
// don't do anything but manipulate the enabled flag and let the other
|
||||
// don't do anything but manipulate the targetVisible flag and let the other
|
||||
// mechanisms decide if the window should be destroyed after the close
|
||||
// animation completes
|
||||
function close() {
|
||||
console.log("Closing " + window)
|
||||
if (destroyOnCloseButton) {
|
||||
destroyOnInvisible = true
|
||||
}
|
||||
enabled = false;
|
||||
visible = false;
|
||||
}
|
||||
|
||||
onEnabledChanged: {
|
||||
if (!enabled) {
|
||||
if (blur) {
|
||||
blur.visible = true;
|
||||
}
|
||||
if (content) {
|
||||
content.visible = false;
|
||||
}
|
||||
}
|
||||
//
|
||||
// Enable window visibility transitions
|
||||
//
|
||||
|
||||
opacity = enabled ? 1.0 : 0.0
|
||||
// If the dialog is initially invisible, setting opacity doesn't
|
||||
// trigger making it visible.
|
||||
if (enabled) {
|
||||
visible = true;
|
||||
raise();
|
||||
// The target property to animate, usually scale or opacity
|
||||
property alias fadeTargetProperty: window.opacity
|
||||
// always start the property at 0 to enable fade in on creation
|
||||
opacity: 0
|
||||
|
||||
// Some dialogs should be destroyed when they become
|
||||
// invisible, so handle that
|
||||
onVisibleChanged: {
|
||||
// If someone directly set the visibility to false
|
||||
// toggle it back on and use the targetVisible flag to transition
|
||||
// via fading.
|
||||
if ((!visible && fadeTargetProperty != 0.0) || (visible && fadeTargetProperty == 0.0)) {
|
||||
var target = visible;
|
||||
visible = !visible;
|
||||
fadeTargetProperty = target ? 1.0 : 0.0;
|
||||
return;
|
||||
}
|
||||
if (!visible && destroyOnInvisible) {
|
||||
console.log("Destroying " + window);
|
||||
destroy();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The offscreen UI will enable an object, rather than manipulating it's
|
||||
// visibility, so that we can do animations in both directions. Because
|
||||
// visibility and enabled are boolean flags, they cannot be animated. So when
|
||||
// enabled is change, we modify a property that can be animated, like scale or
|
||||
// opacity, and then when the target animation value is reached, we can
|
||||
// modify the visibility
|
||||
// visibility is a boolean flags, it cannot be animated. So when
|
||||
// targetVisible is changed, we modify a property that can be animated,
|
||||
// like scale or opacity, and then when the target animation value is reached,
|
||||
// we can modify the visibility
|
||||
|
||||
// The actual animator
|
||||
Behavior on opacity {
|
||||
Behavior on fadeTargetProperty {
|
||||
NumberAnimation {
|
||||
duration: hifi.effects.fadeInDuration
|
||||
easing.type: Easing.OutCubic
|
||||
|
@ -151,31 +158,31 @@ FocusScope {
|
|||
}
|
||||
|
||||
// Once we're transparent, disable the dialog's visibility
|
||||
onOpacityChanged: {
|
||||
visible = (opacity != 0.0);
|
||||
if (opacity == 1.0) {
|
||||
content.visible = true;
|
||||
blur.visible = false;
|
||||
}
|
||||
onFadeTargetPropertyChanged: {
|
||||
visible = (fadeTargetProperty != 0.0);
|
||||
}
|
||||
|
||||
// Some dialogs should be destroyed when they become
|
||||
// invisible, so handle that
|
||||
onVisibleChanged: {
|
||||
if (!visible && destroyOnInvisible) {
|
||||
console.log("Destroying " + window);
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
switch(event.key) {
|
||||
case Qt.Key_W:
|
||||
if (event.modifiers == Qt.ControlModifier) {
|
||||
if (event.modifiers === Qt.ControlModifier) {
|
||||
event.accepted = true
|
||||
enabled = false
|
||||
visible = false
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function clamp(value, min, max) {
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
|
||||
function clampVector(value, min, max) {
|
||||
return Qt.vector2d(
|
||||
clamp(value.x, min.x, max.x),
|
||||
clamp(value.y, min.y, max.y))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -395,7 +395,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
QApplication(argc, argv),
|
||||
_dependencyManagerIsSetup(setupEssentials(argc, argv)),
|
||||
_window(new MainWindow(desktop())),
|
||||
_toolWindow(NULL),
|
||||
_undoStackScriptingInterface(&_undoStack),
|
||||
_frameCount(0),
|
||||
_fps(60.0f),
|
||||
|
@ -678,10 +677,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
_renderEngine->addTask(make_shared<RenderDeferredTask>());
|
||||
_renderEngine->registerScene(_main3DScene);
|
||||
|
||||
_toolWindow = new ToolWindow();
|
||||
_toolWindow->setWindowFlags((_toolWindow->windowFlags() | Qt::WindowStaysOnTopHint) & ~Qt::WindowMinimizeButtonHint);
|
||||
_toolWindow->setWindowTitle("Tools");
|
||||
|
||||
_offscreenContext->makeCurrent();
|
||||
|
||||
// Tell our entity edit sender about our known jurisdictions
|
||||
|
@ -4766,7 +4761,7 @@ void Application::showFriendsWindow() {
|
|||
auto webWindowClass = _window->findChildren<WebWindowClass>(FRIENDS_WINDOW_OBJECT_NAME);
|
||||
if (webWindowClass.empty()) {
|
||||
auto friendsWindow = new WebWindowClass(FRIENDS_WINDOW_TITLE, FRIENDS_WINDOW_URL, FRIENDS_WINDOW_WIDTH,
|
||||
FRIENDS_WINDOW_HEIGHT, false);
|
||||
FRIENDS_WINDOW_HEIGHT);
|
||||
friendsWindow->setParent(_window);
|
||||
friendsWindow->setObjectName(FRIENDS_WINDOW_OBJECT_NAME);
|
||||
connect(friendsWindow, &WebWindowClass::closed, &WebWindowClass::deleteLater);
|
||||
|
|
|
@ -62,7 +62,6 @@
|
|||
#include "ui/OverlayConductor.h"
|
||||
#include "ui/overlays/Overlays.h"
|
||||
#include "ui/SnapshotShareDialog.h"
|
||||
#include "ui/ToolWindow.h"
|
||||
#include "UndoStackScriptingInterface.h"
|
||||
|
||||
class OffscreenGLCanvas;
|
||||
|
@ -164,8 +163,6 @@ public:
|
|||
|
||||
NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; }
|
||||
|
||||
ToolWindow* getToolWindow() { return _toolWindow ; }
|
||||
|
||||
virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; }
|
||||
virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine);
|
||||
|
||||
|
@ -400,8 +397,6 @@ private:
|
|||
|
||||
MainWindow* _window;
|
||||
|
||||
ToolWindow* _toolWindow;
|
||||
|
||||
QUndoStack _undoStack;
|
||||
UndoStackScriptingInterface _undoStackScriptingInterface;
|
||||
|
||||
|
|
|
@ -156,9 +156,8 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false,
|
||||
audioIO.data(), SLOT(toggleMute()));
|
||||
|
||||
// Audio > Level Meter [advanced] -- FIXME: needs implementation
|
||||
auto levelMeterAction = addCheckableActionToQMenuAndActionHash(audioMenu, "Level Meter", 0, false, NULL, NULL, UNSPECIFIED_POSITION, "Advanced");
|
||||
levelMeterAction->setDisabled(true);
|
||||
// Audio > Show Level Meter
|
||||
addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::AudioTools, 0, true);
|
||||
|
||||
|
||||
// Avatar menu ----------------------------------
|
||||
|
@ -325,8 +324,10 @@ Menu::Menu() {
|
|||
// Developer > Render >>>
|
||||
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::WorldAxes);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DebugAmbientOcclusion);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Antialiasing);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, 0, true);
|
||||
|
||||
// Developer > Render > Ambient Light
|
||||
MenuWrapper* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight);
|
||||
|
@ -357,15 +358,8 @@ Menu::Menu() {
|
|||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false));
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false));
|
||||
|
||||
// Developer > Render > Stars
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars,
|
||||
0, // QML Qt::Key_Asterisk,
|
||||
true);
|
||||
|
||||
// Developer > Render > LOD Tools
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools,
|
||||
0, // QML Qt::SHIFT | Qt::Key_L,
|
||||
dialogsManager.data(), SLOT(lodTools()));
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, 0, dialogsManager.data(), SLOT(lodTools()));
|
||||
|
||||
// Developer > Assets >>>
|
||||
MenuWrapper* assetDeveloperMenu = developerMenu->addMenu("Assets");
|
||||
|
@ -498,6 +492,12 @@ Menu::Menu() {
|
|||
MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
|
||||
addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false);
|
||||
|
||||
// Developer > Entities >>>
|
||||
MenuWrapper* entitiesOptionsMenu = developerMenu->addMenu("Entities");
|
||||
addActionToQMenuAndActionHash(entitiesOptionsMenu, MenuOption::OctreeStats, 0,
|
||||
dialogsManager.data(), SLOT(octreeStatsDetails()));
|
||||
addCheckableActionToQMenuAndActionHash(entitiesOptionsMenu, MenuOption::ShowRealtimeEntityStats);
|
||||
|
||||
// Developer > Network >>>
|
||||
MenuWrapper* networkMenu = developerMenu->addMenu("Network");
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()));
|
||||
|
@ -514,12 +514,14 @@ Menu::Menu() {
|
|||
dialogsManager.data(), SLOT(cachesSizeDialog()));
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0,
|
||||
dialogsManager.data(), SLOT(toggleDiskCacheEditor()));
|
||||
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ShowDSConnectTable, 0,
|
||||
dialogsManager.data(), SLOT(showDomainConnectionDialog()));
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::BandwidthDetails, 0,
|
||||
dialogsManager.data(), SLOT(bandwidthDetails()));
|
||||
|
||||
// Developer > Timing and Stats >>>
|
||||
MenuWrapper* timingMenu = developerMenu->addMenu("Timing and Stats");
|
||||
|
||||
// Developer > Timing >>>
|
||||
MenuWrapper* timingMenu = developerMenu->addMenu("Timing");
|
||||
MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true);
|
||||
|
@ -534,7 +536,6 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::LogExtraTimings);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::ShowRealtimeEntityStats);
|
||||
|
||||
// Developer > Audio >>>
|
||||
MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio");
|
||||
|
@ -571,6 +572,10 @@ Menu::Menu() {
|
|||
audioScopeFramesGroup->addAction(fiftyFrames);
|
||||
}
|
||||
|
||||
// Developer > Audio > Audio Network Stats...
|
||||
addActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNetworkStats, 0,
|
||||
dialogsManager.data(), SLOT(audioStatsDetails()));
|
||||
|
||||
// Developer > Physics >>>
|
||||
MenuWrapper* physicsOptionsMenu = developerMenu->addMenu("Physics");
|
||||
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned);
|
||||
|
@ -588,22 +593,6 @@ Menu::Menu() {
|
|||
// Developer > Stats
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats);
|
||||
|
||||
// Developer > Audio Stats...
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::AudioNetworkStats, 0,
|
||||
dialogsManager.data(), SLOT(audioStatsDetails()));
|
||||
|
||||
// Developer > Bandwidth Stats...
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::BandwidthDetails, 0,
|
||||
dialogsManager.data(), SLOT(bandwidthDetails()));
|
||||
|
||||
// Developer > Entity Stats...
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::OctreeStats, 0,
|
||||
dialogsManager.data(), SLOT(octreeStatsDetails()));
|
||||
|
||||
// Developer > World Axes
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::WorldAxes);
|
||||
|
||||
|
||||
|
||||
#if 0 /// -------------- REMOVED FOR NOW --------------
|
||||
addDisabledActionAndSeparator(navigateMenu, "History");
|
||||
|
|
|
@ -156,6 +156,7 @@ namespace MenuOption {
|
|||
const QString AssetMigration = "ATP Asset Migration";
|
||||
const QString Atmosphere = "Atmosphere";
|
||||
const QString Attachments = "Attachments...";
|
||||
const QString AudioNetworkStats = "Audio Network Stats";
|
||||
const QString AudioNoiseReduction = "Audio Noise Reduction";
|
||||
const QString AudioScope = "Show Scope";
|
||||
const QString AudioScopeFiftyFrames = "Fifty";
|
||||
|
@ -163,8 +164,8 @@ namespace MenuOption {
|
|||
const QString AudioScopeFrames = "Display Frames";
|
||||
const QString AudioScopePause = "Pause Scope";
|
||||
const QString AudioScopeTwentyFrames = "Twenty";
|
||||
const QString AudioNetworkStats = "Audio Network Stats";
|
||||
const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams";
|
||||
const QString AudioTools = "Show Level Meter";
|
||||
const QString AutoMuteAudio = "Auto Mute Microphone";
|
||||
const QString AvatarReceiveStats = "Show Receive Stats";
|
||||
const QString Back = "Back";
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QApplication>
|
||||
#include <QMainWindow>
|
||||
|
@ -35,48 +36,25 @@ void ScriptEventBridge::emitScriptEvent(const QString& data) {
|
|||
emit scriptEventReceived(data);
|
||||
}
|
||||
|
||||
WebWindowClass::WebWindowClass(const QString& title, const QString& url, int width, int height, bool isToolWindow)
|
||||
: QObject(NULL),
|
||||
_eventBridge(new ScriptEventBridge(this)),
|
||||
_isToolWindow(isToolWindow) {
|
||||
WebWindowClass::WebWindowClass(const QString& title, const QString& url, int width, int height)
|
||||
: QObject(NULL), _eventBridge(new ScriptEventBridge(this)) {
|
||||
auto dialogWidget = new QDialog(qApp->getWindow(), Qt::Window);
|
||||
dialogWidget->setWindowTitle(title);
|
||||
dialogWidget->resize(width, height);
|
||||
dialogWidget->installEventFilter(this);
|
||||
connect(dialogWidget, &QDialog::finished, this, &WebWindowClass::hasClosed);
|
||||
|
||||
if (_isToolWindow) {
|
||||
ToolWindow* toolWindow = qApp->getToolWindow();
|
||||
auto layout = new QVBoxLayout(dialogWidget);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
dialogWidget->setLayout(layout);
|
||||
|
||||
auto dockWidget = new QDockWidget(title, toolWindow);
|
||||
dockWidget->setFeatures(QDockWidget::DockWidgetMovable);
|
||||
connect(dockWidget, &QDockWidget::visibilityChanged, this, &WebWindowClass::visibilityChanged);
|
||||
_webView = new QWebView(dialogWidget);
|
||||
|
||||
_webView = new QWebView(dockWidget);
|
||||
addEventBridgeToWindowObject();
|
||||
layout->addWidget(_webView);
|
||||
|
||||
dockWidget->setWidget(_webView);
|
||||
addEventBridgeToWindowObject();
|
||||
|
||||
auto titleWidget = new QWidget(dockWidget);
|
||||
dockWidget->setTitleBarWidget(titleWidget);
|
||||
|
||||
toolWindow->addDockWidget(Qt::TopDockWidgetArea, dockWidget, Qt::Horizontal);
|
||||
|
||||
_windowWidget = dockWidget;
|
||||
} else {
|
||||
auto dialogWidget = new QDialog(qApp->getWindow(), Qt::Window);
|
||||
dialogWidget->setWindowTitle(title);
|
||||
dialogWidget->resize(width, height);
|
||||
dialogWidget->installEventFilter(this);
|
||||
connect(dialogWidget, &QDialog::finished, this, &WebWindowClass::hasClosed);
|
||||
|
||||
auto layout = new QVBoxLayout(dialogWidget);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
dialogWidget->setLayout(layout);
|
||||
|
||||
_webView = new QWebView(dialogWidget);
|
||||
|
||||
layout->addWidget(_webView);
|
||||
|
||||
addEventBridgeToWindowObject();
|
||||
|
||||
_windowWidget = dialogWidget;
|
||||
}
|
||||
_windowWidget = dialogWidget;
|
||||
|
||||
auto style = QStyleFactory::create("fusion");
|
||||
if (style) {
|
||||
|
@ -121,13 +99,8 @@ void WebWindowClass::addEventBridgeToWindowObject() {
|
|||
|
||||
void WebWindowClass::setVisible(bool visible) {
|
||||
if (visible) {
|
||||
if (_isToolWindow) {
|
||||
QMetaObject::invokeMethod(
|
||||
qApp->getToolWindow(), "setVisible", Qt::AutoConnection, Q_ARG(bool, visible));
|
||||
} else {
|
||||
QMetaObject::invokeMethod(_windowWidget, "showNormal", Qt::AutoConnection);
|
||||
QMetaObject::invokeMethod(_windowWidget, "raise", Qt::AutoConnection);
|
||||
}
|
||||
QMetaObject::invokeMethod(_windowWidget, "showNormal", Qt::AutoConnection);
|
||||
QMetaObject::invokeMethod(_windowWidget, "raise", Qt::AutoConnection);
|
||||
}
|
||||
QMetaObject::invokeMethod(_windowWidget, "setVisible", Qt::AutoConnection, Q_ARG(bool, visible));
|
||||
}
|
||||
|
@ -182,13 +155,18 @@ void WebWindowClass::raise() {
|
|||
QScriptValue WebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
|
||||
WebWindowClass* retVal;
|
||||
QString file = context->argument(0).toString();
|
||||
if (context->argument(4).toBool()) {
|
||||
qWarning() << "ToolWindow views with WebWindow are no longer supported. Use OverlayWebWindow instead";
|
||||
return QScriptValue();
|
||||
} else {
|
||||
qWarning() << "WebWindow views are deprecated. Use OverlayWebWindow instead";
|
||||
}
|
||||
QMetaObject::invokeMethod(DependencyManager::get<WindowScriptingInterface>().data(), "doCreateWebWindow", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(WebWindowClass*, retVal),
|
||||
Q_ARG(const QString&, file),
|
||||
Q_ARG(QString, context->argument(1).toString()),
|
||||
Q_ARG(int, context->argument(2).toInteger()),
|
||||
Q_ARG(int, context->argument(3).toInteger()),
|
||||
Q_ARG(bool, context->argument(4).toBool()));
|
||||
Q_ARG(int, context->argument(3).toInteger()));
|
||||
|
||||
connect(engine, &QScriptEngine::destroyed, retVal, &WebWindowClass::deleteLater);
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ class WebWindowClass : public QObject {
|
|||
Q_PROPERTY(QSizeF size READ getSize WRITE setSize);
|
||||
|
||||
public:
|
||||
WebWindowClass(const QString& title, const QString& url, int width, int height, bool isToolWindow = false);
|
||||
WebWindowClass(const QString& title, const QString& url, int width, int height);
|
||||
~WebWindowClass();
|
||||
|
||||
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
|
||||
|
@ -75,7 +75,6 @@ private:
|
|||
QWidget* _windowWidget;
|
||||
QWebView* _webView;
|
||||
ScriptEventBridge* _eventBridge;
|
||||
bool _isToolWindow;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "Menu.h"
|
||||
#include "OffscreenUi.h"
|
||||
#include "ui/ModelsBrowser.h"
|
||||
#include "WebWindowClass.h"
|
||||
|
||||
#include "WindowScriptingInterface.h"
|
||||
|
||||
|
@ -37,8 +38,8 @@ WindowScriptingInterface::WindowScriptingInterface() :
|
|||
connect(qApp, &Application::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused);
|
||||
}
|
||||
|
||||
WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title, const QString& url, int width, int height, bool isToolWindow) {
|
||||
return new WebWindowClass(title, url, width, height, isToolWindow);
|
||||
WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title, const QString& url, int width, int height) {
|
||||
return new WebWindowClass(title, url, width, height);
|
||||
}
|
||||
|
||||
QScriptValue WindowScriptingInterface::hasFocus() {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include <QComboBox>
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "WebWindowClass.h"
|
||||
class WebWindowClass;
|
||||
|
||||
class WindowScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
@ -82,8 +82,8 @@ private slots:
|
|||
void nonBlockingFormAccepted() { _nonBlockingFormActive = false; _formResult = QDialog::Accepted; emit nonBlockingFormClosed(); }
|
||||
void nonBlockingFormRejected() { _nonBlockingFormActive = false; _formResult = QDialog::Rejected; emit nonBlockingFormClosed(); }
|
||||
|
||||
WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height, bool isToolWindow);
|
||||
|
||||
WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height);
|
||||
|
||||
private:
|
||||
QString jsRegExp2QtRegExp(QString string);
|
||||
QDialog* createForm(const QString& title, QScriptValue form);
|
||||
|
|
|
@ -67,6 +67,7 @@ void AvatarInputs::update() {
|
|||
AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking));
|
||||
AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking));
|
||||
AI_UPDATE(isHMD, qApp->isHMDMode());
|
||||
AI_UPDATE(showAudioTools, Menu::getInstance()->isOptionChecked(MenuOption::AudioTools));
|
||||
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
const float AUDIO_METER_AVERAGING = 0.5;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
public: \
|
||||
type name() { return _##name; }; \
|
||||
private: \
|
||||
type _##name{ initialValue };
|
||||
type _##name{ initialValue };
|
||||
|
||||
class AvatarInputs : public QQuickItem {
|
||||
Q_OBJECT
|
||||
|
@ -31,6 +31,7 @@ class AvatarInputs : public QQuickItem {
|
|||
AI_PROPERTY(bool, mirrorVisible, false)
|
||||
AI_PROPERTY(bool, mirrorZoomed, true)
|
||||
AI_PROPERTY(bool, isHMD, false)
|
||||
AI_PROPERTY(bool, showAudioTools, true)
|
||||
|
||||
public:
|
||||
static AvatarInputs* getInstance();
|
||||
|
@ -46,6 +47,7 @@ signals:
|
|||
void mirrorVisibleChanged();
|
||||
void mirrorZoomedChanged();
|
||||
void isHMDChanged();
|
||||
void showAudioToolsChanged();
|
||||
|
||||
protected:
|
||||
Q_INVOKABLE void resetSensors();
|
||||
|
|
|
@ -148,8 +148,7 @@ void DialogsManager::lodTools() {
|
|||
}
|
||||
|
||||
void DialogsManager::toggleToolWindow() {
|
||||
QMainWindow* toolWindow = qApp->getToolWindow();
|
||||
toolWindow->setVisible(!toolWindow->isVisible());
|
||||
DependencyManager::get<OffscreenUi>()->toggleToolWindow();
|
||||
}
|
||||
|
||||
void DialogsManager::hmdTools(bool showTools) {
|
||||
|
|
|
@ -79,9 +79,6 @@ HMDToolsDialog::HMDToolsDialog(QWidget* parent) :
|
|||
// what screens we're allowed on
|
||||
watchWindow(windowHandle());
|
||||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
if (qApp->getToolWindow()) {
|
||||
watchWindow(qApp->getToolWindow()->windowHandle());
|
||||
}
|
||||
if (dialogsManager->getBandwidthDialog()) {
|
||||
watchWindow(dialogsManager->getBandwidthDialog()->windowHandle());
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "Snapshot.h"
|
||||
#include "UserActivityLogger.h"
|
||||
#include "UIUtil.h"
|
||||
#include "scripting/WebWindowClass.h"
|
||||
|
||||
|
||||
const int PREFERENCES_HEIGHT_PADDING = 20;
|
||||
|
@ -135,7 +136,7 @@ void PreferencesDialog::openFullAvatarModelBrowser() {
|
|||
const auto WIDTH = 900;
|
||||
const auto HEIGHT = 700;
|
||||
if (!_marketplaceWindow) {
|
||||
_marketplaceWindow = new WebWindowClass("Marketplace", MARKETPLACE_URL, WIDTH, HEIGHT, false);
|
||||
_marketplaceWindow = new WebWindowClass("Marketplace", MARKETPLACE_URL, WIDTH, HEIGHT);
|
||||
}
|
||||
_marketplaceWindow->setVisible(true);
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include <QDialog>
|
||||
#include <QString>
|
||||
|
||||
#include "scripting/WebWindowClass.h"
|
||||
class WebWindowClass;
|
||||
|
||||
class PreferencesDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
@ -41,7 +41,7 @@ private:
|
|||
|
||||
QString _displayNameString;
|
||||
|
||||
WebWindowClass* _marketplaceWindow = NULL;
|
||||
WebWindowClass* _marketplaceWindow { nullptr };
|
||||
|
||||
private slots:
|
||||
void accept();
|
||||
|
|
|
@ -1,145 +0,0 @@
|
|||
//
|
||||
// ToolWindow.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Ryan Huffman on 11/13/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 "Application.h"
|
||||
#include "MainWindow.h"
|
||||
#include "ToolWindow.h"
|
||||
#include "UIUtil.h"
|
||||
|
||||
const int DEFAULT_WIDTH = 300;
|
||||
|
||||
ToolWindow::ToolWindow(QWidget* parent) :
|
||||
QMainWindow(parent),
|
||||
_selfHidden(false),
|
||||
_hasShown(false),
|
||||
_lastGeometry() {
|
||||
|
||||
setTabPosition(Qt::TopDockWidgetArea, QTabWidget::TabPosition::North);
|
||||
|
||||
# ifndef Q_OS_LINUX
|
||||
setDockOptions(QMainWindow::ForceTabbedDocks);
|
||||
# endif
|
||||
qApp->installEventFilter(this);
|
||||
}
|
||||
|
||||
bool ToolWindow::event(QEvent* event) {
|
||||
QEvent::Type type = event->type();
|
||||
if (type == QEvent::Show) {
|
||||
|
||||
if (!_hasShown) {
|
||||
_hasShown = true;
|
||||
|
||||
QMainWindow* mainWindow = qApp->getWindow();
|
||||
QRect mainGeometry = mainWindow->geometry();
|
||||
|
||||
int titleBarHeight = UIUtil::getWindowTitleBarHeight(this);
|
||||
int topMargin = titleBarHeight;
|
||||
|
||||
_lastGeometry = QRect(mainGeometry.topLeft().x(), mainGeometry.topLeft().y() + topMargin,
|
||||
DEFAULT_WIDTH, mainGeometry.height() - topMargin);
|
||||
}
|
||||
|
||||
setGeometry(_lastGeometry);
|
||||
|
||||
return true;
|
||||
} else if (type == QEvent::Hide) {
|
||||
_lastGeometry = geometry();
|
||||
return true;
|
||||
}
|
||||
|
||||
return QMainWindow::event(event);
|
||||
}
|
||||
|
||||
bool ToolWindow::eventFilter(QObject* sender, QEvent* event) {
|
||||
# ifndef Q_OS_LINUX
|
||||
switch (event->type()) {
|
||||
case QEvent::WindowStateChange:
|
||||
if (qApp->getWindow()->isMinimized()) {
|
||||
// If we are already visible, we are self-hiding
|
||||
_selfHidden = isVisible();
|
||||
setVisible(false);
|
||||
} else {
|
||||
if (_selfHidden) {
|
||||
setVisible(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case QEvent::ApplicationDeactivate:
|
||||
_selfHidden = isVisible();
|
||||
setVisible(false);
|
||||
break;
|
||||
case QEvent::ApplicationActivate:
|
||||
if (_selfHidden) {
|
||||
setVisible(true);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
# endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void ToolWindow::onChildVisibilityUpdated(bool visible) {
|
||||
if (!_selfHidden && visible) {
|
||||
setVisible(true);
|
||||
} else {
|
||||
bool hasVisible = false;
|
||||
QList<QDockWidget*> dockWidgets = findChildren<QDockWidget*>();
|
||||
for (int i = 0; i < dockWidgets.count(); i++) {
|
||||
if (dockWidgets[i]->isVisible()) {
|
||||
hasVisible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If a child was hidden and we don't have any children still visible, hide ourself.
|
||||
if (!hasVisible) {
|
||||
setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget) {
|
||||
QList<QDockWidget*> dockWidgets = findChildren<QDockWidget*>();
|
||||
|
||||
QMainWindow::addDockWidget(area, dockWidget);
|
||||
|
||||
// We want to force tabbing, so retabify all of our widgets.
|
||||
QDockWidget* lastDockWidget = dockWidget;
|
||||
|
||||
foreach (QDockWidget* nextDockWidget, dockWidgets) {
|
||||
tabifyDockWidget(lastDockWidget, nextDockWidget);
|
||||
lastDockWidget = nextDockWidget;
|
||||
}
|
||||
|
||||
connect(dockWidget, &QDockWidget::visibilityChanged, this, &ToolWindow::onChildVisibilityUpdated);
|
||||
}
|
||||
|
||||
void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget, Qt::Orientation orientation) {
|
||||
QList<QDockWidget*> dockWidgets = findChildren<QDockWidget*>();
|
||||
|
||||
QMainWindow::addDockWidget(area, dockWidget, orientation);
|
||||
|
||||
QDockWidget* lastDockWidget = dockWidget;
|
||||
|
||||
foreach(QDockWidget* nextDockWidget, dockWidgets) {
|
||||
tabifyDockWidget(lastDockWidget, nextDockWidget);
|
||||
lastDockWidget = nextDockWidget;
|
||||
}
|
||||
|
||||
connect(dockWidget, &QDockWidget::visibilityChanged, this, &ToolWindow::onChildVisibilityUpdated);
|
||||
}
|
||||
|
||||
void ToolWindow::removeDockWidget(QDockWidget* dockWidget) {
|
||||
QMainWindow::removeDockWidget(dockWidget);
|
||||
|
||||
disconnect(dockWidget, &QDockWidget::visibilityChanged, this, &ToolWindow::onChildVisibilityUpdated);
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
//
|
||||
// ToolWindow.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Ryan Huffman on 11/13/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_ToolWindow_h
|
||||
#define hifi_ToolWindow_h
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QEvent>
|
||||
#include <QMainWindow>
|
||||
#include <QRect>
|
||||
#include <QWidget>
|
||||
|
||||
class ToolWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ToolWindow(QWidget* parent = NULL);
|
||||
|
||||
virtual bool event(QEvent* event);
|
||||
virtual void addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget);
|
||||
virtual void addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget, Qt::Orientation orientation);
|
||||
virtual void removeDockWidget(QDockWidget* dockWidget);
|
||||
|
||||
virtual bool eventFilter(QObject* sender, QEvent* event);
|
||||
|
||||
public slots:
|
||||
void onChildVisibilityUpdated(bool visible);
|
||||
|
||||
|
||||
private:
|
||||
// Indicates whether this window was hidden by itself (because the main window lost focus).
|
||||
bool _selfHidden;
|
||||
bool _hasShown;
|
||||
QRect _lastGeometry;
|
||||
};
|
||||
|
||||
#endif // hifi_ToolWindow_h
|
|
@ -340,12 +340,25 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
|
|||
_qmlComponent = new QQmlComponent(_qmlEngine);
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::resize(const QSize& newSize) {
|
||||
void OffscreenQmlSurface::resize(const QSize& newSize_) {
|
||||
|
||||
if (!_renderer || !_renderer->_quickWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
const float MAX_OFFSCREEN_DIMENSION = 4096;
|
||||
QSize newSize = newSize_;
|
||||
|
||||
if (newSize.width() > MAX_OFFSCREEN_DIMENSION || newSize.height() > MAX_OFFSCREEN_DIMENSION) {
|
||||
float scale = std::min(
|
||||
((float)newSize.width() / MAX_OFFSCREEN_DIMENSION),
|
||||
((float)newSize.height() / MAX_OFFSCREEN_DIMENSION));
|
||||
newSize = QSize(
|
||||
std::max(static_cast<int>(scale * newSize.width()), 10),
|
||||
std::max(static_cast<int>(scale * newSize.height()), 10));
|
||||
}
|
||||
|
||||
|
||||
|
||||
QSize currentSize = _renderer->_quickWindow->geometry().size();
|
||||
if (newSize == currentSize) {
|
||||
|
|
|
@ -21,10 +21,6 @@
|
|||
#include "MessageDialog.h"
|
||||
|
||||
// Needs to match the constants in resources/qml/Global.js
|
||||
static const QString OFFSCREEN_ROOT_OBJECT_NAME = "desktopRoot";
|
||||
static const QString OFFSCREEN_WINDOW_OBJECT_NAME = "topLevelWindow";
|
||||
static QQuickItem* _desktop { nullptr };
|
||||
|
||||
class OffscreenFlags : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool navigationFocused READ isNavigationFocused WRITE setNavigationFocused NOTIFY navigationFocusedChanged)
|
||||
|
@ -252,6 +248,48 @@ void OffscreenUi::createDesktop() {
|
|||
_desktop = dynamic_cast<QQuickItem*>(load("Root.qml"));
|
||||
Q_ASSERT(_desktop);
|
||||
getRootContext()->setContextProperty("Desktop", _desktop);
|
||||
_toolWindow = _desktop->findChild<QQuickItem*>("ToolWindow");
|
||||
}
|
||||
|
||||
void OffscreenUi::toggleToolWindow() {
|
||||
_toolWindow->setEnabled(!_toolWindow->isEnabled());
|
||||
}
|
||||
|
||||
QQuickItem* OffscreenUi::getDesktop() {
|
||||
return _desktop;
|
||||
}
|
||||
|
||||
QQuickItem* OffscreenUi::getToolWindow() {
|
||||
return _toolWindow;
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(std::function<void()>);
|
||||
static auto VoidLambdaType = qRegisterMetaType<std::function<void()>>();
|
||||
Q_DECLARE_METATYPE(std::function<QVariant()>);
|
||||
static auto VariantLambdaType = qRegisterMetaType<std::function<QVariant()>>();
|
||||
|
||||
|
||||
void OffscreenUi::executeOnUiThread(std::function<void()> function) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "executeOnUiThread", Qt::QueuedConnection,
|
||||
Q_ARG(std::function<void()>, function));
|
||||
return;
|
||||
}
|
||||
|
||||
function();
|
||||
}
|
||||
|
||||
QVariant OffscreenUi::returnFromUiThread(std::function<QVariant()> function) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QVariant result;
|
||||
QMetaObject::invokeMethod(this, "returnFromUiThread", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QVariant, result),
|
||||
Q_ARG(std::function<QVariant()>, function));
|
||||
return result;
|
||||
}
|
||||
|
||||
return function();
|
||||
}
|
||||
|
||||
|
||||
#include "OffscreenUi.moc"
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#ifndef hifi_OffscreenUi_h
|
||||
#define hifi_OffscreenUi_h
|
||||
|
||||
#include <QtCore/QVariant>
|
||||
#include <gl/OffscreenQmlSurface.h>
|
||||
|
||||
#include <QMessageBox>
|
||||
|
@ -33,6 +34,12 @@ public:
|
|||
bool navigationFocused();
|
||||
void setNavigationFocused(bool focused);
|
||||
|
||||
QQuickItem* getDesktop();
|
||||
QQuickItem* getToolWindow();
|
||||
|
||||
Q_INVOKABLE void executeOnUiThread(std::function<void()> function);
|
||||
Q_INVOKABLE QVariant returnFromUiThread(std::function<QVariant()> function);
|
||||
|
||||
// Messagebox replacement functions
|
||||
using ButtonCallback = std::function<void(QMessageBox::StandardButton)>;
|
||||
static ButtonCallback NO_OP_CALLBACK;
|
||||
|
@ -70,6 +77,11 @@ public:
|
|||
QMessageBox::StandardButtons buttons = QMessageBox::Ok);
|
||||
|
||||
static void error(const QString& text); // Interim dialog in new style
|
||||
|
||||
void toggleToolWindow();
|
||||
private:
|
||||
QQuickItem* _desktop { nullptr };
|
||||
QQuickItem* _toolWindow { nullptr };
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -31,25 +31,15 @@ static const char* const URL_PROPERTY = "source";
|
|||
// Method called by Qt scripts to create a new web window in the overlay
|
||||
QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
|
||||
return QmlWindowClass::internalConstructor("QmlWebWindow.qml", context, engine,
|
||||
[&](QQmlContext* context, QObject* object) { return new QmlWebWindowClass(object); });
|
||||
[&](QObject* object) { return new QmlWebWindowClass(object); });
|
||||
}
|
||||
|
||||
QmlWebWindowClass::QmlWebWindowClass(QObject* qmlWindow) : QmlWindowClass(qmlWindow) {
|
||||
QObject::connect(_qmlWindow, SIGNAL(navigating(QString)), this, SLOT(handleNavigation(QString)));
|
||||
}
|
||||
|
||||
void QmlWebWindowClass::handleNavigation(const QString& url) {
|
||||
bool handled = false;
|
||||
static auto handler = dynamic_cast<AbstractUriHandler*>(qApp);
|
||||
if (handler) {
|
||||
if (handler->canAcceptURL(url)) {
|
||||
handled = handler->acceptURL(url);
|
||||
}
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
QMetaObject::invokeMethod(_qmlWindow, "stop", Qt::AutoConnection);
|
||||
}
|
||||
// FIXME remove.
|
||||
void QmlWebWindowClass::handleNavigation(const QString& url) {
|
||||
}
|
||||
|
||||
QString QmlWebWindowClass::getURL() const {
|
||||
|
|
|
@ -35,6 +35,7 @@ static const char* const TITLE_PROPERTY = "title";
|
|||
static const char* const WIDTH_PROPERTY = "width";
|
||||
static const char* const HEIGHT_PROPERTY = "height";
|
||||
static const char* const VISIBILE_PROPERTY = "visible";
|
||||
static const char* const TOOLWINDOW_PROPERTY = "toolWindow";
|
||||
|
||||
void QmlScriptEventBridge::emitWebEvent(const QString& data) {
|
||||
QMetaObject::invokeMethod(this, "webEventReceived", Qt::QueuedConnection, Q_ARG(QString, data));
|
||||
|
@ -86,13 +87,14 @@ void QmlWindowClass::setupServer() {
|
|||
|
||||
QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
||||
QScriptContext* context, QScriptEngine* engine,
|
||||
std::function<QmlWindowClass*(QQmlContext*, QObject*)> function)
|
||||
std::function<QmlWindowClass*(QObject*)> builder)
|
||||
{
|
||||
const auto argumentCount = context->argumentCount();
|
||||
QString url;
|
||||
QString title;
|
||||
int width = -1, height = -1;
|
||||
bool visible = true;
|
||||
bool toolWindow = false;
|
||||
if (argumentCount > 1) {
|
||||
|
||||
if (!context->argument(0).isUndefined()) {
|
||||
|
@ -107,6 +109,9 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
|||
if (context->argument(3).isNumber()) {
|
||||
height = context->argument(3).toInt32();
|
||||
}
|
||||
if (context->argument(4).isBool()) {
|
||||
toolWindow = context->argument(4).toBool();
|
||||
}
|
||||
} else {
|
||||
auto argumentObject = context->argument(0);
|
||||
qDebug() << argumentObject.toString();
|
||||
|
@ -125,6 +130,9 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
|||
if (argumentObject.property(VISIBILE_PROPERTY).isBool()) {
|
||||
visible = argumentObject.property(VISIBILE_PROPERTY).toBool();
|
||||
}
|
||||
if (argumentObject.property(TOOLWINDOW_PROPERTY).isBool()) {
|
||||
toolWindow = argumentObject.property(TOOLWINDOW_PROPERTY).toBool();
|
||||
}
|
||||
}
|
||||
|
||||
if (!url.startsWith("http") && !url.startsWith("file://") && !url.startsWith("about:")) {
|
||||
|
@ -137,17 +145,44 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
|||
}
|
||||
|
||||
QmlWindowClass* retVal{ nullptr };
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
qDebug() << "Clearing component cache";
|
||||
offscreenUi->getRootContext()->engine()->clearComponentCache();
|
||||
|
||||
// Build the event bridge and wrapper on the main thread
|
||||
QMetaObject::invokeMethod(offscreenUi.data(), "load", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(const QString&, qmlSource),
|
||||
Q_ARG(std::function<void(QQmlContext*, QObject*)>, [&](QQmlContext* context, QObject* object) {
|
||||
if (toolWindow) {
|
||||
auto toolWindow = offscreenUi->getToolWindow();
|
||||
QVariantMap properties;
|
||||
properties.insert(TITLE_PROPERTY, title);
|
||||
properties.insert(SOURCE_PROPERTY, url);
|
||||
if (width != -1 && height != -1) {
|
||||
properties.insert(WIDTH_PROPERTY, width);
|
||||
properties.insert(HEIGHT_PROPERTY, height);
|
||||
}
|
||||
|
||||
// Build the event bridge and wrapper on the main thread
|
||||
QVariant newTabVar;
|
||||
bool invokeResult = QMetaObject::invokeMethod(toolWindow, "addWebTab", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QVariant, newTabVar),
|
||||
Q_ARG(QVariant, QVariant::fromValue(properties)));
|
||||
|
||||
QQuickItem* newTab = qvariant_cast<QQuickItem*>(newTabVar);
|
||||
if (!invokeResult || !newTab) {
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
offscreenUi->returnFromUiThread([&] {
|
||||
setupServer();
|
||||
retVal = function(context, object);
|
||||
retVal = builder(newTab);
|
||||
retVal->_toolWindow = true;
|
||||
offscreenUi->getRootContext()->engine()->setObjectOwnership(retVal->_qmlWindow, QQmlEngine::CppOwnership);
|
||||
registerObject(url.toLower(), retVal);
|
||||
return QVariant();
|
||||
});
|
||||
} else {
|
||||
// Build the event bridge and wrapper on the main thread
|
||||
QMetaObject::invokeMethod(offscreenUi.data(), "load", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(const QString&, qmlSource),
|
||||
Q_ARG(std::function<void(QQmlContext*, QObject*)>, [&](QQmlContext* context, QObject* object) {
|
||||
setupServer();
|
||||
retVal = builder(object);
|
||||
context->engine()->setObjectOwnership(retVal->_qmlWindow, QQmlEngine::CppOwnership);
|
||||
registerObject(url.toLower(), retVal);
|
||||
if (!title.isEmpty()) {
|
||||
|
@ -158,9 +193,12 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
|||
}
|
||||
object->setProperty(SOURCE_PROPERTY, url);
|
||||
if (visible) {
|
||||
object->setProperty("enabled", true);
|
||||
object->setProperty("visible", true);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
retVal->_source = url;
|
||||
connect(engine, &QScriptEngine::destroyed, retVal, &QmlWindowClass::deleteLater);
|
||||
return engine->newQObject(retVal);
|
||||
}
|
||||
|
@ -168,7 +206,7 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
|||
|
||||
// Method called by Qt scripts to create a new web window in the overlay
|
||||
QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
|
||||
return internalConstructor("QmlWindow.qml", context, engine, [&](QQmlContext* context, QObject* object){
|
||||
return internalConstructor("QmlWindow.qml", context, engine, [&](QObject* object){
|
||||
return new QmlWindowClass(object);
|
||||
});
|
||||
}
|
||||
|
@ -181,6 +219,21 @@ QmlWindowClass::QmlWindowClass(QObject* qmlWindow)
|
|||
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow));
|
||||
}
|
||||
|
||||
QmlWindowClass::~QmlWindowClass() {
|
||||
if (_qmlWindow) {
|
||||
if (_toolWindow) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto toolWindow = offscreenUi->getToolWindow();
|
||||
auto invokeResult = QMetaObject::invokeMethod(toolWindow, "removeTabForUrl", Qt::QueuedConnection,
|
||||
Q_ARG(QVariant, _source));
|
||||
Q_ASSERT(invokeResult);
|
||||
} else {
|
||||
_qmlWindow->deleteLater();
|
||||
}
|
||||
_qmlWindow = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void QmlWindowClass::registerObject(const QString& name, QObject* object) {
|
||||
webChannel.registerObject(name, object);
|
||||
}
|
||||
|
@ -189,74 +242,85 @@ void QmlWindowClass::deregisterObject(QObject* object) {
|
|||
webChannel.deregisterObject(object);
|
||||
}
|
||||
|
||||
void QmlWindowClass::setVisible(bool visible) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setVisible", Qt::AutoConnection, Q_ARG(bool, visible));
|
||||
return;
|
||||
}
|
||||
|
||||
auto qmlWindow = asQuickItem();
|
||||
if (qmlWindow->isEnabled() != visible) {
|
||||
qmlWindow->setEnabled(visible);
|
||||
emit visibilityChanged(visible);
|
||||
}
|
||||
}
|
||||
|
||||
QQuickItem* QmlWindowClass::asQuickItem() const {
|
||||
if (_toolWindow) {
|
||||
return DependencyManager::get<OffscreenUi>()->getToolWindow();
|
||||
}
|
||||
return dynamic_cast<QQuickItem*>(_qmlWindow);
|
||||
}
|
||||
|
||||
bool QmlWindowClass::isVisible() const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result;
|
||||
QMetaObject::invokeMethod(const_cast<QmlWindowClass*>(this), "isVisible", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result));
|
||||
return result;
|
||||
void QmlWindowClass::setVisible(bool visible) {
|
||||
// For tool window tabs we special case visiblility as enable / disable of the tab, not the window
|
||||
// The tool window itself has special logic based on whether any tabs are enabled
|
||||
if (_toolWindow) {
|
||||
auto targetTab = dynamic_cast<QQuickItem*>(_qmlWindow);
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||
targetTab->setEnabled(visible);
|
||||
//emit visibilityChanged(visible);
|
||||
});
|
||||
} else {
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||
targetWindow->setVisible(visible);
|
||||
//emit visibilityChanged(visible);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return asQuickItem()->isEnabled();
|
||||
bool QmlWindowClass::isVisible() const {
|
||||
// The tool window itself has special logic based on whether any tabs are enabled
|
||||
if (_toolWindow) {
|
||||
auto targetTab = dynamic_cast<QQuickItem*>(_qmlWindow);
|
||||
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] {
|
||||
return QVariant::fromValue(targetTab->isEnabled());
|
||||
}).toBool();
|
||||
} else {
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] {
|
||||
return QVariant::fromValue(targetWindow->isVisible());
|
||||
}).toBool();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
glm::vec2 QmlWindowClass::getPosition() const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
glm::vec2 result;
|
||||
QMetaObject::invokeMethod(const_cast<QmlWindowClass*>(this), "getPosition", Qt::BlockingQueuedConnection, Q_RETURN_ARG(glm::vec2, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
return glm::vec2(asQuickItem()->x(), asQuickItem()->y());
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
||||
return targetWindow->position();
|
||||
});
|
||||
return toGlm(result.toPointF());
|
||||
}
|
||||
|
||||
|
||||
void QmlWindowClass::setPosition(const glm::vec2& position) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setPosition", Qt::QueuedConnection, Q_ARG(glm::vec2, position));
|
||||
return;
|
||||
}
|
||||
|
||||
asQuickItem()->setPosition(QPointF(position.x, position.y));
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||
targetWindow->setPosition(QPointF(position.x, position.y));
|
||||
});
|
||||
}
|
||||
|
||||
void QmlWindowClass::setPosition(int x, int y) {
|
||||
setPosition(glm::vec2(x, y));
|
||||
}
|
||||
|
||||
// FIXME move to GLM helpers
|
||||
glm::vec2 toGlm(const QSizeF& size) {
|
||||
return glm::vec2(size.width(), size.height());
|
||||
}
|
||||
|
||||
glm::vec2 QmlWindowClass::getSize() const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
glm::vec2 result;
|
||||
QMetaObject::invokeMethod(const_cast<QmlWindowClass*>(this), "getSize", Qt::BlockingQueuedConnection, Q_RETURN_ARG(glm::vec2, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
return glm::vec2(asQuickItem()->width(), asQuickItem()->height());
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
||||
return QSizeF(targetWindow->width(), targetWindow->height());
|
||||
});
|
||||
return toGlm(result.toSizeF());
|
||||
}
|
||||
|
||||
void QmlWindowClass::setSize(const glm::vec2& size) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setSize", Qt::QueuedConnection, Q_ARG(glm::vec2, size));
|
||||
}
|
||||
|
||||
asQuickItem()->setSize(QSizeF(size.x, size.y));
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||
targetWindow->setSize(QSizeF(size.x, size.y));
|
||||
});
|
||||
}
|
||||
|
||||
void QmlWindowClass::setSize(int width, int height) {
|
||||
|
@ -264,27 +328,28 @@ void QmlWindowClass::setSize(int width, int height) {
|
|||
}
|
||||
|
||||
void QmlWindowClass::setTitle(const QString& title) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setTitle", Qt::QueuedConnection, Q_ARG(QString, title));
|
||||
}
|
||||
|
||||
_qmlWindow->setProperty(TITLE_PROPERTY, title);
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||
targetWindow->setProperty(TITLE_PROPERTY, title);
|
||||
});
|
||||
}
|
||||
|
||||
void QmlWindowClass::close() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection);
|
||||
}
|
||||
_qmlWindow->setProperty("destroyOnInvisible", true);
|
||||
_qmlWindow->setProperty("visible", false);
|
||||
_qmlWindow->deleteLater();
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([this] {
|
||||
if (_qmlWindow) {
|
||||
_qmlWindow->setProperty("destroyOnInvisible", true);
|
||||
_qmlWindow->setProperty("visible", false);
|
||||
_qmlWindow->deleteLater();
|
||||
_qmlWindow = nullptr;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void QmlWindowClass::hasClosed() {
|
||||
}
|
||||
|
||||
void QmlWindowClass::raise() {
|
||||
QMetaObject::invokeMethod(_qmlWindow, "raiseWindow", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(asQuickItem(), "raiseWindow", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
#include "QmlWindowClass.moc"
|
||||
|
|
|
@ -51,6 +51,7 @@ class QmlWindowClass : public QObject {
|
|||
public:
|
||||
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
|
||||
QmlWindowClass(QObject* qmlWindow);
|
||||
~QmlWindowClass();
|
||||
|
||||
public slots:
|
||||
bool isVisible() const;
|
||||
|
@ -84,7 +85,7 @@ protected slots:
|
|||
protected:
|
||||
static QScriptValue internalConstructor(const QString& qmlSource,
|
||||
QScriptContext* context, QScriptEngine* engine,
|
||||
std::function<QmlWindowClass*(QQmlContext*, QObject*)> function);
|
||||
std::function<QmlWindowClass*(QObject*)> function);
|
||||
static void setupServer();
|
||||
static void registerObject(const QString& name, QObject* object);
|
||||
static void deregisterObject(QObject* object);
|
||||
|
@ -95,9 +96,10 @@ protected:
|
|||
|
||||
// FIXME needs to be initialized in the ctor once we have support
|
||||
// for tool window panes in QML
|
||||
const bool _isToolWindow { false };
|
||||
bool _toolWindow { false };
|
||||
const int _windowId;
|
||||
QObject* const _qmlWindow;
|
||||
QObject* _qmlWindow;
|
||||
QString _source;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue