1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/lib/sdk/ui/button/view.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,222 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +'use strict'; 1.8 + 1.9 +module.metadata = { 1.10 + 'stability': 'experimental', 1.11 + 'engines': { 1.12 + 'Firefox': '> 28' 1.13 + } 1.14 +}; 1.15 + 1.16 +const { Cu } = require('chrome'); 1.17 +const { on, off, emit } = require('../../event/core'); 1.18 + 1.19 +const { data } = require('sdk/self'); 1.20 + 1.21 +const { isObject } = require('../../lang/type'); 1.22 + 1.23 +const { getMostRecentBrowserWindow } = require('../../window/utils'); 1.24 +const { ignoreWindow } = require('../../private-browsing/utils'); 1.25 +const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {}); 1.26 +const { AREA_PANEL, AREA_NAVBAR } = CustomizableUI; 1.27 + 1.28 +const { events: viewEvents } = require('./view/events'); 1.29 + 1.30 +const XUL_NS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'; 1.31 + 1.32 +const views = new Map(); 1.33 +const customizedWindows = new WeakMap(); 1.34 + 1.35 +const buttonListener = { 1.36 + onCustomizeStart: window => { 1.37 + for (let [id, view] of views) { 1.38 + setIcon(id, window, view.icon); 1.39 + setLabel(id, window, view.label); 1.40 + } 1.41 + 1.42 + customizedWindows.set(window, true); 1.43 + }, 1.44 + onCustomizeEnd: window => { 1.45 + customizedWindows.delete(window); 1.46 + 1.47 + for (let [id, ] of views) { 1.48 + let placement = CustomizableUI.getPlacementOfWidget(id); 1.49 + 1.50 + if (placement) 1.51 + emit(viewEvents, 'data', { type: 'update', target: id, window: window }); 1.52 + } 1.53 + }, 1.54 + onWidgetAfterDOMChange: (node, nextNode, container) => { 1.55 + let { id } = node; 1.56 + let view = views.get(id); 1.57 + let window = node.ownerDocument.defaultView; 1.58 + 1.59 + if (view) { 1.60 + emit(viewEvents, 'data', { type: 'update', target: id, window: window }); 1.61 + } 1.62 + } 1.63 +}; 1.64 + 1.65 +CustomizableUI.addListener(buttonListener); 1.66 + 1.67 +require('../../system/unload').when( _ => 1.68 + CustomizableUI.removeListener(buttonListener) 1.69 +); 1.70 + 1.71 +function getNode(id, window) { 1.72 + return !views.has(id) || ignoreWindow(window) 1.73 + ? null 1.74 + : CustomizableUI.getWidget(id).forWindow(window).node 1.75 +}; 1.76 + 1.77 +function isInToolbar(id) { 1.78 + let placement = CustomizableUI.getPlacementOfWidget(id); 1.79 + 1.80 + return placement && CustomizableUI.getAreaType(placement.area) === 'toolbar'; 1.81 +} 1.82 + 1.83 + 1.84 +function getImage(icon, isInToolbar, pixelRatio) { 1.85 + let targetSize = (isInToolbar ? 18 : 32) * pixelRatio; 1.86 + let bestSize = 0; 1.87 + let image = icon; 1.88 + 1.89 + if (isObject(icon)) { 1.90 + for (let size of Object.keys(icon)) { 1.91 + size = +size; 1.92 + let offset = targetSize - size; 1.93 + 1.94 + if (offset === 0) { 1.95 + bestSize = size; 1.96 + break; 1.97 + } 1.98 + 1.99 + let delta = Math.abs(offset) - Math.abs(targetSize - bestSize); 1.100 + 1.101 + if (delta < 0) 1.102 + bestSize = size; 1.103 + } 1.104 + 1.105 + image = icon[bestSize]; 1.106 + } 1.107 + 1.108 + if (image.indexOf('./') === 0) 1.109 + return data.url(image.substr(2)); 1.110 + 1.111 + return image; 1.112 +} 1.113 + 1.114 +function nodeFor(id, window=getMostRecentBrowserWindow()) { 1.115 + return customizedWindows.has(window) ? null : getNode(id, window); 1.116 +}; 1.117 +exports.nodeFor = nodeFor; 1.118 + 1.119 +function create(options) { 1.120 + let { id, label, icon, type } = options; 1.121 + 1.122 + if (views.has(id)) 1.123 + throw new Error('The ID "' + id + '" seems already used.'); 1.124 + 1.125 + CustomizableUI.createWidget({ 1.126 + id: id, 1.127 + type: 'custom', 1.128 + removable: true, 1.129 + defaultArea: AREA_NAVBAR, 1.130 + allowedAreas: [ AREA_PANEL, AREA_NAVBAR ], 1.131 + 1.132 + onBuild: function(document) { 1.133 + let window = document.defaultView; 1.134 + 1.135 + let node = document.createElementNS(XUL_NS, 'toolbarbutton'); 1.136 + 1.137 + let image = getImage(icon, true, window.devicePixelRatio); 1.138 + 1.139 + if (ignoreWindow(window)) 1.140 + node.style.display = 'none'; 1.141 + 1.142 + node.setAttribute('id', this.id); 1.143 + node.setAttribute('class', 'toolbarbutton-1 chromeclass-toolbar-additional'); 1.144 + node.setAttribute('type', type); 1.145 + node.setAttribute('label', label); 1.146 + node.setAttribute('tooltiptext', label); 1.147 + node.setAttribute('image', image); 1.148 + node.setAttribute('sdk-button', 'true'); 1.149 + 1.150 + views.set(id, { 1.151 + area: this.currentArea, 1.152 + icon: icon, 1.153 + label: label 1.154 + }); 1.155 + 1.156 + node.addEventListener('command', function(event) { 1.157 + if (views.has(id)) { 1.158 + emit(viewEvents, 'data', { 1.159 + type: 'click', 1.160 + target: id, 1.161 + window: event.view, 1.162 + checked: node.checked 1.163 + }); 1.164 + } 1.165 + }); 1.166 + 1.167 + return node; 1.168 + } 1.169 + }); 1.170 +}; 1.171 +exports.create = create; 1.172 + 1.173 +function dispose(id) { 1.174 + if (!views.has(id)) return; 1.175 + 1.176 + views.delete(id); 1.177 + CustomizableUI.destroyWidget(id); 1.178 +} 1.179 +exports.dispose = dispose; 1.180 + 1.181 +function setIcon(id, window, icon) { 1.182 + let node = getNode(id, window); 1.183 + 1.184 + if (node) { 1.185 + icon = customizedWindows.has(window) ? views.get(id).icon : icon; 1.186 + let image = getImage(icon, isInToolbar(id), window.devicePixelRatio); 1.187 + 1.188 + node.setAttribute('image', image); 1.189 + } 1.190 +} 1.191 +exports.setIcon = setIcon; 1.192 + 1.193 +function setLabel(id, window, label) { 1.194 + let node = nodeFor(id, window); 1.195 + 1.196 + if (node) { 1.197 + node.setAttribute('label', label); 1.198 + node.setAttribute('tooltiptext', label); 1.199 + } 1.200 +} 1.201 +exports.setLabel = setLabel; 1.202 + 1.203 +function setDisabled(id, window, disabled) { 1.204 + let node = nodeFor(id, window); 1.205 + 1.206 + if (node) 1.207 + node.disabled = disabled; 1.208 +} 1.209 +exports.setDisabled = setDisabled; 1.210 + 1.211 +function setChecked(id, window, checked) { 1.212 + let node = nodeFor(id, window); 1.213 + 1.214 + if (node) 1.215 + node.checked = checked; 1.216 +} 1.217 +exports.setChecked = setChecked; 1.218 + 1.219 +function click(id) { 1.220 + let node = nodeFor(id); 1.221 + 1.222 + if (node) 1.223 + node.click(); 1.224 +} 1.225 +exports.click = click;