44 "context"
55 "io"
66 "sync/atomic"
7+ "time"
78
89 "github.com/charmbracelet/colorprofile"
910)
@@ -101,17 +102,23 @@ func WithoutRenderer() ProgramOption {
101102 }
102103}
103104
104- // WithFilter supplies an event filter that will be invoked before Bubble Tea
105- // processes a tea.Msg. The event filter can return any tea.Msg which will then
106- // get handled by Bubble Tea instead of the original event. If the event filter
107- // returns nil, the event will be ignored and Bubble Tea will not process it.
105+ // MsgFilter is a function that can be used to filter messages before they are
106+ // processed by Bubble Tea. If the provided function returns nil, the message will
107+ // be ignored and Bubble Tea will not process it.
108+ type MsgFilter func (Model , Msg ) Msg
109+
110+ // WithFilters supplies one or more message filters that will be invoked before
111+ // Bubble Tea processes a [Msg]. The message filter can return any [Msg] which
112+ // will then get handled by Bubble Tea instead of the original message. If the
113+ // filter returns nil for a specific message, the message will be ignored and
114+ // Bubble Tea will not process it, and not continue to the next filter.
108115//
109116// As an example, this could be used to prevent a program from shutting down if
110- // there are unsaved changes.
117+ // there are unsaved changes, or used to throttle/drop high-frequency messages .
111118//
112- // Example:
119+ // Example -- preventing a program from shutting down if there are unsaved changes :
113120//
114- // func filter (m tea.Model, msg tea.Msg) tea.Msg {
121+ // func preventUnsavedFilter (m tea.Model, msg tea.Msg) tea.Msg {
115122// if _, ok := msg.(tea.QuitMsg); !ok {
116123// return msg
117124// }
@@ -124,15 +131,54 @@ func WithoutRenderer() ProgramOption {
124131// return msg
125132// }
126133//
127- // p := tea.NewProgram(Model{}, tea.WithFilter(filter ));
134+ // p := tea.NewProgram(Model{}, tea.WithFilters(preventUnsavedFilter ));
128135//
129136// if _,err := p.Run(); err != nil {
130137// fmt.Println("Error running program:", err)
131138// os.Exit(1)
132139// }
133- func WithFilter ( filter func ( Model , Msg ) Msg ) ProgramOption {
140+ func WithFilters ( filters ... MsgFilter ) ProgramOption {
134141 return func (p * Program ) {
135- p .filter = filter
142+ if len (filters ) == 0 {
143+ p .filter = nil
144+ return
145+ }
146+ p .filter = func (m Model , msg Msg ) Msg {
147+ for _ , filter := range filters {
148+ msg = filter (m , msg )
149+ if msg == nil {
150+ return nil
151+ }
152+ }
153+ return msg
154+ }
155+ }
156+ }
157+
158+ // MouseThrottleFilter is a message filter that throttles [MouseWheelMsg] and
159+ // [MouseMotionMsg] messages. This is particularly useful when enabling
160+ // [MouseModeCellMotion] or [MouseModeAllMotion] mouse modes, which can often
161+ // send excessive messages when the user is moving the mouse very fast, causing
162+ // high-resource usage and sluggish re-rendering.
163+ //
164+ // If the provided throttle duration is 0, the default value of 15ms will be used.
165+ func MouseThrottleFilter (throttle time.Duration ) MsgFilter {
166+ if throttle <= 0 {
167+ throttle = 15 * time .Millisecond
168+ }
169+
170+ var lastMouseMsg , now time.Time
171+
172+ return func (_ Model , msg Msg ) Msg {
173+ switch msg .(type ) {
174+ case MouseWheelMsg , MouseMotionMsg :
175+ now = time .Now ()
176+ if now .Sub (lastMouseMsg ) < throttle {
177+ return nil
178+ }
179+ lastMouseMsg = now
180+ }
181+ return msg
136182 }
137183}
138184
0 commit comments