package profilebtn import ( "bytes" "image" "image/color" "image/draw" "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" ) var _ fyne.Focusable = (*ProfileBtn)(nil) type ProfileBtn struct { widget.DisableableWidget OutlineWidth int OutlineColor color.Color profileImage image.Image minSizeOverridden bool minSizeRequested fyne.Size disabled bool OnTapped func() `json:"-"` } // FocusGained is a hook called by the focus handling logic after this object gained the focus. func (b *ProfileBtn) FocusGained() { // b.focused = true b.Refresh() } // FocusLost is a hook called by the focus handling logic after this object lost the focus. func (b *ProfileBtn) FocusLost() { // b.focused = false b.Refresh() } // TypedRune is a hook called by the input handling logic on text input events if this object is focused. func (b *ProfileBtn) TypedRune(rune) { } // TypedKey is a hook called by the input handling logic on key events if this object is focused. func (b *ProfileBtn) TypedKey(ev *fyne.KeyEvent) { if ev.Name == fyne.KeySpace { b.Tapped(nil) } } func (b *ProfileBtn) Resize(s fyne.Size) { b.BaseWidget.Resize(s) } func (b *ProfileBtn) Disable() { b.disabled = true } func (b *ProfileBtn) Ensable() { b.disabled = false } func (b *ProfileBtn) Disabled() bool { return b.disabled } func (b *ProfileBtn) Tapped(*fyne.PointEvent) { if b.Disabled() { return } //b.tapAnimation() b.Refresh() if b.OnTapped != nil { b.OnTapped() } } func (b *ProfileBtn) CreateRenderer() fyne.WidgetRenderer { return NewProfileBtnRenderer(b) } func NewProfileBtnRenderer(b *ProfileBtn) *profileBtnRenderer { r := &profileBtnRenderer{ canvas.NewRasterFromImage(image.NewRGBA(image.Rect(0, 0, 10, 10))), b, } b.ExtendBaseWidget(b) return r } func NewProfileBtn() *ProfileBtn { w := &ProfileBtn{} w.ExtendBaseWidget(w) w.OutlineWidth = 0 w.OutlineColor = color.White w.SetProfileResource(nil) w.minSizeOverridden = false w.disabled = false return w } func (b *ProfileBtn) SetProfileResource(r fyne.Resource) { if r != nil { i, _, err := image.Decode(bytes.NewReader(r.Content())) if err == nil { b.profileImage = i return } } t, _, _ := image.Decode(bytes.NewReader(resourceProfileBtnPng.StaticContent)) b.profileImage = t } func (b *ProfileBtn) SetMinSize(s fyne.Size) { if s.Height > 0 && s.Width > 0 { b.minSizeRequested = s b.minSizeOverridden = true return } b.minSizeOverridden = false } func (b *ProfileBtn) SetProfileImage(i image.Image) { b.profileImage = i } type profileBtnRenderer struct { rast *canvas.Raster w *ProfileBtn } func min(x, y int) int { if x < y { return x } return y } func minf(x, y float32) float32 { if x < y { return x } return y } func (r *profileBtnRenderer) Render(w, h int) image.Image { i := image.NewRGBA(image.Rect(0, 0, w, h)) if r.w.profileImage != nil { src := r.w.profileImage // convert src to srcRGBA srcb := src.Bounds() srcRGBA := image.NewRGBA(image.Rect(0, 0, srcb.Dx(), srcb.Dy())) draw.Draw(srcRGBA, srcRGBA.Bounds(), src, srcb.Min, draw.Src) b := src.Bounds().Size() wh := image.NewUniform(r.w.OutlineColor) dst := image.NewRGBA(image.Rect(0, 0, b.X, b.Y)) draw.DrawMask(dst, dst.Bounds(), wh, image.ZP, &circle{image.Point{b.X / 2, b.Y / 2}, (min(b.Y, b.Y) / 2)}, image.ZP, draw.Over) if r.w.Disabled() { for x := 0; x < srcRGBA.Bounds().Dx(); x++ { for y := 0; y < srcRGBA.Bounds().Dx(); y++ { c := srcRGBA.RGBA64At(x, y) avg := (c.R / 3) + (c.G / 3) + (c.B / 3) c = color.RGBA64{avg, avg, avg, c.A} srcRGBA.SetRGBA64(x, y, c) } } } draw.DrawMask(dst, dst.Bounds(), srcRGBA, image.ZP, &circle{image.Point{b.X / 2, b.Y / 2}, (min(b.Y, b.Y) / 2) - r.w.OutlineWidth}, image.ZP, draw.Over) return dst } else { return i } } func (r *profileBtnRenderer) MinSize() fyne.Size { if r.w.minSizeOverridden { return r.w.minSizeRequested } return fyne.NewSize(theme.Padding()*4, theme.Padding()*4) } func (r *profileBtnRenderer) Layout(size fyne.Size) { squareSize := fyne.NewSize(minf(size.Width, size.Height), minf(size.Width, size.Height)) r.rast.Resize(squareSize) if squareSize.Width < size.Width { r.rast.Move(fyne.NewPos((size.Width-squareSize.Width)/2, 0)) } else if squareSize.Height < size.Height { r.rast.Move(fyne.NewPos(0, (size.Height-squareSize.Height)/2)) } } func (r *profileBtnRenderer) ApplyTheme() { r.Refresh() } func (r *profileBtnRenderer) BackgprofileColor() color.Color { return theme.ButtonColor() } func (r *profileBtnRenderer) Refresh() { r.rast = canvas.NewRaster(r.Render) r.Layout(r.w.Size()) } func (r *profileBtnRenderer) Destroy() { } func (r *profileBtnRenderer) Objects() []fyne.CanvasObject { r.Refresh() return []fyne.CanvasObject{r.rast} }