Improve viewport manipulation utilities and support scroll in mechanisms.

I.e. allow the user to scroll around with cursor keys,
provided that keeps the cursor still visible.
develop
Alexander Gavrilov 2012-08-22 22:28:52 +04:00
parent 92c0b555dc
commit cf4b8a0196
3 changed files with 188 additions and 75 deletions

@ -245,6 +245,16 @@ end
function Screen:onShown() function Screen:onShown()
end end
function Screen:dismiss()
if self._native and not dscreen.isDismissed(self) then
dscreen.dismiss(self)
self:onDismissed()
end
end
function Screen:onDismissed()
end
------------------------ ------------------------
-- Framed screen object -- -- Framed screen object --
------------------------ ------------------------
@ -311,8 +321,7 @@ local function hint_coord(gap,hint)
end end
end end
function FramedScreen:updateLayout() function FramedScreen:updateFrameSize(sw,sh)
local sw, sh = dscreen.getWindowSize()
local iw, ih = sw-2, sh-2 local iw, ih = sw-2, sh-2
local width = math.min(self.frame_width or iw, iw) local width = math.min(self.frame_width or iw, iw)
local height = math.min(self.frame_height or ih, ih) local height = math.min(self.frame_height or ih, ih)
@ -322,9 +331,11 @@ function FramedScreen:updateLayout()
self.frame_opaque = (gw == 0 and gh == 0) self.frame_opaque = (gw == 0 and gh == 0)
end end
function FramedScreen:onRender() function FramedScreen:onResize(w,h)
self:updateLayout() self:updateFrameSize(w,h)
end
function FramedScreen:onRender()
local rect = self.frame_rect local rect = self.frame_rect
local x1,y1,x2,y2 = rect.x1-1, rect.y1-1, rect.x2+1, rect.y2+1 local x1,y1,x2,y2 = rect.x1-1, rect.y1-1, rect.x2+1, rect.y2+1

