-- @description Keyboard Shortcuts Visualizer -- @version 1.13 -- @author MPL -- @about Script for showing keyboard shortcuts -- @website http://forum.cockos.com/showthread.php?t=188335 -- @changelog -- + Add MAC layout, thanks to RobinGShore [p=281595] local vrs = 1.13 -------------------------------------------------------------------------------- init globals for key in pairs(reaper) do _G[key]=reaper[key] end app_vrs = tonumber(GetAppVersion():match('[%d%.]+')) if app_vrs < 7 then return reaper.MB('This script require REAPER 7.0+','',0) end local ImGui if not reaper.ImGui_GetBuiltinPath then return reaper.MB('This script require ReaImGui extension','',0) end package.path = reaper.ImGui_GetBuiltinPath() .. '/?.lua' ImGui = require 'imgui' '0.9.3' local OS = reaper.GetOS() ismac = OS:match('Win')==nil -------------------------------------------------------------------------------- init external defaults EXT = { viewport_posX = 10, viewport_posY = 10, viewport_posW = 1024, viewport_posH = 300, categoriescnt = 16, search = '', section_ID = 0, layout = 0, extlayoutname = 'QWERTY', dev_char = 0, } for i =1, EXT.categoriescnt do EXT['category'..i..'_color'] = '' end -------------------------------------------------------------------------------- INIT data DATA = { ES_key = 'kb_shortcuts', UI_name = 'Keyboard Shortcuts Visualizer', upd = true, kb={}, actions= {}, section_ID = 0, selectedkey = '', reapervisiblemodifiers_mapping = { ['Ctrl'] = ImGui.Mod_Ctrl, ['Alt'] = ImGui.Mod_Alt, ['Shift'] = ImGui.Mod_Shift, ['Win'] = ImGui.Mod_Super, }, category={ {name = 'All', color = 0x0F9F0F}, {name = 'Item / Take', color = 0xa4c5ea, str = 'item,take,comp'}, {name = 'Track', color = 0xF8FF02, str='track'}, {name = 'FX', color = 0xFFe0a3, str = 'fx,mixer'}, {name = 'View / Navigation',color = 0x9de19a, str='view,grid,layout,marker,region,edit_cursor,play cursor,ruler,screenset,time_selection,toolbar'}, {name = 'Automation', color = 0xbca9e1, str = 'Automation,Envelope'}, {name = 'Edit', color = 0xedff71, str='edit'}, {name = 'Transport', color = 0xf06543, str = 'transport'}, {name = 'Custom', color = 0x4F3bFd, str='custom'}, {name = 'Controller/Wheel', color = 0x08605f, str = 'OSC_only,MIDI_CC_relative,mousewheel'}, {name = '3rd party', color = 0xF03353, str='Script:,SWS,reapack'}, }, sections ={ [0]='Main', [100]='Main (alt recording)', [32060]='MIDI Editor', [32061]='MIDI Event List Editor', [32062]='MIDI Inline Editor', [32063]='Media Explorer', [1]='Main Alt 1', [2]='Main Alt 2', [3]='Main Alt 3', [4]='Main Alt 4', [5]='Main Alt 5', [6]='Main Alt 6', [7]='Main Alt 7', [8]='Main Alt 8', [9]='Main Alt 9', [10]='Main Alt 10', [11]='Main Alt 11', [12]='Main Alt 12', [13]='Main Alt 13', [14]='Main Alt 14', [15]='Main Alt 15', [16]='Main Alt 16', }, layouts = { [0] = 'QWERTY', [1] = 'AZERTY (experimental)', --[2] = 'MIDI', } } if ismac ==true then DATA.reapervisiblemodifiers_mapping = { ['Cmd'] = ImGui.Mod_Ctrl, ['Opt'] = ImGui.Mod_Alt, ['Shift'] = ImGui.Mod_Shift, ['Control'] = ImGui.Mod_Super, } end -------------------------------------------------------------------------------- INIT UI locals for key in pairs(reaper) do _G[key]=reaper[key] end --local ctx -------------------------------------------------------------------------------- UI init variables UI = { tempcoloring = {}, popups = {},} -- font UI.font='Arial' UI.font1sz=15 UI.font2sz=15 UI.font3sz=12 -- style UI.pushcnt = 0 UI.pushcnt2 = 0 -- size / offset UI.spacingX = 2 UI.spacingY = 3 -- mouse UI.hoverdelay = 0.3 UI.hoverdelayshort = 0.1 -- colors UI.main_col = 0x7F7F7F -- grey UI.textcol = 0xFFFFFF UI.but_hovered = 0x878787 UI.windowBg = 0x303030 -- alpha UI.textcol_a_enabled = 1 UI.textcol_a_disabled = 0.5 -- special UI.butBg_green = 0x00B300 UI.butBg_red = 0xB31F0F -- size UI.main_W = 800 UI.main_H = 600 --keyb vis UI.release_time = 0.5 UI.main_butcol = 0x7F7F7F UI.main_butcol_other = 0x0F9F0F UI.main_butcol_script = 0xFF0F0F UI.main_butcol_sws = 0x0F0FFF function msg(s) if not s then return end if type(s) == 'boolean' then if s then s = 'true' else s = 'false' end end ShowConsoleMsg(s..'\n') end ------------------------------------------------------------------------------------------------------ function literalize(str) -- http://stackoverflow.com/questions/1745448/lua-plain-string-gsub if str then return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", function(c) return "%" .. c end) end end -------------------------------------------------------------------------------- function DATA:Init_GetColorByActionName(action_name) if not action_name then return end action_name = action_name:lower() for catID =1 , #DATA.category do local str = DATA.category[catID].str if str then if action_name:match(str:gsub('_', ' ')) then return DATA.category[catID].color end str = str:lower() if str:match(',') then for matchstr in str:gmatch('[^%,]+') do if action_name:match(matchstr:gsub('_', ' ')) then return DATA.category[catID].color end end end end end return DATA.category[1].color end -------------------------------------------------------------------------------- function DATA:Init_kbDefinition_ActionList() local section_ID = EXT.section_ID local maxactionscnt = 100000 local retval, name for cmdID = 0, maxactionscnt do local action_ID, action_name = kbd_enumerateActions( section_ID, cmdID ) if action_ID then local action_bindings = {} local shortcutscnt = CountActionShortcuts( section_ID, action_ID ) for shortcutidx = 1, shortcutscnt do local retval, shortcut_str = GetActionShortcutDesc( section_ID, action_ID, shortcutidx-1 ) local used_shortcuts_t = {} used_shortcuts_t.section_ID=section_ID used_shortcuts_t.action_ID=action_ID used_shortcuts_t.action_name=action_name used_shortcuts_t.shortcut_str=shortcut_str -- patch for parsing [Something] + [Num +] shortcut_str = shortcut_str:gsub('Num %+', 'Num plus') local modifierflags = 0 if shortcut_str:match('+') then for key in shortcut_str:gmatch('[^%+]+') do local modkey_match for mod in pairs(DATA.reapervisiblemodifiers_mapping) do if key:match(mod) then modifierflags = modifierflags|DATA.reapervisiblemodifiers_mapping[mod] modkey_match = true end end --any of mod keys if not modkey_match then used_shortcuts_t.mainkey = key:gsub('Num plus','Num +') end end else used_shortcuts_t.mainkey = shortcut_str:gsub('Num plus','Num +') end used_shortcuts_t.modifierflags=modifierflags used_shortcuts_t.shortcutidx= shortcutidx-1 used_shortcuts_t.color = DATA:Init_GetColorByActionName(used_shortcuts_t.action_name) DATA:Init_kbDefinition_fitUItoActionList(used_shortcuts_t) action_bindings[#action_bindings+1] = used_shortcuts_t end --local action_color = DATA:Init_GetColorByActionName(action_name) DATA.actions[action_ID] = {action_name = action_name,action_bindings=action_bindings,action_color=action_color} end end end -------------------------------------------------------------------------------- function DATA:Init_kbDefinition_fitUItoActionList(used_shortcuts_t) if not (used_shortcuts_t and used_shortcuts_t.mainkey) then return end local mainkey = used_shortcuts_t.mainkey for key in pairs(DATA.kb) do if DATA.kb[key].mainkey and DATA.kb[key].mainkey:lower() == mainkey:lower() then if not DATA.kb[key].bindings then DATA.kb[key].bindings = {} end local bindingID = used_shortcuts_t.modifierflags for key_shortcut in pairs(used_shortcuts_t) do if not DATA.kb[key].bindings[bindingID] then DATA.kb[key].bindings[bindingID] = {} end DATA.kb[key].bindings[bindingID][key_shortcut]=used_shortcuts_t[key_shortcut] end return end end end -------------------------------------------------------------------------------- function DATA:Init_kbDefinition_UI_MIDIOSC() DATA.kb['CC'] = { block = 1, level = 1, pos = 1} end -------------------------------------------------------------------------------- function DATA:Init_kbDefinition_UI() local extlayoutname = EXT.extlayoutname if not (DATA.extlayouts and extlayoutname and DATA.extlayouts[extlayoutname] and DATA.extlayouts[extlayoutname].BLOCKS) then return end for block in pairs(DATA.extlayouts[extlayoutname].BLOCKS) do if not DATA.extlayouts[extlayoutname].BLOCKS[block].LEVELS then goto nextlevel end for level in pairs(DATA.extlayouts[extlayoutname].BLOCKS[block].LEVELS) do local pos = 1 for keyID = 1, #DATA.extlayouts[extlayoutname].BLOCKS[block].LEVELS[level] do local keyt = DATA.extlayouts[extlayoutname].BLOCKS[block].LEVELS[level][keyID] local KBNAME = keyt.KBNAME local EXTW = keyt.EXTW or 1 local EXTH = keyt.EXTH or 1 local BINDINGNAME = keyt.BINDINGNAME if BINDINGNAME:match('C%d+') then local char = BINDINGNAME:match('C(%d+)') if tonumber(char) and string.char(tonumber(char)) then BINDINGNAME = string.char(tonumber(char)) end end local IMGUI = keyt.IMGUI DATA.kb[KBNAME] = { block = block, level = level, pos = pos, extw = EXTW or 1, exth = EXTH or 1, mainkey = BINDINGNAME} if IMGUI ~= 'dummy' and DATA.kb[KBNAME] then if tonumber(IMGUI) then DATA.kb[KBNAME].reaimguikeyID = IMGUI else if ImGui[IMGUI] then DATA.kb[KBNAME].reaimguikey = ImGui[IMGUI] end end end pos = pos + EXTW end end ::nextlevel:: end end -------------------------------------------------------------------------------- function UI.MAIN_calc_layout() if not (UI.calc_butW and UI.calc_butW[1]) then return end UI.calc_butW[2] = UI.calc_butW[1] UI.calc_butW[3] = UI.calc_butW[1] UI.calc_butW[4] = UI.calc_butW[1] UI.calc_butW[5] = UI.calc_butW[1] UI.calc_butW[6] = UI.calc_butW[1] local extlayoutname = EXT.extlayoutname if not (DATA.extlayouts and extlayoutname and DATA.extlayouts[extlayoutname] and DATA.extlayouts[extlayoutname].BLOCKS and DATA.extlayouts[extlayoutname].BLOCKS[1] and DATA.extlayouts[extlayoutname].BLOCKS[1].LEVELS) then return end -- 2nd level for level = 2, 6 do if DATA.extlayouts[extlayoutname].BLOCKS[1].LEVELS[level] then local but_cnt = #DATA.extlayouts[extlayoutname].BLOCKS[1].LEVELS[level] local but_cnt_ext = 0 for keyID = 1, but_cnt do local EXTW = 1 if DATA.extlayouts[extlayoutname].BLOCKS[1].LEVELS[level][keyID].EXTW then EXTW = DATA.extlayouts[extlayoutname].BLOCKS[1].LEVELS[level][keyID].EXTW end but_cnt_ext = but_cnt_ext + 1*EXTW end local xspacing = UI.spacingX*(but_cnt-1) UI.calc_butW[level] = ((UI.calc_mainblockw - xspacing) / but_cnt_ext ) end end UI.cachedlayoutsize = true end -------------------------------------------------------------------------------- function UI.MAIN_calc() -- define x/w UI.calc_butHref= 35 UI.calc_spacingX_wide = UI.spacingX * 4 UI.calc_spacingY_wide = UI.spacingY * 4 if not UI.calc_butW then UI.calc_butW = {} end UI.calc_blockoffs_X= {} UI.calc_butW[1] = math.floor((DATA.display_w - UI.calc_spacingX_wide*4 - UI.spacingX*17)/20) UI.calc_blockoffs_X[1] = UI.calc_spacingX_wide UI.calc_blockoffs_X[2] = UI.calc_spacingX_wide*2 + UI.calc_butW[1]*13 + UI.spacingX*12 UI.calc_blockoffs_X[3] = UI.calc_spacingX_wide*3 + UI.calc_butW[1]*16 + UI.spacingX*15 UI.calc_mainblockw = UI.calc_blockoffs_X[2] - UI.calc_blockoffs_X[1] - UI.calc_spacingX_wide if not UI.cachedlayoutsize then UI.MAIN_calc_layout() end -- define y/h UI.calc_butH = { math.floor(UI.calc_butHref*0.8), math.floor(UI.calc_butHref*1.2), UI.calc_butHref, UI.calc_butHref, UI.calc_butHref, UI.calc_butHref, } UI.calc_blockoffs_Y= { UI.calc_itemH+UI.spacingY, UI.calc_itemH+UI.spacingY*2+ UI.calc_butH[1] + UI.calc_spacingY_wide, UI.calc_itemH+UI.spacingY*2+ UI.calc_butH[1] + UI.calc_spacingY_wide + UI.calc_butH[2] + UI.spacingY, UI.calc_itemH+UI.spacingY*2+ UI.calc_butH[1] + UI.calc_spacingY_wide + UI.calc_butH[2] + UI.spacingY + UI.calc_butH[3] + UI.spacingY, UI.calc_itemH+UI.spacingY*2+ UI.calc_butH[1] + UI.calc_spacingY_wide + UI.calc_butH[2] + UI.spacingY + UI.calc_butH[3] + UI.spacingY + UI.calc_butH[3] + UI.spacingY, UI.calc_itemH+UI.spacingY*2+ UI.calc_butH[1] + UI.calc_spacingY_wide + UI.calc_butH[2] + UI.spacingY + UI.calc_butH[3] + UI.spacingY + UI.calc_butH[3] + UI.spacingY + UI.calc_butH[3] + UI.spacingY, UI.calc_itemH+UI.spacingY*2+ UI.calc_butH[1] + UI.calc_spacingY_wide + UI.calc_butH[2] + UI.spacingY + UI.calc_butH[3] + UI.spacingY + UI.calc_butH[3] + UI.spacingY + UI.calc_butH[3] + UI.spacingY + UI.calc_butH[3] + UI.calc_spacingY_wide, } -- define current action local subblockW = UI.calc_butW[1] * 7 + UI.calc_spacingX_wide + UI.spacingX * 5 UI.calc_KeyDetails_x = UI.calc_spacingX_wide + UI.calc_butW[1]*4 + UI.spacingX * 3 UI.calc_KeyDetails_y = UI.calc_blockoffs_Y[7] UI.calc_KeyDetails_w = subblockW UI.calc_KeyCategories_x = UI.calc_spacingX_wide UI.calc_KeyCategories_y = UI.calc_KeyDetails_y UI.calc_KeyCategories_w = UI.calc_butW[1]*4 + UI.spacingX * 2 UI.calc_ActList_x = UI.calc_spacingX_wide + UI.calc_butW[1]*4 + UI.spacingX * 3 UI.calc_ActList_y = UI.calc_KeyDetails_y UI.calc_ActList_w = UI.calc_KeyDetails_x - (UI.calc_KeyCategories_x + UI.calc_KeyCategories_w) - UI.calc_spacingX_wide -- combos UI.calc_combo1_x = UI.calc_blockoffs_X[3] UI.calc_combo1_y = UI.calc_blockoffs_Y[1] UI.calc_combo1_w = UI.calc_butW[1] * 4+ UI.spacingX*3 UI.calc_combo1_h = UI.calc_butH[1] UI.calc_combo2_x = UI.calc_blockoffs_X[2] UI.calc_combo2_y = UI.calc_blockoffs_Y[1] UI.calc_combo2_w = UI.calc_butW[1] * 3+ UI.spacingX*2 UI.calc_combo2_h = UI.calc_butH[1] end -------------------------------------------------------------------------------- function DATA:SetSelectionFromReimGuiKey(reaimguikey) for key in pairs(DATA.kb) do if DATA.kb[key].disabled ~= true and ( (DATA.kb[key].reaimguikey and DATA.kb[key].reaimguikey == reaimguikey ) or (DATA.kb[key].reaimguikeyID and DATA.kb[key].reaimguikeyID == DATA.input_char ) ) then DATA.selectedkey = key return end end end -------------------------------------------------------------------------------- function UI.HelpMarker(desc) if ImGui.BeginItemTooltip(ctx) then ImGui.PushTextWrapPos(ctx, ImGui.GetFontSize(ctx) * 35.0) ImGui.Text(ctx, desc) ImGui.PopTextWrapPos(ctx) ImGui.EndTooltip(ctx) end end -------------------------------------------------------------------------------- function UI.draw_KeyDetails_tooltip(key_src) if key_src == DATA.selectedkey then return end if ImGui.BeginItemTooltip(ctx) then local modifiers, Shift_flag, Ctrl_flag, Alt_flag, Win_flag if ismac ~= true then Shift_flag = DATA.reapervisiblemodifiers_mapping.Shift Ctrl_flag = DATA.reapervisiblemodifiers_mapping.Ctrl Alt_flag = DATA.reapervisiblemodifiers_mapping.Alt Win_flag = DATA.reapervisiblemodifiers_mapping.Win modifiers = { {str = '-', flags = 0}, {str = 'Shift', flags = Shift_flag}, {str = 'Ctrl', flags = Ctrl_flag}, {str = 'Shift+Ctrl', flags = Shift_flag|Ctrl_flag}, {str = 'Alt', flags = Alt_flag}, {str = 'Shift+Alt', flags = Shift_flag|Alt_flag}, {str = 'Ctrl+Alt', flags = Ctrl_flag|Alt_flag}, {str = 'Shift+Ctrl+Alt', flags = Shift_flag|Ctrl_flag|Alt_flag}, {str = 'Win', flags = Win_flag}, {str = 'Shift+Win', flags = Shift_flag|Win_flag}, {str = 'Ctrl+Win', flags = Ctrl_flag|Win_flag}, {str = 'Shift+Ctrl+Win', flags = Shift_flag|Ctrl_flag|Win_flag}, {str = 'Alt+Win', flags = Alt_flag|Win_flag}, {str = 'Shift+Alt+Win', flags = Shift_flag|Alt_flag|Win_flag}, {str = 'Ctrl+Alt+Win', flags = Ctrl_flag|Alt_flag|Win_flag}, {str = 'Shift+Ctrl+Alt+Win', flags = Shift_flag|Ctrl_flag|Alt_flag|Win_flag}, } else Command_flag = DATA.reapervisiblemodifiers_mapping.Cmd Shift_flag = DATA.reapervisiblemodifiers_mapping.Shift Option_flag = DATA.reapervisiblemodifiers_mapping.Opt Control_flag = DATA.reapervisiblemodifiers_mapping.Control modifiers = { {str = '-', flags = 0}, {str = 'Cmd', flags = Command_flag}, {str = 'Shift', flags = Shift_flag}, {str = 'Opt', flags = Option_flag}, {str = 'Control', flags = Control_flag}, {str = 'Cmd+Shift', flags = Command_flag|Shift_flag}, {str = 'Cmd+Opt', flags = Command_flag|Option_flag}, {str = 'Cmd+Control', flags = Command_flag|Control_flag}, {str = 'Shift+Opt', flags = Shift_flag|Option_flag}, {str = 'Shift+Control', flags = Shift_flag|Control_flag}, {str = 'Opt+Control', flags = Option_flag|Control_flag}, {str = 'Cmd+Shift+Opt', flags = Command_flag|Shift_flag|Option_flag}, {str = 'Cmd+Shift+Control', flags = Command_flag|Shift_flag|Control_flag}, {str = 'Cmd+Opt+Control', flags = Command_flag|Option_flag|Control_flag}, {str = 'Shift+Opt+Control', flags = Shift_flag|Option_flag|Control_flag}, {str = 'Cmd+Shift+Opt+Control', flags = Command_flag|Shift_flag|Option_flag|Control_flag}, } end for i = 1 , #modifiers do if DATA.kb[key_src].bindings and DATA.kb[key_src].bindings[modifiers[i].flags] then local strmod = modifiers[i].str if modifiers[i].str == '-' then strmod = 'No modifier' end ImGui.Selectable(ctx,strmod..':')ImGui.SameLine(ctx) ImGui.SetCursorPosX( ctx, 100 ) ImGui.Selectable(ctx,DATA.kb[key_src].bindings[modifiers[i].flags].action_name) end end ImGui.EndTooltip(ctx) end end -------------------------------------------------------------------------------- function UI.draw_ActionList() ImGui.SetCursorPos( ctx, UI.calc_ActList_x, UI.calc_ActList_y ) if ImGui.BeginChild( ctx, 'actlist', UI.calc_ActList_w, 0, ImGui.ChildFlags_None|ImGui.ChildFlags_Border, ImGui.WindowFlags_None|ImGui.WindowFlags_MenuBar ) then ImGui.PushStyleVar(ctx, ImGui.StyleVar_CellPadding,5,2) if ImGui.BeginMenuBar( ctx ) then if ImGui.BeginMenu(ctx, 'Search action list:') then ImGui.EndMenu(ctx) end local retval, buf = ImGui.InputText( ctx, '##Search', EXT.search,ImGui.InputTextFlags_AutoSelectAll )--|ImGui.InputTextFlags_EnterReturnsTrue if retval == true then EXT.search = buf DATA:Init_Search_ActionList() EXT:save() end ImGui.EndMenuBar( ctx ) end for actionID in pairs(DATA.actions) do if DATA.actions[actionID].fitsearch == true then ImGui.Selectable(ctx,DATA.actions[actionID].action_name ) if ImGui.BeginDragDropSource( ctx, ImGui.DragDropFlags_None ) then ImGui.SetDragDropPayload(ctx, 'actiondrop', actionID, ImGui.Cond_Always) ImGui.EndDragDropSource(ctx) end end end ImGui.PopStyleVar(ctx,1) ImGui.EndChild( ctx) end end -------------------------------------------------------------------------------- function UI.draw_KeyCategories() local indent = 7 ImGui.SetCursorPos( ctx, UI.calc_KeyCategories_x, UI.calc_KeyCategories_y + UI.calc_itemH*2+UI.spacingY*2 ) if ImGui.BeginChild( ctx, 'categories', UI.calc_KeyCategories_w, 0, ImGui.ChildFlags_None|ImGui.ChildFlags_Border, ImGui.WindowFlags_None|ImGui.WindowFlags_MenuBar ) then ImGui.PushStyleVar(ctx, ImGui.StyleVar_CellPadding,5,2) if ImGui.BeginMenuBar( ctx ) then if ImGui.BeginMenu(ctx, 'Categories') then ImGui.EndMenu(ctx) end ImGui.EndMenuBar( ctx ) end --ImGui.PushFont(ctx, DATA.font3) ImGui.Indent(ctx,indent) for catID = 1, #DATA.category do local retval, col_rgb = ImGui.ColorEdit3(ctx, '##cat'..catID, DATA.category[catID].color, ImGui.ColorEditFlags_None|ImGui.ColorEditFlags_NoInputs) if retval then DATA.category[catID].color = col_rgb EXT['category'..catID..'_color'] = col_rgb EXT:save() DATA:Init_LoadExtColors() end if ImGui.IsItemDeactivatedAfterEdit( ctx ) then DATA:CollectData() end ImGui.SameLine(ctx) ImGui.Selectable( ctx, DATA.category[catID].name ) end ImGui.Unindent(ctx,indent) --ImGui.PopFont(ctx) ImGui.PopStyleVar(ctx,1) ImGui.EndChild( ctx) end end -------------------------------------------------------------------------------- function UI.draw_KeyDetails(key_src0) local key_src = key_src0 if not key_src0 then key_src = DATA.selectedkey end if not (key_src ~= '' and DATA.kb[key_src]) then return end if key_src0 == nil then ImGui.SetCursorPos( ctx, UI.calc_KeyDetails_x, UI.calc_KeyDetails_y ) end if ImGui.BeginChild( ctx, 'currentkey'..key_src, 0, 0, ImGui.ChildFlags_None|ImGui.ChildFlags_Border, ImGui.WindowFlags_None|ImGui.WindowFlags_MenuBar ) then--UI.calc_KeyDetails_w ImGui.PushStyleVar(ctx, ImGui.StyleVar_CellPadding,5,2) if ImGui.BeginMenuBar( ctx ) then ImGui.Text(ctx,'Key details ['..(DATA.selectedkey:match('(.-)##') or DATA.selectedkey )..']:') --[[if ImGui.BeginMenu(ctx, 'Key details ['..DATA.selectedkey..']:') then --ImGui_MenuItem( ctx, 'Key details ['..DATA.selectedkey..']:') --ImGui.SeparatorText(ctx, 'Actions') ImGui.EndMenu(ctx) end]] ImGui.EndMenuBar( ctx ) end ImGui.PushFont(ctx, DATA.font3) local modifiers, Shift_flag, Ctrl_flag, Alt_flag, Win_flag if ismac ~= true then Shift_flag = DATA.reapervisiblemodifiers_mapping.Shift Ctrl_flag = DATA.reapervisiblemodifiers_mapping.Ctrl Alt_flag = DATA.reapervisiblemodifiers_mapping.Alt Win_flag = DATA.reapervisiblemodifiers_mapping.Win modifiers = { {str = '-', flags = 0}, {str = 'Shift', flags = Shift_flag}, {str = 'Ctrl', flags = Ctrl_flag}, {str = 'Shift+Ctrl', flags = Shift_flag|Ctrl_flag}, {str = 'Alt', flags = Alt_flag}, {str = 'Shift+Alt', flags = Shift_flag|Alt_flag}, {str = 'Ctrl+Alt', flags = Ctrl_flag|Alt_flag}, {str = 'Shift+Ctrl+Alt', flags = Shift_flag|Ctrl_flag|Alt_flag}, {str = 'Win', flags = Win_flag}, {str = 'Shift+Win', flags = Shift_flag|Win_flag}, {str = 'Ctrl+Win', flags = Ctrl_flag|Win_flag}, {str = 'Shift+Ctrl+Win', flags = Shift_flag|Ctrl_flag|Win_flag}, {str = 'Alt+Win', flags = Alt_flag|Win_flag}, {str = 'Shift+Alt+Win', flags = Shift_flag|Alt_flag|Win_flag}, {str = 'Ctrl+Alt+Win', flags = Ctrl_flag|Alt_flag|Win_flag}, {str = 'Shift+Ctrl+Alt+Win', flags = Shift_flag|Ctrl_flag|Alt_flag|Win_flag}, } else Command_flag = DATA.reapervisiblemodifiers_mapping.Cmd Shift_flag = DATA.reapervisiblemodifiers_mapping.Shift Option_flag = DATA.reapervisiblemodifiers_mapping.Opt Control_flag = DATA.reapervisiblemodifiers_mapping.Control modifiers = { {str = '-', flags = 0}, {str = 'Cmd', flags = Command_flag}, {str = 'Shift', flags = Shift_flag}, {str = 'Opt', flags = Option_flag}, {str = 'Control', flags = Control_flag}, {str = 'Cmd+Shift', flags = Command_flag|Shift_flag}, {str = 'Cmd+Opt', flags = Command_flag|Option_flag}, {str = 'Cmd+Control', flags = Command_flag|Control_flag}, {str = 'Shift+Opt', flags = Shift_flag|Option_flag}, {str = 'Shift+Control', flags = Shift_flag|Control_flag}, {str = 'Opt+Control', flags = Option_flag|Control_flag}, {str = 'Cmd+Shift+Opt', flags = Command_flag|Shift_flag|Option_flag}, {str = 'Cmd+Shift+Control', flags = Command_flag|Shift_flag|Control_flag}, {str = 'Cmd+Opt+Control', flags = Command_flag|Option_flag|Control_flag}, {str = 'Shift+Opt+Control', flags = Shift_flag|Option_flag|Control_flag}, {str = 'Cmd+Shift+Opt+Control', flags = Command_flag|Shift_flag|Option_flag|Control_flag}, } end if ImGui.BeginTable(ctx, 'currentkeytable', 2, ImGui.TableFlags_None|ImGui.TableFlags_BordersInnerV, 0, 0, 0) then ImGui.TableSetupColumn(ctx, 'Modifier', ImGui.TableColumnFlags_None|ImGui.TableColumnFlags_WidthFixed, 100, 0) ImGui.TableSetupColumn(ctx, 'Command', ImGui.TableColumnFlags_None|ImGui.TableColumnFlags_WidthStretch, 0.65, 1) ImGui.TableHeadersRow(ctx) for i = 1 , #modifiers do ImGui.TableNextRow(ctx, ImGui.TableRowFlags_None,0) -- remove + modifier ImGui.TableSetColumnIndex(ctx,0) ImGui.PushStyleVar(ctx, ImGui.StyleVar_SelectableTextAlign,1,1) local bindings if modifiers[i].flags and DATA.kb[key_src] and DATA.kb[key_src].bindings and DATA.kb[key_src].bindings[modifiers[i].flags] then bindings = DATA.kb[key_src].bindings[modifiers[i].flags] end if bindings then if ImGui.Button(ctx, 'X##'..i) then UI.popups['Remove shortcut'] = { mode = 0, trig = true, captions_csv = 'Remove current shortcut?', func_setval = function(retval, retvals_csv) local section = bindings.section_ID local cmdID = bindings.action_ID local shortcutidx = bindings.shortcutidx DeleteActionShortcut( section, cmdID, shortcutidx ) DATA.upd = true end } end end ImGui.SameLine(ctx) ImGui.Selectable(ctx,modifiers[i].str) ImGui.PopStyleVar(ctx,1) -- action ImGui.TableSetColumnIndex(ctx,1) local actionname = '-' if DATA.kb[key_src].bindings and DATA.kb[key_src].bindings[modifiers[i].flags] then actionname = DATA.kb[key_src].bindings[modifiers[i].flags].action_name end ImGui.Selectable(ctx,actionname) if ImGui.BeginDragDropTarget( ctx ) then local rv,cmdID = ImGui.AcceptDragDropPayload(ctx, 'actiondrop') if rv then DoActionShortcutDialog( hwnd, DATA.section_ID, cmdID, -1 ) --[[msg(payload) msg(1) DATA:CollectData()]] end ImGui.EndDragDropTarget( ctx ) end --[[ if retval then msg(payload) end]] end ImGui.EndTable(ctx) end ImGui.PopFont(ctx) ImGui.PopStyleVar(ctx,1) ImGui.EndChild( ctx) end end -------------------------------------------------------------------------------- function UI.draw_keyb() --test --[[ ImGui.SetCursorPos( ctx, UI.calc_blockoffs_X[1], UI.calc_blockoffs_Y[1]+80 ) ImGui.Button(ctx, 'test',UI.calc_mainblockw,20) ]] -- show character if EXT.dev_char ==1 then ImGui.SetCursorPos( ctx, 10, 50 ) ImGui.PushFont(ctx, DATA.font1) local rv, c = ImGui.GetInputQueueCharacter(ctx, 0) if DATA.input_char_hex then ImGui.Text(ctx,DATA.input_char..': '..DATA.input_char_hex) end ImGui.PopFont(ctx) end ImGui.PushFont(ctx, DATA.font2) local local_pos_x, local_pos_y for key in pairs(DATA.kb) do if key:match('dummy') then goto nextkey end local block = DATA.kb[key].block local level = DATA.kb[key].level local pos = DATA.kb[key].pos local extw = DATA.kb[key].extw or 1 local exth = DATA.kb[key].exth or 1 local butW = UI.calc_butW[level] or UI.calc_butW[1] local butH = UI.calc_butH[level] or UI.calc_butH[1] local_pos_x = math.floor(UI.calc_blockoffs_X[block] + (butW * (pos-1)) + UI.spacingX*(pos-1)) local_pos_y = UI.calc_blockoffs_Y[level] local butw = butW*extw+(extw-1)*UI.spacingX local buth = butH*exth+(exth-1)*UI.spacingY if block == 2 or block == 3 then local_pos_x = UI.calc_blockoffs_X[block] + (UI.calc_butW[1] * (pos-1)) + UI.spacingX*(pos-1) butw = UI.calc_butW[1]*extw+(extw-1)*UI.spacingX end ImGui.SetCursorPos( ctx, local_pos_x, local_pos_y ) local col = UI.draw_keyb_definecolor(DATA.kb[key]) local alphanormal0 = 0.5 UI.draw_keyb_handleKBpress(DATA.kb[key]) if DATA.kb[key].state_pressed == true then alphanormal = 0.8 else alphanormal = alphanormal0 end if DATA.kb[key].state_release_fall == true then alphanormal = alphanormal0 + alphanormal * (1-DATA.kb[key].state_release_alphamult) end ImGui.PushStyleColor(ctx, ImGui.Col_Button, math.floor(alphanormal*255)|(col<<8) ) ImGui.PushStyleColor(ctx, ImGui.Col_ButtonActive, math.floor(0.9*255)|(col<<8) ) ImGui.PushStyleColor(ctx, ImGui.Col_ButtonHovered, math.floor(0.6*255)|(col<<8) ) ImGui.BeginDisabled(ctx, DATA.kb[key].disabled or false) -- actually key local butx,buty,butx2,buty2=0,0,0,0 local key_name = key:gsub('\\n','\n') if key_name:match('C%d+') then local char = key_name:match('C(%d+)') if tonumber(char) and string.char(tonumber(char)) then key_name = utf8.char(tonumber(char)) end end if ImGui.Button(ctx, key_name,butw,buth) then DATA.selectedkey = key end butx, buty = ImGui.GetItemRectMin(ctx) butx2, buty2 = ImGui.GetItemRectMax(ctx) if DATA.kb[key].disabled~= true and DATA.kb[key].bindings then UI.draw_KeyDetails_tooltip(key) end -- selection if DATA.selectedkey~= '' and DATA.selectedkey == key then local draw_list = ImGui.GetWindowDrawList(ctx) ImGui.DrawList_AddRect( draw_list, butx, buty,butx2,buty2, 0xFFFFFF8F, 5, ImGui.DrawFlags_None, 1) end ImGui.EndDisabled(ctx) ImGui.PopStyleColor(ctx, 3) ::nextkey:: end ImGui.PopFont(ctx) end -------------------------------------------------------------------------------- function UI.draw_keyb_definecolor(kbt) -- get color local inactive_col = UI.main_butcol local active_col = UI.main_butcol_other if kbt.bindings and kbt.bindings[tflags] and kbt.bindings[tflags].color then active_col = kbt.bindings[tflags].color end -- return col if kbt.bindings then tflags = ImGui.GetKeyMods( ctx ) if kbt.bindings[tflags] then -- no modifier flags return active_col end end return inactive_col end -------------------------------------------------------------------------------- function UI.draw_keyb_handleKBpress(keyt) if not (keyt.reaimguikey or keyt.reaimguikeyID) then return end if keyt.reaimguikey then keyt.state_pressed = false end if keyt.reaimguikey and ImGui.IsKeyDown( ctx, keyt.reaimguikey ) then keyt.state_pressed = true end if keyt.reaimguikeyID then local curstate = keyt.reaimguikeyID == DATA.input_char if curstate == true and keyt.state_pressed ~= true then DATA:SetSelectionFromReimGuiKey(keyt.reaimguikey) end if curstate == false and keyt.state_pressed ==true then DATA:SetSelectionFromReimGuiKey(keyt.reaimguikey) keyt.state_releaseTS = os.clock() end keyt.state_pressed = curstate end -- handle smooth UI release if keyt.reaimguikey and ImGui.IsKeyReleased( ctx, keyt.reaimguikey ) then DATA:SetSelectionFromReimGuiKey(keyt.reaimguikey) keyt.state_releaseTS = os.clock() end keyt.state_release_fall = false keyt.state_release_alphamult = 0 if keyt.state_releaseTS and os.clock() - keyt.state_releaseTS 0.3 then EXT.viewport_posX = DATA.display_x EXT.viewport_posY = DATA.display_y EXT.viewport_posW = DATA.display_w EXT.viewport_posH = DATA.display_h EXT:save() DATA.display_schedule_save = nil end DATA.display_x_last = DATA.display_x DATA.display_y_last = DATA.display_y DATA.display_w_last = DATA.display_w DATA.display_h_last = DATA.display_h end -------------------------------------------------------------------------------- function DATA:handleProjUpdates() local SCC = GetProjectStateChangeCount( 0 ) if (DATA.upd_lastSCC and DATA.upd_lastSCC~=SCC ) then DATA.upd = true end DATA.upd_lastSCC = SCC local editcurpos = GetCursorPosition() if (DATA.upd_last_editcurpos and DATA.upd_last_editcurpos~=editcurpos ) then DATA.upd = true end DATA.upd_last_editcurpos=editcurpos local reaproj = tostring(EnumProjects( -1 )) if (DATA.upd_last_reaproj and DATA.upd_last_reaproj ~= reaproj) then DATA.upd = true end DATA.upd_last_reaproj = reaproj end -------------------------------------------------------------------------------- function UI.draw_combo() --ImGui.SetCursorPos( ctx, UI.calc_combo1_x, UI.calc_combo1_y ) ImGui.SetCursorPos( ctx, UI.calc_KeyCategories_x, UI.calc_KeyCategories_y ) --ImGui.SetNextItemWidth( ctx, UI.calc_combo1_w ) ImGui.SetNextItemWidth( ctx,UI.calc_KeyCategories_w ) -- current section local preview = EXT.section_ID local preview_str = DATA.sections[preview] if ImGui.BeginCombo( ctx, '##section', preview_str, ImGui.ComboFlags_None ) then for sectID in spairs(DATA.sections) do local sectname = DATA.sections[sectID] if ImGui.Selectable(ctx, sectname) then EXT.section_ID = sectID EXT:save() DATA.upd = true end end ImGui.EndCombo( ctx ) end -- ext layout --ImGui.SetCursorPos( ctx, UI.calc_combo2_x, UI.calc_combo2_y ) ImGui.SetCursorPos( ctx, UI.calc_KeyCategories_x, UI.calc_KeyCategories_y + UI.calc_itemH +UI.spacingY) --ImGui.SetNextItemWidth( ctx, UI.calc_combo2_w ) ImGui.SetNextItemWidth( ctx,UI.calc_KeyCategories_w ) local preview_str = EXT.extlayoutname if ImGui.BeginCombo( ctx, '##layout', preview_str, ImGui.ComboFlags_None ) then for layout in spairs(DATA.extlayouts) do local layoutname = layout if ImGui.Selectable(ctx, layoutname) then EXT.extlayoutname = layoutname EXT:save() DATA.upd = true end end local retval, v = ImGui.Checkbox( ctx, 'dev_char', EXT.dev_char == 1 ) if retval then local val = 0 if v == true then val = 1 end EXT.dev_char =val EXT:save() DATA.upd = true end ImGui.EndCombo( ctx ) end end -------------------------------------------------------------------------------- function UI.draw() UI.draw_keyb() UI.draw_combo() UI.draw_KeyCategories() --UI.draw_ActionList() UI.draw_KeyDetails() end --------------------------------------------------- function spairs(t, order) --http://stackoverflow.com/questions/15706270/sort-a-table-in-lua local keys = {} for k in pairs(t) do keys[#keys+1] = k end if order then table.sort(keys, function(a,b) return order(t, a, b) end) else table.sort(keys) end local i = 0 return function() i = i + 1 if keys[i] then return keys[i], t[keys[i]] end end end --------------------------------------------------- function VF_CopyTable(orig)--http://lua-users.org/wiki/CopyTable local orig_type = type(orig) local copy if orig_type == 'table' then copy = {} for orig_key, orig_value in next, orig, nil do copy[VF_CopyTable(orig_key)] = VF_CopyTable(orig_value) end setmetatable(copy, VF_CopyTable(getmetatable(orig))) else -- number, string, boolean, etc copy = orig end return copy end ----------------------------------------------------------------------------------------- function DATA:ValidateLayouts(filename) DATA.extlayouts = {} local layouts_fp = filename:gsub('%.lua',' - layout.txt') --local content = DATA:ValidateLayouts_Init(layouts_fp) local f = io.open(layouts_fp, 'rb') if f then content = f:read('a') f:close() else content = DATA:ValidateLayouts_Init(layouts_fp) end if not content then return end -- parse local id=0 for layout in content:gmatch('') do local name if layout:match('NAME (.-)') then name = layout:match('NAME (.-)[\r\n]') end if name then id = id + 1 DATA.extlayouts[name] = {ID=id, BLOCKS={}} for line in layout:gmatch('[^\r\n]+') do if line:match('KEY') then local KBNAME, BLOCK, LEVEL, IMGUI, BINDINGNAME, EXTW, EXTH = line:match('KEY KBNAME (.-) BLOCK (%d+) LEVEL (%d+) IMGUI (.-) BINDINGNAME ([%a%d%p%s]+) EXTW ([%d%.]+) EXTH ([%d%.]+)') if not KBNAME then KBNAME, BLOCK, LEVEL, IMGUI, BINDINGNAME, EXTH = line:match('KEY KBNAME (.-) BLOCK (%d+) LEVEL (%d+) IMGUI (.-) BINDINGNAME ([%a%d%p%s]+) EXTH ([%d%.]+)') end if not KBNAME then KBNAME, BLOCK, LEVEL, IMGUI, BINDINGNAME, EXTW = line:match('KEY KBNAME (.-) BLOCK (%d+) LEVEL (%d+) IMGUI (.-) BINDINGNAME ([%a%d%p%s]+) EXTW ([%d%.]+)') end if not KBNAME then KBNAME, BLOCK, LEVEL, IMGUI, BINDINGNAME = line:match('KEY KBNAME (.-) BLOCK (%d+) LEVEL (%d+) IMGUI (.-) BINDINGNAME ([%a%d%p%s]+)') end if BLOCK then BLOCK = tonumber(BLOCK) or BLOCK LEVEL = tonumber(LEVEL) or LEVEL if not DATA.extlayouts[name].BLOCKS[BLOCK] then DATA.extlayouts[name].BLOCKS[BLOCK] = {} end if not DATA.extlayouts[name].BLOCKS[BLOCK].LEVELS then DATA.extlayouts[name].BLOCKS[BLOCK].LEVELS = {} end if not DATA.extlayouts[name].BLOCKS[BLOCK].LEVELS[LEVEL] then DATA.extlayouts[name].BLOCKS[BLOCK].LEVELS[LEVEL] = {} end local id = #DATA.extlayouts[name].BLOCKS[BLOCK].LEVELS[LEVEL] +1 DATA.extlayouts[name].BLOCKS[BLOCK].LEVELS[LEVEL][id] = { KBNAME=KBNAME, IMGUI=tonumber(IMGUI) or IMGUI, BINDINGNAME=BINDINGNAME, EXTW=EXTW, EXTH=EXTH, } end end end end end end ----------------------------------------------------------------------------------------- function DATA:ValidateLayouts_Init(layouts_fp) local content = [[ BLOCK 1 LEVEL 5 IMGUI Key_Period BINDINGNAME . KEY KBNAME ? BLOCK 1 LEVEL 5 IMGUI Key_Slash BINDINGNAME ? KEY KBNAME Shift##Rshift BLOCK 1 LEVEL 5 IMGUI Key_RightShift BINDINGNAME Shift EXTW 2 KEY KBNAME dummy BLOCK 2 LEVEL 5 IMGUI dummy BINDINGNAME dummy KEY KBNAME Up##ArrUp BLOCK 2 LEVEL 5 IMGUI Key_UpArrow BINDINGNAME Up KEY KBNAME 1##Num1 BLOCK 3 LEVEL 5 IMGUI Key_Keypad1 BINDINGNAME Num 1 KEY KBNAME 2##Num2 BLOCK 3 LEVEL 5 IMGUI Key_Keypad2 BINDINGNAME Num 2 KEY KBNAME 3##Num3 BLOCK 3 LEVEL 5 IMGUI Key_Keypad3 BINDINGNAME Num 3 KEY KBNAME Enter##NumEnter BLOCK 3 LEVEL 5 IMGUI Key_KeypadEnter BINDINGNAME Num Enter EXTH 2 KEY KBNAME Ctrl##LCtrl BLOCK 1 LEVEL 6 IMGUI Key_LeftCtrl BINDINGNAME Ctrl KEY KBNAME Alt BLOCK 1 LEVEL 6 IMGUI Key_LeftAlt BINDINGNAME Alt KEY KBNAME Win\nL Super##Win BLOCK 1 LEVEL 6 IMGUI Key_LeftSuper BINDINGNAME Win KEY KBNAME Win\nR Super##WinRight BLOCK 1 LEVEL 6 IMGUI Key_RightSuper BINDINGNAME Win KEY KBNAME App\nBack BLOCK 1 LEVEL 6 IMGUI Key_AppBack BINDINGNAME Browser Back KEY KBNAME App\nForward BLOCK 1 LEVEL 6 IMGUI Key_AppForward BINDINGNAME Browser Forward KEY KBNAME Space BLOCK 1 LEVEL 6 IMGUI Key_Space BINDINGNAME Space EXTW 2 KEY KBNAME Left##ArrLeft BLOCK 2 LEVEL 6 IMGUI Key_LeftArrow BINDINGNAME Left KEY KBNAME Down##ArrDown BLOCK 2 LEVEL 6 IMGUI Key_DownArrow BINDINGNAME Down KEY KBNAME Right##ArrRight BLOCK 2 LEVEL 6 IMGUI Key_RightArrow BINDINGNAME Right KEY KBNAME 0##Num0 BLOCK 3 LEVEL 6 IMGUI Key_Keypad0 BINDINGNAME Num 0 EXTW 2 KEY KBNAME ,##NumDel BLOCK 3 LEVEL 6 IMGUI Key_KeypadDecimal BINDINGNAME Num Del ENDLAYOUT> BLOCK 1 LEVEL 5 IMGUI Key_Period BINDINGNAME . KEY KBNAME ? BLOCK 1 LEVEL 5 IMGUI Key_Slash BINDINGNAME ? KEY KBNAME Shift##Rshift BLOCK 1 LEVEL 5 IMGUI Key_RightShift BINDINGNAME Shift EXTW 2 KEY KBNAME dummy BLOCK 2 LEVEL 5 IMGUI dummy BINDINGNAME dummy KEY KBNAME Up##ArrUp BLOCK 2 LEVEL 5 IMGUI Key_UpArrow BINDINGNAME Up KEY KBNAME 1##Num1 BLOCK 3 LEVEL 5 IMGUI Key_Keypad1 BINDINGNAME NumPad 1 KEY KBNAME 2##Num2 BLOCK 3 LEVEL 5 IMGUI Key_Keypad2 BINDINGNAME NumPad 2 KEY KBNAME 3##Num3 BLOCK 3 LEVEL 5 IMGUI Key_Keypad3 BINDINGNAME NumPad 3 KEY KBNAME Enter##NumEnter BLOCK 3 LEVEL 5 IMGUI Key_KeypadEnter BINDINGNAME NumPad Enter EXTH 2 KEY KBNAME Ctrl##LCtrl BLOCK 1 LEVEL 6 IMGUI Key_LeftSuper BINDINGNAME Control KEY KBNAME Opt##LOption BLOCK 1 LEVEL 6 IMGUI Key_LeftAlt BINDINGNAME Opt KEY KBNAME Cmd##LCommand BLOCK 1 LEVEL 6 IMGUI Key_LeftCtrl BINDINGNAME Cmd KEY KBNAME Space BLOCK 1 LEVEL 6 IMGUI Key_Space BINDINGNAME Space EXTW 4.5 KEY KBNAME Ctrl##RCtrl BLOCK 1 LEVEL 6 IMGUI Key_RightSuper BINDINGNAME Control KEY KBNAME Opt##ROption BLOCK 1 LEVEL 6 IMGUI Key_RightAlt BINDINGNAME Opt KEY KBNAME Cmd##RCommand BLOCK 1 LEVEL 6 IMGUI Key_RightCtrl BINDINGNAME Cmd KEY KBNAME Left##ArrLeft BLOCK 2 LEVEL 6 IMGUI Key_LeftArrow BINDINGNAME Left KEY KBNAME Down##ArrDown BLOCK 2 LEVEL 6 IMGUI Key_DownArrow BINDINGNAME Down KEY KBNAME Right##ArrRight BLOCK 2 LEVEL 6 IMGUI Key_RightArrow BINDINGNAME Right KEY KBNAME 0##Num0 BLOCK 3 LEVEL 6 IMGUI Key_Keypad0 BINDINGNAME NumPad 0 EXTW 2 KEY KBNAME .##NumPeriod BLOCK 3 LEVEL 6 IMGUI Key_KeypadDecimal BINDINGNAME NumPad . ENDLAYOUT> ]] local f = io.open(layouts_fp, 'wb') if f then f:write(content) f:close() end return content end ----------------------------------------------------------------------------------------- function main() local is_new_value,filename,sectionID,cmdID,mode,resolution,val,contextstr = reaper.get_action_context() DATA:ValidateLayouts(filename) EXT_defaults = VF_CopyTable(EXT) UI.MAIN() end ----------------------------------------------------------------------------------------- main()