-
Notifications
You must be signed in to change notification settings - Fork 34
Add usage documentation for Table cell styling and headers #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
322adc5
5502b35
5473986
e97fa2f
173e285
47dc65e
a35287e
b489499
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -71,6 +71,8 @@ | |
| docs: | ||
| - list | ||
| - table | ||
| - table-styling | ||
| - table-headers | ||
| - tree | ||
|
|
||
| - title: Data Binding | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,218 @@ | ||
| --- | ||
| title: Table Headers | ||
|
|
||
| redirect_from: | ||
| - /tour/widget/table | ||
| - /widget/table | ||
| --- | ||
|
|
||
| The `Table` collection widget supports a header row and/or column | ||
| which can be formatted to stand out from the data | ||
|
|
||
| Although these samples don't show it, headers can be more complex objects which could trigger table-specific operations like sorting or filtering. | ||
|
|
||
| There is specific support for a "sticky" header, which remains at the top or left of the | ||
| container while the data scrolls. This support uses additional callbacks, `CreateHeader` and `UpdateHeader`. | ||
|
|
||
| ```go | ||
| package main | ||
|
|
||
| import ( | ||
| "fmt" | ||
|
|
||
| "fyne.io/fyne/v2" | ||
| "fyne.io/fyne/v2/app" | ||
| "fyne.io/fyne/v2/widget" | ||
| ) | ||
|
|
||
| var dataTemplateText = "0123456789012345" | ||
| var sampData = []string{ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not a great idea to use 1-dimensional data for a demonstration of a 2-dimensional output.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The sample renders different data in each column and the pattern doesn't repeat till 5 rows later. It doesn't look like 1-dimensional data.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree the data here is not the point - but docs have to consider every single line will be used as best practice - and 1D data is not best practice for a table and could lead to support requests.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To clarify, are you concerned about line 25, |
||
| "Lorem ipsum dolo", | ||
| "consectetur adip", | ||
| "sed do eiusmod t", | ||
| "Ut enim ad minim", | ||
| "quis nostrud exe", | ||
| } | ||
|
|
||
| var tableCols = 3 | ||
|
|
||
| func makeTableComponents() *widget.Table { | ||
|
|
||
| table := widget.NewTable( | ||
| // length: return #rows, #cols | ||
| func() (int, int) { | ||
| return 15, tableCols | ||
| }, | ||
|
|
||
| // create cell template | ||
| func() fyne.CanvasObject { | ||
| tpl := widget.NewLabel(dataTemplateText) | ||
| return tpl | ||
| }, | ||
|
|
||
| // update cell | ||
| func(id widget.TableCellID, cellTpl fyne.CanvasObject) { | ||
| cell := cellTpl.(*widget.Label) | ||
|
|
||
| cell.Text = sampData[(tableCols*id.Row+id.Col)%len(sampData)] | ||
|
|
||
| cell.Refresh() // refresh needed after styling changes | ||
| }, | ||
| ) | ||
|
|
||
| // Enable frozen row and/or column headers | ||
| table.ShowHeaderRow = true | ||
| table.ShowHeaderColumn = true | ||
|
|
||
| table.CreateHeader = func() fyne.CanvasObject { | ||
| return widget.NewLabelWithStyle("row xx header", | ||
| fyne.TextAlignCenter, | ||
| fyne.TextStyle{Bold: true, Underline: true}) | ||
| } | ||
|
|
||
| table.UpdateHeader = func(id widget.TableCellID, template fyne.CanvasObject) { | ||
| cell := template.(*widget.Label) | ||
|
|
||
| if id.Row < 0 && id.Col < 0 { | ||
| // even if table has both Header rows and columns, | ||
| // the top left header cell {-1, -1} is never populated | ||
| cell.SetText("This space intentionally left blank") | ||
| } else if id.Row < 0 { // {row: -1, col: x} Set column header for col x | ||
| cell.SetText(fmt.Sprintf("-- Col %d Header --", id.Col)) | ||
| } else if id.Col < 0 { // {row: x, col: -1} Set row header for row x | ||
| cell.SetText(fmt.Sprintf("Row %d Header", id.Row)) | ||
| } | ||
| } | ||
|
|
||
| return table | ||
| } | ||
| func main() { | ||
| a := app.NewWithID("com.example.sample.table_headers") | ||
| w := a.NewWindow("table_headers") | ||
|
|
||
| table := makeTableComponents() | ||
|
|
||
| w.SetContent(table) | ||
| w.Resize(fyne.NewSize(430, 300)) | ||
| w.ShowAndRun() | ||
| } | ||
| ``` | ||
|
|
||
| This renders like so: | ||
|
|
||
|  | ||
|
|
||
| ## scrolling headers | ||
andydotxyz marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| It is possible to render a table with multiple rows or columns of headers, | ||
| or whose headers scroll with the data, | ||
| or whose upper left corner contains a legend or other content. | ||
|
|
||
| To do this, the Table widget is "tricked" into invoking the standard `CreateCell` and `UpdateCell` callbacks with additional index values | ||
| which are rendered as header content rather than data. | ||
|
|
||
| ```go | ||
| package main | ||
|
|
||
| import ( | ||
| "fmt" | ||
|
|
||
| "fyne.io/fyne/v2" | ||
| "fyne.io/fyne/v2/app" | ||
| "fyne.io/fyne/v2/widget" | ||
| ) | ||
|
|
||
| var sampData = []string{ | ||
| "Lorem ipsum dolo", | ||
| "consectetur adip", | ||
| "sed do eiusmod t", | ||
| "Ut enim ad minim", | ||
| "quis nostrud exe", | ||
| } | ||
|
|
||
| var dataCols = 3 | ||
|
|
||
| // to disable row or column header, set to 0 | ||
| var numHeaderRows = 1 | ||
| var numHeaderCols = 1 | ||
|
|
||
| var headerTemplate = widget.NewLabelWithStyle("row xx header", | ||
| fyne.TextAlignCenter, | ||
| fyne.TextStyle{Bold: true, Underline: true}) | ||
|
|
||
| // example of rendering headers within standard cell callbacks | ||
| func makeTableComponents() *widget.Table { | ||
|
|
||
| table := widget.NewTable( | ||
|
|
||
| // length: return #rows, #cols | ||
| func() (int, int) { | ||
| return 15 + numHeaderRows, // extra rows and columns for headers | ||
| dataCols + numHeaderCols | ||
| }, | ||
|
|
||
| // create cell template | ||
| func() fyne.CanvasObject { | ||
| return widget.NewLabel("0123456789012345") | ||
| }, | ||
|
|
||
| // update cell | ||
| func(id widget.TableCellID, cellTpl fyne.CanvasObject) { | ||
| cell := cellTpl.(*widget.Label) | ||
| // dataIndex is the row and column in the *data array* of the referenced cell. | ||
| dataIndex := widget.TableCellID{Row: id.Row - numHeaderRows, Col: id.Col - numHeaderCols} | ||
| if id.Row >= numHeaderRows && id.Col >= numHeaderCols { | ||
|
|
||
| // index in the data range, render data cell | ||
| cell.Alignment = fyne.TextAlignLeading | ||
| cell.TextStyle = fyne.TextStyle{} | ||
| cell.Text = sampData[(dataCols*(dataIndex.Row)+(dataIndex.Col))%len(sampData)] | ||
| } else { | ||
| // index is in header range | ||
| cell.Alignment = headerTemplate.Alignment | ||
| cell.TextStyle = headerTemplate.TextStyle | ||
|
|
||
| if id.Row < numHeaderRows && id.Col < numHeaderCols { | ||
| cell.Wrapping = fyne.TextWrapWord | ||
| cell.Text = "Table discription" | ||
| // content for upper left corner of table | ||
| } else if id.Row < numHeaderRows { | ||
| // content for the header row(s) | ||
| cell.Text = fmt.Sprintf("-- Col %d Header --", dataIndex.Col) | ||
| } else { // assert id.Col < numHeaderCols | ||
| // content for header col(s) | ||
| cell.Text = fmt.Sprintf("Row %d Header", dataIndex.Row) | ||
| } | ||
| } | ||
|
|
||
| cell.Refresh() // refresh needed after styling changes | ||
| }, | ||
| ) | ||
|
|
||
| // Enable frozen row and/or column headers, set to 1 | ||
| table.StickyRowCount = 0 | ||
| table.StickyColumnCount = 0 | ||
|
|
||
| return table | ||
| } | ||
| func main() { | ||
| a := app.NewWithID("com.example.sample.table_headers") | ||
| w := a.NewWindow("table_headers") | ||
|
|
||
| table := makeTableComponents() | ||
|
|
||
| w.SetContent(table) | ||
| w.Resize(fyne.NewSize(430, 300)) | ||
| w.ShowAndRun() | ||
| } | ||
|
|
||
| ``` | ||
|
|
||
| Which renders as: | ||
|
|
||
|  | ||
|
|
||
|
|
||
|
|
||
| Refer to [`widget.Table`](/api/v2.6/widget/table.html) API for additional details on how to implement headers. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| --- | ||
| title: Table Styling | ||
|
|
||
| redirect_from: | ||
| - /tour/widget/table | ||
| - /widget/table | ||
| --- | ||
|
|
||
| The `Table` collection widget allows customization of the appearance | ||
| of individual cells or whole rows or columns of your table. | ||
|
|
||
| Size, color, alignment or bolding can be changed to achieve such effects as: | ||
|
|
||
| * negative numbers in red | ||
| * numeric columns right adjusted | ||
| * different column widths for different columns, or different heights for different rows | ||
|
|
||
|
|
||
| ## styling cells | ||
|
|
||
| The kinds of styling choices available depend on the `fyne.CanvasObject` returned from the `CreateCell` callback. The most common template is a `widget.Label`, but a `widget.RichText` or `container.Stack` containing multiple widgets can be used. | ||
|
|
||
| To achieve the desired effect, styling attributes are applied to each cell in | ||
| the `UpdateCell` callback based on the content or row and column position it has in the table. You might set an individual cell containing a negative number to display in red, or set all the cells in a given column to display centered, for example. | ||
|
|
||
| Note that most widgets have transparent background. To display a cell with a particular background color, the template can be a `container.Stack` containing a `canvas.Rectangle` for the background and a `widget.Label` for the foreground. | ||
|
|
||
| When changing an objects's styling attributes in the `UpdateCell` callback: | ||
|
|
||
| * call the object's `Refresh` method. | ||
| However, an explicit `Refresh` is not necessary if the only attribute changes are made via setter methods, such as `label.SetText()`. | ||
| * set a given styling attribute on *all* code paths through the callback. | ||
| The `fyne.CanvasObject` passed in to `UpdateCell` is not guaranteed to be the one initialized in `CreateCell` nor the one last displayed in that row and column; | ||
| its attribute settings on entry are unpredictable. | ||
|
|
||
| ## sizing rows and columns | ||
|
|
||
| The default column width and row height for the `Table` are determined by the `MinWidth` of the template object returned from `CreateCell` callback. `MinWidth` cannot be set directly, but is influenced by content of the template object. So, for example a template like | ||
| `widget.NewLabel("123456789012345\nfoo")` | ||
| would render a table with columns that are 15 characters wide by 2 lines high. | ||
|
|
||
| The size of a particular column or row can be changed by: | ||
| `func (t *Table) SetColumnWidth(id int, width float32)` and | ||
| `func (t *Table) SetRowHeight(id int, height float32)`, respectively. | ||
|
|
||
| ## sample table styling | ||
|
|
||
| ```go | ||
| package main | ||
|
|
||
| import ( | ||
| "fmt" | ||
|
|
||
| "fyne.io/fyne/v2" | ||
| "fyne.io/fyne/v2/app" | ||
| "fyne.io/fyne/v2/widget" | ||
| ) | ||
|
|
||
| func makeTableComponents() *widget.Table { | ||
|
|
||
| table := widget.NewTable( | ||
| // length: return #rows, #cols | ||
| func() (int, int) { | ||
| return 15, 3 | ||
| }, | ||
|
|
||
| // create cell template | ||
| func() fyne.CanvasObject { | ||
| return widget.NewLabel("template text") | ||
| }, | ||
|
|
||
| // update cell | ||
| func(id widget.TableCellID, cellTpl fyne.CanvasObject) { | ||
| cell := cellTpl.(*widget.Label) | ||
|
|
||
| // style each column differently | ||
| switch id.Col { | ||
| case 0: | ||
| cell.SetText(fmt.Sprintf( | ||
| "row %d, wider, left aligned", id.Row)) | ||
| cell.Alignment = fyne.TextAlignLeading | ||
| case 1: | ||
| cell.SetText("centered") | ||
| cell.Alignment = fyne.TextAlignCenter | ||
| case 2: | ||
| cell.SetText("right aligned") | ||
| cell.Alignment = fyne.TextAlignTrailing | ||
| } | ||
|
|
||
| // style alternating rows differently | ||
| if id.Row%2 == 1 { | ||
| cell.TextStyle = fyne.TextStyle{Bold: true} | ||
| cell.Importance = widget.DangerImportance | ||
| } else { | ||
| cell.TextStyle = fyne.TextStyle{} | ||
| cell.Importance = widget.MediumImportance | ||
| } | ||
|
|
||
| cell.Refresh() // refresh needed after styling changes | ||
| }, | ||
| ) | ||
|
|
||
| // set per-column widths as needed | ||
| table.SetColumnWidth(0, 190) | ||
|
|
||
| return table | ||
| } | ||
| func main() { | ||
| a := app.NewWithID("com.example.sample.table_style") | ||
| w := a.NewWindow("table_style") | ||
|
|
||
| table := makeTableComponents() | ||
|
|
||
| w.SetContent(table) | ||
| w.Resize(fyne.NewSize(430, 300)) | ||
| w.ShowAndRun() | ||
| } | ||
|
|
||
| ``` | ||
|
|
||
|
|
||
| Next, we'll discuss setting [table headers](table-headers). |
Uh oh!
There was an error while loading. Please reload this page.