From 531d32d7fed0af798d667b69df4a75161ef588f3 Mon Sep 17 00:00:00 2001 From: BenTalagan Date: Thu, 11 Dec 2025 18:27:56 +0100 Subject: [PATCH] Update Reannotate v0.4.3 > v0.4.4 --- Various/talagan_Reannotate.lua | 12 +- .../classes/app_context.lua | 4 + .../talagan_Reannotate/classes/mem_cache.lua | 29 ++- Various/talagan_Reannotate/classes/notes.lua | 153 ++++++++++- .../talagan_Reannotate/classes/sticker.lua | 81 ++++-- Various/talagan_Reannotate/modules/debug.lua | 2 + .../talagan_Reannotate/modules/defines.lua | 131 +++------- .../modules/project_settings.lua | 158 ++++++++++++ .../talagan_Reannotate/modules/settings.lua | 10 +- .../widgets/note_editor.lua | 70 +++-- .../widgets/overlay_canvas.lua | 221 +++++++++++----- .../widgets/quick_preview_overlay.lua | 71 ++++-- .../widgets/settings_editor.lua | 240 ++++++++++++------ .../widgets/sticker_editor.lua | 3 +- .../widgets/sticker_picker.lua | 9 +- 15 files changed, 874 insertions(+), 320 deletions(-) create mode 100644 Various/talagan_Reannotate/modules/project_settings.lua diff --git a/Various/talagan_Reannotate.lua b/Various/talagan_Reannotate.lua index 75e408afb..3ffd58c5c 100644 --- a/Various/talagan_Reannotate.lua +++ b/Various/talagan_Reannotate.lua @@ -1,6 +1,6 @@ --[[ @description Reannotate - Annotation tool for REAPER -@version 0.4.3 +@version 0.4.4 @author Ben 'Talagan' Babut @donation https://www.paypal.com/donate/?business=3YEZMY9D6U8NC&no_recurring=1¤cy_code=EUR @license MIT @@ -10,12 +10,10 @@ Forum Thread https://forum.cockos.com/showthread.php?t=304147 @metapackage @changelog - - [Feature] The markdown stylesheet is now customizable - - [Feature] UI Font size is now customizable - - [Feature] Note editor's width is now remembered - - [Bug Fix] Tooltips with vertical scrollbars could affect the layout of other tooltips shown immediately after - - [Rework] Moved project notes to the transport zone instead of the time ruler (will be used for regions / markers ?) - - [Rework] Optimizations + - [Feature] Posters + - [Feature] Can now add annotations to regions + - [Feature] Progress bar sticker + - [Bug fixes] Various bug fixes @provides [nomain] talagan_Reannotate/ext/**/* [nomain] talagan_Reannotate/classes/**/* diff --git a/Various/talagan_Reannotate/classes/app_context.lua b/Various/talagan_Reannotate/classes/app_context.lua index 6a3a77160..5cc9ea718 100644 --- a/Various/talagan_Reannotate/classes/app_context.lua +++ b/Various/talagan_Reannotate/classes/app_context.lua @@ -131,6 +131,10 @@ function AppContext:_initialize() AppContext.__singleton = self end +-- Return absolute coordinates, normalized by ImGui PointConvertNative. +-- These coordinates are meant to be used with drawlists essentially. +-- Scrollbar corrections may be passed. +-- DPI is applied if there's scaling under windows function AppContext:retrieveCoordinates(sub, scrollbar_w, scrollbar_h) if not sub.hwnd then return end local _, x, y, x2, y2 = reaper.JS_Window_GetRect(sub.hwnd) diff --git a/Various/talagan_Reannotate/classes/mem_cache.lua b/Various/talagan_Reannotate/classes/mem_cache.lua index 78a5d3253..a57944284 100644 --- a/Various/talagan_Reannotate/classes/mem_cache.lua +++ b/Various/talagan_Reannotate/classes/mem_cache.lua @@ -23,7 +23,13 @@ end function MemCache.GetObjectGUID(object) local guid = '' - if reaper.ValidatePtr(object, "MediaTrack*") then + if type(object) == "table" then + if object.t == 'region' then + return object.guid + else + error("Unhandled custom type " .. object.t .. " for object") + end + elseif reaper.ValidatePtr(object, "MediaTrack*") then local tguid = reaper.GetTrackGUID(object) guid = tguid elseif reaper.ValidatePtr(object,"MediaItem*") then @@ -49,24 +55,31 @@ function MemCache:getObjectCache(object) -- Build object cache local name = '' - local type = '' - if reaper.ValidatePtr(object, "MediaTrack*") then + local kind = '' + if type(object) == "table" then + if object.t == 'region' then + name = object.n or '' + kind = object.t + else + error("Unhandled custom type " .. object.t .. " for object") + end + elseif reaper.ValidatePtr(object, "MediaTrack*") then local _, tname = reaper.GetTrackName(object) name = tname - type = 'track' + kind = 'track' elseif reaper.ValidatePtr(object,"MediaItem*") then local take = reaper.GetActiveTake(object) if take then name = reaper.GetTakeName(take) end - type = 'item' + kind = 'item' elseif reaper.ValidatePtr(object, "TrackEnvelope*") then local _, ename = reaper.GetEnvelopeName(object) name = ename - type = 'env' + kind = 'env' elseif reaper.ValidatePtr(object, "ReaProject*") then name = "Project" - type = 'project' + kind = 'project' else error("Unhandled type for object") end @@ -76,7 +89,7 @@ function MemCache:getObjectCache(object) guid = guid, object = object, name = name, - type = type, + type = kind, -- Notes cache notes = Notes:new(object) } diff --git a/Various/talagan_Reannotate/classes/notes.lua b/Various/talagan_Reannotate/classes/notes.lua index e57a05434..6d37650c0 100644 --- a/Various/talagan_Reannotate/classes/notes.lua +++ b/Various/talagan_Reannotate/classes/notes.lua @@ -7,6 +7,7 @@ local JSON = require "ext/json" local CheckboxHelper = require "modules/checkbox_helper" local Sticker = require "classes/sticker" local D = require "modules/defines" +local PS = require "modules/project_settings" local function normalizeNotes(str) return str:gsub("\r","") @@ -23,6 +24,7 @@ local function normalizeReannotateData(data) if not data.slots then data.slots = {} end if not data.stickers then data.stickers = {} end if not data.cb then data.cb = {} end + if not data.posters then data.posters = {} end for i=1, D.MAX_SLOTS-1 do data.slots[i] = data.slots[i] or "" -- Stored text @@ -31,6 +33,9 @@ local function normalizeReannotateData(data) data.tt_sizes[i].h = normalizeCoordinate(data.tt_sizes[i].h, D.TT_DEFAULT_H) data.stickers[i] = data.stickers[i] or {} -- List or empty list of packed stickers data.cb[i] = data.cb[i] or {} -- Checkbox cache + data.posters[i] = data.posters[i] or {} + data.posters[i].t = data.posters[i].t or D.POSTER_TYPES.PROJECT_DEFAULT + data.posters[i].s = data.posters[i].s or "" end -- Special treatment for slot 0 @@ -39,14 +44,23 @@ local function normalizeReannotateData(data) data.sws_reaper_tt_size.h = normalizeCoordinate(data.sws_reaper_tt_size.h, D.TT_DEFAULT_H) data.sws_reaper_stickers = data.sws_reaper_stickers or {} data.sr_cb = data.sr_cb or {} + data.sws_reaper_poster = data.sws_reaper_poster or {} + data.sws_reaper_poster.t = data.sws_reaper_poster.t or D.POSTER_TYPES.PROJECT_DEFAULT + data.sws_reaper_poster.s = data.sws_reaper_poster.s or "" -- data.sws_reaper_text <-- stored in reaper / SWS - return data end local function AttributeGetterSetter(object) local getter = nil - if reaper.ValidatePtr(object, "MediaTrack*") then + if type(object) == 'table' then + if object.t == 'region' then + -- We'll store on the master track with a different ke + getter = reaper.GetSetMediaTrackInfo_String + else + error("Unhandled custom type " .. object.t .. " for object ") + end + elseif reaper.ValidatePtr(object, "MediaTrack*") then getter = reaper.GetSetMediaTrackInfo_String elseif reaper.ValidatePtr(object, "TrackEnvelope*") then getter = reaper.GetSetEnvelopeInfo_String @@ -62,7 +76,13 @@ local function AttributeGetterSetter(object) end local function StoreKey(object) - if reaper.ValidatePtr(object, "ReaProject*") then + if type(object) == 'table' then + if object.t == 'region' then + return "P_EXT:Reannotate_RegionNotes:" .. object.guid + else + error("Unhandled custom type " .. object.t .. " for object ") + end + elseif reaper.ValidatePtr(object, "ReaProject*") then return "P_EXT:Reannotate_ProjectNotes" else return "P_EXT:Reannotate_Notes" @@ -70,7 +90,13 @@ local function StoreKey(object) end local function StoreObject(object) - if reaper.ValidatePtr(object, "ReaProject*") then + if type(object) == 'table' then + if object.t == 'region' then + return reaper.GetMasterTrack(D.ActiveProject()) + else + error("Unhandled custom type " .. object.t .. " for object ") + end + elseif reaper.ValidatePtr(object, "ReaProject*") then return reaper.GetMasterTrack(D.ActiveProject()) else return object @@ -109,12 +135,14 @@ local function SetObjectNotes_Reannotate(object, data) -- SWS/Reaper additional sws_reaper_tt_size = data.sws_reaper_tt_size, sr_cb = data.sr_cb, + sws_reaper_stickers = {}, + sws_reaper_poster = D.deepCopy(data.sws_reaper_poster), -- Reannotate structures tt_sizes = data.tt_sizes, slots = data.slots, cb = data.cb, stickers = {}, - sws_reaper_stickers = {} + posters = D.deepCopy(data.posters) } -- Serialize stickers @@ -123,6 +151,7 @@ local function SetObjectNotes_Reannotate(object, data) end new_data.sws_reaper_stickers = Sticker.PackCollection(data.sws_reaper_stickers) + -- Normalize before packing new_data = normalizeReannotateData(new_data) local data_str = JSON.encode(new_data) @@ -187,9 +216,28 @@ local function SetActiveProjectNotes_SWS_Reaper(str) return reaper.GetSetProjectNotes(project, true, notes) end +------------- +-- REGION -- +------------- + +local function GetRegionNotes_SWS_Reaper(object) + -- Not handled by SWS + return true, "" +end +local function SetRegionNotes_SWS_Reaper(object, str) + -- Not handled by SWS + return true, "" +end + local function GetObjectNotes_SWS_Reaper(notes) local object = notes._object - if reaper.ValidatePtr(object, "MediaTrack*") then + if type(object) == 'table' then + if object.t == 'region' then + return GetRegionNotes_SWS_Reaper(object) + else + error("Unhandled custom type " .. object.t .. " for object ") + end + elseif reaper.ValidatePtr(object, "MediaTrack*") then return GetTrackNotes_SWS_Reaper(object) elseif reaper.ValidatePtr(object, "TrackEnvelope*") then return GetEnvelopeNotes_SWS_Reaper(object) @@ -203,7 +251,13 @@ local function GetObjectNotes_SWS_Reaper(notes) end local function SetObjectNotes_SWS_Reaper(object, str) - if reaper.ValidatePtr(object, "MediaTrack*") then + if type(object) == 'table' then + if object.t == 'region' then + return SetRegionNotes_SWS_Reaper(object, str) + else + error("Unhandled custom type " .. object.t .. " for object ") + end + elseif reaper.ValidatePtr(object, "MediaTrack*") then return SetTrackNotes_SWS_Reaper(object, str) elseif reaper.ValidatePtr(object,"TrackEnvelope*") then return SetEnvelopeNotes_SWS_Reaper(object, str) @@ -248,17 +302,50 @@ function Notes:commit() SetObjectNotes_SWS_Reaper(self._object, self.sws_reaper_notes) end -function Notes:isSlotBlank(slot) +function Notes:isSlotNoteBlank(slot) if slot == 0 then if self.sws_reaper_notes and self.sws_reaper_notes ~= "" then return false end - if #self.reannotate_notes.sws_reaper_stickers > 0 then return false end else if self.reannotate_notes.slots[slot] and self.reannotate_notes.slots[slot] ~= '' then return false end + end + return true +end + +function Notes:isSlotStickersBlank(slot) + if slot == 0 then + if #self.reannotate_notes.sws_reaper_stickers > 0 then return false end + else if #self.reannotate_notes.stickers[slot] > 0 then return false end end return true end +function Notes:isSlotPosterBlank(slot) + local type = self:resolvedSlotPosterType(slot) + if type == D.POSTER_TYPES.NO_POSTER then + return true + elseif type == D.POSTER_TYPES.CUSTOM_PLAIN_POSTER or type == D.POSTER_TYPES.CUSTOM_MARKDOWN_POSTER then + -- If in custom mode, check custom poster + if slot == 0 then + if self.reannotate_notes.sws_reaper_poster and self.reannotate_notes.sws_reaper_poster.s ~= "" then return false end + else + if self.reannotate_notes.posters[slot] and self.reannotate_notes.posters[slot].s ~= '' then return false end + end + return true + else + -- If not in custom mode, check note + return self:isSlotNoteBlank(slot) + end +end + +function Notes:isSlotBlank(slot) + if not self:isSlotNoteBlank(slot) then return false end + if not self:isSlotStickersBlank(slot) then return false end + if not self:isSlotPosterBlank(slot) then return false end + + return true +end + function Notes:isBlank() for slot=0, D.MAX_SLOTS -1 do if not self:isSlotBlank(slot) then return false end @@ -307,6 +394,54 @@ function Notes:setSlotText(slot, str) end end +function Notes:resolvedSlotPosterType(slot) + local type = self:slotPosterType(slot) + if type == D.POSTER_TYPES.PROJECT_DEFAULT then + return PS.ProjectPosterDefaultType() + end + return type +end + +function Notes:slotCustomPoster(slot) + if slot == 0 then + self.reannotate_notes.sws_reaper_poster = self.reannotate_notes.sws_reaper_poster or { t = D.POSTER_TYPES.PROJECT_DEFAULT, s = '' } + return self.reannotate_notes.sws_reaper_poster.s + else + self.reannotate_notes.posters[slot] = self.reannotate_notes.posters[slot] or { t = D.POSTER_TYPES.PROJECT_DEFAULT, s = '' } + return self.reannotate_notes.posters[slot].s + end +end +function Notes:setSlotCustomPoster(slot, str) + if slot == 0 then + self.reannotate_notes.sws_reaper_poster = self.reannotate_notes.sws_reaper_poster or { t = D.POSTER_TYPES.PROJECT_DEFAULT, s = '' } + self.reannotate_notes.sws_reaper_poster.s = str + else + self.reannotate_notes.posters[slot] = self.reannotate_notes.posters[slot] or { t = D.POSTER_TYPES.PROJECT_DEFAULT, s = '' } + self.reannotate_notes.posters[slot].s = str + end +end + + +function Notes:slotPosterType(slot) + if slot == 0 then + self.reannotate_notes.sws_reaper_poster = self.reannotate_notes.sws_reaper_poster or { t = D.POSTER_TYPES.PROJECT_DEFAULT, s = '' } + return self.reannotate_notes.sws_reaper_poster.t + else + self.reannotate_notes.posters[slot] = self.reannotate_notes.posters[slot] or { t = D.POSTER_TYPES.PROJECT_DEFAULT, s = '' } + return self.reannotate_notes.posters[slot].t + end +end +function Notes:setSlotPosterType(slot, type) + if slot == 0 then + self.reannotate_notes.sws_reaper_poster = self.reannotate_notes.sws_reaper_poster or { t = D.POSTER_TYPES.PROJECT_DEFAULT, s = '' } + self.reannotate_notes.sws_reaper_poster.t = type + else + self.reannotate_notes.posters[slot] = self.reannotate_notes.posters[slot] or { t = D.POSTER_TYPES.PROJECT_DEFAULT, s = '' } + self.reannotate_notes.posters[slot].t = type + end +end + + function Notes:slotStickers(slot) if slot == 0 then return self.reannotate_notes.sws_reaper_stickers or {} diff --git a/Various/talagan_Reannotate/classes/sticker.lua b/Various/talagan_Reannotate/classes/sticker.lua index a54ee8d09..731f0b93e 100644 --- a/Various/talagan_Reannotate/classes/sticker.lua +++ b/Various/talagan_Reannotate/classes/sticker.lua @@ -9,6 +9,7 @@ local AppContext = require "classes/app_context" local EmojImGui = require "emojimgui" local Color = require "classes/color" local D = require "modules/defines" +local PS = require "modules/project_settings" local Sticker = {} Sticker.Types = { @@ -17,7 +18,7 @@ Sticker.Types = { } -- String format: "type:detail" --- type == 0, detail = category|checkboxes +-- type == 0, detail = category|checkboxes|progressbar -- type == 1, detail = icon_string:text -- icon_string = font:char (font = nothing|0|1) @@ -93,11 +94,15 @@ function Sticker:_parseHelper(str) end end +function Sticker:isProgressbar() + return self:isSpecial() and self.text == 'progressbar' +end + function Sticker:textToRender() local text = self.text if self.type == Sticker.Types.SPECIAL then if text == 'category' then - text = (self.slot) and (D.SlotLabel(self.slot)) or ("???") + text = (self.slot) and (PS.SlotLabel(self.slot)) or ("???") elseif text == "checkboxes" then if not self.notes then text = "? / ?" @@ -107,6 +112,17 @@ function Sticker:textToRender() counts.t = counts.t or 0 text = "" .. counts.c .. " / " .. counts.t end + elseif text == 'progressbar' then + if not self.notes then + text = "?" + else + local counts = self.notes:slotCheckboxCache(self.slot) + counts.c = counts.c or 0 + counts.t = counts.t or 0 + local p = 0 + if counts.t > 0 then p = (100.0 * counts.c / counts.t) end + text = math.floor(p) -- Gasp, we return a number ... + end end end @@ -158,6 +174,18 @@ function Sticker:computeTextBackgroundColor(base_color_rgba, alpha) return (c:to_irgba() & 0xFFFFFFFF) end +function Sticker:iconBGColor(color) + Sticker.icon_bg_color_cache = Sticker.icon_bg_color_cache or {} + Sticker.icon_bg_color_cache[color] = Sticker.icon_bg_color_cache[color] or self:computeIconBackgroundColor(color, 0) + return Sticker.icon_bg_color_cache[color] +end + +function Sticker:textBGColor(color) + Sticker.text_bg_color_cache = Sticker.text_bg_color_cache or {} + Sticker.text_bg_color_cache[color] = Sticker.text_bg_color_cache[color] or self:computeTextBackgroundColor(color, 0) + return Sticker.text_bg_color_cache[color] +end + function Sticker:renderBackground(draw_list, render_params) local istop = render_params.metrics.icon_stop local min_x = render_params.metrics.min_x @@ -177,13 +205,8 @@ function Sticker:renderBackground(draw_list, render_params) text_bg_color = self:computeTextBackgroundColor(render_params.color, alpha) else -- USe a cache because this will be recomputed a lot and it costs a lot - Sticker.icon_bg_color_cache = Sticker.icon_bg_color_cache or {} - Sticker.icon_bg_color_cache[render_params.color] = Sticker.icon_bg_color_cache[render_params.color] or self:computeIconBackgroundColor(render_params.color, 0) - icon_bg_color = Sticker.icon_bg_color_cache[render_params.color] - - Sticker.text_bg_color_cache = Sticker.text_bg_color_cache or {} - Sticker.text_bg_color_cache[render_params.color] = Sticker.text_bg_color_cache[render_params.color] or self:computeTextBackgroundColor(render_params.color, 0) - text_bg_color = Sticker.text_bg_color_cache[render_params.color] + icon_bg_color = self:iconBGColor(render_params.color) + text_bg_color = self:textBGColor(render_params.color) end if self.icon then @@ -282,16 +305,42 @@ function Sticker:_renderPass(ctx, font_size, should_render, render_params) -- Rendering of icons or texts helper local _textPass = function(_font, _font_size, _txt) ----@diagnostic disable-next-line: redundant-parameter ImGui.PushFont(ctx, _font, _font_size) - local www, hhh = ImGui.CalcTextSize(ctx, _txt) - local diff_height = (hhh - base_text_height) + if self:isProgressbar() then + local pwidth = _font_size * 5 + local pheight = widget_height - 2 * v_pad - 4 + if should_render then + local px = min_x + xcursor + local py = min_y + v_pad + 2 + local ptxt = "" .. _txt .. " %" + local www, hhh = ImGui.CalcTextSize(ctx, ptxt) + local diff_height = (hhh - base_text_height) + local diff_width = (pwidth - www) + + -- Background + ImGui.DrawList_AddRectFilled(draw_list, px, py, px + pwidth, py + pheight, self:iconBGColor(render_params.color), 1) + ImGui.DrawList_AddText(draw_list, min_x + h_pad + diff_width * 0.5, min_y + v_pad - math.floor(diff_height * 0.5) + 1 , 0xFFFFFFFF, ptxt) + + -- Fill + if(_txt > 0) then + ImGui.DrawList_AddRectFilled(draw_list, px+1, py+1, px + (pwidth * _txt/100.0) -1, py + pheight - 1, render_params.color, 1) + end + ImGui.DrawList_PushClipRect(draw_list, px, py, px + (pwidth * _txt/100.0), py + _font_size, true) + ImGui.DrawList_AddText(draw_list, min_x + h_pad + diff_width * 0.5, min_y + v_pad - math.floor(diff_height * 0.5) + 1 , render_params.text_color, ptxt) + ImGui.DrawList_PopClipRect(draw_list) + end + xcursor = xcursor + pwidth + else + ---@diagnostic disable-next-line: redundant-parameter + local www, hhh = ImGui.CalcTextSize(ctx, _txt) + local diff_height = (hhh - base_text_height) - if should_render then - ImGui.DrawList_AddText(draw_list, min_x + xcursor, min_y + v_pad - diff_height * 0.5, render_params.text_color, _txt) - end + if should_render then + ImGui.DrawList_AddText(draw_list, min_x + xcursor, min_y + v_pad - diff_height * 0.5, render_params.text_color, _txt) + end - xcursor = xcursor + www + xcursor = xcursor + www + end ImGui.PopFont(ctx) end diff --git a/Various/talagan_Reannotate/modules/debug.lua b/Various/talagan_Reannotate/modules/debug.lua index 8b9e62781..124dca80c 100644 --- a/Various/talagan_Reannotate/modules/debug.lua +++ b/Various/talagan_Reannotate/modules/debug.lua @@ -59,6 +59,8 @@ local function LaunchProfilerIfNeeded() local EmojImGui = require "emojimgui" local ImGui = require "ext/imgui" + local ImGuiMd = require "reaimgui_markdown" + local ImGuiMdCore = require "reaimgui_markdown/markdown-imgui" local QuickPreviewOverlay = require "widgets/quick_preview_overlay" local OverlayCanvas = require "widgets/overlay_canvas" diff --git a/Various/talagan_Reannotate/modules/defines.lua b/Various/talagan_Reannotate/modules/defines.lua index 264309ded..9d39d1f4b 100644 --- a/Various/talagan_Reannotate/modules/defines.lua +++ b/Various/talagan_Reannotate/modules/defines.lua @@ -3,9 +3,6 @@ -- @license MIT -- @description This file is part of Reannotate -local S = require "modules/settings" -local JSON = require "ext/json" - local Defines = {} Defines.TT_DEFAULT_W = 300 @@ -33,8 +30,16 @@ Defines.POST_IT_COLORS = { 0xff4040, -- RED Slot 7 } -Defines.slot_labels = nil -Defines.slot_labels_dirty = nil +Defines.POSTER_TYPES = { + PROJECT_DEFAULT = 0, + NO_POSTER = 1, + CUSTOM_PLAIN_POSTER = 2, + CUSTOM_MARKDOWN_POSTER = 3, + NOTE_RENDERERED_AS_PLAIN_POSTER = 4, + NOTE_RENDERERED_AS_MARKDOWN_POSTER = 5, + + POSTER_TYPE_COUNT = 6 +} function Defines.deepCopy(orig) local orig_type = type(orig) @@ -102,106 +107,38 @@ function Defines.SlotColor(slot) return Defines.POST_IT_COLORS[slot+1] end ------ Project Labels - -function Defines.RetrieveProjectSlotLabels() - local _, str = reaper.GetSetMediaTrackInfo_String(Defines.ActiveProjectMasterTrack(), "P_EXT:Reannotate_ProjectSlotLabels", "", false) - local slot_labels = {} - if str == "" or str == nil then - else - slot_labels = JSON.decode(str) - end - - -- Ensure labels have names by defaulting to global setting - for i = 0, Defines.MAX_SLOTS -1 do - slot_labels[i+1] = slot_labels[i+1] or S.getSetting("SlotLabel_" .. i) - end - - Defines.slot_labels = slot_labels -end - -function Defines.CommitProjectSlotLabels() - if not Defines.slot_labels_dirty then return end - if not Defines.slot_labels then Defines.RetrieveProjectSlotLabels() end - - local str = JSON.encode(Defines.slot_labels) - reaper.GetSetMediaTrackInfo_String(Defines.ActiveProjectMasterTrack(), "P_EXT:Reannotate_ProjectSlotLabels", str, true) - Defines.slot_labels_dirty = false -end - -function Defines.SlotLabel(slot) - if not Defines.slot_labels then Defines.RetrieveProjectSlotLabels() end - return Defines.slot_labels[slot+1] -end - -function Defines.SetSlotLabel(slot, label) - if not Defines.slot_labels then Defines.RetrieveProjectSlotLabels() end - Defines.slot_labels[slot+1] = label - Defines.slot_labels_dirty = true - Defines.CommitProjectSlotLabels() -end - -------- Project Markdown - --- Not edited directly because we want preview / save features. -function Defines.RetrieveProjectMarkdownStyle() - local _, str = reaper.GetSetMediaTrackInfo_String(Defines.ActiveProjectMasterTrack(), "P_EXT:Reannotate_ProjectMarkdownStyle", "", false) - - local markdown_style = nil - local fallback = false - - if str == "" or str == nil then - fallback = true - else - pcall(function() - markdown_style = JSON.decode(str) - end) - - if not markdown_style then - fallback = true +function Defines.PosterTypeToName(type, default_type) + if type == Defines.POSTER_TYPES.PROJECT_DEFAULT then + local defname = '' + if default_type then + if default_type == Defines.POSTER_TYPES_PROECT_DEFAULT then + error("Developer error : circular poster type for project pointing to itself") + end + defname = " (" .. Defines.PosterTypeToName(default_type, default_type) .. ")" end - end - if fallback then - -- Use new project markdown style - markdown_style = Defines.deepCopy(S.getSetting("NewProjectMarkdown")) - -- Commit it in project - Defines.CommitProjectMarkdownStyle(markdown_style) + return "Project Default" .. defname end + if type == Defines.POSTER_TYPES.NO_POSTER then return "None" end + if type == Defines.POSTER_TYPES.CUSTOM_PLAIN_POSTER then return "Custom plain" end + if type == Defines.POSTER_TYPES.CUSTOM_MARKDOWN_POSTER then return "Custom markdown" end + if type == Defines.POSTER_TYPES.NOTE_RENDERERED_AS_PLAIN_POSTER then return "Note as plain" end + if type == Defines.POSTER_TYPES.NOTE_RENDERERED_AS_MARKDOWN_POSTER then return "Note as markdown" end - -- TODO : Normalize markdown_style after checkout for backward comp - - return markdown_style + error("DEVELOPER ERROR : Unknown post type" .. type) end -function Defines.CommitProjectMarkdownStyle(markdown_style) - -- TODO : Normalize markdown_style before commit for backward comp - - local str = JSON.encode(markdown_style) - - reaper.GetSetMediaTrackInfo_String(Defines.ActiveProjectMasterTrack(),"P_EXT:Reannotate_ProjectMarkdownStyle", str, true) -end - -function Defines.RetrieveProjectStickerSize() - local _, str = reaper.GetSetMediaTrackInfo_String(Defines.ActiveProjectMasterTrack(), "P_EXT:Reannotate_ProjectStickerSize", "", false) - local fallback = false - local size = nil - if str == "" or str == nil then - fallback = true - else - size = tonumber(str) +function Defines.PosterTypeComboInfo(default_type) + local start = 0 + if default_type == nil then start = 1 end + local ret = { list = {} , reverse_lookup = {} } + for i=start, Defines.POSTER_TYPES.POSTER_TYPE_COUNT-1 do + local s = Defines.PosterTypeToName(i, default_type) + ret.list[#ret.list+1] = s + ret.reverse_lookup[s] = i end - - if fallback then - size = S.getSetting("NewProjectStickerSize") - Defines.CommitProjectStickerSize(size) - end - - return size + return ret end -function Defines.CommitProjectStickerSize(size) - reaper.GetSetMediaTrackInfo_String(Defines.ActiveProjectMasterTrack(),"P_EXT:Reannotate_ProjectStickerSize", "" .. size, true) -end return Defines diff --git a/Various/talagan_Reannotate/modules/project_settings.lua b/Various/talagan_Reannotate/modules/project_settings.lua new file mode 100644 index 000000000..fab33e525 --- /dev/null +++ b/Various/talagan_Reannotate/modules/project_settings.lua @@ -0,0 +1,158 @@ +-- @noindex +-- @author Ben 'Talagan' Babut +-- @license MIT +-- @description This file is part of Reannotate + +local S = require "modules/settings" +local D = require "modules/defines" +local JSON = require "ext/json" + +local PS = {} + + +-- Use a cache for slot labels to avoid constant JSON serialization / deserialization +PS.slot_labels = nil +PS.slot_labels_dirty = nil + +PS.poster_default_type = nil + +-- Not edited directly because we want preview / save features. +function PS.RetrieveProjectMarkdownStyle() + local _, str = reaper.GetSetMediaTrackInfo_String(D.ActiveProjectMasterTrack(), "P_EXT:Reannotate_ProjectMarkdownStyle", "", false) + + local markdown_style = nil + local fallback = false + + if str == "" or str == nil then + fallback = true + else + pcall(function() + markdown_style = JSON.decode(str) + end) + + if not markdown_style then + fallback = true + end + end + + if fallback then + -- Use new project markdown style + markdown_style = D.deepCopy(S.getSetting("NewProjectMarkdown")) + -- Commit it in project + PS.CommitProjectMarkdownStyle(markdown_style) + end + + -- TODO : Normalize markdown_style after checkout for backward comp + + return markdown_style +end + +function PS.CommitProjectMarkdownStyle(markdown_style) + -- TODO : Normalize markdown_style before commit for backward comp + + local str = JSON.encode(markdown_style) + + reaper.GetSetMediaTrackInfo_String(D.ActiveProjectMasterTrack(),"P_EXT:Reannotate_ProjectMarkdownStyle", str, true) +end + + + +function PS.RetrieveProjectStickerSize() + local _, str = reaper.GetSetMediaTrackInfo_String(D.ActiveProjectMasterTrack(), "P_EXT:Reannotate_ProjectStickerSize", "", false) + local fallback = false + local size = nil + if str == "" or str == nil then + fallback = true + else + size = tonumber(str) + end + + if fallback then + size = S.getSetting("NewProjectStickerSize") + PS.CommitProjectStickerSize(size) + end + + return size +end + +function PS.CommitProjectStickerSize(size) + reaper.GetSetMediaTrackInfo_String(D.ActiveProjectMasterTrack(),"P_EXT:Reannotate_ProjectStickerSize", "" .. size, true) +end + + +function PS.RetrieveProjectPosterDefaultType() + local _, str = reaper.GetSetMediaTrackInfo_String(D.ActiveProjectMasterTrack(), "P_EXT:Reannotate_PosterDefaultType", "", false) + local fallback = false + local type = nil + if str == "" or str == nil then + fallback = true + else + type = tonumber(str) + end + + if fallback then + type = S.getSetting("NewProjectPosterDefaultType") + PS.CommitProjectPosterDefaultType(type) + end + + return type +end + +function PS.CommitProjectPosterDefaultType(type) + reaper.GetSetMediaTrackInfo_String(D.ActiveProjectMasterTrack(),"P_EXT:Reannotate_PosterDefaultType", "" .. type, true) +end + +function PS.ProjectPosterDefaultType() + if not PS.poster_default_type then + PS.poster_default_type = PS.RetrieveProjectPosterDefaultType() + end + return PS.poster_default_type +end + +function PS.SetProjectPosterDefaultType(type) + PS.poster_default_type = type + PS.CommitProjectPosterDefaultType(type) +end + + + +function PS.RetrieveProjectSlotLabels() + local _, str = reaper.GetSetMediaTrackInfo_String(D.ActiveProjectMasterTrack(), "P_EXT:Reannotate_ProjectSlotLabels", "", false) + local slot_labels = {} + if str == "" or str == nil then + else + slot_labels = JSON.decode(str) + end + + -- Ensure labels have names by defaulting to global setting + for i = 0, D.MAX_SLOTS -1 do + slot_labels[i+1] = slot_labels[i+1] or S.getSetting("SlotLabel_" .. i) + end + + PS.slot_labels = slot_labels +end + +function PS.CommitProjectSlotLabels() + if not PS.slot_labels_dirty then return end + if not PS.slot_labels then PS.RetrieveProjectSlotLabels() end + + local str = JSON.encode(PS.slot_labels) + reaper.GetSetMediaTrackInfo_String(D.ActiveProjectMasterTrack(), "P_EXT:Reannotate_ProjectSlotLabels", str, true) + PS.slot_labels_dirty = false +end + + + +function PS.SlotLabel(slot) + if not PS.slot_labels then PS.RetrieveProjectSlotLabels() end + return PS.slot_labels[slot+1] +end + +function PS.SetSlotLabel(slot, label) + if not PS.slot_labels then PS.RetrieveProjectSlotLabels() end + PS.slot_labels[slot+1] = label + PS.slot_labels_dirty = true + PS.CommitProjectSlotLabels() +end + +return PS diff --git a/Various/talagan_Reannotate/modules/settings.lua b/Various/talagan_Reannotate/modules/settings.lua index 578d39ca5..a16217f3f 100644 --- a/Various/talagan_Reannotate/modules/settings.lua +++ b/Various/talagan_Reannotate/modules/settings.lua @@ -4,6 +4,7 @@ -- @description This file is part of Reannotate local JSON = require "ext/json" +local D = require "modules/defines" local DefaultMarkdownStyle = { default = { font_family = "Arial", base_color = "#CCCCCC", bold_color = "white", autopad = 5 --[[font_size = 13, ]] }, @@ -27,6 +28,8 @@ local DefaultMarkdownStyle = { separator = { padding_top = 3, padding_bottom = 7 } } +local DefaultPosterType = D.POSTER_TYPES.NOTE_RENDERERED_AS_PLAIN_POSTER + local SettingDefs = { UseDebugger = { type = "bool", default = false }, UseProfiler = { type = "bool", default = false }, @@ -41,9 +44,10 @@ local SettingDefs = { SlotLabel_7 = { type = "string", default = "Problems"}, -- Styling - UIFontSize = { type = "int", default = 12 }, - NewProjectMarkdown = { type = "json", default = DefaultMarkdownStyle }, - NewProjectStickerSize = { type = "int", default = 12 } + UIFontSize = { type = "int", default = 12 }, + NewProjectMarkdown = { type = "json", default = DefaultMarkdownStyle }, + NewProjectStickerSize = { type = "int", default = 12 }, + NewProjectPosterDefaultType = { type = "int", default = DefaultPosterType } }; local function unsafestr(str) diff --git a/Various/talagan_Reannotate/widgets/note_editor.lua b/Various/talagan_Reannotate/widgets/note_editor.lua index 68bbc8212..73dfa0354 100644 --- a/Various/talagan_Reannotate/widgets/note_editor.lua +++ b/Various/talagan_Reannotate/widgets/note_editor.lua @@ -10,6 +10,7 @@ local StickerPicker = require "widgets/sticker_picker" local Sticker = require "classes/sticker" local D = require "modules/defines" local S = require "modules/settings" +local PS = require "modules/project_settings" local NoteEditor = {} NoteEditor.__index = NoteEditor @@ -67,6 +68,8 @@ function NoteEditor:title() t = t .. "Item" elseif type == "project" then t = t .. "Project" + elseif type == "region" then + t = t .. "Region" else error("Unimplemented") end @@ -85,13 +88,15 @@ function NoteEditor:onSlotEditChange() end function NoteEditor:onSlotCommit() + -- Clear poster cache and let Reannotate recalculate it on next draw + self.edited_thing.cache.posters = nil if self.slot_commit_callback then self.slot_commit_callback() end end function NoteEditor:stickerSize() - return D.RetrieveProjectStickerSize() + return PS.RetrieveProjectStickerSize() end function NoteEditor:renderStickerZone(ctx, stickers, should_go_to_line) @@ -116,9 +121,10 @@ function NoteEditor:renderStickerZone(ctx, stickers, should_go_to_line) local rw, _ = ImGui.GetContentRegionAvail(ctx) if estimated_width > rw and num_on_line ~= 0 then -- Go to line if no more room - ImGui.NewLine(ctx) xc, yc = ImGui.GetCursorScreenPos(ctx) - yc = yc + 5 + yc = yc + metrics.height + estimated_spacing + ImGui.NewLine(ctx) + xc, _ = ImGui.GetCursorScreenPos(ctx) ImGui.SetCursorScreenPos(ctx, xc, yc) num_on_line = 0 elseif num_on_line ~= 0 then @@ -220,9 +226,9 @@ function NoteEditor:draw() ImGui.Function_SetValue(app_ctx.cursor_func, "WANTED_CURSOR", string.len(entry)) end + -- Tab bar local sel_col = Color:new(D.SlotColor(self.edited_slot)) ImGui.PushStyleColor(ctx, ImGui.Col_TabSelected, sel_col:to_irgba()) - if ImGui.BeginTabBar(ctx, "Test##note_editor_tab", ImGui.TabBarFlags_NoCloseWithMiddleMouseButton | ImGui.TabBarFlags_NoTabListScrollingButtons) then local selection_has_changed = (self.last_selected_tab ~= self.edited_slot) @@ -250,7 +256,7 @@ function NoteEditor:draw() flags = flags | ImGui.TabItemFlags_SetSelected end - local e_vis, e_sel = ImGui.BeginTabItem(ctx, D.SlotLabel(slot), false, flags) + local e_vis, e_sel = ImGui.BeginTabItem(ctx, PS.SlotLabel(slot), false, flags) if e_vis then -- The tab api is awfull and needs to track things to avoid race conditions @@ -274,16 +280,12 @@ function NoteEditor:draw() ImGui.EndTabBar(ctx) end - ImGui.PopStyleColor(ctx) -- Sticker zone + ImGui.SeparatorText(ctx, "Stickers") - ImGui.Dummy(ctx,0,2) - ImGui.Text(ctx, "Stickers") - ImGui.SameLine(ctx) - ImGui.SetCursorPosY(ctx, ImGui.GetCursorPosY(ctx) - 2) - + ImGui.AlignTextToFramePadding(ctx) local slot_stickers = self.edited_thing.notes:slotStickers(self.edited_slot) if #slot_stickers > 0 then slot_stickers = slot_stickers @@ -303,25 +305,63 @@ function NoteEditor:draw() end end + -- '+' sticker button local xc, yc = ImGui.GetCursorScreenPos(ctx) - ImGui.SetCursorScreenPos(ctx, xc + 6, yc + 1) + ImGui.SetCursorScreenPos(ctx, xc + 6, yc) ImGui.PushStyleVar(ctx, ImGui.StyleVar_FrameRounding, 2) local px, py = ImGui.GetCursorScreenPos(ctx) - if ImGui.Button(ctx, "+") then + ImGui.PushFont(ctx, app_ctx.arial_font, PS.RetrieveProjectStickerSize() + 2) + if ImGui.Button(ctx, '+') then self.sticker_picker = StickerPicker:new(self.edited_thing, self.edited_slot) self.sticker_picker:setPosition(px + 20, py) end + ImGui.PopFont(ctx) ImGui.PopStyleVar(ctx) - ImGui.Dummy(ctx,0,2) + ImGui.SeparatorText(ctx, "Poster") + + -- Poster type selection + ImGui.AlignTextToFramePadding(ctx) + ImGui.Text(ctx, "Type") + ImGui.SameLine(ctx) + local poster_combo_info = D.PosterTypeComboInfo(PS.ProjectPosterDefaultType()) + local cur_val = self.edited_thing.notes:slotPosterType(self.edited_slot) + if ImGui.BeginCombo(ctx, "##poster_type_combo", D.PosterTypeToName(cur_val, PS.ProjectPosterDefaultType()) , ImGui.ComboFlags_None | ImGui.ComboFlags_WidthFitPreview) then + for _, v in ipairs(poster_combo_info.list) do + local real_val = poster_combo_info.reverse_lookup[v] + if ImGui.Selectable(ctx, v, cur_val == real_val) then + self.edited_thing.notes:setSlotPosterType(self.edited_slot, real_val) + self.edited_thing.notes:commit() + self:onSlotCommit() + end + end + ImGui.EndCombo(ctx) + end + + local resolved_type = self.edited_thing.notes:resolvedSlotPosterType(self.edited_slot) + if resolved_type == D.POSTER_TYPES.CUSTOM_MARKDOWN_POSTER or resolved_type == D.POSTER_TYPES.CUSTOM_PLAIN_POSTER then + local ct = self.edited_thing.notes:slotCustomPoster(self.edited_slot) + local ax, ay = ImGui.GetContentRegionAvail(ctx) + b, ct = ImGui.InputTextMultiline(ctx, "##reannotate_note_poster_edit_multiline_" .. self.rand, ct, ax , 100, ImGui.InputTextFlags_CallbackAlways, cursor_func) + if b and is_open then + -- Commit on every text change + self.edited_thing.notes:setSlotCustomPoster(self.edited_slot, ct) + self.edited_thing.notes:commit() + self:onSlotCommit() + end + end + + ImGui.SeparatorText(ctx, "Note content") + -- Multi line local ax, ay = ImGui.GetContentRegionAvail(ctx) if self.grab_focus then ImGui.SetKeyboardFocusHere(ctx) end - b, entry = ImGui.InputTextMultiline(ctx, "##reannotate_note_edit_multiline_" .. self.rand, entry, ax , ay, ImGui.InputTextFlags_CallbackAlways, cursor_func) + --------- + -- Because we're in auto commit, close on shift enter or escape if ImGui.IsWindowFocused(ctx) and (ImGui.IsKeyChordPressed(ctx, ImGui.Key_Enter | ImGui.Mod_Shift) or ImGui.IsKeyPressed(ctx, ImGui.Key_Escape, false)) then is_open = false diff --git a/Various/talagan_Reannotate/widgets/overlay_canvas.lua b/Various/talagan_Reannotate/widgets/overlay_canvas.lua index 237245e67..af659a11a 100644 --- a/Various/talagan_Reannotate/widgets/overlay_canvas.lua +++ b/Various/talagan_Reannotate/widgets/overlay_canvas.lua @@ -5,8 +5,11 @@ local ImGui = require "ext/imgui" local AppContext = require "classes/app_context" -local Sticker = require "classes/sticker" local D = require "modules/defines" +local PS = require "modules/project_settings" +local ImGuiMd = require "reaimgui_markdown" +local ImGuiMdAst = require "reaimgui_markdown/markdown-ast" +local ImGuiMdText = require "reaimgui_markdown/markdown-text" local OverlayCanvas = {} OverlayCanvas.__index = OverlayCanvas @@ -79,12 +82,12 @@ function OverlayCanvas:forwardEvent(event) -- TODO : ATM mcp layout changes are not detected so this will scroll the MCP but tracks will be at the wrong place if reaper.JS_Window_IsVisible(app_ctx.mcp_other.hwnd) and - (app_ctx.mcp_other.x <= mx and mx <= app_ctx.mcp_other.x + app_ctx.mcp_other.w) and - (app_ctx.mcp_other.y <= my and my <= app_ctx.mcp_other.y + app_ctx.mcp_other.h) then - target = app_ctx.mcp_other.hwnd + (app_ctx.mcp_other.x <= mx and mx <= app_ctx.mcp_other.x + app_ctx.mcp_other.w) and + (app_ctx.mcp_other.y <= my and my <= app_ctx.mcp_other.y + app_ctx.mcp_other.h) then + target = app_ctx.mcp_other.hwnd end ----@diagnostic disable-next-line: param-type-mismatch + ---@diagnostic disable-next-line: param-type-mismatch reaper.JS_WindowMessage_Send(target, event, wpl, wph, lpl, lph) self.last_peeked_message_times[event] = time end @@ -103,7 +106,6 @@ function OverlayCanvas:drawQuickSettings() local draw_list = ImGui.GetWindowDrawList(ctx) local mx, my = ImGui.GetMousePos(ctx) local is_clicked = ImGui.IsMouseClicked(ctx, ImGui.MouseButton_Left) - local visible_things = self.parent_overlay.visible_things local margin_t = 8 local margin_l = 8 @@ -116,7 +118,7 @@ function OverlayCanvas:drawQuickSettings() local sinalpha = (0xFF - 0x40 + math.floor(0x40 * math.sin(reaper.time_precise()*10))) ----@diagnostic disable-next-line: redundant-parameter + ---@diagnostic disable-next-line: redundant-parameter ImGui.PushFont(ctx, app_ctx.arial_font, 12) ImGui.DrawList_AddRectFilled(draw_list, app_ctx.main_toolbar.x, app_ctx.main_toolbar.y, app_ctx.main_toolbar.x + app_ctx.main_toolbar.w, app_ctx.main_toolbar.y + app_ctx.main_toolbar.h, 0x202020E0, 5) @@ -143,7 +145,7 @@ function OverlayCanvas:drawQuickSettings() ImGui.PushStyleVar(ctx, ImGui.StyleVar_WindowPadding, 3, 1) ImGui.PushStyleColor(ctx, ImGui.Col_PopupBg, color ) ImGui.PushStyleColor(ctx, ImGui.Col_Text, 0x000000FF) - ImGui.SetTooltip(ctx, D.SlotLabel(slot)) + ImGui.SetTooltip(ctx, PS.SlotLabel(slot)) ImGui.PopStyleColor(ctx, 2) ImGui.PopStyleVar(ctx) end @@ -215,7 +217,7 @@ function OverlayCanvas:drawQuickSettings() if font_size > 26 then font_size = 26 end if font_size < 16 then font_size = 16 end ----@diagnostic disable-next-line: redundant-parameter + ---@diagnostic disable-next-line: redundant-parameter ImGui.PushFont(ctx, app_ctx.arial_font_italic, font_size) local rw, rh = ImGui.CalcTextSize(ctx, "Reannotate") @@ -243,7 +245,128 @@ function OverlayCanvas:thingBelongsToThisCanvas(thing) end function OverlayCanvas:stickerSize() - return D.RetrieveProjectStickerSize() + return PS.RetrieveProjectStickerSize() +end + +function OverlayCanvas:renderThingSlotPoster(thing, slot, ctx, draw_list, x1, x2, y1, y2) + if slot == -1 then return end + + local type = thing.notes:resolvedSlotPosterType(slot) + + if type == D.POSTER_TYPES.NO_POSTER then return end + + local h = y2 - y1 + local vpad = (h > 40) and 5 or math.floor( math.max(h - 20) / 4.0) + local hpad = 5 + local poster_w = x2-x1-2*hpad + local poster_h = y2-y1-2*vpad + + if poster_w <= 0 or poster_h <= 0 then return end + + local window_flags = ImGui.WindowFlags_NoBackground | + ImGui.WindowFlags_NoScrollbar | + ImGui.WindowFlags_NoDecoration | + ImGui.WindowFlags_NoDocking | + ImGui.WindowFlags_NoFocusOnAppearing | + ImGui.WindowFlags_NoInputs | + ImGui.WindowFlags_NoSavedSettings | + ImGui.WindowFlags_NoMove + + ImGui.SetCursorScreenPos(ctx, x1 + hpad, y1 + vpad) + if ImGui.BeginChild(ctx, "thing_poster_wrapper_" .. thing.rand .. "_" .. slot, poster_w, poster_h, ImGui.ChildFlags_None, window_flags) then + ImGui.SetCursorPos(ctx, 0, 0) + + if not thing.cache.posters then + thing.cache.posters = {} + end + + -- Create cache or reset it if needed + if not thing.cache.posters[slot] or thing.cache.posters[slot].last_type ~= type then + thing.cache.posters[slot] = { plain = nil, mdwidget = nil, last_type = type } + end + + local cache = thing.cache.posters[slot] + local is_plain_text = (type == D.POSTER_TYPES.CUSTOM_PLAIN_POSTER or type == D.POSTER_TYPES.NOTE_RENDERERED_AS_PLAIN_POSTER) + local use_note_text = (type == D.POSTER_TYPES.NOTE_RENDERERED_AS_PLAIN_POSTER or type == D.POSTER_TYPES.NOTE_RENDERERED_AS_MARKDOWN_POSTER) + + if is_plain_text then + if not cache.plain then + local tttext = (use_note_text and thing.notes:slotText(slot) or thing.notes:slotCustomPoster(slot)) or '' + cache.plain = ImGuiMdText.ASTToPlainText( ImGuiMdAst(tttext), {newlines=true}) + end + ImGui.Text(ctx, cache.plain) + else + if not cache.mdwidget then + local tttext = (use_note_text and thing.notes:slotText(slot) or thing.notes:slotCustomPoster(slot)) or '' + cache.mdwidget = ImGuiMd:new(ctx, "thing_md_widget_" .. thing.rand .. "_" .. slot, {wrap = false, skip_last_whitespace = true, autopad = true, additional_window_flags = ImGui.WindowFlags_NoSavedSettings | ImGui.WindowFlags_NoFocusOnAppearing | ImGui.WindowFlags_NoInputs | ImGui.WindowFlags_NoScrollbar }, PS.RetrieveProjectMarkdownStyle() ) + cache.mdwidget:setText(tttext) + end + cache.mdwidget:render(ctx) + end + ImGui.EndChild(ctx) + end +end + +function OverlayCanvas:renderThingSlotStickers(thing, slot, ctx, x1, x2, y1, y2) + local slot_stickers = thing.notes:slotStickers(slot) + + if #slot_stickers > 0 then + + -- RENDER STICKERS + local hmargin = 5 + local vmargin = 5 + local vpad = 4 + local hpad = 3 + local font_size = self.sticker_size + + -- Reduce margins and the font size if not enough vertical space + if y2 - y1 < font_size + 10 + 2 * vmargin then + vmargin = 0.5 * ((y2 - y1) - font_size - 10) + if vmargin < 0 then + vmargin = 0 + font_size = font_size - 2 + end + end + + local num_on_line = 0 + local cursor_x, cursor_y = x2 - hmargin, y1 + vmargin -- Align top right + + ImGui.SetCursorScreenPos(ctx, x1, y1) + local window_flags = ImGui.WindowFlags_NoBackground | + ImGui.WindowFlags_NoScrollbar | + ImGui.WindowFlags_NoDecoration | + ImGui.WindowFlags_NoDocking | + ImGui.WindowFlags_NoFocusOnAppearing | + ImGui.WindowFlags_NoInputs | + ImGui.WindowFlags_NoSavedSettings | + ImGui.WindowFlags_NoMove + + -- Use a child for receiving all stickers, this will allow clipping and using standard ImGui directives + if ImGui.BeginChild(ctx, "stickers_for_thing_" .. thing.rand .. "_" .. slot, x2-x1, y2-y1, ImGui.ChildFlags_None, window_flags) then + ImGui.SetCursorPos(ctx, 0, 0) + local draw_list = ImGui.GetWindowDrawList(ctx) + ImGui.DrawList_PushClipRect(draw_list, x1 + 3, y1 + 3, x2 + 3, y2 - 3) + for _, sticker in ipairs(slot_stickers) do + local metrics = sticker:PreRender(ctx, font_size) + + local available_width = cursor_x - (x1 + hmargin) + if num_on_line ~= 0 and (available_width < metrics.width) then + cursor_x = x2 - hmargin + cursor_y = cursor_y + metrics.height + vpad + num_on_line = 0 + end + + sticker:Render(ctx, metrics, cursor_x - metrics.width, cursor_y) + cursor_x = cursor_x - metrics.width - hpad + num_on_line = num_on_line + 1 + end + -- Force ImGui to extend bounds + ImGui.DrawList_PopClipRect(draw_list) + ImGui.Dummy(ctx,0,0) + + ImGui.EndChild(ctx) + end + end end function OverlayCanvas:drawVisibleThing(thing) @@ -258,8 +381,6 @@ function OverlayCanvas:drawVisibleThing(thing) local hovered = parent_overlay.hovered_thing and parent_overlay.hovered_thing.object == thing.object local edited = note_editor and note_editor.edited_thing.object == thing.object - local sticker_size = self.sticker_size - if not hovered then -- reset hovered slot thing.hovered_slot = nil @@ -295,12 +416,12 @@ function OverlayCanvas:drawVisibleThing(thing) local div = -1 for i=0, D.MAX_SLOTS-1 do -- We change the slot span order (1,2,3... and then 0) - local is_no_note_slot = (i==0 and not has_notes_to_show) + local is_no_note_slot = (i==0 and not has_notes_to_show) - local slot = (i==D.MAX_SLOTS - 1) and (0) or (i+1) - local is_slot_enabled = (is_no_note_slot or app_ctx.enabled_category_filters[slot + 1]) + local slot = (i==D.MAX_SLOTS - 1) and (0) or (i+1) + local is_slot_enabled = (is_no_note_slot or app_ctx.enabled_category_filters[slot + 1]) - local the_slot_matches_the_search = (thing.cache.search_results[slot+1]) + local the_slot_matches_the_search = (thing.cache.search_results[slot+1]) -- The empty slot is always visible local is_slot_visible = (is_no_note_slot) or (not thing.notes:isSlotBlank(slot) and is_slot_enabled and the_slot_matches_the_search) @@ -367,6 +488,11 @@ function OverlayCanvas:drawVisibleThing(thing) -- Background color. ImGui.DrawList_AddRectFilled(draw_list, x1, y1, x2, y2, bg_color, 0, 0) + if not is_no_note_slot then + self:renderThingSlotPoster(thing, slot, ctx, draw_list, x1, x2, y1, y2) + end + + -- Borders and triangles if hdivide then -- Left border if div == 0 and not thing.clamped_left then @@ -395,45 +521,7 @@ function OverlayCanvas:drawVisibleThing(thing) end if not is_no_note_slot then - -- RENDER STICKERS - local hmargin = 5 - local vmargin = 5 - local vpad = 4 - local hpad = 3 - - local font_size = sticker_size - - -- Reduce margins and the font size if not enough vertical space - if y2 - y1 < font_size + 10 + 2 * vmargin then - vmargin = 0.5 * ((y2 - y1) - font_size - 10) - if vmargin < 0 then - vmargin = 0 - font_size = font_size - 2 - end - end - - local cursor_x, cursor_y = x2 - hmargin, y1 + vmargin -- Align top right - - local slot_stickers = thing.notes:slotStickers(slot) - local num_on_line = 0 - ImGui.DrawList_PushClipRect(draw_list, x1 + 3, y1 + 3, x2 + 3, y2 - 3) - for _, sticker in ipairs(slot_stickers) do - local metrics = sticker:PreRender(ctx, font_size) - - local available_width = cursor_x - (x1 + hmargin) - if num_on_line ~= 0 and (available_width < metrics.width) then - cursor_x = x2 - hmargin - cursor_y = cursor_y + metrics.height + vpad - num_on_line = 0 - end - - sticker:Render(ctx, metrics, cursor_x - metrics.width, cursor_y) - cursor_x = cursor_x - metrics.width - hpad - num_on_line = num_on_line + 1 - end - -- Force ImGui to extend bounds - ImGui.DrawList_PopClipRect(draw_list) - ImGui.Dummy(ctx,0,0) + self:renderThingSlotStickers(thing, slot, ctx, x1, x2, y1, y2) -- Post-it triangles if not (div == 0 and thing.clamped_left) then @@ -456,7 +544,6 @@ function OverlayCanvas:GrabFocus() self.grab_focus = true end - function OverlayCanvas:draw() local parent_overlay = self.parent_overlay local visible_things = parent_overlay.visible_things @@ -480,21 +567,23 @@ function OverlayCanvas:draw() -- Begin ImGui frame with visible background for debugging local succ, is_open = ImGui.Begin(ctx, self:title() , nil, - ImGui.WindowFlags_NoTitleBar | - ImGui.WindowFlags_NoScrollWithMouse | - ImGui.WindowFlags_NoScrollbar | - ImGui.WindowFlags_NoResize | - ImGui.WindowFlags_NoNav | - ImGui.WindowFlags_NoNavInputs | - ImGui.WindowFlags_NoMove | - ImGui.WindowFlags_NoDocking | - ImGui.WindowFlags_NoDecoration - ) + ImGui.WindowFlags_NoTitleBar | + ImGui.WindowFlags_NoScrollWithMouse | + ImGui.WindowFlags_NoScrollbar | + ImGui.WindowFlags_NoResize | + ImGui.WindowFlags_NoNav | + ImGui.WindowFlags_NoNavInputs | + ImGui.WindowFlags_NoMove | + ImGui.WindowFlags_NoDocking | + ImGui.WindowFlags_NoDecoration | + ImGui.WindowFlags_NoSavedSettings) ImGui.PopStyleVar(ctx) if succ then + local draw_list = ImGui.GetWindowDrawList(ctx) + if self.grab_focus then ImGui.SetWindowFocus(ctx) self.grab_focus = false diff --git a/Various/talagan_Reannotate/widgets/quick_preview_overlay.lua b/Various/talagan_Reannotate/widgets/quick_preview_overlay.lua index a93086f29..6e20612db 100644 --- a/Various/talagan_Reannotate/widgets/quick_preview_overlay.lua +++ b/Various/talagan_Reannotate/widgets/quick_preview_overlay.lua @@ -13,6 +13,7 @@ local SettingsEditor = require "widgets/settings_editor" local S = require "modules/settings" local D = require "modules/defines" +local PS = require "modules/project_settings" local reaper_ext = require "modules/reaper_ext" local MemCache = require "classes/mem_cache" @@ -115,6 +116,7 @@ function QuickPreviewOverlay:buildEditContextForThing(object, track_num, parent_ -- Note info cache = cache, notes = cache.notes, + rand = math.random(), -- Track num track_num = track_num + 1 } @@ -242,6 +244,12 @@ function QuickPreviewOverlay:updateVisibleThings() return minx, miny, w, h, clamped_left, clamped_right, clamped_top, clamped_bottom end + -- Get Arrange view time range + local start_time, end_time = avi.start_time, avi.end_time + if start_time == end_time then + start_time, end_time = reaper.GetPlayPosition(), reaper.GetProjectLength(0) + end + -- Iterate through tracks for i = -1, track_count - 1 do local is_master = (i==-1) @@ -312,12 +320,6 @@ function QuickPreviewOverlay:updateVisibleThings() local item_len = reaper.GetMediaItemInfo_Value(item, "D_LENGTH") local item_end = item_pos + item_len - -- Get Arrange view time range - local start_time, end_time = avi.start_time, avi.end_time - if start_time == end_time then - start_time, end_time = reaper.GetPlayPosition(), reaper.GetProjectLength(0) - end - -- Check if item is visible if item_end >= start_time and item_pos <= end_time then local pos_x_pixels = self:timeToPixels(app_ctx, item_pos) @@ -373,6 +375,30 @@ function QuickPreviewOverlay:updateVisibleThings() table.insert(self.visible_things, proj_entry) end + if app_ctx.time_ruler.hwnd and reaper.JS_Window_IsVisible(app_ctx.time_ruler.hwnd) then + local idx = 0 + while true do + local retval, isrgn, pos, rgnend, name, markrgnindexnumber, color = reaper.EnumProjectMarkers3(0, idx) + if retval == 0 then break end + + if isrgn and rgnend >= start_time and pos <= end_time then + local pos_x_pixels = self:timeToPixels(app_ctx, pos) + local pos_y_pixels = 0 + local len_y_pixels = app_ctx.time_ruler.h + local len_x_pixels = self:timeToPixels(app_ctx, rgnend) - pos_x_pixels + + local clamped_left, clamped_right, clamped_top, clamped_bottom = false, false, false, false + pos_x_pixels, pos_y_pixels, len_x_pixels, len_y_pixels, clamped_left, clamped_right, clamped_top, clamped_bottom = block_clamp(pos_x_pixels, pos_y_pixels, len_x_pixels, len_y_pixels, app_ctx.time_ruler.w, app_ctx.time_ruler.h, 0, 0) + + local _, region_guid = reaper.GetSetProjectInfo_String( 0, "MARKER_GUID:" .. markrgnindexnumber, "", false ) + + local region_entry = self:buildEditContextForThing({t="region", n = 'name', guid=region_guid}, -1, "time_ruler", pos_x_pixels, pos_y_pixels, len_x_pixels, len_y_pixels, clamped_left, clamped_right, clamped_top, clamped_bottom) + table.insert(self.visible_things, region_entry) + end + idx = idx + 1 + end + end + -- Visibility has changed, reapply the search self:applySearch() end @@ -438,7 +464,7 @@ function QuickPreviewOverlay:minimizeTopWindowsAtLaunch() local c, l = reaper.JS_Window_ListAllTop() for token in string.gmatch(l, "[^,]+") do ----@diagnostic disable-next-line: param-type-mismatch + ---@diagnostic disable-next-line: param-type-mismatch local subhwnd = reaper.JS_Window_HandleFromAddress(token) if not subhwnd then return end @@ -588,10 +614,10 @@ function QuickPreviewOverlay:editorAdvisedPositionAndSizeForThing(thing, mouse_x -- If no width none assume something big local w = NoteEditor.known_width or 1000 - local h = tth -- Try to use the tooltip height + local h = tth + 200 -- Try to use the tooltip height - if h < 200 then - h = 200 + if h < 400 then + h = 400 end local x = ttpos.x - w @@ -650,14 +676,14 @@ function QuickPreviewOverlay:drawTooltip() if thing_to_tooltip then if not self.mdwidget then -- This will lag a bit. Do it on first capture. - self.mdwidget = ImGuiMd:new(ctx, "markdown_widget_1", { wrap = true, autopad = true, skip_last_whitespace = true }, D.RetrieveProjectMarkdownStyle() ) + self.mdwidget = ImGuiMd:new(ctx, "markdown_widget_1", { wrap = true, autopad = true, skip_last_whitespace = true }, PS.RetrieveProjectMarkdownStyle() ) end local slot_to_tooltip = ((self.note_editor) and (self.note_editor.edited_slot)) or (pinned_thing and pinned_thing.capture_slot) or (thing_to_tooltip.hovered_slot) local tttext = (slot_to_tooltip == -1) and (thing_to_tooltip.no_notes_message) or (thing_to_tooltip.notes:slotText(slot_to_tooltip)) local tooltip_is_empty = (slot_to_tooltip == -1) if tttext == "" or tttext == nil then - tttext = "`:grey:No " .. thing_to_tooltip.cache.type .. " notes for the `_:grey:" .. D.SlotLabel(slot_to_tooltip):lower() .. "_ `:grey:category`" + tttext = "`:grey:No " .. thing_to_tooltip.cache.type .. " notes for the `_:grey:" .. PS.SlotLabel(slot_to_tooltip):lower() .. "_ `:grey:category`" tooltip_is_empty = true end @@ -677,29 +703,37 @@ function QuickPreviewOverlay:drawTooltip() -- First draw, force coordinates and size if self.tt_draw_count == 0 then - ImGui.SetNextWindowSize(ctx, ttw, tth) -- This will force the tooltip window re-creation to avoid keeping precedent state .. and have scrollbar bugs self.rand = math.random() + ImGui.SetNextWindowSize(ctx, ttw, tth) + ImGui.SetNextWindowPos(ctx, tt_pos.x, tt_pos.y) end if self.tt_draw_count == 1 and tooltip_is_empty then -- Auto resize "empty" tooltips. This glitches during 1 frame... local target_h = self.mdwidget.max_y + 18 local target_w = self.mdwidget.max_x + 18 - -- ImGui.SetNextWindowSize(ctx, target_w, target_h) + -- ImGui.SetNextWindowSize(ctx, target_w, target_h) end self.mdwidget:setText(tttext) + if not self.note_editor then + -- Avoid setting every frame if note editor is open cause it will prevent resizing from the left + ImGui.SetNextWindowPos(ctx, tt_pos.x, tt_pos.y) + end ImGui.SetNextWindowBgAlpha(ctx, 1) - ImGui.SetNextWindowPos(ctx, tt_pos.x, tt_pos.y) - local tooltip_flags = ImGui.WindowFlags_NoFocusOnAppearing | ImGui.WindowFlags_NoTitleBar | ImGui.WindowFlags_TopMost | ImGui.WindowFlags_NoSavedSettings | ImGui.WindowFlags_NoScrollbar + local tooltip_flags = ImGui.WindowFlags_NoFocusOnAppearing | + ImGui.WindowFlags_NoTitleBar | + ImGui.WindowFlags_TopMost | + ImGui.WindowFlags_NoSavedSettings | + ImGui.WindowFlags_NoScrollbar if ImGui.Begin(ctx, "Reannotate Notes (Tooltip)##" .. self.rand, true, tooltip_flags) then local cur_x, cur_y = ImGui.GetWindowPos(ctx) - local cur_w, cur_h = ImGui.GetWindowSize(ctx) local draw_list = ImGui.GetWindowDrawList(ctx) + local cur_w, cur_h = ImGui.GetWindowSize(ctx) self.mdwidget:render(ctx) @@ -712,6 +746,7 @@ function QuickPreviewOverlay:drawTooltip() thing_to_tooltip.notes:setSlotText(slot_to_tooltip, tttext) thing_to_tooltip.notes:commit() + thing_to_tooltip.cache.posters = nil end -- Border. Tooltip is shown when edited or hoevered @@ -767,7 +802,7 @@ function QuickPreviewOverlay:draw() local mx, my = reaper.GetMousePosition() mx, my = ImGui.PointConvertNative(ctx, mx, my) ----@diagnostic disable-next-line: redundant-parameter + ---@diagnostic disable-next-line: redundant-parameter ImGui.PushFont(ctx, app_ctx.arial_font, 12) self.draw_count = self.draw_count or 0 diff --git a/Various/talagan_Reannotate/widgets/settings_editor.lua b/Various/talagan_Reannotate/widgets/settings_editor.lua index 030e5e077..7da202a6e 100644 --- a/Various/talagan_Reannotate/widgets/settings_editor.lua +++ b/Various/talagan_Reannotate/widgets/settings_editor.lua @@ -9,6 +9,7 @@ local Color = require "classes/color" local Sticker = require "classes/sticker" local Notes = require "classes/notes" local S = require "modules/settings" +local PS = require "modules/project_settings" local D = require "modules/defines" local ImGuiMd = require "reaimgui_markdown" @@ -30,9 +31,6 @@ function SettingsEditor:_initialize() self.new_project_markdown_style = self:retrieveNewProjectMarkdownStyle() self.cur_project_markdown_style = self:retrieveCurProjectMarkdownStyle() - self.new_project_sticker_size = S.getSetting("NewProjectStickerSize") - self.cur_project_sticker_size = D.RetrieveProjectStickerSize() - self.mdwidget_new_proj = ImGuiMd:new(AppContext:instance().imgui_ctx, "markdown_widget_new_proj", { wrap = true, autopad = true, skip_last_whitespace = true }, self.new_project_markdown_style ) self.mdwidget_cur_proj = ImGuiMd:new(AppContext:instance().imgui_ctx, "markdown_widget_cur_proj", { wrap = true, autopad = true, skip_last_whitespace = true }, self.cur_project_markdown_style ) end @@ -42,7 +40,7 @@ function SettingsEditor:retrieveNewProjectMarkdownStyle() end function SettingsEditor:retrieveCurProjectMarkdownStyle() - return D.deepCopy(D.RetrieveProjectMarkdownStyle()) + return D.deepCopy(PS.RetrieveProjectMarkdownStyle()) end function SettingsEditor:setPosition(pos_x, pos_y) @@ -79,17 +77,17 @@ function SettingsEditor:categoryNamesTable() S.setSetting("SlotLabel_" .. slot, v) end else - ImGui.Text(ctx, " " .. D.SlotLabel(slot)) + ImGui.Text(ctx, " " .. PS.SlotLabel(slot)) end ImGui.TableNextColumn(ctx) ImGui.SetNextItemWidth(ctx, 150) if slot ~= 0 then - local b, v = ImGui.InputText(ctx, "##cur_proj_edit_slot_" .. i, D.SlotLabel(slot)) + local b, v = ImGui.InputText(ctx, "##cur_proj_edit_slot_" .. i, PS.SlotLabel(slot)) if b then - D.SetSlotLabel(slot, v) + PS.SetSlotLabel(slot, v) end else - ImGui.Text(ctx, " " .. D.SlotLabel(slot)) + ImGui.Text(ctx, " " .. PS.SlotLabel(slot)) end end ImGui.TableNextRow(ctx) @@ -103,7 +101,7 @@ function SettingsEditor:categoryNamesTable() ImGui.TableNextColumn(ctx) if ImGui.Button(ctx, "Reset to defaults##reset_project_labels_to_defaults_button") then for i = 0, D.MAX_SLOTS-1 do - D.SetSlotLabel(i, S.getSettingSpec("SlotLabel_"..i).default ) + PS.SetSlotLabel(i, S.getSettingSpec("SlotLabel_"..i).default ) end end @@ -202,34 +200,6 @@ function SettingsEditor:markdownRow(widget, style, entry_name) ImGui.PopID(ctx) end -function SettingsEditor:stickersStyleRow(sticker_size_get_callback, sticker_size_update_callback) - local app_ctx = AppContext:instance() - local ctx = app_ctx.imgui_ctx - local b, v = false, '' - - ImGui.PushID(ctx, "style_sticker") - ImGui.TableNextRow(ctx) - ImGui.TableNextRow(ctx) - - ImGui.TableNextColumn(ctx) - ImGui.AlignTextToFramePadding(ctx) - ImGui.Text(ctx, "stickers") - - ImGui.TableNextColumn(ctx) -- Color normal - ImGui.TableNextColumn(ctx) -- Color bold - ImGui.TableNextColumn(ctx) -- Font Size - ImGui.SetNextItemWidth(ctx, 50) - - b, v = ImGui.SliderInt(ctx, "##font_size", sticker_size_get_callback(), 8, 20) - if b then - sticker_size_update_callback(v) - end - - ImGui.TableNextColumn(ctx) -- padding top - ImGui.TableNextColumn(ctx) -- padding bottom - ImGui.PopID(ctx) -end - function SettingsEditor:markdownStyles(widget, style_container) self:markdownRow(widget, style_container, "default") self:markdownRow(widget, style_container, "link") @@ -281,26 +251,23 @@ function SettingsEditor:markdownTable() local ctx = app_ctx.imgui_ctx ImGui.BeginGroup(ctx) - ImGui.Selectable(ctx, "Stylesheet for new projects", true, 0, WIDGET_WIDTH + (self.preview_new_proj_markdown and PREVIEW_WIDTH + 4 or 0)) + ImGui.Selectable(ctx, "New Project", true, 0, WIDGET_WIDTH + (self.preview_new_proj_markdown and PREVIEW_WIDTH + 4 or 0)) if ImGui.BeginChild(ctx, "aaaa##table_markdown_new", WIDGET_WIDTH, MD_EDITOR_HEIGHT) then ImGui.PushID(ctx, "table_markdown_new_proj") if ImGui.BeginTable(ctx, "", 6, 0, WIDGET_WIDTH) then self:markdownTableHeaders() self:markdownStyles(self.mdwidget_new_proj, self.new_project_markdown_style) - ImGui.TableNextRow(ctx) - - self:stickersStyleRow(function() return self.new_project_sticker_size end, function(v) self.new_project_sticker_size = v end) ImGui.EndTable(ctx) end + -- BUTTONS ImGui.Dummy(ctx,1,10) - local d = not (D.deepCompare(self:retrieveNewProjectMarkdownStyle(), self.new_project_markdown_style) and (S.getSetting("NewProjectStickerSize") == self.new_project_sticker_size)) + local d = not (D.deepCompare(self:retrieveNewProjectMarkdownStyle(), self.new_project_markdown_style)) if d then ImGui.PushStyleColor(ctx, ImGui.Col_Button, 0xFF0000FF) end if ImGui.Button(ctx, "Save and apply !") then S.setSetting("NewProjectMarkdown", self.new_project_markdown_style) - S.setSetting("NewProjectStickerSize", self.new_project_sticker_size) if self.on_commit_new_project_style_callback then self.on_commit_new_project_style_callback(D.deepCopy(self.new_project_markdown_style)) end @@ -314,7 +281,6 @@ function SettingsEditor:markdownTable() ImGui.SameLine(ctx) if ImGui.Button(ctx, "Copy ->") then self.cur_project_markdown_style = D.deepCopy(self.new_project_markdown_style) - self.cur_project_sticker_size = self.new_project_sticker_size self.mdwidget_cur_proj:setStyle(self.cur_project_markdown_style) self.mdwidget_cur_proj:setText("") end @@ -328,20 +294,11 @@ function SettingsEditor:markdownTable() if self.preview_new_proj_markdown then ImGui.SameLine(ctx) ImGui.BeginGroup(ctx) - if ImGui.BeginChild(ctx, "##preview_left", PREVIEW_WIDTH, MD_EDITOR_HEIGHT) then + if ImGui.BeginChild(ctx, "##preview_left", PREVIEW_WIDTH, MD_EDITOR_HEIGHT, ImGui.ChildFlags_None, ImGui.WindowFlags_None) then self.mdwidget_new_proj:setText(markdown_example) self.mdwidget_new_proj:render(ctx) ImGui.EndChild(ctx) end - - ImGui.Dummy(ctx,0,0) - local x, y = ImGui.GetCursorScreenPos(ctx) - local sticker = Sticker:new("1:1:1F60D:Sticker Test", Notes:new(nil, false), 1) - local pr_res = sticker:PreRender(ctx, self.new_project_sticker_size) - sticker:Render(ctx, pr_res, x, y) - ImGui.SetCursorScreenPos(ctx, x, y + pr_res.height) - ImGui.Dummy(ctx, 0, 0) - ImGui.EndGroup(ctx) end @@ -355,7 +312,7 @@ function SettingsEditor:markdownTable() ImGui.SameLine(ctx) ImGui.BeginGroup(ctx) - ImGui.Selectable(ctx, "Stylesheet for current project", true, 0, WIDGET_WIDTH + (self.preview_cur_proj_markdown and PREVIEW_WIDTH + 4 or 0)) + ImGui.Selectable(ctx, "Current Project", true, 0, WIDGET_WIDTH + (self.preview_cur_proj_markdown and PREVIEW_WIDTH + 4 or 0)) if self.preview_cur_proj_markdown then ImGui.BeginGroup(ctx) @@ -364,15 +321,6 @@ function SettingsEditor:markdownTable() self.mdwidget_cur_proj:render(ctx) ImGui.EndChild(ctx) end - - ImGui.Dummy(ctx,0,0) - local x, y = ImGui.GetCursorScreenPos(ctx) - local sticker = Sticker:new("1:1:1F60D:Sticker Test", Notes:new(nil, false), 1) - local pr_res = sticker:PreRender(ctx, self.cur_project_sticker_size) - sticker:Render(ctx, pr_res, x, y) - ImGui.SetCursorScreenPos(ctx, x, y + pr_res.height) - ImGui.Dummy(ctx, 0, 0) - ImGui.EndGroup(ctx) ImGui.SameLine(ctx) end @@ -382,10 +330,6 @@ function SettingsEditor:markdownTable() if ImGui.BeginTable(ctx, "", 6, 0, WIDGET_WIDTH) then self:markdownTableHeaders() self:markdownStyles(self.mdwidget_cur_proj, self.cur_project_markdown_style) - - ImGui.TableNextRow(ctx) - self:stickersStyleRow(function() return self.cur_project_sticker_size end, function(v) self.cur_project_sticker_size = v end) - ImGui.EndTable(ctx) end @@ -393,7 +337,6 @@ function SettingsEditor:markdownTable() ImGui.Dummy(ctx,1,10) if ImGui.Button(ctx, "<- Copy") then self.new_project_markdown_style = D.deepCopy(self.cur_project_markdown_style) - self.new_project_sticker_size = self.cur_project_sticker_size self.mdwidget_new_proj:setStyle(self.new_project_markdown_style) self.mdwidget_new_proj:setText("") end @@ -406,11 +349,10 @@ function SettingsEditor:markdownTable() self.preview_cur_proj_markdown = not b end ImGui.SameLine(ctx) - local d = not ((D.deepCompare(self:retrieveCurProjectMarkdownStyle(), self.cur_project_markdown_style)) and (D.RetrieveProjectStickerSize() == self.cur_project_sticker_size)) + local d = not ((D.deepCompare(self:retrieveCurProjectMarkdownStyle(), self.cur_project_markdown_style))) if d then ImGui.PushStyleColor(ctx, ImGui.Col_Button, 0xFF0000FF) end if ImGui.Button(ctx, "Save and apply !") then - D.CommitProjectMarkdownStyle(self.cur_project_markdown_style) - D.CommitProjectStickerSize(self.cur_project_sticker_size) + PS.CommitProjectMarkdownStyle(self.cur_project_markdown_style) if self.on_commit_current_project_style_callback then self.on_commit_current_project_style_callback(D.deepCopy(self.cur_project_markdown_style)) end @@ -423,6 +365,143 @@ function SettingsEditor:markdownTable() ImGui.EndGroup(ctx) end +function SettingsEditor:stickersTable() + local app_ctx = AppContext:instance() + local ctx = app_ctx.imgui_ctx + + ImGui.PushID(ctx, "###new_proj_sticker_settings") + + ImGui.BeginGroup(ctx) + ImGui.Selectable(ctx, "New Project", true, 0, WIDGET_WIDTH + (self.preview_new_proj_markdown and PREVIEW_WIDTH + 4 or 0)) + + ImGui.AlignTextToFramePadding(ctx) + ImGui.Text(ctx,"Size") + ImGui.SameLine(ctx) + + local sticker_size = S.getSetting("NewProjectStickerSize") + + local b, v = ImGui.SliderInt(ctx, "##font_size", sticker_size, 8, 20) + if b then + S.setSetting("NewProjectStickerSize", v) + end + + if self.preview_new_proj_markdown then + ImGui.SameLine(ctx) + ImGui.BeginGroup(ctx) + ImGui.Dummy(ctx,0,0) + local x, y = ImGui.GetCursorScreenPos(ctx) + local sticker = Sticker:new("1:1:1F60D:Sticker Test", Notes:new(nil, false), 1) + local pr_res = sticker:PreRender(ctx, sticker_size) + sticker:Render(ctx, pr_res, x, y) + ImGui.Dummy(ctx, 0, 0) + ImGui.EndGroup(ctx) + end + + ImGui.EndGroup(ctx) + ImGui.PopID(ctx) + + ImGui.SameLine(ctx) + local cx, cy = ImGui.GetCursorScreenPos(ctx) + ImGui.DrawList_AddLine(ImGui.GetWindowDrawList(ctx), cx + 1, cy, cx + 1, cy + 20, 0x606060FF) + ImGui.Dummy(ctx,3,20) + ImGui.SameLine(ctx) + + ImGui.PushID(ctx, "###cur_proj_sticker_settings") + + ImGui.BeginGroup(ctx) + local xalign = ImGui.GetCursorPosX(ctx) + ImGui.Selectable(ctx, "Current Project", true, 0, WIDGET_WIDTH + (self.preview_cur_proj_markdown and PREVIEW_WIDTH + 4 or 0)) + + ImGui.AlignTextToFramePadding(ctx) + + local sticker_size = PS.RetrieveProjectStickerSize() + + if self.preview_cur_proj_markdown then + ImGui.BeginGroup(ctx) + ImGui.Dummy(ctx,0,0) + local x, y = ImGui.GetCursorScreenPos(ctx) + local sticker = Sticker:new("1:1:1F60D:Sticker Test", Notes:new(nil, false), 1) + local pr_res = sticker:PreRender(ctx, sticker_size) + sticker:Render(ctx, pr_res, x, y) + ImGui.Dummy(ctx, 0, 0) + ImGui.EndGroup(ctx) + ImGui.SameLine(ctx) + end + + ImGui.SetCursorPosX(ctx, xalign + (self.preview_cur_proj_markdown and PREVIEW_WIDTH + 4 or 0)) + ImGui.Text(ctx,"Size") + ImGui.SameLine(ctx) + local b, v = ImGui.SliderInt(ctx, "##font_size", sticker_size, 8, 20) + if b then + PS.CommitProjectStickerSize(v) + end + + ImGui.EndGroup(ctx) + ImGui.PopID(ctx) +end + +function SettingsEditor:postersTable() + local app_ctx = AppContext:instance() + local ctx = app_ctx.imgui_ctx + + ImGui.PushID(ctx, "###new_proj_posters_settings") + + ImGui.BeginGroup(ctx) + ImGui.Selectable(ctx, "New Project", true, 0, WIDGET_WIDTH + (self.preview_new_proj_markdown and PREVIEW_WIDTH + 4 or 0)) + + ImGui.AlignTextToFramePadding(ctx) + ImGui.Text(ctx,"Default type") + ImGui.SameLine(ctx) + + local cur_val = S.getSetting("NewProjectPosterDefaultType") + local poster_combo_info = D.PosterTypeComboInfo(nil) + if ImGui.BeginCombo(ctx, "##poster_type_combo", D.PosterTypeToName(cur_val, nil) , ImGui.ComboFlags_None | ImGui.ComboFlags_WidthFitPreview) then + for _, v in ipairs(poster_combo_info.list) do + local real_val = poster_combo_info.reverse_lookup[v] + if ImGui.Selectable(ctx, v, cur_val == real_val) then + S.setSetting("NewProjectPosterDefaultType", real_val) + end + end + ImGui.EndCombo(ctx) + end + + ImGui.EndGroup(ctx) + ImGui.PopID(ctx) + + ImGui.SameLine(ctx) + local cx, cy = ImGui.GetCursorScreenPos(ctx) + ImGui.DrawList_AddLine(ImGui.GetWindowDrawList(ctx), cx + 1, cy, cx + 1, cy + 20, 0x606060FF) + ImGui.Dummy(ctx,3,20) + ImGui.SameLine(ctx) + + ImGui.PushID(ctx, "###cur_proj_poster_settings") + + ImGui.BeginGroup(ctx) + local xalign = ImGui.GetCursorPosX(ctx) + ImGui.Selectable(ctx, "Current Project", true, 0, WIDGET_WIDTH + (self.preview_cur_proj_markdown and PREVIEW_WIDTH + 4 or 0)) + + ImGui.AlignTextToFramePadding(ctx) + + ImGui.SetCursorPosX(ctx, xalign + (self.preview_cur_proj_markdown and PREVIEW_WIDTH + 4 or 0)) + ImGui.Text(ctx,"Default type") + ImGui.SameLine(ctx) + + local cur_val = PS.ProjectPosterDefaultType() + local poster_combo_info = D.PosterTypeComboInfo(nil) + if ImGui.BeginCombo(ctx, "##poster_type_combo", D.PosterTypeToName(cur_val, nil) , ImGui.ComboFlags_None | ImGui.ComboFlags_WidthFitPreview) then + for _, v in ipairs(poster_combo_info.list) do + local real_val = poster_combo_info.reverse_lookup[v] + if ImGui.Selectable(ctx, v, cur_val == real_val) then + PS.SetProjectPosterDefaultType(real_val) + end + end + ImGui.EndCombo(ctx) + end + + ImGui.EndGroup(ctx) + ImGui.PopID(ctx) +end + function SettingsEditor:draw() local app_ctx = AppContext:instance() local ctx = app_ctx.imgui_ctx @@ -430,11 +509,11 @@ function SettingsEditor:draw() ImGui.PushFont(ctx, app_ctx.arial_font, S.getSetting("UIFontSize")) local b, open = ImGui.Begin(ctx, "Reannotate Settings##reannotate_settings_editor", true, - ImGui.WindowFlags_AlwaysAutoResize | - ImGui.WindowFlags_NoDocking | - ImGui.WindowFlags_NoResize | - ImGui.WindowFlags_TopMost | - ImGui.WindowFlags_NoCollapse) + ImGui.WindowFlags_AlwaysAutoResize | + ImGui.WindowFlags_NoDocking | + ImGui.WindowFlags_NoResize | + ImGui.WindowFlags_TopMost | + ImGui.WindowFlags_NoCollapse) -- Set initiial position if ImGui.IsWindowAppearing(ctx) and self.pos then @@ -468,8 +547,13 @@ function SettingsEditor:draw() self.tmp_values["UIFontSize"] = nil end - ImGui.SeparatorText(ctx, "Markdown") + ImGui.SeparatorText(ctx, "Posters") + self:postersTable() + ImGui.SeparatorText(ctx, "Stickers") + self:stickersTable() + + ImGui.SeparatorText(ctx, "Markdown") self:markdownTable() ImGui.EndTabItem(ctx) diff --git a/Various/talagan_Reannotate/widgets/sticker_editor.lua b/Various/talagan_Reannotate/widgets/sticker_editor.lua index 5d8fb2fb6..ac0db91cb 100644 --- a/Various/talagan_Reannotate/widgets/sticker_editor.lua +++ b/Various/talagan_Reannotate/widgets/sticker_editor.lua @@ -8,6 +8,7 @@ local AppContext = require "classes/app_context" local EmojImGui = require "emojimgui" local Sticker = require "classes/sticker" +local PS = require "modules/project_settings" local D = require "modules/defines" local StickerEditor = {} @@ -56,7 +57,7 @@ function StickerEditor:title() end function StickerEditor:stickerSize() - return D.RetrieveProjectStickerSize() + return PS.RetrieveProjectStickerSize() end function StickerEditor:draw(color) diff --git a/Various/talagan_Reannotate/widgets/sticker_picker.lua b/Various/talagan_Reannotate/widgets/sticker_picker.lua index b0dcc6a29..a56c7b312 100644 --- a/Various/talagan_Reannotate/widgets/sticker_picker.lua +++ b/Various/talagan_Reannotate/widgets/sticker_picker.lua @@ -8,6 +8,7 @@ local AppContext = require "classes/app_context" local Sticker = require "classes/sticker" local StickerEditor = require "widgets/sticker_editor" local D = require "modules/defines" +local PS = require "modules/project_settings" local StickerPicker = {} StickerPicker.__index = StickerPicker @@ -58,7 +59,7 @@ function StickerPicker:commit() end function StickerPicker:stickerSize() - return D.RetrieveProjectStickerSize() + return PS.RetrieveProjectStickerSize() end function StickerPicker:renderStickerZone(ctx, stickers, should_go_to_line) @@ -103,6 +104,9 @@ function StickerPicker:renderStickerZone(ctx, stickers, should_go_to_line) if sticker.text == 'checkboxes' then ImGui.Text(ctx, "Special sticker that shows the number of checked\nchecbkoxes and the total of checbkoxes in this note") end + if sticker.text == 'progressbar' then + ImGui.Text(ctx, "Special sticker that shows the proportion of checked\ncheckboxes in this note as a progressbar in %") + end if sticker.text == 'category' then ImGui.Text(ctx, "Special sticker that shows the note's category\n(will change if the categories are edited)") end @@ -189,7 +193,8 @@ function StickerPicker:draw() ImGui.SeparatorText(ctx, "Special Stickers") picked = picked or self:renderStickerZone(ctx, { Sticker:new("0:category", self.thing.notes, self.slot), - Sticker:new("0:checkboxes", self.thing.notes, self.slot) + Sticker:new("0:checkboxes", self.thing.notes, self.slot), + Sticker:new("0:progressbar", self.thing.notes, self.slot) }, true) ---@diagnostic disable-next-line: redundant-parameter