2015-10-31 00:33:45 +08:00
|
|
|
// Copyright 2015 The Tcell Authors
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the license at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package views
|
|
|
|
|
|
|
|
import (
|
2022-10-15 10:00:59 +08:00
|
|
|
"sync"
|
|
|
|
|
2020-08-26 07:20:58 +08:00
|
|
|
"github.com/gdamore/tcell/v2"
|
2015-10-31 00:33:45 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// Widget is the base object that all onscreen elements implement.
|
|
|
|
type Widget interface {
|
|
|
|
// Draw is called to inform the widget to draw itself. A containing
|
|
|
|
// Widget will generally call this during the application draw loop.
|
|
|
|
Draw()
|
|
|
|
|
|
|
|
// Resize is called in response to a resize of the View. Unlike with
|
|
|
|
// other events, Resize performed by parents first, and they must
|
|
|
|
// then call their children. This is because the children need to
|
|
|
|
// see the updated sizes from the parents before they are called.
|
|
|
|
// In general this is done *after* the views have updated.
|
|
|
|
Resize()
|
|
|
|
|
|
|
|
// HandleEvent is called to ask the widget to handle any events.
|
|
|
|
// If the widget has consumed the event, it should return true.
|
|
|
|
// Generally, events are handled by the lower layers first, that
|
|
|
|
// is for example, a button may have a chance to handle an event
|
|
|
|
// before the enclosing window or panel.
|
|
|
|
//
|
|
|
|
// Its expected that Resize events are consumed by the outermost
|
|
|
|
// Widget, and the turned into a Resize() call.
|
|
|
|
HandleEvent(ev tcell.Event) bool
|
|
|
|
|
|
|
|
// SetView is used by callers to set the visual context of the
|
|
|
|
// Widget. The Widget should use the View as a context for
|
|
|
|
// drawing.
|
|
|
|
SetView(view View)
|
|
|
|
|
|
|
|
// Size returns the size of the widget (content size) as width, height
|
|
|
|
// in columns. Layout managers should attempt to ensure that at least
|
|
|
|
// this much space is made available to the View for this Widget. Extra
|
|
|
|
// space may be allocated on as an needed basis.
|
|
|
|
Size() (int, int)
|
|
|
|
|
|
|
|
// Watch is used to register an interest in this widget's events.
|
|
|
|
// The handler will receive EventWidget events for this widget.
|
|
|
|
// The order of event delivery when there are multiple watchers is
|
|
|
|
// not specified, and may change from one event to the next.
|
|
|
|
Watch(handler tcell.EventHandler)
|
|
|
|
|
|
|
|
// Unwatch is used to urnegister an interest in this widget's events.
|
|
|
|
Unwatch(handler tcell.EventHandler)
|
|
|
|
}
|
|
|
|
|
2015-11-05 03:24:00 +08:00
|
|
|
// EventWidget is an event delivered by a specific widget.
|
2015-10-31 00:33:45 +08:00
|
|
|
type EventWidget interface {
|
|
|
|
Widget() Widget
|
|
|
|
tcell.Event
|
|
|
|
}
|
|
|
|
|
|
|
|
type widgetEvent struct {
|
|
|
|
widget Widget
|
|
|
|
tcell.EventTime
|
|
|
|
}
|
|
|
|
|
|
|
|
func (wev *widgetEvent) Widget() Widget {
|
|
|
|
return wev.widget
|
|
|
|
}
|
|
|
|
|
|
|
|
func (wev *widgetEvent) SetWidget(widget Widget) {
|
|
|
|
wev.widget = widget
|
|
|
|
}
|
|
|
|
|
2022-10-15 10:00:59 +08:00
|
|
|
// WidgetWatchers provides a common implementation for base Widget Watch and
|
|
|
|
// Unwatch interfaces, suitable for embedding in more concrete widget
|
|
|
|
// implementations. This implementation is thread-safe, meaning an embedder can
|
|
|
|
// expect safety when calling WidgetWatcher methods from separate goroutines.
|
|
|
|
// In general, the views package does not support thread safety.
|
2015-10-31 00:33:45 +08:00
|
|
|
type WidgetWatchers struct {
|
2022-10-15 10:00:59 +08:00
|
|
|
sync.Mutex
|
2015-10-31 00:33:45 +08:00
|
|
|
watchers map[tcell.EventHandler]struct{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Watch monitors this WidgetWatcher, causing the handler to be fired
|
|
|
|
// with EventWidget as they are occur on the watched Widget.
|
|
|
|
func (ww *WidgetWatchers) Watch(handler tcell.EventHandler) {
|
2022-10-15 10:00:59 +08:00
|
|
|
ww.Lock()
|
|
|
|
defer ww.Unlock()
|
2015-10-31 00:33:45 +08:00
|
|
|
if ww.watchers == nil {
|
|
|
|
ww.watchers = make(map[tcell.EventHandler]struct{})
|
|
|
|
}
|
|
|
|
ww.watchers[handler] = struct{}{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unwatch stops monitoring this WidgetWatcher. The handler will no longer
|
|
|
|
// be fired for Widget events.
|
|
|
|
func (ww *WidgetWatchers) Unwatch(handler tcell.EventHandler) {
|
2022-10-15 10:00:59 +08:00
|
|
|
ww.Lock()
|
|
|
|
defer ww.Unlock()
|
2015-10-31 00:33:45 +08:00
|
|
|
if ww.watchers != nil {
|
|
|
|
delete(ww.watchers, handler)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// PostEvent delivers the EventWidget to all registered watchers. It is
|
|
|
|
// to be called by the Widget implementation.
|
|
|
|
func (ww *WidgetWatchers) PostEvent(wev EventWidget) {
|
2022-10-15 10:00:59 +08:00
|
|
|
ww.Lock()
|
|
|
|
watcherCopy := make(map[tcell.EventHandler]struct{}, len(ww.watchers))
|
|
|
|
for k := range ww.watchers {
|
|
|
|
watcherCopy[k] = struct{}{}
|
|
|
|
}
|
|
|
|
ww.Unlock()
|
|
|
|
for watcher := range watcherCopy {
|
2015-10-31 00:33:45 +08:00
|
|
|
// Deliver events to all listeners, ignoring return value.
|
|
|
|
watcher.HandleEvent(wev)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// PostEventWidgetContent is called by the Widget when its content is
|
|
|
|
// changed, delivering EventWidgetContent to all watchers.
|
|
|
|
func (ww *WidgetWatchers) PostEventWidgetContent(w Widget) {
|
|
|
|
ev := &EventWidgetContent{}
|
|
|
|
ev.SetWidget(w)
|
|
|
|
ev.SetEventNow()
|
|
|
|
ww.PostEvent(ev)
|
|
|
|
}
|
|
|
|
|
|
|
|
// PostEventWidgetResize is called by the Widget when the underlying View
|
|
|
|
// has resized, delivering EventWidgetResize to all watchers.
|
|
|
|
func (ww *WidgetWatchers) PostEventWidgetResize(w Widget) {
|
|
|
|
ev := &EventWidgetResize{}
|
|
|
|
ev.SetWidget(w)
|
|
|
|
ev.SetEventNow()
|
|
|
|
ww.PostEvent(ev)
|
|
|
|
}
|
|
|
|
|
|
|
|
// PostEventWidgetMove is called by the Widget when it is moved to a new
|
|
|
|
// location, delivering EventWidgetMove to all watchers.
|
|
|
|
func (ww *WidgetWatchers) PostEventWidgetMove(w Widget) {
|
|
|
|
ev := &EventWidgetMove{}
|
|
|
|
ev.SetWidget(w)
|
|
|
|
ev.SetEventNow()
|
|
|
|
ww.PostEvent(ev)
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX: WidgetExposed, Hidden?
|
|
|
|
// XXX: WidgetExposed, Hidden?
|
|
|
|
|
|
|
|
// EventWidgetContent is fired whenever a widget's content changes.
|
|
|
|
type EventWidgetContent struct {
|
|
|
|
widgetEvent
|
|
|
|
}
|
|
|
|
|
|
|
|
// EventWidgetResize is fired whenever a widget is resized.
|
|
|
|
type EventWidgetResize struct {
|
|
|
|
widgetEvent
|
|
|
|
}
|
|
|
|
|
2024-08-23 03:13:53 +08:00
|
|
|
// EventWidgetMove is fired whenever a widget changes location.
|
2015-10-31 00:33:45 +08:00
|
|
|
type EventWidgetMove struct {
|
|
|
|
widgetEvent
|
|
|
|
}
|