En av de nåværende begrensningene med GNOME 3,2 Mantler øverste panelknapper er som det ikke er noen støtte for viseen separate menyers for forskjellig musknappklikk. Ja ja kan jeg høre GNOME Mantelkonstruktører sier med komplett myndighet og overbevisning at dette bruken tilfellet er ved design. Deres svar til noen kritikk, konstruktiv eller ellers, om deres design er vanligvis at de vet bedre enn enhver ellers og deres (aldri-publisert) brukervennlighetsstudier støtter deres design. Akkurat titt på det Suspenderer omvendt Kraft Av menyalternativdebatt som brøt fram når GNOME Mantelen opprinnelig ble utløst !
Likevel har denne spesielle begrensningen ergret meg for en stund og jeg bestemte nylig å meg se på utgivelsen og forsøket å og komme opp med en enkel gjennomførlig løsning til begrensningen.
Anta at jeg ville at den følgende menyen vist blir når jeg bruker min venstre musknapp å aktivisere GNOME Fotknapp på det øverste panelet :

og jeg vil at de følgende meny alternativene vist blir når jeg bruker min riktig musknapp å aktivisere den samme øverste panelet knappen :

Ikke er det noe i å ligging under Murr Kod som grenser støtter for slik funkjsonalitet. Faktisk Murr Har eksplisitt støtte for returnereen et heltalls som representerer en trykket eller utløst musknapp. Gir til det Rot (som Murrer på er basert) dokumentasjon, i tilfelle en normal rullmus, er de følgende antallene pålitelig :
- 1 = Forlot musknapp
- 2 = Ruller hjul
- 3 = Riktig musknapp
For mus med mer knapper kan måtte eksperimentere se du som spesielle knappreturer som spesielt verdi.
I din kode hvordan fastsetter du som spesiell musknapp ble trykkedd ? Enkelt forbind en begivenhetsbehandler (OGSÅKJENTSOM callback eller signalbehandlere) til knapp-presse-begivenheten og/eller knapp-utløsning-begivenhetene signalene av en passende skuespiller. I begivenheten behandleren bruk get_button() å returnere heltallet som representerer den spesifikke mus knappen som ble trykkedd og/eller utløste. Du kan bruke en enkel funksjon for både presse og kan utløse signaler.
...
_init: function(menuAlignment) {
PanelMenu.ButtonBox.prototype._init.call(this,
{ reactive: true,
can_focus: true,
track_hover: true
});
...
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
...
},
_onButtonPress: function(actor, event) {
let button = event.get_button();
if (button == 1) {
// do something
} else if (button == 3) {
// do something
}
},
...
En knapp-presse-begivenhet er sendt ut når en musknapp er trykkedd, men er ikke nødvendigvis utløst, på en reaktiv skuespiller. En knapp-utløsning-begivenhet er sendt ut når en musknapp er utløst på en reaktiv skuespiller (selv om musa ble trykkedd ned ett eller annet sted ellers påforhånd). Vanligvis må du eksplisitt muliggjøre den reaktive eiendomen av en skuespiller. Forresten er en reaktiv skuespiller (et Rot begrep) en skuespiller som kan sende ut viserbegivenheter.
En alternativ tilnærming er å bruke Rot.Clickaction.
...
let clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', Lang.bind(this, function(button) {
this._onButtonPress(button);
}));
this.actor.add_action(clickAction);
...
_onButtonPress: function(button) {
if (button == 1) {
// do something
} else if (button == 3) {
// do something
}
},
...
For eksempeler på hvordan og hvor Rot.Clickaction er brukt i GNOME Mantelen 3,2 koder, titt i /usr/share/gnome-shell/js/ui/shellentry.js og
Tilbake til problemet tilgjengelig. Det vender ut at hovedforhindringen til å iverksette dobbelte tiltaksmenyer i GNOME Mantel 3,2 løgner i koden som iverksetter Knappen objekt. Se /usr/share/gnome-shell/js/ui/panelmenu.js. Her er den relevante koden :
function Button(menuAlignment) {
this._init(menuAlignment);
}
Button.prototype = {
__proto__: ButtonBox.prototype,
_init: function(menuAlignment) {
ButtonBox.prototype._init.call(this, { reactive: true,
can_focus: true,
track_hover: true });
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP);
this.menu.actor.add_style_class_name('panel-menu');
this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
Main.uiGroup.add_actor(this.menu.actor);
this.menu.actor.hide();
},
function Button(menuAlignment) {
this._init(menuAlignment);
}
Button.prototype = {
__proto__: ButtonBox.prototype,
_init: function(menuAlignment) {
ButtonBox.prototype._init.call(this, { reactive: true,
can_focus: true,
track_hover: true });
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP);
this.menu.actor.add_style_class_name('panel-menu');
this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
Main.uiGroup.add_actor(this.menu.actor);
this.menu.actor.hide();
},
_onButtonPress: function(actor, event) {
if (!this.menu.isOpen) {
// Setting the max-height won't do any good if the minimum height of the
// menu is higher then the screen; it's useful if part of the menu is
// scrollable so the minimum height is smaller than the natural height
let monitor = Main.layoutManager.primaryMonitor;
this.menu.actor.style = ('max-height: ' +
Math.round(monitor.height - Main.panel.actor.height) +
'px;');
}
this.menu.toggle();
},
_onSourceKeyPress: function(actor, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
this.menu.toggle();
return true;
} else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) {
this.menu.close();
return true;
} else if (symbol == Clutter.KEY_Down) {
if (!this.menu.isOpen)
this.menu.toggle();
this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false);
return true;
} else
return false;
},
_onMenuKeyPress: function(actor, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
let focusManager = St.FocusManager.get_for_stage(global.stage);
let group = focusManager.get_group(this.actor);
if (group) {
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
group.navigate_focus(this.actor, direction, false);
return true;
}
}
return false;
},
_onOpenStateChanged: function(menu, open) {
if (open)
this.actor.add_style_pseudo_class('active');
else
this.actor.remove_style_pseudo_class('active');
},
destroy: function() {
this.actor._delegate = null;
this.menu.destroy();
this.actor.destroy();
this.emit('destroy');
}
};
Signals.addSignalMethods(Button.prototype);
Hvis du undersøker den ovenfor koden, vil se du som ingen forsøk blir laget å differensiere mellom de forskjellige mus knappene. Så fjern som er den ovenfor koden angått, er en musknapp akkurat som enda en musknapp. Kanskje vokste konstruktørene av GNOME Mantelen opp å bruke av en enkel knapp Eple mus eller, mens mange kommentatorer har foreslått, GNOME Mantelen ble virkelig utformet for tabletter og palmhelds hvor ingen mus er vanligvis brukt eller tilgjengelig.
Løsningen til å iverksette dobbelte menyfunkjsonalitetsbrukmusknapper var å modifisere buttom-presse-begivenheten signalet behandleren i den ovenfor Knapp koden å fastsette hvilken musa knappen ble trykkedd og ble fungert deretter.
Her er kilden koden for en enkel GNOME Mantelutvidelse som jeg brukt til prototyp en arbeidsimplementering av dobbelt menyfunkjsonalitet :
const St = imports.gi.St;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Lang = imports.lang;
const Clutter = imports.gi.Clutter;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
function DualActionButton(menuAlignment) {
this._init(menuAlignment);
}
DualActionButton.prototype = {
__proto__: PanelMenu.ButtonBox.prototype,
_init: function(menuAlignment) {
PanelMenu.ButtonBox.prototype._init.call(this, { reactive: true,
can_focus: true,
track_hover: true });
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
this.menuL = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP);
this.menuL.actor.add_style_class_name('panel-menu');
this.menuL.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
this.menuL.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
Main.uiGroup.add_actor(this.menuL.actor);
this.menuL.actor.hide();
this.menuR = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP);
this.menuR.actor.add_style_class_name('panel-menu');
this.menuR.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
this.menuR.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
Main.uiGroup.add_actor(this.menuR.actor);
this.menuR.actor.hide();
},
_onButtonPress: function(actor, event) {
let button = event.get_button();
if (button == 1) {
if (this.menuL.isOpen) {
this.menuL.close();
} else {
if (this.menuR.isOpen)
this.menuR.close();
this.menuL.open();
}
} else if (button == 3) {
if (this.menuR.isOpen) {
this.menuR.close();
} else {
if (this.menuL.isOpen)
this.menuL.close();
this.menuR.open();
}
}
},
_onButtonPress: function(actor, event) {
let button = event.get_button();
if (button == 1) {
if (this.menuL.isOpen) {
this.menuL.close();
} else {
if (this.menuR.isOpen)
this.menuR.close();
this.menuL.open();
}
} else if (button == 3) {
if (this.menuR.isOpen) {
this.menuR.close();
} else {
if (this.menuL.isOpen)
this.menuL.close();
this.menuR.open();
}
}
},
_onSourceKeyPress: function(actor, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
if (this.menuL.isOpen) {
this.menuL.close();
} else if (this.menuR.isOpen) {
this.menuR.close();
}
return true;
} else if (symbol == Clutter.KEY_Escape) {
if (this.menuL.isOpen)
this.menuL.close();
if (this.menuR.isOpen)
this.menuR.close();
return true;
} else
return false;
},
_onMenuKeyPress: function(actor, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
let focusManager = St.FocusManager.get_for_stage(global.stage);
let group = focusManager.get_group(this.actor);
if (group) {
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
group.navigate_focus(this.actor, direction, false);
return true;
}
}
return false;
},
_onOpenStateChanged: function(menu, open) {
if (open)
this.actor.add_style_pseudo_class('active');
else
this.actor.remove_style_pseudo_class('active');
},
destroy: function() {
this.actor._delegate = null;
this.menuL.destroy();
this.menuR.destroy();
this.actor.destroy();
this.emit('destroy');
},
};
Signals.addSignalMethods(DualActionButton.prototype);
function DemoDualActionButton() {
this._init();
}
DemoDualActionButton.prototype = {
__proto__: DualActionButton.prototype,
_init: function() {
DualActionButton.prototype._init.call(this, 0.0);
this._iconActor = new St.Icon({ icon_name: 'start-here',
icon_type: St.IconType.SYMBOLIC,
style_class: 'system-status-icon' });
this.actor.add_actor(this._iconActor);
this.actor.add_style_class_name('panel-status-button');
let item = new PopupMenu.PopupMenuItem(_("Left Menu Item 1"));
this.menuL.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Left Menu Item 2"));
this.menuL.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Right Menu Item 1"));
this.menuR.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Right Menu Item 2"));
this.menuR.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Right Menu Item 3"));
this.menuR.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Right Menu Item 4"));
this.menuR.addMenuItem(item);
},
enable: function() {
Main.panel._centerBox.add(this.actor, { y_fill: true });
Main.panel._menus.addMenu(this.menuL);
Main.panel._menus.addMenu(this.menuR);
},
disable: function() {
Main.panel._centerBox.remove_actor(this.actor);
Main.panel._menus.removeMenu(this.menuL);
Main.panel._menus.removeMenu(this.menuR);
}
};
function init() {
return new DemoDualActionButton();
}
Dualactionbutton objektkode er enkelt en modifiseret versjon av Knappen kode. Å tilføye støtte for en midtpunktmusknappmeny er banal ; jeg vil forlate at for du å gjøre hvis du trenger det.
Bemerk som jeg ikke inkluderer et komma etter etternavn/verdien paret. Gir til Mozilla Javascriptdokumentasjon, Douglas Crockford Og andre, dette er den riktige syntaksen. Dessverre kommer mange Mantler utvidelsesfremkallere fram ikke å vite den riktige syntaksen for objekt ordrett og å inkludere kommaet siden gjs, som er basert på Mozilla Spidermonkey Javascriptmaskin og det GObject Introspection rammeverk, klager ikke om denne spesielle syntaksen feilen.
For de leserne som ikke liker til encapsulate GNOME Mantelutvidelse muliggjører og udyktiggjør metoder, og det er helt et par av deg gi til kommentarer og e-post som jeg mottar, er her kilden koden for funksjon baserte start, som muliggjører og å udyktiggjøre av den ovenfor utvidelsen.
let button;
function init() {
button = new DemoDualActionButton();
}
function enable() {
Main.panel._centerBox.add(button.actor, { y_fill: true });
Main.panel._menus.addMenu(button.menuL);
Main.panel._menus.addMenu(button.menuR);
}
function disable() {
Main.panel._centerBox.remove_actor(button.actor);
Main.panel._menus.removeMenu(button.menuL);
Main.panel._menus.removeMenu(button.menuR);
}
Noen folk krever som den ovenfor koden for håndtereen en Mantels utvidelse er enklere og lettere å forstå enn objektet ordrett kodestil som jeg vanligvis bruker. Mitt svar er at funksjonen tilnærmingen krever deg ved å bruke på minstvariabel som har utvidelsesrekkevidde og, ofte tider, mange slike variablene i mer komplisert Mantel utvidelser. Den for tiden ikke-standardisert Mozilla Javascripten lot stikkord begrenser en variabel’s leksikalsk rekkevidde til blokken som som noen indre blokker i blir inneholdt innenfor det lar blokk seg. Imidlertid hvis var stikkordet er brukt i stedet for det lar stikkord eller ingen stikkord er brukt, rekkevidden av de variabele standardverdiene til global. Globale variabler i Javascript burde bli unngått hvor mulig på grunn av de potensielle bivirkningene og feilene som de kan introdusere. Denne typen scoping feilen kan være vanskelig å finne og slik jeg velger å unngå utgivelsen helt. Les Vedlegg ET (Skrekkelig Deler) av Douglas Crockford’s seminal bok Javascript : De Gode Delene Hvis du ikke forstår hvorfor Javascript variabler med global rekkevidde er ond.
Forresten er jeg ikke sikker hvis begrepet av en dobbelt tiltaksmenyknapp ville bryte datatilgjengelighet (a11y) Retningslinjer for GNOME Mantelen men gitt at mønstereksempelet er felles i Microsoft Vinduer, ikke frykter jeg.
Jeg har skapt en enkel GNOME Mantelutvidelse, demodualmenubutton, baserte på den ovenfor koden som du kan download fra mitt GNOME Mantelutvidelser Website.
Nyt !


























