diff --git a/dialog/base.go b/dialog/base.go index 993bb27b70..ff863f910b 100644 --- a/dialog/base.go +++ b/dialog/base.go @@ -40,7 +40,7 @@ type dialog struct { desiredSize fyne.Size win *widget.PopUp - bg *canvas.Rectangle + bg *themedBackground content, label fyne.CanvasObject dismiss *widget.Button parent fyne.Window @@ -120,7 +120,6 @@ func (d *dialog) Show() { } func (d *dialog) Refresh() { - d.applyTheme() d.win.Refresh() } @@ -164,12 +163,6 @@ func (d *dialog) SetOnClosed(closed func()) { } } -func (d *dialog) applyTheme() { - r, g, b, _ := theme.BackgroundColor().RGBA() - bg := &color.NRGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 230} - d.bg.FillColor = bg -} - func (d *dialog) hideWithResponse(resp bool) { d.win.Hide() if d.callback != nil { @@ -178,8 +171,8 @@ func (d *dialog) hideWithResponse(resp bool) { } func (d *dialog) setButtons(buttons fyne.CanvasObject) { - d.bg = canvas.NewRectangle(theme.BackgroundColor()) - d.label = newDialogTitle(d.title, d) + d.bg = newThemedBackground() + d.label = widget.NewLabelWithStyle(d.title, fyne.TextAlignLeading, fyne.TextStyle{Bold: true}) var content fyne.CanvasObject if d.icon == nil { @@ -227,31 +220,56 @@ func newButtonList(buttons ...*widget.Button) fyne.CanvasObject { return list } -// dialogTitle is really just a normal title but we use the Refresh() hook to update the background rectangle. -type dialogTitle struct { - widget.Label +// =============================================================== +// ThemedBackground +// =============================================================== - d *dialog +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.BackgroundColor()) + return &themedBackgroundRenderer{rect, []fyne.CanvasObject{rect}} } -// Refresh applies the current theme to the whole dialog before refreshing the underlying label. -func (t *dialogTitle) Refresh() { - t.d.Refresh() +type themedBackgroundRenderer struct { + rect *canvas.Rectangle + objects []fyne.CanvasObject +} - t.BaseWidget.Refresh() +func (renderer *themedBackgroundRenderer) Destroy() { } -func newDialogTitle(title string, d *dialog) *dialogTitle { - l := &dialogTitle{} - l.Text = title - l.Alignment = fyne.TextAlignLeading - l.TextStyle.Bold = true +func (renderer *themedBackgroundRenderer) Layout(size fyne.Size) { + renderer.rect.Resize(size) +} - l.d = d - l.ExtendBaseWidget(l) - return l +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, _ := theme.BackgroundColor().RGBA() + bg := &color.NRGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 230} + renderer.rect.FillColor = bg +} + +// =============================================================== +// DialogLayout +// =============================================================== + type dialogLayout struct { d *dialog } @@ -260,7 +278,7 @@ func (l *dialogLayout) Layout(obj []fyne.CanvasObject, size fyne.Size) { l.d.bg.Move(fyne.NewPos(0, 0)) l.d.bg.Resize(size) - btnMin := obj[3].MinSize().Max(obj[3].Size()) + btnMin := obj[3].MinSize() // icon iconHeight := padHeight*2 + l.d.label.MinSize().Height*2 - theme.Padding() @@ -280,7 +298,7 @@ func (l *dialogLayout) Layout(obj []fyne.CanvasObject, size fyne.Size) { func (l *dialogLayout) MinSize(obj []fyne.CanvasObject) fyne.Size { contentMin := obj[2].MinSize() - btnMin := obj[3].MinSize().Max(obj[3].Size()) + btnMin := obj[3].MinSize() width := fyne.Max(fyne.Max(contentMin.Width, btnMin.Width), obj[4].MinSize().Width) + padWidth height := contentMin.Height + btnMin.Height + l.d.label.MinSize().Height + theme.Padding() + padHeight*2 diff --git a/dialog/base_test.go b/dialog/base_test.go index f1f2395159..7c10d2127f 100644 --- a/dialog/base_test.go +++ b/dialog/base_test.go @@ -48,3 +48,51 @@ func TestShowCustom_Resize(t *testing.T) { d.Show() assert.Equal(t, size, d.(*dialog).win.Content.Size().Add(fyne.NewSize(theme.Padding()*2, theme.Padding()*2))) } + +func TestCustom_ApplyThemeOnShow(t *testing.T) { + test.NewApp() + defer test.NewApp() + w := test.NewWindow(canvas.NewRectangle(color.Transparent)) + w.Resize(fyne.NewSize(200, 300)) + + label := widget.NewLabel("Content") + label.Alignment = fyne.TextAlignCenter + d := NewCustom("Title", "OK", label, w) + + test.ApplyTheme(t, test.Theme()) + d.Show() + test.AssertImageMatches(t, "dialog-onshow-theme-default.png", w.Canvas().Capture()) + d.Hide() + + test.ApplyTheme(t, test.NewTheme()) + d.Show() + test.AssertImageMatches(t, "dialog-onshow-theme-changed.png", w.Canvas().Capture()) + d.Hide() + + test.ApplyTheme(t, test.Theme()) + d.Show() + test.AssertImageMatches(t, "dialog-onshow-theme-default.png", w.Canvas().Capture()) + d.Hide() +} + +func TestCustom_ResizeOnShow(t *testing.T) { + test.NewApp() + defer test.NewApp() + w := test.NewWindow(canvas.NewRectangle(color.Transparent)) + size := fyne.NewSize(200, 300) + w.Resize(size) + + label := widget.NewLabel("Content") + label.Alignment = fyne.TextAlignCenter + d := NewCustom("Title", "OK", label, w).(*dialog) + + d.Show() + assert.Equal(t, size, d.win.Size()) + d.Hide() + + size = fyne.NewSize(500, 500) + w.Resize(size) + d.Show() + assert.Equal(t, size, d.win.Size()) + d.Hide() +} diff --git a/dialog/file_test.go b/dialog/file_test.go index e6c7bc405c..fdb43c4754 100644 --- a/dialog/file_test.go +++ b/dialog/file_test.go @@ -387,7 +387,7 @@ func TestFileFilters(t *testing.T) { count++ } } - assert.Equal(t, 3, count) + assert.Equal(t, 5, count) f.SetFilter(storage.NewMimeTypeFileFilter([]string{"image/jpeg"})) @@ -412,7 +412,7 @@ func TestFileFilters(t *testing.T) { count++ } } - assert.Equal(t, 4, count) + assert.Equal(t, 6, count) } func TestFileFavorites(t *testing.T) { diff --git a/dialog/progress.go b/dialog/progress.go index 72da3794db..56386c0981 100644 --- a/dialog/progress.go +++ b/dialog/progress.go @@ -1,7 +1,11 @@ package dialog import ( + "image/color" + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" ) @@ -27,8 +31,9 @@ func (p *ProgressDialog) SetValue(v float64) { func NewProgress(title, message string, parent fyne.Window) *ProgressDialog { d := newDialog(title, message, theme.InfoIcon(), nil /*cancel?*/, parent) bar := widget.NewProgressBar() - bar.Resize(fyne.NewSize(200, bar.MinSize().Height)) + rect := canvas.NewRectangle(color.Transparent) + rect.SetMinSize(fyne.NewSize(200, 0)) - d.setButtons(bar) + d.setButtons(container.NewMax(rect, bar)) return &ProgressDialog{d, bar} } diff --git a/dialog/progressinfinite.go b/dialog/progressinfinite.go index af937f04fe..a78090773a 100644 --- a/dialog/progressinfinite.go +++ b/dialog/progressinfinite.go @@ -1,7 +1,11 @@ package dialog import ( + "image/color" + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" ) @@ -22,9 +26,10 @@ type ProgressInfiniteDialog struct { func NewProgressInfinite(title, message string, parent fyne.Window) *ProgressInfiniteDialog { d := newDialog(title, message, theme.InfoIcon(), nil /*cancel?*/, parent) bar := widget.NewProgressBarInfinite() - bar.Resize(fyne.NewSize(200, bar.MinSize().Height)) + rect := canvas.NewRectangle(color.Transparent) + rect.SetMinSize(fyne.NewSize(200, 0)) - d.setButtons(bar) + d.setButtons(container.NewMax(rect, bar)) return &ProgressInfiniteDialog{d, bar} } diff --git a/dialog/testdata/color/dialog_expanded_theme_default.png b/dialog/testdata/color/dialog_expanded_theme_default.png index 9021e756a6..4bcd2a9df9 100644 Binary files a/dialog/testdata/color/dialog_expanded_theme_default.png and b/dialog/testdata/color/dialog_expanded_theme_default.png differ diff --git a/dialog/testdata/dialog-onshow-theme-changed.png b/dialog/testdata/dialog-onshow-theme-changed.png new file mode 100644 index 0000000000..210755985d Binary files /dev/null and b/dialog/testdata/dialog-onshow-theme-changed.png differ diff --git a/dialog/testdata/dialog-onshow-theme-default.png b/dialog/testdata/dialog-onshow-theme-default.png new file mode 100644 index 0000000000..94e19a0615 Binary files /dev/null and b/dialog/testdata/dialog-onshow-theme-default.png differ diff --git a/internal/driver/glfw/canvas_test.go b/internal/driver/glfw/canvas_test.go index f5f6ccfe0e..9eb55b6d03 100644 --- a/internal/driver/glfw/canvas_test.go +++ b/internal/driver/glfw/canvas_test.go @@ -430,6 +430,7 @@ func TestGlCanvas_ResizeWithPopUpOverlay(t *testing.T) { content := widget.NewLabel("Content") over := widget.NewPopUp(widget.NewLabel("Over"), w.Canvas()) + over.Show() // to ensure popup content size is not zero w.SetContent(content) w.Canvas().Overlays().Add(over) diff --git a/widget/popup.go b/widget/popup.go index 848f33e321..955607160b 100644 --- a/widget/popup.go +++ b/widget/popup.go @@ -63,6 +63,7 @@ func (p *PopUp) Show() { p.Canvas.Overlays().Add(p) p.overlayShown = true } + p.Refresh() p.BaseWidget.Show() } @@ -172,26 +173,27 @@ type popUpRenderer struct { } func (r *popUpRenderer) Layout(_ fyne.Size) { - r.popUp.Content.Resize(r.popUp.innerSize.Subtract(r.padding())) + innerSize := r.popUp.innerSize.Max(r.popUp.MinSize()) + r.popUp.Content.Resize(innerSize.Subtract(r.padding())) innerPos := r.popUp.innerPos - if innerPos.X+r.popUp.innerSize.Width > r.popUp.Canvas.Size().Width { - innerPos.X = r.popUp.Canvas.Size().Width - r.popUp.innerSize.Width + if innerPos.X+innerSize.Width > r.popUp.Canvas.Size().Width { + innerPos.X = r.popUp.Canvas.Size().Width - innerSize.Width if innerPos.X < 0 { innerPos.X = 0 // TODO here we may need a scroller as it's wider than our canvas } } - if innerPos.Y+r.popUp.innerSize.Height > r.popUp.Canvas.Size().Height { - innerPos.Y = r.popUp.Canvas.Size().Height - r.popUp.innerSize.Height + if innerPos.Y+innerSize.Height > r.popUp.Canvas.Size().Height { + innerPos.Y = r.popUp.Canvas.Size().Height - innerSize.Height if innerPos.Y < 0 { innerPos.Y = 0 // TODO here we may need a scroller as it's longer than our canvas } } r.popUp.Content.Move(innerPos.Add(r.offset())) - r.background.Resize(r.popUp.innerSize) + r.background.Resize(innerSize) r.background.Move(innerPos) - r.LayoutShadow(r.popUp.innerSize, innerPos) + r.LayoutShadow(innerSize, innerPos) } func (r *popUpRenderer) MinSize() fyne.Size { @@ -200,9 +202,16 @@ func (r *popUpRenderer) MinSize() fyne.Size { func (r *popUpRenderer) Refresh() { r.background.FillColor = theme.BackgroundColor() - if r.background.Size() != r.popUp.innerSize || r.background.Position() != r.popUp.innerPos { + expectedContentSize := r.popUp.innerSize.Max(r.popUp.MinSize()).Subtract(r.padding()) + shouldRelayout := !r.popUp.Content.Size().Subtract(expectedContentSize).IsZero() + + if r.background.Size() != r.popUp.innerSize || r.background.Position() != r.popUp.innerPos || shouldRelayout { r.Layout(r.popUp.Size()) } + if !r.popUp.Canvas.Size().Subtract(r.popUp.BaseWidget.Size()).IsZero() { + r.popUp.BaseWidget.Resize(r.popUp.Canvas.Size()) + } + r.popUp.Content.Refresh() r.background.Refresh() } @@ -238,8 +247,15 @@ func (r *modalPopUpRenderer) MinSize() fyne.Size { func (r *modalPopUpRenderer) Refresh() { r.underlay.FillColor = theme.ShadowColor() r.background.FillColor = theme.BackgroundColor() - if r.background.Size() != r.popUp.innerSize { + expectedContentSize := r.popUp.innerSize.Max(r.popUp.MinSize()).Subtract(r.padding()) + shouldRelayout := !r.popUp.Content.Size().Subtract(expectedContentSize).IsZero() + + if r.background.Size() != r.popUp.innerSize || shouldRelayout { r.Layout(r.popUp.Size()) } + if !r.popUp.Canvas.Size().Subtract(r.popUp.BaseWidget.Size()).IsZero() { + r.popUp.BaseWidget.Resize(r.popUp.Canvas.Size()) + } + r.popUp.Content.Refresh() r.background.Refresh() } diff --git a/widget/popup_test.go b/widget/popup_test.go index f693beb572..5356c31042 100644 --- a/widget/popup_test.go +++ b/widget/popup_test.go @@ -144,7 +144,7 @@ func TestPopUp_Move(t *testing.T) { label := NewLabel("Hi") win := test.NewWindow(NewLabel("OK")) defer win.Close() - win.Resize(fyne.NewSize(50, 50)) + win.Resize(fyne.NewSize(70, 70)) pop := newPopUp(label, win.Canvas()) defer test.Canvas().Overlays().Remove(pop) @@ -208,7 +208,7 @@ func TestPopUp_Resize(t *testing.T) { pop.Show() defer test.Canvas().Overlays().Remove(pop) - size := fyne.NewSize(50, 40) + size := fyne.NewSize(60, 50) pop.Resize(size) assert.Equal(t, size.Subtract(fyne.NewSize(theme.Padding()*2, theme.Padding()*2)), pop.Content.Size()) @@ -289,7 +289,7 @@ func TestPopUp_Layout(t *testing.T) { pop.ShowAtPosition(pos) defer test.Canvas().Overlays().Remove(pop) - size := fyne.NewSize(50, 40) + size := fyne.NewSize(60, 50) pop.Resize(size) r := cache.Renderer(pop) require.GreaterOrEqual(t, len(r.Objects()), 3) @@ -306,6 +306,50 @@ func TestPopUp_Layout(t *testing.T) { assert.Equal(t, r.Objects()[2], content) } +func TestPopUp_ApplyThemeOnShow(t *testing.T) { + test.NewApp() + defer test.NewApp() + w := test.NewWindow(canvas.NewRectangle(color.Transparent)) + w.Resize(fyne.NewSize(200, 300)) + + pop := NewPopUp(NewLabel("Label"), w.Canvas()) + + test.ApplyTheme(t, test.Theme()) + pop.Show() + test.AssertImageMatches(t, "popup/normal-onshow-theme-default.png", w.Canvas().Capture()) + pop.Hide() + + test.ApplyTheme(t, test.NewTheme()) + pop.Show() + test.AssertImageMatches(t, "popup/normal-onshow-theme-changed.png", w.Canvas().Capture()) + pop.Hide() + + test.ApplyTheme(t, test.Theme()) + pop.Show() + test.AssertImageMatches(t, "popup/normal-onshow-theme-default.png", w.Canvas().Capture()) + pop.Hide() +} + +func TestPopUp_ResizeOnShow(t *testing.T) { + test.NewApp() + defer test.NewApp() + w := test.NewWindow(canvas.NewRectangle(color.Transparent)) + size := fyne.NewSize(200, 300) + w.Resize(size) + + pop := NewPopUp(NewLabel("Label"), w.Canvas()) + + pop.Show() + assert.Equal(t, size, pop.Size()) + pop.Hide() + + size = fyne.NewSize(500, 500) + w.Resize(size) + pop.Show() + assert.Equal(t, size, pop.Size()) + pop.Hide() +} + func TestModalPopUp_Tapped(t *testing.T) { label := NewLabel("Hi") pop := NewModalPopUp(label, test.Canvas()) @@ -363,3 +407,47 @@ func TestModalPopUp_Resize_Constrained(t *testing.T) { assert.Equal(t, float32(80), pop.Size().Width) assert.Equal(t, float32(80), pop.Size().Height) } + +func TestModalPopUp_ApplyThemeOnShow(t *testing.T) { + test.NewApp() + defer test.NewApp() + w := test.NewWindow(canvas.NewRectangle(color.Transparent)) + w.Resize(fyne.NewSize(200, 300)) + + pop := NewModalPopUp(NewLabel("Label"), w.Canvas()) + + test.ApplyTheme(t, test.Theme()) + pop.Show() + test.AssertImageMatches(t, "popup/modal-onshow-theme-default.png", w.Canvas().Capture()) + pop.Hide() + + test.ApplyTheme(t, test.NewTheme()) + pop.Show() + test.AssertImageMatches(t, "popup/modal-onshow-theme-changed.png", w.Canvas().Capture()) + pop.Hide() + + test.ApplyTheme(t, test.Theme()) + pop.Show() + test.AssertImageMatches(t, "popup/modal-onshow-theme-default.png", w.Canvas().Capture()) + pop.Hide() +} + +func TestModalPopUp_ResizeOnShow(t *testing.T) { + test.NewApp() + defer test.NewApp() + w := test.NewWindow(canvas.NewRectangle(color.Transparent)) + size := fyne.NewSize(200, 300) + w.Resize(size) + + pop := NewModalPopUp(NewLabel("Label"), w.Canvas()) + + pop.Show() + assert.Equal(t, size, pop.Size()) + pop.Hide() + + size = fyne.NewSize(500, 500) + w.Resize(size) + pop.Show() + assert.Equal(t, size, pop.Size()) + pop.Hide() +} diff --git a/widget/testdata/popup/modal-onshow-theme-changed.png b/widget/testdata/popup/modal-onshow-theme-changed.png new file mode 100644 index 0000000000..8f3730535b Binary files /dev/null and b/widget/testdata/popup/modal-onshow-theme-changed.png differ diff --git a/widget/testdata/popup/modal-onshow-theme-default.png b/widget/testdata/popup/modal-onshow-theme-default.png new file mode 100644 index 0000000000..ccabfa6efb Binary files /dev/null and b/widget/testdata/popup/modal-onshow-theme-default.png differ diff --git a/widget/testdata/popup/normal-onshow-theme-changed.png b/widget/testdata/popup/normal-onshow-theme-changed.png new file mode 100644 index 0000000000..96e5e56450 Binary files /dev/null and b/widget/testdata/popup/normal-onshow-theme-changed.png differ diff --git a/widget/testdata/popup/normal-onshow-theme-default.png b/widget/testdata/popup/normal-onshow-theme-default.png new file mode 100644 index 0000000000..6d3b4e2001 Binary files /dev/null and b/widget/testdata/popup/normal-onshow-theme-default.png differ