Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 194 additions & 1 deletion src/Classes/EditControl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,46 @@ local function newlineCount(str)
end
end

local function getColorCodeLength(str, index)
if str:sub(index, index) ~= "^" then
return 0
end
local nextChar = str:sub(index + 1, index + 1)
if nextChar == "x" and str:sub(index + 2, index + 7):match("^%x%x%x%x%x%x$") then
return 8
elseif nextChar:match("^%d$") then
return 2
end
return 0
end

local function buildVisibleLineMap(rawLine)
local visible = ""
local rawStarts = {}
local rawEnds = {}
local rawIndex = 1
while rawIndex <= #rawLine do
local colorCodeLength = getColorCodeLength(rawLine, rawIndex)
if colorCodeLength > 0 then
rawIndex = rawIndex + colorCodeLength
else
local rawEnd = utf8.next(rawLine, rawIndex, 1)
if not rawEnd or rawEnd <= rawIndex then
rawEnd = rawIndex + 1
end
local char = rawLine:sub(rawIndex, rawEnd - 1)
local visibleStart = #visible + 1
visible = visible .. char
for offset = 0, #char - 1 do
rawStarts[visibleStart + offset] = rawIndex
rawEnds[visibleStart + offset] = rawEnd
end
rawIndex = rawEnd
end
end
return visible, rawStarts, rawEnds
end

