Люблю делать UI и офисные приложения
Information
- Rating
- 804-th
- Location
- Россия
- Registered
- Activity
Specialization
Software Developer, Application Developer
HTML
CSS
JavaScript
Windows API
C++
UI/UX design
Interface development
Product Design
Adobe Photoshop
Designing interfaces
Если современные версии Delphi копируют CSS-подход, то… как сказал всё тот же специалист, которого я выше уже цитировал: столкнувшись с тем, что подход HTML/CSS тупо лучше, авторы остальных инструментов оказались перед выбором — либо продолжать придерживаться устаревшего подхода, либо делать свою, ни с чем не совместимую версию HTML/CSS (и очень часто — ужасающе кривую).
Я посмотрел вашу статью, не обижайтесь, но это как раз про FMX.
Серьёзно? Покадровая анимация — отдельная сущность? Для сравнения, в CSS это делается через анимацию
background-position
, а чтобы кадры мгновенно меняли друг друга (а не плавно скроллировались) — там есть специальная универсальная кусочно-линейная функция steps(). Чувствуете разницу в универсальности подходов? А если вам понадобится сделать нелинейную (например, ускоряющуюся) покадровую анимацию? CSS вам даёт способ описать любую временнУю функцию. Что-то я сомневаюсь (даже не глядя в документацию), что FMX даёт что-то близко похожее по универсальности.Ну, можем сравнить на других примерах. Вот посмотрите видео. Это — два РАЗНЫХ куска DOM'а, вставленных в два разных контейнера. Как я понимаю, в терминологии Delphi, это будут два изначально несвязанных компонента. Кнопочки у 4-pad'а имеют семантику button, так что они без проблем озвучиваются скринридером как кнопки. Связаны они с контролируемым объектом («вебкой», представляющей в этой демке реальную камеру) чисто через CSS. Это работает, не требуя ни одной строчки джаваскрипта! Вы говорите, что у вас в Delphi «CSS подход (только визуальный)». Вы можете получить такой результат, пользуясь только визуальным редактором? Чтобы не писать на собственно Delphi? Вот тогда будет паритет. Но что-то я, опять же, сомневаюсь.
А знаете, КАК ИМЕННО я добился этого результата строго декларативно, через pure CSS?
Вот этот вот трюк с
body:has(контрол:состояние) .контролируемый-объект { управление отображением }
позволяет bind'ить одни компоненты к другим вообще без написания кода (императивного, имею в виду). И вы говорите, что этот замечательный язык, позволяющий такое — не "убер-фича"?Да, и обратите внимание. За счёт того, что нажатие на визуальную кнопку (
button.left:active
) дублируется через класс (.pad.ArrowLeft
), этот компонент (4-pad), если не забыть перенаправить на него клавиатурный поток, автоматически становится управляемым с обычной, аппаратной клавиатуры. Для полупарализованных и одноруких. Это называется accessibility, она же — доступность, вечная головная боль UI'шника.Ещё примеры?
Давайте посмотрим, как анимации перетекают друг в друга. Моё решение, универсальное, приведено там же (в виде ответа). Любую анимацию, в любой момент я могу преобразовать в любую другую, хоть оставив свойства, которые имеются в моменте, хоть сбросив их к начальной, конечной или произвольно заданной точке анимации. А чтобы было проще представить, вот второе видео, где показано, как на лету меняется скорость и направление анимации фона.
Там же, в самом конце, мигает фокус-рамка (опять же, для полупарализованных, одноруких и просто любителей клавиатуры). Вот как она сделана на этом языке, который вы не признаёте убер-фичей:
Написано один раз, и пока что работало везде, где я использовал tabbable-элементы. И даже скругления получены автоматически. Так что, это реально убер-фича.
Если мы говорим про аватар как учебный пример, то таких вопросов быть не должно. А если про реальное применение, я бы начал с вопроса, зачем вообще аватар оформлять компонентом.
Почему это адресовано мне, а не автору статьи, код из которой я процитировал? Я себе такое позволяю только при написании шаблонизаторов, но не в прикладном коде.
Это же просто пример. Пример, кстати, свежий (плюс-минус год) и взят с Habr Q&A. Человек там интересовался, как одну из самых популярных каруселей заадаптивить. Я ему ответил: напиши рядом размер с
!important
, и всё. А если бы это был изолированный компонент?Но есть и другие причины не изолировать код от внешней стилизации. Например, чтобы можно было после каждого обновления компонента не патчить его, если создаваемая им структура не поменялась.
Век живи, век учись! Спасибо, я как-то упустил.
А мне он не нужен, потому что я не хочу, чтобы Майкрософт контролировал, что из приложений я ставлю. И чтобы вообще знал (но про это сама винда стучит, не сомневаюсь). И если сегодня мы, юзеры, поведёмся на эти сторы, завтра релиз с гитхаба будет не поставить. РКН скажет Майкрософту удалить какой-нибудь goodbyedpi, та возьмёт под козырёк и привет (уже сегодня мы видим такое в эппловском сторе с VPN'ами — и это завтрашний день пользователей Виндовс, судя по трендам). Зачем своими руками помогать строить тюрьму, в которую нас всех посадят?
Кстати, стор ихний ещё и очень глюкаво написан. Я решил его потестить (в рамках изучения UX) и поставил через него Firefox. Потом снёс (тоже через стор). Всё через их интерфейсы! А потом поставил Firefox с мозилловского сайта. Теперь у меня в системе два Firefox'а, если верить списку приложений. (Второй инстанс на самом деле отсутствует). С такими друзьями врагов не надо.
Могу только поделиться своей историей. В одной конторе я проектировал куски UI. Там так получилось, что я писал кусок на HTML/CSS/JS и отдавал сидевшему рядом коллеге, который считал его референсом и переписывал на Qt. Если у меня уходила, допустим, неделя на полноценную имплементацию, включавшую в себя написание демки с реальными данными, то у него — несколько недель на переписывание (и интеграцию), при этом имплементировал он, максимум, 50% от сделанного мной. Если нужен был трёхмерный рычажок, я его делал с перспективными искажениями, а он — тупо двигал спрайт. Небольшие повороты в 3D (в HTML их очень просто делать через
transform
) им просто выкидывались. Если у меня был многоточечный сложный градиент, он в лучшем случае растягивал битмапку. Клиппинг в лучшем случае упрощался до прямоугольного. Я плакал кровавыми слезами, глядя во что превращается мой дизайн и как «qt дарит юзверю куда более приятный экспириенс». Хотя и понимал, насколько трудно ему воспроизвести на Qt все мои нюансы. (Он был хороший программист и даже научил меня паре трюков в графонии).Но я умолчал об одной детали. Всё это было в 2012 году. Тогда у программистов было оправдание так себя вести: мобильные устройства всё ещё были медленными. Особенно то говно, которые наша жадная контора покупала пучок за пятачок и впаривала своим клиентам. Но в 2025 году такого оправдания больше НЕТ. Можно и проектировать сложное поведение, и сразу вставлять в проект, и всё на очень простых описательных языках.
Это совсем другое. Эти умники берут и полностью пытаются влезть в браузер. Со всей бизнес-логикой, обработкой данных и т.п. Как в анекдоте про «только губы наружу оставил бы — уж очень курить люблю». Так, в частности, получается Electron. А потом меня спрашивают: «Ты же на Электроне пишешь?», и не понимают, почему я оскорбляюсь. Нет, ребята, я пишу на CEF, и только UI. Бизнес-логику и интеграцию я пишу, как и полагается, на нативных языках.
Да, ещё эти умники любят размазать UI по всей связке клиент-сервер. Это тоже не про меня. И я горячо согласен, что всё обозначенное — самый настоящий мусор.
Так можно сказать, что и какой-нибудь file API и SQL — тоже, «по сути», одно и то же. Но в одном случае у нас будет набор императивных функций «сделай сам», а в другом — декларативный язык запросов. И я не просто так вспомнил SQL. Все декларативные языки запросов очень похожи друг на друга. SQL — декларативный язык запросов для базифицированных данных. LINQ — декларативный язык запросов для коллекций (массивы и т.п.). А этот DSL — декларативный язык запросов для UI (конкретно — для DOM). Он, конечно, ТОЛЬКО для адресации (нахождения объектов), а не для преобразований, в этом его отличие от SQL, где есть
UPDATE
. Именно поэтому умные люди дополнили этот язык такой штукой, как jQuery. jQuery добавляет к этому декларативному языку декларативные же (!) средства преобразования.Покажу разницу на вашем примере: «код прописывается предку всех контролов». А вы можете взять чужой готовый контрол, и не меняя его исходный код (имея доступ только к своей форме) сделать так, что его составные части (какие-нибудь подписи) станут красными? И не только те, которые контрол уже создал на момент вашего вмешательства, а вообще любые, которые он когда-либо создаёт в процессе работы? И сделать это простым сопоставлением вида «текст контрола X — красный», без перебора в циклах и прочего залезания в кишки?
Неправда ваша, дяденька. Этот DSL соотносится с XPath примерно как HTML соотносится с XML: любители XML говорят, что HTML это производный язык от XML, его частный случай и его большое упрощение, а не-любители говорят: а ну отошли от нас! я щас милицию позову!
Вот стандарт этого языка: https://drafts.csswg.org/selectors/ Если я пропустил название, ткните, пожалуйста, пальцем. Насколько я вижу в этом документе, названия у DSL нет, потому что он считается частью CSS. Хотя помимо CSS он используется в браузерном DOM API.
Если вышенаписанное не убеждает, объясните такой TIME PARADOX:
:is()
был добавлен в 2021 (не кем-то, а CSSWG), а последняя версия XPath (XPath 3.1) вышла в 2017-м.Может, стоит быть последовательным, и удалять Микрософт Стор вместе с дефендером? Я сделал именно так. По крайней мере, всё, что нашёл на диске.
Интересно, кто-нибудь ещё помнит Х.Мотолога — героя малышни девяностых? Как же он тогда раздраконил Shadow Warrior! Я до сих пор помню, потому что такого рейтинга (23%) больше не видел ни у одной игры. Он вообще не любил Build (потому что любил idTech), но Shadow Warrior досталось сильнее всего.
Эти ваши парадоксы основаны на следующей ошибке. (Прошу не обижаться: слово «парадокс», выбранное вами для заголовка добровольно, для меня, в этом и в любом другом случае, всегда означает наличие скрытой ошибки и весёлую игру по её поиску). Теория вероятности — математическая теория*. А вы с Гарднером натягиваете её на физические процессы. Делать этого ни в коем случае нельзя. Физическая теория обязательно включает в себя критерии, по которым надо отбирать для рассмотрения как произошедшие** события, так и принципиально возможные. Математическая теория делать это не обязана, поскольку не привязана ни к каким конкретным физическим законам. В ней в этом месте оставлен вполне сознательный пробел. Заполняя его по своему усмотрению (как именно считать мальчиков-девочек) можно получить несколько разных ответов, только один из которых будет корреспондировать с реальностью. Парадокс именно в этом.
Важные оговорки:
* Теория вероятности в том виде, в котором она обычно преподаётся, не является чисто математической теорией. Когда мне читали её курс, то постоянно ссылались на монетки и кубики, не делая никаких оговорок по поводу их физической квантовой природы. Тем не менее, по существу это именно математическая теория.
** Произошедшие во всём мультиверсе, это принципиально.
Если же взять физическое объяснение происходящего… Я предлагаю MWI, но можете выбрать конкурирующее объяснение, будет интересно сравнить и обдумать. Главное — объяснение должно объяснять, а не отмораживаться в уголке, как «копенгаген», делая вид, что его это не касается, и что «задавать такие вопросы нельзя» (реально в одном из изложений видел процитированную фразу!). Так вот, в рамках MWI узнать о том, что старший ребёнок — девочка, можно только в таком типе вселенных, которые в дальнейшем разделяются по вопросу неоднозначности пола детей на два типа, встречающихся одинаково часто***. А не знать это можно в тех вселенных, которые в дальнейшем разделяются на три типа. Если вы знаете, что старший ребёнок — девочка, вы можете понять, к какому из исходных типов вселенных принадлежите, и так и определяется физическая вероятность. Игнорировать этот факт нельзя, и свободно задаваться тем, что считаем, а что нет — тоже.
*** Это очень сильное упрощение во многих аспектах. Оно подходит, чтобы не путать математику и физику, но не годится для построения полной физической картины. Во-первых, и это очевидно, вас могут просто обмануть. Обман — это физический процесс, хотя и очень высокоуровневый (эмерджентный), поэтому физический аппарат его не рассматривает.
Во-вторых, разделение происходит на вселенные не двух типов, а множества. Вы должны задаться диапазоном значений для всех параметров, которые согласитесь считать «одинаковыми во всех остальных отношениях». Если в одной вселенной бабочка летит слева направо, а в другой — справа налево, не влияя на пол ребёнка, это один случай или разные? А если бабочка, летящая налево, приведёт к власти фашистов, и они убьют и девочку, и вас прямо в процессе вычислений? Далее, как вообще может быть такая штука как «вселенные, одинаковые во всех остальных отношениях»? Это просто невозможно. Например, если соседский ребёнок любит младшую, а в другой вселенной младшая — младший, то либо соседский ребёнок в другой вселенной её не любит, либо имеет там другую ориентацию. Считать ли равнодушного соседа и соседа-гея за один случай?
В-третьих, физика оперирует такими понятиями как протяжённость процесса во времени (что, по некоторым теориям, само по себе упрощение) и спонтанные реакции. Пока вы считали, младший(ая) мог сменить пол хирургически или в результате спонтанных превращений атомов. Физика на то и физика, что она говорит: хирургическая смена пола должна рассматриваться в рамках физически совместимой эмерджентной науки (какого-нибудь обществоведения), а спонтанным превращением атомов в большинстве случаев можно пренебречь.
В-четвёртых, квантовые явления не просто так относят к микромиру. Спин может быть истинно случайным (давать производные вселенные строго двух типов, встречающихся со строго равной частотой), а вот его измерение — уже нет. Нет никакой возможности построить идеальный квантовый микроскоп (того самого кота), выносящий случайность на макроуровень без искажений. Обычно при измерении пола мы об этом не думаем (и опять же, физика объясняет, почему), но вообще фотоны могут дать искажённую информацию, или она может исказиться непосредственно в мозгу.
В-пятых, и, пожалуй, главное: мы делаем слишком много допущений, основываясь на том, что девочек и мальчиков рождается примерно поровну. Если есть другие причинно-следственные связи (например, мальчики на многие порядки агрессивнее, и рождение каждого нового мальчика нелинейно приближает мировой апокалипсис), тот факт, что вы вообще задаётесь парадоксами, может говорить о том, во вселенной какого типа вы находитесь с большей физической вероятностью. Утверждение «мальчики на многие порядки агрессивнее» не кажется истинным, но могут быть иные связи, как в качественном, так и количественном отношении.
Вот пять пунктов, и наверняка я ещё что-то упустил. Может быть даже, что что-то намного более важное.
Когда я скачал DefenderRemover, он его сразу обнаружил!
В Андроиде можно выбрать кнопку Пуск (launcher), а в Win11 — нет. (Не надо мне рассказывать про OpenShell, я его форкал и дописывал. Я говорю про дизайн системы).
В Андроиде можно выбрать любой файловый менеджер и назначить его для открытия файлов в приложениях. В Win11 Explorer прибит гвоздями.
В Андроиде появились новые юзерские техники, например sharing — развитие идеи clipboard в сторону безопасности путём ограничения доступа к данным выбранным реципиентом. В Win11 не придумали ничего умнее, чем добавить логгер (для копирования паролей из файла, наверно, особенно прекрасное решение).
У Андроида есть AOSP без гаппсов (ни плейсторов, ни приложения «Google», ни других анальных зондов), у Win11 нет.
Можно долго продолжать, но если посмотреть непредвзято, уже видно, что Андроид, в прошлом телефонная игрушка, в наши дни гораздо более инженерная ось. Дожили, б.
Отвечу именно вам, потому что ваш ник у меня ассоциируется с толковыми комментариями (это НЕ значит, что другие ники ассоциируются у меня с чем-то плохим). Но отвечу коротко, потому что я столько раз писал на эту тему, что у меня руки устали.
Любой, кто серьёзно (в больших объёмах, с изощрёнными требованиями, для большого числа пользователей) занимается UI должен знать, что:
1. Система адресации элементов, введённая в HTML, делает все альтернативные системы устаревшими. Под системой адресации я имею в виду DSL, который (пока?) не называется никак. Вот вам пример:
Что тут написано? Тут написано: если пользователь навигирует по всему интерфейсу при помощи клавиатуры (не мышки! не тачскрина!), и выделил элемент, разрешённый для навигации (это называется tabbable-элемент), то надо отобрать все панели управления (тулбары, dockable-панели и пр.), могущие перекрывать сфокусированный элемент. (Чтобы, например, скрыть их, или свернуть, или сделать полупрозрачными — короче, сделать так, чтобы они не мешались). При этом, если пользователь клавиатурой сфокусировался куда-нибудь внутрь панели (на кнопку тулбара, например), то включать сюда панель не надо. Ещё не надо включать сюда панели, которые имеют очень высокий приоритет (управляют удалением или показывают важный warning). Видите, как просто (в одну строчку!) это сформулировано на DSL, и как много слов на русском языке занимает это описание? А теперь покажите для сравнения, как выразить ту же мысль на Qt, WinAPI, Delphi, WinForms, Java FX или на чём угодно ещё. Написали? А теперь проверьте себя: устойчив ли ваш код к добавлению новой панели? Не сломается ли он? Сможете ли вы модифицировать условие отбора в одном месте, скажем, добавив или убрав проверку на закреп (pinning)?
2. Эту адресацию надо как-то привязать к способам отображения (которые, в свою очередь, желательно выразить декларативно). В CSS это делается так (панельки в данном случае становятся полупрозрачными и рамку фокуса видно через них):
(Пример, естественно, искусственный, потому что в реальности не пишут в лоб, чтобы его не разбить, а управляют отображением как-то типа
--control-panel-state: var(--control-panel-shy-mode);
. Кроме того, полупрозрачность панели — не лучший способ видеть рамку фокуса, но для примера сойдёт). Обратите внимание: чтобы привести всю схему в действие (т.е., чтобы панельки не мешались), не надо подписываться на событие «что-то глобально изменилось, не знаю что» или «юзер нажал Tab». Вообще не нужно лезть на уровень событий. Надо просто описать поведение, которое вы хотите.Естественно, сделать это другими инструментами очень трудно, поэтому или у вас огромная команда (включая тестировщиков) пишет килотонны бойлерплейта и тестирует его, или вы просто смиряетесь с тем, что такой уровень детализации UI («панельки не должны мешать навигации с клавиатуры») вам недоступен.
3. Если результат адресации надо, не дай бог, обработать императивно (а в реальном мире, увы, это бывает нужно постоянно), мы привязываемся к условию примерно так:
Обратите внимание: всё, что возвращает вам селектор, вы не проверяете ни на валидность памяти (не было ли удалено), ни на утечки (не забыть удалить самому). А это цепочка! Да ещё и с удалением внутри! Я даже не буду просить показать, как это делается на Qt (то есть, на C++).
Вы, конечно, скажете, что этот же аргумент применим к чему угодно (например, к бизнес-логике), так что теперь, всюду переходить на ЯП с автоуправлением памятью? А я вам отвечу следующее. Человек, у которого я учился своему ремеслу (UI), и у которого полмиллиарда (!!!) активных юзеров… И это не дворник в Майкрософт, который на свой счёт записывает все продажи Виндовс, он отвечал за львиную долю UI сотен продуктов… Так вот, он однажды сказал, что исследовал граф владения в UI и в остальных областях, и пришёл к выводу, что запутанность графа в UI на порядок выше, чем везде. И это количество даёт новое качество. Так что, лучше всего, если вы можете свои преобразования записать декларативно, но если уж писать их императивно, то только на скрипте с автоуправлением памятью. А иначе вы сами себе максимально злобный буратино! Я не так крут, у меня на последнем проекте было… немного меньше миллиона юзеров. Не полмиллиарда. Но я подписываюсь под каждым словом! Бизнес-логику, интеграцию с ОС, вот это всё — пишите на плюсах, на здоровье (я и сам писал, а сейчас Раст изучаю). Но бог вас упаси писать на них сложный UI. Например, эффект удаления сообщения «унесённые ветром» (как в Телеграме). Или эффект спойлера (размытие со снежинками). Мало того, что на CSS это записывается в несколько строк, это БЕЗОПАСНЫЕ несколько строк. С ними никогда не будет такого, что вы в пятницу добавили один из эффектов и поехали гулеванить, а в два часа ночи у вас отвалилось 10-50 тысяч юзеров номинальной ценой 1 доллар (и долгосрочными потерями в сто раз больше), и коллеги звонят прямо в сисечную и говорят: «Дорогой imagine.tables! Нам весьма неприятно прерывать твой всецело заслуженный отдых, но неужели ты не видишь, что твоим товарищам на лоб капает раскалённое олово?» («Мать-мать-мать…», повторило эхо).
4. (Последнее. Потому что кофе уже допил, а не потому, что мне больше нечего сказать). Когда у вас счёт юзеров идёт на сотни тысяч (не говоря про сотни миллионов), вы с интересом узнаёте, что среди них есть какие угодно, на любой вкус: однорукие, кривые, хромые, парализованные, слабовидящие, слепые, глухие,
тупые. *Конечно, когда все три юзера сидят в соседней комнате, вы с этим не сталкиваетесь). Если у человека одна рука или ему трудно двигаться, на компьютере весь функционал должен быть доступен с клавиатуры, а на телефоне — одним пальцем. Если он слабовидящий, надо уметь поддерживать схемы с контрастом. Если слепой — скрин-ридеры. Поддержка их всех встроена в HTML, а главное — он так спроектирован, чтобы эта поддержка ничего вам не стоила, и всё равно на неё уходит запросто 10% времени и больше. А если нет всех этих псевдоклассов для состояний и ролей для семантики?Короче, столкнувшись со всем вышеописанным при работе для массового юзера люди быстро приходят в разум. Но многие просто не сталкиваются. Пилят себе АРМ для кого-нибудь заводоуправления, где два пользователя, и оба как Герасим. (Который был на всё согласен :-Р).
QML? 🤦♂️Это какой-то эпический провал. Взять устаревшую разметку от полуживого коммерческого фреймворка (кутеры, без обид, всё по фактам), да ещё и от компании, заблокировавшей* свои продукты в России.
* Процитирую для тех, кому лень нажать F12 и посмотреть, что это за белый квадрат Малевича:
Видимо, это как когда-то было с Турбо Паскалем. «Наш завкаф в юности выучил Паскаль, и теперь поколения студентов могут забыть о Java, C++ и C#».
А ведь всё могло быть совсем иначе. Можно было не умничать, а взять за основу HTML, в который проброшены native APIs, повторив таким образом идею FirefoxOS. Глядишь, и мобильная ось from Russia with love выстрелила бы глобально (через сколько-то лет, и если открыть исходники). Такую ОС я бы и сам с удовольствием поставил, особенно если портировать под популярные модели телефонов. А свои закладки для товарища майора могли бы спрятать в RuStore.
К стоп-фразе «Умение разбираться в чужом коде» добавится ещё одна: «Умение разбираться в сгенерированном коде».
Так, похоже, пора делать бэкапы.
Я просто следую правилу: статические переменные — в препроцессор, динамические — в браузер. (Таблицу цветов пока пихаю в
:root
, но думаю, не отказаться ли и от этой практики). Во-первых, в детстве я читал книги по программированию (игр) и там было написано: золотое правило — всё, что можно посчитать при компиляции, не надо тащить в рантайм. Уж и компьютеры стали в тыщу раз быстрее, а всё равно мне непонятно, зачем. Во-вторых, и главное, это удобно: если видишь, что переменная в браузере, значит предполагается динамический сценарий (может, в будущем). Если в препроцессоре — значит нет.Минусы — надо держать в голове исходник и преобразования. Тем, кто привык разрабатывать, тестировать и вообще жить в DevTools, может не понравиться.
Браузерные миксины, которые я жду как и все, я хочу тоже посмотреть, как они поведут себя в динамике, а затем, возможно, применить к ним ту же стратегию, что и к переменным.
И, кстати, есть ещё и браузерные ограничения. Некоторые вещи типа брейкпоинтов я бы рад был использовать через браузерные переменные (мне это было надо, чтобы состыковать два разных препроцессора) — ан фиг.
Классика. «Любезнейший, а у вас есть другой глобус?»