652 lines
17 KiB
Go
652 lines
17 KiB
Go
package dialogs
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
|
|
"fyne.io/fyne/v2"
|
|
"fyne.io/fyne/v2/container"
|
|
"fyne.io/fyne/v2/dialog"
|
|
"fyne.io/fyne/v2/lang"
|
|
"fyne.io/fyne/v2/layout"
|
|
"fyne.io/fyne/v2/theme"
|
|
"fyne.io/fyne/v2/widget"
|
|
ttwidget "github.com/dweymouth/fyne-tooltip/widget"
|
|
"github.com/dweymouth/supersonic/backend"
|
|
"github.com/dweymouth/supersonic/ui/layouts"
|
|
myTheme "github.com/dweymouth/supersonic/ui/theme"
|
|
"github.com/dweymouth/supersonic/ui/util"
|
|
)
|
|
|
|
type GraphicEqualizer struct {
|
|
widget.BaseWidget
|
|
|
|
OnChanged func(band int, gain float64)
|
|
OnPreampChanged func(gain float64)
|
|
OnLoadAutoEQProfile func()
|
|
OnManualAdjustment func() // Called when user manually changes a slider
|
|
OnPresetSelected func(presetName string) // Called when user selects a preset
|
|
OnPresetDeleted func(presetName string) // Called when user deletes a preset
|
|
OnEQTypeChanged func(eqType string) // Called when EQ type is changed
|
|
|
|
bandSliders []*eqSlider
|
|
preampSlider *eqSlider
|
|
presetSelect *widget.Select
|
|
eqTypeSelect *widget.Select
|
|
autoEQBtn *widget.Button
|
|
profileLabel *widget.Label
|
|
container *fyne.Container
|
|
sliderArea *fyne.Container // Stores the slider area for dynamic rebuilding
|
|
topBar *fyne.Container // Stores the top bar
|
|
eqPresets []backend.EQPreset
|
|
presetManager *backend.EQPresetManager
|
|
parentWindow fyne.Window
|
|
isApplyingPreset bool // Flag to prevent clearing profile during preset application
|
|
currentEQType string // Current EQ type ("ISO10Band" or "ISO15Band")
|
|
isDirty bool // true when sliders modified since last preset load/save
|
|
loadedPreset *backend.EQPreset // currently loaded preset (nil if none)
|
|
saveBtn *ttwidget.Button // reference for enable/disable control
|
|
}
|
|
|
|
func NewGraphicEqualizer(preamp float64, bandFreqs []string, bandGains []float64, eqType string, presetMgr *backend.EQPresetManager, parentWindow fyne.Window, activePresetName string) *GraphicEqualizer {
|
|
g := &GraphicEqualizer{
|
|
presetManager: presetMgr,
|
|
parentWindow: parentWindow,
|
|
currentEQType: eqType,
|
|
}
|
|
g.ExtendBaseWidget(g)
|
|
g.loadPresets()
|
|
g.buildSliders(preamp, bandFreqs, bandGains)
|
|
|
|
// Set the dropdown to the active preset if one exists
|
|
if activePresetName != "" {
|
|
g.setActivePreset(activePresetName)
|
|
// Populate loadedPreset and detect dirty state on dialog reopen
|
|
for i, p := range g.eqPresets {
|
|
if p.Name == activePresetName {
|
|
g.loadedPreset = &g.eqPresets[i]
|
|
g.isDirty = !g.matchesPreset(p)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
g.updateSaveButtonState()
|
|
|
|
return g
|
|
}
|
|
|
|
func (g *GraphicEqualizer) loadPresets() {
|
|
presets, err := g.presetManager.LoadPresets()
|
|
if err != nil {
|
|
// Fallback to empty list if load fails
|
|
g.eqPresets = []backend.EQPreset{}
|
|
return
|
|
}
|
|
g.eqPresets = presets
|
|
}
|
|
|
|
func (g *GraphicEqualizer) buildSliders(preamp float64, bands []string, bandGains []float64) {
|
|
// Build preset selector
|
|
g.updatePresetSelect()
|
|
|
|
// Build EQ type selector
|
|
if g.eqTypeSelect == nil {
|
|
g.eqTypeSelect = widget.NewSelect([]string{"ISO 15-Band", "ISO 10-Band"}, func(selected string) {
|
|
// Convert display name to type
|
|
newType := "ISO15Band"
|
|
if selected == "ISO 10-Band" {
|
|
newType = "ISO10Band"
|
|
}
|
|
|
|
if newType != g.currentEQType {
|
|
g.currentEQType = newType
|
|
if g.OnEQTypeChanged != nil {
|
|
g.OnEQTypeChanged(newType)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
// Set current selection
|
|
if g.currentEQType == "ISO10Band" {
|
|
g.eqTypeSelect.SetSelected("ISO 10-Band")
|
|
} else {
|
|
g.eqTypeSelect.SetSelected("ISO 15-Band")
|
|
}
|
|
|
|
// Reset button
|
|
resetBtn := widget.NewButton(lang.L("Reset"), func() {
|
|
// Find and apply the "Flat" preset
|
|
for _, p := range g.eqPresets {
|
|
if p.Name == "Flat" {
|
|
g.applyPreset(p)
|
|
g.presetSelect.SetSelected(p.Name)
|
|
if g.OnPresetSelected != nil {
|
|
g.OnPresetSelected(p.Name)
|
|
}
|
|
break
|
|
}
|
|
}
|
|
})
|
|
|
|
// Save button (overwrites current loaded preset)
|
|
g.saveBtn = ttwidget.NewButtonWithIcon("", myTheme.SaveIcon, func() {
|
|
g.saveCurrentPreset()
|
|
})
|
|
g.saveBtn.Disable() // starts disabled
|
|
g.saveBtn.SetToolTip(lang.L("Save"))
|
|
|
|
// Save As button (always enabled, opens name-entry dialog)
|
|
saveAsBtn := ttwidget.NewButtonWithIcon("", myTheme.SaveAsIcon, func() {
|
|
g.showSaveAsDialog()
|
|
})
|
|
saveAsBtn.SetToolTip(lang.L("Save As"))
|
|
|
|
// Delete button
|
|
deleteBtn := ttwidget.NewButtonWithIcon("", theme.DeleteIcon(), func() {
|
|
g.showDeletePresetDialog()
|
|
})
|
|
deleteBtn.SetToolTip(lang.L("Delete"))
|
|
|
|
// AutoEQ button
|
|
g.autoEQBtn = widget.NewButton(lang.L("AutoEQ"), func() {
|
|
if g.OnLoadAutoEQProfile != nil {
|
|
g.OnLoadAutoEQProfile()
|
|
}
|
|
})
|
|
|
|
// Profile label (hidden by default)
|
|
g.profileLabel = widget.NewLabel("")
|
|
g.profileLabel.Hide()
|
|
|
|
// Set minimum width for preset dropdown
|
|
g.presetSelect.Resize(fyne.NewSize(200, g.presetSelect.MinSize().Height))
|
|
|
|
// Top bar with controls - AutoEQ in main row for better discoverability
|
|
topBar := container.NewVBox(
|
|
// Main row: EQ type, preset selector, AutoEQ, and action buttons
|
|
container.NewHBox(
|
|
widget.NewLabel(lang.L("EQ Type:")),
|
|
g.eqTypeSelect,
|
|
widget.NewLabel(lang.L("EQ Preset:")),
|
|
g.presetSelect,
|
|
layout.NewSpacer(),
|
|
g.saveBtn,
|
|
saveAsBtn,
|
|
deleteBtn,
|
|
resetBtn,
|
|
g.autoEQBtn,
|
|
),
|
|
// Second row: Profile label (shown only when AutoEQ profile is active)
|
|
g.profileLabel,
|
|
)
|
|
|
|
// Build slider area
|
|
g.sliderArea = g.buildSliderArea(preamp, bands, bandGains)
|
|
|
|
// Store topBar and create main container
|
|
g.topBar = topBar
|
|
g.container = container.NewBorder(g.topBar, nil, nil, nil, g.sliderArea)
|
|
}
|
|
|
|
func (g *GraphicEqualizer) buildSliderArea(preamp float64, bands []string, bandGains []float64) *fyne.Container {
|
|
// Range labels
|
|
rng := container.NewVBox(
|
|
newCaptionTextSizeLabel("+12", fyne.TextAlignTrailing),
|
|
layout.NewSpacer(),
|
|
newCaptionTextSizeLabel("0 dB", fyne.TextAlignTrailing),
|
|
layout.NewSpacer(),
|
|
newCaptionTextSizeLabel("-12", fyne.TextAlignTrailing),
|
|
)
|
|
|
|
g.bandSliders = make([]*eqSlider, len(bands))
|
|
bandSlidersCtr := container.New(layouts.NewGridLayoutWithColumnsAndPadding(len(bands)+2, -16))
|
|
|
|
// Preamp slider
|
|
pre := newCaptionTextSizeLabel(lang.L("EQ Preamp"), fyne.TextAlignCenter)
|
|
g.preampSlider = newEQSlider()
|
|
g.preampSlider.SetValue(preamp)
|
|
g.preampSlider.OnChanged = func(f float64) {
|
|
if g.OnPreampChanged != nil {
|
|
g.OnPreampChanged(f)
|
|
}
|
|
g.preampSlider.UpdateToolTip()
|
|
if !g.isApplyingPreset {
|
|
g.isDirty = true
|
|
g.updateSaveButtonState()
|
|
if g.OnManualAdjustment != nil {
|
|
g.OnManualAdjustment()
|
|
}
|
|
}
|
|
}
|
|
g.preampSlider.UpdateToolTip()
|
|
bandSlidersCtr.Add(container.NewBorder(nil, pre, nil, nil, g.preampSlider))
|
|
bandSlidersCtr.Add(container.NewBorder(nil, widget.NewLabel(""), nil, nil, rng))
|
|
|
|
// Band sliders
|
|
for i, band := range bands {
|
|
s := newEQSlider()
|
|
if i < len(bandGains) {
|
|
s.SetValue(bandGains[i])
|
|
s.UpdateToolTip()
|
|
}
|
|
_i := i
|
|
s.OnChanged = func(f float64) {
|
|
if g.OnChanged != nil {
|
|
g.OnChanged(_i, f)
|
|
}
|
|
g.bandSliders[_i].UpdateToolTip()
|
|
if !g.isApplyingPreset {
|
|
g.isDirty = true
|
|
g.updateSaveButtonState()
|
|
if g.OnManualAdjustment != nil {
|
|
g.OnManualAdjustment()
|
|
}
|
|
}
|
|
}
|
|
l := newCaptionTextSizeLabel(band, fyne.TextAlignCenter)
|
|
c := container.NewBorder(nil, l, nil, nil, s)
|
|
bandSlidersCtr.Add(c)
|
|
g.bandSliders[i] = s
|
|
}
|
|
|
|
return container.NewStack(
|
|
container.NewBorder(nil, widget.NewLabel(""), nil, nil,
|
|
container.NewBorder(nil, nil, util.NewHSpace(5), util.NewHSpace(5),
|
|
container.NewVBox(
|
|
layout.NewSpacer(),
|
|
myTheme.NewThemedRectangle(theme.ColorNameInputBackground),
|
|
layout.NewSpacer(),
|
|
),
|
|
),
|
|
),
|
|
bandSlidersCtr,
|
|
)
|
|
}
|
|
|
|
// RebuildForEQType rebuilds the sliders for a new EQ type
|
|
func (g *GraphicEqualizer) RebuildForEQType(eqType string, bandGains []float64) {
|
|
// Determine band frequencies for the new type
|
|
var bands []string
|
|
if eqType == "ISO10Band" {
|
|
bands = []string{"31", "62", "125", "250", "500", "1k", "2k", "4k", "8k", "16k"}
|
|
} else {
|
|
bands = []string{"25", "40", "63", "100", "160", "250", "400", "630", "1k", "1.6k", "2.5k", "4k", "6.3k", "10k", "16k"}
|
|
}
|
|
|
|
// Get current preamp value
|
|
currentPreamp := 0.0
|
|
if g.preampSlider != nil {
|
|
currentPreamp = g.preampSlider.Value
|
|
}
|
|
|
|
// Rebuild the slider area
|
|
newSliderArea := g.buildSliderArea(currentPreamp, bands, bandGains)
|
|
|
|
// Replace the old slider area in the container
|
|
g.sliderArea = newSliderArea
|
|
g.container.Objects = []fyne.CanvasObject{g.topBar, g.sliderArea}
|
|
g.container.Refresh()
|
|
|
|
// Clear loaded preset when EQ type changes
|
|
g.loadedPreset = nil
|
|
g.isDirty = false
|
|
g.updateSaveButtonState()
|
|
}
|
|
|
|
func (g *GraphicEqualizer) updatePresetSelect() {
|
|
presetNames := make([]string, len(g.eqPresets))
|
|
for i, p := range g.eqPresets {
|
|
displayName := p.Name
|
|
if !p.IsBuiltin {
|
|
displayName = p.Name + " *" // Mark custom presets with asterisk
|
|
}
|
|
presetNames[i] = displayName
|
|
}
|
|
|
|
if g.presetSelect == nil {
|
|
g.presetSelect = widget.NewSelect(presetNames, func(selected string) {
|
|
// Remove asterisk marker if present
|
|
cleanName := selected
|
|
if len(selected) > 2 && selected[len(selected)-2:] == " *" {
|
|
cleanName = selected[:len(selected)-2]
|
|
}
|
|
for _, p := range g.eqPresets {
|
|
if p.Name == cleanName {
|
|
g.applyPreset(p)
|
|
if g.OnPresetSelected != nil {
|
|
g.OnPresetSelected(cleanName)
|
|
}
|
|
break
|
|
}
|
|
}
|
|
})
|
|
g.presetSelect.PlaceHolder = lang.L("EQ Preset")
|
|
} else {
|
|
g.presetSelect.Options = presetNames
|
|
g.presetSelect.Refresh()
|
|
}
|
|
}
|
|
|
|
// setActivePreset sets the dropdown selection to match the given preset name
|
|
func (g *GraphicEqualizer) setActivePreset(presetName string) {
|
|
if presetName == "" {
|
|
return
|
|
}
|
|
|
|
// Find the preset and determine display name (with asterisk for custom)
|
|
for _, p := range g.eqPresets {
|
|
if p.Name == presetName {
|
|
displayName := p.Name
|
|
if !p.IsBuiltin {
|
|
displayName = p.Name + " *"
|
|
}
|
|
g.presetSelect.SetSelected(displayName)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (g *GraphicEqualizer) applyPreset(preset backend.EQPreset) {
|
|
g.isApplyingPreset = true
|
|
defer func() { g.isApplyingPreset = false }()
|
|
|
|
// If preset type differs from current type, switch type first
|
|
if preset.Type != "" && preset.Type != g.currentEQType {
|
|
g.currentEQType = preset.Type
|
|
// Update the type selector UI
|
|
if preset.Type == "ISO10Band" {
|
|
g.eqTypeSelect.SetSelected("ISO 10-Band")
|
|
} else {
|
|
g.eqTypeSelect.SetSelected("ISO 15-Band")
|
|
}
|
|
// Notify about type change
|
|
if g.OnEQTypeChanged != nil {
|
|
g.OnEQTypeChanged(preset.Type)
|
|
}
|
|
}
|
|
|
|
// Apply preamp
|
|
g.preampSlider.SetValue(preset.Preamp)
|
|
g.preampSlider.UpdateToolTip()
|
|
if g.OnPreampChanged != nil {
|
|
g.OnPreampChanged(preset.Preamp)
|
|
}
|
|
|
|
// Apply band gains
|
|
for i, gain := range preset.Bands {
|
|
if i < len(g.bandSliders) {
|
|
g.bandSliders[i].SetValue(gain)
|
|
g.bandSliders[i].UpdateToolTip()
|
|
if g.OnChanged != nil {
|
|
g.OnChanged(i, gain)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Track the loaded preset and clear dirty state
|
|
presetCopy := preset
|
|
g.loadedPreset = &presetCopy
|
|
g.isDirty = false
|
|
g.updateSaveButtonState()
|
|
}
|
|
|
|
func (g *GraphicEqualizer) getCurrentSettings() backend.EQPreset {
|
|
bands := make([]float64, len(g.bandSliders))
|
|
for i, slider := range g.bandSliders {
|
|
bands[i] = slider.Value
|
|
}
|
|
return backend.EQPreset{
|
|
Type: g.currentEQType,
|
|
Preamp: g.preampSlider.Value,
|
|
Bands: bands,
|
|
}
|
|
}
|
|
|
|
func (g *GraphicEqualizer) updateSaveButtonState() {
|
|
if g.saveBtn == nil {
|
|
return
|
|
}
|
|
if g.loadedPreset != nil && !g.loadedPreset.IsBuiltin && g.isDirty {
|
|
g.saveBtn.Enable()
|
|
} else {
|
|
g.saveBtn.Disable()
|
|
}
|
|
}
|
|
|
|
func (g *GraphicEqualizer) saveCurrentPreset() {
|
|
if g.loadedPreset == nil || g.loadedPreset.IsBuiltin {
|
|
return
|
|
}
|
|
g.savePresetWithName(g.loadedPreset.Name)
|
|
}
|
|
|
|
func (g *GraphicEqualizer) savePresetWithName(name string) {
|
|
preset := g.getCurrentSettings()
|
|
preset.Name = name
|
|
preset.IsBuiltin = false
|
|
|
|
if err := g.presetManager.SavePreset(preset); err != nil {
|
|
dialog.ShowError(err, g.parentWindow)
|
|
return
|
|
}
|
|
|
|
// Update loaded preset and clear dirty state
|
|
g.loadedPreset = &preset
|
|
g.isDirty = false
|
|
g.updateSaveButtonState()
|
|
|
|
// Reload presets and update UI
|
|
g.loadPresets()
|
|
g.updatePresetSelect()
|
|
|
|
// Select the newly saved preset
|
|
g.presetSelect.SetSelected(preset.Name + " *")
|
|
if g.OnPresetSelected != nil {
|
|
g.OnPresetSelected(preset.Name)
|
|
}
|
|
}
|
|
|
|
func (g *GraphicEqualizer) showSaveAsDialog() {
|
|
nameEntry := widget.NewEntry()
|
|
nameEntry.SetPlaceHolder(lang.L("Preset name"))
|
|
|
|
// Pre-fill with loaded preset name if it's a custom preset
|
|
if g.loadedPreset != nil && !g.loadedPreset.IsBuiltin {
|
|
nameEntry.SetText(g.loadedPreset.Name)
|
|
}
|
|
|
|
formDialog := dialog.NewForm(
|
|
lang.L("Save Preset As"),
|
|
lang.L("Save"),
|
|
lang.L("Cancel"),
|
|
[]*widget.FormItem{
|
|
widget.NewFormItem(lang.L("Name"), nameEntry),
|
|
},
|
|
func(confirmed bool) {
|
|
if !confirmed || nameEntry.Text == "" {
|
|
return
|
|
}
|
|
|
|
name := nameEntry.Text
|
|
|
|
// Check if name matches a builtin preset
|
|
for _, p := range g.eqPresets {
|
|
if p.Name == name && p.IsBuiltin {
|
|
dialog.ShowInformation(
|
|
lang.L("Invalid Name"),
|
|
lang.L("Cannot use the name of a builtin preset"),
|
|
g.parentWindow,
|
|
)
|
|
return
|
|
}
|
|
}
|
|
|
|
// Check if name matches an existing custom preset
|
|
for _, p := range g.eqPresets {
|
|
if p.Name == name && !p.IsBuiltin {
|
|
dialog.ShowConfirm(
|
|
lang.L("Overwrite Preset"),
|
|
fmt.Sprintf(lang.L("Preset '%s' already exists. Overwrite?"), name),
|
|
func(overwrite bool) {
|
|
if overwrite {
|
|
g.savePresetWithName(name)
|
|
}
|
|
},
|
|
g.parentWindow,
|
|
)
|
|
return
|
|
}
|
|
}
|
|
|
|
g.savePresetWithName(name)
|
|
},
|
|
g.parentWindow,
|
|
)
|
|
|
|
formDialog.Resize(fyne.NewSize(400, 150))
|
|
formDialog.Show()
|
|
}
|
|
|
|
// matchesPreset compares current slider values against a preset
|
|
func (g *GraphicEqualizer) matchesPreset(preset backend.EQPreset) bool {
|
|
if g.preampSlider == nil {
|
|
return false
|
|
}
|
|
if math.Abs(g.preampSlider.Value-preset.Preamp) > 0.05 {
|
|
return false
|
|
}
|
|
if len(g.bandSliders) != len(preset.Bands) {
|
|
return false
|
|
}
|
|
for i, slider := range g.bandSliders {
|
|
if math.Abs(slider.Value-preset.Bands[i]) > 0.05 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// ClearLoadedPresetState clears the loaded preset and dirty state
|
|
func (g *GraphicEqualizer) ClearLoadedPresetState() {
|
|
g.loadedPreset = nil
|
|
g.isDirty = false
|
|
g.updateSaveButtonState()
|
|
}
|
|
|
|
func (g *GraphicEqualizer) showDeletePresetDialog() {
|
|
selected := g.presetSelect.Selected
|
|
if selected == "" {
|
|
dialog.ShowInformation(lang.L("No Preset Selected"), lang.L("Please select a preset to delete"), g.parentWindow)
|
|
return
|
|
}
|
|
|
|
// Remove asterisk marker if present
|
|
cleanName := selected
|
|
if len(selected) > 2 && selected[len(selected)-2:] == " *" {
|
|
cleanName = selected[:len(selected)-2]
|
|
}
|
|
|
|
// Find the preset
|
|
var presetToDelete *backend.EQPreset
|
|
for i, p := range g.eqPresets {
|
|
if p.Name == cleanName {
|
|
presetToDelete = &g.eqPresets[i]
|
|
break
|
|
}
|
|
}
|
|
|
|
if presetToDelete == nil || presetToDelete.IsBuiltin {
|
|
dialog.ShowInformation(lang.L("Cannot Delete"), lang.L("Cannot delete builtin presets"), g.parentWindow)
|
|
return
|
|
}
|
|
|
|
dialog.ShowConfirm(
|
|
lang.L("Delete Preset"),
|
|
fmt.Sprintf(lang.L("Delete preset '%s'?"), cleanName),
|
|
func(confirmed bool) {
|
|
if !confirmed {
|
|
return
|
|
}
|
|
|
|
if err := g.presetManager.DeletePreset(cleanName); err != nil {
|
|
dialog.ShowError(err, g.parentWindow)
|
|
return
|
|
}
|
|
|
|
// Notify about deletion
|
|
if g.OnPresetDeleted != nil {
|
|
g.OnPresetDeleted(cleanName)
|
|
}
|
|
|
|
// Reload presets and update UI
|
|
g.loadPresets()
|
|
g.updatePresetSelect()
|
|
g.presetSelect.ClearSelected()
|
|
},
|
|
g.parentWindow,
|
|
)
|
|
}
|
|
|
|
// SetProfileLabel displays the name of the applied AutoEQ profile
|
|
func (g *GraphicEqualizer) SetProfileLabel(profileName string) {
|
|
if profileName == "" {
|
|
g.profileLabel.SetText("")
|
|
g.profileLabel.Hide()
|
|
} else {
|
|
g.profileLabel.SetText(fmt.Sprintf("%s: %s", lang.L("Profile"), profileName))
|
|
g.profileLabel.Show()
|
|
}
|
|
}
|
|
|
|
// ClearProfileLabel hides the profile label (called on manual adjustment)
|
|
func (g *GraphicEqualizer) ClearProfileLabel() {
|
|
g.SetProfileLabel("")
|
|
}
|
|
|
|
// ClearPresetSelection clears the preset dropdown selection
|
|
func (g *GraphicEqualizer) ClearPresetSelection() {
|
|
g.presetSelect.ClearSelected()
|
|
}
|
|
|
|
func newCaptionTextSizeLabel(text string, alignment fyne.TextAlign) *widget.RichText {
|
|
l := widget.NewRichTextWithText(text)
|
|
ts := l.Segments[0].(*widget.TextSegment)
|
|
ts.Style.SizeName = theme.SizeNameCaptionText
|
|
ts.Style.Alignment = alignment
|
|
return l
|
|
}
|
|
|
|
func (g *GraphicEqualizer) CreateRenderer() fyne.WidgetRenderer {
|
|
return widget.NewSimpleRenderer(g.container)
|
|
}
|
|
|
|
type eqSlider struct {
|
|
ttwidget.Slider
|
|
}
|
|
|
|
func newEQSlider() *eqSlider {
|
|
s := &eqSlider{
|
|
Slider: ttwidget.Slider{
|
|
Slider: widget.Slider{
|
|
Orientation: widget.Vertical,
|
|
Min: -12,
|
|
Max: 12,
|
|
Step: 0.1,
|
|
},
|
|
},
|
|
}
|
|
s.UpdateToolTip()
|
|
s.ExtendBaseWidget(s)
|
|
return s
|
|
}
|
|
|
|
func (s *eqSlider) UpdateToolTip() {
|
|
s.SetToolTip(fmt.Sprintf("%0.1f dB", s.Value))
|
|
}
|
|
|
|
// We implement our own double tapping so that the Tapped behavior
|
|
// can be triggered instantly.
|
|
func (s *eqSlider) DoubleTapped(e *fyne.PointEvent) {
|
|
s.SetValue(0)
|
|
}
|