[gnome-shell] messageTray: make links in message banners clickable
- From: Maxim Ermilov <mermilov src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell] messageTray: make links in message banners clickable
- Date: Tue, 23 Nov 2010 23:41:16 +0000 (UTC)
commit 65f0b483f801378cdb3c5f808badc9822ca0e784
Author: Maxim Ermilov <zaspire rambler ru>
Date: Wed Nov 24 02:27:47 2010 +0300
messageTray: make links in message banners clickable
https://bugzilla.gnome.org/show_bug.cgi?id=610219
data/theme/gnome-shell.css | 4 +
js/ui/dnd.js | 10 ++--
js/ui/messageTray.js | 134 ++++++++++++++++++++++++++++++++++++++++----
src/shell-global.c | 5 ++
src/shell-global.h | 3 +-
5 files changed, 138 insertions(+), 18 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 82b8440..c5b6b54 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -847,6 +847,10 @@ StTooltip StLabel {
color: #cccccc;
}
+.url-highlighter {
+ link-color: #ccccff;
+}
+
/* Message Tray */
#message-tray {
background-gradient-direction: vertical;
diff --git a/js/ui/dnd.js b/js/ui/dnd.js
index 66f4680..71d8c04 100644
--- a/js/ui/dnd.js
+++ b/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;
diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js
index e0c4ecc..2b6226a 100644
--- a/js/ui/messageTray.js
+++ b/js/ui/messageTray.js
@@ -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;
-
- text = text ? _cleanMarkup(text) : '';
- body.clutter_text.set_markup(text);
+ let label = new URLHighlighter(text, true);
- this.addActor(body);
- return body;
+ this.addActor(label.actor);
+ return label.actor;
},
_addBannerBody: function() {
diff --git a/src/shell-global.c b/src/shell-global.c
index dad0b20..77e2d85 100644
--- a/src/shell-global.c
+++ b/src/shell-global.c
@@ -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;
diff --git a/src/shell-global.h b/src/shell-global.h
index b8c5af7..91a82c4 100644
--- a/src/shell-global.h
+++ b/src/shell-global.h
@@ -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,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]