タブの折りたたみをアニメーションするようにした

git-svn-id: http://www.cozmixng.org/repos/piro/treestyletab/trunk@4067 599a83e7-65a4-db11-8015-0010dcdd6dc2
This commit is contained in:
piro 2009-04-07 17:14:09 +00:00
parent 192634fcb2
commit ec1fb499e3
3 changed files with 149 additions and 35 deletions

View File

@ -16,7 +16,7 @@ tabbrowser[treestyletab-allow-subtree-collapse="true"] .tabbrowser-tab[treestyle
display: -moz-box; display: -moz-box;
} }
tabbrowser[treestyletab-allow-subtree-collapse="true"] .tabbrowser-tab[treestyletab-collapsed="true"], tabbrowser[treestyletab-allow-subtree-collapse="true"] .tabbrowser-tab[treestyletab-collapsed-done="true"],
.tabbrowser-strip[collapsed="true"]+splitter:not([state="collapsed"]), .tabbrowser-strip[collapsed="true"]+splitter:not([state="collapsed"]),
tabbrowser[treestyletab-tabbar-autohide="hidden"] .tabbrowser-strip, tabbrowser[treestyletab-tabbar-autohide="hidden"] .tabbrowser-strip,
tabbrowser[treestyletab-tabbar-autohide="hidden"] .tabbrowser-strip+splitter, tabbrowser[treestyletab-tabbar-autohide="hidden"] .tabbrowser-strip+splitter,
@ -24,7 +24,8 @@ tabbrowser:not([treestyletab-tabbar-autohide="hidden"]) .treestyletab-tabbar-tog
#appcontent[ahFull="true"] .tabbrowser-strip[ahHIDE="true"]+splitter /* AutoHide */, #appcontent[ahFull="true"] .tabbrowser-strip[ahHIDE="true"]+splitter /* AutoHide */,
tabbrowser[treestyletab-tabbar-fixed="true"] .tabbrowser-strip+splitter { tabbrowser[treestyletab-tabbar-fixed="true"] .tabbrowser-strip+splitter {
visibility: collapse; visibility: collapse;
} }
tabbrowser:not([treestyletab-mode="horizontal"]) .tabbrowser-arrowscrollbox > .scrollbutton-up, tabbrowser:not([treestyletab-mode="horizontal"]) .tabbrowser-arrowscrollbox > .scrollbutton-up,
tabbrowser:not([treestyletab-mode="horizontal"]) .tabbrowser-arrowscrollbox > .scrollbutton-down-stack, tabbrowser:not([treestyletab-mode="horizontal"]) .tabbrowser-arrowscrollbox > .scrollbutton-down-stack,

View File

