diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 8c9655b69..a3b91af18 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4094,7 +4094,15 @@ Clicks that are not over any visible ZScreen element, of course, are passed through to the underlying viewscreen. If :kbd:`Esc` or the right mouse button is pressed, and the ZScreen widgets -don't otherwise handle them, then the top ZScreen is dismissed. +don't otherwise handle them, then the top ZScreen is dismissed. If the ZScreen +is "locked", then the screen is not dismissed and the input is passed on to the +underlying DF viewscreen. :kbd:`Alt`:kbd:`L` toggles the locked status if the +ZScreen widgets don't otherwise handle that key sequence. If you have a +``Panel`` with the ``lockable`` attribute set and a frame that has pens defined +for the lock icon (like ``Window`` widgets have by default), then a lock icon +will appear in the upper right corner of the frame. Clicking on this icon will +toggle the ZScreen ``locked`` status just as if :kbd:`Alt`:kbd:`L` had been +pressed. Keyboard input goes to the top ZScreen, as usual. If the subviews of the top ZScreen don't handle the input (i.e. they all return something falsey), the @@ -4122,14 +4130,16 @@ ZScreen provides the following functions: when the tool command is run and raise the existing dialog if it exists or show a new dialog if it doesn't. See the sample code below for an example. +* ``zscreen:toggleLocked()`` + + Toggles whether the window closes on :kbd:`ESC` or r-click (unlocked) or not + (locked). + * ``zscreen:isMouseOver()`` - If the ZScreen subclass has a subview with a ``view_id`` equal to "main", - then the mouse will be considered to be over the visible viewscreen elements - when ``self.subviews.main:getMouseFramePos()`` returns a position. Subclasses - can override this function if that logic is not appropriate, for example if - there are multiple independent windows being shown and this function should - return true if the mouse is over any of them. + The default implementation iterates over the direct subviews of the ZScreen + subclass and sees if ``getMouseFramePos()`` returns a position for any of + them. Subclasses can override this function if that logic is not appropriate. Here is an example skeleton for a ZScreen tool dialog:: @@ -4159,7 +4169,7 @@ Here is an example skeleton for a ZScreen tool dialog:: } function MyScreen:init() - self:addviews{MyWindow{view_id='main'}} + self:addviews{MyWindow{}} end function MyScreen:onDismiss() @@ -4315,6 +4325,11 @@ Has attributes: hitting :kbd:`Esc` (while resizing with the mouse or keyboard), or by calling ``Panel:setKeyboardResizeEnabled(false)`` (while resizing with the keyboard). +* ``lockable = bool`` (default: ``false``) + + Determines whether the panel will draw a lock icon in its frame. See + `ZScreen class`_ for details. + * ``autoarrange_subviews = bool`` (default: ``false``) * ``autoarrange_gap = int`` (default: ``0``) @@ -4376,7 +4391,7 @@ Window class ------------ Subclass of Panel; sets Panel attributes to useful defaults for a top-level -framed, draggable window. +framed, lockable, draggable window. ResizingPanel class ------------------- diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 4777f8186..426458159 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -706,6 +706,10 @@ function ZScreen:isOnTop() return dfhack.gui.getCurViewscreen(true) == self._native end +function ZScreen:toggleLocked() + self.locked = not self.locked +end + function ZScreen:onInput(keys) if not self:isOnTop() then if keys._MOUSE_L_DOWN and self:isMouseOver() then @@ -719,7 +723,13 @@ function ZScreen:onInput(keys) if ZScreen.super.onInput(self, keys) then return end - if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then + + if keys.CUSTOM_ALT_L then + self:toggleLocked() + return + end + + if not self.locked and (keys.LEAVESCREEN or keys._MOUSE_R_DOWN) then self:dismiss() -- ensure underlying DF screens don't also react to the click df.global.enabler.mouse_rbut_down = 0 @@ -732,7 +742,6 @@ function ZScreen:onInput(keys) end end --- move this viewscreen to the top of the stack (if it's not there already) function ZScreen:raise() if self:isDismissed() or self:isOnTop() then return self @@ -741,10 +750,10 @@ function ZScreen:raise() return self end --- subclasses should either annotate their viewable panel with view_id='main' --- or override this and return whether the mouse is over an owned screen element function ZScreen:isMouseOver() - return self.subviews.main and self.subviews.main:getMouseFramePos() or false + for _,sv in ipairs(self.subviews) do + if sv:getMouseFramePos() then return true end + end end -------------------------- @@ -777,9 +786,11 @@ GREY_LINE_FRAME = { rb_frame_pen = to_pen{ tile=917, ch=188, fg=COLOR_GREY, bg=COLOR_BLACK }, title_pen = to_pen{ fg=COLOR_BLACK, bg=COLOR_GREY }, signature_pen = to_pen{ fg=COLOR_GREY, bg=COLOR_BLACK }, + locked_pen = to_pen{tile=779, ch=216, fg=COLOR_GREY, bg=COLOR_GREEN}, + unlocked_pen = to_pen{tile=782, ch=216, fg=COLOR_GREY, bg=COLOR_BLACK}, } -function paint_frame(dc,rect,style,title) +function paint_frame(dc,rect,style,title,show_lock,locked) 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) @@ -802,6 +813,14 @@ function paint_frame(dc,rect,style,title) end dscreen.paintString(style.title_pen or pen, x, y1, tstr) end + + if show_lock then + if locked and style.locked_pen then + dscreen.paintTile(style.locked_pen, x2-1, y1) + elseif not locked and style.unlocked_pen then + dscreen.paintTile(style.unlocked_pen, x2-1, y1) + end + end end FramedScreen = defclass(FramedScreen, Screen) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index e95410fb2..4df6d07ff 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -81,6 +81,7 @@ Panel.ATTRS { resize_min = DEFAULT_NIL, on_resize_begin = DEFAULT_NIL, on_resize_end = DEFAULT_NIL, + lockable = false, autoarrange_subviews = false, -- whether to automatically lay out subviews autoarrange_gap = 0, -- how many blank lines to insert between widgets } @@ -464,7 +465,11 @@ end function Panel:onRenderFrame(dc, rect) Panel.super.onRenderFrame(self, dc, rect) if not self.frame_style then return end - gui.paint_frame(dc, rect, self.frame_style, self.frame_title) + local locked = nil + if self.lockable then + locked = self.parent_view and self.parent_view.locked + end + gui.paint_frame(dc, rect, self.frame_style, self.frame_title, self.lockable, locked) if self.kbd_get_pos then local pos = self.kbd_get_pos() local pen = to_pen{fg=COLOR_GREEN, bg=COLOR_BLACK} @@ -487,8 +492,20 @@ Window.ATTRS { frame_background = gui.CLEAR_PEN, frame_inset = 1, draggable = true, + lockable = true, } +function Window:onInput(keys) + if keys._MOUSE_L_DOWN and self.parent_view and self.parent_view.toggleLocked then + local x,y = dscreen.getMousePos() + local frame_rect = self.frame_rect + if x == frame_rect.x2-1 and y == frame_rect.y1 then + self.parent_view:toggleLocked() + end + end + return Window.super.onInput(self, keys) +end + ------------------- -- ResizingPanel -- -------------------