Перейти к содержанию
Fire Monkey от А до Я

Евгений Корепов

Пользователи
  • Постов

    738
  • Зарегистрирован

  • Посещение

  • Победитель дней

    100

Весь контент Евгений Корепов

  1. Переработал и запустил один из проектов в Берлине. Все хорошо кроме странного бага - не работает перемотка. Оттягивается список и возвращается на место как приклеенный. Всю голову уже сломал, не понимаю как победить. Может кто знает как добиться такого эффекта? Так я пойму как его устранить ;-) Видео прикладываю: https://www.youtube.com/watch?v=R3ESp5RweEY
  2. Проверил патч - не помогает, проще в исходниках поправить строчку
  3. Оказывается эта проблема была обнаружена еще 26/Apr/16 12:29 AM https://quality.embarcadero.com/browse/RSP-14307 . И пока доступен только патч с хелперами https://quality.embarcadero.com/secure/attachment/17147/RSP14307.patch.zip Написана что проблема решена в Berlin Update 1. Но как понимаю этого апдейта еще не было?
  4. Начал переносить проекты из XE8 в Berlin, столкнулся с странным затыком в простеньком коде - делаем запрос на сайт, получаем куки, делаем post авторизацию, получаем редирект, если все хорошо, то октрываем страницу из header Location. Выяснилось что не смотря на HTTPClient.AllowCookies:=True, в HTTPResponse.Cookies всегда пустота. Пришлось копать исходники. Вот что обнаружилось в source\rtl\net\System.Net.HttpClient.pas: procedure THTTPClient.ExecuteHTTPInternal(const ARequest: IHTTPRequest; const AContentStream: TStream; const AResponse: IHTTPResponse); var LRequest: THTTPRequest; LResponse: THTTPResponse; State: THTTPState; LExecResult: TExecutionResult; LClientCertificateList: TCertificateList; OrigSourceStreamPosition: Int64; OrigContentStreamPosition: Int64; OrigContentStreamSize: Int64; Status: Integer; LCookieHeader: string; begin LResponse := AResponse as THTTPResponse; LRequest := ARequest as THTTPRequest; OrigSourceStreamPosition := 0; if LRequest.FSourceStream <> nil then OrigSourceStreamPosition := LRequest.FSourceStream.Position; if AContentStream <> nil then begin OrigContentStreamPosition := AContentStream.Position; OrigContentStreamSize := AContentStream.Size; end else begin OrigContentStreamPosition := 0; OrigContentStreamSize := 0; end; State := Default(THTTPState); LClientCertificateList := TCertificateList.Create; try while True do begin LRequest.DoPrepare; // Add Cookies if FCookieManager <> nil then begin LCookieHeader := FCookieManager.CookieHeaders(LRequest.FURL); if LCookieHeader <> '' then LRequest.AddHeader('Cookie', LCookieHeader); // do not localize end; if not SetServerCredential(LRequest, LResponse, State) then Break; if not SetProxyCredential(LRequest, LResponse, State) then Break; if LRequest.FSourceStream <> nil then LRequest.FSourceStream.Position := OrigSourceStreamPosition; if LResponse <> nil then begin LResponse.FStream.Position := OrigContentStreamPosition; LResponse.FStream.Size := OrigContentStreamSize; end; LExecResult := DoExecuteRequest(LRequest, LResponse, AContentStream); case LExecResult of TExecutionResult.Success: begin if not SameText(LRequest.FMethodString, sHTTPMethodHead) then LResponse.DoReadData(LResponse.FStream); Status := LResponse.GetStatusCode; case Status of 200: begin Break; // Если запрос удачен, то выходим из цикла end; 401: begin State.Status := InternalState.ServerAuthRequired; end; 407: begin State.Status := InternalState.ProxyAuthRequired; end; else begin case Status of 301..304, 307: if FHandleRedirects and (LRequest.FMethodString <> sHTTPMethodHead) then begin Inc(State.Redirections); if State.Redirections > FMaxRedirects then raise ENetHTTPRequestException.CreateResFmt(@SNetHttpMaxRedirections, [FMaxRedirects]); end else Break; else end; State.Status := InternalState.Other; if DoProcessStatus(LRequest, LResponse) then Break; end; end; end; TExecutionResult.ServerCertificateInvalid: begin DoValidateServerCertificate(LRequest); end; TExecutionResult.ClientCertificateNeeded: begin DoNeedClientCertificate(LRequest, LClientCertificateList); end else raise ENetHTTPClientException.CreateRes(@SNetHttpClientUnknownError); end; if AllowCookies then UpdateCookiesFromResponse(LResponse); // Вот эта, критически важная процедура, при Status=200 никогда не выполняется end; // После выхода из цикла попадаем сюда if LRequest.FSourceStream <> nil then LRequest.FSourceStream.Seek(0, TSeekOrigin.soEnd); LResponse.FStream.Position := OrigContentStreamPosition; finally LClientCertificateList.Free; end; end; Т.е. разработчики исключили выполнение UpdateCookiesFromResponse(LResponse), которая помещает куки из ответа в HTTPClient. А вот код из XE8 который нормально работает с Cookies: function THTTPClient.ExecuteHTTPInternal(const ARequest: IHTTPRequest; const AContentStream: TStream): IHTTPResponse; var LRequest: THTTPRequest; LResponse: THTTPResponse; State: THTTPState; LExecResult: TExecutionResult; LClientCertificateList: TCertificateList; OrigSourceStreamPosition: Int64; OrigContentStreamPosition: Int64; OrigContentStreamSize: Int64; Status: Integer; LCookieHeader: string; begin Result := nil; LResponse := nil; LRequest := ARequest as THTTPRequest; OrigSourceStreamPosition := 0; if LRequest.FSourceStream <> nil then OrigSourceStreamPosition := LRequest.FSourceStream.Position; if AContentStream <> nil then begin OrigContentStreamPosition := AContentStream.Position; OrigContentStreamSize := AContentStream.Size; end else begin OrigContentStreamPosition := 0; OrigContentStreamSize := 0; end; State := Default(THTTPState); LClientCertificateList := TCertificateList.Create; try while True do begin LRequest.DoPrepare; // Add Cookies if FCookieManager <> nil then begin LCookieHeader := FCookieManager.CookieHeaders(LRequest.FURL); if LCookieHeader <> '' then LRequest.AddHeader('Cookie', LCookieHeader); // do not localize end; if not SetServerCredential(LRequest, LResponse, State) then Break; if not SetProxyCredential(LRequest, LResponse, State) then Break; if LRequest.FSourceStream <> nil then LRequest.FSourceStream.Position := OrigSourceStreamPosition; if LResponse <> nil then begin LResponse.FStream.Position := OrigContentStreamPosition; LResponse.FStream.Size := OrigContentStreamSize; end; LExecResult := DoExecuteRequest(LRequest, LResponse, AContentStream); case LExecResult of TExecutionResult.Success: begin if not SameText(LRequest.FMethodString, sHTTPMethodHead) then LResponse.DoReadData(LResponse.FStream); Status := LResponse.GetStatusCode; case Status of 200: begin Break; end; 401: begin State.Status := InternalState.ServerAuthRequired; end; 407: begin State.Status := InternalState.ProxyAuthRequired; end; else begin case Status of 301..304, 307: if FHandleRedirects and (LRequest.FMethodString <> sHTTPMethodHead) then begin Inc(State.Redirections); if State.Redirections > FMaxRedirects then raise ENetHTTPRequestException.CreateResFmt(@SNetHttpMaxRedirections, [FMaxRedirects]); end else Break; else end; State.Status := InternalState.Other; if DoProcessStatus(LRequest, LResponse) then Break; end; end; end; TExecutionResult.ServerCertificateInvalid: begin DoValidateServerCertificate(LRequest); end; TExecutionResult.ClientCertificateNeeded: begin DoNeedClientCertificate(LRequest, LClientCertificateList); end else raise ENetHTTPClientException.CreateRes(@SNetHttpClientUnknownError); end; end; if LRequest.FSourceStream <> nil then LRequest.FSourceStream.Seek(0, TSeekOrigin.soEnd); if AllowCookies then UpdateCookiesFromResponse(LResponse); // Здесь все верно, процедура за пределами цикла и выполняется всегда когда нужно. finally LClientCertificateList.Free; Result := IHTTPResponse(LResponse); end; end; А теперь вопрос: ну как так то? В продукте за 54 тысячи рублей сильно обидно исправлять такие косяки. Такое ощущение что разраб подрабатывал на стороне в проектах на php и забыл переключится на другой язык, там break прерывает работу аналога case и код работал бы правильно.
  5. Отличный пример, спасибо! А UListItemElements это просто праздник какой то, даже не представлял что так можно. Не подскажете источник информации по TListItemDrawable, TListViewItem.Adapter и Objects.FindObjectT? В справке эмбаркадеры информация разнится от "Embarcadero Technologies does not currently have any additional information" до "Triggers the OnResetView event, passes the specified list item to its event handler, and calls DoResetView." что не особо раскрывает тему.
  6. Ура! Сенсоры наконец то полноценно работают! Даже HumanProximity и AmbientLight!
  7. Нововведения конечно впечатляют! Руки чешутся все испробовать. Правда некоторые примеры не работают (их кстати тоже еле отыскал в недрах профиля) :-( И удивляет стиль написания примеров. С каждой версией все хуже хуже, в XE8 они были избыточно запутанными мусорным кодом, а теперь еще одно нововведение - имена переменных почти в стиле Turbo Pascal. Ну почему нужно делать TS: TToggleSwitch вместо ToggleSwitch: TToggleSwitch, и далее по тексту grpColors, lblColor, cbxColor, lblThumbColor, grpState, grpAlignment. Это ведь пример, а не образец кода с понтами типа "куллхацкер". При изучении кода приходится выяснять что FClient это THTTPClient, а не какой либо другой клиент. Долгожданные сервисы почему то не заботают, пример AndroidNotificationServiceDemo ничего не делает. А вот DownloadServiceDemo прекрасно заработал, отлично!
  8. Самое смешное что у меня для таких случаем настроена переменная среды окружения TEMP=D:\Temp\System_Temp\, но видимо писатели не стали парится с их использованием, а написали нечто вроде Куда_срем := Папка_профиля + 'AppData\Local\Temp'; Установить в итоге удалось выделив на системном около 45 гигов. Но мусор на системном диске остался, буду разбираться как его переносить. Интересно, хоть кто нибудь пишет проекты в папке профиля Documents\Embarcadero\Studio\Projects, по станной задумке эмбаркадеры? Нужно больше вложенных папок! Глубже, еще глубже! Все еще не достаточно глубоко! ;-) С трудом нашел Android SDK C:\Users\Public\Documents\Embarcadero\Studio\18.0\CatalogRepository\AndroidSDK-24.3.3_GIB.Build.22858.6822, хотя не уверен что это используемая средой копия, слишком мало вложенных папок, нужно копать глубже ;-)
  9. Получил сегодня ключи от Берлина, на радостях начал установку. Как и у многих, конфигурация типичная - SSD 128Gb в качестве системного, и большой HDD в качестве рабочего. На SSD было 20 гигов свободного места, студию естественно устанавливал в D:\Embarcadero\Studio\18.0. Установка закончилась падением винды. Убогий установщик Берлина засрал системный диск полностью, распихав по системному диску 30 гигов мусора и обрушил систему. Посмотреть бы в глаза разработчику этого убожества. Почему нельзя ставить студию туда куда указано? Для чего такой кривой интерфейс установщика, скрин прилагаю, надеюсь ниже обезанного пункта в списке ничего нет что мне понадобится. Почему нельзя использовать Application.ProcessMessage для долгих операций, чтоб винда по нескольку раз не говорила что приложение не отвечает, давай ее убъем. Пока складывается ощущение что я выкинул 54 тысячи на ветер, если среда исполнена теми же авторами, то чувствую весь ад еще впереди.
  10. ОБНОВЛЕНИЕ NAMED (UPGRADE) С РЕДАКЦИИ STARTER + BONUS PACK + MOBILE ADD-ON PACK В ПОДАРОК ДО 15.09.2016 Цена 75 999,00 руб UPDATE: Обновление с стартер возможно и с той же версии. Я так на XE7 Proffesional обновлялся. Но лучше позвонить и узнать. Но звоните не в allsoft, там никогда ничего не знают, а в отдел продаж эмбаркадеры.
  11. Один из способов Edit1.KillFocusByReturn:=True; Или можно использовать более гибкий метод, скрывать клавиатуру когда вам захочется: В Uses добавьте FMX.VirtualKeyboard, FMX.Platform. Код примерно такой: procedure TForm1.Edit1ChangeTracking(Sender: TObject); begin If Edit1.Text.ToLower.Contains('карамба') Then HideVirtualKeyboard; end; procedure TForm1.HideVirtualKeyboard; var Keyboard: IFMXVirtualKeyboardService; begin if TPlatformServices.Current.SupportsPlatformService( IFMXVirtualKeyboardService, IInterface( Keyboard ) ) then Keyboard.HideVirtualKeyboard; end;
  12. Вот кусок кода исходников из XE8 unit FMX.Controls.Model : procedure TDataModel.SetData(const Index: string; const Value: TValue); var DataRecord: TDataRecord; begin if FDataSource = nil then FDataSource := TDictionary<string, TValue>.Create; if Value.IsEmpty then FDataSource.Remove(Index) else FDataSource.AddOrSetValue(Index, Value); DataRecord := TDataRecord.Create(Index, Value); SendMessage<TDataRecord>(MM_DATA_CHANGED, DataRecord); end; Судя по коду, нужно делать как то так : procedure TForm1.FormCreate(Sender: TObject); begin Edit1.Model.Data['aaa']:=TStringList.Create; Edit1.Model.Data['aaa'].AsType<TStringList>.Add('111'); Edit1.Model.Data['aaa'].AsType<TStringList>.Add('222'); Edit1.Model.Data['aaa'].AsType<TStringList>.Add('333'); Edit1.Model.Data['aaa'].AsType<TStringList>.Clear; Edit1.Model.Data['aaa'].AsType<TStringList>.Free; Edit1.Model.Data['aaa']:=TValue.Empty; if Edit1.Model.Data['aaa'].IsEmpty Then beep; end;
  13. Нет, нельзя. Имя пакета это главный идентификатор приложения, создается раз и навсегда. Даже при блокировке приложения и повторной публикации имя пакета нужно другое. Единственное исключение, если вам заблокировали приложение, но вы доказали свою правоту, то спустя неопределенное количество времени (в моем случае почти два месяца), можно опубликовать приложение с тем же именем пакета.
  14. У них сейчас еще одна замечательная акция - Delphi 10.1 Berlin Professional Обновление Named (Upgrade) + Bonus Pack + Mobile Add-on Pack в подарок до 15.09.2016 Я воспользовался. Хватит уже сидеть на XE8, вливаюсь в ряды берлинщиков :-) Бонус пак кстати приятный - Konopka Signature VCL Controls, Radiant Shapes, VCL & FireMonkey Premium Styles и Marco Cantu’s Object Pascal handbook. Не знаю пригодится ли Konopka и Radiant, но книгу как раз в отпуске на море буду читать.
  15. Со всем уважением, но категоричность ваших высказываний вызывает удивление. Те же разработчики андроид, всего лишь люди, а не апостолы религиозной секты, которым нужно слепо подчиняться. Для меня "нельзя" - лишь повод проверить почему нельзя, и выяснить что оказывается можно, еще как можно. В качестве примера приведу одно из своих приложений - Инвентаризация для узкого круга заказчиков. В Т.З. были довольно жесткие ограничения по цене устройств, обязательная оффлайн работа, с последующей синхронизацией, слабые, нестабильные каналы этой самой синхронизации и довольно большой объем данных (от 60 до 100 тысяч наименований) с которым нужно работать. Естественно заказчик хотел мгновенного отклика от приложения. Мои конкуренты пошли по пути завещанному вебинарами и правильными теоретиками - база данных на устройстве, дата-адаптер, визуализация. На тестовом пакете данных в 50к наименований это было жалкое и печальное зрелище, минимум трехсекундные интервалы после любого телодвижения. Импорт данных (из csv файла) занимал несколько минут, тесовые устройства грелись и сажали батарею. Естественно они сдулись, заявив что выполнить задание не возможно. Было понятно, что конкурировать с бригадой "правильных" разработчиков в классической схеме мне не светит, поэтому пошел "запрещенным путём" - в приложении, помимо необходимых элементов управления был один ListView, которых адаптировался под текущую задачу и хранил все данные. Результат был потрясающий. Мгновенная реакция на поиск, фильтры. Для смеха использовал пакет данных с 150 тысячами записей. Демонстрировал на Samsung GT-S7390 https://market.yandex.ru/product/10542116/spec?hid=91491&track=char . Импорт/экспорт занимал около 30 секунд. Честно говоря я снял шляпу перед разработчиками ListView. Приложение успешно работает по сей день, конечно спустя полтора года я бы многое изменил/дополнил (особенно доступ ко всем данным несмотря на фильтрацию, тогда не нашел решения кроме ее временного отключения, выполнения действий, включения, недавно опубликовал здесь найденное решение), но ListView оставил бы в качестве хранилища.
  16. Повторю вопрос - для чего? ListView мощный инструмент способный превосходно хранить данные и манипулировать ими. В определенных задачах естественно. Искусственное раздувание требуемых объемов оперативной памяти путем хранения трех-четырех дубликатов необходимых данных может и оправдано на настольных платформах, но в мобильных приложениях резко ограничит количество потенциальных пользователей. Напомню - тема всего лишь о простом решении продвинутой фильтрации в ListView. Давайте не будем раздувать флейм о моделях хранения.
  17. К примеру данные получены от удаленного сервера в виде json. Для чего в таком случае плодить прокладки в виде баз данных, увеличивая объем и сложность приложения в два раза?
  18. Я руководствуюсь принципом бритвы Оккама - не использую дополнительные сущности (базы данных к примеру), без крайней на то необходимости ;-)
  19. Один из вариантов вызывать активити вот так: procedure ShareFile(aFileName, aComment : String); var Intent : JIntent; uri : Jnet_Uri; AttachmentFile: JFile; S : String; begin Intent := TJIntent.Create; Intent.setAction(TJIntent.JavaClass.ACTION_SEND); Intent.setFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK); S:=TPath.GetFileName(aFileName); Intent.putExtra(TJIntent.JavaClass.EXTRA_SUBJECT, StringToJString(aComment)); Intent.putExtra(TJIntent.JavaClass.EXTRA_TEXT, StringToJString(aComment)); AttachmentFile := TJFile.JavaClass.init(StringToJString(aFileName)); Uri := TJnet_Uri.JavaClass.fromFile(AttachmentFile); Intent.putExtra(TJIntent.JavaClass.EXTRA_STREAM, TJParcelable.Wrap((Uri as ILocalObject).GetObjectID)); Intent.setType(StringToJString('text/plain')); // Intent.setDataAndType(StrToJURI('file:' + TPath.Combine(TPath.GetSharedDownloadsPath, 'picture.png')), StringToJString ('image/png')); SharedActivity.startActivity(Intent); end; Пользователю нужно будет выбрать что именно использовать, список будет из всего что установлено (гугледиск, дропбокс и т.д.). Можно вызывать активити для определенного приложения, но под рукой примера нет.
  20. Возникла задача поиска (фильтрации) в ListView по нескольким критериям. К примеру нужно искать по Item.Text, Item.Detail и Item.Date['Category']. Пошел путем, ранее подсказанным на этом форуме - правкой "function TListViewItems.ApplyFilter: Boolean" в FMX.ListView.pas. Передавать строку поиска хотел в строке типа "Text=Пеньки&Detail=по три рубля&Category=Осиновые", но быстро понял что код превращается в одноразовую кашу, а хотелось сделать красиво и на будущее. Сделал так - добавил еще один тип "TFilterPredicateEx = TPredicate<TListViewItem>;", к существующему "TFilterPredicate = TPredicate<string>;" в TListViewItems. Ну и далее по списку добавил аналоги к переменным, функциям и property. Смысл в том чтобы вместо обычного бутылочного горлышка в фильтации: ListView.Items.Filter:= function(X: string): Boolean begin Result := AFilterText.IsEmpty or X.ToLower.Contains(AFilterText.ToLower); end; иметь мощный инструмент с доступом ко всем плюшкам TListViewItem. Теперь это выглядит вот так: type TListViewFilterEx = record Category : String; Name : String; end; ... Var AFilter : TListViewFilterEx; // это для удобства хранения фильтров в одном месте ... AFilter.Category:='Береза'; AFilter.Name:='Пеньки'; AListView.Items.FilterEx:= function(X: TListViewItem): Boolean begin Result:= (AFilter.Category.IsEmpty or X.Data['Category'].AsString.ToLower.Contains(AFilter.Category.ToLower)) And ((AFilter.Name.IsEmpty or X.Text.ToLower.Contains(AFilter.Name.ToLower)) or (AFilter.Name.IsEmpty or X.Detail.ToLower.Contains(AFilter.Name.ToLower))); end; Подозреваю что все это можно было сделать с помощью хелперов, но до их освоения руки никак не дойдут. Если кто то сделает хелпер, дабы не править FMX.ListView.pas, буду очень благодарен. Внимание! FMX.ListView.pas от Delphi XE8, к другим версиям думаю не подойдет. Прилагаю дополненный FMX.ListView.pas к сообщению. FMX.ListViewWithFilterEx.zip
  21. Операция Result := lApiResponse.ResultObject; копирует в Result ссылку на тот же самый объект. И логично что при попытке освободить его функция вернет пустоту, так как вы освобождаете и результат функции. Как вариант можно использовать функцию Assign - копирование объекта в Result, тогда освобождение lApiResponse.Free не затронет результат возвращаемый функцией.
  22. Решил проблему следующим образом: ListViewItem создаю в массиве ListViewItemArray : TArray<TListViewItem>, вот таки образом ListViewItemArray[I]:=AListView.Items.Add; В итоге имею массив для доступа к ListViewItem, вне зависимости от текущего состояния фильтрации в ListView. Костыль конечно, но другого способа не нашел. Да и вроде по ресурсам и памяти нагрузки никакой, добавился только массив ссылок на объекты.
  23. Я бы отделил логику от интерфейса. Проще создать двумерный массив (или сразу класс) для доступа к чекбоксам и остальному. Вот пример кода: unit Unit1; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.DateTimeCtrls, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Layouts, FMX.ListBox; type TWeekCheckBox = array[1..7] of TCheckBox; TMyItem = record TimeStart : TTimeEdit; TimeEnd : TTimeEdit; WeekCheckBoxArray : TWeekCheckBox; end; TForm1 = class(TForm) ListBox: TListBox; procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } MyItems : TArray<TMyItem>; end; var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.FormCreate(Sender: TObject); Var I, J : Integer; ListBoxItem : TListBoxItem; begin SetLength(MyItems,7); for I := 0 to Length(MyItems)-1 do begin ListBoxItem:=TListBoxItem.Create(ListBox); MyItems[I].TimeStart:=TTimeEdit.Create(ListBoxItem); ListBoxItem.AddObject(MyItems[I].TimeStart); MyItems[I].TimeEnd:=TTimeEdit.Create(ListBoxItem); ListBoxItem.AddObject(MyItems[I].TimeEnd); for J := 1 to 7 do begin MyItems[I].WeekCheckBoxArray[J]:=TCheckBox.Create(ListBoxItem); MyItems[I].WeekCheckBoxArray[J].Align:=TAlignLayout.Left; ListBoxItem.AddObject(MyItems[I].WeekCheckBoxArray[J]); end; ListBox.AddObject(ListBoxItem); end; end; end. Естественно нужно еще все контролы расставить как вам нужно. В итоге вы имеете двумерный массив, с помощью которого можете с легкостью обращаться к нужным элементам. Для наглядности туда же добавил и TTimeEdit подсмотренные на вашем скриншоте.
  24. Как получить доступ к отфильтрованным элементам ListView. Ситуация такая - ListView частично сформирован и показан пользователю, он может начать поиск через SearchBox, но потоки должны продолжать заполнение некоторых данных, не критичных для работы. Но дело в том, что получить доступ по индексу, к отфильтрованному итему нельзя, а нужно. Что делать? Может есть другой путь получить нужный ListViewItem?
  25. Как узнать количество свободного места на "диске"? К примеру получаю путь System.IOUtils.TPath.GetCachePath, и перед тем как туда писать, хочу узнать есть ли и сколько свободного места. С наскока не нашел ответ. Заранее благодарю!
×
×
  • Создать...