All Activity
- Today
-
Implements a familiar UX where typing letters moves the selection to the first matching item, similar to Windows Explorer. I thought someone might find this useful... UniListBox.ClientEvents.ExtEvents -> function afterrender(sender, eOpts) { var lb = sender, store = lb.getStore(), field = lb.displayField || 'val'; lb._searchBuffer = ''; lb.on('keypress', function(e){ var ch = e.event.key; if (!ch || ch.length !== 1) return; lb._searchBuffer += ch.toLowerCase(); Ext.defer(function(){ lb._searchBuffer = ''; }, 1000); var idx = store.findBy(function(rec){ var text = rec.get(field); return text && text.toLowerCase().startsWith(lb._searchBuffer); }); if (idx !== -1) { lb.boundList.getSelectionModel().select(idx); lb.boundList.getNavigationModel().setPosition(idx); } }); }
- Yesterday
-
WilliamErath started following TUniChart добавить Axes справа
-
WilliamErath joined the community
-
Improved: function TUniStringList.StringFromId(Id: Integer): string; var lIndexOfId : Integer; begin if (Id >= 0) then begin lIndexOfId := IndexOfId(Id); if lIndexOfId <> -1 then Result := Strings[lIndexOfId] end else Result := ''; end; Current: function TUniStringList.StringFromId(Id: Integer): string; begin if Id >= 0 then Result := Strings[IndexOfId(Id)] else Result := ''; end; Call Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.0.0 Versão/IP: 1.0.0.3/168.196.81.31 Navegador/Versão/OS: chrome/145/windows SessionID: hjRgKbcDZv112C373CF Component: UniFormCadNegociacaoVeiculo (TUniJSForm) Parent: UniFormCadNegociacaoVeiculo LastAjaxEvent: activate Class: EStringListError Message: List index out of bounds (-1). TUniStringList range is 0..20 CallStack: [00CA7DC4] System.Classes.TStringList.Get [0159165F] uniMultiItem.TUniStringList.StringFromId (Line 590, "uniMultiItem.pas" + 2) [016AE807] uniListBox.TUniCustomListBox.DoSetRemoteValue (Line 392, "uniListBox.pas" + 50) [0134C078] uniGUIClasses.TUniFormControl.SetRemoteValue (Line 8629, "uniGUIClasses.pas" + 34) [0134A055] uniGUIClasses.ProcessComponent (Line 7957, "uniGUIClasses.pas" + 16) [0134A2D6] uniGUIClasses.TUniContainer.AssignFieldValues (Line 8024, "uniGUIClasses.pas" + 49) [00BAAA16] System.@IsClass [0134A0B3] uniGUIClasses.ProcessComponent (Line 7965, "uniGUIClasses.pas" + 24) [01331755] uniGUIBaseClasses.TUniGUIBaseControl.GetControl (Line 785, "uniGUIBaseClasses.pas" + 13) [0134A2D6] uniGUIClasses.TUniContainer.AssignFieldValues (Line 8024, "uniGUIClasses.pas" + 49) [00BAAA16] System.@IsClass [0134A0B3] uniGUIClasses.ProcessComponent (Line 7965, "uniGUIClasses.pas" + 24) [01331755] uniGUIBaseClasses.TUniGUIBaseControl.GetControl (Line 785, "uniGUIBaseClasses.pas" + 13) [0134A2D6] uniGUIClasses.TUniContainer.AssignFieldValues (Line 8024, "uniGUIClasses.pas" + 49) [00BAAA16] System.@IsClass [0134A0B3] uniGUIClasses.ProcessComponent (Line 7965, "uniGUIClasses.pas" + 24) [01331755] uniGUIBaseClasses.TUniGUIBaseControl.GetControl (Line 785, "uniGUIBaseClasses.pas" + 13) [0134A2D6] uniGUIClasses.TUniContainer.AssignFieldValues (Line 8024, "uniGUIClasses.pas" + 4 Would it be possible to implement this improvement?
-
Hi, 10x to You and Sherzod @FMSOFT
-
Yes, Thx to Sherzod. procedure TMainForm.UniFormCreate(Sender: TObject); begin // user is define on MainModule (at start session) if MainModule.UniMainModule.user <>'admin' then begin // Thx to Sherzod @FMSOFT with JSInterface do JSAddListener('afterrender', 'function(me){me.getRefItems().forEach(function(item){if (item.xtype && item.xtype=="toolbar"){item.hide()}})}', WebForm.JSForm); end; end; Hide_MainMenu.7z
- Last week
-
rc24Buimb joined the community
-
kkelchev started following How to hide TUniMainMenu
-
Hi. There is a case when we need to HIDE (completely) control such "TUniMainMenu" - application main-menu. Main menu should not be visible at all. If did try as: FreeAndNil(MainForm.MainMenu) in "UniFormCreate" event but result is EXCEPTION. Execute FreeAndNil(MainForm.MainMenu) latter: there is NO efect. Is it possible this to be done. Thanks Kamen.
-
Hello Sherzod, I've currently installed FMSoft_uniGUI_Complete_Professional_1.95.0.1610, so likeley all versions are affected. I havent looked in depth into the grids code but just traced the cause to it. My initial guess would be that you are not using data access for visual controls correctly, because why would you need to scroll within the DataSet during creation? To do db control aware access and "internal" scrolling (This even allows changing the active row without triggering any changes) you'll have to use TDataLink. I'll attach the demo setup showing the issue to this I am now allowed to do so. Further descriptions on how to trigger/reproduce are inside the Memo. Are you aware that, for whatever reason, the upload feature is not available under Firefox? If you need further informations or help, just drop me a message, regards, Thomas. UniGridBug.zip
-
Ralf Schroedter started following TUniDBGrid bug: lost updates when dynamically creating
-
Hello, Which build and edition of uniGUI are you using?
-
Hello, unfortunateley I cannot upload any files here nor post to the bugs subforum. As I've set up a demo project to show it, feel free to drop me an email or contact me otherwise if you want me to supply it. To reproduce this yourself you will need a simple Master/Detail setup. * Take two TFDMemTables for example. * Now link a Detail Table Field to any DB aware control like a TUniDBEdit or just link the whole Table to TUniDBGrid. * Drop a TUniDBNavigator to navigate the Master * Edit content in a Detail Field (but do not Post) * After the editing: Create a TUniDBGrid instance and link it to the Master. ***** * Use the Navigator to move the master forth and back. The non posted update will now be lost due to (*****). (*****): The reason for this is in TUniCustomDBGrid.MoveCursor which is called during construction. It basically does the following logic: DataSet.DisableControls; DataSet.First; MoveDataSetBy(APos); DataSet.EnableControls; Which causes a state change to dsSetKey and a deDataSetChange notification to the detail, leading it to get into Modified=False but still staying in dsEdit mode. For our use case I've added a Master.CheckBrowseMode; before creating the Grid, which essentially saves all pending detail changes. However this would not be a proper fix for the grid, as you expect the Detail to stay in dsEdit with Modified=True. Regards, Thomas.
-
Ralf Schroedter joined the community
-
TJost joined the community
-
XRumer23Hon joined the community
-
I thought you meant something like Windows Explorer behavior, where typing characters moves the selection to the first matching folder or file.
-
Я попробую проверить.
-
Пожалуйста, используйте этот код, я исправил автоматический фокус. function afterrender(sender, eOpts) { var me = sender; // Проверяем, не было ли уже добавлено поле поиска if (me._searchFieldAdded) { return; } me._searchFieldAdded = true; // Находим основной элемент boundlist var boundlist = me.el; // Находим контейнер для элементов списка и сам список var listWrap = boundlist.down('.x-boundlist-list-ct'); var listEl = boundlist.down('.x-list-plain'); // Отключаем прокрутку в listWrap if (listWrap) { listWrap.setStyle('overflow', 'hidden'); } // Создаем уникальные ID для элементов var searchFieldId = me.getId() + '_searchField'; var clearBtnId = me.getId() + '_clearBtn'; // Создаем контейнер для поиска с кнопкой очистки var searchContainer = Ext.DomHelper.createDom({ tag: 'div', cls: 'x-boundlist-search-container', style: 'border-bottom: 1px solid #d0d0d0; background: #f5f5f5; padding: 8px; width: 100%; box-sizing: border-box; flex-shrink: 0;', children: [{ tag: 'div', style: 'position: relative; width: 100%;', children: [{ tag: 'input', type: 'text', id: searchFieldId, placeholder: 'Поиск...', style: 'width: 100%; padding: 5px 30px 5px 8px; box-sizing: border-box; border: 1px solid #c0c0c0; border-radius: 4px; outline: none; font-size: 13px;' }, { tag: 'span', id: clearBtnId, style: 'position: absolute; right: 8px; top: 50%; transform: translateY(-50%); cursor: pointer; font-size: 20px; color: #999; display: none; line-height: 1;', html: '×' // Символ крестика для кнопки очистки }] }] }); // Вставляем контейнер поиска в начало списка if (listEl) { listWrap.dom.insertBefore(searchContainer, listEl.dom); } else { listWrap.dom.insertBefore(searchContainer, listWrap.dom.firstChild); } // Получаем ссылки на созданные DOM-элементы var searchField = document.getElementById(searchFieldId); var clearBtn = document.getElementById(clearBtnId); // Если элементы не найдены, выходим из функции if (!searchField || !clearBtn) return; // Добавляем CSS стили для правильного отображения компонентов var style = document.createElement('style'); style.textContent = ` /* Убираем фокус с элементов списка */ .x-boundlist-item:focus, .x-boundlist-item:active { outline: none !important; } /* Настройки контейнера списка */ .x-boundlist-floating { outline: none !important; overflow: hidden !important; } /* Настройки контейнера с элементами */ .x-boundlist-list-ct { overflow: hidden !important; display: flex !important; flex-direction: column !important; padding: 0 !important; height: 100% !important; } /* Настройки списка элементов */ .x-list-plain { overflow-y: auto !important; overflow-x: hidden !important; width: 100% !important; box-sizing: border-box !important; flex: 1 !important; margin: 0 !important; } /* Контейнер поиска */ .x-boundlist-search-container { width: 100%; box-sizing: border-box; flex-shrink: 0; } /* Убираем подсветку фокуса у boundlist */ .x-boundlist:focus { outline: none !important; } `; document.head.appendChild(style); /** * Функция фильтрации элементов списка * Скрывает элементы, не соответствующие тексту поиска */ var filterList = function() { var searchText = searchField.value.toLowerCase().trim(); // Показываем или скрываем кнопку очистки if (searchText.length > 0) { clearBtn.style.display = 'block'; } else { clearBtn.style.display = 'none'; } // Получаем все элементы списка var items = listEl ? listEl.query('.x-boundlist-item') : []; // Если строка поиска пуста, показываем все элементы if (searchText === '') { items.forEach(function(item) { item.style.display = ''; }); return; } // Фильтруем элементы по тексту items.forEach(function(item) { var itemText = (item.textContent || item.innerText).toLowerCase(); if (itemText.indexOf(searchText) > -1) { item.style.display = ''; // Показываем, если текст совпадает } else { item.style.display = 'none'; // Скрываем, если не совпадает } }); }; // Предотвращаем всплытие событий для поля ввода // Используем capturing phase, чтобы перехватить события до их обработки ExtJS searchField.addEventListener('mousedown', function(e) { e.stopPropagation(); // Останавливаем всплытие события }, true); // Обработчик клика на поле поиска searchField.addEventListener('click', function(e) { e.stopPropagation(); // Останавливаем всплытие this.focus(); // Устанавливаем фокус на поле }, true); // Основной обработчик ввода текста для фильтрации searchField.addEventListener('input', filterList); // Дополнительный обработчик для надежности searchField.addEventListener('keyup', filterList); // Обработчик для кнопки очистки поля поиска clearBtn.addEventListener('mousedown', function(e) { e.stopPropagation(); // Останавливаем всплытие e.preventDefault(); // Отменяем стандартное поведение searchField.value = ''; // Очищаем поле clearBtn.style.display = 'none'; // Скрываем кнопку очистки filterList(); // Обновляем фильтрацию (показываем все элементы) searchField.focus(); // Возвращаем фокус на поле поиска }, true); // Глобальный обработчик для всего boundlist // Предотвращает закрытие списка при клике на поле поиска или кнопку очистки boundlist.dom.addEventListener('mousedown', function(e) { // Если клик был по полю ввода или кнопке очистки, ничего не делаем // Позволяем событиям обрабатываться нормально if (e.target === searchField || e.target === clearBtn || searchField.contains(e.target) || clearBtn.contains(e.target)) { return; } }, true); /** * Функция принудительной установки фокуса на поле поиска * Использует рекурсивные попытки с задержкой * @param {number} attempts - количество попыток */ var forceFocus = function(attempts) { if (!attempts) attempts = 0; setTimeout(function() { if (searchField && document.body.contains(searchField)) { // Проверяем, видим ли элемент и доступен ли он var style = window.getComputedStyle(searchField); if (style.display !== 'none' && style.visibility !== 'hidden' && !searchField.disabled) { try { searchField.focus(); // Пытаемся установить фокус // Проверяем, удалось ли установить фокус setTimeout(function() { if (document.activeElement !== searchField && attempts < 10) { forceFocus(attempts + 1); // Повторяем попытку } }, 50); } catch(e) { if (attempts < 10) { forceFocus(attempts + 1); // Повторяем при ошибке } } } else if (attempts < 10) { forceFocus(attempts + 1); // Повторяем, если элемент невидим } } }, 100); }; // Запускаем несколько попыток установки фокуса с разными задержками setTimeout(function() { forceFocus(0); }, 200); setTimeout(function() { forceFocus(0); }, 500); setTimeout(function() { forceFocus(0); }, 1000); /** * Функция точной настройки высоты компонентов * Пересчитывает высоту списка с учетом поля поиска */ var adjustHeight = function() { setTimeout(function() { if (!me.rendered) return; // Получаем высоту поля поиска var searchHeight = searchContainer.offsetHeight || 49; var totalHeight = me.getHeight(); // Общая высота boundlist // Учитываем толщину границы var borderWidth = 0; var boundlistStyle = window.getComputedStyle(boundlist.dom); if (boundlistStyle.borderWidth) { borderWidth = parseInt(boundlistStyle.borderTopWidth) + parseInt(boundlistStyle.borderBottomWidth); } // Доступная высота для контейнера списка var availableHeight = totalHeight - borderWidth; // Высота для списка элементов (общая минус поле поиска) var listHeight = availableHeight - searchHeight; // Устанавливаем высоту контейнера списка if (listWrap) { listWrap.dom.style.height = availableHeight + 'px'; listWrap.dom.style.maxHeight = availableHeight + 'px'; } // Устанавливаем высоту для списка элементов if (listEl) { listEl.dom.style.height = listHeight + 'px'; listEl.dom.style.maxHeight = listHeight + 'px'; listEl.dom.style.overflowY = 'auto'; listEl.dom.style.overflowX = 'hidden'; } // Отключаем прокрутку основного контейнера boundlist.dom.style.overflow = 'hidden'; }, 50); }; // Первоначальная настройка высоты setTimeout(adjustHeight, 250); // Используем ResizeObserver для отслеживания изменения размеров var resizeObserver = new ResizeObserver(function() { adjustHeight(); // Пересчитываем высоту при изменении размеров }); if (boundlist.dom) { resizeObserver.observe(boundlist.dom); // Начинаем наблюдение } // Обработчики событий списка // При раскрытии списка me.on('expand', function() { setTimeout(function() { adjustHeight(); // Корректируем высоту forceFocus(0); // Пытаемся установить фокус }, 150); }); // При показе списка me.on('show', function() { setTimeout(function() { adjustHeight(); // Корректируем высоту forceFocus(0); // Пытаемся установить фокус }, 150); }); }
-
Спасибо ещё раз. Подтверждено. Работает идеально.
-
я видел этот пост и у меня не получилось повторить, чтобы все работало если рассматривать это событие, то здесь мне интересно только это, выделил жирным function chart.boxready(sender, width, height, eOpts) { this.series.items[0].addListener('itemmousedown', function(el) { var series = this.chart.series.get(0); series.highlight = true; series.unHighlightItem(); series.cleanHighlights(); series.highlightItem(el); series.highlight = false; var rec = MainForm.UniDBGrid1.getStore().findRecord('2', el.storeItem.data.LL); if (rec) { MainForm.UniDBGrid1.getSelectionModel().select(rec); }; }) } я преобразовал под свою задачу так, хотя можно сразу завязаться на событие function chart.itemmousedown(chart, item, event, eOpts) function chart.boxready(sender, width, height, eOpts) { if (sender.series) { Ext.each(sender.series, function(series, index) { series.addListener('itemmousedown', function(el) { series.highlight = true; series.unHighlightItem(); series.cleanHighlights(); series.highlightItem(el); series.highlight = false; }); }); } } но в любом случае этот код не работает, ругается series.unHighlightItem is not a function series.cleanHighlights is not a function series.highlightItem is not a function
-
Спасибо, вы великий
-
// Функция, которая выполняется после рендеринга компонента TUniListBox function afterrender(sender, eOpts) { var me = sender; // Проверяем, не было ли уже добавлено поле поиска if (me._searchFieldAdded) { return; } me._searchFieldAdded = true; // Находим основной элемент boundlist var boundlist = me.el; // Находим контейнер для элементов списка и сам список var listWrap = boundlist.down('.x-boundlist-list-ct'); var listEl = boundlist.down('.x-list-plain'); // Отключаем прокрутку в listWrap (чтобы не было двойной прокрутки) if (listWrap) { listWrap.setStyle('overflow', 'hidden'); } // Создаем контейнер для поиска с кнопкой очистки var searchContainer = Ext.DomHelper.createDom({ tag: 'div', cls: 'x-boundlist-search-container', style: 'border-bottom: 1px solid #d0d0d0; background: #f5f5f5; padding: 8px; width: 100%; box-sizing: border-box; flex-shrink: 0;', children: [{ tag: 'div', style: 'position: relative; width: 100%;', children: [{ tag: 'input', type: 'text', id: me.getId() + '_searchField', placeholder: 'Поиск...', style: 'width: 100%; padding: 8px 30px 8px 8px; box-sizing: border-box; border: 1px solid #c0c0c0; border-radius: 4px; outline: none; font-size: 14px;' }, { tag: 'span', id: me.getId() + '_clearBtn', style: 'position: absolute; right: 8px; top: 50%; transform: translateY(-50%); cursor: pointer; font-size: 20px; color: #999; display: none; line-height: 1;', html: '×' // Символ "×" для кнопки очистки }] }] }); // Вставляем контейнер поиска перед списком элементов if (listEl) { listWrap.dom.insertBefore(searchContainer, listEl.dom); } else { listWrap.dom.insertBefore(searchContainer, listWrap.dom.firstChild); } var searchField = Ext.get(me.getId() + '_searchField'); var clearBtn = Ext.get(me.getId() + '_clearBtn'); // Добавляем CSS стили для исправления различных проблем var style = document.createElement('style'); style.textContent = ` .x-boundlist-item:focus, .x-boundlist-item:active { outline: none !important; } .x-boundlist-item { outline: none !important; } .x-boundlist-floating { outline: none !important; overflow: hidden !important; } .x-boundlist-list-ct { overflow: hidden !important; display: flex !important; flex-direction: column !important; padding: 0 !important; height: 100% !important; } .x-list-plain { overflow-y: auto !important; overflow-x: hidden !important; width: 100% !important; box-sizing: border-box !important; flex: 1 !important; margin: 0 !important; } .x-boundlist-item { width: 100% !important; box-sizing: border-box !important; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .x-boundlist-search-container { width: 100%; box-sizing: border-box; flex-shrink: 0; } `; document.head.appendChild(style); // Функция фильтрации списка var filterList = function() { var searchText = searchField.dom.value.toLowerCase().trim(); // Показываем/скрываем кнопку очистки if (searchText.length > 0) { clearBtn.setStyle('display', 'block'); } else { clearBtn.setStyle('display', 'none'); } // Получаем все элементы списка var items = listEl ? listEl.query('.x-boundlist-item') : []; // Если поле поиска пустое, показываем все элементы if (searchText === '') { items.forEach(function(item) { item.style.display = ''; }); return; } // Фильтруем элементы по тексту items.forEach(function(item) { var itemText = (item.textContent || item.innerText).toLowerCase(); if (itemText.indexOf(searchText) > -1) { item.style.display = ''; } else { item.style.display = 'none'; } }); }; // Обработчик ввода текста для фильтрации searchField.on('keyup', filterList); // Обработчик клика по кнопке очистки clearBtn.on('click', function() { searchField.dom.value = ''; clearBtn.setStyle('display', 'none'); filterList(); searchField.focus(); }); // Устанавливаем фокус на поле поиска setTimeout(function() { if (searchField) { searchField.focus(); } }, 200); // Функция точной настройки высоты var adjustHeight = function() { setTimeout(function() { if (!me.rendered) return; var searchHeight = Ext.fly(searchContainer).getHeight() || 49; var totalHeight = me.getHeight(); // Учитываем толщину границы при расчете высоты var borderWidth = 0; var boundlistStyle = window.getComputedStyle(boundlist.dom); if (boundlistStyle.borderWidth) { borderWidth = parseInt(boundlistStyle.borderTopWidth) + parseInt(boundlistStyle.borderBottomWidth); } var availableHeight = totalHeight - borderWidth; var listHeight = availableHeight - searchHeight; // Настраиваем высоту listWrap if (listWrap) { listWrap.setStyle('height', availableHeight + 'px'); listWrap.setStyle('maxHeight', availableHeight + 'px'); } // Настраиваем высоту listEl if (listEl) { listEl.setStyle('height', listHeight + 'px'); listEl.setStyle('maxHeight', listHeight + 'px'); listEl.setStyle('overflow-y', 'auto'); listEl.setStyle('overflow-x', 'hidden'); } // Убеждаемся, что нет пустого пространства boundlist.setStyle('overflow', 'hidden'); }, 50); }; // Выполняем настройку высоты после полного рендеринга setTimeout(adjustHeight, 250); me.on('resize', adjustHeight); // Перенастраиваем высоту при клике me.on('click', function() { setTimeout(adjustHeight, 10); }); // Перенастраиваем высоту после полного отображения setTimeout(adjustHeight, 500); } This is very important. procedure TMainForm.UniListBox1Click(Sender: TObject); begin if UniListBox1.ItemIndex <> -1 then Caption := UniListBox1.Items.Strings[UniListBox1.ItemIndex]; end;
-
Этот пост может помочь:
-
Добрый день! А как можно принудительно обновить Chart, чтобы сработали события series.renderer Хочу чтобы при выделении столбика он раскрашивался в другой цвет, для этого добавил в chart.beforeInit переменную sender.select_index = 0; и series.renderer = function(sprite, cfg, rendererData, index) для BarSeries function chart.beforeInit(sender, config) { sender.select_index = 0; config.innerPadding = { left: 4, right: 10, top: 40 }; config.axes = [{ type: 'numeric', position: 'left', fields: ['A'], title: 'Count', grid: true },{ type: 'numeric', position: 'right', fields: ['B'], minimum: 0, maximum: 100, title: 'Percent', label: { renderer: function(v){ return v + '%'; } } },{ type: 'category', position: 'bottom', fields: ['LL'] }]; Ext.Array.each(config.series || [], function(series){ // BAR SERIES if (series.type === 'bar') { series.label = { field: 'A', display: 'outside', orientation: 'horizontal', padding: 10, renderer: function(v){ return Ext.util.Format.number(v,'0,000'); } }; series.renderer = function(sprite, cfg, rendererData, index) { cfg.fillStyle = 'green'; cfg.strokeStyle = 'green'; if (index === sender.select_index) { cfg.fillStyle = 'blue'; cfg.strokeStyle = 'blue'; } return cfg; }; } // LINE SERIES if (series.type === 'line') { series.axis = 'right'; series.style = { strokeStyle: '#7cb342', lineWidth: 2 }; series.marker = { radius: 4 }; series.label = { field: 'B', display: 'over', padding: 8, renderer: function(v){ return v + '%'; } }; series.renderer = function(sprite, cfg, rendererData, index) { var rec = rendererData.store.getAt(index); var val = rec.get('B'); if (val >= 80) { cfg.fillStyle = '#d9534f'; cfg.strokeStyle = '#d9534f'; } else { cfg.fillStyle = '#7cb342'; cfg.strokeStyle = '#7cb342'; } return cfg; }; } }); } также добавил обработчик в ExtEvents function chart.itemmousedown(chart, item, event, eOpts) { chart.select_index = item.index; chart.redraw; } И вот при вызове chart.redraw; ничего не происходит если же в легенде выключить серию и потом включить, то столбик выделяется, т.е. срабатывает series.renderer Видео-12-03-2026 213950.mp4
-
kkelchev started following Quick search in TuniListBox
-
Hi How to do quick search with keyboard in TuniListBox. Thanks Kamen
-
Okay.
-
using tabler icons instead of fontawesome
Jean-Marc Kiener replied to Jean-Marc Kiener's topic in General
Hello, Don't worry, Just asked myself if it would be possible. Because i used tabler for another project. -
@Farshad Mohajeri The issue in the Uni framework has not been fully resolved, and it is easy to disable the web application by sending too many requests!!!
-
Hello, What is the goal of replacing the icons? If you mean completely replacing FontAwesome with Tabler icons, it is most likely not possible to do it directly since the icon sets do not fully match. In that case you would need to create a mapping (for example fa-user → ti-user, etc.). Also keep in mind that some icons used by Ext JS / uniGUI components are not font icons but background-image icons defined in the theme CSS (for example arrows, grid indicators, etc.), so those cannot be replaced simply by switching the icon font...
-
Hello, I get it to work to add tabler icons fonts to my unigui project so i can use them: procedure TUniServerModule.UniGUIServerModuleCreate(Sender: TObject); begin //Tabler Fonts laden if TFile.Exists( '.\files\tabler\icons\webfont\tabler-icons.css' ) then CustomMeta.Add( '<link rel="stylesheet" type="text/css" href="/files/tabler/icons/webfont/tabler-icons.css">' ); end; I set the property TUniServerModule.IncludeFontawesome4 to false. But it seems to load it: Now my question: is it possible to use tabler icons instead of fontawesome icons. I mean replace fontawesome/pictos with tabler?
