package rui import ( "fmt" "strconv" "strings" ) const ( // HiddenTabs - tabs of TabsLayout are hidden HiddenTabs = 0 // TopTabs - tabs of TabsLayout are on the top TopTabs = 1 // BottomTabs - tabs of TabsLayout are on the bottom BottomTabs = 2 // LeftTabs - tabs of TabsLayout are on the left LeftTabs = 3 // RightTabs - tabs of TabsLayout are on the right RightTabs = 4 // LeftListTabs - tabs of TabsLayout are on the left LeftListTabs = 5 // RightListTabs - tabs of TabsLayout are on the right RightListTabs = 6 ) // TabsLayoutCurrentChangedListener - listener of the current tab changing type TabsLayoutCurrentChangedListener interface { OnTabsLayoutCurrentChanged(tabsLayout TabsLayout, newCurrent int, newCurrentView View, oldCurrent int, oldCurrentView View) } type tabsLayoutCurrentChangedListenerFunc struct { listenerFunc func(tabsLayout TabsLayout, newCurrent int, newCurrentView View, oldCurrent int, oldCurrentView View) } func (listener *tabsLayoutCurrentChangedListenerFunc) OnTabsLayoutCurrentChanged(tabsLayout TabsLayout, newCurrent int, newCurrentView View, oldCurrent int, oldCurrentView View) { if listener.listenerFunc != nil { listener.listenerFunc(tabsLayout, newCurrent, newCurrentView, oldCurrent, oldCurrentView) } } // TabsLayout - multi-tab container of View type TabsLayout interface { ViewsContainer /* // Current return the index of active tab currentItem() int // SetCurrent set the index of active tab SetCurrent(current int) // TabsLocation return the location of tabs. It returns one of the following values: HiddenTabs (0), // TopTabs (1), BottomTabs (2), LeftTabs (3), RightTabs (4), LeftListTabs (5), RightListTabs (6) tabsLocation() int // TabsLocation set the location of tabs. Valid values: HiddenTabs (0), TopTabs (1), // BottomTabs (2), LeftTabs (3), RightTabs (4), LeftListTabs (5), RightListTabs (6) SetTabsLocation(location int) // TabStyle() return styles of tab in the passive and the active state TabStyle() (string, string) SetTabStyle(tabStyle string, activeTabStyle string) */ // SetCurrentTabChangedListener add the listener of the current tab changing SetCurrentTabChangedListener(listener TabsLayoutCurrentChangedListener) // SetCurrentTabChangedListener add the listener function of the current tab changing SetCurrentTabChangedListenerFunc(listenerFunc func(tabsLayout TabsLayout, newCurrent int, newCurrentView View, oldCurrent int, oldCurrentView View)) } type tabsLayoutData struct { viewsContainerData //currentTab, tabsLocation int //tabStyle, activeTabStyle string tabListener TabsLayoutCurrentChangedListener } // NewTabsLayout create new TabsLayout object and return it func NewTabsLayout(session Session) TabsLayout { view := new(tabsLayoutData) view.Init(session) return view } func newTabsLayout(session Session) View { return NewTabsLayout(session) } // Init initialize fields of ViewsContainer by default values func (tabsLayout *tabsLayoutData) Init(session Session) { tabsLayout.viewsContainerData.Init(session) tabsLayout.tag = "TabsLayout" tabsLayout.systemClass = "ruiTabsLayout" tabsLayout.tabListener = nil } func (tabsLayout *tabsLayoutData) currentItem() int { result, _ := intProperty(tabsLayout, Current, tabsLayout.session, 0) return result } func (tabsLayout *tabsLayoutData) Set(tag string, value interface{}) bool { switch tag { case Current: oldCurrent := tabsLayout.currentItem() if !tabsLayout.setIntProperty(Current, value) { return false } if !tabsLayout.session.ignoreViewUpdates() { current := tabsLayout.currentItem() if oldCurrent != current { tabsLayout.session.runScript(fmt.Sprintf("activateTab(%v, %d);", tabsLayout.htmlID(), current)) if tabsLayout.tabListener != nil { oldView := tabsLayout.views[oldCurrent] view := tabsLayout.views[current] tabsLayout.tabListener.OnTabsLayoutCurrentChanged(tabsLayout, current, view, oldCurrent, oldView) } } } case Tabs: if !tabsLayout.setEnumProperty(Tabs, value, enumProperties[Tabs].values) { return false } if !tabsLayout.session.ignoreViewUpdates() { htmlID := tabsLayout.htmlID() updateCSSStyle(htmlID, tabsLayout.session) updateInnerHTML(htmlID, tabsLayout.session) } case TabStyle, CurrentTabStyle: if value == nil { delete(tabsLayout.properties, tag) } else if text, ok := value.(string); ok { if text == "" { delete(tabsLayout.properties, tag) } else { tabsLayout.properties[tag] = text } } else { notCompatibleType(tag, value) return false } if !tabsLayout.session.ignoreViewUpdates() { htmlID := tabsLayout.htmlID() updateProperty(htmlID, "data-tabStyle", tabsLayout.inactiveTabStyle(), tabsLayout.session) updateProperty(htmlID, "data-activeTabStyle", tabsLayout.activeTabStyle(), tabsLayout.session) updateInnerHTML(htmlID, tabsLayout.session) } default: return tabsLayout.viewsContainerData.Set(tag, value) } return true } func (tabsLayout *tabsLayoutData) tabsLocation() int { tabs, _ := enumProperty(tabsLayout, Tabs, tabsLayout.session, 0) return tabs } func (tabsLayout *tabsLayoutData) inactiveTabStyle() string { if style, ok := stringProperty(tabsLayout, TabStyle, tabsLayout.session); ok { return style } switch tabsLayout.tabsLocation() { case LeftTabs, RightTabs: return "ruiInactiveVerticalTab" } return "ruiInactiveTab" } func (tabsLayout *tabsLayoutData) activeTabStyle() string { if style, ok := stringProperty(tabsLayout, CurrentTabStyle, tabsLayout.session); ok { return style } switch tabsLayout.tabsLocation() { case LeftTabs, RightTabs: return "ruiActiveVerticalTab" } return "ruiActiveTab" } func (tabsLayout *tabsLayoutData) TabStyle() (string, string) { return tabsLayout.inactiveTabStyle(), tabsLayout.activeTabStyle() } func (tabsLayout *tabsLayoutData) SetCurrentTabChangedListener(listener TabsLayoutCurrentChangedListener) { tabsLayout.tabListener = listener } /* // SetCurrentTabChangedListener add the listener of the current tab changing func (tabsLayout *tabsLayoutData) SetCurrentTabChangedListener(listener TabsLayoutCurrentChangedListener) { tabsLayout.tabListener = listener } // SetCurrentTabChangedListener add the listener function of the current tab changing func (tabsLayout *tabsLayoutData) SetCurrentTabChangedListenerFunc(listenerFunc func(tabsLayout TabsLayout, newCurrent int, newCurrentView View, oldCurrent int, oldCurrentView View)) { } */ func (tabsLayout *tabsLayoutData) SetCurrentTabChangedListenerFunc(listenerFunc func(tabsLayout TabsLayout, newCurrent int, newCurrentView View, oldCurrent int, oldCurrentView View)) { listener := new(tabsLayoutCurrentChangedListenerFunc) listener.listenerFunc = listenerFunc tabsLayout.SetCurrentTabChangedListener(listener) } // Append appends view to the end of view list func (tabsLayout *tabsLayoutData) Append(view View) { if tabsLayout.views == nil { tabsLayout.views = []View{} } tabsLayout.viewsContainerData.Append(view) if len(tabsLayout.views) == 1 { tabsLayout.setIntProperty(Current, 0) if tabsLayout.tabListener != nil { tabsLayout.tabListener.OnTabsLayoutCurrentChanged(tabsLayout, 0, tabsLayout.views[0], -1, nil) } } updateInnerHTML(tabsLayout.htmlID(), tabsLayout.session) } // Insert inserts view to the "index" position in view list func (tabsLayout *tabsLayoutData) Insert(view View, index uint) { if tabsLayout.views == nil { tabsLayout.views = []View{} } tabsLayout.viewsContainerData.Insert(view, index) current := tabsLayout.currentItem() if current >= int(index) { tabsLayout.Set(Current, current+1) } } // Remove removes view from list and return it func (tabsLayout *tabsLayoutData) RemoveView(index uint) View { if tabsLayout.views == nil { tabsLayout.views = []View{} return nil } i := int(index) count := len(tabsLayout.views) if i >= count { return nil } if count == 1 { view := tabsLayout.views[0] tabsLayout.views = []View{} if tabsLayout.tabListener != nil { tabsLayout.tabListener.OnTabsLayoutCurrentChanged(tabsLayout, 0, nil, 0, view) } return view } current := tabsLayout.currentItem() removeCurrent := (i == current) if i < current || (removeCurrent && i == count-1) { tabsLayout.properties[Current] = current - 1 if tabsLayout.tabListener != nil { currentView := tabsLayout.views[current-1] oldCurrentView := tabsLayout.views[current] tabsLayout.tabListener.OnTabsLayoutCurrentChanged(tabsLayout, current-1, currentView, current, oldCurrentView) } } return tabsLayout.viewsContainerData.RemoveView(index) } func (tabsLayout *tabsLayoutData) htmlProperties(self View, buffer *strings.Builder) { tabsLayout.viewsContainerData.htmlProperties(self, buffer) buffer.WriteString(` data-inactiveTabStyle="`) buffer.WriteString(tabsLayout.inactiveTabStyle()) buffer.WriteString(`" data-activeTabStyle="`) buffer.WriteString(tabsLayout.activeTabStyle()) buffer.WriteString(`" data-current="`) buffer.WriteString(tabsLayout.htmlID()) buffer.WriteRune('-') buffer.WriteString(strconv.Itoa(tabsLayout.currentItem())) buffer.WriteRune('"') } func (tabsLayout *tabsLayoutData) cssStyle(self View, builder cssBuilder) { tabsLayout.viewsContainerData.cssStyle(self, builder) switch tabsLayout.tabsLocation() { case TopTabs: builder.add(`grid-template-rows`, `auto 1fr`) case BottomTabs: builder.add(`grid-template-rows`, `1fr auto`) case LeftTabs, LeftListTabs: builder.add(`grid-template-columns`, `auto 1fr`) case RightTabs, RightListTabs: builder.add(`grid-template-columns`, `1fr auto`) } } func (tabsLayout *tabsLayoutData) htmlSubviews(self View, buffer *strings.Builder) { if tabsLayout.views == nil { return } //viewCount := len(tabsLayout.views) current := tabsLayout.currentItem() location := tabsLayout.tabsLocation() tabsLayoutID := tabsLayout.htmlID() if location != HiddenTabs { tabsHeight, _ := sizeConstant(tabsLayout.session, "ruiTabHeight") tabsSpace, _ := sizeConstant(tabsLayout.session, "ruiTabSpace") rowLayout := false buffer.WriteString(`
`) inactiveStyle := tabsLayout.inactiveTabStyle() activeStyle := tabsLayout.activeTabStyle() notTranslate := GetNotTranslate(tabsLayout, "") last := len(tabsLayout.views) - 1 for n, view := range tabsLayout.views { title, _ := stringProperty(view, "title", tabsLayout.session) if !notTranslate { title, _ = tabsLayout.Session().GetString(title) } buffer.WriteString(`
`) case RightTabs: buffer.WriteString(` style="writing-mode: vertical-lr;">`) default: buffer.WriteByte('>') } buffer.WriteString(title) buffer.WriteString(`
`) } buffer.WriteString(``) } for n, view := range tabsLayout.views { buffer.WriteString(`
`) view.addToCSSStyle(map[string]string{`position`: `absolute`, `left`: `0`, `right`: `0`, `top`: `0`, `bottom`: `0`}) viewHTML(tabsLayout.views[n], buffer) buffer.WriteString(`
`) } } func (tabsLayout *tabsLayoutData) handleCommand(self View, command string, data DataObject) bool { switch command { case "tabClick": if numberText, ok := data.PropertyValue("number"); ok { if number, err := strconv.Atoi(numberText); err == nil { current := tabsLayout.currentItem() if current != number { tabsLayout.properties[Current] = number if tabsLayout.tabListener != nil { oldView := tabsLayout.views[current] view := tabsLayout.views[number] tabsLayout.tabListener.OnTabsLayoutCurrentChanged(tabsLayout, number, view, current, oldView) } } } } return true } return tabsLayout.viewsContainerData.handleCommand(self, command, data) }