rui_orig/checkbox.go

326 lines
9.7 KiB
Go

package rui
import (
"strings"
)
// CheckboxChangedEvent is the constant for "checkbox-event" property tag.
//
// Used by `Checkbox`.
// Event occurs when the checkbox becomes checked/unchecked.
//
// General listener format:
// `func(checkbox rui.Checkbox, checked bool)`.
//
// where:
// checkbox - Interface of a checkbox which generated this event,
// checked - Checkbox state.
//
// Allowed listener formats:
// `func(checkbox rui.Checkbox)`,
// `func(checked bool)`,
// `func()`.
const CheckboxChangedEvent PropertyName = "checkbox-event"
// Checkbox represent a Checkbox view
type Checkbox interface {
ViewsContainer
}
type checkboxData struct {
viewsContainerData
}
// NewCheckbox create new Checkbox object and return it
func NewCheckbox(session Session, params Params) Checkbox {
view := new(checkboxData)
view.init(session)
setInitParams(view, params)
return view
}
func newCheckbox(session Session) View {
return new(checkboxData)
}
func (button *checkboxData) init(session Session) {
button.viewsContainerData.init(session)
button.tag = "Checkbox"
button.systemClass = "ruiGridLayout ruiCheckbox"
button.set = button.setFunc
button.remove = button.removeFunc
button.changed = button.propertyChanged
button.setRaw(ClickEvent, []func(View, MouseEvent){checkboxClickListener})
button.setRaw(KeyDownEvent, []func(View, KeyEvent){checkboxKeyListener})
}
func (button *checkboxData) Focusable() bool {
return true
}
func (button *checkboxData) propertyChanged(tag PropertyName) {
switch tag {
case Checked:
session := button.Session()
checked := IsCheckboxChecked(button)
if listeners := GetCheckboxChangedListeners(button); len(listeners) > 0 {
for _, listener := range listeners {
listener(button, checked)
}
}
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
checkboxHtml(button, buffer, checked)
session.updateInnerHTML(button.htmlID()+"checkbox", buffer.String())
case CheckboxHorizontalAlign, CheckboxVerticalAlign:
htmlID := button.htmlID()
session := button.Session()
updateCSSStyle(htmlID, session)
updateInnerHTML(htmlID, session)
case VerticalAlign:
button.Session().updateCSSProperty(button.htmlID()+"content", "align-items", checkboxVerticalAlignCSS(button))
case HorizontalAlign:
button.Session().updateCSSProperty(button.htmlID()+"content", "justify-items", checkboxHorizontalAlignCSS(button))
case AccentColor:
updateInnerHTML(button.htmlID(), button.Session())
default:
button.viewsContainerData.propertyChanged(tag)
}
}
func (button *checkboxData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag {
case ClickEvent:
if listeners, ok := valueToOneArgEventListeners[View, MouseEvent](value); ok && listeners != nil {
listeners = append(listeners, checkboxClickListener)
button.setRaw(tag, listeners)
return []PropertyName{tag}
}
return nil
case KeyDownEvent:
if listeners, ok := valueToOneArgEventListeners[View, KeyEvent](value); ok && listeners != nil {
listeners = append(listeners, checkboxKeyListener)
button.setRaw(tag, listeners)
return []PropertyName{tag}
}
return nil
case CheckboxChangedEvent:
return setOneArgEventListener[Checkbox, bool](button, tag, value)
case Checked:
return setBoolProperty(button, Checked, value)
case CellVerticalAlign, CellHorizontalAlign, CellWidth, CellHeight:
ErrorLogF(`"%s" property is not compatible with the BoundsProperty`, string(tag))
return nil
}
return button.viewsContainerData.setFunc(tag, value)
}
func (button *checkboxData) removeFunc(tag PropertyName) []PropertyName {
switch tag {
case ClickEvent:
button.setRaw(ClickEvent, []func(View, MouseEvent){checkboxClickListener})
return []PropertyName{ClickEvent}
case KeyDownEvent:
button.setRaw(KeyDownEvent, []func(View, KeyEvent){checkboxKeyListener})
return []PropertyName{ClickEvent}
}
return button.viewsContainerData.removeFunc(tag)
}
/*
func (button *checkboxData) changedCheckboxState(state bool) {
for _, listener := range GetCheckboxChangedListeners(button) {
listener(button, state)
}
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
button.htmlCheckbox(buffer, state)
button.Session().updateInnerHTML(button.htmlID()+"checkbox", buffer.String())
}
*/
func checkboxClickListener(view View, _ MouseEvent) {
view.Set(Checked, !IsCheckboxChecked(view))
BlurView(view)
}
func checkboxKeyListener(view View, event KeyEvent) {
switch event.Code {
case "Enter", "Space":
view.Set(Checked, !IsCheckboxChecked(view))
}
}
func (button *checkboxData) cssStyle(self View, builder cssBuilder) {
session := button.Session()
vAlign := GetCheckboxVerticalAlign(button)
hAlign := GetCheckboxHorizontalAlign(button)
switch hAlign {
case CenterAlign:
if vAlign == BottomAlign {
builder.add("grid-template-rows", "1fr auto")
} else {
builder.add("grid-template-rows", "auto 1fr")
}
case RightAlign:
builder.add("grid-template-columns", "1fr auto")
default:
builder.add("grid-template-columns", "auto 1fr")
}
if gap, ok := sizeConstant(session, "ruiCheckboxGap"); ok && gap.Type != Auto && gap.Value > 0 {
builder.add("gap", gap.cssString("0", session))
}
builder.add("align-items", "stretch")
builder.add("justify-items", "stretch")
button.viewsContainerData.cssStyle(self, builder)
}
func checkboxHtml(button View, buffer *strings.Builder, checked bool) (int, int) {
//func (button *checkboxData) htmlCheckbox(buffer *strings.Builder, checked bool) (int, int) {
vAlign := GetCheckboxVerticalAlign(button)
hAlign := GetCheckboxHorizontalAlign(button)
buffer.WriteString(`<div id="`)
buffer.WriteString(button.htmlID())
buffer.WriteString(`checkbox" style="display: grid;`)
if hAlign == CenterAlign {
buffer.WriteString(" justify-items: center; grid-column-start: 1; grid-column-end: 2;")
if vAlign == BottomAlign {
buffer.WriteString(" grid-row-start: 2; grid-row-end: 3;")
} else {
buffer.WriteString(" grid-row-start: 1; grid-row-end: 2;")
}
} else {
if hAlign == RightAlign {
buffer.WriteString(" grid-column-start: 2; grid-column-end: 3;")
} else {
buffer.WriteString(" grid-column-start: 1; grid-column-end: 2;")
}
buffer.WriteString(" grid-row-start: 1; grid-row-end: 2;")
switch vAlign {
case BottomAlign:
buffer.WriteString(" align-items: end;")
case CenterAlign:
buffer.WriteString(" align-items: center;")
default:
buffer.WriteString(" align-items: start;")
}
}
buffer.WriteString(`">`)
accentColor := Color(0)
if color := GetAccentColor(button, ""); color != 0 {
accentColor = color
}
if checked {
buffer.WriteString(button.Session().checkboxOnImage(accentColor))
} else {
buffer.WriteString(button.Session().checkboxOffImage(accentColor))
}
buffer.WriteString(`</div>`)
return vAlign, hAlign
}
func (button *checkboxData) htmlSubviews(self View, buffer *strings.Builder) {
vCheckboxAlign, hCheckboxAlign := checkboxHtml(button, buffer, IsCheckboxChecked(button))
buffer.WriteString(`<div id="`)
buffer.WriteString(button.htmlID())
buffer.WriteString(`content" style="display: grid;`)
if hCheckboxAlign == LeftAlign {
buffer.WriteString(" grid-column-start: 2; grid-column-end: 3;")
} else {
buffer.WriteString(" grid-column-start: 1; grid-column-end: 2;")
}
if hCheckboxAlign == CenterAlign && vCheckboxAlign != BottomAlign {
buffer.WriteString(" grid-row-start: 2; grid-row-end: 3;")
} else {
buffer.WriteString(" grid-row-start: 1; grid-row-end: 2;")
}
buffer.WriteString(" align-items: ")
buffer.WriteString(checkboxVerticalAlignCSS(button))
buffer.WriteRune(';')
buffer.WriteString(" justify-items: ")
buffer.WriteString(checkboxHorizontalAlignCSS(button))
buffer.WriteRune(';')
buffer.WriteString(`">`)
button.viewsContainerData.htmlSubviews(self, buffer)
buffer.WriteString(`</div>`)
}
func checkboxHorizontalAlignCSS(view View) string {
align := GetHorizontalAlign(view)
values := enumProperties[CellHorizontalAlign].cssValues
if align >= 0 && align < len(values) {
return values[align]
}
return values[0]
}
func checkboxVerticalAlignCSS(view View) string {
align := GetVerticalAlign(view)
values := enumProperties[CellVerticalAlign].cssValues
if align >= 0 && align < len(values) {
return values[align]
}
return values[0]
}
// IsCheckboxChecked returns true if the Checkbox is checked, false otherwise.
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func IsCheckboxChecked(view View, subviewID ...string) bool {
return boolStyledProperty(view, subviewID, Checked, false)
}
// GetCheckboxVerticalAlign return the vertical align of a Checkbox subview: TopAlign (0), BottomAlign (1), CenterAlign (2)
// If the second argument (subviewID) is not specified or it is "" then a left position of the first argument (view) is returned
func GetCheckboxVerticalAlign(view View, subviewID ...string) int {
return enumStyledProperty(view, subviewID, CheckboxVerticalAlign, LeftAlign, false)
}
// GetCheckboxHorizontalAlign return the vertical align of a Checkbox subview: LeftAlign (0), RightAlign (1), CenterAlign (2)
// If the second argument (subviewID) is not specified or it is "" then a left position of the first argument (view) is returned
func GetCheckboxHorizontalAlign(view View, subviewID ...string) int {
return enumStyledProperty(view, subviewID, CheckboxHorizontalAlign, TopAlign, false)
}
// GetCheckboxChangedListeners returns the CheckboxChangedListener list of an Checkbox subview.
// If there are no listeners then the empty list is returned
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetCheckboxChangedListeners(view View, subviewID ...string) []func(Checkbox, bool) {
return getOneArgEventListeners[Checkbox, bool](view, subviewID, CheckboxChangedEvent)
}