├── src
├── Widgets
│ ├── Spinner.lua
│ ├── Image.lua
│ ├── Label.lua
│ ├── MenuBar.lua
│ ├── MenuItem.lua
│ ├── Tooltip.lua
│ ├── Button.lua
│ ├── Menu.lua
│ ├── CheckBox.lua
│ ├── ProgressBar.lua
│ ├── RadioButton.lua
│ ├── Stepper.lua
│ ├── Slider.lua
│ ├── Window.lua
│ ├── ImageButton.lua
│ ├── Scaler.lua
│ ├── TextEntry.lua
│ └── Layout.lua
├── Themes
│ └── Primitive
│ │ ├── assets
│ │ └── roboto.ttf
│ │ └── init.lua
├── Core
│ ├── init.lua
│ ├── param.lua
│ ├── UIState.lua
│ ├── assetLoader.lua
│ ├── theme.lua
│ ├── mosaic.lua
│ ├── drawCommands.lua
│ ├── base.lua
│ └── util.lua
└── init.lua
└── README.md
/src/Widgets/Spinner.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | Text-Entry + Two up-and-down buttons
3 | I would appreciate your help in this!!
4 | ]]
--------------------------------------------------------------------------------
/src/Themes/Primitive/assets/roboto.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/besnoi/lovely-imgui/HEAD/src/Themes/Primitive/assets/roboto.ttf
--------------------------------------------------------------------------------
/src/Core/init.lua:
--------------------------------------------------------------------------------
1 | local CORE_PATH = (...)
2 |
3 | imgui=require(CORE_PATH..'.base')
4 |
5 | function imgui.init()
6 | love.keyboard.setKeyRepeat(true)
7 | end
8 |
9 | function imgui.isMouseReleased()
10 | return imgui.uiState.mouseUp
11 | end
12 |
13 | function imgui.getNextID()
14 | --this will get the ID of the next widget!
15 | return imgui['_genID']+1
16 | end
17 |
18 | return imgui
--------------------------------------------------------------------------------
/src/Core/param.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | Since Magic-Numbers are bad we'll take care of them here!
3 | ]]
4 |
5 | local param={}
6 |
7 | function param.getStepperHeight(w) return w*.25 end
8 |
9 | function param.getStepperLeftButton(stepper,x,y,w,h)
10 | return x,y,w*.2,h
11 | end
12 |
13 | function param.getStepperRightButton(stepper,x,y,w,h)
14 | return x+w-w*.2,y,w*.2,h
15 | end
16 |
17 | return param
--------------------------------------------------------------------------------
/src/Core/UIState.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | ImGUI needs window-dimensions, cursor-positon, etc
3 | So UIState stores all these information!
4 | ]]
5 |
6 | --Setting default values *just in case*!!
7 |
8 | local UIState={
9 | dt, --delta-time
10 | mouseX=-100,
11 | mouseY=-100,
12 | keyChar, --for text input
13 | scrollDX,
14 | scrollDY,
15 | mouseUp, --meant to be used externally through an interface!
16 | mouseDown, --used internally
17 | hotItem,
18 | activeItem,
19 | lastActiveItem,
20 | winWidth,
21 | winHeight
22 | }
23 |
24 | return UIState
--------------------------------------------------------------------------------
/src/Widgets/Image.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | This draws the Image at a relative/absolute position
3 | ]]
4 |
5 | local LIB_PATH = (...):
6 | match("^(.+)%.[^%.]+"):
7 | match("^(.+)%.[^%.]+")
8 |
9 | local util=require(LIB_PATH..'.Core.util')
10 | local core=require(LIB_PATH..'.Core')
11 | local DrawCommands=require(LIB_PATH..'.Core.drawCommands')
12 |
13 | local function Image(img,x,y,w,h)
14 | if core.idle then return end
15 | local imgColor,r
16 | img,imgColor,r,x,y,w,h=util.getImageParams(img,x,y,w,h)
17 |
18 | local imgW,imgH=img:getDimensions()
19 | DrawCommands.registerCommand(function()
20 | love.graphics.setColor(1,1,1)
21 | if imgColor then love.graphics.setColor(unpack(imgColor)) end
22 | love.graphics.draw(img,x,y,r,w/imgW,h/imgH,imgW/2,imgH/2)
23 | -- love.graphics.draw(img,x,y,0,1,1,w/2,h/2)
24 | end)
25 | end
26 |
27 | return Image
--------------------------------------------------------------------------------
/src/Widgets/Label.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | This draws the label at a relative/absolute position
3 | ]]
4 |
5 | local LIB_PATH = (...):
6 | match("^(.+)%.[^%.]+"):
7 | match("^(.+)%.[^%.]+")
8 |
9 | local theme=require(LIB_PATH..'.Core.theme')
10 | local util=require(LIB_PATH..'.Core.util')
11 | local core=require(LIB_PATH..'.Core')
12 | local DrawCommands=require(LIB_PATH..'.Core.drawCommands')
13 |
14 | local function Label(text,x,y)
15 | if core.idle then return end
16 | x,y=util.getLabelParams(text,x,y)
17 | DrawCommands.registerCommand(function()
18 | if type(text)=='string' then
19 | theme.drawLabel(text,x,y)
20 | else
21 | love.graphics.setColor(1,1,1)
22 | if text.color then love.graphics.setColor(text.color) end
23 | if text.font then love.graphics.setFont(text.font) end
24 | love.graphics.print(text.text,x,y)
25 | end
26 | end)
27 | end
28 |
29 | return Label
--------------------------------------------------------------------------------
/src/Widgets/MenuBar.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | MenuBar is a container for Menus!
3 | Menus generally don't need a menubar for them to rendered
4 | making them context-menus
5 | ]]
6 |
7 | local LIB_PATH = (...):
8 | match("^(.+)%.[^%.]+"):
9 | match("^(.+)%.[^%.]+")
10 |
11 | local uiState=require(LIB_PATH..'.Core.UIState')
12 | local util=require(LIB_PATH..'.Core.util')
13 | local theme=require(LIB_PATH..'.Core.theme')
14 | local core=require(LIB_PATH..'.Core')
15 | local DrawCommands=require(LIB_PATH..'.Core.drawCommands')
16 | local Window=require(LIB_PATH..'.Widgets.Window')
17 |
18 | local function BeginMenuBar()
19 | Window.addMenuBar()
20 | return true
21 | end
22 |
23 | local function EndMenuBar()
24 | local x,y=Window.getTopLeft()
25 | local w,h=theme.getFontSize('menu','')
26 | w=Window.getDimensions()
27 | DrawCommands.registerCommand(function()
28 | theme.drawMenuBar(x,y,w,h)
29 | end)
30 | end
31 |
32 | return {BeginMenuBar,EndMenuBar}
33 |
34 | --[[
35 | WORK IN PROGRESS: Wanna help me out :>
36 | ]]
--------------------------------------------------------------------------------
/src/Widgets/MenuItem.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | MenuItem is really just anything that goes into a menu!
3 | There's checked menu-item, radio-menu item and normal
4 | label-menu-item!
5 | ]]
6 |
7 | local LIB_PATH = (...):
8 | match("^(.+)%.[^%.]+"):
9 | match("^(.+)%.[^%.]+")
10 |
11 | local uiState=require(LIB_PATH..'.Core.UIState')
12 | local util=require(LIB_PATH..'.Core.util')
13 | local theme=require(LIB_PATH..'.Core.theme')
14 | local core=require(LIB_PATH..'.Core')
15 | local DrawCommands=require(LIB_PATH..'.Core.drawCommands')
16 | local Window=require(LIB_PATH..'.Widgets.Window')
17 |
18 | local function BeginMenuBar()
19 | Window.addMenuBar()
20 | return true
21 | end
22 |
23 | local function EndMenuBar()
24 | local x,y=Window.getTopLeft()
25 | local w,h=theme.getFontSize('menu','')
26 | w=Window.getDimensions()
27 | DrawCommands.registerCommand(function()
28 | theme.drawMenuBar(x,y,w,h)
29 | end)
30 | end
31 |
32 | return {BeginMenuBar,EndMenuBar}
33 |
34 | --[[
35 | WORK IN PROGRESS: Wanna help me out :>
36 | ]]
--------------------------------------------------------------------------------
/src/Widgets/Tooltip.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | This draws the Tooltip at a relative/absolute position
3 | Tooltip speaks for the next widget that is to be rendered!
4 | Tooltip will normally be displayed instantly! For delays
5 | one must rely on the LAF!
6 | ]]
7 |
8 | local LIB_PATH = (...):
9 | match("^(.+)%.[^%.]+"):
10 | match("^(.+)%.[^%.]+")
11 |
12 | local theme=require(LIB_PATH..'.Core.theme')
13 | local uiState=require(LIB_PATH..'.Core.UIState')
14 | local util=require(LIB_PATH..'.Core.util')
15 | local core=require(LIB_PATH..'.Core')
16 | local DrawCommands=require(LIB_PATH..'.Core.drawCommands')
17 |
18 | local function Tooltip(text,x,y)
19 | if core.idle then return end
20 | local id=core.genID()
21 | x,y=util.getTooltipParams(text,x,y)
22 | DrawCommands.registerCommand(-1,function()
23 | if uiState.hotItem~=id+1 then return end
24 |
25 | if type(text)=='string' then
26 | theme.drawTooltip(text,x,y)
27 | else
28 | love.graphics.setColor(1,1,1)
29 | if text.color then love.graphics.setColor(text.color) end
30 | if text.font then love.graphics.setFont(text.font) end
31 | love.graphics.print(text.text,x,y)
32 | end
33 | end)
34 | end
35 |
36 | return Tooltip
--------------------------------------------------------------------------------
/src/Widgets/Button.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | This draws the button at a relative/absolute position
3 | ]]
4 |
5 | local LIB_PATH = (...):
6 | match("^(.+)%.[^%.]+"):
7 | match("^(.+)%.[^%.]+")
8 |
9 | local uiState=require(LIB_PATH..'.Core.UIState')
10 | local util=require(LIB_PATH..'.Core.util')
11 | local theme=require(LIB_PATH..'.Core.theme')
12 | local core=require(LIB_PATH..'.Core')
13 | local DrawCommands=require(LIB_PATH..'.Core.drawCommands')
14 |
15 | local function Button(text,x,y,w,h)
16 | if core.idle then return end
17 | local id=core.genID()
18 | text,x,y,w,h=util.getButtonParams(text,x,y,w,h)
19 | core.updateWidget(id,util.mouseOver(x,y,w,h))
20 | if uiState.hotItem==id then
21 | if uiState.activeItem==id then
22 | DrawCommands.registerCommand(function()
23 | theme.drawButtonPressed(text,x,y,w,h)
24 | end)
25 | else
26 | DrawCommands.registerCommand(function()
27 | theme.drawButtonHover(text,x,y,w,h)
28 | end)
29 | end
30 | else
31 | DrawCommands.registerCommand(function()
32 | theme.drawButtonNormal(text,x,y,w,h)
33 | end)
34 | end
35 |
36 | return (uiState.hotItem==id and uiState.activeItem==id) or (
37 | uiState.lastActiveItem==id and uiState.mouseUp
38 | )
39 | end
40 |
41 | return Button
--------------------------------------------------------------------------------
/src/Core/assetLoader.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | Whole point -> to load all the assets at one go!!
3 | Here Assets refer to quads and PNG images!
4 | ]]
5 |
6 | local AssetLoader={}
7 | local imgCache={}
8 | local slicesCache={}
9 |
10 | local CORE_PATH = (...):match("^(.+)%.[^%.]+")
11 | local Mosaic=require(CORE_PATH..'.mosaic')
12 |
13 | local function isAFile(url)
14 | if love.filesystem.getInfo(url) then
15 | return love.filesystem.getInfo(url).type=="file"
16 | end
17 | end
18 |
19 | local function masterLoad(path,func)
20 | path=path:gsub('[.]','/')
21 | local assets={}
22 | local items=love.filesystem.getDirectoryItems(path)
23 | for i,item in ipairs(items) do
24 | if not isAFile(path..'/'..item) then
25 | goto continue
26 | end
27 | local len=item:len()
28 | if len>4 and item:sub(len-3)=='.png' then
29 | assets[item:sub(1,item:len()-4)]=func(path..'/'..item)
30 | end
31 | ::continue::
32 | end
33 | return assets
34 | end
35 |
36 | function AssetLoader.loadImages(path)
37 | if not imgCache[path] then
38 | imgCache[path]=masterLoad(path,Mosaic.loadImage)
39 | end
40 | return imgCache[path]
41 | end
42 |
43 | function AssetLoader.loadSlices(path)
44 | if not slicesCache[path] then
45 | slicesCache[path]=masterLoad(path,Mosaic.loadSlices)
46 | end
47 | return slicesCache[path]
48 | end
49 |
50 | return AssetLoader
--------------------------------------------------------------------------------
/src/Widgets/Menu.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | Menus generally don't need a menubar for them to rendered
3 | making them context-menus
4 | ]]
5 |
6 | local LIB_PATH = (...):
7 | match("^(.+)%.[^%.]+"):
8 | match("^(.+)%.[^%.]+")
9 |
10 | local uiState=require(LIB_PATH..'.Core.UIState')
11 | local util=require(LIB_PATH..'.Core.util')
12 | local theme=require(LIB_PATH..'.Core.theme')
13 | local core=require(LIB_PATH..'.Core')
14 | local DrawCommands=require(LIB_PATH..'.Core.drawCommands')
15 | local Window=require(LIB_PATH..'.Widgets.Window')
16 |
17 | --Text of the menu and width of all the previous menus
18 | local width --why waste memory in stacks when a single variable can do the job!
19 | local menuText --same case here
20 |
21 | local function BeginMenu(text)
22 | if core.idle then return end
23 | menuText=text
24 | width=Window.getMenuWidth()
25 | local w,h=theme.getFontSize('menu',text..'a')--trick for padding!
26 | Window.addMenu(w)
27 | local x,y=Window.getTopLeft() x=x+width
28 | if util.mouseOver(x,y,w,h) then
29 | DrawCommands.registerCommand(-1,function()
30 | theme.drawMenuHover(text,x,y,w,h)
31 | end)
32 | else
33 | DrawCommands.registerCommand(-1,function()
34 | theme.drawMenuNormal(text,x,y)
35 | end)
36 | end
37 | return true
38 | end
39 |
40 | local function EndMenu()
41 | local text=menuText --IMPORTANT!!! This is how Lua works dude!
42 |
43 | end
44 |
45 | return {BeginMenu,EndMenu}
46 |
47 | --[[
48 | WORK IN PROGRESS: Wanna help me out :>
49 | ]]
--------------------------------------------------------------------------------
/src/Widgets/CheckBox.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | This draws a checkbox (with/without label) at a relative/absolute position
3 | ]]
4 |
5 | local LIB_PATH = (...):
6 | match("^(.+)%.[^%.]+"):
7 | match("^(.+)%.[^%.]+")
8 |
9 | local uiState=require(LIB_PATH..'.Core.UIState')
10 | local util=require(LIB_PATH..'.Core.util')
11 | local theme=require(LIB_PATH..'.Core.theme')
12 | local core=require(LIB_PATH..'.Core')
13 | local DrawCommands=require(LIB_PATH..'.Core.drawCommands')
14 |
15 | local function CheckBox(isChecked,text,x,y,w,h)
16 | if core.idle then return end
17 | local id=core.genID()
18 | isChecked,text,x,y,w,h=util.getCheckBoxParams(isChecked,text,x,y,w,h)
19 | --For the check-box and the label!!
20 | core.updateWidget(
21 | id,
22 | util.mouseOver(x,y,w,h) or
23 | (text and
24 | util.mouseOver(
25 | x+w+15,y,theme.getFontSize('checkbox',text),h
26 | )
27 | )
28 | )
29 | if uiState.hotItem==id then
30 | if uiState.activeItem==id then
31 | DrawCommands.registerCommand(function()
32 | theme.drawCheckBoxPressed(isChecked[1],text,x,y,w,h)
33 | end)
34 | else
35 | DrawCommands.registerCommand(function()
36 | theme.drawCheckBoxHover(isChecked[1],text,x,y,w,h)
37 | end)
38 | end
39 | else
40 | DrawCommands.registerCommand(function()
41 | theme.drawCheckBoxNormal(isChecked[1],text,x,y,w,h)
42 | end)
43 | end
44 | if uiState.lastActiveItem==id then
45 | --If the last thing that was clicked was this check-box
46 | isChecked[1]=not isChecked[1]
47 | uiState.lastActiveItem=nil
48 | return true
49 | end
50 | end
51 |
52 | return CheckBox
--------------------------------------------------------------------------------
/src/Widgets/ProgressBar.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | Progress Bar are very essential for loading screens and stuff like that!
3 | Because of the way Lovely-imGUI has been designed you can have variety
4 | of progress-bars with themes! If you want different progress bars
5 | without messing with themes then you should probably edit this file!
6 | Orientation of progress bars is again handled by themes which can make use
7 | of the style parameter that's passed in the last argument!
8 | ]]
9 |
10 | local LIB_PATH = (...):
11 | match("^(.+)%.[^%.]+"):
12 | match("^(.+)%.[^%.]+")
13 |
14 | local uiState=require(LIB_PATH..'.Core.UIState')
15 | local theme=require(LIB_PATH..'.Core.theme')
16 | local util=require(LIB_PATH..'.Core.util')
17 | local core=require(LIB_PATH..'.Core')
18 | local DrawCommands=require(LIB_PATH..'.Core.drawCommands')
19 |
20 | local function ProgressBar(progressBar,x,y,w,h)
21 | if core.idle then return end
22 | x,y,w,h=util.getProgressBarParams(progressBar,x,y,w,h)
23 | progressBar._timer=progressBar._timer or 0
24 | progressBar._timer=progressBar._timer+uiState.dt
25 | --By default the progress is linear and lasts for 5 seconds!
26 | progressBar.update=progressBar.update or function(dt) return dt/5 end
27 | progressBar.value=progressBar.value or 0
28 | if progressBar.value<1 then
29 | local updateValue=progressBar.update(uiState.dt,progressBar._timer)
30 | progressBar.value=math.min(1,progressBar.value+updateValue)
31 | else
32 | progressBar._timer=0
33 | end
34 | DrawCommands.registerCommand(function()
35 | theme.drawProgressBar(progressBar.text,progressBar.value,x,y,w,h,progressBar.style)
36 | end)
37 | end
38 |
39 | return ProgressBar
--------------------------------------------------------------------------------
/src/init.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | lovely-imgui V. 1.0 (beta)
3 | Author: Neer (https://github.com/YoungNeer/lovely-imgui)
4 | ]]
5 |
6 | local LIB_PATH=(...)
7 |
8 | local imgui=require(LIB_PATH..'.Core')
9 |
10 | imgui.label=require(LIB_PATH..'.Widgets.Label')
11 | imgui.button=require(LIB_PATH..'.Widgets.Button')
12 | imgui.tooltip=require(LIB_PATH..'.Widgets.Tooltip')
13 | imgui.checkBox=require(LIB_PATH..'.Widgets.CheckBox')
14 | imgui.radioButton=require(LIB_PATH..'.Widgets.RadioButton')
15 | imgui.progressBar=require(LIB_PATH..'.Widgets.ProgressBar')
16 | imgui.image=require(LIB_PATH..'.Widgets.Image')
17 | imgui.imageButton=require(LIB_PATH..'.Widgets.ImageButton')
18 | imgui.scaler=require(LIB_PATH..'.Widgets.Scaler')
19 | imgui.slider=require(LIB_PATH..'.Widgets.Slider')
20 | imgui.stepper=require(LIB_PATH..'.Widgets.Stepper')
21 | imgui.textEntry=require(LIB_PATH..'.Widgets.TextEntry')
22 | imgui.canvas=imgui.image --you can also draw canvas!
23 | imgui.beginMenuBar=require(LIB_PATH..'.Widgets.MenuBar')[1]
24 | imgui.endMenuBar=require(LIB_PATH..'.Widgets.MenuBar')[2]
25 | imgui.beginMenu=require(LIB_PATH..'.Widgets.Menu')[1]
26 | imgui.endMenu=require(LIB_PATH..'.Widgets.Menu')[2]
27 |
28 | -- assetLoader=require(LIB_PATH..'.Core.assetLoader')
29 | -- require(LIB_PATH..'.Themes.Light')
30 |
31 | love.draw = love.draw or imgui.draw
32 | love.update = love.update or imgui.update
33 | love.mousepressed = love.mousepressed or imgui.mousepressed
34 | love.keypressed = love.keypressed or imgui.keypressed
35 | love.mousereleased = love.mousereleased or imgui.mousereleased
36 | love.keyreleased = love.keyreleased or imgui.keyreleased
37 | love.mousemoved = love.mousemoved or imgui.mousemoved
38 | love.resize = love.resize or imgui.resize
39 | love.textinput = love.textinput or imgui.textinput
40 |
41 | return imgui
42 |
--------------------------------------------------------------------------------
/src/Widgets/RadioButton.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | This draws a radio-button (with/without label) at a relative/absolute position
3 | ]]
4 |
5 | local LIB_PATH = (...):
6 | match("^(.+)%.[^%.]+"):
7 | match("^(.+)%.[^%.]+")
8 |
9 | local uiState=require(LIB_PATH..'.Core.UIState')
10 | local util=require(LIB_PATH..'.Core.util')
11 | local theme=require(LIB_PATH..'.Core.theme')
12 | local core=require(LIB_PATH..'.Core')
13 | local DrawCommands=require(LIB_PATH..'.Core.drawCommands')
14 |
15 | local function RadioButton(rbtn,text,x,y,w,h)
16 | if core.idle then return end
17 | local id=core.genID()
18 | --TODO: Remove rbtn from getRadioParams for possible performance optimization!
19 | rbtn,text,x,y,w,h=util.getRadioButtonParams(rbtn,text,x,y,w,h)
20 | --For the check-box and the label!!
21 | core.updateWidget(
22 | id,
23 | util.mouseOver(x,y,w,h) or
24 | (text and
25 | util.mouseOver(
26 | x+w+15,y,theme.getFontSize('radiobutton',text),h
27 | )
28 | )
29 | )
30 | if uiState.hotItem==id then
31 | if uiState.activeItem==id then
32 | DrawCommands.registerCommand(function()
33 | theme.drawRadioButtonPressed(rbtn.active,text,x,y,w,h)
34 | end)
35 | else
36 | DrawCommands.registerCommand(function()
37 | theme.drawRadioButtonHover(rbtn.active,text,x,y,w,h)
38 | end)
39 | end
40 | else
41 | DrawCommands.registerCommand(function()
42 | theme.drawRadioButtonNormal(rbtn.active,text,x,y,w,h)
43 | end)
44 | end
45 | --TODO: Make other radio buttons return 0 and this return 1
46 | if uiState.lastActiveItem==id then
47 | --If the last thing that was clicked was this radio-button
48 | rbtn.active=true
49 | if rbtn.group then
50 | for i=1,#rbtn.group do
51 | if rbtn.group[i]~=rbtn then
52 | rbtn.group[i].active=nil
53 | end
54 | end
55 | end
56 | uiState.lastActiveItem=nil
57 | return true
58 | end
59 | end
60 |
61 | return RadioButton
--------------------------------------------------------------------------------
/src/Widgets/Stepper.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | Stepper is basically a label+two buttons that may-be top-and-down
3 | or left-and-right depending on direction!
4 | ]]
5 |
6 |
7 | --[[
8 | WORK IN PROGRESS: Wanna help me out :>
9 | ]]
10 |
11 |
12 | local LIB_PATH = (...):
13 | match("^(.+)%.[^%.]+"):
14 | match("^(.+)%.[^%.]+")
15 |
16 | local uiState=require(LIB_PATH..'.Core.UIState')
17 | local util=require(LIB_PATH..'.Core.util')
18 | local param=require(LIB_PATH..'.Core.param')
19 | local theme=require(LIB_PATH..'.Core.theme')
20 | local core=require(LIB_PATH..'.Core')
21 | local DrawCommands=require(LIB_PATH..'.Core.drawCommands')
22 |
23 | local function Stepper(stepper,x,y,w,h)
24 | if core.idle then return end
25 | local id=core.genID()
26 | x,y,w,h=util.getStepperParams(stepper,x,y,w,h)
27 | local lx,ly,lw,lh=param.getStepperLeftButton(stepper,x,y,w,h)
28 | local rx,ry,rw,rh=param.getStepperRightButton(stepper,x,y,w,h)
29 | core.updateWidget(
30 | id,
31 | util.mouseOver(lx,ly,lw,lh) or util.mouseOver(rx,ry,rw,rh)
32 | )
33 | local updated
34 | if core.isMouseReleased() then
35 | if util.mouseOver(lx,ly,lw,lh) then
36 | stepper.active=util.warp(stepper.active-1,1,#stepper.list)
37 | updated=true
38 | elseif util.mouseOver(rx,ry,rw,rh) then
39 | stepper.active=util.warp(stepper.active+1,1,#stepper.list)
40 | updated=true
41 | end
42 |
43 | end
44 | if uiState.hotItem==id then
45 | if uiState.activeItem==id then
46 | DrawCommands.registerCommand(function()
47 | theme.drawStepperPressed(stepper,util.mouseOver(lx,ly,lw,lh),x,y,w,h)
48 | end)
49 | else
50 | DrawCommands.registerCommand(function()
51 | theme.drawStepperHover(stepper,util.mouseOver(lx,ly,lw,lh),x,y,w,h)
52 | end)
53 | end
54 | else
55 | DrawCommands.registerCommand(function()
56 | theme.drawStepperNormal(stepper,x,y,w,h)
57 | end)
58 | end
59 |
60 | return updated
61 | end
62 |
63 | return Stepper
--------------------------------------------------------------------------------
/src/Widgets/Slider.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | A Slider is slightly more complicated variant of slider!
3 | It has increments and a "thumb" and by default it doesn't have
4 | a progress bar but themes can go nasty and implement it!
5 | Unlike Scalers, Sliders do not have to worry about orientation!
6 | ]]
7 |
8 | local LIB_PATH = (...):
9 | match("^(.+)%.[^%.]+"):
10 | match("^(.+)%.[^%.]+")
11 |
12 | local uiState=require(LIB_PATH..'.Core.UIState')
13 | local util=require(LIB_PATH..'.Core.util')
14 | local theme=require(LIB_PATH..'.Core.theme')
15 | local core=require(LIB_PATH..'.Core')
16 | local DrawCommands=require(LIB_PATH..'.Core.drawCommands')
17 |
18 | local function Slider(slider,x,y,w,h)
19 | if core.idle then return end
20 | local fraction,sx,sy,sw,sh --thumb dimenions and position
21 | local id=core.genID()
22 | fraction,x,y,w,h,sx,sy,sw,sh=util.getSliderParams(slider,x,y,w,h)
23 |
24 | core.pianoMode=not core.pianoMode
25 | core.updateWidget(id,util.mouseOver(sx,sy,sw,sh)) --remove 's' if you want to disable "thumb only"!
26 | core.pianoMode=not core.pianoMode
27 |
28 | if uiState.activeItem==id then
29 | if slider.vertical then
30 | fraction = math.min(1, math.max(0, (uiState.mouseY - y) / h))
31 | else
32 | fraction = math.min(1, math.max(0, (uiState.mouseX - x) / w))
33 | end
34 | DrawCommands.registerCommand(function()
35 | theme.drawSliderPressed(fraction,slider.vertical,x,y,w,h,slider.style)
36 | end)
37 | local v = fraction * (slider.max - slider.min) + slider.min
38 | if v ~= slider.value then
39 | slider.value = v
40 | return true
41 | end
42 | elseif uiState.hotItem==id then
43 | DrawCommands.registerCommand(function()
44 | theme.drawSliderHover(fraction,slider.vertical,x,y,w,h,slider.style)
45 | end)
46 | else
47 | DrawCommands.registerCommand(function()
48 | theme.drawSliderNormal(fraction,slider.vertical,x,y,w,h,slider.style)
49 | end)
50 | end
51 | end
52 |
53 | return Slider
--------------------------------------------------------------------------------
/src/Widgets/Window.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | Even if a program doesn't have any widget it'd still have
3 | a window!!
4 | Window can refer to the program window or the user-created
5 | window (also called frame!)
6 | ]]
7 |
8 |
9 | --The first element in the stack will always be root window!
10 | local stack={
11 | --The Window will house all the widgets
12 | {x=0,y=0,widgets={}}
13 | }
14 | stack[1].width,stack[1].height=love.graphics.getDimensions()
15 |
16 | local Window={stack=stack}
17 |
18 | --Add menu-bar to the window
19 | Window.addMenuBar=function()
20 | stack[#stack].menuw=5 --think of this as padding!
21 | end
22 |
23 | --Add menus to the window
24 | Window.addMenu=function(w)
25 | -- table.insert(stack[#stack].menu)
26 | stack[#stack].menuw=stack[#stack].menuw+w
27 | end
28 |
29 | --Get the width of all the menus (if that makes sense)
30 | Window.getMenuWidth=function()
31 | return stack[#stack].menuw
32 | end
33 |
34 | --Get the top-left most point (origin) of the window!
35 | Window.getTopLeft=function()
36 | return stack[#stack].x,stack[#stack].y
37 | end
38 |
39 | --Get the dimensions of the window!
40 | Window.getDimensions=function()
41 | return stack[#stack].width,stack[#stack].height
42 | end
43 |
44 | --Gets the bottom-right most point of the window
45 | Window.getBottomRight=function()
46 | local w,h=Window.getDimensions()
47 | return stack[#stack].x+w,stack[#stack].y+h
48 | end
49 |
50 | --Gets the center of the window!
51 | Window.getCenter=function()
52 | local w,h=Window.getDimensions()
53 | return w/2,h/2
54 | end
55 |
56 | Window.getOrigin=Window.getTopLeft
57 | Window.getCenterX=Window.getCenter
58 | Window.getCenterY=function() return select(2,Window.getCenter()) end
59 | Window.getTop=function() return select(2,Window.getTopLeft()) end
60 | Window.getLeft=Window.getTopLeft
61 | Window.getBottom=function() return select(2,Window.getBottomRight()) end
62 | Window.getRight=Window.getBottomRight
63 |
64 |
65 | return Window
--------------------------------------------------------------------------------
/src/Core/theme.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | How should a button be rendered when it's hovered, etc?
3 | All of this done by the (current) theme!
4 | ]]
5 |
6 | local LIB_PATH=(...):
7 | match("^(.+)%.[^%.]+"):
8 | match("^(.+)%.[^%.]+")
9 |
10 | local theme={
11 | current,
12 | list={}
13 | }
14 |
15 | theme.list['default']=require(LIB_PATH..'.Themes.Primitive')
16 |
17 | --[[
18 | Every widget will have an outline that
19 | depending on theme may or may not get drawn!
20 | ]]
21 | local funcs={
22 | 'update','getFontSize','drawButtonNormal','drawButtonHover',
23 | 'drawButtonPressed','drawCheckBoxNormal',
24 | 'drawCheckBoxHover','drawCheckBoxPressed','drawRadioButtonNormal',
25 | 'drawRadioButtonHover','drawRadioButtonPressed','drawScalerNormal',
26 | 'drawScalerHover','drawScalerPressed',
27 | 'drawLabel','drawTooltip','drawProgressBar','drawMenuBar','drawMenuNormal',
28 | 'drawMenuHover','drawSliderNormal','drawSliderHover','drawSliderPressed',
29 | 'drawTextEntryNormal','drawTextEntryHover','drawTextEntryPressed',
30 | 'drawStepperNormal','drawStepperHover','drawStepperPressed',
31 | 'drawOutline',
32 | }
33 |
34 | theme.set=function(themeName)
35 | theme.current=theme.list[themeName]
36 | --So rendering the theme will simply render the current theme!
37 | for _,func in ipairs(funcs) do
38 | theme[func]=theme.current[func]
39 | end
40 | end
41 |
42 | theme.set('default')
43 |
44 | --[[
45 | Why theme.update(dt)???
46 | You know that themes not just affect appearance but also performance!
47 | And themes can just change the "look" but also the "feel" which
48 | makes them so similar to Java's LAF! With update, one can do tweening
49 | for a widget transition! (button-hover,etc!)
50 |
51 | Why onActivate and onDeactivate?
52 | So that themes can turn on/off idle-mode, piano-mode and canvas-rendering
53 | (eg. canvas-rendering is on by default and is good for performance but is
54 | sometimes bad for appearance hence a theme may disable it and enable it
55 | when it is switched!!)
56 | ]]
57 |
58 | return theme
--------------------------------------------------------------------------------
/src/Widgets/ImageButton.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | This draws the Image at a relative/absolute position
3 | ]]
4 |
5 | local LIB_PATH = (...):
6 | match("^(.+)%.[^%.]+"):
7 | match("^(.+)%.[^%.]+")
8 |
9 | local uiState=require(LIB_PATH..'.Core.UIState')
10 | local util=require(LIB_PATH..'.Core.util')
11 | local core=require(LIB_PATH..'.Core')
12 | local DrawCommands=require(LIB_PATH..'.Core.drawCommands')
13 |
14 | local function ImageButton(img,x,y,w,h)
15 | if core.idle then return end
16 |
17 | local id=core.genID()
18 | x,y,w,h=util.getImageButtonParams(img,x,y,w,h)
19 |
20 | local imgW,imgH=img.image:getDimensions()
21 | local cond=util.mouseOver(x-w/2,y-h/2,w,h)
22 | if cond and img.precise then
23 | --If mouse is in the bounding-box and precise-mask is on then
24 | local imageData=img.data
25 | end
26 | core.updateWidget(id,cond)
27 |
28 | if uiState.hotItem==id then
29 | if uiState.activeItem==id then
30 | DrawCommands.registerCommand(function()
31 | love.graphics.setColor(1,1,1)
32 | if img.active then love.graphics.setColor(unpack(img.active)) end
33 | love.graphics.draw(img.image,x,y,img.rotation,w/imgW,h/imgH,imgW/2,imgH/2)
34 | end)
35 | else
36 | DrawCommands.registerCommand(function()
37 | love.graphics.setColor(1,1,1)
38 | if img.hover then love.graphics.setColor(unpack(img.hover)) end
39 | love.graphics.draw(img.image,x,y,img.rotation,w/imgW,h/imgH,imgW/2,imgH/2)
40 | end)
41 | end
42 | else
43 | DrawCommands.registerCommand(function()
44 | love.graphics.setColor(1,1,1)
45 | if img.default then love.graphics.setColor(unpack(img.default)) end
46 | love.graphics.draw(img.image,x,y,img.rotation,w/imgW,h/imgH,imgW/2,imgH/2)
47 | end)
48 | end
49 |
50 | -- love.graphics.rectangle('line',x-w/2,y-h/2,w,h)
51 |
52 |
53 | DrawCommands.registerCommand(function()
54 | love.graphics.setColor(1,1,1)
55 |
56 |
57 | end)
58 |
59 | return (uiState.hotItem==id and uiState.activeItem==id) or (
60 | uiState.lastActiveItem==id and uiState.mouseUp
61 | )
62 | end
63 |
64 | return ImageButton
--------------------------------------------------------------------------------
/src/Widgets/Scaler.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | This draws the Scaler at a relative/absolute position!
3 | A Scaler is different than a slider in that it doesn't have
4 | a "thumb"! It also doesn't have to worry about slide-increments
5 | and stuff like that!
6 | Also a scaler have *orientation* instead of *direction*!!
7 | ]]
8 |
9 | local LIB_PATH = (...):
10 | match("^(.+)%.[^%.]+"):
11 | match("^(.+)%.[^%.]+")
12 |
13 | local uiState=require(LIB_PATH..'.Core.UIState')
14 | local util=require(LIB_PATH..'.Core.util')
15 | local theme=require(LIB_PATH..'.Core.theme')
16 | local core=require(LIB_PATH..'.Core')
17 | local DrawCommands=require(LIB_PATH..'.Core.drawCommands')
18 |
19 | local function Scaler(scaler,x,y,w,h)
20 | if core.idle then return end
21 | local id=core.genID()
22 |
23 | x,y,w,h=util.getScalerParams(scaler,x,y,w,h)
24 |
25 | --Normalize the value of the scaler!
26 | local fraction = (scaler.value - scaler.min) / (scaler.max - scaler.min)
27 | core.pianoMode=not core.pianoMode
28 | core.updateWidget(id,util.mouseOver(x,y,w,h))
29 | core.pianoMode=not core.pianoMode
30 |
31 | if uiState.activeItem==id then
32 | if scaler.orientation=='t-b' then
33 | fraction = math.min(1, math.max(0, (uiState.mouseY - y) / h))
34 | elseif scaler.orientation=='b-t' then
35 | fraction = math.min(1, math.max(0, (y+h-uiState.mouseY) / h))
36 | elseif scaler.orientation=='l-r' then
37 | fraction = math.min(1, math.max(0, (x+w-uiState.mouseX) / w))
38 | else
39 | fraction = math.min(1, math.max(0, (uiState.mouseX - x) / w))
40 | end
41 | DrawCommands.registerCommand(function()
42 | theme.drawScalerPressed(fraction,scaler.orientation,x,y,w,h,scaler.style)
43 | end)
44 | local v = fraction * (scaler.max - scaler.min) + scaler.min
45 | if v ~= scaler.value then
46 | scaler.value = v
47 | return true
48 | end
49 | elseif uiState.hotItem==id then
50 | DrawCommands.registerCommand(function()
51 | theme.drawScalerHover(fraction,scaler.orientation,x,y,w,h,scaler.style)
52 | end)
53 | else
54 | DrawCommands.registerCommand(function()
55 | theme.drawScalerNormal(fraction,scaler.orientation,x,y,w,h,scaler.style)
56 | end)
57 | end
58 | end
59 |
60 | return Scaler
--------------------------------------------------------------------------------
/src/Core/mosaic.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | [DISCLAIMER: Stolen from Luigi! Credit:Airstruck ]
3 |
4 | Mosaic's main job is to cut slices which are then
5 | used when rendering a widget which is done by it as well!
6 | This module is also responsible for maintaining a img-cache
7 | ]]
8 |
9 | local Mosaic={}
10 |
11 | local imgCache = {}
12 | local sliceCache = {}
13 |
14 | function Mosaic.loadImage (path)
15 | if not imgCache[path] then
16 | imgCache[path] = love.graphics.newImage(path)
17 | end
18 | return imgCache[path]
19 | end
20 |
21 | local makeSlice = love.graphics.newQuad
22 | local drawSlice=love.graphics.draw
23 |
24 | function Mosaic.loadSlices (path)
25 | local slices = sliceCache[path]
26 |
27 | if not slices then
28 | slices = {}
29 | sliceCache[path] = slices
30 | local image = Mosaic.loadImage(path)
31 | local iw, ih = image:getWidth(), image:getHeight()
32 | local w, h = math.floor(iw / 3), math.floor(ih / 3)
33 |
34 | slices.image = image
35 | slices.width = w
36 | slices.height = h
37 |
38 | slices.topLeft = makeSlice(0, 0, w, h, iw, ih)
39 | slices.topCenter = makeSlice(w, 0, w, h, iw, ih)
40 | slices.topRight = makeSlice(iw - w, 0, w, h, iw, ih)
41 | slices.middleLeft = makeSlice(0, h, w, h, iw, ih)
42 | slices.middleCenter = makeSlice(w, h, w, h, iw, ih)
43 | slices.middleRight = makeSlice(iw - w, h, w, h, iw, ih)
44 | slices.bottomLeft = makeSlice(0, ih - h, w, h, iw, ih)
45 | slices.bottomCenter = makeSlice(w, ih - h, w, h, iw, ih)
46 | slices.bottomRight = makeSlice(iw - w, ih - h, w, h, iw, ih)
47 | end
48 | return slices
49 | end
50 |
51 | function Mosaic.draw(img,slices,x,y,w,h)
52 | local sw, sh = slices.width, slices.height
53 | local xs = (w - sw * 2) / sw -- x scale
54 | local ys = (h - sh * 2) / sh -- y scale
55 |
56 | drawSlice(img,slices.middleCenter, x + sw, y + sh, 0, xs, ys)
57 | drawSlice(img,slices.topCenter, x + sw, y, 0, xs, 1)
58 | drawSlice(img,slices.bottomCenter, x + sw, y + h - sh, 0, xs, 1)
59 | drawSlice(img,slices.middleLeft, x, y + sh, 0, 1, ys)
60 | drawSlice(img,slices.middleRight, x + w - sw, y + sh, 0, 1, ys)
61 | drawSlice(img,slices.topLeft, x, y)
62 | drawSlice(img,slices.topRight, x + w - sw, y)
63 | drawSlice(img,slices.bottomLeft, x, y + h - sh)
64 | drawSlice(img,slices.bottomRight, x + w - sw, y + h - sh)
65 | end
66 |
67 | return Mosaic
--------------------------------------------------------------------------------
/src/Core/drawCommands.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | We don't need to draw at every frame! That'd be stupid
3 | So to make the library "bloat-free", imgui draws to a
4 | canvas which only updates when an input happens!
5 | Now using a canvas which may result in loss of quality
6 | so to turn off this feature simply say `imgui.setCanvas(false)`
7 | anywhere before `imgui.draw()`!
8 | To explicity refresh canvas say - `imgui.refresh()`
9 | ]]
10 |
11 | local CORE_PATH = (...):match("^(.+)%.[^%.]+")
12 |
13 | --Command Format:
14 | -- function() theme.drawButtonNormal(t,x,y,w,h) end
15 | local commands={}
16 |
17 | local drawCommands={
18 | reverseTop=0, --needed for handling Z-index
19 | canvas=love.graphics.newCanvas(love.graphics.getDimensions()),
20 | directDraw=false,
21 | needsRefresh=true, --refresh canvas?
22 | autoRefresh=true, --automatically refresh canvas?
23 | commands=commands
24 | }
25 |
26 | local setCanvas=love.graphics.setCanvas
27 |
28 | drawCommands.draw=function()
29 | if drawCommands.directDraw or
30 | (drawCommands.autoRefresh and drawCommands.needsRefresh) then
31 | -- print('drawing')
32 | if drawCommands.needsRefresh and not drawCommands.directDraw then
33 | -- print('setting canvas')
34 | setCanvas(drawCommands.canvas)
35 | love.graphics.clear()
36 | end
37 | --Z-index is automatically handled before drawing!
38 | drawCommands.handleDepth()
39 | for i=1,#commands do
40 | commands[i]()
41 | end
42 | if drawCommands.needsRefresh and not drawCommands.directDraw then
43 | drawCommands.needsRefresh=false
44 | drawCommands.clear() --> TODO: Check if this is buggy
45 | setCanvas()
46 | end
47 | end
48 | if not drawCommands.directDraw then
49 | love.graphics.setColor(1,1,1)
50 | love.graphics.draw(drawCommands.canvas)
51 | end
52 | drawCommands.reverseTop=0
53 | end
54 |
55 | drawCommands.registerCommand=function (zindex,func)
56 | if not func then func,zindex=zindex,#commands+1 end
57 |
58 | if zindex==-1 then
59 | --We want this widget to have high depth!
60 | drawCommands.reverseTop=drawCommands.reverseTop-1
61 | commands[drawCommands.reverseTop]=func
62 | else
63 | table.insert(commands,zindex,func)
64 | end
65 | drawCommands.needsRefresh=true
66 | end
67 |
68 | --Move all the negative values to positive values!
69 | drawCommands.handleDepth=function()
70 | local i=-1
71 | while true do
72 | if not commands[i] then return end
73 | table.insert(commands,commands[i])
74 | commands[i]=nil
75 | i=i-1
76 | end
77 | end
78 |
79 | drawCommands.clear=function()
80 | if #commands>100 then
81 | commands={}
82 | else
83 | for i=1,#commands do table.remove(commands,i) end
84 | end
85 | end
86 |
87 | return drawCommands
--------------------------------------------------------------------------------
/src/Widgets/TextEntry.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | A TextEntry is similar to Java Swing TextField - it's single-line
3 | and when you hit enter it's done!
4 | ]]
5 |
6 |
7 | --[[
8 | WORK IN PROGRESS: Wanna help me out :>
9 | TODO: Auto-scrolling for text (_offset here means just that!)
10 | TODO: Add selection support!
11 | ]]
12 |
13 |
14 | local LIB_PATH = (...):
15 | match("^(.+)%.[^%.]+"):
16 | match("^(.+)%.[^%.]+")
17 |
18 | local uiState=require(LIB_PATH..'.Core.UIState')
19 | local util=require(LIB_PATH..'.Core.util')
20 | local theme=require(LIB_PATH..'.Core.theme')
21 | local core=require(LIB_PATH..'.Core')
22 | local DrawCommands=require(LIB_PATH..'.Core.drawCommands')
23 |
24 | local function split(str, pos) return str:sub(1, pos), str:sub(1+pos) end
25 |
26 | local function TextEntry(textEntry,x,y,w,h)
27 | if core.idle then return end
28 | local id=core.genID()
29 | x,y,w,h=util.getTextEntryParams(textEntry,x,y,w,h)
30 | core.updateWidget(id,util.mouseOver(x,y,w,h))
31 | if uiState.lastActiveItem==id then
32 | if uiState.keyChar then
33 | if not textEntry.limit or textEntry.text:len()
11 | A demo of LovelyImgui
12 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | ## Documentation
43 |
44 | Documentation will be live very soon once the alpha version is released! Curious souls can study the [examples](https://github.com/YoungNeer/lovely-imgui/tree/examples) to learn how the library works!
45 |
46 | ## Contribution
47 |
48 | Making a GUI is easy! Making a reusable GUI is hard and by hard I mean *really hard*! There's currently a lot of stuff that I can't figure out (such as menu-bars, combo-boxes, windows, etc) so definitely I need some help. (And by help I mean "contribution" just so you don't get the wrong idea :laughing:)
49 |
50 |