Difference between revisions of "Dear ImGui Examples"
From GiderosMobile
(Created page with "__TOC__ '''note''': you may have to provide your own assets (fonts, gfx, …). === ImGui Custom Font === <syntaxhighlight lang="lua"> require "ImGui" -- imgui self.imgui =...") |
|||
(6 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
__TOC__ | __TOC__ | ||
+ | '''The below are fully functional working demo you can copy/paste'''. | ||
− | '''note''': you may have to provide your own assets (fonts, gfx, …) | + | '''note''': you may have to provide your own assets (fonts, gfx, …) |
− | == | + | == Dear ImGui Custom Font == |
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
require "ImGui" | require "ImGui" | ||
− | + | local imgui = ImGui.new() | |
− | + | stage:addChild(imgui) | |
− | + | ||
− | local | + | local IO = imgui:getIO() |
− | local fontatlas = | + | local fontatlas = IO:getFonts() |
− | local myfont = fontatlas:addFont("fonts/Cabin-Regular-TTF.ttf", | + | local myfont = fontatlas:addFont("fonts/Cabin-Regular-TTF.ttf", 16) -- your custom font here |
− | + | IO:setFontDefault(myfont) | |
fontatlas:build() | fontatlas:build() | ||
+ | |||
-- imgui style | -- imgui style | ||
− | local | + | local style = imgui:getStyle() |
− | + | style:setWindowMinSize(64*4, 64*1.5) | |
+ | |||
+ | function onEnterFrame(e) | ||
+ | -- imgui start | ||
+ | imgui:newFrame(e.deltaTime) | ||
+ | |||
+ | -- window | ||
+ | if imgui:beginWindow("Window") then | ||
+ | -- widgets | ||
+ | imgui:text("This is using 'myfont' to write text.") | ||
+ | end | ||
+ | imgui:endWindow() | ||
+ | |||
+ | -- imgui end | ||
+ | imgui:render() | ||
+ | imgui:endFrame() | ||
+ | end | ||
+ | |||
+ | stage:addEventListener("enterFrame", onEnterFrame) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | === | + | == Start a Window at a given position == |
− | |||
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
require "ImGui" | require "ImGui" | ||
+ | |||
+ | application:setBackgroundColor(0x323232) | ||
local imgui = ImGui.new() | local imgui = ImGui.new() | ||
stage:addChild(imgui) | stage:addChild(imgui) | ||
+ | |||
+ | local window01 = true -- this window can be closed | ||
+ | local windowdrawn = false -- boolean to hold our windows status | ||
+ | |||
+ | local posx, posy = 32*8, 32*4 -- starting window position | ||
+ | local offsetx, offsety = 0, 0 -- new window position | ||
+ | local dragflag = false | ||
+ | |||
+ | function onEnterFrame(e) | ||
+ | -- 1. we start ImGui | ||
+ | imgui:newFrame(e.deltaTime) | ||
+ | |||
+ | -- 2. we add some windows | ||
+ | if window01 then -- if window exists (not closed) | ||
+ | imgui:setNextWindowPos(posx, posy) | ||
+ | window01, windowdrawn = imgui:beginWindow("window01", window01) | ||
+ | -- update window new position | ||
+ | local mouseClicked = imgui:isMouseClicked(KeyCode.MOUSE_LEFT) | ||
+ | if not dragflag and mouseClicked and imgui:isWindowHovered() then | ||
+ | dragflag = true | ||
+ | end | ||
+ | if imgui:isWindowFocused() and dragflag then | ||
+ | local mx,my = imgui:getMousePos() | ||
+ | local wx, wy = imgui:getWindowPos() | ||
+ | if mouseClicked or imgui:isMouseReleased(KeyCode.MOUSE_LEFT) then | ||
+ | offsetx, offsety = mx - wx, my - wy | ||
+ | end | ||
+ | if imgui:isMouseDragging(KeyCode.MOUSE_LEFT, 0) then | ||
+ | posx = (mx - offsetx) | ||
+ | posy = (my - offsety) | ||
+ | else | ||
+ | dragflag = false | ||
+ | end | ||
+ | end | ||
+ | -- window widgets | ||
+ | if windowdrawn then -- the variable is false when main window is collapsed | ||
+ | imgui:text("Hello Dear ImGui!") | ||
+ | imgui:newLine() | ||
+ | imgui:textColored("This window can be moved, resized and closed!", 0x00ff7f, 1) | ||
+ | imgui:text("its position is: "..posx..", "..posy) | ||
+ | end | ||
+ | imgui:endWindow() | ||
+ | end | ||
+ | |||
+ | -- 3. we end ImGui frame and render to screen | ||
+ | imgui:endFrame() | ||
+ | imgui:render() | ||
+ | end | ||
+ | |||
+ | stage:addEventListener(Event.ENTER_FRAME, onEnterFrame) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == Dear ImGui Fullscreen Window == | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | require "ImGui" | ||
+ | |||
+ | local imgui = ImGui.new() | ||
+ | stage:addChild(imgui) | ||
+ | |||
-- some imgui params | -- some imgui params | ||
local IO = imgui:getIO() | local IO = imgui:getIO() | ||
− | IO:setFontGlobalScale(2 | + | IO:setFontGlobalScale(2) |
--imgui:setLightStyle() | --imgui:setLightStyle() | ||
--imgui:setClassicStyle() | --imgui:setClassicStyle() | ||
+ | imgui:setDarkStyle() | ||
+ | |||
-- some vars | -- some vars | ||
− | local | + | local CW = application:getContentWidth() |
+ | local text, text2 = "Centered Text", "Colored Text" | ||
-- the loop | -- the loop | ||
function enterFrame(e) | function enterFrame(e) | ||
− | + | -- 1. we start ImGui | |
− | |||
− | -- 1 | ||
imgui:newFrame(e.deltaTime) | imgui:newFrame(e.deltaTime) | ||
− | -- 2 our GUI | + | -- 2. our GUI |
− | if | + | if imgui:beginFullScreenWindow("Hello ImGui") then |
-- some spacing | -- some spacing | ||
imgui:dummy(CW, 32 * 2) -- imgui:dummy(w, h) | imgui:dummy(CW, 32 * 2) -- imgui:dummy(w, h) | ||
-- a centered text | -- a centered text | ||
− | local textW = imgui:calcTextSize( | + | local textW = imgui:calcTextSize(text) |
local textMid = (CW - textW) / 2 | local textMid = (CW - textW) / 2 | ||
imgui:dummy(textMid, 0) | imgui:dummy(textMid, 0) | ||
imgui:sameLine(0, 0) -- puts the next element on the same line with no gap, for gaps use imgui:sameLine() | imgui:sameLine(0, 0) -- puts the next element on the same line with no gap, for gaps use imgui:sameLine() | ||
− | imgui:text( | + | imgui:text(text) |
-- some spacing | -- some spacing | ||
imgui:dummy(CW, 32 * 4) | imgui:dummy(CW, 32 * 4) | ||
-- a colored text | -- a colored text | ||
− | imgui:textColored( | + | imgui:textColored(text2, 0xff00ff) |
− | + | end | |
+ | imgui:endWindow() | ||
− | + | -- 3. we end ImGui frame and render to screen | |
− | |||
imgui:endFrame() | imgui:endFrame() | ||
imgui:render() | imgui:render() | ||
Line 68: | Line 149: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | == | + | == Dear ImGui Buttons == |
− | |||
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
require "ImGui" | require "ImGui" | ||
− | + | ||
local imgui = ImGui.new() | local imgui = ImGui.new() | ||
stage:addChild(imgui) | stage:addChild(imgui) | ||
+ | |||
-- some imgui params | -- some imgui params | ||
local IO = imgui:getIO() | local IO = imgui:getIO() | ||
− | IO:setFontGlobalScale(2 | + | IO:setFontGlobalScale(2) |
− | |||
imgui:setClassicStyle() | imgui:setClassicStyle() | ||
− | |||
− | |||
-- some vars | -- some vars | ||
− | local | + | local CW = application:getContentWidth() |
− | + | local text = "Centered Text" | |
− | + | local imageTex = Texture.new("gfx/100 (2).png") | |
− | local imageTex = Texture.new("gfx/ | ||
+ | -- loop | ||
function enterFrame(e) | function enterFrame(e) | ||
− | -- 1 | + | -- 1. we start ImGui |
imgui:newFrame(e.deltaTime) | imgui:newFrame(e.deltaTime) | ||
− | + | -- 2. our GUI | |
− | -- 2 our GUI | + | if imgui:beginFullScreenWindow("Hello ImGui") then |
− | if | ||
-- some spacing | -- some spacing | ||
− | imgui:dummy(CW, 32 * 2) | + | imgui:dummy(CW, 32*2) |
− | -- a centered text | + | -- a centered text |
− | local textW = imgui:calcTextSize( | + | local textW = imgui:calcTextSize(text) |
local textMid = (CW - textW) / 2 | local textMid = (CW - textW) / 2 | ||
imgui:dummy(textMid, 0) | imgui:dummy(textMid, 0) | ||
imgui:sameLine(0, 0) | imgui:sameLine(0, 0) | ||
− | imgui:text( | + | imgui:text(text) |
-- some spacing | -- some spacing | ||
− | imgui:dummy(CW, 32 * 2) | + | imgui:dummy(CW, 32*2) |
-- a button | -- a button | ||
imgui:text("A button") | imgui:text("A button") | ||
imgui:pushStyleVar(ImGui.StyleVar_ButtonTextAlign, 0.5, 0.5) | imgui:pushStyleVar(ImGui.StyleVar_ButtonTextAlign, 0.5, 0.5) | ||
− | imgui:button(("x"), 128, 64) -- text, w, h | + | imgui:button(("x"), 128, 64) -- "text", w, h |
imgui:popStyleVar() | imgui:popStyleVar() | ||
-- some spacing | -- some spacing | ||
− | imgui:dummy(CW, 32 * 1) | + | imgui:dummy(CW, 32*1) |
-- a grid of buttons | -- a grid of buttons | ||
imgui:text("A grid of buttons") | imgui:text("A grid of buttons") | ||
− | |||
local i = 0 | local i = 0 | ||
− | for x = 0,1,0.5 do | + | for x = 0, 1, 0.5 do |
− | for y = 0,1,0.5 do | + | for y = 0, 1, 0.5 do |
imgui:pushStyleVar(ImGui.StyleVar_ButtonTextAlign, x, y) | imgui:pushStyleVar(ImGui.StyleVar_ButtonTextAlign, x, y) | ||
− | if | + | if imgui:button(("[%.1f, %.1f]"):format(x,y), application:getContentWidth()//3, 100) then |
print(x, y) | print(x, y) | ||
end | end | ||
Line 131: | Line 207: | ||
end | end | ||
-- some spacing | -- some spacing | ||
− | imgui:dummy(application:getContentWidth(), 32 * 1) | + | imgui:dummy(application:getContentWidth(), 32*1) |
-- an image button with text | -- an image button with text | ||
imgui:text("An image button \nwith text") | imgui:text("An image button \nwith text") | ||
− | if | + | if imgui:scaledImageButtonWithText(imageTex, "button", 32*1, 32*1, 32*4.5, 32*1.5) then |
print("pressed") | print("pressed") | ||
end | end | ||
end | end | ||
− | |||
imgui:endWindow() | imgui:endWindow() | ||
− | -- 3 end | + | -- 3. we end ImGui frame and render to screen |
imgui:endFrame() | imgui:endFrame() | ||
imgui:render() | imgui:render() | ||
end | end | ||
+ | |||
-- add listener to draw GUI | -- add listener to draw GUI | ||
stage:addEventListener("enterFrame", enterFrame) | stage:addEventListener("enterFrame", enterFrame) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | == File Open/Save Dialog Project Demo == | |
− | [https://github.com/MultiPain/Gideros_examples/tree/master/ImGuiFileDialogDemo GitHub] | + | [[File:Dear_ImGui_FileDialog_Demo.png|300px]] |
+ | '''[https://github.com/MultiPain/Gideros_examples/tree/master/ImGuiFileDialogDemo GitHub project]''' (a little bit outdated, fix below!) | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | TestScene = Core.class(Sprite) | ||
+ | |||
+ | local function dummyConstraints(x,y, w,h, dw,dh) return dw,dh end | ||
+ | |||
+ | function TestScene:init() | ||
+ | self.imgui = ImGui.new() | ||
+ | self:addChild(self.imgui) | ||
+ | |||
+ | local IO = self.imgui:getIO() | ||
+ | IO:setIniFilename(nil) | ||
+ | IO:setLogFilename("|D|log.txt") | ||
+ | |||
+ | self:appResize() | ||
+ | self:addEventListener("enterFrame", self.drawGUI, self) | ||
+ | self:addEventListener("applicationResize", self.appResize, self) | ||
+ | |||
+ | ------------------------------------------------------------------ | ||
+ | self.cachedDirs = {} -- cached dirs and files (refilled only when directory is changed) | ||
+ | self.fileName = "" -- current file name in open/save dialog | ||
+ | self.openModalType = "" -- type of modal window ("Save file" or "Open file") | ||
+ | self.currentItem = 0 -- current selected item in open dialog | ||
+ | end | ||
+ | |||
+ | -- callbacks | ||
+ | function TestScene:onSaveDialogOK(dir) | ||
+ | self.imgui:logToFile() -- (auto_open_depth = -1, filename = nil) | ||
+ | end | ||
+ | |||
+ | function TestScene:onSaveDialogError(errorMessage) | ||
+ | self.imgui:logToFile() -- (auto_open_depth = -1, filename = nil) | ||
+ | end | ||
+ | |||
+ | function TestScene:onOpenDialogOK(dir) | ||
+ | self.imgui:logToFile() -- (auto_open_depth = -1, filename = nil) | ||
+ | end | ||
+ | |||
+ | function TestScene:onOpenDialogError(errorMessage) | ||
+ | self.imgui:logToFile() -- (auto_open_depth = -1, filename = nil) | ||
+ | end | ||
+ | |||
+ | function TestScene:updateDirCache(dir) | ||
+ | if (self.cachedDirs.isUpdated) then return end | ||
+ | |||
+ | self.cachedDirs.isUpdated = true | ||
+ | self.cachedDirs.file = {} | ||
+ | self.cachedDirs.directory = {} | ||
+ | |||
+ | -- scan directory | ||
+ | for entry in lfs.dir(dir) do | ||
+ | if (entry ~= "." and entry ~= "..") then | ||
+ | local path = dir.."\\"..entry | ||
+ | local attributes = lfs.attributes(path) | ||
+ | local t = self.cachedDirs[attributes.mode] -- pick "self.cachedDirs.file" or "self.cachedDirs.directory" table | ||
+ | if (t) then | ||
+ | t[#t+1] = { | ||
+ | path = path, | ||
+ | name = entry, | ||
+ | attr = attributes, | ||
+ | selected = false, | ||
+ | } | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | |||
+ | function TestScene:drawFileDialog(UI) | ||
+ | -- setup min and max sizes | ||
+ | UI:setNextWindowSizeConstraints(800, 400, 1920, 1080) | ||
+ | UI:setNextFrameWantCaptureMouse(true) | ||
+ | -- draw window | ||
+ | -- 2nd argument draws "X" on window | ||
+ | if UI:beginPopupModal(self.openModalType, true, 0, dummyConstraints) then | ||
+ | local isOpenMode = self.openModalType == "Open file" | ||
+ | local currentDir = lfs.currentdir() | ||
+ | |||
+ | if UI:button("Back") then | ||
+ | -- reset selected item and selected filename | ||
+ | self.currentItem = 0 | ||
+ | self.fileName = "" | ||
+ | -- change dir | ||
+ | currentDir = currentDir:match("(.*[/\\])") | ||
+ | lfs.chdir(currentDir) | ||
+ | -- force to rewrite dirs | ||
+ | self.cachedDirs.isUpdated = false | ||
+ | end | ||
+ | UI:sameLine() | ||
+ | |||
+ | -- show path, write dir to tmp variable | ||
+ | local tmp, flag = UI:inputText("Path", currentDir, 256, ImGui.InputTextFlags_EnterReturnsTrue) | ||
+ | -- if input ends | ||
+ | if flag then | ||
+ | -- check if dir exists | ||
+ | local status = lfs.chdir(tmp) | ||
+ | if status then | ||
+ | -- change dir | ||
+ | currentDir = tmp | ||
+ | lfs.chdir(currentDir) | ||
+ | -- force to rewrite dirs | ||
+ | self.cachedDirs.isUpdated = false | ||
+ | end | ||
+ | end | ||
+ | |||
+ | UI:separator() | ||
+ | |||
+ | self:updateDirCache(currentDir) | ||
+ | |||
+ | -- draw folders as buttons | ||
+ | local frameHeightWithSpacing = UI:getFrameHeightWithSpacing() | ||
+ | -- UI:beginChild("DIRS", 300, -frameHeightWithSpacing, ImGui.WindowFlags_HorizontalScrollbar) | ||
+ | UI:beginChild("DIRS", 300, -frameHeightWithSpacing, true) | ||
+ | for _, data in ipairs(self.cachedDirs.directory) do | ||
+ | if UI:button(data.name, -1, 0) then | ||
+ | lfs.chdir(data.path) | ||
+ | self.cachedDirs.isUpdated = false | ||
+ | end | ||
+ | end | ||
+ | UI:endChild() | ||
+ | UI:sameLine() | ||
+ | |||
+ | UI:beginChild("FILES", 0, -frameHeightWithSpacing) | ||
+ | |||
+ | -- draw table of files | ||
+ | UI:columns(4) | ||
+ | UI:text("Name") UI:nextColumn() | ||
+ | UI:text("Change") UI:nextColumn() | ||
+ | UI:text("Modification") UI:nextColumn() | ||
+ | UI:text("Size") UI:nextColumn() | ||
+ | UI:separator() | ||
− | === ImGui | + | for i, data in ipairs(self.cachedDirs.file) do |
− | '' | + | -- files are selectables |
+ | if ImGui:selectable(data.name, self.currentItem == i) then | ||
+ | self.currentItem = i | ||
+ | self.fileName = data.name | ||
+ | end | ||
+ | |||
+ | -- draw last change date | ||
+ | UI:nextColumn() | ||
+ | UI:text(os.date('%c', data.attr.change)) | ||
+ | UI:nextColumn() | ||
+ | -- draw last modification date | ||
+ | UI:text(os.date('%c', data.attr.modification)) | ||
+ | UI:nextColumn() | ||
+ | |||
+ | -- calculate text object position aligned to the right edge | ||
+ | local text = ("%.2f KB"):format(data.attr.size / 1024) | ||
+ | local spaceX = UI:getStyle():getItemSpacing() | ||
+ | local tw, _th = UI:calcTextSize(text) | ||
+ | UI:setCursorPosX(UI:getCursorPosX()+UI:getColumnWidth()-tw-UI:getScrollX()-(2*spaceX)) | ||
+ | -- draw file size | ||
+ | UI:text(text) | ||
+ | UI:nextColumn() | ||
+ | end | ||
+ | UI:endChild() | ||
+ | |||
+ | UI:text("File name:") | ||
+ | UI:sameLine() | ||
+ | |||
+ | local w = UI:getContentRegionAvail() | ||
+ | -- leave 150 pixels for buttons | ||
+ | UI:setNextItemWidth(w-150) | ||
+ | local inputFlag = false | ||
+ | -- draw filename input box | ||
+ | self.fileName, inputFlag = UI:inputText("##x", self.fileName, 64, ImGui.InputTextFlags_EnterReturnsTrue) | ||
+ | UI:sameLine() | ||
+ | |||
+ | w = UI:getContentRegionAvail() | ||
+ | if isOpenMode then | ||
+ | -- search and highlight filename | ||
+ | if self.fileName ~= "" then | ||
+ | local itemFound = false | ||
+ | for i, data in ipairs(self.cachedDirs.file) do | ||
+ | if data.name == self.fileName then | ||
+ | itemFound = true | ||
+ | self.currentItem = i | ||
+ | break | ||
+ | end | ||
+ | end | ||
+ | if (not itemFound) then | ||
+ | self.currentItem = 0 | ||
+ | end | ||
+ | end | ||
+ | |||
+ | -- if ENTER (on input box) or button was pressed | ||
+ | if (UI:button("Open", w*.5) or inputFlag) then | ||
+ | -- try to open file | ||
+ | if (self.fileName ~= "") then | ||
+ | local status, error = pcall(self.onOpenDialogOK, self, currentDir .."\\"..self.fileName) | ||
+ | if (not status) then | ||
+ | self:onOpenDialogError(error) | ||
+ | end | ||
+ | end | ||
+ | |||
+ | UI:closeCurrentPopup() | ||
+ | self.fileName = "" | ||
+ | end | ||
+ | else | ||
+ | if (UI:button("Save", w*.5) or inputFlag) then | ||
+ | local status, error = pcall(self.onSaveDialogOK, self, currentDir.."\\"..self.fileName) | ||
+ | if (not status) then | ||
+ | self:onSaveDialogError(error) | ||
+ | else | ||
+ | self.cachedDirs.isUpdated = false | ||
+ | end | ||
+ | |||
+ | UI:closeCurrentPopup() | ||
+ | self.fileName = "" | ||
+ | end | ||
+ | end | ||
+ | UI:sameLine() | ||
+ | |||
+ | w = UI:getContentRegionAvail() | ||
+ | if (UI:button("Cancel", w) or UI:isKeyPressed(KeyCode.ESC)) then | ||
+ | UI:closeCurrentPopup() | ||
+ | self.fileName = "" | ||
+ | end | ||
+ | |||
+ | UI:endPopup() | ||
+ | end | ||
+ | end | ||
+ | |||
+ | -- loop | ||
+ | function TestScene:drawGUI(e) | ||
+ | local UI = self.imgui | ||
+ | |||
+ | UI:newFrame(e.deltaTime) | ||
+ | |||
+ | UI:pushStyleColor(ImGui.Col_WindowBg, 0, 0) | ||
+ | if UI:beginFullScreenWindow("File dialog demo", nil, ImGui.WindowFlags_MenuBar) then | ||
+ | UI:popStyleColor() | ||
+ | |||
+ | if UI:beginMenuBar() then | ||
+ | local openModal = false | ||
+ | |||
+ | if UI:beginMenu("File") then | ||
+ | if UI:menuItem("Open", "CTRL+O") then | ||
+ | openModal = true -- you can't open popups from menu bars (ImGui problem) | ||
+ | self.openModalType = "Open file" | ||
+ | end | ||
+ | if UI:menuItem("Save as", "CTRL+SHIFT+S") then | ||
+ | openModal = true -- you can't open popups from menu bars (ImGui problem) | ||
+ | self.openModalType = "Save file" | ||
+ | end | ||
+ | UI:endMenu() | ||
+ | end | ||
+ | -- see: https://github.com/ocornut/imgui/issues/331 | ||
+ | if openModal then | ||
+ | self.currentItem = 0 | ||
+ | UI:openPopup(self.openModalType) | ||
+ | end | ||
+ | |||
+ | self:drawFileDialog(UI) | ||
+ | UI:endMenuBar() | ||
+ | end | ||
+ | end | ||
+ | UI:endWindow() | ||
+ | |||
+ | UI:render() | ||
+ | UI:endFrame() | ||
+ | end | ||
+ | |||
+ | -- adapt ImGui to fill window size | ||
+ | function TestScene:appResize() | ||
+ | local minX, minY, maxX, maxY = application:getLogicalBounds() | ||
+ | local sx = application:getLogicalScaleX() | ||
+ | local sy = application:getLogicalScaleY() | ||
+ | |||
+ | self.imgui:setScale(1/sx, 1/sy) | ||
+ | self.imgui:setPosition(minX, minY) | ||
+ | |||
+ | local IO = self.imgui:getIO() | ||
+ | IO:setDisplaySize((maxX-minX)*sx, (maxY-minY)*sy) | ||
+ | end | ||
+ | |||
+ | -- START THE DEMO | ||
+ | require "lfs" -- don't forget to add the plugin in your project | ||
+ | require "ImGui" -- don't forget to add the plugin in your project | ||
+ | |||
+ | application:setBackgroundColor(0x323232) | ||
+ | stage:addChild(TestScene.new()) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == Dear ImGui Snap to Grid == | ||
[[File:Imgui sample03.png|200px]] | [[File:Imgui sample03.png|200px]] | ||
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
require "ImGui" | require "ImGui" | ||
− | GRID_CELL | + | GRID_CELL = 64 |
application:setBackgroundColor(0x323232) | application:setBackgroundColor(0x323232) | ||
local CW = application:getContentWidth() | local CW = application:getContentWidth() | ||
Line 182: | Line 540: | ||
local dragFlag = false | local dragFlag = false | ||
− | local function myResizeCallback(x,y,cw,ch,dw,dh) | + | local function myResizeCallback(x, y, cw, ch, dw, dh) |
− | return (dw // GRID_CELL) * GRID_CELL, (dh // GRID_CELL) * GRID_CELL | + | return (dw//GRID_CELL)*GRID_CELL, (dh//GRID_CELL)*GRID_CELL |
end | end | ||
+ | -- loop | ||
function enterFrame(e) | function enterFrame(e) | ||
imgui:newFrame(e.deltaTime) | imgui:newFrame(e.deltaTime) | ||
− | if | + | if mainWindowOpen then |
imgui:setNextWindowPos(snapX, snapY) | imgui:setNextWindowPos(snapX, snapY) | ||
imgui:setNextWindowSizeConstraints(GRID_CELL*2, GRID_CELL*1, GRID_CELL*8, GRID_CELL*8) | imgui:setNextWindowSizeConstraints(GRID_CELL*2, GRID_CELL*1, GRID_CELL*8, GRID_CELL*8) | ||
mainWindowOpen, drawMainWindowOpen = imgui:beginWindow("My window", mainWindowOpen, nil, myResizeCallback) | mainWindowOpen, drawMainWindowOpen = imgui:beginWindow("My window", mainWindowOpen, nil, myResizeCallback) | ||
local mouseClicked = imgui:isMouseClicked(KeyCode.MOUSE_LEFT) | local mouseClicked = imgui:isMouseClicked(KeyCode.MOUSE_LEFT) | ||
− | if not dragFlag and mouseClicked and imgui:isWindowHovered() then dragFlag=true end | + | if not dragFlag and mouseClicked and imgui:isWindowHovered() then |
+ | dragFlag = true | ||
+ | end | ||
if imgui:isWindowFocused() and dragFlag then | if imgui:isWindowFocused() and dragFlag then | ||
− | local mx,my = imgui:getMousePos() | + | local mx, my = imgui:getMousePos() |
local wx, wy = imgui:getWindowPos() | local wx, wy = imgui:getWindowPos() | ||
if mouseClicked or imgui:isMouseReleased(KeyCode.MOUSE_LEFT) then | if mouseClicked or imgui:isMouseReleased(KeyCode.MOUSE_LEFT) then | ||
− | offsetX, offsetY = mx - wx, my - wy | + | offsetX, offsetY = mx-wx, my-wy |
end | end | ||
if imgui:isMouseDragging(KeyCode.MOUSE_LEFT, 0) then | if imgui:isMouseDragging(KeyCode.MOUSE_LEFT, 0) then | ||
− | snapX = ((mx - offsetX) // GRID_CELL) * GRID_CELL | + | snapX = ((mx-offsetX)//GRID_CELL)*GRID_CELL |
− | snapY = ((my - offsetY) // GRID_CELL) * GRID_CELL | + | snapY = ((my-offsetY)//GRID_CELL)*GRID_CELL |
+ | -- restrict | ||
+ | if snapX <= 0 then snapX = 0 end | ||
+ | if snapY <= 0 then snapY = 0 end | ||
+ | if snapX >= CW-GRID_CELL then snapX = CW-GRID_CELL end | ||
+ | if snapY >= CH-GRID_CELL then snapY = CH-GRID_CELL end | ||
else | else | ||
dragFlag = false | dragFlag = false | ||
end | end | ||
end | end | ||
− | if | + | if drawMainWindowOpen then |
-- do stuff | -- do stuff | ||
end | end | ||
Line 216: | Line 582: | ||
end | end | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
stage:addEventListener("enterFrame", enterFrame) | stage:addEventListener("enterFrame", enterFrame) | ||
+ | --stage:addEventListener("mouseDown", function(e) imgui:onMouseDown(e) end) | ||
+ | --stage:addEventListener("mouseUp", function(e) imgui:onMouseUp(e) end) | ||
+ | --stage:addEventListener("mouseHover", function(e) imgui:onMouseHover(e) end) | ||
+ | --stage:addEventListener("mouseMove", function(e) imgui:onMouseMove(e) end) | ||
+ | --stage:addEventListener("mouseWheel", function(e) imgui:onMouseWheel(e) end) | ||
+ | --stage:addEventListener("keyDown", function(e) imgui:onKeyDown(e) end) | ||
+ | --stage:addEventListener("keyUp", function(e) imgui:onKeyUp(e) end) | ||
+ | --stage:addEventListener("keyChar", function(e) imgui:onKeyChar(e) end) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | '''More to come God's willing...''' | ||
'''[[Dear ImGui]]''' | '''[[Dear ImGui]]''' | ||
{{GIDEROS IMPORTANT LINKS}} | {{GIDEROS IMPORTANT LINKS}} |
Latest revision as of 01:50, 9 October 2024
The below are fully functional working demo you can copy/paste.
note: you may have to provide your own assets (fonts, gfx, …)
Dear ImGui Custom Font
require "ImGui"
local imgui = ImGui.new()
stage:addChild(imgui)
local IO = imgui:getIO()
local fontatlas = IO:getFonts()
local myfont = fontatlas:addFont("fonts/Cabin-Regular-TTF.ttf", 16) -- your custom font here
IO:setFontDefault(myfont)
fontatlas:build()
-- imgui style
local style = imgui:getStyle()
style:setWindowMinSize(64*4, 64*1.5)
function onEnterFrame(e)
-- imgui start
imgui:newFrame(e.deltaTime)
-- window
if imgui:beginWindow("Window") then
-- widgets
imgui:text("This is using 'myfont' to write text.")
end
imgui:endWindow()
-- imgui end
imgui:render()
imgui:endFrame()
end
stage:addEventListener("enterFrame", onEnterFrame)
Start a Window at a given position
require "ImGui"
application:setBackgroundColor(0x323232)
local imgui = ImGui.new()
stage:addChild(imgui)
local window01 = true -- this window can be closed
local windowdrawn = false -- boolean to hold our windows status
local posx, posy = 32*8, 32*4 -- starting window position
local offsetx, offsety = 0, 0 -- new window position
local dragflag = false
function onEnterFrame(e)
-- 1. we start ImGui
imgui:newFrame(e.deltaTime)
-- 2. we add some windows
if window01 then -- if window exists (not closed)
imgui:setNextWindowPos(posx, posy)
window01, windowdrawn = imgui:beginWindow("window01", window01)
-- update window new position
local mouseClicked = imgui:isMouseClicked(KeyCode.MOUSE_LEFT)
if not dragflag and mouseClicked and imgui:isWindowHovered() then
dragflag = true
end
if imgui:isWindowFocused() and dragflag then
local mx,my = imgui:getMousePos()
local wx, wy = imgui:getWindowPos()
if mouseClicked or imgui:isMouseReleased(KeyCode.MOUSE_LEFT) then
offsetx, offsety = mx - wx, my - wy
end
if imgui:isMouseDragging(KeyCode.MOUSE_LEFT, 0) then
posx = (mx - offsetx)
posy = (my - offsety)
else
dragflag = false
end
end
-- window widgets
if windowdrawn then -- the variable is false when main window is collapsed
imgui:text("Hello Dear ImGui!")
imgui:newLine()
imgui:textColored("This window can be moved, resized and closed!", 0x00ff7f, 1)
imgui:text("its position is: "..posx..", "..posy)
end
imgui:endWindow()
end
-- 3. we end ImGui frame and render to screen
imgui:endFrame()
imgui:render()
end
stage:addEventListener(Event.ENTER_FRAME, onEnterFrame)
Dear ImGui Fullscreen Window
require "ImGui"
local imgui = ImGui.new()
stage:addChild(imgui)
-- some imgui params
local IO = imgui:getIO()
IO:setFontGlobalScale(2)
--imgui:setLightStyle()
--imgui:setClassicStyle()
imgui:setDarkStyle()
-- some vars
local CW = application:getContentWidth()
local text, text2 = "Centered Text", "Colored Text"
-- the loop
function enterFrame(e)
-- 1. we start ImGui
imgui:newFrame(e.deltaTime)
-- 2. our GUI
if imgui:beginFullScreenWindow("Hello ImGui") then
-- some spacing
imgui:dummy(CW, 32 * 2) -- imgui:dummy(w, h)
-- a centered text
local textW = imgui:calcTextSize(text)
local textMid = (CW - textW) / 2
imgui:dummy(textMid, 0)
imgui:sameLine(0, 0) -- puts the next element on the same line with no gap, for gaps use imgui:sameLine()
imgui:text(text)
-- some spacing
imgui:dummy(CW, 32 * 4)
-- a colored text
imgui:textColored(text2, 0xff00ff)
end
imgui:endWindow()
-- 3. we end ImGui frame and render to screen
imgui:endFrame()
imgui:render()
end
-- the listeners
stage:addEventListener("enterFrame", enterFrame)
Dear ImGui Buttons
require "ImGui"
local imgui = ImGui.new()
stage:addChild(imgui)
-- some imgui params
local IO = imgui:getIO()
IO:setFontGlobalScale(2)
imgui:setClassicStyle()
-- some vars
local CW = application:getContentWidth()
local text = "Centered Text"
local imageTex = Texture.new("gfx/100 (2).png")
-- loop
function enterFrame(e)
-- 1. we start ImGui
imgui:newFrame(e.deltaTime)
-- 2. our GUI
if imgui:beginFullScreenWindow("Hello ImGui") then
-- some spacing
imgui:dummy(CW, 32*2)
-- a centered text
local textW = imgui:calcTextSize(text)
local textMid = (CW - textW) / 2
imgui:dummy(textMid, 0)
imgui:sameLine(0, 0)
imgui:text(text)
-- some spacing
imgui:dummy(CW, 32*2)
-- a button
imgui:text("A button")
imgui:pushStyleVar(ImGui.StyleVar_ButtonTextAlign, 0.5, 0.5)
imgui:button(("x"), 128, 64) -- "text", w, h
imgui:popStyleVar()
-- some spacing
imgui:dummy(CW, 32*1)
-- a grid of buttons
imgui:text("A grid of buttons")
local i = 0
for x = 0, 1, 0.5 do
for y = 0, 1, 0.5 do
imgui:pushStyleVar(ImGui.StyleVar_ButtonTextAlign, x, y)
if imgui:button(("[%.1f, %.1f]"):format(x,y), application:getContentWidth()//3, 100) then
print(x, y)
end
imgui:popStyleVar()
-- count elements
i += 1
-- make new line on 3d element
if (i % 3 ~= 0) then imgui:sameLine() end
end
end
-- some spacing
imgui:dummy(application:getContentWidth(), 32*1)
-- an image button with text
imgui:text("An image button \nwith text")
if imgui:scaledImageButtonWithText(imageTex, "button", 32*1, 32*1, 32*4.5, 32*1.5) then
print("pressed")
end
end
imgui:endWindow()
-- 3. we end ImGui frame and render to screen
imgui:endFrame()
imgui:render()
end
-- add listener to draw GUI
stage:addEventListener("enterFrame", enterFrame)
File Open/Save Dialog Project Demo
GitHub project (a little bit outdated, fix below!)
TestScene = Core.class(Sprite)
local function dummyConstraints(x,y, w,h, dw,dh) return dw,dh end
function TestScene:init()
self.imgui = ImGui.new()
self:addChild(self.imgui)
local IO = self.imgui:getIO()
IO:setIniFilename(nil)
IO:setLogFilename("|D|log.txt")
self:appResize()
self:addEventListener("enterFrame", self.drawGUI, self)
self:addEventListener("applicationResize", self.appResize, self)
------------------------------------------------------------------
self.cachedDirs = {} -- cached dirs and files (refilled only when directory is changed)
self.fileName = "" -- current file name in open/save dialog
self.openModalType = "" -- type of modal window ("Save file" or "Open file")
self.currentItem = 0 -- current selected item in open dialog
end
-- callbacks
function TestScene:onSaveDialogOK(dir)
self.imgui:logToFile() -- (auto_open_depth = -1, filename = nil)
end
function TestScene:onSaveDialogError(errorMessage)
self.imgui:logToFile() -- (auto_open_depth = -1, filename = nil)
end
function TestScene:onOpenDialogOK(dir)
self.imgui:logToFile() -- (auto_open_depth = -1, filename = nil)
end
function TestScene:onOpenDialogError(errorMessage)
self.imgui:logToFile() -- (auto_open_depth = -1, filename = nil)
end
function TestScene:updateDirCache(dir)
if (self.cachedDirs.isUpdated) then return end
self.cachedDirs.isUpdated = true
self.cachedDirs.file = {}
self.cachedDirs.directory = {}
-- scan directory
for entry in lfs.dir(dir) do
if (entry ~= "." and entry ~= "..") then
local path = dir.."\\"..entry
local attributes = lfs.attributes(path)
local t = self.cachedDirs[attributes.mode] -- pick "self.cachedDirs.file" or "self.cachedDirs.directory" table
if (t) then
t[#t+1] = {
path = path,
name = entry,
attr = attributes,
selected = false,
}
end
end
end
end
function TestScene:drawFileDialog(UI)
-- setup min and max sizes
UI:setNextWindowSizeConstraints(800, 400, 1920, 1080)
UI:setNextFrameWantCaptureMouse(true)
-- draw window
-- 2nd argument draws "X" on window
if UI:beginPopupModal(self.openModalType, true, 0, dummyConstraints) then
local isOpenMode = self.openModalType == "Open file"
local currentDir = lfs.currentdir()
if UI:button("Back") then
-- reset selected item and selected filename
self.currentItem = 0
self.fileName = ""
-- change dir
currentDir = currentDir:match("(.*[/\\])")
lfs.chdir(currentDir)
-- force to rewrite dirs
self.cachedDirs.isUpdated = false
end
UI:sameLine()
-- show path, write dir to tmp variable
local tmp, flag = UI:inputText("Path", currentDir, 256, ImGui.InputTextFlags_EnterReturnsTrue)
-- if input ends
if flag then
-- check if dir exists
local status = lfs.chdir(tmp)
if status then
-- change dir
currentDir = tmp
lfs.chdir(currentDir)
-- force to rewrite dirs
self.cachedDirs.isUpdated = false
end
end
UI:separator()
self:updateDirCache(currentDir)
-- draw folders as buttons
local frameHeightWithSpacing = UI:getFrameHeightWithSpacing()
-- UI:beginChild("DIRS", 300, -frameHeightWithSpacing, ImGui.WindowFlags_HorizontalScrollbar)
UI:beginChild("DIRS", 300, -frameHeightWithSpacing, true)
for _, data in ipairs(self.cachedDirs.directory) do
if UI:button(data.name, -1, 0) then
lfs.chdir(data.path)
self.cachedDirs.isUpdated = false
end
end
UI:endChild()
UI:sameLine()
UI:beginChild("FILES", 0, -frameHeightWithSpacing)
-- draw table of files
UI:columns(4)
UI:text("Name") UI:nextColumn()
UI:text("Change") UI:nextColumn()
UI:text("Modification") UI:nextColumn()
UI:text("Size") UI:nextColumn()
UI:separator()
for i, data in ipairs(self.cachedDirs.file) do
-- files are selectables
if ImGui:selectable(data.name, self.currentItem == i) then
self.currentItem = i
self.fileName = data.name
end
-- draw last change date
UI:nextColumn()
UI:text(os.date('%c', data.attr.change))
UI:nextColumn()
-- draw last modification date
UI:text(os.date('%c', data.attr.modification))
UI:nextColumn()
-- calculate text object position aligned to the right edge
local text = ("%.2f KB"):format(data.attr.size / 1024)
local spaceX = UI:getStyle():getItemSpacing()
local tw, _th = UI:calcTextSize(text)
UI:setCursorPosX(UI:getCursorPosX()+UI:getColumnWidth()-tw-UI:getScrollX()-(2*spaceX))
-- draw file size
UI:text(text)
UI:nextColumn()
end
UI:endChild()
UI:text("File name:")
UI:sameLine()
local w = UI:getContentRegionAvail()
-- leave 150 pixels for buttons
UI:setNextItemWidth(w-150)
local inputFlag = false
-- draw filename input box
self.fileName, inputFlag = UI:inputText("##x", self.fileName, 64, ImGui.InputTextFlags_EnterReturnsTrue)
UI:sameLine()
w = UI:getContentRegionAvail()
if isOpenMode then
-- search and highlight filename
if self.fileName ~= "" then
local itemFound = false
for i, data in ipairs(self.cachedDirs.file) do
if data.name == self.fileName then
itemFound = true
self.currentItem = i
break
end
end
if (not itemFound) then
self.currentItem = 0
end
end
-- if ENTER (on input box) or button was pressed
if (UI:button("Open", w*.5) or inputFlag) then
-- try to open file
if (self.fileName ~= "") then
local status, error = pcall(self.onOpenDialogOK, self, currentDir .."\\"..self.fileName)
if (not status) then
self:onOpenDialogError(error)
end
end
UI:closeCurrentPopup()
self.fileName = ""
end
else
if (UI:button("Save", w*.5) or inputFlag) then
local status, error = pcall(self.onSaveDialogOK, self, currentDir.."\\"..self.fileName)
if (not status) then
self:onSaveDialogError(error)
else
self.cachedDirs.isUpdated = false
end
UI:closeCurrentPopup()
self.fileName = ""
end
end
UI:sameLine()
w = UI:getContentRegionAvail()
if (UI:button("Cancel", w) or UI:isKeyPressed(KeyCode.ESC)) then
UI:closeCurrentPopup()
self.fileName = ""
end
UI:endPopup()
end
end
-- loop
function TestScene:drawGUI(e)
local UI = self.imgui
UI:newFrame(e.deltaTime)
UI:pushStyleColor(ImGui.Col_WindowBg, 0, 0)
if UI:beginFullScreenWindow("File dialog demo", nil, ImGui.WindowFlags_MenuBar) then
UI:popStyleColor()
if UI:beginMenuBar() then
local openModal = false
if UI:beginMenu("File") then
if UI:menuItem("Open", "CTRL+O") then
openModal = true -- you can't open popups from menu bars (ImGui problem)
self.openModalType = "Open file"
end
if UI:menuItem("Save as", "CTRL+SHIFT+S") then
openModal = true -- you can't open popups from menu bars (ImGui problem)
self.openModalType = "Save file"
end
UI:endMenu()
end
-- see: https://github.com/ocornut/imgui/issues/331
if openModal then
self.currentItem = 0
UI:openPopup(self.openModalType)
end
self:drawFileDialog(UI)
UI:endMenuBar()
end
end
UI:endWindow()
UI:render()
UI:endFrame()
end
-- adapt ImGui to fill window size
function TestScene:appResize()
local minX, minY, maxX, maxY = application:getLogicalBounds()
local sx = application:getLogicalScaleX()
local sy = application:getLogicalScaleY()
self.imgui:setScale(1/sx, 1/sy)
self.imgui:setPosition(minX, minY)
local IO = self.imgui:getIO()
IO:setDisplaySize((maxX-minX)*sx, (maxY-minY)*sy)
end
-- START THE DEMO
require "lfs" -- don't forget to add the plugin in your project
require "ImGui" -- don't forget to add the plugin in your project
application:setBackgroundColor(0x323232)
stage:addChild(TestScene.new())
Dear ImGui Snap to Grid
require "ImGui"
GRID_CELL = 64
application:setBackgroundColor(0x323232)
local CW = application:getContentWidth()
local CH = application:getContentHeight()
for i = 1, CW, GRID_CELL do
local line = Pixel.new(0xffffff, 1, 1, CH)
line:setX(i)
stage:addChild(line)
end
for i = 1, CH, GRID_CELL do
local line = Pixel.new(0xffffff, 1, CW, 1)
line:setY(i)
stage:addChild(line)
end
local imgui = ImGui.new()
stage:addChild(imgui)
local mainWindowOpen, drawMainWindowOpen = true, false
local snapX,snapY = 0, 0
local offsetX, offsetY = 0, 0
local dragFlag = false
local function myResizeCallback(x, y, cw, ch, dw, dh)
return (dw//GRID_CELL)*GRID_CELL, (dh//GRID_CELL)*GRID_CELL
end
-- loop
function enterFrame(e)
imgui:newFrame(e.deltaTime)
if mainWindowOpen then
imgui:setNextWindowPos(snapX, snapY)
imgui:setNextWindowSizeConstraints(GRID_CELL*2, GRID_CELL*1, GRID_CELL*8, GRID_CELL*8)
mainWindowOpen, drawMainWindowOpen = imgui:beginWindow("My window", mainWindowOpen, nil, myResizeCallback)
local mouseClicked = imgui:isMouseClicked(KeyCode.MOUSE_LEFT)
if not dragFlag and mouseClicked and imgui:isWindowHovered() then
dragFlag = true
end
if imgui:isWindowFocused() and dragFlag then
local mx, my = imgui:getMousePos()
local wx, wy = imgui:getWindowPos()
if mouseClicked or imgui:isMouseReleased(KeyCode.MOUSE_LEFT) then
offsetX, offsetY = mx-wx, my-wy
end
if imgui:isMouseDragging(KeyCode.MOUSE_LEFT, 0) then
snapX = ((mx-offsetX)//GRID_CELL)*GRID_CELL
snapY = ((my-offsetY)//GRID_CELL)*GRID_CELL
-- restrict
if snapX <= 0 then snapX = 0 end
if snapY <= 0 then snapY = 0 end
if snapX >= CW-GRID_CELL then snapX = CW-GRID_CELL end
if snapY >= CH-GRID_CELL then snapY = CH-GRID_CELL end
else
dragFlag = false
end
end
if drawMainWindowOpen then
-- do stuff
end
imgui:endWindow()
end
imgui:endFrame()
imgui:render()
end
stage:addEventListener("enterFrame", enterFrame)
--stage:addEventListener("mouseDown", function(e) imgui:onMouseDown(e) end)
--stage:addEventListener("mouseUp", function(e) imgui:onMouseUp(e) end)
--stage:addEventListener("mouseHover", function(e) imgui:onMouseHover(e) end)
--stage:addEventListener("mouseMove", function(e) imgui:onMouseMove(e) end)
--stage:addEventListener("mouseWheel", function(e) imgui:onMouseWheel(e) end)
--stage:addEventListener("keyDown", function(e) imgui:onKeyDown(e) end)
--stage:addEventListener("keyUp", function(e) imgui:onKeyUp(e) end)
--stage:addEventListener("keyChar", function(e) imgui:onKeyChar(e) end)
More to come God's willing...