forked from mbk-lab/rui_orig
375 lines
9.7 KiB
Go
375 lines
9.7 KiB
Go
|
package rui
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// CheckboxChangedEvent is the constant for "checkbox-event" property tag.
|
||
|
// The "checkbox-event" event occurs when the checkbox becomes checked/unchecked.
|
||
|
// The main listener format: func(Checkbox, bool), where the second argument is the checkbox state.
|
||
|
const CheckboxChangedEvent = "checkbox-event"
|
||
|
|
||
|
// Checkbox - checkbox view
|
||
|
type Checkbox interface {
|
||
|
ViewsContainer
|
||
|
}
|
||
|
|
||
|
type checkboxData struct {
|
||
|
viewsContainerData
|
||
|
checkedListeners []func(Checkbox, bool)
|
||
|
}
|
||
|
|
||
|
// NewCheckbox create new Checkbox object and return it
|
||
|
func NewCheckbox(session Session, params Params) Checkbox {
|
||
|
view := new(checkboxData)
|
||
|
view.Init(session)
|
||
|
setInitParams(view, Params{
|
||
|
ClickEvent: checkboxClickListener,
|
||
|
KeyDownEvent: checkboxKeyListener,
|
||
|
})
|
||
|
setInitParams(view, params)
|
||
|
return view
|
||
|
}
|
||
|
|
||
|
func newCheckbox(session Session) View {
|
||
|
return NewCheckbox(session, nil)
|
||
|
}
|
||
|
|
||
|
func (button *checkboxData) Init(session Session) {
|
||
|
button.viewsContainerData.Init(session)
|
||
|
button.tag = "Checkbox"
|
||
|
button.systemClass = "ruiGridLayout ruiCheckbox"
|
||
|
button.checkedListeners = []func(Checkbox, bool){}
|
||
|
}
|
||
|
|
||
|
func (button *checkboxData) Focusable() bool {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func (button *checkboxData) Get(tag string) interface{} {
|
||
|
switch strings.ToLower(tag) {
|
||
|
case CheckboxChangedEvent:
|
||
|
return button.checkedListeners
|
||
|
}
|
||
|
|
||
|
return button.viewsContainerData.Get(tag)
|
||
|
}
|
||
|
|
||
|
func (button *checkboxData) Set(tag string, value interface{}) bool {
|
||
|
switch strings.ToLower(tag) {
|
||
|
case CheckboxChangedEvent:
|
||
|
ok := button.setChangedListener(value)
|
||
|
if !ok {
|
||
|
notCompatibleType(tag, value)
|
||
|
}
|
||
|
return ok
|
||
|
|
||
|
case Checked:
|
||
|
oldChecked := button.checked()
|
||
|
if !button.setBoolProperty(Checked, value) {
|
||
|
return false
|
||
|
}
|
||
|
if button.created {
|
||
|
checked := button.checked()
|
||
|
if checked != oldChecked {
|
||
|
button.changedCheckboxState(checked)
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
|
||
|
case CheckboxHorizontalAlign, CheckboxVerticalAlign:
|
||
|
if button.setEnumProperty(tag, value, enumProperties[tag].values) {
|
||
|
if button.created {
|
||
|
htmlID := button.htmlID()
|
||
|
updateCSSStyle(htmlID, button.session)
|
||
|
updateInnerHTML(htmlID, button.session)
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
|
||
|
case VerticalAlign:
|
||
|
if button.setEnumProperty(tag, value, enumProperties[tag].values) {
|
||
|
if button.created {
|
||
|
updateCSSProperty(button.htmlID()+"content", "align-items", button.cssVerticalAlign(), button.session)
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
|
||
|
case HorizontalAlign:
|
||
|
if button.setEnumProperty(tag, value, enumProperties[tag].values) {
|
||
|
if button.created {
|
||
|
updateCSSProperty(button.htmlID()+"content", "justify-items", button.cssHorizontalAlign(), button.session)
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
|
||
|
case CellVerticalAlign, CellHorizontalAlign, CellWidth, CellHeight:
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
return button.viewsContainerData.Set(tag, value)
|
||
|
}
|
||
|
|
||
|
func (button *checkboxData) Remove(tag string) {
|
||
|
switch strings.ToLower(tag) {
|
||
|
case CheckboxChangedEvent:
|
||
|
if len(button.checkedListeners) > 0 {
|
||
|
button.checkedListeners = []func(Checkbox, bool){}
|
||
|
}
|
||
|
|
||
|
case Checked:
|
||
|
oldChecked := button.checked()
|
||
|
delete(button.properties, tag)
|
||
|
if oldChecked {
|
||
|
button.changedCheckboxState(false)
|
||
|
}
|
||
|
|
||
|
case CheckboxHorizontalAlign, CheckboxVerticalAlign:
|
||
|
delete(button.properties, tag)
|
||
|
htmlID := button.htmlID()
|
||
|
updateCSSStyle(htmlID, button.session)
|
||
|
updateInnerHTML(htmlID, button.session)
|
||
|
|
||
|
case VerticalAlign:
|
||
|
delete(button.properties, tag)
|
||
|
updateCSSProperty(button.htmlID()+"content", "align-items", button.cssVerticalAlign(), button.session)
|
||
|
|
||
|
case HorizontalAlign:
|
||
|
delete(button.properties, tag)
|
||
|
updateCSSProperty(button.htmlID()+"content", "justify-items", button.cssHorizontalAlign(), button.session)
|
||
|
|
||
|
default:
|
||
|
button.viewsContainerData.Remove(tag)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (button *checkboxData) checked() bool {
|
||
|
checked, _ := boolProperty(button, Checked, button.Session())
|
||
|
return checked
|
||
|
}
|
||
|
|
||
|
func (button *checkboxData) changedCheckboxState(state bool) {
|
||
|
for _, listener := range button.checkedListeners {
|
||
|
listener(button, state)
|
||
|
}
|
||
|
|
||
|
buffer := allocStringBuilder()
|
||
|
defer freeStringBuilder(buffer)
|
||
|
|
||
|
button.htmlCheckbox(buffer, state)
|
||
|
button.Session().runScript(fmt.Sprintf(`updateInnerHTML('%v', '%v');`, button.htmlID()+"checkbox", buffer.String()))
|
||
|
}
|
||
|
|
||
|
func checkboxClickListener(view View) {
|
||
|
view.Set(Checked, !IsCheckboxChecked(view, ""))
|
||
|
}
|
||
|
|
||
|
func checkboxKeyListener(view View, event KeyEvent) {
|
||
|
switch event.Code {
|
||
|
case "Enter", "Space":
|
||
|
view.Set(Checked, !IsCheckboxChecked(view, ""))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (button *checkboxData) setChangedListener(value interface{}) bool {
|
||
|
if value == nil {
|
||
|
if len(button.checkedListeners) > 0 {
|
||
|
button.checkedListeners = []func(Checkbox, bool){}
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
switch value := value.(type) {
|
||
|
case func(Checkbox, bool):
|
||
|
button.checkedListeners = []func(Checkbox, bool){value}
|
||
|
|
||
|
case func(bool):
|
||
|
fn := func(view Checkbox, checked bool) {
|
||
|
value(checked)
|
||
|
}
|
||
|
button.checkedListeners = []func(Checkbox, bool){fn}
|
||
|
|
||
|
case []func(Checkbox, bool):
|
||
|
button.checkedListeners = value
|
||
|
|
||
|
case []func(bool):
|
||
|
listeners := make([]func(Checkbox, bool), len(value))
|
||
|
for i, val := range value {
|
||
|
if val == nil {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
listeners[i] = func(view Checkbox, checked bool) {
|
||
|
val(checked)
|
||
|
}
|
||
|
}
|
||
|
button.checkedListeners = listeners
|
||
|
|
||
|
case []interface{}:
|
||
|
listeners := make([]func(Checkbox, bool), len(value))
|
||
|
for i, val := range value {
|
||
|
if val == nil {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
switch val := val.(type) {
|
||
|
case func(Checkbox, bool):
|
||
|
listeners[i] = val
|
||
|
|
||
|
case func(bool):
|
||
|
listeners[i] = func(view Checkbox, date bool) {
|
||
|
val(date)
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
button.checkedListeners = listeners
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func (button *checkboxData) cssStyle(self View, builder cssBuilder) {
|
||
|
session := button.Session()
|
||
|
vAlign, _ := enumStyledProperty(button, CheckboxVerticalAlign, LeftAlign)
|
||
|
hAlign, _ := enumStyledProperty(button, CheckboxHorizontalAlign, TopAlign)
|
||
|
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"))
|
||
|
}
|
||
|
|
||
|
builder.add("align-items", "stretch")
|
||
|
builder.add("justify-items", "stretch")
|
||
|
|
||
|
button.viewsContainerData.cssStyle(self, builder)
|
||
|
}
|
||
|
|
||
|
func (button *checkboxData) htmlCheckbox(buffer *strings.Builder, checked bool) (int, int) {
|
||
|
vAlign, _ := enumStyledProperty(button, CheckboxVerticalAlign, LeftAlign)
|
||
|
hAlign, _ := enumStyledProperty(button, CheckboxHorizontalAlign, TopAlign)
|
||
|
|
||
|
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(`">`)
|
||
|
if checked {
|
||
|
buffer.WriteString(button.Session().checkboxOnImage())
|
||
|
} else {
|
||
|
buffer.WriteString(button.Session().checkboxOffImage())
|
||
|
}
|
||
|
buffer.WriteString(`</div>`)
|
||
|
|
||
|
return vAlign, hAlign
|
||
|
}
|
||
|
|
||
|
func (button *checkboxData) htmlSubviews(self View, buffer *strings.Builder) {
|
||
|
|
||
|
vCheckboxAlign, hCheckboxAlign := button.htmlCheckbox(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(button.cssVerticalAlign())
|
||
|
buffer.WriteRune(';')
|
||
|
|
||
|
buffer.WriteString(" justify-items: ")
|
||
|
buffer.WriteString(button.cssHorizontalAlign())
|
||
|
buffer.WriteRune(';')
|
||
|
|
||
|
buffer.WriteString(`">`)
|
||
|
button.viewsContainerData.htmlSubviews(self, buffer)
|
||
|
buffer.WriteString(`</div>`)
|
||
|
}
|
||
|
|
||
|
func (button *checkboxData) cssHorizontalAlign() string {
|
||
|
align, _ := enumStyledProperty(button, HorizontalAlign, TopAlign)
|
||
|
values := enumProperties[CellHorizontalAlign].cssValues
|
||
|
if align >= 0 && align < len(values) {
|
||
|
return values[align]
|
||
|
}
|
||
|
return values[0]
|
||
|
}
|
||
|
|
||
|
func (button *checkboxData) cssVerticalAlign() string {
|
||
|
align, _ := enumStyledProperty(button, VerticalAlign, TopAlign)
|
||
|
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 "" then a value from the first argument (view) is returned.
|
||
|
func IsCheckboxChecked(view View, subviewID string) bool {
|
||
|
if subviewID != "" {
|
||
|
view = ViewByID(view, subviewID)
|
||
|
}
|
||
|
if view != nil {
|
||
|
if checked := view.Get(Checked); checked != nil {
|
||
|
if b, ok := checked.(bool); ok {
|
||
|
return b
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|