diff --git a/js/ui/boxpointer.js b/js/ui/boxpointer.js index 17c18016e..5749a6513 100644 --- a/js/ui/boxpointer.js +++ b/js/ui/boxpointer.js @@ -274,14 +274,51 @@ const BoxPointer = new Lang.Class({ let [x1, y1] = [halfBorder, halfBorder]; let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder]; + let skipTopLeft = false; + let skipTopRight = false; + let skipBottomLeft = false; + let skipBottomRight = false; + + switch (this._arrowSide) { + case St.Side.TOP: + if (this._arrowOrigin == x1) + skipTopLeft = true; + else if (this._arrowOrigin == x2) + skipTopRight = true; + break; + + case St.Side.RIGHT: + if (this._arrowOrigin == y1) + skipTopRight = true; + else if (this._arrowOrigin == y2) + skipBottomRight = true; + break; + + case St.Side.BOTTOM: + if (this._arrowOrigin == x1) + skipBottomLeft = true; + else if (this._arrowOrigin == x2) + skipBottomRight = true; + break; + + case St.Side.LEFT: + if (this._arrowOrigin == y1) + skipTopLeft = true; + else if (this._arrowOrigin == y2) + skipBottomLeft = true; + break; + } + cr.moveTo(x1 + borderRadius, y1); if (this._arrowSide == St.Side.TOP) { - if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) { - cr.lineTo(this._arrowOrigin, y1 - rise); - cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y1); - } else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) { - cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y1); - cr.lineTo(this._arrowOrigin, y1 - rise); + if (skipTopLeft) { + cr.moveTo(x1, y2 - borderRadius); + cr.lineTo(x1, y1 - rise); + cr.lineTo(x1 + halfBase, y1); + } else if (skipTopRight) { + cr.lineTo(x2 - halfBase, y1); + cr.lineTo(x2, y1 - rise); + cr.lineTo(x2, y1 + borderRadius); } else { cr.lineTo(this._arrowOrigin - halfBase, y1); cr.lineTo(this._arrowOrigin, y1 - rise); @@ -289,19 +326,21 @@ const BoxPointer = new Lang.Class({ } } - cr.lineTo(x2 - borderRadius, y1); - - // top-right corner - cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius, - 3*Math.PI/2, Math.PI*2); + if (!skipTopRight) { + // top-right corner + cr.lineTo(x2 - borderRadius, y1); + cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius, + 3*Math.PI/2, Math.PI*2); + } if (this._arrowSide == St.Side.RIGHT) { - if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) { - cr.lineTo(x2 + rise, this._arrowOrigin); - cr.lineTo(x2, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase); - } else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) { - cr.lineTo(x2, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase); - cr.lineTo(x2 + rise, this._arrowOrigin); + if (skipTopRight) { + cr.lineTo(x2 + rise, y1); + cr.lineTo(x2 + rise, y1 + halfBase); + } else if (skipBottomRight) { + cr.lineTo(x2, y2 - halfBase); + cr.lineTo(x2 + rise, y2); + cr.lineTo(x2 - borderRadius, y2); } else { cr.lineTo(x2, this._arrowOrigin - halfBase); cr.lineTo(x2 + rise, this._arrowOrigin); @@ -309,19 +348,21 @@ const BoxPointer = new Lang.Class({ } } - cr.lineTo(x2, y2 - borderRadius); - - // bottom-right corner - cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius, - 0, Math.PI/2); + if (!skipBottomRight) { + // bottom-right corner + cr.lineTo(x2, y2 - borderRadius); + cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius, + 0, Math.PI/2); + } if (this._arrowSide == St.Side.BOTTOM) { - if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) { - cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y2); - cr.lineTo(this._arrowOrigin, y2 + rise); - } else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) { - cr.lineTo(this._arrowOrigin, y2 + rise); - cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y2); + if (skipBottomLeft) { + cr.lineTo(x1 + halfBase, y2); + cr.lineTo(x1, y2 + rise); + cr.lineTo(x1, y2 - borderRadius); + } else if (skipBottomRight) { + cr.lineTo(x2, y2 + rise); + cr.lineTo(x2 - halfBase, y2); } else { cr.lineTo(this._arrowOrigin + halfBase, y2); cr.lineTo(this._arrowOrigin, y2 + rise); @@ -329,19 +370,21 @@ const BoxPointer = new Lang.Class({ } } - cr.lineTo(x1 + borderRadius, y2); - - // bottom-left corner - cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius, - Math.PI/2, Math.PI); + if (!skipBottomLeft) { + // bottom-left corner + cr.lineTo(x1 + borderRadius, y2); + cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius, + Math.PI/2, Math.PI); + } if (this._arrowSide == St.Side.LEFT) { - if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) { - cr.lineTo(x1, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase); - cr.lineTo(x1 - rise, this._arrowOrigin); - } else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) { - cr.lineTo(x1 - rise, this._arrowOrigin); - cr.lineTo(x1, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase); + if (skipTopLeft) { + cr.lineTo(x1, y1 + halfBase); + cr.lineTo(x1 - rise, y1); + cr.lineTo(x1 + borderRadius, y1); + } else if (skipBottomLeft) { + cr.lineTo(x1 - rise, y2) + cr.lineTo(x1 - rise, y2 - halfBase); } else { cr.lineTo(x1, this._arrowOrigin + halfBase); cr.lineTo(x1 - rise, this._arrowOrigin); @@ -349,11 +392,12 @@ const BoxPointer = new Lang.Class({ } } - cr.lineTo(x1, y1 + borderRadius); - - // top-left corner - cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius, - Math.PI, 3*Math.PI/2); + if (!skipTopLeft) { + // top-left corner + cr.lineTo(x1, y1 + borderRadius); + cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius, + Math.PI, 3*Math.PI/2); + } Clutter.cairo_set_source_color(cr, backgroundColor); cr.fillPreserve(); @@ -406,9 +450,8 @@ const BoxPointer = new Lang.Class({ let arrowBase = themeNode.get_length('-arrow-base'); let borderRadius = themeNode.get_length('-arrow-border-radius'); let margin = (4 * borderRadius + borderWidth + arrowBase); - let halfMargin = margin / 2; - let themeNode = this.actor.get_theme_node(); + let halfMargin = margin / 2; let gap = themeNode.get_length('-boxpointer-gap'); let padding = themeNode.get_length('-arrow-rise'); @@ -429,8 +472,23 @@ const BoxPointer = new Lang.Class({ break; } - // Now align and position the pointing axis, making sure - // it fits on screen + // Now align and position the pointing axis, making sure it fits on + // screen. If the arrowOrigin is so close to the edge that the arrow + // will not be isosceles, we try to compensate as follows: + // - We skip the rounded corner and settle for a right angled arrow + // as shown below. See _drawBorder for further details. + // |\_____ + // | + // | + // - If the arrow was going to be acute angled, we move the position + // of the box to maintain the arrow's accuracy. + + let arrowOrigin; + let halfBase = Math.floor(arrowBase/2); + let halfBorder = borderWidth / 2; + let [x1, y1] = [halfBorder, halfBorder]; + let [x2, y2] = [natWidth - halfBorder, natHeight - halfBorder]; + switch (this._arrowSide) { case St.Side.TOP: case St.Side.BOTTOM: @@ -438,7 +496,17 @@ const BoxPointer = new Lang.Class({ resX = Math.max(resX, monitor.x + padding); resX = Math.min(resX, monitor.x + monitor.width - (padding + natWidth)); - this.setArrowOrigin(sourceCenterX - resX); + + arrowOrigin = sourceCenterX - resX; + if (arrowOrigin <= (x1 + (borderRadius + halfBase))) { + if (arrowOrigin > x1) + resX += (arrowOrigin - x1); + arrowOrigin = x1; + } else if (arrowOrigin >= (x2 - (borderRadius + halfBase))) { + if (arrowOrigin < x2) + resX -= (x2 - arrowOrigin); + arrowOrigin = x2; + } break; case St.Side.LEFT: @@ -448,10 +516,21 @@ const BoxPointer = new Lang.Class({ resY = Math.max(resY, monitor.y + padding); resY = Math.min(resY, monitor.y + monitor.height - (padding + natHeight)); - this.setArrowOrigin(sourceCenterY - resY); + arrowOrigin = sourceCenterY - resY; + if (arrowOrigin <= (y1 + (borderRadius + halfBase))) { + if (arrowOrigin > y1) + resY += (arrowOrigin - y1); + arrowOrigin = y1; + } else if (arrowOrigin >= (y2 - (borderRadius + halfBase))) { + if (arrowOrigin < y2) + resX -= (y2 - arrowOrigin); + arrowOrigin = y2; + } break; } + this.setArrowOrigin(arrowOrigin); + let parent = this.actor.get_parent(); let success, x, y; while (!success) {