Moved to use ZO_SortFilterList as the base for the addon, hopefully to improve performance.

Jayden Platell [04-24-14 - 07:31]
Moved to use ZO_SortFilterList as the base for the addon, hopefully to improve performance.
Filename
Librarian.lua
Librarian.xml
diff --git a/Librarian.lua b/Librarian.lua
index 06aa5c0..4266fd8 100644
--- a/Librarian.lua
+++ b/Librarian.lua
@@ -1,4 +1,4 @@
-Librarian = ZO_Object:Subclass()
+Librarian = ZO_SortFilterList:Subclass()
 Librarian.defaults = {}

 ZO_CreateStringId("SI_BINDING_NAME_TOGGLE_LIBRARIAN", "Toggle Librarian")
@@ -17,15 +17,29 @@ ZO_CreateStringId("SI_LIBRARIAN_NEW_BOOK_FOUND", "Book added to librarian")

 local SORT_ARROW_UP = "EsoUI/Art/Miscellaneous/list_sortUp.dds"
 local SORT_ARROW_DOWN = "EsoUI/Art/Miscellaneous/list_sortDown.dds"
-
-local previousBook
-local scrollChild
-local sortField = "Found"
-local sortAscending = false
+local LIBRARIAN_DATA = 1
+
+local ENTRY_SORT_KEYS =
+{
+    ["title"] = { },
+    ["unread"] = { tiebreaker = "timeStamp" },
+    ["timeStamp"] = { tiebreaker = "title" },
+    ["wordCount"] = { tiebreaker = "title" }
+}
+
+function Librarian:New()
+	local librarian = ZO_SortFilterList.New(self, LibrarianFrame)
+	librarian:Initialise()
+	return librarian
+end

 function Librarian:Initialise()
- 	scrollChild = LibrarianFrameScrollContainer:GetNamedChild("ScrollChild")
- 	scrollChild:SetAnchor(TOPRIGHT, nil, TOPRIGHT, -5, 0)
+ 	self.masterList = {}
+    self.sortHeaderGroup:SelectHeaderByKey("timeStamp")
+
+ 	ZO_ScrollList_AddDataType(self.list, LIBRARIAN_DATA, "LibrarianBookRow", 30, function(control, data) self:SetupBookRow(control, data) end)
+ 	ZO_ScrollList_EnableHighlight(self.list, "ZO_ThinListHighlight")
+
 	self.localSavedVars = ZO_SavedVars:New("Librarian_SavedVariables", 1, nil, self.defaults, nil)
 	self.globalSavedVars = ZO_SavedVars:NewAccountWide("Librarian_SavedVariables", 1, nil, self.defaults, nil)

@@ -38,13 +52,15 @@ function Librarian:Initialise()
 	if not self.localSavedVars.characterBooks then self.localSavedVars.characterBooks = {} end
 	self.characterBooks = self.localSavedVars.characterBooks

+	self.sortFunction = function(listEntry1, listEntry2) return ZO_TableOrderingFunction(listEntry1.data, listEntry2.data, self.currentSortKey, ENTRY_SORT_KEYS, self.currentSortOrder) end
+
 	self:UpdateSavedVariables()

 	local settings = LibrarianSettings:New(self.settings)

 	local function OnShowAllBooksClicked(checkButton, isChecked)
         self.settings.showAllBooks = isChecked
-        self:LayoutBooks()
+        self:RefreshFilters()
     end

     local function GetShowAllBooks()
@@ -55,12 +71,73 @@ function Librarian:Initialise()
 	ZO_CheckButton_SetToggleFunction(showAllBooks, OnShowAllBooksClicked)
     ZO_CheckButton_SetCheckState(showAllBooks, GetShowAllBooks())

-	self:SortBooks()
+	self:RefreshData()

 	self:InitializeKeybindStripDescriptors()
 	self:InitializeScene()
 end

