From 57086ac56eb489abd0c7759aed084020edc71148 Mon Sep 17 00:00:00 2001
From: Alexander Gavrilov
Date: Wed, 5 Sep 2012 19:45:45 +0400
Subject: [PATCH] Add stock MessageBox and InputBox dialog screens for lua
scripts.
---
LUA_API.rst | 8 ++
Lua API.html | 6 ++
library/LuaApi.cpp | 2 +
library/LuaTools.cpp | 3 +
library/Process-darwin.cpp | 8 ++
library/Process-linux.cpp | 8 ++
library/Process-windows.cpp | 5 ++
library/include/MemAccess.h | 3 +
library/lua/gui.lua | 12 ++-
library/lua/gui/dialogs.lua | 175 ++++++++++++++++++++++++++++++++++++
library/lua/utils.lua | 13 +++
11 files changed, 241 insertions(+), 2 deletions(-)
create mode 100644 library/lua/gui/dialogs.lua
diff --git a/LUA_API.rst b/LUA_API.rst
index 22130efd6..799f623eb 100644
--- a/LUA_API.rst
+++ b/LUA_API.rst
@@ -553,6 +553,10 @@ Exception handling
Miscellaneous
-------------
+* ``dfhack.VERSION``
+
+ DFHack version string constant.
+
* ``dfhack.curry(func,args...)``, or ``curry(func,args...)``
Returns a closure that invokes the function with args combined
@@ -719,6 +723,10 @@ can be omitted.
Returns the dfhack directory path, i.e. ``".../df/hack/"``.
+* ``dfhack.getTickCount()``
+
+ Returns the tick count in ms, exactly as DF ui uses.
+
* ``dfhack.isWorldLoaded()``
Checks if the world is loaded.
diff --git a/Lua API.html b/Lua API.html
index f6f2d42b3..f05ee5511 100644
--- a/Lua API.html
+++ b/Lua API.html
@@ -846,6 +846,9 @@ following properties:
+dfhack.VERSION
+DFHack version string constant.
+
dfhack.curry(func,args...), or curry(func,args...)
Returns a closure that invokes the function with args combined
both from the curry call and the closure call itself. I.e.
@@ -985,6 +988,9 @@ can be omitted.
dfhack.getHackPath()
Returns the dfhack directory path, i.e. ".../df/hack/".
+dfhack.getTickCount()
+Returns the tick count in ms, exactly as DF ui uses.
+
dfhack.isWorldLoaded()
Checks if the world is loaded.
diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp
index 4e57b113f..1dcb001f1 100644
--- a/library/LuaApi.cpp
+++ b/library/LuaApi.cpp
@@ -728,6 +728,7 @@ static std::string getOSType()
}
static std::string getDFVersion() { return Core::getInstance().vinfo->getVersion(); }
+static uint32_t getTickCount() { return Core::getInstance().p->getTickCount(); }
static std::string getDFPath() { return Core::getInstance().p->getPath(); }
static std::string getHackPath() { return Core::getInstance().getHackPath(); }
@@ -739,6 +740,7 @@ static const LuaWrapper::FunctionReg dfhack_module[] = {
WRAP(getOSType),
WRAP(getDFVersion),
WRAP(getDFPath),
+ WRAP(getTickCount),
WRAP(getHackPath),
WRAP(isWorldLoaded),
WRAP(isMapLoaded),
diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp
index 9f0477538..a283d215c 100644
--- a/library/LuaTools.cpp
+++ b/library/LuaTools.cpp
@@ -1580,6 +1580,9 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_BASE_G_TOKEN);
lua_setfield(state, -2, "BASE_G");
+ lua_pushstring(state, DFHACK_VERSION);
+ lua_setfield(state, -2, "VERSION");
+
lua_pushboolean(state, IsCoreContext(state));
lua_setfield(state, -2, "is_core_context");
diff --git a/library/Process-darwin.cpp b/library/Process-darwin.cpp
index 5a97d9e00..3893cfc5f 100644
--- a/library/Process-darwin.cpp
+++ b/library/Process-darwin.cpp
@@ -27,6 +27,7 @@ distribution.
#include
#include
#include
+#include
#include
@@ -262,6 +263,13 @@ bool Process::getThreadIDs(vector & threads )
return true;
}
+uint32_t Process::getTickCount()
+{
+ struct timeval tp;
+ gettimeofday(&tp, NULL);
+ return (tp.tv_sec * 1000) + (tp.tv_usec / 1000);
+}
+
string Process::getPath()
{
char path[1024];
diff --git a/library/Process-linux.cpp b/library/Process-linux.cpp
index fe8647845..4a66470f9 100644
--- a/library/Process-linux.cpp
+++ b/library/Process-linux.cpp
@@ -27,6 +27,7 @@ distribution.
#include
#include
#include
+#include
#include
#include
@@ -192,6 +193,13 @@ bool Process::getThreadIDs(vector & threads )
return true;
}
+uint32_t Process::getTickCount()
+{
+ struct timeval tp;
+ gettimeofday(&tp, NULL);
+ return (tp.tv_sec * 1000) + (tp.tv_usec / 1000);
+}
+
string Process::getPath()
{
const char * cwd_name = "/proc/self/cwd";
diff --git a/library/Process-windows.cpp b/library/Process-windows.cpp
index 7eb6ff5f7..db58c4d33 100644
--- a/library/Process-windows.cpp
+++ b/library/Process-windows.cpp
@@ -410,6 +410,11 @@ string Process::doReadClassName (void * vptr)
return raw;
}
+uint32_t Process::getTickCount()
+{
+ return GetTickCount();
+}
+
string Process::getPath()
{
HMODULE hmod;
diff --git a/library/include/MemAccess.h b/library/include/MemAccess.h
index 0e5f618e2..a226018a6 100644
--- a/library/include/MemAccess.h
+++ b/library/include/MemAccess.h
@@ -281,6 +281,9 @@ namespace DFHack
/// get the DF Process FilePath
std::string getPath();
+ /// millisecond tick count, exactly as DF uses
+ uint32_t getTickCount();
+
/// modify permisions of memory range
bool setPermisions(const t_memrange & range,const t_memrange &trgrange);
diff --git a/library/lua/gui.lua b/library/lua/gui.lua
index 9e189ea13..23904c14f 100644
--- a/library/lua/gui.lua
+++ b/library/lua/gui.lua
@@ -94,6 +94,9 @@ function Painter:isValidPos()
end
function Painter:viewport(x,y,w,h)
+ if type(x) == 'table' then
+ x,y,w,h = x.x1, x.y1, x.width, x.height
+ end
local x1,y1 = self.x1+x, self.y1+y
local x2,y2 = x1+w-1, y1+h-1
local vp = {
@@ -353,11 +356,16 @@ local function hint_coord(gap,hint)
end
end
+function FramedScreen:getWantedFrameSize()
+ return self.frame_width, self.frame_height
+end
+
function FramedScreen:updateFrameSize()
local sw, sh = dscreen.getWindowSize()
local iw, ih = sw-2, sh-2
- local width = math.min(self.frame_width or iw, iw)
- local height = math.min(self.frame_height or ih, ih)
+ local fw, fh = self:getWantedFrameSize()
+ local width = math.min(fw or iw, iw)
+ local height = math.min(fh or ih, ih)
local gw, gh = iw-width, ih-height
local x1, y1 = hint_coord(gw,self.frame_xhint), hint_coord(gh,self.frame_yhint)
self.frame_rect = mkdims_wh(x1+1,y1+1,width,height)
diff --git a/library/lua/gui/dialogs.lua b/library/lua/gui/dialogs.lua
new file mode 100644
index 000000000..e6d30c970
--- /dev/null
+++ b/library/lua/gui/dialogs.lua
@@ -0,0 +1,175 @@
+-- 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.frame_style = gui.GREY_LINE_FRAME
+
+function MessageBox:init(info)
+ info = info or {}
+ self:init_fields{
+ text = info.text or {},
+ frame_title = info.title,
+ frame_width = info.frame_width,
+ on_accept = info.on_accept,
+ on_cancel = info.on_cancel,
+ on_close = info.on_close,
+ text_pen = info.text_pen
+ }
+ if type(self.text) == 'string' then
+ self.text = utils.split_string(self.text, "\n")
+ end
+ gui.FramedScreen.init(self, info)
+ return self
+end
+
+function MessageBox:getWantedFrameSize()
+ local text = self.text
+ local w = #(self.frame_title or '') + 2
+ 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, #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 x,y = self.frame_rect.x1+1, self.frame_rect.y2+1
+ dscreen.paintString({fg=COLOR_LIGHTGREEN},x,y,'ESC')
+ dscreen.paintString({fg=COLOR_GREY},x+3,y,'/')
+ dscreen.paintString({fg=COLOR_LIGHTGREEN},x+4,y,'y')
+ 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)
+ mkinstance(MessageBox):init{
+ text = text,
+ title = title,
+ text = text,
+ text_pen = tcolor,
+ on_close = on_close
+ }:show()
+end
+
+function showYesNoPrompt(title, text, tcolor, on_accept, on_cancel)
+ mkinstance(MessageBox):init{
+ title = title,
+ text = text,
+ text_pen = tcolor,
+ on_accept = on_accept,
+ on_cancel = on_cancel,
+ }:show()
+end
+
+InputBox = defclass(InputBox, MessageBox)
+
+function InputBox:init(info)
+ info = info or {}
+ self:init_fields{
+ input = info.input or '',
+ input_pen = info.input_pen,
+ on_input = info.on_input,
+ }
+ MessageBox.init(self, info)
+ self.on_accept = nil
+ return self
+end
+
+function InputBox:getWantedFrameSize()
+ local mw, mh = MessageBox.getWantedFrameSize(self)
+ return mw, mh+2
+end
+
+function InputBox:onRenderBody(dc)
+ MessageBox.onRenderBody(self, dc)
+
+ dc:newline(1)
+ dc:pen(self.input_pen or COLOR_LIGHTCYAN)
+ dc:fill(dc.x1+1,dc.y,dc.x2-1,dc.y)
+
+ local cursor = '_'
+ if math.floor(dfhack.getTickCount()/500) % 2 == 0 then
+ cursor = ' '
+ end
+ local txt = self.input .. cursor
+ if #txt > dc.width-2 then
+ txt = string.sub(txt, #txt-dc.width+3)
+ -- Add prefix arrow
+ dc:advance(-1):char(27)
+ 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)
+ mkinstance(InputBox):init{
+ title = title,
+ text = text,
+ text_pen = tcolor,
+ input = input,
+ on_input = on_input,
+ on_cancel = on_cancel,
+ frame_width = min_width,
+ }:show()
+end
+
+
+return _ENV
diff --git a/library/lua/utils.lua b/library/lua/utils.lua
index 19a4e6f6a..9fa473ed8 100644
--- a/library/lua/utils.lua
+++ b/library/lua/utils.lua
@@ -381,6 +381,19 @@ function getBuildingCenter(building)
return xyz2pos(building.centerx, building.centery, building.z)
end
+function split_string(self, delimiter)
+ local result = { }
+ local from = 1
+ local delim_from, delim_to = string.find( self, delimiter, from )
+ while delim_from do
+ table.insert( result, string.sub( self, from , delim_from-1 ) )
+ from = delim_to + 1
+ delim_from, delim_to = string.find( self, delimiter, from )
+ end
+ table.insert( result, string.sub( self, from ) )
+ return result
+end
+
-- Ask a yes-no question
function prompt_yes_no(msg,default)
local prompt = msg