Полностью свой TextBox на WinAPI+GDI как клон системной

Разговоры на любые темы: вы можете обсудить здесь какой-либо сайт, найти единомышленников или просто пообщаться...
FireFenix
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1640
Зарегистрирован: 25.05.2007 (Пт) 10:24
Откуда: Mugen no Sora

Полностью свой TextBox на WinAPI+GDI как клон системной

Сообщение FireFenix » 09.01.2018 (Вт) 19:23

Собственно когда-то давно я находил пример реализации полностью своего TextBox'a ... Сейчас не находится...
Хочется сделать универсальную прослойку GDI/Vulkan/OGL/etc (аля убийца wpf) и чтоб выглядело одинаково во всех контекстах... Т.е. вручную звать всякие FillRect, расчёт кегель и т.д.

Кто-нить знает простой пример на основе TextBox или аналогов? Чтоб понять какие функции и в какой последовательности стоит дёргать после CreateWindow

Я конечно понимаю, что есть RichTextBox'ы и Scintilla из Notepad++ но там уж очень много лишнего (для меня) кода помимо рендера
Птицей Гермеса меня называют, свои крылья пожирая... сам себя я укрощаю
私はヘルメスの鳥 私は自らの羽根を喰らい 飼い慣らされる

Admiralisimys
Постоялец
Постоялец
 
Сообщения: 318
Зарегистрирован: 01.06.2009 (Пн) 10:26

Re: Полностью свой TextBox на WinAPI+GDI как клон системной

Сообщение Admiralisimys » 10.01.2018 (Ср) 3:29

Вот пример из 2014 года - Fast Colored TextBox for Syntax Highlighting.

FireFenix
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1640
Зарегистрирован: 25.05.2007 (Пт) 10:24
Откуда: Mugen no Sora

Re: Полностью свой TextBox на WinAPI+GDI как клон системной

Сообщение FireFenix » 10.01.2018 (Ср) 11:16

Это я тоже находил, но это не совсем то.
В этих примерах .NET вызывает через свою прослойку System.Drawing или у WPF. Т.е. рисуется через высокоуровневую прослойку, а не прямо через GDI/Direct2D

Пока что самое близкое, что я нашёл - это Scintilla. Там идут интерфейсы для работы через GDI/Direct2D/GTK/Qt, но там чёрт ногу сломит в километрах кода...
Птицей Гермеса меня называют, свои крылья пожирая... сам себя я укрощаю
私はヘルメスの鳥 私は自らの羽根を喰らい 飼い慣らされる

The trick
Постоялец
Постоялец
 
Сообщения: 774
Зарегистрирован: 26.06.2010 (Сб) 23:08

Re: Полностью свой TextBox на WinAPI+GDI как клон системной

Сообщение The trick » 10.01.2018 (Ср) 18:19

UA6527P

FireFenix
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1640
Зарегистрирован: 25.05.2007 (Пт) 10:24
Откуда: Mugen no Sora

Re: Полностью свой TextBox на WinAPI+GDI как клон системной

Сообщение FireFenix » 11.01.2018 (Чт) 23:26

А вы батенька подлец, линковать на другой форум Изображение

Конечно спасибо за пример, но там всё равно не то, что я бы хотел...
Тут реализовано как я понял через UserControl, который есть прослойка VB6 и всё гораздо проще, т.е. ты не обрабатываешь/реагируешь на WM ивенты и сам не рисуешь каретку :)
Птицей Гермеса меня называют, свои крылья пожирая... сам себя я укрощаю
私はヘルメスの鳥 私は自らの羽根を喰らい 飼い慣らされる

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16473
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Полностью свой TextBox на WinAPI+GDI как клон системной

Сообщение Хакер » 13.01.2018 (Сб) 1:00

FireFenix, я делал свой CodeBox — полностью с нуля. Обрабатывал все сообщения, ничего не сабклассил — под моей оконной процедурной никакой другой не было (кроме DefWindowProc). Никаких, конечно OGL И прочего у меня не было, чисто WinAPI.

Ну и конечно, поскольку это CodeBox, мне было проще в том плане, что текст только моноширинный, никаких разных кеглей, никаких особых случаев с форматированием, хотя была экспериментальная поддержка BiDi-исходников. Кодом не поделюсь (да и он на Си), но если интересуют какие-то принципиальные вопросы — расскажу.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16473
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Полностью свой TextBox на WinAPI+GDI как клон системной

Сообщение Хакер » 13.01.2018 (Сб) 1:11

