package main import ( "fmt" "path/filepath" "strings" "fyne.io/fyne/v2" "fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/widget" ) // Вспомогательные методы func (a *App) showError(title, message string) { dialog.ShowError(fmt.Errorf(message), a.window) a.statusLabel.SetText("❌ " + title) } func (a *App) showSuccess(title, message string) { dialog.ShowInformation(title, message, a.window) } func (a *App) detectMimeType() string { ext := strings.ToLower(filepath.Ext(a.fileName)) mimeTypes := map[string]string{ ".txt": "text/plain", ".pdf": "application/pdf", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".png": "image/png", ".zip": "application/zip", ".doc": "application/msword", ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", } if mime, ok := mimeTypes[ext]; ok { return mime } return "application/octet-stream" } // // Это делал DeepSeek оно криво, но работает. Оставлю это так, потому что всё равно планирую не использовать больше fyne type JournalEntryWidget struct { widget.BaseWidget event journalctlEntry label *widget.Label } func NewJournalEntryWidget(event journalctlEntry) *JournalEntryWidget { j := &JournalEntryWidget{ event: event, } j.ExtendBaseWidget(j) return j } func (j *JournalEntryWidget) CreateRenderer() fyne.WidgetRenderer { j.label = widget.NewLabel(j.formatEventText()) j.label.Wrapping = fyne.TextWrapBreak j.label.Selectable = true return widget.NewSimpleRenderer(j.label) } func (j *JournalEntryWidget) formatEventText() string { return fmt.Sprintf("%s :: %s :: %s :: %s :: %s", j.event.Timestamp, j.event.CommandLine, j.event.CommandName, j.event.SyslogIdentifier, j.event.Message) } func (j *JournalEntryWidget) UpdateEvent(event journalctlEntry) { j.event = event if j.label != nil { j.label.SetText(j.formatEventText()) } j.Refresh() } // // // // // func (a *App) isTextFile() bool { // ext := strings.ToLower(filepath.Ext(a.fileName)) // textExts := []string{".txt", ".csv", ".json", ".xml", ".yaml", ".yml", ".md", ".go", ".py", ".js"} // for _, textExt := range textExts { // if ext == textExt { // return true // } // }ц= // // Проверяем содержимое (простейшая проверка) // if len(a.fileContent) == 0 { // return false // } // // Если файл содержит null-байты, это скорее бинарный файл // for _, b := range a.fileContent { // if b == 0 { // return false // } // } // return true // } // type TextStats struct { // lines int // words int // chars int // bytes int // } // func (a *App) calculateTextStats() TextStats { // if len(a.fileContent) == 0 { // return TextStats{} // } // content := string(a.fileContent) // stats := TextStats{ // bytes: len(a.fileContent), // chars: len(content), // lines: strings.Count(content, "\n") + 1, // words: len(strings.Fields(content)), // } // return stats // } // Это делал DeepSeek оно криво, но работает. Оставлю это так, потому что всё равно планирую не использовать больше fyne // Кастомный виджет для элемента списка с динамической высотой type JournalListItem struct { widget.BaseWidget event journalctlEntry label *widget.Label } func NewJournalListItem(event journalctlEntry) *JournalListItem { j := &JournalListItem{ event: event, } j.ExtendBaseWidget(j) return j } func (j *JournalListItem) CreateRenderer() fyne.WidgetRenderer { j.label = widget.NewLabel(j.formatText()) j.label.Wrapping = fyne.TextWrapBreak j.label.Selectable = true return &journalListItemRenderer{ item: j, label: j.label, } } func (j *JournalListItem) formatText() string { return fmt.Sprintf("%s :: %s :: %s :: %s :: %s", j.event.Timestamp, j.event.CommandLine, j.event.CommandName, j.event.SyslogIdentifier, j.event.Message) } func (j *JournalListItem) Update(event journalctlEntry) { j.event = event if j.label != nil { j.label.SetText(j.formatText()) } j.Refresh() } type journalListItemRenderer struct { item *JournalListItem label *widget.Label } func (r *journalListItemRenderer) Layout(size fyne.Size) { // Рассчитываем высоту label на основе его содержимого minSize := r.label.MinSize() labelHeight := minSize.Height // Если высота label больше 50, используем её, иначе минимальную height := labelHeight if height < 50 { height = 50 } r.label.Resize(fyne.NewSize(size.Width, height)) r.label.Move(fyne.NewPos(0, 0)) } func (r *journalListItemRenderer) MinSize() fyne.Size { minSize := r.label.MinSize() if minSize.Height < 50 { minSize.Height = 50 } return minSize } func (r *journalListItemRenderer) Refresh() { r.label.Refresh() } func (r *journalListItemRenderer) Objects() []fyne.CanvasObject { return []fyne.CanvasObject{r.label} } func (r *journalListItemRenderer) Destroy() {} // // // // // // // // // // // Вспомогательные функции func formatFileSize(bytes int64) string { const unit = 1024 if bytes < unit { return fmt.Sprintf("%d Б", bytes) } div, exp := int64(unit), 0 for n := bytes / unit; n >= unit; n /= unit { div *= unit exp++ } return fmt.Sprintf("%.1f %cБ", float64(bytes)/float64(div), "КМГТП"[exp]) }