- 417
- 337
Я думаю почти каждый видел сниппет от @chapo, который позволял делать прикольные вертикальные вкладки, но в нём был ряд недочётов один из которых неверный порядок расположения кнопок. Для некоторых сниппет был сложен в понимании, поэтому я решил "создать" библиотеку, который реимплементирует способ реализации такой менюхи и делает управление этой менюхой намного проще. И так встречайте: 🪑 StoolMenu . Это самая первая версия, так что я буду рад вашим идеям, замечаниям и исправлениям.
В аналогии с решением от @chapo, менюшка красиво вываливает кнопки и даёт возможность посмотреть на тултип (подсказку), наведя курсор на кнопку. Помимо этого я пересмотрел решение с точки зрения дизайна и вынес тайтл-бар из окна, поступив так же и с кнопкой закрытия меню. StoolMenu не должен конфликтовать с кастомными темами, хорошо работает со шрифтами и в целом функционирует так, как и ожидается.
ТРЕБОВАНИЯ К УСТАНОВКЕ
- MoonLoader
- mimgui
- mimgui_blur
- Мозги
- Руки (желательно прямые)
ПОРЯДОК УСТАНОВКИ
1. Скачать mimgui_stoolmenu.zip
2. Распаковать содержимое архива в директорию %gta%/moonloader/lib
3. Всё)
- 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 = ... -- положи содержимое сам
В планах разработки:
- Добавить плавные анимации
- Добавить уведомления
- Доработать кнопку закрытия
- Добавить больше кастомизации
- ...
Повторюсь: буду крайне рад ответить на вопросы, получить фидбек или внедрить ваши исправления.
Спасибо за внимание 🪑
Вложения
Последнее редактирование: