Исходник Софт 🪑 StoolMenu - Простота, красота и удобство!

stool

Известный
Автор темы
417
337
1716847076264.png

Я думаю почти каждый видел сниппет от @chapo, который позволял делать прикольные вертикальные вкладки, но в нём был ряд недочётов один из которых неверный порядок расположения кнопок. Для некоторых сниппет был сложен в понимании, поэтому я решил "создать" библиотеку, который реимплементирует способ реализации такой менюхи и делает управление этой менюхой намного проще. И так встречайте: 🪑 StoolMenu . Это самая первая версия, так что я буду рад вашим идеям, замечаниям и исправлениям.
1716847167899.png


В аналогии с решением от @chapo, менюшка красиво вываливает кнопки и даёт возможность посмотреть на тултип (подсказку), наведя курсор на кнопку. Помимо этого я пересмотрел решение с точки зрения дизайна и вынес тайтл-бар из окна, поступив так же и с кнопкой закрытия меню. StoolMenu не должен конфликтовать с кастомными темами, хорошо работает со шрифтами и в целом функционирует так, как и ожидается.
















ТРЕБОВАНИЯ К УСТАНОВКЕ
- MoonLoader
- mimgui
- mimgui_blur
- Мозги
- Руки (желательно прямые)

ПОРЯДОК УСТАНОВКИ
1. Скачать mimgui_stoolmenu.zip
2. Распаковать содержимое архива в директорию %gta%/moonloader/lib
3. Всё)

Lua:
StoolMenu.MenuManager:new()
-- Возвращает объект (таблицу) для работы с меню.
-- Имеет поля:
---- isOpen (bool) - активно ли текущее окно
---- idxSelection (int) - индекс или номер выбранной вкладки
---- subMenuList (table) - список подменюшек (вкладок, кнопок...), каждый итем содержит: иконку, название, описание

StoolMenu.MenuManager:addMenu(icon, title, desc)
-- Функция для добавления ОДНОГО пункта меню в таблицу
-- Ничего не возвращает

StoolMenu.MenuManager:addMenuList(_table)
-- Функция которая добавит ВСЕ пункты меню из указанной таблицы (_table) в subMenuList

StoolMenu.MenuManager:getMenus()
-- Функция которая возвращает список менюшек (subMenuList)

StoolMenu.MenuManager:select(menu)
-- Функция для выбора вкладки (делает вкладку активной через idxSelection)
-- Принимает номер\индекс, либо название

StoolMenu.MenuManager:isSelected(menu_num)
-- вернет true или false в зависимости выбрана ли эта вкладка

imgui.BeginStoolMenu(winTitle, winVariable, winFlags, menu, vertMenuW, blur_d)
-- Начало структуры меню \ кастомный виджет
-- Принимает:
---- Название окна
---- Переменная окна
---- Флаги окна
---- Список меню (StoolMenu.MenuManager:new())
---- Ширину полоски под вкладками (опционально)
---- Интенсивность размытия (опционально)

imgui.EndStoolMenu()
-- Конец структуры меню
Lua:
-- NE BUD PIDOROM
-- USE CP1251 KODIROVKU

script_author('stool')
script_name('stool menu example')
script_version('v0.1')
-- важная хуйня, импортируем то с чем будем работать
local imgui             = require("mimgui")          -- имхуй
local fa                = require("fAwesome6")      -- прикольные иконки
local mimgui_stoolmenu  = require("mimgui_stoolmenu")
-- кодировочка
local encoding          = require('encoding')
encoding.default        = ('CP1251')
u8                      = encoding.UTF8
-- параметры для окон
local winSize           = {500, 300}

-- тут надо настроить че у тебя за пункты в меню
-- иконка, название вкладки, описание
local subMenus          = {
    {fa('HOUSE'),             u8"Главная",      u8"Ничего особенного"},
    {fa('PERSON_RUNNING'),    u8"Передвижение", u8"Функции влияющие на передвижение. Спидхак, полёт, +с, итд..."},
    {fa('TREASURE_CHEST'),    u8"Инвентарь",    u8"Работа с диалогами"},
    {fa('EYE'),               u8"Хуйня",        u8"Работа с хуйнёй"}
}

-- объявляем меню и добавляем в него пункты
myMenu = mimgui_stoolmenu.MenuManager:new()
myMenu:addMenuList(subMenus)

