package screen import ( "errors" "fmt" "time" "bitbucket.org/hevanto/ui/uiwidget" "fyne.io/fyne/v2" "fyne.io/fyne/v2/data/binding" "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/widget" xwidget "fyne.io/x/fyne/widget" ) type Widget string const ( Button Widget = "Button" Calendar Widget = "Calendar" Check Widget = "Check" H1 Widget = "H1" H2 Widget = "H2" H3 Widget = "H3" H4 Widget = "H4" H5 Widget = "H5" H6 Widget = "H6" Label Widget = "Label" List Widget = "List" Separator Widget = "Separator" Spacer Widget = "Spacer" UpDownLabel Widget = "UpDownLabel" ) func (w Widget) Build(e *Element, s ScreenHandler) (c fyne.CanvasObject, err error) { e.localize(s) switch w { case Button: c, err = w.buildButtonWidget(e, s) case Calendar: c, err = w.buildCalendarWidget(e, s) case Check: c, err = w.buildCheckWidget(e, s) case H1, H2, H3, H4, H5, H6: c, err = w.buildHeaderLabelWidget(e, s) case Label: c, err = w.buildLabelWidget(e, s) case List: c, err = w.buildListWidget(e, s) case Separator: c, err = w.buildSeparatorWidget(e, s) case Spacer: c, err = w.buildSpacerWidget(e, s) case UpDownLabel: c, err = w.buildUpDownLabelWidget(e, s) default: err = errors.New("invalid widget") } if err != nil { return } if e.Decorators != nil { for _, dec := range e.Decorators { switch dec { case "Border": c = uiwidget.NewWidgetBorder(c) } } } if e.Hidden { c.Hide() } return } func (w Widget) buildButtonWidget(e *Element, s ScreenHandler) (c fyne.CanvasObject, err error) { var btn *widget.Button if e.Icon != "" { btn = widget.NewButtonWithIcon( e.Text, s.GetIcon(e.Icon), s.GetClickedHandler(e.OnClicked)) } else { btn = widget.NewButton(e.Text, s.GetClickedHandler(e.OnClicked)) } for opt, val := range e.Options { switch opt { case "alignment": btn.Alignment = getButtonAlignment(val) case "iconPlacement": btn.IconPlacement = getButtonIconPlacement(val) case "importance": btn.Importance = getImportance(val) } } if e.Disabled { btn.Disable() } c = btn return } func (w Widget) buildCalendarWidget(e *Element, s ScreenHandler) (c fyne.CanvasObject, err error) { t := time.Now() if e.Time != nil { timeFormat := time.DateOnly if e.TimeFormat != nil { timeFormat = parseTimeFormat(*e.TimeFormat) } if t, err = time.Parse(timeFormat, *e.Time); err != nil { err = fmt.Errorf("CalendarWidget: failed to parse time: %w", err) return } } c = xwidget.NewCalendar(t, s.GetDateSelectedHandler(e.OnDateSelected)) return } func (w Widget) buildCheckWidget(e *Element, s ScreenHandler) (c fyne.CanvasObject, err error) { var chk *widget.Check if e.Binding != "" { chk = widget.NewCheckWithData( e.Text, s.GetBinding(e.Binding).(binding.Bool)) } else { chk = widget.NewCheck( e.Text, s.GetCheckChangedHandler(e.OnChanged)) chk.SetChecked(e.Checked) } if e.Disabled { chk.Disable() } c = chk return } func (w Widget) buildHeaderLabelWidget(e *Element, s ScreenHandler) (c fyne.CanvasObject, err error) { var rt *widget.RichText level := 1 switch e.Widget { case H1: level = 1 case H2: level = 2 case H3: level = 3 case H4: level = 4 case H5: level = 5 case H6: level = 6 } if e.Binding != "" { rt = uiwidget.NewHWithData(level, s.GetBinding(e.Binding).(binding.String)) } else { rt = uiwidget.NewH(level, e.Text) } for opt, val := range e.Options { switch opt { case "wrapping": rt.Wrapping = getTextWrap(val) case "truncation": rt.Truncation = getTruncation(val) } } c = rt return } func (w Widget) buildLabelWidget(e *Element, s ScreenHandler) (c fyne.CanvasObject, err error) { var lbl *widget.Label if e.Binding != "" { lbl = widget.NewLabelWithData(s.GetBinding(e.Binding).(binding.String)) } else { lbl = widget.NewLabel(e.Text) } for opt, val := range e.Options { switch opt { case "alignment": lbl.Alignment = getTextAlignment(val) case "wrapping": lbl.Wrapping = getTextWrap(val) case "textStyle": lbl.TextStyle = getTextStyle(val) case "truncation": lbl.Truncation = getTruncation(val) case "importance": lbl.Importance = getImportance(val) } } c = lbl return } func (w Widget) buildListWidget(e *Element, s ScreenHandler) (c fyne.CanvasObject, err error) { var lst *widget.List if e.Binding != "" { lst = widget.NewListWithData( s.GetBinding(e.Binding).(binding.DataList), s.GetListItemTemplate(e.ItemTemplate), s.GetListDataItemRenderer(e.ItemRenderer)) } else { lst = widget.NewList( s.GetListLength(e.ListLength), s.GetListItemTemplate(e.ItemTemplate), s.GetListItemRenderer(e.ItemRenderer)) } if e.OnSelected != "" { lst.OnSelected = s.GetOnSelectedHandler(e.OnSelected) } if e.OnUnselected != "" { lst.OnUnselected = s.GetOnUnselectedHandler(e.OnUnselected) } c = lst return } func (w Widget) buildSeparatorWidget(_ *Element, _ ScreenHandler) (c fyne.CanvasObject, err error) { sep := widget.NewSeparator() c = sep return } func (w Widget) buildSpacerWidget(e *Element, _ ScreenHandler) (c fyne.CanvasObject, err error) { spc := &layout.Spacer{ FixHorizontal: e.FixHorizontal, FixVertical: e.FixVertical, } c = spc return } func (w Widget) buildUpDownLabelWidget(e *Element, s ScreenHandler) (c fyne.CanvasObject, err error) { btn := uiwidget.NewUpDownLabelWithData( s.GetBinding(e.Binding).(binding.String), s.GetClickedHandler(e.OnUpClicked), s.GetClickedHandler(e.OnDownClicked)) c = btn return } func getButtonAlignment(v any) widget.ButtonAlign { switch v.(string) { case "ButtonAlignLeading": return widget.ButtonAlignLeading case "ButtonAlignTrailing": return widget.ButtonAlignTrailing } return widget.ButtonAlignCenter } func getButtonIconPlacement(v any) widget.ButtonIconPlacement { if v.(string) == "ButtonIconTrailingText" { return widget.ButtonIconTrailingText } return widget.ButtonIconLeadingText } func getImportance(v any) widget.Importance { switch v.(string) { case "LowImportance": return widget.LowImportance case "HighImportance": return widget.HighImportance case "DangerImportance": return widget.DangerImportance case "WarningImportance": return widget.WarningImportance case "SuccessImportance": return widget.SuccessImportance } return widget.MediumImportance } func getTextAlignment(v any) fyne.TextAlign { switch v.(string) { case "TextAlignCenter": return fyne.TextAlignCenter case "TextAlignTrailing": return fyne.TextAlignTrailing } return fyne.TextAlignLeading } func getTextStyle(v any) fyne.TextStyle { var ts fyne.TextStyle props, ok := v.(map[string]any) if !ok { return ts } for key, val := range props { switch key { case "bold": ts.Bold, _ = val.(bool) case "italic": ts.Italic, _ = val.(bool) case "monospace": ts.Monospace, _ = val.(bool) case "symbol": ts.Symbol, _ = val.(bool) case "tabWidth": ts.TabWidth, _ = val.(int) } } return ts } func getTextWrap(v any) fyne.TextWrap { switch v.(string) { case "TextWrapBreak": return fyne.TextWrapBreak case "TextWrapWord": return fyne.TextWrapWord } return fyne.TextWrapOff } func getTruncation(v any) fyne.TextTruncation { switch v.(string) { case "TextTruncateClip": return fyne.TextTruncateClip case "TextTruncateEllipsis": return fyne.TextTruncateEllipsis } return fyne.TextTruncateOff } func parseTimeFormat(format string) string { switch format { case "ANSIC": return time.ANSIC case "UnixDate": return time.UnixDate case "RubyDate": return time.RubyDate case "RFC822": return time.RFC822 case "RFC822Z": return time.RFC822Z case "RFC850": return time.RFC850 case "RFC1123": return time.RFC1123 case "RFC1123Z": return time.RFC1123Z case "RFC3339": return time.RFC3339 case "RFC3339Nano": return time.RFC3339Nano case "Kitchen": return time.Kitchen case "Stamp": return time.Stamp case "StampMilli": return time.StampMilli case "StampMicro": return time.StampMicro case "StampNano": return time.StampNano case "DateTime": return time.DateTime case "DateOnly": return time.DateOnly case "TimeOnly": return time.TimeOnly default: return format } }