From d08e7c8eedf8104180671d76d7a8a14de0bd3e5a Mon Sep 17 00:00:00 2001 From: Michel Tome Date: Mon, 9 Feb 2026 21:38:02 +0000 Subject: [PATCH 1/5] dev(8948): Added functionality for multiple notes. --- src/Classes/NotesListControl.lua | 106 ++++++++++++++++++++++++++ src/Classes/NotesTab.lua | 127 +++++++++++++++++++++++++++---- 2 files changed, 220 insertions(+), 13 deletions(-) create mode 100644 src/Classes/NotesListControl.lua diff --git a/src/Classes/NotesListControl.lua b/src/Classes/NotesListControl.lua new file mode 100644 index 00000000000..2b0dcdddfc1 --- /dev/null +++ b/src/Classes/NotesListControl.lua @@ -0,0 +1,106 @@ +-- Path of Building +-- +-- Class: Note List +-- Note list control. +-- +local t_insert = table.insert +local t_remove = table.remove +local m_max = math.max + +local NotesListClass = newClass("NotesListControl", "ListControl", function(self, anchor, rect, notesTab) + self.ListControl(anchor, rect, 16, "VERTICAL", true, notesTab.notesOrderList) + self.notesTab = notesTab + + self.label = "^7Notes:" + self.controls.delete = new("ButtonControl", {"BOTTOMRIGHT",self,"TOPRIGHT"}, {0, -2, 60, 18}, "Delete", function() + self:OnSelDelete(self.selIndex, self.selValue) + end) + + self.controls.delete.enabled = function() + return self.selValue ~= nil and #self.list > 1 + end + + self.controls.rename = new("ButtonControl", {"RIGHT",self.controls.delete,"LEFT"}, {-2, 0, 60, 18}, "Rename", function() + self:RenameNote(notesTab.notes[self.selValue]) + end) + self.controls.rename.enabled = function() + return self.selValue ~= nil + end + + self.controls.new = new("ButtonControl", {"RIGHT",self.controls.rename,"LEFT"}, {-2, 0, 60, 18}, "New", function() + self:RenameNote(notesTab:NewNote(), true) + end) +end) + +-- Triggered when the order of the list has changed +function NotesListClass:OnOrderChange(selIndex, selDragIndex) + self.notesTab.modFlag = true +end + +function NotesListClass:OnSelDelete(index, noteId) + local note = self.notesTab.notes[noteId] + if #self.list > 1 then + main:OpenConfirmPopup("Delete Note", "Are you sure you want to delete '"..(note.title or "Default").."'?", "Delete", function() + t_remove(self.list, index) + self.notesTab.notes[noteId] = nil + + self.selIndex = nil + self.selValue = nil + + if noteId == self.notesTab.activeNoteId then + self.notesTab:SetActiveNote(self.list[m_max(1, index - 1)]) + end + end) + end +end + +-- Get the value to display in the list row +function NotesListClass:GetRowValue(column, index, noteId) + local note = self.notesTab.notes[noteId] + if column == 1 then + return (note.title or "Default") .. (noteId == self.notesTab.activeNoteId and " ^9(Current)" or "") + end +end + +function NotesListClass:RenameNote(note, addOnName) + local controls = { } + controls.label = new("LabelControl", nil, {0, 20, 0, 16}, "^7Enter name for this note:") + controls.edit = new("EditControl", nil, {0, 40, 350, 20}, note.title, nil, nil, 100, function(buf) + controls.save.enabled = buf:match("%S") + end) + controls.save = new("ButtonControl", nil, {-45, 70, 80, 20}, "Save", function() + note.title = controls.edit.buf + self.notesTab.modFlag = true + + if addOnName then + t_insert(self.list, note.id) + self.selIndex = #self.list + self.selValue = note.id + end + + self.notesTab:SetActiveNote(note.id) + main:ClosePopup() + end) + controls.save.enabled = false + controls.cancel = new("ButtonControl", nil, {45, 70, 80, 20}, "Cancel", function() + if addOnName then + self.notesTab.notes[note.id] = nil + end + main:ClosePopup() + end) + main:OpenPopup(370, 100, note.title and "Rename" or "Note Name", controls, "save", "edit", "cancel") +end + +function NotesListClass:OnSelClick(index, noteId) + self.notesTab:SaveContentToNote(self.notesTab.activeNoteId) + + if noteId ~= self.notesTab.activeNoteId then + self.notesTab:SetActiveNote(noteId) + end +end + +function NotesListClass:OnSelClick(index, noteId, doubleClick) + if doubleClick and noteId ~= self.notesTab.activeNoteId then + self.notesTab:SetActiveNote(noteId) + end +end \ No newline at end of file diff --git a/src/Classes/NotesTab.lua b/src/Classes/NotesTab.lua index f78ea2eb41a..5b1d5163eaa 100644 --- a/src/Classes/NotesTab.lua +++ b/src/Classes/NotesTab.lua @@ -5,15 +5,19 @@ -- local t_insert = table.insert -local NotesTabClass = newClass("NotesTab", "ControlHost", "Control", function(self, build) +local NotesTabClass = newClass("NotesTab", "ControlHost", "Control", function(self, build) self.ControlHost() self.Control() self.build = build - self.lastContent = "" + self.notes = { } + self.notesOrderList = { } + self.showColorCodes = false + local listSize = 250 + local notesDesc = [[^7You can use Ctrl +/- (or Ctrl+Scroll) to zoom in and out and Ctrl+0 to reset. This field also supports different colors. Using the caret symbol (^) followed by a Hex code or a number (0-9) will set the color. Below are some common color codes PoB uses: ]] @@ -31,17 +35,23 @@ Below are some common color codes PoB uses: ]] self.controls.intelligence = new("ButtonControl", {"TOPLEFT",self.controls.dexterity,"TOPLEFT"}, {120, 0, 100, 18}, colorCodes.INTELLIGENCE.."INTELLIGENCE", function() self:SetColor(colorCodes.INTELLIGENCE) end) self.controls.default = new("ButtonControl", {"TOPLEFT",self.controls.intelligence,"TOPLEFT"}, {120, 0, 100, 18}, "^7DEFAULT", function() self:SetColor("^7") end) - self.controls.edit = new("EditControl", {"TOPLEFT",self.controls.fire,"TOPLEFT"}, {0, 48, 0, 0}, "", nil, "^%C\t\n", nil, nil, 16, true) + -- Notes group list + self.controls.noteList = new("NotesListControl", { "TOPLEFT", self.controls.strength, "TOPLEFT" }, { 0, 48, listSize - 16, 300 }, self) + + self.controls.edit = new("EditControl", {"TOPLEFT",self.controls.strength,"TOPLEFT"}, {listSize, 48, 0, 0}, "", nil, "^%C\t\n", nil, nil, 16, true) self.controls.edit.width = function() - return self.width - 16 + return self.width - listSize - 16 end self.controls.edit.height = function() - return self.height - 128 + return self.height - 148 end self.controls.toggleColorCodes = new("ButtonControl", {"TOPRIGHT",self,"TOPRIGHT"}, {-10, 70, 160, 20}, "Show Color Codes", function() self.showColorCodes = not self.showColorCodes self:SetShowColorCodes(self.showColorCodes) end) + + self:SetActiveNote(1) + self:SelectControl(self.controls.edit) end) @@ -68,18 +78,62 @@ function NotesTabClass:SetColor(color) end function NotesTabClass:Load(xml, fileName) - for _, node in ipairs(xml) do + self.activeNoteId = 0 + self.notes = { } + self.noteOrderList = { } + + for index, node in ipairs(xml) do + -- backwards compatibility if type(node) == "string" then - self.controls.edit:SetText(node) + self.notesOrderList[1] = 1 + self.notes[1] = { + id = 1, + content = node + } + + self:SetActiveNote(1) + else + local savedNoteId = tonumber(node.attrib.id) + self.notesOrderList[index] = savedNoteId + + self.notes[savedNoteId] = { + id = savedNoteId, + title = node.attrib.title, + content = node[1] or "" + } + + if node.attrib.active ~= nil then + self:SetActiveNote(savedNoteId) + end end end - self.lastContent = self.controls.edit.buf + + self.modFlag = false end function NotesTabClass:Save(xml) self:SetShowColorCodes(false) - t_insert(xml, self.controls.edit.buf) - self.lastContent = self.controls.edit.buf + + self.notes[self.activeNoteId].content = self.controls.edit.buf + + for _, noteId in ipairs(self.notesOrderList) do + local attrib = { + id = tostring(noteId), + title = self.notes[noteId].title, + } + + if self.activeNoteId == noteId then + attrib.active = "1" + end + + local note = { + elem = "Note", + attrib = attrib, + [1] = self.notes[noteId].content + } + + t_insert(xml, note) + end end function NotesTabClass:Draw(viewPort, inputEvents) @@ -91,17 +145,64 @@ function NotesTabClass:Draw(viewPort, inputEvents) for id, event in ipairs(inputEvents) do if event.type == "KeyDown" then if event.key == "z" and IsKeyDown("CTRL") then - self.controls.edit:Undo() + if self.controls.edit.hasFocus then + self.controls.edit:Undo() + end elseif event.key == "y" and IsKeyDown("CTRL") then - self.controls.edit:Redo() + if self.controls.edit.hasFocus then + self.controls.edit:Redo() + end end end end + self:ProcessControlsInput(inputEvents, viewPort) main:DrawBackground(viewPort) self:DrawControls(viewPort) - self.modFlag = (self.lastContent ~= self.controls.edit.buf) + self.modFlag = (self.notes[self.activeNoteId].content ~= self.controls.edit.buf) or self.modFlag end + + +-- Creates a new note +function NotesTabClass:NewNote(noteId) + local note = { id = noteId, content = "" } + + if not noteId then + note.id = 1 + while self.notes[note.id] do + note.id = note.id + 1 + end + end + + self.notes[note.id] = note + + return note +end + +-- Changes the active note +function NotesTabClass:SetActiveNote(noteId) + -- Initialize note if needed + if not self.notesOrderList[1] then + self.notesOrderList[1] = 1 + self:NewNote(1) + end + + if not noteId then + noteId = self.activeNoteId + end + + if not self.notes[noteId] then + noteId = self.notesOrderList[1] + end + + self.activeNoteId = noteId + self.notes[self.activeNoteId].lastContent = self.controls.edit.buf + self.controls.edit:SetText(self.notes[noteId].content) +end + +function NotesTabClass:SaveContentToNote(noteId) + self.notes[noteId].content = self.controls.edit.buf +end \ No newline at end of file From 7f1d92cdae6fabf86ebfbb911e79f88d03226114 Mon Sep 17 00:00:00 2001 From: Michel Tome Date: Mon, 9 Feb 2026 22:18:30 +0000 Subject: [PATCH 2/5] chore: Removed duplicated function declaration --- src/Classes/NotesListControl.lua | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Classes/NotesListControl.lua b/src/Classes/NotesListControl.lua index 2b0dcdddfc1..f86f9e2d97f 100644 --- a/src/Classes/NotesListControl.lua +++ b/src/Classes/NotesListControl.lua @@ -91,16 +91,11 @@ function NotesListClass:RenameNote(note, addOnName) main:OpenPopup(370, 100, note.title and "Rename" or "Note Name", controls, "save", "edit", "cancel") end -function NotesListClass:OnSelClick(index, noteId) +-- Triggered when a note is selected from the list +function NotesListClass:OnSelClick(index, noteId, doubleClick) self.notesTab:SaveContentToNote(self.notesTab.activeNoteId) - - if noteId ~= self.notesTab.activeNoteId then - self.notesTab:SetActiveNote(noteId) - end -end -function NotesListClass:OnSelClick(index, noteId, doubleClick) if doubleClick and noteId ~= self.notesTab.activeNoteId then self.notesTab:SetActiveNote(noteId) end -end \ No newline at end of file +end From e4f64863d60eef52832ed109ca99e2fbfda73b64 Mon Sep 17 00:00:00 2001 From: Michel Tome Date: Mon, 9 Feb 2026 22:22:26 +0000 Subject: [PATCH 3/5] chore: Cleaned up spacing. --- src/Classes/NotesTab.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Classes/NotesTab.lua b/src/Classes/NotesTab.lua index 5b1d5163eaa..c2860fac6b6 100644 --- a/src/Classes/NotesTab.lua +++ b/src/Classes/NotesTab.lua @@ -5,7 +5,7 @@ -- local t_insert = table.insert -local NotesTabClass = newClass("NotesTab", "ControlHost", "Control", function(self, build) +local NotesTabClass = newClass("NotesTab", "ControlHost", "Control", function(self, build) self.ControlHost() self.Control() @@ -165,7 +165,6 @@ function NotesTabClass:Draw(viewPort, inputEvents) self.modFlag = (self.notes[self.activeNoteId].content ~= self.controls.edit.buf) or self.modFlag end - -- Creates a new note function NotesTabClass:NewNote(noteId) local note = { id = noteId, content = "" } @@ -205,4 +204,4 @@ end function NotesTabClass:SaveContentToNote(noteId) self.notes[noteId].content = self.controls.edit.buf -end \ No newline at end of file +end From a78b9dbdfb4a208bdc39054c086fa65278598529 Mon Sep 17 00:00:00 2001 From: Michel Tome Date: Tue, 10 Feb 2026 09:32:00 +0000 Subject: [PATCH 4/5] dev(8948): Changed notes list to top left as requested in PR. --- src/Classes/NotesTab.lua | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Classes/NotesTab.lua b/src/Classes/NotesTab.lua index c2860fac6b6..ac436df4abd 100644 --- a/src/Classes/NotesTab.lua +++ b/src/Classes/NotesTab.lua @@ -21,7 +21,11 @@ local NotesTabClass = newClass("NotesTab", "ControlHost", "Control", function(se local notesDesc = [[^7You can use Ctrl +/- (or Ctrl+Scroll) to zoom in and out and Ctrl+0 to reset. This field also supports different colors. Using the caret symbol (^) followed by a Hex code or a number (0-9) will set the color. Below are some common color codes PoB uses: ]] - self.controls.notesDesc = new("LabelControl", {"TOPLEFT",self,"TOPLEFT"}, {8, 8, 150, 16}, notesDesc) + + -- Notes group list + self.controls.noteList = new("NotesListControl", { "TOPLEFT", self, "TOPLEFT" }, { 8, 30, listSize - 16, 80 }, self) + + self.controls.notesDesc = new("LabelControl", {"TOPLEFT",self.controls.noteList,"TOPRIGHT"}, {8, -22, 150, 16}, notesDesc) self.controls.normal = new("ButtonControl", {"TOPLEFT",self.controls.notesDesc,"TOPLEFT"}, {0, 48, 100, 18}, colorCodes.NORMAL.."NORMAL", function() self:SetColor(colorCodes.NORMAL) end) self.controls.magic = new("ButtonControl", {"TOPLEFT",self.controls.normal,"TOPLEFT"}, {120, 0, 100, 18}, colorCodes.MAGIC.."MAGIC", function() self:SetColor(colorCodes.MAGIC) end) self.controls.rare = new("ButtonControl", {"TOPLEFT",self.controls.magic,"TOPLEFT"}, {120, 0, 100, 18}, colorCodes.RARE.."RARE", function() self:SetColor(colorCodes.RARE) end) @@ -35,15 +39,12 @@ Below are some common color codes PoB uses: ]] self.controls.intelligence = new("ButtonControl", {"TOPLEFT",self.controls.dexterity,"TOPLEFT"}, {120, 0, 100, 18}, colorCodes.INTELLIGENCE.."INTELLIGENCE", function() self:SetColor(colorCodes.INTELLIGENCE) end) self.controls.default = new("ButtonControl", {"TOPLEFT",self.controls.intelligence,"TOPLEFT"}, {120, 0, 100, 18}, "^7DEFAULT", function() self:SetColor("^7") end) - -- Notes group list - self.controls.noteList = new("NotesListControl", { "TOPLEFT", self.controls.strength, "TOPLEFT" }, { 0, 48, listSize - 16, 300 }, self) - - self.controls.edit = new("EditControl", {"TOPLEFT",self.controls.strength,"TOPLEFT"}, {listSize, 48, 0, 0}, "", nil, "^%C\t\n", nil, nil, 16, true) + self.controls.edit = new("EditControl", {"TOPLEFT",self.controls.noteList,"BOTTOMLEFT"}, {0, 8, 0, 0}, "", nil, "^%C\t\n", nil, nil, 16, true) self.controls.edit.width = function() - return self.width - listSize - 16 + return self.width - 16 end self.controls.edit.height = function() - return self.height - 148 + return self.height - 128 end self.controls.toggleColorCodes = new("ButtonControl", {"TOPRIGHT",self,"TOPRIGHT"}, {-10, 70, 160, 20}, "Show Color Codes", function() self.showColorCodes = not self.showColorCodes From a1a4ff6abbd452ec0c1feddb9098ddb80e2065b6 Mon Sep 17 00:00:00 2001 From: Michel Tome Date: Thu, 21 May 2026 23:45:44 +0100 Subject: [PATCH 5/5] dev(8948): Link multiple notes feature to build loadouts. --- src/Modules/Build.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Modules/Build.lua b/src/Modules/Build.lua index b7376f5d6bb..b75c2a792c5 100644 --- a/src/Modules/Build.lua +++ b/src/Modules/Build.lua @@ -338,6 +338,10 @@ function buildMode:Init(dbFileName, buildName, buildXML, convertBuild, importLin t_insert(self.configTab.configSetOrderList, configSet.id) configSet.title = loadout + local note = self.notesTab:NewNote(#self.notesTab.notes + 1) + t_insert(self.notesTab.notesOrderList, note.id) + note.title = loadout + self:SyncLoadouts() self.modFlag = true main:ClosePopup() @@ -387,14 +391,16 @@ function buildMode:Init(dbFileName, buildName, buildXML, convertBuild, importLin local oneSkill = self.skillsTab and #self.skillsTab.skillSetOrderList == 1 local oneItem = self.itemsTab and #self.itemsTab.itemSetOrderList == 1 local oneConfig = self.configTab and #self.configTab.configSetOrderList == 1 + local oneNote = self.notesTab and #self.notesTab.notesOrderList == 1 local newSpecId = findNamedSetId(self.treeTab:GetSpecList(), value, self.treeListSpecialLinks) local newItemId = oneItem and 1 or findSetId(self.itemsTab.itemSetOrderList, value, self.itemsTab.itemSets, self.itemListSpecialLinks) local newSkillId = oneSkill and 1 or findSetId(self.skillsTab.skillSetOrderList, value, self.skillsTab.skillSets, self.skillListSpecialLinks) local newConfigId = oneConfig and 1 or findSetId(self.configTab.configSetOrderList, value, self.configTab.configSets, self.configListSpecialLinks) + local newNoteId = oneNote and 1 or findSetId(self.notesTab.notesOrderList, value, self.notesTab.notes, self.noteListSpecialLinks) -- if exact match nor special grouping cannot find setIds, bail - if newSpecId == nil or newItemId == nil or newSkillId == nil or newConfigId == nil then + if newSpecId == nil or newItemId == nil or newSkillId == nil or newConfigId == nil or newNoteId == nil then return end @@ -410,6 +416,9 @@ function buildMode:Init(dbFileName, buildName, buildXML, convertBuild, importLin if newConfigId ~= self.configTab.activeConfigSetId then self.configTab:SetActiveConfigSet(newConfigId) end + if newNoteId ~= self.notesTab.activeNoteId then + self.notesTab:SetActiveNote(newNoteId) + end self.controls.buildLoadouts:SelByValue(value) end)