imgui.OnInitialize(function()
    imgui.GetIO().IniFilename = nil -- не даём имгуи запоминать и вспоминать параметры окна
    local config = imgui.ImFontConfig() -- готовимся к работе со шрифтами
    config.MergeMode = true
    config.PixelSnapH = true
    iconRanges = imgui.new.ImWchar[3](fa.min_range, fa.max_range, 0) -- настройка fAwesome6
    imgui.GetIO().Fonts:AddFontFromMemoryCompressedBase85TTF(fa.get_font_data_base85('solid'), 14, config, iconRanges) -- solid - тип иконок, так же
end)

myMenu.idxSelection = 1 -- принудительно делаем активной первую вкладку

local frm = imgui.OnFrame(
    function() return myMenu.isOpen end,
    function (_f)
        local scr_x, scr_y = getScreenResolution() -- размер экрана
        -- ставим размер окна в соответствии с конфигурацией
        imgui.SetNextWindowSize(imgui.ImVec2(winSize[1], winSize[2]), imgui.Cond.FirstUseEver)
        -- ставим окно по середине экрана
        imgui.SetNextWindowPos(imgui.ImVec2((scr_x/2) - winSize[1]/2, (scr_y/2) - winSize[2]/2), imgui.Cond.FirstUseEver)
        -- вызываем красивое меню
        imgui.BeginStoolMenu(
            fa('CHAIR') .. u8"  Stool Menu Example", -- иконка + название окна
            nil, -- похуй
            imgui.WindowFlags.NoCollapse + imgui.WindowFlags.NoTitleBar, -- флаги окна
            myMenu, -- переменная с менюшками
            40, -- ширина хуйни с кнопками слева
            50  -- интенсивность размытия
        )
            if myMenu.idxSelection == 0 then -- тут проверяем какая вкладка активна в данный момент и отображаем её контент
                imgui.Text(u8"Ничего не открыто")
            elseif myMenu.idxSelection == 1 then
                imgui.TextWrapped(u8"Добро пожаловать!")
                imgui.TextWrapped(u8"Нажмите что-нибудь прикольное, чтобы что-нибудь прикольное произошло")
                imgui.Button(u8"Кнопочка")
            elseif myMenu.idxSelection == 2 then
                imgui.Text(u8"Тут что-то еще")
                imgui.Checkbox(u8"Галочка", imgui.new.bool(false))
            elseif myMenu.idxSelection == 3 then
                imgui.TextWrapped(u8"Ого, третья вкладка и всё по порядку!")
            elseif myMenu.idxSelection == 4 then
                imgui.Text(u8"Ну тут наверное и всё")
            end
        imgui.EndStoolMenu()
    end
)

-- главная рутина
function main()
    -- ждём, пока самп не оклимается
    repeat wait(0) until isSampAvailable()
    -- регистрируем команду, чтобы открывать окошко
    sampRegisterChatCommand("stool_f", function () myMenu.isOpen = not myMenu.isOpen end)
end
Lua:
-- NE BUD PIDOROM
-- USE CP1251 KODIROVKU

script_author('stool')
script_name('stool menu example')
script_version('v0.1')
-- важная хуйня, импортируем то с чем будем работать
local imgui             = require("mimgui")          -- имхуй
local fa                = require("fAwesome6")      -- прикольные иконки
local mimgui_stoolmenu  = require("mimgui_stoolmenu")
-- кодировочка
local encoding          = require('encoding')
encoding.default        = ('CP1251')
u8                      = encoding.UTF8

-- параметры для окон
local winSize           = {500, 300}

-- тут надо настроить че у тебя за пункты в меню
-- иконка, название вкладки, описание
local subMenus          = {
    {fa('HOUSE'),             u8"Главная",      u8"Ничего особенного"},
    {fa('PERSON_RUNNING'),    u8"Передвижение", u8"Функции влияющие на передвижение. Спидхак, полёт, +с, итд..."},
    {fa('TREASURE_CHEST'),    u8"Инвентарь",    u8"Работа с диалогами"},
    {fa('EYE'),               u8"Хуйня",        u8"Работа с хуйнёй"}
}

-- объявляем меню и добавляем в него пункты
myMenu = mimgui_stoolmenu.MenuManager:new()
myMenu:addMenuList(subMenus)

