Traduza

Translate to EnglishÜbersetzen Sie zum Deutsch/GermanΜεταφράστε στα ελληνικά/GreekПереведите к русскому/RussianOversetter til Norsk/NorwegianÖversätta till Svensk/Swedishहिनà¥à¤¦à¥€ अनà¥à¤µà¤¾à¤¦ करने के लिà¤/Hindi
Tradueix al català/CatalanTulkot uz latviešu/LatvianPreložiť do slovenčiny/SlovakVertaal aan het Nederlands/Dutchترجمة الى العربية/ArabicTraduzca al Español/SpanishTraduisez au Français/French
Traduca ad Italiano/ItalianTraduza ao Português/Portuguese日本語に翻訳しなさい /Japanese한국어에게 번역하십시오/Korean中文翻译/Chinese Simplified中文翻译/Chinese TraditionalПереклад на українську/Ukrainian
Imagem de conceitos do sistema de exploração
Imagem de XSLT 2.0 e de referência de programador de XPath 2.0 (programador ao programador)
Imagem do desenvolvimento da semente do linux (ó edição)
Imagem de começar Google Maps API 3

Menus duplos para uma tecla de Shell 3.2 do GNOME

Uma das limitações atuais com as teclas do painel superior de Shell do GNOME 3.2 é que não há nenhuma sustentação para indicar menus separados para cliques diferentes da tecla de rato. Sim, sim, eu posso ouvir-se que os desenhadores de Shell do GNOME dizem com autoridade e convicção completas que este caso do uso é pelo projeto. Seus resposta a toda a desaprovação, construtiva ou de outra maneira, sobre seu projeto são geralmente que sabe melhor do que qualquer um mais e seus estudos (nunca-publicados) da usabilidade suportam seu projeto. Apenas olhe o debate da opção do menu do poder do versa da suspensão fora que entrou em erupção quando o GNOME Shell foi liberado original!

Em todo o caso, esta limitação particular irritou-me me que por um quando e decidiu recentemente olhar a edição e a tentar acima e vir com uma solução praticável simples à limitação.

Supor que eu quis o seguinte menu ser indicado quando eu usar minha tecla de rato esquerdo para ativar a tecla do pé do GNOME no painel superior:

e eu quero as seguintes opções do menu ser indicado quando eu uso minha tecla de rato direito para ativar a mesma tecla do painel superior:

Não há nada em ser a base murmura o código que os limites suportam para tal funcionalidade. Murmure de facto tem a sustentação explícita para retornar um inteiro que represente uma tecla de rato pressionada ou liberada. De acordo com a documentação da desordem (em quais murmuram é baseado), no caso de um rato padrão do rolo, os seguintes números são de confiança:

  • 1 = tecla de rato esquerdo
  • 2 = roda do rolo
  • 3 = tecla de rato direito

Para ratos com mais teclas, você pode ter que experimentar para ver que tecla particular retorna que valor particular.

Em seu código, como você determina que tecla de rato particular foi pressionada? Conecte simplesmente um alimentador de evento (alimentadores da rechamada ou do sinal de AKA) ao tecla-pressionar-evento e/ou os sinais do tecla-liberar-evento de um ator apropriado. No alimentador de evento, get_button do uso () para retornar o inteiro que representa a tecla de rato específica que foi pressionada e/ou liberada. Você pode usar uma única função para sinais da imprensa e da liberação.

   ...

   _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
        }
    },

    ...


Um tecla-pressionar-evento é emitido quando uma tecla de rato é pressionada, mas liberado não necessariamente, em um ator reactivo. Um tecla-liberar-evento é emitido quando uma tecla de rato é liberada em um ator reactivo (mesmo se o rato estêve comprimido em algum lugar mais de antemão). Geralmente você deve explicitamente permitir a propriedade reactiva de um ator. Pela maneira, um ator reactivo (um conceito da desordem) é um ator que possa se emitir eventos do ponteiro.

Uma aproximação alternativa é usar Clutter.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
        }
    },

    ...


Para exemplos de como e onde Clutter.ClickAction é usado no código de Shell 3.2 do GNOME, no olhar em /usr/share/gnome-shell/js/ui/shellEntry.js e no /usr/share/gnome-shell/js/ui/workspace.js onde é usado ao instrumento estalado e longo-pressione a manipulação do evento.

De volta ao problema à mão. Despeja que o impedimento principal a executar menus duplos da ação no GNOME Shell 3.2 se encontra no código que executa o objeto da tecla. Veja /usr/share/gnome-shell/js/ui/panelMenu.js. Está aqui o código relevante:

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);


Se você examina o código acima, você verá que nenhuma tentativa está feita de se diferenciar entre as teclas de rato diferentes. Tanto quanto o código acima, uma tecla de rato é a mesma que uma outra tecla de rato. Talvez os desenhadores do GNOME Shell cresceram acima de utilização um único rato de Apple da tecla ou, tantos como comentadores sugeriram, o GNOME Shell foi projetado realmente para tabuletas e palmhelds onde nenhum rato está usado geralmente ou disponível.

