260 lines
6.2 KiB
Go
260 lines
6.2 KiB
Go
// Package dialog defines standard dialog windows for application GUIs.
|
|
package dialog // import "fyne.io/fyne/v2/dialog"
|
|
|
|
import (
|
|
"image/color"
|
|
|
|
"fyne.io/fyne/v2"
|
|
"fyne.io/fyne/v2/canvas"
|
|
"fyne.io/fyne/v2/container"
|
|
col "fyne.io/fyne/v2/internal/color"
|
|
"fyne.io/fyne/v2/layout"
|
|
"fyne.io/fyne/v2/theme"
|
|
"fyne.io/fyne/v2/widget"
|
|
)
|
|
|
|
const (
|
|
padWidth = 32
|
|
padHeight = 16
|
|
)
|
|
|
|
// Dialog is the common API for any dialog window with a single dismiss button
|
|
type Dialog interface {
|
|
Show()
|
|
Hide()
|
|
SetDismissText(label string)
|
|
SetOnClosed(closed func())
|
|
Refresh()
|
|
Resize(size fyne.Size)
|
|
|
|
// MinSize returns the size that this dialog should not shrink below.
|
|
//
|
|
// Since: 2.1
|
|
MinSize() fyne.Size
|
|
|
|
// Dismiss instructs the dialog to close without any affirmative action.
|
|
//
|
|
// Since: 2.6
|
|
Dismiss()
|
|
}
|
|
|
|
// Declare conformity to Dialog interface
|
|
var _ Dialog = (*dialog)(nil)
|
|
|
|
type dialog struct {
|
|
callback func(bool)
|
|
title string
|
|
icon fyne.Resource
|
|
desiredSize fyne.Size
|
|
|
|
win *widget.PopUp
|
|
content fyne.CanvasObject
|
|
dismiss *widget.Button
|
|
parent fyne.Window
|
|
|
|
// allows derived dialogs to inject logic that runs before Show()
|
|
beforeShowHook func()
|
|
}
|
|
|
|
func (d *dialog) Dismiss() {
|
|
d.Hide()
|
|
}
|
|
|
|
func (d *dialog) Hide() {
|
|
d.hideWithResponse(false)
|
|
}
|
|
|
|
// MinSize returns the size that this dialog should not shrink below.
|
|
//
|
|
// Since: 2.1
|
|
func (d *dialog) MinSize() fyne.Size {
|
|
return d.win.MinSize()
|
|
}
|
|
|
|
func (d *dialog) Show() {
|
|
if d.beforeShowHook != nil {
|
|
d.beforeShowHook()
|
|
}
|
|
if !d.desiredSize.IsZero() {
|
|
d.win.Resize(d.desiredSize)
|
|
}
|
|
d.win.Show()
|
|
}
|
|
|
|
func (d *dialog) Refresh() {
|
|
d.win.Refresh()
|
|
}
|
|
|
|
// Resize dialog, call this function after dialog show
|
|
func (d *dialog) Resize(size fyne.Size) {
|
|
d.desiredSize = size
|
|
if d.win != nil { // could be called before popup is created!
|
|
d.win.Resize(size)
|
|
}
|
|
}
|
|
|
|
// SetDismissText allows custom text to be set in the dismiss button
|
|
// This is a no-op for dialogs without dismiss buttons.
|
|
func (d *dialog) SetDismissText(label string) {
|
|
if d.dismiss == nil {
|
|
return
|
|
}
|
|
|
|
d.dismiss.SetText(label)
|
|
d.win.Refresh()
|
|
}
|
|
|
|
// SetOnClosed allows to set a callback function that is called when
|
|
// the dialog is closed
|
|
func (d *dialog) SetOnClosed(closed func()) {
|
|
// if there is already a callback set, remember it and call both
|
|
originalCallback := d.callback
|
|
|
|
d.callback = func(response bool) {
|
|
if originalCallback != nil {
|
|
originalCallback(response)
|
|
}
|
|
closed()
|
|
}
|
|
}
|
|
|
|
func (d *dialog) hideWithResponse(resp bool) {
|
|
d.win.Hide()
|
|
if d.callback != nil {
|
|
d.callback(resp)
|
|
}
|
|
}
|
|
|
|
func (d *dialog) create(buttons fyne.CanvasObject) {
|
|
label := widget.NewLabelWithStyle(d.title, fyne.TextAlignLeading, fyne.TextStyle{Bold: true})
|
|
|
|
var image fyne.CanvasObject
|
|
if d.icon != nil {
|
|
image = &canvas.Image{Resource: d.icon}
|
|
} else {
|
|
image = &layout.Spacer{}
|
|
}
|
|
|
|
content := container.New(&dialogLayout{d: d},
|
|
image,
|
|
newThemedBackground(),
|
|
d.content,
|
|
buttons,
|
|
label,
|
|
)
|
|
|
|
d.win = widget.NewModalPopUp(content, d.parent.Canvas())
|
|
}
|
|
|
|
func (d *dialog) setButtons(buttons fyne.CanvasObject) {
|
|
d.win.Content.(*fyne.Container).Objects[3] = buttons
|
|
d.win.Refresh()
|
|
}
|
|
|
|
func (d *dialog) setIcon(icon fyne.Resource) {
|
|
if icon == nil {
|
|
d.win.Content.(*fyne.Container).Objects[0] = &layout.Spacer{}
|
|
d.win.Refresh()
|
|
return
|
|
}
|
|
d.win.Content.(*fyne.Container).Objects[0] = &canvas.Image{Resource: icon}
|
|
d.win.Refresh()
|
|
}
|
|
|
|
// The method .create() needs to be called before the dialog can be shown.
|
|
func newDialog(title, message string, icon fyne.Resource, callback func(bool), parent fyne.Window) *dialog {
|
|
d := &dialog{content: newCenterWrappedLabel(message), title: title, icon: icon, parent: parent}
|
|
d.callback = callback
|
|
|
|
return d
|
|
}
|
|
|
|
// ===============================================================
|
|
// ThemedBackground
|
|
// ===============================================================
|
|
|
|
type themedBackground struct {
|
|
widget.BaseWidget
|
|
}
|
|
|
|
func newThemedBackground() *themedBackground {
|
|
t := &themedBackground{}
|
|
t.ExtendBaseWidget(t)
|
|
return t
|
|
}
|
|
|
|
func (t *themedBackground) CreateRenderer() fyne.WidgetRenderer {
|
|
t.ExtendBaseWidget(t)
|
|
rect := canvas.NewRectangle(theme.Color(theme.ColorNameOverlayBackground))
|
|
return &themedBackgroundRenderer{rect, []fyne.CanvasObject{rect}}
|
|
}
|
|
|
|
type themedBackgroundRenderer struct {
|
|
rect *canvas.Rectangle
|
|
objects []fyne.CanvasObject
|
|
}
|
|
|
|
func (renderer *themedBackgroundRenderer) Destroy() {
|
|
}
|
|
|
|
func (renderer *themedBackgroundRenderer) Layout(size fyne.Size) {
|
|
renderer.rect.Resize(size)
|
|
}
|
|
|
|
func (renderer *themedBackgroundRenderer) MinSize() fyne.Size {
|
|
return renderer.rect.MinSize()
|
|
}
|
|
|
|
func (renderer *themedBackgroundRenderer) Objects() []fyne.CanvasObject {
|
|
return renderer.objects
|
|
}
|
|
|
|
func (renderer *themedBackgroundRenderer) Refresh() {
|
|
r, g, b, _ := col.ToNRGBA(theme.Color(theme.ColorNameOverlayBackground))
|
|
bg := &color.NRGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 230}
|
|
renderer.rect.FillColor = bg
|
|
}
|
|
|
|
// ===============================================================
|
|
// DialogLayout
|
|
// ===============================================================
|
|
|
|
type dialogLayout struct {
|
|
d *dialog
|
|
}
|
|
|
|
func (l *dialogLayout) Layout(obj []fyne.CanvasObject, size fyne.Size) {
|
|
btnMin := obj[3].MinSize()
|
|
labelMin := obj[4].MinSize()
|
|
|
|
// icon
|
|
iconHeight := padHeight*2 + labelMin.Height*2 - theme.Padding()
|
|
obj[0].Resize(fyne.NewSize(iconHeight, iconHeight))
|
|
obj[0].Move(fyne.NewPos(size.Width-iconHeight+theme.Padding(), -theme.Padding()))
|
|
|
|
// background
|
|
obj[1].Move(fyne.NewPos(0, 0))
|
|
obj[1].Resize(size)
|
|
|
|
// content
|
|
contentStart := obj[4].Position().Y + labelMin.Height + padHeight
|
|
contentEnd := obj[3].Position().Y - theme.Padding()
|
|
obj[2].Move(fyne.NewPos(padWidth/2, labelMin.Height+padHeight))
|
|
obj[2].Resize(fyne.NewSize(size.Width-padWidth, contentEnd-contentStart))
|
|
|
|
// buttons
|
|
obj[3].Resize(btnMin)
|
|
obj[3].Move(fyne.NewPos(size.Width/2-(btnMin.Width/2), size.Height-padHeight-btnMin.Height))
|
|
}
|
|
|
|
func (l *dialogLayout) MinSize(obj []fyne.CanvasObject) fyne.Size {
|
|
contentMin := obj[2].MinSize()
|
|
btnMin := obj[3].MinSize()
|
|
labelMin := obj[4].MinSize()
|
|
|
|
width := fyne.Max(fyne.Max(contentMin.Width, btnMin.Width), labelMin.Width) + padWidth
|
|
height := contentMin.Height + btnMin.Height + labelMin.Height + theme.Padding() + padHeight*2
|
|
|
|
return fyne.NewSize(width, height)
|
|
}
|