local EditClass = newClass("EditControl", "ControlHost", "Control", "UndoHandler", "TooltipHost", function(self, anchor, rect, init, prompt, filter, limit, changeFunc, lineHeight, allowZoom, clearable)
self.ControlHost()
self.Control(anchor, rect)
Expand All @@ -55,6 +95,14 @@ local EditClass = newClass("EditControl", "ControlHost", "Control", "UndoHandler
self.disableCol = "^9"
self.selCol = "^0"
self.selBGCol = "^xBBBBBB"
self.searchBGFillCol = { 0.03, 0.03, 0.04, 0.78 }
self.searchFocusFillCol = { 0.03, 0.03, 0.04, 0.88 }
self.searchBGCol = { 0.58, 0.60, 0.64, 0.98 }
self.searchFocusBGCol = { 0.96, 0.97, 0.99, 1.00 }
self.searchQuery = ""
self.searchMatches = {}
self.searchMatchesByLine = {}
self.searchFocusIndex = nil
self.blinkStart = GetTime()
self.allowZoom = allowZoom
local function buttonSize()
Expand Down Expand Up @@ -238,6 +286,137 @@ function EditClass:MoveCaretVertically(offset)
self.blinkStart = GetTime()
end

function EditClass:SetSearchQuery(query, centerFocused)
query = tostring(query or "")
local resetFocus = query ~= self.searchQuery
self.searchQuery = query
self:RefreshSearch(centerFocused, resetFocus)
end

function EditClass:AdvanceSearchMatch(direction)
local matchCount = #self.searchMatches
if matchCount == 0 then
return false
end
if direction and direction < 0 then
if not self.searchFocusIndex or self.searchFocusIndex <= 1 then
self.searchFocusIndex = matchCount
else
self.searchFocusIndex = self.searchFocusIndex - 1
end
else
if not self.searchFocusIndex or self.searchFocusIndex >= matchCount then
self.searchFocusIndex = 1
else
self.searchFocusIndex = self.searchFocusIndex + 1
end
end
self:CenterOnSearchMatch(self.searchFocusIndex)
return true
end

function EditClass:RefreshSearch(centerFocused, resetFocus)
local query = self.searchQuery or ""
local lowerQuery = query:lower()
local previousFocus = self.searchFocusIndex
self.searchMatches = {}
self.searchMatchesByLine = {}
self.searchFocusIndex = nil
if query == "" then
return
end

local lineIndex = 0
for s, line in (self.buf.."\n"):gmatch("()([^\n]*)\n") do
lineIndex = lineIndex + 1
local visibleLine, rawStarts, rawEnds = buildVisibleLineMap(line)
local searchLine = visibleLine:lower()
local searchStart = 1
while true do
local visibleStart, visibleEnd = searchLine:find(lowerQuery, searchStart, true)
if not visibleStart then
break
end
local rawStart = rawStarts[visibleStart]
local rawEnd = rawEnds[visibleEnd]
if rawStart and rawEnd then
local matchIndex = #self.searchMatches + 1
local match = {
index = matchIndex,
lineIndex = lineIndex,
line = line,
rawStart = rawStart,
rawEnd = rawEnd,
}
self.searchMatches[matchIndex] = match
self.searchMatchesByLine[lineIndex] = self.searchMatchesByLine[lineIndex] or {}
table.insert(self.searchMatchesByLine[lineIndex], match)
end
searchStart = visibleStart + 1
end
end

if #self.searchMatches > 0 then
if not resetFocus and previousFocus then
self.searchFocusIndex = m_min(previousFocus, #self.searchMatches)
else
self.searchFocusIndex = 1
end
if centerFocused then
self:CenterOnSearchMatch(self.searchFocusIndex)
end
end
end

function EditClass:CenterOnSearchMatch(matchIndex)
if not self.lineHeight then
return
end
local match = self.searchMatches[matchIndex]
if not match then
return
end
self:UpdateScrollBars()
if self.controls.scrollBarV.enabled then
local targetY = (match.lineIndex - 1) * self.lineHeight
self.controls.scrollBarV:SetOffset(targetY - (self.controls.scrollBarV.viewDim - self.lineHeight) / 2)
end
if self.controls.scrollBarH.enabled then
local matchStartX = DrawStringWidth(self.lineHeight, self.font, match.line:sub(1, match.rawStart - 1))
local matchWidth = DrawStringWidth(self.lineHeight, self.font, match.line:sub(match.rawStart, match.rawEnd - 1))
self.controls.scrollBarH:SetOffset(matchStartX + matchWidth / 2 - self.controls.scrollBarH.viewDim / 2)
end
end

function EditClass:DrawSearchHighlightsForLine(lineIndex, line, textX, textY, textHeight)
local matches = self.searchMatchesByLine[lineIndex]
if not matches then
return
end
for _, match in ipairs(matches) do
local matchStartX = DrawStringWidth(textHeight, self.font, line:sub(1, match.rawStart - 1))
local matchWidth = DrawStringWidth(textHeight, self.font, line:sub(match.rawStart, match.rawEnd - 1))
if matchWidth > 0 then
local isFocused = match.index == self.searchFocusIndex
local fillColor = isFocused and self.searchFocusFillCol or self.searchBGFillCol
local borderColor = isFocused and self.searchFocusBGCol or self.searchBGCol
local drawX = textX + matchStartX - 2
local drawWidth = matchWidth + 4
local borderX = drawX - 1
local borderY = textY - 1
local borderWidth = drawWidth + 2
local borderHeight = textHeight + 2
SetDrawColor(fillColor[1], fillColor[2], fillColor[3], fillColor[4])
DrawImage(nil, drawX, textY, drawWidth, textHeight)
SetDrawColor(borderColor[1], borderColor[2], borderColor[3], borderColor[4])
DrawImage(nil, borderX, borderY, borderWidth, 2)
DrawImage(nil, borderX, borderY + borderHeight - 2, borderWidth, 2)
DrawImage(nil, borderX, borderY, 2, borderHeight)
DrawImage(nil, borderX + borderWidth - 2, borderY, 2, borderHeight)
end
end
end

function EditClass:Draw(viewPort, noTooltip)
local x, y = self:GetPos()
local width, height = self:GetSize()
Expand Down Expand Up @@ -299,6 +478,16 @@ function EditClass:Draw(viewPort, noTooltip)
if self.inactiveText then
local inactiveText = type(inactiveText) == "string" and self.inactiveText or self.inactiveText(self.buf)
DrawString(-self.controls.scrollBarH.offset, -self.controls.scrollBarV.offset, "LEFT", textHeight, self.font, inactiveText)
elseif self.lineHeight and #self.searchMatches > 0 then
local lineIndex = 0
local drawY = -self.controls.scrollBarV.offset
for line in (self.buf.."\n"):gmatch("([^\n]*)\n") do
lineIndex = lineIndex + 1
self:DrawSearchHighlightsForLine(lineIndex, line, -self.controls.scrollBarH.offset, drawY, textHeight)
SetDrawColor(self.inactiveCol)
DrawString(-self.controls.scrollBarH.offset, drawY, "LEFT", textHeight, self.font, line)
drawY = drawY + textHeight
end
elseif self.protected then
DrawString(-self.controls.scrollBarH.offset, -self.controls.scrollBarV.offset, "LEFT", textHeight, self.font, string.rep(protected_replace, #self.buf))
else
Expand All @@ -324,9 +513,13 @@ function EditClass:Draw(viewPort, noTooltip)
local left = m_min(self.caret, self.sel or self.caret)
local right = m_max(self.caret, self.sel or self.caret)
local caretX
local lineIndex = 0
SetDrawColor(self.textCol)
for s, line, e in (self.buf.."\n"):gmatch("()([^\n]*)\n()") do
lineIndex = lineIndex + 1
textX = -self.controls.scrollBarH.offset
self:DrawSearchHighlightsForLine(lineIndex, line, textX, textY, textHeight)
SetDrawColor(self.textCol)
if left >= e or right <= s then
DrawString(textX, textY, "LEFT", textHeight, self.font, line)
end
Expand Down Expand Up @@ -507,7 +700,7 @@ function EditClass:OnKeyDown(key, doubleClick)
if self.enterFunc then
self.enterFunc(self.buf)
end
return
return self
end
elseif key == "a" and ctrl then
self:SelectAll()
Expand Down
91 changes: 89 additions & 2 deletions src/Classes/NotesTab.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
-- Notes tab for the current build.
--
local t_insert = table.insert
local m_floor = math.floor

local NotesTabClass = newClass("NotesTab", "ControlHost", "Control", function(self, build)
self.ControlHost()
Expand Down Expand Up @@ -31,14 +32,75 @@ 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)
self.controls.edit = new("EditControl", {"TOPLEFT",self.controls.fire,"TOPLEFT"}, {0, 48, 0, 0}, "", nil, "^%C\t\n", nil, function()
self.controls.edit:RefreshSearch()
end, 16, true)
self.controls.edit.width = function()
return self.width - 16
end
self.controls.edit.height = function()
return self.height - 128
end
self.controls.toggleColorCodes = new("ButtonControl", {"TOPRIGHT",self,"TOPRIGHT"}, {-10, 70, 160, 20}, "Show Color Codes", function()

self.controls.searchClear = new("ButtonControl", {"TOPRIGHT",self,"TOPRIGHT"}, {-10, 10, 20, 20}, "x", function()
self:ClearSearch()
end)
self.controls.searchClear.tooltipText = function()
return "Clear search"
end
self.controls.searchClear.enabled = function()
return self.controls.search.buf ~= ""
end
self.controls.searchNext = new("ButtonControl", {"RIGHT",self.controls.searchClear,"LEFT"}, {-4, 0, 24, 20}, "\\/", function()
self:AdvanceSearch(1)
end)
self.controls.searchNext.tooltipText = function()
return "Next match\n\nShortcut: Enter"
end
self.controls.searchNext.enabled = function()
return #self.controls.edit.searchMatches > 0
end
self.controls.searchPrev = new("ButtonControl", {"RIGHT",self.controls.searchNext,"LEFT"}, {-4, 0, 24, 20}, "/\\", function()
self:AdvanceSearch(-1)
end)
self.controls.searchPrev.tooltipText = function()
return "Previous match\n\nShortcut: Shift+Enter"
end
self.controls.searchPrev.enabled = function()
return #self.controls.edit.searchMatches > 0
end
self.controls.search = new("EditControl", {"RIGHT",self.controls.searchPrev,"LEFT"}, {-8, 0, 220, 20}, "", nil, "%c", 100, function(buf)
self.controls.edit:SetSearchQuery(buf, true)
end)
self.controls.search.width = function()
local baseWidth = math.max(140, math.min(320, self.width - 700))
return math.max(100, m_floor(baseWidth * 0.7))
end
self.controls.search.enterFunc = function()
self:AdvanceSearch(IsKeyDown("SHIFT") and -1 or 1)
end
self.controls.search:SetPlaceholder("Search")

self.controls.searchCount = new("LabelControl", {"RIGHT",self.controls.search,"LEFT"}, {-8, 0, 60, 16}, function()
if self.controls.search.buf == "" then
return ""
end
local matchCount = #self.controls.edit.searchMatches
if matchCount == 0 then
return "^10/0"
end
return ("^7%d/%d"):format(self.controls.edit.searchFocusIndex or 0, matchCount)
end)
self.controls.searchCount.x = function()
local reservedWidth = self.controls.searchCount:GetProperty("width")
local labelWidth = DrawStringWidth(self.controls.searchCount:GetProperty("height"), "VAR", self.controls.searchCount:GetProperty("label"))
return reservedWidth - 8 - labelWidth
end
self.controls.searchCount.width = function()
return DrawStringWidth(self.controls.searchCount:GetProperty("height"), "VAR", "^7999/9999") + 8
end

self.controls.toggleColorCodes = new("ButtonControl", {"TOPRIGHT",self,"TOPRIGHT"}, {-10, 38, 160, 20}, "Show Color Codes", function()
self.showColorCodes = not self.showColorCodes
self:SetShowColorCodes(self.showColorCodes)
end)
Expand All @@ -54,6 +116,7 @@ function NotesTabClass:SetShowColorCodes(setting)
self.controls.toggleColorCodes.label = "Show Color Codes"
self.controls.edit.buf = self.controls.edit.buf:gsub("%^_x(%x%x%x%x%x%x)","^x%1"):gsub("%^_(%d)","^%1")
end
self.controls.edit:RefreshSearch(#self.controls.edit.searchMatches > 0)
end

function NotesTabClass:SetColor(color)
Expand Down Expand Up @@ -82,6 +145,19 @@ function NotesTabClass:Save(xml)
self.lastContent = self.controls.edit.buf
end

function NotesTabClass:ClearSearch()
self.controls.search:SetText("", true)
self.controls.search:SelectAll()
self:SelectControl(self.controls.search)
return self.controls.search
end

function NotesTabClass:AdvanceSearch(direction)
self.controls.edit:AdvanceSearchMatch(direction)
self:SelectControl(self.controls.search)
return self.controls.search
end

function NotesTabClass:Draw(viewPort, inputEvents)
self.x = viewPort.x
self.y = viewPort.y
Expand All @@ -94,6 +170,17 @@ function NotesTabClass:Draw(viewPort, inputEvents)
self.controls.edit:Undo()
elseif event.key == "y" and IsKeyDown("CTRL") then
self.controls.edit:Redo()
elseif event.key == "f" and IsKeyDown("CTRL") then
self:SelectControl(self.controls.search)
self.controls.search:SelectAll()
inputEvents[id] = nil
elseif event.key == "ESCAPE" and self.controls.search.hasFocus then
if self.controls.search.buf ~= "" then
self:ClearSearch()
else
self:SelectControl(self.controls.edit)
end
inputEvents[id] = nil
end
end
end
Expand Down
Loading