228 lines
5.7 KiB
Go
228 lines
5.7 KiB
Go
package widget
|
|
|
|
import (
|
|
"image/color"
|
|
|
|
"fyne.io/fyne/v2"
|
|
"fyne.io/fyne/v2/canvas"
|
|
"fyne.io/fyne/v2/driver/desktop"
|
|
"fyne.io/fyne/v2/internal/widget"
|
|
"fyne.io/fyne/v2/theme"
|
|
)
|
|
|
|
var _ fyne.Widget = (*radioItem)(nil)
|
|
var _ desktop.Hoverable = (*radioItem)(nil)
|
|
var _ fyne.Tappable = (*radioItem)(nil)
|
|
var _ fyne.Focusable = (*radioItem)(nil)
|
|
|
|
func newRadioItem(label string, onTap func(*radioItem)) *radioItem {
|
|
i := &radioItem{Label: label, onTap: onTap}
|
|
i.ExtendBaseWidget(i)
|
|
return i
|
|
}
|
|
|
|
// radioItem is a single radio item to be used by RadioGroup.
|
|
type radioItem struct {
|
|
DisableableWidget
|
|
|
|
Label string
|
|
Selected bool
|
|
|
|
focused bool
|
|
hovered bool
|
|
onTap func(item *radioItem)
|
|
}
|
|
|
|
// CreateRenderer is a private method to Fyne which links this widget to its renderer.
|
|
//
|
|
// Implements: fyne.Widget
|
|
func (i *radioItem) CreateRenderer() fyne.WidgetRenderer {
|
|
focusIndicator := canvas.NewCircle(color.Transparent)
|
|
// TODO move to `theme.RadioButtonFillIcon()` when we add it in 2.4
|
|
icon := canvas.NewImageFromResource(fyne.CurrentApp().Settings().Theme().Icon("iconNameRadioButtonFill"))
|
|
over := canvas.NewImageFromResource(theme.NewThemedResource(theme.RadioButtonIcon()))
|
|
label := canvas.NewText(i.Label, theme.ForegroundColor())
|
|
label.Alignment = fyne.TextAlignLeading
|
|
r := &radioItemRenderer{
|
|
BaseRenderer: widget.NewBaseRenderer([]fyne.CanvasObject{focusIndicator, icon, over, label}),
|
|
focusIndicator: focusIndicator,
|
|
icon: icon,
|
|
over: over,
|
|
item: i,
|
|
label: label,
|
|
}
|
|
r.update()
|
|
return r
|
|
}
|
|
|
|
// FocusGained is called when this item gained the focus.
|
|
//
|
|
// Implements: fyne.Focusable
|
|
func (i *radioItem) FocusGained() {
|
|
i.focused = true
|
|
i.Refresh()
|
|
}
|
|
|
|
// FocusLost is called when this item lost the focus.
|
|
//
|
|
// Implements: fyne.Focusable
|
|
func (i *radioItem) FocusLost() {
|
|
i.focused = false
|
|
i.Refresh()
|
|
}
|
|
|
|
// MouseIn is called when a desktop pointer enters the widget.
|
|
//
|
|
// Implements: desktop.Hoverable
|
|
func (i *radioItem) MouseIn(_ *desktop.MouseEvent) {
|
|
if i.Disabled() {
|
|
return
|
|
}
|
|
|
|
i.hovered = true
|
|
i.Refresh()
|
|
}
|
|
|
|
// MouseMoved is called when a desktop pointer hovers over the widget.
|
|
//
|
|
// Implements: desktop.Hoverable
|
|
func (i *radioItem) MouseMoved(_ *desktop.MouseEvent) {
|
|
}
|
|
|
|
// MouseOut is called when a desktop pointer exits the widget
|
|
//
|
|
// Implements: desktop.Hoverable
|
|
func (i *radioItem) MouseOut() {
|
|
if i.Disabled() {
|
|
return
|
|
}
|
|
|
|
i.hovered = false
|
|
i.Refresh()
|
|
}
|
|
|
|
// SetSelected sets whether this radio item is selected or not.
|
|
func (i *radioItem) SetSelected(selected bool) {
|
|
if i.Disabled() || i.Selected == selected {
|
|
return
|
|
}
|
|
|
|
i.Selected = selected
|
|
i.Refresh()
|
|
}
|
|
|
|
// Tapped is called when a pointer tapped event is captured and triggers any change handler
|
|
//
|
|
// Implements: fyne.Tappable
|
|
func (i *radioItem) Tapped(_ *fyne.PointEvent) {
|
|
if !i.focused && !fyne.CurrentDevice().IsMobile() {
|
|
impl := i.super()
|
|
|
|
if c := fyne.CurrentApp().Driver().CanvasForObject(impl); c != nil {
|
|
c.Focus(impl.(fyne.Focusable))
|
|
}
|
|
}
|
|
i.toggle()
|
|
}
|
|
|
|
// TypedKey is called when this item receives a key event.
|
|
//
|
|
// Implements: fyne.Focusable
|
|
func (i *radioItem) TypedKey(_ *fyne.KeyEvent) {
|
|
}
|
|
|
|
// TypedRune is called when this item receives a char event.
|
|
//
|
|
// Implements: fyne.Focusable
|
|
func (i *radioItem) TypedRune(r rune) {
|
|
if r == ' ' {
|
|
i.toggle()
|
|
}
|
|
}
|
|
|
|
func (i *radioItem) toggle() {
|
|
if i.Disabled() || i.onTap == nil {
|
|
return
|
|
}
|
|
|
|
i.onTap(i)
|
|
}
|
|
|
|
type radioItemRenderer struct {
|
|
widget.BaseRenderer
|
|
|
|
focusIndicator *canvas.Circle
|
|
icon, over *canvas.Image
|
|
item *radioItem
|
|
label *canvas.Text
|
|
}
|
|
|
|
func (r *radioItemRenderer) Layout(size fyne.Size) {
|
|
focusIndicatorSize := fyne.NewSquareSize(theme.IconInlineSize() + theme.InnerPadding())
|
|
r.focusIndicator.Resize(focusIndicatorSize)
|
|
r.focusIndicator.Move(fyne.NewPos(theme.InputBorderSize(), (size.Height-focusIndicatorSize.Height)/2))
|
|
|
|
labelSize := fyne.NewSize(size.Width, size.Height)
|
|
r.label.Resize(labelSize)
|
|
r.label.Move(fyne.NewPos(focusIndicatorSize.Width+theme.Padding(), 0))
|
|
|
|
iconPos := fyne.NewPos(theme.InnerPadding()/2+theme.InputBorderSize(), (size.Height-theme.IconInlineSize())/2)
|
|
iconSize := fyne.NewSquareSize(theme.IconInlineSize())
|
|
r.icon.Resize(iconSize)
|
|
r.icon.Move(iconPos)
|
|
r.over.Resize(iconSize)
|
|
r.over.Move(iconPos)
|
|
}
|
|
|
|
func (r *radioItemRenderer) MinSize() fyne.Size {
|
|
inPad := theme.InnerPadding() * 2
|
|
|
|
return r.label.MinSize().
|
|
Add(fyne.NewSize(inPad, inPad)).
|
|
Add(fyne.NewSize(theme.IconInlineSize()+theme.Padding(), 0))
|
|
}
|
|
|
|
func (r *radioItemRenderer) Refresh() {
|
|
r.update()
|
|
canvas.Refresh(r.item.super())
|
|
}
|
|
|
|
func (r *radioItemRenderer) update() {
|
|
r.label.Text = r.item.Label
|
|
r.label.Color = theme.ForegroundColor()
|
|
r.label.TextSize = theme.TextSize()
|
|
if r.item.Disabled() {
|
|
r.label.Color = theme.DisabledColor()
|
|
}
|
|
|
|
out := theme.NewThemedResource(theme.RadioButtonIcon())
|
|
out.ColorName = theme.ColorNameInputBorder
|
|
// TODO move to `theme.RadioButtonFillIcon()` when we add it in 2.4
|
|
in := theme.NewThemedResource(fyne.CurrentApp().Settings().Theme().Icon("iconNameRadioButtonFill"))
|
|
in.ColorName = theme.ColorNameInputBackground
|
|
if r.item.Selected {
|
|
in.ColorName = theme.ColorNamePrimary
|
|
out.ColorName = theme.ColorNameForeground
|
|
}
|
|
if r.item.Disabled() {
|
|
if r.item.Selected {
|
|
in.ColorName = theme.ColorNameDisabled
|
|
} else {
|
|
in.ColorName = theme.ColorNameBackground
|
|
}
|
|
out.ColorName = theme.ColorNameDisabled
|
|
}
|
|
r.icon.Resource = in
|
|
r.over.Resource = out
|
|
|
|
if r.item.Disabled() {
|
|
r.focusIndicator.FillColor = color.Transparent
|
|
} else if r.item.focused {
|
|
r.focusIndicator.FillColor = theme.FocusColor()
|
|
} else if r.item.hovered {
|
|
r.focusIndicator.FillColor = theme.HoverColor()
|
|
} else {
|
|
r.focusIndicator.FillColor = color.Transparent
|
|
}
|
|
}
|