Added Market column sorting (fixes #429), updated UITable and fixed not working methods

This commit is contained in:
TheSumm 2015-01-21 23:40:15 +01:00
parent 6edc73a8ba
commit 607dab01d6
6 changed files with 395 additions and 138 deletions

View File

@ -1,26 +1,62 @@
Table < UITable
layout: verticalBox
header-column-style: HeaderTableColumn
header-row-style: HeaderTableRow
header-column-style: TableHeaderColumn
header-row-style: TableHeaderRow
column-style: TableColumn
row-style: TableRow
TableData < UIScrollArea
layout: verticalBox
TableRow < Label
TableRow < UITableRow
layout: horizontalBox
height: 10
text-wrap: true
focusable: true
even-background-color: alpha
odd-background-color: #00000022
$focus:
background-color: #294f6d
color: #ffffff
TableColumn < Label
width: 30
text-wrap: true
focusable: false
TableHeaderRow < Label
layout: horizontalBox
focusable: false
height: 10
text-wrap: true
TableHeaderColumn < Button
width: 30
TableHeaderColumn < UITableHeaderColumn
font: verdana-11px-antialised
background-color: alpha
color: #dfdfdfff
height: 23
focusable: true
text-offset: 0 0
image-source: /images/ui/button
image-color: #dfdfdf
image-clip: 0 0 22 23
image-border: 3
padding: 5 10 5 10
enabled: false
focusable: false
$hover !disabled:
image-clip: 0 23 22 23
$pressed:
image-clip: 0 46 22 23
text-offset: 1 1
$disabled:
color: #dfdfdf88
opacity: 0.8
SortableTableHeaderColumn < TableHeaderColumn
enabled: true
focusable: true

View File

@ -3,33 +3,45 @@
TODO:
* Make table headers more robust.
* Get dynamic row heights working with text wrapping.
* Every second row different background color applied.
]]
TABLE_SORTING_ASC = 0
TABLE_SORTING_DESC = 1
UITable = extends(UIWidget, "UITable")
local HEADER_ID = 'row0'
-- Initialize default values
function UITable.create()
local table = UITable.internalCreate()
table.headerRow = nil
table.headerColumns = {}
table.dataSpace = nil
table.rows = {}
table.rowBaseStyle = nil
table.columns = {}
table.columnWidth = {}
table.columBaseStyle = nil
table.headerRowBaseStyle = nil
table.headerColumnBaseStyle = nil
table.selectedRow = nil
table.defaultColumnWidth = 80
table.sortColumn = -1
table.sortType = TABLE_SORTING_ASC
table.autoSort = false
return table
end
-- Clear table values
function UITable:onDestroy()
for k,row in pairs(self.rows) do
for _,row in pairs(self.rows) do
row.onClick = nil
end
self.rows = {}
self.columns = {}
self.headerRow = {}
self.headerRow = nil
self.headerColumns = {}
self.columnWidth = {}
self.selectedRow = nil
if self.dataSpace then
@ -38,8 +50,18 @@ function UITable:onDestroy()
end
end
-- Detect if a header is already defined
function UITable:onSetup()
local header = self:getChildById('header')
if header then
self:setHeader(header)
end
end
-- Parse table related styles
function UITable:onStyleApply(styleName, styleNode)
for name, value in pairs(styleNode) do
if value ~= false then
if name == 'table-data' then
addEvent(function()
self:setTableData(self:getParent():getChildById(value))
@ -62,12 +84,24 @@ function UITable:onStyleApply(styleName, styleNode)
end)
end
end
end
end
function UITable:setColumnWidth(width)
if self:hasHeader() then return end
self.columnWidth = width
end
function UITable:setDefaultColumnWidth(width)
self.defaultColumnWidth = width
end
-- Check if the table has a header
function UITable:hasHeader()
return self.headerRow ~= nil
end
-- Clear all rows
function UITable:clearData()
if not self.dataSpace then
return
@ -78,23 +112,49 @@ function UITable:clearData()
self.rows = {}
end
function UITable:addHeaderRow(data)
-- Set existing child as header
function UITable:setHeader(headerWidget)
self:removeHeader()
if self.dataSpace then
local newHeight = self.dataSpace:getHeight()-headerRow:getHeight()-self.dataSpace:getMarginTop()
self.dataSpace:applyStyle({ height = newHeight })
end
self.headerColumns = {}
self.columnWidth = {}
for colId, column in pairs(headerWidget:getChildren()) do
column.colId = colId
column.table = self
table.insert(self.columnWidth, column:getWidth())
table.insert(self.headerColumns, column)
end
self.headerRow = headerWidget
end
-- Create and add header from table data
function UITable:addHeader(data)
if not data or type(data) ~= 'table' then
g_logger.error('UITable:addHeaderRow - table columns must be provided in a table')
return
end
self:removeHeader()
-- build header columns
local columns = {}
for _, column in pairs(data) do
for colId, column in pairs(data) do
local col = g_ui.createWidget(self.headerColumnBaseStyle)
col.colId = colId
col.table = self
for type, value in pairs(column) do
if type == 'width' then
col:setWidth(value)
elseif type == 'height' then
col:setHeight(value)
elseif type == 'text' then
col:setText(value)
col:setText(tr(value))
elseif type == 'onClick' then
col.onClick = value
end
@ -104,23 +164,34 @@ function UITable:addHeaderRow(data)
-- create a new header
local headerRow = g_ui.createWidget(self.headerRowBaseStyle, self)
local newHeight = (self.dataSpace:getHeight()-headerRow:getHeight())-self.dataSpace:getMarginTop()
local newHeight = self.dataSpace:getHeight()-headerRow:getHeight()-self.dataSpace:getMarginTop()
self.dataSpace:applyStyle({ height = newHeight })
headerRow:setId(HEADER_ID)
headerRow:setId('header')
self.headerColumns = {}
self.columnWidth = {}
for _, column in pairs(columns) do
headerRow:addChild(column)
self.columns[HEADER_ID] = column
table.insert(self.columnWidth, column:getWidth())
table.insert(self.headerColumns, column)
end
headerRow.onClick = function(headerRow) self:selectRow(headerRow) end
self.headerRow = headerRow
return headerRow
end
function UITable:removeHeaderRow()
-- Remove header
function UITable:removeHeader()
if self:hasHeader() then
if self.dataSpace then
local newHeight = self.dataSpace:getHeight()+self.headerRow:getHeight()+self.dataSpace:getMarginTop()
self.dataSpace:applyStyle({ height = newHeight })
end
self.headerColumns = {}
self.columnWidth = {}
self.headerRow:destroy()
self.headerRow = nil
end
end
function UITable:addRow(data, ref, height)
@ -134,41 +205,124 @@ function UITable:addRow(data, ref, height)
end
local row = g_ui.createWidget(self.rowBaseStyle)
row.table = self
if ref then row.ref = ref end
if height then row:setHeight(height) end
local rowId = #self.rows
row:setId('row'..(rowId < 1 and 1 or rowId))
local rowId = #self.rows + 1
row.rowId = rowId
row:setId('row'..rowId)
row:updateBackgroundColor()
for _, column in pairs(data) do
self.columns[rowId] = {}
for colId, column in pairs(data) do
local col = g_ui.createWidget(self.columBaseStyle, row)
for type, value in pairs(column) do
if type == 'width' then
col:setWidth(value)
elseif type == 'height' then
col:setHeight(value)
elseif type == 'text' then
col:setText(value)
if column.width then
col:setWidth(column.width)
else
col:setWidth(self.columnWidth[colId] or self.defaultColumnWidth)
end
if column.height then
col:setHeight(column.height)
end
self.columns[rowId] = col
if column.text then
col:setText(column.text)
end
if column.sortvalue then
col.sortvalue = column.sortvalue
else
col.sortvalue = column.text or 0
end
table.insert(self.columns[rowId], col)
end
row.onFocusChange = function(row, focused)
if focused then self:selectRow(row) end
end
self.dataSpace:addChild(row)
table.insert(self.rows, row)
if self.autoSort then
self:sort()
end
return row
end
-- Update row indices and background color
function UITable:updateRows()
for rowId = 1, #self.rows do
local row = self.rows[rowId]
row.rowId = rowId
row:setId('row'..rowId)
row:updateBackgroundColor()
end
end
-- Removes the given row widget from the table
function UITable:removeRow(row)
if self.selectedRow == row then
self:selectRow(nil)
end
row.onClick = nil
table.removevalue(self.rows, row)
row.table = nil
table.remove(self.columns, row.rowId)
table.remove(self.rows, row.rowId)
self.dataSpace:removeChild(row)
self:updateRows()
end
function UITable:toggleSorting(enabled)
self.autoSort = enabled
end
function UITable:setSorting(colId, sortType)
self.headerColumns[colId]:focus()
if sortType then
self.sortType = sortType
elseif self.sortColumn == colId then
if self.sortType == TABLE_SORTING_ASC then
self.sortType = TABLE_SORTING_DESC
else
self.sortType = TABLE_SORTING_ASC
end
else
self.sortType = TABLE_SORTING_ASC
end
self.sortColumn = colId
end
function UITable:sort()
if self.sortColumn <= 0 then
return
end
if self.sortType == TABLE_SORTING_ASC then
table.sort(self.rows, function(rowA, b)
return rowA:getChildByIndex(self.sortColumn).sortvalue < b:getChildByIndex(self.sortColumn).sortvalue
end)
else
table.sort(self.rows, function(rowA, b)
return rowA:getChildByIndex(self.sortColumn).sortvalue > b:getChildByIndex(self.sortColumn).sortvalue
end)
end
if self.dataSpace then
for _, child in pairs(self.dataSpace:getChildren()) do
self.dataSpace:removeChild(child)
end
end
self:updateRows()
self.columns = {}
for _, row in pairs(self.rows) do
if self.dataSpace then
self.dataSpace:addChild(row)
end
self.columns[row.rowId] = {}
for _, column in pairs(row:getChildren()) do
table.insert(self.columns[row.rowId], column)
end
end
end
function UITable:selectRow(selectedRow)
@ -189,8 +343,13 @@ function UITable:selectRow(selectedRow)
end
function UITable:setTableData(tableData)
local headerHeight = 0
if self.headerRow then
headerHeight = self.headerRow:getHeight()
end
self.dataSpace = tableData
self.dataSpace:applyStyle({ height = self:getHeight() })
self.dataSpace:applyStyle({ height = self:getHeight()-headerHeight-self:getMarginTop() })
end
function UITable:setRowStyle(style)
@ -202,9 +361,11 @@ end
function UITable:setColumnStyle(style)
self.columBaseStyle = style
for _, col in pairs(self.columns) do
for _, columns in pairs(self.columns) do
for _, col in pairs(columns) do
col:setStyle(style)
end
end
end
function UITable:setHeaderRowStyle(style)
@ -216,7 +377,51 @@ end
function UITable:setHeaderColumnStyle(style)
self.headerColumnBaseStyle = style
if table.haskey(HEADER_ID) then
self.columns[HEADER_ID]:setStyle(style)
for _, col in pairs(self.headerColumns) do
col:setStyle(style)
end
end
UITableRow = extends(UIWidget, "UITableRow")
function UITableRow:onFocusChange(focused)
if focused then
if self.table then self.table:selectRow(self) end
end
end
function UITableRow:onStyleApply(styleName, styleNode)
for name,value in pairs(styleNode) do
if name == 'even-background-color' then
self.evenBackgroundColor = value
elseif name == 'odd-background-color' then
self.oddBackgroundColor = value
end
end
end
function UITableRow:updateBackgroundColor()
self.backgroundColor = nil
local isEven = (self.rowId % 2 == 0)
if isEven and self.evenBackgroundColor then
self.backgroundColor = self.evenBackgroundColor
elseif not isEven and self.oddBackgroundColor then
self.backgroundColor = self.oddBackgroundColor
end
if self.backgroundColor then
self:mergeStyle({ ['background-color'] = self.backgroundColor })
end
end
UITableHeaderColumn = extends(UIButton, "UITableHeaderColumn")
function UITableHeaderColumn:onClick()
if self.table then
self.table:setSorting(self.colId)
self.table:sort()
end
end