Кстати, то был CodeBox — с подсветкой синтаксиса, возможностью расстановки маркеров в линейке боковой (для брекпоинтов) и автодополнением. И это было 10 лет назад. А буквально в прошлом (или позопрошлом?) году я на VB написал (без WinAPI по-моему) реализацию лог-окошка, которое могло бы без тормозов переваривать огромные логи.

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

А вообще, началось это как: чисто исследовательский код по ходу своей работы вываливал много отладочной и целевой информации — первоначально через Debug.Print. По мере роста объёмов входной информации возникло, как обычно, 2 проблемы:
  • Immediate Pane имеет ограничение на количество строк, и новые строки затируют старые
  • 90 процентов времени работы программы тратилось на отработку вызова Debug.Print

Поэтому сперва было написано простенькое окошечко то ли с ListBox-ом, то ли с TextBox-ом, которое и использовалось в качетсве лога. А на уровне кода был введён интерфейс ILogTarget, у которого семантика использования походила на Debug.Print без точки с запятой и с точкой запятой. Сделано это было для того, чтобы с минимальными правками заменить цепочки вызовов Debug.Print, сильно не переписывая код, не меняя форматирования, не прибегая к необходимости склеивать из кусочков длинную строку.

Но и ListBox и TextBox оказались не в состоянии переваривать быстро большие объёмы строк с логами, там засел типичный алгоритм Маляра-Шлемеля, и по мере добавления новых строк каждое новое добавление занимало больше времени, да и лимиты на число строк/символов там неудовлетворительные.
Поэтому создана форма FLogWnd2, котороая тоже имплементировала ILogTarget, но реализация была совершенно другой (не использующей ни ListBox, ни TextBox) — скроллинг, отрисовка, поиск, копи-пейст по тексту были полностью своими.

Новое окно в лёгкую могло иметь миллион строк логгинга и не тормозить совсем. И тут нет, кстати, ничего удивительного: как ни крути, но при отрисовке нужно обработать только пару-тройку десятков строк, а быстрота добавления новых строк в набор обеспечивалась продуманной структурой для хранения данных (блочный список с отложенной оптимизацией).
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

FireFenix
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1640
Зарегистрирован: 25.05.2007 (Пт) 10:24
Откуда: Mugen no Sora

Re: Полностью свой TextBox на WinAPI+GDI как клон системной

Сообщение FireFenix » 15.01.2018 (Пн) 2:47

Хакер писал(а):Кстати, то был CodeBox — с подсветкой синтаксиса

Идея найти TextBox в том, что TextBox реализует много вещей по поводу которых есть вопросы... Т.е. сам TextBox/CodeBox не явлется целью, но хочу попозже попытаться их реализовать в полной мере :)
Как я уже сказал, моя идея состоит в том, чтоб написать прослойку, где контролы в различных средах/os/редакторах выглядели бы одинаково, ну и слизать Win стили :)

Хакер писал(а):(блочный список с отложенной оптимизацией)

А вот это интересно, не расскажешь?

Хакер писал(а):но если интересуют какие-то принципиальные вопросы — расскажу

Вопрос просто милииард, не знаю с чего начать прям... Но они все качаются больше корректной реализации архитектуры UI/UX нежели порядок и поиск вызова функций.
Я попытаюсь сформировать вопросы...

Когда мы начинаем изучать вопрос ui с самого низа, то встречаем API - gdi/directx/openg/etc и получаем первый вопрос про различия и сходства API:
1) сли ogl/dx/etc это GAPI, команды которых, драйвер "интерпретирует" на железе (видео карте), то с GDI всё как-то сложно. Если когда-то давно GDI был софтвраным, то сейчас там сложная развязка на WDDM и Direct[какой-то].
Так вот, если взять GDI и рисовать через него какую-то форму с контролами, и взять OGL, пытаясь сделать тоже самое. - Будет ли это полным аналогом? (ну естественно если стремится к схожей оптимизации - инвалидация областей и т.д.)
И на сколько я понимаю - при использовании OGL будет довольно больной темой растеризация шрифта и рисование линий, как в GDI...

Так же я помню исследовал вопрос наличия исходников WDDM в Kernel Research паке - там их нет. То и исходников GDI я так понимаю тоже нету в открытом доступе?

Дальше прикинув, как будем рисовать контрол - нам нужно внедрить его в форму и создать логику его поведения:
2) В моём скудном понимании: в Win UI основана на логике оконных сообщений, а так же сабклассинге для их (контролов) элементов; у x11/xlib похожая ситуация, только там вроде шлются/перебираются "события" и запрашивается их контекст.
Т.е. приходим к тому, что как минимум для главной формы нужна поддержка оконных сообщений (желательно в полной мере), а как быть с контролами и с их элементами?

