boxpointer: Avoid malformed boxpointer arrow
If the arrow's origin 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 as shown below. |\_____ | | - If the arrow was going to be acute angled, we move the position of the box to maintain the arrow's accuracy. https://bugzilla.gnome.org/show_bug.cgi?id=680077
This commit is contained in:
parent
19f92df8ab
commit
00f15c1075
@ -274,14 +274,51 @@ const BoxPointer = new Lang.Class({
|
|||||||
let [x1, y1] = [halfBorder, halfBorder];
|
let [x1, y1] = [halfBorder, halfBorder];
|
||||||
let [x2, y2] = [boxWidth - halfBorder, boxHeight - 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);
|
cr.moveTo(x1 + borderRadius, y1);
|
||||||
if (this._arrowSide == St.Side.TOP) {
|
if (this._arrowSide == St.Side.TOP) {
|
||||||
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
|
if (skipTopLeft) {
|
||||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
cr.moveTo(x1, y2 - borderRadius);
|
||||||
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y1);
|
cr.lineTo(x1, y1 - rise);
|
||||||
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
|
cr.lineTo(x1 + halfBase, y1);
|
||||||
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y1);
|
} else if (skipTopRight) {
|
||||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
cr.lineTo(x2 - halfBase, y1);
|
||||||
|
cr.lineTo(x2, y1 - rise);
|
||||||
|
cr.lineTo(x2, y1 + borderRadius);
|
||||||
} else {
|
} else {
|
||||||
cr.lineTo(this._arrowOrigin - halfBase, y1);
|
cr.lineTo(this._arrowOrigin - halfBase, y1);
|
||||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
cr.lineTo(this._arrowOrigin, y1 - rise);
|
||||||
@ -289,19 +326,21 @@ const BoxPointer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cr.lineTo(x2 - borderRadius, y1);
|
if (!skipTopRight) {
|
||||||
|
|
||||||
// top-right corner
|
// top-right corner
|
||||||
|
cr.lineTo(x2 - borderRadius, y1);
|
||||||
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
|
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
|
||||||
3*Math.PI/2, Math.PI*2);
|
3*Math.PI/2, Math.PI*2);
|
||||||
|
}
|
||||||
|
|
||||||
if (this._arrowSide == St.Side.RIGHT) {
|
if (this._arrowSide == St.Side.RIGHT) {
|
||||||
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
|
if (skipTopRight) {
|
||||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
cr.lineTo(x2 + rise, y1);
|
||||||
cr.lineTo(x2, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
|
cr.lineTo(x2 + rise, y1 + halfBase);
|
||||||
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
|
} else if (skipBottomRight) {
|
||||||
cr.lineTo(x2, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
|
cr.lineTo(x2, y2 - halfBase);
|
||||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
cr.lineTo(x2 + rise, y2);
|
||||||
|
cr.lineTo(x2 - borderRadius, y2);
|
||||||
} else {
|
} else {
|
||||||
cr.lineTo(x2, this._arrowOrigin - halfBase);
|
cr.lineTo(x2, this._arrowOrigin - halfBase);
|
||||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
cr.lineTo(x2 + rise, this._arrowOrigin);
|
||||||
@ -309,19 +348,21 @@ const BoxPointer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cr.lineTo(x2, y2 - borderRadius);
|
if (!skipBottomRight) {
|
||||||
|
|
||||||
// bottom-right corner
|
// bottom-right corner
|
||||||
|
cr.lineTo(x2, y2 - borderRadius);
|
||||||
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
|
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
|
||||||
0, Math.PI/2);
|
0, Math.PI/2);
|
||||||
|
}
|
||||||
|
|
||||||
if (this._arrowSide == St.Side.BOTTOM) {
|
if (this._arrowSide == St.Side.BOTTOM) {
|
||||||
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
|
if (skipBottomLeft) {
|
||||||
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y2);
|
cr.lineTo(x1 + halfBase, y2);
|
||||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
cr.lineTo(x1, y2 + rise);
|
||||||
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
|
cr.lineTo(x1, y2 - borderRadius);
|
||||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
} else if (skipBottomRight) {
|
||||||
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y2);
|
cr.lineTo(x2, y2 + rise);
|
||||||
|
cr.lineTo(x2 - halfBase, y2);
|
||||||
} else {
|
} else {
|
||||||
cr.lineTo(this._arrowOrigin + halfBase, y2);
|
cr.lineTo(this._arrowOrigin + halfBase, y2);
|
||||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
cr.lineTo(this._arrowOrigin, y2 + rise);
|
||||||
@ -329,19 +370,21 @@ const BoxPointer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cr.lineTo(x1 + borderRadius, y2);
|
if (!skipBottomLeft) {
|
||||||
|
|
||||||
// bottom-left corner
|
// bottom-left corner
|
||||||
|
cr.lineTo(x1 + borderRadius, y2);
|
||||||
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
|
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
|
||||||
Math.PI/2, Math.PI);
|
Math.PI/2, Math.PI);
|
||||||
|
}
|
||||||
|
|
||||||
if (this._arrowSide == St.Side.LEFT) {
|
if (this._arrowSide == St.Side.LEFT) {
|
||||||
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
|
if (skipTopLeft) {
|
||||||
cr.lineTo(x1, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
|
cr.lineTo(x1, y1 + halfBase);
|
||||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
cr.lineTo(x1 - rise, y1);
|
||||||
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
|
cr.lineTo(x1 + borderRadius, y1);
|
||||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
} else if (skipBottomLeft) {
|
||||||
cr.lineTo(x1, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
|
cr.lineTo(x1 - rise, y2)
|
||||||
|
cr.lineTo(x1 - rise, y2 - halfBase);
|
||||||
} else {
|
} else {
|
||||||
cr.lineTo(x1, this._arrowOrigin + halfBase);
|
cr.lineTo(x1, this._arrowOrigin + halfBase);
|
||||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
cr.lineTo(x1 - rise, this._arrowOrigin);
|
||||||
@ -349,11 +392,12 @@ const BoxPointer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cr.lineTo(x1, y1 + borderRadius);
|
if (!skipTopLeft) {
|
||||||
|
|
||||||
// top-left corner
|
// top-left corner
|
||||||
|
cr.lineTo(x1, y1 + borderRadius);
|
||||||
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
|
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
|
||||||
Math.PI, 3*Math.PI/2);
|
Math.PI, 3*Math.PI/2);
|
||||||
|
}
|
||||||
|
|
||||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||||
cr.fillPreserve();
|
cr.fillPreserve();
|
||||||
@ -406,9 +450,8 @@ const BoxPointer = new Lang.Class({
|
|||||||
let arrowBase = themeNode.get_length('-arrow-base');
|
let arrowBase = themeNode.get_length('-arrow-base');
|
||||||
let borderRadius = themeNode.get_length('-arrow-border-radius');
|
let borderRadius = themeNode.get_length('-arrow-border-radius');
|
||||||
let margin = (4 * borderRadius + borderWidth + arrowBase);
|
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 gap = themeNode.get_length('-boxpointer-gap');
|
||||||
let padding = themeNode.get_length('-arrow-rise');
|
let padding = themeNode.get_length('-arrow-rise');
|
||||||
|
|
||||||
@ -429,8 +472,23 @@ const BoxPointer = new Lang.Class({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now align and position the pointing axis, making sure
|
// Now align and position the pointing axis, making sure it fits on
|
||||||
// it fits on screen
|
// 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) {
|
switch (this._arrowSide) {
|
||||||
case St.Side.TOP:
|
case St.Side.TOP:
|
||||||
case St.Side.BOTTOM:
|
case St.Side.BOTTOM:
|
||||||
@ -438,7 +496,17 @@ const BoxPointer = new Lang.Class({
|
|||||||
|
|
||||||
resX = Math.max(resX, monitor.x + padding);
|
resX = Math.max(resX, monitor.x + padding);
|
||||||
resX = Math.min(resX, monitor.x + monitor.width - (padding + natWidth));
|
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;
|
break;
|
||||||
|
|
||||||
case St.Side.LEFT:
|
case St.Side.LEFT:
|
||||||
@ -448,10 +516,21 @@ const BoxPointer = new Lang.Class({
|
|||||||
resY = Math.max(resY, monitor.y + padding);
|
resY = Math.max(resY, monitor.y + padding);
|
||||||
resY = Math.min(resY, monitor.y + monitor.height - (padding + natHeight));
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setArrowOrigin(arrowOrigin);
|
||||||
|
|
||||||
let parent = this.actor.get_parent();
|
let parent = this.actor.get_parent();
|
||||||
let success, x, y;
|
let success, x, y;
|
||||||
while (!success) {
|
while (!success) {
|
||||||
|
Loading…
Reference in New Issue
Block a user