Add an alternate bubble arrow drawing

When the bubble is near the screen border, draw a half arrow at the
corner of the bubble.

https://bugzilla.gnome.org/show_bug.cgi?id=639979
This commit is contained in:
Sardem FF7 2011-01-28 09:19:54 +01:00 committed by Dan Winship
parent 8abbd5dacb
commit fd75c7c7b4

View File

@ -27,6 +27,7 @@ BoxPointer.prototype = {
_init: function(arrowSide, binProperties) { _init: function(arrowSide, binProperties) {
this._arrowSide = arrowSide; this._arrowSide = arrowSide;
this._arrowOrigin = 0; this._arrowOrigin = 0;
this._arrowCorner = null;
this.actor = new St.Bin({ x_fill: true, this.actor = new St.Bin({ x_fill: true,
y_fill: true }); y_fill: true });
this._container = new Shell.GenericContainer(); this._container = new Shell.GenericContainer();
@ -212,47 +213,140 @@ BoxPointer.prototype = {
cr.translate(rise, 0); cr.translate(rise, 0);
} }
cr.moveTo(borderRadius, halfBorder); let [x1, y1] = [halfBorder, halfBorder];
let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
cr.moveTo(x1 + borderRadius, y1);
if (this._arrowSide == St.Side.TOP) { if (this._arrowSide == St.Side.TOP) {
cr.lineTo(this._arrowOrigin - halfBase, halfBorder); if (this._arrowCorner == St.Corner.TOPLEFT) {
cr.lineTo(this._arrowOrigin, halfBorder - rise); cr.moveTo(x1, y1);
cr.lineTo(this._arrowOrigin + halfBase, halfBorder); cr.lineTo(x1, y1 - rise);
} cr.lineTo(x1 + halfBase, y1);
cr.lineTo(boxWidth - borderRadius, halfBorder); cr.lineTo(x2 - borderRadius, y1);
} else if (this._arrowCorner == St.Corner.TOPRIGHT) {
cr.lineTo(x2 - halfBase, y1);
cr.lineTo(x2, y1 - rise);
} else if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
cr.lineTo(this._arrowOrigin, y1);
cr.lineTo(this._arrowOrigin, y1 - rise);
cr.lineTo(this._arrowOrigin + halfBase, y1);
cr.lineTo(x2 - borderRadius, y1);
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
cr.lineTo(this._arrowOrigin - halfBase, y1);
cr.lineTo(this._arrowOrigin, y1 - rise);
cr.lineTo(this._arrowOrigin, y1);
cr.lineTo(x2 - borderRadius, y1);
} else {
cr.lineTo(this._arrowOrigin - halfBase, y1);
cr.lineTo(this._arrowOrigin, y1 - rise);
cr.lineTo(this._arrowOrigin + halfBase, y1);
cr.lineTo(x2 - borderRadius, y1);
}
} else
cr.lineTo(x2 - borderRadius, y1);
cr.arc(boxWidth - borderRadius - halfBorder, borderRadius + halfBorder, borderRadius, // top-right corner
3*Math.PI/2, Math.PI*2); if (this._arrowCorner != St.Corner.TOPRIGHT)
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
3*Math.PI/2, Math.PI*2);
if (this._arrowSide == St.Side.RIGHT) { if (this._arrowSide == St.Side.RIGHT) {
cr.lineTo(boxWidth - halfBorder, this._arrowOrigin - halfBase); if (this._arrowCorner == St.Corner.TOPRIGHT) {
cr.lineTo(boxWidth - halfBorder + rise, this._arrowOrigin); cr.lineTo(x2, y1);
cr.lineTo(boxWidth - halfBorder, this._arrowOrigin + halfBase); cr.lineTo(x2 + rise, y1);
} cr.lineTo(x2, y1 + halfBase);
cr.lineTo(boxWidth - halfBorder, boxHeight - borderRadius); cr.lineTo(x2, y2 - borderRadius);
} else if (this._arrowCorner == St.Corner.BOTTOMRIGHT) {
cr.moveTo(x2, y2 - halfBase);
cr.lineTo(x2 + rise, y2);
} else if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
cr.lineTo(x2, this._arrowOrigin);
cr.lineTo(x2 + rise, this._arrowOrigin);
cr.lineTo(x2, this._arrowOrigin + halfBase);
cr.lineTo(x2, y2 - borderRadius);
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
cr.lineTo(x2, this._arrowOrigin - halfBase);
cr.lineTo(x2 + rise, this._arrowOrigin);
cr.lineTo(x2, this._arrowOrigin);
cr.lineTo(x2, y2 - borderRadius);
} else {
cr.lineTo(x2, this._arrowOrigin - halfBase);
cr.lineTo(x2 + rise, this._arrowOrigin);
cr.lineTo(x2, this._arrowOrigin + halfBase);
cr.lineTo(x2, y2 - borderRadius);
}
} else
cr.lineTo(x2, y2 - borderRadius);
cr.arc(boxWidth - borderRadius - halfBorder, boxHeight - borderRadius - halfBorder, borderRadius, // bottom-right corner
0, Math.PI/2); if (this._arrowCorner != St.Corner.BOTTOMRIGHT)
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
0, Math.PI/2);
if (this._arrowSide == St.Side.BOTTOM) { if (this._arrowSide == St.Side.BOTTOM) {
cr.lineTo(this._arrowOrigin + halfBase, boxHeight - halfBorder); if (this._arrowCorner == St.Corner.BOTTOMLEFT) {
cr.lineTo(this._arrowOrigin, boxHeight - halfBorder + rise); cr.lineTo(x1 + halfBase, y2);
cr.lineTo(this._arrowOrigin - halfBase, boxHeight - halfBorder); cr.lineTo(x1, y2 + rise);
} } else if (this._arrowCorner == St.Corner.BOTTOMRIGHT) {
cr.lineTo(borderRadius, boxHeight - halfBorder); cr.lineTo(x2, y2 + rise);
cr.lineTo(x2 - halfBase, y2);
cr.lineTo(x1 + borderRadius, y2);
} else if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
cr.lineTo(this._arrowOrigin + halfBase, y2);
cr.lineTo(this._arrowOrigin, y2 + rise);
cr.lineTo(this._arrowOrigin, y2);
cr.lineTo(x1 + borderRadius, y2);
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
cr.lineTo(this._arrowOrigin, y2);
cr.lineTo(this._arrowOrigin, y2 + rise);
cr.lineTo(this._arrowOrigin - halfBase, y2);
cr.lineTo(x1 + borderRadius, y2);
} else {
cr.lineTo(this._arrowOrigin + halfBase, y2);
cr.lineTo(this._arrowOrigin, y2 + rise);
cr.lineTo(this._arrowOrigin - halfBase, y2);
cr.lineTo(x1 + borderRadius, y2);
}
} else
cr.lineTo(x1 + borderRadius, y2);
cr.arc(borderRadius + halfBorder, boxHeight - borderRadius - halfBorder, borderRadius, // bottom-left corner
Math.PI/2, Math.PI); if (this._arrowCorner != St.Corner.BOTTOMLEFT)
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
Math.PI/2, Math.PI);
if (this._arrowSide == St.Side.LEFT) { if (this._arrowSide == St.Side.LEFT) {
cr.lineTo(halfBorder, this._arrowOrigin + halfBase); if (this._arrowCorner == St.Corner.TOPLEFT) {
cr.lineTo(halfBorder - rise, this._arrowOrigin); cr.lineTo(x2, y1 + halfBase);
cr.lineTo(halfBorder, this._arrowOrigin - halfBase); cr.lineTo(x1 - rise, y1);
} } else if (this._arrowCorner == St.Corner.BOTTOMLEFT) {
cr.lineTo(halfBorder, borderRadius); cr.lineTo(x1 + rise, y2);
cr.moveTo(x1, y2 - halfBase);
} else if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
cr.lineTo(x1, this._arrowOrigin + halfBase);
cr.lineTo(x1 - rise, this._arrowOrigin);
cr.lineTo(x1, this._arrowOrigin);
cr.lineTo(x1, y1 + borderRadius);
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
cr.lineTo(x1, this._arrowOrigin);
cr.lineTo(x1 - rise, this._arrowOrigin);
cr.lineTo(x1, this._arrowOrigin - halfBase);
cr.lineTo(x1, y1 + borderRadius);
} else {
cr.lineTo(x1, this._arrowOrigin + halfBase);
cr.lineTo(x1 - rise, this._arrowOrigin);
cr.lineTo(x1, this._arrowOrigin - halfBase);
cr.lineTo(x1, y1 + borderRadius);
}
} else
cr.lineTo(x1, y1 + borderRadius);
cr.arc(borderRadius + halfBorder, borderRadius + halfBorder, borderRadius, // top-left corner
Math.PI, 3*Math.PI/2); if (this._arrowCorner != St.Corner.TOPLEFT)
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
Math.PI, 3*Math.PI/2);
else
cr.lineTo(x1, y1);
Clutter.cairo_set_source_color(cr, backgroundColor); Clutter.cairo_set_source_color(cr, backgroundColor);
cr.fillPreserve(); cr.fillPreserve();
@ -269,7 +363,7 @@ BoxPointer.prototype = {
// Position correctly relative to the sourceActor // Position correctly relative to the sourceActor
let [sourceX, sourceY] = sourceActor.get_transformed_position(); let [sourceX, sourceY] = sourceActor.get_transformed_position();
let [sourceWidth, sourceHeight] = sourceActor.get_transformed_size(); let [sourceWidth, sourceHeight] = sourceActor.get_transformed_size();
let [sourceCenterX, sourceCenterY] = [sourceX + (sourceWidth / 2), sourceY + (sourceHeight / 2)];
let [minWidth, minHeight, natWidth, natHeight] = this.actor.get_preferred_size(); let [minWidth, minHeight, natWidth, natHeight] = this.actor.get_preferred_size();
// We also want to keep it onscreen, and separated from the // We also want to keep it onscreen, and separated from the
@ -277,10 +371,14 @@ BoxPointer.prototype = {
// separated from its sourceActor // separated from its sourceActor
let primary = global.get_primary_monitor(); let primary = global.get_primary_monitor();
let themeNode = this.actor.get_theme_node(); let themeNode = this.actor.get_theme_node();
let arrowRise = themeNode.get_length('-arrow-rise'); let halfBorder = themeNode.get_length('-arrow-border-width') / 2;
let halfBase = themeNode.get_length('-arrow-base') / 2;
let borderRadius = themeNode.get_length('-arrow-border-radius'); let borderRadius = themeNode.get_length('-arrow-border-radius');
let margin = 2 * borderRadius + halfBorder;
let resX, resY; let resX, resY;
this._arrowCorner = null;
switch (this._arrowSide) { switch (this._arrowSide) {
case St.Side.TOP: case St.Side.TOP:
@ -304,40 +402,59 @@ BoxPointer.prototype = {
case St.Side.BOTTOM: case St.Side.BOTTOM:
switch (alignment) { switch (alignment) {
case St.Align.START: case St.Align.START:
resX = sourceX - 2 * borderRadius; resX = sourceCenterX - (halfBase + borderRadius + halfBorder);
break; break;
case St.Align.MIDDLE: case St.Align.MIDDLE:
resX = sourceX - Math.floor((natWidth - sourceWidth) / 2); resX = sourceCenterX - (natWidth / 2);
break; break;
case St.Align.END: case St.Align.END:
resX = sourceX - (natWidth - sourceWidth) + 2 * borderRadius; resX = sourceCenterX - natWidth + (halfBase + borderRadius + halfBorder);
break; break;
} }
if (sourceCenterX < margin) {
// Not enough space to the top
this._arrowCorner = (this._arrowSide == St.Side.TOP) ? St.Corner.TOPLEFT : St.Corner.BOTTOMLEFT;
resX = 10;
} else if (sourceCenterX > (primary.width - margin)) {
// Not enough space to the botom
this._arrowCorner = (this._arrowSide == St.Side.TOP) ? St.Corner.TOPRIGHT : St.Corner.BOTTOMRIGHT;
resX = primary.width - (10 + natWidth);
}
resX = Math.min(resX, primary.x + primary.width - natWidth - arrowRise - gap); resX = Math.max(resX, 10);
resX = Math.max(resX, primary.x); resX = Math.min(resX, primary.width - (10 + natWidth));
this.setArrowOrigin((sourceX - resX) + Math.floor(sourceWidth / 2)); this.setArrowOrigin(sourceCenterX - resX);
break; break;
case St.Side.LEFT: case St.Side.LEFT:
case St.Side.RIGHT: case St.Side.RIGHT:
switch (alignment) { switch (alignment) {
case St.Align.START: case St.Align.START:
resY = sourceY - 2 * borderRadius; resY = sourceCenterY - (halfBase + borderRadius + halfBorder);
break; break;
case St.Align.MIDDLE: case St.Align.MIDDLE:
resY = sourceY - Math.floor((natHeight - sourceHeight) / 2); resY = sourceCenterY - (natHeight / 2);
break; break;
case St.Align.END: case St.Align.END:
resY = sourceY - (natHeight - sourceHeight) + 2 * borderRadius; resY = sourceCenterY - natHeight + (halfBase + borderRadius + halfBorder);
break; break;
} }
if (sourceCenterY < margin) {
// Not enough space to the left
this._arrowCorner = (this._arrowSide == St.Side.LEFT) ? St.Corner.TOPLEFT : St.Corner.TORIGHT;
resY = 10;
}
else if (sourceCenterY > (primary.height - margin)) {
// Not enough space to the right
this._arrowCorner = (this._arrowSide == St.Side.LEFT) ? St.Corner.BOTTOMLEFT : St.Corner.BOTTOMRIGHT;
resY = primary.height - (10 + natHeight);
}
resY = Math.min(resY, primary.y + primary.height - natHeight - arrowRise - gap); resY = Math.max(resY, 10);
resY = Math.max(resY, primary.y); resY = Math.min(resY, primary.height - (10 + natHeight));
this.setArrowOrigin((sourceY - resY) + Math.floor(sourceHeight / 2)); this.setArrowOrigin(sourceCenterY - resY);
break; break;
} }