dfhack/library/lua/gui/dialogs.lua

268 lines
6.2 KiB
Lua

-- Some simple dialog screens
local _ENV = mkmodule('gui.dialogs')
local gui = require('gui')
local utils = require('utils')
local dscreen = dfhack.screen
MessageBox = defclass(MessageBox, gui.FramedScreen)
MessageBox.focus_path = 'MessageBox'
MessageBox.ATTRS{
frame_style = gui.GREY_LINE_FRAME,
-- new attrs
text = {},
on_accept = DEFAULT_NIL,
on_cancel = DEFAULT_NIL,
on_close = DEFAULT_NIL,
text_pen = DEFAULT_NIL,
}
function MessageBox:preinit(info)
if type(info.text) == 'string' then
info.text = utils.split_string(info.text, "\n")
end
end
function MessageBox:getWantedFrameSize()
local text = self.text
local w = #(self.frame_title or '') + 4
w = math.max(w, 20)
w = math.max(self.frame_width or w, w)
for _, l in ipairs(text) do
w = math.max(w, #l)
end
local h = #text+1
if h > 1 then
h = h+1
end
return w+2, #text+2
end
function MessageBox:onRenderBody(dc)
if #self.text > 0 then
dc:newline(1):pen(self.text_pen or COLOR_GREY)
for _, l in ipairs(self.text or {}) do
dc:string(l):newline(1)
end
end
if self.on_accept then
local fr = self.frame_rect
local dc2 = gui.Painter.new_xy(fr.x1+1,fr.y2+1,fr.x2-8,fr.y2+1)
dc2:key('LEAVESCREEN'):string('/'):key('MENU_CONFIRM')
end
end
function MessageBox:onDestroy()
if self.on_close then
self.on_close()
end
end
function MessageBox:onInput(keys)
if keys.MENU_CONFIRM then
self:dismiss()
if self.on_accept then
self.on_accept()
end
elseif keys.LEAVESCREEN or (keys.SELECT and not self.on_accept) then
self:dismiss()
if self.on_cancel then
self.on_cancel()
end
end
end
function showMessage(title, text, tcolor, on_close)
MessageBox{
frame_title = title,
text = text,
text_pen = tcolor,
on_close = on_close
}:show()
end
function showYesNoPrompt(title, text, tcolor, on_accept, on_cancel)
MessageBox{
frame_title = title,
text = text,
text_pen = tcolor,
on_accept = on_accept,
on_cancel = on_cancel,
}:show()
end
InputBox = defclass(InputBox, MessageBox)
InputBox.focus_path = 'InputBox'
InputBox.ATTRS{
input = '',
input_pen = DEFAULT_NIL,
on_input = DEFAULT_NIL,
}
function InputBox:preinit(info)
info.on_accept = nil
end
function InputBox:getWantedFrameSize()
local mw, mh = InputBox.super.getWantedFrameSize(self)
return mw, mh+2
end
function InputBox:onRenderBody(dc)
InputBox.super.onRenderBody(self, dc)
dc:newline(1)
dc:pen(self.input_pen or COLOR_LIGHTCYAN)
dc:fill(1,dc:localY(),dc.width-2,dc:localY())
local cursor = '_'
if math.floor(dfhack.getTickCount()/300) % 2 == 0 then
cursor = ' '
end
local txt = self.input .. cursor
if #txt > dc.width-2 then
txt = string.char(27)..string.sub(txt, #txt-dc.width+4)
end
dc:string(txt)
end
function InputBox:onInput(keys)
if keys.SELECT then
self:dismiss()
if self.on_input then
self.on_input(self.input)
end
elseif keys.LEAVESCREEN then
self:dismiss()
if self.on_cancel then
self.on_cancel()
end
elseif keys._STRING then
if keys._STRING == 0 then
self.input = string.sub(self.input, 1, #self.input-1)
else
self.input = self.input .. string.char(keys._STRING)
end
end
end
function showInputPrompt(title, text, tcolor, input, on_input, on_cancel, min_width)
InputBox{
frame_title = title,
text = text,
text_pen = tcolor,
input = input,
on_input = on_input,
on_cancel = on_cancel,
frame_width = min_width,
}:show()
end
ListBox = defclass(ListBox, MessageBox)
ListBox.focus_path = 'ListBox'
ListBox.ATTRS{
selection = 0,
choices = {},
select_pen = DEFAULT_NIL,
on_input = DEFAULT_NIL
}
function InputBox:preinit(info)
info.on_accept = nil
end
function ListBox:init(info)
self.page_top = 0
end
function ListBox:getWantedFrameSize()
local mw, mh = ListBox.super.getWantedFrameSize(self)
return mw, mh+#self.choices
end
function ListBox:onRenderBody(dc)
ListBox.super.onRenderBody(self, dc)
dc:newline(1)
if self.selection>dc.height-3 then
self.page_top=self.selection-(dc.height-3)
elseif self.selection<self.page_top and self.selection >0 then
self.page_top=self.selection-1
end
for i,entry in ipairs(self.choices) do
if type(entry)=="table" then
entry=entry[1]
end
if i>self.page_top then
if i == self.selection then
dc:pen(self.select_pen or COLOR_LIGHTCYAN)
else
dc:pen(self.text_pen or COLOR_GREY)
end
dc:string(entry)
dc:newline(1)
end
end
end
function ListBox:moveCursor(delta)
local newsel=self.selection+delta
if #self.choices ~=0 then
if newsel<1 or newsel>#self.choices then
newsel=newsel % #self.choices
end
end
self.selection=newsel
end
function ListBox:onInput(keys)
if keys.SELECT then
self:dismiss()
local choice=self.choices[self.selection]
if self.on_input then
self.on_input(self.selection,choice)
end
if choice and choice[2] then
choice[2](choice,self.selection) -- maybe reverse the arguments?
end
elseif keys.LEAVESCREEN then
self:dismiss()
if self.on_cancel then
self.on_cancel()
end
elseif keys.CURSOR_UP then
self:moveCursor(-1)
elseif keys.CURSOR_DOWN then
self:moveCursor(1)
elseif keys.CURSOR_UP_FAST then
self:moveCursor(-10)
elseif keys.CURSOR_DOWN_FAST then
self:moveCursor(10)
end
end
function showListPrompt(title, text, tcolor, choices, on_input, on_cancel, min_width)
ListBox{
frame_title = title,
text = text,
text_pen = tcolor,
choices = choices,
on_input = on_input,
on_cancel = on_cancel,
frame_width = min_width,
}:show()
end
return _ENV