...

Source file src/code.rocketnine.space/tslocum/cview/windowmanager.go

Documentation: code.rocketnine.space/tslocum/cview

     1  package cview
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/gdamore/tcell/v2"
     7  )
     8  
     9  // WindowManager provides an area which windows may be added to.
    10  type WindowManager struct {
    11  	*Box
    12  
    13  	windows []*Window
    14  
    15  	sync.RWMutex
    16  }
    17  
    18  // NewWindowManager returns a new window manager.
    19  func NewWindowManager() *WindowManager {
    20  	return &WindowManager{
    21  		Box: NewBox(),
    22  	}
    23  }
    24  
    25  // Add adds a window to the manager.
    26  func (wm *WindowManager) Add(w ...*Window) {
    27  	wm.Lock()
    28  	defer wm.Unlock()
    29  
    30  	for _, window := range w {
    31  		window.SetBorder(true)
    32  	}
    33  
    34  	wm.windows = append(wm.windows, w...)
    35  }
    36  
    37  // Clear removes all windows from the manager.
    38  func (wm *WindowManager) Clear() {
    39  	wm.Lock()
    40  	defer wm.Unlock()
    41  
    42  	wm.windows = nil
    43  }
    44  
    45  // Focus is called when this primitive receives focus.
    46  func (wm *WindowManager) Focus(delegate func(p Primitive)) {
    47  	wm.Lock()
    48  	defer wm.Unlock()
    49  
    50  	if len(wm.windows) == 0 {
    51  		return
    52  	}
    53  
    54  	wm.windows[len(wm.windows)-1].Focus(delegate)
    55  }
    56  
    57  // HasFocus returns whether or not this primitive has focus.
    58  func (wm *WindowManager) HasFocus() bool {
    59  	wm.RLock()
    60  	defer wm.RUnlock()
    61  
    62  	for _, w := range wm.windows {
    63  		if w.HasFocus() {
    64  			return true
    65  		}
    66  	}
    67  
    68  	return false
    69  }
    70  
    71  // Draw draws this primitive onto the screen.
    72  func (wm *WindowManager) Draw(screen tcell.Screen) {
    73  	if !wm.GetVisible() {
    74  		return
    75  	}
    76  
    77  	wm.RLock()
    78  	defer wm.RUnlock()
    79  
    80  	wm.Box.Draw(screen)
    81  
    82  	x, y, width, height := wm.GetInnerRect()
    83  
    84  	var hasFullScreen bool
    85  	for _, w := range wm.windows {
    86  		if !w.fullscreen || !w.GetVisible() {
    87  			continue
    88  		}
    89  
    90  		hasFullScreen = true
    91  		w.SetRect(x-1, y, width+2, height+1)
    92  
    93  		w.Draw(screen)
    94  	}
    95  	if hasFullScreen {
    96  		return
    97  	}
    98  
    99  	for _, w := range wm.windows {
   100  		if !w.GetVisible() {
   101  			continue
   102  		}
   103  
   104  		// Reposition out of bounds windows
   105  		margin := 3
   106  		wx, wy, ww, wh := w.GetRect()
   107  		ox, oy := wx, wy
   108  		if wx > x+width-margin {
   109  			wx = x + width - margin
   110  		}
   111  		if wx+ww < x+margin {
   112  			wx = x - ww + margin
   113  		}
   114  		if wy > y+height-margin {
   115  			wy = y + height - margin
   116  		}
   117  		if wy < y {
   118  			wy = y // No top margin
   119  		}
   120  		if wx != ox || wy != oy {
   121  			w.SetRect(wx, wy, ww, wh)
   122  		}
   123  
   124  		w.Draw(screen)
   125  	}
   126  }
   127  
   128  // MouseHandler returns the mouse handler for this primitive.
   129  func (wm *WindowManager) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
   130  	return wm.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
   131  		if !wm.InRect(event.Position()) {
   132  			return false, nil
   133  		}
   134  
   135  		if action == MouseMove {
   136  			mouseX, mouseY := event.Position()
   137  
   138  			for _, w := range wm.windows {
   139  				if w.dragWX != -1 || w.dragWY != -1 {
   140  					offsetX := w.x - mouseX
   141  					offsetY := w.y - mouseY
   142  
   143  					w.x -= offsetX + w.dragWX
   144  					w.y -= offsetY + w.dragWY
   145  
   146  					w.updateInnerRect()
   147  					consumed = true
   148  				}
   149  
   150  				if w.dragX != 0 {
   151  					if w.dragX == -1 {
   152  						offsetX := w.x - mouseX
   153  
   154  						if w.width+offsetX >= Styles.WindowMinWidth {
   155  							w.x -= offsetX
   156  							w.width += offsetX
   157  						}
   158  					} else {
   159  						offsetX := mouseX - (w.x + w.width) + 1
   160  
   161  						if w.width+offsetX >= Styles.WindowMinWidth {
   162  							w.width += offsetX
   163  						}
   164  					}
   165  
   166  					w.updateInnerRect()
   167  					consumed = true
   168  				}
   169  
   170  				if w.dragY != 0 {
   171  					if w.dragY == -1 {
   172  						offsetY := mouseY - (w.y + w.height) + 1
   173  
   174  						if w.height+offsetY >= Styles.WindowMinHeight {
   175  							w.height += offsetY
   176  						}
   177  					} else {
   178  						offsetY := w.y - mouseY
   179  
   180  						if w.height+offsetY >= Styles.WindowMinHeight {
   181  							w.y -= offsetY
   182  							w.height += offsetY
   183  						}
   184  					}
   185  
   186  					w.updateInnerRect()
   187  					consumed = true
   188  				}
   189  			}
   190  		} else if action == MouseLeftUp {
   191  			for _, w := range wm.windows {
   192  				w.dragX, w.dragY = 0, 0
   193  				w.dragWX, w.dragWY = -1, -1
   194  			}
   195  		}
   196  
   197  		// Focus window on mousedown
   198  		var (
   199  			focusWindow      *Window
   200  			focusWindowIndex int
   201  		)
   202  		for i := len(wm.windows) - 1; i >= 0; i-- {
   203  			if wm.windows[i].InRect(event.Position()) {
   204  				focusWindow = wm.windows[i]
   205  				focusWindowIndex = i
   206  				break
   207  			}
   208  		}
   209  		if focusWindow != nil {
   210  			if action == MouseLeftDown || action == MouseMiddleDown || action == MouseRightDown {
   211  				for _, w := range wm.windows {
   212  					if w != focusWindow {
   213  						w.Blur()
   214  					}
   215  				}
   216  
   217  				wm.windows = append(append(wm.windows[:focusWindowIndex], wm.windows[focusWindowIndex+1:]...), focusWindow)
   218  			}
   219  
   220  			return focusWindow.MouseHandler()(action, event, setFocus)
   221  		}
   222  
   223  		return consumed, nil
   224  	})
   225  }
   226  

View as plain text