-- Some simple dialog screens local _ENV = mkmodule('gui.dialogs') local gui = require('gui') local widgets = require('gui.widgets') --------------------------------- -- DialogWindow and DialogScreen -- DialogWindow = defclass(DialogWindow, widgets.Window) DialogWindow.ATTRS { frame={w=20, h=10}, label_params=DEFAULT_NIL, accept_label=DEFAULT_NIL, on_accept=DEFAULT_NIL, on_cancel=DEFAULT_NIL, } function DialogWindow:init(info) self:addviews{ widgets.Label{ view_id='label', frame={t=0, l=0, b=3}, text=self.label_params.text, }, widgets.HotkeyLabel{ frame={b=0, l=0, r=0}, label=self.accept_label, key='SELECT', auto_width=true, on_activate=self:callback('accept'), }, } end function DialogWindow:accept() if self.on_accept then self.on_accept() end self.parent_view:dismiss() end function DialogWindow:cancel() if self.on_cancel then self.on_cancel() end self.parent_view:dismiss() end function DialogWindow:computeFrame() local min_width = math.max(self.frame.w or 0, 20, #(self.frame_title or '') + 4) local label = self.subviews.label local text_area_width = label:getTextWidth() + 1 local text_height = label:getTextHeight() local sw, sh = dfhack.screen.getWindowSize() if text_height >= sh - 6 then -- account for scrollbar text_area_width = text_area_width + 2 end local fw, fh = math.max(min_width, text_area_width), text_height + 3 return gui.compute_frame_body(sw, sh, {w=fw, h=fh}, 1, 1, true) end function DialogWindow:onInput(keys) if (keys.LEAVESCREEN or keys._MOUSE_R) and self.on_cancel then self:cancel() end return DialogWindow.super.onInput(self, keys) end DialogScreen = defclass(DialogScreen, gui.ZScreenModal) DialogScreen.ATTRS { focus_path='MessageBox', title=DEFAULT_NIL, label_params={}, accept_label='Ok', on_accept=DEFAULT_NIL, on_cancel=DEFAULT_NIL, on_close=DEFAULT_NIL, } function DialogScreen:init() self:addviews{ DialogWindow{ frame_title=self.title, accept_label=self.accept_label, label_params=self.label_params, on_accept=self.on_accept, on_cancel=self.on_cancel, } } end function DialogScreen:onDismiss() if self.on_close then self.on_close() end end ------------------------ -- Convenience methods -- function showMessage(title, text, tcolor, on_close) return DialogScreen{ title=title, label_params={ text=text, text_pen=tcolor, }, on_close=on_close, }:show() end function showYesNoPrompt(title, text, tcolor, on_accept, on_cancel) return DialogScreen{ title=title, label_params={ text=text, text_pen=tcolor, }, accept_label='Yes, proceed', on_accept=on_accept, on_cancel=on_cancel, }:show() end ------------------------ -- Legacy classes -- MessageBox = defclass(MessageBox, gui.FramedScreen) MessageBox.focus_path = 'MessageBox' MessageBox.ATTRS{ frame_style = gui.WINDOW_FRAME, frame_inset = {l=1, t=1, b=1, r=0}, -- new attrs on_accept = DEFAULT_NIL, on_cancel = DEFAULT_NIL, on_close = DEFAULT_NIL, } function MessageBox:init(info) self:addviews{ widgets.Label{ view_id = 'label', text = info.text, text_pen = info.text_pen, frame = { l = 0, t = 0 }, auto_height = true } } end function MessageBox:getWantedFrameSize() local label = self.subviews.label local width = math.max(self.frame_width or 0, 20, #(self.frame_title or '') + 4) local text_area_width = label:getTextWidth() + 1 local text_height = label:getTextHeight() local sw, sh = dfhack.screen.getWindowSize() if text_height > sh - 4 then -- account for scrollbar text_area_width = text_area_width + 2 end return math.max(width, text_area_width), text_height end function MessageBox:onRenderFrame(dc,rect) MessageBox.super.onRenderFrame(self,dc,rect) if self.on_accept then dc:seek(rect.x1+2,rect.y2):key('LEAVESCREEN'):string('/'):key('SELECT') end end function MessageBox:onDestroy() if self.on_close then self.on_close() end end function MessageBox:onInput(keys) if keys.SELECT or keys.LEAVESCREEN or keys._MOUSE_R then self:dismiss() if keys.SELECT and self.on_accept then self.on_accept() elseif (keys.LEAVESCREEN or keys._MOUSE_R) and self.on_cancel then self.on_cancel() end return true end self:inputToSubviews(keys) return true end InputBox = defclass(InputBox, MessageBox) InputBox.focus_path = 'InputBox' InputBox.ATTRS{ on_input = DEFAULT_NIL, } function InputBox:preinit(info) info.on_accept = nil end function InputBox:init(info) self:addviews{ widgets.EditField{ view_id = 'edit', text = info.input, text_pen = info.input_pen, frame = { l = 0, r = 0, h = 1 }, } } end function InputBox:getWantedFrameSize() local mw, mh = InputBox.super.getWantedFrameSize(self) self.subviews.edit.frame.t = mh+1 return mw, mh+2 end function InputBox:onInput(keys) if keys.SELECT then self:dismiss() if self.on_input then self.on_input(self.subviews.edit.text) end return true elseif keys.LEAVESCREEN or keys._MOUSE_R then self:dismiss() if self.on_cancel then self.on_cancel() end return true end self:inputToSubviews(keys) return true end function showInputPrompt(title, text, tcolor, input, on_input, on_cancel, min_width) local ib = InputBox{ frame_title = title, text = text, text_pen = tcolor, input = input, on_input = on_input, on_cancel = on_cancel, frame_width = min_width, } ib:show() return ib end ListBox = defclass(ListBox, MessageBox) ListBox.focus_path = 'ListBox' ListBox.ATTRS{ with_filter = false, dismiss_on_select = true, dismiss_on_select2 = true, cursor_pen = DEFAULT_NIL, select_pen = DEFAULT_NIL, on_select = DEFAULT_NIL, on_select2 = DEFAULT_NIL, select2_hint = DEFAULT_NIL, row_height = DEFAULT_NIL, list_frame_inset = DEFAULT_NIL, } function ListBox:preinit(info) info.on_accept = nil end function ListBox:init(info) local spen = dfhack.pen.parse(COLOR_CYAN, self.select_pen, nil, false) local cpen = dfhack.pen.parse(COLOR_LIGHTCYAN, self.cursor_pen or self.select_pen, nil, true) local list_widget = widgets.List if self.with_filter then list_widget = widgets.FilteredList end local on_submit2 if self.select2_hint or self.on_select2 then on_submit2 = function(sel, obj) if self.dismiss_on_select2 then self:dismiss() end if self.on_select2 then self.on_select2(sel, obj) end local cb = obj.on_select2 if cb then cb(obj, sel) end end end self:addviews{ list_widget{ view_id = 'list', selected = info.selected, choices = info.choices, icon_width = info.icon_width, text_pen = spen, cursor_pen = cpen, on_submit = function(sel,obj) if self.dismiss_on_select then self:dismiss() end if self.on_select then self.on_select(sel, obj) end local cb = obj.on_select or obj[2] if cb then cb(obj, sel) end end, on_submit2 = on_submit2, frame = { l = 0, r = 0}, frame_inset = self.list_frame_inset, row_height = self.row_height, } } end function ListBox:onRenderFrame(dc,rect) ListBox.super.onRenderFrame(self,dc,rect) if self.select2_hint then dc:seek(rect.x1+2,rect.y2):string('Shift-Click', COLOR_LIGHTGREEN):string(': '..self.select2_hint, COLOR_GREY) end end function ListBox:getWantedFrameSize() local mw, mh = ListBox.super.getWantedFrameSize(self) local list = self.subviews.list list.frame.t = mh+1 return math.max(mw, list:getContentWidth()), mh+3+math.min(18,list:getContentHeight()) end function ListBox:onInput(keys) if keys.LEAVESCREEN or keys._MOUSE_R then self:dismiss() if self.on_cancel then self.on_cancel() end return true end self:inputToSubviews(keys) return true end function showListPrompt(title, text, tcolor, choices, on_select, on_cancel, min_width, filter) local lb = ListBox{ frame_title = title, text = text, text_pen = tcolor, choices = choices, on_select = on_select, on_cancel = on_cancel, frame_width = min_width, with_filter = filter, } lb:show() return lb end return _ENV