1// Copyright 2015 The Tcell Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use file except in compliance with the License.
5// You may obtain a copy of the license at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package views
16
17import (
18	"github.com/gdamore/tcell/v2"
19)
20
21// Widget is the base object that all onscreen elements implement.
22type Widget interface {
23	// Draw is called to inform the widget to draw itself.  A containing
24	// Widget will generally call this during the application draw loop.
25	Draw()
26
27	// Resize is called in response to a resize of the View.  Unlike with
28	// other events, Resize performed by parents first, and they must
29	// then call their children.  This is because the children need to
30	// see the updated sizes from the parents before they are called.
31	// In general this is done *after* the views have updated.
32	Resize()
33
34	// HandleEvent is called to ask the widget to handle any events.
35	// If the widget has consumed the event, it should return true.
36	// Generally, events are handled by the lower layers first, that
37	// is for example, a button may have a chance to handle an event
38	// before the enclosing window or panel.
39	//
40	// Its expected that Resize events are consumed by the outermost
41	// Widget, and the turned into a Resize() call.
42	HandleEvent(ev tcell.Event) bool
43
44	// SetView is used by callers to set the visual context of the
45	// Widget.  The Widget should use the View as a context for
46	// drawing.
47	SetView(view View)
48
49	// Size returns the size of the widget (content size) as width, height
50	// in columns.  Layout managers should attempt to ensure that at least
51	// this much space is made available to the View for this Widget.  Extra
52	// space may be allocated on as an needed basis.
53	Size() (int, int)
54
55	// Watch is used to register an interest in this widget's events.
56	// The handler will receive EventWidget events for this widget.
57	// The order of event delivery when there are multiple watchers is
58	// not specified, and may change from one event to the next.
59	Watch(handler tcell.EventHandler)
60
61	// Unwatch is used to urnegister an interest in this widget's events.
62	Unwatch(handler tcell.EventHandler)
63}
64
65// EventWidget is an event delivered by a specific widget.
66type EventWidget interface {
67	Widget() Widget
68	tcell.Event
69}
70
71type widgetEvent struct {
72	widget Widget
73	tcell.EventTime
74}
75
76func (wev *widgetEvent) Widget() Widget {
77	return wev.widget
78}
79
80func (wev *widgetEvent) SetWidget(widget Widget) {
81	wev.widget = widget
82}
83
84// WidgetWatchers provides a common implementation for base Widget
85// Watch and Unwatch interfaces, suitable for embedding in more concrete
86// widget implementations.
87type WidgetWatchers struct {
88	watchers map[tcell.EventHandler]struct{}
89}
90
91// Watch monitors this WidgetWatcher, causing the handler to be fired
92// with EventWidget as they are occur on the watched Widget.
93func (ww *WidgetWatchers) Watch(handler tcell.EventHandler) {
94	if ww.watchers == nil {
95		ww.watchers = make(map[tcell.EventHandler]struct{})
96	}
97	ww.watchers[handler] = struct{}{}
98}
99
100// Unwatch stops monitoring this WidgetWatcher. The handler will no longer
101// be fired for Widget events.
102func (ww *WidgetWatchers) Unwatch(handler tcell.EventHandler) {
103	if ww.watchers != nil {
104		delete(ww.watchers, handler)
105	}
106}
107
108// PostEvent delivers the EventWidget to all registered watchers.  It is
109// to be called by the Widget implementation.
110func (ww *WidgetWatchers) PostEvent(wev EventWidget) {
111	for watcher := range ww.watchers {
112		// Deliver events to all listeners, ignoring return value.
113		watcher.HandleEvent(wev)
114	}
115}
116
117// PostEventWidgetContent is called by the Widget when its content is
118// changed, delivering EventWidgetContent to all watchers.
119func (ww *WidgetWatchers) PostEventWidgetContent(w Widget) {
120	ev := &EventWidgetContent{}
121	ev.SetWidget(w)
122	ev.SetEventNow()
123	ww.PostEvent(ev)
124}
125
126// PostEventWidgetResize is called by the Widget when the underlying View
127// has resized, delivering EventWidgetResize to all watchers.
128func (ww *WidgetWatchers) PostEventWidgetResize(w Widget) {
129	ev := &EventWidgetResize{}
130	ev.SetWidget(w)
131	ev.SetEventNow()
132	ww.PostEvent(ev)
133}
134
135// PostEventWidgetMove is called by the Widget when it is moved to a new
136// location, delivering EventWidgetMove to all watchers.
137func (ww *WidgetWatchers) PostEventWidgetMove(w Widget) {
138	ev := &EventWidgetMove{}
139	ev.SetWidget(w)
140	ev.SetEventNow()
141	ww.PostEvent(ev)
142}
143
144// XXX: WidgetExposed, Hidden?
145// XXX: WidgetExposed, Hidden?
146
147// EventWidgetContent is fired whenever a widget's content changes.
148type EventWidgetContent struct {
149	widgetEvent
150}
151
152// EventWidgetResize is fired whenever a widget is resized.
153type EventWidgetResize struct {
154	widgetEvent
155}
156
157// EventWidgetMove is fired whenver a widget changes location.
158type EventWidgetMove struct {
159	widgetEvent
160}
161