как я понимаю, в базовом представлении цепочку рисования видим как: OS -> Paint/Expose -> Form -> Paint/Expose -> Control -> Internal call -> Element
Идеальная ли это система?

или же можем:
OS -> Paint/Expose -> Form -> Paint/Expose -> Control -> Paint/Expose -> Element
или
OS -> Paint/Expose -> Form -> Internal call -> Control -> Internal call -> Element

3) Одной из фитч оконных сообщений -> мы можем посылать сообщения из мест не связанных с UI или реализовать RPC. Является ли механизм хаком/багом и лучше ли полностью изолировать модуль UI от внешнего мира? или такая открытость является плюсом?

4) Если заморочится поддержкой множественных ЯП, то является ли COM оптимальным решением основа API под различные OS? Или может взять COM за основу и изобрести/развить в свой велосипед?
Птицей Гермеса меня называют, свои крылья пожирая... сам себя я укрощаю
私はヘルメスの鳥 私は自らの羽根を喰らい 飼い慣らされる

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16473
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Полностью свой TextBox на WinAPI+GDI как клон системной

Сообщение Хакер » 15.01.2018 (Пн) 9:35

FireFenix писал(а):написать прослойку, где контролы в различных средах/os/редакторах выглядели бы одинаково, ну и слизать Win стили

Так речь не о VB, оказывается. Правильно?

FireFenix писал(а):А вот это интересно, не расскажешь?

Ну всё довольно просто устроено. (Особенно если вспомнить, что новые строки и их фрагменты только добавляются, а никакого редактирования/удаления нет.)

Хранилище строк у меня огранизовано как массив блоков, где каждый блок — это массив строк, где каждая строка — это массив фрагментов. Такой подход (массив массивов aka блочный список) выбран потому, что, если рассмотреть весь функционал, который вовлечён в добавление новых строк и их фрагментов, то оказывается, что самое времязатратное в этом — это итеративное увеличение массива на 1 элемент. И по этой причине от подхода, когда по факту добавления каждого нового элемента нужно realloc-чить/ReDim-ить массив на 1 элемент — надо бежать.

Чуть более умные люди всегда наращивают массив сразу на тысячи большое блок новых элементов или же каждый раз вдвое. Но и от этого надо надо бежать в нашем случае (с миллионами лог-записей), потому что массив гарантирует непрерывность элементов в памяти, а ради этого, если при росте массива он своим концом упирается в занятый участок адресного пространства, приходится целиком переносить массив в другое место в АП. И в лучшем случае его приходится целиком копировать в новое место, а в худшем случае АП процесса вообще фрагментировано маленькими выделениями так, что во всём АП не найдётся одного протяжённого, достаточно длинного и непрерывного свободного участка под новый массив увеличенной длины. И тогда мы получим Out of memory при очередном увеличении. В общем, непрерывность массива в памяти в нашем случае нисколько не полезная, а наоборот, вредная черта.

Другое дело массив массивов. В этом случае верхний массив является по сути массивом указателей (на SAFEARRAY) и занимает не слишком много места — один элемент такого массива олицетворяет целый блок, который, в свою очередь, олицетворяет тысячи строк. Так что верхнеуровневый массив (массив блоков) наращивается не так часто, и размер имеет не такой большой. Сами же блоки — это отдельные небольшие массивы, которые дорастают до определённой величины, а дальше они не растут — начинает новый блок. Эти отдельные маленькие массивы могут в каком угодно месте АП размещаться, фрагментация АП на них никаких ограничений не накладывает. И что самое главное, для таких массивов можно сразу выделять столько места, сколько нужно, не делая ReDim на каждую новую строку.

Однако, в моём случае даже с такой организацией оказалось, что всё как-то слишком медленно — выяснилось, что конкатенация фрагментов строчек при формировании лога тоже съедает кучу времени. Допустим, код, работая 2 секунды, формирует миллион лог-строчек. И каждая строчка состоит из 10 фрагментов. Ну и зачем нам нужно при формировании лога делать конкатенацию этих фрагментов? Незачем.

Поэтому у меня в блоке дочерние элементы — строки — это тоже массивы, массивы фрагментов.

