docs: Update dialogs documentation to match actual v3 API (#4793)

* All the documentation files have been updated. Here's a summary of the changes I made:

## Summary of Documentation Updates

### Key API Corrections Made:

1. **Dialog Creation**:
   - Changed `app.InfoDialog()` → `application.InfoDialog()` (package-level functions)
   - Also documented `app.Dialog.Info()` via DialogManager

2. **Question Dialog Buttons**:
   - Removed non-existent `SetButtons("Save", "Don't Save", "Cancel")` method
   - Now uses `AddButton("label")` which returns a `*Button`
   - Button callbacks via `button.OnClick(func() { ... })`

3. **Default/Cancel Buttons**:
   - Changed `SetDefaultButton("Cancel")` (string) → `SetDefaultButton(cancelBtn)` (`*Button`)
   - Changed `SetCancelButton("Cancel")` (string) → `SetCancelButton(cancelBtn)` (`*Button`)
   - Also documented fluent `SetAsDefault()` and `SetAsCancel()` methods on Button

4. **File Dialog Filters**:
   - Changed `SetFilters([]application.FileFilter{...})` → `AddFilter("Display Name", "*.ext;*.ext2")`

5. **Save Dialog Filename**:
   - Changed `SetDefaultFilename()` → `SetFilename()`

6. **Folder Selection**:
   - Removed non-existent `app.SelectFolderDialog()`
   - Now uses `application.OpenFileDialog().CanChooseDirectories(true).CanChooseFiles(false)`

7. **Show() Return Value**:
   - Removed incorrect `result, err := dialog.Show()` pattern
   - `Show()` returns nothing - responses are handled via button callbacks

### Files Modified:
- `docs/src/content/docs/features/dialogs/message.mdx` - Complete rewrite of question dialog examples
- `docs/src/content/docs/features/dialogs/overview.mdx` - Updated all code examples
- `docs/src/content/docs/features/dialogs/file.mdx` - Updated file dialog examples
- `v3/UNRELEASED_CHANGELOG.md` - Added changelog entry

* All changes are complete. Here's a summary of what was updated:

## Summary

I've updated the Wails v3 dialogs documentation to match the actual API. The documentation now consistently uses:

### DialogManager Pattern (Recommended)
- `app.Dialog.Info()`, `app.Dialog.Question()`, `app.Dialog.Warning()`, `app.Dialog.Error()`
- `app.Dialog.OpenFile()`, `app.Dialog.SaveFile()`

This is consistent with other runtime features like `app.Window`, `app.Event`, etc.

### Key API Corrections

1. **Dialog Creation**: Both `app.Dialog.*()` (DialogManager) and `application.*Dialog()` (package-level) are documented
2. **Question Dialog Buttons**: Use `AddButton("label")` which returns `*Button`, then `button.OnClick(callback)`
3. **Default/Cancel Buttons**: `SetDefaultButton(button)` and `SetCancelButton(button)` take `*Button` pointers
4. **File Filters**: Use `AddFilter("Display Name", "*.ext")` instead of `SetFilters()`
5. **Save Dialog Filename**: Use `SetFilename()` instead of `SetDefaultFilename()`
6. **Folder Selection**: Use `app.Dialog.OpenFile().CanChooseDirectories(true).CanChooseFiles(false)`

### Files Modified
- `docs/src/content/docs/features/dialogs/overview.mdx`
- `docs/src/content/docs/features/dialogs/message.mdx`
- `docs/src/content/docs/features/dialogs/file.mdx`
- `v3/UNRELEASED_CHANGELOG.md`

* Audit complete! All documentation files now consistently use `app.Dialog.*` for all code examples. Here's the summary:

## Audit Complete

All dialog documentation has been updated to consistently use the `app.Dialog.*` pattern:

### Changes Made:

**message.mdx:**
- All examples now use `app.Dialog.Info()`, `app.Dialog.Question()`, `app.Dialog.Warning()`, `app.Dialog.Error()`
- Function signatures updated to accept `app *application.App` parameter
- Nested dialog calls within callbacks also updated

**file.mdx:**
- All examples now use `app.Dialog.OpenFile()`, `app.Dialog.SaveFile()`
- Function signatures updated to accept `app *application.App` parameter
- All error/info dialogs within file operations also updated

**overview.mdx:**
- Already consistent from earlier edits

### Documentation Structure:
Each file now properly documents both approaches in a dedicated section:

```go
// Via DialogManager (recommended when you have an app instance):
app.Dialog.Info()
app.Dialog.OpenFile()

// Package-level functions (useful in callbacks or when app isn't in scope):
application.InfoDialog()
application.OpenFileDialog()
```

But all code examples use the `app.Dialog.*` pattern for consistency with other runtime features like `app.Window`, `app.Event`, etc.

* docs: Fix reference/dialogs.mdx and reference/application.mdx API documentation

Updated docs to match actual Wails v3 Dialogs API:

- reference/dialogs.mdx: Complete rewrite with correct API
  - Use `app.Dialog.OpenFile()` and `app.Dialog.SaveFile()` instead of `app.OpenFileDialog()`
  - Use `AddFilter("name", "pattern")` instead of `SetFilters([]FileFilter{...})`
  - Use `SetFilename()` instead of `SetDefaultFilename()`
  - Use `SetDirectory()` instead of `SetDefaultDirectory()`
  - Remove non-existent `SelectFolderDialog()` - use `OpenFile().CanChooseDirectories(true).CanChooseFiles(false)`
  - Use `AddButton()` with callbacks instead of `SetButtons()`
  - Use `SetDefaultButton(*Button)` instead of `SetDefaultButton(int)`
  - Document that `Show()` returns nothing, use callbacks

- reference/application.mdx: Fix Dialog Methods section
  - Use `app.Dialog.*` manager pattern
  - Show correct Question dialog with button callbacks
  - Fix file dialog examples with `AddFilter()`
  - Remove `SelectFolderDialog()` reference

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* docs: Remove package-level dialog function references

Remove all references to package-level dialog functions
(application.InfoDialog(), application.OpenFileDialog(), etc.)
from documentation. Only the app.Dialog manager pattern
should be used.

Updated files:
- reference/dialogs.mdx
- features/dialogs/overview.mdx
- features/dialogs/message.mdx
- features/dialogs/file.mdx

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: Remove package-level dialog functions in favor of app.Dialog manager

BREAKING CHANGE: Remove package-level dialog functions. Use app.Dialog manager instead.

Removed functions:
- application.InfoDialog()
- application.QuestionDialog()
- application.WarningDialog()
- application.ErrorDialog()
- application.OpenFileDialog()
- application.SaveFileDialog()

Use the Dialog manager pattern instead:
- app.Dialog.Info()
- app.Dialog.Question()
- app.Dialog.Warning()
- app.Dialog.Error()
- app.Dialog.OpenFile()
- app.Dialog.SaveFile()

This aligns dialogs with other runtime managers like app.Window and app.Event.

Updated files:
- v3/pkg/application/application.go - Remove exported dialog functions
- v3/pkg/application/dialog_manager.go - Use internal newMessageDialog/newOpenFileDialog
- v3/pkg/application/messageprocessor_dialog.go - Use internal dialog constructors
- v3/examples/* - Update all examples to use app.Dialog pattern
- v3/internal/commands/appimage_testfiles/main.go - Update test file
- v3/UNRELEASED_CHANGELOG.md - Document breaking change

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: Use application.Get() in dialogs-basic example and correct filter docs

- Update dialogs-basic helper functions to use application.Get() instead
  of passing app through function parameters
- Fix incorrect documentation claiming space/comma delimiters work for
  filter patterns (only semicolons are supported)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Lea Anthony 2025-12-16 06:05:40 +11:00 committed by GitHub
commit 84b9021ea9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 1109 additions and 1044 deletions

View file

@ -10,20 +10,28 @@ import { Card, CardGrid } from "@astrojs/starlight/components";
## File dialogs
Wails provides **native file dialogs** with platform-appropriate appearance for opening files, saving files, and selecting folders. Simple API with file type filtering, multiple selection support, and default locations.
## Creating File Dialogs
File dialogs are accessed through the `app.Dialog` manager:
```go
app.Dialog.OpenFile()
app.Dialog.SaveFile()
```
## Open File dialog
Select files to open:
```go
path, err := app.OpenFileDialog().
path, err := app.Dialog.OpenFile().
SetTitle("Select Image").
SetFilters([]application.FileFilter{
{DisplayName: "Images", Pattern: "*.png;*.jpg;*.gif"},
{DisplayName: "All Files", Pattern: "*.*"},
}).
AddFilter("Images", "*.png;*.jpg;*.gif").
AddFilter("All Files", "*.*").
PromptForSingleSelection()
if err != nil {
if err != nil || path == "" {
return
}
@ -39,15 +47,13 @@ openFile(path)
### Single File Selection
```go
path, err := app.OpenFileDialog().
path, err := app.Dialog.OpenFile().
SetTitle("Open Document").
SetFilters([]application.FileFilter{
{DisplayName: "Text Files", Pattern: "*.txt"},
{DisplayName: "All Files", Pattern: "*.*"},
}).
AddFilter("Text Files", "*.txt").
AddFilter("All Files", "*.*").
PromptForSingleSelection()
if err != nil {
if err != nil || path == "" {
// User cancelled or error occurred
return
}
@ -59,11 +65,9 @@ data, _ := os.ReadFile(path)
### Multiple File Selection
```go
paths, err := app.OpenFileDialog().
paths, err := app.Dialog.OpenFile().
SetTitle("Select Images").
SetFilters([]application.FileFilter{
{DisplayName: "Images", Pattern: "*.png;*.jpg;*.jpeg;*.gif"},
}).
AddFilter("Images", "*.png;*.jpg;*.jpeg;*.gif").
PromptForMultipleSelection()
if err != nil {
@ -79,7 +83,7 @@ for _, path := range paths {
### With Default Directory
```go
path, err := app.OpenFileDialog().
path, err := app.Dialog.OpenFile().
SetTitle("Open File").
SetDirectory("/Users/me/Documents").
PromptForSingleSelection()
@ -90,16 +94,13 @@ path, err := app.OpenFileDialog().
Choose where to save:
```go
path, err := app.SaveFileDialog().
SetTitle("Save Document").
SetDefaultFilename("document.txt").
SetFilters([]application.FileFilter{
{DisplayName: "Text Files", Pattern: "*.txt"},
{DisplayName: "All Files", Pattern: "*.*"},
}).
path, err := app.Dialog.SaveFile().
SetFilename("document.txt").
AddFilter("Text Files", "*.txt").
AddFilter("All Files", "*.*").
PromptForSingleSelection()
if err != nil {
if err != nil || path == "" {
return
}
@ -115,48 +116,48 @@ saveFile(path, data)
### With Default Filename
```go
path, err := app.SaveFileDialog().
SetTitle("Export Data").
SetDefaultFilename("export.csv").
SetFilters([]application.FileFilter{
{DisplayName: "CSV Files", Pattern: "*.csv"},
}).
path, err := app.Dialog.SaveFile().
SetFilename("export.csv").
AddFilter("CSV Files", "*.csv").
PromptForSingleSelection()
```
### With Default Directory
```go
path, err := app.SaveFileDialog().
SetTitle("Save File").
path, err := app.Dialog.SaveFile().
SetDirectory("/Users/me/Documents").
SetDefaultFilename("untitled.txt").
SetFilename("untitled.txt").
PromptForSingleSelection()
```
### Overwrite Confirmation
```go
path, err := app.SaveFileDialog().
SetTitle("Save File").
SetDefaultFilename("document.txt").
path, err := app.Dialog.SaveFile().
SetFilename("document.txt").
PromptForSingleSelection()
if err != nil {
if err != nil || path == "" {
return
}
// Check if file exists
if _, err := os.Stat(path); err == nil {
result, _ := app.QuestionDialog().
dialog := app.Dialog.Question().
SetTitle("Confirm Overwrite").
SetMessage("File already exists. Overwrite?").
SetButtons("Overwrite", "Cancel").
Show()
if result != "Overwrite" {
return
}
SetMessage("File already exists. Overwrite?")
overwriteBtn := dialog.AddButton("Overwrite")
overwriteBtn.OnClick(func() {
saveFile(path, data)
})
cancelBtn := dialog.AddButton("Cancel")
dialog.SetDefaultButton(cancelBtn)
dialog.SetCancelButton(cancelBtn)
dialog.Show()
return
}
saveFile(path, data)
@ -164,14 +165,16 @@ saveFile(path, data)
## Select Folder dialog
Choose a directory:
Choose a directory using the open file dialog with directory selection enabled:
```go
path, err := app.SelectFolderDialog().
path, err := app.Dialog.OpenFile().
SetTitle("Select Output Folder").
CanChooseDirectories(true).
CanChooseFiles(false).
PromptForSingleSelection()
if err != nil {
if err != nil || path == "" {
return
}
@ -187,66 +190,45 @@ exportToFolder(path)
### With Default Directory
```go
path, err := app.SelectFolderDialog().
path, err := app.Dialog.OpenFile().
SetTitle("Select Folder").
SetDirectory("/Users/me/Documents").
CanChooseDirectories(true).
CanChooseFiles(false).
PromptForSingleSelection()
```
## File Filters
Use the `AddFilter()` method to add file type filters to dialogs. Each call adds a new filter option.
### Basic Filters
```go
filters := []application.FileFilter{
{DisplayName: "Text Files", Pattern: "*.txt"},
{DisplayName: "All Files", Pattern: "*.*"},
}
path, _ := app.OpenFileDialog().
SetFilters(filters).
path, _ := app.Dialog.OpenFile().
AddFilter("Text Files", "*.txt").
AddFilter("All Files", "*.*").
PromptForSingleSelection()
```
### Multiple Extensions
Use semicolons to specify multiple extensions in a single filter:
```go
filters := []application.FileFilter{
{
DisplayName: "Images",
Pattern: "*.png;*.jpg;*.jpeg;*.gif;*.bmp",
},
{
DisplayName: "Documents",
Pattern: "*.txt;*.doc;*.docx;*.pdf",
},
{
DisplayName: "All Files",
Pattern: "*.*",
},
}
dialog := app.Dialog.OpenFile().
AddFilter("Images", "*.png;*.jpg;*.jpeg;*.gif;*.bmp").
AddFilter("Documents", "*.txt;*.doc;*.docx;*.pdf").
AddFilter("All Files", "*.*")
```
### Platform-Specific Patterns
### Pattern Format
**Windows:**
```go
// Semicolon-separated
Pattern: "*.png;*.jpg;*.gif"
```
Use **semicolons** to separate multiple extensions in a single filter:
**macOS/Linux:**
```go
// Space or comma-separated (both work)
Pattern: "*.png *.jpg *.gif"
// or
Pattern: "*.png,*.jpg,*.gif"
```
**Cross-platform (recommended):**
```go
// Use semicolons (works on all platforms)
Pattern: "*.png;*.jpg;*.gif"
// Multiple extensions separated by semicolons
AddFilter("Images", "*.png;*.jpg;*.gif")
```
## Complete Examples
@ -254,41 +236,40 @@ Pattern: "*.png;*.jpg;*.gif"
### Open Image File
```go
func openImage() (image.Image, error) {
path, err := app.OpenFileDialog().
func openImage(app *application.App) (image.Image, error) {
path, err := app.Dialog.OpenFile().
SetTitle("Select Image").
SetFilters([]application.FileFilter{
{
DisplayName: "Images",
Pattern: "*.png;*.jpg;*.jpeg;*.gif;*.bmp",
},
}).
AddFilter("Images", "*.png;*.jpg;*.jpeg;*.gif;*.bmp").
PromptForSingleSelection()
if err != nil {
return nil, err
}
if path == "" {
return nil, errors.New("no file selected")
}
// Open and decode image
file, err := os.Open(path)
if err != nil {
app.ErrorDialog().
app.Dialog.Error().
SetTitle("Open Failed").
SetMessage(err.Error()).
Show()
return nil, err
}
defer file.Close()
img, _, err := image.Decode(file)
if err != nil {
app.ErrorDialog().
app.Dialog.Error().
SetTitle("Invalid Image").
SetMessage("Could not decode image file.").
Show()
return nil, err
}
return img, nil
}
```
@ -296,214 +277,216 @@ func openImage() (image.Image, error) {
### Save Document with Validation
```go
func saveDocument(content string) error {
path, err := app.SaveFileDialog().
SetTitle("Save Document").
SetDefaultFilename("document.txt").
SetFilters([]application.FileFilter{
{DisplayName: "Text Files", Pattern: "*.txt"},
{DisplayName: "Markdown Files", Pattern: "*.md"},
{DisplayName: "All Files", Pattern: "*.*"},
}).
func saveDocument(app *application.App, content string) {
path, err := app.Dialog.SaveFile().
SetFilename("document.txt").
AddFilter("Text Files", "*.txt").
AddFilter("Markdown Files", "*.md").
AddFilter("All Files", "*.*").
PromptForSingleSelection()
if err != nil {
return err
if err != nil || path == "" {
return
}
// Validate extension
ext := filepath.Ext(path)
if ext != ".txt" && ext != ".md" {
result, _ := app.QuestionDialog().
dialog := app.Dialog.Question().
SetTitle("Confirm Extension").
SetMessage(fmt.Sprintf("Save as %s file?", ext)).
SetButtons("Save", "Cancel").
Show()
if result != "Save" {
return errors.New("cancelled")
}
SetMessage(fmt.Sprintf("Save as %s file?", ext))
saveBtn := dialog.AddButton("Save")
saveBtn.OnClick(func() {
doSave(app, path, content)
})
cancelBtn := dialog.AddButton("Cancel")
dialog.SetDefaultButton(cancelBtn)
dialog.SetCancelButton(cancelBtn)
dialog.Show()
return
}
// Save file
doSave(app, path, content)
}
func doSave(app *application.App, path, content string) {
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
app.ErrorDialog().
app.Dialog.Error().
SetTitle("Save Failed").
SetMessage(err.Error()).
Show()
return err
return
}
app.InfoDialog().
app.Dialog.Info().
SetTitle("Saved").
SetMessage("Document saved successfully!").
Show()
return nil
}
```
### Batch File Processing
```go
func processMultipleFiles() {
paths, err := app.OpenFileDialog().
func processMultipleFiles(app *application.App) {
paths, err := app.Dialog.OpenFile().
SetTitle("Select Files to Process").
SetFilters([]application.FileFilter{
{DisplayName: "Images", Pattern: "*.png;*.jpg"},
}).
AddFilter("Images", "*.png;*.jpg").
PromptForMultipleSelection()
if err != nil || len(paths) == 0 {
return
}
// Confirm processing
result, _ := app.QuestionDialog().
dialog := app.Dialog.Question().
SetTitle("Confirm Processing").
SetMessage(fmt.Sprintf("Process %d file(s)?", len(paths))).
SetButtons("Process", "Cancel").
Show()
if result != "Process" {
return
}
// Process files
var errors []error
for i, path := range paths {
if err := processFile(path); err != nil {
errors = append(errors, err)
SetMessage(fmt.Sprintf("Process %d file(s)?", len(paths)))
processBtn := dialog.AddButton("Process")
processBtn.OnClick(func() {
// Process files
var errs []error
for i, path := range paths {
if err := processFile(path); err != nil {
errs = append(errs, err)
}
// Update progress
// app.Event.Emit("progress", map[string]interface{}{
// "current": i + 1,
// "total": len(paths),
// })
_ = i // suppress unused variable warning in example
}
// Update progress
app.EmitEvent("progress", map[string]interface{}{
"current": i + 1,
"total": len(paths),
})
}
// Show results
if len(errors) > 0 {
app.WarningDialog().
SetTitle("Processing Complete").
SetMessage(fmt.Sprintf("Processed %d files with %d errors.",
len(paths), len(errors))).
Show()
} else {
app.InfoDialog().
SetTitle("Success").
SetMessage(fmt.Sprintf("Processed %d files successfully!", len(paths))).
Show()
}
// Show results
if len(errs) > 0 {
app.Dialog.Warning().
SetTitle("Processing Complete").
SetMessage(fmt.Sprintf("Processed %d files with %d errors.",
len(paths), len(errs))).
Show()
} else {
app.Dialog.Info().
SetTitle("Success").
SetMessage(fmt.Sprintf("Processed %d files successfully!", len(paths))).
Show()
}
})
cancelBtn := dialog.AddButton("Cancel")
dialog.SetCancelButton(cancelBtn)
dialog.Show()
}
```
### Export with Folder Selection
```go
func exportData(data []byte) error {
func exportData(app *application.App, data []byte) {
// Select output folder
folder, err := app.SelectFolderDialog().
folder, err := app.Dialog.OpenFile().
SetTitle("Select Export Folder").
SetDirectory(getDefaultExportFolder()).
CanChooseDirectories(true).
CanChooseFiles(false).
PromptForSingleSelection()
if err != nil {
return err
if err != nil || folder == "" {
return
}
// Generate filename
filename := fmt.Sprintf("export_%s.csv",
filename := fmt.Sprintf("export_%s.csv",
time.Now().Format("2006-01-02_15-04-05"))
path := filepath.Join(folder, filename)
// Save file
if err := os.WriteFile(path, data, 0644); err != nil {
app.ErrorDialog().
app.Dialog.Error().
SetTitle("Export Failed").
SetMessage(err.Error()).
Show()
return err
return
}
// Show success with option to open folder
result, _ := app.QuestionDialog().
dialog := app.Dialog.Question().
SetTitle("Export Complete").
SetMessage(fmt.Sprintf("Exported to %s", filename)).
SetButtons("Open Folder", "OK").
Show()
if result == "Open Folder" {
SetMessage(fmt.Sprintf("Exported to %s", filename))
openBtn := dialog.AddButton("Open Folder")
openBtn.OnClick(func() {
openFolder(folder)
}
return nil
})
dialog.AddButton("OK")
dialog.Show()
}
```
### Import with Validation
```go
func importConfiguration() error {
path, err := app.OpenFileDialog().
func importConfiguration(app *application.App) {
path, err := app.Dialog.OpenFile().
SetTitle("Import Configuration").
SetFilters([]application.FileFilter{
{DisplayName: "JSON Files", Pattern: "*.json"},
{DisplayName: "YAML Files", Pattern: "*.yaml;*.yml"},
}).
AddFilter("JSON Files", "*.json").
AddFilter("YAML Files", "*.yaml;*.yml").
PromptForSingleSelection()
if err != nil {
return err
if err != nil || path == "" {
return
}
// Read file
data, err := os.ReadFile(path)
if err != nil {
app.ErrorDialog().
app.Dialog.Error().
SetTitle("Read Failed").
SetMessage(err.Error()).
Show()
return err
return
}
// Validate configuration
config, err := parseConfig(data)
if err != nil {
app.ErrorDialog().
app.Dialog.Error().
SetTitle("Invalid Configuration").
SetMessage("File is not a valid configuration.").
Show()
return err
return
}
// Confirm import
result, _ := app.QuestionDialog().
dialog := app.Dialog.Question().
SetTitle("Confirm Import").
SetMessage("Import this configuration?").
SetButtons("Import", "Cancel").
Show()
if result != "Import" {
return errors.New("cancelled")
}
// Apply configuration
if err := applyConfig(config); err != nil {
app.ErrorDialog().
SetTitle("Import Failed").
SetMessage(err.Error()).
SetMessage("Import this configuration?")
importBtn := dialog.AddButton("Import")
importBtn.OnClick(func() {
// Apply configuration
if err := applyConfig(config); err != nil {
app.Dialog.Error().
SetTitle("Import Failed").
SetMessage(err.Error()).
Show()
return
}
app.Dialog.Info().
SetTitle("Success").
SetMessage("Configuration imported successfully!").
Show()
return err
}
app.InfoDialog().
SetTitle("Success").
SetMessage("Configuration imported successfully!").
Show()
return nil
})
cancelBtn := dialog.AddButton("Cancel")
dialog.SetCancelButton(cancelBtn)
dialog.Show()
}
```

View file

@ -10,12 +10,26 @@ import { Card, CardGrid } from "@astrojs/starlight/components";
## Message dialogs
Wails provides **native message dialogs** with platform-appropriate appearance: info, warning, error, and question dialogs with customisable titles, messages, and buttons. Simple API, native behaviour, accessible by default.
## Creating Dialogs
Message dialogs are accessed through the `app.Dialog` manager:
```go
app.Dialog.Info()
app.Dialog.Question()
app.Dialog.Warning()
app.Dialog.Error()
```
All methods return a `*MessageDialog` that can be configured using method chaining.
## Information dialog
Display informational messages:
```go
app.InfoDialog().
app.Dialog.Info().
SetTitle("Success").
SetMessage("File saved successfully!").
Show()
@ -30,16 +44,16 @@ app.InfoDialog().
**Example - Save confirmation:**
```go
func saveFile(path string, data []byte) error {
func saveFile(app *application.App, path string, data []byte) error {
if err := os.WriteFile(path, data, 0644); err != nil {
return err
}
app.InfoDialog().
app.Dialog.Info().
SetTitle("File Saved").
SetMessage(fmt.Sprintf("Saved to %s", filepath.Base(path))).
Show()
return nil
}
```
@ -49,7 +63,7 @@ func saveFile(path string, data []byte) error {
Show warnings:
```go
app.WarningDialog().
app.Dialog.Warning().
SetTitle("Warning").
SetMessage("This action cannot be undone.").
Show()
@ -64,11 +78,11 @@ app.WarningDialog().
**Example - Disk space warning:**
```go
func checkDiskSpace() {
func checkDiskSpace(app *application.App) {
available := getDiskSpace()
if available < 100*1024*1024 { // Less than 100MB
app.WarningDialog().
app.Dialog.Warning().
SetTitle("Low Disk Space").
SetMessage(fmt.Sprintf("Only %d MB available.", available/(1024*1024))).
Show()
@ -81,7 +95,7 @@ func checkDiskSpace() {
Display errors:
```go
app.ErrorDialog().
app.Dialog.Error().
SetTitle("Error").
SetMessage("Failed to connect to server.").
Show()
@ -96,41 +110,48 @@ app.ErrorDialog().
**Example - Network error:**
```go
func fetchData(url string) ([]byte, error) {
func fetchData(app *application.App, url string) ([]byte, error) {
resp, err := http.Get(url)
if err != nil {
app.ErrorDialog().
app.Dialog.Error().
SetTitle("Network Error").
SetMessage(fmt.Sprintf("Failed to connect: %v", err)).
Show()
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
```
## Question dialog
Ask users questions:
Ask users questions and handle responses via button callbacks:
```go
result, err := app.QuestionDialog().
dialog := app.Dialog.Question().
SetTitle("Confirm").
SetMessage("Save changes before closing?").
SetButtons("Save", "Don't Save", "Cancel").
SetDefaultButton("Save").
Show()
SetMessage("Save changes before closing?")
switch result {
case "Save":
save := dialog.AddButton("Save")
save.OnClick(func() {
saveChanges()
case "Don't Save":
})
dontSave := dialog.AddButton("Don't Save")
dontSave.OnClick(func() {
// Continue without saving
case "Cancel":
})
cancel := dialog.AddButton("Cancel")
cancel.OnClick(func() {
// Don't close
}
})
dialog.SetDefaultButton(save)
dialog.SetCancelButton(cancel)
dialog.Show()
```
**Use cases:**
@ -142,32 +163,34 @@ case "Cancel":
**Example - Unsaved changes:**
```go
func closeDocument() bool {
func closeDocument(app *application.App) {
if !hasUnsavedChanges() {
return true
doClose()
return
}
result, err := app.QuestionDialog().
dialog := app.Dialog.Question().
SetTitle("Unsaved Changes").
SetMessage("Do you want to save your changes?").
SetButtons("Save", "Don't Save", "Cancel").
SetDefaultButton("Save").
Show()
if err != nil {
return false
}
switch result {
case "Save":
return saveDocument()
case "Don't Save":
return true
case "Cancel":
return false
}
return false
SetMessage("Do you want to save your changes?")
save := dialog.AddButton("Save")
save.OnClick(func() {
if saveDocument() {
doClose()
}
})
dontSave := dialog.AddButton("Don't Save")
dontSave.OnClick(func() {
doClose()
})
cancel := dialog.AddButton("Cancel")
// Cancel button has no callback - just closes the dialog
dialog.SetDefaultButton(save)
dialog.SetCancelButton(cancel)
dialog.Show()
}
```
@ -176,7 +199,7 @@ func closeDocument() bool {
### Title and Message
```go
dialog := app.InfoDialog().
dialog := app.Dialog.Info().
SetTitle("Operation Complete").
SetMessage("All files have been processed successfully.")
```
@ -190,30 +213,85 @@ dialog := app.InfoDialog().
**Single button (Info/Warning/Error):**
Info, warning, and error dialogs display a default "OK" button:
```go
// Default "OK" button
app.InfoDialog().
app.Dialog.Info().
SetMessage("Done!").
Show()
```
**Multiple buttons (Question):**
You can also add custom buttons:
```go
result, _ := app.QuestionDialog().
SetMessage("Choose an action").
SetButtons("Option 1", "Option 2", "Option 3").
Show()
dialog := app.Dialog.Info().
SetMessage("Done!")
ok := dialog.AddButton("Got it!")
dialog.SetDefaultButton(ok)
dialog.Show()
```
**Default button:**
**Multiple buttons (Question):**
Use `AddButton()` to add buttons, which returns a `*Button` you can configure:
```go
app.QuestionDialog().
SetMessage("Delete file?").
SetButtons("Delete", "Cancel").
SetDefaultButton("Cancel"). // Safe option
Show()
dialog := app.Dialog.Question().
SetMessage("Choose an action")
option1 := dialog.AddButton("Option 1")
option1.OnClick(func() {
handleOption1()
})
option2 := dialog.AddButton("Option 2")
option2.OnClick(func() {
handleOption2()
})
option3 := dialog.AddButton("Option 3")
option3.OnClick(func() {
handleOption3()
})
dialog.Show()
```
**Default and Cancel buttons:**
Use `SetDefaultButton()` to specify which button is highlighted and triggered by Enter.
Use `SetCancelButton()` to specify which button is triggered by Escape.
```go
dialog := app.Dialog.Question().
SetMessage("Delete file?")
deleteBtn := dialog.AddButton("Delete")
deleteBtn.OnClick(func() {
performDelete()
})
cancelBtn := dialog.AddButton("Cancel")
// No callback needed - just dismisses dialog
dialog.SetDefaultButton(cancelBtn) // Safe option as default
dialog.SetCancelButton(cancelBtn) // Escape triggers Cancel
dialog.Show()
```
You can also use the fluent `SetAsDefault()` and `SetAsCancel()` methods on buttons:
```go
dialog := app.Dialog.Question().
SetMessage("Delete file?")
dialog.AddButton("Delete").OnClick(func() {
performDelete()
})
dialog.AddButton("Cancel").SetAsDefault().SetAsCancel()
dialog.Show()
```
**Best practices:**
@ -222,16 +300,29 @@ app.QuestionDialog().
- **Safe default:** Non-destructive action
- **Order matters:** Most likely action first (except Cancel)
### Custom Icon
Set a custom icon for the dialog:
```go
app.Dialog.Info().
SetTitle("Custom Icon Example").
SetMessage("Using a custom icon").
SetIcon(myIconBytes).
Show()
```
### Window Attachment
Attach to specific window:
```go
dialog := app.QuestionDialog().
dialog := app.Dialog.Question().
SetMessage("Window-specific question").
AttachToWindow(window)
result, _ := dialog.Show()
dialog.AddButton("OK")
dialog.Show()
```
**Benefits:**
@ -244,163 +335,111 @@ result, _ := dialog.Show()
### Confirm Destructive Action
```go
func deleteFiles(paths []string) error {
func deleteFiles(app *application.App, paths []string) {
// Confirm deletion
message := fmt.Sprintf("Delete %d file(s)?", len(paths))
if len(paths) == 1 {
message = fmt.Sprintf("Delete %s?", filepath.Base(paths[0]))
}
result, err := app.QuestionDialog().
dialog := app.Dialog.Question().
SetTitle("Confirm Delete").
SetMessage(message).
SetButtons("Delete", "Cancel").
SetDefaultButton("Cancel").
Show()
if err != nil || result != "Delete" {
return errors.New("cancelled")
}
// Perform deletion
var errors []error
for _, path := range paths {
if err := os.Remove(path); err != nil {
errors = append(errors, err)
}
}
// Show result
if len(errors) > 0 {
app.ErrorDialog().
SetTitle("Delete Failed").
SetMessage(fmt.Sprintf("Failed to delete %d file(s)", len(errors))).
Show()
return fmt.Errorf("%d errors", len(errors))
}
app.InfoDialog().
SetTitle("Delete Complete").
SetMessage(fmt.Sprintf("Deleted %d file(s)", len(paths))).
Show()
return nil
}
```
SetMessage(message)
### Error Handling with Retry
```go
func connectToServer(url string) error {
for attempts := 0; attempts < 3; attempts++ {
if err := tryConnect(url); err == nil {
return nil
}
if attempts < 2 {
result, _ := app.QuestionDialog().
SetTitle("Connection Failed").
SetMessage("Failed to connect. Retry?").
SetButtons("Retry", "Cancel").
Show()
if result != "Retry" {
return errors.New("cancelled")
deleteBtn := dialog.AddButton("Delete")
deleteBtn.OnClick(func() {
// Perform deletion
var errs []error
for _, path := range paths {
if err := os.Remove(path); err != nil {
errs = append(errs, err)
}
}
}
app.ErrorDialog().
SetTitle("Connection Failed").
SetMessage("Could not connect after 3 attempts.").
Show()
return errors.New("connection failed")
}
```
### Multi-Step Workflow
```go
func exportAndEmail() {
// Step 1: Confirm export
result, _ := app.QuestionDialog().
SetTitle("Export and Email").
SetMessage("Export data and send via email?").
SetButtons("Continue", "Cancel").
Show()
if result != "Continue" {
return
}
// Step 2: Export data
data, err := exportData()
if err != nil {
app.ErrorDialog().
SetTitle("Export Failed").
SetMessage(err.Error()).
Show()
return
}
// Step 3: Send email
if err := sendEmail(data); err != nil {
app.ErrorDialog().
SetTitle("Email Failed").
SetMessage(err.Error()).
Show()
return
}
// Step 4: Success
app.InfoDialog().
SetTitle("Success").
SetMessage("Data exported and emailed successfully!").
Show()
}
```
### Validation with Feedback
```go
func validateAndSave(data string) error {
// Validate
if err := validate(data); err != nil {
app.WarningDialog().
SetTitle("Validation Warning").
SetMessage(err.Error()).
Show()
result, _ := app.QuestionDialog().
SetMessage("Save anyway?").
SetButtons("Save", "Cancel").
SetDefaultButton("Cancel").
Show()
if result != "Save" {
return errors.New("cancelled")
// Show result
if len(errs) > 0 {
app.Dialog.Error().
SetTitle("Delete Failed").
SetMessage(fmt.Sprintf("Failed to delete %d file(s)", len(errs))).
Show()
} else {
app.Dialog.Info().
SetTitle("Delete Complete").
SetMessage(fmt.Sprintf("Deleted %d file(s)", len(paths))).
Show()
}
}
// Save
if err := save(data); err != nil {
app.ErrorDialog().
SetTitle("Save Failed").
SetMessage(err.Error()).
Show()
return err
}
app.InfoDialog().
SetTitle("Saved").
SetMessage("Data saved successfully!").
Show()
return nil
})
cancelBtn := dialog.AddButton("Cancel")
dialog.SetDefaultButton(cancelBtn)
dialog.SetCancelButton(cancelBtn)
dialog.Show()
}
```
### Quit Confirmation
```go
func confirmQuit(app *application.App) {
dialog := app.Dialog.Question().
SetTitle("Quit").
SetMessage("You have unsaved work. Are you sure you want to quit?")
yes := dialog.AddButton("Yes")
yes.OnClick(func() {
app.Quit()
})
no := dialog.AddButton("No")
dialog.SetDefaultButton(no)
dialog.Show()
}
```
### Update Dialog with Download Option
```go
func showUpdateDialog(app *application.App) {
dialog := app.Dialog.Question().
SetTitle("Update").
SetMessage("A new version is available. The cancel button is selected when pressing escape.")
download := dialog.AddButton("📥 Download")
download.OnClick(func() {
app.Dialog.Info().SetMessage("Downloading...").Show()
})
cancel := dialog.AddButton("Cancel")
dialog.SetDefaultButton(download)
dialog.SetCancelButton(cancel)
dialog.Show()
}
```
### Custom Icon Question
```go
func showCustomIconQuestion(app *application.App, iconBytes []byte) {
dialog := app.Dialog.Question().
SetTitle("Custom Icon Example").
SetMessage("Using a custom icon").
SetIcon(iconBytes)
likeIt := dialog.AddButton("I like it!")
likeIt.OnClick(func() {
app.Dialog.Info().SetMessage("Thanks!").Show()
})
notKeen := dialog.AddButton("Not so keen...")
notKeen.OnClick(func() {
app.Dialog.Info().SetMessage("Too bad!").Show()
})
dialog.SetDefaultButton(likeIt)
dialog.Show()
}
## Best Practices
### ✅ Do

View file

@ -15,33 +15,48 @@ Wails provides **native system dialogs** that work across all platforms: message
```go
// Information dialog
app.InfoDialog().
app.Dialog.Info().
SetTitle("Success").
SetMessage("File saved successfully!").
Show()
// Question dialog
result, _ := app.QuestionDialog().
// Question dialog with button callbacks
dialog := app.Dialog.Question().
SetTitle("Confirm").
SetMessage("Delete this file?").
SetButtons("Delete", "Cancel").
Show()
SetMessage("Delete this file?")
if result == "Delete" {
deleteBtn := dialog.AddButton("Delete")
deleteBtn.OnClick(func() {
deleteFile()
}
})
cancelBtn := dialog.AddButton("Cancel")
dialog.SetDefaultButton(cancelBtn)
dialog.SetCancelButton(cancelBtn)
dialog.Show()
// File open dialog
path, _ := app.OpenFileDialog().
path, _ := app.Dialog.OpenFile().
SetTitle("Select Image").
SetFilters([]application.FileFilter{
{DisplayName: "Images", Pattern: "*.png;*.jpg"},
}).
AddFilter("Images", "*.png;*.jpg").
PromptForSingleSelection()
```
**That's it!** Native dialogs with minimal code.
## Accessing Dialogs
Dialogs are accessed through the `app.Dialog` manager:
```go
app.Dialog.Info()
app.Dialog.Question()
app.Dialog.Warning()
app.Dialog.Error()
app.Dialog.OpenFile()
app.Dialog.SaveFile()
```
## dialog Types
### Information dialog
@ -49,7 +64,7 @@ path, _ := app.OpenFileDialog().
Display simple messages:
```go
app.InfoDialog().
app.Dialog.Info().
SetTitle("Welcome").
SetMessage("Welcome to our application!").
Show()
@ -65,7 +80,7 @@ app.InfoDialog().
Show warnings:
```go
app.WarningDialog().
app.Dialog.Warning().
SetTitle("Warning").
SetMessage("This action cannot be undone.").
Show()
@ -81,7 +96,7 @@ app.WarningDialog().
Display errors:
```go
app.ErrorDialog().
app.Dialog.Error().
SetTitle("Error").
SetMessage("Failed to save file: " + err.Error()).
Show()
@ -94,19 +109,22 @@ app.ErrorDialog().
### Question dialog
Ask users questions:
Ask users questions and handle responses via button callbacks:
```go
result, err := app.QuestionDialog().
dialog := app.Dialog.Question().
SetTitle("Confirm Delete").
SetMessage("Are you sure you want to delete this file?").
SetButtons("Delete", "Cancel").
SetDefaultButton("Cancel").
Show()
SetMessage("Are you sure you want to delete this file?")
if result == "Delete" {
deleteBtn := dialog.AddButton("Delete")
deleteBtn.OnClick(func() {
deleteFile()
}
})
cancelBtn := dialog.AddButton("Cancel")
dialog.SetDefaultButton(cancelBtn)
dialog.SetCancelButton(cancelBtn)
dialog.Show()
```
**Use cases:**
@ -121,15 +139,13 @@ if result == "Delete" {
Select files to open:
```go
path, err := app.OpenFileDialog().
path, err := app.Dialog.OpenFile().
SetTitle("Select Image").
SetFilters([]application.FileFilter{
{DisplayName: "Images", Pattern: "*.png;*.jpg;*.gif"},
{DisplayName: "All Files", Pattern: "*.*"},
}).
AddFilter("Images", "*.png;*.jpg;*.gif").
AddFilter("All Files", "*.*").
PromptForSingleSelection()
if err == nil {
if err == nil && path != "" {
openFile(path)
}
```
@ -137,15 +153,15 @@ if err == nil {
**Multiple selection:**
```go
paths, err := app.OpenFileDialog().
paths, err := app.Dialog.OpenFile().
SetTitle("Select Images").
SetFilters([]application.FileFilter{
{DisplayName: "Images", Pattern: "*.png;*.jpg"},
}).
AddFilter("Images", "*.png;*.jpg").
PromptForMultipleSelection()
for _, path := range paths {
processFile(path)
if err == nil {
for _, path := range paths {
processFile(path)
}
}
```
@ -154,30 +170,29 @@ for _, path := range paths {
Choose where to save:
```go
path, err := app.SaveFileDialog().
SetTitle("Save Document").
SetDefaultFilename("document.txt").
SetFilters([]application.FileFilter{
{DisplayName: "Text Files", Pattern: "*.txt"},
{DisplayName: "All Files", Pattern: "*.*"},
}).
path, err := app.Dialog.SaveFile().
SetFilename("document.txt").
AddFilter("Text Files", "*.txt").
AddFilter("All Files", "*.*").
PromptForSingleSelection()
if err == nil {
if err == nil && path != "" {
saveFile(path)
}
```
### Select Folder dialog
Choose a directory:
Choose a directory using the open file dialog with directory selection enabled:
```go
path, err := app.SelectFolderDialog().
path, err := app.Dialog.OpenFile().
SetTitle("Select Output Folder").
CanChooseDirectories(true).
CanChooseFiles(false).
PromptForSingleSelection()
if err == nil {
if err == nil && path != "" {
exportToFolder(path)
}
```
@ -187,42 +202,67 @@ if err == nil {
### Title and Message
```go
dialog := app.InfoDialog().
dialog := app.Dialog.Info().
SetTitle("Success").
SetMessage("Operation completed successfully!")
```
### Buttons
**Predefined buttons:**
**Default button for simple dialogs:**
Info, warning, and error dialogs display a default "OK" button:
```go
// OK button
app.InfoDialog().
app.Dialog.Info().
SetMessage("Done!").
Show()
// Yes/No buttons
result, _ := app.QuestionDialog().
SetMessage("Continue?").
SetButtons("Yes", "No").
Show()
// Custom buttons
result, _ := app.QuestionDialog().
SetMessage("Choose action").
SetButtons("Save", "Don't Save", "Cancel").
Show()
```
**Default button:**
**Custom buttons for question dialogs:**
Use `AddButton()` to add buttons, which returns a `*Button` you can configure with callbacks:
```go
app.QuestionDialog().
SetMessage("Delete file?").
SetButtons("Delete", "Cancel").
SetDefaultButton("Cancel"). // Highlighted by default
Show()
dialog := app.Dialog.Question().
SetMessage("Choose action")
save := dialog.AddButton("Save")
save.OnClick(func() {
saveDocument()
})
dontSave := dialog.AddButton("Don't Save")
dontSave.OnClick(func() {
discardChanges()
})
cancel := dialog.AddButton("Cancel")
// No callback needed - just dismisses dialog
dialog.SetDefaultButton(save)
dialog.SetCancelButton(cancel)
dialog.Show()
```
**Default and Cancel buttons:**
Use `SetDefaultButton()` to specify which button is highlighted and triggered by Enter.
Use `SetCancelButton()` to specify which button is triggered by Escape.
```go
dialog := app.Dialog.Question().
SetMessage("Delete file?")
deleteBtn := dialog.AddButton("Delete")
deleteBtn.OnClick(func() {
performDelete()
})
cancelBtn := dialog.AddButton("Cancel")
dialog.SetDefaultButton(cancelBtn) // Safe option highlighted by default
dialog.SetCancelButton(cancelBtn) // Escape triggers Cancel
dialog.Show()
```
### Window Attachment
@ -230,7 +270,7 @@ app.QuestionDialog().
Attach dialog to specific window:
```go
dialog := app.InfoDialog().
dialog := app.Dialog.Info().
SetMessage("Window-specific message").
AttachToWindow(window)
@ -258,10 +298,12 @@ dialog.Show()
**Example:**
```go
// Appears as sheet on macOS
app.QuestionDialog().
dialog := app.Dialog.Question().
SetMessage("Save changes?").
AttachToWindow(window).
Show()
AttachToWindow(window)
dialog.AddButton("Yes")
dialog.AddButton("No")
dialog.Show()
```
</TabItem>
@ -278,7 +320,7 @@ dialog.Show()
**Example:**
```go
// Modal dialog on Windows
app.ErrorDialog().
app.Dialog.Error().
SetTitle("Error").
SetMessage("Operation failed").
Show()
@ -297,7 +339,7 @@ dialog.Show()
**Example:**
```go
// GTK dialog on Linux
app.InfoDialog().
app.Dialog.Info().
SetMessage("Update complete").
Show()
```
@ -309,35 +351,41 @@ dialog.Show()
### Confirm Before Destructive Action
```go
func deleteFile(path string) error {
result, err := app.QuestionDialog().
func deleteFile(app *application.App, path string) {
dialog := app.Dialog.Question().
SetTitle("Confirm Delete").
SetMessage(fmt.Sprintf("Delete %s?", filepath.Base(path))).
SetButtons("Delete", "Cancel").
SetDefaultButton("Cancel").
Show()
if err != nil || result != "Delete" {
return errors.New("cancelled")
}
return os.Remove(path)
SetMessage(fmt.Sprintf("Delete %s?", filepath.Base(path)))
deleteBtn := dialog.AddButton("Delete")
deleteBtn.OnClick(func() {
if err := os.Remove(path); err != nil {
app.Dialog.Error().
SetTitle("Delete Failed").
SetMessage(err.Error()).
Show()
}
})
cancelBtn := dialog.AddButton("Cancel")
dialog.SetDefaultButton(cancelBtn)
dialog.SetCancelButton(cancelBtn)
dialog.Show()
}
```
### Error Handling with dialog
```go
func saveDocument(path string, data []byte) {
func saveDocument(app *application.App, path string, data []byte) {
if err := os.WriteFile(path, data, 0644); err != nil {
app.ErrorDialog().
app.Dialog.Error().
SetTitle("Save Failed").
SetMessage(fmt.Sprintf("Could not save file: %v", err)).
Show()
return
}
app.InfoDialog().
app.Dialog.Info().
SetTitle("Success").
SetMessage("File saved successfully!").
Show()
@ -347,27 +395,29 @@ func saveDocument(path string, data []byte) {
### File Selection with Validation
```go
func selectImageFile() (string, error) {
path, err := app.OpenFileDialog().
func selectImageFile(app *application.App) (string, error) {
path, err := app.Dialog.OpenFile().
SetTitle("Select Image").
SetFilters([]application.FileFilter{
{DisplayName: "Images", Pattern: "*.png;*.jpg;*.jpeg;*.gif"},
}).
AddFilter("Images", "*.png;*.jpg;*.jpeg;*.gif").
PromptForSingleSelection()
if err != nil {
return "", err
}
if path == "" {
return "", errors.New("no file selected")
}
// Validate file
if !isValidImage(path) {
app.ErrorDialog().
app.Dialog.Error().
SetTitle("Invalid File").
SetMessage("Selected file is not a valid image.").
Show()
return "", errors.New("invalid image")
}
return path, nil
}
```
@ -375,45 +425,43 @@ func selectImageFile() (string, error) {
### Multi-Step dialog Flow
```go
func exportData() {
func exportData(app *application.App) {
// Step 1: Confirm export
result, _ := app.QuestionDialog().
dialog := app.Dialog.Question().
SetTitle("Export Data").
SetMessage("Export all data to CSV?").
SetButtons("Export", "Cancel").
Show()
if result != "Export" {
return
}
// Step 2: Select destination
path, err := app.SaveFileDialog().
SetTitle("Save Export").
SetDefaultFilename("export.csv").
SetFilters([]application.FileFilter{
{DisplayName: "CSV Files", Pattern: "*.csv"},
}).
PromptForSingleSelection()
if err != nil {
return
}
// Step 3: Perform export
if err := performExport(path); err != nil {
app.ErrorDialog().
SetTitle("Export Failed").
SetMessage(err.Error()).
SetMessage("Export all data to CSV?")
exportBtn := dialog.AddButton("Export")
exportBtn.OnClick(func() {
// Step 2: Select destination
path, err := app.Dialog.SaveFile().
SetFilename("export.csv").
AddFilter("CSV Files", "*.csv").
PromptForSingleSelection()
if err != nil || path == "" {
return
}
// Step 3: Perform export
if err := performExport(path); err != nil {
app.Dialog.Error().
SetTitle("Export Failed").
SetMessage(err.Error()).
Show()
return
}
// Step 4: Success
app.Dialog.Info().
SetTitle("Export Complete").
SetMessage("Data exported successfully!").
Show()
return
}
// Step 4: Success
app.InfoDialog().
SetTitle("Export Complete").
SetMessage("Data exported successfully!").
Show()
})
cancelBtn := dialog.AddButton("Cancel")
dialog.SetCancelButton(cancelBtn)
dialog.Show()
}
```

View file

@ -261,96 +261,78 @@ app.OnApplicationEvent(application.EventApplicationShutdown, func(e *application
})
```
## dialog Methods
## Dialog Methods
### InfoDialog()
Dialogs are accessed through the `app.Dialog` manager. See [Dialogs API](/reference/dialogs/) for complete reference.
Creates an information dialog.
### Message Dialogs
```go
func (a *App) InfoDialog() InfoDialog
```
**Example:**
```go
app.InfoDialog().
// Information dialog
app.Dialog.Info().
SetTitle("Success").
SetMessage("Operation completed!").
Show()
```
### QuestionDialog()
Creates a question dialog.
```go
func (a *App) QuestionDialog() QuestionDialog
```
**Example:**
```go
result, _ := app.QuestionDialog().
SetTitle("Confirm").
SetMessage("Continue?").
SetButtons("Yes", "No").
// Error dialog
app.Dialog.Error().
SetTitle("Error").
SetMessage("Something went wrong.").
Show()
if result == "Yes" {
// Continue
}
// Warning dialog
app.Dialog.Warning().
SetTitle("Warning").
SetMessage("This action cannot be undone.").
Show()
```
### ErrorDialog()
### Question Dialogs
Creates an error dialog.
Question dialogs use button callbacks to handle user responses:
```go
func (a *App) ErrorDialog() ErrorDialog
dialog := app.Dialog.Question().
SetTitle("Confirm").
SetMessage("Continue?")
yes := dialog.AddButton("Yes")
yes.OnClick(func() {
// Handle yes
})
no := dialog.AddButton("No")
no.OnClick(func() {
// Handle no
})
dialog.SetDefaultButton(yes)
dialog.SetCancelButton(no)
dialog.Show()
```
### WarningDialog()
Creates a warning dialog.
### File Dialogs
```go
func (a *App) WarningDialog() WarningDialog
```
### OpenFileDialog()
Creates a file open dialog.
```go
func (a *App) OpenFileDialog() OpenFileDialog
```
**Example:**
```go
path, err := app.OpenFileDialog().
// Open file dialog
path, err := app.Dialog.OpenFile().
SetTitle("Select File").
SetFilters([]application.FileFilter{
{DisplayName: "Images", Pattern: "*.png;*.jpg"},
}).
AddFilter("Images", "*.png;*.jpg").
PromptForSingleSelection()
```
### SaveFileDialog()
// Save file dialog
path, err := app.Dialog.SaveFile().
SetTitle("Save File").
SetFilename("document.pdf").
AddFilter("PDF", "*.pdf").
PromptForSingleSelection()
Creates a file save dialog.
```go
func (a *App) SaveFileDialog() SaveFileDialog
```
### SelectFolderDialog()
Creates a folder selection dialog.
```go
func (a *App) SelectFolderDialog() SelectFolderDialog
// Folder selection (use OpenFile with directory options)
path, err := app.Dialog.OpenFile().
SetTitle("Select Folder").
CanChooseDirectories(true).
CanChooseFiles(false).
PromptForSingleSelection()
```
## Logger

View file

@ -9,37 +9,50 @@ import { Card, CardGrid } from "@astrojs/starlight/components";
## Overview
The Dialogs API provides methods to show native file dialogs and message dialogs.
The Dialogs API provides methods to show native file dialogs and message dialogs. Access dialogs through the `app.Dialog` manager.
**Dialog Types:**
- **File Dialogs** - Open, Save, and Select Folder dialogs
- **File Dialogs** - Open and Save dialogs
- **Message Dialogs** - Info, Error, Warning, and Question dialogs
All dialogs are **native OS dialogs** that match the platform's look and feel.
## Accessing Dialogs
Dialogs are accessed through the `app.Dialog` manager:
```go
app.Dialog.OpenFile()
app.Dialog.SaveFile()
app.Dialog.Info()
app.Dialog.Question()
app.Dialog.Warning()
app.Dialog.Error()
```
## File Dialogs
### OpenFileDialog()
### OpenFile()
Creates a file open dialog.
```go
func (a *App) OpenFileDialog() *OpenFileDialog
func (dm *DialogManager) OpenFile() *OpenFileDialogStruct
```
**Example:**
```go
dialog := app.OpenFileDialog()
dialog := app.Dialog.OpenFile()
```
### OpenFileDialog Methods
### OpenFileDialogStruct Methods
#### SetTitle()
Sets the dialog title.
```go
func (d *OpenFileDialog) SetTitle(title string) *OpenFileDialog
func (d *OpenFileDialogStruct) SetTitle(title string) *OpenFileDialogStruct
```
**Example:**
@ -47,43 +60,87 @@ func (d *OpenFileDialog) SetTitle(title string) *OpenFileDialog
dialog.SetTitle("Select Image")
```
#### SetFilters()
#### AddFilter()
Sets file type filters.
Adds a file type filter.
```go
func (d *OpenFileDialog) SetFilters(filters []FileFilter) *OpenFileDialog
func (d *OpenFileDialogStruct) AddFilter(displayName, pattern string) *OpenFileDialogStruct
```
**FileFilter structure:**
```go
type FileFilter struct {
DisplayName string // "Images", "Documents", etc.
Pattern string // "*.png;*.jpg", "*.pdf;*.doc", etc.
}
```
**Parameters:**
- `displayName` - Filter description shown to user (e.g., "Images", "Documents")
- `pattern` - Semicolon-separated list of extensions (e.g., "*.png;*.jpg")
**Example:**
```go
dialog.SetFilters([]application.FileFilter{
{DisplayName: "Images", Pattern: "*.png;*.jpg;*.gif"},
{DisplayName: "Documents", Pattern: "*.pdf;*.docx"},
{DisplayName: "All Files", Pattern: "*.*"},
})
dialog.AddFilter("Images", "*.png;*.jpg;*.gif").
AddFilter("Documents", "*.pdf;*.docx").
AddFilter("All Files", "*.*")
```
#### SetDefaultDirectory()
#### SetDirectory()
Sets the initial directory.
```go
func (d *OpenFileDialog) SetDefaultDirectory(path string) *OpenFileDialog
func (d *OpenFileDialogStruct) SetDirectory(directory string) *OpenFileDialogStruct
```
**Example:**
```go
homeDir, _ := os.UserHomeDir()
dialog.SetDefaultDirectory(homeDir)
dialog.SetDirectory(homeDir)
```
#### CanChooseDirectories()
Enables or disables directory selection.
```go
func (d *OpenFileDialogStruct) CanChooseDirectories(canChooseDirectories bool) *OpenFileDialogStruct
```
**Example (folder selection):**
```go
// Select folders instead of files
path, err := app.Dialog.OpenFile().
SetTitle("Select Folder").
CanChooseDirectories(true).
CanChooseFiles(false).
PromptForSingleSelection()
```
#### CanChooseFiles()
Enables or disables file selection.
```go
func (d *OpenFileDialogStruct) CanChooseFiles(canChooseFiles bool) *OpenFileDialogStruct
```
#### CanCreateDirectories()
Enables or disables creating new directories.
```go
func (d *OpenFileDialogStruct) CanCreateDirectories(canCreateDirectories bool) *OpenFileDialogStruct
```
#### ShowHiddenFiles()
Shows or hides hidden files.
```go
func (d *OpenFileDialogStruct) ShowHiddenFiles(showHiddenFiles bool) *OpenFileDialogStruct
```
#### AttachToWindow()
Attaches the dialog to a specific window.
```go
func (d *OpenFileDialogStruct) AttachToWindow(window Window) *OpenFileDialogStruct
```
#### PromptForSingleSelection()
@ -91,20 +148,18 @@ dialog.SetDefaultDirectory(homeDir)
Shows the dialog and returns the selected file.
```go
func (d *OpenFileDialog) PromptForSingleSelection() (string, error)
func (d *OpenFileDialogStruct) PromptForSingleSelection() (string, error)
```
**Returns:**
- `string` - Selected file path
- `error` - Error if cancelled or failed
- `string` - Selected file path (empty if cancelled)
- `error` - Error if dialog failed
**Example:**
```go
path, err := app.OpenFileDialog().
path, err := app.Dialog.OpenFile().
SetTitle("Select Image").
SetFilters([]application.FileFilter{
{DisplayName: "Images", Pattern: "*.png;*.jpg;*.gif"},
}).
AddFilter("Images", "*.png;*.jpg;*.gif").
PromptForSingleSelection()
if err != nil {
@ -121,20 +176,18 @@ processFile(path)
Shows the dialog and returns multiple selected files.
```go
func (d *OpenFileDialog) PromptForMultipleSelection() ([]string, error)
func (d *OpenFileDialogStruct) PromptForMultipleSelection() ([]string, error)
```
**Returns:**
- `[]string` - Array of selected file paths
- `error` - Error if cancelled or failed
- `error` - Error if dialog failed
**Example:**
```go
paths, err := app.OpenFileDialog().
paths, err := app.Dialog.OpenFile().
SetTitle("Select Images").
SetFilters([]application.FileFilter{
{DisplayName: "Images", Pattern: "*.png;*.jpg"},
}).
AddFilter("Images", "*.png;*.jpg").
PromptForMultipleSelection()
if err != nil {
@ -146,56 +199,70 @@ for _, path := range paths {
}
```
### SaveFileDialog()
### SaveFile()
Creates a file save dialog.
```go
func (a *App) SaveFileDialog() *SaveFileDialog
func (dm *DialogManager) SaveFile() *SaveFileDialogStruct
```
**Example:**
```go
dialog := app.SaveFileDialog()
dialog := app.Dialog.SaveFile()
```
### SaveFileDialog Methods
### SaveFileDialogStruct Methods
#### SetTitle()
Sets the dialog title.
```go
func (d *SaveFileDialog) SetTitle(title string) *SaveFileDialog
func (d *SaveFileDialogStruct) SetTitle(title string) *SaveFileDialogStruct
```
#### SetDefaultFilename()
#### SetFilename()
Sets the default filename.
```go
func (d *SaveFileDialog) SetDefaultFilename(filename string) *SaveFileDialog
func (d *SaveFileDialogStruct) SetFilename(filename string) *SaveFileDialogStruct
```
**Example:**
```go
dialog.SetDefaultFilename("document.pdf")
dialog.SetFilename("document.pdf")
```
#### SetFilters()
#### AddFilter()
Sets file type filters (same as OpenFileDialog).
Adds a file type filter.
```go
func (d *SaveFileDialog) SetFilters(filters []FileFilter) *SaveFileDialog
func (d *SaveFileDialogStruct) AddFilter(displayName, pattern string) *SaveFileDialogStruct
```
#### SetDefaultDirectory()
**Example:**
```go
dialog.AddFilter("PDF Document", "*.pdf").
AddFilter("Text Document", "*.txt")
```
Sets the initial directory (same as OpenFileDialog).
#### SetDirectory()
Sets the initial directory.
```go
func (d *SaveFileDialog) SetDefaultDirectory(path string) *SaveFileDialog
func (d *SaveFileDialogStruct) SetDirectory(directory string) *SaveFileDialogStruct
```
#### AttachToWindow()
Attaches the dialog to a specific window.
```go
func (d *SaveFileDialogStruct) AttachToWindow(window Window) *SaveFileDialogStruct
```
#### PromptForSingleSelection()
@ -203,17 +270,15 @@ func (d *SaveFileDialog) SetDefaultDirectory(path string) *SaveFileDialog
Shows the dialog and returns the save path.
```go
func (d *SaveFileDialog) PromptForSingleSelection() (string, error)
func (d *SaveFileDialogStruct) PromptForSingleSelection() (string, error)
```
**Example:**
```go
path, err := app.SaveFileDialog().
path, err := app.Dialog.SaveFile().
SetTitle("Save Document").
SetDefaultFilename("untitled.pdf").
SetFilters([]application.FileFilter{
{DisplayName: "PDF Document", Pattern: "*.pdf"},
}).
SetFilename("untitled.pdf").
AddFilter("PDF Document", "*.pdf").
PromptForSingleSelection()
if err != nil {
@ -225,49 +290,15 @@ if err != nil {
saveDocument(path)
```
### SelectFolderDialog()
### Folder Selection
Creates a folder selection dialog.
There is no separate `SelectFolderDialog`. Use `OpenFile()` with directory options:
```go
func (a *App) SelectFolderDialog() *SelectFolderDialog
```
**Example:**
```go
dialog := app.SelectFolderDialog()
```
### SelectFolderDialog Methods
#### SetTitle()
Sets the dialog title.
```go
func (d *SelectFolderDialog) SetTitle(title string) *SelectFolderDialog
```
#### SetDefaultDirectory()
Sets the initial directory.
```go
func (d *SelectFolderDialog) SetDefaultDirectory(path string) *SelectFolderDialog
```
#### PromptForSingleSelection()
Shows the dialog and returns the selected folder.
```go
func (d *SelectFolderDialog) PromptForSingleSelection() (string, error)
```
**Example:**
```go
path, err := app.SelectFolderDialog().
path, err := app.Dialog.OpenFile().
SetTitle("Select Output Folder").
CanChooseDirectories(true).
CanChooseFiles(false).
PromptForSingleSelection()
if err != nil {
@ -281,105 +312,98 @@ outputDir = path
## Message Dialogs
### InfoDialog()
All message dialogs return `*MessageDialog` and share the same methods.
### Info()
Creates an information dialog.
```go
func (a *App) InfoDialog() *InfoDialog
func (dm *DialogManager) Info() *MessageDialog
```
**Example:**
```go
dialog := app.InfoDialog()
```
### InfoDialog Methods
#### SetTitle()
Sets the dialog title.
```go
func (d *InfoDialog) SetTitle(title string) *InfoDialog
```
#### SetMessage()
Sets the dialog message.
```go
func (d *InfoDialog) SetMessage(message string) *InfoDialog
```
#### Show()
Shows the dialog.
```go
func (d *InfoDialog) Show()
```
**Example:**
```go
app.InfoDialog().
app.Dialog.Info().
SetTitle("Success").
SetMessage("File saved successfully!").
Show()
```
### ErrorDialog()
### Error()
Creates an error dialog.
```go
func (a *App) ErrorDialog() *ErrorDialog
func (dm *DialogManager) Error() *MessageDialog
```
**Methods:** Same as InfoDialog (SetTitle, SetMessage, Show)
**Example:**
```go
app.ErrorDialog().
app.Dialog.Error().
SetTitle("Error").
SetMessage("Failed to save file: " + err.Error()).
Show()
```
### WarningDialog()
### Warning()
Creates a warning dialog.
```go
func (a *App) WarningDialog() *WarningDialog
func (dm *DialogManager) Warning() *MessageDialog
```
**Methods:** Same as InfoDialog (SetTitle, SetMessage, Show)
**Example:**
```go
app.WarningDialog().
app.Dialog.Warning().
SetTitle("Warning").
SetMessage("This action cannot be undone.").
Show()
```
### QuestionDialog()
### Question()
Creates a question dialog with custom buttons.
```go
func (a *App) QuestionDialog() *QuestionDialog
func (dm *DialogManager) Question() *MessageDialog
```
### QuestionDialog Methods
**Example:**
```go
dialog := app.Dialog.Question().
SetTitle("Confirm").
SetMessage("Do you want to save changes?")
save := dialog.AddButton("Save")
save.OnClick(func() {
saveDocument()
})
dontSave := dialog.AddButton("Don't Save")
dontSave.OnClick(func() {
// Continue without saving
})
cancel := dialog.AddButton("Cancel")
cancel.OnClick(func() {
// Do nothing
})
dialog.SetDefaultButton(save)
dialog.SetCancelButton(cancel)
dialog.Show()
```
### MessageDialog Methods
#### SetTitle()
Sets the dialog title.
```go
func (d *QuestionDialog) SetTitle(title string) *QuestionDialog
func (d *MessageDialog) SetTitle(title string) *MessageDialog
```
#### SetMessage()
@ -387,24 +411,33 @@ func (d *QuestionDialog) SetTitle(title string) *QuestionDialog
Sets the dialog message.
```go
func (d *QuestionDialog) SetMessage(message string) *QuestionDialog
func (d *MessageDialog) SetMessage(message string) *MessageDialog
```
#### SetButtons()
#### SetIcon()
Sets the button labels.
Sets a custom icon for the dialog.
```go
func (d *QuestionDialog) SetButtons(buttons ...string) *QuestionDialog
func (d *MessageDialog) SetIcon(icon []byte) *MessageDialog
```
**Parameters:**
- `buttons` - Variable number of button labels
#### AddButton()
Adds a button to the dialog and returns the button for configuration.
```go
func (d *MessageDialog) AddButton(label string) *Button
```
**Returns:** `*Button` - The button instance for further configuration
**Example:**
```go
dialog.SetButtons("Yes", "No")
dialog.SetButtons("Save", "Don't Save", "Cancel")
button := dialog.AddButton("OK")
button.OnClick(func() {
// Handle click
})
```
#### SetDefaultButton()
@ -412,13 +445,14 @@ dialog.SetButtons("Save", "Don't Save", "Cancel")
Sets which button is the default (activated by pressing Enter).
```go
func (d *QuestionDialog) SetDefaultButton(index int) *QuestionDialog
func (d *MessageDialog) SetDefaultButton(button *Button) *MessageDialog
```
**Example:**
```go
// Make "Yes" the default button
dialog.SetButtons("Yes", "No").SetDefaultButton(0)
yes := dialog.AddButton("Yes")
no := dialog.AddButton("No")
dialog.SetDefaultButton(yes)
```
#### SetCancelButton()
@ -426,49 +460,58 @@ dialog.SetButtons("Yes", "No").SetDefaultButton(0)
Sets which button is the cancel button (activated by pressing Escape).
```go
func (d *QuestionDialog) SetCancelButton(index int) *QuestionDialog
func (d *MessageDialog) SetCancelButton(button *Button) *MessageDialog
```
**Example:**
```go
// Make "Cancel" the cancel button
dialog.SetButtons("Save", "Don't Save", "Cancel").SetCancelButton(2)
ok := dialog.AddButton("OK")
cancel := dialog.AddButton("Cancel")
dialog.SetCancelButton(cancel)
```
#### AttachToWindow()
Attaches the dialog to a specific window.
```go
func (d *MessageDialog) AttachToWindow(window Window) *MessageDialog
```
#### Show()
Shows the dialog and returns the selected button.
Shows the dialog. Button callbacks handle user responses.
```go
func (d *QuestionDialog) Show() (string, error)
func (d *MessageDialog) Show()
```
**Returns:**
- `string` - The label of the button that was clicked
- `error` - Error if dialog failed to show
**Note:** `Show()` does not return a value. Use button callbacks to handle user responses.
### Button Methods
#### OnClick()
Sets the callback function for when the button is clicked.
**Example:**
```go
result, err := app.QuestionDialog().
SetTitle("Confirm").
SetMessage("Do you want to save changes?").
SetButtons("Save", "Don't Save", "Cancel").
SetDefaultButton(0).
SetCancelButton(2).
Show()
func (b *Button) OnClick(callback func()) *Button
```
if err != nil {
return
}
#### SetAsDefault()
switch result {
case "Save":
saveDocument()
case "Don't Save":
// Continue without saving
case "Cancel":
return
}
Marks this button as the default button.
```go
func (b *Button) SetAsDefault() *Button
```
#### SetAsCancel()
Marks this button as the cancel button.
```go
func (b *Button) SetAsCancel() *Button
```
## Complete Examples
@ -477,16 +520,14 @@ case "Cancel":
```go
type FileService struct {
app *application.Application
app *application.App
}
func (s *FileService) OpenImage() (string, error) {
path, err := s.app.OpenFileDialog().
path, err := s.app.Dialog.OpenFile().
SetTitle("Select Image").
SetFilters([]application.FileFilter{
{DisplayName: "Images", Pattern: "*.png;*.jpg;*.jpeg;*.gif"},
{DisplayName: "All Files", Pattern: "*.*"},
}).
AddFilter("Images", "*.png;*.jpg;*.jpeg;*.gif").
AddFilter("All Files", "*.*").
PromptForSingleSelection()
if err != nil {
@ -497,13 +538,11 @@ func (s *FileService) OpenImage() (string, error) {
}
func (s *FileService) SaveDocument(defaultName string) (string, error) {
path, err := s.app.SaveFileDialog().
path, err := s.app.Dialog.SaveFile().
SetTitle("Save Document").
SetDefaultFilename(defaultName).
SetFilters([]application.FileFilter{
{DisplayName: "PDF Document", Pattern: "*.pdf"},
{DisplayName: "Text Document", Pattern: "*.txt"},
}).
SetFilename(defaultName).
AddFilter("PDF Document", "*.pdf").
AddFilter("Text Document", "*.txt").
PromptForSingleSelection()
if err != nil {
@ -514,8 +553,10 @@ func (s *FileService) SaveDocument(defaultName string) (string, error) {
}
func (s *FileService) SelectOutputFolder() (string, error) {
path, err := s.app.SelectFolderDialog().
path, err := s.app.Dialog.OpenFile().
SetTitle("Select Output Folder").
CanChooseDirectories(true).
CanChooseFiles(false).
PromptForSingleSelection()
if err != nil {
@ -529,64 +570,61 @@ func (s *FileService) SelectOutputFolder() (string, error) {
### Confirmation Dialog Example
```go
func (s *Service) DeleteItem(id string) error {
// Confirm before deleting
result, err := s.app.QuestionDialog().
func (s *Service) DeleteItem(app *application.App, id string) {
dialog := app.Dialog.Question().
SetTitle("Confirm Delete").
SetMessage("Are you sure you want to delete this item?").
SetButtons("Delete", "Cancel").
SetDefaultButton(1). // Default to Cancel
SetCancelButton(1).
Show()
SetMessage("Are you sure you want to delete this item?")
if err != nil || result == "Cancel" {
return nil // User cancelled
}
deleteBtn := dialog.AddButton("Delete")
deleteBtn.OnClick(func() {
deleteFromDatabase(id)
})
// Perform deletion
return deleteFromDatabase(id)
cancelBtn := dialog.AddButton("Cancel")
// Cancel does nothing
dialog.SetDefaultButton(cancelBtn) // Default to Cancel for safety
dialog.SetCancelButton(cancelBtn)
dialog.Show()
}
```
### Save Changes Dialog
```go
func (s *Editor) PromptSaveChanges() (bool, error) {
result, err := s.app.QuestionDialog().
func (s *Editor) PromptSaveChanges(app *application.App) {
dialog := app.Dialog.Question().
SetTitle("Unsaved Changes").
SetMessage("Do you want to save your changes before closing?").
SetButtons("Save", "Don't Save", "Cancel").
SetDefaultButton(0).
SetCancelButton(2).
Show()
SetMessage("Do you want to save your changes before closing?")
if err != nil {
return false, err
}
save := dialog.AddButton("Save")
save.OnClick(func() {
s.Save()
s.Close()
})
switch result {
case "Save":
return s.Save()
case "Don't Save":
return true, nil
case "Cancel":
return false, nil
}
dontSave := dialog.AddButton("Don't Save")
dontSave.OnClick(func() {
s.Close()
})
return false, nil
cancel := dialog.AddButton("Cancel")
// Cancel does nothing, dialog closes
dialog.SetDefaultButton(save)
dialog.SetCancelButton(cancel)
dialog.Show()
}
```
### Multi-File Processing
```go
func (s *Service) ProcessMultipleFiles() error {
func (s *Service) ProcessMultipleFiles(app *application.App) error {
// Select multiple files
paths, err := s.app.OpenFileDialog().
paths, err := app.Dialog.OpenFile().
SetTitle("Select Files to Process").
SetFilters([]application.FileFilter{
{DisplayName: "Images", Pattern: "*.png;*.jpg"},
}).
AddFilter("Images", "*.png;*.jpg").
PromptForMultipleSelection()
if err != nil {
@ -594,7 +632,7 @@ func (s *Service) ProcessMultipleFiles() error {
}
if len(paths) == 0 {
s.app.InfoDialog().
app.Dialog.Info().
SetTitle("No Files Selected").
SetMessage("Please select at least one file.").
Show()
@ -602,25 +640,19 @@ func (s *Service) ProcessMultipleFiles() error {
}
// Process files
for i, path := range paths {
for _, path := range paths {
err := processFile(path)
if err != nil {
s.app.ErrorDialog().
app.Dialog.Error().
SetTitle("Processing Error").
SetMessage(fmt.Sprintf("Failed to process %s: %v", path, err)).
Show()
continue
}
// Show progress
s.app.EmitEvent("progress", map[string]interface{}{
"current": i + 1,
"total": len(paths),
})
}
// Show completion
s.app.InfoDialog().
app.Dialog.Info().
SetTitle("Complete").
SetMessage(fmt.Sprintf("Successfully processed %d files", len(paths))).
Show()
@ -632,14 +664,12 @@ func (s *Service) ProcessMultipleFiles() error {
### Error Handling with Dialogs
```go
func (s *Service) SaveFile(data []byte) error {
func (s *Service) SaveFile(app *application.App, data []byte) error {
// Select save location
path, err := s.app.SaveFileDialog().
path, err := app.Dialog.SaveFile().
SetTitle("Save File").
SetDefaultFilename("data.json").
SetFilters([]application.FileFilter{
{DisplayName: "JSON File", Pattern: "*.json"},
}).
SetFilename("data.json").
AddFilter("JSON File", "*.json").
PromptForSingleSelection()
if err != nil {
@ -651,7 +681,7 @@ func (s *Service) SaveFile(data []byte) error {
err = os.WriteFile(path, data, 0644)
if err != nil {
// Show error dialog
s.app.ErrorDialog().
app.Dialog.Error().
SetTitle("Save Failed").
SetMessage(fmt.Sprintf("Could not save file: %v", err)).
Show()
@ -659,7 +689,7 @@ func (s *Service) SaveFile(data []byte) error {
}
// Show success
s.app.InfoDialog().
app.Dialog.Info().
SetTitle("Success").
SetMessage("File saved successfully!").
Show()
@ -692,33 +722,32 @@ func (s *Service) GetDefaultDirectory() string {
}
}
func (s *Service) OpenWithDefaults() (string, error) {
return s.app.OpenFileDialog().
func (s *Service) OpenWithDefaults(app *application.App) (string, error) {
return app.Dialog.OpenFile().
SetTitle("Open File").
SetDefaultDirectory(s.GetDefaultDirectory()).
SetFilters([]application.FileFilter{
{DisplayName: "All Files", Pattern: "*.*"},
}).
SetDirectory(s.GetDefaultDirectory()).
AddFilter("All Files", "*.*").
PromptForSingleSelection()
}
```
## Best Practices
### Do
### Do
- **Use native dialogs** - They match the platform's look and feel
- **Provide clear titles** - Help users understand the purpose
- **Set appropriate filters** - Guide users to correct file types
- **Handle cancellation** - Check for errors (user may cancel)
- **Show confirmation for destructive actions** - Use QuestionDialog
- **Provide feedback** - Use InfoDialog for success messages
- **Show confirmation for destructive actions** - Use Question dialogs
- **Provide feedback** - Use Info dialogs for success messages
- **Set sensible defaults** - Default directory, filename, etc.
- **Use callbacks for button actions** - Handle user responses properly
### Don't
### Don't
- **Don't ignore errors** - User cancellation returns an error
- **Don't use ambiguous button labels** - "OK" vs "Save"/"Cancel"
- **Don't use ambiguous button labels** - Be specific: "Save"/"Cancel"
- **Don't overuse dialogs** - They interrupt workflow
- **Don't show errors for cancellation** - It's a normal action
- **Don't forget file filters** - Help users find the right files
@ -749,14 +778,14 @@ func (s *Service) OpenWithDefaults() (string, error) {
### "Save As" Pattern
```go
func (s *Service) SaveAs(currentPath string) (string, error) {
func (s *Service) SaveAs(app *application.App, currentPath string) (string, error) {
// Extract filename from current path
filename := filepath.Base(currentPath)
// Show save dialog
path, err := s.app.SaveFileDialog().
path, err := app.Dialog.SaveFile().
SetTitle("Save As").
SetDefaultFilename(filename).
SetFilename(filename).
PromptForSingleSelection()
if err != nil {
@ -770,18 +799,21 @@ func (s *Service) SaveAs(currentPath string) (string, error) {
### "Open Recent" Pattern
```go
func (s *Service) OpenRecent(recentPath string) error {
func (s *Service) OpenRecent(app *application.App, recentPath string) error {
// Check if file still exists
if _, err := os.Stat(recentPath); os.IsNotExist(err) {
result, _ := s.app.QuestionDialog().
dialog := app.Dialog.Question().
SetTitle("File Not Found").
SetMessage("The file no longer exists. Remove from recent files?").
SetButtons("Remove", "Cancel").
Show()
SetMessage("The file no longer exists. Remove from recent files?")
if result == "Remove" {
remove := dialog.AddButton("Remove")
remove.OnClick(func() {
s.removeFromRecent(recentPath)
}
})
cancel := dialog.AddButton("Cancel")
dialog.SetCancelButton(cancel)
dialog.Show()
return err
}

View file

@ -21,6 +21,8 @@ After processing, the content will be moved to the main changelog and this file
## Changed
<!-- Changes in existing functionality -->
- **BREAKING**: Remove package-level dialog functions (`application.InfoDialog()`, `application.QuestionDialog()`, etc.). Use the `app.Dialog` manager instead: `app.Dialog.Info()`, `app.Dialog.Question()`, `app.Dialog.Warning()`, `app.Dialog.Error()`, `app.Dialog.OpenFile()`, `app.Dialog.SaveFile()`
- Update dialogs documentation to match actual API: use `app.Dialog.*`, `AddButton()` with callbacks (not `SetButtons()`), `SetDefaultButton(*Button)` (not string), `AddFilter()` (not `SetFilters()`), `SetFilename()` (not `SetDefaultFilename()`), and `app.Dialog.OpenFile().CanChooseDirectories(true)` for folder selection
## Fixed
<!-- Bug fixes -->

View file

@ -143,7 +143,7 @@ func main() {
sizeMenu.Add("Get Current WebviewWindow Size").OnClick(func(ctx *application.Context) {
currentWindow(func(w application.Window) {
width, height := w.Size()
application.InfoDialog().SetTitle("Current WebviewWindow Size").SetMessage("Width: " + strconv.Itoa(width) + " Height: " + strconv.Itoa(height)).Show()
app.Dialog.Info().SetTitle("Current WebviewWindow Size").SetMessage("Width: " + strconv.Itoa(width) + " Height: " + strconv.Itoa(height)).Show()
})
})
@ -174,7 +174,7 @@ func main() {
positionMenu.Add("Get Relative Position").OnClick(func(ctx *application.Context) {
currentWindow(func(w application.Window) {
x, y := w.RelativePosition()
application.InfoDialog().SetTitle("Current WebviewWindow Relative Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show()
app.Dialog.Info().SetTitle("Current WebviewWindow Relative Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show()
})
})
@ -241,24 +241,24 @@ func main() {
stateMenu.Add("Get Primary Screen").OnClick(func(ctx *application.Context) {
screen := app.Screen.GetPrimary()
msg := fmt.Sprintf("Screen: %+v", screen)
application.InfoDialog().SetTitle("Primary Screen").SetMessage(msg).Show()
app.Dialog.Info().SetTitle("Primary Screen").SetMessage(msg).Show()
})
stateMenu.Add("Get Screens").OnClick(func(ctx *application.Context) {
screens := app.Screen.GetAll()
for _, screen := range screens {
msg := fmt.Sprintf("Screen: %+v", screen)
application.InfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
app.Dialog.Info().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
}
})
stateMenu.Add("Get Screen for WebviewWindow").OnClick(func(ctx *application.Context) {
currentWindow(func(w application.Window) {
screen, err := w.GetScreen()
if err != nil {
application.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
app.Dialog.Error().SetTitle("Error").SetMessage(err.Error()).Show()
return
}
msg := fmt.Sprintf("Screen: %+v", screen)
application.InfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
app.Dialog.Info().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
})
})
app.Window.New()

View file

@ -30,28 +30,28 @@ func main() {
setClipboardMenu.Add("Set Text 'Hello'").OnClick(func(ctx *application.Context) {
success := app.Clipboard.SetText("Hello")
if !success {
application.InfoDialog().SetMessage("Failed to set clipboard text").Show()
app.Dialog.Info().SetMessage("Failed to set clipboard text").Show()
}
})
setClipboardMenu.Add("Set Text 'World'").OnClick(func(ctx *application.Context) {
success := app.Clipboard.SetText("World")
if !success {
application.InfoDialog().SetMessage("Failed to set clipboard text").Show()
app.Dialog.Info().SetMessage("Failed to set clipboard text").Show()
}
})
setClipboardMenu.Add("Set Text (current time)").OnClick(func(ctx *application.Context) {
success := app.Clipboard.SetText(time.Now().String())
if !success {
application.InfoDialog().SetMessage("Failed to set clipboard text").Show()
app.Dialog.Info().SetMessage("Failed to set clipboard text").Show()
}
})
getClipboardMenu := menu.AddSubmenu("Get Clipboard")
getClipboardMenu.Add("Get Text").OnClick(func(ctx *application.Context) {
result, ok := app.Clipboard.Text()
if !ok {
application.InfoDialog().SetMessage("Failed to get clipboard text").Show()
app.Dialog.Info().SetMessage("Failed to get clipboard text").Show()
} else {
application.InfoDialog().SetMessage("Got:\n\n" + result).Show()
app.Dialog.Info().SetMessage("Got:\n\n" + result).Show()
}
})
@ -59,9 +59,9 @@ func main() {
clearClipboardMenu.Add("Clear Text").OnClick(func(ctx *application.Context) {
success := app.Clipboard.SetText("")
if success {
application.InfoDialog().SetMessage("Clipboard text cleared").Show()
app.Dialog.Info().SetMessage("Clipboard text cleared").Show()
} else {
application.InfoDialog().SetMessage("Clipboard text not cleared").Show()
app.Dialog.Info().SetMessage("Clipboard text not cleared").Show()
}
})

View file

@ -44,7 +44,7 @@ func main() {
// Test 1: Basic file open with no filters (no window)
testMenu.Add("1. Basic Open (No Window)").OnClick(func(ctx *application.Context) {
result, err := application.OpenFileDialog().
result, err := app.Dialog.OpenFile().
CanChooseFiles(true).
PromptForSingleSelection()
showResult("Basic Open", result, err, nil)
@ -52,7 +52,7 @@ func main() {
// Test 1b: Basic file open with window
testMenu.Add("1b. Basic Open (With Window)").OnClick(func(ctx *application.Context) {
result, err := application.OpenFileDialog().
result, err := app.Dialog.OpenFile().
CanChooseFiles(true).
AttachToWindow(mainWindow).
PromptForSingleSelection()
@ -61,7 +61,7 @@ func main() {
// Test 2: Open with single extension filter
testMenu.Add("2. Single Filter").OnClick(func(ctx *application.Context) {
result, err := application.OpenFileDialog().
result, err := app.Dialog.OpenFile().
CanChooseFiles(true).
AddFilter("Text Files", "*.txt").
AttachToWindow(mainWindow).
@ -71,7 +71,7 @@ func main() {
// Test 3: Open with multiple extension filter
testMenu.Add("3. Multiple Filter").OnClick(func(ctx *application.Context) {
result, err := application.OpenFileDialog().
result, err := app.Dialog.OpenFile().
CanChooseFiles(true).
AddFilter("Documents", "*.txt;*.md;*.doc;*.docx").
AttachToWindow(mainWindow).
@ -81,7 +81,7 @@ func main() {
// Test 4: Multiple file selection
testMenu.Add("4. Multiple Selection").OnClick(func(ctx *application.Context) {
results, err := application.OpenFileDialog().
results, err := app.Dialog.OpenFile().
CanChooseFiles(true).
AddFilter("Images", "*.png;*.jpg;*.jpeg").
AttachToWindow(mainWindow).
@ -95,7 +95,7 @@ func main() {
// Test 5: Directory selection
testMenu.Add("5. Directory Selection").OnClick(func(ctx *application.Context) {
result, err := application.OpenFileDialog().
result, err := app.Dialog.OpenFile().
CanChooseDirectories(true).
CanChooseFiles(false).
AttachToWindow(mainWindow).
@ -105,7 +105,7 @@ func main() {
// Test 6: Save dialog with extension
testMenu.Add("6. Save Dialog").OnClick(func(ctx *application.Context) {
result, err := application.SaveFileDialog().
result, err := app.Dialog.SaveFile().
SetFilename("test.txt").
AddFilter("Text Files", "*.txt").
AttachToWindow(mainWindow).
@ -115,7 +115,7 @@ func main() {
// Test 7: Complex filters
testMenu.Add("7. Complex Filters").OnClick(func(ctx *application.Context) {
result, err := application.OpenFileDialog().
result, err := app.Dialog.OpenFile().
CanChooseFiles(true).
AddFilter("All Documents", "*.txt;*.md;*.doc;*.docx;*.pdf").
AddFilter("Text Files", "*.txt").
@ -129,7 +129,7 @@ func main() {
// Test 8: Hidden files
testMenu.Add("8. Show Hidden").OnClick(func(ctx *application.Context) {
result, err := application.OpenFileDialog().
result, err := app.Dialog.OpenFile().
CanChooseFiles(true).
ShowHiddenFiles(true).
AttachToWindow(mainWindow).
@ -140,7 +140,7 @@ func main() {
// Test 9: Default directory
testMenu.Add("9. Default Directory").OnClick(func(ctx *application.Context) {
home, _ := os.UserHomeDir()
result, err := application.OpenFileDialog().
result, err := app.Dialog.OpenFile().
CanChooseFiles(true).
SetDirectory(home).
AttachToWindow(mainWindow).
@ -151,7 +151,7 @@ func main() {
// Test 10: Full featured dialog
testMenu.Add("10. Full Featured").OnClick(func(ctx *application.Context) {
home, _ := os.UserHomeDir()
dialog := application.OpenFileDialog().
dialog := app.Dialog.OpenFile().
SetTitle("Full Featured Dialog").
SetDirectory(home).
CanChooseFiles(true).
@ -192,7 +192,7 @@ func showResult(test string, result string, err error, window *application.Webvi
return
}
if result == "" {
dialog := application.InfoDialog().
dialog := application.Get().Dialog.Info().
SetTitle(test).
SetMessage("No file selected")
if window != nil {
@ -201,7 +201,7 @@ func showResult(test string, result string, err error, window *application.Webvi
dialog.Show()
return
}
dialog := application.InfoDialog().
dialog := application.Get().Dialog.Info().
SetTitle(test).
SetMessage(fmt.Sprintf("Selected: %s\nType: %s", result, getFileType(result)))
if window != nil {
@ -212,7 +212,7 @@ func showResult(test string, result string, err error, window *application.Webvi
func showResults(test string, results []string, window *application.WebviewWindow) {
if len(results) == 0 {
dialog := application.InfoDialog().
dialog := application.Get().Dialog.Info().
SetTitle(test).
SetMessage("No files selected")
if window != nil {
@ -226,7 +226,7 @@ func showResults(test string, results []string, window *application.WebviewWindo
for _, result := range results {
message.WriteString(fmt.Sprintf("%s (%s)\n", result, getFileType(result)))
}
dialog := application.InfoDialog().
dialog := application.Get().Dialog.Info().
SetTitle(test).
SetMessage(message.String())
if window != nil {
@ -236,7 +236,7 @@ func showResults(test string, results []string, window *application.WebviewWindo
}
func showError(test string, err error, window *application.WebviewWindow) {
dialog := application.ErrorDialog().
dialog := application.Get().Dialog.Error().
SetTitle(test).
SetMessage(fmt.Sprintf("Error: %v", err))
if window != nil {
@ -249,11 +249,8 @@ func getFileType(path string) string {
if path == "" {
return "unknown"
}
ext := strings.ToLower(filepath.Ext(path))
ext := filepath.Ext(path)
if ext == "" {
if fi, err := os.Stat(path); err == nil && fi.IsDir() {
return "directory"
}
return "no extension"
}
return ext

View file

@ -36,24 +36,24 @@ func main() {
// Let's make a "Demo" menu
infoMenu := menu.AddSubmenu("Info")
infoMenu.Add("Info").OnClick(func(ctx *application.Context) {
dialog := application.InfoDialog()
dialog := app.Dialog.Info()
dialog.SetTitle("Custom Title")
dialog.SetMessage("This is a custom message")
dialog.Show()
})
infoMenu.Add("Info (Title only)").OnClick(func(ctx *application.Context) {
dialog := application.InfoDialog()
dialog := app.Dialog.Info()
dialog.SetTitle("Custom Title")
dialog.Show()
})
infoMenu.Add("Info (Message only)").OnClick(func(ctx *application.Context) {
dialog := application.InfoDialog()
dialog := app.Dialog.Info()
dialog.SetMessage("This is a custom message")
dialog.Show()
})
infoMenu.Add("Info (Custom Icon)").OnClick(func(ctx *application.Context) {
dialog := application.InfoDialog()
dialog := app.Dialog.Info()
dialog.SetTitle("Custom Icon Example")
dialog.SetMessage("Using a custom icon")
dialog.SetIcon(icons.ApplicationDarkMode256)
@ -65,14 +65,14 @@ func main() {
questionMenu := menu.AddSubmenu("Question")
questionMenu.Add("Question (No default)").OnClick(func(ctx *application.Context) {
dialog := application.QuestionDialog()
dialog := app.Dialog.Question()
dialog.SetMessage("No default button")
dialog.AddButton("Yes")
dialog.AddButton("No")
dialog.Show()
})
questionMenu.Add("Question (Attached to Window)").OnClick(func(ctx *application.Context) {
dialog := application.QuestionDialog()
dialog := app.Dialog.Question()
dialog.AttachToWindow(app.Window.Current())
dialog.SetMessage("No default button")
dialog.AddButton("Yes")
@ -80,7 +80,7 @@ func main() {
dialog.Show()
})
questionMenu.Add("Question (With Default)").OnClick(func(ctx *application.Context) {
dialog := application.QuestionDialog()
dialog := app.Dialog.Question()
dialog.SetTitle("Quit")
dialog.SetMessage("You have unsaved work. Are you sure you want to quit?")
dialog.AddButton("Yes").OnClick(func() {
@ -91,12 +91,12 @@ func main() {
dialog.Show()
})
questionMenu.Add("Question (With Cancel)").OnClick(func(ctx *application.Context) {
dialog := application.QuestionDialog().
dialog := app.Dialog.Question().
SetTitle("Update").
SetMessage("The cancel button is selected when pressing escape")
download := dialog.AddButton("📥 Download")
download.OnClick(func() {
application.InfoDialog().SetMessage("Downloading...").Show()
app.Dialog.Info().SetMessage("Downloading...").Show()
})
no := dialog.AddButton("Cancel")
dialog.SetDefaultButton(download)
@ -104,15 +104,15 @@ func main() {
dialog.Show()
})
questionMenu.Add("Question (Custom Icon)").OnClick(func(ctx *application.Context) {
dialog := application.QuestionDialog()
dialog := app.Dialog.Question()
dialog.SetTitle("Custom Icon Example")
dialog.SetMessage("Using a custom icon")
dialog.SetIcon(icons.WailsLogoWhiteTransparent)
likeIt := dialog.AddButton("I like it!").OnClick(func() {
application.InfoDialog().SetMessage("Thanks!").Show()
app.Dialog.Info().SetMessage("Thanks!").Show()
})
dialog.AddButton("Not so keen...").OnClick(func() {
application.InfoDialog().SetMessage("Too bad!").Show()
app.Dialog.Info().SetMessage("Too bad!").Show()
})
dialog.SetDefaultButton(likeIt)
dialog.Show()
@ -120,23 +120,23 @@ func main() {
warningMenu := menu.AddSubmenu("Warning")
warningMenu.Add("Warning").OnClick(func(ctx *application.Context) {
application.WarningDialog().
app.Dialog.Warning().
SetTitle("Custom Title").
SetMessage("This is a custom message").
Show()
})
warningMenu.Add("Warning (Title only)").OnClick(func(ctx *application.Context) {
dialog := application.WarningDialog()
dialog := app.Dialog.Warning()
dialog.SetTitle("Custom Title")
dialog.Show()
})
warningMenu.Add("Warning (Message only)").OnClick(func(ctx *application.Context) {
dialog := application.WarningDialog()
dialog := app.Dialog.Warning()
dialog.SetMessage("This is a custom message")
dialog.Show()
})
warningMenu.Add("Warning (Custom Icon)").OnClick(func(ctx *application.Context) {
dialog := application.WarningDialog()
dialog := app.Dialog.Warning()
dialog.SetTitle("Custom Icon Example")
dialog.SetMessage("Using a custom icon")
dialog.SetIcon(icons.ApplicationLightMode256)
@ -145,23 +145,23 @@ func main() {
errorMenu := menu.AddSubmenu("Error")
errorMenu.Add("Error").OnClick(func(ctx *application.Context) {
dialog := application.ErrorDialog()
dialog := app.Dialog.Error()
dialog.SetTitle("Ooops")
dialog.SetMessage("I accidentally the whole of Twitter")
dialog.Show()
})
errorMenu.Add("Error (Title Only)").OnClick(func(ctx *application.Context) {
dialog := application.ErrorDialog()
dialog := app.Dialog.Error()
dialog.SetTitle("Custom Title")
dialog.Show()
})
errorMenu.Add("Error (Custom Message)").OnClick(func(ctx *application.Context) {
application.ErrorDialog().
app.Dialog.Error().
SetMessage("This is a custom message").
Show()
})
errorMenu.Add("Error (Custom Icon)").OnClick(func(ctx *application.Context) {
dialog := application.ErrorDialog()
dialog := app.Dialog.Error()
dialog.SetTitle("Custom Icon Example")
dialog.SetMessage("Using a custom icon")
dialog.SetIcon(icons.WailsLogoWhite)
@ -170,90 +170,90 @@ func main() {
openMenu := menu.AddSubmenu("Open")
openMenu.Add("Open File").OnClick(func(ctx *application.Context) {
result, _ := application.OpenFileDialog().
result, _ := app.Dialog.OpenFile().
CanChooseFiles(true).
PromptForSingleSelection()
if result != "" {
application.InfoDialog().SetMessage(result).Show()
app.Dialog.Info().SetMessage(result).Show()
} else {
application.InfoDialog().SetMessage("No file selected").Show()
app.Dialog.Info().SetMessage("No file selected").Show()
}
})
openMenu.Add("Open File (Show Hidden Files)").OnClick(func(ctx *application.Context) {
result, _ := application.OpenFileDialog().
result, _ := app.Dialog.OpenFile().
CanChooseFiles(true).
CanCreateDirectories(true).
ShowHiddenFiles(true).
PromptForSingleSelection()
if result != "" {
application.InfoDialog().SetMessage(result).Show()
app.Dialog.Info().SetMessage(result).Show()
} else {
application.InfoDialog().SetMessage("No file selected").Show()
app.Dialog.Info().SetMessage("No file selected").Show()
}
})
openMenu.Add("Open File (Attach to window)").OnClick(func(ctx *application.Context) {
result, _ := application.OpenFileDialog().
result, _ := app.Dialog.OpenFile().
CanChooseFiles(true).
CanCreateDirectories(true).
ShowHiddenFiles(true).
AttachToWindow(app.Window.Current()).
PromptForSingleSelection()
if result != "" {
application.InfoDialog().SetMessage(result).Show()
app.Dialog.Info().SetMessage(result).Show()
} else {
application.InfoDialog().SetMessage("No file selected").Show()
app.Dialog.Info().SetMessage("No file selected").Show()
}
})
openMenu.Add("Open Multiple Files (Show Hidden Files)").OnClick(func(ctx *application.Context) {
result, _ := application.OpenFileDialog().
result, _ := app.Dialog.OpenFile().
CanChooseFiles(true).
CanCreateDirectories(true).
ShowHiddenFiles(true).
PromptForMultipleSelection()
if len(result) > 0 {
application.InfoDialog().SetMessage(strings.Join(result, ",")).Show()
app.Dialog.Info().SetMessage(strings.Join(result, ",")).Show()
} else {
application.InfoDialog().SetMessage("No file selected").Show()
app.Dialog.Info().SetMessage("No file selected").Show()
}
})
openMenu.Add("Open Directory").OnClick(func(ctx *application.Context) {
result, _ := application.OpenFileDialog().
result, _ := app.Dialog.OpenFile().
CanChooseDirectories(true).
CanChooseFiles(false).
PromptForSingleSelection()
if result != "" {
application.InfoDialog().SetMessage(result).Show()
app.Dialog.Info().SetMessage(result).Show()
} else {
application.InfoDialog().SetMessage("No directory selected").Show()
app.Dialog.Info().SetMessage("No directory selected").Show()
}
})
openMenu.Add("Open Directory (Create Directories)").OnClick(func(ctx *application.Context) {
result, _ := application.OpenFileDialog().
result, _ := app.Dialog.OpenFile().
CanChooseDirectories(true).
CanCreateDirectories(true).
CanChooseFiles(false).
PromptForSingleSelection()
if result != "" {
application.InfoDialog().SetMessage(result).Show()
app.Dialog.Info().SetMessage(result).Show()
} else {
application.InfoDialog().SetMessage("No directory selected").Show()
app.Dialog.Info().SetMessage("No directory selected").Show()
}
})
openMenu.Add("Open Directory (Resolves Aliases)").OnClick(func(ctx *application.Context) {
result, _ := application.OpenFileDialog().
result, _ := app.Dialog.OpenFile().
CanChooseDirectories(true).
CanCreateDirectories(true).
CanChooseFiles(false).
ResolvesAliases(true).
PromptForSingleSelection()
if result != "" {
application.InfoDialog().SetMessage(result).Show()
app.Dialog.Info().SetMessage(result).Show()
} else {
application.InfoDialog().SetMessage("No directory selected").Show()
app.Dialog.Info().SetMessage("No directory selected").Show()
}
})
openMenu.Add("Open File/Directory (Set Title)").OnClick(func(ctx *application.Context) {
dialog := application.OpenFileDialog().
dialog := app.Dialog.OpenFile().
CanChooseDirectories(true).
CanCreateDirectories(true).
ResolvesAliases(true)
@ -265,14 +265,14 @@ func main() {
result, _ := dialog.PromptForSingleSelection()
if result != "" {
application.InfoDialog().SetMessage(result).Show()
app.Dialog.Info().SetMessage(result).Show()
} else {
application.InfoDialog().SetMessage("No file/directory selected").Show()
app.Dialog.Info().SetMessage("No file/directory selected").Show()
}
})
openMenu.Add("Open (Full Example)").OnClick(func(ctx *application.Context) {
cwd, _ := os.Getwd()
dialog := application.OpenFileDialog().
dialog := app.Dialog.OpenFile().
SetTitle("Select a file").
SetMessage("Select a file to open").
SetButtonText("Let's do this!").
@ -294,46 +294,46 @@ func main() {
result, _ := dialog.PromptForSingleSelection()
if result != "" {
application.InfoDialog().SetMessage(result).Show()
app.Dialog.Info().SetMessage(result).Show()
} else {
application.InfoDialog().SetMessage("No file selected").Show()
app.Dialog.Info().SetMessage("No file selected").Show()
}
})
saveMenu := menu.AddSubmenu("Save")
saveMenu.Add("Select File (Defaults)").OnClick(func(ctx *application.Context) {
result, _ := application.SaveFileDialog().
result, _ := app.Dialog.SaveFile().
PromptForSingleSelection()
if result != "" {
application.InfoDialog().SetMessage(result).Show()
app.Dialog.Info().SetMessage(result).Show()
}
})
saveMenu.Add("Select File (Attach To WebviewWindow)").OnClick(func(ctx *application.Context) {
result, _ := application.SaveFileDialog().
result, _ := app.Dialog.SaveFile().
AttachToWindow(app.Window.Current()).
PromptForSingleSelection()
if result != "" {
application.InfoDialog().SetMessage(result).Show()
app.Dialog.Info().SetMessage(result).Show()
}
})
saveMenu.Add("Select File (Show Hidden Files)").OnClick(func(ctx *application.Context) {
result, _ := application.SaveFileDialog().
result, _ := app.Dialog.SaveFile().
ShowHiddenFiles(true).
PromptForSingleSelection()
if result != "" {
application.InfoDialog().SetMessage(result).Show()
app.Dialog.Info().SetMessage(result).Show()
}
})
saveMenu.Add("Select File (Cannot Create Directories)").OnClick(func(ctx *application.Context) {
result, _ := application.SaveFileDialog().
result, _ := app.Dialog.SaveFile().
CanCreateDirectories(false).
PromptForSingleSelection()
if result != "" {
application.InfoDialog().SetMessage(result).Show()
app.Dialog.Info().SetMessage(result).Show()
}
})
saveMenu.Add("Select File (Full Example)").OnClick(func(ctx *application.Context) {
result, _ := application.SaveFileDialog().
result, _ := app.Dialog.SaveFile().
CanCreateDirectories(false).
ShowHiddenFiles(true).
SetMessage("Select a file").
@ -346,7 +346,7 @@ func main() {
ShowHiddenFiles(true).
PromptForSingleSelection()
if result != "" {
application.InfoDialog().SetMessage(result).Show()
app.Dialog.Info().SetMessage(result).Show()
}
})

View file

@ -6,8 +6,10 @@ import (
"log"
)
var app *application.App
func main() {
app := application.New(application.Options{
app = application.New(application.Options{
Name: "Key Bindings Demo",
Description: "A demo of the Key Bindings Options",
Mac: application.MacOptions{
@ -15,7 +17,7 @@ func main() {
},
KeyBindings: map[string]func(window application.Window){
"shift+ctrl+c": func(window application.Window) {
selection, err := application.OpenFileDialog().
selection, err := app.Dialog.OpenFile().
CanChooseFiles(true).
CanCreateDirectories(true).
ShowHiddenFiles(true).

View file

@ -65,7 +65,7 @@ func main() {
})
window.OnWindowEvent(events.Common.WindowShow, func(event *application.WindowEvent) {
application.InfoDialog().
app.Dialog.Info().
SetTitle("File Opened").
SetMessage("Application opened with file: " + filename).
Show()

View file

@ -3,6 +3,7 @@ package main
import (
_ "embed"
"encoding/base64"
"fmt"
"log"
"runtime"
"strings"
@ -25,10 +26,11 @@ func main() {
// Check if running on macOS
if runtime.GOOS != "darwin" {
// Show dialog for non-macOS platforms
application.InfoDialog().
app.Dialog.Info().
SetTitle("macOS Only Demo").
SetMessage("The Liquid Glass effect is a macOS-specific feature that uses native NSGlassEffectView (macOS 15.0+) or NSVisualEffectView.\n\nThis demo is not available on " + runtime.GOOS + ".").
Show()
fmt.Println("The Liquid Glass effect is a macOS-specific feature. This demo is not available on", runtime.GOOS)
return
}

View file

@ -10,6 +10,8 @@ import (
//go:embed assets/*
var assets embed.FS
var app *application.App
type WindowService struct{}
func (s *WindowService) GeneratePanic() {
@ -27,7 +29,7 @@ func (s *WindowService) call2() {
// ==============================================
func main() {
app := application.New(application.Options{
app = application.New(application.Options{
Name: "Panic Handler Demo",
Description: "A demo of Handling Panics",
Assets: application.AssetOptions{
@ -44,7 +46,7 @@ func main() {
fmt.Printf("Time: %s\n", panicDetails.Time)
fmt.Printf("Error: %s\n", panicDetails.Error)
fmt.Printf("Stacktrace: %s\n", panicDetails.StackTrace)
application.InfoDialog().SetMessage("There was a panic!").Show()
app.Dialog.Info().SetMessage("There was a panic!").Show()
},
})

View file

@ -59,7 +59,7 @@ func main() {
myMenu.Add("Hello World!").OnClick(func(ctx *application.Context) {
println("Hello World!")
q := application.QuestionDialog().SetTitle("Ready?").SetMessage("Are you feeling ready?")
q := app.Dialog.Question().SetTitle("Ready?").SetMessage("Are you feeling ready?")
q.AddButton("Yes").OnClick(func() {
println("Awesome!")
})
@ -75,7 +75,7 @@ func main() {
myMenu.AddSeparator()
myMenu.AddCheckbox("Checked", true).OnClick(func(ctx *application.Context) {
println("Checked: ", ctx.ClickedMenuItem().Checked())
application.InfoDialog().SetTitle("Hello World!").SetMessage("Hello World!").Show()
app.Dialog.Info().SetTitle("Hello World!").SetMessage("Hello World!").Show()
})
myMenu.Add("Enabled").OnClick(func(ctx *application.Context) {
println("Click me!")

View file

@ -512,7 +512,7 @@ func main() {
sizeMenu.Add("Get Current WebviewWindow Size").OnClick(func(ctx *application.Context) {
currentWindow(func(w application.Window) {
width, height := w.Size()
application.InfoDialog().SetTitle("Current WebviewWindow Size").SetMessage("Width: " + strconv.Itoa(width) + " Height: " + strconv.Itoa(height)).Show()
app.Dialog.Info().SetTitle("Current WebviewWindow Size").SetMessage("Width: " + strconv.Itoa(width) + " Height: " + strconv.Itoa(height)).Show()
})
})
@ -544,7 +544,7 @@ func main() {
positionMenu.Add("Get Position").OnClick(func(ctx *application.Context) {
currentWindow(func(w application.Window) {
x, y := w.Position()
application.InfoDialog().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show()
app.Dialog.Info().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show()
})
})
@ -568,7 +568,7 @@ func main() {
positionMenu.Add("Get Relative Position").OnClick(func(ctx *application.Context) {
currentWindow(func(w application.Window) {
x, y := w.RelativePosition()
application.InfoDialog().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show()
app.Dialog.Info().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show()
})
})
@ -681,24 +681,24 @@ func main() {
stateMenu.Add("Get Primary Screen").OnClick(func(ctx *application.Context) {
screen := app.Screen.GetPrimary()
msg := fmt.Sprintf("Screen: %+v", screen)
application.InfoDialog().SetTitle("Primary Screen").SetMessage(msg).Show()
app.Dialog.Info().SetTitle("Primary Screen").SetMessage(msg).Show()
})
stateMenu.Add("Get Screens").OnClick(func(ctx *application.Context) {
screens := app.Screen.GetAll()
for _, screen := range screens {
msg := fmt.Sprintf("Screen: %+v", screen)
application.InfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
app.Dialog.Info().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
}
})
stateMenu.Add("Get Screen for WebviewWindow").OnClick(func(ctx *application.Context) {
currentWindow(func(w application.Window) {
screen, err := w.GetScreen()
if err != nil {
application.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
app.Dialog.Error().SetTitle("Error").SetMessage(err.Error()).Show()
return
}
msg := fmt.Sprintf("Screen: %+v", screen)
application.InfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
app.Dialog.Info().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
})
})
stateMenu.Add("Disable for 5s").OnClick(func(ctx *application.Context) {

View file

@ -225,7 +225,7 @@ func main() {
sizeMenu.Add("Get Current WebviewWindow Size").OnClick(func(ctx *application.Context) {
currentWindow(func(w application.Window) {
width, height := w.Size()
application.InfoDialog().SetTitle("Current WebviewWindow Size").SetMessage("Width: " + strconv.Itoa(width) + " Height: " + strconv.Itoa(height)).Show()
app.Dialog.Info().SetTitle("Current WebviewWindow Size").SetMessage("Width: " + strconv.Itoa(width) + " Height: " + strconv.Itoa(height)).Show()
})
})
@ -256,7 +256,7 @@ func main() {
positionMenu.Add("Get Relative Position").OnClick(func(ctx *application.Context) {
currentWindow(func(w application.Window) {
x, y := w.RelativePosition()
application.InfoDialog().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show()
app.Dialog.Info().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show()
})
})
@ -275,7 +275,7 @@ func main() {
positionMenu.Add("Get Position").OnClick(func(ctx *application.Context) {
currentWindow(func(w application.Window) {
x, y := w.Position()
application.InfoDialog().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show()
app.Dialog.Info().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show()
})
})
@ -342,24 +342,24 @@ func main() {
stateMenu.Add("Get Primary Screen").OnClick(func(ctx *application.Context) {
screen := app.Screen.GetPrimary()
msg := fmt.Sprintf("Screen: %+v", screen)
application.InfoDialog().SetTitle("Primary Screen").SetMessage(msg).Show()
app.Dialog.Info().SetTitle("Primary Screen").SetMessage(msg).Show()
})
stateMenu.Add("Get Screens").OnClick(func(ctx *application.Context) {
screens := app.Screen.GetAll()
for _, screen := range screens {
msg := fmt.Sprintf("Screen: %+v", screen)
application.InfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
app.Dialog.Info().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
}
})
stateMenu.Add("Get Screen for WebviewWindow").OnClick(func(ctx *application.Context) {
currentWindow(func(w application.Window) {
screen, err := w.GetScreen()
if err != nil {
application.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
app.Dialog.Error().SetTitle("Error").SetMessage(err.Error()).Show()
return
}
msg := fmt.Sprintf("Screen: %+v", screen)
application.InfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
app.Dialog.Info().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
})
})
stateMenu.Add("Disable for 5s").OnClick(func(ctx *application.Context) {

View file

@ -863,30 +863,6 @@ func (a *App) SetIcon(icon []byte) {
}
}
func InfoDialog() *MessageDialog {
return newMessageDialog(InfoDialogType)
}
func QuestionDialog() *MessageDialog {
return newMessageDialog(QuestionDialogType)
}
func WarningDialog() *MessageDialog {
return newMessageDialog(WarningDialogType)
}
func ErrorDialog() *MessageDialog {
return newMessageDialog(ErrorDialogType)
}
func OpenFileDialog() *OpenFileDialogStruct {
return newOpenFileDialog()
}
func SaveFileDialog() *SaveFileDialogStruct {
return newSaveFileDialog()
}
func (a *App) dispatchOnMainThread(fn func()) {
// If we are on the main thread, just call the function
if a.impl.isOnMainThread() {

View file

@ -14,44 +14,44 @@ func newDialogManager(app *App) *DialogManager {
// OpenFile creates a file dialog for selecting files
func (dm *DialogManager) OpenFile() *OpenFileDialogStruct {
return OpenFileDialog()
return newOpenFileDialog()
}
// OpenFileWithOptions creates a file dialog with options
func (dm *DialogManager) OpenFileWithOptions(options *OpenFileDialogOptions) *OpenFileDialogStruct {
result := OpenFileDialog()
result := newOpenFileDialog()
result.SetOptions(options)
return result
}
// SaveFile creates a save file dialog
func (dm *DialogManager) SaveFile() *SaveFileDialogStruct {
return SaveFileDialog()
return newSaveFileDialog()
}
// SaveFileWithOptions creates a save file dialog with options
func (dm *DialogManager) SaveFileWithOptions(options *SaveFileDialogOptions) *SaveFileDialogStruct {
result := SaveFileDialog()
result := newSaveFileDialog()
result.SetOptions(options)
return result
}
// Info creates an information dialog
func (dm *DialogManager) Info() *MessageDialog {
return InfoDialog()
return newMessageDialog(InfoDialogType)
}
// Question creates a question dialog
func (dm *DialogManager) Question() *MessageDialog {
return QuestionDialog()
return newMessageDialog(QuestionDialogType)
}
// Warning creates a warning dialog
func (dm *DialogManager) Warning() *MessageDialog {
return WarningDialog()
return newMessageDialog(WarningDialogType)
}
// Error creates an error dialog
func (dm *DialogManager) Error() *MessageDialog {
return ErrorDialog()
return newMessageDialog(ErrorDialogType)
}

View file

@ -43,13 +43,13 @@ func (m *MessageProcessor) processDialogMethod(req *RuntimeRequest, window Windo
var dialog *MessageDialog
switch req.Method {
case DialogInfo:
dialog = InfoDialog()
dialog = newMessageDialog(InfoDialogType)
case DialogWarning:
dialog = WarningDialog()
dialog = newMessageDialog(WarningDialogType)
case DialogError:
dialog = ErrorDialog()
dialog = newMessageDialog(ErrorDialogType)
case DialogQuestion:
dialog = QuestionDialog()
dialog = newMessageDialog(QuestionDialogType)
}
var detached = args.Bool("Detached")
if detached == nil || !*detached {