+function Librarian:SetupBookRow(control, data)
+	control.data = data
+	control.unread = GetControl(control, "Unread")
+	control.found = GetControl(control, "Found")
+	control.title = GetControl(control, "Title")
+	control.wordCount = GetControl(control, "WordCount")
+
+	control.unread.nonRecolorable = true
+	if data.unread then control.unread:SetAlpha(1) else control.unread:SetAlpha(0) end
+
+	control.found.normalColor = ZO_NORMAL_TEXT
+	control.found:SetText(self:FormatClockTime(data.timeStamp))
+
+	control.title.normalColor = ZO_NORMAL_TEXT
+	control.title:SetText(data.title)
+
+	control.wordCount.normalColor = ZO_NORMAL_TEXT
+	control.wordCount:SetText(data.wordCount)
+
+	ZO_SortFilterList.SetupRow(self, control, data)
+end
+
+function Librarian:BuildMasterList()
+    for i, book in ipairs(self.books) do
+		local bookCopy = {}
+		for k,v in pairs(book) do
+    		bookCopy[k] = v
+  		end
+  		local characterBook = self:FindCharacterBook(book.title)
+  		if characterBook then
+			bookCopy.seenByCurrentCharacter = true
+			bookCopy.timeStamp = characterBook.timeStamp
+		else
+			bookCopy.seenByCurrentCharacter = false
+			bookCopy.timeStamp = book.timeStamp
+		end
+  		self.masterList[i] = bookCopy
+    end
+end
+
+function Librarian:FilterScrollList()
+    local scrollData = ZO_ScrollList_GetDataList(self.list)
+    ZO_ClearNumericallyIndexedTable(scrollData)
+
+    local bookCount = 0
+    for i = 1, #self.masterList do
+        local data = self.masterList[i]
+        if self.settings.showAllBooks or data.seenByCurrentCharacter then
+            table.insert(scrollData, ZO_ScrollList_CreateDataEntry(LIBRARIAN_DATA, data))
+            bookCount = bookCount + 1
+        end
+    end
+
+	LibrarianFrameBookCount:SetText(string.format(GetString(SI_LIBRARIAN_BOOK_COUNT), bookCount))
+end
+
+function Librarian:SortScrollList()
+    local scrollData = ZO_ScrollList_GetDataList(self.list)
+    table.sort(scrollData, self.sortFunction)
+end
+
 function Librarian:UpdateSavedVariables()
 	-- Version 1.0.4 - Settings moved to global variables.
 	if self.localSavedVars.setting_time_format then
@@ -94,16 +171,18 @@ function Librarian:InitializeKeybindStripDescriptors()
             alignment = KEYBIND_STRIP_ALIGN_RIGHT,
             name = GetString(SI_LORE_LIBRARY_READ),
             keybind = "UI_SHORTCUT_PRIMARY",
+            visible = function()
+            	return self.mouseOverRow
+            end,
             callback = function()
