summaryrefslogtreecommitdiff
path: root/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'main.go')
-rw-r--r--main.go235
1 files changed, 126 insertions, 109 deletions
diff --git a/main.go b/main.go
index 0a32930..ea16eee 100644
--- a/main.go
+++ b/main.go
@@ -1,144 +1,161 @@
package main
import (
- "fmt"
- "os"
- "time"
-
- "fyne.io/fyne/v2"
- "fyne.io/fyne/v2/app"
- "fyne.io/fyne/v2/container"
- "fyne.io/fyne/v2/dialog"
- "fyne.io/fyne/v2/widget"
+ "errors"
+ "fmt"
+ "log"
+ "os"
+ "path/filepath"
+ "time"
+
+ "fyne.io/fyne/v2"
+ "fyne.io/fyne/v2/app"
+ "fyne.io/fyne/v2/container"
+ "fyne.io/fyne/v2/dialog"
+ "fyne.io/fyne/v2/widget"
)
const (
- appId = "org.buetow.quicklogger"
+ appID = "org.buetow.quicklogger"
placeholderText = "Enter text here..."
maxTextLength = 5000 // Limit text length to prevent performance issues
)
-var (
- defaultDirectory = "."
-)
+const defaultDirectory = "."
var windowSize = fyne.NewSize(400, 100)
-func createPreferenceWindow(a fyne.App) fyne.Window {
- window := a.NewWindow("Preferences")
- directoryPreference := widget.NewEntry()
- directoryPreference.SetText(a.Preferences().StringWithFallback("Directory", defaultDirectory))
-
- window.SetContent(container.NewVBox(
- container.NewVBox(
- widget.NewLabel("Directory:"),
- directoryPreference,
- ),
- container.NewHBox(
- widget.NewButton("Save", func() {
- a.Preferences().SetString("Directory", directoryPreference.Text)
- window.Hide()
- }),
- )))
- window.Resize(windowSize)
-
- return window
+// logEntry writes text to a timestamped markdown file in dir.
+// Separates persistence logic from the UI so it can be tested independently.
+func logEntry(dir, text string) error {
+ filename := filepath.Join(dir, "ql-"+time.Now().Format("060102-150405")+".md")
+ return os.WriteFile(filename, []byte(text), 0o644)
}
-func createMainWindow(a fyne.App) fyne.Window {
- // Create main window
- window := a.NewWindow("Quick logger")
-
+// newInputWidget creates the multi-line text entry with platform-appropriate
+// wrapping and row count settings.
+func newInputWidget() *widget.Entry {
input := widget.NewMultiLineEntry()
- // Optimization 1: Disable word wrapping on Android to improve performance
- // Word wrapping causes expensive recalculations on every text change
+ input.SetPlaceHolder(placeholderText)
+
+ // On mobile, disable word wrapping and reduce visible rows to limit
+ // expensive recalculations and rendering area.
if fyne.CurrentDevice().IsMobile() {
input.Wrapping = fyne.TextWrapOff
+ input.SetMinRowsVisible(10)
} else {
input.Wrapping = fyne.TextWrapWord
+ input.SetMinRowsVisible(30)
}
- input.SetPlaceHolder(placeholderText)
- // Optimization 2: Reduce visible rows on mobile to limit rendering area
- if fyne.CurrentDevice().IsMobile() {
- input.SetMinRowsVisible(10)
- } else {
- input.SetMinRowsVisible(30)
- }
-
- // Optimization 3: Add text length indicator
- charCount := widget.NewLabel("0 chars")
- // Optimization 4: Throttle text changes with validation
- input.OnChanged = func(text string) {
- // Update character count
- charCount.SetText(fmt.Sprintf("%d chars", len(text)))
+ return input
+}
+
+func createPreferenceWindow(a fyne.App) fyne.Window {
+ window := a.NewWindow("Preferences")
+ directoryPreference := widget.NewEntry()
+ directoryPreference.SetText(a.Preferences().StringWithFallback("Directory", defaultDirectory))
+
+ window.SetContent(container.NewVBox(
+ container.NewVBox(
+ widget.NewLabel("Directory:"),
+ directoryPreference,
+ ),
+ container.NewHBox(
+ widget.NewButton("Save", func() {
+ a.Preferences().SetString("Directory", directoryPreference.Text)
+ window.Hide()
+ }),
+ )))
+ window.Resize(windowSize)
+
+ return window
+}
+
+func createMainWindow(a fyne.App) fyne.Window {
+ window := a.NewWindow("Quick logger")
+ input := newInputWidget()
+ charCount := widget.NewLabel("0 chars")
- // Warn if text is getting too long
- if len(text) > maxTextLength {
+ // Track whether the length warning has been shown so we don't fire a
+ // modal dialog on every keystroke above the limit.
+ warnShown := false
+ input.OnChanged = func(text string) {
+ charCount.SetText(fmt.Sprintf("%d chars", len(text)))
+ if len(text) > maxTextLength && !warnShown {
+ warnShown = true
dialog.ShowInformation("Text Limit",
fmt.Sprintf("Text is getting long (%d chars). Consider logging to avoid performance issues.", len(text)),
window)
+ } else if len(text) <= maxTextLength {
+ warnShown = false
+ }
+ }
+
+ // resetInput clears the text entry and character count.
+ resetInput := func() {
+ input.SetText("")
+ charCount.SetText("0 chars")
+ }
+
+ logTextButton := widget.NewButton("Log text", func() {
+ dir := a.Preferences().StringWithFallback("Directory", defaultDirectory)
+ if err := logEntry(dir, input.Text); err != nil {
+ dialog.ShowError(err, window)
+ return
+ }
+ resetInput()
+ })
+
+ clearButton := widget.NewButton("Clear", func() {
+ resetInput()
+ window.Canvas().Focus(input)
+ })
+
+ // loadSharedText reads Android-shared text from cache and populates the input.
+ // Used both at startup and when the app returns to the foreground.
+ // A missing cache file is expected (no share pending); real errors are logged.
+ loadSharedText := func() {
+ txt, err := readSharedFromCache()
+ if err != nil {
+ if !errors.Is(err, os.ErrNotExist) {
+ log.Printf("readSharedFromCache: %v", err)
+ }
+ return
+ }
+ if txt != "" {
+ input.SetText(txt)
+ charCount.SetText(fmt.Sprintf("%d chars", len(txt)))
+ window.Canvas().Focus(input)
}
}
- logTextButton := widget.NewButton("Log text", func() {
- filename := fmt.Sprintf("%s/ql-%s.md",
- a.Preferences().StringWithFallback("Directory", defaultDirectory),
- time.Now().Format("060102-150405"),
- )
- if err := os.WriteFile(filename, []byte(input.Text), 0644); err != nil {
- dialog.ShowError(err, window)
- return
- }
- input.SetText("")
- input.SetPlaceHolder(placeholderText)
- // Reset character count
- charCount.SetText("0 chars")
- })
-
- // Optimization 5: Add clear button for quick text clearing
- clearButton := widget.NewButton("Clear", func() {
- input.SetText("")
- charCount.SetText("0 chars")
- window.Canvas().Focus(input)
- })
-
- // If running on Android and shared text file exists, load it into the input
- if fyne.CurrentDevice().IsMobile() {
- if txt, err := readSharedFromCache(); err == nil && txt != "" {
- input.SetText(txt)
- charCount.SetText(fmt.Sprintf("%d chars", len(txt)))
- }
- }
-
- window.SetContent(container.NewVBox(
- input,
- container.NewHBox(
- logTextButton,
- clearButton,
- widget.NewButton("Preferences", func() {
- createPreferenceWindow(a).Show()
- }),
- charCount, // Show character count
- ),
- ))
- window.Resize(windowSize)
- window.Canvas().Focus(input)
-
- // On Android, also check for new shared text whenever app returns to foreground.
- if lc := a.Lifecycle(); lc != nil {
- lc.SetOnEnteredForeground(func() {
- if txt, err := readSharedFromCache(); err == nil && txt != "" {
- input.SetText(txt)
- charCount.SetText(fmt.Sprintf("%d chars", len(txt)))
- window.Canvas().Focus(input)
- }
- })
- }
+ if fyne.CurrentDevice().IsMobile() {
+ loadSharedText()
+ }
+
+ window.SetContent(container.NewVBox(
+ input,
+ container.NewHBox(
+ logTextButton,
+ clearButton,
+ widget.NewButton("Preferences", func() {
+ createPreferenceWindow(a).Show()
+ }),
+ charCount,
+ ),
+ ))
+ window.Resize(windowSize)
+ window.Canvas().Focus(input)
+
+ // On Android, also check for new shared text whenever app returns to foreground.
+ if lc := a.Lifecycle(); lc != nil {
+ lc.SetOnEnteredForeground(loadSharedText)
+ }
return window
}
func main() {
- createMainWindow(app.NewWithID(appId)).ShowAndRun()
+ createMainWindow(app.NewWithID(appID)).ShowAndRun()
}