summaryrefslogtreecommitdiff
path: root/library/lua
diff options
context:
space:
mode:
authorAlexander Gavrilov2012-08-21 11:35:39 +0400
committerAlexander Gavrilov2012-08-21 11:35:39 +0400
commit451e965936c8e3fb911366abe40f2753772acfc2 (patch)
tree2a0330da7b43ae8d4e33a8cca109c2e677706f55 /library/lua
parentbe7bce1541665909283769d1847e6793c4715df3 (diff)
downloaddfhack-451e965936c8e3fb911366abe40f2753772acfc2.tar.gz
dfhack-451e965936c8e3fb911366abe40f2753772acfc2.tar.bz2
dfhack-451e965936c8e3fb911366abe40f2753772acfc2.tar.xz
Add a Painter class for lua viewscreens, and extract other utilities.
Painter clips to an arbitrary rectangle window, and tracks current cursor and color state.
Diffstat (limited to 'library/lua')
-rw-r--r--library/lua/gui.lua173
-rw-r--r--library/lua/gui/dwarfmode.lua80
-rw-r--r--library/lua/utils.lua12
3 files changed, 234 insertions, 31 deletions
diff --git a/library/lua/gui.lua b/library/lua/gui.lua
index 5883a2ee..35ae899c 100644
--- a/library/lua/gui.lua
+++ b/library/lua/gui.lua
@@ -40,12 +40,153 @@ function simulateInput(screen,...)
dscreen._doSimulateInput(screen, keys)
end
-Screen = defclass(Screen, dfhack.screen)
+function mkdims_xy(x1,y1,x2,y2)
+ return { x1=x1, y1=y1, x2=x2, y2=y2, width=x2-x1+1, height=y2-y1+1 }
+end
+function mkdims_wh(x1,y1,w,h)
+ return { x1=x1, y1=y1, x2=x1+w-1, y2=y1+h-1, width=w, height=h }
+end
+function inset(rect,dx1,dy1,dx2,dy2)
+ return mkdims_xy(
+ rect.x1+dx1, rect.y1+dy1,
+ rect.x2-(dx2 or dx1), rect.y2-(dy2 or dy1)
+ )
+end
+
+function to_pen(pen, default)
+ if pen == nil then
+ return default or {}
+ elseif type(pen) ~= 'table' then
+ return {fg=pen}
+ else
+ return pen
+ end
+end
+
+----------------------------
+-- Clipped painter object --
+----------------------------
+
+Painter = defclass(Painter, nil)
+
+function Painter.new(rect, pen)
+ rect = rect or {}
+ local sw, sh = dscreen.getWindowSize()
+ local self = mkdims_xy(
+ math.max(rect.x1 or 0, 0),
+ math.max(rect.y1 or 0, 0),
+ math.min(rect.x2 or sw-1, sw-1),
+ math.min(rect.y2 or sh-1, sh-1)
+ )
+ self.cur_pen = to_pen(pen or COLOR_GREY)
+ self.x = self.x1
+ self.y = self.y1
+ return mkinstance(Painter, self)
+end
-function Screen.new(attrs)
- return mkinstance(Screen, attrs)
+function Painter:setRect(x1,y1,x2,y2)
+ local rect = mkdims_xy(x1,y1,x2,y2)
+ self.x1 = rect.x1
+ self.y1 = rect.y1
+ self.x2 = rect.x2
+ self.y2 = rect.y2
+ self.width = rect.width
+ self.height = rect.height
end
+function Painter:isValidPos()
+ return self.x >= self.x1 and self.x <= self.x2 and self.y >= self.y1 and self.y <= self.y2
+end
+
+function Painter:inset(dx1,dy1,dx2,dy1)
+ self:setRect(
+ self.x1+dx1, self.y1+dy1,
+ self.x2-(dx2 or dx1), self.y2-(dy2 or dy1)
+ )
+ return self
+end
+
+function Painter:seek(x,y)
+ if x then self.x = self.x1 + x end
+ if y then self.y = self.y1 + y end
+ return self
+end
+
+function Painter:advance(dx,dy)
+ if dx then self.x = self.x + dx end
+ if dy then self.y = self.y + dy end
+ return self
+end
+
+function Painter:newline(dx)
+ self.y = self.y + 1
+ self.x = self.x1 + (dx or 0)
+ return self
+end
+
+function Painter:pen(pen)
+ self.cur_pen = to_pen(pen, self.cur_pen)
+ return self
+end
+
+function Painter:clear()
+ dscreen.fillRect(CLEAR_PEN, self.x1, self.y1, self.x2, self.y2)
+ return self
+end
+
+function Painter:fill(x1,y1,x2,y2,pen)
+ if type(x1) == 'table' then
+ x1, y1, x2, y2, pen = x1.x1, x1.y1, x1.x2, x1.y2, x2
+ end
+ x1 = math.max(x1,self.x1)
+ y1 = math.max(y1,self.y1)
+ x2 = math.min(x2,self.x2)
+ y2 = math.min(y2,self.y2)
+ dscreen.fillRect(to_pen(pen, self.cur_pen),x1,y1,x2,y2)
+ return self
+end
+
+function Painter:char(char,pen)
+ if self:isValidPos() then
+ dscreen.paintTile(to_pen(pen, self.cur_pen), self.x, self.y, char)
+ end
+ return self:advance(1, nil)
+end
+
+function Painter:tile(char,tile,pen)
+ if self:isValidPos() then
+ dscreen.paintTile(to_pen(pen, self.cur_pen), self.x, self.y, char, tile)
+ end
+ return self:advance(1, nil)
+end
+
+function Painter:string(text,pen)
+ if self.y >= self.y1 and self.y <= self.y2 then
+ local dx = 0
+ if self.x < self.x1 then
+ dx = self.x1 - self.x
+ end
+ local len = #text
+ if self.x + len - 1 > self.x2 then
+ len = self.x2 - self.x + 1
+ end
+ if len > dx then
+ dscreen.paintString(
+ to_pen(pen, self.cur_pen),
+ self.x+dx, self.y,
+ string.sub(text,dx+1,len)
+ )
+ end
+ end
+ return self:advance(#text, nil)
+end
+
+------------------------
+-- Base screen object --
+------------------------
+
+Screen = defclass(Screen, dfhack.screen)
+
function Screen:isShown()
return self._native ~= nil
end
@@ -68,6 +209,22 @@ function Screen:inputToParent(...)
end
end
+function Screen:show()
+ if self._native then
+ error("This screen is already on display")
+ end
+ self:onAboutToShow()
+ if dscreen.show(self) then
+ self:onShown()
+ end
+end
+
+function Screen:onAboutToShow()
+end
+
+function Screen:onShown()
+end
+
function paint_border(x1,y1,x2,y2,color,title)
local pen = { ch = ' ', fg = COLOR_BLACK, bg = color }
dscreen.fillRect(pen,x1,y1,x2,y1)
@@ -92,13 +249,6 @@ local function hint_coord(gap,hint)
end
end
-function mkdims_xy(x1,y1,x2,y2)
- return { x1=x1, y1=y1, x2=x2, y2=y2, width=x2-x1+1, height=y2-y1+1 }
-end
-function mkdims_wh(x1,y1,w,h)
- return { x1=x1, y1=y1, x2=x1+w-1, y2=y1+h-1, width=w, height=h }
-end
-
function Screen:renderFrame(color,title,width,height,xhint,yhint)
local sw, sh = dscreen.getWindowSize()
local iw, ih = sw-2, sh-2
@@ -116,7 +266,8 @@ function Screen:renderFrame(color,title,width,height,xhint,yhint)
end
paint_border(x1,y1,x2,y2,color,title)
- return x1+1,y1+1,width,height,x2-1,y2-1
+
+ return Painter.new(mkdims_wh(x1+1,y1+1,width,height))
end
return _ENV
diff --git a/library/lua/gui/dwarfmode.lua b/library/lua/gui/dwarfmode.lua
index 2c62751e..c10933f0 100644
--- a/library/lua/gui/dwarfmode.lua
+++ b/library/lua/gui/dwarfmode.lua
@@ -39,6 +39,14 @@ function getPanelLayout()
return rv
end
+function getCursorPos()
+ return copyall(df.global.cursor)
+end
+
+function setCursorPos(cursor)
+ df.global.cursor = cursor
+end
+
function getViewportPos()
return {
x = df.global.window_x,
@@ -47,39 +55,59 @@ function getViewportPos()
}
end
-function getCursorPos()
- return copyall(df.global.cursor)
-end
-
-function setCursorPos(cursor)
- df.global.cursor = cursor
+function clipViewport(view, layout)
+ local map = df.global.world.map
+ layout = layout or getPanelLayout()
+ return {
+ x = math.max(0, math.min(view.x, map.x_count-layout.map.width)),
+ y = math.max(0, math.min(view.y, map.y_count-layout.map.height)),
+ z = math.max(0, math.min(view.z, map.z_count-1))
+ }
end
-function setViewportPos(layout,view)
+function setViewportPos(view, layout)
local map = df.global.world.map
- df.global.window_x = math.max(0, math.min(view.x, map.x_count-layout.map.width))
- df.global.window_y = math.max(0, math.min(view.y, map.y_count-layout.map.height))
- df.global.window_z = math.max(0, math.min(view.z, map.z_count-1))
- return getViewportPos()
+ layout = layout or getPanelLayout()
+ local vp = clipViewport(view, layout)
+ df.global.window_x = vp.x
+ df.global.window_y = vp.y
+ df.global.window_z = vp.z
+ return vp
end
-function centerViewportOn(layout,target)
- local mapsz = layout.map
+function centerViewportOn(target, layout)
+ layout = layout or getPanelLayout()
local view = xyz2pos(
target.x-math.floor(layout.map.width/2),
target.y-math.floor(layout.map.height/2),
target.z
)
- return setViewportPos(layout, view)
+ return setViewportPos(view, layout)
end
function isInViewport(layout,view,target,gap)
gap = gap or 0
- return target.x-gap >= view.x and target.x+gap < view.x+layout.map.width
- and target.y-gap >= view.y and target.y+gap < view.y+layout.map.height
+
+ local map = df.global.world.map
+ return math.max(target.x-gap,0) >= view.x
+ and math.min(target.x+gap,map.x_count-1) < view.x+layout.map.width
+ and math.max(target.y-gap,0) >= view.y
+ and math.min(target.y+gap,map.y_count-1) < view.y+layout.map.height
and target.z == view.z
end
+function revealInViewport(target,gap,view,layout)
+ layout = layout or getPanelLayout()
+
+ if not isInViewport(layout, getViewportPos(), target, gap) then
+ if view and isInViewport(layout, view, target, gap) then
+ return setViewportPos(view, layout)
+ else
+ return centerViewportOn(target, layout)
+ end
+ end
+end
+
DwarfOverlay = defclass(DwarfOverlay, gui.Screen)
function DwarfOverlay:updateLayout()
@@ -106,14 +134,26 @@ function DwarfOverlay:propagateMoveKeys(keys)
end
function DwarfOverlay:onIdle()
+ -- Dwarfmode constantly needs repainting
dscreen.invalidate()
end
-function DwarfOverlay:clearMenu()
+function DwarfOverlay:onAboutToShow()
+ if not df.viewscreen_dwarfmodest:is_instance(dfhack.gui.getCurViewscreen()) then
+ error("This screen requires the main dwarfmode view")
+ end
+end
+
+MenuOverlay = defclass(MenuOverlay, DwarfOverlay)
+
+function MenuOverlay:onRender()
+ self:renderParent()
+ self:updateLayout()
+
local menu = self.df_layout.menu
- if not menu then return nil end
- dscreen.fillRect(gui.CLEAR_PEN,menu.x1,menu.y1,menu.x2,menu.y2)
- return menu.x1,menu.y1,menu.width,menu.height
+ if menu then
+ self:onRenderBody(gui.Painter.new(menu))
+ end
end
return _ENV
diff --git a/library/lua/utils.lua b/library/lua/utils.lua
index 38a1e6c4..009bdf98 100644
--- a/library/lua/utils.lua
+++ b/library/lua/utils.lua
@@ -361,6 +361,18 @@ function insert_or_update(vector,item,field,cmp)
return added,cur,pos
end
+-- Calls a method with a string temporary
+function call_with_string(obj,methodname,...)
+ return dfhack.with_temp_object(
+ df.new "string",
+ function(str,obj,methodname,...)
+ obj[methodname](obj,str,...)
+ return str.value
+ end,
+ obj,methodname,...
+ )
+end
+
-- Ask a yes-no question
function prompt_yes_no(msg,default)
local prompt = msg