@ -6,6 +6,8 @@ var TreeStyleTabService = {
kINSERT_BEFORE : 'treestyletab-insert-before', kINSERT_BEFORE : 'treestyletab-insert-before',
kSUBTREE_COLLAPSED : 'treestyletab-subtree-collapsed', kSUBTREE_COLLAPSED : 'treestyletab-subtree-collapsed',
kCOLLAPSED : 'treestyletab-collapsed', kCOLLAPSED : 'treestyletab-collapsed',
kCOLLAPSED_DONE : 'treestyletab-collapsed-done',
kCOLLAPSING : 'treestyletab-collapsing',
kTWISTY_HOVER : 'treestyletab-twisty-hover', kTWISTY_HOVER : 'treestyletab-twisty-hover',
kNEST : 'treestyletab-nest', kNEST : 'treestyletab-nest',
kDROP_POSITION : 'treestyletab-drop-position', kDROP_POSITION : 'treestyletab-drop-position',
@ -93,9 +95,12 @@ var TreeStyleTabService = {
kINSERT_FISRT : 0, kINSERT_FISRT : 0,
kINSERT_LAST : 1, kINSERT_LAST : 1,
baseLebelMargin : 12, baseIndent : 12,
shouldDetectClickOnIndentSpaces : true, shouldDetectClickOnIndentSpaces : true,
animationEnabled : true, animationEnabled : true,
indentDelay : 200,
collapseDelay : 150,
NSResolver : { NSResolver : {
lookupNamespaceURI : function(aPrefix) lookupNamespaceURI : function(aPrefix)
@ -1032,13 +1037,15 @@ var TreeStyleTabService = {
this.processRestoredTabs(); this.processRestoredTabs();
this.observe(null, 'nsPref:changed', 'extensions.treestyletab.levelMargin'); this.observe(null, 'nsPref:changed', 'extensions.treestyletab.indent');
this.observe(null, 'nsPref:changed', 'extensions.treestyletab.tabbar.autoHide.mode'); this.observe(null, 'nsPref:changed', 'extensions.treestyletab.tabbar.autoHide.mode');
this.observe(null, 'nsPref:changed', 'extensions.treestyletab.clickOnIndentSpaces.enabled'); this.observe(null, 'nsPref:changed', 'extensions.treestyletab.clickOnIndentSpaces.enabled');
this.observe(null, 'nsPref:changed', 'browser.link.open_newwindow.restriction.override'); this.observe(null, 'nsPref:changed', 'browser.link.open_newwindow.restriction.override');
this.observe(null, 'nsPref:changed', 'browser.tabs.loadFolderAndReplace.override'); this.observe(null, 'nsPref:changed', 'browser.tabs.loadFolderAndReplace.override');
this.observe(null, 'nsPref:changed', 'extensions.treestyletab.tabbar.style'); this.observe(null, 'nsPref:changed', 'extensions.treestyletab.tabbar.style');
this.observe(null, 'nsPref:changed', 'extensions.treestyletab.animation.enabled'); this.observe(null, 'nsPref:changed', 'extensions.treestyletab.animation.enabled');
this.observe(null, 'nsPref:changed', 'extensions.treestyletab.animation.indent.delay');
this.observe(null, 'nsPref:changed', 'extensions.treestyletab.animation.collapse.delay');
}, },
initialized : false, initialized : false,
@ -2093,9 +2100,9 @@ catch(e) {
var value = this.getPref(aPrefName); var value = this.getPref(aPrefName);
switch (aPrefName) switch (aPrefName)
{ {
case 'extensions.treestyletab.levelMargin': case 'extensions.treestyletab.indent':
this.baseLebelMargin = value; this.baseIndent = value;
this.ObserverService.notifyObservers(null, 'TreeStyleTab:levelMarginModified', value); this.ObserverService.notifyObservers(null, 'TreeStyleTab:indentModified', value);
break; break;
case 'extensions.treestyletab.tabbar.autoHide.mode': case 'extensions.treestyletab.tabbar.autoHide.mode':
@ -2148,6 +2155,12 @@ catch(e) {
case 'extensions.treestyletab.animation.enabled': case 'extensions.treestyletab.animation.enabled':
this.animationEnabled = value; this.animationEnabled = value;
break; break;
case 'extensions.treestyletab.animation.indent.delay':
this.indentDelay = value;
break;
case 'extensions.treestyletab.animation.collapse.delay':
this.collapseDelay = value;
break;
case 'extensions.treestyletab.tabbar.style': case 'extensions.treestyletab.tabbar.style':
case 'extensions.treestyletab.tabbar.position': case 'extensions.treestyletab.tabbar.position':

View File

@ -24,13 +24,29 @@ TreeStyleTabBrowser.prototype = {
tabbarResizing : false, tabbarResizing : false,
levelMargin : -1, indent : -1,
levelMarginProp : 'margin-left', indentProp : 'margin-left',
collapseProp : 'margin-top',
positionProp : 'screenY', positionProp : 'screenY',
sizeProp : 'height', sizeProp : 'height',
invertedPositionProp : 'screenX', invertedPositionProp : 'screenX',
invertedSizeProp : 'width', invertedSizeProp : 'width',
kVERTICAL_MARGIN_RULES_PATTERN : /margin-(top|bottom):[^;]+;?/g,
kHORIZONTAL_MARGIN_RULES_PATTERN : /margin-(left|right):[^;]+;?/g,
get indentRulesRegExp()
{
return this.isVertical ?
this.kHORIZONTAL_MARGIN_RULES_PATTERN :
this.kVERTICAL_MARGIN_RULES_PATTERN ;
},
get collapseRulesRegExp()
{
return this.isVertical ?
this.kVERTICAL_MARGIN_RULES_PATTERN :
this.kHORIZONTAL_MARGIN_RULES_PATTERN ;
},
togglerSize : 0, togglerSize : 0,
sensitiveArea : 7, sensitiveArea : 7,
@ -542,7 +558,7 @@ TreeStyleTabBrowser.prototype = {
this.clearTabbarCanvas(); this.clearTabbarCanvas();
} }
this.ObserverService.addObserver(this, 'TreeStyleTab:levelMarginModified', false); this.ObserverService.addObserver(this, 'TreeStyleTab:indentModified', false);
this.ObserverService.addObserver(this, 'TreeStyleTab:collapseExpandAllSubtree', false); this.ObserverService.addObserver(this, 'TreeStyleTab:collapseExpandAllSubtree', false);
this.addPrefListener(this); this.addPrefListener(this);
}, },
@ -738,6 +754,7 @@ TreeStyleTabBrowser.prototype = {
b.removeAttribute(this.kRESIZING); b.removeAttribute(this.kRESIZING);
if (pos & this.kTABBAR_VERTICAL) { if (pos & this.kTABBAR_VERTICAL) {
this.collapseProp = 'margin-top';
this.positionProp = 'screenY'; this.positionProp = 'screenY';
this.sizeProp = 'height'; this.sizeProp = 'height';
this.invertedPositionProp = 'screenX'; this.invertedPositionProp = 'screenX';
@ -778,11 +795,11 @@ TreeStyleTabBrowser.prototype = {
b.setAttribute(this.kTABBAR_POSITION, 'right'); b.setAttribute(this.kTABBAR_POSITION, 'right');
if (this.getTreePref('tabbar.invertUI')) { if (this.getTreePref('tabbar.invertUI')) {
b.setAttribute(this.kUI_INVERTED, 'true'); b.setAttribute(this.kUI_INVERTED, 'true');
this.levelMarginProp = 'margin-right'; this.indentProp = 'margin-right';
} }
else { else {
b.removeAttribute(this.kUI_INVERTED); b.removeAttribute(this.kUI_INVERTED);
this.levelMarginProp = 'margin-left'; this.indentProp = 'margin-left';
} }
window.setTimeout(function(aWidth) { window.setTimeout(function(aWidth) {
/* in Firefox 3, the width of the rightside tab bar /* in Firefox 3, the width of the rightside tab bar
@ -800,7 +817,7 @@ TreeStyleTabBrowser.prototype = {
else { else {
b.setAttribute(this.kTABBAR_POSITION, 'left'); b.setAttribute(this.kTABBAR_POSITION, 'left');
b.removeAttribute(this.kUI_INVERTED); b.removeAttribute(this.kUI_INVERTED);
this.levelMarginProp = 'margin-left'; this.indentProp = 'margin-left';
window.setTimeout(function() { window.setTimeout(function() {
b.mTabDropIndicatorBar.setAttribute('ordinal', 1); b.mTabDropIndicatorBar.setAttribute('ordinal', 1);
b.mStrip.setAttribute('ordinal', 10); b.mStrip.setAttribute('ordinal', 10);
@ -812,6 +829,7 @@ TreeStyleTabBrowser.prototype = {
} }
} }
else { else {
this.collapseProp = 'margin-left';
this.positionProp = 'screenX'; this.positionProp = 'screenX';
this.sizeProp = 'width'; this.sizeProp = 'width';
this.invertedPositionProp = 'screenY'; this.invertedPositionProp = 'screenY';
@ -849,7 +867,7 @@ TreeStyleTabBrowser.prototype = {
b.removeAttribute(this.kUI_INVERTED); b.removeAttribute(this.kUI_INVERTED);
if (pos == this.kTABBAR_BOTTOM) { if (pos == this.kTABBAR_BOTTOM) {
b.setAttribute(this.kTABBAR_POSITION, 'bottom'); b.setAttribute(this.kTABBAR_POSITION, 'bottom');
this.levelMarginProp = 'margin-bottom'; this.indentProp = 'margin-bottom';
window.setTimeout(function() { window.setTimeout(function() {
b.mTabDropIndicatorBar.setAttribute('ordinal', 1); b.mTabDropIndicatorBar.setAttribute('ordinal', 1);
b.mStrip.setAttribute('ordinal', 30); b.mStrip.setAttribute('ordinal', 30);
@ -860,7 +878,7 @@ TreeStyleTabBrowser.prototype = {
} }
else { else {
b.setAttribute(this.kTABBAR_POSITION, 'top'); b.setAttribute(this.kTABBAR_POSITION, 'top');
this.levelMarginProp = 'margin-top'; this.indentProp = 'margin-top';
window.setTimeout(function() { window.setTimeout(function() {
b.mTabDropIndicatorBar.setAttribute('ordinal', 1); b.mTabDropIndicatorBar.setAttribute('ordinal', 1);
b.mStrip.setAttribute('ordinal', 10); b.mStrip.setAttribute('ordinal', 10);
@ -915,7 +933,7 @@ TreeStyleTabBrowser.prototype = {
this.tabbarCanvas = null; this.tabbarCanvas = null;
} }
this.ObserverService.removeObserver(this, 'TreeStyleTab:levelMarginModified'); this.ObserverService.removeObserver(this, 'TreeStyleTab:indentModified');
this.ObserverService.removeObserver(this, 'TreeStyleTab:collapseExpandAllSubtree'); this.ObserverService.removeObserver(this, 'TreeStyleTab:collapseExpandAllSubtree');
this.removePrefListener(this); this.removePrefListener(this);
@ -937,8 +955,8 @@ TreeStyleTabBrowser.prototype = {
var b = this.mTabBrowser; var b = this.mTabBrowser;
switch (aTopic) switch (aTopic)
{ {
case 'TreeStyleTab:levelMarginModified': case 'TreeStyleTab:indentModified':
if (this.levelMargin > -1) { if (this.indent > -1) {
this.updateAllTabsIndent(); this.updateAllTabsIndent();
} }
break; break;
@ -1376,6 +1394,7 @@ TreeStyleTabBrowser.prototype = {
var b = this.mTabBrowser; var b = this.mTabBrowser;
this.stopTabIndentAnimation(tab); this.stopTabIndentAnimation(tab);
this.stopTabCollapseAnimation(tab);
this.destroyTab(tab); this.destroyTab(tab);
var closeParentBehavior = this.getTreePref('closeParentBehavior'); var closeParentBehavior = this.getTreePref('closeParentBehavior');
@ -2728,13 +2747,13 @@ TreeStyleTabBrowser.prototype = {
var b = this.mTabBrowser; var b = this.mTabBrowser;
if (!aProp) { if (!aProp) {
aProp = this.getTreePref('enableSubtreeIndent') ? this.levelMarginProp : null ; aProp = this.getTreePref('enableSubtreeIndent') ? this.indentProp : null ;
} }
var margin = this.levelMargin < 0 ? this.baseLebelMargin : this.levelMargin ; var margin = this.indent < 0 ? this.baseIndent : this.indent ;
var indent = margin * aLevel; var indent = margin * aLevel;
var multirow = this.isMultiRow(); var multirow = this.isMultiRow();
var topBottom = this.levelMarginProp.match(/top|bottom/); var topBottom = this.indentProp.match(/top|bottom/);
var innerBoxes, var innerBoxes,
j, j,
colors, colors,
@ -2772,6 +2791,7 @@ TreeStyleTabBrowser.prototype = {
{ {
this.stopTabIndentAnimation(aTab); this.stopTabIndentAnimation(aTab);
var regexp = this.indentRulesRegExp;
if ( if (
!this.animationEnabled || !this.animationEnabled ||
!aProp || !aProp ||
@ -2780,7 +2800,7 @@ TreeStyleTabBrowser.prototype = {
aTab.setAttribute( aTab.setAttribute(
'style', 'style',
aTab.getAttribute('style') aTab.getAttribute('style')
.replace(this.kMARGIN_RULES_PATTERN, '')+';'+ .replace(regexp, '')+';'+
(aProp ? aProp+':'+aIndent+'px !important;' : '' ) (aProp ? aProp+':'+aIndent+'px !important;' : '' )
); );
return; return;
@ -2788,7 +2808,7 @@ TreeStyleTabBrowser.prototype = {
var startIndent = this.getPropertyPixelValue(aTab, aProp); var startIndent = this.getPropertyPixelValue(aTab, aProp);
var delta = aIndent - startIndent; var delta = aIndent - startIndent;
var delay = 200; var delay = this.indentDelay;
var startTime = Date.now(); var startTime = Date.now();
aTab.__treestyletab__updateTabIndentTimer = window.setInterval(function(aSelf) { aTab.__treestyletab__updateTabIndentTimer = window.setInterval(function(aSelf) {
var power = Math.min(1, (Date.now() - startTime) / delay); var power = Math.min(1, (Date.now() - startTime) / delay);
@ -2798,7 +2818,7 @@ TreeStyleTabBrowser.prototype = {
aTab.setAttribute( aTab.setAttribute(
'style', 'style',
aTab.getAttribute('style') aTab.getAttribute('style')
.replace(aSelf.kMARGIN_RULES_PATTERN, '')+';'+ .replace(regexp, '')+';'+
aProp+':'+indent+'px !important;' aProp+':'+indent+'px !important;'
); );
@ -2811,15 +2831,15 @@ TreeStyleTabBrowser.prototype = {
window.clearInterval(aTab.__treestyletab__updateTabIndentTimer); window.clearInterval(aTab.__treestyletab__updateTabIndentTimer);
aTab.__treestyletab__updateTabIndentTimer = null; aTab.__treestyletab__updateTabIndentTimer = null;
}, },
kMARGIN_RULES_PATTERN : /margin(-[^:]+):[^;]+;?/g,
inheritTabIndent : function(aNewTab, aExistingTab) inheritTabIndent : function(aNewTab, aExistingTab)
{ {
var margins = (aExistingTab.getAttribute('style') || '').match(this.kMARGIN_RULES_PATTERN); var regexp = this.indentRulesRegExp;
var indents = (aExistingTab.getAttribute('style') || '').match(regexp) || [];
aNewTab.setAttribute( aNewTab.setAttribute(
'style', 'style',
aNewTab.getAttribute('style') aNewTab.getAttribute('style')
.replace(this.kMARGIN_RULES_PATTERN, '')+';'+margins.join(';') .replace(regexp, '')+';'+indents.join(';')
); );
}, },
@ -2854,24 +2874,24 @@ TreeStyleTabBrowser.prototype = {
var nest = tabs[tabs.length-1].getAttribute(this.kNEST); var nest = tabs[tabs.length-1].getAttribute(this.kNEST);
if (!nest) return; if (!nest) return;
var oldMargin = this.levelMargin; var oldIndent = this.indent;
var indent = (oldMargin < 0 ? this.baseLebelMargin : oldMargin ) * nest; var indent = (oldIndent < 0 ? this.baseIndent : oldIndent ) * nest;
var maxIndent = ( var maxIndent = (
this.getFirstTab(b).boxObject[this.invertedSizeProp] || this.getFirstTab(b).boxObject[this.invertedSizeProp] ||
b.mTabContainer.boxObject[this.invertedSizeProp] b.mTabContainer.boxObject[this.invertedSizeProp]
) * 0.33; ) * 0.33;
var marginUnit = Math.max(Math.floor(maxIndent / nest), 1); var indentUnit = Math.max(Math.floor(maxIndent / nest), 1);
if (indent > maxIndent) { if (indent > maxIndent) {
this.levelMargin = marginUnit; this.indent = indentUnit;
} }
else { else {
this.levelMargin = -1; this.indent = -1;
if ((this.baseLebelMargin * nest) > maxIndent) if ((this.baseIndent * nest) > maxIndent)
this.levelMargin = marginUnit; this.indent = indentUnit;
} }
if (oldMargin != this.levelMargin) { if (oldIndent != this.indent) {
this.updateAllTabsIndent(); this.updateAllTabsIndent();
} }
}, },
@ -2983,6 +3003,7 @@ TreeStyleTabBrowser.prototype = {
if (!aTab || !this.getParentTab(aTab)) return; if (!aTab || !this.getParentTab(aTab)) return;
this.setTabValue(aTab, this.kCOLLAPSED, aCollapse); this.setTabValue(aTab, this.kCOLLAPSED, aCollapse);
this.updateTabCollapsed(aTab, aCollapse);
var event = document.createEvent('Events'); var event = document.createEvent('Events');
event.initEvent('TreeStyleTabCollapsedStateChange', true, true); event.initEvent('TreeStyleTabCollapsedStateChange', true, true);
@ -3010,6 +3031,85 @@ TreeStyleTabBrowser.prototype = {
this.collapseExpandTab(tabs[i], aCollapse); this.collapseExpandTab(tabs[i], aCollapse);
} }
}, },
updateTabCollapsed : function(aTab, aCollapsed)
{
this.stopTabCollapseAnimation(aTab);
var regexp = this.collapseRulesRegExp;
if (!this.isVertical || !this.animationEnabled) {
aTab.setAttribute(
'style',
aTab.getAttribute('style')
.replace(regexp, '')
.replace(this.kOPACITY_RULE_REGEXP, '')
);
aTab.removeAttribute(this.kCOLLAPSING);
if (aCollapsed)
aTab.setAttribute(this.kCOLLAPSED_DONE, true);
else
aTab.removeAttribute(this.kCOLLAPSED_DONE);
return;
}
var height = this.getFirstTab(this.mTabBrowser).boxObject.height;
var startMargin = aCollapsed ? 0 : height ;
var endMargin = aCollapsed ? height : 0 ;
var deltaMargin = endMargin - startMargin;
var startOpacity = aCollapsed ? 1 : 0 ;
var endOpacity = aCollapsed ? 0 : 1 ;
var deltaOpacity = endOpacity - startOpacity;
var delay = this.collapseDelay;
var startTime = Date.now();
var collapseProp = this.collapseProp;
aTab.setAttribute(this.kCOLLAPSING, true);
aTab.setAttribute(
'style',
aTab.getAttribute('style')
.replace(regexp, '')+';'+
collapseProp+': -'+startMargin+'px !important;'+
'opacity: '+startOpacity+' !important;'
);
if (!aCollapsed) aTab.removeAttribute(this.kCOLLAPSED_DONE);
aTab.__treestyletab__updateTabCollapsedTimer = window.setInterval(function(aSelf) {
var power = Math.min(1, (Date.now() - startTime) / delay);
var powerForStyle = Math.sin(power * 90 * Math.PI / 180);
var margin = (power == 1) ?
endMargin :
startMargin + (deltaMargin * powerForStyle);
var opacity = (power == 1) ?
endOpacity :
startOpacity + (deltaOpacity * powerForStyle);
aTab.setAttribute(
'style',
aTab.getAttribute('style')
.replace(regexp, '')+';'+
collapseProp+': -'+margin+'px !important;'+
'opacity: '+opacity+' !important;'
);
if (power == 1) {
aSelf.stopTabCollapseAnimation(aTab);
aTab.removeAttribute(aSelf.kCOLLAPSING);
aTab.setAttribute(
'style',
aTab.getAttribute('style')
.replace(regexp, '')
.replace(aSelf.kOPACITY_RULE_REGEXP, '')
);
if (aCollapsed)
aTab.setAttribute(aSelf.kCOLLAPSED_DONE, true);
}
}, 10, this);
},
kOPACITY_RULE_REGEXP : /opacity\s*:[^;]+;?/,
stopTabCollapseAnimation : function(aTab)
{
if (!aTab.__treestyletab__updateTabCollapsedTimer) return;
window.clearInterval(aTab.__treestyletab__updateTabCollapsedTimer);
aTab.__treestyletab__updateTabCollapsedTimer = null;
},
collapseExpandTreesIntelligentlyFor : function(aTab) collapseExpandTreesIntelligentlyFor : function(aTab)
{ {