2010-05-19 13:26:41 -04:00
|
|
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
2010-05-04 14:32:28 -04:00
|
|
|
|
|
|
|
const Clutter = imports.gi.Clutter;
|
2010-05-19 13:26:41 -04:00
|
|
|
const Lang = imports.lang;
|
2010-05-04 14:32:28 -04:00
|
|
|
const St = imports.gi.St;
|
|
|
|
const Shell = imports.gi.Shell;
|
|
|
|
|
2010-10-15 00:53:34 -04:00
|
|
|
const Tweener = imports.ui.tweener;
|
|
|
|
|
|
|
|
const POPUP_ANIMATION_TIME = 0.15;
|
|
|
|
|
2010-05-04 14:32:28 -04:00
|
|
|
/**
|
|
|
|
* BoxPointer:
|
2010-11-22 14:10:42 -05:00
|
|
|
* @side: side to draw the arrow on
|
2010-05-04 14:32:28 -04:00
|
|
|
* @binProperties: Properties to set on contained bin
|
|
|
|
*
|
|
|
|
* An actor which displays a triangle "arrow" pointing to a given
|
|
|
|
* side. The .bin property is a container in which content can be
|
|
|
|
* placed. The arrow position may be controlled via setArrowOrigin().
|
|
|
|
*
|
|
|
|
*/
|
2010-05-12 17:07:41 -04:00
|
|
|
function BoxPointer(side, binProperties) {
|
|
|
|
this._init(side, binProperties);
|
2010-05-04 14:32:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
BoxPointer.prototype = {
|
2010-05-12 17:07:41 -04:00
|
|
|
_init: function(arrowSide, binProperties) {
|
2010-05-04 14:32:28 -04:00
|
|
|
this._arrowSide = arrowSide;
|
|
|
|
this._arrowOrigin = 0;
|
2011-01-28 03:19:54 -05:00
|
|
|
this._arrowCorner = null;
|
2010-05-04 14:32:28 -04:00
|
|
|
this.actor = new St.Bin({ x_fill: true,
|
|
|
|
y_fill: true });
|
|
|
|
this._container = new Shell.GenericContainer();
|
|
|
|
this.actor.set_child(this._container);
|
|
|
|
this._container.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
|
|
|
this._container.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
|
|
|
this._container.connect('allocate', Lang.bind(this, this._allocate));
|
|
|
|
this.bin = new St.Bin(binProperties);
|
|
|
|
this._container.add_actor(this.bin);
|
|
|
|
this._border = new St.DrawingArea();
|
|
|
|
this._border.connect('repaint', Lang.bind(this, this._drawBorder));
|
|
|
|
this._container.add_actor(this._border);
|
|
|
|
this.bin.raise(this._border);
|
|
|
|
},
|
|
|
|
|
2010-11-18 16:18:54 -05:00
|
|
|
show: function(animate, onComplete) {
|
2010-10-15 00:53:34 -04:00
|
|
|
let x = this.actor.x;
|
|
|
|
let y = this.actor.y;
|
|
|
|
let themeNode = this.actor.get_theme_node();
|
StThemeNode: simplify use of get_color/get_double/get_length
Although within St itself there are situations where the semantics of
these functions (return TRUE or FALSE and return the actual value in
an out parameter) is useful, it's mostly just annoying at the
application level, where you generally know that the CSS property is
going to specified, and there is no especially sane fallback if it's
not.
So rename the current methods to lookup_color, lookup_double, and
lookup_length, and add new get_color, get_double, and get_length
methods that don't take an "inherit" parameter, and return their
values directly. (Well, except for get_color, due to the lack of (out
caller-allocates) in gjs.)
And update the code to use either the old or new methods as appropriate.
https://bugzilla.gnome.org/show_bug.cgi?id=632590
2010-09-26 17:38:36 -04:00
|
|
|
let rise = themeNode.get_length('-arrow-rise');
|
2010-10-15 00:53:34 -04:00
|
|
|
|
|
|
|
this.actor.opacity = 0;
|
|
|
|
this.actor.show();
|
|
|
|
|
2010-11-18 16:18:54 -05:00
|
|
|
if (animate) {
|
|
|
|
switch (this._arrowSide) {
|
|
|
|
case St.Side.TOP:
|
|
|
|
this.actor.y -= rise;
|
|
|
|
break;
|
|
|
|
case St.Side.BOTTOM:
|
|
|
|
this.actor.y += rise;
|
|
|
|
break;
|
|
|
|
case St.Side.LEFT:
|
|
|
|
this.actor.x -= rise;
|
|
|
|
break;
|
|
|
|
case St.Side.RIGHT:
|
|
|
|
this.actor.x += rise;
|
|
|
|
break;
|
|
|
|
}
|
2010-10-15 00:53:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
Tweener.addTween(this.actor, { opacity: 255,
|
|
|
|
x: x,
|
|
|
|
y: y,
|
|
|
|
transition: "linear",
|
|
|
|
onComplete: onComplete,
|
|
|
|
time: POPUP_ANIMATION_TIME });
|
|
|
|
},
|
|
|
|
|
2010-11-18 16:18:54 -05:00
|
|
|
hide: function(animate, onComplete) {
|
2010-10-15 00:53:34 -04:00
|
|
|
let x = this.actor.x;
|
|
|
|
let y = this.actor.y;
|
|
|
|
let originalX = this.actor.x;
|
|
|
|
let originalY = this.actor.y;
|
|
|
|
let themeNode = this.actor.get_theme_node();
|
StThemeNode: simplify use of get_color/get_double/get_length
Although within St itself there are situations where the semantics of
these functions (return TRUE or FALSE and return the actual value in
an out parameter) is useful, it's mostly just annoying at the
application level, where you generally know that the CSS property is
going to specified, and there is no especially sane fallback if it's
not.
So rename the current methods to lookup_color, lookup_double, and
lookup_length, and add new get_color, get_double, and get_length
methods that don't take an "inherit" parameter, and return their
values directly. (Well, except for get_color, due to the lack of (out
caller-allocates) in gjs.)
And update the code to use either the old or new methods as appropriate.
https://bugzilla.gnome.org/show_bug.cgi?id=632590
2010-09-26 17:38:36 -04:00
|
|
|
let rise = themeNode.get_length('-arrow-rise');
|
2010-10-15 00:53:34 -04:00
|
|
|
|
2010-11-18 16:18:54 -05:00
|
|
|
if (animate) {
|
|
|
|
switch (this._arrowSide) {
|
|
|
|
case St.Side.TOP:
|
|
|
|
y += rise;
|
|
|
|
break;
|
|
|
|
case St.Side.BOTTOM:
|
|
|
|
y -= rise;
|
|
|
|
break;
|
|
|
|
case St.Side.LEFT:
|
|
|
|
x += rise;
|
|
|
|
break;
|
|
|
|
case St.Side.RIGHT:
|
|
|
|
x -= rise;
|
|
|
|
break;
|
|
|
|
}
|
2010-10-15 00:53:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
Tweener.addTween(this.actor, { opacity: 0,
|
|
|
|
x: x,
|
|
|
|
y: y,
|
|
|
|
transition: "linear",
|
|
|
|
time: POPUP_ANIMATION_TIME,
|
|
|
|
onComplete: Lang.bind(this, function () {
|
|
|
|
this.actor.hide();
|
|
|
|
this.actor.x = originalX;
|
|
|
|
this.actor.y = originalY;
|
|
|
|
if (onComplete)
|
|
|
|
onComplete();
|
|
|
|
})
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2010-05-04 14:32:28 -04:00
|
|
|
_adjustAllocationForArrow: function(isWidth, alloc) {
|
|
|
|
let themeNode = this.actor.get_theme_node();
|
StThemeNode: simplify use of get_color/get_double/get_length
Although within St itself there are situations where the semantics of
these functions (return TRUE or FALSE and return the actual value in
an out parameter) is useful, it's mostly just annoying at the
application level, where you generally know that the CSS property is
going to specified, and there is no especially sane fallback if it's
not.
So rename the current methods to lookup_color, lookup_double, and
lookup_length, and add new get_color, get_double, and get_length
methods that don't take an "inherit" parameter, and return their
values directly. (Well, except for get_color, due to the lack of (out
caller-allocates) in gjs.)
And update the code to use either the old or new methods as appropriate.
https://bugzilla.gnome.org/show_bug.cgi?id=632590
2010-09-26 17:38:36 -04:00
|
|
|
let borderWidth = themeNode.get_length('-arrow-border-width');
|
2010-05-04 14:32:28 -04:00
|
|
|
alloc.min_size += borderWidth * 2;
|
|
|
|
alloc.natural_size += borderWidth * 2;
|
|
|
|
if ((!isWidth && (this._arrowSide == St.Side.TOP || this._arrowSide == St.Side.BOTTOM))
|
|
|
|
|| (isWidth && (this._arrowSide == St.Side.LEFT || this._arrowSide == St.Side.RIGHT))) {
|
StThemeNode: simplify use of get_color/get_double/get_length
Although within St itself there are situations where the semantics of
these functions (return TRUE or FALSE and return the actual value in
an out parameter) is useful, it's mostly just annoying at the
application level, where you generally know that the CSS property is
going to specified, and there is no especially sane fallback if it's
not.
So rename the current methods to lookup_color, lookup_double, and
lookup_length, and add new get_color, get_double, and get_length
methods that don't take an "inherit" parameter, and return their
values directly. (Well, except for get_color, due to the lack of (out
caller-allocates) in gjs.)
And update the code to use either the old or new methods as appropriate.
https://bugzilla.gnome.org/show_bug.cgi?id=632590
2010-09-26 17:38:36 -04:00
|
|
|
let rise = themeNode.get_length('-arrow-rise');
|
2010-05-04 14:32:28 -04:00
|
|
|
alloc.min_size += rise;
|
|
|
|
alloc.natural_size += rise;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_getPreferredWidth: function(actor, forHeight, alloc) {
|
|
|
|
let [minInternalSize, natInternalSize] = this.bin.get_preferred_width(forHeight);
|
2010-05-12 17:07:41 -04:00
|
|
|
alloc.min_size = minInternalSize;
|
|
|
|
alloc.natural_size = natInternalSize;
|
2010-05-04 14:32:28 -04:00
|
|
|
this._adjustAllocationForArrow(true, alloc);
|
|
|
|
},
|
|
|
|
|
|
|
|
_getPreferredHeight: function(actor, forWidth, alloc) {
|
|
|
|
let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth);
|
|
|
|
alloc.min_size = minSize;
|
|
|
|
alloc.natural_size = naturalSize;
|
|
|
|
this._adjustAllocationForArrow(false, alloc);
|
|
|
|
},
|
|
|
|
|
|
|
|
_allocate: function(actor, box, flags) {
|
|
|
|
let themeNode = this.actor.get_theme_node();
|
StThemeNode: simplify use of get_color/get_double/get_length
Although within St itself there are situations where the semantics of
these functions (return TRUE or FALSE and return the actual value in
an out parameter) is useful, it's mostly just annoying at the
application level, where you generally know that the CSS property is
going to specified, and there is no especially sane fallback if it's
not.
So rename the current methods to lookup_color, lookup_double, and
lookup_length, and add new get_color, get_double, and get_length
methods that don't take an "inherit" parameter, and return their
values directly. (Well, except for get_color, due to the lack of (out
caller-allocates) in gjs.)
And update the code to use either the old or new methods as appropriate.
https://bugzilla.gnome.org/show_bug.cgi?id=632590
2010-09-26 17:38:36 -04:00
|
|
|
let borderWidth = themeNode.get_length('-arrow-border-width');
|
|
|
|
let rise = themeNode.get_length('-arrow-rise');
|
2010-05-04 14:32:28 -04:00
|
|
|
let childBox = new Clutter.ActorBox();
|
|
|
|
let availWidth = box.x2 - box.x1;
|
|
|
|
let availHeight = box.y2 - box.y1;
|
|
|
|
|
2010-05-19 13:26:41 -04:00
|
|
|
childBox.x1 = 0;
|
2010-05-04 14:32:28 -04:00
|
|
|
childBox.y1 = 0;
|
|
|
|
childBox.x2 = availWidth;
|
|
|
|
childBox.y2 = availHeight;
|
|
|
|
this._border.allocate(childBox, flags);
|
2010-05-20 11:18:46 -04:00
|
|
|
|
|
|
|
childBox.x1 = borderWidth;
|
|
|
|
childBox.y1 = borderWidth;
|
|
|
|
childBox.x2 = availWidth - borderWidth;
|
|
|
|
childBox.y2 = availHeight - borderWidth;
|
2010-05-04 14:32:28 -04:00
|
|
|
switch (this._arrowSide) {
|
|
|
|
case St.Side.TOP:
|
2010-05-20 11:18:46 -04:00
|
|
|
childBox.y1 += rise;
|
|
|
|
break;
|
|
|
|
case St.Side.BOTTOM:
|
|
|
|
childBox.y2 -= rise;
|
|
|
|
break;
|
|
|
|
case St.Side.LEFT:
|
|
|
|
childBox.x1 += rise;
|
2010-05-04 14:32:28 -04:00
|
|
|
break;
|
2010-05-20 11:18:46 -04:00
|
|
|
case St.Side.RIGHT:
|
|
|
|
childBox.x2 -= rise;
|
2010-05-04 14:32:28 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
this.bin.allocate(childBox, flags);
|
|
|
|
},
|
|
|
|
|
|
|
|
_drawBorder: function(area) {
|
|
|
|
let themeNode = this.actor.get_theme_node();
|
|
|
|
|
StThemeNode: simplify use of get_color/get_double/get_length
Although within St itself there are situations where the semantics of
these functions (return TRUE or FALSE and return the actual value in
an out parameter) is useful, it's mostly just annoying at the
application level, where you generally know that the CSS property is
going to specified, and there is no especially sane fallback if it's
not.
So rename the current methods to lookup_color, lookup_double, and
lookup_length, and add new get_color, get_double, and get_length
methods that don't take an "inherit" parameter, and return their
values directly. (Well, except for get_color, due to the lack of (out
caller-allocates) in gjs.)
And update the code to use either the old or new methods as appropriate.
https://bugzilla.gnome.org/show_bug.cgi?id=632590
2010-09-26 17:38:36 -04:00
|
|
|
let borderWidth = themeNode.get_length('-arrow-border-width');
|
|
|
|
let base = themeNode.get_length('-arrow-base');
|
|
|
|
let rise = themeNode.get_length('-arrow-rise');
|
|
|
|
let borderRadius = themeNode.get_length('-arrow-border-radius');
|
2010-05-04 14:32:28 -04:00
|
|
|
|
|
|
|
let halfBorder = borderWidth / 2;
|
2010-05-20 11:18:46 -04:00
|
|
|
let halfBase = Math.floor(base/2);
|
2010-05-04 14:32:28 -04:00
|
|
|
|
|
|
|
let borderColor = new Clutter.Color();
|
StThemeNode: simplify use of get_color/get_double/get_length
Although within St itself there are situations where the semantics of
these functions (return TRUE or FALSE and return the actual value in
an out parameter) is useful, it's mostly just annoying at the
application level, where you generally know that the CSS property is
going to specified, and there is no especially sane fallback if it's
not.
So rename the current methods to lookup_color, lookup_double, and
lookup_length, and add new get_color, get_double, and get_length
methods that don't take an "inherit" parameter, and return their
values directly. (Well, except for get_color, due to the lack of (out
caller-allocates) in gjs.)
And update the code to use either the old or new methods as appropriate.
https://bugzilla.gnome.org/show_bug.cgi?id=632590
2010-09-26 17:38:36 -04:00
|
|
|
themeNode.get_color('-arrow-border-color', borderColor);
|
2010-05-04 14:32:28 -04:00
|
|
|
let backgroundColor = new Clutter.Color();
|
StThemeNode: simplify use of get_color/get_double/get_length
Although within St itself there are situations where the semantics of
these functions (return TRUE or FALSE and return the actual value in
an out parameter) is useful, it's mostly just annoying at the
application level, where you generally know that the CSS property is
going to specified, and there is no especially sane fallback if it's
not.
So rename the current methods to lookup_color, lookup_double, and
lookup_length, and add new get_color, get_double, and get_length
methods that don't take an "inherit" parameter, and return their
values directly. (Well, except for get_color, due to the lack of (out
caller-allocates) in gjs.)
And update the code to use either the old or new methods as appropriate.
https://bugzilla.gnome.org/show_bug.cgi?id=632590
2010-09-26 17:38:36 -04:00
|
|
|
themeNode.get_color('-arrow-background-color', backgroundColor);
|
2010-05-04 14:32:28 -04:00
|
|
|
|
|
|
|
let [width, height] = area.get_surface_size();
|
|
|
|
let [boxWidth, boxHeight] = [width, height];
|
|
|
|
if (this._arrowSide == St.Side.TOP || this._arrowSide == St.Side.BOTTOM) {
|
|
|
|
boxHeight -= rise;
|
|
|
|
} else {
|
|
|
|
boxWidth -= rise;
|
|
|
|
}
|
|
|
|
let cr = area.get_context();
|
|
|
|
Clutter.cairo_set_source_color(cr, borderColor);
|
2010-05-20 11:18:46 -04:00
|
|
|
|
|
|
|
// Translate so that box goes from 0,0 to boxWidth,boxHeight,
|
|
|
|
// with the arrow poking out of that
|
2010-05-04 14:32:28 -04:00
|
|
|
if (this._arrowSide == St.Side.TOP) {
|
|
|
|
cr.translate(0, rise);
|
2010-05-20 11:18:46 -04:00
|
|
|
} else if (this._arrowSide == St.Side.LEFT) {
|
|
|
|
cr.translate(rise, 0);
|
2010-05-04 14:32:28 -04:00
|
|
|
}
|
2010-05-20 11:18:46 -04:00
|
|
|
|
2011-01-28 03:19:54 -05:00
|
|
|
let [x1, y1] = [halfBorder, halfBorder];
|
|
|
|
let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
|
2010-05-20 11:18:46 -04:00
|
|
|
|
2011-01-28 03:19:54 -05:00
|
|
|
cr.moveTo(x1 + borderRadius, y1);
|
2010-05-04 14:32:28 -04:00
|
|
|
if (this._arrowSide == St.Side.TOP) {
|
2011-01-28 03:19:54 -05:00
|
|
|
if (this._arrowCorner == St.Corner.TOPLEFT) {
|
|
|
|
cr.moveTo(x1, y1);
|
|
|
|
cr.lineTo(x1, y1 - rise);
|
|
|
|
cr.lineTo(x1 + halfBase, y1);
|
|
|
|
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);
|
2010-05-20 11:18:46 -04:00
|
|
|
|
2011-01-28 03:19:54 -05:00
|
|
|
// top-right corner
|
|
|
|
if (this._arrowCorner != St.Corner.TOPRIGHT)
|
|
|
|
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
|
|
|
|
3*Math.PI/2, Math.PI*2);
|
2010-05-20 11:18:46 -04:00
|
|
|
|
|
|
|
if (this._arrowSide == St.Side.RIGHT) {
|
2011-01-28 03:19:54 -05:00
|
|
|
if (this._arrowCorner == St.Corner.TOPRIGHT) {
|
|
|
|
cr.lineTo(x2, y1);
|
|
|
|
cr.lineTo(x2 + rise, y1);
|
|
|
|
cr.lineTo(x2, y1 + halfBase);
|
|
|
|
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);
|
2010-05-20 11:18:46 -04:00
|
|
|
|
2011-01-28 03:19:54 -05:00
|
|
|
// bottom-right corner
|
|
|
|
if (this._arrowCorner != St.Corner.BOTTOMRIGHT)
|
|
|
|
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
|
|
|
|
0, Math.PI/2);
|
2010-05-20 11:18:46 -04:00
|
|
|
|
|
|
|
if (this._arrowSide == St.Side.BOTTOM) {
|
2011-01-28 03:19:54 -05:00
|
|
|
if (this._arrowCorner == St.Corner.BOTTOMLEFT) {
|
|
|
|
cr.lineTo(x1 + halfBase, y2);
|
|
|
|
cr.lineTo(x1, y2 + rise);
|
|
|
|
} else if (this._arrowCorner == St.Corner.BOTTOMRIGHT) {
|
|
|
|
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);
|
2010-05-20 11:18:46 -04:00
|
|
|
|
2011-01-28 03:19:54 -05:00
|
|
|
// bottom-left corner
|
|
|
|
if (this._arrowCorner != St.Corner.BOTTOMLEFT)
|
|
|
|
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
|
|
|
|
Math.PI/2, Math.PI);
|
2010-05-20 11:18:46 -04:00
|
|
|
|
|
|
|
if (this._arrowSide == St.Side.LEFT) {
|
2011-01-28 03:19:54 -05:00
|
|
|
if (this._arrowCorner == St.Corner.TOPLEFT) {
|
|
|
|
cr.lineTo(x2, y1 + halfBase);
|
|
|
|
cr.lineTo(x1 - rise, y1);
|
|
|
|
} else if (this._arrowCorner == St.Corner.BOTTOMLEFT) {
|
|
|
|
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);
|
2010-05-20 11:18:46 -04:00
|
|
|
|
2011-01-28 03:19:54 -05:00
|
|
|
// top-left corner
|
|
|
|
if (this._arrowCorner != St.Corner.TOPLEFT)
|
|
|
|
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
|
|
|
|
Math.PI, 3*Math.PI/2);
|
|
|
|
else
|
|
|
|
cr.lineTo(x1, y1);
|
2010-05-20 11:18:46 -04:00
|
|
|
|
2010-05-04 14:32:28 -04:00
|
|
|
Clutter.cairo_set_source_color(cr, backgroundColor);
|
|
|
|
cr.fillPreserve();
|
|
|
|
Clutter.cairo_set_source_color(cr, borderColor);
|
|
|
|
cr.setLineWidth(borderWidth);
|
|
|
|
cr.stroke();
|
|
|
|
},
|
|
|
|
|
2010-10-19 15:10:48 -04:00
|
|
|
setPosition: function(sourceActor, gap, alignment) {
|
|
|
|
// We need to show it now to force an allocation,
|
|
|
|
// so that we can query the correct size.
|
|
|
|
this.actor.show();
|
|
|
|
|
|
|
|
// Position correctly relative to the sourceActor
|
|
|
|
let [sourceX, sourceY] = sourceActor.get_transformed_position();
|
|
|
|
let [sourceWidth, sourceHeight] = sourceActor.get_transformed_size();
|
2011-01-28 03:19:54 -05:00
|
|
|
let [sourceCenterX, sourceCenterY] = [sourceX + (sourceWidth / 2), sourceY + (sourceHeight / 2)];
|
2010-10-19 15:10:48 -04:00
|
|
|
let [minWidth, minHeight, natWidth, natHeight] = this.actor.get_preferred_size();
|
|
|
|
|
2010-10-25 11:50:00 -04:00
|
|
|
// We also want to keep it onscreen, and separated from the
|
|
|
|
// edge by the same distance as the main part of the box is
|
|
|
|
// separated from its sourceActor
|
|
|
|
let primary = global.get_primary_monitor();
|
2010-11-22 14:10:42 -05:00
|
|
|
let themeNode = this.actor.get_theme_node();
|
2011-01-28 03:19:54 -05:00
|
|
|
let halfBorder = themeNode.get_length('-arrow-border-width') / 2;
|
|
|
|
let halfBase = themeNode.get_length('-arrow-base') / 2;
|
2010-11-22 14:10:42 -05:00
|
|
|
let borderRadius = themeNode.get_length('-arrow-border-radius');
|
2010-10-25 11:50:00 -04:00
|
|
|
|
2011-01-28 03:19:54 -05:00
|
|
|
let margin = 2 * borderRadius + halfBorder;
|
|
|
|
|
2010-10-19 15:10:48 -04:00
|
|
|
let resX, resY;
|
2011-01-28 03:19:54 -05:00
|
|
|
this._arrowCorner = null;
|
2010-10-19 15:10:48 -04:00
|
|
|
|
|
|
|
switch (this._arrowSide) {
|
|
|
|
case St.Side.TOP:
|
|
|
|
resY = sourceY + sourceHeight + gap;
|
|
|
|
break;
|
|
|
|
case St.Side.BOTTOM:
|
|
|
|
resY = sourceY - natHeight - gap;
|
|
|
|
break;
|
|
|
|
case St.Side.LEFT:
|
|
|
|
resX = sourceX + sourceWidth + gap;
|
|
|
|
break;
|
|
|
|
case St.Side.RIGHT:
|
|
|
|
resX = sourceX - natWidth - gap;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now align and position the pointing axis, making sure
|
|
|
|
// it fits on screen
|
|
|
|
switch (this._arrowSide) {
|
|
|
|
case St.Side.TOP:
|
|
|
|
case St.Side.BOTTOM:
|
|
|
|
switch (alignment) {
|
|
|
|
case St.Align.START:
|
2011-01-28 03:19:54 -05:00
|
|
|
resX = sourceCenterX - (halfBase + borderRadius + halfBorder);
|
2010-10-19 15:10:48 -04:00
|
|
|
break;
|
|
|
|
case St.Align.MIDDLE:
|
2011-01-28 03:19:54 -05:00
|
|
|
resX = sourceCenterX - (natWidth / 2);
|
2010-10-19 15:10:48 -04:00
|
|
|
break;
|
|
|
|
case St.Align.END:
|
2011-01-28 03:19:54 -05:00
|
|
|
resX = sourceCenterX - natWidth + (halfBase + borderRadius + halfBorder);
|
2010-10-19 15:10:48 -04:00
|
|
|
break;
|
|
|
|
}
|
2011-01-28 03:19:54 -05:00
|
|
|
if (sourceCenterX < margin) {
|
|
|
|
// Not enough space to the top
|
|
|
|
this._arrowCorner = (this._arrowSide == St.Side.TOP) ? St.Corner.TOPLEFT : St.Corner.BOTTOMLEFT;
|
2011-02-01 18:37:43 -05:00
|
|
|
resX = primary.x + 10;
|
|
|
|
} else if (sourceCenterX > (primary.x + primary.width - margin)) {
|
2011-01-28 03:19:54 -05:00
|
|
|
// Not enough space to the botom
|
|
|
|
this._arrowCorner = (this._arrowSide == St.Side.TOP) ? St.Corner.TOPRIGHT : St.Corner.BOTTOMRIGHT;
|
2011-02-01 18:37:43 -05:00
|
|
|
resX = primary.x + primary.width - (10 + natWidth);
|
2011-01-28 03:19:54 -05:00
|
|
|
}
|
2010-10-19 15:10:48 -04:00
|
|
|
|
2011-02-01 18:37:43 -05:00
|
|
|
resX = Math.max(resX, primary.x + 10);
|
|
|
|
resX = Math.min(resX, primary.x + primary.width - (10 + natWidth));
|
2011-01-28 03:19:54 -05:00
|
|
|
this.setArrowOrigin(sourceCenterX - resX);
|
2010-10-19 15:10:48 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case St.Side.LEFT:
|
|
|
|
case St.Side.RIGHT:
|
|
|
|
switch (alignment) {
|
|
|
|
case St.Align.START:
|
2011-01-28 03:19:54 -05:00
|
|
|
resY = sourceCenterY - (halfBase + borderRadius + halfBorder);
|
2010-10-19 15:10:48 -04:00
|
|
|
break;
|
|
|
|
case St.Align.MIDDLE:
|
2011-01-28 03:19:54 -05:00
|
|
|
resY = sourceCenterY - (natHeight / 2);
|
2010-10-19 15:10:48 -04:00
|
|
|
break;
|
|
|
|
case St.Align.END:
|
2011-01-28 03:19:54 -05:00
|
|
|
resY = sourceCenterY - natHeight + (halfBase + borderRadius + halfBorder);
|
2010-10-19 15:10:48 -04:00
|
|
|
break;
|
|
|
|
}
|
2011-01-28 03:19:54 -05:00
|
|
|
if (sourceCenterY < margin) {
|
|
|
|
// Not enough space to the left
|
|
|
|
this._arrowCorner = (this._arrowSide == St.Side.LEFT) ? St.Corner.TOPLEFT : St.Corner.TORIGHT;
|
|
|
|
resY = 10;
|
|
|
|
}
|
2011-02-01 18:37:43 -05:00
|
|
|
else if (sourceCenterY > (primary.y + primary.height - margin)) {
|
2011-01-28 03:19:54 -05:00
|
|
|
// Not enough space to the right
|
|
|
|
this._arrowCorner = (this._arrowSide == St.Side.LEFT) ? St.Corner.BOTTOMLEFT : St.Corner.BOTTOMRIGHT;
|
2011-02-01 18:37:43 -05:00
|
|
|
resY = primary.y + primary.height - (10 + natHeight);
|
2011-01-28 03:19:54 -05:00
|
|
|
}
|
2010-10-19 15:10:48 -04:00
|
|
|
|
2011-02-01 18:37:43 -05:00
|
|
|
resY = Math.max(resY, primary.y + 10);
|
|
|
|
resY = Math.min(resY, primary.y + primary.height - (10 + natHeight));
|
2010-10-19 15:10:48 -04:00
|
|
|
|
2011-01-28 03:19:54 -05:00
|
|
|
this.setArrowOrigin(sourceCenterY - resY);
|
2010-10-19 15:10:48 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
let parent = this.actor.get_parent();
|
|
|
|
let success, x, y;
|
|
|
|
while (!success) {
|
|
|
|
[success, x, y] = parent.transform_stage_point(resX, resY);
|
|
|
|
parent = parent.get_parent();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Actually set the position
|
|
|
|
this.actor.x = Math.floor(x);
|
|
|
|
this.actor.y = Math.floor(y);
|
|
|
|
},
|
|
|
|
|
2010-05-04 14:32:28 -04:00
|
|
|
// @origin: Coordinate specifying middle of the arrow, along
|
|
|
|
// the Y axis for St.Side.LEFT, St.Side.RIGHT from the top and X axis from
|
|
|
|
// the left for St.Side.TOP and St.Side.BOTTOM.
|
|
|
|
setArrowOrigin: function(origin) {
|
|
|
|
if (this._arrowOrigin != origin) {
|
|
|
|
this._arrowOrigin = origin;
|
|
|
|
this._border.queue_repaint();
|
|
|
|
}
|
|
|
|
}
|
2010-05-19 13:26:41 -04:00
|
|
|
};
|