Дальше возникает две проблемы: при отображении из массива фрагментов нужно делать цельную строку. Но нужно делать это только с теми строками, которые сейчас отображаются. А это обычно 50 строчек из пары миллионов. Другой момент — это поиск по логу. Встроенная в VB реализация поиска по строке (InStr) всё равно быстрее, чем мы можем написать свою, а для InStr необходимо, чтобы строки были в цельном виде (т.е. склеены, а не в в иде массива фрагментов). Причём для поиска по логу нужно склеить как раз фрагменты всех строк, а не только тех, которые сейчас видимы.

Поэтому у меня использован подход с отложной (on-demand) дефрагментацией: после того, как наполнение лога закончено (или вышло из активной фазы), запускается таймер, которые проходит по всему логу и небольшими порциями проводит дефрагментацию. Кроме того, немедленной дефрагментации подвергаются те строки, которые попадают в текущую зону отображения (а это значит, что быстро поскроллив окошко с миллионами записей можно вызвать насильную дефрагментацию всего хранилища строк). Ну и запуск поиска принудительно дефрагментирует всё хранилище, при условии, что к моменту запуска поиска оно ещё не было дефрагментировано скроллингом или таймером.

FireFenix писал(а):1) сли ogl/dx/etc это GAPI, команды которых, драйвер "интерпретирует" на железе (видео карте), то с GDI всё как-то сложно. Если когда-то давно GDI был софтвраным, то сейчас там сложная развязка на WDDM и Direct[какой-то].

Я не задумывался над этим совершенно. Зачем? Если скорости вывода текста силами GDI совершенно хватает, а никакого сглаживания, никакой векторной графики и никаких причуд мне не надо. К тому же не совсем правильно говорить, что GDI был софтверным. Он всегда исполнение команд отдавал на откуп драйверу, а там уже дело за разработчиком драйвера.

По поводу п.2 — я не писал под X11, так что ничего не могу сказать.

По поводу п.3 — не очень понял конкретной подоплёки вопроса, но в общем случае это является фичей, если сообщения будут обрабатываться квази-асинхронно (PostMessage в случае с Windows). Обмен сообщениями — это ведь разновидность ООП, и при таком подходе обрабатывающую сторону не должно волновать, откуда пришло сообщение: если пришло, надо обработать как полагается.

По повоу п.4 — не понял вообще, возможно по причине несогласованности предложения.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

bon818
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 267
Зарегистрирован: 29.08.2009 (Сб) 4:49
Откуда: Ташкент

Re: Полностью свой TextBox на WinAPI+GDI как клон системной

Сообщение bon818 » 15.01.2018 (Пн) 11:42

Вот вариант, чистый WINAPI:
PowerBASIC EDM32 Source Code Editing Component
Вложения
edm32.zip
PowerBASIC EDM32 Source Code Editing Component
(118.72 Кб) Скачиваний: 157

FireFenix
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1640
Зарегистрирован: 25.05.2007 (Пт) 10:24
Откуда: Mugen no Sora

Re: Полностью свой TextBox на WinAPI+GDI как клон системной

Сообщение FireFenix » 15.01.2018 (Пн) 16:29

bon818 писал(а):Вот вариант, чистый WINAPI:
PowerBASIC EDM32 Source Code Editing Component

Да спасибо, примерно это я и искал :)

Хакер писал(а):Так речь не о VB, оказывается. Правильно?

Да, меня больше интересует сам принцип организации, чем как это сделать под конкретный ЯП.
Ну и я начал с базовых проблем, которые не дают мне покоя :)

Хакер писал(а):Я не задумывался над этим совершенно. Зачем? Если скорости вывода текста силами GDI совершенно хватает, а никакого сглаживания, никакой векторной графики и никаких причуд мне не надо. К тому же не совсем правильно говорить, что GDI был софтверным. Он всегда исполнение команд отдавал на откуп драйверу, а там уже дело за разработчиком драйвера.

Понятно.

Хакер писал(а):По поводу п.2 — я не писал под X11, так что ничего не могу сказать.

А по поводу самой организации? Т.е. нужно ли вообще контролам создаваться через WinApi? или же лучше, быть рисуемой областью, которая форма дёргает? (обьектом некоторого фреймворка без OS-API)
А так же, если у нас уже созданный контрол, то нужны ли ему оконные сообщения? или лучше/красивее/элегантнее разрулить через ООП, как вызовы от формы к детям без OS-API?

Хакер писал(а):Хранилище строк у меня огранизовано как массив блоков, где каждый блок — это массив строк, где каждая строка — это массив фрагментов.

А вот сами строки, являются ли элементами, которые рисуются (как отдельный UI объект) ? или у тебя сам CodeBox является вьюхой, а строки и фрагменты данные?

