diff --git a/library/lua/gui.lua b/library/lua/gui.lua index abe1047c9..f9a45548f 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -245,6 +245,16 @@ end function Screen:onShown() 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 -- ------------------------ @@ -311,8 +321,7 @@ local function hint_coord(gap,hint) end end -function FramedScreen:updateLayout() - local sw, sh = dscreen.getWindowSize() +function FramedScreen:updateFrameSize(sw,sh) 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) @@ -322,9 +331,11 @@ function FramedScreen:updateLayout() self.frame_opaque = (gw == 0 and gh == 0) end -function FramedScreen:onRender() - self:updateLayout() +function FramedScreen:onResize(w,h) + self:updateFrameSize(w,h) +end +function FramedScreen:onRender() local rect = self.frame_rect local x1,y1,x2,y2 = rect.x1-1, rect.y1-1, rect.x2+1, rect.y2+1 diff --git a/library/lua/gui/dwarfmode.lua b/library/lua/gui/dwarfmode.lua index 2d19b2f03..dde5225a1 100644 --- a/library/lua/gui/dwarfmode.lua +++ b/library/lua/gui/dwarfmode.lua @@ -4,6 +4,7 @@ local _ENV = mkmodule('gui.dwarfmode') local gui = require('gui') local dscreen = dfhack.screen +local world_map = df.global.world.map AREA_MAP_WIDTH = 23 MENU_WIDTH = 30 @@ -40,71 +41,135 @@ function getPanelLayout() end function getCursorPos() - return copyall(df.global.cursor) + if df.global.cursor.x ~= -30000 then + return copyall(df.global.cursor) + end end function setCursorPos(cursor) df.global.cursor = cursor end -function getViewportPos() - return { - x = df.global.window_x, - y = df.global.window_y, - z = df.global.window_z - } +function clearCursorPos() + df.global.cursor = xyz2pos(nil) +end + +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 -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)) - } +function Viewport:resize(layout) + return Viewport.make( + (layout or getPanelLayout()).map, + self.x1, self.y1, self.z + ) end -function setViewportPos(view, layout) - local map = df.global.world.map - layout = layout or getPanelLayout() - local vp = clipViewport(view, layout) - df.global.window_x = vp.x - df.global.window_y = vp.y +function Viewport:set() + local vp = self:clip() + df.global.window_x = vp.x1 + df.global.window_y = vp.y1 df.global.window_z = vp.z return vp end -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 +function Viewport:clip(x,y,z) + return self:make( + math.max(0, math.min(x or self.x1, world_map.x_count-self.width)), + math.max(0, math.min(y or self.y1, world_map.y_count-self.height)), + math.max(0, math.min(z or self.z, world_map.z_count-1)) ) - return setViewportPos(view, layout) end -function isInViewport(layout,view,target,gap) +function Viewport:isVisibleXY(target,gap) gap = gap or 0 - 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 + return math.max(target.x-gap,0) >= self.x1 + and math.min(target.x+gap,world_map.x_count-1) <= self.x2 + and math.max(target.y-gap,0) >= self.y1 + and math.min(target.y+gap,world_map.y_count-1) <= self.y2 end -function revealInViewport(target,gap,view,layout) - layout = layout or getPanelLayout() +function Viewport:isVisible(target,gap) + gap = gap or 0 - 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 + return self:isVisibleXY(target,gap) and target.z == self.z +end + +function Viewport:centerOn(target) + return self:clip( + 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 @@ -112,23 +177,53 @@ DwarfOverlay = defclass(DwarfOverlay, gui.Screen) function DwarfOverlay:updateLayout() self.df_layout = getPanelLayout() - self.df_viewport = getViewportPos() - self.df_cursor = getCursorPos() end -local move_keys = { - 'CURSOR_UP', 'CURSOR_DOWN', 'CURSOR_LEFT', 'CURSOR_RIGHT', - 'CURSOR_UPLEFT', 'CURSOR_UPRIGHT', 'CURSOR_DOWNLEFT', 'CURSOR_DOWNRIGHT', - 'CURSOR_UP_FAST', 'CURSOR_DOWN_FAST', 'CURSOR_LEFT_FAST', 'CURSOR_RIGHT_FAST', - 'CURSOR_UPLEFT_FAST', 'CURSOR_UPRIGHT_FAST', 'CURSOR_DOWNLEFT_FAST', 'CURSOR_DOWNRIGHT_FAST', - 'CURSOR_UP_Z', 'CURSOR_DOWN_Z', 'CURSOR_UP_Z_AUX', 'CURSOR_DOWN_Z_AUX' -} +function DwarfOverlay:onShown() + self:updateLayout() +end + +function DwarfOverlay:onResize(w,h) + 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) - for _,v in ipairs(move_keys) do - if keys[v] then - self:sendInputToParent(v) - return v + for code,_ in pairs(MOVEMENT_KEYS) do + if keys[code] then + self:sendInputToParent(code) + 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 diff --git a/scripts/gui/mechanisms.lua b/scripts/gui/mechanisms.lua index b8c01b59a..fe45d4acd 100644 --- a/scripts/gui/mechanisms.lua +++ b/scripts/gui/mechanisms.lua @@ -8,11 +8,19 @@ function getBuildingName(building) return utils.call_with_string(building, 'getName') end +function getBuildingCenter(building) + return xyz2pos(building.centerx, building.centery, building.z) +end + function listMechanismLinks(building) local lst = {} local function push(item, mode) 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 @@ -55,7 +63,7 @@ end function MechanismList:init(building) local links = listMechanismLinks(building) - links[1].viewport = guidm.getViewportPos() + links[1].viewport = self:getViewport() links[1].cursor = guidm.getCursorPos() if #links <= 1 then links[1].mode = 'none' @@ -95,18 +103,16 @@ function MechanismList:onRenderBody(dc) dc:string("Enter", COLOR_LIGHTGREEN):string(": Switch") end -function MechanismList:zoomToLink(link) - self:updateLayout() - +function MechanismList:zoomToLink(link,back) df.global.world.selected_building = link.obj - local cursor = link.cursor - if not cursor then - cursor = xyz2pos(link.obj.centerx, link.obj.centery, link.obj.z) + if back then + guidm.setCursorPos(link.cursor) + self:getViewport(link.viewport):set() + else + guidm.setCursorPos(link.center) + self:getViewport():reveal(link.center, 5, 0, 10):set() end - guidm.setCursorPos(cursor) - - guidm.revealInViewport(cursor, 5, link.viewport, self.df_layout) end function MechanismList:changeSelected(delta) @@ -116,22 +122,23 @@ function MechanismList:changeSelected(delta) end function MechanismList:onInput(keys) - if keys.STANDARDSCROLL_UP or keys.SECONDSCROLL_UP then + if keys.SECONDSCROLL_UP then self:changeSelected(-1) - elseif keys.STANDARDSCROLL_DOWN or keys.SECONDSCROLL_DOWN then + elseif keys.SECONDSCROLL_DOWN then self:changeSelected(1) elseif keys.LEAVESCREEN then self:dismiss() if self.selected ~= 1 then - self:zoomToLink(self.links[1]) + self:zoomToLink(self.links[1], true) end elseif keys.SELECT_ALL then if self.selected > 1 then self:init(self.links[self.selected].obj) - self.invalidate() end elseif keys.SELECT then self:dismiss() + elseif self:simulateViewScroll(keys) then + return end end