Για προχωρημένους
August 16, 2026 · View on GitHub
Συντομεύσεις πληκτρολογίου, ασύγχρονες γορουτίνες, ειδοποιήσεις τοστ, modals, καρτέλες, ακορντεόν, μενού περιβάλλοντος και οτιδήποτε άλλο δεν ταιριάζει τις προηγούμενες σελίδες.
Ειδοποιήσεις τοστ
Ένα χρονομετρημένο μήνυμα που εμφανίζεται, παραμένει για λίγα δευτερόλεπτα και εξαφανίζεται πάνω του δικός. Χωρίς διάλογο, χωρίς αποκλεισμό του χρήστη.
type UI struct {
toast proton.ToastState
}
// trigger from anywhere — goroutine-safe
u.toast.Show("File saved!", 2*time.Second)
// draw it LAST in your draw function so it renders on top of everything
proton.Toast(ctx, &u.toast)
Εάν δεν υπάρχει ενεργό τοστ, το «Τοστ» δεν αντλεί τίποτα. Δεν χρειάζεται να ελέγξετε πρώτα.
func (t *ToastState) Show(msg string, duration time.Duration)
proton.Toast(ctx proton.Context, state *proton.ToastState)
Overlay / Modal
Ένα σκοτεινό φόντο με κεντραρισμένο περιεχόμενο πάνω από όλα.
type UI struct {
modal proton.OverlayState
openBtn proton.Clickable
closeBtn proton.Clickable
}
// open it
proton.Pad(ctx, 4, func(ctx proton.Context) {
if proton.Button(ctx, &u.openBtn, "Open dialog") {
u.modal.Show()
}
})
// draw it — also at the end of your draw function
proton.Overlay(ctx, &u.modal, func(ctx proton.Context) {
proton.MinSize(ctx, 300, 0, func(ctx proton.Context) {
proton.Card(ctx, proton.RGB(0x2e3440), 12, 24, func(ctx proton.Context) {
proton.H5(ctx, "Are you sure?")
proton.Gap(ctx, 8)
proton.Label(ctx, "This cannot be undone.")
proton.Gap(ctx, 20)
proton.RowEnd(ctx,
func(ctx proton.Context) {
proton.Pad(ctx, 4, func(ctx proton.Context) {
if proton.OutlineButton(ctx, &u.closeBtn, "Cancel") {
u.modal.Hide()
}
})
},
func(ctx proton.Context) { proton.Gap(ctx, 8) },
func(ctx proton.Context) {
proton.Pad(ctx, 4, func(ctx proton.Context) {
if proton.Button(ctx, &u.openBtn, "Confirm") {
u.modal.Hide()
doThing()
}
})
},
)
})
})
})
func (o *OverlayState) Show()
func (o *OverlayState) Hide()
func (o *OverlayState) Toggle()
proton.Overlay(ctx proton.Context, state *proton.OverlayState, content func(proton.Context))
Η "Επικάλυψη" δεν αντλεί τίποτα όταν το "state.Visible" είναι ψευδές, επομένως μπορείτε να το καλέσετε κάθε πλαίσιο χωρίς καμία προϋπόθεση περιτύλιξης.
Συντομεύσεις πληκτρολογίου
Καταχωρίστε μια λειτουργία για ενεργοποίηση όταν πατηθεί ένας συνδυασμός πλήκτρων.
proton.OnKey(ctx, proton.ModCtrl, "S", func() { save() })
proton.OnKey(ctx, proton.ModCtrl, "Z", func() { undo() })
proton.OnKey(ctx, proton.ModCtrl|proton.ModShift, "N", func() { newFile() })
proton.OnKey(ctx, proton.ModNone, proton.KeyEscape, func() { closeDialog() })
Καλέστε το «OnKey» μέσα στη συνάρτηση κλήρωσης. Καταγράφει τη συντόμευση για αυτό πλαίσιο. Εφόσον η συνάρτηση σχεδίασης εκτελεί κάθε καρέ, οι συντομεύσεις παραμένουν ενεργές ως όσο το παράθυρο είναι ανοιχτό.
proton.OnKey(ctx proton.Context, modifiers proton.Modifier, keyName string, fn func())
Σταθερές τροποποιητή:
proton.ModNone // no modifier — just the key
proton.ModCtrl // Ctrl (Cmd on macOS)
proton.ModShift
proton.ModAlt
// combine with |
proton.ModCtrl | proton.ModShift
Σταθερές ονόματος κλειδιού (για κλειδιά χωρίς γράμματα):
proton.KeyEscape
proton.KeyReturn
proton.KeyBackspace
proton.KeyDelete
proton.KeyTab
proton.KeySpace
proton.KeyUp
proton.KeyDown
proton.KeyLeft
proton.KeyRight
Τα πλήκτρα γραμμάτων είναι απλώς συμβολοσειρές: "S", "Z", "N", ""A"".
Καρτέλες
Μια οριζόντια γραμμή καρτελών με μία περιοχή περιεχομένου που αλλάζει με βάση το επιλεγμένη καρτέλα.
type UI struct {
tabs proton.TabState
tabBtns [3]proton.Clickable
}
proton.Tabs(ctx,
[]string{"Files", "Settings", "About"},
u.tabBtns[:],
&u.tabs,
func(ctx proton.Context, i int) {
switch i {
case 0: drawFiles(ctx)
case 1: drawSettings(ctx)
case 2: drawAbout(ctx)
}
},
)
Το "u.tabs.Selected" διατηρεί το ευρετήριο της ενεργής καρτέλας. Μπορείτε να το ρυθμίσετε μέσω προγραμματισμού για εναλλαγή καρτελών από κώδικα.
proton.Tabs(ctx proton.Context, labels []string, btns []proton.Clickable, state *proton.TabState, content func(proton.Context, int))
Το τμήμα btns χρειάζεται ένα «με δυνατότητα κλικ» ανά καρτέλα. Το πέρασμα του "u.tabBtns[:]" είναι
ο ιδιωματικός τρόπος όταν δηλώνετε έναν πίνακα σταθερού μεγέθους στη δομή σας.
Ακορντεόν
Μια πτυσσόμενη ενότητα με κεφαλίδα με δυνατότητα κλικ.
type UI struct {
sec1 proton.AccordionState
sec1btn proton.Clickable
}
proton.Accordion(ctx, &u.sec1, &u.sec1btn, "Advanced Options", func(ctx proton.Context) {
proton.Label(ctx, "These options are hidden until the user expands this.")
proton.Gap(ctx, 8)
proton.Toggle(ctx, &u.expertMode, "Expert mode")
})
proton.Accordion(ctx proton.Context, state *proton.AccordionState, btn *proton.Clickable, title string, content func(proton.Context))
Το "state.Open" παρακολουθεί εάν έχει επεκταθεί. Μπορείτε να το ρυθμίσετε απευθείας για να ξεκινήσει
ένα ακορντεόν ανοιχτό: u.sec1.Open = true.
Μενού περιβάλλοντος
Ένα μενού με δεξί κλικ που εμφανίζεται στη θέση του δρομέα.
type UI struct {
menu proton.ContextMenuState
menuTag proton.FrameTag
}
items := []proton.ContextMenuItem{
{Label: "Copy"},
{Label: "Paste"},
{Label: "Delete"},
{Label: "Disabled option", Disabled: true},
}
chosen := proton.ContextMenu(ctx, &u.menu, &u.menuTag, items, func(ctx proton.Context) {
proton.Label(ctx, "Right-click anywhere in this area")
})
if chosen >= 0 {
fmt.Println("User picked:", items[chosen].Label)
}
proton.ContextMenu(ctx proton.Context, state *proton.ContextMenuState, tag *proton.FrameTag, items []proton.ContextMenuItem, content func(proton.Context)) int
Επιστρέφει -1 όταν δεν έχει επιλεγεί τίποτα και το ευρετήριο του στοιχείου στο πλαίσιο κάτι γίνεται κλικ. Το μενού κλείνει αυτόματα μετά από μια επιλογή.
Ασύγχρονες ενημερώσεις και γορουτίνες
Η συνάρτηση σχεδίασης εκτελείται στο κύριο νήμα. Όταν μια γορουτίνα τελειώνει τη δουλειά και αλλάζει κατάσταση, καλέστε «ctx.Invalidate()» για να ζητήσετε επανασχεδιασμό.
type UI struct {
loading bool
result string
fetchBtn proton.Clickable
}
// in your draw function
proton.Pad(ctx, 4, func(ctx proton.Context) {
if proton.Button(ctx, &u.fetchBtn, "Fetch") && !u.loading {
u.loading = true
go func() {
data := fetchFromAPI() // takes a while
u.result = data
u.loading = false
ctx.Invalidate() // wake up the render loop
}()
}
})
if u.loading {
proton.Row(ctx,
func(ctx proton.Context) { proton.Spinner(ctx, &u.spin, 18) },
func(ctx proton.Context) { proton.Gap(ctx, 8) },
func(ctx proton.Context) { proton.Muted(ctx, "Loading...") },
)
} else if u.result != "" {
proton.Label(ctx, u.result)
}
Χωρίς το "ctx.Invalidate()", το παράθυρο δεν θα επανασχεδιαστεί μέχρι να μετακινηθεί ο χρήστης το ποντίκι ή αλληλεπιδρά με αυτό. Καλέστε το πάντα μετά την αλλαγή κατάστασης από μια γορουτίνα.
Spinner
Μια κινούμενη ένδειξη φόρτωσης. Η κλήση του 'Spinner' διατηρεί αυτόματα το επανασχεδιασμός παραθύρου — δεν απαιτείται βρόχος «Invalidate()».
type UI struct {
spin proton.SpinnerState
}
proton.Spinner(ctx, &u.spin, 32) // 32dp diameter
proton.Spinner(ctx proton.Context, state *proton.SpinnerState, sizeDp float32)
Το "SpinnerState" παρακολουθεί την ώρα έναρξης της κινούμενης εικόνας. Δηλώστε ένα ανά κλώστη στην κρατική σας δομή.
SelectBox (αναπτυσσόμενο)
type UI struct {
langSel proton.SelectBoxState
}
langs := []string{"Go", "Rust", "Zig", "C", "Python"}
i := proton.SelectBox(ctx, &u.langSel, langs)
proton.Caption(ctx, "Selected: "+langs[i])
Το αναπτυσσόμενο μενού ανοίγει κάτω από το κουμπί και κλείνει με επιλογή ή εξωτερικό κλικ.
proton.SelectBox(ctx proton.Context, state *proton.SelectBoxState, options []string) int
Εάν — Απόδοση υπό όρους
Αποδίδει περιεχόμενο μόνο όταν μια συνθήκη είναι αληθής. Αποθηκεύει ένα μπλοκ "if" όταν εσείς απλά θέλετε να εμφανίσετε ή να αποκρύψετε ένα γραφικό στοιχείο.
proton.If(ctx, user.IsAdmin, func(ctx proton.Context) {
proton.Pad(ctx, 4, func(ctx proton.Context) {
if proton.Button(ctx, &u.deleteBtn, "Delete everything") {
deleteEverything()
}
})
})
proton.If(ctx proton.Context, cond bool, content func(proton.Context))
Περιοχή εστίασης — Χειρισμός κλειδιού εμβέλειας
Όταν χρειάζεστε ενεργά συμβάντα πληκτρολογίου μόνο σε μια συγκεκριμένη περιοχή της διεπαφής χρήστη, όχι σε παγκόσμιο επίπεδο. Συνήθως το "OnKey" είναι αρκετό — προσεγγίστε αυτό όταν έχετε δύο πάνελ που θα πρέπει να έχουν ανεξάρτητες συντομεύσεις πληκτρολογίου.
type UI struct {
editorTag proton.FrameTag
}
proton.FocusArea(ctx, &u.editorTag, "A", func(ctx proton.Context) {
proton.TextArea(ctx, &u.text, "Type here...")
})
proton.FocusArea(ctx proton.Context, tag *proton.FrameTag, keyName string, content func(proton.Context))
Επιλογές παραθύρου
// fullscreen
a.WindowEx("App", 800, 600, []proton.WindowOption{
proton.Fullscreen(),
}, draw)
// maximized
a.WindowEx("App", 800, 600, []proton.WindowOption{
proton.Maximized(),
}, draw)
proton.Fullscreen() proton.WindowOption
proton.Maximized() proton.WindowOption
Διατήρηση κινούμενων εικόνων σε λειτουργία
Το Proton επανασχεδιάζεται μόνο όταν υπάρχει είσοδος χρήστη ή καλείτε το "ctx.Invalidate()". Για κινούμενα σχέδια — γραμμές προόδου που γεμίζουν με την πάροδο του χρόνου, αντίστροφες μετρήσεις, οτιδήποτε βάσει χρόνου — καλέστε «Invalidate» στο τέλος κάθε καρέ για να διατηρήσετε τις επανασχεδιάσεις πηγαίνοντας:
func draw(ctx proton.Context, u *UI) {
if u.animating {
u.progress += 0.01
if u.progress >= 1.0 {
u.progress = 0
u.animating = false
}
proton.ProgressBar(ctx, u.progress)
ctx.Invalidate() // draw again next frame
}
}
Όταν το "u.animating" είναι ψευδές, το "Invalidate" σταματά να καλείται και το Proton επιστρέφει στην επανασχεδίαση μόνο με την εισαγωγή του χρήστη. Το γραφικό στοιχείο Spinner το κάνει αυτό αυτόματα — δεν χρειάζεται να το διαχειριστείτε μόνοι σας.