j
update, add support for https
e378a99
--[[
TranscriptUI.lua - @Transcript table & actions UI
]]
TranscriptUI = Polo {
TITLE = 'Transcript',
FLOAT_FORMAT = '%.4f',
COLUMN_WIDTH = 55,
LARGE_COLUMN_WIDTH = 300,
ACTIONS_MARGIN = 8,
ACTIONS_PADDING = 8,
SCORE_COLORS = {
bright_green = 0xa3ff00a6,
dark_green = 0x2cba00a6,
orange = 0xffa700a6,
red = 0xff2c2cff
}
}
TranscriptUI.table_flags = function (sortable)
local sort_flags = 0
if sortable then
sort_flags = ImGui.TableFlags_Sortable() | ImGui.TableFlags_SortTristate()
end
return (
sort_flags
| ImGui.TableFlags_Borders()
| ImGui.TableFlags_Hideable()
| ImGui.TableFlags_Resizable()
| ImGui.TableFlags_Reorderable()
| ImGui.TableFlags_RowBg()
| ImGui.TableFlags_ScrollX()
| ImGui.TableFlags_ScrollY()
| ImGui.TableFlags_SizingFixedFit()
)
end
function TranscriptUI:init()
assert(self.transcript, 'missing transcript')
self.words = false
self.colorize_words = false
self.autoplay = true
self.transcript_editor = TranscriptEditor.new { transcript = self.transcript }
self.transcript_exporter = TranscriptExporter.new { transcript = self.transcript }
self:init_layouts()
end
function TranscriptUI:init_layouts()
local renderers = {
self.render_create_regions,
self.render_create_markers,
self.render_create_notes,
self.render_word_options,
self.render_result_actions,
self.render_auto_play,
self.render_search
}
self.actions_layout = ColumnLayout.new {
column_padding = self.ACTIONS_PADDING,
num_columns = #renderers,
render_column = function (column)
renderers[column.num](self, column)
end
}
end
function TranscriptUI:render()
if self.transcript:has_segments() then
ImGui.SeparatorText(ctx, "Transcript")
self.actions_layout:render()
self:render_table()
end
self.transcript_editor:render()
self.transcript_exporter:render()
end
function TranscriptUI:render_create_regions(column)
if ImGui.Button(ctx, "Create Regions", column.width) then
self:handle_create_markers(true)
end
end
function TranscriptUI:render_create_markers(column)
if ImGui.Button(ctx, "Create Markers", column.width) then
self:handle_create_markers(false)
end
end
function TranscriptUI:render_create_notes(column)
if ImGui.Button(ctx, "Create Notes", column.width) then
self:handle_create_notes()
end
end
function TranscriptUI:render_word_options()
local rv, value = ImGui.Checkbox(ctx, "Words", self.words)
if rv then
self.words = value
end
if self.words then
ImGui.SameLine(ctx)
rv, value = ImGui.Checkbox(ctx, "Colorize", self.colorize_words)
if rv then
self.colorize_words = value
end
end
end
function TranscriptUI:render_result_actions()
self:render_export()
ImGui.SameLine(ctx)
self:render_clear()
end
function TranscriptUI:render_export()
if ImGui.Button(ctx, "Export") then
self:handle_export()
end
end
function TranscriptUI:render_clear()
if ImGui.Button(ctx, "Clear") then
self:handle_transcript_clear()
end
end
function TranscriptUI:render_auto_play()
local rv, value = ImGui.Checkbox(ctx, "Auto Play", self.autoplay)
if rv then
self.autoplay = value
end
end
function TranscriptUI:render_search(column)
ImGui.SetCursorPosX(ctx, ImGui.GetWindowWidth(ctx) - column.width - self.ACTIONS_MARGIN)
ImGui.PushItemWidth(ctx, column.width)
app:trap(function()
local search_changed, search = ImGui.InputTextWithHint(ctx, '##search', 'Search', self.transcript.search)
if search_changed then
self:handle_search(search)
end
end)
ImGui.PopItemWidth(ctx)
end
function TranscriptUI:handle_create_markers(regions)
reaper.PreventUIRefresh(1)
reaper.Undo_BeginBlock()
self.transcript:create_markers(0, regions, self.words)
reaper.Undo_EndBlock(
("Create %s from speech"):format(regions and 'regions' or 'markers'), -1)
reaper.PreventUIRefresh(-1)
end
function TranscriptUI:handle_create_notes()
reaper.PreventUIRefresh(1)
reaper.Undo_BeginBlock()
self.transcript:create_notes_track(self.words)
reaper.Undo_EndBlock("Create notes from speech", -1)
reaper.PreventUIRefresh(-1)
end
function TranscriptUI:handle_export()
self.transcript_exporter:open()
end
function TranscriptUI:handle_transcript_clear()
self.transcript:clear()
end
function TranscriptUI:handle_search(search)
self.transcript.search = search
self.transcript:update()
end
-- formerly ReaSpeechUI:render_table()
function TranscriptUI:render_table()
local columns = self.transcript:get_columns()
local num_columns = #columns + 1
local ok = ImGui.BeginTable(ctx, "results", num_columns, self.table_flags(true))
if ok then
app:trap(function ()
ImGui.TableSetupColumn(ctx, "##actions", ImGui.TableColumnFlags_NoSort(), 20)
for _, column in pairs(columns) do
local column_flags = 0
if TranscriptSegment.default_hide(column) then
-- reaper.ShowConsoleMsg(string.format('column %s: %s\n', column, TranscriptSegment.default_hide(column)))
column_flags = column_flags | ImGui.TableColumnFlags_DefaultHide()
end
local init_width = self.COLUMN_WIDTH
if column == "text" or column == "file" then
init_width = self.LARGE_COLUMN_WIDTH
end
-- reaper.ShowConsoleMsg(string.format('column %s: %s / flags: %s\n', column, TranscriptSegment.default_hide(column), column_flags))
ImGui.TableSetupColumn(ctx, column, column_flags, init_width)
end
ImGui.TableSetupScrollFreeze(ctx, 0, 1)
ImGui.TableHeadersRow(ctx)
self:sort_table()
for index, segment in pairs(self.transcript:get_segments()) do
ImGui.TableNextRow(ctx)
ImGui.TableNextColumn(ctx)
self:render_segment_actions(segment, index)
for _, column in pairs(columns) do
ImGui.TableNextColumn(ctx)
self:render_table_cell(segment, column)
end
end
end)
ImGui.EndTable(ctx)
end
end
function TranscriptUI:render_segment_actions(segment, index)
ImGui.PushFont(ctx, Fonts.icons)
app:trap(function()
ImGui.Text(ctx, Fonts.ICON.pencil)
end)
ImGui.PopFont(ctx)
if ImGui.IsItemHovered(ctx) then
ImGui.SetMouseCursor(ctx, ImGui.MouseCursor_Hand())
end
if ImGui.IsItemClicked(ctx) then
self.transcript_editor:edit_segment(segment, index)
end
app:tooltip("Edit")
end
function TranscriptUI:render_table_cell(segment, column)
if column == "text" or column == "word" then
self:render_text(segment, column)
elseif column == "score" then
self:render_score(segment:get(column, 0.0))
elseif column == "start" or column == "end" then
ImGui.Text(ctx, reaper.format_timestr(segment:get(column, 0.0), ''))
else
local value = segment:get(column)
if type(value) == 'table' then
value = table.concat(value, ', ')
elseif math.type(value) == 'float' then
value = self.FLOAT_FORMAT:format(value)
end
ImGui.Text(ctx, tostring(value))
end
end
function TranscriptUI:render_link(text, onclick, text_color, underline_color)
text_color = text_color or 0xffffffff
underline_color = underline_color or 0xffffffa0
ImGui.TextColored(ctx, text_color, text)
if ImGui.IsItemHovered(ctx) then
local rect_min_x, rect_min_y = ImGui.GetItemRectMin(ctx)
local rect_max_x, _ = ImGui.GetItemRectMax(ctx)
local _, rect_size_y = ImGui.GetItemRectSize(ctx)
local line_y = rect_min_y + rect_size_y - 1
ImGui.DrawList_AddLine(
ImGui.GetWindowDrawList(ctx),
rect_min_x, line_y, rect_max_x, line_y,
underline_color, 1.0)
ImGui.SetMouseCursor(ctx, ImGui.MouseCursor_Hand())
end
if ImGui.IsItemClicked(ctx) then
onclick()
end
end
function TranscriptUI:render_text(segment, column)
if self.words then
self:render_text_words(segment, column)
else
self:render_text_simple(segment, column)
end
end
function TranscriptUI:render_text_simple(segment, column)
self:render_link(segment:get(column, ""), function () segment:navigate(nil,self.autoplay) end)
end
function TranscriptUI:render_text_words(segment, _)
if segment.words then
for i, word in pairs(segment.words) do
if i > 1 then
ImGui.SameLine(ctx, 0, 0)
ImGui.Text(ctx, ' ')
ImGui.SameLine(ctx, 0, 0)
end
local color = nil
if self.colorize_words then
color = self.score_color(word:score())
end
self:render_link(word.word, function () segment:navigate(i, self.autoplay) end, color)
end
end
end
function TranscriptUI:render_score(value)
local w, h = 50 * value, 3
local color = self.score_color(value)
if color then
local draw_list = ImGui.GetWindowDrawList(ctx)
local x, y = ImGui.GetCursorScreenPos(ctx)
y = y + 7
ImGui.DrawList_AddRectFilled(draw_list, x, y, x + w, y + h, color)
end
ImGui.Dummy(ctx, w, h)
end
function TranscriptUI.score_color(value)
local colors = TranscriptUI.SCORE_COLORS
if value > 0.9 then
return colors.bright_green
elseif value > 0.8 then
return colors.dark_green
elseif value > 0.7 then
return colors.orange
elseif value > 0.0 then
return colors.red
else
return nil
end
end
function TranscriptUI:sort_table()
local specs_dirty, has_specs = ImGui.TableNeedSort(ctx)
if has_specs and specs_dirty then
local columns = self.transcript:get_columns()
local column = nil
local ascending = true
for next_id = 0, math.huge do
local ok, _, col_idx, _, sort_direction =
ImGui.TableGetColumnSortSpecs(ctx, next_id)
if not ok then break end
column = columns[col_idx]
ascending = (sort_direction == ImGui.SortDirection_Ascending())
end
if column then
self.transcript:sort(column, ascending)
else
self.transcript:update()
end
end
end