Mouse Coordinates
What This Example Shows
This example demonstrates how to handle mouse events in a Bubble Tea application, including clicks, scrolling, and motion. It captures and displays mouse coordinates and event types in real-time.
The key here is enabling mouse support with options like tea.WithMouseAllMotion() to capture detailed mouse events without blocking the UI.
Understanding Mouse Events in Bubble Tea
Bubble Tea extends terminal UIs beyond keyboard input by supporting mouse events, allowing for more interactive apps. However, mouse support depends on the terminal emulator's ability to handle escape codes.
Bubble Tea provides options for mouse tracking:
tea.WithMouseAllMotion(): Captures all events, including continuous motion.tea.WithMouseCellMotion(): Captures events only on cell changes for better performance.tea.WithMouseNoMotion(): Basic clicks and wheel events, ignoring motion.
When enabled, Bubble Tea sends tea.MouseMsg with details like position (X, Y), event type, and button.
This is ideal for building rich, interactive terminal apps that respond to mouse input seamlessly, enhancing user experience in supported terminals.
Here's what happens:
- The app starts with mouse tracking enabled
- Mouse events trigger
tea.MouseMsgmessages - The update handles them by printing coordinates and event details immediately
How It Works
Enable Mouse Tracking
Initialize the program with mouse options:
p := tea.NewProgram(model{}, tea.WithMouseAllMotion())This captures all mouse motion events.
Handle Mouse Messages
In the update, process tea.MouseMsg:
case tea.MouseMsg:
return m, tea.Printf("(X: %d, Y: %d) %s", msg.X, msg.Y, tea.MouseEvent(msg))It prints the position and event type.
Allow Quitting
Handle keyboard input to exit:
case tea.KeyMsg:
if s := msg.String(); s == "ctrl+c" || s == "q" || s == "esc" {
return m, tea.Quit
}Users can quit anytime.
Mouse support varies by terminal; test in multiple environments as some may not transmit events properly.
Code Breakdown
The model stores mouse event state (though minimally used here):
type model struct {
mouseEvent tea.MouseEvent
}It could track last events in more complex apps.
Start the program with mouse enabled:
func main() {
p := tea.NewProgram(model{}, tea.WithMouseAllMotion())
if _, err := p.Run(); err != nil {
log.Fatal(err)
}
}
func (m model) Init() tea.Cmd {
return nil
}No initial commands needed.
Core event handling:
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
// Handle keys
case tea.MouseMsg:
// Handle mouse
}
return m, nil
}Switches on message type.
Process and print mouse events:
case tea.MouseMsg:
return m, tea.Printf("(X: %d, Y: %d) %s", msg.X, msg.Y, tea.MouseEvent(msg))Uses tea.Printf for immediate output outside the view cycle.
Quit on specific keys:
case tea.KeyMsg:
if s := msg.String(); s == "ctrl+c" || s == "q" || s == "esc" {
return m, tea.Quit
}Ensures clean exit.
The View
The view provides static instructions:
func (m model) View() string {
s := "Do mouse stuff. When you're done press q to quit.\n"
return s
}Dynamic output comes from tea.Printf in updates.
Final Code
package main
import (
"log"
tea "github.com/charmbracelet/bubbletea"
)
func main() {
p := tea.NewProgram(model{}, tea.WithMouseAllMotion())
if _, err := p.Run(); err != nil {
log.Fatal(err)
}
}
type model struct {
mouseEvent tea.MouseEvent
}
func (m model) Init() tea.Cmd {
return nil
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
if s := msg.String(); s == "ctrl+c" || s == "q" || s == "esc" {
return m, tea.Quit
}
case tea.MouseMsg:
return m, tea.Printf("(X: %d, Y: %d) %s", msg.X, msg.Y, tea.MouseEvent(msg))
}
return m, nil
}
func (m model) View() string {
s := "Do mouse stuff. When you're done press q to quit.\n"
return s
}How is this guide?