Как организовать иерархию пользовательского интерфейса и правильно информировать элементы событий? - PullRequest
0 голосов
/ 24 мая 2019

Библиотеки, такие как GLFW, информируют программиста, использующего программу, только через определенные обратные вызовы типа «Вот куда мышь переместилась: (x, y)» о входе пользователя.

A «TLDR», каков ваш мета-вопрос??»заблаговременно: есть ли учебник, руководство, справочник или любой другой ресурс о том, что разработчики программного обеспечения или фреймворки, такие как Java Swing, JavaFX, AWT, QT, SDL, GTK и т. д. думали, обдумывали и решали, когда они появятсяреализовать свой инструментарий GUI / библиотеки / рамки?Где я могу найти людей, которые думают об этом?

Идем отсюда, получая (x, y) координаты курсора мыши, вычисляя разницу от предыдущего экземпляра вызова, чтобы получить (dx,dy) значение, мне нужно было бы сообщить элементам пользовательского интерфейса в моем пользовательском интерфейсе о том, что произошло, чтобы их можно было соответствующим образом обновить.

Первый вопрос: какие специальные элементы пользовательского интерфейса могут существовать в обобщенном пользовательском интерфейсе?

Поскольку кто-то хочет организовать свой пользовательский интерфейс в, возможно, глубокой иерархии, система управления пользовательским интерфейсом знает только несколько элементов пользовательского интерфейса верхнего уровня.Поскольку существует множество различных способов представления и упорядочения элементов пользовательского интерфейса внутри, обобщенный тип элемента пользовательского интерфейса поддерживает ответ только на один вопрос: «Какой элемент пользовательского интерфейса ниже этой (x, y) координаты для вас?»(отныне вызывается: get_hotspot (x, y) ).Элемент пользовательского интерфейса, который не находится в этой позиции, ответит «ничего», в то время как элемент пользовательского интерфейса, который находится непосредственно под курсором мыши, вернется сам, а элемент пользовательского интерфейса, содержащий подэлементы, которые сами находятся под курсором мыши, вернет суб-элемент ниже, если это так, или сам, если нет.

С точки зрения того, что существуют «специальные» элементы пользовательского интерфейса, которые необходимо отслеживать отдельно, я могу думать о следующем:

  • Элемент, находящийся непосредственно под курсором мыши: HOTSPOT
  • Элемент, по которому пользователь щелкнул мышью (но еще не отпустил): один элемент DRAGGED для каждой кнопки мыши.Должно ли это называться элементом GESTURE SOURCE?
  • Элемент, который получает ввод с клавиатуры или геймпада, организация FOCUS

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

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

Поэтому самой сложной функцией является обратный вызов "события" движения мыши (здесь пока нет переноса событий), это голые кости): Прежде чем что-либо случится, все элементы DRAGGED должны быть проинформированы о движении.

После этого мне нужно будет управлять текущим HOTSPOT, если он существует: - Мышь могла бы простодвигался в пределах своих границ.- Мышь могла выйти за пределы своих границ.- Мышь могла переместиться в элемент пользовательского интерфейса внутри границ текущей горячей точки.

Последний случай меня смущает из-за всех различных случаев, которые могут произойти здесь и которые необходимо учитывать:

  • Мышь не покинула предыдущий HOTSPOT, она просто ввела новый элемент ВНУТРИ предыдущего, поэтому информирование предыдущего HOTSPOT об уходе мыши здесь будет ошибочным.
  • Если мышь входит в элемент и одновременно входит в подэлемент (например, поскольку границы двух элементов одинаковы), то и содержащий, и содержащийся элементы должны быть проинформированы о том, что мышь входит в них , Это трудно, если функция get_hotspot (x, y) возвращает только точку доступа непосредственно под опрашиваемым элементом, потому что наложенный менеджер пользовательского интерфейса никогда не узнает, что два целых элемента были введены мышью. Использование стека горячих точек (объясняется ниже) может привести к ошибкам стека в случае, если это произойдет.

Аналогичные проблемы возникают, когда мышь оставляет элемент:

  • Мышь оставила элемент в родительском элементе? Если это так, родитель не должен быть проинформирован о том, что мышь теперь ввела его, потому что он никогда не уходил.
  • Мышь покинула элемент И его родителя одновременно? Если это так, родитель также должен быть проинформирован об уходе мыши.

