...

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

View as plain text