imgui.OnInitialize(function()
    imgui.GetIO().IniFilename = nil -- не даём имгуи запоминать и вспоминать параметры окна
    local config = imgui.ImFontConfig() -- готовимся к работе со шрифтами
    config.MergeMode = true
    config.PixelSnapH = true
    iconRanges = imgui.new.ImWchar[3](fa.min_range, fa.max_range, 0) -- настройка fAwesome6
    font = {} -- мини-реестр шрифтиов
    local size = {16, 23} -- забиваем размеры шрифтов которые будем использовать
    local range = imgui.ImVector_ImWchar() -- подготовка к работе с wide-characters
    local builder = imgui.ImFontGlyphRangesBuilder() -- готовим шрифт
    builder:AddRanges(imgui.GetIO().Fonts:GetGlyphRangesCyrillic())
    builder:AddText("‚„…†‡€‰‹‘’“”•–-™›№")
    builder:BuildRanges(range)
    for k, v in pairs(size) do -- добавляем шрифты
        -- подгружаем кастомный шрифт и fAwesome6
        font[k] = imgui.GetIO().Fonts:AddFontFromMemoryCompressedBase85TTF(customFont_compressed_data_base85, v, nil, range[0].Data)
        imgui.GetIO().Fonts:AddFontFromMemoryCompressedBase85TTF(fa.get_font_data_base85('solid'), 14, config, iconRanges) -- solid - тип иконок, так же есть thin, regular, light и duotone
    end
    theme() -- настройка темы imgui
end)

myMenu.idxSelection = 1 -- принудительно делаем активной первую вкладку

local frm = imgui.OnFrame(
    function() return myMenu.isOpen end,
    function (_f)
        local scr_x, scr_y = getScreenResolution() -- размер экрана
        imgui.PushFont(font[1]) -- установка фона
        -- ставим размер окна в соответствии с конфигурацией
        imgui.SetNextWindowSize(imgui.ImVec2(winSize[1], winSize[2]), imgui.Cond.FirstUseEver)
        -- ставим окно по середине экрана
        imgui.SetNextWindowPos(imgui.ImVec2((scr_x/2) - winSize[1]/2, (scr_y/2) - winSize[2]/2), imgui.Cond.FirstUseEver)
        -- вызываем красивое меню
        imgui.BeginStoolMenu(
            fa('CHAIR') .. u8"  Stool Menu Example", -- иконка + название окна
            nil, -- похуй
                -- imgui.WindowFlags.NoResize -- флаги окна
                imgui.WindowFlags.NoCollapse
                + imgui.WindowFlags.NoTitleBar,
            myMenu, -- переменная с менюшками
            40, -- ширина хуйни с кнопками слева
            50  -- интенсивность размытия
        )
            if myMenu.idxSelection == 0 then -- тут проверяем какая вкладка активна в данный момент и отображаем её контент
                imgui.Text(u8"Ничего не открыто")
            elseif myMenu.idxSelection == 1 then
                imgui.TextWrapped(u8"Добро пожаловать!")
                imgui.TextWrapped(u8"Нажмите что-нибудь прикольное, чтобы что-нибудь прикольное произошло")
                imgui.Button(u8"Кнопочка")
            elseif myMenu.idxSelection == 2 then
                imgui.Text(u8"Тут что-то еще")
                imgui.Checkbox(u8"Галочка", imgui.new.bool(false))
            elseif myMenu.idxSelection == 3 then
                imgui.TextWrapped(u8"Ого, третья вкладка и всё по порядку!")
            elseif myMenu.idxSelection == 4 then
                imgui.Text(u8"Ну тут наверное и всё")
            end
        imgui.EndStoolMenu()
    end
)

-- главная рутина
function main()
    -- ждём, пока самп не оклимается
    repeat wait(0) until isSampAvailable()
    -- регистрируем команду, чтобы открывать окошко
    sampRegisterChatCommand("stool", function () myMenu.isOpen = not myMenu.isOpen end)
end

