Как сделать оконный менеджер?

Я пробовал писать код несколько раз, но каждый раз приходил к ошибке. По сути, я пытаюсь сделать «окна», похожие, скажем, на Explorer, Paint, MediaPlayer, где вы можете перетаскивать их, взаимодействовать с ними, сворачивать и закрывать. Конечно, если вы щелкнули по окну, окно под ним (они могут перекрываться) не должно пострадать.

Я знаю, как это сделать, у меня есть список класса, который я называю Window, перебираю его, и я взаимодействую только с первым окном, чтобы указать местоположение щелчка мыши. Таким образом, перекрытие других окон не пострадает.[1]

Затем мне нужно было сделать так, чтобы две перекрывающиеся кнопки не активировались, когда пользователь нажимает на «пересечение обеих кнопок». Я справился с этим, используя тот же метод, что и выше.[2]

Но проблема, с которой я столкнулся сейчас, заключается в том, что если я удерживаю левый щелчок, но затем решаю не нажимать кнопку, я перетаскиваю мышь от кнопки и отпускаю левый щелчок, так что событие нажатия кнопки не будет активирован. Но когда я убираю мышь за границы кнопки и, скажем, в другую... активируется новая кнопка. Чего не должно быть.[3]

Моя установка такова: у меня есть класс под названием Window. В Window у меня есть список классов с именем Interface (аналогично классу Control в WinForms). И в каждом интерфейсе есть структура, которая содержит 4 логических значения, если левый/правый в настоящее время не работают, и если они были отключены в предыдущей обработке. (предлевый, предыдущий правый, текущий левый, текущий правый)

Итак, я готов отказаться от этого (пока нет, поэтому у меня есть исходный код), но мне нужна хорошая структура для создания объектно-ориентированного типа приложения. Однако я не использую WinForms. Мне нужна помощь только со структурой, поэтому код не нужен, достаточно описания. Мне нужно избежать 3 проблем, о которых я упоминал выше.


person Dave    schedule 28.09.2013    source источник
comment
Вы говорите, что не используете WinForms, так какую платформу вы используете? Вы все еще должны показать свой код.   -  person OneFineDay    schedule 29.09.2013
comment
Я использую Xna. Однако только потому, что он может отображать быстрее, чем GDI. Код очень длинный. Должен ли я загрузить его в Pastebin или просто отредактировать небольшую часть?   -  person Dave    schedule 29.09.2013
comment
Я бы сказал только код, с которым у вас проблемы. Таким образом, мы могли видеть, были ли замечены какие-либо оптимизации или ошибки. Почувствуйте, как события реагируют и как они расположены, поможет вашей логике.   -  person OneFineDay    schedule 29.09.2013


Ответы (2)


Создание собственного менеджера окон — непростая задача. Я знаю это, потому что я тоже делаю один;)

Вы можете использовать существующее, хотя, возможно, и не самое лучшее решение, например, Nuclex.UI, который я лично отверг, когда впервые увидел, но если вы не настроены на создание собственного WM, я предлагаю использовать его или гибридный подход WinForms-XNA.

Но если вы действительно твердо настроены на реализацию собственного оконного менеджера, вы должны понимать, как работает любой другой WM. Поскольку мы говорим о XNA, это означает Windows, а это означает Windows Explorer, у которого есть чему поучиться.

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

  • Чтобы отслеживать все окна, я использую Dictionary<string, Window>, где Window — это пользовательский класс, а string — его уникальное имя для редких случаев, когда мне приходится вызывать окна по имени. Думайте об этом как о GUID или дескрипторе окна. Но вы можете просто сделать так, чтобы «Форма» появлялась только один раз и сохраняла все ссылки в статических переменных.

  • Чтобы WM понял, какой элемент управления вы нажимаете, я использую прямоугольники и проверяю, содержат ли они Point, который находится в координатах курсора и имеет размер {1; 1} пикселей, что, вероятно, примерно так же, как это делается в проводнике Windows. Для этого ваш WM должен знать, в каком порядке обновлять активные окна. Обычно вы хотите начать с самого верхнего окна и продолжить к концу списка активных окон. Для этого вы можете просто перебрать список с помощью цикла foreach.

  • Но это еще не все, потому что каждое окно само по себе является Container, что означает, что оно содержит другие элементы управления, некоторые из которых могут быть сами Container, например класс WinForms Panel. Это означает, что вам нужно выполнить итерацию по каждому из элементов управления Children Windows. Порядок обновления также должен иметь смысл — обновление от самого верхнего дочернего элемента к самому нижнему, рекурсивно для элементов управления Container, если в них также есть элементы Container. По сути, это означает, что вы захотите реализовать рекурсивный метод GetAllControls() для вашего класса WindowManager, который перебирает все Containers и возвращает список всех Control.

  • Отрисовка всех этих Control должна выполняться в порядке, обратном их обновлению, поэтому вы можете просто GetAllControls().Reverse() и перебирать его в цикле foreach.

  • Где рисовать и что обновлять, зависит от всех родительских контейнеров текущего контейнера и их общего смещения от верхнего левого угла игрового окна. Я решаю эту проблему, сохраняя ссылку ParentContainer во всех дочерних элементах управления, чтобы получить соответствующие DrawRectangle и обновить области с помощью рекурсивных свойств.

  • Когда вы щелкаете где-нибудь на экране, и щелчок регистрируется на Control, заставьте WindowManager помнить об этом (bool clickRegistered) и не запускайте никакие события OnClick на каких-либо базовых Control.

  • Проводник Windows запоминает элемент управления, на который вы нажали, и активирует его событие OnRelease, если затем отпустить курсор в области обновления того же элемента управления. Таким образом, в основном диспетчер Windows делает что-то только тогда, когда вы отпускаете кнопку мыши. Вы можете настроить WindowManager и Control для обработки событий щелчка по-разному, например, запускать событие сразу после нажатия кнопки мыши, то есть OnMouseDown. Но помните, что Microsoft не нубы, и есть причина такого поведения в проводнике Windows, и это потому, что если вы случайно нажмете кнопку мыши не там, где вы собирались, вы все равно можете исправить это, переместив курсор за пределы обновления нажатого элемента управления. область и не запускать ее действие.

В этот момент вы можете подумать: «Действительно ли стоит все это реализовывать?» Для меня ответ был «может быть», потому что я был полным нубом в C# и XNA в то время, когда я начинал, и теперь я знаю, что моя игра, которая изначально должна была использовать некоторый оконный менеджер, выиграет от моего собственного Внедрения WM гораздо больше, чем от готовых сторонних решений. Кроме того, это отличное упражнение в логике и программировании.

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

person user1306322    schedule 28.09.2013
comment
Большое спасибо! Я уже делал несколько пуль, которые вы указали, но теперь я думаю, что смогу правильно сделать оконный менеджер, как вы сказали. Я посмотрю, как это получится. - person Dave; 29.09.2013
comment
Пришло время отметить это как ответ. Информация, которую вы дали, была очень полезна в то время и актуальна и сегодня. - person Dave; 28.03.2021

Вместо того, чтобы иметь структуру с 4 булевыми значениями (аналогично xna), как насчет того, чтобы указать, где «находится» мышь. Таким образом, в некотором смысле мышь находится в окне номер 5, который является Paint, и пользователь удерживает мышь на интерфейсе /control номер 2, который является кнопкой. Похоже, это может сработать.

person null    schedule 28.09.2013
comment
Звучит как план, однако мне нужна структура 4 bool, чтобы я мог создавать события. (prevLeft опущен, currLeft не опущен означает, что мышь только что была отпущена, иначе говоря, событие щелчка). - person Dave; 29.09.2013