Хакер писал(а):Чуть более умные люди всегда наращивают массив сразу на тысячи большое блок новых элементов или же каждый раз вдвое. Но и от этого надо надо бежать в нашем случае (с миллионами лог-записей), потому что массив гарантирует непрерывность элементов в памяти, а ради этого, если при росте массива он своим концом упирается в занятый участок адресного пространства, приходится целиком переносить массив в другое место в АП. И в лучшем случае его приходится целиком копировать в новое место, а в худшем случае АП процесса вообще фрагментировано маленькими выделениями так, что во всём АП не найдётся одного протяжённого, достаточно длинного и непрерывного свободного участка под новый массив увеличенной длины. И тогда мы получим Out of memory при очередном увеличении. В общем, непрерывность массива в памяти в нашем случае нисколько не полезная, а наоборот, вредная черта.

Звучит как бакеты/чанки в Dictionary/Map словарях.
И на сколько я понял, раз у тебя блоки перевыделяются по требованию, то в памяти следование строк нелинейно?

И зачем вообще нужны фрагменты? почему не работать с целой строкой?
Т.е. если мы вначале выделили дефолтные 80 и поправка x2 = 160 символов, редактируем, как только вылезаем - реаллоцируем.
Или фрагменты используются типо как страницы памяти, т.е. атомарные единицы аллокации, из которых собирается регион строки?

Дада, я знаю, что тебе не нравится .NET, но мне кажется по схожей технологии работает StringBuilder - https://referencesource.microsoft.com/# ... e46ebd299f
Птицей Гермеса меня называют, свои крылья пожирая... сам себя я укрощаю
私はヘルメスの鳥 私は自らの羽根を喰らい 飼い慣らされる

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16473
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Полностью свой TextBox на WinAPI+GDI как клон системной

Сообщение Хакер » 19.01.2018 (Пт) 15:27

FireFenix писал(а):А по поводу самой организации? Т.е. нужно ли вообще контролам создаваться через WinApi? или же лучше, быть рисуемой областью, которая форма дёргает? (обьектом некоторого фреймворка без OS-API)
А так же, если у нас уже созданный контрол, то нужны ли ему оконные сообщения? или лучше/красивее/элегантнее разрулить через ООП, как вызовы от формы к детям без OS-API?

Оба подхода рабочие, только я не вижу смысл делать контрол в обход user32-подсистемы и обрабатывать всё самому. Если такая необходимость будет обоснована (какими-то ограничениями подсистемы окон), то ради бога. Поскольку я писал под Windows, я не видел ни малейшего смысла писать в обход штатной парадигмы, поэтому контрол у меня имел своё окно.

FireFenix писал(а):А вот сами строки, являются ли элементами, которые рисуются (как отдельный UI объект) ? или у тебя сам CodeBox является вьюхой, а строки и фрагменты данные?

Ты всё смешал и напутал. Блоки со строками, состоящими из фрагментов не имеют никакого отношения к CodeBox-у — они относятся к написанному пару лет назад наспех на VB6 функционалу по отображению гигантских логов (и не только по отображению, но и по накоплению и построению). Это одна VB6-форма с 1.5 тыс. строк кода. А CodeBox — из проекта 10-летней давности. На Си, занимал примерно 15—20 тыс. строк кода. Там совершенно другая организация данных была.

FireFenix писал(а):И зачем вообще нужны фрагменты? почему не работать с целой строкой?

Потому что при выводе логов редко какая строка выводится за раз. Обычно она выводит за как минимум 2 вызова AddLine/AppendLine, а то и за 10 таких вызовов. Соответственно, так как она передаётся в логгер по кусочками, либо надо сразу же склеивать эти кусочки, либо так и оставлять как массив кусочков.

Очевидно, что оставлять как массив выгоднее с точки зрения производительности (но не с точки зрения эффективного использования памяти), потому что итеративная конкатенация это не просто копирование, это выделение в строковом пуле COM новой строки при каждой конкатенации. Такое выделение — это проход по структурам данных, поддерживающим информацию о занятых/свободных участках памяти под строковый пул COM и поиск там достаточно вместительног свободного кусочка. Иногда такого кусочка нет — приходится наращивать строковый пул. Это жутко медленно. И если строк в логе миллион, и каждая состоит из 10 фрагментов, значит нужно 9 миллионов раз делать конкатенацию. Естественно, что при таком подходе работа алгоритма вместе с выводом в лог занимает 30 секунд, а с отложенной дефрагментацией — только 6.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.


Вернуться в Народный треп

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 11

    TopList