понедельник, 31 марта 2014 г.

Как жить с таким TNumberBox?

Очень мне хотелось бы поговорить о TNumberBox. С одной стороны - чудо-контрол, который нам должен давать в установленных пределах готовое число, а с другой стороны - что-то он работает не слишком предсказуемо. 


Сначала обсудим, что в нём вкусного. Во-первых, он нацелен на цифровую виртуальную клавиатуру. Это хорошо. А то ужас как неудобно устанавливать KeyboardType в vktNumberPad. Шучу, конечно. Но ведь в самом деле, вряд ли только из-за этого нужен дополнительный контрол. Поэтому смотрим, что там во-вторых.

Во вторых, он обрабатывает вертикальный и горизонтальный жесты, позволяя менять численное значение без ввода цифр. Да и мышь в Windows (и, наверно, на Mac ) тоже прекрасно работает. Причём мы можем отдельно регулировать скорость приращения по вертикали и по горизонтали! Допустим, вдоль длинной горизонтали меняется целая часть числа, а вдоль короткой вертикали - десятые доли. Такого поведения в VCL не было!

В третьих, ЧисловойКоробок способен форматировать ввод и обеспечивать валидный (от франц. valide - законный, действующий) результат ввода в виде численного значения Value. Для форматирования и валидации у нас есть количество точек после запятой и диапазон значений Min и Max. Вот это уже кажется настоящим делом для нового контрола.


А теперь рассмотрим некоторые сложности. Обратите внимание - Min и Max = 0..100. Вас устроит такое ограничение? А если вообще ограничения не нужны, если нужен диапазон "по-максимуму"? Установка 0..0 по моей логике должна бы отменить всякие ограничения, но - нет. Стоит отвернуться, отвлечься и нажать даже не другой контрол, а всего лишь какую-нибудь "не такую" клавишу, как значение в поле редактирования сбрасывается в 0. Вот беда! Да ладно, не беда! - скажем мы, посмотрим в справкепосмотрим в справке ещё и найдём диапазон Single = 1.5e-45..3.4e+38 и ... Увы, Object Inspector не понимает таких значений. Я лично оставляю -1E38..1E38. Если переборщить, то мы даже можем увидеть нехорошее слово из трёх букв - INF (я думаю, что это Infinity - бесконечность). Нехороши эти буквы потому, что вместо обещания Infinity они не только не дадут нам диапазон -∞↔+∞, но и не дадут запустить программу, и, что особенно неприятно, не дадут загрузить  в следующий раз саму форму, если нечаянно записать её с этими буквами вместо цифр. Придётся открывать <unit>.FMX в текстовом редакторе и менять буквы на на 0. При этом не помешает соблюдать осторожность, чтобы не споткнуться о какой-нибудь [Inf]o или Ma[inF]orm.

Теперь жесты. Казалось бы - вот здорово! А оказалось - а вот и не очень. Если поле ввода перекрывается всплывающей клавиатурой, то что надо сделать с полем? Да, мы его двигаем вверх, да ещё и, как real programmers, с анимацией. А пока мы двигаем поле, NumberBox отслеживает точку нажатия относительно своих границ. Т.е. он думает, что это не он перемещается, а что это мы двигаем пальцем! Он меняет значение, хотя палец просто ткнулся и стоит там, где только что было поле ввода. Мне кажется, что когда программа вдруг меняет значение, которое должен менять лично сам пользователь, то это доверия пользователя к программе совсем не добавляет. Это какая-то ерунда. Но тогда, может, ну их - жесты эти - к лешему? Мы как-то и без них раньше обходились. Верно? К чему тут виски напрягать да копья ломать? Возьмём просто и отключим их! Это же кажется легче лёгкого - достаточно установить HorizontalIncrement и/или VerticalIncrement в 0. Но увы и снова увы - после запуска (!) приложения мы обнаруживаем, в VerticalIncrement что 0 заменяется дефолтным 5! Когда я столкнулся с этим мне почему-то вспомнилось классическое "Братья и сёстры! Это ли не чудо?!" Я пробовал организовать чудоборство запоминанием в TagString текста контрола перед каждым FloatAnimation1.Start и, соответственно, восстановлением на OnFinish анимации. Но - увы, ничего хорошего не получилось. Кстати, что мешало сделать TAnimation.OnStart? Если кто знает как уведомить EMBT о целесообразности этого события, напишите, пожалуйста.

И форматирование ввода - тоже не слишком похоже на ожидаемое. Когда при значении 0 вводится какая-то цифра, то вводится следующая цифра! Что это за число такое "008,9" или "+00"? Что это за левые (во всех смыслах) нолики? Может, это мода такая новая? Мне  лично эта мода кажется ругательной, и я прошу её ко мне не применять! Поэтому я вешаюсь на ChangeTracking и режу первый символ, если он 0 и при этом второй - цифра (минус бы тоже учесть можно, но это уже для гурманов). Такую ерунду подвешивать к каждому полю - это разве дело? Но иначе я на поле ввода числа смотреть не могу. Чес-сло, я уже с теплом начинаю вспоминать TMaskEdit. И ещё вопрос у меня - а кому там нужен плюсик? Ведь любое ненулевое число без минуса - уже плюс! Просветите меня, если можете. Мне кажется, я какой-то малопрозрачный.

А как апофеоз моих исследований - свойство Value. Я-то думал, что стоит ValueType установить в vtFloat, так сразу буду получать введённое пользователем значение типа Single. Ну да, кто-то кое-где у нас порой этого не хочет, ему подавай Int64, а Value - только Single. Но это уже не так важно, как то, что не получится получить значение "сразу". Я имею в виду, что если по какой-то причине вы захотите получить значение Value сразу, как только пользователь нажмёт Enter (Ну и что же тут криминального, если в OnKeyDown установить if Key = vkReturn then AddValue?), то скорее всего в Value будет что-то такое, что напоминает число в свойстве Text очень и очень отдалённо. Вешаться же на OnChange для того, чтобы отследить нажатие Enter на виртуальной клавиатуре я вам не советую - оно срабатывает совсем не только на Enter и совсем не всегда на Enter. Слава Богу, что он вдохновил программиста (или его начальника) вынести в интерфейс модуля FMX.Edit процедуру TryTextToValue, которую я использую сам, и рекомендую вам. Она запускает старый (как время летит!) добрый TryStrToFloat, которому входная строка причёсывается по пробелам и десятичному сепаратору. В случае неудачи возвращается дефолтное значение. Вот спасибо!

Эпилог

TNumberBox, не смотря на далеко не первую версию платформы FM,  выглядит довольно сыро. Возможно, это связано с небольшим опытом его применения программистами. Возможно,  это связано с небольшим количеством обращений программистов в qc.embarcadero.com. Я лично (это моя стандартная отговорка) плохо формулирую свои мысли даже на русском. А то, что получилось сложить - слишком многословно, чтобы внятно перевести на английский. Если вы чувствуете в себе силы написать о проблемах этого контрола в EMBT, то прошу вас, напишите. Если вы можете что-то подсказать мне по применению этого контрола - тоже пишите.

Спасибо за внимание!
Всем всего доброго!

ЗЫ: Спустя некоторое время я стал использовать специальную заглушку-перехватчик, чтобы обойти основные проблемы контрола. http://alhymov.blogspot.ru/2014/04/tnumberbox.html