mirror of
https://github.com/vsariola/sointu.git
synced 2025-07-20 14:04:34 -04:00
refactor(tracker/gioui): Dialog binds to Model during Layout
This commit is contained in:
parent
5f43bc3067
commit
33f7b5fb6a
@ -1,70 +1,143 @@
|
|||||||
package gioui
|
package gioui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
"gioui.org/io/key"
|
"gioui.org/io/key"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/op/paint"
|
"gioui.org/op/paint"
|
||||||
"gioui.org/unit"
|
|
||||||
"gioui.org/widget"
|
"gioui.org/widget"
|
||||||
"gioui.org/widget/material"
|
"gioui.org/widget/material"
|
||||||
"github.com/vsariola/sointu/tracker"
|
"github.com/vsariola/sointu/tracker"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Dialog struct {
|
const DIALOG_MAX_BTNS = 3
|
||||||
BtnAlt widget.Clickable
|
|
||||||
BtnOk widget.Clickable
|
|
||||||
BtnCancel widget.Clickable
|
|
||||||
|
|
||||||
ok, alt, cancel tracker.Action
|
type (
|
||||||
|
// DialogState is the state that needs to be retained between frames
|
||||||
|
DialogState struct {
|
||||||
|
Clickables [DIALOG_MAX_BTNS]widget.Clickable
|
||||||
|
|
||||||
|
visible bool // this is used to control the visibility of the dialog
|
||||||
}
|
}
|
||||||
|
|
||||||
type DialogStyle struct {
|
// DialogStyle is the style for a dialog that is store in the theme.yml
|
||||||
dialog *Dialog
|
DialogStyle struct {
|
||||||
|
TitleInset layout.Inset
|
||||||
|
TextInset layout.Inset
|
||||||
|
ButtonStyle ButtonStyle
|
||||||
|
Title LabelStyle
|
||||||
|
Text LabelStyle
|
||||||
|
Bg color.NRGBA
|
||||||
|
Buttons ButtonStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dialog is the widget with a Layout method that can be used to display a dialog.
|
||||||
|
Dialog struct {
|
||||||
|
Theme *Theme
|
||||||
|
State *DialogState
|
||||||
|
Style *DialogStyle
|
||||||
|
Btns [DIALOG_MAX_BTNS]DialogButton
|
||||||
|
NumBtns int
|
||||||
Title string
|
Title string
|
||||||
Text string
|
Text string
|
||||||
Inset layout.Inset
|
|
||||||
TextInset layout.Inset
|
|
||||||
AltStyle material.ButtonStyle
|
|
||||||
OkStyle material.ButtonStyle
|
|
||||||
CancelStyle material.ButtonStyle
|
|
||||||
Theme *Theme
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDialog(ok, alt, cancel tracker.Action) *Dialog {
|
DialogButton struct {
|
||||||
ret := &Dialog{ok: ok, alt: alt, cancel: cancel}
|
Text string
|
||||||
|
Action tracker.Action
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
func ConfirmDialog(gtx C, th *Theme, dialog *Dialog, title, text string) DialogStyle {
|
func MakeDialog(th *Theme, d *DialogState, title, text string, btns ...DialogButton) Dialog {
|
||||||
ret := DialogStyle{
|
ret := Dialog{
|
||||||
dialog: dialog,
|
Theme: th,
|
||||||
|
Style: &th.Dialog,
|
||||||
|
State: d,
|
||||||
Title: title,
|
Title: title,
|
||||||
Text: text,
|
Text: text,
|
||||||
Inset: layout.Inset{Top: unit.Dp(12), Bottom: unit.Dp(12), Left: unit.Dp(20), Right: unit.Dp(20)},
|
|
||||||
TextInset: layout.Inset{Top: unit.Dp(12), Bottom: unit.Dp(12)},
|
|
||||||
AltStyle: material.Button(&th.Material, &dialog.BtnAlt, "Alt"),
|
|
||||||
OkStyle: material.Button(&th.Material, &dialog.BtnOk, "Ok"),
|
|
||||||
CancelStyle: material.Button(&th.Material, &dialog.BtnCancel, "Cancel"),
|
|
||||||
Theme: th,
|
|
||||||
}
|
}
|
||||||
for _, b := range [...]*material.ButtonStyle{&ret.AltStyle, &ret.OkStyle, &ret.CancelStyle} {
|
if len(btns) > DIALOG_MAX_BTNS {
|
||||||
b.Background = color.NRGBA{}
|
panic(fmt.Sprintf("too many buttons for dialog: %d, max is %d", len(btns), DIALOG_MAX_BTNS))
|
||||||
b.Inset = layout.UniformInset(unit.Dp(6))
|
|
||||||
b.Color = th.Material.Palette.Fg
|
|
||||||
}
|
}
|
||||||
|
copy(ret.Btns[:], btns)
|
||||||
|
ret.NumBtns = len(btns)
|
||||||
|
d.visible = true
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Dialog) handleKeysForButton(gtx C, btn, next, prev *widget.Clickable) {
|
func DialogBtn(text string, action tracker.Action) DialogButton {
|
||||||
|
return DialogButton{Text: text, Action: action}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dialog) Layout(gtx C) D {
|
||||||
|
anyFocused := false
|
||||||
|
for i := 0; i < d.NumBtns; i++ {
|
||||||
|
anyFocused = anyFocused || gtx.Source.Focused(&d.State.Clickables[i])
|
||||||
|
}
|
||||||
|
if !anyFocused {
|
||||||
|
gtx.Execute(key.FocusCmd{Tag: &d.State.Clickables[d.NumBtns-1]})
|
||||||
|
}
|
||||||
|
d.handleKeys(gtx)
|
||||||
|
paint.Fill(gtx.Ops, d.Style.Bg)
|
||||||
|
return layout.Center.Layout(gtx, func(gtx C) D {
|
||||||
|
return Popup(d.Theme, &d.State.visible).Layout(gtx, func(gtx C) D {
|
||||||
|
return layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx,
|
||||||
|
layout.Rigid(func(gtx C) D {
|
||||||
|
return d.Style.TitleInset.Layout(gtx, Label(d.Theme, &d.Style.Title, d.Title).Layout)
|
||||||
|
}),
|
||||||
|
layout.Rigid(func(gtx C) D {
|
||||||
|
return d.Style.TextInset.Layout(gtx, Label(d.Theme, &d.Style.Text, d.Text).Layout)
|
||||||
|
}),
|
||||||
|
layout.Rigid(func(gtx C) D {
|
||||||
|
return layout.E.Layout(gtx, func(gtx C) D {
|
||||||
|
var fcs [DIALOG_MAX_BTNS]layout.FlexChild
|
||||||
|
var actBtns [DIALOG_MAX_BTNS]material.ButtonStyle
|
||||||
|
for i := 0; i < d.NumBtns; i++ {
|
||||||
|
actBtns[i] = material.Button(&d.Theme.Material, &d.State.Clickables[i], d.Btns[i].Text)
|
||||||
|
actBtns[i].Background = d.Style.Buttons.Background
|
||||||
|
actBtns[i].Color = d.Style.Buttons.Color
|
||||||
|
actBtns[i].TextSize = d.Style.Buttons.TextSize
|
||||||
|
actBtns[i].Font = d.Style.Buttons.Font
|
||||||
|
actBtns[i].Inset = d.Style.Buttons.Inset
|
||||||
|
actBtns[i].CornerRadius = d.Style.Buttons.CornerRadius
|
||||||
|
}
|
||||||
|
// putting this inside these inside the for loop
|
||||||
|
// cause heap escapes, so that's why this ugliness;
|
||||||
|
// remember to update if you change the
|
||||||
|
// DIAOLG_MAX_BTNS constant
|
||||||
|
fcs[0] = layout.Rigid(actBtns[0].Layout)
|
||||||
|
fcs[1] = layout.Rigid(actBtns[1].Layout)
|
||||||
|
fcs[2] = layout.Rigid(actBtns[2].Layout)
|
||||||
|
gtx.Constraints.Min.Y = gtx.Dp(d.Style.Buttons.Height)
|
||||||
|
return layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceBetween}.Layout(gtx, fcs[:d.NumBtns]...)
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dialog) handleKeys(gtx C) {
|
||||||
|
for i := 0; i < d.NumBtns; i++ {
|
||||||
|
for d.State.Clickables[i].Clicked(gtx) {
|
||||||
|
d.Btns[i].Action.Do()
|
||||||
|
}
|
||||||
|
d.handleKeysForButton(gtx, (i+d.NumBtns-1)%d.NumBtns, i, (i+1)%d.NumBtns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dialog) handleKeysForButton(gtx C, prev, cur, next int) {
|
||||||
|
cPrev := &d.State.Clickables[prev]
|
||||||
|
cCur := &d.State.Clickables[cur]
|
||||||
|
cNext := &d.State.Clickables[next]
|
||||||
for {
|
for {
|
||||||
e, ok := gtx.Event(
|
e, ok := gtx.Event(
|
||||||
key.Filter{Focus: btn, Name: key.NameLeftArrow},
|
key.Filter{Focus: cCur, Name: key.NameLeftArrow},
|
||||||
key.Filter{Focus: btn, Name: key.NameRightArrow},
|
key.Filter{Focus: cCur, Name: key.NameRightArrow},
|
||||||
key.Filter{Focus: btn, Name: key.NameEscape},
|
key.Filter{Focus: cCur, Name: key.NameEscape},
|
||||||
key.Filter{Focus: btn, Name: key.NameTab, Optional: key.ModShift},
|
key.Filter{Focus: cCur, Name: key.NameTab, Optional: key.ModShift},
|
||||||
)
|
)
|
||||||
if !ok {
|
if !ok {
|
||||||
break
|
break
|
||||||
@ -72,68 +145,12 @@ func (d *Dialog) handleKeysForButton(gtx C, btn, next, prev *widget.Clickable) {
|
|||||||
if e, ok := e.(key.Event); ok && e.State == key.Press {
|
if e, ok := e.(key.Event); ok && e.State == key.Press {
|
||||||
switch {
|
switch {
|
||||||
case e.Name == key.NameLeftArrow || (e.Name == key.NameTab && e.Modifiers.Contain(key.ModShift)):
|
case e.Name == key.NameLeftArrow || (e.Name == key.NameTab && e.Modifiers.Contain(key.ModShift)):
|
||||||
gtx.Execute(key.FocusCmd{Tag: prev})
|
gtx.Execute(key.FocusCmd{Tag: cPrev})
|
||||||
case e.Name == key.NameRightArrow || (e.Name == key.NameTab && !e.Modifiers.Contain(key.ModShift)):
|
case e.Name == key.NameRightArrow || (e.Name == key.NameTab && !e.Modifiers.Contain(key.ModShift)):
|
||||||
gtx.Execute(key.FocusCmd{Tag: next})
|
gtx.Execute(key.FocusCmd{Tag: cNext})
|
||||||
case e.Name == key.NameEscape:
|
case e.Name == key.NameEscape:
|
||||||
d.cancel.Do()
|
d.Btns[d.NumBtns-1].Action.Do() // last button is always the cancel button
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Dialog) handleKeys(gtx C) {
|
|
||||||
for d.BtnOk.Clicked(gtx) {
|
|
||||||
d.ok.Do()
|
|
||||||
}
|
|
||||||
for d.BtnAlt.Clicked(gtx) {
|
|
||||||
d.alt.Do()
|
|
||||||
}
|
|
||||||
for d.BtnCancel.Clicked(gtx) {
|
|
||||||
d.cancel.Do()
|
|
||||||
}
|
|
||||||
if d.alt.Enabled() && d.cancel.Enabled() {
|
|
||||||
d.handleKeysForButton(gtx, &d.BtnAlt, &d.BtnCancel, &d.BtnOk)
|
|
||||||
d.handleKeysForButton(gtx, &d.BtnCancel, &d.BtnOk, &d.BtnAlt)
|
|
||||||
d.handleKeysForButton(gtx, &d.BtnOk, &d.BtnAlt, &d.BtnCancel)
|
|
||||||
} else if d.ok.Enabled() {
|
|
||||||
d.handleKeysForButton(gtx, &d.BtnOk, &d.BtnCancel, &d.BtnCancel)
|
|
||||||
d.handleKeysForButton(gtx, &d.BtnCancel, &d.BtnOk, &d.BtnOk)
|
|
||||||
} else {
|
|
||||||
d.handleKeysForButton(gtx, &d.BtnCancel, &d.BtnCancel, &d.BtnCancel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogStyle) Layout(gtx C) D {
|
|
||||||
if !gtx.Source.Focused(&d.dialog.BtnOk) && !gtx.Source.Focused(&d.dialog.BtnCancel) && !gtx.Source.Focused(&d.dialog.BtnAlt) {
|
|
||||||
gtx.Execute(key.FocusCmd{Tag: &d.dialog.BtnCancel})
|
|
||||||
}
|
|
||||||
d.dialog.handleKeys(gtx)
|
|
||||||
paint.Fill(gtx.Ops, d.Theme.Dialog.Bg)
|
|
||||||
visible := true
|
|
||||||
return layout.Center.Layout(gtx, func(gtx C) D {
|
|
||||||
return Popup(d.Theme, &visible).Layout(gtx, func(gtx C) D {
|
|
||||||
return d.Inset.Layout(gtx, func(gtx C) D {
|
|
||||||
return layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx,
|
|
||||||
layout.Rigid(Label(d.Theme, &d.Theme.Dialog.Title, d.Title).Layout),
|
|
||||||
layout.Rigid(Label(d.Theme, &d.Theme.Dialog.Text, d.Text).Layout),
|
|
||||||
layout.Rigid(func(gtx C) D {
|
|
||||||
return layout.E.Layout(gtx, func(gtx C) D {
|
|
||||||
fl := layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceBetween}
|
|
||||||
ok := layout.Rigid(d.OkStyle.Layout)
|
|
||||||
alt := layout.Rigid(d.AltStyle.Layout)
|
|
||||||
cancel := layout.Rigid(d.CancelStyle.Layout)
|
|
||||||
if d.dialog.alt.Enabled() && d.dialog.cancel.Enabled() {
|
|
||||||
return fl.Layout(gtx, ok, alt, cancel)
|
|
||||||
}
|
|
||||||
if d.dialog.ok.Enabled() {
|
|
||||||
return fl.Layout(gtx, ok, cancel)
|
|
||||||
}
|
|
||||||
return fl.Layout(gtx, cancel)
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -27,8 +27,6 @@ type Theme struct {
|
|||||||
}
|
}
|
||||||
Oscilloscope OscilloscopeStyle
|
Oscilloscope OscilloscopeStyle
|
||||||
NumericUpDown NumericUpDownStyle
|
NumericUpDown NumericUpDownStyle
|
||||||
DialogTitle LabelStyle
|
|
||||||
DialogText LabelStyle
|
|
||||||
SongPanel struct {
|
SongPanel struct {
|
||||||
RowHeader LabelStyle
|
RowHeader LabelStyle
|
||||||
RowValue LabelStyle
|
RowValue LabelStyle
|
||||||
@ -55,11 +53,7 @@ type Theme struct {
|
|||||||
OneBeat color.NRGBA
|
OneBeat color.NRGBA
|
||||||
TwoBeat color.NRGBA
|
TwoBeat color.NRGBA
|
||||||
}
|
}
|
||||||
Dialog struct {
|
Dialog DialogStyle
|
||||||
Bg color.NRGBA
|
|
||||||
Title LabelStyle
|
|
||||||
Text LabelStyle
|
|
||||||
}
|
|
||||||
OrderEditor struct {
|
OrderEditor struct {
|
||||||
TrackTitle LabelStyle
|
TrackTitle LabelStyle
|
||||||
RowTitle LabelStyle
|
RowTitle LabelStyle
|
||||||
|
@ -33,7 +33,7 @@ button:
|
|||||||
cornerradius: &buttoncornerradius 18
|
cornerradius: &buttoncornerradius 18
|
||||||
height: &buttonheight 36
|
height: &buttonheight 36
|
||||||
inset: &buttoninset { top: 0, bottom: 0, left: 6, right: 6 }
|
inset: &buttoninset { top: 0, bottom: 0, left: 6, right: 6 }
|
||||||
text:
|
text: &textbutton
|
||||||
background: *transparentcolor
|
background: *transparentcolor
|
||||||
color: *primarycolor
|
color: *primarycolor
|
||||||
textsize: *buttontextsize
|
textsize: *buttontextsize
|
||||||
@ -110,10 +110,6 @@ alert:
|
|||||||
info:
|
info:
|
||||||
bg: { r: 50, g: 50, b: 51, a: 255 }
|
bg: { r: 50, g: 50, b: 51, a: 255 }
|
||||||
text: { textsize: 16, color: *highemphasis, shadowcolor: *black }
|
text: { textsize: 16, color: *highemphasis, shadowcolor: *black }
|
||||||
dialog:
|
|
||||||
bg: { r: 0, g: 0, b: 0, a: 224 }
|
|
||||||
title: { textsize: 16, color: *highemphasis, shadowcolor: *black }
|
|
||||||
text: { textsize: 16, color: *highemphasis, shadowcolor: *black }
|
|
||||||
ordereditor:
|
ordereditor:
|
||||||
tracktitle: { textsize: 12, color: *mediumemphasis }
|
tracktitle: { textsize: 12, color: *mediumemphasis }
|
||||||
rowtitle:
|
rowtitle:
|
||||||
@ -189,3 +185,10 @@ tooltip: { color: *white, bg: *black }
|
|||||||
popup:
|
popup:
|
||||||
bg: { r: 50, g: 50, b: 51, a: 255 }
|
bg: { r: 50, g: 50, b: 51, a: 255 }
|
||||||
shadow: { r: 0, g: 0, b: 0, a: 192 }
|
shadow: { r: 0, g: 0, b: 0, a: 192 }
|
||||||
|
dialog:
|
||||||
|
bg: { r: 0, g: 0, b: 0, a: 224 }
|
||||||
|
title: { textsize: 16, color: *highemphasis, shadowcolor: *black }
|
||||||
|
text: { textsize: 16, color: *highemphasis, shadowcolor: *black }
|
||||||
|
titleinset: { top: 12, left: 20, right: 20 }
|
||||||
|
textinset: { top: 12, bottom: 12, left: 20, right: 20 }
|
||||||
|
buttons: *textbutton
|
||||||
|
@ -40,9 +40,7 @@ type (
|
|||||||
PopupAlert *PopupAlert
|
PopupAlert *PopupAlert
|
||||||
Zoom int
|
Zoom int
|
||||||
|
|
||||||
SaveChangesDialog *Dialog
|
DialogState *DialogState
|
||||||
WaveTypeDialog *Dialog
|
|
||||||
LicenseDialog *Dialog
|
|
||||||
|
|
||||||
ModalDialog layout.Widget
|
ModalDialog layout.Widget
|
||||||
InstrumentEditor *InstrumentEditor
|
InstrumentEditor *InstrumentEditor
|
||||||
@ -85,9 +83,7 @@ func NewTracker(model *tracker.Model) *Tracker {
|
|||||||
BottomHorizontalSplit: &Split{Ratio: -.6, MinSize1: 180, MinSize2: 180},
|
BottomHorizontalSplit: &Split{Ratio: -.6, MinSize1: 180, MinSize2: 180},
|
||||||
VerticalSplit: &Split{Axis: layout.Vertical, MinSize1: 180, MinSize2: 180},
|
VerticalSplit: &Split{Axis: layout.Vertical, MinSize1: 180, MinSize2: 180},
|
||||||
|
|
||||||
SaveChangesDialog: NewDialog(model.SaveSong(), model.DiscardSong(), model.Cancel()),
|
DialogState: new(DialogState),
|
||||||
WaveTypeDialog: NewDialog(model.ExportInt16(), model.ExportFloat(), model.Cancel()),
|
|
||||||
LicenseDialog: NewDialog(tracker.MakeAction(nil), tracker.MakeAction(nil), model.Cancel()),
|
|
||||||
InstrumentEditor: NewInstrumentEditor(model),
|
InstrumentEditor: NewInstrumentEditor(model),
|
||||||
OrderEditor: NewOrderEditor(model),
|
OrderEditor: NewOrderEditor(model),
|
||||||
TrackEditor: NewNoteEditor(model),
|
TrackEditor: NewNoteEditor(model),
|
||||||
@ -275,15 +271,19 @@ func (t *Tracker) showDialog(gtx C) {
|
|||||||
}
|
}
|
||||||
switch t.Dialog() {
|
switch t.Dialog() {
|
||||||
case tracker.NewSongChanges, tracker.OpenSongChanges, tracker.QuitChanges:
|
case tracker.NewSongChanges, tracker.OpenSongChanges, tracker.QuitChanges:
|
||||||
dstyle := ConfirmDialog(gtx, t.Theme, t.SaveChangesDialog, "Save changes to song?", "Your changes will be lost if you don't save them.")
|
dialog := MakeDialog(t.Theme, t.DialogState, "Save changes to song?", "Your changes will be lost if you don't save them.",
|
||||||
dstyle.OkStyle.Text = "Save"
|
DialogBtn("Save", t.SaveSong()),
|
||||||
dstyle.AltStyle.Text = "Don't save"
|
DialogBtn("Don't save", t.DiscardSong()),
|
||||||
dstyle.Layout(gtx)
|
DialogBtn("Cancel", t.Cancel()),
|
||||||
|
)
|
||||||
|
dialog.Layout(gtx)
|
||||||
case tracker.Export:
|
case tracker.Export:
|
||||||
dstyle := ConfirmDialog(gtx, t.Theme, t.WaveTypeDialog, "", "Export .wav in int16 or float32 sample format?")
|
dialog := MakeDialog(t.Theme, t.DialogState, "Export format", "Choose the sample format for the exported .wav file.",
|
||||||
dstyle.OkStyle.Text = "Int16"
|
DialogBtn("Int16", t.ExportInt16()),
|
||||||
dstyle.AltStyle.Text = "Float32"
|
DialogBtn("Float32", t.ExportFloat()),
|
||||||
dstyle.Layout(gtx)
|
DialogBtn("Cancel", t.Cancel()),
|
||||||
|
)
|
||||||
|
dialog.Layout(gtx)
|
||||||
case tracker.OpenSongOpenExplorer:
|
case tracker.OpenSongOpenExplorer:
|
||||||
t.explorerChooseFile(t.ReadSong, ".yml", ".json")
|
t.explorerChooseFile(t.ReadSong, ".yml", ".json")
|
||||||
case tracker.NewSongSaveExplorer, tracker.OpenSongSaveExplorer, tracker.QuitSaveExplorer, tracker.SaveAsExplorer:
|
case tracker.NewSongSaveExplorer, tracker.OpenSongSaveExplorer, tracker.QuitSaveExplorer, tracker.SaveAsExplorer:
|
||||||
@ -301,9 +301,10 @@ func (t *Tracker) showDialog(gtx C) {
|
|||||||
t.WriteWav(wc, t.Dialog() == tracker.ExportInt16Explorer)
|
t.WriteWav(wc, t.Dialog() == tracker.ExportInt16Explorer)
|
||||||
}, filename)
|
}, filename)
|
||||||
case tracker.License:
|
case tracker.License:
|
||||||
dstyle := ConfirmDialog(gtx, t.Theme, t.LicenseDialog, "License", sointu.License)
|
dialog := MakeDialog(t.Theme, t.DialogState, "License", sointu.License,
|
||||||
dstyle.CancelStyle.Text = "Close"
|
DialogBtn("Close", t.Cancel()),
|
||||||
dstyle.Layout(gtx)
|
)
|
||||||
|
dialog.Layout(gtx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user