-- кастомная тема
function theme()
    imgui.SwitchContext()
    local a = imgui.GetStyle()
    local b = a.Colors
    a.Alpha = 1
    a.WindowPadding = imgui.ImVec2(8.00, 8.00)
    a.WindowRounding = 1
    a.WindowBorderSize = 1
    a.WindowMinSize = imgui.ImVec2(32.00, 32.00)
    a.WindowTitleAlign = imgui.ImVec2(0.00, 0.50)
    a.ChildRounding = 0
    a.ChildBorderSize = 1
    a.PopupRounding = 0
    a.PopupBorderSize = 1
    a.FramePadding = imgui.ImVec2(4.00, 3.00)
    a.FrameRounding = 1
    a.FrameBorderSize = 0
    a.ItemSpacing = imgui.ImVec2(8.00, 4.00)
    a.ItemInnerSpacing = imgui.ImVec2(4.00, 4.00)
    a.IndentSpacing = 21
    a.ScrollbarSize = 14
    a.ScrollbarRounding = 9
    a.GrabMinSize = 10
    a.GrabRounding = 1
    a.TabRounding = 1
    a.ButtonTextAlign = imgui.ImVec2(0.50, 0.50)
    a.SelectableTextAlign = imgui.ImVec2(0.00, 0.00)
    b[imgui.Col.Text]                   = imgui.ImVec4(1.00, 1.00, 1.00, 1.00)
    b[imgui.Col.TextDisabled]           = imgui.ImVec4(0.50, 0.50, 0.50, 1.00)
    b[imgui.Col.WindowBg]               = imgui.ImVec4(0.06, 0.06, 0.06, 0.7)
    b[imgui.Col.ChildBg]                = imgui.ImVec4(0.00, 0.00, 0.00, 0.00)
    b[imgui.Col.PopupBg]                = imgui.ImVec4(0.08, 0.08, 0.08, 0.94)
    b[imgui.Col.Border]                 = imgui.ImVec4(0.43, 0.43, 0.50, 0.50)
    b[imgui.Col.BorderShadow]           = imgui.ImVec4(0.00, 0.00, 0.00, 0.00)
    b[imgui.Col.FrameBg]                = imgui.ImVec4(0.48, 0.48, 0.48, 0.54)
    b[imgui.Col.FrameBgHovered]         = imgui.ImVec4(0.98, 0.98, 0.98, 0.40)
    b[imgui.Col.FrameBgActive]          = imgui.ImVec4(0.98, 0.98, 0.98, 0.67)
    b[imgui.Col.TitleBg]                = imgui.ImVec4(0.04, 0.04, 0.04, 1.00)
    b[imgui.Col.TitleBgActive]          = imgui.ImVec4(0.48, 0.48, 0.48, 1.00)
    b[imgui.Col.TitleBgCollapsed]       = imgui.ImVec4(0.00, 0.00, 0.00, 0.51)
    b[imgui.Col.MenuBarBg]              = imgui.ImVec4(0.14, 0.14, 0.14, 1.00)
    b[imgui.Col.ScrollbarBg]            = imgui.ImVec4(0.02, 0.02, 0.02, 0.53)
    b[imgui.Col.ScrollbarGrab]          = imgui.ImVec4(0.31, 0.31, 0.31, 1.00)
    b[imgui.Col.ScrollbarGrabHovered]   = imgui.ImVec4(0.41, 0.41, 0.41, 1.00)
    b[imgui.Col.ScrollbarGrabActive]    = imgui.ImVec4(0.51, 0.51, 0.51, 1.00)
    b[imgui.Col.CheckMark]              = imgui.ImVec4(0.98, 0.98, 0.98, 1.00)
    b[imgui.Col.SliderGrab]             = imgui.ImVec4(0.88, 0.88, 0.88, 1.00)
    b[imgui.Col.SliderGrabActive]       = imgui.ImVec4(0.98, 0.98, 0.98, 1.00)
    b[imgui.Col.Button]                 = imgui.ImVec4(0.98, 0.98, 0.98, 0.40)
    b[imgui.Col.ButtonHovered]          = imgui.ImVec4(0.59, 0.59, 0.59, 1.00)
    b[imgui.Col.ButtonActive]           = imgui.ImVec4(0.39, 0.39, 0.39, 1.00)
    b[imgui.Col.Header]                 = imgui.ImVec4(0.98, 0.98, 0.98, 0.31)
    b[imgui.Col.HeaderHovered]          = imgui.ImVec4(0.98, 0.98, 0.98, 0.80)
    b[imgui.Col.HeaderActive]           = imgui.ImVec4(0.98, 0.98, 0.98, 1.00)
    b[imgui.Col.Separator]              = imgui.ImVec4(0.50, 0.50, 0.50, 0.50)
    b[imgui.Col.SeparatorHovered]       = imgui.ImVec4(0.75, 0.75, 0.75, 0.78)
    b[imgui.Col.SeparatorActive]        = imgui.ImVec4(0.75, 0.75, 0.75, 1.00)
    b[imgui.Col.ResizeGrip]             = imgui.ImVec4(0.98, 0.98, 0.98, 0.25)
    b[imgui.Col.ResizeGripHovered]      = imgui.ImVec4(0.98, 0.98, 0.98, 0.67)
    b[imgui.Col.ResizeGripActive]       = imgui.ImVec4(0.98, 0.98, 0.98, 0.95)
    b[imgui.Col.Tab]                    = imgui.ImVec4(0.58, 0.58, 0.58, 0.86)
    b[imgui.Col.TabHovered]             = imgui.ImVec4(0.98, 0.98, 0.98, 0.80)
    b[imgui.Col.TabActive]              = imgui.ImVec4(0.68, 0.68, 0.68, 1.00)
    b[imgui.Col.TabUnfocused]           = imgui.ImVec4(0.15, 0.15, 0.15, 0.97)
    b[imgui.Col.TabUnfocusedActive]     = imgui.ImVec4(0.42, 0.42, 0.42, 1.00)
    b[imgui.Col.PlotLines]              = imgui.ImVec4(0.61, 0.61, 0.61, 1.00)
    b[imgui.Col.PlotLinesHovered]       = imgui.ImVec4(1.00, 0.43, 0.35, 1.00)
    b[imgui.Col.PlotHistogram]          = imgui.ImVec4(0.90, 0.70, 0.00, 1.00)
    b[imgui.Col.PlotHistogramHovered]   = imgui.ImVec4(1.00, 0.60, 0.00, 1.00)
    b[imgui.Col.TextSelectedBg]         = imgui.ImVec4(0.98, 0.98, 0.98, 0.35)
    b[imgui.Col.DragDropTarget]         = imgui.ImVec4(1.00, 1.00, 0.00, 0.90)
    b[imgui.Col.NavHighlight]           = imgui.ImVec4(0.98, 0.98, 0.98, 1.00)
    b[imgui.Col.NavWindowingHighlight]  = imgui.ImVec4(1.00, 1.00, 1.00, 0.70)
    b[imgui.Col.NavWindowingDimBg]      = imgui.ImVec4(0.80, 0.80, 0.80, 0.20)
    b[imgui.Col.ModalWindowDimBg]       = imgui.ImVec4(0.80, 0.80, 0.80, 0.35)
