fckeuspy-go/vendor/fyne.io/fyne/v2/container/innerwindow.go

443 lines
11 KiB
Go

package container
import (
"image/color"
"runtime"
"strings"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
intWidget "fyne.io/fyne/v2/internal/widget"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
type titleBarButtonMode int
const (
modeClose titleBarButtonMode = iota
modeMinimize
modeMaximize
modeIcon
)
var _ fyne.Widget = (*InnerWindow)(nil)
// InnerWindow defines a container that wraps content in a window border - that can then be placed inside
// a regular container/canvas.
//
// Since: 2.5
type InnerWindow struct {
widget.BaseWidget
CloseIntercept func() `json:"-"`
OnDragged, OnResized func(*fyne.DragEvent) `json:"-"`
OnMinimized, OnMaximized, OnTappedBar, OnTappedIcon func() `json:"-"`
Icon fyne.Resource
// Alignment allows an inner window to specify if the buttons should be on the left
// (`ButtonAlignLeading`) or right of the window border.
//
// Since: 2.6
Alignment widget.ButtonAlign
title string
content *fyne.Container
maximized bool
}
// NewInnerWindow creates a new window border around the given `content`, displaying the `title` along the top.
// This will behave like a normal contain and will probably want to be added to a `MultipleWindows` parent.
//
// Since: 2.5
func NewInnerWindow(title string, content fyne.CanvasObject) *InnerWindow {
w := &InnerWindow{title: title, content: NewPadded(content)}
w.ExtendBaseWidget(w)
return w
}
func (w *InnerWindow) Close() {
w.Hide()
}
func (w *InnerWindow) CreateRenderer() fyne.WidgetRenderer {
w.ExtendBaseWidget(w)
th := w.Theme()
v := fyne.CurrentApp().Settings().ThemeVariant()
min := newBorderButton(theme.WindowMinimizeIcon(), modeMinimize, th, w.OnMinimized)
if w.OnMinimized == nil {
min.Disable()
}
max := newBorderButton(theme.WindowMaximizeIcon(), modeMaximize, th, w.OnMaximized)
if w.OnMaximized == nil {
max.Disable()
}
close := newBorderButton(theme.WindowCloseIcon(), modeClose, th, func() {
if f := w.CloseIntercept; f != nil {
f()
} else {
w.Close()
}
})
buttons := NewCenter(NewHBox(close, min, max))
borderIcon := newBorderButton(w.Icon, modeIcon, th, func() {
if f := w.OnTappedIcon; f != nil {
f()
}
})
if w.OnTappedIcon == nil {
borderIcon.Disable()
}
if w.Icon == nil {
borderIcon.Hide()
}
title := newDraggableLabel(w.title, w)
title.Truncation = fyne.TextTruncateEllipsis
height := w.Theme().Size(theme.SizeNameWindowTitleBarHeight)
off := (height - title.labelMinSize().Height) / 2
barMid := New(layout.NewCustomPaddedLayout(off, 0, 0, 0), title)
if w.buttonPosition() == widget.ButtonAlignTrailing {
buttons = NewCenter(NewHBox(min, max, close))
}
bg := canvas.NewRectangle(th.Color(theme.ColorNameOverlayBackground, v))
contentBG := canvas.NewRectangle(th.Color(theme.ColorNameBackground, v))
corner := newDraggableCorner(w)
bar := New(&titleBarLayout{buttons: buttons, icon: borderIcon, title: barMid, win: w},
buttons, borderIcon, barMid)
if w.content == nil {
w.content = NewPadded(canvas.NewRectangle(color.Transparent))
}
objects := []fyne.CanvasObject{bg, contentBG, bar, w.content, corner}
r := &innerWindowRenderer{ShadowingRenderer: intWidget.NewShadowingRenderer(objects, intWidget.DialogLevel),
win: w, bar: bar, buttonBox: buttons, buttons: []*borderButton{close, min, max}, bg: bg,
corner: corner, contentBG: contentBG, icon: borderIcon}
r.Layout(w.Size())
return r
}
func (w *InnerWindow) SetContent(obj fyne.CanvasObject) {
w.content.Objects[0] = obj
w.content.Refresh()
}
// SetMaximized tells the window if the maximized state should be set or not.
//
// Since: 2.6
func (w *InnerWindow) SetMaximized(max bool) {
w.maximized = max
w.Refresh()
}
func (w *InnerWindow) SetPadded(pad bool) {
if pad {
w.content.Layout = layout.NewPaddedLayout()
} else {
w.content.Layout = layout.NewStackLayout()
}
w.content.Refresh()
}
func (w *InnerWindow) SetTitle(title string) {
w.title = title
w.Refresh()
}
func (w *InnerWindow) buttonPosition() widget.ButtonAlign {
if w.Alignment != widget.ButtonAlignCenter {
return w.Alignment
}
if runtime.GOOS == "windows" || runtime.GOOS == "linux" || strings.Contains(runtime.GOOS, "bsd") {
return widget.ButtonAlignTrailing
}
// macOS
return widget.ButtonAlignLeading
}
var _ fyne.WidgetRenderer = (*innerWindowRenderer)(nil)
type innerWindowRenderer struct {
*intWidget.ShadowingRenderer
win *InnerWindow
bar, buttonBox *fyne.Container
buttons []*borderButton
icon *borderButton
bg, contentBG *canvas.Rectangle
corner fyne.CanvasObject
}
func (i *innerWindowRenderer) Layout(size fyne.Size) {
th := i.win.Theme()
pad := th.Size(theme.SizeNamePadding)
i.LayoutShadow(size, fyne.Position{})
i.bg.Resize(size)
barHeight := i.win.Theme().Size(theme.SizeNameWindowTitleBarHeight)
i.bar.Move(fyne.NewPos(pad, 0))
i.bar.Resize(fyne.NewSize(size.Width-pad*2, barHeight))
innerPos := fyne.NewPos(pad, barHeight)
innerSize := fyne.NewSize(size.Width-pad*2, size.Height-pad-barHeight)
i.contentBG.Move(innerPos)
i.contentBG.Resize(innerSize)
i.win.content.Move(innerPos)
i.win.content.Resize(innerSize)
cornerSize := i.corner.MinSize()
i.corner.Move(fyne.NewPos(size.Components()).Subtract(cornerSize).AddXY(1, 1))
i.corner.Resize(cornerSize)
}
func (i *innerWindowRenderer) MinSize() fyne.Size {
th := i.win.Theme()
pad := th.Size(theme.SizeNamePadding)
contentMin := i.win.content.MinSize()
barHeight := th.Size(theme.SizeNameWindowTitleBarHeight)
innerWidth := fyne.Max(i.bar.MinSize().Width, contentMin.Width)
return fyne.NewSize(innerWidth+pad*2, contentMin.Height+pad+barHeight)
}
func (i *innerWindowRenderer) Refresh() {
th := i.win.Theme()
v := fyne.CurrentApp().Settings().ThemeVariant()
i.bg.FillColor = th.Color(theme.ColorNameOverlayBackground, v)
i.bg.Refresh()
i.contentBG.FillColor = th.Color(theme.ColorNameBackground, v)
i.contentBG.Refresh()
if i.win.buttonPosition() == widget.ButtonAlignTrailing {
i.buttonBox.Objects[0].(*fyne.Container).Objects = []fyne.CanvasObject{i.buttons[1], i.buttons[2], i.buttons[0]}
} else {
i.buttonBox.Objects[0].(*fyne.Container).Objects = []fyne.CanvasObject{i.buttons[0], i.buttons[1], i.buttons[2]}
}
for _, b := range i.buttons {
b.setTheme(th)
}
i.bar.Refresh()
if i.win.OnMinimized == nil {
i.buttons[1].Disable()
} else {
i.buttons[1].SetOnTapped(i.win.OnMinimized)
i.buttons[1].Enable()
}
max := i.buttons[2]
if i.win.OnMaximized == nil {
i.buttons[2].Disable()
} else {
max.SetOnTapped(i.win.OnMaximized)
max.Enable()
}
if i.win.maximized {
max.b.SetIcon(theme.ViewRestoreIcon())
} else {
max.b.SetIcon(theme.WindowMaximizeIcon())
}
title := i.bar.Objects[2].(*fyne.Container).Objects[0].(*draggableLabel)
title.SetText(i.win.title)
i.ShadowingRenderer.RefreshShadow()
if i.win.OnTappedIcon == nil {
i.icon.Disable()
} else {
i.icon.Enable()
}
if i.win.Icon != nil {
i.icon.b.SetIcon(i.win.Icon)
i.icon.Show()
} else {
i.icon.Hide()
}
}
type draggableLabel struct {
widget.Label
win *InnerWindow
}
func newDraggableLabel(title string, win *InnerWindow) *draggableLabel {
d := &draggableLabel{win: win}
d.ExtendBaseWidget(d)
d.Text = title
return d
}
func (d *draggableLabel) Dragged(ev *fyne.DragEvent) {
if f := d.win.OnDragged; f != nil {
f(ev)
}
}
func (d *draggableLabel) DragEnd() {
}
func (d *draggableLabel) MinSize() fyne.Size {
width := d.Label.MinSize().Width
height := d.Label.Theme().Size(theme.SizeNameWindowButtonHeight)
return fyne.NewSize(width, height)
}
func (d *draggableLabel) Tapped(_ *fyne.PointEvent) {
if f := d.win.OnTappedBar; f != nil {
f()
}
}
func (d *draggableLabel) labelMinSize() fyne.Size {
return d.Label.MinSize()
}
type draggableCorner struct {
widget.BaseWidget
win *InnerWindow
}
func newDraggableCorner(w *InnerWindow) *draggableCorner {
d := &draggableCorner{win: w}
d.ExtendBaseWidget(d)
return d
}
func (c *draggableCorner) CreateRenderer() fyne.WidgetRenderer {
prop := canvas.NewImageFromResource(fyne.CurrentApp().Settings().Theme().Icon(theme.IconNameDragCornerIndicator))
prop.SetMinSize(fyne.NewSquareSize(16))
return widget.NewSimpleRenderer(prop)
}
func (c *draggableCorner) Dragged(ev *fyne.DragEvent) {
if f := c.win.OnResized; f != nil {
c.win.OnResized(ev)
}
}
func (c *draggableCorner) DragEnd() {
}
type borderButton struct {
widget.BaseWidget
b *widget.Button
c *ThemeOverride
mode titleBarButtonMode
}
func newBorderButton(icon fyne.Resource, mode titleBarButtonMode, th fyne.Theme, fn func()) *borderButton {
buttonImportance := widget.MediumImportance
if mode == modeIcon {
buttonImportance = widget.LowImportance
}
b := &widget.Button{Icon: icon, Importance: buttonImportance, OnTapped: fn}
c := NewThemeOverride(b, &buttonTheme{Theme: th, mode: mode})
ret := &borderButton{b: b, c: c, mode: mode}
ret.ExtendBaseWidget(ret)
return ret
}
func (b *borderButton) CreateRenderer() fyne.WidgetRenderer {
return widget.NewSimpleRenderer(b.c)
}
func (b *borderButton) Disable() {
b.b.Disable()
}
func (b *borderButton) Enable() {
b.b.Enable()
}
func (b *borderButton) SetOnTapped(fn func()) {
b.b.OnTapped = fn
}
func (b *borderButton) MinSize() fyne.Size {
height := b.Theme().Size(theme.SizeNameWindowButtonHeight)
return fyne.NewSquareSize(height)
}
func (b *borderButton) setTheme(th fyne.Theme) {
b.c.Theme = &buttonTheme{Theme: th, mode: b.mode}
}
type buttonTheme struct {
fyne.Theme
mode titleBarButtonMode
}
func (b *buttonTheme) Color(n fyne.ThemeColorName, v fyne.ThemeVariant) color.Color {
switch n {
case theme.ColorNameHover:
if b.mode == modeClose {
n = theme.ColorNameError
}
}
return b.Theme.Color(n, v)
}
func (b *buttonTheme) Size(n fyne.ThemeSizeName) float32 {
switch n {
case theme.SizeNameInputRadius:
if b.mode == modeIcon {
return 0
}
n = theme.SizeNameWindowButtonRadius
case theme.SizeNameInlineIcon:
n = theme.SizeNameWindowButtonIcon
}
return b.Theme.Size(n)
}
type titleBarLayout struct {
win *InnerWindow
buttons, icon, title fyne.CanvasObject
}
func (t *titleBarLayout) Layout(_ []fyne.CanvasObject, s fyne.Size) {
buttonMinWidth := t.buttons.MinSize().Width
t.buttons.Resize(fyne.NewSize(buttonMinWidth, s.Height))
t.icon.Resize(fyne.NewSquareSize(s.Height))
usedWidth := buttonMinWidth
if t.icon.Visible() {
usedWidth += s.Height
}
t.title.Resize(fyne.NewSize(s.Width-usedWidth, s.Height))
if t.win.buttonPosition() == widget.ButtonAlignTrailing {
t.buttons.Move(fyne.NewPos(s.Width-buttonMinWidth, 0))
t.icon.Move(fyne.Position{})
if t.icon.Visible() {
t.title.Move(fyne.NewPos(s.Height, 0))
} else {
t.title.Move(fyne.Position{})
}
} else {
t.buttons.Move(fyne.NewPos(0, 0))
t.icon.Move(fyne.NewPos(s.Width-s.Height, 0))
t.title.Move(fyne.NewPos(buttonMinWidth, 0))
}
}
func (t *titleBarLayout) MinSize(_ []fyne.CanvasObject) fyne.Size {
buttonMin := t.buttons.MinSize()
iconMin := t.icon.MinSize()
titleMin := t.title.MinSize() // can truncate
return fyne.NewSize(buttonMin.Width+iconMin.Width+titleMin.Width,
fyne.Max(fyne.Max(buttonMin.Height, iconMin.Height), titleMin.Height))
}