From 8c47c7b9897620aaa4d45382969e623246f9e3a0 Mon Sep 17 00:00:00 2001 From: Seiji Emery Date: Tue, 11 Aug 2015 20:12:21 -0700 Subject: [PATCH] Added attachment --- examples/games/solarsystem.js | 189 +++++++++++--------------- examples/libraries/uiwidgets.js | 233 +++++++++++++++++++++++++------- 2 files changed, 265 insertions(+), 157 deletions(-) diff --git a/examples/games/solarsystem.js b/examples/games/solarsystem.js index 94e76ece08..0681ff19df 100644 --- a/examples/games/solarsystem.js +++ b/examples/games/solarsystem.js @@ -592,48 +592,89 @@ var ICON_HEIGHT = 40.0; var ICON_COLOR = UI.rgba(45, 45, 45, 0.7); var FOCUSED_COLOR = UI.rgba(250, 250, 250, 1.0); +var PANEL_BACKGROUND_COLOR = UI.rgba(50, 50, 50, 0.7); + +var PANEL_PADDING = 7.0; +var PANEL_BORDER = 12.0; var SUBPANEL_GAP = 1.0; + + + +var icons = []; function addIcon(panel, iconId) { - return panel.add(new UI.Icon({ + var icon = panel.add(new UI.Icon({ 'imageURL': ICONS_URL + iconId + '.svg', 'width': ICON_WIDTH, 'height': ICON_HEIGHT, 'color': ICON_COLOR, 'alpha': ICON_COLOR.a })); + icons.push(icon); + return icon; } -var panel = { - background: { - backgroundColor: UI.rgb(50, 50, 50), - backgroundAlpha: 0.7, - textColor: UI.rgb(10, 10, 20) - }, - padding: { x: 7, y: 7 }, - border: { x: 12, y: 12 } +var panels = []; +function addPanel (properties) { + properties.background = properties.background || {}; + properties.background.backgroundColor = properties.background.backgroundColor || + PANEL_BACKGROUND_COLOR; + properties.background.backgroundAlpha = properties.background.backgroundAlpha || + PANEL_BACKGROUND_COLOR.a; + properties.padding = properties.padding || { x: PANEL_PADDING, y: PANEL_PADDING }; + properties.border = properties.border || { x: PANEL_BORDER, y: PANEL_BORDER }; + + var panel = new UI.WidgetStack(properties); + panels.push(panel); + return panel; } // var panelContainer = new UI.WidgetContainer(); // panelContainer.setPosition(500, 250); // panelContainer.setVisible(true); -var mainPanel = new UI.WidgetStack({ - dir: '+y', - padding: panel.padding, - border: panel.border, - background: panel.background -}); -// panelContainer.add(mainPanel); +var mainPanel = addPanel({ dir: '+y' }); mainPanel.setPosition(500, 250); mainPanel.setVisible(true); - + var systemViewButton = addIcon(mainPanel, 'solarsystems'); var zoomButton = addIcon(mainPanel, 'magnifier'); var satelliteButton = addIcon(mainPanel, 'satellite'); var settingsButton = addIcon(mainPanel, 'settings'); var stopButton = addIcon(mainPanel, 'close'); -mainPanel.relayout(); + + +var systemViewPanel = addPanel({ dir: '+x', visible: false }); +var reverseButton = addIcon(systemViewPanel, 'reverse'); +var pauseButton = addIcon(systemViewPanel, 'playpause'); +var forwardButton = addIcon(systemViewPanel, 'forward'); + +var zoomPanel = addPanel({ dir: '+y', visible: true }); + +UI.updateLayout(); + +function hideSubpanels () { + systemViewPanel.setVisible(false); + zoomPanel.setVisible(false); +} + +function attachPanel (panel, button) { + button.addAction('onClick', function () { + var visible = !panel.visible; + hideSubpanels(); + panel.setVisible(visible); + UI.updateLayout(); + }) + + UI.addAttachment(panel, button, function (target, rel) { + target.setPosition( + rel.position.x - (target.cachedWidth + target.border.x + SUBPANEL_GAP), + rel.position.y - target.border.y + ); + }); +} +attachPanel(systemViewPanel, systemViewButton); +attachPanel(zoomPanel, zoomButton); var addColorToggle = function (widget) { widget.addAction('onMouseOver', function () { @@ -644,67 +685,32 @@ var addColorToggle = function (widget) { }); } -var systemViewPanel = new UI.WidgetStack({ - dir: '+x', - visible: true, - padding: panel.padding, - border: panel.border, - background: panel.background +reverseButton.addAction('onClick', function() { + +}); +pauseButton.addAction('onClick', function() { + // paused ? resume() : pause(); }); -UI.addAttachment({ - target: systemViewPanel, - rel: systemViewButton, - layout: function (target, rel) { - target.setPosition({ - x: rel.position - - }) +zoomButton.addAction('onClick', function() { + hideSubpanels(); + UI.updateLayout(); + + if (zoomButton.visible) { + MyAvatar.position = startingPosition; + Camera.setOrientation(cameraStart); + // resume(); + } else { + // pause(); } }); - -mainPanel.add({}); -// panelContainer.add(systemViewPanel); - -var reverseButton = addIcon(systemViewPanel, 'reverse'); -var pauseButton = addIcon(systemViewPanel, 'playpause'); -var forwardButton = addIcon(systemViewPanel, 'forward'); - - -var zoomPanel = new UI.WidgetStack({ - dir: '+y', - visible: false, - padding: panel.padding, - border: panel.border, - background: panel.background -}); -// panelContainer.add(zoomPanel); - - UI.updateLayout(); -function hideSubpanels () { - systemViewPanel.setVisible(false); - zoomPanel.setVisible(false); -} -function attachTogglePanel (button, panel) { - button.addAction('onClick', function () { - var visible = !panel.visible; - - hideSubpanels(); - panel.setVisible(true); - panel.relayout(); - var offset = { - 'x': -(panel.getWidth() + panel.border.x + SUBPANEL_GAP), - 'y': -panel.border.y - }; - - panel.setPosition(Vec2.sum(button.position, offset)); - panel.setVisible(false); // force dirty - panel.setVisible(visible); - UI.updateLayout(); - }) -} +stopButton.addAction('onClick', function() { + // Script.stop(); + teardown(); +}); // Panel drag behavior // (click + drag on border to drag) @@ -745,47 +751,12 @@ function makeDraggable (panel, target) { }); } -var buttons = [ systemViewButton, zoomButton, satelliteButton, settingsButton, stopButton, - reverseButton, pauseButton, forwardButton ]; -var panels = [ mainPanel, systemViewPanel, zoomPanel ]; +var buttons = icons; buttons.map(addColorToggle); -panels.map(function (panel) { makeDraggable(panel, panelContainer); }); +panels.map(function (panel) { makeDraggable(panel, mainPanel); }); -attachTogglePanel(systemViewButton, systemViewPanel); -// systemViewButton.addAction('onMouseDown', function () { - // systemViewPanel.setVisible(true); - // UI.updateLayout(); -// }); - -reverseButton.addAction('onClick', function() { - -}); -pauseButton.addAction('onClick', function() { - // paused ? resume() : pause(); -}); - -zoomButton.addAction('onClick', function() { - hideSubpanels(); - UI.updateLayout(); - - if (zoomButton.visible) { - MyAvatar.position = startingPosition; - Camera.setOrientation(cameraStart); - // resume(); - } else { - // pause(); - } -}); -attachTogglePanel(zoomButton, zoomPanel); -UI.updateLayout(); - - -stopButton.addAction('onClick', function() { - // Script.stop(); - teardown(); -}); // Script.include('../utilities/tools/cookies.js'); diff --git a/examples/libraries/uiwidgets.js b/examples/libraries/uiwidgets.js index 129675b02c..0e53e9fb18 100644 --- a/examples/libraries/uiwidgets.js +++ b/examples/libraries/uiwidgets.js @@ -157,7 +157,7 @@ UI.setDefaultVisibility = function (visible) { // Hide overlays impl function makeOverlay(type, properties) { - var _TRACE = traceEnter.call(this, "makeOverlay"); + var _TRACE = traceEnter.call(this, "makeOverlay"); var overlay = Overlays.addOverlay(type, properties); // overlay.update = function (properties) { // Overlays.editOverlay(overlay, properties); @@ -554,57 +554,188 @@ Icon.prototype.setColor = function (color) { }); } -var Attachment = UI.Attachment = function (target, impl) { - Widget.prototype.constructor.apply(this); - this.rel = { - x: target.x - this.position.x, - y: target.y - this.position.y - }; -} -Attachment.prototype.setDirty = function (layout) { - this._dirty = true; - this.target.setDirty(); -} -Attachment.prototype.relayout = function (layout) { - targetPos = Vec2.sum(this.position, this.target.position); -} - -// UI.updateLayout = function () { -// var touched = {}; - -// function recalcDimensions(widget) { -// if (widget.parent && !touched[widget.parent.id]) -// recalcDimensions(widget.parent); -// touched[widget.id] = true; -// widget.cachedWidth = widget.calcWidth(); -// widget.cachedHeight = widget.calcHeight(); -// } -// widgetList.forEach(function (widget) { -// if (!touched[widget.id]) { -// recalcDimensions(widget); -// } -// }); +// var Attachment = UI.Attachment = function (target, impl) { +// Widget.prototype.constructor.apply(this); +// this.rel = { +// x: target.x - this.position.x, +// y: target.y - this.position.y +// }; // } +// Attachment.prototype.setDirty = function (layout) { +// this._dirty = true; +// this.target.setDirty(); +// } +// Attachment.prototype.relayout = function (layout) { +// targetPos = Vec2.sum(this.position, this.target.position); +// } + + +// New layout functions +Widget.prototype.calcWidth = function () { + return 0; +}; +Widget.prototype.calcHeight = function () { + return 0; +}; +Widget.prototype.applyLayout = function () {}; +Widget.prototype.updateOverlays = function () {}; + + +Icon.prototype.calcWidth = function () { + return this.width; +} +Icon.prototype.calcHeight = function () { + return this.height; +} +Icon.prototype.updateOverlays = function () { + this.iconOverlay.update({ + width: this.width, + height: this.height, + x: this.position.x, + y: this.position.y, + visible: this.isVisible() + }); +} + +var sumOf = function (list, f) { + var sum = 0.0; + list.forEach(function (elem) { + sum += f(elem); + }) + return sum; +} + +WidgetStack.prototype.calcWidth = function () { + var totalWidth = 0.0, maxWidth = 0.0; + this.widgets.forEach(function (widget) { + totalWidth += widget.calcWidth() + this.padding.x; + maxWidth = Math.max(maxWidth, widget.calcWidth()); + }, this); + return this.border.x * 2 + + Math.max(totalWidth * this.layoutDir.x - this.padding.x, maxWidth); +} +WidgetStack.prototype.calcHeight = function () { + var totalHeight = 0.0, maxHeight = 0.0; + this.widgets.forEach(function (widget) { + totalHeight += widget.calcHeight() + this.padding.y; + maxHeight = Math.max(maxHeight, widget.calcHeight()); + }, this); + return this.border.y * 2 + + Math.max(totalHeight * this.layoutDir.y - this.padding.y, maxHeight); +} +WidgetStack.prototype.applyLayout = function () { + print("Applying layout " + this); + var x = this.position.x + this.border.x; + var y = this.position.y + this.border.y; + + this.widgets.forEach(function (widget) { + widget.setPosition(x, y); + print("setting position for " + widget + ": " + x + ", " + y) + x += (widget.cachedWidth + this.padding.x) * this.layoutDir.x; + y += (widget.cachedHeight + this.padding.y) * this.layoutDir.y; + widget._parentVisible = this.isVisible(); + }, this); +} +WidgetStack.prototype.updateOverlays = function () { + this.backgroundOverlay.update({ + width: this.cachedWidth, + height: this.cachedHeight, + x: this.position.x, + y: this.position.y, + visible: this.isVisible() + }); +} +UI.addAttachment = function (target, rel, update) { + attachment = { + target: target, + rel: rel, + applyLayout: update + }; + ui.attachmentList.push(attachment); + return attachment; +} + + +UI.updateLayout = function () { + // Recalc dimensions + var touched = {}; + function recalcDimensions(widget) { + if (widget.parent && !touched[widget.parent.id]) + recalcDimensions(widget.parent); + touched[widget.id] = true; + widget.cachedWidth = widget.calcWidth(); + widget.cachedHeight = widget.calcHeight(); + } + ui.widgetList.forEach(function (widget) { + if (!touched[widget.id]) { + recalcDimensions(widget); + } + }); + + function insertAndPush (list, index, elem) { + if (list[index]) + list[index].push(elem); + else + list[index] = [ elem ]; + } + + // Generate attachment lookup + var attachmentDeps = {}; + ui.attachmentList.forEach(function(attachment) { + insertAndPush(attachmentDeps, attachment.target.id, { + dep: attachment.rel, + eval: attachment.applyLayout + }); + }); + updated = {}; + + // Walk the widget list and relayout everything + function recalcLayout (widget) { + // Short circuit if we've already updated + if (updated[widget.id]) + return; + + // Walk up the tree + update top level first + if (widget.parent) + recalcLayout(widget.parent); + + // Resolve and apply attachment dependencies + if (attachmentDeps[widget.id]) { + attachmentDeps[widget.id].forEach(function (attachment) { + recalcLayout(attachment.dep); + attachment.eval(widget, attachment.dep); + }); + } + + widget.applyLayout(); + updated[widget.id] = true; + } + ui.widgetList.forEach(recalcLayout); + + ui.widgetList.forEach(function (widget) { + widget.updateOverlays(); + }); +} UI.setDefaultVisibility = function(visibility) { ui.defaultVisible = visibility; }; -UI.updateLayout = function () { - var _TRACE = traceEnter.call(this, "UI.updateLayout()"); - ui.widgetList.forEach(function(widget, index, array) { - assert(typeof(widget) == 'object', 'typeof(widget) == "object"'); - if (widget._dirty) { - var top = widget; - while (top.parent && top.parent._dirty) - top = top.parent; - top.relayout(); - if (widget !== top && widget._dirty) - widget.relayout(); - assert(!widget._dirty, "widget._dirty == false"); - } - }); - _TRACE.exit(); -}; +// UI.updateLayout = function () { +// var _TRACE = traceEnter.call(this, "UI.updateLayout()"); +// ui.widgetList.forEach(function(widget, index, array) { +// assert(typeof(widget) == 'object', 'typeof(widget) == "object"'); +// if (widget._dirty) { +// var top = widget; +// while (top.parent && top.parent._dirty) +// top = top.parent; +// top.relayout(); +// if (widget !== top && widget._dirty) +// widget.relayout(); +// assert(!widget._dirty, "widget._dirty == false"); +// } +// }); +// _TRACE.exit(); +// }; function dispatchEvent(actions, widget, event) { var _TRACE = traceEnter.call(this, "UI.dispatchEvent()"); @@ -615,6 +746,7 @@ function dispatchEvent(actions, widget, event) { } ui.focusedWidget = null; +ui.clickedWidget = null; // ui.selectedWidget = null; var getWidgetWithOverlay = function (overlay) { @@ -678,6 +810,7 @@ UI.handleMousePress = function (event) { print("Mouse clicked"); UI.handleMouseMove(event); if (ui.focusedWidget) { + ui.clickedWidget = ui.focusedWidget; dispatchEvent('onMouseDown', event, ui.focusedWidget); } } @@ -687,7 +820,11 @@ UI.handleMouseRelease = function (event) { UI.handleMouseMove(event); if (ui.focusedWidget) { dispatchEvent('onMouseUp', event, ui.focusedWidget); - dispatchEvent('onClick', event, ui.focusedWidget); + + if (ui.clickedWidget == ui.focusedWidget) { + dispatchEvent('onClick', event, ui.focusedWidget); + } + ui.clickedWidget = null;; } }