forked from mbk-lab/rui_orig
2
0
Fork 0

Updated TableView

Added selection scroll
Fixed TableVerticalAlign property
ListView bug fixing
This commit is contained in:
Alexei Anoshenko 2022-01-16 12:25:45 -05:00
parent 32b6182dbf
commit 09544fdf25
6 changed files with 315 additions and 173 deletions

View File

@ -668,8 +668,6 @@ function selectListItem(element, item, needSendMessage) {
if (current) {
if (current.classList) {
current.classList.remove(focusStyle, blurStyle);
} else { // IE < 10
current.className = "ruiListItem";
}
if (sendMessage) {
message = "itemUnselected{session=" + sessionID + ",id=" + element.id + "}";
@ -681,14 +679,10 @@ function selectListItem(element, item, needSendMessage) {
if (element === document.activeElement) {
if (item.classList) {
item.classList.add(focusStyle);
} else { // IE < 10
item.className = "ruiListItem " + focusStyle
}
} else {
if (item.classList) {
item.classList.add(blurStyle);
} else { // IE < 10
item.className = "ruiListItem " + blurStyle
}
}
@ -700,6 +694,12 @@ function selectListItem(element, item, needSendMessage) {
}
}
if (item.scrollIntoViewIfNeeded) {
item.scrollIntoViewIfNeeded()
} else {
item.scrollIntoView({block: "nearest", inline: "nearest"});
}
/*
var left = item.offsetLeft - element.offsetLeft;
if (left < element.scrollLeft) {
element.scrollLeft = left;
@ -718,7 +718,7 @@ function selectListItem(element, item, needSendMessage) {
var bottom = top + item.offsetHeight
if (bottom > element.scrollTop + element.clientHeight) {
element.scrollTop = bottom - element.clientHeight;
}
}*/
}
if (needSendMessage && message != undefined) {
@ -1418,6 +1418,11 @@ function setTableCellCursor(element, row, column) {
cell.classList.add(focusStyle);
element.setAttribute("data-current", cellID);
if (cell.scrollIntoViewIfNeeded) {
cell.scrollIntoViewIfNeeded()
} else {
cell.scrollIntoView({block: "nearest", inline: "nearest"});
}
sendMessage("currentCell{session=" + sessionID + ",id=" + element.id +
",row=" + row + ",column=" + column + "}");
@ -1466,108 +1471,107 @@ function moveTableCellCursor(element, row, column, dr, dc) {
function tableViewCellKeyDownEvent(element, event) {
const key = getKey(event);
if (key) {
const currentId = element.getAttribute("data-current");
if (currentId) {
const elements = currentId.split("-");
if (elements.length >= 3) {
const row = parseInt(elements[1], 10)
const column = parseInt(elements[2], 10)
switch (key) {
case " ":
case "Enter":
sendMessage("cellClick{session=" + sessionID + ",id=" + element.id +
",row=" + row + ",column=" + column + "}");
break;
case "ArrowLeft":
moveTableCellCursor(element, row, column, 0, -1)
break;
case "ArrowRight":
moveTableCellCursor(element, row, column, 0, 1)
break;
case "ArrowDown":
moveTableCellCursor(element, row, column, 1, 0)
break;
case "ArrowUp":
moveTableCellCursor(element, row, column, -1, 0)
break;
case "Home":
// TODO
break;
case "End":
/*var newRow = rowCount-1;
while (newRow > row) {
if (setTableRowCursor(element, newRow)) {
break;
}
newRow--;
}*/
// TODO
break;
case "PageUp":
// TODO
break;
case "PageDown":
// TODO
break;
default:
return;
}
event.stopPropagation();
event.preventDefault();
return;
}
}
switch (key) {
case "ArrowLeft":
case "ArrowRight":
case "ArrowDown":
case "ArrowUp":
case "Home":
case "End":
case "PageUp":
case "PageDown":
const rows = element.getAttribute("data-rows");
const columns = element.getAttribute("data-columns");
if (rows && columns) {
const rowCount = parseInt(rows);
const columnCount = parseInt(rows);
row = 0;
while (row < rowCount) {
column = 0;
while (columns < columnCount) {
if (setTableCellCursor(element, row, column)) {
event.stopPropagation();
event.preventDefault();
return;
}
column++;
}
row++;
}
}
break;
default:
return;
}
if (!key) {
return;
}
event.stopPropagation();
event.preventDefault();
const currentId = element.getAttribute("data-current");
if (!currentId || currentId == "") {
switch (key) {
case "ArrowLeft":
case "ArrowRight":
case "ArrowDown":
case "ArrowUp":
case "Home":
case "End":
case "PageUp":
case "PageDown":
const rows = element.getAttribute("data-rows");
const columns = element.getAttribute("data-columns");
if (rows && columns) {
const rowCount = parseInt(rows);
const columnCount = parseInt(rows);
row = 0;
while (row < rowCount) {
column = 0;
while (columns < columnCount) {
if (setTableCellCursor(element, row, column)) {
event.stopPropagation();
event.preventDefault();
return;
}
column++;
}
row++;
}
}
event.stopPropagation();
event.preventDefault();
break;
}
return;
}
const elements = currentId.split("-");
if (elements.length >= 3) {
const row = parseInt(elements[1], 10)
const column = parseInt(elements[2], 10)
switch (key) {
case " ":
case "Enter":
sendMessage("cellClick{session=" + sessionID + ",id=" + element.id +
",row=" + row + ",column=" + column + "}");
break;
case "ArrowLeft":
moveTableCellCursor(element, row, column, 0, -1)
break;
case "ArrowRight":
moveTableCellCursor(element, row, column, 0, 1)
break;
case "ArrowDown":
moveTableCellCursor(element, row, column, 1, 0)
break;
case "ArrowUp":
moveTableCellCursor(element, row, column, -1, 0)
break;
case "Home":
// TODO
break;
case "End":
/*var newRow = rowCount-1;
while (newRow > row) {
if (setTableRowCursor(element, newRow)) {
break;
}
newRow--;
}*/
// TODO
break;
case "PageUp":
// TODO
break;
case "PageDown":
// TODO
break;
default:
return;
}
event.stopPropagation();
event.preventDefault();
} else {
element.setAttribute("data-current", "");
}
}
function setTableRowCursor(element, row) {
@ -1590,6 +1594,11 @@ function setTableRowCursor(element, row) {
tableRow.classList.add(focusStyle);
element.setAttribute("data-current", tableRowID);
if (tableRow.scrollIntoViewIfNeeded) {
tableRow.scrollIntoViewIfNeeded()
} else {
tableRow.scrollIntoView({block: "nearest", inline: "nearest"});
}
sendMessage("currentRow{session=" + sessionID + ",id=" + element.id + ",row=" + row + "}");
return true;

View File

@ -36,6 +36,8 @@ GridLayout {
DropDownList { row = 8, column = 1, id = tableSelectionMode, current = 0, items = ["none", "cell", "row"]},
Checkbox { row = 9, column = 0:1, id = tableDisableHead, content = "Disable head selection" },
Checkbox { row = 10, column = 0:1, id = tableDisableFoot, content = "Disable foot selection" },
TextView { row = 11, text = "Vertical align in cells" },
DropDownList { row = 11, column = 1, id = tableVerticalAlign, current = 0, items = ["top", "bottom", "center", "baseline"]},
]
}
]
@ -274,5 +276,9 @@ func createTableViewDemo(session rui.Session) rui.View {
}
})
rui.Set(view, "tableVerticalAlign", rui.DropDownEvent, func(list rui.DropDownList, number int) {
rui.Set(view, "demoTableView1", rui.TableVerticalAlign, number)
})
return view
}

View File

@ -928,6 +928,8 @@ func (listView *listViewData) htmlProperties(self View, buffer *strings.Builder)
buffer.WriteString(strconv.Itoa(current))
buffer.WriteRune('"')
}
listView.viewData.htmlProperties(self, buffer)
}
/*

View File

@ -1,31 +1,66 @@
package rui
// TableAdapter describes the TableView content
type TableAdapter interface {
// RowCount returns number of rows in the table
RowCount() int
// ColumnCount returns number of columns in the table
ColumnCount() int
// Cell returns the contents of a table cell. The function can return elements of the following types:
// * string
// * rune
// * float32, float64
// * integer values: int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64
// * bool
// * rui.Color
// * rui.View
// * fmt.Stringer
// * rui.VerticalTableJoin, rui.HorizontalTableJoin
Cell(row, column int) interface{}
}
// TableColumnStyle describes the style of TableView columns.
// To set column styles, you must either implement the TableColumnStyle interface in the table adapter
// or assign its separate implementation to the "column-style" property.
type TableColumnStyle interface {
ColumnStyle(column int) Params
}
// TableRowStyle describes the style of TableView rows.
// To set row styles, you must either implement the TableRowStyle interface in the table adapter
// or assign its separate implementation to the "row-style" property.
type TableRowStyle interface {
RowStyle(row int) Params
}
// TableCellStyle describes the style of TableView cells.
// To set row cells, you must either implement the TableCellStyle interface in the table adapter
// or assign its separate implementation to the "cell-style" property.
type TableCellStyle interface {
CellStyle(row, column int) Params
}
// TableAllowCellSelection determines whether TableView cell selection is allowed.
// It is only used if the "selection-mode" property is set to CellSelection (1).
// To set cell selection allowing, you must either implement the TableAllowCellSelection interface
// in the table adapter or assign its separate implementation to the "allow-selection" property.
type TableAllowCellSelection interface {
AllowCellSelection(row, column int) bool
}
// TableAllowRowSelection determines whether TableView row selection is allowed.
// It is only used if the "selection-mode" property is set to RowSelection (2).
// To set row selection allowing, you must either implement the TableAllowRowSelection interface
// in the table adapter or assign its separate implementation to the "allow-selection" property.
type TableAllowRowSelection interface {
AllowRowSelection(row int) bool
}
// SimpleTableAdapter is implementation of TableAdapter where the content
// defines as [][]interface{}.
// When you assign [][]interface{} value to the "content" property, it is converted to SimpleTableAdapter
type SimpleTableAdapter interface {
TableAdapter
TableCellStyle
@ -36,6 +71,9 @@ type simpleTableAdapter struct {
columnCount int
}
// TextTableAdapter is implementation of TableAdapter where the content
// defines as [][]string.
// When you assign [][]string value to the "content" property, it is converted to TextTableAdapter
type TextTableAdapter interface {
TableAdapter
}
@ -45,12 +83,17 @@ type textTableAdapter struct {
columnCount int
}
// NewTextTableAdapter is an auxiliary structure. It used as cell content and
// specifies that the cell should be merged with the one above it
type VerticalTableJoin struct {
}
// HorizontalTableJoin is an auxiliary structure. It used as cell content and
// specifies that the cell should be merged with the one before it
type HorizontalTableJoin struct {
}
// NewSimpleTableAdapter creates the new SimpleTableAdapter
func NewSimpleTableAdapter(content [][]interface{}) SimpleTableAdapter {
if content == nil {
return nil
@ -145,6 +188,7 @@ func (adapter *simpleTableAdapter) CellStyle(row, column int) Params {
return params
}
// NewTextTableAdapter creates the new TextTableAdapter
func NewTextTableAdapter(content [][]string) TextTableAdapter {
if content == nil {
return nil
@ -337,3 +381,14 @@ func (table *tableViewData) getColumnStyle() TableColumnStyle {
}
return nil
}
func (table *tableViewData) getCellStyle() TableCellStyle {
for _, tag := range []string{CellStyle, Content} {
if value := table.getRaw(tag); value != nil {
if style, ok := value.(TableCellStyle); ok {
return style
}
}
}
return nil
}

View File

@ -9,7 +9,7 @@ import (
const (
// TableVerticalAlign is the constant for the "table-vertical-align" property tag.
// The "table-vertical-align" int property sets the vertical alignment of the content inside a table cell.
// Valid values are LeftAlign (0), RightAlign (1), CenterAlign (2), and BaselineAlign (3, 4)
// Valid values are TopAlign (0), BottomAlign (1), CenterAlign (2), and BaselineAlign (3, 4)
TableVerticalAlign = "table-vertical-align"
// HeadHeight is the constant for the "head-height" property tag.
@ -221,7 +221,12 @@ type TableView interface {
ParanetView
ReloadTableData()
CellFrame(row, column int) Frame
content() TableAdapter
getCurrent() CellIndex
getRowStyle() TableRowStyle
getColumnStyle() TableColumnStyle
getCellStyle() TableCellStyle
}
type tableViewData struct {
@ -298,8 +303,8 @@ func (table *tableViewData) remove(tag string) {
table.removeBoundsSide(CellPadding, tag)
table.propertyChanged(tag)
case Gap, CellBorder, CellPadding, RowStyle, ColumnStyle, CellStyle,
HeadHeight, HeadStyle, FootHeight, FootStyle, AllowSelection:
case SelectionMode, TableVerticalAlign, Gap, CellBorder, CellPadding, RowStyle,
ColumnStyle, CellStyle, HeadHeight, HeadStyle, FootHeight, FootStyle, AllowSelection:
if _, ok := table.properties[tag]; ok {
delete(table.properties, tag)
table.propertyChanged(tag)
@ -326,10 +331,6 @@ func (table *tableViewData) remove(tag string) {
table.current.Column = -1
table.propertyChanged(tag)
case SelectionMode:
table.viewData.remove(tag)
table.propertyChanged(tag)
default:
table.viewData.remove(tag)
}
@ -489,7 +490,7 @@ func (table *tableViewData) set(tag string, value interface{}) bool {
return false
}
case CellBorder, CellBorderStyle, CellBorderColor, CellBorderWidth,
case SelectionMode, TableVerticalAlign, CellBorder, CellBorderStyle, CellBorderColor, CellBorderWidth,
CellBorderLeft, CellBorderLeftStyle, CellBorderLeftColor, CellBorderLeftWidth,
CellBorderRight, CellBorderRightStyle, CellBorderRightColor, CellBorderRightWidth,
CellBorderTop, CellBorderTopStyle, CellBorderTopColor, CellBorderTopWidth,
@ -498,11 +499,6 @@ func (table *tableViewData) set(tag string, value interface{}) bool {
return false
}
case SelectionMode:
if !table.setEnumProperty(SelectionMode, value, enumProperties[SelectionMode].values) {
return false
}
case AllowSelection:
switch value.(type) {
case TableAllowCellSelection:
@ -575,8 +571,8 @@ func (table *tableViewData) set(tag string, value interface{}) bool {
func (table *tableViewData) propertyChanged(tag string) {
if table.created {
switch tag {
case Content, RowStyle, ColumnStyle, CellStyle, CellPadding, CellBorder,
HeadHeight, HeadStyle, FootHeight, FootStyle,
case Content, TableVerticalAlign, RowStyle, ColumnStyle, CellStyle, CellPadding,
CellBorder, HeadHeight, HeadStyle, FootHeight, FootStyle,
CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft,
TableCellClickedEvent, TableCellSelectedEvent, TableRowClickedEvent,
TableRowSelectedEvent, AllowSelection:
@ -851,18 +847,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
table.cellFrame = make([]Frame, rowCount*columnCount)
rowStyle := table.getRowStyle()
var cellStyle1 TableCellStyle = nil
if style, ok := adapter.(TableCellStyle); ok {
cellStyle1 = style
}
var cellStyle2 TableCellStyle = nil
if value := table.getRaw(CellStyle); value != nil {
if style, ok := value.(TableCellStyle); ok {
cellStyle2 = style
}
}
cellStyle := table.getCellStyle()
session := table.Session()
@ -901,6 +886,14 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
}
}
vAlignCss := enumProperties[TableVerticalAlign].cssValues
vAlignValue := GetTableVerticalAlign(table, "")
if vAlignValue < 0 || vAlignValue >= len(vAlignCss) {
vAlignValue = 0
}
vAlign := vAlignCss[vAlignValue]
tableCSS := func(startRow, endRow int, cellTag string, cellBorder BorderProperty, cellPadding BoundsProperty) {
for row := startRow; row < endRow; row++ {
@ -968,41 +961,37 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
view.set(Padding, cellPadding)
}
appendFrom := func(cellStyle TableCellStyle) {
if cellStyle != nil {
if styles := cellStyle.CellStyle(row, column); styles != nil {
for tag, value := range styles {
valueToInt := func() int {
switch value := value.(type) {
case int:
return value
if cellStyle != nil {
if styles := cellStyle.CellStyle(row, column); styles != nil {
for tag, value := range styles {
valueToInt := func() int {
switch value := value.(type) {
case int:
return value
case string:
if value, ok := session.resolveConstants(value); ok {
if n, err := strconv.Atoi(value); err == nil {
return n
}
case string:
if value, ok := session.resolveConstants(value); ok {
if n, err := strconv.Atoi(value); err == nil {
return n
}
}
return 0
}
return 0
}
switch tag = strings.ToLower(tag); tag {
case RowSpan:
rowSpan = valueToInt()
switch tag = strings.ToLower(tag); tag {
case RowSpan:
rowSpan = valueToInt()
case ColumnSpan:
columnSpan = valueToInt()
case ColumnSpan:
columnSpan = valueToInt()
default:
view.set(tag, value)
}
default:
view.set(tag, value)
}
}
}
}
appendFrom(cellStyle1)
appendFrom(cellStyle2)
if len(view.properties) > 0 {
view.cssStyle(&view, &cssBuilder)
@ -1162,13 +1151,17 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
if style, ok := session.resolveConstants(value); ok {
buffer.WriteString(` class="`)
buffer.WriteString(style)
buffer.WriteString(`">`)
buffer.WriteString(`" style="vertical-align: `)
buffer.WriteString(vAlign)
buffer.WriteString(`;">`)
return table.cellBorderFromStyle(style), table.cellPaddingFromStyle(style)
}
case Params:
cssBuilder.buffer.Reset()
view.Clear()
view.Set(TableVerticalAlign, vAlignValue)
for tag, val := range value {
view.Set(tag, val)
}
@ -1203,7 +1196,10 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
return border, padding
}
}
buffer.WriteRune('>')
buffer.WriteString(` style="vertical-align: `)
buffer.WriteString(vAlign)
buffer.WriteString(`;">`)
return nil, nil
}
@ -1231,7 +1227,9 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
}
if rowCount > footHeight+headHeight {
buffer.WriteString("<tbody>")
buffer.WriteString(`<tbody style="vertical-align: `)
buffer.WriteString(vAlign)
buffer.WriteString(`;">`)
tableCSS(headHeight, rowCount-footHeight, "td", cellBorder, cellPadding)
buffer.WriteString("</tbody>")
}

View File

@ -23,6 +23,70 @@ func (cell *tableCellView) cssStyle(self View, builder cssBuilder) {
}
}
// GetTableContent returns a TableAdapter which defines the TableView content.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableContent(view View, subviewID string) TableAdapter {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if tableView, ok := view.(TableView); ok {
return tableView.content()
}
}
return nil
}
// GetTableRowStyle returns a TableRowStyle which defines styles of TableView rows.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableRowStyle(view View, subviewID string) TableRowStyle {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if tableView, ok := view.(TableView); ok {
return tableView.getRowStyle()
}
}
return nil
}
// GetTableColumnStyle returns a TableColumnStyle which defines styles of TableView columns.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableColumnStyle(view View, subviewID string) TableColumnStyle {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if tableView, ok := view.(TableView); ok {
return tableView.getColumnStyle()
}
}
return nil
}
// GetTableCellStyle returns a TableCellStyle which defines styles of TableView cells.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableCellStyle(view View, subviewID string) TableCellStyle {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if tableView, ok := view.(TableView); ok {
return tableView.getCellStyle()
}
}
return nil
}
// GetSelectionMode returns the mode of the TableView elements selection.
// Valid values are NoneSelection (0), CellSelection (1), and RowSelection (2).
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
@ -38,28 +102,36 @@ func GetSelectionMode(view View, subviewID string) int {
return NoneSelection
}
// GetSelectionMode returns the index of the TableView selected row.
// If there is no selected row, then a value less than 0 are returned.
// GetTableVerticalAlign returns a vertical align in a TavleView cell. Returns one of next values:
// TopAlign (0), BottomAlign (1), CenterAlign (2), and BaselineAlign (3)
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetCurrentTableRow(view View, subviewID string) int {
func GetTableVerticalAlign(view View, subviewID string) int {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if selectionMode := GetSelectionMode(view, ""); selectionMode != NoneSelection {
if tableView, ok := view.(TableView); ok {
return tableView.getCurrent().Row
}
if result, ok := enumStyledProperty(view, TableVerticalAlign, TopAlign); ok {
return result
}
}
return -1
return TopAlign
}
// GetCurrentTableCell returns the row and column index of the TableView selected cell.
// If there is no selected cell, then a value of the row and column index less than 0 is returned.
//
//HeadHeight = "head-height"
//HeadStyle = "head-style"
//FootHeight = "foot-height"
//FootStyle = "foot-style"
// GetTableCurrent returns the row and column index of the TableView selected cell/row.
// If there is no selected cell/row or the selection mode is NoneSelection (0),
// then a value of the row and column index less than 0 is returned.
// If the selection mode is RowSelection (2) then the returned column index is less than 0.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetCurrentTableCell(view View, subviewID string) CellIndex {
func GetTableCurrent(view View, subviewID string) CellIndex {
if subviewID != "" {
view = ViewByID(view, subviewID)
}