Привет!
Потребовалось написать простенький GUI, вообще пока нужны только кнопки, но потом может быть потребуется что-то еще, у меня есть вопрос по реализации.
Так как нужны пока только кнопки, набросала прототип, думаю мысль ясна:
Стоит ли делать так? Хотелось бы, чтобы для каждого элемента GUI, можно было самому передавать действия которые он должен выполнить, чтобы не плодить несколько разных классов кнопок. Тут возникает так же несколько проблем, где хранить сами данные кнопок, в классе Action или же в guiButton, так как обращение к ним будет затруднено из Action. Передавать ли в Action (guiElement &ptr) или сделать Action абстрактным и унаследоваться от него ActionButton и уже туда передавать guiButton.
Yuno
DXUTgui бери и копируй.
Всё равно лучше решения не придумаешь.
Yuno
std::function
>std::function
Но зачем?
void Render() <- зачем ? GUI должен рендериться в своем потоке, своими методами, сам компонент не чего о рендере знать не должен ...
Yuno
> Стоит ли делать так? Хотелось бы, чтобы для каждого элемента GUI, можно было
> самому передавать действия которые он должен выполнить, чтобы не плодить
> несколько разных классов кнопок. Тут возникает так же несколько проблем, где
> хранить сами данные кнопок, в классе Action или же в guiButton, так как
> обращение к ним будет затруднено из Action. Передавать ли в Action (guiElement
> &ptr) или сделать Action абстрактным и унаследоваться от него ActionButton и
> уже туда передавать guiButton.
1 нужно передавать не действия, а подписываться на события из контролла
2 какие данные кнопок ? кнопка это кнопка, у которой есть параметры ( базовые )
вобще иерархия должна быть приблизительно такая
View { bool userInteraction; // default false addEventListener; .... } - GUIButton { } в коде делаешь просто так GUIButton*button1 = button1->addEventListener(MESSAGE,method); function METHOD(... info) { // message resave }
как именно ты внутри реализуешь эту схему или снаружи зависит от того как тебе будет удобнее
Yuno
> >std::function
> Но зачем?
Для реализации реакции на события наиболее естественным образом. Всякие Action-классы это переголова.
Как это реализуется.
Сперва определяются базовые понятия - например GUI у нас состоит из прямоугольных контролов, возможно вложенных друг в друга и взаимодействует с пользователем через клавиатуру и мышь по определенной спецификации.
Тогда вырисовывается что то типа:
class Control { int x, y; int width, height; std::list<Control *> childs; Contorl *owner; virtual bool IsUnderPoint(int x, int y ); virtual void MouseMove( int x, int y ); virtual void MouseClick( int x, int y ); virtual void Paint( ); // возможно более богато типа Paint( Canvas &c ) };
Заметим, что реализация IsUnderPoint должна сперва опрашивать childs (получается что рекурсивно) на предмет не попал кто то из них под мышь, и естественным образом возникает понятие Z-order.
Код управляющий всем этим безобразим при получении от ОС клика мыши допустим что делает - ищет по IsUnderPoint на кого мышь попала и вызывает ему MouseClick. Нормально.
Кроме того нужны понятия фокуса (активного контрола) для ввода с клавиатуры, порядка обхода для TAB, если нужно, захвата мыши и т.п. Если пробовать писать и писать то это всё само начнёт вылазить.
Но идем далее - что должна делать та же Button получив MouseClick? Если для разных реализаций MouseClick разных кнопок придётся писать новых наследников от Button или даже от некоего более абстрактного Action, то это переголова начинается.
Идеальный вариант - когда мы можем на событие клика мыши назначить внешний метод. В С это были бы указатели на функции. В С++ возникает резонное желание потянуться по аналогии к указателям на методы, но увы, указатели на методы слишком узкий механизм непригодный по целому ряду причин для наших целей. Выручает std::function!
class Button: public Control { std::function< Button *, int x, int y > onClick; // вот это - делегат /*virtual*/ void MouseClick(int x, int y ) { if ( onClick ) onClick( this, x, y ); }; };
Важно тут то, что Button не знает что ему вменят в onClick - указатель на функцию или указатель на метод класса, или даже лямбду - для него выглядит так что он просто кому то посылает сообщение что состоялось нажатие. В коде использовать такую кнопку становится очень просто:
class MyWindow: public Window // что то такое тоже должно быть. или Application. { Button btn1; ... btn1.owner = this; btn1.onClick = std::bind(&MyWindow::onButton1Click, this, _1, _2, _3 ); // что это такое - см. доки ... void onButton1Click( Button *sender, int x, int y ) { show( std::string( "button " ) + control_to_string( sender ) + " was clicked at: " + int_to_str( x ) + ":" + int_to_str( y ) ); }; };
Заметим - мы не создаём чертову кучу оголтелых классов для разных реализаций нажатия - это конкретные методы, обычные регулярные кусочки кода какого то контрола же (допустим составного, собственно MyWindow от Window тоже обычно наследуется от Control, таки да), которые пристёгиваются к кнопке через его std::function onClick налету. Такая архитектура обычно называется событийной.
Yuno, гуй представляет собой mvc в сферическом факкуме.
То бишь это система из трех модулей: модель, контролл, вьюха.
Модель:
Контроллер:
Вьюха:
Yuno
>Стоит ли делать так? Хотелось бы, чтобы для каждого элемента GUI, можно было самому передавать действия которые он должен выполнить
>самому передавать действия которые он должен выполнить
>самому
Ой палишься )
=A=L=X=
Kartonagnick
Большое спасибо за объяснения, не сильно поняла конечно, особенно в плане рендера, не привязывая конкретный рендер к объектам гуи, как именно в таком случае содержать данные, спрайтов, шрифтов и.т.д.? Все же попробую разобраться, может что получится или посмотрю исходники HGE и на их основе сделаю свой.
nes
Лал, была когда-то привычка писать от мужского пола, иногда проскальзывает. ;)
Yuno
>Лал, была когда-то привычка писать от мужского пола, иногда проскальзывает. ;)
Хлюп хлюп хлюп )
Возьми отсюда. Проще вряд ли придумаешь.
http://dxgames.narod.ru/other/cgl/cgl.htm
Тема в архиве.