diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst index f6f8920c2..81415e3a9 100644 --- a/docs/Quickstart.rst +++ b/docs/Quickstart.rst @@ -99,37 +99,33 @@ How do DFHack in-game windows work? Many DFHack tools have graphical interfaces that appear in-game. You can tell which windows belong to DFHack tools because they will have the word "DFHack" -printed across their bottom frame edge. DFHack provides a custom windowing system -that gives the player a lot of control over where the windows appear and whether -they capture keyboard and mouse input. - -The DFHack windowing system allows you to use DFHack tools without interrupting -the game. That is, if the game is unpaused, it will continue to run while a -DFHack window is open. You can also interact with the map, scrolling it with the -keyboard or mouse and selecting units, buildings, and items. Some tools will -capture all keyboard input, such as tools with editable text fields, and some will -force-pause the game if it makes sense to, like `gui/quickfort`, since you cannot -interact with the map normally while trying to apply a blueprint. +printed across their bottom frame edge. DFHack provides an advanced windowing +system that gives the player a lot of control over where the windows appear and +whether they capture keyboard and mouse input. + +The DFHack windowing system allows multiple overlapping windows to be active at +once. The one with the highlighted title bar has focus and will receive anything +you type at the keyboard. Hit Esc or right click to close the window or cancel +the current operation. You can click anywhere on the screen that is not a +DFHack window to unfocus the window and let it just sit in the background. It won't +respond to key presses or mouse clicks until you click on it again to give it +focus. You can right click directly on an unfocused window to close it without +left clicking to activate it first. DFHack windows are draggable from the title bar or from anywhere on the window that doesn't have a mouse-clickable widget on it. Many are resizable as well (if the tool window has components that can reasonably be resized). -DFHack windows close with a right mouse click or keyboard Esc, but if you -want to keep a DFHack tool open while you interact with the game, you can click the -pin in the upper right corner of the DFHack window or hit Alt-L so -that the pin turns green. The DFHack window will then ignore right clicks and -Esc key presses that would otherwise close the window. This is especially -useful for the configuration tool windows for the automation tools. For example, -you can pin the `gui/autochop` window, set it to minimal mode, and let it sit -there monitoring your logging industry as you play, using it as a live status -window. Note that you can still right click *on* the DFHack tool window to close -it, even when it is pinned. - -You can have multiple DFHack tool windows on the screen at the same time. The -one that is receiving keyboard input has a highlighted title bar and will appear -over other windows if dragged across them. Clicking on a DFHack window that is not -currently active will bring it to the foreground and make it the active window. +You can generally use DFHack tools without interrupting the game. That is, if the +game is unpaused, it can continue to run while a DFHack window is open. Many tools +will initially pause the game to let you focus on the task at hand, but you can +unpause like normal if you want. You can also interact with the map, scrolling it +with the keyboard or mouse and selecting units, buildings, and items. Some tools +will capture all keyboard input, such as tools with editable text fields, and some +will force-pause the game if it makes sense to, like `gui/quickfort`, since you +cannot interact with the map normally while trying to apply a blueprint. Windows +for tools that force-pause the game will have a pause icon in their upper right +corner to indicate which tool is responsible for the pausing. Where do I go next? ------------------- @@ -161,10 +157,11 @@ You can enable it from the GUI, so you don't need to run `enable autochop `` Saves all the current manager orders in a file. diff --git a/library/Core.cpp b/library/Core.cpp index bdd38799f..98582d8cd 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -804,7 +804,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v if (!plug->can_be_enabled()) continue; con.print( - "%20s\t%-3s%s\n", + "%21s %-3s%s\n", (plug->getName()+":").c_str(), plug->is_enabled() ? "on" : "off", plug->can_set_enabled() ? "" : " (controlled internally)" diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 4b6bf6517..2274c8794 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -13,16 +13,18 @@ CLEAR_PEN = to_pen{tile=909, ch=32, fg=0, bg=0, write_to_lower=true} TRANSPARENT_PEN = to_pen{tile=0, ch=0} KEEP_LOWER_PEN = to_pen{ch=32, fg=0, bg=0, keep_lower=true} -local FAKE_INPUT_KEYS = { +local MOUSE_KEYS = { _MOUSE_L = true, _MOUSE_R = true, _MOUSE_M = true, _MOUSE_L_DOWN = true, _MOUSE_R_DOWN = true, _MOUSE_M_DOWN = true, - _STRING = true, } +local FAKE_INPUT_KEYS = copyall(MOUSE_KEYS) +FAKE_INPUT_KEYS._STRING = true + function simulateInput(screen,...) local keys = {} local function push_key(arg) @@ -692,8 +694,35 @@ end ----------------------------- ZScreen = defclass(ZScreen, Screen) +ZScreen.ATTRS{ + initial_pause=true, + force_pause=false, + pass_pause=true, + pass_movement_keys=false, + pass_mouse_clicks=true, +} + +function ZScreen:init() + self.saved_pause_state = df.global.pause_state + if self.initial_pause then + df.global.pause_state = true + end + self.defocused = false +end + +function ZScreen:dismiss() + ZScreen.super.dismiss(self) + if self.force_pause or self.initial_pause then + -- never go from unpaused to paused, just from paused to unpaused + df.global.pause_state = df.global.pause_state and self.saved_pause_state + end +end +-- this is necessary for middle-click map scrolling to function function ZScreen:onIdle() + if self.force_pause then + df.global.pause_state = true + end if self._native and self._native.parent then self._native.parent:logic() end @@ -704,17 +733,15 @@ function ZScreen:render(dc) ZScreen.super.render(self, dc) end -function ZScreen:isOnTop() - return dfhack.gui.getCurViewscreen(true) == self._native -end - -function ZScreen:togglePinned() - self.pinned = not self.pinned +function ZScreen:hasFocus() + return not self.defocused + and dfhack.gui.getCurViewscreen(true) == self._native end function ZScreen:onInput(keys) - if not self:isOnTop() then - if keys._MOUSE_L_DOWN and self:isMouseOver() then + local has_mouse = self:isMouseOver() + if not self:hasFocus() then + if (keys._MOUSE_L_DOWN or keys._MOUSE_R_DOWN) and has_mouse then self:raise() else self:sendInputToParent(keys) @@ -734,30 +761,42 @@ function ZScreen:onInput(keys) return end - if keys.CUSTOM_ALT_L then - self:togglePinned() + if self.pass_mouse_clicks and keys._MOUSE_L_DOWN and not has_mouse then + self.defocused = true + self:sendInputToParent(keys) return - end - - if (self:isMouseOver() or not self.pinned) - and (keys.LEAVESCREEN or keys._MOUSE_R_DOWN) then + elseif keys.LEAVESCREEN or keys._MOUSE_R_DOWN then self:dismiss() - -- ensure underlying DF screens don't also react to the click + -- ensure underlying DF screens don't also react to the rclick df.global.enabler.mouse_rbut_down = 0 df.global.enabler.mouse_rbut = 0 return - end - - if not keys._MOUSE_L or not self:isMouseOver() then - self:sendInputToParent(keys) + else + local passit = self.pass_pause and keys.D_PAUSE + if not passit and self.pass_mouse_clicks then + for key in pairs(MOUSE_KEYS) do + if keys[key] then + passit = true + break + end + end + end + if not passit and self.pass_movement_keys then + passit = require('gui.dwarfmode').getMapKey(keys) + end + if passit then + self:sendInputToParent(keys) + end + return end end function ZScreen:raise() - if self:isDismissed() or self:isOnTop() then + if self:isDismissed() or self:hasFocus() then return self end dscreen.raise(self) + self.defocused = false return self end @@ -809,8 +848,7 @@ local BASE_FRAME = { title_pen = to_pen{ fg=COLOR_BLACK, bg=COLOR_GREY }, inactive_title_pen = to_pen{ fg=COLOR_GREY, bg=COLOR_BLACK }, signature_pen = to_pen{ fg=COLOR_GREY, bg=COLOR_BLACK }, - pinned_pen = to_pen{tile=779, ch=216, fg=COLOR_GREY, bg=COLOR_GREEN}, - unpinned_pen = to_pen{tile=782, ch=216, fg=COLOR_GREY, bg=COLOR_BLACK}, + paused_pen = to_pen{tile=782, ch=216, fg=COLOR_GREY, bg=COLOR_BLACK}, } local function make_frame(name, double_line) @@ -839,10 +877,8 @@ THIN_FRAME = make_frame('Thin', false) -- for compatibility with pre-steam code GREY_LINE_FRAME = WINDOW_FRAME -BOUNDARY_FRAME = PANEL_FRAME -GREY_FRAME = MEDIUM_FRAME -function paint_frame(dc,rect,style,title,show_pin,pinned,inactive) +function paint_frame(dc,rect,style,title,inactive, pause_forced) local pen = style.frame_pen local x1,y1,x2,y2 = dc.x1+rect.x1, dc.y1+rect.y1, dc.x1+rect.x2, dc.y1+rect.y2 dscreen.paintTile(style.lt_frame_pen or pen, x1, y1) @@ -867,27 +903,19 @@ function paint_frame(dc,rect,style,title,show_pin,pinned,inactive) x, y1, tstr) end - if show_pin then - if pinned and style.pinned_pen then - local pin_texpos = dfhack.textures.getGreenPinTexposStart() - if pin_texpos == -1 then - dscreen.paintTile(style.pinned_pen, x2-1, y1) - else - dscreen.paintTile(style.pinned_pen, x2-2, y1-1, nil, pin_texpos+0) - dscreen.paintTile(style.pinned_pen, x2-1, y1-1, nil, pin_texpos+1) - dscreen.paintTile(style.pinned_pen, x2-2, y1, nil, pin_texpos+2) - dscreen.paintTile(style.pinned_pen, x2-1, y1, nil, pin_texpos+3) - end - elseif not pinned and style.unpinned_pen then - local pin_texpos = dfhack.textures.getRedPinTexposStart() - if pin_texpos == -1 then - dscreen.paintTile(style.unpinned_pen, x2-1, y1) - else - dscreen.paintTile(style.unpinned_pen, x2-2, y1-1, nil, pin_texpos+0) - dscreen.paintTile(style.unpinned_pen, x2-1, y1-1, nil, pin_texpos+1) - dscreen.paintTile(style.unpinned_pen, x2-2, y1, nil, pin_texpos+2) - dscreen.paintTile(style.unpinned_pen, x2-1, y1, nil, pin_texpos+3) - end + if pause_forced then + -- get the tiles for the activated pause symbol + local pause_texpos_ul = dfhack.screen.findGraphicsTile('INTERFACE_BITS', 18, 28) + local pause_texpos_ur = dfhack.screen.findGraphicsTile('INTERFACE_BITS', 19, 28) + local pause_texpos_ll = dfhack.screen.findGraphicsTile('INTERFACE_BITS', 18, 29) + local pause_texpos_lr = dfhack.screen.findGraphicsTile('INTERFACE_BITS', 19, 29) + if not pause_texpos_ul then + dscreen.paintTile(style.paused_pen, x2-1, y1) + else + dscreen.paintTile(style.paused_pen, x2-2, y1-1, nil, pause_texpos_ul) + dscreen.paintTile(style.paused_pen, x2-1, y1-1, nil, pause_texpos_ur) + dscreen.paintTile(style.paused_pen, x2-2, y1, nil, pause_texpos_ll) + dscreen.paintTile(style.paused_pen, x2-1, y1, nil, pause_texpos_lr) end end end diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 4538c39df..ab4dad88e 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -81,7 +81,6 @@ Panel.ATTRS { resize_min = DEFAULT_NIL, on_resize_begin = DEFAULT_NIL, on_resize_end = DEFAULT_NIL, - pinnable = false, autoarrange_subviews = false, -- whether to automatically lay out subviews autoarrange_gap = 0, -- how many blank lines to insert between widgets } @@ -272,21 +271,21 @@ local function Panel_on_double_click(self) Panel_update_frame(self, frame, true) end -local function panel_is_on_pin(self) +local function panel_mouse_is_on_pause_icon(self) local frame_rect = self.frame_rect local x,y = dscreen.getMousePos() return (x == frame_rect.x2-2 or x == frame_rect.x2-1) and (y == frame_rect.y1-1 or y == frame_rect.y1) end -local function panel_is_pinnable(self) - return self.pinnable and self.parent_view and self.parent_view.togglePinned +local function panel_has_pause_icon(self) + return self.parent_view and self.parent_view.force_pause end function Panel:getMouseFramePos() local x,y = Panel.super.getMouseFramePos(self) if x then return x, y end - if panel_is_pinnable(self) and panel_is_on_pin(self) then + if panel_has_pause_icon(self) and panel_mouse_is_on_pause_icon(self) then local frame_rect = self.frame_rect return frame_rect.width - 3, 0 end @@ -320,12 +319,6 @@ function Panel:onInput(keys) end return true end - if panel_is_pinnable(self) and keys._MOUSE_L_DOWN then - if panel_is_on_pin(self) then - self.parent_view:togglePinned() - return true - end - end if Panel.super.onInput(self, keys) then return true end @@ -505,14 +498,11 @@ end function Panel:onRenderFrame(dc, rect) Panel.super.onRenderFrame(self, dc, rect) if not self.frame_style then return end - local pinned = nil - if self.pinnable then - pinned = self.parent_view and self.parent_view.pinned - end - local inactive = self.parent_view and self.parent_view.isOnTop - and not self.parent_view:isOnTop() - gui.paint_frame(dc, rect, self.frame_style, self.frame_title, - self.pinnable, pinned, inactive) + local inactive = self.parent_view and self.parent_view.hasFocus + and not self.parent_view:hasFocus() + local pause_forced = self.parent_view and self.parent_view.force_pause + gui.paint_frame(dc, rect, self.frame_style, self.frame_title, inactive, + pause_forced) if self.kbd_get_pos then local pos = self.kbd_get_pos() local pen = to_pen{fg=COLOR_GREEN, bg=COLOR_BLACK} @@ -535,7 +525,6 @@ Window.ATTRS { frame_background = gui.CLEAR_PEN, frame_inset = 1, draggable = true, - pinnable = true, } ------------------- diff --git a/library/lua/script-manager.lua b/library/lua/script-manager.lua index 008e9443e..c1b8c80d5 100644 --- a/library/lua/script-manager.lua +++ b/library/lua/script-manager.lua @@ -49,7 +49,7 @@ function list() -- just been added reload() for name,fn in pairs(enabled_map) do - print(('%20s\t%-3s'):format(name..':', fn() and 'on' or 'off')) + print(('%21s %-3s'):format(name..':', fn() and 'on' or 'off')) end end diff --git a/plugins/lua/hotkeys.lua b/plugins/lua/hotkeys.lua index 99ba08a05..c377666c2 100644 --- a/plugins/lua/hotkeys.lua +++ b/plugins/lua/hotkeys.lua @@ -297,6 +297,7 @@ end MenuScreen = defclass(MenuScreen, gui.ZScreen) MenuScreen.ATTRS { focus_path='hotkeys/menu', + initial_pause=false, hotspot=DEFAULT_NIL, } diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 4e750cb3f..64c976424 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -249,7 +249,12 @@ private: for (auto w : worn) { auto ty = w->getType(); - auto o = itemTypeMap.at(ty); + auto oo = itemTypeMap.find(ty); + if (oo == itemTypeMap.end()) + { + continue; + } + const df::job_type o = oo->second; int size = world->raws.creatures.all[w->getMakerRace()]->adultsize; std::string description; @@ -330,9 +335,13 @@ private: } } - const df::job_type j = itemTypeMap.at(ty); - orders[std::make_tuple(j, sub, size)] += count; - DEBUG(cycle).print("tailor: %s times %d of size %d ordered\n", ENUM_KEY_STR(job_type, j).c_str(), count, size); + auto jj = itemTypeMap.find(ty); + if (jj != itemTypeMap.end()) + { + const df::job_type j = jj->second; + orders[std::make_tuple(j, sub, size)] += count; + DEBUG(cycle).print("tailor: %s times %d of size %d ordered\n", ENUM_KEY_STR(job_type, j).c_str(), count, size); + } } } diff --git a/scripts b/scripts index cec17bfaa..39ee60363 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit cec17bfaaa777de62833c818316e33bad75fa8e6 +Subproject commit 39ee60363892b7b916d670b02d46a606e3969b7c