libGUI

Discussing libraries simplifying applications development
  • Насколько я понимаю, память под функции DLL выделяется самой системой .Поэтому "высвобождение памяти под функции" - бессмысленная фраза.Или я что-то не так понимаю ?
    Да ты прав. Ты загружаешь DLL потом, просишь у системы память и экспортируешь т.е. перемещаешь туда функции. Или я не прав ?
  • Сделать систему сообщений на уровне приложения ничего не мешает. В пределах программы можно сделать всё что угодно.

    Я бы предложил сделать Win-подобную организвцию элементов управления. Только не надо сразу кидаться в меня кирпичами. Вся их система работает на двух функциях SendMessage и оконном обработчике WndProc. И очень проста а всякие извращения вроде MFC её только портят.
    Можно сделать одно окно верхнего уровня, которое будет получать системные сообщения и раздавать их дочерним элементам управления. Контролы в свою очередь могут иметь свои дочерние элементы управления. У каждого контрола своя функция-обработчик куда будут передаваться все сообщения. Сообщения от клавиатуры будут посылаться контролу который имеет фокус ввода. Сообщения от мыши контролу над которым находится курсор или захватившему мышь. Каждый контрол создаётся своей функцией которой передаются необходимые параметры. Функция выполняет все необходимые действия и возвращает указатель на элемент управления. Передача сообщения - просто вызов обработчика данного элемента управления через указатель. Адрес обработчика может храниться в первом элементе структуры каждого контрола. Тогда если eax указатель на элемент управления отправка сообщения будет выглядеть так stdcall [eax], eax, mesaage,... А функция-обработчик так: proc button_proc stdcall, ctrl:dword, message:dword,... В этом случае ДЛЛ сможет работать и с контролами которые реализованы в программе, причём неважно на чём она написана. Библиотека обеспечит выделение памяти под элемент управления, его уничтожение и отправку сообщений а код элементов может находиться в программе или других библиотеках.
  • Serge

    Объясни пожалуйста, что ты понимаеш под словом "контрол".Иначе большая часть того, что ты написал, мне совершенно непонятна.Надо примеров по больше и по проще.
  • Контрол (control) элемент управления: кнопка, список, меню, ползунок, поле ввода и т.д. Определяем общий заголовок
    struc Control
    {
    .ctr_proc dd ? ;указатель на функцию обработчик
    .ctlr_fd dd ? ;следующий элемент в списке
    .ctrl_bk dd ? ;предыдущий элемент в списке
    .child_fd dd ? ;первый дочерний элемент
    .child_bk dd ? ;последний дочерний элемент
    .parend dd ? ;родитель (главное окно или другой контрол)
    }

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

    Если надо создать кнопку то определяем для неё структуру с первыми полями из Control а дальше добавляем поля которые нам нужны. тогда create_button может выглядеть так (псевдо C)
    button *create_button(parent, список параметров)
    {
    button *btn = create_control(parent, sizeof(button));
    btn.ctrl_proc= button_proc;

    дальше заполняем необходимые поля
    и возвращяем указатель

    return btn;
    }

    create_control(control *parent, int size)
    функция выделяет из кучи необходимую память, помещает элемент в список дочерних элементов parent и возвращает указатель. Для уничтожения элемнта delete_control(control *ctrl) она удалит элемент управления из списка и вызовет free(ctrl) чтобы освободить память.

    Всё это похоже на ООП с объектами на асме. Таким образом в ядре организованы курсоры и звуковые буферы, "small kernel objects".
  • Уже понятнее. :)

    Тоесть структура Control хранит информацию:
    ctr_proc - какой GUI компонент будет обрабатываться обработчиком(для какого компонента создана структура)
    ctlr_fd - указатель на структуру Control для следующего элемента
    ctrl_bk - для предыдущего элемента.
    child_fd - указатель на структуру Control для самого первого компонента
    child_bk - указатель на структуру Control для самого последнего компонента
    parend - указатель на функцию которую нужно вызывать в случае активации контрола.

    Например, если создаётся кнопка, то функцией create_control создаётся структура(выделяется под неё память, заноситься информация) контрола с обработчиком кнопки.Потом обработчику контролов(контролов будет от 1 до N ) передаются состояния мыши и клавиатуры.Он в цикле вызывает обработчики кнопок.Если какя-то из кнопок активирована(например курсор мыши над кнопкой или кнопка нажата), то обработчик кнопки сообщает об этом обработчику контролов.А обработчик контролов вызывает соответствующую функцию parentd и передает ей данные от обработчика кнопок.

    В случае удаления контрола происходит освобождение памяти, занятой под структуру Control, и изменение соответствующей информации в других структурах Control.

    Serge, я правильно тебя понял или я ушёл не в ту степь ?
  • andrew_programmer

    В основном да, но не совсем.
    В терминах ООП Control это базовый класс. А ctr_proc виртуальная функция то есть это поле хранит адрес функции-обработчика событий данного элемента.

    поля child_fd и child_bk содержат указатели списка дочерних элементов, то есть каждый контрол может состоять из нескольких других контролов. Например список имеет полоску прокрутки, а у полоски есть кнопки. Тогда полоска будет дочерним элементом для списка а кнопки дочерними для для полоски. Это как структура каталогов. Главное окно - корневой каталог, его дочерние контролы как папки в этом каталоге, а у них могут быть свои дочерние контролы как вложенные папки. Каждый элемент управления кроме главного окна имеет родителя ( parent)

    Когда удаляется контрол то сначала удаляются все его дочерние контролы а потом он сам.

    Теперь как всё это работает.

    Главный принцип обработки событий: получил событие - свали на подчинённого, а когда он закончит работу доложи начальству.

    Главное окно получает все события от системы. Допустим нажата кнопка мыши. Окно проверяет в каком дочернем контроле находился курсор (надо добавить поле rect с координатами к структуре Control и ещё поле id с номером контрола, будет совсем похоже на Win ) и передаёт ему управление. Причём окну не обязательно знать какого типа элемент управления, оно просто вызывает функцию адрес которой находится в поле ctrl_proc и передаёт указатель на контрол как один из параметров. Если это был список он проверит свои дочерние элементы (он у него один - полоска), если курсор там - передаст управление ей, а та своей дочерней кнопке. Наконец кнопка определит что она нажата, перерисует себя и начнётся доклад начальству. Кнопка посылает полоске сообщение "нажата кнопка" и свой id. Полоска по id определяет какая кнопка нажата вверх или вниз и посылает списку сообщение "line_up" или "line_down" и тоже свой id. Список по id определит вертикальная полоска или горизонтальная и выполнит перерисовку. Дальше он может передать сообщение родительскому окну а может и не отправлять. Такие цепочки вызовов могут быть довольно длинными но сами сообщения обрабатываются просто если не забывать про иерархию и главный принцип.

    Основное удобство такой схемы в том что можно создавать сложные элементы управления из простых. А вся обработка событий происходит однотипно. Если контрол получил сообщение о перерисовке он рисует себя а потом вызыват обработчики дочерних контролов (рассылает сообщение) и т.д.
  • Serge
    Согласен, система подобная Win достаточно удобна и проста.
  • У такой схемы есть ещё одно достоинство.
    Программа может заменить обработчик контрола на свой собственный. Например надо сделать отображение нестандартного меню при клике правой кнопкой мыши на поле ввода или сделать свою обработку вводимых символов. Программа сохраняет адрес старой функции и заменяет его своей. Теперь все сообщения предназначенные контролу поступают в эту функцию. Если это сообщение от мыши или клавиатуры то функция выполнит необходимые действия, а если нет то вызовет сохранённый обработчик. Таким образом любая программа может легко изменить поведение контрола не меняя при этом код ДЛЛ.
  • Я тут все эти дни многократно прокручивал идею реализации libGUI с использованием контролов.Когда казалось, что всё уже понятно и я приступал к реализации, то возникали некоторые нестыковки с тем, что описано Serge-ем выше.С одной стороны предлагается создать список из контролов, а с другой строны пример с реализацией кнопки(пример Serge-а) указывает на то, что в библиотеке libGUI должны содержатьсятолько базовые элементы GUI, а связывание их в списки должна производить другая библиотека.

    Вот давайте рассмотрим пример.Скролер.

    Мы вызываем из библиотеки функцию create_control(parent, sizeof(button));
    У нас возвращается указатель на зарезервированную структуру для родителя(parend).Потом в поле child_fd это структуры мы должны занести указатель на структуру для скролера.Казалось бы, нужно просто вызвать еще раз create_control(scroler, sizeof(scroler)), НО в структуре для скролера есть поля: child_fd, child_bk.Первое поле должно содержать указатель на контрол для первой кнопки скролера, а второй на контрол для второй кнопки скролера.А контролы для кнопок скролера имеют другой размер.Значит create_control должен знать, структура для какого контрола ему передается и в соответствии с этим выделять память.Тоесть нужно чтобы для каждого вида контрола были свои функции выделения и освобождения памяти.
    Можно сделать чтобы create_control просто выделял память для структуры, а размер стуктуры передавался в качестве параметра(это и предлжено в примере Serge-а).Но тогда связывать контролы в список придётся другой библиотеке, а libGUI будет содержать только базовые GUI элементы.

    Возникает естественный вопрос.Как поступить в реализации с контролами ?
  • Сколлер является одним из базовых контролов, и нет смысла лепить его из других базовых контролов (это не является логически правильным).
  • Честно говоря я тоже так думаю.Но так как другие люди не высказывались, то вариант с кнопками оставался.Теперь скорее всего он подлежит изменению.

    Тем не менее вопрос об выделении и освобождении памяти для списка из контролов остаётся открытым.Мне кажется, что надо делать для каждого типа контролов свою функцию выделения и освобождения памяти.А также свою функцию для посылки сообщения(перерисовки компонентов).

    А как считаете вы ? Какие варианты у вас ?
  • andrew_programmer

    У всех контролов одинаковый заголовок. Посмотри в исходниках ядра в const.inc структуры APP_OBJ, CURSOR, EVENT и STREAM в /drivers/main.inc У всех структур заголовок совпадает с APP_OBJ поэтому они легко объединяются в один список. Для кода который манипулирует списком важны только поля из APP_OBJ а остальные не имеют значения. Тоже самое и с контролами.

    struct BUTTON
    {
    CONRTROL ctrl;
    //дальше поля специфичные для кнопок
    }

    struct SCROLLER
    {
    CONRTROL ctrl;
    //кнопки вверх/вниз или влево/вправо в зависимости от ориентации
    button* btn_up;
    button* btn_down;
    //дальше поля специфичные для прокруток
    }
    stuct LISTBOX
    {
    CONRTROL ctrl;
    scroller *scrl;
    //дальше поля специфичные для списков
    }

    Для работы со списком нужны только общие поля CONRTROL остальные не имеют значения.

    Главное окно имеет такой же заголовок CОNTROL, только у него нет родителя.

    >Мы вызываем из библиотеки функцию create_control(parent, sizeof(button));
    >У нас возвращается указатель на зарезервированную структуру для родителя(parend).

    Не так.
    parent передаётся в функцию как указатель на родителя а функция возвращает структуру нужного размера с заголовком CONTROL.

    Сначала программа создаёт главное оконо которое будет принимать все сообщения от системы. Это окно родительское для
    остальных контролов.

    Если надо создать скроллер мы вызываем функцию create_scroller() ипередаём ей указатель на родительское окно как parent.

    Псевдокод create_scroller()

    scroller* create_scroller(control* parent, int x, int y, необходимые_параметры)
    {
    scroller* scr;

    //создаём структуру необходимого размера и помещаем ее с список
    //дочерних контролов для parent

    scr = (scroller*)create_control(parent, sizeof(scroller));

    //заполняем необходимые поля

    scr->ctrl_proc = scroller_proc;
    scr->x = x;
    scr->y = y;
    ...
    ...

    // создаём дочерние кнопки
    //для кнопок родителем будет скроллер поэтому передаем его указатель
    //как parent
    scr->btn_up= (button*)create_button(scr, остальные_параметры);
    scr->btn_down= (button*)create_button(scr, остальные_параметры);

    //возвращяем указатель

    return scr;
    };

    Этот код для примера. Внутренняя реализация и скроллеров и списков может быть любой. Делать кнопки самому или использовать дочерние элементы решает программист. Кнопка скроллера в Win ведёт себя так же как и обычная кнопка. Она подсвечивается если навести на неё курсор мыши, если нажать кнопку мыши переходит в нажатое состояние, если убрать курсор не отпуская кнопку переходит в отжатое состояние и т.д. В первом случае придётся дублировать весь код работы с кнопками.

    >Мне кажется, что надо делать для каждого типа контролов свою функцию выделения и освобождения памяти.А также >свою функцию для посылки сообщения(перерисовки компонентов).

    Зачем так всё усложнять ? Выделение памяти сводится к malloc. create_object это malloc с расширенными возможностями.
    Если делать отдельные функции для отправки сообщений то надо знать тип контрола, а вся вся система основана на том что это не важно. По сути это ООП система. CONTROL - базовый класс. Все остальные производные от него. ctrl_proc() виртуальная функция. create_control() - специализированная new. Примеры можно найти в любой книге по ООП и С++ начиная со Страуструпа.
  • Serge

    Как со списками работать я знаю.Мне было непонятно другое.

    >Если надо создать скроллер мы вызываем функцию create_scroller() ипередаём ей указатель на родительское окно как parent.

    Вот именно.Я про это и говорил, только другими словами.
    Для каждого GUI компонента нужно иметь в DLL функции : создания компонента(занесения информации в структуру, в которой есть заголовок control и поле для остальных параметров, необходимых компоненту(тоесть организация связянного списка для данного компонента)),функция посылки сообщения компоненту, функция удаления компонента.
    Созданием или удалением потомков компонента занимаются функции из DLL предназначенные для этого компонента.

    Остаётся ещё один вопрос.
    Если я хочу создать 10 скролеров, значит они должны иметь 10 parend-ов (у каждого скролера свой parend) ?
    И при создании каждого parend-а он добовляется в список, а при удалении убирается из него ?
  • andrew_programmer
    parent - родитель. Ещё раз, это как в структуре каталогов. /rd/1 - корневой каталог /3d /demos /develop дочерние элементы ( child) . Parent у них один - /rd/1 Так же и с контролами. В типичном случе когда есть одно окно и несколько контролов в нём это окно и будет родителем для всех контролов.

    >Внутренняя реализация и скроллеров и списков может быть любой.
    Я имел ввиду listbox

    >Для каждого GUI компонента нужно иметь в DLL функции : создания компонента(занесения информации в структуру, в которой >есть заголовок control и поле для остальных параметров, необходимых компоненту(тоесть организация связянного списка для >данного компонента)),функция посылки сообщения компоненту, функция удаления компонента.
    >Созданием или удалением потомков компонента занимаются функции из DLL предназначенные для этого компонента.

    Функция отправки сообщений одна для всех компонентов. Для нее не важен тип контрола. Удаление контрола может делать код в обработчике контрола. Обычно всё сводится к тому что код освобождает все ресурсы а потом вызывает destroy_control() которая удалит его из списка и освободит память. Если у контрола были свои дочерние контролы то destroy_control() сначала уничтожит их, то есть отправит сообщение о ликвидации.
  • Who is online

    Users browsing this forum: No registered users and 1 guest