package rui import ( "fmt" "strconv" "strings" ) // Constants for [TableView] specific properties and events 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 TopAlign (0), BottomAlign (1), CenterAlign (2), and BaselineAlign (3, 4) TableVerticalAlign = "table-vertical-align" // HeadHeight is the constant for the "head-height" property tag. // The "head-height" int property sets the number of rows in the table header. // The default value is 0 (no header) HeadHeight = "head-height" // HeadStyle is the constant for the "head-style" property tag. // The "head-style" string property sets the header style name HeadStyle = "head-style" // FootHeight is the constant for the "foot-height" property tag. // The "foot-height" int property sets the number of rows in the table footer. // The default value is 0 (no footer) FootHeight = "foot-height" // FootStyle is the constant for the "foot-style" property tag. // The "foot-style" string property sets the footer style name FootStyle = "foot-style" // RowSpan is the constant for the "row-span" property tag. // The "row-span" int property sets the number of table row to span. // Used only when specifying cell parameters in the implementation of TableCellStyle RowSpan = "row-span" // ColumnSpan is the constant for the "column-span" property tag. // The "column-span" int property sets the number of table column to span. // Used only when specifying cell parameters in the implementation of TableCellStyle ColumnSpan = "column-span" // RowStyle is the constant for the "row-style" property tag. // The "row-style" property sets the adapter which specifies styles of each table row. // This property can be assigned or by an implementation of TableRowStyle interface, or by an array of Params. RowStyle = "row-style" // ColumnStyle is the constant for the "column-style" property tag. // The "column-style" property sets the adapter which specifies styles of each table column. // This property can be assigned or by an implementation of TableColumnStyle interface, or by an array of Params. ColumnStyle = "column-style" // CellStyle is the constant for the "cell-style" property tag. // The "cell-style" property sets the adapter which specifies styles of each table cell. // This property can be assigned only by an implementation of TableCellStyle interface. CellStyle = "cell-style" // CellPadding is the constant for the "cell-padding" property tag. // The "cell-padding" Bounds property sets the padding area on all four sides of a table call at once. // An element's padding area is the space between its content and its border. CellPadding = "cell-padding" // CellPaddingLeft is the constant for the "cell-padding-left" property tag. // The "cell-padding-left" SizeUnit property sets the width of the padding area to the left of a cell content. // An element's padding area is the space between its content and its border. CellPaddingLeft = "cell-padding-left" // CellPaddingRight is the constant for the "cell-padding-right" property tag. // The "cell-padding-right" SizeUnit property sets the width of the padding area to the left of a cell content. // An element's padding area is the space between its content and its border. CellPaddingRight = "cell-padding-right" // CellPaddingTop is the constant for the "cell-padding-top" property tag. // The "cell-padding-top" SizeUnit property sets the height of the padding area to the top of a cell content. // An element's padding area is the space between its content and its border. CellPaddingTop = "cell-padding-top" // CellPaddingBottom is the constant for the "cell-padding-bottom" property tag. // The "cell-padding-bottom" SizeUnit property sets the height of the padding area to the bottom of a cell content. CellPaddingBottom = "cell-padding-bottom" // CellBorder is the constant for the "cell-border" property tag. // The "cell-border" property sets a table cell's border. It sets the values of a border width, style, and color. // This property can be assigned a value of BorderProperty type, or ViewBorder type, or BorderProperty text representation. CellBorder = "cell-border" // CellBorderLeft is the constant for the "cell-border-left" property tag. // The "cell-border-left" property sets a view's left border. It sets the values of a border width, style, and color. // This property can be assigned a value of BorderProperty type, or ViewBorder type, or BorderProperty text representation. CellBorderLeft = "cell-border-left" // CellBorderRight is the constant for the "cell-border-right" property tag. // The "cell-border-right" property sets a view's right border. It sets the values of a border width, style, and color. // This property can be assigned a value of BorderProperty type, or ViewBorder type, or BorderProperty text representation. CellBorderRight = "cell-border-right" // CellBorderTop is the constant for the "cell-border-top" property tag. // The "cell-border-top" property sets a view's top border. It sets the values of a border width, style, and color. // This property can be assigned a value of BorderProperty type, or ViewBorder type, or BorderProperty text representation. CellBorderTop = "cell-border-top" // CellBorderBottom is the constant for the "cell-border-bottom" property tag. // The "cell-border-bottom" property sets a view's bottom border. It sets the values of a border width, style, and color. // This property can be assigned a value of BorderProperty type, or ViewBorder type, or BorderProperty text representation. CellBorderBottom = "cell-border-bottom" // CellBorderStyle is the constant for the "cell-border-style" property tag. // The "cell-border-style" int property sets the line style for all four sides of a table cell's border. // Valid values are NoneLine (0), SolidLine (1), DashedLine (2), DottedLine (3), and DoubleLine (4). CellBorderStyle = "cell-border-style" // CellBorderLeftStyle is the constant for the "cell-border-left-style" property tag. // The "cell-border-left-style" int property sets the line style of a table cell's left border. // Valid values are NoneLine (0), SolidLine (1), DashedLine (2), DottedLine (3), and DoubleLine (4). CellBorderLeftStyle = "cell-border-left-style" // CellBorderRightStyle is the constant for the "cell-border-right-style" property tag. // The "cell-border-right-style" int property sets the line style of a table cell's right border. // Valid values are NoneLine (0), SolidLine (1), DashedLine (2), DottedLine (3), and DoubleLine (4). CellBorderRightStyle = "cell-border-right-style" // CellBorderTopStyle is the constant for the "cell-border-top-style" property tag. // The "cell-border-top-style" int property sets the line style of a table cell's top border. // Valid values are NoneLine (0), SolidLine (1), DashedLine (2), DottedLine (3), and DoubleLine (4). CellBorderTopStyle = "cell-border-top-style" // CellBorderBottomStyle is the constant for the "cell-border-bottom-style" property tag. // The "cell-border-bottom-style" int property sets the line style of a table cell's bottom border. // Valid values are NoneLine (0), SolidLine (1), DashedLine (2), DottedLine (3), and DoubleLine (4). CellBorderBottomStyle = "cell-border-bottom-style" // CellBorderWidth is the constant for the "cell-border-width" property tag. // The "cell-border-width" property sets the line width for all four sides of a table cell's border. CellBorderWidth = "cell-border-width" // CellBorderLeftWidth is the constant for the "cell-border-left-width" property tag. // The "cell-border-left-width" SizeUnit property sets the line width of a table cell's left border. CellBorderLeftWidth = "cell-border-left-width" // CellBorderRightWidth is the constant for the "cell-border-right-width" property tag. // The "cell-border-right-width" SizeUnit property sets the line width of a table cell's right border. CellBorderRightWidth = "cell-border-right-width" // CellBorderTopWidth is the constant for the "cell-border-top-width" property tag. // The "cell-border-top-width" SizeUnit property sets the line width of a table cell's top border. CellBorderTopWidth = "cell-border-top-width" // CellBorderBottomWidth is the constant for the "cell-border-bottom-width" property tag. // The "cell-border-bottom-width" SizeUnit property sets the line width of a table cell's bottom border. CellBorderBottomWidth = "cell-border-bottom-width" // CellBorderColor is the constant for the "cell-border-color" property tag. // The "cell-border-color" property sets the line color for all four sides of a table cell's border. CellBorderColor = "cell-border-color" // CellBorderLeftColor is the constant for the "cell-border-left-color" property tag. // The "cell-border-left-color" property sets the line color of a table cell's left border. CellBorderLeftColor = "cell-border-left-color" // CellBorderRightColor is the constant for the "cell-border-right-color" property tag. // The "cell-border-right-color" property sets the line color of a table cell's right border. CellBorderRightColor = "cell-border-right-color" // CellBorderTopColor is the constant for the "cell-border-top-color" property tag. // The "cell-border-top-color" property sets the line color of a table cell's top border. CellBorderTopColor = "cell-border-top-color" // CellBorderBottomColor is the constant for the "cell-border-bottom-color" property tag. // The "cell-border-bottom-color" property sets the line color of a table cell's bottom border. CellBorderBottomColor = "cell-border-bottom-color" // SelectionMode is the constant for the "selection-mode" property tag. // The "selection-mode" int property sets the mode of the table elements selection. // Valid values are NoneSelection (0), CellSelection (1), and RowSelection (2) SelectionMode = "selection-mode" // TableCellClickedEvent is the constant for "table-cell-clicked" property tag. // The "table-cell-clicked" event occurs when the user clicks on a table cell. // The main listener format: func(TableView, int, int), where the second argument is the row number, // and third argument is the column number. TableCellClickedEvent = "table-cell-clicked" // TableCellSelectedEvent is the constant for "table-cell-selected" property tag. // The "table-cell-selected" event occurs when a table cell becomes selected. // The main listener format: func(TableView, int, int), where the second argument is the row number, // and third argument is the column number. TableCellSelectedEvent = "table-cell-selected" // TableRowClickedEvent is the constant for "table-row-clicked" property tag. // The "table-row-clicked" event occurs when the user clicks on a table row. // The main listener format: func(TableView, int), where the second argument is the row number. TableRowClickedEvent = "table-row-clicked" // TableRowSelectedEvent is the constant for "table-row-selected" property tag. // The "table-row-selected" event occurs when a table row becomes selected. // The main listener format: func(TableView, int), where the second argument is the row number. TableRowSelectedEvent = "table-row-selected" // AllowSelection is the constant for the "allow-selection" property tag. // The "allow-selection" property sets the adapter which specifies styles of each table row. // This property can be assigned or by an implementation of TableAllowCellSelection // or TableAllowRowSelection interface. AllowSelection = "allow-selection" ) // Constants which represent values of "selection-mode" property of a [TableView] const ( // NoneSelection the selection is forbidden. NoneSelection = 0 // CellSelection the selection of a single cell only is enabled. CellSelection = 1 // RowSelection the selection of a table row only is enabled. RowSelection = 2 ) // CellIndex defines coordinates of the [TableView] cell type CellIndex struct { Row, Column int } // TableView represents a TableView view type TableView interface { View ParentView // ReloadTableData forces the table view to reload all data and redraw the entire table ReloadTableData() // ReloadCell forces the table view to reload the data for a specific cell and redraw it ReloadCell(row, column int) // CellFrame returns the frame of a specific cell, describing its position and size within the table view CellFrame(row, column int) Frame content() TableAdapter getCurrent() CellIndex getRowStyle() TableRowStyle getColumnStyle() TableColumnStyle getCellStyle() TableCellStyle } type tableViewData struct { viewData cellViews []View cellFrame []Frame cellSelectedListener, cellClickedListener []func(TableView, int, int) rowSelectedListener, rowClickedListener []func(TableView, int) current CellIndex } type tableCellView struct { viewData } // NewTableView create new TableView object and return it func NewTableView(session Session, params Params) TableView { view := new(tableViewData) view.init(session) setInitParams(view, params) return view } func newTableView(session Session) View { return NewTableView(session, nil) } // Init initialize fields of TableView by default values func (table *tableViewData) init(session Session) { table.viewData.init(session) table.tag = "TableView" table.cellViews = []View{} table.cellFrame = []Frame{} table.cellSelectedListener = []func(TableView, int, int){} table.cellClickedListener = []func(TableView, int, int){} table.rowSelectedListener = []func(TableView, int){} table.rowClickedListener = []func(TableView, int){} table.current.Row = -1 table.current.Column = -1 } func (table *tableViewData) String() string { return getViewString(table, nil) } func (table *tableViewData) normalizeTag(tag string) string { switch tag = strings.ToLower(tag); tag { case "top-cell-padding": tag = CellPaddingTop case "right-cell-padding": tag = CellPaddingRight case "bottom-cell-padding": tag = CellPaddingBottom case "left-cell-padding": tag = CellPaddingLeft } return tag } func (table *tableViewData) Focusable() bool { return GetTableSelectionMode(table) != NoneSelection } func (table *tableViewData) Get(tag string) any { return table.get(table.normalizeTag(tag)) } func (table *tableViewData) Remove(tag string) { table.remove(table.normalizeTag(tag)) } func (table *tableViewData) remove(tag string) { switch tag { case CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft: table.removeBoundsSide(CellPadding, tag) table.propertyChanged(tag) 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) } case TableCellClickedEvent: table.cellClickedListener = []func(TableView, int, int){} table.propertyChanged(tag) case TableCellSelectedEvent: table.cellSelectedListener = []func(TableView, int, int){} table.propertyChanged(tag) case TableRowClickedEvent: table.rowClickedListener = []func(TableView, int){} table.propertyChanged(tag) case TableRowSelectedEvent: table.rowSelectedListener = []func(TableView, int){} table.propertyChanged(tag) case Current: table.current.Row = -1 table.current.Column = -1 table.propertyChanged(tag) default: table.viewData.remove(tag) } } func (table *tableViewData) Set(tag string, value any) bool { return table.set(table.normalizeTag(tag), value) } func (table *tableViewData) set(tag string, value any) bool { if value == nil { table.remove(tag) return true } switch tag { case Content: switch val := value.(type) { case TableAdapter: table.properties[Content] = value case [][]any: table.properties[Content] = NewSimpleTableAdapter(val) case [][]string: table.properties[Content] = NewTextTableAdapter(val) default: notCompatibleType(tag, value) return false } case TableCellClickedEvent: listeners := table.valueToCellListeners(value) if listeners == nil { notCompatibleType(tag, value) return false } table.cellClickedListener = listeners case TableCellSelectedEvent: listeners := table.valueToCellListeners(value) if listeners == nil { notCompatibleType(tag, value) return false } table.cellSelectedListener = listeners case TableRowClickedEvent: listeners, ok := valueToEventListeners[TableView, int](value) if !ok { notCompatibleType(tag, value) return false } else if listeners == nil { listeners = []func(TableView, int){} } table.rowClickedListener = listeners case TableRowSelectedEvent: listeners, ok := valueToEventListeners[TableView, int](value) if !ok { notCompatibleType(tag, value) return false } else if listeners == nil { listeners = []func(TableView, int){} } table.rowSelectedListener = listeners case CellStyle: if style, ok := value.(TableCellStyle); ok { table.properties[tag] = style } else { notCompatibleType(tag, value) return false } case RowStyle: if !table.setRowStyle(value) { notCompatibleType(tag, value) return false } case ColumnStyle: if !table.setColumnStyle(value) { notCompatibleType(tag, value) return false } case HeadHeight, FootHeight: switch value := value.(type) { case string: if isConstantName(value) { table.properties[tag] = value } else if n, err := strconv.Atoi(value); err == nil { table.properties[tag] = n } else { ErrorLog(err.Error()) notCompatibleType(tag, value) return false } default: if n, ok := isInt(value); ok { table.properties[tag] = n } } case HeadStyle, FootStyle: switch value := value.(type) { case string: table.properties[tag] = value case Params: if len(value) > 0 { table.properties[tag] = value } else { delete(table.properties, tag) } case DataObject: params := Params{} for k := 0; k < value.PropertyCount(); k++ { if prop := value.Property(k); prop != nil && prop.Type() == TextNode { params[prop.Tag()] = prop.Text() } } if len(params) > 0 { table.properties[tag] = params } else { delete(table.properties, tag) } case DataNode: switch value.Type() { case ObjectNode: return table.set(tag, value.Object()) case TextNode: table.properties[tag] = value.Text() default: notCompatibleType(tag, value) return false } default: notCompatibleType(tag, value) return false } case CellPadding: if !table.setBounds(tag, value) { return false } case CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft: if !table.setBoundsSide(CellPadding, tag, value) { return false } case Gap: if !table.setSizeProperty(Gap, value) { return false } case SelectionMode, TableVerticalAlign, CellBorder, CellBorderStyle, CellBorderColor, CellBorderWidth, CellBorderLeft, CellBorderLeftStyle, CellBorderLeftColor, CellBorderLeftWidth, CellBorderRight, CellBorderRightStyle, CellBorderRightColor, CellBorderRightWidth, CellBorderTop, CellBorderTopStyle, CellBorderTopColor, CellBorderTopWidth, CellBorderBottom, CellBorderBottomStyle, CellBorderBottomColor, CellBorderBottomWidth: if !table.viewData.set(tag, value) { return false } case AllowSelection: switch value.(type) { case TableAllowCellSelection: table.properties[tag] = value case TableAllowRowSelection: table.properties[tag] = value default: notCompatibleType(tag, value) return false } case Current: switch value := value.(type) { case CellIndex: table.current = value case DataObject: if row, ok := dataIntProperty(value, "row"); ok { table.current.Row = row } if column, ok := dataIntProperty(value, "column"); ok { table.current.Column = column } case string: if strings.Contains(value, ",") { if values := strings.Split(value, ","); len(values) == 2 { var n = []int{0, 0} for i := 0; i < 2; i++ { var err error if n[i], err = strconv.Atoi(values[i]); err != nil { ErrorLog(err.Error()) return false } } table.current.Row = n[0] table.current.Column = n[1] } else { notCompatibleType(tag, value) return false } } else { n, err := strconv.Atoi(value) if err != nil { ErrorLog(err.Error()) return false } table.current.Row = n table.current.Column = -1 } default: if n, ok := isInt(value); ok { table.current.Row = n table.current.Column = -1 } else { notCompatibleType(tag, value) return false } } default: return table.viewData.set(tag, value) } table.propertyChanged(tag) return true } func (table *tableViewData) propertyChanged(tag string) { if table.created { switch tag { case Content, TableVerticalAlign, RowStyle, ColumnStyle, CellStyle, CellPadding, CellBorder, HeadHeight, HeadStyle, FootHeight, FootStyle, CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft, TableCellClickedEvent, TableCellSelectedEvent, TableRowClickedEvent, TableRowSelectedEvent, AllowSelection, AccentColor: table.ReloadTableData() case Current: switch GetTableSelectionMode(table) { case CellSelection: table.session.callFunc("setTableCellCursorByID", table.htmlID(), table.current.Row, table.current.Column) case RowSelection: table.session.callFunc("setTableRowCursorByID", table.htmlID(), table.current.Row) } case Gap: htmlID := table.htmlID() session := table.Session() gap, ok := sizeProperty(table, Gap, session) if !ok || gap.Type == Auto || gap.Value <= 0 { session.updateCSSProperty(htmlID, "border-spacing", "0") session.updateCSSProperty(htmlID, "border-collapse", "collapse") } else { session.updateCSSProperty(htmlID, "border-spacing", gap.cssString("0", session)) session.updateCSSProperty(htmlID, "border-collapse", "separate") } case SelectionMode: htmlID := table.htmlID() session := table.Session() switch GetTableSelectionMode(table) { case CellSelection: tabIndex, _ := intProperty(table, TabIndex, session, 0) session.updateProperty(htmlID, "tabindex", tabIndex) session.updateProperty(htmlID, "onfocus", "tableViewFocusEvent(this, event)") session.updateProperty(htmlID, "onblur", "tableViewBlurEvent(this, event)") session.updateProperty(htmlID, "data-selection", "cell") session.updateProperty(htmlID, "data-focusitemstyle", table.currentStyle()) session.updateProperty(htmlID, "data-bluritemstyle", table.currentInactiveStyle()) if table.current.Row >= 0 && table.current.Column >= 0 { session.updateProperty(htmlID, "data-current", table.cellID(table.current.Row, table.current.Column)) } else { session.removeProperty(htmlID, "data-current") } session.updateProperty(htmlID, "onkeydown", "tableViewCellKeyDownEvent(this, event)") case RowSelection: tabIndex, _ := intProperty(table, TabIndex, session, 0) session.updateProperty(htmlID, "tabindex", tabIndex) session.updateProperty(htmlID, "onfocus", "tableViewFocusEvent(this, event)") session.updateProperty(htmlID, "onblur", "tableViewBlurEvent(this, event)") session.updateProperty(htmlID, "data-selection", "row") session.updateProperty(htmlID, "data-focusitemstyle", table.currentStyle()) session.updateProperty(htmlID, "data-bluritemstyle", table.currentInactiveStyle()) if table.current.Row >= 0 { session.updateProperty(htmlID, "data-current", table.rowID(table.current.Row)) } else { session.removeProperty(htmlID, "data-current") } session.updateProperty(htmlID, "onkeydown", "tableViewRowKeyDownEvent(this, event)") default: // NoneSelection if tabIndex, ok := intProperty(table, TabIndex, session, -1); !ok || tabIndex < 0 { session.removeProperty(htmlID, "tabindex") } for _, prop := range []string{"data-current", "onfocus", "onblur", "onkeydown", "data-selection"} { session.removeProperty(htmlID, prop) } } updateInnerHTML(htmlID, session) } } table.propertyChangedEvent(tag) } func (table *tableViewData) currentStyle() string { if value := table.getRaw(CurrentStyle); value != nil { if style, ok := value.(string); ok { if style, ok = table.session.resolveConstants(style); ok { return style } } } if value := valueFromStyle(table, CurrentStyle); value != nil { if style, ok := value.(string); ok { if style, ok = table.session.resolveConstants(style); ok { return style } } } return "ruiCurrentTableCellFocused" } func (table *tableViewData) currentInactiveStyle() string { if value := table.getRaw(CurrentInactiveStyle); value != nil { if style, ok := value.(string); ok { if style, ok = table.session.resolveConstants(style); ok { return style } } } if value := valueFromStyle(table, CurrentInactiveStyle); value != nil { if style, ok := value.(string); ok { if style, ok = table.session.resolveConstants(style); ok { return style } } } return "ruiCurrentTableCell" } func (table *tableViewData) valueToCellListeners(value any) []func(TableView, int, int) { if value == nil { return []func(TableView, int, int){} } switch value := value.(type) { case func(TableView, int, int): return []func(TableView, int, int){value} case func(int, int): fn := func(_ TableView, row, column int) { value(row, column) } return []func(TableView, int, int){fn} case []func(TableView, int, int): return value case []func(int, int): listeners := make([]func(TableView, int, int), len(value)) for i, val := range value { if val == nil { return nil } listeners[i] = func(_ TableView, row, column int) { val(row, column) } } return listeners case []any: listeners := make([]func(TableView, int, int), len(value)) for i, val := range value { if val == nil { return nil } switch val := val.(type) { case func(TableView, int, int): listeners[i] = val case func(int, int): listeners[i] = func(_ TableView, row, column int) { val(row, column) } default: return nil } } return listeners } return nil } func (table *tableViewData) htmlTag() string { return "table" } func (table *tableViewData) rowID(index int) string { return fmt.Sprintf("%s-%d", table.htmlID(), index) } func (table *tableViewData) cellID(row, column int) string { return fmt.Sprintf("%s-%d-%d", table.htmlID(), row, column) } func (table *tableViewData) htmlProperties(self View, buffer *strings.Builder) { if content := table.content(); content != nil { buffer.WriteString(` data-rows="`) buffer.WriteString(strconv.Itoa(content.RowCount())) buffer.WriteString(`" data-columns="`) buffer.WriteString(strconv.Itoa(content.ColumnCount())) buffer.WriteRune('"') } if selectionMode := GetTableSelectionMode(table); selectionMode != NoneSelection { buffer.WriteString(` onfocus="tableViewFocusEvent(this, event)" onblur="tableViewBlurEvent(this, event)" data-focusitemstyle="`) buffer.WriteString(table.currentStyle()) buffer.WriteString(`" data-bluritemstyle="`) buffer.WriteString(table.currentInactiveStyle()) buffer.WriteRune('"') switch selectionMode { case RowSelection: buffer.WriteString(` data-selection="row" onkeydown="tableViewRowKeyDownEvent(this, event)"`) if table.current.Row >= 0 { buffer.WriteString(` data-current="`) buffer.WriteString(table.rowID(table.current.Row)) buffer.WriteRune('"') } case CellSelection: buffer.WriteString(` data-selection="cell" onkeydown="tableViewCellKeyDownEvent(this, event)"`) if table.current.Row >= 0 && table.current.Column >= 0 { buffer.WriteString(` data-current="`) buffer.WriteString(table.cellID(table.current.Row, table.current.Column)) buffer.WriteRune('"') } } } table.viewData.htmlProperties(self, buffer) } func (table *tableViewData) content() TableAdapter { if content := table.getRaw(Content); content != nil { if adapter, ok := content.(TableAdapter); ok { return adapter } } return nil } func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) { table.cellViews = []View{} table.cellFrame = []Frame{} adapter := table.content() if adapter == nil { return } rowCount := adapter.RowCount() columnCount := adapter.ColumnCount() if rowCount == 0 || columnCount == 0 { return } table.cellFrame = make([]Frame, rowCount*columnCount) rowStyle := table.getRowStyle() cellStyle := table.getCellStyle() session := table.Session() if !session.ignoreViewUpdates() { session.setIgnoreViewUpdates(true) defer session.setIgnoreViewUpdates(false) } cssBuilder := viewCSSBuilder{buffer: allocStringBuilder()} defer freeStringBuilder(cssBuilder.buffer) var view tableCellView view.init(session) ignoreCells := []struct{ row, column int }{} selectionMode := GetTableSelectionMode(table) var allowCellSelection TableAllowCellSelection = nil if allow, ok := adapter.(TableAllowCellSelection); ok { allowCellSelection = allow } if value := table.getRaw(AllowSelection); value != nil { if style, ok := value.(TableAllowCellSelection); ok { allowCellSelection = style } } var allowRowSelection TableAllowRowSelection = nil if allow, ok := adapter.(TableAllowRowSelection); ok { allowRowSelection = allow } if value := table.getRaw(AllowSelection); value != nil { if style, ok := value.(TableAllowRowSelection); ok { allowRowSelection = style } } 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) { //var namedColors []NamedColor = nil for row := startRow; row < endRow; row++ { cssBuilder.buffer.Reset() if rowStyle != nil { if styles := rowStyle.RowStyle(row); styles != nil { view.Clear() for tag, value := range styles { view.Set(tag, value) } view.cssStyle(&view, &cssBuilder) } } buffer.WriteString(`