More work on utilities for making lua viewscreens.

develop
Alexander Gavrilov 2012-08-21 19:40:37 +04:00
parent 451e965936
commit 15235cc938
4 changed files with 149 additions and 91 deletions

@ -53,11 +53,11 @@ function inset(rect,dx1,dy1,dx2,dy2)
) )
end end
function to_pen(pen, default) local function to_pen(default, pen, bg, bold)
if pen == nil then if pen == nil then
return default or {} return default or {}
elseif type(pen) ~= 'table' then elseif type(pen) ~= 'table' then
return {fg=pen} return {fg=pen,bg=bg,bold=bold}
else else
return pen return pen
end end
@ -70,40 +70,48 @@ end
Painter = defclass(Painter, nil) Painter = defclass(Painter, nil)
function Painter.new(rect, pen) function Painter.new(rect, pen)
rect = rect or {} rect = rect or mkdims_wh(0,0,dscreen.getWindowSize())
local sw, sh = dscreen.getWindowSize() local self = {
local self = mkdims_xy( x1 = rect.x1, clip_x1 = rect.x1,
math.max(rect.x1 or 0, 0), y1 = rect.y1, clip_y1 = rect.y1,
math.max(rect.y1 or 0, 0), x2 = rect.x2, clip_x2 = rect.x2,
math.min(rect.x2 or sw-1, sw-1), y2 = rect.y2, clip_y2 = rect.y2,
math.min(rect.y2 or sh-1, sh-1) width = rect.x2-rect.x1+1,
) height = rect.y2-rect.y1+1,
self.cur_pen = to_pen(pen or COLOR_GREY) cur_pen = to_pen(nil, pen or COLOR_GREY)
self.x = self.x1 }
self.y = self.y1 return mkinstance(Painter, self):seek(0,0)
return mkinstance(Painter, self)
end end
function Painter:setRect(x1,y1,x2,y2) function Painter:isValidPos()
local rect = mkdims_xy(x1,y1,x2,y2) return self.x >= self.clip_x1 and self.x <= self.clip_x2
self.x1 = rect.x1 and self.y >= self.clip_y1 and self.y <= self.clip_y2
self.y1 = rect.y1
self.x2 = rect.x2
self.y2 = rect.y2
self.width = rect.width
self.height = rect.height
end end
function Painter:isValidPos() function Painter:viewport(x,y,w,h)
return self.x >= self.x1 and self.x <= self.x2 and self.y >= self.y1 and self.y <= self.y2 local x1,y1 = self.x1+x, self.y1+y
local x2,y2 = x1+w-1, y1+h-1
local vp = {
-- Logical viewport
x1 = x1, y1 = y1, x2 = x2, y2 = y2,
width = w, height = h,
-- Actual clipping rect
clip_x1 = math.max(self.clip_x1, x1),
clip_y1 = math.max(self.clip_y1, y1),
clip_x2 = math.min(self.clip_x2, x2),
clip_y2 = math.min(self.clip_y2, y2),
-- Pen
cur_pen = self.cur_pen
}
return mkinstance(Painter, vp):seek(0,0)
end end
function Painter:inset(dx1,dy1,dx2,dy1) function Painter:localX()
self:setRect( return self.x - self.x1
self.x1+dx1, self.y1+dy1, end
self.x2-(dx2 or dx1), self.y2-(dy2 or dy1)
) function Painter:localY()
return self return self.y - self.y1
end end
function Painter:seek(x,y) function Painter:seek(x,y)
@ -124,55 +132,63 @@ function Painter:newline(dx)
return self return self
end end
function Painter:pen(pen) function Painter:pen(pen,...)
self.cur_pen = to_pen(pen, self.cur_pen) self.cur_pen = to_pen(self.cur_pen, pen, ...)
return self
end
function Painter:color(fg,bold,bg)
self.cur_pen = copyall(self.cur_pen)
self.cur_pen.fg = fg
self.cur_pen.bold = bold
if bg then self.cur_pen.bg = bg end
return self return self
end end
function Painter:clear() function Painter:clear()
dscreen.fillRect(CLEAR_PEN, self.x1, self.y1, self.x2, self.y2) dscreen.fillRect(CLEAR_PEN, self.clip_x1, self.clip_y1, self.clip_x2, self.clip_y2)
return self return self
end end
function Painter:fill(x1,y1,x2,y2,pen) function Painter:fill(x1,y1,x2,y2,pen,bg,bold)
if type(x1) == 'table' then if type(x1) == 'table' then
x1, y1, x2, y2, pen = x1.x1, x1.y1, x1.x2, x1.y2, x2 x1, y1, x2, y2, pen, bg, bold = x1.x1, x1.y1, x1.x2, x1.y2, y1, x2, y2
end end
x1 = math.max(x1,self.x1) x1 = math.max(x1,self.clip_x1)
y1 = math.max(y1,self.y1) y1 = math.max(y1,self.clip_y1)
x2 = math.min(x2,self.x2) x2 = math.min(x2,self.clip_x2)
y2 = math.min(y2,self.y2) y2 = math.min(y2,self.clip_y2)
dscreen.fillRect(to_pen(pen, self.cur_pen),x1,y1,x2,y2) dscreen.fillRect(to_pen(self.cur_pen,pen,bg,bold),x1,y1,x2,y2)
return self return self
end end
function Painter:char(char,pen) function Painter:char(char,pen,...)
if self:isValidPos() then if self:isValidPos() then
dscreen.paintTile(to_pen(pen, self.cur_pen), self.x, self.y, char) dscreen.paintTile(to_pen(self.cur_pen, pen, ...), self.x, self.y, char)
end end
return self:advance(1, nil) return self:advance(1, nil)
end end
function Painter:tile(char,tile,pen) function Painter:tile(char,tile,pen,...)
if self:isValidPos() then if self:isValidPos() then
dscreen.paintTile(to_pen(pen, self.cur_pen), self.x, self.y, char, tile) dscreen.paintTile(to_pen(self.cur_pen, pen, ...), self.x, self.y, char, tile)
end end
return self:advance(1, nil) return self:advance(1, nil)
end end
function Painter:string(text,pen) function Painter:string(text,pen,...)
if self.y >= self.y1 and self.y <= self.y2 then if self.y >= self.clip_y1 and self.y <= self.clip_y2 then
local dx = 0 local dx = 0
if self.x < self.x1 then if self.x < self.clip_x1 then
dx = self.x1 - self.x dx = self.clip_x1 - self.x
end end
local len = #text local len = #text
if self.x + len - 1 > self.x2 then if self.x + len - 1 > self.clip_x2 then
len = self.x2 - self.x + 1 len = self.clip_x2 - self.x + 1
end end
if len > dx then if len > dx then
dscreen.paintString( dscreen.paintString(
to_pen(pen, self.cur_pen), to_pen(self.cur_pen, pen, ...),
self.x+dx, self.y, self.x+dx, self.y,
string.sub(text,dx+1,len) string.sub(text,dx+1,len)
) )
@ -209,12 +225,12 @@ function Screen:inputToParent(...)
end end
end end
function Screen:show() function Screen:show(below)
if self._native then if self._native then
error("This screen is already on display") error("This screen is already on display")
end end
self:onAboutToShow() self:onAboutToShow(below)
if dscreen.show(self) then if dscreen.show(self, below) then
self:onShown() self:onShown()
end end
end end
@ -225,20 +241,40 @@ end
function Screen:onShown() function Screen:onShown()
end end
function paint_border(x1,y1,x2,y2,color,title) ------------------------
local pen = { ch = ' ', fg = COLOR_BLACK, bg = color } -- Framed screen object --
dscreen.fillRect(pen,x1,y1,x2,y1) ------------------------
dscreen.fillRect(pen,x1,y2,x2,y2)
dscreen.fillRect(pen,x1,y1+1,x1,y2-1) GREY_FRAME = {
dscreen.fillRect(pen,x2,y1+1,x2,y2-1) frame_pen = { ch = ' ', fg = COLOR_BLACK, bg = COLOR_GREY },
title_pen = { fg = COLOR_BLACK, bg = COLOR_WHITE },
}
function paint_frame(x1,y1,x2,y2,style,title)
local pen = style.frame_pen
dscreen.paintTile(style.lt_frame_pen or pen, x1, y1)
dscreen.paintTile(style.rt_frame_pen or pen, x2, y1)
dscreen.paintTile(style.lb_frame_pen or pen, x1, y2)
dscreen.paintTile(style.rb_frame_pen or pen, x2, y2)
dscreen.fillRect(style.t_frame_pen or style.h_frame_pen or pen,x1+1,y1,x2-1,y1)
dscreen.fillRect(style.b_frame_pen or style.h_frame_pen or pen,x1+1,y2,x2-1,y2)
dscreen.fillRect(style.l_frame_pen or style.v_frame_pen or pen,x1,y1+1,x1,y2-1)
dscreen.fillRect(style.r_frame_pen or style.v_frame_pen or pen,x2,y1+1,x2,y2-1)
if title then if title then
local x = math.floor((x2-x1-3-#title)/2 + x1) local x = math.max(0,math.floor((x2-x1-3-#title)/2)) + x1
pen.bg = bit32.bxor(pen.bg, 8) local tstr = ' '..title..' '
dscreen.paintString(pen, x, y1, ' '..title..' ') if #tstr > x2-x1-1 then
tstr = string.sub(tstr,1,x2-x1-1)
end
dscreen.paintString(style.title_pen or pen, x, y1, tstr)
end end
end end
FramedScreen = defclass(FramedScreen, Screen)
FramedScreen.frame_style = GREY_FRAME
local function hint_coord(gap,hint) local function hint_coord(gap,hint)
if hint and hint > 0 then if hint and hint > 0 then
return math.min(hint,gap) return math.min(hint,gap)
@ -249,25 +285,33 @@ local function hint_coord(gap,hint)
end end
end end
function Screen:renderFrame(color,title,width,height,xhint,yhint) function FramedScreen:updateLayout()
local sw, sh = dscreen.getWindowSize() local sw, sh = dscreen.getWindowSize()
local iw, ih = sw-2, sh-2 local iw, ih = sw-2, sh-2
width = math.min(width or iw, iw) local width = math.min(self.frame_width or iw, iw)
height = math.min(height or ih, ih) local height = math.min(self.frame_height or ih, ih)
local gw, gh = iw-width, ih-height local gw, gh = iw-width, ih-height
local x1, y1 = hint_coord(gw,xhint), hint_coord(gh,yhint) local x1, y1 = hint_coord(gw,self.frame_xhint), hint_coord(gh,self.frame_yhint)
local x2, y2 = x1+width+1, y1+height+1 self.frame_rect = mkdims_wh(x1+1,y1+1,width,height)
self.frame_opaque = (gw == 0 and gh == 0)
end
function FramedScreen:onRender()
self:updateLayout()
local rect = self.frame_rect
local x1,y1,x2,y2 = rect.x1-1, rect.y1-1, rect.x2+1, rect.y2+1
if gw == 0 and gh == 0 then if self.frame_opaque then
dscreen.clear() dscreen.clear()
else else
self:renderParent() self:renderParent()
dscreen.fillRect(CLEAR_PEN,x1,y1,x2,y2) dscreen.fillRect(CLEAR_PEN,x1,y1,x2,y2)
end end
paint_border(x1,y1,x2,y2,color,title) paint_frame(x1,y1,x2,y2,self.frame_style,self.frame_title)
return Painter.new(mkdims_wh(x1+1,y1+1,width,height)) self:onRenderBody(Painter.new(rect))
end end
return _ENV return _ENV

@ -138,14 +138,25 @@ function DwarfOverlay:onIdle()
dscreen.invalidate() dscreen.invalidate()
end end
function DwarfOverlay:onAboutToShow() function DwarfOverlay:onAboutToShow(below)
if not df.viewscreen_dwarfmodest:is_instance(dfhack.gui.getCurViewscreen()) then local screen = dfhack.gui.getCurViewscreen()
if below then screen = below.parent end
if not df.viewscreen_dwarfmodest:is_instance(screen) then
error("This screen requires the main dwarfmode view") error("This screen requires the main dwarfmode view")
end end
end end
MenuOverlay = defclass(MenuOverlay, DwarfOverlay) MenuOverlay = defclass(MenuOverlay, DwarfOverlay)
function MenuOverlay:onAboutToShow(below)
DwarfOverlay.onAboutToShow(self,below)
self:updateLayout()
if not self.df_layout.menu then
error("The menu panel of dwarfmode is not visible")
end
end
function MenuOverlay:onRender() function MenuOverlay:onRender()
self:renderParent() self:renderParent()
self:updateLayout() self:updateLayout()

@ -1,18 +0,0 @@
-- Test lua viewscreens.
local gui = require 'gui'
local screen = mkinstance(gui.Screen, {
onRender = function(self)
local text = 'Woohoo, lua viewscreen :)'
local dc = self:renderFrame(COLOR_GREY,'Hello World',#text+6,3)
dc:seek(3,1):string(text, {fg=COLOR_LIGHTGREEN})
end,
onInput = function(self,keys)
if keys and (keys.LEAVESCREEN or keys.SELECT) then
self:dismiss()
end
end
})
screen:show()

@ -0,0 +1,21 @@
-- Test lua viewscreens.
local gui = require 'gui'
local text = 'Woohoo, lua viewscreen :)'
local screen = mkinstance(gui.FramedScreen, {
frame_title = 'Hello World',
frame_width = #text+6,
frame_height = 3,
onRenderBody = function(self, dc)
dc:seek(3,1):string(text, COLOR_LIGHTGREEN)
end,
onInput = function(self,keys)
if keys.LEAVESCREEN or keys.SELECT then
self:dismiss()
end
end
})
screen:show()