feat: replace grey dropdowns with ColoredSelect in Convert & Merge modules (batch 1)
This is the first batch focusing on critical Convert module dropdowns and
some Merge module dropdowns. All dropdowns now have vibrant color coding
for faster visual navigation.
Convert Module - Replaced:
- Quality presets (simple & advanced) - quality gradient colors
- Bitrate mode select - generic rainbow colors
- CRF preset select - quality gradient colors
- Bitrate preset selects (×2) - quality gradient colors
- Target file size select - generic rainbow colors
- DVD aspect select - generic rainbow colors
Merge Module - Replaced:
- DVD region select (NTSC/PAL) - rainbow colors
- DVD aspect select (16:9/4:3) - rainbow colors
- Format select - semantic format colors (MKV=teal, MP4=blue, etc)
- Frame rate select - rainbow colors
ColoredSelect enhancements:
- Added UpdateOptions() method for dynamic option updates
- Added Enable() and Disable() methods
- Added disabled state tracking and visual feedback
- Fixed Selected() method to be callable (not a field)
Build status: ✅ Successful
This commit is contained in:
parent
eb8c553c71
commit
f8d05d3876
|
|
@ -1037,6 +1037,7 @@ type ColoredSelect struct {
|
|||
popup *widget.PopUp
|
||||
window fyne.Window
|
||||
placeHolder string
|
||||
disabled bool
|
||||
}
|
||||
|
||||
// NewColoredSelect creates a new colored select widget
|
||||
|
|
@ -1066,11 +1067,41 @@ func (cs *ColoredSelect) SetSelected(option string) {
|
|||
cs.Refresh()
|
||||
}
|
||||
|
||||
// UpdateOptions updates the available options and their colors
|
||||
func (cs *ColoredSelect) UpdateOptions(options []string, colorMap map[string]color.Color) {
|
||||
cs.options = options
|
||||
cs.colorMap = colorMap
|
||||
// If current selection is not in new options, select first option
|
||||
found := false
|
||||
for _, opt := range options {
|
||||
if opt == cs.selected {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found && len(options) > 0 {
|
||||
cs.selected = options[0]
|
||||
}
|
||||
cs.Refresh()
|
||||
}
|
||||
|
||||
// Selected returns the currently selected option
|
||||
func (cs *ColoredSelect) Selected() string {
|
||||
return cs.selected
|
||||
}
|
||||
|
||||
// Enable enables the widget
|
||||
func (cs *ColoredSelect) Enable() {
|
||||
cs.disabled = false
|
||||
cs.Refresh()
|
||||
}
|
||||
|
||||
// Disable disables the widget
|
||||
func (cs *ColoredSelect) Disable() {
|
||||
cs.disabled = true
|
||||
cs.Refresh()
|
||||
}
|
||||
|
||||
// CreateRenderer creates the renderer for the colored select
|
||||
func (cs *ColoredSelect) CreateRenderer() fyne.WidgetRenderer {
|
||||
// Create the button that shows current selection
|
||||
|
|
@ -1153,7 +1184,9 @@ func (cs *ColoredSelect) showPopup() {
|
|||
|
||||
// Tapped implements the Tappable interface
|
||||
func (cs *ColoredSelect) Tapped(*fyne.PointEvent) {
|
||||
cs.showPopup()
|
||||
if !cs.disabled {
|
||||
cs.showPopup()
|
||||
}
|
||||
}
|
||||
|
||||
type coloredSelectRenderer struct {
|
||||
|
|
|
|||
162
main.go
162
main.go
|
|
@ -3463,16 +3463,20 @@ func (s *appState) showMergeView() {
|
|||
}
|
||||
|
||||
// DVD-specific options
|
||||
dvdRegionSelect := widget.NewSelect([]string{"NTSC", "PAL"}, func(val string) {
|
||||
dvdRegionOptions := []string{"NTSC", "PAL"}
|
||||
dvdRegionColors := ui.BuildGenericColorMap(dvdRegionOptions)
|
||||
dvdRegionSelect := ui.NewColoredSelect(dvdRegionOptions, dvdRegionColors, func(val string) {
|
||||
s.mergeDVDRegion = val
|
||||
s.persistMergeConfig()
|
||||
})
|
||||
}, s.window)
|
||||
dvdRegionSelect.SetSelected(s.mergeDVDRegion)
|
||||
|
||||
dvdAspectSelect := widget.NewSelect([]string{"16:9", "4:3"}, func(val string) {
|
||||
dvdAspectOptions := []string{"16:9", "4:3"}
|
||||
dvdAspectColors := ui.BuildGenericColorMap(dvdAspectOptions)
|
||||
dvdAspectSelect := ui.NewColoredSelect(dvdAspectOptions, dvdAspectColors, func(val string) {
|
||||
s.mergeDVDAspect = val
|
||||
s.persistMergeConfig()
|
||||
})
|
||||
}, s.window)
|
||||
dvdAspectSelect.SetSelected(s.mergeDVDAspect)
|
||||
|
||||
dvdOptionsRow := container.NewHBox(
|
||||
|
|
@ -3486,7 +3490,8 @@ func (s *appState) showMergeView() {
|
|||
dvdOptionsContainer := container.NewVBox(dvdOptionsRow)
|
||||
|
||||
// Create format selector (after outputEntry and updateOutputExt are defined)
|
||||
formatSelect := widget.NewSelect(formatKeys, func(val string) {
|
||||
formatColors := ui.BuildFormatColorMap(formatKeys)
|
||||
formatSelect := ui.NewColoredSelect(formatKeys, formatColors, func(val string) {
|
||||
s.mergeFormat = formatMap[val]
|
||||
|
||||
// Show/hide DVD options based on selection
|
||||
|
|
@ -3520,7 +3525,7 @@ func (s *appState) showMergeView() {
|
|||
updateOutputExt()
|
||||
}
|
||||
s.persistMergeConfig()
|
||||
})
|
||||
}, s.window)
|
||||
for label, val := range formatMap {
|
||||
if val == s.mergeFormat {
|
||||
formatSelect.SetSelected(label)
|
||||
|
|
@ -3536,10 +3541,12 @@ func (s *appState) showMergeView() {
|
|||
}
|
||||
|
||||
// Frame Rate controls
|
||||
frameRateSelect := widget.NewSelect([]string{"Source", "23.976", "24", "25", "29.97", "30", "50", "59.94", "60"}, func(val string) {
|
||||
frameRateOptions := []string{"Source", "23.976", "24", "25", "29.97", "30", "50", "59.94", "60"}
|
||||
frameRateColors := ui.BuildGenericColorMap(frameRateOptions)
|
||||
frameRateSelect := ui.NewColoredSelect(frameRateOptions, frameRateColors, func(val string) {
|
||||
s.mergeFrameRate = val
|
||||
s.persistMergeConfig()
|
||||
})
|
||||
}, s.window)
|
||||
frameRateSelect.SetSelected(s.mergeFrameRate)
|
||||
|
||||
motionInterpCheck := widget.NewCheck("Use Motion Interpolation (slower, smoother)", func(checked bool) {
|
||||
|
|
@ -6528,10 +6535,12 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
|||
outputHintContainer := container.NewPadded(outputHint)
|
||||
|
||||
// DVD-specific aspect ratio selector (only shown for DVD formats)
|
||||
dvdAspectSelect := widget.NewSelect([]string{"4:3", "16:9"}, func(value string) {
|
||||
dvdAspectOpts := []string{"4:3", "16:9"}
|
||||
dvdAspectCols := ui.BuildGenericColorMap(dvdAspectOpts)
|
||||
dvdAspectSelect := ui.NewColoredSelect(dvdAspectOpts, dvdAspectCols, func(value string) {
|
||||
logging.Debug(logging.CatUI, "DVD aspect set to %s", value)
|
||||
state.convert.OutputAspect = value
|
||||
})
|
||||
}, state.window)
|
||||
dvdAspectSelect.SetSelected("16:9")
|
||||
dvdAspectLabel := widget.NewLabelWithStyle("DVD Aspect Ratio", fyne.TextAlignLeading, fyne.TextStyle{Bold: true})
|
||||
|
||||
|
|
@ -6554,20 +6563,20 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
|||
|
||||
// Forward declarations for encoding controls (used in reset/update callbacks)
|
||||
var (
|
||||
bitrateModeSelect *widget.Select
|
||||
bitratePresetSelect *widget.Select
|
||||
crfPresetSelect *widget.Select
|
||||
bitrateModeSelect *ui.ColoredSelect
|
||||
bitratePresetSelect *ui.ColoredSelect
|
||||
crfPresetSelect *ui.ColoredSelect
|
||||
crfEntry *widget.Entry
|
||||
manualCrfRow *fyne.Container
|
||||
videoBitrateEntry *widget.Entry
|
||||
manualBitrateRow *fyne.Container
|
||||
targetFileSizeSelect *widget.Select
|
||||
targetFileSizeSelect *ui.ColoredSelect
|
||||
targetFileSizeEntry *widget.Entry
|
||||
qualitySelectSimple *widget.Select
|
||||
qualitySelectAdv *widget.Select
|
||||
qualitySelectSimple *ui.ColoredSelect
|
||||
qualitySelectAdv *ui.ColoredSelect
|
||||
qualitySectionSimple fyne.CanvasObject
|
||||
qualitySectionAdv fyne.CanvasObject
|
||||
simpleBitrateSelect *widget.Select
|
||||
simpleBitrateSelect *ui.ColoredSelect
|
||||
crfContainer *fyne.Container
|
||||
bitrateContainer *fyne.Container
|
||||
targetSizeContainer *fyne.Container
|
||||
|
|
@ -6608,7 +6617,8 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
|||
|
||||
var syncingQuality bool
|
||||
|
||||
qualitySelectSimple = widget.NewSelect(qualityOptions, func(value string) {
|
||||
qualityColors := ui.BuildQualityColorMap(qualityOptions)
|
||||
qualitySelectSimple = ui.NewColoredSelect(qualityOptions, qualityColors, func(value string) {
|
||||
if syncingQuality {
|
||||
return
|
||||
}
|
||||
|
|
@ -6625,9 +6635,9 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
|||
if buildCommandPreview != nil {
|
||||
buildCommandPreview()
|
||||
}
|
||||
})
|
||||
}, state.window)
|
||||
|
||||
qualitySelectAdv = widget.NewSelect(qualityOptions, func(value string) {
|
||||
qualitySelectAdv = ui.NewColoredSelect(qualityOptions, qualityColors, func(value string) {
|
||||
if syncingQuality {
|
||||
return
|
||||
}
|
||||
|
|
@ -6644,7 +6654,7 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
|||
if buildCommandPreview != nil {
|
||||
buildCommandPreview()
|
||||
}
|
||||
})
|
||||
}, state.window)
|
||||
|
||||
if !slices.Contains(qualityOptions, state.convert.Quality) {
|
||||
state.convert.Quality = "Standard (CRF 23)"
|
||||
|
|
@ -6667,12 +6677,11 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
|||
}
|
||||
}
|
||||
|
||||
qualitySelectSimple.Options = newOptions
|
||||
qualitySelectAdv.Options = newOptions
|
||||
newColors := ui.BuildQualityColorMap(newOptions)
|
||||
qualitySelectSimple.UpdateOptions(newOptions, newColors)
|
||||
qualitySelectAdv.UpdateOptions(newOptions, newColors)
|
||||
qualitySelectSimple.SetSelected(state.convert.Quality)
|
||||
qualitySelectAdv.SetSelected(state.convert.Quality)
|
||||
qualitySelectSimple.Refresh()
|
||||
qualitySelectAdv.Refresh()
|
||||
}
|
||||
|
||||
outputEntry := widget.NewEntry()
|
||||
|
|
@ -7253,7 +7262,8 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
|||
"VBR": "VBR (Variable Bitrate)",
|
||||
"Target Size": "Target Size (Calculate from file size)",
|
||||
}
|
||||
bitrateModeSelect = widget.NewSelect(bitrateModeOptions, func(value string) {
|
||||
bitrateModeColors := ui.BuildGenericColorMap(bitrateModeOptions)
|
||||
bitrateModeSelect = ui.NewColoredSelect(bitrateModeOptions, bitrateModeColors, func(value string) {
|
||||
// Extract short code from label
|
||||
if shortCode, ok := bitrateModeMap[value]; ok {
|
||||
state.convert.BitrateMode = shortCode
|
||||
|
|
@ -7267,7 +7277,7 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
|||
if buildCommandPreview != nil {
|
||||
buildCommandPreview()
|
||||
}
|
||||
})
|
||||
}, state.window)
|
||||
// Set selected using full label
|
||||
if fullLabel, ok := reverseMap[state.convert.BitrateMode]; ok {
|
||||
bitrateModeSelect.SetSelected(fullLabel)
|
||||
|
|
@ -7300,7 +7310,8 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
|||
"28 (Draft)",
|
||||
"Manual",
|
||||
}
|
||||
crfPresetSelect = widget.NewSelect(crfPresetOptions, func(value string) {
|
||||
crfPresetColors := ui.BuildQualityColorMap(crfPresetOptions)
|
||||
crfPresetSelect = ui.NewColoredSelect(crfPresetOptions, crfPresetColors, func(value string) {
|
||||
switch value {
|
||||
case "Auto (from Quality preset)":
|
||||
state.convert.CRF = ""
|
||||
|
|
@ -7328,7 +7339,7 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
|||
if buildCommandPreview != nil {
|
||||
buildCommandPreview()
|
||||
}
|
||||
})
|
||||
}, state.window)
|
||||
switch state.convert.CRF {
|
||||
case "":
|
||||
crfPresetSelect.SetSelected("Auto (from Quality preset)")
|
||||
|
|
@ -7563,14 +7574,15 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
|||
var setBitratePreset func(string)
|
||||
var syncingBitratePreset bool
|
||||
|
||||
bitratePresetSelect = widget.NewSelect(bitratePresetLabels, func(value string) {
|
||||
bitratePresetColors := ui.BuildQualityColorMap(bitratePresetLabels)
|
||||
bitratePresetSelect = ui.NewColoredSelect(bitratePresetLabels, bitratePresetColors, func(value string) {
|
||||
if syncingBitratePreset {
|
||||
return
|
||||
}
|
||||
if setBitratePreset != nil {
|
||||
setBitratePreset(value)
|
||||
}
|
||||
})
|
||||
}, state.window)
|
||||
state.convert.BitratePreset = normalizePresetLabel(state.convert.BitratePreset)
|
||||
if state.convert.BitratePreset == "" || bitratePresetLookup[state.convert.BitratePreset].Label == "" {
|
||||
state.convert.BitratePreset = "2.5 Mbps - Medium"
|
||||
|
|
@ -7578,14 +7590,14 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
|||
bitratePresetSelect.SetSelected(state.convert.BitratePreset)
|
||||
|
||||
// Simple bitrate selector (shares presets)
|
||||
simpleBitrateSelect = widget.NewSelect(bitratePresetLabels, func(value string) {
|
||||
simpleBitrateSelect = ui.NewColoredSelect(bitratePresetLabels, bitratePresetColors, func(value string) {
|
||||
if syncingBitratePreset {
|
||||
return
|
||||
}
|
||||
if setBitratePreset != nil {
|
||||
setBitratePreset(value)
|
||||
}
|
||||
})
|
||||
}, state.window)
|
||||
simpleBitrateSelect.SetSelected(state.convert.BitratePreset)
|
||||
|
||||
// Manual bitrate row (hidden by default)
|
||||
|
|
@ -7732,50 +7744,50 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
|||
}
|
||||
|
||||
updateTargetSizeOptions := func() {
|
||||
if src == nil {
|
||||
targetFileSizeSelect.Options = []string{"Manual", "25MB", "50MB", "100MB", "200MB", "500MB", "1GB"}
|
||||
return
|
||||
options := []string{"Manual", "25MB", "50MB", "100MB", "200MB", "500MB", "1GB"}
|
||||
|
||||
if src != nil {
|
||||
// Calculate smart reduction options based on source file size
|
||||
srcPath := src.Path
|
||||
fileInfo, err := os.Stat(srcPath)
|
||||
if err == nil {
|
||||
srcSize := fileInfo.Size()
|
||||
srcSizeMB := float64(srcSize) / (1024 * 1024)
|
||||
|
||||
// Calculate smart reductions
|
||||
size25 := int(srcSizeMB * 0.75) // 25% reduction
|
||||
size33 := int(srcSizeMB * 0.67) // 33% reduction
|
||||
size50 := int(srcSizeMB * 0.50) // 50% reduction
|
||||
size75 := int(srcSizeMB * 0.25) // 75% reduction
|
||||
|
||||
smartOptions := []string{"Manual"}
|
||||
|
||||
if size75 > 5 {
|
||||
smartOptions = append(smartOptions, fmt.Sprintf("%dMB (75%% smaller)", size75))
|
||||
}
|
||||
if size50 > 10 {
|
||||
smartOptions = append(smartOptions, fmt.Sprintf("%dMB (50%% smaller)", size50))
|
||||
}
|
||||
if size33 > 15 {
|
||||
smartOptions = append(smartOptions, fmt.Sprintf("%dMB (33%% smaller)", size33))
|
||||
}
|
||||
if size25 > 20 {
|
||||
smartOptions = append(smartOptions, fmt.Sprintf("%dMB (25%% smaller)", size25))
|
||||
}
|
||||
|
||||
// Add common sizes
|
||||
smartOptions = append(smartOptions, "25MB", "50MB", "100MB", "200MB", "500MB", "1GB")
|
||||
options = smartOptions
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate smart reduction options based on source file size
|
||||
srcPath := src.Path
|
||||
fileInfo, err := os.Stat(srcPath)
|
||||
if err != nil {
|
||||
targetFileSizeSelect.Options = []string{"Manual", "25MB", "50MB", "100MB", "200MB", "500MB", "1GB"}
|
||||
return
|
||||
}
|
||||
|
||||
srcSize := fileInfo.Size()
|
||||
srcSizeMB := float64(srcSize) / (1024 * 1024)
|
||||
|
||||
// Calculate smart reductions
|
||||
size25 := int(srcSizeMB * 0.75) // 25% reduction
|
||||
size33 := int(srcSizeMB * 0.67) // 33% reduction
|
||||
size50 := int(srcSizeMB * 0.50) // 50% reduction
|
||||
size75 := int(srcSizeMB * 0.25) // 75% reduction
|
||||
|
||||
options := []string{"Manual"}
|
||||
|
||||
if size75 > 5 {
|
||||
options = append(options, fmt.Sprintf("%dMB (75%% smaller)", size75))
|
||||
}
|
||||
if size50 > 10 {
|
||||
options = append(options, fmt.Sprintf("%dMB (50%% smaller)", size50))
|
||||
}
|
||||
if size33 > 15 {
|
||||
options = append(options, fmt.Sprintf("%dMB (33%% smaller)", size33))
|
||||
}
|
||||
if size25 > 20 {
|
||||
options = append(options, fmt.Sprintf("%dMB (25%% smaller)", size25))
|
||||
}
|
||||
|
||||
// Add common sizes
|
||||
options = append(options, "25MB", "50MB", "100MB", "200MB", "500MB", "1GB")
|
||||
|
||||
targetFileSizeSelect.Options = options
|
||||
targetSizeColors := ui.BuildGenericColorMap(options)
|
||||
targetFileSizeSelect.UpdateOptions(options, targetSizeColors)
|
||||
}
|
||||
|
||||
targetFileSizeSelect = widget.NewSelect([]string{"25MB", "50MB", "100MB", "200MB", "500MB", "1GB", "Manual"}, func(value string) {
|
||||
targetSizeOpts := []string{"25MB", "50MB", "100MB", "200MB", "500MB", "1GB", "Manual"}
|
||||
targetSizeCols := ui.BuildGenericColorMap(targetSizeOpts)
|
||||
targetFileSizeSelect = ui.NewColoredSelect(targetSizeOpts, targetSizeCols, func(value string) {
|
||||
if value == "Manual" {
|
||||
targetSizeManualRow.Show()
|
||||
if state.convert.TargetFileSize != "" {
|
||||
|
|
@ -7810,7 +7822,7 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
|||
targetSizeManualRow.Hide()
|
||||
}
|
||||
logging.Debug(logging.CatUI, "target file size set to %s", state.convert.TargetFileSize)
|
||||
})
|
||||
}, state.window)
|
||||
targetFileSizeSelect.SetSelected("100MB")
|
||||
updateTargetSizeOptions()
|
||||
|
||||
|
|
@ -8164,7 +8176,7 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
|||
)
|
||||
|
||||
// Prefer the explicit DVD aspect select if set; otherwise derive from source
|
||||
targetAR = dvdAspectSelect.Selected
|
||||
targetAR = dvdAspectSelect.Selected()
|
||||
|
||||
if strings.Contains(state.convert.SelectedFormat.Label, "NTSC") {
|
||||
dvdNotes = "NTSC DVD: 720×480 @ 29.97fps, MPEG-2 Video, AC-3 Stereo 48kHz (bitrate 8000k, 9000k max PS2-safe)"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user