2011-09-28 09:16:26 -04:00
|
|
|
// -*- mode: js; js-indent-level: 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;
|
2011-03-25 10:04:28 -04:00
|
|
|
const Meta = imports.gi.Meta;
|
2010-05-04 14:32:28 -04:00
|
|
|
const Shell = imports.gi.Shell;
|
2013-09-10 05:48:55 -04:00
|
|
|
const Signals = imports.signals;
|
|
|
|
const St = imports.gi.St;
|
2010-05-04 14:32:28 -04:00
|
|
|
|
2011-06-13 09:54:05 -04:00
|
|
|
const Main = imports.ui.main;
|
2010-10-15 00:53:34 -04:00
|
|
|
const Tweener = imports.ui.tweener;
|
|
|
|
|
2012-06-15 13:16:10 -04:00
|
|
|
const PopupAnimation = {
|
|
|
|
NONE: 0,
|
|
|
|
SLIDE: 1 << 0,
|
|
|
|
FADE: 1 << 1,
|
|
|
|
FULL: ~0,
|
|
|
|
};
|
|
|
|
|
2010-10-15 00:53:34 -04:00
|
|
|
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
|
2012-06-14 13:43:15 -04:00
|
|
|
* placed. The arrow position may be controlled via
|
|
|
|
* setArrowOrigin(). The arrow side might be temporarily flipped
|
|
|
|
* depending on the box size and source position to keep the box
|
|
|
|
* totally inside the monitor if possible.
|
2010-05-04 14:32:28 -04:00
|
|
|
*
|
|
|
|
*/
|
2011-11-20 12:56:27 -05:00
|
|
|
const BoxPointer = new Lang.Class({
|
|
|
|
Name: 'BoxPointer',
|
2010-05-04 14:32:28 -04:00
|
|
|
|
2010-05-12 17:07:41 -04:00
|
|
|
_init: function(arrowSide, binProperties) {
|
2010-05-04 14:32:28 -04:00
|
|
|
this._arrowSide = arrowSide;
|
2012-06-14 13:43:15 -04:00
|
|
|
this._userArrowSide = arrowSide;
|
2010-05-04 14:32:28 -04:00
|
|
|
this._arrowOrigin = 0;
|
2013-02-20 08:13:46 -05:00
|
|
|
this._arrowActor = 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);
|
2011-03-26 16:38:57 -04:00
|
|
|
this._xOffset = 0;
|
|
|
|
this._yOffset = 0;
|
|
|
|
this._xPosition = 0;
|
|
|
|
this._yPosition = 0;
|
2011-09-14 18:14:03 -04:00
|
|
|
this._sourceAlignment = 0.5;
|
2012-01-17 21:31:03 -05:00
|
|
|
this._capturedEventId = 0;
|
|
|
|
this._muteInput();
|
|
|
|
},
|
|
|
|
|
2013-09-10 05:48:55 -04:00
|
|
|
get arrowSide() {
|
|
|
|
return this._arrowSide;
|
|
|
|
},
|
|
|
|
|
2012-01-17 21:31:03 -05:00
|
|
|
_muteInput: function() {
|
|
|
|
if (this._capturedEventId == 0)
|
|
|
|
this._capturedEventId = this.actor.connect('captured-event',
|
|
|
|
function() { return true; });
|
|
|
|
},
|
|
|
|
|
|
|
|
_unmuteInput: function() {
|
|
|
|
if (this._capturedEventId != 0) {
|
|
|
|
this.actor.disconnect(this._capturedEventId);
|
|
|
|
this._capturedEventId = 0;
|
|
|
|
}
|
2010-05-04 14:32:28 -04:00
|
|
|
},
|
|
|
|
|
2010-11-18 16:18:54 -05:00
|
|
|
show: function(animate, onComplete) {
|
2010-10-15 00:53:34 -04:00
|
|
|
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');
|
2012-06-15 13:16:10 -04:00
|
|
|
let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0;
|
|
|
|
|
|
|
|
if (animate & PopupAnimation.FADE)
|
|
|
|
this.opacity = 0;
|
|
|
|
else
|
|
|
|
this.opacity = 255;
|
2010-10-15 00:53:34 -04:00
|
|
|
|
|
|
|
this.actor.show();
|
|
|
|
|
2012-06-15 13:16:10 -04:00
|
|
|
if (animate & PopupAnimation.SLIDE) {
|
2010-11-18 16:18:54 -05:00
|
|
|
switch (this._arrowSide) {
|
|
|
|
case St.Side.TOP:
|
2011-03-26 16:38:57 -04:00
|
|
|
this.yOffset = -rise;
|
2010-11-18 16:18:54 -05:00
|
|
|
break;
|
|
|
|
case St.Side.BOTTOM:
|
2011-03-26 16:38:57 -04:00
|
|
|
this.yOffset = rise;
|
2010-11-18 16:18:54 -05:00
|
|
|
break;
|
|
|
|
case St.Side.LEFT:
|
2011-03-26 16:38:57 -04:00
|
|
|
this.xOffset = -rise;
|
2010-11-18 16:18:54 -05:00
|
|
|
break;
|
|
|
|
case St.Side.RIGHT:
|
2011-03-26 16:38:57 -04:00
|
|
|
this.xOffset = rise;
|
2010-11-18 16:18:54 -05:00
|
|
|
break;
|
|
|
|
}
|
2010-10-15 00:53:34 -04:00
|
|
|
}
|
|
|
|
|
2011-03-26 16:38:57 -04:00
|
|
|
Tweener.addTween(this, { opacity: 255,
|
|
|
|
xOffset: 0,
|
|
|
|
yOffset: 0,
|
2011-09-30 17:30:47 -04:00
|
|
|
transition: 'linear',
|
2012-01-17 21:31:03 -05:00
|
|
|
onComplete: Lang.bind(this, function() {
|
|
|
|
this._unmuteInput();
|
|
|
|
if (onComplete)
|
|
|
|
onComplete();
|
|
|
|
}),
|
2012-06-15 13:16:10 -04:00
|
|
|
time: animationTime });
|
2010-10-15 00:53:34 -04:00
|
|
|
},
|
|
|
|
|
2010-11-18 16:18:54 -05:00
|
|
|
hide: function(animate, onComplete) {
|
2011-03-26 16:38:57 -04:00
|
|
|
let xOffset = 0;
|
|
|
|
let yOffset = 0;
|
2010-10-15 00:53:34 -04:00
|
|
|
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');
|
2012-06-15 13:16:10 -04:00
|
|
|
let fade = (animate & PopupAnimation.FADE);
|
|
|
|
let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0;
|
2010-10-15 00:53:34 -04:00
|
|
|
|
2012-06-15 13:16:10 -04:00
|
|
|
if (animate & PopupAnimation.SLIDE) {
|
2010-11-18 16:18:54 -05:00
|
|
|
switch (this._arrowSide) {
|
|
|
|
case St.Side.TOP:
|
2011-03-26 16:38:57 -04:00
|
|
|
yOffset = rise;
|
2010-11-18 16:18:54 -05:00
|
|
|
break;
|
|
|
|
case St.Side.BOTTOM:
|
2011-03-26 16:38:57 -04:00
|
|
|
yOffset = -rise;
|
2010-11-18 16:18:54 -05:00
|
|
|
break;
|
|
|
|
case St.Side.LEFT:
|
2011-03-26 16:38:57 -04:00
|
|
|
xOffset = rise;
|
2010-11-18 16:18:54 -05:00
|
|
|
break;
|
|
|
|
case St.Side.RIGHT:
|
2011-03-26 16:38:57 -04:00
|
|
|
xOffset = -rise;
|
2010-11-18 16:18:54 -05:00
|
|
|
break;
|
|
|
|
}
|
2010-10-15 00:53:34 -04:00
|
|
|
}
|
|
|
|
|
2012-01-17 21:31:03 -05:00
|
|
|
this._muteInput();
|
|
|
|
|
2012-10-19 17:13:16 -04:00
|
|
|
Tweener.removeTweens(this);
|
2012-06-15 13:16:10 -04:00
|
|
|
Tweener.addTween(this, { opacity: fade ? 0 : 255,
|
2011-03-26 16:38:57 -04:00
|
|
|
xOffset: xOffset,
|
|
|
|
yOffset: yOffset,
|
2011-09-30 17:30:47 -04:00
|
|
|
transition: 'linear',
|
2012-06-15 13:16:10 -04:00
|
|
|
time: animationTime,
|
2011-03-26 16:38:57 -04:00
|
|
|
onComplete: Lang.bind(this, function () {
|
|
|
|
this.actor.hide();
|
2012-06-15 13:16:10 -04:00
|
|
|
this.opacity = 0;
|
2011-03-26 16:38:57 -04:00
|
|
|
this.xOffset = 0;
|
|
|
|
this.yOffset = 0;
|
|
|
|
if (onComplete)
|
|
|
|
onComplete();
|
|
|
|
})
|
|
|
|
});
|
2010-10-15 00:53:34 -04:00
|
|
|
},
|
|
|
|
|
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);
|
2011-03-25 10:04:28 -04:00
|
|
|
|
2012-06-14 13:43:15 -04:00
|
|
|
if (this._sourceActor && this._sourceActor.mapped) {
|
2012-12-21 09:05:34 -05:00
|
|
|
this._reposition();
|
2012-12-21 08:40:40 -05:00
|
|
|
this._updateFlip();
|
2012-06-14 13:43:15 -04:00
|
|
|
}
|
2010-05-04 14:32:28 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
_drawBorder: function(area) {
|
|
|
|
let themeNode = this.actor.get_theme_node();
|
|
|
|
|
2013-02-20 08:13:46 -05:00
|
|
|
if (this._arrowActor) {
|
|
|
|
let [sourceX, sourceY] = this._arrowActor.get_transformed_position();
|
|
|
|
let [sourceWidth, sourceHeight] = this._arrowActor.get_transformed_size();
|
|
|
|
let [absX, absY] = this.actor.get_transformed_position();
|
|
|
|
|
|
|
|
if (this._arrowSide == St.Side.TOP ||
|
|
|
|
this._arrowSide == St.Side.BOTTOM) {
|
|
|
|
this._arrowOrigin = sourceX - absX + sourceWidth / 2;
|
|
|
|
} else {
|
|
|
|
this._arrowOrigin = sourceY - absY + sourceHeight / 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2011-02-14 09:20:22 -05:00
|
|
|
let backgroundColor = themeNode.get_color('-arrow-background-color');
|
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();
|
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
|
|
|
|
2012-09-01 10:42:50 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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) {
|
2012-09-01 10:42:50 -04:00
|
|
|
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);
|
2011-01-28 03:19:54 -05:00
|
|
|
} else {
|
|
|
|
cr.lineTo(this._arrowOrigin - halfBase, y1);
|
|
|
|
cr.lineTo(this._arrowOrigin, y1 - rise);
|
|
|
|
cr.lineTo(this._arrowOrigin + halfBase, y1);
|
|
|
|
}
|
2011-02-02 17:13:55 -05:00
|
|
|
}
|
|
|
|
|
2012-09-01 10:42:50 -04:00
|
|
|
if (!skipTopRight) {
|
|
|
|
cr.lineTo(x2 - borderRadius, y1);
|
|
|
|
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) {
|
2012-09-01 10:42:50 -04:00
|
|
|
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);
|
2011-01-28 03:19:54 -05:00
|
|
|
} else {
|
|
|
|
cr.lineTo(x2, this._arrowOrigin - halfBase);
|
|
|
|
cr.lineTo(x2 + rise, this._arrowOrigin);
|
|
|
|
cr.lineTo(x2, this._arrowOrigin + halfBase);
|
|
|
|
}
|
2011-02-02 17:13:55 -05:00
|
|
|
}
|
|
|
|
|
2012-09-01 10:42:50 -04:00
|
|
|
if (!skipBottomRight) {
|
|
|
|
cr.lineTo(x2, y2 - borderRadius);
|
|
|
|
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) {
|
2012-09-01 10:42:50 -04:00
|
|
|
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);
|
2011-01-28 03:19:54 -05:00
|
|
|
} else {
|
|
|
|
cr.lineTo(this._arrowOrigin + halfBase, y2);
|
|
|
|
cr.lineTo(this._arrowOrigin, y2 + rise);
|
|
|
|
cr.lineTo(this._arrowOrigin - halfBase, y2);
|
|
|
|
}
|
2011-02-02 17:13:55 -05:00
|
|
|
}
|
|
|
|
|
2012-09-01 10:42:50 -04:00
|
|
|
if (!skipBottomLeft) {
|
|
|
|
cr.lineTo(x1 + borderRadius, y2);
|
|
|
|
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) {
|
2012-09-01 10:42:50 -04:00
|
|
|
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);
|
2011-01-28 03:19:54 -05:00
|
|
|
} else {
|
|
|
|
cr.lineTo(x1, this._arrowOrigin + halfBase);
|
|
|
|
cr.lineTo(x1 - rise, this._arrowOrigin);
|
|
|
|
cr.lineTo(x1, this._arrowOrigin - halfBase);
|
|
|
|
}
|
2011-02-02 17:13:55 -05:00
|
|
|
}
|
|
|
|
|
2012-09-01 10:42:50 -04:00
|
|
|
if (!skipTopLeft) {
|
|
|
|
cr.lineTo(x1, y1 + borderRadius);
|
|
|
|
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
|
|
|
|
Math.PI, 3*Math.PI/2);
|
|
|
|
}
|
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();
|
2012-07-22 22:02:31 -04:00
|
|
|
|
|
|
|
if (borderWidth > 0) {
|
|
|
|
let borderColor = themeNode.get_color('-arrow-border-color');
|
|
|
|
Clutter.cairo_set_source_color(cr, borderColor);
|
|
|
|
cr.setLineWidth(borderWidth);
|
|
|
|
cr.stroke();
|
|
|
|
}
|
2013-01-07 15:07:40 -05:00
|
|
|
|
|
|
|
cr.$dispose();
|
2010-05-04 14:32:28 -04:00
|
|
|
},
|
|
|
|
|
2011-07-30 13:24:24 -04:00
|
|
|
setPosition: function(sourceActor, alignment) {
|
2010-10-19 15:10:48 -04:00
|
|
|
// We need to show it now to force an allocation,
|
|
|
|
// so that we can query the correct size.
|
|
|
|
this.actor.show();
|
|
|
|
|
2011-03-25 10:04:28 -04:00
|
|
|
this._sourceActor = sourceActor;
|
2011-09-14 18:14:03 -04:00
|
|
|
this._arrowAlignment = alignment;
|
2011-03-25 10:04:28 -04:00
|
|
|
|
2012-12-21 09:05:34 -05:00
|
|
|
this._reposition();
|
2012-12-21 08:40:40 -05:00
|
|
|
this._updateFlip();
|
2011-03-25 10:04:28 -04:00
|
|
|
},
|
|
|
|
|
2011-09-14 18:14:03 -04:00
|
|
|
setSourceAlignment: function(alignment) {
|
|
|
|
this._sourceAlignment = alignment;
|
|
|
|
|
|
|
|
if (!this._sourceActor)
|
|
|
|
return;
|
|
|
|
|
2012-06-14 13:43:15 -04:00
|
|
|
this.setPosition(this._sourceActor, this._arrowAlignment);
|
2011-09-14 18:14:03 -04:00
|
|
|
},
|
|
|
|
|
2012-12-21 09:05:34 -05:00
|
|
|
_reposition: function() {
|
|
|
|
let sourceActor = this._sourceActor;
|
|
|
|
let alignment = this._arrowAlignment;
|
|
|
|
|
2010-10-19 15:10:48 -04:00
|
|
|
// Position correctly relative to the sourceActor
|
2011-02-02 17:38:55 -05:00
|
|
|
let sourceNode = sourceActor.get_theme_node();
|
|
|
|
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
|
2011-03-26 16:36:27 -04:00
|
|
|
let sourceAllocation = Shell.util_get_transformed_allocation(sourceActor);
|
2011-09-14 18:14:03 -04:00
|
|
|
let sourceCenterX = sourceAllocation.x1 + sourceContentBox.x1 + (sourceContentBox.x2 - sourceContentBox.x1) * this._sourceAlignment;
|
|
|
|
let sourceCenterY = sourceAllocation.y1 + sourceContentBox.y1 + (sourceContentBox.y2 - sourceContentBox.y1) * this._sourceAlignment;
|
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
|
2011-09-22 15:52:58 -04:00
|
|
|
let monitor = Main.layoutManager.findMonitorForActor(sourceActor);
|
2010-11-22 14:10:42 -05:00
|
|
|
let themeNode = this.actor.get_theme_node();
|
2011-02-09 12:27:00 -05:00
|
|
|
let borderWidth = themeNode.get_length('-arrow-border-width');
|
|
|
|
let arrowBase = themeNode.get_length('-arrow-base');
|
2010-11-22 14:10:42 -05:00
|
|
|
let borderRadius = themeNode.get_length('-arrow-border-radius');
|
2011-02-09 12:27:00 -05:00
|
|
|
let margin = (4 * borderRadius + borderWidth + arrowBase);
|
2010-10-25 11:50:00 -04:00
|
|
|
|
2011-07-30 13:24:24 -04:00
|
|
|
let gap = themeNode.get_length('-boxpointer-gap');
|
2012-08-31 18:49:14 -04:00
|
|
|
let padding = themeNode.get_length('-arrow-rise');
|
2011-07-30 13:24:24 -04:00
|
|
|
|
2010-10-19 15:10:48 -04:00
|
|
|
let resX, resY;
|
|
|
|
|
|
|
|
switch (this._arrowSide) {
|
|
|
|
case St.Side.TOP:
|
2011-03-26 16:36:27 -04:00
|
|
|
resY = sourceAllocation.y2 + gap;
|
2010-10-19 15:10:48 -04:00
|
|
|
break;
|
|
|
|
case St.Side.BOTTOM:
|
2011-03-26 16:36:27 -04:00
|
|
|
resY = sourceAllocation.y1 - natHeight - gap;
|
2010-10-19 15:10:48 -04:00
|
|
|
break;
|
|
|
|
case St.Side.LEFT:
|
2011-03-26 16:36:27 -04:00
|
|
|
resX = sourceAllocation.x2 + gap;
|
2010-10-19 15:10:48 -04:00
|
|
|
break;
|
|
|
|
case St.Side.RIGHT:
|
2011-03-26 16:36:27 -04:00
|
|
|
resX = sourceAllocation.x1 - natWidth - gap;
|
2010-10-19 15:10:48 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-09-01 10:42:50 -04:00
|
|
|
// 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;
|
2012-09-06 14:59:58 -04:00
|
|
|
let halfMargin = margin / 2;
|
2012-09-01 10:42:50 -04:00
|
|
|
let [x1, y1] = [halfBorder, halfBorder];
|
|
|
|
let [x2, y2] = [natWidth - halfBorder, natHeight - halfBorder];
|
|
|
|
|
2010-10-19 15:10:48 -04:00
|
|
|
switch (this._arrowSide) {
|
|
|
|
case St.Side.TOP:
|
|
|
|
case St.Side.BOTTOM:
|
2011-02-09 12:27:00 -05:00
|
|
|
resX = sourceCenterX - (halfMargin + (natWidth - margin) * alignment);
|
2010-10-19 15:10:48 -04:00
|
|
|
|
2012-08-31 18:49:14 -04:00
|
|
|
resX = Math.max(resX, monitor.x + padding);
|
|
|
|
resX = Math.min(resX, monitor.x + monitor.width - (padding + natWidth));
|
2012-09-01 10:42:50 -04:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2010-10-19 15:10:48 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case St.Side.LEFT:
|
|
|
|
case St.Side.RIGHT:
|
2011-02-09 12:27:00 -05:00
|
|
|
resY = sourceCenterY - (halfMargin + (natHeight - margin) * alignment);
|
2010-10-19 15:10:48 -04:00
|
|
|
|
2012-08-31 18:49:14 -04:00
|
|
|
resY = Math.max(resY, monitor.y + padding);
|
|
|
|
resY = Math.min(resY, monitor.y + monitor.height - (padding + natHeight));
|
2010-10-19 15:10:48 -04:00
|
|
|
|
2012-09-01 10:42:50 -04:00
|
|
|
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;
|
|
|
|
}
|
2010-10-19 15:10:48 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-09-01 10:42:50 -04:00
|
|
|
this.setArrowOrigin(arrowOrigin);
|
|
|
|
|
2010-10-19 15:10:48 -04:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2011-03-26 16:38:57 -04:00
|
|
|
this._xPosition = Math.floor(x);
|
|
|
|
this._yPosition = Math.floor(y);
|
|
|
|
this._shiftActor();
|
2010-10-19 15:10:48 -04:00
|
|
|
},
|
|
|
|
|
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();
|
|
|
|
}
|
2011-03-26 16:38:57 -04:00
|
|
|
},
|
|
|
|
|
2013-02-20 08:13:46 -05:00
|
|
|
// @actor: an actor relative to which the arrow is positioned.
|
|
|
|
// Differently from setPosition, this will not move the boxpointer itself,
|
|
|
|
// on the arrow
|
|
|
|
setArrowActor: function(actor) {
|
|
|
|
if (this._arrowActor != actor) {
|
|
|
|
this._arrowActor = actor;
|
|
|
|
this._border.queue_repaint();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2011-03-26 16:38:57 -04:00
|
|
|
_shiftActor : function() {
|
|
|
|
// Since the position of the BoxPointer depends on the allocated size
|
|
|
|
// of the BoxPointer and the position of the source actor, trying
|
2012-12-21 10:46:29 -05:00
|
|
|
// to position the BoxPointer via the x/y properties will result in
|
2011-03-26 16:38:57 -04:00
|
|
|
// 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));
|
|
|
|
},
|
|
|
|
|
2012-12-21 08:40:40 -05:00
|
|
|
_calculateArrowSide: function(arrowSide) {
|
2012-06-14 13:43:15 -04:00
|
|
|
let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor);
|
2012-12-21 09:17:49 -05:00
|
|
|
let [minWidth, minHeight, boxWidth, boxHeight] = this._container.get_preferred_size();
|
2012-06-14 13:43:15 -04:00
|
|
|
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
|
|
|
|
|
2012-12-21 08:40:40 -05:00
|
|
|
switch (arrowSide) {
|
2012-06-14 13:43:15 -04:00
|
|
|
case St.Side.TOP:
|
2012-12-21 09:17:49 -05:00
|
|
|
if (sourceAllocation.y2 + boxHeight > monitor.y + monitor.height &&
|
2012-06-14 13:43:15 -04:00
|
|
|
boxHeight < sourceAllocation.y1 - monitor.y)
|
2012-12-21 08:40:40 -05:00
|
|
|
return St.Side.BOTTOM;
|
2012-06-14 13:43:15 -04:00
|
|
|
break;
|
|
|
|
case St.Side.BOTTOM:
|
2012-12-21 09:17:49 -05:00
|
|
|
if (sourceAllocation.y1 - boxHeight < monitor.y &&
|
2012-06-14 13:43:15 -04:00
|
|
|
boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
|
2012-12-21 08:40:40 -05:00
|
|
|
return St.Side.TOP;
|
2012-06-14 13:43:15 -04:00
|
|
|
break;
|
|
|
|
case St.Side.LEFT:
|
2013-07-01 12:37:59 -04:00
|
|
|
if (sourceAllocation.x2 + boxWidth > monitor.x + monitor.width &&
|
2012-06-14 13:43:15 -04:00
|
|
|
boxWidth < sourceAllocation.x1 - monitor.x)
|
2012-12-21 08:40:40 -05:00
|
|
|
return St.Side.RIGHT;
|
2012-06-14 13:43:15 -04:00
|
|
|
break;
|
|
|
|
case St.Side.RIGHT:
|
2013-07-01 12:37:59 -04:00
|
|
|
if (sourceAllocation.x1 - boxWidth < monitor.x &&
|
2012-06-14 13:43:15 -04:00
|
|
|
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
|
2012-12-21 08:40:40 -05:00
|
|
|
return St.Side.LEFT;
|
2012-06-14 13:43:15 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-12-21 08:40:40 -05:00
|
|
|
return arrowSide;
|
|
|
|
},
|
|
|
|
|
|
|
|
_updateFlip: function() {
|
|
|
|
let arrowSide = this._calculateArrowSide(this._userArrowSide);
|
|
|
|
if (this._arrowSide != arrowSide) {
|
|
|
|
this._arrowSide = arrowSide;
|
|
|
|
this._reposition();
|
2012-12-21 09:20:04 -05:00
|
|
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
|
|
|
this._container.queue_relayout();
|
|
|
|
return false;
|
|
|
|
}));
|
2013-09-10 05:48:55 -04:00
|
|
|
|
|
|
|
this.emit('arrow-side-changed');
|
2012-12-21 08:40:40 -05:00
|
|
|
}
|
2012-06-14 13:43:15 -04:00
|
|
|
},
|
|
|
|
|
2011-03-26 16:38:57 -04:00
|
|
|
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;
|
2013-08-30 12:50:35 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
updateArrowSide: function(side) {
|
|
|
|
this._arrowSide = side;
|
|
|
|
this._border.queue_repaint();
|
2013-09-10 05:48:55 -04:00
|
|
|
|
|
|
|
this.emit('arrow-side-changed');
|
2013-08-30 12:50:35 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
getPadding: function(side) {
|
|
|
|
return this.bin.get_theme_node().get_padding(side);
|
|
|
|
},
|
|
|
|
|
|
|
|
getArrowHeight: function() {
|
|
|
|
return this.actor.get_theme_node().get_length('-arrow-rise');
|
2010-05-04 14:32:28 -04:00
|
|
|
}
|
2011-11-20 12:56:27 -05:00
|
|
|
});
|
2013-09-10 05:48:55 -04:00
|
|
|
Signals.addSignalMethods(BoxPointer.prototype);
|