end

-- контейнер для шрифта, чтобы не хранить его отдельно на диске
customFont_compressed_data_base85 = ... -- положи содержимое сам

В планах разработки:
- Добавить плавные анимации
- Добавить уведомления
- Доработать кнопку закрытия
- Добавить больше кастомизации
- ...

Повторюсь: буду крайне рад ответить на вопросы, получить фидбек или внедрить ваши исправления.
Спасибо за внимание 🪑
 

Вложения

  • mimgui_stoolmenu.zip
    2.7 KB · Просмотры: 55
Последнее редактирование:

stool

Известный
Автор темы
417
337
С чекмарком из примера проблема, не прожимается он
потому что он и не должен был прожиматься, тебе надо в переменную вынести состояние чекбокса

Lua:
local checkboxExample = imgui.new.bool(false)

... тут ты типа чет нашкодил
    imgui.CheckBox(u8"Пример галки", checkboxExample)
... тут ты типа чет нашкодил
вроде так
 
  • Нравится
Реакции: fakelag46

fakelag46

Участник
25
21
потому что он и не должен был прожиматься, тебе надо в переменную вынести состояние чекбокса

Lua:
local checkboxExample = imgui.new.bool(false)

... тут ты типа чет нашкодил
    imgui.CheckBox(u8"Пример галки", checkboxExample)
... тут ты типа чет нашкодил
вроде так
понял, вопросов нет, просто взял сразу пример даже не смотря код)
 
  • Ха-ха
Реакции: stool

prikolov

Участник
63
16
чтооо скрипт стула да нууу чтоооо я не верю своим глазам ого однозначно лайк