--@module = true local gui = require('gui') local widgets = require('gui.widgets') local INVISIBLE_FRAME = { frame_pen=gui.CLEAR_PEN, signature_pen=false, } -- MAIN BUG TRIGGERED HERE ----------------------------------------- BugDemoPanel = defclass(BugDemoPanel, widgets.Panel) BugDemoPanel.ATTRS { --frame_style = INVISIBLE_FRAME -- ^ This is the key part that triggers the bug. -- if style is nil, it will be nil and panel.lua::Panel_end_drag will never be called -- https://github.com/DFHack/dfhack/blob/e3165854e80411eec584c74949c73000f2aa0dcb/library/lua/gui/widgets/containers/panel.lua#L497C1-L498C1 -- even if user intends to finish drag } ----------------------------------------- function BugDemoPanel:onInput(keys) local key_info = {} for key, value in pairs(keys) do table.insert(key_info, string.format("%s=%s", tostring(key), tostring(value))) end table.sort(key_info) return BugDemoPanel.super.onInput(self, keys) end BugDemoWindow = defclass(BugDemoWindow, widgets.Window) BugDemoWindow.ATTRS { view_id = 'bugdemo_window', frame_title = 'Frame Style Bug Demo', resizable = true, resize_min = {w = 54, h = 20}, frame_inset = {l = 0, r = 0, t = 0, b = 0} } BugDemoScreen = defclass(BugDemoScreen, gui.ZScreen) BugDemoScreen.ATTRS { view_id = 'bugdemo_screen', focus_path = 'bugdemo', pass_movement_keys = true, defocusable = true } function BugDemoWindow:init() self.resizing_panels = false self:addviews{ BugDemoPanel { view_id = 'category_panel', frame = {l = 0, w = 22, t = 0, b = 1}, frame_inset = {l = 1, t = 0, b = 1, r = 1}, resizable = true, resize_anchors = {l = false, t = false, r = true, b = true}, resize_min = {w = 24}, on_resize_begin = self:callback('onPanelResizeBegin'), on_resize_end = self:callback('onPanelResizeEnd'), subviews = { widgets.Label { frame = {t = 0, l = 0}, text = 'category_panel', text_pen = COLOR_YELLOW } } }, widgets.Divider { view_id = 'divider1', frame = {l = 30, t = 0, b = 2, w = 1}, interior_b = true, frame_style_t = false }, widgets.Panel { view_id = 'item_panel', frame = {t = 1, b = 3, l = 25, r = 0}, resize_min = {w = 30, h = 10}, frame_inset = {l = 1, r = 0}, subviews = { widgets.Label { view_id = 'item_header', frame = {t = 0, l = 0, r = 0, h = 1}, text = 'item_panel', text_pen = COLOR_YELLOW } } }, widgets.HelpButton { command = "gui/stockmonitor", frame = {r = 0, t = 1} }, widgets.Divider { frame = {l = 0, r = 0, b = 2, h = 1}, frame_style_l = false, frame_style_r = false, interior_l = true }, widgets.Panel { frame = {l = 0, r = 0, b = 1, h = 1}, frame_inset = {l = 1, r = 1, t = 0, w = 100}, subviews = { widgets.HotkeyLabel { frame = {l = 0}, key = 'CUSTOM_CTRL_R', label = 'Button A', auto_width = true, on_activate = function() self:testResize() end }, widgets.HotkeyLabel { frame = {l = 25}, key = 'CUSTOM_CTRL_O', label = 'Button B', auto_width = true, on_activate = function() end } } } } end function BugDemoWindow:testResize() local category_panel = self.subviews.category_panel if category_panel then category_panel.frame.w = (category_panel.frame.w == 22) and 32 or 22 self:ensurePanelsRelSize() self:updateLayout() end end function BugDemoWindow:onPanelResizeBegin() self.resizing_panels = true end function BugDemoWindow:onPanelResizeEnd() self.resizing_panels = false self:ensurePanelsRelSize() self:updateLayout() end function BugDemoWindow:ensurePanelsRelSize() local category_panel = self.subviews.category_panel local item_panel = self.subviews.item_panel local divider1 = self.subviews.divider1 if not category_panel or not item_panel or not divider1 then return end local main_width = self.frame.w or 100 local min_item_width = 30 category_panel.frame.w = math.min(math.max(category_panel.frame.w, 24), main_width - min_item_width) local cat_width = category_panel.frame.w item_panel.frame.l = category_panel.visible and cat_width or 1 divider1.frame.l = item_panel.frame.l - 1 end function BugDemoWindow:onRenderBody(dc) if self.resizing_panels then self:ensurePanelsRelSize() self:updateLayout() end return BugDemoWindow.super.onRenderBody(self, dc) end function BugDemoWindow:preUpdateLayout() self:ensurePanelsRelSize() end function BugDemoWindow:onInput(keys) local key_info = {} for key, value in pairs(keys) do table.insert(key_info, string.format("%s=%s", tostring(key), tostring(value))) end table.sort(key_info) return BugDemoWindow.super.onInput(self, keys) end function BugDemoScreen:init() local optimal_size = self:calculateOptimalFrameSize() self:addviews{BugDemoWindow {frame = optimal_size}} end function BugDemoScreen:calculateOptimalFrameSize() local base_width = 100 local base_height = 35 local optimal_width = math.max(base_width, 90) local optimal_height = math.max(base_height, 25) return {w = optimal_width, h = optimal_height} end function BugDemoScreen:onDismiss() bug_demo_screen = nil end bug_demo_screen = bug_demo_screen or nil if dfhack_flags.module then return end -- Main entry point -- if not dfhack.isWorldLoaded() then -- qerror("This script requires a loaded world") -- end -- Check for existing instance if bug_demo_screen and not bug_demo_screen._native then bug_demo_screen = nil end if bug_demo_screen then local success, is_dismissed = pcall(function() return bug_demo_screen:isDismissed() end) if not success or is_dismissed then bug_demo_screen = nil else return end end -- Create and show new instance bug_demo_screen = BugDemoScreen{}:show()