From cb4ad9a96366bf6bffcf41d24e3ffa3009164b1c Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Thu, 26 Feb 2009 17:05:35 -0500 Subject: [PATCH] Fix behavior of More... links by adding a Link class We had problems because the More links were reacting on press but other elements were reacting on release. (Often the link would trigger *and* an item.) Just connecting to ::button-release-event on ClutterText gives a stuck grab (since ClutterText gets the press but not the release), so we need more complicated code that we encapsulate into a new class. link.js: new "pseudo-widget" that implements a clickable link. overlay.js: Use Link.Link for the More.. links http://bugzilla.gnome.org/show_bug.cgi?id=573323 --- js/ui/link.js | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ js/ui/overlay.js | 49 ++++++++++++++--------------- 2 files changed, 104 insertions(+), 26 deletions(-) create mode 100644 js/ui/link.js diff --git a/js/ui/link.js b/js/ui/link.js new file mode 100644 index 000000000..fb8ecd85f --- /dev/null +++ b/js/ui/link.js @@ -0,0 +1,81 @@ +/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ + +const Clutter = imports.gi.Clutter; +const Lang = imports.lang; +const Signals = imports.signals; + +// Link is a clickable link. Right now it just handles properly capturing +// press and release events and short-circuiting the button handling in +// ClutterText, but more features like different colors for hover/pressed states +// or a different mouse cursor could be implemented. +// +// The properties passed in are forwarded to the Clutter.Text() constructor, +// so can include, 'text', 'font_name', etc. +function Link(props) { + this._init(props); +} + +Link.prototype = { + _init : function(props) { + let realProps = { reactive: true }; + // The user can pass in reactive: false to override the above and get + // a non-reactive link (a link to the current page, perhaps) + Lang.copyProperties(props, realProps); + + this.actor = new Clutter.Text(realProps); + this.actor._delegate = this; + this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress)); + this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease)); + this.actor.connect('enter-event', Lang.bind(this, this._onEnter)); + this.actor.connect('leave-event', Lang.bind(this, this._onLeave)); + + this._buttonDown = false; + this._havePointer = false; + }, + + // Update the text of the link + setText : function(text) { + this.actor.text = text; + }, + + // We want to react on buttonDown, but if we override button-release-event for + // ClutterText, but not button-press-event, we get a stuck grab. Tracking + // buttonDown and doing the grab isn't really necessary, but doing it makes + // the behavior perfectly correct if the user clicks on one actor, drags + // to another and releases - that should not trigger either actor. + _onButtonPress : function(actor, event) { + this._buttonDown = true; + this._havePointer = true; // Hack to work around poor enter/leave tracking in Clutter + Clutter.grab_pointer(actor); + + return true; + }, + + _onButtonRelease : function(actor, event) { + if (this._buttonDown) { + this._buttonDown = false; + Clutter.ungrab_pointer(actor); + + if (this._havePointer) + this.emit('clicked'); + } + + return true; + }, + + _onEnter : function(actor, event) { + if (event.get_source() == actor) + this._havePointer = true; + + return false; + }, + + _onLeave : function(actor, event) { + if (event.get_source() == actor) + this._havePointer = false; + + return false; + } +}; + +Signals.addSignalMethods(Link.prototype); diff --git a/js/ui/overlay.js b/js/ui/overlay.js index 1d7ca16c3..2cb3aa3d0 100644 --- a/js/ui/overlay.js +++ b/js/ui/overlay.js @@ -11,6 +11,7 @@ const Signals = imports.signals; const AppDisplay = imports.ui.appDisplay; const DocDisplay = imports.ui.docDisplay; const GenericDisplay = imports.ui.genericDisplay; +const Link = imports.ui.link; const Main = imports.ui.main; const Panel = imports.ui.panel; const Tweener = imports.ui.tweener; @@ -198,12 +199,11 @@ Sideshow.prototype = { this._appsSection.append(this._appDisplay.actor, Big.BoxPackFlags.EXPAND); let moreAppsBox = new Big.Box({x_align: Big.BoxAlignment.END}); - this._moreAppsText = new Clutter.Text({ color: SIDESHOW_TEXT_COLOR, - font_name: "Sans Bold 14px", - text: "More...", - height: LABEL_HEIGHT, - reactive: true}); - moreAppsBox.append(this._moreAppsText, Big.BoxPackFlags.EXPAND); + this._moreAppsLink = new Link.Link({ color: SIDESHOW_TEXT_COLOR, + font_name: "Sans Bold 14px", + text: "More...", + height: LABEL_HEIGHT }); + moreAppsBox.append(this._moreAppsLink.actor, Big.BoxPackFlags.EXPAND); this._appsSection.append(moreAppsBox, Big.BoxPackFlags.EXPAND); this.actor.add_actor(this._appsSection); @@ -226,12 +226,11 @@ Sideshow.prototype = { this._docsSection.append(this._docDisplay.actor, Big.BoxPackFlags.EXPAND); let moreDocsBox = new Big.Box({x_align: Big.BoxAlignment.END}); - this._moreDocsText = new Clutter.Text({ color: SIDESHOW_TEXT_COLOR, - font_name: "Sans Bold 14px", - text: "More...", - height: LABEL_HEIGHT, - reactive: true}); - moreDocsBox.append(this._moreDocsText, Big.BoxPackFlags.EXPAND); + this._moreDocsLink = new Link.Link({ color: SIDESHOW_TEXT_COLOR, + font_name: "Sans Bold 14px", + text: "More...", + height: LABEL_HEIGHT }); + moreDocsBox.append(this._moreDocsLink.actor, Big.BoxPackFlags.EXPAND); this._docsSection.append(moreDocsBox, Big.BoxPackFlags.EXPAND); this.actor.add_actor(this._docsSection); @@ -267,24 +266,22 @@ Sideshow.prototype = { me._appDisplay.selectFirstItem(); }); - this._moreAppsText.connect('button-press-event', + this._moreAppsLink.connect('clicked', function(o, event) { if (me._moreAppsMode) { me._unsetMoreAppsMode(); } else { me._setMoreAppsMode(); } - return true; }); - this._moreDocsText.connect('button-press-event', + this._moreDocsLink.connect('clicked', function(o, event) { if (me._moreDocsMode) { me._unsetMoreDocsMode(); } else { me._setMoreDocsMode(); } - return true; }); }, @@ -319,7 +316,7 @@ Sideshow.prototype = { this._docsSection.set_clip(0, 0, this._docsSection.width, this._docsSection.height); - this._moreAppsText.hide(); + this._moreAppsLink.actor.hide(); this._appsSection.set_clip(0, 0, this._appsSection.width, this._appsSection.height); // Move the selection to the applications section if it was in the docs section. @@ -356,7 +353,7 @@ Sideshow.prototype = { this._moreAppsMode = false; - this._moreAppsText.hide(); + this._moreAppsLink.actor.hide(); this._appsSection.set_clip(0, 0, this._appsSection.width, this._appsSection.height); @@ -410,12 +407,12 @@ Sideshow.prototype = { this._appDisplay.updateDimensions(this._width + this._additionalWidth, this._itemDisplayHeight + SIDESHOW_SECTION_MISC_HEIGHT, EXPANDED_SIDESHOW_COLUMNS); - this._moreAppsText.text = "Less..."; + this._moreAppsLink.setText("Less..."); } else { this._appDisplay.updateDimensions(this._width, this._appsSectionDefaultHeight - SIDESHOW_SECTION_MISC_HEIGHT, SIDESHOW_COLUMNS); - this._moreAppsText.text = "More..."; + this._moreAppsLink.setText("More..."); } - this._moreAppsText.show(); + this._moreAppsLink.actor.show(); }, // Sets the 'More' mode for browsing documents. Updates the documents section to have more items. @@ -430,7 +427,7 @@ Sideshow.prototype = { if (!this._appsSection.has_clip) this._appsSection.set_clip(0, 0, this._appsSection.width, this._appsSection.height); - this._moreDocsText.hide(); + this._moreDocsLink.actor.hide(); this._docsSection.set_clip(0, 0, this._docsSection.width, this._docsSection.height); // Move the selection to the docs section if it was in the apps section. @@ -469,7 +466,7 @@ Sideshow.prototype = { this._moreDocsMode = false; - this._moreDocsText.hide(); + this._moreDocsLink.actor.hide(); this._docsSection.set_clip(0, 0, this._docsSection.width, this._docsSection.height); @@ -520,12 +517,12 @@ Sideshow.prototype = { this._docDisplay.updateDimensions(this._width + this._additionalWidth, this._itemDisplayHeight + SIDESHOW_SECTION_MISC_HEIGHT, EXPANDED_SIDESHOW_COLUMNS); - this._moreDocsText.text = "Less..."; + this._moreDocsLink.setText("Less..."); } else { this._docDisplay.updateDimensions(this._width, this._docsSectionDefaultHeight - SIDESHOW_SECTION_MISC_HEIGHT, SIDESHOW_COLUMNS); - this._moreDocsText.text = "More..."; + this._moreDocsLink.setText("More..."); } - this._moreDocsText.show(); + this._moreDocsLink.actor.show(); } };