Jeg hater å være slik kort. Men hvorfor sløser du din tid på alle disse utvidelsene (som jeg bruker daglig forresten) ? Jeg ser din navn og denne website over garnn alt tiden – som jeg lærer fra sidene som du posterer, men en spørsmål forblir : Hvorfor aren’t som du lede koden for GNOME ?
Faktisk bruker jeg meget liten tid eller energi på GNOME utvidelser. Enkelt ha ikke tiden eller hellingen. Som for “leading koden for GNOME”, det kommer ikke til å skje mens jeg er uenig med designet og utførelsesfilosofi av GNOME Mantelkjernelag.
Jeg var nysgjerrig om din copyright. Betyr copyrighten din arbeid kan’t blir omfordelt ? Hvorfor krev opphavsrett til FOSS ?
Tillatelse og copyright er to forskjellige begreper som ofte er forvirret i FOSS verden. Mitt arbeid kan bli omfordelt pr. GPL git deg forlater min copyrightnotis på det. Du kan tilføye din egen copyrightnotis hvis du modifiserer mitt arbeid men du må forlate min copyrightnotis på plass.
I USA og I mest land, du har automatisk copyright til noen publisert arbeid. Det skjer ved standardverdi så snart arbeidet er publisert. Virkelig må du plassere din arbeid eksplisitt i det offentlige domenet hvis du ikke vil at arbeidet krevd blir opphavsrett til.
Se http://www.gnu.org/licenses/gpl-faq.html#RequiredToClaimCopyright For ytterligere informasjon.