Это второе замешательство заставляет меня поверить, что использование СТЕКА ГОРЯЧИХ МЕСТОВ является способом решения этих вопросов. Если событие должно быть обработано дочерним элементом, я бы проинформировал его о том, что мышь ушла, извлеките его из стека и затем рекурсивно вызовите процедуру уведомления снова, чтобы сообщить следующий элемент в стеке, пока ничего не изменится.

Другие вопросы возникают как:

  • Если сразу за уведомлением «enter» сразу следует уведомление «move» о самом элементе, в который только что вошла мышь, или о том, что движение будет отправлено на него только при следующем перемещении мыши ВНУТРИ нового HOTSPOT

Завершив эту PREAMBLE, я могу предоставить некоторый код того, как я разработал ИНТЕРФЕЙС для UI Manager (здесь, в C ++):

// Focus Management for Keyboard Focus
    void request_focus(UI::Element &focus);
    void release_focus();

//Drag and Drop
    void attach_element(MouseButton button, UI::Element &drag);
    void release_element(MouseButton button, UI::Element &drag);

//Callback functions from GLFW
    virtual void notify_movement(int x, int y, int dx, int dy, input::Mouse &from);
    virtual void notify_press(int x, int y, int button, input::Mouse &from);
    virtual void notify_release(int x, int y, int button, input::Mouse &from);
    virtual void notify_scroll(int x, int y, int dx, int dy, input::Mouse &from);

    virtual void notify_key_press(int key, int scancode, int mods, input::Keyboard &from);
    virtual void notify_key_release(int key, int scancode, int mods, input::Keyboard &from);
    virtual void notify_key_repeat(int key, int scancode, int mods, input::Keyboard &from);

    virtual void notify_typed(int codepoint, input::Keyboard &from);

//API to be used by the scene-manager
    void add_element(UI::Element &element);
    void remove_element(UI::Element &element);

//Keeping track of:
    std::set<UI::Element *> elements; //all top-level elements

    std::stack<UI::Element *> hotspot_stack;
    UI::Element *current_focus;
    UI::Element *current_dragged; //for left mouse button - repeat for all other supported mouse buttons ...

С элементами пользовательского интерфейса, поддерживающими следующий API:

    virtual void notify_motion(int x, int y, int dx, int dy, UI::UserInterface &from);
    virtual void notify_drag(int x, int y, int dx, int dy, int button, UI::UserInterface &from);

    virtual void notify_press(int x, int y, int button, UI::UserInterface &from);
    virtual void notify_release(int x, int y, int button, UI::UserInterface &from);

    virtual void notify_drop(int x, int y, int button, Element *dragged, UI::UserInterface &from);
    virtual void notify_scroll(int x, int y, int dx, int dy, UI::UserInterface &from);

    virtual void notify_key_press(int key, int scancode, int mods, UI::UserInterface &from);
    virtual void notify_key_release(int key, int scancode, int mods, UI::UserInterface &from);
    virtual void notify_key_repeat(int key, int scancode, int mods, UI::UserInterface &from);

    virtual void notify_typed(int codepoint, UI::UserInterface &from);

    virtual void notify_enter(int x, int y, UI::UserInterface &from);
    virtual void notify_leave(int x, int y, UI::UserInterface &from);

    virtual void notify_focus_gained(UI::UserInterface &from);
    virtual void notify_focus_lost(UI::UserInterface &from);

    //Looking deep into the Code of JFX I mentioned one can also support:
    virtual void notify_dragged_over([...]);

Почему все эти мысли были вложены в это? Хорошо, если у вас есть кнопка пользовательского интерфейса, которую пользователь нажимает и удерживает левой кнопкой мыши, затем перетаскивает мышь от кнопки мыши, НЕ отпускает кнопку мыши, повторно вводит кнопку пользовательского интерфейса тем же жестом, и ТО отпускает левая кнопка мыши, кнопка должна считаться нажатой, в то время как кнопка должна изменить состояние с «IDLE» на «FOCUSED» на «DEPRESSED» на «IDLE» (при выходе) обратно на «DEPRESSED» (при повторном входе в тоже самое движение)! (Вы можете попробовать это, например, на Reddit, когда делаете движение, упомянутое выше, на кнопках «сворачивания» рядом с метаданными публикации).

А теперь актуальный вопрос, связанный с названием этого вопроса:

Я что-то пропустил? Я думаю, что-то ужасно неправильно? Этот подход вообще выполним или есть какой-то общий способ сделать это, который принят со времен Windows 3.1, и я просто не могу найти, где эта информация расположена?

Как организовать иерархию пользовательского интерфейса и информировать элементы обо всех событиях, происходящих ПРАВИЛЬНО?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...