Полезные сниппеты и функции

Tectrex

Известный
145
171
Описание: Скриншот экрана, который сохраняется в буфер обмена, типа Print Screen. Не знаю зачем, но пусть будет.
Пример использования:
Lua:
screenshotToClipboard()
Код:
Lua:
local ffi = require("ffi")

ffi.cdef[[
    typedef void* HANDLE;
    typedef void* HDC;
    typedef void* HBITMAP;
    typedef const char* LPCSTR;
    typedef unsigned int UINT;
    typedef int BOOL;
    typedef unsigned long DWORD;

    HDC GetDC(void* hWnd);
    int GetSystemMetrics(int nIndex);
    HDC CreateCompatibleDC(HDC hdc);
    HBITMAP CreateCompatibleBitmap(HDC hdc, int cx, int cy);
    HBITMAP SelectObject(HDC hdc, HBITMAP h);
    BOOL BitBlt(HDC hdcDest, int xDest, int yDest, int w, int h, HDC hdcSrc, int xSrc, int ySrc, DWORD rop);
    BOOL DeleteDC(HDC hdc);
    BOOL DeleteObject(HBITMAP hObject);

    BOOL OpenClipboard(void* hWndNewOwner);
    BOOL EmptyClipboard();
    BOOL CloseClipboard();
    HANDLE SetClipboardData(UINT uFormat, HANDLE hMem);

    HANDLE GlobalAlloc(UINT uFlags, size_t dwBytes);
    HANDLE GlobalLock(HANDLE hMem);
    BOOL GlobalUnlock(HANDLE hMem);
    BOOL IsClipboardFormatAvailable(UINT format);
   
    BOOL OpenClipboard(void*);
    BOOL CloseClipboard();
    enum { SM_CXSCREEN = 0, SM_CYSCREEN = 1 };
    enum { SRCCOPY = 0x00CC0020 };
    enum { CF_BITMAP = 2 };
]]

local user32 = ffi.load("user32")
local gdi32 = ffi.load("gdi32")
local kernel32 = ffi.load("kernel32")

function screenshotToClipboard()
    local width = user32.GetSystemMetrics(0)
    local height = user32.GetSystemMetrics(1)

    local hdcScreen = user32.GetDC(nil)
    local hdcMem = gdi32.CreateCompatibleDC(hdcScreen)
    local hBitmap = gdi32.CreateCompatibleBitmap(hdcScreen, width, height)
    gdi32.SelectObject(hdcMem, hBitmap)
    gdi32.BitBlt(hdcMem, 0, 0, width, height, hdcScreen, 0, 0, ffi.C.SRCCOPY)

    if user32.OpenClipboard(nil) ~= 0 then
        user32.EmptyClipboard()
        user32.SetClipboardData(ffi.C.CF_BITMAP, hBitmap)
        user32.CloseClipboard()
        print("Скриншот скопирован в буфер обмена.")
    else
        print("Не удалось открыть буфер обмена.")
    end

    -- очистка
    gdi32.DeleteDC(hdcMem)
    -- hBitmap не удаляем, т.к. он теперь в буфере обмена
end
я не догнал, а ReleaseDC не надо вызывать шоль?
 
  • Нравится
Реакции: Орк

chapo

tg/inst: @moujeek
Всефорумный модератор
9,198
12,528
Описание: небольшой пародия на iota из Go
Код:
Lua:
local iota = setmetatable({ value = -1 }, {
    __call = function(self, newValue)
        self.value = newValue or self.value + 1;
        return self.value;
    end
});
Пример использования:
Lua:
local iota = setmetatable({ value = -1 }, {
    __call = function(self, newValue)
        self.value = newValue or self.value + 1;
        return self.value;
    end
});

Packet = {
    Connect = iota(),
    ConnectResponse = iota(),
    BotConnect = iota(),
    BotDisconnect = iota()
};

print(Packet.Connect, Packet.ConnectResponse, Packet.BotConnect, Packet.BotDisconnect);
1745317523431.png
 

Орк

Известный
399
344
Описание: *Изменяет цвет текста в EditBox диалога*
Lua:
function editDialogEditBoxTextColor(color)
    local pDialog = memory.read(getModuleHandle("samp.dll") + 0x21A0B8, 4, true)
    local pEditBox = memory.read(pDialog + 0x24, 4, true)
    memory.write(pEditBox + 0x127, color, true)
end
Пример использования:
Lua:
function main()   
     editDialogEditBoxTextColor(0xFF00FF00) -- argb color (Green)
end
За столько времени никто не сказал, что он забыл memory.write(pEditBox + 0x127, color, uint size, true) поставить.
А я голову ломал, почему в диалогах не меняется цвет, даже по-другому сделал.
Lua:
--    ['R1'] = 0x21A0B8 , ['R2'] = 0x21A0C0, 
--    ['R3-1'] = 0x26E898, ['R4'] = 0x26E9C8, 
--    ['R4-2'] = 0x26E9C8, ['R5'] = 0x26EB50, ['DL-R1'] = 0x2AC9E0}
function SetDialogInputColors(color)
    local p = memory.getint32(memory.getint32(getModuleHandle('samp.dll') + 0x26E898) + 0x24)
    if p == 0 then return end
    memory.setuint32(p + 0x127, color) -- Цвет текста
    memory.setuint32(p + 0x133, color) -- символа | на конце
end
Возможно нужно запускать в беск. цикле
1745340369426.png
 
  • Нравится
  • Эм
Реакции: Tema05 и VanoKLR

Орк

Известный
399
344
Описание: Меняет имя скришота, сделанного на фотоаппарат. Не захотел создавать отдельную тему, покажу просто, как это реализуется, в примере сохранится по пути "\GTA San Andreas User Files\Gallery\03 мая 2025\photo - 001.jpg"
1746226368627.png

Адреса спизжены отсюда
Пример использования:
Lua:
local memory = require 'memory'
local encoding = require 'encoding'
encoding.default = 'CP1251'
local u8 = encoding.UTF8