View File

@ -13,9 +13,6 @@
* Clean up the interface building
- Add a new market interface file to handle building?
* Add offer table column ordering.
- Player Name, Amount, Total Price, Peice Price and Ends At
* Extend information features
- Hover over offers for purchase information (balance after transaction, etc)
- Display out of trend market offers based on their previous statistics (like cipsoft does)
@ -78,14 +75,6 @@ fee = 0
loaded = false
local offerTableHeader = {
{['text'] = 'Player Name', ['width'] = 100},
{['text'] = 'Amount', ['width'] = 60},
{['text'] = 'Total Price', ['width'] = 90},
{['text'] = 'Piece Price', ['width'] = 80},
{['text'] = 'Ends at', ['width'] = 120}
}
local function isItemValid(item, category, searchFilter)
if not item or not item.marketData then
return false
@ -169,7 +158,7 @@ local function refreshTypeList()
end
end
local function addOffer(offer, type)
local function addOffer(offer, offerType)
if not offer then
return false
end
@ -179,26 +168,35 @@ local function addOffer(offer, type)
local price = offer:getPrice()
local timestamp = offer:getTimeStamp()
buyOfferTable:toggleSorting(false)
sellOfferTable:toggleSorting(false)
if amount < 1 then return false end
if type == MarketAction.Buy then
if offerType == MarketAction.Buy then
local data = {
{['text'] = player, ['width'] = 100},
{['text'] = amount, ['width'] = 60},
{['text'] = price*amount, ['width'] = 90},
{['text'] = price, ['width'] = 80},
{['text'] = string.gsub(os.date('%c', timestamp), " ", " "), ['width'] = 120}
{text = player},
{text = amount},
{text = price*amount},
{text = price},
{text = string.gsub(os.date('%c', timestamp), " ", " ")}
}
buyOfferTable:addRow(data, id)
else
local data = {
{['text'] = player, ['width'] = 100},
{['text'] = amount, ['width'] = 60},
{['text'] = price*amount, ['width'] = 90},
{['text'] = price, ['width'] = 80},
{['text'] = string.gsub(os.date('%c', timestamp), " ", " "), ['width'] = 120}
{text = player},
{text = amount},
{text = price*amount},
{text = price},
{text = string.gsub(os.date('%c', timestamp), " ", " "), sortvalue = timestamp}
}
sellOfferTable:addRow(data, id)
end
buyOfferTable:toggleSorting(false)
sellOfferTable:toggleSorting(false)
buyOfferTable:sort()
sellOfferTable:sort()
return true
end
@ -207,11 +205,11 @@ local function mergeOffer(offer)
return false
end
local id = offer:getId()
local type = offer:getType()
local offerType = offer:getType()
local amount = offer:getAmount()
local replaced = false
if type == MarketAction.Buy then
if offerType == MarketAction.Buy then
for i = 1, #marketOffers[MarketAction.Buy] do
local o = marketOffers[MarketAction.Buy][i]
-- replace existing offer
@ -250,7 +248,9 @@ local function updateOffers(offers)
-- clear existing offer data
buyOfferTable:clearData()
buyOfferTable:setSorting(4, TABLE_SORTING_DESC)
sellOfferTable:clearData()
sellOfferTable:setSorting(4, TABLE_SORTING_ASC)
sellButton:setEnabled(false)
buyButton:setEnabled(false)
@ -274,8 +274,8 @@ local function updateDetails(itemId, descriptions, purchaseStats, saleStats)
detailsTable:clearData()
for k, desc in pairs(descriptions) do
local columns = {
{['text'] = getMarketDescriptionName(desc[1])..':'},
{['text'] = desc[2], ['width'] = 330}
{text = getMarketDescriptionName(desc[1])..':'},
{text = desc[2]}
}
detailsTable:addRow(columns)
end
@ -283,7 +283,7 @@ local function updateDetails(itemId, descriptions, purchaseStats, saleStats)
-- update sale item statistics
sellStatsTable:clearData()
if table.empty(saleStats) then
sellStatsTable:addRow({{['text'] = 'No information'}})
sellStatsTable:addRow({{text = 'No information'}})
else
local transactions, totalPrice, highestPrice, lowestPrice = 0, 0, 0, 0
for _, stat in pairs(saleStats) do
@ -301,28 +301,24 @@ local function updateDetails(itemId, descriptions, purchaseStats, saleStats)
end
end
end
sellStatsTable:addRow({{['text'] = 'Total Transations:'},
{['text'] = transactions, ['width'] = 270}})
sellStatsTable:addRow({{['text'] = 'Highest Price:'},
{['text'] = highestPrice, ['width'] = 270}})
sellStatsTable:addRow({{text = 'Total Transations:'}, {text = transactions}})
sellStatsTable:addRow({{text = 'Highest Price:'}, {text = highestPrice}})
if totalPrice > 0 and transactions > 0 then
sellStatsTable:addRow({{['text'] = 'Average Price:'},
{['text'] = math.floor(totalPrice/transactions), ['width'] = 270}})
sellStatsTable:addRow({{text = 'Average Price:'},
{text = math.floor(totalPrice/transactions)}})
else
sellStatsTable:addRow({{['text'] = 'Average Price:'},
{['text'] = 0, ['width'] = 270}})
sellStatsTable:addRow({{text = 'Average Price:'}, {text = 0}})
end
sellStatsTable:addRow({{['text'] = 'Lowest Price:'},
{['text'] = lowestPrice, ['width'] = 270}})
sellStatsTable:addRow({{text = 'Lowest Price:'}, {text = lowestPrice}})
end
-- update buy item statistics
buyStatsTable:clearData()
if table.empty(purchaseStats) then
buyStatsTable:addRow({{['text'] = 'No information'}})
buyStatsTable:addRow({{text = 'No information'}})
else
local transactions, totalPrice, highestPrice, lowestPrice = 0, 0, 0, 0
for _, stat in pairs(purchaseStats) do
@ -340,22 +336,18 @@ local function updateDetails(itemId, descriptions, purchaseStats, saleStats)
end
end
end
buyStatsTable:addRow({{['text'] = 'Total Transations:'},
{['text'] = transactions, ['width'] = 270}})
buyStatsTable:addRow({{['text'] = 'Highest Price:'},
{['text'] = highestPrice, ['width'] = 270}})
buyStatsTable:addRow({{text = 'Total Transations:'},{text = transactions}})
buyStatsTable:addRow({{text = 'Highest Price:'}, {text = highestPrice}})
if totalPrice > 0 and transactions > 0 then
buyStatsTable:addRow({{['text'] = 'Average Price:'},
{['text'] = math.floor(totalPrice/transactions), ['width'] = 270}})
buyStatsTable:addRow({{text = 'Average Price:'},
{text = math.floor(totalPrice/transactions)}})
else
buyStatsTable:addRow({{['text'] = 'Average Price:'},
{['text'] = 0, ['width'] = 270}})
buyStatsTable:addRow({{text = 'Average Price:'}, {text = 0}})
end
buyStatsTable:addRow({{['text'] = 'Lowest Price:'},
{['text'] = lowestPrice, ['width'] = 270}})
buyStatsTable:addRow({{text = 'Lowest Price:'}, {text = lowestPrice}})
end
end
@ -737,6 +729,13 @@ local function initInterface()
sellStatsTable = itemStatsPanel:recursiveGetChildById('sellStatsTable')
buyOfferTable.onSelectionChange = onSelectBuyOffer
sellOfferTable.onSelectionChange = onSelectSellOffer
buyStatsTable:setColumnWidth({120, 270})
sellStatsTable:setColumnWidth({120, 270})
detailsTable:setColumnWidth({80, 330})
buyOfferTable:setSorting(4, TABLE_SORTING_DESC)
sellOfferTable:setSorting(4, TABLE_SORTING_ASC)
end
function init()
@ -1125,14 +1124,6 @@ function Market.onMarketEnter(depotItems, offers, balance, vocation)
Market.loadMarketItems(MarketCategory.First)
end
-- build offer table header
if buyOfferTable and not buyOfferTable:hasHeader() then
buyOfferTable:addHeaderRow(offerTableHeader)
end
if sellOfferTable and not sellOfferTable:hasHeader() then
sellOfferTable:addHeaderRow(offerTableHeader)
end
if g_game.isOnline() then
marketWindow:lock()
marketWindow:show()

