boxPointer: Use the anchor point to fix problems with allocations

Instead of setting the x/y position of the box pointer, which results
in a long change of workarounds for limitations of the Clutter
layout system, set the anchor point instead, which takes the
positioning out of the layout system.

The position is computed as a combination of the position computed
from the allocation and the box pointer's size, and an offset that
we tween when animating showing and hiding the box pointer.

https://bugzilla.gnome.org/show_bug.cgi?id=645744
This commit is contained in:
Owen W. Taylor 2011-03-26 16:38:57 -04:00
parent bc48bd5f4a
commit 22c22e0d7a

View File

@ -41,80 +41,80 @@ BoxPointer.prototype = {
this._border.connect('repaint', Lang.bind(this, this._drawBorder)); this._border.connect('repaint', Lang.bind(this, this._drawBorder));
this._container.add_actor(this._border); this._container.add_actor(this._border);
this.bin.raise(this._border); this.bin.raise(this._border);
this._xOffset = 0;
this._yOffset = 0;
this._xPosition = 0;
this._yPosition = 0;
}, },
show: function(animate, onComplete) { show: function(animate, onComplete) {
let x = this.actor.x;
let y = this.actor.y;
let themeNode = this.actor.get_theme_node(); let themeNode = this.actor.get_theme_node();
let rise = themeNode.get_length('-arrow-rise'); let rise = themeNode.get_length('-arrow-rise');
this.actor.opacity = 0; this.opacity = 0;
this.actor.show(); this.actor.show();
if (animate) { if (animate) {
switch (this._arrowSide) { switch (this._arrowSide) {
case St.Side.TOP: case St.Side.TOP:
this.actor.y -= rise; this.yOffset = -rise;
break; break;
case St.Side.BOTTOM: case St.Side.BOTTOM:
this.actor.y += rise; this.yOffset = rise;
break; break;
case St.Side.LEFT: case St.Side.LEFT:
this.actor.x -= rise; this.xOffset = -rise;
break; break;
case St.Side.RIGHT: case St.Side.RIGHT:
this.actor.x += rise; this.xOffset = rise;
break; break;
} }
} }
Tweener.addTween(this.actor, { opacity: 255, Tweener.addTween(this, { opacity: 255,
x: x, xOffset: 0,
y: y, yOffset: 0,
transition: "linear", transition: "linear",
onComplete: onComplete, onComplete: onComplete,
time: POPUP_ANIMATION_TIME }); time: POPUP_ANIMATION_TIME });
}, },
hide: function(animate, onComplete) { hide: function(animate, onComplete) {
let x = this.actor.x; let xOffset = 0;
let y = this.actor.y; let yOffset = 0;
let originalX = this.actor.x;
let originalY = this.actor.y;
let themeNode = this.actor.get_theme_node(); let themeNode = this.actor.get_theme_node();
let rise = themeNode.get_length('-arrow-rise'); let rise = themeNode.get_length('-arrow-rise');
if (animate) { if (animate) {
switch (this._arrowSide) { switch (this._arrowSide) {
case St.Side.TOP: case St.Side.TOP:
y += rise; yOffset = rise;
break; break;
case St.Side.BOTTOM: case St.Side.BOTTOM:
y -= rise; yOffset = -rise;
break; break;
case St.Side.LEFT: case St.Side.LEFT:
x += rise; xOffset = rise;
break; break;
case St.Side.RIGHT: case St.Side.RIGHT:
x -= rise; xOffset = -rise;
break; break;
} }
} }
Tweener.addTween(this.actor, { opacity: 0, Tweener.addTween(this, { opacity: 0,
x: x, xOffset: xOffset,
y: y, yOffset: yOffset,
transition: "linear", transition: "linear",
time: POPUP_ANIMATION_TIME, time: POPUP_ANIMATION_TIME,
onComplete: Lang.bind(this, function () { onComplete: Lang.bind(this, function () {
this.actor.hide(); this.actor.hide();
this.actor.x = originalX; this.xOffset = 0;
this.actor.y = originalY; this.yOffset = 0;
if (onComplete) if (onComplete)
onComplete(); onComplete();
}) })
}); });
}, },
_adjustAllocationForArrow: function(isWidth, alloc) { _adjustAllocationForArrow: function(isWidth, alloc) {
@ -178,17 +178,8 @@ BoxPointer.prototype = {
} }
this.bin.allocate(childBox, flags); this.bin.allocate(childBox, flags);
if (this._sourceActor) { if (this._sourceActor && this._sourceActor.mapped)
Meta.later_add (Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, this._reposition(this._sourceActor, this._gap, this._alignment);
function () {
// This won't cause a loop if _allocate() was
// called as a result of repositioning, because in
// that case _reposition() will set the same
// coordinates again, which Clutter will just
// ignore.
this._reposition(this._sourceActor, this._gap, this._alignment);
}));
}
}, },
_drawBorder: function(area) { _drawBorder: function(area) {
@ -393,9 +384,9 @@ BoxPointer.prototype = {
parent = parent.get_parent(); parent = parent.get_parent();
} }
// Actually set the position this._xPosition = Math.floor(x);
this.actor.x = Math.floor(x); this._yPosition = Math.floor(y);
this.actor.y = Math.floor(y); this._shiftActor();
}, },
// @origin: Coordinate specifying middle of the arrow, along // @origin: Coordinate specifying middle of the arrow, along
@ -406,5 +397,42 @@ BoxPointer.prototype = {
this._arrowOrigin = origin; this._arrowOrigin = origin;
this._border.queue_repaint(); this._border.queue_repaint();
} }
},
_shiftActor : function() {
// Since the position of the BoxPointer depends on the allocated size
// of the BoxPointer and the position of the source actor, trying
// to position the BoxPoiner via the x/y properties will result in
// allocation loops and warnings. Instead we do the positioning via
// the anchor point, which is independent of allocation, and leave
// x == y == 0.
this.actor.set_anchor_point(-(this._xPosition + this._xOffset),
-(this._yPosition + this._yOffset));
},
set xOffset(offset) {
this._xOffset = offset;
this._shiftActor();
},
get xOffset() {
return this._xOffset;
},
set yOffset(offset) {
this._yOffset = offset;
this._shiftActor();
},
get yOffset() {
return this._yOffset;
},
set opacity(opacity) {
this.actor.opacity = opacity;
},
get opacity() {
return this.actor.opacity;
} }
}; };