Add styled output folder and filename rows to convert UI
This commit is contained in:
parent
2f76ffb9c4
commit
586b84c39d
150
main.go
150
main.go
|
|
@ -628,6 +628,7 @@ var formatOptions = []formatOption{
|
||||||
|
|
||||||
type convertConfig struct {
|
type convertConfig struct {
|
||||||
OutputBase string
|
OutputBase string
|
||||||
|
OutputDir string
|
||||||
SelectedFormat formatOption
|
SelectedFormat formatOption
|
||||||
Quality string // Preset quality (Draft/Standard/High/Lossless)
|
Quality string // Preset quality (Draft/Standard/High/Lossless)
|
||||||
Mode string // Simple or Advanced
|
Mode string // Simple or Advanced
|
||||||
|
|
@ -699,6 +700,7 @@ func defaultConvertConfig() convertConfig {
|
||||||
return convertConfig{
|
return convertConfig{
|
||||||
SelectedFormat: formatOptions[0],
|
SelectedFormat: formatOptions[0],
|
||||||
OutputBase: "converted",
|
OutputBase: "converted",
|
||||||
|
OutputDir: "",
|
||||||
Quality: "Standard (CRF 23)",
|
Quality: "Standard (CRF 23)",
|
||||||
Mode: "Simple",
|
Mode: "Simple",
|
||||||
UseAutoNaming: false,
|
UseAutoNaming: false,
|
||||||
|
|
@ -2202,7 +2204,10 @@ func (s *appState) addConvertToQueueForSource(src *videoSource, addToTop bool) e
|
||||||
cfg := s.convert
|
cfg := s.convert
|
||||||
cfg.OutputBase = outputBase
|
cfg.OutputBase = outputBase
|
||||||
|
|
||||||
outDir := filepath.Dir(src.Path)
|
outDir := strings.TrimSpace(cfg.OutputDir)
|
||||||
|
if outDir == "" {
|
||||||
|
outDir = filepath.Dir(src.Path)
|
||||||
|
}
|
||||||
outName := cfg.OutputFile()
|
outName := cfg.OutputFile()
|
||||||
if outName == "" {
|
if outName == "" {
|
||||||
outName = "converted" + cfg.SelectedFormat.Ext
|
outName = "converted" + cfg.SelectedFormat.Ext
|
||||||
|
|
@ -2228,6 +2233,7 @@ func (s *appState) addConvertToQueueForSource(src *videoSource, addToTop bool) e
|
||||||
config := map[string]interface{}{
|
config := map[string]interface{}{
|
||||||
"inputPath": src.Path,
|
"inputPath": src.Path,
|
||||||
"outputPath": outPath,
|
"outputPath": outPath,
|
||||||
|
"outputDir": outDir,
|
||||||
"outputBase": cfg.OutputBase,
|
"outputBase": cfg.OutputBase,
|
||||||
"selectedFormat": cfg.SelectedFormat,
|
"selectedFormat": cfg.SelectedFormat,
|
||||||
"quality": cfg.Quality,
|
"quality": cfg.Quality,
|
||||||
|
|
@ -6981,6 +6987,16 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
if updateDVDOptions != nil {
|
if updateDVDOptions != nil {
|
||||||
updateDVDOptions()
|
updateDVDOptions()
|
||||||
}
|
}
|
||||||
|
if outputExtLabel != nil {
|
||||||
|
outputExtLabel.SetText(state.convert.SelectedFormat.Ext)
|
||||||
|
}
|
||||||
|
if outputExtBG != nil {
|
||||||
|
outputExtBG.FillColor = ui.GetContainerColor(strings.TrimPrefix(state.convert.SelectedFormat.Ext, "."))
|
||||||
|
outputExtBG.Refresh()
|
||||||
|
}
|
||||||
|
if updateOutputHint != nil {
|
||||||
|
updateOutputHint()
|
||||||
|
}
|
||||||
if buildCommandPreview != nil {
|
if buildCommandPreview != nil {
|
||||||
buildCommandPreview()
|
buildCommandPreview()
|
||||||
}
|
}
|
||||||
|
|
@ -6990,11 +7006,37 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
}, state.window)
|
}, state.window)
|
||||||
formatContainer.SetSelected(state.convert.SelectedFormat.Label)
|
formatContainer.SetSelected(state.convert.SelectedFormat.Label)
|
||||||
|
|
||||||
outputHint := widget.NewLabel(fmt.Sprintf("Output file: %s", state.convert.OutputFile()))
|
var outputExtLabel *widget.Label
|
||||||
|
var outputExtBG *canvas.Rectangle
|
||||||
|
var updateOutputHint func()
|
||||||
|
|
||||||
|
getOutputDir := func() string {
|
||||||
|
if strings.TrimSpace(state.convert.OutputDir) != "" {
|
||||||
|
return state.convert.OutputDir
|
||||||
|
}
|
||||||
|
if src != nil {
|
||||||
|
return filepath.Dir(src.Path)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
getOutputPathPreview := func() string {
|
||||||
|
outDir := getOutputDir()
|
||||||
|
if outDir == "" {
|
||||||
|
return state.convert.OutputFile()
|
||||||
|
}
|
||||||
|
return filepath.Join(outDir, state.convert.OutputFile())
|
||||||
|
}
|
||||||
|
|
||||||
|
outputHint := widget.NewLabel(fmt.Sprintf("Output file: %s", getOutputPathPreview()))
|
||||||
outputHint.Wrapping = fyne.TextWrapWord
|
outputHint.Wrapping = fyne.TextWrapWord
|
||||||
// Wrap hint in padded container to ensure proper text wrapping in narrow windows
|
// Wrap hint in padded container to ensure proper text wrapping in narrow windows
|
||||||
outputHintContainer := container.NewPadded(outputHint)
|
outputHintContainer := container.NewPadded(outputHint)
|
||||||
|
|
||||||
|
updateOutputHint = func() {
|
||||||
|
outputHint.SetText(fmt.Sprintf("Output file: %s", getOutputPathPreview()))
|
||||||
|
}
|
||||||
|
|
||||||
// DVD-specific aspect ratio selector (only shown for DVD formats)
|
// DVD-specific aspect ratio selector (only shown for DVD formats)
|
||||||
dvdAspectOpts := []string{"4:3", "16:9"}
|
dvdAspectOpts := []string{"4:3", "16:9"}
|
||||||
dvdAspectSelect := widget.NewSelect(dvdAspectOpts, func(value string) {
|
dvdAspectSelect := widget.NewSelect(dvdAspectOpts, func(value string) {
|
||||||
|
|
@ -7072,21 +7114,24 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quality select widgets - use state manager to eliminate sync flags
|
// Quality select widgets - use state manager to eliminate sync flags
|
||||||
qualitySelectSimple = widget.NewSelect(qualityOptions, func(value string) {
|
// Convert quality selects to ColoredSelect and register with state manager
|
||||||
|
qualityColorMap := ui.BuildQualityColorMap(qualityOptions)
|
||||||
|
|
||||||
|
qualitySelectSimple = ui.NewColoredSelect(qualityOptions, qualityColorMap, func(value string) {
|
||||||
logging.Debug(logging.CatUI, "quality preset %s (simple)", value)
|
logging.Debug(logging.CatUI, "quality preset %s (simple)", value)
|
||||||
setQuality(value)
|
setQuality(value)
|
||||||
if buildCommandPreview != nil {
|
if buildCommandPreview != nil {
|
||||||
buildCommandPreview()
|
buildCommandPreview()
|
||||||
}
|
}
|
||||||
})
|
}, state.window)
|
||||||
|
|
||||||
qualitySelectAdv = widget.NewSelect(qualityOptions, func(value string) {
|
qualitySelectAdv = ui.NewColoredSelect(qualityOptions, qualityColorMap, func(value string) {
|
||||||
logging.Debug(logging.CatUI, "quality preset %s (advanced)", value)
|
logging.Debug(logging.CatUI, "quality preset %s (advanced)", value)
|
||||||
setQuality(value)
|
setQuality(value)
|
||||||
if buildCommandPreview != nil {
|
if buildCommandPreview != nil {
|
||||||
buildCommandPreview()
|
buildCommandPreview()
|
||||||
}
|
}
|
||||||
})
|
}, state.window)
|
||||||
|
|
||||||
if !slices.Contains(qualityOptions, state.convert.Quality) {
|
if !slices.Contains(qualityOptions, state.convert.Quality) {
|
||||||
state.convert.Quality = "Standard (CRF 23)"
|
state.convert.Quality = "Standard (CRF 23)"
|
||||||
|
|
@ -7094,6 +7139,9 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
qualitySelectSimple.SetSelected(state.convert.Quality)
|
qualitySelectSimple.SetSelected(state.convert.Quality)
|
||||||
qualitySelectAdv.SetSelected(state.convert.Quality)
|
qualitySelectAdv.SetSelected(state.convert.Quality)
|
||||||
|
|
||||||
|
// Register both quality widgets with state manager for automatic synchronization
|
||||||
|
uiState.qualityWidgets = []*ui.ColoredSelect{qualitySelectSimple, qualitySelectAdv}
|
||||||
|
|
||||||
// Update quality options based on codec
|
// Update quality options based on codec
|
||||||
updateQualityOptions = func() {
|
updateQualityOptions = func() {
|
||||||
var newOptions []string
|
var newOptions []string
|
||||||
|
|
@ -7109,12 +7157,16 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qualitySelectSimple.Options = newOptions
|
// Update options and color map for all registered quality widgets
|
||||||
qualitySelectAdv.Options = newOptions
|
qualityColorMap := ui.BuildQualityColorMap(newOptions)
|
||||||
qualitySelectSimple.SetSelected(state.convert.Quality)
|
for _, w := range uiState.qualityWidgets {
|
||||||
qualitySelectAdv.SetSelected(state.convert.Quality)
|
w.Options = newOptions
|
||||||
qualitySelectSimple.Refresh()
|
w.ColorMap = qualityColorMap
|
||||||
qualitySelectAdv.Refresh()
|
w.Refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use state manager to synchronize selected value across all widgets
|
||||||
|
setQuality(state.convert.Quality)
|
||||||
}
|
}
|
||||||
|
|
||||||
outputEntry := widget.NewEntry()
|
outputEntry := widget.NewEntry()
|
||||||
|
|
@ -7132,9 +7184,60 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.convert.OutputBase = val
|
state.convert.OutputBase = val
|
||||||
outputHint.SetText(fmt.Sprintf("Output file: %s", state.convert.OutputFile()))
|
updateOutputHint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outputDirEntry := widget.NewEntry()
|
||||||
|
outputDirEntry.SetPlaceHolder("Output folder path")
|
||||||
|
outputDirEntry.SetText(state.convert.OutputDir)
|
||||||
|
outputDirEntry.OnChanged = func(val string) {
|
||||||
|
state.convert.OutputDir = val
|
||||||
|
updateOutputHint()
|
||||||
|
state.persistConvertConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
browseOutputDir := func() {
|
||||||
|
dialog.ShowFolderOpen(func(uri fyne.ListableURI, err error) {
|
||||||
|
if err != nil {
|
||||||
|
dialog.ShowError(err, state.window)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if uri == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
state.convert.OutputDir = uri.Path()
|
||||||
|
outputDirEntry.SetText(state.convert.OutputDir)
|
||||||
|
updateOutputHint()
|
||||||
|
state.persistConvertConfig()
|
||||||
|
}, state.window)
|
||||||
|
}
|
||||||
|
|
||||||
|
outputDirBtnLabel := canvas.NewText("Browse", textColor)
|
||||||
|
outputDirBtnLabel.Alignment = fyne.TextAlignCenter
|
||||||
|
outputDirBtnLabel.TextSize = 14
|
||||||
|
outputDirBtnBG := canvas.NewRectangle(utils.MustHex("#344256"))
|
||||||
|
outputDirBtnBG.CornerRadius = 8
|
||||||
|
outputDirBtnBG.SetMinSize(fyne.NewSize(92, 36))
|
||||||
|
outputDirBtn := ui.NewTappable(container.NewMax(outputDirBtnBG, container.NewPadded(outputDirBtnLabel)), browseOutputDir)
|
||||||
|
|
||||||
|
outputExtLabel = widget.NewLabel(state.convert.SelectedFormat.Ext)
|
||||||
|
outputExtLabel.Alignment = fyne.TextAlignCenter
|
||||||
|
outputExtBG = canvas.NewRectangle(ui.GetContainerColor(strings.TrimPrefix(state.convert.SelectedFormat.Ext, ".")))
|
||||||
|
outputExtBG.CornerRadius = 8
|
||||||
|
outputExtBG.SetMinSize(fyne.NewSize(72, 36))
|
||||||
|
outputExtPill := container.NewMax(outputExtBG, container.NewPadded(outputExtLabel))
|
||||||
|
|
||||||
|
buildOutputRow := func(entry *widget.Entry, right fyne.CanvasObject) fyne.CanvasObject {
|
||||||
|
bg := canvas.NewRectangle(utils.MustHex("#344256"))
|
||||||
|
bg.CornerRadius = 8
|
||||||
|
bg.SetMinSize(fyne.NewSize(0, 36))
|
||||||
|
row := container.NewBorder(nil, nil, nil, right, entry)
|
||||||
|
return container.NewMax(bg, container.NewPadded(row))
|
||||||
|
}
|
||||||
|
|
||||||
|
outputDirRow := buildOutputRow(outputDirEntry, outputDirBtn)
|
||||||
|
outputNameRow := buildOutputRow(outputEntry, outputExtPill)
|
||||||
|
|
||||||
applyAutoName := func(force bool) {
|
applyAutoName := func(force bool) {
|
||||||
if !force && !state.convert.UseAutoNaming {
|
if !force && !state.convert.UseAutoNaming {
|
||||||
return
|
return
|
||||||
|
|
@ -7144,7 +7247,7 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
state.convert.OutputBase = newBase
|
state.convert.OutputBase = newBase
|
||||||
outputEntry.SetText(newBase)
|
outputEntry.SetText(newBase)
|
||||||
updatingOutput = false
|
updatingOutput = false
|
||||||
outputHint.SetText(fmt.Sprintf("Output file: %s", state.convert.OutputFile()))
|
updateOutputHint()
|
||||||
}
|
}
|
||||||
|
|
||||||
autoNameCheck = widget.NewCheck("Auto-name from metadata", func(checked bool) {
|
autoNameCheck = widget.NewCheck("Auto-name from metadata", func(checked bool) {
|
||||||
|
|
@ -7182,7 +7285,7 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
updatingOutput = false
|
updatingOutput = false
|
||||||
// Update output hint to show the change immediately
|
// Update output hint to show the change immediately
|
||||||
if outputHint != nil {
|
if outputHint != nil {
|
||||||
outputHint.SetText(fmt.Sprintf("Output file: %s", state.convert.OutputFile()))
|
updateOutputHint()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
appendSuffixCheck.Checked = state.convert.AppendSuffix
|
appendSuffixCheck.Checked = state.convert.AppendSuffix
|
||||||
|
|
@ -8741,8 +8844,10 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
chapterWarningLabel, // Warning when converting chapters to DVD
|
chapterWarningLabel, // Warning when converting chapters to DVD
|
||||||
preserveChaptersCheck,
|
preserveChaptersCheck,
|
||||||
dvdAspectBox, // DVD options appear here when DVD format selected
|
dvdAspectBox, // DVD options appear here when DVD format selected
|
||||||
widget.NewLabelWithStyle("Output Name", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
widget.NewLabelWithStyle("Output Folder", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||||
outputEntry,
|
outputDirRow,
|
||||||
|
widget.NewLabelWithStyle("Output Filename", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||||
|
outputNameRow,
|
||||||
outputHintContainer,
|
outputHintContainer,
|
||||||
appendSuffixCheck,
|
appendSuffixCheck,
|
||||||
widget.NewSeparator(),
|
widget.NewSeparator(),
|
||||||
|
|
@ -8807,8 +8912,10 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
chapterWarningLabel, // Warning when converting chapters to DVD
|
chapterWarningLabel, // Warning when converting chapters to DVD
|
||||||
preserveChaptersCheck,
|
preserveChaptersCheck,
|
||||||
dvdAspectBox, // DVD options appear here when DVD format selected
|
dvdAspectBox, // DVD options appear here when DVD format selected
|
||||||
widget.NewLabelWithStyle("Output Name", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
widget.NewLabelWithStyle("Output Folder", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||||
outputEntry,
|
outputDirRow,
|
||||||
|
widget.NewLabelWithStyle("Output Filename", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||||
|
outputNameRow,
|
||||||
outputHintContainer,
|
outputHintContainer,
|
||||||
appendSuffixCheck,
|
appendSuffixCheck,
|
||||||
widget.NewSeparator(),
|
widget.NewSeparator(),
|
||||||
|
|
@ -8876,7 +8983,8 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
autoNameTemplate.SetText(state.convert.AutoNameTemplate)
|
autoNameTemplate.SetText(state.convert.AutoNameTemplate)
|
||||||
appendSuffixCheck.SetChecked(state.convert.AppendSuffix)
|
appendSuffixCheck.SetChecked(state.convert.AppendSuffix)
|
||||||
outputEntry.SetText(state.convert.OutputBase)
|
outputEntry.SetText(state.convert.OutputBase)
|
||||||
outputHint.SetText(fmt.Sprintf("Output file: %s", state.convert.OutputFile()))
|
outputDirEntry.SetText(state.convert.OutputDir)
|
||||||
|
updateOutputHint()
|
||||||
preserveChaptersCheck.SetChecked(state.convert.PreserveChapters)
|
preserveChaptersCheck.SetChecked(state.convert.PreserveChapters)
|
||||||
resolutionSelectSimple.SetSelected(state.convert.TargetResolution)
|
resolutionSelectSimple.SetSelected(state.convert.TargetResolution)
|
||||||
resolutionSelect.SetSelected(state.convert.TargetResolution)
|
resolutionSelect.SetSelected(state.convert.TargetResolution)
|
||||||
|
|
@ -9401,7 +9509,7 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
|
|
||||||
// Replace INPUT and OUTPUT placeholders with actual file paths for preview
|
// Replace INPUT and OUTPUT placeholders with actual file paths for preview
|
||||||
inputPath := src.Path
|
inputPath := src.Path
|
||||||
outputPath := state.convert.OutputFile()
|
outputPath := getOutputPathPreview()
|
||||||
cmdStr = strings.ReplaceAll(cmdStr, "INPUT", inputPath)
|
cmdStr = strings.ReplaceAll(cmdStr, "INPUT", inputPath)
|
||||||
cmdStr = strings.ReplaceAll(cmdStr, "OUTPUT", outputPath)
|
cmdStr = strings.ReplaceAll(cmdStr, "OUTPUT", outputPath)
|
||||||
cmdStr = strings.ReplaceAll(cmdStr, "[COVER_ART]", state.convert.CoverArtPath)
|
cmdStr = strings.ReplaceAll(cmdStr, "[COVER_ART]", state.convert.CoverArtPath)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user