Осваиваем Dear ImGUI ч.1 | begin_main_menu_bar(), begin_menu(), menu_item()

Dear ImGui — это библиотека графического интерфейса немедленного режима (Immediate Mode GUI) для создания UI в играх или в большинстве случаев для создания инструментов разработки видеоигр.

Ссылка на GitHub: GitHub - ocornut/imgui: Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
Расширение Dear ImGui для Defold: GitHub - britzl/extension-imgui: Dear ImGUI extension for Defold

Что за немедленный режим?

UI рисуется каждый кадр через последовательность вызовов функций (например, для кнопок, текстов, окон и т.д). Затем библиотека обрабатывает отрисовку, генерируя необходимые данные для рендера в текущем кадре. Если коротко, мы описываем UI с помощью функций, а затем UI обновляется, т.е рисуется каждый кадр на нашем экране.

Что такое меню бар в UI?

Меню бар (панель меню) — это графический элемент пользовательского интерфейса, который организует и предоставляет доступ к различным функциям, командам или параметрам навигации в приложениях или на веб-сайтах. Обычно он(а) располагается в верхней части страницы, под заголовком.
Кстати. в игровом движке Defold есть меню бар и он расположен сверху:

Подключаем Dear ImGUI в ваш проект:




Подтяните зависимость:

Должен появиться каталог:

Добавляем скрипт imgui в игровой объект:

Может быть полезно:
Как добавить библиотечную зависимость в проект на Defold
Изучаем Dear ImGUI в Defold. Часть 1. Создание окна и таблицы с полями


Сегодня мы разберём функцию imgui.begin_main_menu_bar() и затронем ряд нескольких смежных функций: imgui.begin_menu(), imgui.menu_item().

Давайте рассмотрим все функции по порядку:

imgui.begin_main_menu_bar()

imgui.begin_main_menu_bar() — это функция необходима для создания главного меню-бар (верхней панели меню) во всём экране. Обычно вызывается без параметров и возвращает один булев флаг — удалось ли начать рендеринг меню-бара в этом кадре.

imgui.begin_main_menu_bar() — это “обёртка-начало” глобальной верхней панели меню; дальше внутри неё обычно идут begin_menu() / menu_item() и завершается всё end_main_menu_bar().

function init(self) end

local function main_menu_bar(self)
	if imgui.begin_main_menu_bar() then  -- Начинаем главную панель
		imgui.end_main_menu_bar()  -- Обязательно закрываем!
	end
end

function update(self)
	main_menu_bar(self)
end

Результат:
image

imgui.begin_menu()

imgui.begin_menu() — функция создаёт выпадающее подменю внутри меню-бара или главного меню.

В редакторе Defold одно из множества подменю (“File”) выглядит так:

function init(self) end

local function main_menu_bar(self)
	if imgui.begin_main_menu_bar() then 
		if imgui.begin_menu("File") then -- Создаём подменю без элементов меню
			imgui.end_menu() -- Обязательно закрываем!
		end
		imgui.end_main_menu_bar()  
	end
end

function update(self)
	main_menu_bar(self)
end

image

imgui.menu_item()

Параметры функции

imgui.menu_item(label, [shortcut], [selected], [enabled])

Параметр Описание Пример
label Текст пункта (обязательный) "New..."
shortcut Горячая клавиша (визуально) "Ctrl+N"
selected ✓ Галочка (true/false) self.is_open
enabled Активен/выключен false

Добавление горячей клавиши:

if imgui.menu_item("< Undo", "Ctrl+Z") then end

Можем сделать переключатель с галочкой:

local changed, active = imgui.menu_item("Show Grid", nil, self.show_grid)
if changed then
	self.show_grid = active  -- ImGui сам переключает
end

Мы можем сделать пункт меню неактивным:

if imgui.menu_item("Premium Feature", nil, nil, false) then end

Пример:


Домашняя работа:

  1. Попробуйте воссоздать подменю редактора Defold с помощью Dear ImGui.
  2. Почему мы используем функции DearImGui в update()?
Должно получиться примерно так:






Моё решение:

