2022-07-23 15:36:21 +00:00
-- Copyright (c) 2019-2022 David Vogel
2019-10-22 20:48:43 +00:00
--
-- This software is released under the MIT License.
-- https://opensource.org/licenses/MIT
2022-07-27 23:48:49 +00:00
-----------------------
-- Load global stuff --
-----------------------
-- TODO: Wrap Noita utilities and wrap them into a table: https://stackoverflow.com/questions/9540732/loadfile-without-polluting-global-environment
2022-08-27 12:32:01 +00:00
require ( " utilities " ) -- Loads Noita's utilities from `data/scripts/lib/utilities.lua`.
2022-07-27 23:48:49 +00:00
2022-07-23 18:43:04 +00:00
--------------------------
-- Load library modules --
--------------------------
2022-07-23 15:36:21 +00:00
----------
-- Code --
----------
2022-07-29 20:49:12 +00:00
---Splits the given string to fit inside maxLength.
---@param gui userdata
---@param text string
---@param maxLength number -- In UI pixels.
---@return string[]
local function splitString ( gui , text , maxLength )
local splitted = { }
local first , rest = text , " "
while first : len ( ) > 0 do
local width , height = GuiGetTextDimensions ( gui , first , 1 , 2 )
if width <= maxLength then
table.insert ( splitted , first )
first , rest = rest , " "
else
first , rest = first : sub ( 1 , - 2 ) , first : sub ( - 1 , - 1 ) .. rest
end
end
return splitted
end
2022-07-27 23:48:49 +00:00
---Returns unique IDs for the widgets.
---`_ResetID` has to be called every time before the UI is rebuilt.
---@return integer
function UI : _GenID ( )
self.CurrentID = ( self.CurrentID or 0 ) + 1
return self.CurrentID
2022-07-26 22:06:09 +00:00
end
2019-10-25 15:29:52 +00:00
2022-07-27 23:48:49 +00:00
function UI : _ResetID ( )
self.CurrentID = nil
end
2022-07-26 22:06:09 +00:00
2022-07-28 08:07:47 +00:00
---Stops the UI from drawing for the next few frames.
---@param frames integer
function UI : SuspendDrawing ( frames )
self.suspendFrames = math.max ( self.suspendFrames or 0 , frames )
end
2022-07-27 23:48:49 +00:00
function UI : _DrawToolbar ( )
local gui = self.gui
GuiZSet ( gui , 0 )
GuiLayoutBeginHorizontal ( gui , 2 , 2 , true , 2 , 2 )
2022-07-28 16:54:44 +00:00
local captureMode = tostring ( ModSettingGet ( " noita-mapcap.capture-mode " ) )
2022-07-27 23:48:49 +00:00
if Capture.MapCapturingCtx : IsRunning ( ) then
local clicked , clickedRight = GuiImageButton ( gui , self : _GenID ( ) , 0 , 0 , " " , " mods/noita-mapcap/files/ui-gfx/stop-16x16.png " )
GuiTooltip ( gui , " Stop capture " , " Stop the capturing process. \n \n Right click: Reset any modifications that this mod has done to Noita. " )
if clicked then Capture : StopCapturing ( ) end
if clickedRight then Message : ShowResetNoitaSettings ( ) end
else
local clicked , clickedRight = GuiImageButton ( gui , self : _GenID ( ) , 0 , 0 , " " , " mods/noita-mapcap/files/ui-gfx/record-16x16.png " )
2022-07-28 16:54:44 +00:00
GuiTooltip ( gui , string.format ( " Start %s capture " , captureMode ) , " Go into mod settings to configure the capturing process. \n \n Right click: Reset any modifications that this mod has done to Noita. " )
2022-07-27 23:48:49 +00:00
if clicked then Capture : StartCapturing ( ) end
if clickedRight then Message : ShowResetNoitaSettings ( ) end
end
local clicked = GuiImageButton ( gui , self : _GenID ( ) , 0 , 0 , " " , " mods/noita-mapcap/files/ui-gfx/open-output-16x16.png " )
GuiTooltip ( gui , " Open output directory " , " Reveals the output directory in your file browser. " )
if clicked then os.execute ( " start . \\ mods \\ noita-mapcap \\ output \\ " ) end
2022-07-26 22:06:09 +00:00
2022-07-30 17:10:48 +00:00
local clicked = GuiImageButton ( gui , self : _GenID ( ) , 0 , 0 , " " , " mods/noita-mapcap/files/ui-gfx/stitch-16x16.png " )
GuiTooltip ( gui , " Open stitch directory " , " Reveals the directory of the stitching tool in your file browser. " )
if clicked then os.execute ( " start . \\ mods \\ noita-mapcap \\ bin \\ stitch \\ " ) end
2022-07-26 22:06:09 +00:00
GuiLayoutEnd ( gui )
2022-07-27 23:48:49 +00:00
end
2022-07-26 22:06:09 +00:00
2022-07-27 23:48:49 +00:00
function UI : _DrawMessages ( messages )
local gui = self.gui
2022-07-23 15:36:21 +00:00
2022-07-27 23:48:49 +00:00
-- Abort if there is no messages list.
if not messages then return end
2022-07-29 17:42:44 +00:00
2022-07-29 20:49:12 +00:00
local screenWidth , screenHeight = GuiGetScreenDimensions ( gui )
2022-07-27 23:48:49 +00:00
GuiZSet ( gui , 0 )
2019-10-22 20:48:43 +00:00
2022-07-27 23:48:49 +00:00
-- Unfortunately you can't stack multiple layout containers with the same direction.
-- So keep track of the y position manually.
local posY = 60
for key , message in pairs ( messages ) do
GuiZSet ( gui , - 10 )
GuiBeginAutoBox ( gui )
2019-11-01 01:40:21 +00:00
2022-07-27 23:48:49 +00:00
GuiLayoutBeginHorizontal ( gui , 27 , posY , true , 5 , 0 ) posY = posY + 20
2019-11-01 01:40:21 +00:00
2022-07-27 23:48:49 +00:00
if message.Type == " warning " or message.Type == " error " then
GuiImage ( gui , self : _GenID ( ) , 0 , 0 , " mods/noita-mapcap/files/ui-gfx/warning-16x16.png " , 1 , 1 , 0 , 0 , 0 , " " )
elseif message.Type == " hint " or message.Type == " info " then
GuiImage ( gui , self : _GenID ( ) , 0 , 0 , " mods/noita-mapcap/files/ui-gfx/hint-16x16.png " , 1 , 1 , 0 , 0 , 0 , " " )
else
GuiImage ( gui , self : _GenID ( ) , 0 , 0 , " mods/noita-mapcap/files/ui-gfx/hint-16x16.png " , 1 , 1 , 0 , 0 , 0 , " " )
end
2022-07-28 09:55:34 +00:00
2022-07-27 23:48:49 +00:00
GuiLayoutBeginVertical ( gui , 0 , 0 , false , 0 , 0 )
if type ( message.Lines ) == " table " then
for _ , line in ipairs ( message.Lines ) do
2022-07-28 09:55:34 +00:00
for splitLine in tostring ( line ) : gmatch ( " [^ \n ]+ " ) do
2022-07-29 20:49:12 +00:00
for _ , splitLine in ipairs ( splitString ( gui , splitLine , screenWidth - 80 ) ) do
GuiText ( gui , 0 , 0 , splitLine ) posY = posY + 11
end
2022-07-28 09:55:34 +00:00
end
2022-07-27 23:48:49 +00:00
end
end
if type ( message.Actions ) == " table " then
posY = posY + 11
for _ , action in ipairs ( message.Actions ) do
local clicked = GuiButton ( gui , self : _GenID ( ) , 0 , 11 , " > " .. action.Name .. " < " ) posY = posY + 11
if action.Hint or action.HintDesc then
GuiTooltip ( gui , action.Hint or " " , action.HintDesc or " " )
end
if clicked then
local ok , err = pcall ( action.Callback )
if not ok then
Message : ShowRuntimeError ( " MessageAction " , " Message action error: " , err )
2019-11-01 01:40:21 +00:00
end
2022-07-27 23:48:49 +00:00
messages [ key ] = nil
2019-11-01 01:40:21 +00:00
end
2020-06-01 20:39:00 +00:00
end
2022-07-27 23:48:49 +00:00
end
GuiLayoutEnd ( gui )
2019-11-01 01:40:21 +00:00
2022-08-27 12:32:01 +00:00
if not message.AutoClose then
2022-07-29 18:44:36 +00:00
local clicked = GuiImageButton ( gui , self : _GenID ( ) , 5 , 0 , " " , " mods/noita-mapcap/files/ui-gfx/dismiss-8x8.png " )
--GuiTooltip(gui, "Dismiss message", "")
if clicked then messages [ key ] = nil end
end
2019-11-01 01:40:21 +00:00
2022-07-27 23:48:49 +00:00
GuiLayoutEnd ( gui )
2019-11-01 01:40:21 +00:00
2022-07-27 23:48:49 +00:00
GuiZSet ( gui , - 9 )
GuiEndAutoBoxNinePiece ( gui , 5 , 0 , 0 , false , 0 , " data/ui_gfx/decorations/9piece0_gray.png " , " data/ui_gfx/decorations/9piece0_gray.png " )
end
end
2020-06-01 20:39:00 +00:00
2022-07-29 17:42:44 +00:00
function UI : _DrawProgress ( )
local gui = self.gui
-- Check if there is progress to show.
local state = Capture.MapCapturingCtx : GetState ( )
if not state then return end
local factor
if state.Current and state.Max > 0 then
factor = state.Current / state.Max
end
local width , height = GuiGetScreenDimensions ( gui )
local widthHalf , heightHalf = math.floor ( width / 2 ) , math.floor ( height / 2 )
GuiZSet ( gui , - 20 )
local barWidth = width - 60
local y = heightHalf
if factor then
GuiImageNinePiece ( gui , self : _GenID ( ) , 30 , y , barWidth , 9 , 1 , " mods/noita-mapcap/files/ui-gfx/progress-a.png " , " mods/noita-mapcap/files/ui-gfx/progress-a.png " )
GuiImageNinePiece ( gui , self : _GenID ( ) , 30 , y , math.floor ( barWidth * factor + 0.5 ) , 9 , 1 , " mods/noita-mapcap/files/ui-gfx/progress-b.png " , " mods/noita-mapcap/files/ui-gfx/progress-b.png " )
GuiOptionsAddForNextWidget ( gui , GUI_OPTION.Align_HorizontalCenter )
GuiText ( gui , widthHalf , y , string.format ( " %d of %d (%.1f%%) " , state.Current , state.Max , factor * 100 ) ) y = y + 11
y = y + 15
end
if state.WaitFrames then
GuiOptionsAddForNextWidget ( gui , GUI_OPTION.Align_HorizontalCenter )
GuiText ( gui , widthHalf , y , string.format ( " Waiting for %d frames. " , state.WaitFrames ) ) y = y + 11
end
end
2022-07-27 23:48:49 +00:00
function UI : Draw ( )
self.gui = self.gui or GuiCreate ( )
local gui = self.gui
-- Skip drawing if we are asked to do so.
2022-08-27 12:32:01 +00:00
-- TODO: Find a way to suspend UI drawing, but still being able to receive events
2022-07-27 23:48:49 +00:00
if self.suspendFrames and self.suspendFrames > 0 then self.suspendFrames = self.suspendFrames - 1 return end
self.suspendFrames = nil
-- Reset ID generator.
self : _ResetID ( )
GuiStartFrame ( gui )
GuiIdPushString ( gui , " noita-mapcap " )
self : _DrawToolbar ( )
self : _DrawMessages ( Message.List )
2022-07-29 17:42:44 +00:00
self : _DrawProgress ( )
2022-07-27 23:48:49 +00:00
GuiIdPop ( gui )
2020-06-01 20:39:00 +00:00
end