A solução a executar a funcionalidade dupla do menu que usa teclas de rato era modificar o alimentador do sinal do buttom-pressionar-evento no código acima da tecla para determinar que tecla de rato foi pressionada e ato conformemente.

Está aqui o código fonte para uma extensão simples de Shell do GNOME que eu use ao protótipo uma execução de trabalho da funcionalidade dupla do menu:

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();
}


O código de objeto de DualActionButton é simplesmente uma versão modificada do código da tecla. Adicionar a sustentação para um menu center da tecla de rato é trivial; Eu deixarei aquele para que você faça se você o precisa.

Anote que eu não incluo uma vírgula após os pares do apelido/valor. De acordo com a documentação do Javascript de Mozilla, Douglas Crockford e outro, este são a sintaxe correta. Infelizmente, muitos colaboradores das extensões de Shell parecem não saber a sintaxe correta para literals do objeto e para incluir a vírgula desde gjs, que é baseada no motor do Javascript de Mozilla SpiderMonkey e na estrutura da introspecção de GObject, não se queixa sobre este erro de sintaxe particular.

Para aqueles leitores que não gostam de encapsulate a extensão de Shell do GNOME permita e incapacite métodos, e há completamente alguns de você de acordo com comentários e o email que eu recebo, é aqui o código fonte para a iniciação baseada função, a possibilidade e a desabilitação da extensão acima.

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);
}


Alguns povos reivindicam que o código acima para segurar uma extensão de Shell é mais simples e mais fácil compreender do que o estilo literal da codificação do objeto que eu uso geralmente. Minha resposta é que a aproximação da função o exige usar pelo menos uma variável que tem o espaço da extensão e, frequentemente épocas, as muitas tais variáveis em umas extensões mais complexas de Shell. O Javascript atualmente não padronizado de Mozilla deixou limites da palavra-chave espaço lexical de uma variável ao bloco em que a variável é definida assim como todos os blocos internos conteve o interior deixou o bloco próprio. Entretanto, se a palavra-chave do var está usada em vez do deixou a palavra-chave ou nenhuma palavra-chave está usada, o espaço da variável opta global. As variáveis globais no Javascript devem ser evitadas sempre que seja possível por causa dos efeitos secundários e dos erros que potenciais podem introduzir. Este tipo de erro do scoping pode ser difícil de encontrar e assim que eu escolho evitar inteiramente a edição. Leia o apêndice A (peças terríveis) do Javascript seminal do livro de Douglas Crockford: As boas partes se você não compreende porque as variáveis do Javascript com espaço global são más.

Pela maneira, eu não sou certo se o conceito de uma tecla dupla do menu da ação romperia directrizes da acessibilidade do computador (a11y) para o GNOME Shell mas dado que o paradigma é comum em Microsoft Windows, eu suspeito não.

Eu criei uma extensão simples de Shell do GNOME, demodualmenubutton, baseado no código acima que você pode transferir de meu Web site das extensões de Shell do GNOME.

Aprecie!

4 comentários a dual menus para uma tecla de Shell 3.2 do GNOME

  • Eu deio ser tão lacónico. Mas por que você está desperdiçando seu tempo em todas estas extensões (essas eu uso o diário pela maneira)? Eu ver seu nome e este Web site através da rede todo o tempo - eu aprendo das páginas que você afixa, mas uma pergunta permanece: Por que você não está conduzindo a codificação para o GNOME?

    • Realmente, eu gasto o tempo ou a energia muito pequena em extensões do GNOME. Simplesmente não tenha o tempo ou a inclinação. Quanto para a conduzir a codificação para o GNOME, não está indo acontecer enquanto eu discordo com o projeto e a filosofia da execução da equipe do núcleo de Shell do GNOME.

  • LONNIEFUTURE

    Eu era curioso sobre seus direitos reservados. Os direitos reservados significam seu trabalho não podem ser redistribuídos? Por que direitos reservados FOSS?

    • A licença e os direitos reservados são dois conceitos diferentes que são confundidos frequentemente no mundo de FOSS. Meu trabalho pode ser redistribuído por o GPL forneceu-lhe a licença minha observação de direitos reservados nele. Você pode adicionar sua própria observação de direitos reservados se você modifica meu trabalho mas você deve deixar minha observação de direitos reservados no lugar.

      Nos EUA, e em a maioria de países, você tem automaticamente direitos reservados a todo o trabalho publicado. Isso acontece à revelia assim que o trabalho for publicado. Certamente, você tem que coloc seu trabalho explicitamente no public domain se você não quer o trabalho ser com direitos de autor.

      Veja http://www.gnu.org/licenses/gpl-faq.html#RequiredToClaimCopyright para mais informações.

Deixe uma resposta