local function main_menu_bar(self)
	if imgui.begin_main_menu_bar() then  -- Начинаем главную панель
		if imgui.begin_menu("File") then
			if imgui.menu_item("New...") then
				sys.exit()
			end
			if imgui.menu_item("Open...", "Ctrl+N") then end
			if imgui.menu_item("Save All", "Ctrl+P") then end
			if imgui.menu_item("Upgrade File Formats...", "Ctrl+S") then end
			if imgui.menu_item("Search in Files...", "Ctrl+Shift+F") then end
			if imgui.begin_menu("Recent Files") then
				if imgui.menu_item("Re-Open Closed Files", "Ctrl+Shift+T") then end
				if imgui.menu_item("/imgui/api/imgui.lua | Code") then end
				if imgui.menu_item("/game.project | Form") then end
				if imgui.menu_item("/main/main.collection | Scene") then end
				if imgui.menu_item("/README.md | HTML") then end
				if imgui.menu_item("More...", "Alt+R") then end
				imgui.end_menu()
			 end
			if imgui.menu_item("Close", "Ctrl+W") then end
			if imgui.menu_item("Close All", "Ctrl+Shift+W") then end
			if imgui.menu_item("Close others") then end
			if imgui.menu_item("Referencing files...") then end
			if imgui.menu_item("Dependencies...") then end
			if imgui.menu_item("Show Overrides...") then end
			if imgui.menu_item("Pull Up Overrides", nil, nil, false) then end
			if imgui.menu_item("Push Down Overrides", nil, nil, false) then end
			if imgui.menu_item("Hot Reload", "Ctrl+R", nil, false) then end
			if imgui.menu_item("Open Project...") then end
			if imgui.menu_item("Preferences...", "Ctrl+,") then end
			if imgui.menu_item("Quit", "Ctrl+Q") then end
			imgui.end_menu()
		end

		if imgui.begin_menu("Edit") then
			if imgui.menu_item("< Undo", "Ctrl+Z", nil, false) then end
			if imgui.menu_item("> Redo", "Ctrl+Shift+Z", nil, false) then end

			if imgui.menu_item("Cut", "Ctrl+X", nil, false) then end
			if imgui.menu_item("Copy", "Ctrl+C", nil, false) then end
			if imgui.menu_item("Paste", "Ctrl+V", nil, false) then end
			if imgui.menu_item("Select All", "Ctrl+A") then end
			if imgui.menu_item("Delete", "Delete", nil, false) then end

			if imgui.menu_item("Find", "Ctrl+F") then end
			if imgui.menu_item("Find Next", "Ctrl+G") then end
			if imgui.menu_item("Find Previous", "Ctrl+Shift+G") then end

			if imgui.menu_item("Replace...", "Ctrl+H") then end
			if imgui.menu_item("Replace Next", "Ctrl+Shift+H") then end

			if imgui.menu_item("Toggle Comment", "Ctrl+/") then end
			if imgui.menu_item("Reindent Lines", "Ctrl+I", nil, false) then end
			if imgui.begin_menu("Convert Indentation") then
				if imgui.menu_item("To tabs") then end
				if imgui.menu_item("To Two Spaces") then end
				if imgui.menu_item("To Four Spaces") then end
				imgui.end_menu()
			end

			if imgui.menu_item("Sort Lines", nil, nil, false) then end
			if imgui.menu_item("Sort Lines (Case Sensitive", nil, nil, false) then end

			if imgui.menu_item("Select Next Occurrence", "Ctrl+D") then end
			if imgui.menu_item("Split Selection Into Line", "Ctrl+Shift+L") then end

			if imgui.menu_item("Rename", "F2") then end
			if imgui.menu_item("Go to Definition", "F12") then end
			if imgui.menu_item("Find References", "Shift+F12") then end

			if imgui.menu_item("Toggle Breakpoint", "F9") then end
			if imgui.menu_item("Edit Breakpoint", "Alt+F9") then end
			

			imgui.end_menu()
		end

		if imgui.begin_menu("View") then
			if imgui.menu_item("Toggle Assets Pane", "F6") then end
			if imgui.menu_item("Toggle Changed Files") then end
			if imgui.menu_item("Toggle Tools Pane", "F7") then end
			if imgui.menu_item("Toggle Properties Pane", "F8") then end

			if imgui.menu_item("Show Console") then end
			if imgui.menu_item("Show Curve Editor") then end
			if imgui.menu_item("Show Build Errors") then end
			if imgui.menu_item("Show Search Results") then end

			if imgui.menu_item("Minimap", nil, true, nil) then end
			if imgui.menu_item("Indentation Guides", nil, true, nil) then end
			if imgui.menu_item("Visible Whitespace", nil, true, nil) then end

			if imgui.menu_item("Increase Font Size", "Ctrl++") then end
			if imgui.menu_item("Decrease Font Size", "Ctrl+-") then end
			if imgui.menu_item("Reset Font Size", "Ctrl+0") then end

			if imgui.menu_item("Go to Line...", "Ctrl+L") then end
			imgui.end_menu()
		end

		if imgui.begin_menu("Project") then
			if imgui.menu_item("Build", "Ctrl+B") then end
			if imgui.menu_item("Clean Build", "Ctrl+Shift+B") then end
			if imgui.menu_item("Build HTML5", "Ctrl+M") then end
			if imgui.menu_item("Clean Build HTNML5", "Ctrl+Shift+M") then end
			if imgui.begin_menu("Bundle") then 
				imgui.menu_item("Android Application...", nil)
				imgui.menu_item("macOS Application...", nil)
				imgui.menu_item("Windows Application...", nil)
				imgui.menu_item("Linux Application...", nil)
				imgui.menu_item("HTNML5 Application...", nil)
				imgui.end_menu()
			end
			if imgui.menu_item("Rebundle", "Ctrl+U", nil, false) then end
			if imgui.menu_item("Fetch Libraries") then end
			if imgui.menu_item("Reload Editor Scripts", "Ctrl+Shift+R") then end

			if imgui.menu_item("Shared Editor Settings") then end
			if imgui.menu_item("Live Update Settings") then end

			if imgui.begin_menu("Target") then
				imgui.menu_item("New Local Engine", nil, nil, true)
				imgui.end_menu()
			end
			if imgui.menu_item("Close Engine", "Ctrl+J", nil, false) then end
			if imgui.begin_menu("Launched Instance Count") then 
				imgui.menu_item("1 Instance", nil, true)
				imgui.menu_item("2 Instance", nil, true)
				imgui.menu_item("3 Instance", nil, true)
				imgui.menu_item("4 Instance", nil, true)
				imgui.end_menu()
			end
			if imgui.menu_item("Enter Target IP") then end
			if imgui.menu_item("Target Discovery Log") then end
			
			imgui.end_menu()
		end

		if imgui.begin_menu("Debug") then
			if imgui.menu_item("Start/Attach", "F5") then end
			if imgui.menu_item("Break", nil, nil, false) then end
			if imgui.menu_item("Step Over", "F10", nil, false) then end
			if imgui.menu_item("Step Into", "F11", nil, false) then end
			if imgui.menu_item("Step Out", "Shift+F11", nil, false) then end

			if imgui.menu_item("Detach Debugger", nil, nil, false) then end
			if imgui.menu_item("Stop Debugger", "Shift+F5", nil, false) then end

			if imgui.menu_item("Open Web Profiler") then end
			if imgui.menu_item("Open Resource Profiler") then end

			if imgui.menu_item("Reset Simulated Resolution") then end
			if imgui.begin_menu("Set Resolutions") then
				if imgui.begin_menu("Custom Resolution...") then 
					if imgui.begin_menu("Apple") then
						if imgui.begin_menu("iPhone") then
							if imgui.menu_item("Viewport Resolutions", nil, nil, false) then end
							if imgui.menu_item("iPhone 4 (320x480)") then end
							if imgui.menu_item("iPhone 5/SE (320x568)") then end
							if imgui.menu_item("iPhone 6/7/8 (375x667)") then end
							if imgui.menu_item("iPhone 6/7/8 Plus (414x736)") then end
							if imgui.menu_item("iPhone X/XS (375x812)") then end
							if imgui.menu_item("iPhone XR/XS Max(414x896)") then end
							if imgui.menu_item("iPhone 12 Pro Max/13 Pro Max/14 Plus (428x926)") then end
							if imgui.menu_item("iPhone 14 Pro Max/15 Plus/15 Pro Max (430x932)") then end

							if imgui.menu_item("Native Resolutions", nil, nil, false) then end
							if imgui.menu_item("iPhone 4 (640x960)") then end
							if imgui.menu_item("iPhone 5/SE (640x1136)") then end
							if imgui.menu_item("iPhone 6/7/8 (750x1334)") then end
							if imgui.menu_item("iPhone 6/7/8 Plus (1242x2208)") then end
							if imgui.menu_item("iPhone X/XS/11 Pro (1125x2436)") then end
							if imgui.menu_item("iPhone XS Max/11 Pro Max (1242x2688)") then end
							if imgui.menu_item("iPhone XR (828x1792)") then end
							if imgui.menu_item("iPhone 12 Pro Max/13 Pro Max/14 Plus (1284x2778)") then end
							if imgui.menu_item("iPhone 14 Pro Max/15 Plus/15 Pro Max (1290x2796)") then end
							
							imgui.end_menu()
						end
						if imgui.begin_menu("iPad") then
							if imgui.menu_item("Viewport Resolutions", nil, nil, false) then end
							if imgui.menu_item("iPad Pro (1024x1366)") then end
							if imgui.menu_item("iPad (768x1024") then end

							if imgui.menu_item("Native Resolutions", nil, nil, false) then end
							if imgui.menu_item("iPad Pro (2048x2732)") then end
							if imgui.menu_item("iPad (1536x2048)") then end
							if imgui.menu_item("iPad Mini (768x1024)") then end
							
							imgui.end_menu()
						end
						imgui.end_menu()
					end
					if imgui.begin_menu("Android_Devices") then
						if imgui.menu_item("Viewport Resolutions", nil, nil, false) then end
						if imgui.menu_item("Samsung Galaxy S7 (360x640") then end
						if imgui.menu_item("Samsung Galaxy S8/S9/Note 9 (360x740)") then end
						if imgui.menu_item("Google Pixel 1/2/XL (412x732)") then end
						if imgui.menu_item("Google Pixel 3 (412x824)") then end
						if imgui.menu_item("Google Pixel 3 XL (412x847)") then end

						if imgui.menu_item("Native Resolutions", nil, nil, false) then end
						if imgui.menu_item("Samsung Galaxy S7 (1440x2560") then end
						if imgui.menu_item("Samsung Galaxy S8/S9/Note 9 (1440x2960)") then end
						if imgui.menu_item("Google Pixel 1/2/XL (1080x1920)") then end
						if imgui.menu_item("Google Pixel 2/XL (1440x2560)") then end
						if imgui.menu_item("Google Pixel 3 (1080x2160)") then end
						if imgui.menu_item("Google Pixel 3 XL (1440x2960)") then end
						
						imgui.end_menu()
					end
					if imgui.begin_menu("Handheld") then
						if imgui.menu_item("Native Resolutions", nil, nil, false) then end
						if imgui.menu_item("1080p (1920x1080)") then end
						if imgui.menu_item("720p (1280x720)") then end
						if imgui.menu_item("Steam Deck (1280x800)") then end
						imgui.end_menu()
					end
					imgui.end_menu()
				end
				imgui.end_menu()
			end
			if imgui.menu_item("Rotated Device") then end
			
			imgui.end_menu()
		end

		if imgui.begin_menu("Help") then
			if imgui.menu_item("Reload Stylesheet") then end
			if imgui.menu_item("Show Logs") then end
			if imgui.menu_item("Documentation", "F1") then end
			if imgui.menu_item("Support Forum") then end
			if imgui.menu_item("Find Assets") then end
			if imgui.menu_item("Report Issue") then end
			if imgui.menu_item("Report Suggestion") then end
			if imgui.menu_item("Search Issues") then end
			if imgui.menu_item("Development Fund") then end
			if imgui.menu_item("About") then end
			
			imgui.end_menu()
		end
		
		imgui.end_main_menu_bar()  -- Обязательно закрываем!
	end
end

function init(self) end

function update(self)
	main_menu_bar(self)
end

Всем спасибо за внимание!
Если есть какие-то вопросы, задавайте!