Added various widgets

This commit is contained in:
Maarten Heremans 2024-04-11 14:52:32 +02:00
parent 9b3d5df603
commit 8befd14d13
8 changed files with 330 additions and 9 deletions

View File

@ -368,7 +368,7 @@ func (d *FormDialog) Show(parent ...fyne.Window) {
d.items = d.itemsFn() d.items = d.itemsFn()
d.concreteDialog.OnShow() d.concreteDialog.OnShow()
d.dlg = dialog.NewForm( frm := dialog.NewForm(
d.concreteDialog.Title(), d.concreteDialog.Title(),
d.confirmLabel, d.confirmLabel,
d.dismissLabel, d.dismissLabel,
@ -376,6 +376,7 @@ func (d *FormDialog) Show(parent ...fyne.Window) {
d.concreteDialog.OnClose, d.concreteDialog.OnClose,
d.window, d.window,
) )
d.dlg = frm
d.dlg.Show() d.dlg.Show()
d.dlg.Resize(fyne.NewSize(640, 480)) d.dlg.Resize(fyne.NewSize(640, 480))
if d.focusItem != nil { if d.focusItem != nil {

View File

@ -55,14 +55,16 @@ type Element struct {
Disabled bool `yaml:"disabled"` Disabled bool `yaml:"disabled"`
Hidden bool `yaml:"hidden"` Hidden bool `yaml:"hidden"`
// Properties shared by most widgets
Placeholder string `yaml:"placeholder"`
PlaceholderLocalized bool `yaml:"placeholderLocalized"`
// Calendar Properties // Calendar Properties
Time *string `yaml:"time"` Time *string `yaml:"time"`
TimeFormat *string `yaml:"timeFormat"` TimeFormat *string `yaml:"timeFormat"`
// Entry Properties // Entry Properties
MultiLine bool `yaml:"multiLine"` MultiLine bool `yaml:"multiLine"`
Placeholder string `yaml:"placeholder"`
PlaceholderLocalized bool `yaml:"placeholderLocalized"`
Validator string `yaml:"validator"` Validator string `yaml:"validator"`
// List Properties // List Properties
@ -73,6 +75,10 @@ type Element struct {
// Check Properties // Check Properties
Checked bool `yaml:"checked"` Checked bool `yaml:"checked"`
// Select Properties
SelectOptionsBinding string `yaml:"selectOptionsBinding"`
SelectOptions []string `yaml:"selectOptions"`
// Spacer Properties // Spacer Properties
FixHorizontal bool `yaml:"fixHorizontal"` FixHorizontal bool `yaml:"fixHorizontal"`
FixVertical bool `yaml:"fixVertical"` FixVertical bool `yaml:"fixVertical"`
@ -85,6 +91,7 @@ type Element struct {
OnSelected string `yaml:"onSelected"` OnSelected string `yaml:"onSelected"`
OnUnselected string `yaml:"onUnselected"` OnUnselected string `yaml:"onUnselected"`
OnDateSelected string `yaml:"onDateSelected"` OnDateSelected string `yaml:"onDateSelected"`
OnOptionSelected string `yaml:"onOptionSelected"`
OnValidationChanged string `yaml:"onValidationChanged"` OnValidationChanged string `yaml:"onValidationChanged"`
} }

View File

@ -219,6 +219,7 @@ func (l Layout) buildFormLayout(
children = append(children, label, field) children = append(children, label, field)
} }
c = container.New(layout.NewFormLayout(), children...) c = container.New(layout.NewFormLayout(), children...)
return return
} }

View File

@ -24,6 +24,7 @@ type ListItemHandlerFn func(widget.ListItemID)
type ClickHandlerFn func() type ClickHandlerFn func()
type CheckHandlerFn func(bool) type CheckHandlerFn func(bool)
type DateSelectedHandlerFn func(time.Time) type DateSelectedHandlerFn func(time.Time)
type OptionSelectedHandlerFn func(string)
type ValidationChangedHandlerFn func(error) type ValidationChangedHandlerFn func(error)
type UIElement struct { type UIElement struct {
@ -48,6 +49,7 @@ type ScreenHandler interface {
GetOnClickedHandler(string) ClickHandlerFn GetOnClickedHandler(string) ClickHandlerFn
GetOnCheckChangedHandler(string) CheckHandlerFn GetOnCheckChangedHandler(string) CheckHandlerFn
GetOnDateSelectedHandler(string) DateSelectedHandlerFn GetOnDateSelectedHandler(string) DateSelectedHandlerFn
GetOnOptionSelectedHandler(string) OptionSelectedHandlerFn
GetOnValidationChangedHandler(string) ValidationChangedHandlerFn GetOnValidationChangedHandler(string) ValidationChangedHandlerFn
} }
@ -195,6 +197,10 @@ func (*TemplateScreenHandler) GetOnDateSelectedHandler(string) DateSelectedHandl
return func(time.Time) {} return func(time.Time) {}
} }
func (*TemplateScreenHandler) GetOnOptionSelectedHandler(string) OptionSelectedHandlerFn {
return func(string) {}
}
func (*TemplateScreenHandler) GetOnValidationChangedHandler(string) ValidationChangedHandlerFn { func (*TemplateScreenHandler) GetOnValidationChangedHandler(string) ValidationChangedHandlerFn {
return func(error) {} return func(error) {}
} }

View File

@ -55,6 +55,8 @@ const (
// - onChanged: The function to call when the checkbox is changed // - onChanged: The function to call when the checkbox is changed
Check Widget = "Check" Check Widget = "Check"
DateEntry Widget = "DateEntry"
// Entry Widget // Entry Widget
// //
// Available yaml options: // Available yaml options:
@ -165,6 +167,12 @@ const (
// - onUnselect: The on unselect function for the list // - onUnselect: The on unselect function for the list
List Widget = "List" List Widget = "List"
// Select Widget
Select Widget = "Select"
// SelectEntry Widget
SelectEntry Widget = "SelectEntry"
// Separator Widget // Separator Widget
Separator Widget = "Separator" Separator Widget = "Separator"
@ -207,6 +215,8 @@ func (w Widget) Build(
widget, err = w.buildCalendarWidget(e, s) widget, err = w.buildCalendarWidget(e, s)
case Check: case Check:
widget, err = w.buildCheckWidget(e, s) widget, err = w.buildCheckWidget(e, s)
case DateEntry:
widget, err = w.buildDateEntryWidget(e, s)
case Entry: case Entry:
widget, err = w.buildEntryWidget(e, s) widget, err = w.buildEntryWidget(e, s)
case H1, H2, H3, H4, H5, H6: case H1, H2, H3, H4, H5, H6:
@ -217,6 +227,10 @@ func (w Widget) Build(
widget, err = w.buildLabelWidget(e, s) widget, err = w.buildLabelWidget(e, s)
case List: case List:
widget, err = w.buildListWidget(e, s) widget, err = w.buildListWidget(e, s)
case Select:
widget, err = w.buildSelectWidget(e, s)
case SelectEntry:
widget, err = w.buildSelectEntryWidget(e, s)
case Separator: case Separator:
widget, err = w.buildSeparatorWidget(e, s) widget, err = w.buildSeparatorWidget(e, s)
case Spacer: case Spacer:
@ -317,6 +331,23 @@ func (w Widget) buildCheckWidget(
return return
} }
func (w Widget) buildDateEntryWidget(
e *Element,
s ScreenHandler,
) (c fyne.CanvasObject, err error) {
ent := uiwidget.NewDateEntry()
if e.OnDateSelected != "" {
ent.OnDateChanged = s.GetOnDateSelectedHandler(e.OnDateSelected)
}
if e.Disabled {
ent.Disable()
}
c = ent
return
}
func (w Widget) buildEntryWidget( func (w Widget) buildEntryWidget(
e *Element, e *Element,
s ScreenHandler, s ScreenHandler,
@ -452,6 +483,82 @@ func (w Widget) buildListWidget(
return return
} }
func (w Widget) buildSelectWidget(
e *Element,
s ScreenHandler,
) (c fyne.CanvasObject, err error) {
var slc *widget.Select
var options []string
if e.Binding != "" {
if lst, ok := s.GetBinding(e.SelectOptionsBinding).(binding.StringList); ok {
options, _ = lst.Get()
}
} else {
options = e.SelectOptions
}
slc = widget.NewSelect(options,
s.GetOnOptionSelectedHandler(e.OnOptionSelected))
slc.PlaceHolder = e.Placeholder
for opt, val := range e.Options {
switch opt {
case "alignment":
slc.Alignment = getTextAlignment(val)
}
}
if e.Disabled {
slc.Disable()
}
c = slc
return
}
func (w Widget) buildSelectEntryWidget(
e *Element,
s ScreenHandler,
) (c fyne.CanvasObject, err error) {
var slc *widget.SelectEntry
var options []string
if e.SelectOptionsBinding != "" {
if lst, ok := s.GetBinding(e.SelectOptionsBinding).(binding.StringList); ok {
options, _ = lst.Get()
lst.AddListener(binding.NewDataListener(func() {
options, _ = lst.Get()
if slc != nil {
slc.SetOptions(options)
}
}))
}
} else {
options = e.SelectOptions
}
slc = widget.NewSelectEntry(options)
if e.Binding != "" {
slc.Bind(s.GetBinding(e.Binding).(binding.String))
} else {
slc.Text = e.Text
}
slc.PlaceHolder = e.Placeholder
if e.Validator != "" {
slc.Validator = s.GetValidator(e.Validator)
}
if e.OnValidationChanged != "" {
slc.SetOnValidationChanged(s.GetOnValidationChangedHandler(e.OnValidationChanged))
}
if e.Disabled {
slc.Disable()
}
c = slc
return
}
func (w Widget) buildSeparatorWidget( func (w Widget) buildSeparatorWidget(
_ *Element, _ *Element,
_ ScreenHandler, _ ScreenHandler,
@ -477,7 +584,12 @@ func (w Widget) buildUpDownLabelWidget(
e *Element, e *Element,
s ScreenHandler, s ScreenHandler,
) (c fyne.CanvasObject, err error) { ) (c fyne.CanvasObject, err error) {
var minSize *fyne.Size
if e.MinSize != nil {
minSize = &fyne.Size{Width: e.MinSize.Width, Height: e.MinSize.Height}
}
btn := uiwidget.NewUpDownLabelWithData( btn := uiwidget.NewUpDownLabelWithData(
minSize,
s.GetBinding(e.Binding).(binding.String), s.GetBinding(e.Binding).(binding.String),
s.GetOnClickedHandler(e.OnUpClicked), s.GetOnClickedHandler(e.OnUpClicked),
s.GetOnClickedHandler(e.OnDownClicked)) s.GetOnClickedHandler(e.OnDownClicked))

View File

@ -40,6 +40,7 @@ type ScreenHandler struct {
onClickedHandlers map[string]screen.ClickHandlerFn onClickedHandlers map[string]screen.ClickHandlerFn
onCheckChangedHandlers map[string]screen.CheckHandlerFn onCheckChangedHandlers map[string]screen.CheckHandlerFn
onDateSelectedHandlers map[string]screen.DateSelectedHandlerFn onDateSelectedHandlers map[string]screen.DateSelectedHandlerFn
onOptionSelectedHandlers map[string]screen.OptionSelectedHandlerFn
onValidationChangedHandlers map[string]screen.ValidationChangedHandlerFn onValidationChangedHandlers map[string]screen.ValidationChangedHandlerFn
} }
@ -82,6 +83,7 @@ func NewScreenHandler(
h.onClickedHandlers = make(map[string]screen.ClickHandlerFn) h.onClickedHandlers = make(map[string]screen.ClickHandlerFn)
h.onCheckChangedHandlers = make(map[string]screen.CheckHandlerFn) h.onCheckChangedHandlers = make(map[string]screen.CheckHandlerFn)
h.onDateSelectedHandlers = make(map[string]screen.DateSelectedHandlerFn) h.onDateSelectedHandlers = make(map[string]screen.DateSelectedHandlerFn)
h.onOptionSelectedHandlers = make(map[string]screen.OptionSelectedHandlerFn)
h.onValidationChangedHandlers = make(map[string]screen.ValidationChangedHandlerFn) h.onValidationChangedHandlers = make(map[string]screen.ValidationChangedHandlerFn)
return return
} }
@ -202,6 +204,17 @@ func (h *ScreenHandler) RegisterOnDateSelectedHandler(
h.onDateSelectedHandlers[name] = fn h.onDateSelectedHandlers[name] = fn
} }
// RegisterOnOptionSelectedHandler registers a OnOptionSelectedHandler with the ScreenHandler.
//
// Use this function to register the handler functions used in the screen
// definition.
func (h *ScreenHandler) RegisterOnOptionSelectedHandler(
name string,
fn screen.OptionSelectedHandlerFn,
) {
h.onOptionSelectedHandlers[name] = fn
}
// RegisterOnValidationChangedHandler registers a OnValidationChangedHandler with the ScreenHandler. // RegisterOnValidationChangedHandler registers a OnValidationChangedHandler with the ScreenHandler.
// //
// Use this function to register the handler functions used in the screen // Use this function to register the handler functions used in the screen
@ -245,6 +258,42 @@ func (h *ScreenHandler) GetElement(id string) *screen.UIElement {
return elem return elem
} }
func (h *ScreenHandler) ReplaceElementObject(
containerId string,
id string,
newDecorator fyne.CanvasObject,
newObject fyne.CanvasObject,
) {
containerElement := h.GetElement(containerId)
if containerElement == nil {
return
}
containerObj := containerElement.Object.(*fyne.Container)
element := h.GetElement(id)
if element == nil {
return
}
decorator := element.Decorator
allObjs := containerObj.Objects
replaced := false
for i, obj := range allObjs {
if obj == decorator {
allObjs[i] = newDecorator
replaced = true
break
}
}
if replaced {
h.exportedElements[id].Decorator = newDecorator
h.exportedElements[id].Object = newObject
}
containerObj.Refresh()
}
// GetLocalizer returns the i18n.Localizer used for localization. // GetLocalizer returns the i18n.Localizer used for localization.
func (h *ScreenHandler) GetLocalizer() *i18n.Localizer { func (h *ScreenHandler) GetLocalizer() *i18n.Localizer {
return h.localizer return h.localizer
@ -354,6 +403,15 @@ func (h *ScreenHandler) GetOnDateSelectedHandler(name string) screen.DateSelecte
return fn return fn
} }
func (h *ScreenHandler) GetOnOptionSelectedHandler(name string) screen.OptionSelectedHandlerFn {
fn, ok := h.onOptionSelectedHandlers[name]
if !ok {
log.Printf("option selected handler %s not found", name)
return nil
}
return fn
}
// GetOnValidationChangedHandler returns the OnValidationChangedHandler with the given name. // GetOnValidationChangedHandler returns the OnValidationChangedHandler with the given name.
func (h *ScreenHandler) GetOnValidationChangedHandler(name string) screen.ValidationChangedHandlerFn { func (h *ScreenHandler) GetOnValidationChangedHandler(name string) screen.ValidationChangedHandlerFn {
fn, ok := h.onValidationChangedHandlers[name] fn, ok := h.onValidationChangedHandlers[name]

131
uiwidget/dateentry.go Normal file
View File

@ -0,0 +1,131 @@
package uiwidget
import (
"math"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
xwidget "fyne.io/x/fyne/widget"
)
type DateEntry struct {
widget.Entry
calendar *xwidget.Calendar
popUp *widget.PopUp
OnDateChanged func(time.Time)
date time.Time
}
func NewDateEntry() *DateEntry {
e := &DateEntry{}
e.Validator = func(s string) error {
_, err := time.Parse("02/01/2006", s)
return err
}
e.OnChanged = func(s string) {
if e.Validate() != nil {
if e.OnDateChanged != nil {
time, _ := time.Parse("02/01/2006", s)
e.OnDateChanged(time)
}
}
}
e.ExtendBaseWidget(e)
e.SetDate(time.Now())
return e
}
func (e *DateEntry) CreateRenderer() fyne.WidgetRenderer {
e.ExtendBaseWidget(e)
if e.ActionItem == nil {
e.ActionItem = e.setupDropdown()
if e.Disabled() {
e.ActionItem.(fyne.Disableable).Disable()
}
}
return e.Entry.CreateRenderer()
}
func (e *DateEntry) Enable() {
if e.ActionItem != nil {
e.ActionItem.(fyne.Disableable).Enable()
}
e.Entry.Enable()
}
func (e *DateEntry) Disable() {
if e.ActionItem != nil {
e.ActionItem.(fyne.Disableable).Disable()
}
e.Entry.Disable()
}
func (e *DateEntry) MinSize() fyne.Size {
e.ExtendBaseWidget(e)
return e.Entry.MinSize()
}
func (e *DateEntry) Move(pos fyne.Position) {
e.Entry.Move(pos)
if e.popUp != nil {
e.popUp.Move(e.popUpPos())
}
}
func (e *DateEntry) Resize(size fyne.Size) {
e.Entry.Resize(size)
if e.popUp != nil {
e.popUp.Resize(fyne.NewSize(size.Width, e.popUp.Size().Height))
}
}
func (e *DateEntry) SetDate(date time.Time) {
e.date = date
e.calendar = xwidget.NewCalendar(date, e.onDateSelected)
e.SetText(date.Format("02/01/2006"))
if e.ActionItem == nil {
e.ActionItem = e.setupDropdown()
if e.Disabled() {
e.ActionItem.(fyne.Disableable).Disable()
}
}
}
func (e *DateEntry) GetDate() time.Time {
return e.date
}
func (e *DateEntry) popUpPos() fyne.Position {
entryPos := fyne.CurrentApp().Driver().AbsolutePositionForObject(e)
return entryPos.Add(fyne.NewPos(0, e.Size().Height-theme.InputBorderSize()))
}
func (e *DateEntry) setupDropdown() *widget.Button {
dropdownButton := widget.NewButton("", func() {
c := fyne.CurrentApp().Driver().CanvasForObject(e)
e.popUp = widget.NewPopUp(e.calendar, c)
e.popUp.ShowAtPosition(e.popUpPos())
e.popUp.Resize(
fyne.NewSize(
float32(math.Max(float64(e.Size().Width), float64(e.popUp.MinSize().Width))),
e.popUp.MinSize().Height))
})
dropdownButton.Importance = widget.LowImportance
dropdownButton.SetIcon(theme.MenuDropDownIcon())
return dropdownButton
}
func (e *DateEntry) onDateSelected(date time.Time) {
e.date = date
e.SetText(e.date.Format("02/01/2006"))
if e.popUp != nil {
e.popUp.Hide()
}
}

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"bitbucket.org/hevanto/ui/uilayout"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding" "fyne.io/fyne/v2/data/binding"
@ -125,11 +126,11 @@ func NewLabelWithData(data binding.String, opts ...LabelOpts) *widget.Label {
// //
// Returns fyne.CanvasObject: the created widget. // Returns fyne.CanvasObject: the created widget.
func NewUpDownLabelWithData( func NewUpDownLabelWithData(
minSize *fyne.Size,
data binding.String, data binding.String,
upHandler func(), upHandler func(),
downHandler func(), downHandler func(),
) fyne.CanvasObject { ) fyne.CanvasObject {
c1 := container.NewWithoutLayout()
c2 := container.NewWithoutLayout() c2 := container.NewWithoutLayout()
w := NewWidgetBorder( w := NewWidgetBorder(
widget.NewLabelWithData(data), widget.NewLabelWithData(data),
@ -137,15 +138,19 @@ func NewUpDownLabelWithData(
bu := widget.NewButtonWithIcon("", theme.MoveUpIcon(), upHandler) bu := widget.NewButtonWithIcon("", theme.MoveUpIcon(), upHandler)
bd := widget.NewButtonWithIcon("", theme.MoveDownIcon(), downHandler) bd := widget.NewButtonWithIcon("", theme.MoveDownIcon(), downHandler)
w.Resize(fyne.NewSize(100, w.MinSize().Height)) w.Resize(fyne.NewSize(100, w.MinSize().Height))
c1.Add(w)
c2.Move(fyne.NewPos(w.Size().Width, 0)) c2.Move(fyne.NewPos(w.Size().Width, 0))
c1.Add(c2)
bu.Resize(fyne.NewSize(30, w.Size().Height*0.5)) bu.Resize(fyne.NewSize(30, w.Size().Height*0.5))
bd.Resize(fyne.NewSize(30, w.Size().Height*0.5)) bd.Resize(fyne.NewSize(30, w.Size().Height*0.5))
bd.Move(fyne.NewPos(0, w.Size().Height*0.5)) bd.Move(fyne.NewPos(0, w.Size().Height*0.5))
c2.Add(bu) c2.Add(bu)
c2.Add(bd) c2.Add(bd)
return c1
if minSize == nil {
minSize = &fyne.Size{Width: 100, Height: 0}
}
return container.NewHBox(
container.New(uilayout.NewMinSizeLayout(*minSize),
w), c2)
} }
// NewH creates a RichText widget formatted as a header of the provided level. // NewH creates a RichText widget formatted as a header of the provided level.