@ -4,6 +4,7 @@ local _ENV = mkmodule('gui.dwarfmode')
local gui = require('gui') local gui = require('gui')
local dscreen = dfhack.screen local dscreen = dfhack.screen
local world_map = df.global.world.map
AREA_MAP_WIDTH = 23 AREA_MAP_WIDTH = 23
MENU_WIDTH = 30 MENU_WIDTH = 30
@ -40,71 +41,135 @@ function getPanelLayout()
end end
function getCursorPos() function getCursorPos()
return copyall(df.global.cursor) if df.global.cursor.x ~= -30000 then
return copyall(df.global.cursor)
end
end end
function setCursorPos(cursor) function setCursorPos(cursor)
df.global.cursor = cursor df.global.cursor = cursor
end end
function getViewportPos() function clearCursorPos()
return { df.global.cursor = xyz2pos(nil)
x = df.global.window_x, end
y = df.global.window_y,
z = df.global.window_z Viewport = defclass(Viewport)
}
function Viewport.make(map,x,y,z)
local self = gui.mkdims_wh(x,y,map.width,map.height)
self.z = z
return mkinstance(Viewport, self)
end
function Viewport.get(layout)
return Viewport.make(
(layout or getPanelLayout()).map,
df.global.window_x,
df.global.window_y,
df.global.window_z
)
end end
function clipViewport(view, layout) function Viewport:resize(layout)
local map = df.global.world.map return Viewport.make(
layout = layout or getPanelLayout() (layout or getPanelLayout()).map,
return { self.x1, self.y1, self.z
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 end
function setViewportPos(view, layout) function Viewport:set()
local map = df.global.world.map local vp = self:clip()
layout = layout or getPanelLayout() df.global.window_x = vp.x1
local vp = clipViewport(view, layout) df.global.window_y = vp.y1
df.global.window_x = vp.x
df.global.window_y = vp.y
df.global.window_z = vp.z df.global.window_z = vp.z
return vp return vp
end end
function centerViewportOn(target, layout) function Viewport:clip(x,y,z)
layout = layout or getPanelLayout() return self:make(
local view = xyz2pos( math.max(0, math.min(x or self.x1, world_map.x_count-self.width)),
target.x-math.floor(layout.map.width/2), math.max(0, math.min(y or self.y1, world_map.y_count-self.height)),
target.y-math.floor(layout.map.height/2), math.max(0, math.min(z or self.z, world_map.z_count-1))
target.z
) )
return setViewportPos(view, layout)
end end
function isInViewport(layout,view,target,gap) function Viewport:isVisibleXY(target,gap)
gap = gap or 0 gap = gap or 0
local map = df.global.world.map return math.max(target.x-gap,0) >= self.x1
return math.max(target.x-gap,0) >= view.x and math.min(target.x+gap,world_map.x_count-1) <= self.x2
and math.min(target.x+gap,map.x_count-1) < view.x+layout.map.width and math.max(target.y-gap,0) >= self.y1
and math.max(target.y-gap,0) >= view.y and math.min(target.y+gap,world_map.y_count-1) <= self.y2
and math.min(target.y+gap,map.y_count-1) < view.y+layout.map.height
and target.z == view.z
end end
function revealInViewport(target,gap,view,layout) function Viewport:isVisible(target,gap)
layout = layout or getPanelLayout() gap = gap or 0
if not isInViewport(layout, getViewportPos(), target, gap) then return self:isVisibleXY(target,gap) and target.z == self.z
if view and isInViewport(layout, view, target, gap) then end
return setViewportPos(view, layout)
else function Viewport:centerOn(target)
return centerViewportOn(target, layout) return self:clip(
end target.x - math.floor(self.width/2),
target.y - math.floor(self.height/2),
target.z
)
end
function Viewport:scrollTo(target,gap)
gap = math.max(0, gap or 5)
if gap*2 >= math.min(self.width, self.height) then
gap = math.floor(math.min(self.width, self.height)/2)
end
local x = math.min(self.x1, target.x-gap)
x = math.max(x, target.x+gap+1-self.width)
local y = math.min(self.y1, target.y-gap)
y = math.max(y, target.y+gap+1-self.height)
return self:clip(x, y, target.z)
end
function Viewport:reveal(target,gap,max_scroll,scroll_gap,scroll_z)
gap = math.max(0, gap or 5)
if self:isVisible(target, gap) then
return self
end
max_scroll = math.max(0, max_scroll or 5)
if self:isVisibleXY(target, -max_scroll)
and (scroll_z or target.z == self.z) then
return self:scrollTo(target, scroll_gap or gap)
else
return self:centerOn(target)
end
end
MOVEMENT_KEYS = {
CURSOR_UP = { 0, -1, 0 }, CURSOR_DOWN = { 0, 1, 0 },
CURSOR_LEFT = { -1, 0, 0 }, CURSOR_RIGHT = { 1, 0, 0 },
CURSOR_UPLEFT = { -1, -1, 0 }, CURSOR_UPRIGHT = { 1, -1, 0 },
CURSOR_DOWNLEFT = { -1, 1, 0 }, CURSOR_DOWNRIGHT = { 1, 1, 0 },
CURSOR_UP_FAST = { 0, -1, 0, true }, CURSOR_DOWN_FAST = { 0, 1, 0, true },
CURSOR_LEFT_FAST = { -1, 0, 0, true }, CURSOR_RIGHT_FAST = { 1, 0, 0, true },
CURSOR_UPLEFT_FAST = { -1, -1, 0, true }, CURSOR_UPRIGHT_FAST = { 1, -1, 0, true },
CURSOR_DOWNLEFT_FAST = { -1, 1, 0, true }, CURSOR_DOWNRIGHT_FAST = { 1, 1, 0, true },
CURSOR_UP_Z = { 0, 0, 1 }, CURSOR_DOWN_Z = { 0, 0, -1 },
CURSOR_UP_Z_AUX = { 0, 0, 1 }, CURSOR_DOWN_Z_AUX = { 0, 0, -1 },
}
function Viewport:scrollByKey(key)
local info = MOVEMENT_KEYS[key]
if info then
local delta = 10
if info[4] then delta = 20 end
return self:clip(
self.x1 + delta*info[1],
self.y1 + delta*info[2],
self.z + info[3]
)
else
return self
end end
end end
@ -112,23 +177,53 @@ DwarfOverlay = defclass(DwarfOverlay, gui.Screen)
function DwarfOverlay:updateLayout() function DwarfOverlay:updateLayout()
self.df_layout = getPanelLayout() self.df_layout = getPanelLayout()
self.df_viewport = getViewportPos()
self.df_cursor = getCursorPos()
end end
local move_keys = { function DwarfOverlay:onShown()
'CURSOR_UP', 'CURSOR_DOWN', 'CURSOR_LEFT', 'CURSOR_RIGHT', self:updateLayout()
'CURSOR_UPLEFT', 'CURSOR_UPRIGHT', 'CURSOR_DOWNLEFT', 'CURSOR_DOWNRIGHT', end
'CURSOR_UP_FAST', 'CURSOR_DOWN_FAST', 'CURSOR_LEFT_FAST', 'CURSOR_RIGHT_FAST',
'CURSOR_UPLEFT_FAST', 'CURSOR_UPRIGHT_FAST', 'CURSOR_DOWNLEFT_FAST', 'CURSOR_DOWNRIGHT_FAST', function DwarfOverlay:onResize(w,h)
'CURSOR_UP_Z', 'CURSOR_DOWN_Z', 'CURSOR_UP_Z_AUX', 'CURSOR_DOWN_Z_AUX' self:updateLayout()
} end
function DwarfOverlay:getViewport(old_vp)
if old_vp then
return old_vp:resize(self.df_layout)
else
return Viewport.get(self.df_layout)
end
end
function DwarfOverlay:propagateMoveKeys(keys) function DwarfOverlay:propagateMoveKeys(keys)
for _,v in ipairs(move_keys) do for code,_ in pairs(MOVEMENT_KEYS) do
if keys[v] then if keys[code] then
self:sendInputToParent(v) self:sendInputToParent(code)
return v return code
end
end
end
function DwarfOverlay:simulateViewScroll(keys, anchor, no_clip_cursor)
local layout = self.df_layout
local cursor = getCursorPos()
anchor = anchor or cursor
if anchor and keys.A_MOVE_SAME_SQUARE then
self:getViewport():centerOn(anchor):set()
return 'A_MOVE_SAME_SQUARE'
end
for code,_ in pairs(MOVEMENT_KEYS) do
if keys[code] then
local vp = self:getViewport():scrollByKey(code)
if (cursor and not no_clip_cursor) or no_clip_cursor == false then
vp = vp:reveal(anchor,4,20,4,true)
end
vp:set()
return code
end end
end end
end end

@ -8,11 +8,19 @@ function getBuildingName(building)
return utils.call_with_string(building, 'getName') return utils.call_with_string(building, 'getName')
end end
function getBuildingCenter(building)
return xyz2pos(building.centerx, building.centery, building.z)
end
function listMechanismLinks(building) function listMechanismLinks(building)
local lst = {} local lst = {}
local function push(item, mode) local function push(item, mode)
if item then if item then
lst[#lst+1] = { obj = item, name = getBuildingName(item), mode = mode } lst[#lst+1] = {
obj = item, mode = mode,
name = getBuildingName(item),
center = getBuildingCenter(item)
}
end end
end end
@ -55,7 +63,7 @@ end
function MechanismList:init(building) function MechanismList:init(building)
local links = listMechanismLinks(building) local links = listMechanismLinks(building)
links[1].viewport = guidm.getViewportPos() links[1].viewport = self:getViewport()
links[1].cursor = guidm.getCursorPos() links[1].cursor = guidm.getCursorPos()
if #links <= 1 then if #links <= 1 then
links[1].mode = 'none' links[1].mode = 'none'
@ -95,18 +103,16 @@ function MechanismList:onRenderBody(dc)
dc:string("Enter", COLOR_LIGHTGREEN):string(": Switch") dc:string("Enter", COLOR_LIGHTGREEN):string(": Switch")
end end
function MechanismList:zoomToLink(link) function MechanismList:zoomToLink(link,back)
self:updateLayout()
df.global.world.selected_building = link.obj df.global.world.selected_building = link.obj
local cursor = link.cursor if back then
if not cursor then guidm.setCursorPos(link.cursor)
cursor = xyz2pos(link.obj.centerx, link.obj.centery, link.obj.z) self:getViewport(link.viewport):set()
else
guidm.setCursorPos(link.center)
self:getViewport():reveal(link.center, 5, 0, 10):set()
end end
guidm.setCursorPos(cursor)
guidm.revealInViewport(cursor, 5, link.viewport, self.df_layout)
end end
function MechanismList:changeSelected(delta) function MechanismList:changeSelected(delta)
@ -116,22 +122,23 @@ function MechanismList:changeSelected(delta)
end end
function MechanismList:onInput(keys) function MechanismList:onInput(keys)
if keys.STANDARDSCROLL_UP or keys.SECONDSCROLL_UP then if keys.SECONDSCROLL_UP then
self:changeSelected(-1) self:changeSelected(-1)
elseif keys.STANDARDSCROLL_DOWN or keys.SECONDSCROLL_DOWN then elseif keys.SECONDSCROLL_DOWN then
self:changeSelected(1) self:changeSelected(1)
elseif keys.LEAVESCREEN then elseif keys.LEAVESCREEN then
self:dismiss() self:dismiss()
if self.selected ~= 1 then if self.selected ~= 1 then
self:zoomToLink(self.links[1]) self:zoomToLink(self.links[1], true)
end end
elseif keys.SELECT_ALL then elseif keys.SELECT_ALL then
if self.selected > 1 then if self.selected > 1 then
self:init(self.links[self.selected].obj) self:init(self.links[self.selected].obj)
self.invalidate()
end end
elseif keys.SELECT then elseif keys.SELECT then
self:dismiss() self:dismiss()
elseif self:simulateViewScroll(keys) then
return
end end
end end