diff options
| author | Alexander Gavrilov | 2012-08-21 11:35:39 +0400 |
|---|---|---|
| committer | Alexander Gavrilov | 2012-08-21 11:35:39 +0400 |
| commit | 451e965936c8e3fb911366abe40f2753772acfc2 (patch) | |
| tree | 2a0330da7b43ae8d4e33a8cca109c2e677706f55 /library | |
| parent | be7bce1541665909283769d1847e6793c4715df3 (diff) | |
| download | dfhack-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')
| -rw-r--r-- | library/LuaApi.cpp | 31 | ||||
| -rw-r--r-- | library/lua/gui.lua | 173 | ||||
| -rw-r--r-- | library/lua/gui/dwarfmode.lua | 80 | ||||
| -rw-r--r-- | library/lua/utils.lua | 12 |
4 files changed, 258 insertions, 38 deletions
diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index b8c1248b..7f18ce3e 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -194,16 +194,28 @@ static bool get_int_field(lua_State *L, T *pf, int idx, const char *name, int de return !nil; } +static bool get_char_field(lua_State *L, char *pf, int idx, const char *name, char defval) +{ + lua_getfield(L, idx, name); + + if (lua_type(L, -1) == LUA_TSTRING) + { + *pf = lua_tostring(L, -1)[0]; + lua_pop(L, 1); + return true; + } + else + { + lua_pop(L, 1); + return get_int_field(L, pf, idx, name, defval); + } +} + static void decode_pen(lua_State *L, Pen &pen, int idx) { idx = lua_absindex(L, idx); - lua_getfield(L, idx, "ch"); - if (lua_isstring(L, -1)) - pen.ch = lua_tostring(L, -1)[0]; - else - get_int_field(L, &pen.ch, idx, "ch", 0); - lua_pop(L, 1); + get_char_field(L, &pen.ch, idx, "ch", 0); get_int_field(L, &pen.fg, idx, "fg", 7); get_int_field(L, &pen.bg, idx, "bg", 0); @@ -1104,7 +1116,12 @@ static int screen_paintTile(lua_State *L) int x = luaL_checkint(L, 2); int y = luaL_checkint(L, 3); if (lua_gettop(L) >= 4 && !lua_isnil(L, 4)) - pen.ch = luaL_checkint(L, 4); + { + if (lua_type(L, 4) == LUA_TSTRING) + pen.ch = lua_tostring(L, 4)[0]; + else + pen.ch = luaL_checkint(L, 4); + } if (lua_gettop(L) >= 5 && !lua_isnil(L, 5)) pen.tile = luaL_checkint(L, 5); lua_pushboolean(L, Screen::paintTile(pen, x, y)); 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 |
