Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Commit

Permalink
Merge pull request #25391 from alivedise/bugzilla/1079748/hierarchy-m…
Browse files Browse the repository at this point in the history
…anager-v2

Bug 1079748 - Implement hierarchy manager, r=etienne
  • Loading branch information
alivedise committed Nov 6, 2014
2 parents 5b185fb + 2553214 commit 8ca903f
Show file tree
Hide file tree
Showing 39 changed files with 1,265 additions and 209 deletions.
3 changes: 3 additions & 0 deletions apps/system/js/app_window.js
Expand Up @@ -232,6 +232,9 @@
*/
AppWindow.prototype.setVisibleForScreenReader =
function aw_setVisibleForScreenReader(visible) {
if (!this.element) {
return;
}
this.element.setAttribute('aria-hidden', !visible);
};

Expand Down
41 changes: 39 additions & 2 deletions apps/system/js/app_window_manager.js
Expand Up @@ -17,12 +17,36 @@
var AppWindowManager = function() {};
AppWindowManager.prototype = {
DEBUG: false,
CLASS_NAME: 'AppWindowManager',
name: 'AppWindowManager',
EVENT_PREFIX: 'appwindowmanager',
continuousTransition: false,

element: document.getElementById('windows'),
screen: document.getElementById('screen'),

isActive: function() {
return !!this._activeApp;
},

setHierarchy: function(active) {
if (!this._activeApp) {
this.debug('No active app.');
return;
}
if (active) {
this.focus();
}
this._activeApp.setVisibleForScreenReader(active);
},

focus: function() {
if (!this._activeApp) {
return;
}
this.debug('focusing ' + this._activeApp.name);
this._activeApp.focus();
},

/**
* Test the app is already running.
* @param {AppConfig} app The configuration of app.
Expand All @@ -36,6 +60,15 @@
}
},

/**
* HierarchyManager will use this function to
* get the active window instance.
* @return {AppWindow|null} The active app window instance
*/
getActiveWindow: function() {
return this.getActiveApp();
},

/**
* Get active app. If active app is null, we'll return homescreen as
* default.
Expand Down Expand Up @@ -351,6 +384,7 @@
this._settingsObserveHandler[name].callback
);
}
System.request('registerHierarchy', this);
},

/**
Expand Down Expand Up @@ -400,6 +434,7 @@
}

this._settingsObserveHandler = null;
System.request('unregisterHierarchy', this);
},

handleEvent: function awm_handleEvent(evt) {
Expand Down Expand Up @@ -590,12 +625,14 @@
if (document.mozFullScreen) {
document.mozCancelFullScreen();
}
activeApp && activeApp.setVisibleForScreenReader(false);
this.broadcastMessage('sheetsgesturebegin');
break;

case 'sheets-gesture-end':
// All inactive app window instances need to be aware of this so they
// can hide the screenshot overlay. The check occurs in the AppWindow.
activeApp && activeApp.setVisibleForScreenReader(true);
this.broadcastMessage('sheetsgestureend');
break;

Expand Down Expand Up @@ -705,7 +742,7 @@

debug: function awm_debug() {
if (this.DEBUG) {
console.log('[' + this.CLASS_NAME + ']' +
console.log('[' + this.name + ']' +
'[' + System.currentTime() + ']' +
Array.slice(arguments).concat());
}
Expand Down
27 changes: 21 additions & 6 deletions apps/system/js/attention_window_manager.js
Expand Up @@ -7,18 +7,20 @@
AttentionWindowManager.prototype = {
DEBUG: false,
TRACE: false,
CLASS_NAME: 'AttentionWindowManager',
name: 'AttentionWindowManager',
_openedInstances: null,
EVENT_PREFIX: 'attentionwindowmanager',

publish: function vm_publish(eventName, detail) {
this.debug('publishing: ', eventName);
var evt = new CustomEvent(eventName, { detail: detail });
var evt = new CustomEvent(this.EVENT_PREFIX + eventName,
{ detail: detail });
window.dispatchEvent(evt);
},

debug: function aw_debug() {
if (this.DEBUG) {
console.log('[' + this.CLASS_NAME + ']' +
console.log('[' + this.name + ']' +
'[' + System.currentTime() + '] ' +
Array.slice(arguments).concat());
if (this.TRACE) {
Expand All @@ -27,10 +29,18 @@
}
},

isActive: function() {
return this.hasActiveWindow();
},

hasActiveWindow: function attwm_hasActiveWindow() {
return (this._openedInstances.size !== 0);
},

getActiveWindow: function() {
return this.getTopMostWindow();
},

getTopMostWindow: function attwm_hasActiveWindow() {
return this._topMostWindow;
},
Expand Down Expand Up @@ -67,13 +77,16 @@
window.addEventListener('lockscreen-appclosed', this);
window.addEventListener('lockscreen-appopened', this);
window.addEventListener('rocketbar-overlayopened', this);
System.request('registerHierarchy', this);
},

stop: function attwm_stop() {
this._instances = null;
this._openedInstances = null;
this.attentionIndicator.stop();
this.attentionIndicator = null;
if (this.attentionIndicator) {
this.attentionIndicator.stop();
this.attentionIndicator = null;
}
window.removeEventListener('attentioncreated', this);
window.removeEventListener('attentionterminated', this);
window.removeEventListener('attentionshow', this);
Expand All @@ -91,6 +104,7 @@
window.removeEventListener('lockscreen-appclosed', this);
window.removeEventListener('lockscreen-appopened', this);
window.removeEventListener('rocketbar-overlayopened', this);
System.request('unregisterHierarchy', this);
},

handleEvent: function attwm_handleEvent(evt) {
Expand All @@ -105,6 +119,7 @@
case 'attentionopened':
this._openedInstances.set(attention, attention);
this.updateAttentionIndicator();
this.publish('-activated');
break;

case 'attentionrequestclose':
Expand Down Expand Up @@ -149,7 +164,7 @@
}
attention.demote();
if (this._openedInstances.size === 0) {
this.publish('attention-inactive');
this.publish('-deactivated');
}
this.updateAttentionIndicator();
break;
Expand Down
4 changes: 4 additions & 0 deletions apps/system/js/core.js
Expand Up @@ -10,6 +10,10 @@
var Core = function() {
};

Core.SUB_MODULES = [
'HierarchyManager'
];

BaseModule.create(Core, {
name: 'Core',

Expand Down
177 changes: 177 additions & 0 deletions apps/system/js/hierarchy_manager.js
@@ -0,0 +1,177 @@
/* global BaseModule */
'use strict';

(function() {
var HierarchyManager = function() {};
HierarchyManager.SERVICES = [
'focus',
'registerHierarchy',
'unregisterHierarchy'
];
BaseModule.create(HierarchyManager, {
name: 'HierarchyManager',
EVENT_PREFIX: 'hierachy',
_ui_list: null,
_topMost: null,
DEBUG: false,

_start: function() {
this._ui_list = [];
},

/**
* Provide the top most window information.
* Usually used by integration tests.
* @return {AppWindow|undefined} The top most window instance.
*/
getTopMostWindow: function() {
var topMostWindowManager;
this._ui_list.some(function(module) {
if (module.getActiveWindow && module.isActive()) {
topMostWindowManager = module;
return true;
}
}, this);
return topMostWindowManager &&
topMostWindowManager.getActiveWindow() &&
topMostWindowManager.getActiveWindow().getTopMostWindow();
},

getTopMostUI: function() {
this.debug('getting top most...', this._topMost);
return this._topMost;
},

/**
* Predefined priorities per module by module name.
* @type {Array}
*/
PRIORITIES: [
'InitLogoHandler',
'AttentionWindowManager',
'SecureWindowManager',
'LockScreenWindowManager',
'UtilityTray',
'TaskManager',
'SystemDialogManager',
'Rocketbar',
'AppWindowManager'
],

_stop: function() {
this._ui_list.forEach(function(module) {
window.removeEventListener(module + 'active', this);
window.removeEventListener(module + 'inactive', this);
}, this);
this._ui_list = [];
},

updateHierarchy: function() {
if (this._ui_list.length === 0) {
this.debug('no any module watching.');
return;
}
var lastTopMost = this._topMost;
this._topMost = null;
var found = this._ui_list.some(function(module) {
if (module.isActive()) {
this.debug(module.name + ' is becoming active now.');
this._topMost = module;
return true;
}
}, this);

if (this._topMost !== lastTopMost) {
if (lastTopMost) {
this.debug('last top most is ' + lastTopMost.name);
}
if (found) {
this.debug('next top most is ' + this._topMost.name);
}
// Blur the last top most module.
lastTopMost && lastTopMost.setHierarchy &&
lastTopMost.setHierarchy(false);
// Focus the new one.
this._topMost && this._topMost.setHierarchy &&
this._topMost.setHierarchy(true);
this.publish('changed');
} else {
this.debug('top most is the same.', this._topMost ?
this._topMost.name : 'NaN');
}
},

dumpHierarchy: function() {
this._ui_list.forEach(function(module, index) {
this.debug(
'[' + index + '] (' +
this.PRIORITIES.indexOf(module.name) +')' +
module.name +
', active state = ' + module.isActive());
}, this);
},

focus: function(module) {
if (this._topMost !== module) {
return;
}
module.setHierarchy(true);
},

handleEvent: function(evt) {
this.debug('handling ' + evt.type);
this.updateHierarchy();
return false;
},

/**
* This function is used for any UI module who wants to occupy the hierachy.
*/
registerHierarchy: function(module) {
if (!module.isActive) {
return;
}
if (this._ui_list.indexOf(module) >= 0) {
return;
}

this.debug(module.name + ' is registering the hierarchy');
this._ui_list.push(module);
this.sortHierarchy();
window.addEventListener(module.EVENT_PREFIX + '-activating', this);
window.addEventListener(module.EVENT_PREFIX + '-activated', this);
window.addEventListener(module.EVENT_PREFIX + '-deactivating', this);
window.addEventListener(module.EVENT_PREFIX + '-deactivated', this);
this.updateHierarchy();
},

sortHierarchy: function() {
this.debug('before sorting...');
this.dumpHierarchy();
var self = this;
this._ui_list.sort(function(a, b) {
return (self.PRIORITIES.indexOf(b.name) <
self.PRIORITIES.indexOf(a.name));
});
this.debug('after sorting...');
this.dumpHierarchy();
},

/**
* Remove the module reference from the living UI list.
*/
unregisterHierarchy: function(module) {
var index = this._ui_list.indexOf(module);
if (index < 0) {
return;
}
this.debug(module.name + ' is unregistering the hierarchy');
var removed = this._ui_list.splice(index, 1);
this.sortHierarchy();
this.debug(removed.name);
window.removeEventListener(module.EVENT_PREFIX + '-activated', this);
window.removeEventListener(module.EVENT_PREFIX + '-deactivated', this);
this.updateHierarchy();
}
});
}());

0 comments on commit 8ca903f

Please sign in to comment.