-                self:ReadBook(self.mouseOverRow.id)
+                self:ReadBook(self.mouseOverRow.data.title)
             end,
         },
         {
             alignment = KEYBIND_STRIP_ALIGN_RIGHT,
             name = function()
             	if not self.mouseOverRow then return nil end
-            	local sortedBook = self.sortedBooks[self.mouseOverRow.id]
-            	local book = self:FindBook(sortedBook.title)
+            	local book = self:FindBook(self.mouseOverRow.data.title)
             	if book.unread then
             		return GetString(SI_LIBRARIAN_MARK_READ)
             	else
@@ -115,8 +194,7 @@ function Librarian:InitializeKeybindStripDescriptors()
             	return self.mouseOverRow
             end,
             callback = function()
-            	local sortedBook = self.sortedBooks[self.mouseOverRow.id]
-            	local book = self:FindBook(sortedBook.title)
+            	local book = self:FindBook(self.mouseOverRow.data.title)
                 book.unread = not book.unread
                 if book.unread then self.mouseOverRow.unread:SetAlpha(1) else self.mouseOverRow.unread:SetAlpha(0) end
                 KEYBIND_STRIP:RemoveKeybindButtonGroup(self.keybindStripDescriptor)
@@ -138,50 +216,59 @@ function Librarian:InitializeScene()
 		LIBRARIAN_SCENE:AddFragment(LIBRARIAN_TITLE_FRAGMENT)
 		LIBRARIAN_SCENE:AddFragment(EXPERIENCE_BAR_FRAGMENT)
 		LIBRARIAN_SCENE:AddFragment(CODEX_WINDOW_SOUNDS)
-	end
-end

-function Librarian:OpenBook(book)
-	if not self:FindCharacterBook(book.title) then
-		self:AddBook(book)
+		LIBRARIAN_SCENE:RegisterCallback("StateChange",
+			function(oldState, newState)
+				if(newState == SCENE_SHOWING) then
+                    KEYBIND_STRIP:AddKeybindButtonGroup(self.keybindStripDescriptor)
+                elseif(newState == SCENE_HIDDEN) then
+                    KEYBIND_STRIP:RemoveKeybindButtonGroup(self.keybindStripDescriptor)
+                end
+            end)
 	end
 end

 function Librarian:FindCharacterBook(title)
-	for _,book in ipairs(self.characterBooks) do
+	if not self.characterBooks then return nil end
+	for _,book in pairs(self.characterBooks) do
 		if book.title == title then return book end
 	end
 end

 function Librarian:FindBook(title)
-	for _,book in ipairs(self.books) do
+	for _,book in pairs(self.books) do
 		if book.title == title then return book end
 	end
 end

 function Librarian:AddBook(book)
-	local characterBook = {title = book.title, timeStamp = GetTimeStamp()}
-	table.insert(self.characterBooks, characterBook)
+	if not self:FindCharacterBook(book.title) then
+		local characterBook = {title = book.title, timeStamp = GetTimeStamp()}
+		table.insert(self.characterBooks, characterBook)

-	local function IsBookInGlobalData(book)
-		for _,i in ipairs(self.books) do
-			if i.title == book.title then return true end
+		local function IsBookInGlobalData(book)
+			for _,i in ipairs(self.books) do
+				if i.title == book.title then return true end
+			end
+			return false
 		end
-		return false
-	end

-	if not IsBookInGlobalData(book) then
-		book.timeStamp = GetTimeStamp()
-		book.unread = true
-		table.insert(self.books, book)
-	end
-
-	self:SortBooks()
-	if self.settings.alertEnabled then
-		ZO_CenterScreenAnnounce_GetAnnounceObject():AddMessage(EVENT_SKILL_RANK_UPDATE, CSA_EVENT_LARGE_TEXT, SOUNDS.BOOK_ACQUIRED, GetString(SI_LIBRARIAN_NEW_BOOK_FOUND))
-	end
-	if self.settings.chatEnabled then
-		d(GetString(SI_LIBRARIAN_NEW_BOOK_FOUND))
+		if not IsBookInGlobalData(book) then
+			book.timeStamp = GetTimeStamp()
+			book.unread = true
+			local wordCount = 0
+			for w in book.body:gmatch("%S+") do wordCount = wordCount + 1 end
+			book.wordCount = wordCount
+			table.insert(self.books, book)
+		end
+
+		self:RefreshData()
+		if self.settings.alertEnabled then
+			ZO_CenterScreenAnnounce_GetAnnounceObject():AddMessage(EVENT_SKILL_RANK_UPDATE, CSA_EVENT_LARGE_TEXT, SOUNDS.BOOK_ACQUIRED, GetString(SI_LIBRARIAN_NEW_BOOK_FOUND))
+		end
+		if self.settings.chatEnabled then
+			d(GetString(SI_LIBRARIAN_NEW_BOOK_FOUND))
+		end
 	end
 end

@@ -193,176 +280,14 @@ function Librarian:Toggle()
 	end
 end

-function Librarian:LayoutBooks()
-    ZO_Scroll_ResetToTop(LibrarianFrameScrollContainer)
-    previousBook = nil
-
-    for i, book in ipairs(self.sortedBooks) do
-		self:LayoutBook(i, book)
-    end
-
-    local bookCount = 0
-    if self.settings.showAllBooks then
-    	bookCount = table.getn(self.books)
-    else
-    	for _,book in pairs(self.sortedBooks) do
-    		if book.seenByCurrentCharacter then bookCount = bookCount + 1 end
-    	end
-    end
-    LibrarianFrameBookCount:SetText(string.format(GetString(SI_LIBRARIAN_BOOK_COUNT), bookCount))
-end
-
-function Librarian:LayoutBook(i, book)
-	local bookControl = GetControl("LibrarianBook"..i)
-	if not bookControl then
-		bookControl = CreateControlFromVirtual("LibrarianBook", scrollChild, "LibrarianBook", i)
-		bookControl.id = i
-		bookControl.unread = bookControl:GetNamedChild("Unread")
-		bookControl.found = bookControl:GetNamedChild("Found")
-		bookControl.title = bookControl:GetNamedChild("Title")
-		bookControl.wordCount = bookControl:GetNamedChild("WordCount")
-	end
-
-	if self.settings.showAllBooks or book.seenByCurrentCharacter then
-		bookControl:SetHidden(false)
-		if book.unread then bookControl.unread:SetAlpha(1) else bookControl.unread:SetAlpha(0) end
-		bookControl.found:SetText(self:FormatClockTime(book.timeStamp))
-		bookControl.title:SetText(book.title)
-		bookControl.wordCount:SetText(book.wordCount)
-
-		if not previousBook then
-	    	bookControl:SetAnchor(TOPLEFT, scrollChild, TOPLEFT)
-	    else
-	    	bookControl:SetAnchor(TOPLEFT, previousBook, BOTTOMLEFT)
-	    end
-	    previousBook = bookControl
-	else
-		bookControl:SetHidden(true)
-	end
-end
-
-function Librarian:InitialiseSortHeader(control, name, tag)
-	control.tag = tag
-	local nameControl = GetControl(control, "Name")
-    nameControl:SetFont("ZoFontHeader")
-    nameControl:SetColor(ZO_NORMAL_TEXT:UnpackRGBA())
-    nameControl:SetText(GetString(name))
-    nameControl:SetHorizontalAlignment(alignment or TEXT_ALIGN_LEFT)
-    control.initialDirection = initialDirection or ZO_SORT_ORDER_DOWN
-    control.usesArrow = true
-end
-
-function Librarian:SortBy(control)
-	local field = control.tag
-	if field == sortField then
-		sortAscending = not sortAscending
-	else
-		sortField = field
-		sortAscending = true
-	end
-
-	self:SortBooks()
-end
-
-function Librarian:SortBooks()
-	local control
-
-	self.sortedBooks = {}
-	for _,book in pairs(self.books) do
-		local characterBook = self:FindCharacterBook(book.title)
-		if not book.wordCount then
-			local wordCount = 0
-			for w in book.body:gmatch("%S+") do wordCount = wordCount + 1 end
-			book.wordCount = wordCount
-		end
-		local sortedBook = {title = book.title, unread = book.unread, wordCount = book.wordCount}
-		if characterBook then
-			sortedBook.seenByCurrentCharacter = true
-			sortedBook.timeStamp = characterBook.timeStamp
-		else
-			sortedBook.seenByCurrentCharacter = false
-			sortedBook.timeStamp = book.timeStamp
-		end
-		table.insert(self.sortedBooks, sortedBook)
-	end
-
-	if sortField == "Unread" then
-		control = LibrarianFrameSortByUnread
-		if sortAscending then
-			table.sort(self.sortedBooks, function(a, b) return a.unread and not b.unread end)
-		else
-			table.sort(self.sortedBooks, function(a, b) return not a.unread and b.unread end)
-		end
-	elseif sortField == "Found" then
-		control = LibrarianFrameSortByTime
-		if sortAscending then
-			table.sort(self.sortedBooks, function(a, b) return a.timeStamp < b.timeStamp end)
-		else
-			table.sort(self.sortedBooks, function(a, b) return a.timeStamp > b.timeStamp end)
-		end
-	elseif sortField == "Title" then
-		control = LibrarianFrameSortByTitle
-		if sortAscending then
-			table.sort(self.sortedBooks, function(a, b) return a.title < b.title end)
-		else
-			table.sort(self.sortedBooks, function(a, b) return a.title > b.title end)
-		end
-	elseif sortField == "WordCount" then
-		control = LibrarianFrameSortByWordCount
-		if sortAscending then
-			table.sort(self.sortedBooks, function(a, b) return a.wordCount < b.wordCount end)
-		else
-			table.sort(self.sortedBooks, function(a, b) return a.wordCount > b.wordCount end)
-		end
-	end
-
-	LibrarianFrameSortByUnread:GetNamedChild("Arrow"):SetHidden(true)
-	LibrarianFrameSortByTime:GetNamedChild("Arrow"):SetHidden(true)
-	LibrarianFrameSortByTitle:GetNamedChild("Arrow"):SetHidden(true)
-
-	local arrow = control:GetNamedChild("Arrow")
-	if sortAscending then
-		arrow:SetTexture(SORT_ARROW_DOWN)
-	else
-		arrow:SetTexture(SORT_ARROW_UP)
-	end
-	arrow:SetHidden(false)
-
-	self:LayoutBooks()
-end
-
-function Librarian:ReadBook(id)
-	local sortedBook = self.sortedBooks[id]
-	local book = self:FindBook(sortedBook.title)
+function Librarian:ReadBook(title)
+	local book = self:FindBook(title)
 	LORE_READER:SetupBook(book.title, book.body, book.medium, book.showTitle)
 	LORE_READER.returnScene = "librarian"
     SCENE_MANAGER:Show("loreReaderInteraction")
     PlaySound(LORE_READER.OpenSound)
 end

-function Librarian:OnMouseEnter(buttonPart)
-	self.mouseOverRow = buttonPart
-	local highlight = buttonPart:GetNamedChild("Highlight")
-	if highlight then
-		if not highlight.animation then
-	        highlight.animation = ANIMATION_MANAGER:CreateTimelineFromVirtual("ShowOnMouseOverLabelAnimation", highlight)
-	    end
-    	highlight.animation:PlayForward()
-    end
-
-    KEYBIND_STRIP:AddKeybindButtonGroup(self.keybindStripDescriptor)
-end
-
-function Librarian:OnMouseExit(buttonPart)
-	self.mouseOverRow = nil
-	local highlight = buttonPart:GetNamedChild("Highlight")
-	if highlight and highlight.animation then
-        highlight.animation:PlayBackward()
-    end
-
-    KEYBIND_STRIP:RemoveKeybindButtonGroup(self.keybindStripDescriptor)
-end
-
 function Librarian:FormatClockTime(time)
     local midnightSeconds = GetSecondsSinceMidnight()
     local utcSeconds = GetTimeStamp() % 86400
@@ -382,13 +307,25 @@ end

 local function OnAddonLoaded(event, addon)
 	if addon == "Librarian" then
-		Librarian:Initialise()
+		LIBRARIAN = Librarian:New()
 	end
 end

 local function OnShowBook(eventCode, title, body, medium, showTitle)
 	local book = {title = title, body = body, medium = medium, showTitle = showTitle}
-	Librarian:OpenBook(book)
+	LIBRARIAN:AddBook(book)
+end
+
+function LibrarianRow_OnMouseEnter(control)
+    LIBRARIAN:Row_OnMouseEnter(control)
+end
+
+function LibrarianRow_OnMouseExit(control)
+    LIBRARIAN:Row_OnMouseExit(control)
+end
+
+function LibrarianRow_OnMouseUp(control, button, upInside)
+    LIBRARIAN:ReadBook(control.data.title)
 end

 SLASH_COMMANDS["/librarian"] = SlashCommand
diff --git a/Librarian.xml b/Librarian.xml
index 5128101..4061cc9 100644
--- a/Librarian.xml
+++ b/Librarian.xml
@@ -1,22 +1,18 @@
 <GuiXml>
     <Controls>
-        <Button name="LibrarianBook" virtual="true">
+        <Button name="LibrarianBookRow" virtual="true">
             <Dimensions y="30" />
             <Anchor point="RIGHT" relativeTo="$(parent)" />
-            <OnMouseUp>
-                Librarian:ReadBook(self.id)
-            </OnMouseUp>
             <OnMouseEnter>
-                Librarian:OnMouseEnter(self)
+                LibrarianRow_OnMouseEnter(self)
             </OnMouseEnter>
             <OnMouseExit>
-                Librarian:OnMouseExit(self)
+                LibrarianRow_OnMouseExit(self)
             </OnMouseExit>
+            <OnMouseUp>
+                LibrarianRow_OnMouseUp(self, button, upInside)
+            </OnMouseUp>
             <Controls>
-                <Texture name="$(parent)Highlight" textureFile="EsoUI/Art/Miscellaneous/listItem_highlight.dds" alpha="0">
-                    <AnchorFill />
-                    <TextureCoords left="0" right="1" top="0" bottom=".625" />
-                </Texture>
                 <Texture name="$(parent)Unread" textureFile="EsoUI/Art/Inventory/newitem_icon.dds">
                     <Dimensions x="32" y="32" />
                     <Anchor point="TOPLEFT" relativeTo="$(parent)" offsetX="19" />
@@ -32,67 +28,49 @@
                 </Label>
             </Controls>
         </Button>
-        <Button name="LibrarianSortHeader" virtual="true">
-            <OnMouseUp>
-                Librarian:SortBy(self)
-            </OnMouseUp>
-            <OnMouseEnter>
-                self:GetNamedChild("Name"):SetColor(ZO_HIGHLIGHT_TEXT:UnpackRGBA())
-            </OnMouseEnter>
-            <OnMouseExit>
-                self:GetNamedChild("Name"):SetColor(ZO_NORMAL_TEXT:UnpackRGBA())
-            </OnMouseExit>
-            <Controls>
-                <Label name="$(parent)Name" font="ZoFontGame" color="INTERFACE_TEXT_COLOR_NORMAL" modifyTextType="UPPERCASE" horizontalAlignment="CENTER" verticalAlignment="CENTER" wrapMode="ELLIPSIS">
-                    <Anchor point="TOPLEFT" />
-                </Label>
-                <Texture name="$(parent)Arrow" hidden="true" layer="TEXT">
-                    <Dimensions x="16" y="16"/>
-                    <Anchor point="TOPLEFT" relativeTo="$(parent)Name" relativePoint="TOPRIGHT" offsetY="2" />
-                </Texture>
-            </Controls>
-        </Button>
         <TopLevelControl name="LibrarianFrame" inherits="ZO_RightPanelFootPrint"  hidden="true">
             <Controls>
                 <Label name="$(parent)BookCount" font="ZoFontWindowTitle" color="INTERFACE_COLOR_TYPE_TEXT_COLORS:INTERFACE_TEXT_COLOR_SELECTED" modifyTextType="UPPERCASE">
                     <Anchor point="TOPRIGHT" offsetX="-15" offsetY="-40" />
                 </Label>
-                <Control name="$(parent)SortBy">
-                    <Anchor point="TOPLEFT" relativeTo="$(parent)" offsetY="10" />
-                    <Anchor point="BOTTOMRIGHT" relativeTo="$(parent)" relativePoint="TOPRIGHT" offsetY="0" />
+                <Control name="$(parent)Headers">
+                    <Anchor point="TOPLEFT" relativeTo="$(parent)" offsetY="5" />
+                    <Anchor point="TOPRIGHT" relativeTo="$(parent)" offsetY="5" />
+                    <Dimensions y="32" />
                     <Controls>
-                        <Button name="$(parent)Unread" inherits="LibrarianSortHeader">
-                            <Dimensions x="90" y="20" />
-                            <Anchor point="TOPLEFT" />
+                        <Control name="$(parent)Unread" inherits="ZO_SortHeader">
                             <OnInitialized>
-                                Librarian:InitialiseSortHeader(self, SI_LIBRARIAN_SORT_TYPE_UNREAD, "Unread")
+                                ZO_SortHeader_Initialize(self, GetString(SI_LIBRARIAN_SORT_TYPE_UNREAD), "unread", ZO_SORT_ORDER_DOWN, TEXT_ALIGN_LEFT, "ZoFontGameLargeBold")
                             </OnInitialized>
-                        </Button>
-                        <Button name="$(parent)Time" inherits="LibrarianSortHeader">
-                            <Dimensions x="200" y="20" />
-                            <Anchor point="TOPLEFT" offsetX="90" />
+                            <Anchor point="TOPLEFT" />
+                            <Dimensions x="90" y="32" />
+                        </Control>
+                        <Control name="$(parent)Found" inherits="ZO_SortHeader">
                             <OnInitialized>
-                                Librarian:InitialiseSortHeader(self, SI_LIBRARIAN_SORT_TYPE_FOUND, "Found")
+                                ZO_SortHeader_Initialize(self, GetString(SI_LIBRARIAN_SORT_TYPE_FOUND), "timeStamp", ZO_SORT_ORDER_DOWN, TEXT_ALIGN_LEFT, "ZoFontGameLargeBold")
                             </OnInitialized>
-                        </Button>
-                        <Button name="$(parent)Title" inherits="LibrarianSortHeader">
-                            <Anchor point="TOPLEFT" relativeTo="$(parent)Time" relativePoint="TOPRIGHT" />
-                            <Anchor point="BOTTOMRIGHT" relativeTo="$(parent)" relativePoint="TOPRIGHT" offsetY="20" />
+                            <Anchor point="TOPLEFT" relativeTo="$(parent)Unread" relativePoint="TOPRIGHT" />
+                            <Dimensions x="200" y="32" />
+                        </Control>
+                        <Control name="$(parent)WordCount" inherits="ZO_SortHeader">
                             <OnInitialized>
-                                Librarian:InitialiseSortHeader(self, SI_LIBRARIAN_SORT_TYPE_TITLE, "Title")
+                                ZO_SortHeader_Initialize(self, GetString(SI_LIBRARIAN_SORT_TYPE_WORD_COUNT), "wordCount", ZO_SORT_ORDER_UP, TEXT_ALIGN_LEFT, "ZoFontGameLargeBold")
                             </OnInitialized>
-                        </Button>
-                        <Button name="$(parent)WordCount" inherits="LibrarianSortHeader">
-                            <Dimensions x="80" y="20" />
-                            <Anchor point="TOPRIGHT" relativeTo="$(parent)" relativePoint="TOPRIGHT" offsetX="-10" />
+                            <Anchor point="TOPRIGHT" />
+                            <Dimensions x="80" y="32" />
+                        </Control>
+                        <Control name="$(parent)Title" inherits="ZO_SortHeader">
                             <OnInitialized>
-                                Librarian:InitialiseSortHeader(self, SI_LIBRARIAN_SORT_TYPE_WORD_COUNT, "WordCount")
+                                ZO_SortHeader_Initialize(self, GetString(SI_LIBRARIAN_SORT_TYPE_TITLE), "title", ZO_SORT_ORDER_UP, TEXT_ALIGN_LEFT, "ZoFontGameLargeBold")
                             </OnInitialized>
-                        </Button>
+                            <Anchor point="TOPLEFT" relativeTo="$(parent)Found" relativePoint="TOPRIGHT" />
+                            <Anchor point="TOPRIGHT" relativeTo="$(parent)WordCount" relativePoint="TOPLEFT" />
+                            <Dimensions y="32" />
+                        </Control>
                     </Controls>
                 </Control>
-                <Control name="$(parent)ScrollContainer" inherits="ZO_ScrollContainer">
-                    <Anchor point="TOPLEFT" relativeTo="$(parent)SortBy" relativePoint="BOTTOMLEFT" offsetY="30" />
+                <Control name="$(parent)List" inherits="ZO_ScrollList">
+                    <Anchor point="TOPLEFT" relativeTo="$(parent)Headers" relativePoint="BOTTOMLEFT" />
                     <Anchor point="BOTTOMRIGHT" offsetX="-12" offsetY="0" />
                 </Control>
                 <Button name="$(parent)ShowAllBooks" inherits="ZO_CheckButton">