FireMonkey

TChromium у FireMonkey

Компонент TChromiumFMX з офіційного складання цілком собі працює у FireMonkey (у XE2), але у FMX2 навіть не компілюється. Довелося трохи розібратися з тим, як він улаштований та виправити. Добре, серйозних змін не знадобилося.

У FMX2 змінилися дві необхідні компоненту речі.

Перше - TBitmap більше не має властивостей ScanLine та StartLine. Прямий доступ до вмісту TBitmap переробили (цікаво, навіщо?) і тепер він доступний через клас TBitmapData, який повертає метод TBitmap.Map.

Ну і друге, більш відоме – Platform.* більше немає, тепер необхідно отримувати потрібний інтерфейс через TPlatformServices.GetPlatformService. Тут все досить прямолінійно, і проблем немає.

FireMonkey

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. Метод довгий, але з пісні слів не викинеш. Можна перемотати, його суть двома словами я перекажу трохи пізніше.

Тут взагалі ніякої магії немає. Метод за великим рахунком робить лише три важливі речі:

  1. Пробігає по списку вкладок, визначаючимаксимальну висоту вкладки (MaxHeight);
  2. Має «тіло» TTabControl'а на доступному йому просторі, зрушуючи його верхню частину вниз на ці MaxHeight пікселів;
  3. Таким чином, зверху залишається смужка вільного простору і там спокійно можна розташувати кнопки вкладок.

До цього всього потрібно додати установку позиції моєї нової неклієнтської панелі. Що для цього потрібно? Значення 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 терпіння та удачі у доведенні цього фреймворку до пуття.