diff --git a/propertyNames.go b/propertyNames.go index 78b492a..553b865 100644 --- a/propertyNames.go +++ b/propertyNames.go @@ -690,4 +690,12 @@ const ( // "color-dodge" (6), "color-burn" (7), "hard-light" (8), "soft-light" (9), "difference" (10), // "exclusion" (11), "hue" (12), "saturation" (13), "color" (14), "luminosity" (15). MixBlendMode = "mix-blend-mode" + + // TabIndex is the constant for the "tabindex" property tag. + // The "tabindex" int property indicates that View can be focused, and where it participates in sequential keyboard navigation + // (usually with the Tab key, hence the name). + // * A negative value means that View is not reachable via sequential keyboard navigation, but could be focused by clicking with the mouse or touching. + // * tabindex="0" means that View should be focusable in sequential keyboard navigation, after any positive tabindex values and its order is defined in order of its addition. + // * A positive value means View should be focusable in sequential keyboard navigation, with its order defined by the value of the number. + TabIndex = "tabindex" ) diff --git a/propertySet.go b/propertySet.go index 8d8a775..810a368 100644 --- a/propertySet.go +++ b/propertySet.go @@ -74,6 +74,7 @@ var intProperties = []string{ ColumnSpan, ColumnCount, Order, + TabIndex, } var floatProperties = map[string]struct{ min, max float64 }{ diff --git a/tableView.go b/tableView.go index db618d4..94a3871 100644 --- a/tableView.go +++ b/tableView.go @@ -606,7 +606,8 @@ func (table *tableViewData) propertyChanged(tag string) { switch GetTableSelectionMode(table) { case CellSelection: - session.updateProperty(htmlID, "tabindex", "0") + 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") @@ -621,7 +622,8 @@ func (table *tableViewData) propertyChanged(tag string) { session.updateProperty(htmlID, "onkeydown", "tableViewCellKeyDownEvent(this, event)") case RowSelection: - session.updateProperty(htmlID, "tabindex", "0") + 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") @@ -636,7 +638,11 @@ func (table *tableViewData) propertyChanged(tag string) { session.updateProperty(htmlID, "onkeydown", "tableViewRowKeyDownEvent(this, event)") default: // NoneSelection - for _, prop := range []string{"tabindex", "data-current", "onfocus", "onblur", "onkeydown", "data-selection"} { + 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) } } diff --git a/view.go b/view.go index f7a62eb..8a251fe 100644 --- a/view.go +++ b/view.go @@ -187,6 +187,14 @@ func (view *viewData) remove(tag string) { case ID: view.viewID = "" + case TabIndex, "tab-index": + delete(view.properties, tag) + if view.Focusable() { + view.session.updateProperty(view.htmlID(), "tabindex", "0") + } else { + view.session.updateProperty(view.htmlID(), "tabindex", "-1") + } + case UserData: delete(view.properties, tag) @@ -312,6 +320,18 @@ func (view *viewData) set(tag string, value any) bool { } view.viewID = text + case TabIndex, "tab-index": + if !view.setIntProperty(tag, value) { + return false + } + if value, ok := intProperty(view, TabIndex, view.Session(), 0); ok { + view.session.updateProperty(view.htmlID(), "tabindex", strconv.Itoa(value)) + } else if view.Focusable() { + view.session.updateProperty(view.htmlID(), "tabindex", "0") + } else { + view.session.updateProperty(view.htmlID(), "tabindex", "-1") + } + case UserData: view.properties[tag] = value @@ -758,8 +778,14 @@ func viewHTML(view View, buffer *strings.Builder) { buffer.WriteRune(' ') } - if view.Focusable() && !disabled { - buffer.WriteString(`tabindex="0" `) + if !disabled { + if value, ok := intProperty(view, TabIndex, view.Session(), -1); ok { + buffer.WriteString(`tabindex="`) + buffer.WriteString(strconv.Itoa(value)) + buffer.WriteString(`" `) + } else if view.Focusable() { + buffer.WriteString(`tabindex="0" `) + } } buffer.WriteString(`onscroll="scrollEvent(this, event)" `) diff --git a/viewUtils.go b/viewUtils.go index 335274e..fd8bb48 100644 --- a/viewUtils.go +++ b/viewUtils.go @@ -153,6 +153,25 @@ func GetOverflow(view View, subviewID ...string) int { return enumStyledProperty(view, subviewID, Overflow, defaultOverflow, false) } +// GetTabIndex returns the subview tab-index. +// If the second argument (subviewID) is not specified or it is "" then a tab-index of the first argument (view) is returned +func GetTabIndex(view View, subviewID ...string) int { + if len(subviewID) > 0 && subviewID[0] != "" { + view = ViewByID(view, subviewID[0]) + } + + defaultValue := -1 + if view != nil { + if view.Focusable() { + defaultValue = 0 + } + if value, ok := intProperty(view, TabIndex, view.Session(), defaultValue); ok { + return value + } + } + return defaultValue +} + // GetZIndex returns the subview z-order. // If the second argument (subviewID) is not specified or it is "" then a z-order of the first argument (view) is returned func GetZIndex(view View, subviewID ...string) int {