...

Source file src/code.rocket9labs.com/tslocum/etk/window.go

Documentation: code.rocket9labs.com/tslocum/etk

     1  package etk
     2  
     3  import (
     4  	"image"
     5  	"image/color"
     6  	"sync"
     7  
     8  	"github.com/hajimehoshi/ebiten/v2"
     9  	"golang.org/x/image/font"
    10  )
    11  
    12  // Window displays child widgets in floating or maximized windows.
    13  type Window struct {
    14  	*Box
    15  	fontFace   font.Face
    16  	fontMutex  *sync.Mutex
    17  	frameSize  int
    18  	titleSize  int
    19  	titles     []*Text
    20  	floating   []bool
    21  	fullscreen []Widget
    22  	modified   bool
    23  }
    24  
    25  // NewWindow returns a new Window widget.
    26  func NewWindow() *Window {
    27  	return &Window{
    28  		Box:       NewBox(),
    29  		fontFace:  Style.TextFont,
    30  		fontMutex: Style.TextFontMutex,
    31  		frameSize: Scale(4),
    32  		titleSize: Scale(40),
    33  	}
    34  }
    35  
    36  // SetRect sets the position and size of the widget.
    37  func (w *Window) SetRect(r image.Rectangle) {
    38  	w.Lock()
    39  	defer w.Unlock()
    40  
    41  	w.rect = r
    42  	w.modified = true
    43  }
    44  
    45  // SetFont sets the font face of the window titles.
    46  func (w *Window) SetFont(face font.Face, mutex *sync.Mutex) {
    47  	w.Lock()
    48  	defer w.Unlock()
    49  
    50  	w.fontFace = face
    51  	w.fontMutex = mutex
    52  
    53  	for _, title := range w.titles {
    54  		title.SetFont(w.fontFace, w.fontMutex)
    55  	}
    56  }
    57  
    58  // SetFrameSize sets the size of the frame around each window.
    59  func (w *Window) SetFrameSize(size int) {
    60  	w.Lock()
    61  	defer w.Unlock()
    62  
    63  	w.frameSize = size
    64  	w.modified = true
    65  }
    66  
    67  // SetTitleSize sets the height of the title bars.
    68  func (w *Window) SetTitleSize(size int) {
    69  	w.Lock()
    70  	defer w.Unlock()
    71  
    72  	w.titleSize = size
    73  	w.modified = true
    74  }
    75  
    76  // SetFullscreen expands the specified widget to fill the netire screen, hiding
    77  // the title bar. When -1 is provided, the currently fullscreen widget is
    78  // restored to its a normal size.
    79  func (w *Window) SetFullscreen(index int) {
    80  	w.Lock()
    81  	defer w.Unlock()
    82  
    83  	if index == -1 {
    84  		w.fullscreen = w.fullscreen[:0]
    85  	} else if index >= 0 && index < len(w.children) {
    86  		w.fullscreen = append(w.fullscreen[:0], w.children[index])
    87  	}
    88  	w.modified = true
    89  }
    90  
    91  // Children returns the children of the widget.
    92  func (w *Window) Children() []Widget {
    93  	w.Lock()
    94  	defer w.Unlock()
    95  
    96  	if len(w.fullscreen) != 0 {
    97  		return w.fullscreen
    98  	}
    99  	return w.children
   100  }
   101  
   102  // Clear removes all children from the widget.
   103  func (w *Window) Clear() {
   104  	w.Lock()
   105  	defer w.Unlock()
   106  
   107  	w.children = w.children[:0]
   108  	w.titles = w.titles[:0]
   109  	w.floating = w.floating[:0]
   110  	w.fullscreen = w.fullscreen[:0]
   111  }
   112  
   113  // HandleKeyboard is called when a keyboard event occurs.
   114  func (w *Window) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
   115  	return true, nil
   116  }
   117  
   118  // HandleMouse is called when a mouse event occurs.
   119  func (w *Window) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
   120  	return true, nil
   121  }
   122  
   123  // Draw draws the widget on the screen.
   124  func (w *Window) Draw(screen *ebiten.Image) error {
   125  	if w.modified {
   126  		if len(w.fullscreen) != 0 {
   127  			w.fullscreen[0].SetRect(w.rect)
   128  		} else {
   129  			for i, wgt := range w.children {
   130  				r := wgt.Rect()
   131  				if r.Empty() || (!w.floating[i] && !r.Eq(w.rect)) {
   132  					r = w.rect
   133  				}
   134  				if r.Max.X >= w.rect.Max.X {
   135  					r = r.Sub(image.Point{r.Max.X - w.rect.Max.X, 0})
   136  				}
   137  				if r.Max.Y >= w.rect.Max.Y {
   138  					r = r.Sub(image.Point{0, r.Max.Y - w.rect.Max.Y})
   139  				}
   140  				if r.Min.X < w.rect.Min.X {
   141  					r.Min.X = w.rect.Min.X
   142  				}
   143  				if r.Min.Y < w.rect.Min.Y {
   144  					r.Min.Y = w.rect.Min.Y
   145  				}
   146  				wgt.SetRect(r)
   147  			}
   148  		}
   149  		w.modified = false
   150  	}
   151  	return nil
   152  }
   153  
   154  // AddChild adds a child to the window.
   155  func (w *Window) AddChild(wgt ...Widget) {
   156  	w.Lock()
   157  	defer w.Unlock()
   158  
   159  	for _, widget := range wgt {
   160  		t := NewText("")
   161  		t.SetFont(w.fontFace, w.fontMutex)
   162  
   163  		w.children = append(w.children, &windowWidget{NewBox(), t, widget, w})
   164  		w.titles = append(w.titles, t)
   165  		w.floating = append(w.floating, false)
   166  	}
   167  	w.modified = true
   168  }
   169  
   170  // AddChildWithTitle adds a child to the window with the specified window title.
   171  func (w *Window) AddChildWithTitle(wgt Widget, title string) int {
   172  	w.Lock()
   173  	defer w.Unlock()
   174  
   175  	t := NewText(title)
   176  	t.SetFont(w.fontFace, w.fontMutex)
   177  
   178  	w.children = append(w.children, &windowWidget{NewBox(), t, wgt, w})
   179  	w.titles = append(w.titles, t)
   180  	w.floating = append(w.floating, false)
   181  
   182  	w.modified = true
   183  	return len(w.children) - 1
   184  }
   185  
   186  type windowWidget struct {
   187  	*Box
   188  	title *Text
   189  	wgt   Widget
   190  	w     *Window
   191  }
   192  
   193  func (w *windowWidget) SetRect(r image.Rectangle) {
   194  	w.Lock()
   195  	defer w.Unlock()
   196  
   197  	w.rect = r
   198  	w.title.SetRect(image.Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+w.w.titleSize))
   199  	w.wgt.SetRect(image.Rect(r.Min.X, r.Min.Y+w.w.titleSize, r.Max.X, r.Max.Y))
   200  }
   201  
   202  func (w *windowWidget) Background() color.RGBA {
   203  	return color.RGBA{0, 0, 0, 255}
   204  }
   205  
   206  func (w *windowWidget) Draw(screen *ebiten.Image) error {
   207  	w.title.Draw(screen)
   208  
   209  	background := w.wgt.Background()
   210  	if background.A != 0 {
   211  		screen.SubImage(w.wgt.Rect()).(*ebiten.Image).Fill(background)
   212  	}
   213  	return w.wgt.Draw(screen)
   214  }
   215  

View as plain text