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 15 package views 16 17 import ( 18 "sync" 19 20 "github.com/gdamore/tcell/v2" 21 ) 22 23 // Widget is the base object that all onscreen elements implement. 24 type Widget interface { 25 // Draw is called to inform the widget to draw itself. A containing 26 // Widget will generally call this during the application draw loop. 27 Draw() 28 29 // Resize is called in response to a resize of the View. Unlike with 30 // other events, Resize performed by parents first, and they must 31 // then call their children. This is because the children need to 32 // see the updated sizes from the parents before they are called. 33 // In general this is done *after* the views have updated. 34 Resize() 35 36 // HandleEvent is called to ask the widget to handle any events. 37 // If the widget has consumed the event, it should return true. 38 // Generally, events are handled by the lower layers first, that 39 // is for example, a button may have a chance to handle an event 40 // before the enclosing window or panel. 41 // 42 // Its expected that Resize events are consumed by the outermost 43 // Widget, and the turned into a Resize() call. 44 HandleEvent(ev tcell.Event) bool 45 46 // SetView is used by callers to set the visual context of the 47 // Widget. The Widget should use the View as a context for 48 // drawing. 49 SetView(view View) 50 51 // Size returns the size of the widget (content size) as width, height 52 // in columns. Layout managers should attempt to ensure that at least 53 // this much space is made available to the View for this Widget. Extra 54 // space may be allocated on as an needed basis. 55 Size() (int, int) 56 57 // Watch is used to register an interest in this widget's events. 58 // The handler will receive EventWidget events for this widget. 59 // The order of event delivery when there are multiple watchers is 60 // not specified, and may change from one event to the next. 61 Watch(handler tcell.EventHandler) 62 63 // Unwatch is used to urnegister an interest in this widget's events. 64 Unwatch(handler tcell.EventHandler) 65 } 66 67 // EventWidget is an event delivered by a specific widget. 68 type EventWidget interface { 69 Widget() Widget 70 tcell.Event 71 } 72 73 type widgetEvent struct { 74 widget Widget 75 tcell.EventTime 76 } 77 78 func (wev *widgetEvent) Widget() Widget { 79 return wev.widget 80 } 81 82 func (wev *widgetEvent) SetWidget(widget Widget) { 83 wev.widget = widget 84 } 85 86 // WidgetWatchers provides a common implementation for base Widget Watch and 87 // Unwatch interfaces, suitable for embedding in more concrete widget 88 // implementations. This implementation is thread-safe, meaning an embedder can 89 // expect safety when calling WidgetWatcher methods from separate goroutines. 90 // In general, the views package does not support thread safety. 91 type WidgetWatchers struct { 92 sync.Mutex 93 watchers map[tcell.EventHandler]struct{} 94 } 95 96 // Watch monitors this WidgetWatcher, causing the handler to be fired 97 // with EventWidget as they are occur on the watched Widget. 98 func (ww *WidgetWatchers) Watch(handler tcell.EventHandler) { 99 ww.Lock() 100 defer ww.Unlock() 101 if ww.watchers == nil { 102 ww.watchers = make(map[tcell.EventHandler]struct{}) 103 } 104 ww.watchers[handler] = struct{}{} 105 } 106 107 // Unwatch stops monitoring this WidgetWatcher. The handler will no longer 108 // be fired for Widget events. 109 func (ww *WidgetWatchers) Unwatch(handler tcell.EventHandler) { 110 ww.Lock() 111 defer ww.Unlock() 112 if ww.watchers != nil { 113 delete(ww.watchers, handler) 114 } 115 } 116 117 // PostEvent delivers the EventWidget to all registered watchers. It is 118 // to be called by the Widget implementation. 119 func (ww *WidgetWatchers) PostEvent(wev EventWidget) { 120 ww.Lock() 121 watcherCopy := make(map[tcell.EventHandler]struct{}, len(ww.watchers)) 122 for k := range ww.watchers { 123 watcherCopy[k] = struct{}{} 124 } 125 ww.Unlock() 126 for watcher := range watcherCopy { 127 // Deliver events to all listeners, ignoring return value. 128 watcher.HandleEvent(wev) 129 } 130 } 131 132 // PostEventWidgetContent is called by the Widget when its content is 133 // changed, delivering EventWidgetContent to all watchers. 134 func (ww *WidgetWatchers) PostEventWidgetContent(w Widget) { 135 ev := &EventWidgetContent{} 136 ev.SetWidget(w) 137 ev.SetEventNow() 138 ww.PostEvent(ev) 139 } 140 141 // PostEventWidgetResize is called by the Widget when the underlying View 142 // has resized, delivering EventWidgetResize to all watchers. 143 func (ww *WidgetWatchers) PostEventWidgetResize(w Widget) { 144 ev := &EventWidgetResize{} 145 ev.SetWidget(w) 146 ev.SetEventNow() 147 ww.PostEvent(ev) 148 } 149 150 // PostEventWidgetMove is called by the Widget when it is moved to a new 151 // location, delivering EventWidgetMove to all watchers. 152 func (ww *WidgetWatchers) PostEventWidgetMove(w Widget) { 153 ev := &EventWidgetMove{} 154 ev.SetWidget(w) 155 ev.SetEventNow() 156 ww.PostEvent(ev) 157 } 158 159 // XXX: WidgetExposed, Hidden? 160 // XXX: WidgetExposed, Hidden? 161 162 // EventWidgetContent is fired whenever a widget's content changes. 163 type EventWidgetContent struct { 164 widgetEvent 165 } 166 167 // EventWidgetResize is fired whenever a widget is resized. 168 type EventWidgetResize struct { 169 widgetEvent 170 } 171 172 // EventWidgetMove is fired whenver a widget changes location. 173 type EventWidgetMove struct { 174 widgetEvent 175 } 176