Сосредоточьтесь на MouseOver в Windows с помощью Ogre3D

У меня есть приложение, использующее Ogre3D для создания нескольких окон рендеринга, и я использую решение, опубликованное здесь, для поддержки не- эксклюзивный ввод мыши в эти окна. Однако я обнаружил, что мне нужно физически щелкнуть окно рендеринга, прежде чем оно восстановит фокус, тогда как мне бы очень хотелось, чтобы окна рендеринга получали фокус на событии наведения мыши. Можно ли в Ogre3D/OIS зафиксировать событие наведения курсора мыши на несфокусированном окне рендеринга, а затем установить фокус для окна рендеринга?


person hatboyzero    schedule 06.03.2012    source источник
comment
Имейте в виду, что во многих оконных менеджерах получение фокуса также означает перенос окна на шрифт. Такое поведение, скорее всего, будет очень... необычным для пользователей, привыкших к своему оконному менеджеру.   -  person Nicol Bolas    schedule 07.03.2012
comment
Должным образом отмечено - в моем конкретном приложении окна выложены плиткой и имеют статическое положение, и поведение, которое вы предлагаете, действительно желательно...   -  person hatboyzero    schedule 07.03.2012
comment
Если это правда, то почему они вообще окна? Разве вы не можете просто сделать рендеринг для отдельных мозаичных видовых экранов?   -  person Nicol Bolas    schedule 07.03.2012
comment
Если бы я имел дело с одним монитором, обработка его как мозаичных окон просмотра в полноэкранном окне рендеринга была бы идеальным подходом, да. Тем не менее, я имею дело с шестью мониторами, и мне нужно иметь возможность взаимодействовать с окнами рендеринга на каждом — до сих пор лучший способ сделать это — использовать окна без полей размером с каждый монитор с мышь, настроенная для неэксклюзивного режима - к сожалению, для этого требуется щелкнуть по определенному окну рендеринга, прежде чем элементы пользовательского интерфейса в нем будут реагировать на события наведения мыши...   -  person hatboyzero    schedule 07.03.2012
comment
Есть ли у вас способ получить события выхода/входа для окон? Если вы подключите их к фокусировке, это должно помочь. Я не думаю, что Ogre или OIS обеспечивают это, поэтому вам нужно будет углубиться на один уровень в зависимости от вашей ОС...   -  person LiMuBei    schedule 28.06.2012


Ответы (1)


Чтобы поддерживать такую ​​функциональность с помощью Ogre3D в Windows, мне пришлось реализовать объект-одиночку, который хранил коллекцию всех созданных дисплеев.

class InputProcessor
{
    /// @name Types
    /// @{
public:
    /// @}

    /// @name InputProcessor implementation
    /// @{
public:
    void addDisplay(Display* _pDisplay);

    bool processMouseMoved(int _x, int _y, int _z, int _keyModifier);
    bool processMousePressed(int _keyModifier, int _id);
    bool processMouseReleased(int _keyModifier, int _id);

    static InputProcessor& getSingleton();
    /// @}

    /// @name 'Structors
    /// @{
private:
     InputProcessor();
    ~InputProcessor();
    /// @}

    /// @name Member Variables
    /// @{
private:
    typedef std::set<Display*>     Displays_type;
    Displays_type                  m_displays;
    /// @}

};  // class InputProcessor

Затем в моем UIFrameListener (который является производным от ExampleFrameListener Ogre3D) я преобразовываю координаты окна мыши в глобальные экранные координаты. Если мышь оказывается за пределами области окна, я применяю относительное движение мыши к последней записанной позиции мыши; в противном случае я просто применяю абсолютную позицию мыши в окне:

bool
UIFrameListener::mouseMoved(const OIS::MouseEvent& e)
{
    int keyModifierState = GetKeyModifierState();
    int windowLeft = m_display.getLeft();
    int windowTop = m_display.getTop();
    int windowWidth = m_display.m_pWindow->getWidth();
    int windowHeight = m_display.m_pWindow->getHeight();

    if (e.state.X.abs != 0 && e.state.X.abs != windowWidth)
    {
        m_lastX = e.state.X.abs;
    }
    else
    {
        m_lastX += e.state.X.rel;
    }
    int x = windowLeft + (m_display.m_width * m_lastX) / windowWidth;

    if (e.state.Y.abs != 0 && e.state.Y.abs != windowHeight)
    {
        m_lastY = e.state.Y.abs;
    }
    else
    {
        m_lastY += e.state.Y.rel;
    }
    int y = windowTop + (m_display.m_height * m_lastY) / windowHeight;

    int z = 0;
    if (e.state.Z.rel != 0)
    {
        z = e.state.Z.rel / -120;
    }

    return InputProcessor::getSingleton().processMouseMoved(x, y, z, keyModifierState);
}

А в InputProcessor::processMouseMoved() я определяю, в каком окне находится курсор мыши (если он есть), а затем соответствующим образом устанавливаю фокус, т.е.

bool
InputProcessor::processMouseMoved(int _x,
                                  int _y,
                                  int _z,
                                  int _keyModifier)
{
    bool found = false;

    Displays_type::iterator iter = m_displays.begin();
    while (iter != m_displays.end() && !found)
    {
        int left = (*iter)->getLeft();
        int top = (*iter)->getTop();
        int width = (*iter)->m_pWindow->getWidth();
        int height = (*iter)->m_pWindow->getHeight();

        if (left <= _x && left + width  > _x &&
            top  <= _y && top  + height > _y)
        {
            found = true;
        }
        else
        {
            iter++;
        }
    }

    if (iter != m_displays.end())
    {
        int left = (*iter)->getLeft();
        int top = (*iter)->getTop();

        (*iter)->m_pContext->ProcessMouseMove(
            _x - left, _y - top, _keyModifier
        );

        (*iter)->m_pContext->ProcessMouseWheel(_z, _keyModifier);

        if (!(*iter)->hasFocus())
        {
            (*iter)->setFocus(true);
        }
    }

    return true;
}

А в реализации Display у меня есть метод Display::setFocus(), который устанавливает фокус на соответствующее окно:

void
Display::setFocus(bool _hasFocus)
{
    if (m_handle != NULL && _hasFocus)
    {
        SetFocus(m_handle);
    }
}
person hatboyzero    schedule 08.08.2012