FireMonkey
TChromium у FireMonkey
Компонент TChromiumFMX з офіційного складання цілком собі працює у FireMonkey (у XE2), але у FMX2 навіть не компілюється. Довелося трохи розібратися з тим, як він улаштований та виправити. Добре, серйозних змін не знадобилося.
У FMX2 змінилися дві необхідні компоненту речі.
Перше - TBitmap більше не має властивостей ScanLine та StartLine. Прямий доступ до вмісту TBitmap переробили (цікаво, навіщо?) і тепер він доступний через клас TBitmapData, який повертає метод TBitmap.Map.
Ну і друге, більш відоме – Platform.* більше немає, тепер необхідно отримувати потрібний інтерфейс через TPlatformServices.GetPlatformService. Тут все досить прямолінійно, і проблем немає.

NakeyMonkey
Jason Southwell пропонує розробити набір FireMonkey-оберток для нативних контролів Windows/OSX і збирає на це гроші. Планує спочатку зібрати 20 тисяч доларів.
Ідея зрозуміла. Існуючі компоненти FireMonkey малюються засобами Delphi практично з нуля, що з одного боку багато в чому і забезпечує їх кросплатформеність, але з іншого — в результаті ми отримуємо компоненти, що не цілком природно виглядають в обох ОС, що підтримуються на сьогодні. І це півбіди – крім зовнішнього вигляду, доводиться самостійно розробляти і логіку цих компонентів. Наприклад, RichEdit досить складний, самостійно повторити його логіку в рамках FireMonkey – завдання не з очевидних. І VCL, і CLX не винаходили велосипедів, а користувалися готовим.
На резонне питання «чому NakeyMonkey вийде те, що Kylix CLX свого часу не вийшло?» Джейсон відповідає, що проблема CLX була у використанні зайвого проміжного шару у вигляді Qt, NakeyMonkey не буде прив'язаний до циклу розробки ііншим забаганкам третьосторонньої бібліотеки, але в цілому їй будуть притаманні всі тяготи та позбавлення CLX.
Ваші думки? Як це могло б вписатись в ідеологію стилів FireMonkey?
Трохи покритикую FireMonkey
Напередодні попередньої посади ще трохи поговоримо про FireMonkey. Дуже неприємна для мене річ — відчуття, що при розробці стандартних компонентів ніхто й не замислювався, що від них хтось успадковуватиметься, намагатиметься розширювати та змінювати їхню поведінку. Якщо порівнювати з VCL, то доступних компонентів FireMonkey зараз дуже мало. І мені здається, що доля фреймворку сьогодні багато в чому залежить саме від зручності розробки нових компонентів.
Розберу конкретний приклад.
Продовжую роботу з TTabControl. Цього разу я хочу отримати можливість розміщувати компоненти у неклієнтській області. Зізнаюся, хочу повторити функціонал браузерів – розмістити кнопку для відкриття нових вкладок праворуч від них.
Проблеми бути не повинно, тому що у FireMonkey усі стилізовані контролі можуть бути контейнерами для інших контролів. Особливість цієї конкретної ситуації в тому, що в цьому місці просто немає нічого, на чому можна було б розміщувати компоненти.
Реалізація очевидна. Потрібен контрол із найпростішим стилем (скажімо, тільки TLayout), його розташуємо на TTabControl'і і позиціонуватимемо за аналогією з вкладками. Нагадаю, що вкладка тут - це тільки сама кнопка, мені ж хочеться всього лише виявитися правіше кнопок.
Дивимося метод TTabControl.Realign. Метод довгий, але з пісні слів не викинеш. Можна перемотати, його суть двома словами я перекажу трохи пізніше.
Тут взагалі ніякої магії немає. Метод за великим рахунком робить лише три важливі речі:
- Пробігає по списку вкладок, визначаючимаксимальну висоту вкладки (MaxHeight);
- Має «тіло» TTabControl'а на доступному йому просторі, зрушуючи його верхню частину вниз на ці MaxHeight пікселів;
- Таким чином, зверху залишається смужка вільного простору і там спокійно можна розташувати кнопки вкладок.
До цього всього потрібно додати установку позиції моєї нової неклієнтської панелі. Що для цього потрібно? Значення MaxHeight знати висоту цього простору, і координати крайньої правої кнопки. До того ж потрібно внести невеликі зміни в логіку розміщення кнопок, заборонивши їм займати всю ширину TTabControl'а — вкладки не повинні мати можливості зробити ширину неклієнтської панелі меншою від якогось встановленого значення.
І, щиро кажучи, я не бачу способу зробити це красиво. Перевантаження Realign не допоможе. У результаті доведеться продублювати досить пристойні за обсягом шматки коду. Наприклад, MaxHeight обчислюватиметься двічі. Це, звичайно, дрібниця, але швидкодії не додасть, а от багів додати може.
Не пропонуватиму нічого глобального, покажу тільки до чого міг би привести маленький і дуже простий рефакторинг. Давайте зізнаємося самі собі, що зараз Realign якщо і не порушує "single responsibility principle", то явно балансує на межі.
Таким чином, вийшло б щось на зразок:
Разом 4 нових методи зі старим кодом.Кожен з яких має бути віртуальним. Тоді вся моя робота звелася б до перевантаження методу AlignTabs. У ньому б і значення MaxHeight було доступно, і логіку розміщення вкладок я б зміг змінити, не торкаючись всього іншого, і додати нову панель.
На жаль, описана проблема – це не окремий випадок. Покажу ще один приклад.
TMemo у FireMonkeyреалізовано досить цікаво. Текст малюється простим FillText'ом на канві, а для позиціонування каретки використовується відповідно обчислення висоти рядків та ширини фрагмента поточного рядка. Можливості кастомізації здавалося б безмежні.
Мені здалося цікавим спробувати реалізувати просте підсвічування синтаксису. Малювання влаштоване просто: обробнику OnPaint канви надається метод DoContentPaintWithCache, він коли потрібно викликає DoContentPaint для малювання, а коли можна просто малює стару картинку з кешу.
Проблеми починаються з того, що обидві функції не є віртуальними. Тобто я не можу просто перевантажити DoContentPaint, я маю спочатку скопіювати у своєму спадкоємці код DoContentPaintWithCache, замінивши виклик DoContentPaint на свій новий метод малювання. А потім цю копію встановити обробником OnPaint. Щойно відбулося дублювання
Але це ще не проблема. DoContentPaint - великий метод (
120 рядків), а мені всього лише потрібно замінити в ньому простий FillText на щось трохи складніше. Тобто, знову необхідно скопіювати весь код, замінивши в ньому пару викликів.
Якщо йти далі і змінювати не лише колір окремих слів, а й робити їх напівжирними (фактично змінювати ширину), доведеться трохи доопрацювати логіку позиціонування каретки. Метод TextWidth, який використовується при обчисленні позиції каретки в пікселях, в поточній реалізації цих нюансів звичайно не враховує. Але і цей метод не віртуальний. До речі, він навіть приватний. Незрозуміло чому.
Disclaimer: Не хочу бути неправильно зрозумілий, насправді витівка з FireMonkey мені подобається. Але головою об стіни я побився вже неабияк. Бажаю Embarcadero терпіння та удачі у доведенні цього фреймворку до пуття.