View File

@ -1,11 +1,12 @@
DetailsTableRow < TableRow
font: verdana-11px-monochrome
background-color: alpha
focusable: true
color: #cccccc
height: 45
focusable: false
padding: 2
even-background-color: alpha
odd-background-color: alpha
DetailsTableColumn < TableColumn
font: verdana-11px-monochrome

View File

@ -1,35 +1,24 @@
OfferTableRow < TableRow
font: verdana-11px-monochrome
background-color: alpha
focusable: true
color: #cccccc
height: 15
$focus:
background-color: #294f6d
color: #ffffff
OfferTableColumn < TableColumn
font: verdana-11px-monochrome
background-color: alpha
text-offset: 5 0
color: #cccccc
width: 80
focusable: false
OfferTableHeaderRow < TableHeaderRow
font: verdana-11px-monochrome
focusable: false
color: #cccccc
height: 20
OfferTableHeaderColumn < TableHeaderColumn
OfferTableHeaderColumn < SortableTableHeaderColumn
font: verdana-11px-monochrome
background-color: alpha
text-offset: 2 0
color: #cccccc
width: 80
focusable: true
$focus:
background-color: #294f6d
@ -74,8 +63,26 @@ Panel
table-data: sellingTableData
row-style: OfferTableRow
column-style: OfferTableColumn
header-row-style: OfferTableHeaderRow
header-column-style: OfferTableHeaderColumn
header-column-style: false
header-row-style: false
OfferTableHeaderRow
id: header
OfferTableHeaderColumn
!text: tr('Buyer Name')
width: 100
OfferTableHeaderColumn
!text: tr('Amount')
width: 60
OfferTableHeaderColumn
!text: tr('Total Price')
width: 90
OfferTableHeaderColumn
!text: tr('Piece Price')
width: 80
OfferTableHeaderColumn
!text: tr('Auction End')
width: 120
TableData
id: sellingTableData
@ -129,8 +136,26 @@ Panel
table-data: buyingTableData
row-style: OfferTableRow
column-style: OfferTableColumn
header-row-style: OfferTableHeaderRow
header-column-style: OfferTableHeaderColumn
header-column-style: false
header-row-style: false
OfferTableHeaderRow
id: header
OfferTableHeaderColumn
!text: tr('Seller Name')
width: 100
OfferTableHeaderColumn
!text: tr('Amount')
width: 60
OfferTableHeaderColumn
!text: tr('Total Price')
width: 90
OfferTableHeaderColumn
!text: tr('Piece Price')
width: 80
OfferTableHeaderColumn
!text: tr('Auction End')
width: 120
TableData
id: buyingTableData

View File

@ -1,6 +1,5 @@
StatsTableRow < TableRow
font: verdana-11px-monochrome
background-color: alpha
focusable: true
color: #cccccc
height: 20
@ -9,7 +8,7 @@ StatsTableRow < TableRow
StatsTableColumn < TableColumn
font: verdana-11px-monochrome
background-color: alpha
text-offset: 5 0
text-offset: 5 3
color: #cccccc
width: 110
focusable: false