diff --git a/changelog.txt b/changelog.txt index d9d0d44ea..611f2d28a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -29,6 +29,7 @@ Template for new versions: ## New Tools ## New Features +- `gui/quickcmd`: added custom command names and option to display command output ## Fixes diff --git a/gui/quickcmd.lua b/gui/quickcmd.lua index c1e986698..bc86d89e9 100644 --- a/gui/quickcmd.lua +++ b/gui/quickcmd.lua @@ -34,17 +34,33 @@ local json = require('json') local gui = require('gui') local widgets = require('gui.widgets') -local CONFIG_FILE = 'dfhack-config/quickcmd.json' +local CONFIG_FILE_OLD = 'dfhack-config/quickcmd.json' +local CONFIG_FILE = 'dfhack-config/quickcmd-v2.json' local HOTKEYWIDTH = 7 +local OUTWIDTH = 4 local HOTKEYS = 'asdfghjklqwertyuiopzxcvbnm' +local function save_commands(data) + json.encode_file(data, CONFIG_FILE) +end + local function load_commands() + -- Try to load from new config file first local ok, data = pcall(json.decode_file, CONFIG_FILE) - return ok and data or {} -end + if ok then return data end -local function save_commands(data) - json.encode_file(data, CONFIG_FILE) + -- New file doesn't exist or is invalid - try old file for migration + ok, data = pcall(json.decode_file, CONFIG_FILE_OLD) + if not ok then return {} end + + -- Migrate old string format to new object format (in-memory only) + for i, cmd in ipairs(data) do + if type(cmd) == 'string' then + data[i] = {command = cmd, name = '', show_output = false} + end + end + + return data end QCMDDialog = defclass(QCMDDialog, widgets.Window) @@ -61,12 +77,12 @@ function QCMDDialog:init(info) self:addviews{ widgets.Label{ frame={t=0}, - text={{text='Hotkey', width=HOTKEYWIDTH}, ' Command'}, + text={{text='Hotkey', width=HOTKEYWIDTH}, {text='Out', width=OUTWIDTH}, 'Name/Command'}, visible=function() return #self.commands > 0 end, }, widgets.List{ view_id='list', - frame={t=2, b=3}, + frame={t=2, b=4}, on_submit=self:callback('submit'), }, widgets.Label{ @@ -75,53 +91,132 @@ function QCMDDialog:init(info) visible=function() return #self.commands == 0 end, }, widgets.HotkeyLabel{ - frame={b=1, l=0}, + frame={b=2, l=0}, key='CUSTOM_SHIFT_A', label='Add command', auto_width=true, on_activate=self:callback('onAddCommand'), }, widgets.HotkeyLabel{ - frame={b=1, l=19}, + frame={b=2, l=19}, key='CUSTOM_SHIFT_D', label='Delete command', auto_width=true, on_activate=self:callback('onDelCommand'), }, widgets.HotkeyLabel{ - frame={b=0, l=0}, + frame={b=1, l=0}, key='CUSTOM_SHIFT_E', label='Edit command', auto_width=true, on_activate=self:callback('onEditCommand'), }, + widgets.HotkeyLabel{ + frame={b=1, l=19}, + key='CUSTOM_SHIFT_N', + label='Edit name', + auto_width=true, + on_activate=self:callback('onSetName'), + }, + widgets.HotkeyLabel{ + frame={b=0, l=0}, + key='CUSTOM_SHIFT_O', + label='Capture output', + auto_width=true, + on_activate=self:callback('onToggleOutput'), + }, } self:updateList() end function QCMDDialog:submit(idx, choice) + local cmd_obj = self.commands[idx] + + if cmd_obj.show_output then + self:showCommandOutput(cmd_obj.command, cmd_obj.name) + else + local screen = self.parent_view + dfhack.screen.hideGuard(screen, function() + dfhack.run_command(cmd_obj.command) + end) + screen:dismiss() + end +end + +function QCMDDialog:showCommandOutput(command, name) + local output = dfhack.run_command_silent(command) + + -- Dismiss the quickcmd dialog before showing output local screen = self.parent_view - dfhack.screen.hideGuard(screen, function() - dfhack.run_command(choice.command) - end) screen:dismiss() + + local OutputDialog = defclass(OutputDialog, gui.ZScreenModal) + OutputDialog.ATTRS{ + focus_path='quickcmd_output', + command='', + name='', + output='', + } + + function OutputDialog:init() + local title + if self.name and self.name ~= '' then + title = self.name .. ': ' .. self.command + else + title = self.command + end + self:addviews{ + widgets.Window{ + frame_title=title, + frame={w=80, h=25}, + resizable=true, + resize_min={h=10, w=40}, + subviews={ + widgets.WrappedLabel{ + view_id='output', + frame={t=0, l=0, r=0, b=2}, + text_to_wrap=self.output or 'No output', + scroll_keys=widgets.STANDARDSCROLL, + }, + widgets.HotkeyLabel{ + frame={b=0, l=0}, + key='LEAVESCREEN', + label='Close', + auto_width=true, + on_activate=self:callback('dismiss'), + }, + } + } + } + end + + if #output == 0 then + output = 'Command finished successfully' + end + + OutputDialog{command=command, name=name, output=output}:show() end function QCMDDialog:updateList() -- Build the list entries. local choices = {} - for i,command in ipairs(self.commands) do + for i,cmd_obj in ipairs(self.commands) do -- Get the hotkey for this entry. local hotkey = nil if i <= HOTKEYS:len() then hotkey = HOTKEYS:sub(i, i) end + -- Display name if set, otherwise display command + local display_text = cmd_obj.name and cmd_obj.name ~= '' and cmd_obj.name or cmd_obj.command + -- Store the entry. table.insert(choices, { - text={{text=hotkey or '', width=HOTKEYWIDTH}, ' ', command}, - command=command, + text={{text=hotkey or '', width=HOTKEYWIDTH}, {text=cmd_obj.show_output and '[X]' or '[ ]', width=OUTWIDTH}, display_text}, + command=cmd_obj.command, + name=cmd_obj.name, + show_output=cmd_obj.show_output, hotkey=hotkey and ('CUSTOM_' .. hotkey:upper()) or '', }) end @@ -148,7 +243,7 @@ function QCMDDialog:onAddCommand() COLOR_GREEN, '', function(command) - table.insert(self.commands, command) + table.insert(self.commands, {command=command, name='', show_output=false}) save_commands(self.commands) self:updateList() end @@ -165,7 +260,7 @@ function QCMDDialog:onDelCommand() -- Prompt for confirmation. dlg.showYesNoPrompt( 'Delete command', - 'Are you sure you want to delete this command: ' .. NEWLINE .. item.command, + 'Are you sure you want to delete this command: ' .. NEWLINE .. self.commands[index].command, COLOR_GREEN, function() table.remove(self.commands, index) @@ -187,15 +282,49 @@ function QCMDDialog:onEditCommand() 'Edit command', 'Enter command:', COLOR_GREEN, - item.command, + self.commands[index].command, function(command) - self.commands[index] = command + self.commands[index].command = command + save_commands(self.commands) + self:updateList() + end + ) +end + +function QCMDDialog:onSetName() + -- Get the selected command. + local index, item = self.subviews.list:getSelected() + if not item then + return + end + + -- Prompt for new name. + dlg.showInputPrompt( + 'Set name', + 'Enter name:', + COLOR_GREEN, + self.commands[index].name or '', + function(name) + self.commands[index].name = name save_commands(self.commands) self:updateList() end ) end +function QCMDDialog:onToggleOutput() + -- Get the selected command. + local index, item = self.subviews.list:getSelected() + if not item then + return + end + + -- Toggle the show_output flag. + self.commands[index].show_output = not self.commands[index].show_output + save_commands(self.commands) + self:updateList() +end + QCMDScreen = defclass(QCMDScreen, gui.ZScreen) QCMDScreen.ATTRS { focus_path='quickcmd',