messageTray: make links in message banners clickable
https://bugzilla.gnome.org/show_bug.cgi?id=610219
This commit is contained in:
parent
6a52deec7d
commit
65f0b483f8
@ -847,6 +847,10 @@ StTooltip StLabel {
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
.url-highlighter {
|
||||
link-color: #ccccff;
|
||||
}
|
||||
|
||||
/* Message Tray */
|
||||
#message-tray {
|
||||
background-gradient-direction: vertical;
|
||||
|
10
js/ui/dnd.js
10
js/ui/dnd.js
@ -26,9 +26,9 @@ const DragMotionResult = {
|
||||
};
|
||||
|
||||
const DRAG_CURSOR_MAP = {
|
||||
0: Shell.Cursor.UNSUPPORTED_TARGET,
|
||||
1: Shell.Cursor.COPY,
|
||||
2: Shell.Cursor.MOVE
|
||||
0: Shell.Cursor.DND_UNSUPPORTED_TARGET,
|
||||
1: Shell.Cursor.DND_COPY,
|
||||
2: Shell.Cursor.DND_MOVE
|
||||
};
|
||||
|
||||
const DragDropResult = {
|
||||
@ -221,7 +221,7 @@ _Draggable.prototype = {
|
||||
if (this._onEventId)
|
||||
this._ungrabActor();
|
||||
this._grabEvents();
|
||||
global.set_cursor(Shell.Cursor.IN_DRAG);
|
||||
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
|
||||
|
||||
this._dragX = this._dragStartX = stageX;
|
||||
this._dragY = this._dragStartY = stageY;
|
||||
@ -382,7 +382,7 @@ _Draggable.prototype = {
|
||||
}
|
||||
target = target.get_parent();
|
||||
}
|
||||
global.set_cursor(Shell.Cursor.IN_DRAG);
|
||||
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1,6 +1,8 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
@ -14,6 +16,7 @@ const Tweener = imports.ui.tweener;
|
||||
const Main = imports.ui.main;
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const Params = imports.misc.params;
|
||||
const Utils = imports.misc.utils;
|
||||
|
||||
const ANIMATION_TIME = 0.2;
|
||||
const NOTIFICATION_TIMEOUT = 4;
|
||||
@ -44,6 +47,117 @@ function _cleanMarkup(text) {
|
||||
return _text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, '<$1');
|
||||
}
|
||||
|
||||
function URLHighlighter(text, lineWrap) {
|
||||
this._init(text, lineWrap);
|
||||
}
|
||||
|
||||
URLHighlighter.prototype = {
|
||||
_init: function(text, lineWrap) {
|
||||
if (!text)
|
||||
text = '';
|
||||
this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter' });
|
||||
this._linkColor = '#ccccff';
|
||||
this.actor.connect('style-changed', Lang.bind(this, function() {
|
||||
let color = new Clutter.Color();
|
||||
let hasColor = this.actor.get_theme_node().get_color('link-color', color);
|
||||
if (hasColor) {
|
||||
let linkColor = color.to_string().substr(0, 7);
|
||||
if (linkColor != this._linkColor) {
|
||||
this._linkColor = linkColor;
|
||||
this._highlightUrls();
|
||||
}
|
||||
}
|
||||
}));
|
||||
if (lineWrap) {
|
||||
this.actor.clutter_text.line_wrap = true;
|
||||
this.actor.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
|
||||
this.actor.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
}
|
||||
|
||||
this.setMarkup(text);
|
||||
this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) {
|
||||
let urlId = this._findUrlAtPos(event);
|
||||
if (urlId != -1) {
|
||||
let url = this._urls[urlId].url;
|
||||
if (url.indexOf(':') == -1)
|
||||
url = 'http://' + url;
|
||||
try {
|
||||
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
|
||||
return true;
|
||||
} catch (e) {
|
||||
// TODO: remove this after gnome 3 release
|
||||
let p = new Shell.Process({ 'args' : ['gvfs-open', url] });
|
||||
p.run();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
this.actor.connect('motion-event', Lang.bind(this, function(actor, event) {
|
||||
let urlId = this._findUrlAtPos(event);
|
||||
if (urlId != -1 && !this._cursorChanged) {
|
||||
global.set_cursor(Shell.Cursor.POINTING_HAND);
|
||||
this._cursorChanged = true;
|
||||
} else if (urlId == -1) {
|
||||
global.unset_cursor();
|
||||
this._cursorChanged = false;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
this.actor.connect('leave-event', Lang.bind(this, function() {
|
||||
if (this._cursorChanged) {
|
||||
this._cursorChanged = false;
|
||||
global.unset_cursor();
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
setMarkup: function(text) {
|
||||
text = text ? _cleanMarkup(text) : '';
|
||||
this._text = text;
|
||||
|
||||
this.actor.clutter_text.set_markup(text);
|
||||
/* clutter_text.text contain text without markup */
|
||||
this._urls = Utils.findUrls(this.actor.clutter_text.text);
|
||||
this._highlightUrls();
|
||||
},
|
||||
|
||||
_highlightUrls: function() {
|
||||
// text here contain markup
|
||||
let urls = Utils.findUrls(this._text);
|
||||
let markup = '';
|
||||
let pos = 0;
|
||||
for (let i = 0; i < urls.length; i++) {
|
||||
let url = urls[i];
|
||||
let str = this._text.substr(pos, url.pos - pos);
|
||||
markup += str + '<span foreground="' + this._linkColor + '"><u>' + url.url + '</u></span>';
|
||||
pos = url.pos + url.url.length;
|
||||
}
|
||||
markup += this._text.substr(pos);
|
||||
this.actor.clutter_text.set_markup(markup);
|
||||
},
|
||||
|
||||
_findUrlAtPos: function(event) {
|
||||
let success;
|
||||
let [x, y] = event.get_coords();
|
||||
[success, x, y] = this.actor.transform_stage_point(x, y);
|
||||
let find_pos = -1;
|
||||
for (let i = 0; i < this.actor.clutter_text.text.length; i++) {
|
||||
let [success, px, py, line_height] = this.actor.clutter_text.position_to_coords(i);
|
||||
if (py > y || py + line_height < y || x < px)
|
||||
continue;
|
||||
find_pos = i;
|
||||
}
|
||||
if (find_pos != -1) {
|
||||
for (let i = 0; i < this._urls.length; i++)
|
||||
if (find_pos >= this._urls[i].pos &&
|
||||
this._urls[i].pos + this._urls[i].url.length > find_pos)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
// Notification:
|
||||
// @source: the notification's Source
|
||||
// @title: the title
|
||||
@ -148,7 +262,8 @@ Notification.prototype = {
|
||||
|
||||
this._titleLabel = new St.Label();
|
||||
this._bannerBox.add_actor(this._titleLabel);
|
||||
this._bannerLabel = new St.Label();
|
||||
this._bannerUrlHighlighter = new URLHighlighter();
|
||||
this._bannerLabel = this._bannerUrlHighlighter.actor;
|
||||
this._bannerBox.add_actor(this._bannerLabel);
|
||||
|
||||
this.update(title, banner, params);
|
||||
@ -214,8 +329,9 @@ Notification.prototype = {
|
||||
// not fitting fully in the single-line mode.
|
||||
this._bannerBodyText = this._customContent ? null : banner;
|
||||
|
||||
banner = banner ? _cleanMarkup(banner.replace(/\n/g, ' ')) : '';
|
||||
this._bannerLabel.clutter_text.set_markup(banner);
|
||||
banner = banner ? banner.replace(/\n/g, ' ') : '';
|
||||
|
||||
this._bannerUrlHighlighter.setMarkup(banner);
|
||||
this._bannerLabel.queue_relayout();
|
||||
|
||||
// Add the bannerBody now if we know for sure we'll need it
|
||||
@ -259,16 +375,10 @@ Notification.prototype = {
|
||||
//
|
||||
// Return value: the newly-added label
|
||||
addBody: function(text) {
|
||||
let body = new St.Label();
|
||||
body.clutter_text.line_wrap = true;
|
||||
body.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
|
||||
body.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
let label = new URLHighlighter(text, true);
|
||||
|
||||
text = text ? _cleanMarkup(text) : '';
|
||||
body.clutter_text.set_markup(text);
|
||||
|
||||
this.addActor(body);
|
||||
return body;
|
||||
this.addActor(label.actor);
|
||||
return label.actor;
|
||||
},
|
||||
|
||||
_addBannerBody: function() {
|
||||
|
@ -497,6 +497,9 @@ shell_global_set_cursor (ShellGlobal *global,
|
||||
case SHELL_CURSOR_DND_UNSUPPORTED_TARGET:
|
||||
name = "dnd-none";
|
||||
break;
|
||||
case SHELL_CURSOR_POINTING_HAND:
|
||||
name = "hand";
|
||||
break;
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
}
|
||||
@ -516,6 +519,8 @@ shell_global_set_cursor (ShellGlobal *global,
|
||||
case SHELL_CURSOR_DND_COPY:
|
||||
cursor_type = GDK_PLUS;
|
||||
break;
|
||||
case SHELL_CURSOR_POINTING_HAND:
|
||||
cursor_type = GDK_HAND2;
|
||||
case SHELL_CURSOR_DND_UNSUPPORTED_TARGET:
|
||||
cursor_type = GDK_X_CURSOR;
|
||||
break;
|
||||
|
@ -38,7 +38,8 @@ typedef enum {
|
||||
SHELL_CURSOR_DND_IN_DRAG,
|
||||
SHELL_CURSOR_DND_UNSUPPORTED_TARGET,
|
||||
SHELL_CURSOR_DND_MOVE,
|
||||
SHELL_CURSOR_DND_COPY
|
||||
SHELL_CURSOR_DND_COPY,
|
||||
SHELL_CURSOR_POINTING_HAND
|
||||
} ShellCursor;
|
||||
|
||||
void shell_global_set_cursor (ShellGlobal *global,
|
||||
|
Loading…
Reference in New Issue
Block a user