Есть ли способ использовать обычные символы ASCII (например, запятую) в качестве ускорителей меню wxWidgets?

#menu #wxwidgets

#меню #wxwidgets

Вопрос:

Я хочу несколько пунктов меню, которые показывают ускорители, являющиеся обычными клавишами, такими как пробел или запятая, но я не хочу, чтобы wxWidgets создавал эти ускорители самостоятельно (потому что тогда их нельзя будет использовать нигде в программе, в том числе в таких вещах, как поля редактирования).

К сожалению, wxWidgets настаивает на том, чтобы всегда преобразовывать все, что он распознает в этом столбце, в ускоритель под своим контролем, и просто стирает все, что он не распознает.

Я ищу какой-нибудь способ либо поместить произвольный текст в столбец accelerator (который, я думаю, не существует, я просмотрел исходный код), либо получить доступ к таблице accelerator, используемой для меню, чтобы я мог изменить ее сам (пока не нашел ее). Кто-нибудь может указать мне правильное направление?

Ответ №1:

Вы можете попробовать wxKeyBinder. Это позволяет вам привязывать горячие клавиши к командам (обычно элементам меню), сохранять / загружать / добавлять / удалять / изменять … их легко

Комментарии:

1. Приятно, я не знал об этом. Похоже, что это позволило бы мне модифицировать клавиши ускорителя… не мое предпочтительное решение, но допустимое. Я работаю над решением, которое немного лучше соответствует моим потребностям, но вы определенно получите 1 за это. 🙂

Ответ №2:

Я не смог найти способ получить прямой доступ к клавишам ускорителя меню, но изменение текста меню ускорителя работает так же хорошо. Вот код, который я придумал:

В заголовочном файле:

 class accel_t {
    public:
    // If idcount == -1, idlist must be null or terminated with a -1 entry.
    accel_t(): mMenu(0) { }
    accel_t(wxMenuBar *m, int *idlist = 0, int idcount = -1);
    void reset(wxMenuBar *m, int *idlist = 0, int idcount = -1);
    void restore() const;
    void remove() const;

    private: //
    struct accelitem_t {
        accelitem_t(int _id, wxAcceleratorEntry _key): id(_id), hotkey(_key) { }
        int id;
        wxAcceleratorEntry hotkey;
    };
    typedef std::vector<accelitem_t> data_t;

    void noteProblemMenuItems(wxMenu *m);
    static bool isProblemAccelerator(wxAcceleratorEntry *a);

    wxMenuBar *mMenu;
    data_t mData;
};
  

В cpp-файле:

 accel_t::accel_t(wxMenuBar *m, int *idlist, int idcount) {
    reset(m, idlist, idcount);
}

void accel_t::reset(wxMenuBar *m, int *idlist, int idcount) {
    mMenu = m;
    mData.clear();
    if (idlist == 0) {
        for (int i = 0, ie = m->GetMenuCount(); i != ie;   i)
            noteProblemMenuItems(m->GetMenu(i));
    } else {
        if (idcount < 0) {
            int *i = idlist;
            while (*i != -1)   i;
            idcount = (i - idlist);
        }

        for (int *i = idlist, *ie = i   idcount; i != ie;   i) {
            wxMenuItem *item = mMenu->FindItem(*i);
            if (item) {
                wxAcceleratorEntry *a = item->GetAccel();
                if (a != 0) mData.push_back(accelitem_t(*i, *a));
            }
        }
    }
}

bool accel_t::isProblemAccelerator(wxAcceleratorEntry *a) {
    if (a == 0) return false;
    int flags = a->GetFlags(), keycode = a->GetKeyCode();

    // Normal ASCII characters, when used with no modifier or Shift-only, would
    // interfere with editing.
    if ((flags == wxACCEL_NORMAL || flags == wxACCEL_SHIFT) amp;amp;
        (keycode >= 32 amp;amp; keycode < 127)) return true;

    // Certain other values, when used as normal accelerators, could cause
    // problems too.
    if (flags == wxACCEL_NORMAL) {
        if (keycode == WXK_RETURN ||
            keycode == WXK_DELETE ||
            keycode == WXK_BACK) return true;
    }

    return false;
}

void accel_t::noteProblemMenuItems(wxMenu *m) {
    // Problem menu items have hotkeys that are ASCII characters with normal or
    // shift-only modifiers.
    for (size_t i = 0, ie = m->GetMenuItemCount(); i != ie;   i) {
        wxMenuItem *item = m->FindItemByPosition(i);
        if (item->IsSubMenu())
            noteProblemMenuItems(item->GetSubMenu());
        else {
            wxAcceleratorEntry *a = item->GetAccel();
            if (isProblemAccelerator(a))
                mData.push_back(accelitem_t(item->GetId(), *a));
        }
    }
}

void accel_t::restore() const {
    if (mMenu == 0) return;
    for (data_t::const_iterator i = mData.begin(), ie = mData.end(); i != ie;
          i)
    {
        wxMenuItem *item = mMenu->FindItem(i->id);
        if (item) {
            wxString text = item->GetItemLabel().BeforeFirst(wxT('t'));
            wxString hotkey = i->hotkey.ToString();
            if (hotkey.empty()) {
                // The wxWidgets authors apparently don't expect ASCII
                // characters to be used for accelerators, because
                // wxAcceleratorEntry::ToString just returns an empty string for
                // them. This code deals with that.
                int flags = i->hotkey.GetFlags(), key = i->hotkey.GetKeyCode();
                if (flags == wxACCEL_SHIFT) hotkey = wx("Shift-")   wxChar(key);
                else hotkey = wxChar(key);
            }
            item->SetItemLabel(text   't'   hotkey);
        }
    }
}

void accel_t::remove() const {
    if (mMenu == 0) return;
    for (data_t::const_iterator i = mData.begin(), ie = mData.end(); i != ie;
          i)
    {
        wxMenuItem *item = mMenu->FindItem(i->id);
        if (item) {
            wxString text = item->GetItemLabel().BeforeFirst(wxT('t'));
            item->SetItemLabel(text);
        }
    }
}
  

Самый простой способ использовать это — создать свою строку меню в обычном режиме, со всеми клавишами акселератора (включая проблемные) на месте, затем создать accel_t элемент из нее примерно так:

 // mProblemAccelerators is an accel_t item in the private part of my frame class.
// This code is in the frame class's constructor.
wxMenuBar *menubar = _createMenuBar();
SetMenuBar(menubar);
mProblemAccelerators.reset(menubar);
  

Он определит и запишет клавиши ускорения, которые создают проблемы. Наконец, при необходимости вызовите функции remove и restore , чтобы удалить или восстановить проблемные клавиши ускорения. Я вызываю их через сообщения, передаваемые во фрейм, всякий раз, когда я открываю окно, которое необходимо выполнить стандартное редактирование.