function main()
    while memory.getuint8(0xC8D4C0) ~= 9 do wait(1000) end

    -- Получаем путь к Gallery
    local userFilesPath = getFolderPath(5) .. "\\GTA San Andreas User Files"
    local galleryBasePath = userFilesPath .. "\\Gallery"

    -- Получаем текущую дату
    local t = os.date("*t")
    local months = {"января", "февраля", "марта", "апреля", "мая", "июня", "июля", "августа", "сентября", "октября", "ноября", "декабря"}
    local dateStr = u8:decode(string.format("%02d %s %04d", t.day, months[t.month], t.year ))

    -- Путь: Gallery\YYYY-MM-DD
    local fullFolderPath = string.format("%s\\%s", galleryBasePath, dateStr)

    -- Создаём папку, если её нет
    if not doesDirectoryExist(galleryBasePath) then
        createDirectory(galleryBasePath)
    end
    if not doesDirectoryExist(fullFolderPath) then
        createDirectory(fullFolderPath)
    end

    -- Устанавливаем шаблон для имени фото
    local relativePath = string.format("Gallery\\%s\\photo - %%03d.jpg", dateStr)
 
    local str_ptr = memory.strptr(relativePath .. "\0")

    memory.fill(0x872644, 0, 260)
    memory.copy(0x872644, str_ptr, #relativePath + 1)
 
    wait(-1)
end
Код:
Lua:
local photo_pattern = "Gallery\\photo - %03d.jpg"
local str_ptr = memory.strptr(photo_pattern .. "\0")

memory.fill(0x872644, 0, 260)
memory.copy(0x872644, str_ptr, #photo_pattern + 1)
 
Последнее редактирование:
  • Нравится
Реакции: вайега52

Tectrex

Известный
145
171
Описание: Меняет имя скришота, сделанного на фотоаппарат. Не захотел создавать отдельную тему, покажу просто, как это реализуется, в примере сохранится по пути "\GTA San Andreas User Files\Gallery\03 мая 2025\photo - 001.jpg"
Посмотреть вложение 269189
Адреса спизжены отсюда
Пример использования:
Lua:
local memory = require 'memory'
local encoding = require 'encoding'
encoding.default = 'CP1251'
local u8 = encoding.UTF8

function main()
    while memory.getuint8(0xC8D4C0) ~= 9 do wait(1000) end

    -- Получаем путь к Gallery
    local userFilesPath = getFolderPath(5) .. "\\GTA San Andreas User Files"
    local galleryBasePath = userFilesPath .. "\\Gallery"

    -- Получаем текущую дату
    local t = os.date("*t")
    local months = {"января", "февраля", "марта", "апреля", "мая", "июня", "июля", "августа", "сентября", "октября", "ноября", "декабря"}
    local dateStr = u8:decode(string.format("%02d %s %04d", t.day, months[t.month], t.year ))

    -- Путь: Gallery\YYYY-MM-DD
    local fullFolderPath = string.format("%s\\%s", galleryBasePath, dateStr)

    -- Создаём папку, если её нет
    if not doesDirectoryExist(galleryBasePath) then
        createDirectory(galleryBasePath)
    end
    if not doesDirectoryExist(fullFolderPath) then
        createDirectory(fullFolderPath)
    end

    -- Устанавливаем шаблон для имени фото
    local relativePath = string.format("Gallery\\%s\\photo - %%03d.jpg", dateStr)
 
    local str_ptr = memory.strptr(relativePath .. "\0")

    memory.fill(0x872644, 0, 260)
    memory.copy(0x872644, str_ptr, #relativePath + 1)
 
    wait(-1)
end
Код:
Lua:
local photo_pattern = "Gallery\\photo - %03d.jpg"
local str_ptr = memory.strptr(photo_pattern .. "\0")

memory.fill(0x872644, 0, 260)
memory.copy(0x872644, str_ptr, #photo_pattern + 1)
Ты уверен, что там всегда будет путь в формате %UserProfile%\Documents или %UserDir%\GTA San Andreas User Files? А что если у чела стоит не стандартная винда, а какая-нибудь линуксовая сборка или виртуалка?
 

kin4stat

mq-team · kin4@naebalovo.team
Всефорумный модератор
2,761
4,889
Ты уверен, что там всегда будет путь в формате %UserProfile%\Documents или %UserDir%\GTA San Andreas User Files? А что если у чела стоит не стандартная винда, а какая-нибудь линуксовая сборка или виртуалка?
откуда ты вообще это взял
 
  • Нравится
Реакции: kyrtion и Winstаl

kyrtion

Известный
1,314
478
Ты уверен, что там всегда будет путь в формате %UserProfile%\Documents или %UserDir%\GTA San Andreas User Files? А что если у чела стоит не стандартная винда, а какая-нибудь линуксовая сборка или виртуалка?
Вообще-то пускают в линукс игру через Wine из-за того что написана в ОС Windows. В Linux сомневаюсь что запустится без вайна
Могу сказать что игра для Windows и тогда будет использовать скрипт Lua под Windows (или Wine с Linux) в GTA:SA
Если ты можешь доказать обратной, то расскажи как ты сумел запустить GTA:SA без Wine

UPD: Wine создает готовые стандартные Windows папки вроде Документы, Изображение и тд, если ты об этом не слышал
 
  • Нравится
Реакции: Tectrex и Орк

cord

Хватит спать на мне
Проверенный
588
695
Описание: Отключает возвращение камеры при движении в тс, оставляя ее в исходном положении.
Пример использования:
Lua:
function main()
    repeat wait(100) until isSampLoaded()
    local bool = false
    sampRegisterChatCommand("patch", function()
        bool = not bool
        patch(bool)
        sampAddChatMessage("patched", -1)
    end)
    wait(-1)
end
Код:
Lua:
local memory = require("memory")
function patch(bool)
    if bool then
        memory.setuint16(0x5258EC, 0x9090, true)
        memory.setuint16(0x525A31, 0x9090, true)
        memory.setuint32(0x525A33, 0x90909090, true)
    else
        memory.setuint16(0x5258EC, 0x0674, true)
        memory.setuint16(0x525A31, 0x1274, true)
        memory.setuint32(0x525A33, 0xD9442410, true)
    end
end
 

dmitry.karle

Известный
408
108
Сливаю свои штуки дуки для фрейма в мимгуи

Описание: Кастомные тултипы, можно использовать вместо imgui.Hint() если кому надо. Выглядят хайпово как по мне для mimgui (использую moonmonet)
Пример использования:
Lua:
if imgui.Button('button1') then
    sampAddChatMessage('button1', -1)
end
if imgui.IsItemHovered() then
    AnimatedTooltip('Кнопка1', fa.EXCLAMATION) --можно и без иконки
end


if imgui.Button('button2') then
    sampAddChatMessage('button2', -1)
end
if imgui.IsItemHovered() then
    CustomTooltip('кнопка2', fa.CIRCLE_EXCLAMATION) --можно и без иконки
end
1748047708344.png


Код с анимацией пульсации:
Lua:
function AnimatedTooltip(text, icon, speed) --анимационный тултип
    local time = os.clock() * (speed or 2.0)
    local alpha = 0.7 + math.sin(time) * 0.3  -- Пульсация прозрачности
    local bgColor = imgui.ImVec4(0.1 + math.sin(time * 0.5) * 0.05, 0.1 + math.cos(time * 0.7) * 0.05, 0.15, alpha + 0.3) -- Градиентный фон с легкой анимацией
    local textColor = imgui.ImVec4(1.0, 1.0, 1.0, alpha) -- Цвет текста и иконки с анимацией
    imgui.PushStyleVarVec2(imgui.StyleVar.WindowPadding, imgui.ImVec2(12, 10))
    imgui.PushStyleVarFloat(imgui.StyleVar.WindowRounding, 8.0)
    imgui.PushStyleVarFloat(imgui.StyleVar.WindowBorderSize, 1.5)
    imgui.BeginTooltip()
    if icon then
        imgui.TextColored(textColor, icon .. "  ")
        imgui.SameLine()
    end
    imgui.TextColored(textColor, u8(text))
    imgui.EndTooltip()
    imgui.PopStyleColor(1)
    imgui.PopStyleVar(3)
end

Код со статичным тултипом:
Lua:
function CustomTooltip(text, icon) --обычный тултип
    imgui.PushStyleVarVec2(imgui.StyleVar.WindowPadding, imgui.ImVec2(12, 10))
    imgui.PushStyleVarFloat(imgui.StyleVar.WindowRounding, 8.0)
    imgui.PushStyleVarFloat(imgui.StyleVar.WindowBorderSize, 1.5)
    imgui.BeginTooltip()
    if icon then
        imgui.TextColored(imgui.GetStyle().Colors[imgui.Col.ButtonActive], icon .. "  ")  -- Иконка
        imgui.SameLine()
    end
    imgui.Text(u8(text))
    imgui.EndTooltip()
    imgui.PopStyleVar(3)
end

Описание: Кастомные радиуботтоны, если кому надо. Выглядят хайпово как по мне для mimgui (использую moonmonet)
Пример использования:
Lua:
if imgui.RadioButtonBool(u8"##1", s.type == "t1", frame.to_vec4(frame.palette.accent1.color_600), frame.to_vec4(frame.palette.accent1.color_600)) then
    s.type = "t1"
end; imgui.SameLine()
if imgui.RadioButtonBool(u8"##2", s.type == "t2", frame.to_vec4(frame.palette.accent1.color_600), frame.to_vec4(frame.palette.accent1.color_600)) then
    s.type = "t2"
end


if imgui.AnimatedRadioButtonBool(u8"##1", s.type == "t1", frame.to_vec4(frame.palette.accent1.color_600), frame.to_vec4(frame.palette.accent1.color_600)) then
    s.type = "t1"
end; imgui.SameLine()
if imgui.AnimatedRadioButtonBool(u8"##2", s.type == "t2", frame.to_vec4(frame.palette.accent1.color_600), frame.to_vec4(frame.palette.accent1.color_600)) then
    s.type = "t2"
end;
1748048232131.png


Код с анимацией пульсации:
Lua:
function imgui.AnimatedRadioButtonBool(label, state, active_color, inactive_color)
    local draw_list = imgui.GetWindowDrawList()
    local cursor_pos = imgui.GetCursorScreenPos()
    local button_size = imgui.ImVec2(23, 23)
    local original_pos = imgui.GetCursorPos()
    local style = imgui.GetStyle()
  
    local display_text = label:gsub('##.*$', '')
    local text_size = imgui.CalcTextSize(u8(display_text))
  
    local button_id = '##radio_'..label
    local pressed = imgui.InvisibleButton(button_id, button_size)
  
    -- центр круга
    local center = imgui.ImVec2(cursor_pos.x + button_size.x / 2,cursor_pos.y + button_size.y / 2)
  
    -- Параметры анимации
    local time = imgui.GetTime()
    local anim_speed = 10.0
    local pulse = 0.2 * math.sin(time * anim_speed) + 0.8
  
    -- Цвета для состояний
    local outer_color = state and active_color or inactive_color
    local inner_color = imgui.ImVec4(outer_color.x,outer_color.y,outer_color.z,outer_color.w * 0.7 * (state and pulse or 1.0))
  
    -- внешний круг с анимацией
    draw_list:AddCircleFilled(center,button_size.x / 2, imgui.GetColorU32Vec4(imgui.ImVec4(outer_color.x,outer_color.y,outer_color.z,outer_color.w * 0.3)), 32)
  
    -- внутренний круг
    draw_list:AddCircleFilled(center,button_size.x / 2.5, imgui.GetColorU32Vec4(inner_color), 32)
  
    -- Эффект выделения при наведении
    if imgui.IsItemHovered() then
        draw_list:AddCircle(center,button_size.x / 2 + 1, imgui.GetColorU32Vec4(imgui.ImVec4(1, 1, 1, 0.3)), 32, 1.5)
    end
  
    -- Обводка для активного
    if state then
        draw_list:AddCircle(center,button_size.x / 2 + 1, imgui.GetColorU32Vec4(imgui.ImVec4(1, 1, 1, 0.8)), 32, 1.5)
    end
  
    -- Текст метки
    imgui.SetCursorPos(imgui.ImVec2(original_pos.x + button_size.x + style.ItemSpacing.x, original_pos.y + (button_size.y - text_size.y) / 2
    ))
    imgui.Text(label)
    return pressed
end


Статичный радиобуттон:
Lua:
function imgui.RadioButtonBool(label, state, color)
    local style = imgui.GetStyle()
    local size = imgui.ImVec2(23, 23)
    local pos = imgui.GetCursorScreenPos()
    local center = imgui.ImVec2(pos.x + size.x / 2, pos.y + size.y / 2)
    local radius = size.x / 2
    local innerRadius = size.x / 2.7
    local draw_list = imgui.GetWindowDrawList()
    local original_cursor_pos = imgui.GetCursorPos()
    local button = imgui.InvisibleButton(label .. "##RadioColor", size)
    draw_list:AddCircleFilled( center,  radius,  imgui.GetColorU32Vec4(imgui.ImVec4(color.x, color.y, color.z, 0.5)),  32) -- Внешний круг (фон)
    draw_list:AddCircleFilled(center,  innerRadius,  imgui.GetColorU32Vec4(imgui.ImVec4(color.x, color.y, color.z, color.w)),  32) -- Внутренний круг
    if state then -- Обводка в зависимости от состояния
        draw_list:AddCircle(center,  radius + 1,  imgui.GetColorU32Vec4(imgui.ImVec4(1, 1, 1, 1)),  32,  2) -- Яркая обводка для активного состояния
        draw_list:AddCircle(center,  radius + 3,  imgui.GetColorU32Vec4(imgui.ImVec4(1, 1, 1, 0.3)),  32,  1) -- Свечение
    end
    imgui.SetCursorPos(imgui.ImVec2(original_cursor_pos.x + size.x + style.ItemSpacing.x, original_cursor_pos.y + (size.y - imgui.GetTextLineHeight()) / 2))
    imgui.Text(label)
    imgui.SetCursorPos(imgui.ImVec2(original_cursor_pos.x, original_cursor_pos.y + size.y + style.ItemSpacing.y))
    return button
end

Описание: Кастомные радиобуттон с анимацией при наведении и со свечящей обводкой (знаю что сливали их), но даю свою версию, кому надо. Можно использовать для стилей mimgui. Выглядят хайпово как по мне для mimgui (использую moonmonet)
Пример использования:
Lua:
--стили moonmonet в моём случае
local f = {
    list = {
        4281859840,
        4278206719,
    }
}

--во фрейме
for k, v in ipairs(f.list) do
    if RadioColor(tostring(k), ваш кфг (s.t.m) == k, imgui.ColorConvertU32ToFloat4(v)) then
        ваш кфг (s.t.m) = k
    end
end
1748048628690.png


Код цветных радиобуттон:
Lua:
function RadioColor(label, state, color)
    local size = imgui.ImVec2(28, 28)
    local center = imgui.ImVec2(imgui.GetCursorScreenPos().x + size.x / 2, imgui.GetCursorScreenPos().y + size.y / 2)
    local radius = size.x / 2
    local innerRadius = size.x / 2.7
    local button = imgui.InvisibleButton(label .. "##RadioColor", size)
    imgui.GetWindowDrawList():AddCircleFilled(center, radius, imgui.GetColorU32Vec4(imgui.ImVec4(color.z, color.y, color.x, 0.5)), 100) -- Внешний круг (фон)
    imgui.GetWindowDrawList():AddCircleFilled(center, innerRadius, imgui.GetColorU32Vec4(imgui.ImVec4(color.z, color.y, color.x, color.w)), 100) -- Внутренний круг (основной цвет)
    if state then
        imgui.GetWindowDrawList():AddCircle(center, radius + 1, imgui.GetColorU32Vec4(imgui.ImVec4(1, 1, 1, 1)), 100, 2) -- Белая обводка
        imgui.GetWindowDrawList():AddCircle(center, radius + 3, imgui.GetColorU32Vec4(imgui.ImVec4(1, 1, 1, 0.3)), 100, 1) -- Эффект "свечения" (опционально)
    else
        imgui.GetWindowDrawList():AddCircle(center, radius, imgui.GetColorU32Vec4(imgui.ImVec4(0.5, 0.5, 0.5, 0.3)), 100, 1) -- Сероватая обводка для неактивного состояния
    end
    if imgui.IsItemHovered() then
        imgui.GetWindowDrawList():AddCircle(center, radius + 2, imgui.GetColorU32Vec4(imgui.ImVec4(1, 1, 1, 0.5)), 100, 1) -- Эффект при наведении (даже для неактивного состояния)
    end
    return button
end

Описание: Кастомная менюшка с плавными анимациями, если кому надо. mimgui (использую moonmonet)
Пример использования:
Lua:
softMenuItem = 0
softMenu = {
    'No bike fall',
    'ClickWarp',
}

--во фрейме
for i=1, #softMenu do
    if imgui.softMenu(u8(softMenu[i]), imgui.ImVec2(180, 25), softMenuItem == i) then
        softMenuItem = i
    end
end

if softMenuItem == 1 then; imgui.SetCursorPos(imgui.ImVec2(10, 10))
    imgui.CenterColoredText(imgui.GetStyle().Colors[imgui.Col.ButtonActive], 'text1')
end
if softMenuItem == 2 then; imgui.SetCursorPos(imgui.ImVec2(10, 10))
    imgui.CenterColoredText(imgui.GetStyle().Colors[imgui.Col.ButtonActive], 'text2')
end
1748049320302.png


Код:
Lua:
function imgui.softMenu(text, size, is_selected, rounding, element_id)
    local SELECTION_BAR_WIDTH = 3
    local HOVER_ANIM_SPEED = 0.2
    local SELECTED_BG_ALPHA = 0.40
    function lerp(a, b, t) -- функция для плавного перехода
        return a + (b - a) * math.min(math.max(t, 0), 1)
    end
    if not imgui.softMenu_State then
        imgui.softMenu_State = {animations = {}, last_time = imgui.GetTime()}
    end
    local state = imgui.softMenu_State
    imgui.BeginGroup()
    local button_pressed = imgui.InvisibleButton(text, size)
    local is_hovered = imgui.IsItemHovered()
    local item_min = imgui.GetItemRectMin()
    local item_max = imgui.GetItemRectMax()
    element_id = element_id or text
    if not state.animations[element_id] then
        state.animations[element_id] = {alpha = 0, last_update = state.last_time}
    end
    local anim = state.animations[element_id]
    local current_time = imgui.GetTime()
    local delta_time = current_time - anim.last_update
    anim.last_update = current_time
    state.last_time = current_time
    if is_hovered then
        anim.alpha = lerp(anim.alpha, 1.0, delta_time * 10 * HOVER_ANIM_SPEED)
    else
        anim.alpha = lerp(anim.alpha, 0.0, delta_time * 10 * HOVER_ANIM_SPEED)
        if anim.alpha < 0.01 then
            anim.alpha = 0
        end
    end
    local draw_list = imgui.GetWindowDrawList()
    local col_text = imgui.GetStyle().Colors[imgui.Col.Text]
    local col_active = imgui.GetStyle().Colors[imgui.Col.ButtonActive]
    local col_hover = imgui.GetStyle().Colors[imgui.Col.ButtonHovered]
    rounding = rounding or 4.0 -- Настройка закругления
    if is_selected then  -- Полоска выделения и фон для выбранного элемента
        local bar_pos = imgui.ImVec2(item_min.x, item_min.y)
        local bar_size = imgui.ImVec2(SELECTION_BAR_WIDTH, item_max.y - item_min.y)
        draw_list:AddRectFilled(bar_pos, imgui.ImVec2(bar_pos.x + bar_size.x, bar_pos.y + bar_size.y), imgui.GetColorU32Vec4(col_active), rounding, imgui.DrawCornerFlags.Left)
        local selected_bg_color = imgui.ImVec4(col_active.x, col_active.y, col_active.z, col_active.w * SELECTED_BG_ALPHA) -- Фон выбранного элемента
        draw_list:AddRectFilled(imgui.ImVec2(item_min.x + SELECTION_BAR_WIDTH, item_min.y), item_max, imgui.GetColorU32Vec4(selected_bg_color), rounding, imgui.DrawCornerFlags.Right)
    end
    if anim.alpha > 0.20 then -- Фон при наведении
        local bg_color = imgui.ImVec4(col_hover.x, col_hover.y, col_hover.z, col_hover.w * anim.alpha * 0.3)
        draw_list:AddRectFilled(imgui.ImVec2(item_min.x + SELECTION_BAR_WIDTH, item_min.y), item_max, imgui.GetColorU32Vec4(bg_color), rounding, imgui.DrawCornerFlags.Right)
    end
    local text_size = imgui.CalcTextSize(text) -- Текст
    local text_pos = imgui.ImVec2(item_min.x + SELECTION_BAR_WIDTH + 5 + (size.x - text_size.x - SELECTION_BAR_WIDTH - 5) * 0.5, item_min.y + (size.y - text_size.y) * 0.5)
    local text_color = imgui.ImVec4(col_text.x, col_text.y, col_text.z, is_selected and col_text.w or col_text.w * 0.9) -- Цвет текста
    draw_list:AddText(text_pos, imgui.GetColorU32Vec4(text_color), text)
    imgui.EndGroup()
    return button_pressed
end

Описание: Кастомный спиннер с приятной анимацией + анимацией точек "Загрузка..." , для mimgui (использую moonmonet)
Пример использования:
Lua:
--во фрейме
imgui.Spinner(u8' Поиск/загрузка', 10, 2, imgui.ColorConvertFloat4ToU32(imgui.GetStyle().Colors[imgui.Col.ButtonHovered]))
imgui.Spinner(u8' Загрузка', 9, 2, imgui.ColorConvertFloat4ToU32(imgui.GetStyle().Colors[imgui.Col.ButtonHovered]))
1748072314033.png


Код:
Lua:
function imgui.Spinner(label, radius, thickness, color, speed)
    speed = speed or 1.0
    local segments = 12
    local trail_length = 0.6
    local style = imgui.GetStyle()
    local pos = imgui.GetCursorScreenPos()
    local size = imgui.ImVec2(radius * 2, (radius + style.FramePadding.y) * 2)
    imgui.BeginGroup()
    local draw_list = imgui.GetWindowDrawList()
    local center = imgui.ImVec2(pos.x + radius, pos.y + radius + style.FramePadding.y)
    local current_time = imgui.GetTime() * speed
    local col_vec4 = type(color) == "number" and imgui.ColorConvertU32ToFloat4(color) or imgui.ImVec4(color.Value.x, color.Value.y, color.Value.z, color.Value.w)
    local text_color = imgui.ImVec4(1.0, 1.0, 1.0, 1.0)  -- Белый цвет
    local spin_angle = current_time * 6.28318 -- Анимация спиннера
    for i = 0, segments - 1 do
        local angle = spin_angle + (i / segments) * 6.28318
        local opacity = 0.3 + 0.7 * (1.0 - ((segments - i) % segments) / segments)
        local point = imgui.ImVec2(center.x + math.cos(angle) * radius, center.y + math.sin(angle) * radius)
        draw_list:AddCircleFilled(point, thickness, imgui.GetColorU32Vec4(imgui.ImVec4(col_vec4.x, col_vec4.y, col_vec4.z, col_vec4.w * opacity)), 8)
    end
    local dot_animation = math.floor(current_time * 2) % 4 -- Анимация текста "Загрузка..."
    local loading_text = label .. string.rep(".", dot_animation)
    imgui.SameLine()
    local text_height = imgui.GetTextLineHeight()
    local text_pos = imgui.ImVec2(pos.x + radius * 2 + style.ItemSpacing.x, pos.y + (radius * 2 - text_height) / 2)
    draw_list:AddText(text_pos, imgui.GetColorU32Vec4(text_color), loading_text) --text
    imgui.EndGroup()
    local text_width = imgui.CalcTextSize(label .. "...").x
    return imgui.ImVec2(radius * 2 + style.ItemSpacing.x + text_width, radius * 2)
end
 
Последнее редактирование:

вайега52

Налуашил состояние
Модератор
2,975
3,092
Описание: Рендер чата как отдельной текстуры в ImGUI (не обязательно ImGUI, т.к. получается IDirect3DTexture9, который можно рендерить, как тебе нужно)
Пример использования:
Lua:
local imgui = require("mimgui")
imgui.OnFrame(
  function() return true end,
  function(player)
    local sizeX, sizeY = getScreenResolution()
    imgui.SetNextWindowPos(imgui.ImVec2(sizeX / 2, sizeY / 2), imgui.Cond.FirstUseEver, imgui.ImVec2(0.5, 0.5))
    imgui.SetNextWindowSize(imgui.ImVec2(500, 500), imgui.Cond.FirstUseEver)
    imgui.Begin("Main Window", renderWindow)
    local texture = getChatTexture()
    imgui.Image(texture, imgui.ImVec2(sizeX / 2, sizeY / 2))
    imgui.End()
  end
)
Код:
Lua:
local ffi = require("ffi")

function getChatTexture()
    -- Вроде как для всех версий сампа один оффсет
    return ffi.cast("void**", sampGetChatInfoPtr() + 0x63BA)[0]
end
1749850143673.png
 

ARMOR

Я креветка
Модератор
5,068
7,373
Описание: Возвращает текст выбраного елемента диалога типа LISTBOX

Код ( Только для R3, адреса для других версий ищите ниже ):
Lua:
local ffi = require("ffi")
local samp = getModuleHandle("samp.dll")

function getDialogSelectedItemText()
    local CDXUTListBox__GetSelectedIntex = ffi.cast("int(__thiscall*)(uintptr_t this, int nPreviousSelected)", samp + 0x88760)
    local CDXUTListBox__GetItem = ffi.cast("uintptr_t(__thiscall*)(uintptr_t this, int nIndex)", samp + 0x8A2B0)

    local pDialog = sampGetDialogInfoPtr()
    if (pDialog == nil) then return false, "" end

    local pListBox = ffi.cast("uintptr_t*", pDialog + 0x20)[0];
    if (pListBox == nil) then return false, "" end

    local selectedIndex = CDXUTListBox__GetSelectedIntex(pListBox, -1)
    local pItem = CDXUTListBox__GetItem(pListBox, selectedIndex)
    return true, ffi.string(ffi.cast("const char*", pItem + 0x00))
end

Адреса для других версий:
C++:
R1:
CDXUTListBox__GetSelectedIntex = 0x84850 
CDXUTListBox__GetItem = 0x86390

R5:
CDXUTListBox__GetSelectedIntex = 0x88E70 
CDXUTListBox__GetItem = 0x8A9C0


DL:
CDXUTListBox__GetSelectedIntex = 0x888F0
CDXUTListBox__GetItem = 0x8A440

Скриншот:
1750897733361.jpeg
 
  • Нравится
Реакции: bfix

ARMOR

Я креветка
Модератор
5,068
7,373
За столько времени никто не сказал, что он забыл memory.write(pEditBox + 0x127, color, uint size, true) поставить.
А я голову ломал, почему в диалогах не меняется цвет, даже по-другому сделал.
Lua:
--    ['R1'] = 0x21A0B8 , ['R2'] = 0x21A0C0,
--    ['R3-1'] = 0x26E898, ['R4'] = 0x26E9C8,
--    ['R4-2'] = 0x26E9C8, ['R5'] = 0x26EB50, ['DL-R1'] = 0x2AC9E0}
function SetDialogInputColors(color)
    local p = memory.getint32(memory.getint32(getModuleHandle('samp.dll') + 0x26E898) + 0x24)
    if p == 0 then return end
    memory.setuint32(p + 0x127, color) -- Цвет текста
    memory.setuint32(p + 0x133, color) -- символа | на конце
end
Возможно нужно запускать в беск. цикле
Посмотреть вложение 268470
В таком случае уже бы дал оффсеты для всех елементов editBox'а. ( Слева оффсеты )
1750898161564.png
 

cord

Хватит спать на мне
Проверенный
588
695
Описание: Устанавливает высоту ChatBubble
Пример использования:
Lua:
function main()
    repeat wait(100) until isSampAvailable()
    setChatBubbleHeight(0.015)
end
Код:
Lua:
--- @param height float
--- @return boolean result
function setChatBubbleHeight(height)
    height = height or 0.06499999762
    local samp = getModuleHandle("samp.dll")
    return require("memory").setfloat(samp + 0xD795C, height, true)
end
Заменить 0xD795C на 0x668B1
 
  • Нравится
Реакции: bfix и вайега52

chromiusj

модерирую шмодерирую
Модератор
5,944
4,280
Описание: Возможно сырой,но процедурный генератор тем. Создает полноценную, гармоничную и настраиваемую дизайн-систему для окна на основе одного базового цвета и продвинутых вычислениях (короче прочитал https://matthewstrom.com/writing/generating-color-palettes/):
Пример использования:
Lua:
-- 1. Создание генератора (вызывается один раз)
-- config: необязательная таблица; если не указана, используется default_theme_config.
local theme_generator = createThemeGenerator(config)

-- 2. Применение темы (вызывается в OnInitialize, не особо сильно влияет на производительность)
-- Применяет сгенерированную тему.
theme_generator:apply()

-- 3. Отрисовка настроек (вызывается в OnFrame, где вам удобно)
-- Рисует все элементы управления: колорпикер, чекбокс и слайдеры настроек.
theme_generator:render_controls()
Конфигурация (default_theme_config):
Это главная таблица, управляющая внешним видом. Она состоит из двух частей:
1. colors
Здесь каждому элементу интерфейса назначается цвет из сгенерированных палитр.
Типо так: [imgui.Col.Элемент] = { настройки }
Настройки:
- source: Из какой палитры брать цвет.
- 'primary' - Основная, цветная палитра. Используется для акцентов и интерактивных элементов.
- 'neutral' - Вспомогательная, тонированная серая палитра. Используется для фона и неактивных элементов.
- light_shade: Какой оттенок (от 1 до 10) использовать для СВЕТЛОЙ темы. 1 - самый светлый, 10 - самый темный.
- dark_shade: Какой оттенок (от 1 до 10) использовать для ТЕМНОЙ темы. 1 - самый светлый, 10 - самый темный.
- a: Прозрачность (альфа-канал). Необязательный параметр. 1.0 - полностью непрозрачный.
Пример: [imgui.Col.Button] = { source = 'primary', light_shade = 6, dark_shade = 5 } означает, что в светлой теме кнопка возьмет 6-й (довольно темный) оттенок из цветной палитры, а в темной даст 5-й (более яркий).
2. style
Отвечает за не-цветовые параметры: размеры, скругления, отступы и т.д.
Пример: WindowRounding = 8.0 задает скругление углов окна. WindowPadding = {8, 8} задает внутренние отступы в окне.
Пример работы:
Lua:
local imgui = require 'mimgui'
local encoding = require 'encoding'
encoding.default = 'CP1251'
local u8 = encoding.UTF8
local new = imgui.new

local default_theme_config = {
    colors = {
        [imgui.Col.Text]                  = { source = 'neutral', light_shade = 10, dark_shade = 2 },
        [imgui.Col.TextDisabled]          = { source = 'neutral', light_shade = 7,  dark_shade = 4 },
        [imgui.Col.TextSelectedBg]        = { source = 'primary', light_shade = 3,  dark_shade = 8, a = 0.35 },
        [imgui.Col.WindowBg]              = { source = 'neutral', light_shade = 2,  dark_shade = 10, a = 0.97 },
        [imgui.Col.ChildBg]               = { source = 'neutral', light_shade = 1,  dark_shade = 9, a = 1.0 },
        [imgui.Col.PopupBg]               = { source = 'neutral', light_shade = 1,  dark_shade = 10, a = 0.96 },
        [imgui.Col.Border]                = { source = 'neutral', light_shade = 5,  dark_shade = 7 },
        [imgui.Col.BorderShadow]          = { a = 0.0 },
        [imgui.Col.Separator]             = { source = 'neutral', light_shade = 4,  dark_shade = 8 },
        [imgui.Col.SeparatorHovered]      = { source = 'primary', light_shade = 5,  dark_shade = 6 },
        [imgui.Col.SeparatorActive]       = { source = 'primary', light_shade = 6,  dark_shade = 5 },
        [imgui.Col.FrameBg]               = { source = 'neutral', light_shade = 3,  dark_shade = 8 },
        [imgui.Col.FrameBgHovered]        = { source = 'neutral', light_shade = 4,  dark_shade = 7 },
        [imgui.Col.FrameBgActive]         = { source = 'primary', light_shade = 3,  dark_shade = 9 },
        [imgui.Col.TitleBg]               = { source = 'primary', light_shade = 4,  dark_shade = 8 },
        [imgui.Col.TitleBgActive]         = { source = 'primary', light_shade = 5,  dark_shade = 7 },
        [imgui.Col.TitleBgCollapsed]      = { source = 'neutral', light_shade = 3,  dark_shade = 9 },
        [imgui.Col.Button]                = { source = 'primary', light_shade = 6,  dark_shade = 5 },
        [imgui.Col.ButtonHovered]         = { source = 'primary', light_shade = 5,  dark_shade = 6 },
        [imgui.Col.ButtonActive]          = { source = 'primary', light_shade = 7,  dark_shade = 4 },
        [imgui.Col.Header]                = { source = 'primary', light_shade = 6,  dark_shade = 6 },
        [imgui.Col.HeaderHovered]         = { source = 'primary', light_shade = 5,  dark_shade = 7 },
        [imgui.Col.HeaderActive]          = { source = 'primary', light_shade = 7,  dark_shade = 5 },
        [imgui.Col.CheckMark]             = { source = 'primary', light_shade = 8,  dark_shade = 3 },
        [imgui.Col.SliderGrab]            = { source = 'primary', light_shade = 7,  dark_shade = 4 },
        [imgui.Col.SliderGrabActive]      = { source = 'primary', light_shade = 8,  dark_shade = 3 },
        [imgui.Col.ResizeGrip]            = { source = 'neutral', light_shade = 5,  dark_shade = 5, a = 0.25 },
        [imgui.Col.ResizeGripHovered]     = { source = 'neutral', light_shade = 6,  dark_shade = 4, a = 0.67 },
        [imgui.Col.ResizeGripActive]      = { source = 'neutral', light_shade = 7,  dark_shade = 3, a = 0.95 },
        [imgui.Col.PlotLines]             = { source = 'primary', light_shade = 7,  dark_shade = 4 },
        [imgui.Col.PlotLinesHovered]      = { source = 'primary', light_shade = 8,  dark_shade = 3 },
        [imgui.Col.PlotHistogram]         = { source = 'primary', light_shade = 7,  dark_shade = 4 },
        [imgui.Col.PlotHistogramHovered]  = { source = 'primary', light_shade = 8,  dark_shade = 3 },
        [imgui.Col.Tab]                   = { source = 'neutral', light_shade = 4,  dark_shade = 8 },
        [imgui.Col.TabHovered]            = { source = 'primary', light_shade = 5,  dark_shade = 6 },
        [imgui.Col.TabActive]             = { source = 'primary', light_shade = 5,  dark_shade = 7 },
        [imgui.Col.TabUnfocused]          = { source = 'neutral', light_shade = 3,  dark_shade = 9 },
        [imgui.Col.TabUnfocusedActive]    = { source = 'primary', light_shade = 4,  dark_shade = 8 },
        [imgui.Col.MenuBarBg]             = { source = 'neutral', light_shade = 2,  dark_shade = 10 },
        [imgui.Col.ScrollbarBg]           = { source = 'neutral', light_shade = 2,  dark_shade = 9 },
        [imgui.Col.ScrollbarGrab]         = { source = 'neutral', light_shade = 5,  dark_shade = 6 },
        [imgui.Col.ScrollbarGrabHovered]  = { source = 'neutral', light_shade = 6,  dark_shade = 5 },
        [imgui.Col.ScrollbarGrabActive]   = { source = 'neutral', light_shade = 7,  dark_shade = 4 },
        [imgui.Col.DragDropTarget]        = { source = 'primary', light_shade = 8,  dark_shade = 2, a = 0.9 },
        [imgui.Col.NavHighlight]          = { source = 'primary', light_shade = 5,  dark_shade = 5, a = 0.8 },
        [imgui.Col.NavWindowingHighlight] = { source = 'primary', light_shade = 6,  dark_shade = 4, a = 0.7 },
        [imgui.Col.NavWindowingDimBg]     = { a = 0.2 },
        [imgui.Col.ModalWindowDimBg]      = { a = 0.35 },
    },
    style = {
        WindowPadding={8,8}, FramePadding={6,4}, ItemSpacing={8,4}, ItemInnerSpacing={4,4}, WindowRounding=8.0, FrameRounding=6.0,
        ChildRounding=6.0, PopupRounding=6.0, GrabRounding=6.0, ScrollbarRounding=12.0, TabRounding=6.0, WindowBorderSize=1.0,
        FrameBorderSize=0.0, ChildBorderSize=1.0,
    }
}

function createThemeGenerator(config)
    config = config or default_theme_config
    local function rgb_to_hsv(r,g,b) local m,n=math.max(r,g,b),math.min(r,g,b) local h,s,v=0,0,m local d=m-n if m~=0 then s=d/m end if m~=n then if r==m then h=(g-b)/d elseif g==m then h=2+(b-r)/d else h=4+(r-g)/d end h=h/6;if h<0 then h=h+1 end end return h,s,v end
    local function hsv_to_rgb(h,s,v) if s==0 then return v,v,v end local i=math.floor(h*6) local f=(h*6)-i local p=v*(1-s) local q=v*(1-s*f) local t=v*(1-s*(1-f)) i=i%6 if i==0 then return v,t,p elseif i==1 then return q,v,p elseif i==2 then return p,v,t elseif i==3 then return p,q,v elseif i==4 then return t,p,v else return v,p,q end end
    local function lerp(a,b,t) return a+(b-a)*t end
    local function easeInOutSine(x) return -(math.cos(math.pi * x) - 1) / 2 end
    local generator={base_color=new.float[4]({0.5,0.1,0.6,0.96}),is_dark_theme=new.bool(true),hue_shift_amount=new.float({-0.03}),saturation=new.float({1.0}),brightness=new.float({0.0}),contrast=new.float({1.0}),generated_palettes={primary={},neutral={}},anim={progress=1.0,start_progress=1.0,start_time=0,duration=0.3}}
    local function generate_palette_scale(h_base, s_base, v_base, base_alpha)
        local scale = {}
        local V_MIN_BASE, V_MAX_BASE = 0.10, 0.98
        local v_min = math.max(0, V_MIN_BASE + generator.brightness[0])
        local v_max = math.min(1, V_MAX_BASE + generator.brightness[0])
        local v_center = (v_min + v_max) / 2
        local v_range = (v_max - v_min) * generator.contrast[0]
        for i = 1, 10 do
            local t = (i - 1) / 9
            local v_curve = (1 - t)^2
            local final_v = lerp(v_center - v_range/2, v_center + v_range/2, v_curve)
            local s_curve = s_base * (-0.002 * t^3 + 0.04 * t^2 + 0.86 * t + 0.1)
            local final_s = s_curve * generator.saturation[0]
            local final_h = (h_base + generator.hue_shift_amount[0] * t + 1) % 1
            final_s, final_v = math.max(0, math.min(1, final_s)), math.max(0, math.min(1, final_v))
            local r, g, b = hsv_to_rgb(final_h, final_s, final_v)
            scale[i] = imgui.ImVec4(r, g, b, base_alpha)
        end
        return scale
    end

    function generator:apply()
        local style, colors = imgui.GetStyle(), imgui.GetStyle().Colors
        local r, g, b, a = self.base_color[0], self.base_color[1], self.base_color[2], self.base_color[3]
        local h, s, v = rgb_to_hsv(r, g, b)
        local neutral_s = math.max(0.05, s * 0.1)
        self.generated_palettes.primary = generate_palette_scale(h, s, v, a)
        self.generated_palettes.neutral = generate_palette_scale(h, neutral_s, v, a)
        local target_inversion = self.is_dark_theme[0] and 1.0 or 0.0
        if self.anim.progress ~= target_inversion then
            local time_since_start = os.clock() - self.anim.start_time
            if time_since_start >= self.anim.duration then self.anim.progress = target_inversion
            else self.anim.progress = lerp(self.anim.start_progress, target_inversion, easeInOutSine(time_since_start / self.anim.duration)) end
        end
        local theme_inversion = self.anim.progress
        for col_id, cfg in pairs(config.colors) do
            if cfg.source then
                local source_palette = self.generated_palettes[cfg.source]
                local light_shade, dark_shade = cfg.light_shade, cfg.dark_shade
                local current_shade = lerp(light_shade, dark_shade, theme_inversion)
                local final_idx = math.max(1, math.min(10, math.floor(current_shade + 0.5)))
                colors[col_id] = source_palette[final_idx]
            end
            if cfg.a then colors[col_id].w = cfg.a end
        end
        for style_name, value in pairs(config.style) do
            if type(value) == 'table' then style[style_name] = imgui.ImVec2(value[1], value[2])
            else style[style_name] = value end
        end
    end
    function generator:render_controls()
        local theme_changed = false
        if imgui.ColorPicker4(u8'Базовый цвет', self.base_color, imgui.ColorEditFlags.NoSidePreview + imgui.ColorEditFlags.NoSmallPreview + imgui.ColorEditFlags.NoInputs + imgui.ColorEditFlags.PickerHueWheel + imgui.ColorEditFlags.AlphaBar) then theme_changed = true end
        imgui.Separator()
        if imgui.Checkbox(u8'Темная тема', self.is_dark_theme) then self.anim.start_time = os.clock(); self.anim.start_progress = self.anim.progress end
        imgui.Spacing()
        imgui.Text(u8'Тонкая настройка палитры')
        if imgui.SliderFloat(u8'Насыщенность', self.saturation, 0.2, 1.8, '%.2f') then theme_changed = true end
        if imgui.SliderFloat(u8'Яркость', self.brightness, -0.2, 0.2, '%.2f') then theme_changed = true end
        if imgui.SliderFloat(u8'Контраст', self.contrast, 0.5, 1.5, '%.2f') then theme_changed = true end
        if imgui.SliderFloat(u8'Сдвиг оттенка', self.hue_shift_amount, -0.2, 0.2, '%.3f') then theme_changed = true end
        if theme_changed then self:apply() end
    end
    return generator
end



local theme_generator = createThemeGenerator(default_theme_config)
local window_state = new.bool(true)

imgui.OnInitialize(function()
    theme_generator:apply()
end)

imgui.OnFrame(function() return window_state[0] end, function()
    theme_generator:apply()
    imgui.SetNextWindowSize(imgui.ImVec2(370, 520), imgui.Cond.FirstUseEver)
    imgui.Begin(u8'Генератор тем 666', window_state)
    if imgui.BeginTabBar('Tabs') then
        if imgui.BeginTabItem(u8'Главное') then
            imgui.TextWrapped(u8'Пример элементов')
            imgui.Separator()
            if imgui.CollapsingHeader(u8'Интерактивные элементы') then
                imgui.Button(u8'Кнопка')
                imgui.Checkbox(u8"Чекбокс)", new.bool(true))
                imgui.SliderFloat(u8'Слайдер', new.float(0.5), 0, 1)
            end
            imgui.EndTabItem()
        end
        if imgui.BeginTabItem(u8'Настройки темы') then
            theme_generator:render_controls()
            imgui.Separator()
            imgui.Text(u8'Основная (акцентная) палитра:')
            local palette = theme_generator.generated_palettes.primary
            for i=1, 10 do
                imgui.ColorButton(('c%d'):format(i), palette[i], imgui.ColorEditFlags.NoTooltip, imgui.ImVec2(28, 28))
                if i < 10 then imgui.SameLine() end
            end
            imgui.Text(u8'Нейтральная (основная) палитра:')
            local neutral_palette = theme_generator.generated_palettes.neutral
            for i=1, 10 do
                imgui.ColorButton(('nc%d'):format(i), neutral_palette[i], imgui.ColorEditFlags.NoTooltip, imgui.ImVec2(28, 28))
                if i < 10 then imgui.SameLine() end
            end
            imgui.EndTabItem()
        end
        imgui.EndTabBar()
    end
    imgui.End()
end)
Пример темы:

123.png

В планах добавить более удобную адаптацию цветов к неактивным элементам по типу фона окна, чайлдов и т.п