continue to add more building blocks
This commit is contained in:
		
							
								
								
									
										135
									
								
								cmd/manage/interface.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								cmd/manage/interface.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,135 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/gdamore/tcell" | ||||
| ) | ||||
|  | ||||
| type Drawable interface { | ||||
| 	Draw(tcell.Screen) | ||||
| 	SetSize(x, y, h, w int) | ||||
| } | ||||
|  | ||||
| type offsets struct { | ||||
| 	top    int | ||||
| 	bottom int | ||||
| 	left   int | ||||
| 	right  int | ||||
| } | ||||
|  | ||||
| type contents []struct { | ||||
| 	offsets   offsets | ||||
| 	container Drawable | ||||
| } | ||||
|  | ||||
| type box struct { | ||||
| 	x, y      int | ||||
| 	h, w      int | ||||
| 	title     Drawable | ||||
| 	menuItems Drawable | ||||
| 	contents  []struct { | ||||
| 		offsets   offsets | ||||
| 		container Drawable | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func NewBox(title string, menuItems []string, contents contents) *box { | ||||
| 	return &box{ | ||||
| 		title:     NewPaddedText(title), | ||||
| 		menuItems: NewPaddedText(strings.Join(menuItems, " ")), | ||||
| 		contents:  contents, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *box) SetSize(x, y, h, w int) { | ||||
| 	b.x, b.y, b.h, b.w = x, y, h, w | ||||
| 	b.title.SetSize(b.x+2, b.y, 0, 0) | ||||
| 	b.menuItems.SetSize(b.x+2, b.y+b.h-1, 0, 0) | ||||
| 	for c := range b.contents { | ||||
| 		x := b.x + b.contents[c].offsets.left | ||||
| 		y := b.y + b.contents[c].offsets.top | ||||
| 		h := b.h - b.contents[c].offsets.bottom | ||||
| 		w := b.w - b.contents[c].offsets.right | ||||
| 		b.contents[c].container.SetSize(x, y, h, w) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *box) Draw(s tcell.Screen) { | ||||
| 	for m := 1; m < b.w-1; m++ { | ||||
| 		s.SetContent(m, b.y, tcell.RuneHLine, nil, tcell.StyleDefault) | ||||
| 		s.SetContent(m, b.h-1, tcell.RuneHLine, nil, tcell.StyleDefault) | ||||
| 	} | ||||
| 	for m := 1; m < b.h-1; m++ { | ||||
| 		s.SetContent(b.x, m, tcell.RuneVLine, nil, tcell.StyleDefault) | ||||
| 		s.SetContent(b.w-1, m, tcell.RuneVLine, nil, tcell.StyleDefault) | ||||
| 	} | ||||
| 	s.SetContent(b.x, b.y, tcell.RuneULCorner, nil, tcell.StyleDefault) | ||||
| 	s.SetContent(b.w-1, b.y, tcell.RuneURCorner, nil, tcell.StyleDefault) | ||||
| 	s.SetContent(b.x, b.h-1, tcell.RuneLLCorner, nil, tcell.StyleDefault) | ||||
| 	s.SetContent(b.w-1, b.h-1, tcell.RuneLRCorner, nil, tcell.StyleDefault) | ||||
|  | ||||
| 	if b.title != nil { | ||||
| 		b.title.Draw(s) | ||||
| 	} | ||||
| 	if b.menuItems != nil { | ||||
| 		b.menuItems.Draw(s) | ||||
| 	} | ||||
| 	for c := range b.contents { | ||||
| 		b.contents[c].container.Draw(s) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type list struct { | ||||
| 	x, y      int | ||||
| 	h, w      int | ||||
| 	selected  int | ||||
| 	listItems []string | ||||
| } | ||||
|  | ||||
| func NewList(listItems []string, initialSelected int) *list { | ||||
| 	return &list{ | ||||
| 		listItems: listItems, | ||||
| 		selected:  initialSelected, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (l *list) SetSize(x, y, h, w int) { | ||||
| 	l.x, l.y, l.h, l.w = x, y, h, w | ||||
| } | ||||
|  | ||||
| func (l *list) Draw(s tcell.Screen) { | ||||
| 	for i := range l.listItems { | ||||
| 		for j, r := range l.listItems[i] { | ||||
| 			s.SetContent(l.x+j, l.y+i, r, nil, tcell.StyleDefault) | ||||
| 		} | ||||
| 		if i == l.selected { | ||||
| 			s.SetContent(l.x+len(l.listItems[i])+1, l.y+i, '<', nil, tcell.StyleDefault) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type paddedText struct { | ||||
| 	x, y int | ||||
| 	h, w int | ||||
| 	text string | ||||
| } | ||||
|  | ||||
| func NewPaddedText(text string) *paddedText { | ||||
| 	return &paddedText{text: text} | ||||
| } | ||||
|  | ||||
| func (p *paddedText) SetSize(x, y, _, _ int) { | ||||
| 	p.x, p.y, p.h, p.w = x, y, 1, len(p.text)+2 | ||||
| } | ||||
|  | ||||
| func (p *paddedText) Draw(s tcell.Screen) { | ||||
| 	t := p.x | ||||
| 	s.SetContent(t, p.y, ' ', nil, tcell.StyleDefault) | ||||
| 	t++ | ||||
| 	for _, r := range p.text { | ||||
| 		s.SetContent(t, p.y, r, nil, tcell.StyleDefault) | ||||
| 		t++ | ||||
| 	} | ||||
| 	s.SetContent(t, p.y, ' ', nil, tcell.StyleDefault) | ||||
| } | ||||
| @@ -15,7 +15,6 @@ func main() { | ||||
| 	if err != nil { | ||||
| 		log.Fatalln(err) | ||||
| 	} | ||||
|  | ||||
| 	screen, err := tcell.NewScreen() | ||||
| 	if err != nil { | ||||
| 		log.Fatalln(err) | ||||
| @@ -24,31 +23,31 @@ func main() { | ||||
| 	if err != nil { | ||||
| 		log.Fatalln(err) | ||||
| 	} | ||||
|  | ||||
| 	l := NewList([]string{"foo", "bar", "baz"}, 0) | ||||
| 	b := NewBox( | ||||
| 		"library", | ||||
| 		[]string{"(e)dit", "(q)uit"}, | ||||
| 		contents{{ | ||||
| 			offsets:   offsets{top: 1, left: 2, bottom: -2, right: -2}, | ||||
| 			container: l, | ||||
| 		}}, | ||||
| 	) | ||||
|  | ||||
| 	// init | ||||
| 	screen.Clear() | ||||
| 	w, h := screen.Size() | ||||
| 	l := &list{ | ||||
| 		w: w - 4, h: h - 4, | ||||
| 		x: 2, y: 1, | ||||
| 		selected:  2, | ||||
| 		listItems: []string{"foo", "bar", "baz"}, | ||||
| 	} | ||||
| 	b := &box{ | ||||
| 		w: w, h: h, | ||||
| 		x: 0, y: 0, | ||||
| 		title:     "library", | ||||
| 		menuItems: []string{"(e)dit", "(q)uit"}, | ||||
| 		contents:  l, | ||||
| 	} | ||||
| 	b.SetSize(0, 0, h, w) | ||||
| 	b.Draw(screen) | ||||
| 	screen.Sync() | ||||
|  | ||||
| 	// UI loop | ||||
| 	for { | ||||
| 		e := screen.PollEvent() | ||||
| 		switch v := e.(type) { | ||||
| 		case *tcell.EventError: | ||||
| 			screen.Clear() | ||||
| 			screen.Beep() | ||||
| 			b.draw(screen) | ||||
| 			screen.SetContent(1, 1, 'E', nil, tcell.StyleDefault) | ||||
| 		case *tcell.EventKey: | ||||
| 			screen.Clear() | ||||
| 		case *tcell.EventKey: // input handling | ||||
| 			if v.Key() == tcell.KeyUp && l.selected > 0 { | ||||
| 				l.selected = l.selected - 1 | ||||
| 			} | ||||
| @@ -60,89 +59,18 @@ func main() { | ||||
| 				fmt.Printf("Thank you for playing Wing Commander!\n\n") | ||||
| 				return | ||||
| 			} | ||||
| 			b.draw(screen) | ||||
| 		case *tcell.EventResize: | ||||
| 			screen.Clear() | ||||
| 			b.Draw(screen) | ||||
| 		case *tcell.EventResize: // screen redraw | ||||
| 			w, h := screen.Size() | ||||
| 			b.w, b.h = w, h | ||||
| 			b.draw(screen) | ||||
| 			b.SetSize(0, 0, h, w) | ||||
| 			screen.Clear() | ||||
| 			b.Draw(screen) | ||||
| 		case *tcell.EventInterrupt: | ||||
| 		case *tcell.EventMouse: | ||||
| 		case *tcell.EventTime: | ||||
| 		default: | ||||
| 		} | ||||
| 		screen.Sync() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type drawable interface { | ||||
| 	draw(tcell.Screen) | ||||
| } | ||||
|  | ||||
| type box struct { | ||||
| 	x, y      int | ||||
| 	h, w      int | ||||
| 	title     string | ||||
| 	menuItems []string | ||||
| 	contents  drawable | ||||
| } | ||||
|  | ||||
| func (b *box) draw(s tcell.Screen) { | ||||
| 	for m := 1; m < b.w-1; m++ { | ||||
| 		s.SetContent(m, b.y, tcell.RuneHLine, nil, tcell.StyleDefault) | ||||
| 		s.SetContent(m, b.h-1, tcell.RuneHLine, nil, tcell.StyleDefault) | ||||
| 	} | ||||
| 	for m := 1; m < b.h-1; m++ { | ||||
| 		s.SetContent(b.x, m, tcell.RuneVLine, nil, tcell.StyleDefault) | ||||
| 		s.SetContent(b.w-1, m, tcell.RuneVLine, nil, tcell.StyleDefault) | ||||
| 	} | ||||
| 	s.SetContent(b.x, b.y, tcell.RuneULCorner, nil, tcell.StyleDefault) | ||||
| 	s.SetContent(b.w-1, b.y, tcell.RuneURCorner, nil, tcell.StyleDefault) | ||||
| 	s.SetContent(b.x, b.h-1, tcell.RuneLLCorner, nil, tcell.StyleDefault) | ||||
| 	s.SetContent(b.w-1, b.h-1, tcell.RuneLRCorner, nil, tcell.StyleDefault) | ||||
|  | ||||
| 	if b.title != "" { | ||||
| 		t := b.x + 2 | ||||
| 		s.SetContent(t, b.y, ' ', nil, tcell.StyleDefault) | ||||
| 		t++ | ||||
| 		for _, r := range b.title { | ||||
| 			s.SetContent(t, b.y, r, nil, tcell.StyleDefault) | ||||
| 			t++ | ||||
| 		} | ||||
| 		s.SetContent(t, b.y, ' ', nil, tcell.StyleDefault) | ||||
| 	} | ||||
| 	if len(b.menuItems) != 0 { | ||||
| 		t := b.x + 2 | ||||
| 		s.SetContent(t, b.h-1, ' ', nil, tcell.StyleDefault) | ||||
| 		t++ | ||||
| 		for i := range b.menuItems { | ||||
| 			for _, r := range b.menuItems[i] { | ||||
| 				s.SetContent(t, b.h-1, r, nil, tcell.StyleDefault) | ||||
| 				t++ | ||||
| 			} | ||||
| 			s.SetContent(t, b.h-1, ' ', nil, tcell.StyleDefault) | ||||
| 			t++ | ||||
| 		} | ||||
| 	} | ||||
| 	if b.contents != nil { | ||||
| 		b.contents.draw(s) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type list struct { | ||||
| 	x, y      int | ||||
| 	h, w      int | ||||
| 	selected  int | ||||
| 	listItems []string | ||||
| } | ||||
|  | ||||
| func (l *list) draw(s tcell.Screen) { | ||||
| 	for i := range l.listItems { | ||||
| 		for j, r := range l.listItems[i] { | ||||
| 			s.SetContent(l.x+j, l.y+i, r, nil, tcell.StyleDefault) | ||||
| 		} | ||||
| 		if i == l.selected { | ||||
| 			s.SetContent(l.x+len(l.listItems[i])+1, l.y+i, '<', nil, tcell.StyleDefault) | ||||
| 		} | ||||
| 		screen.Sync() // repaint | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -5,4 +5,4 @@ services: | ||||
|     ports: | ||||
|       - 3306:3306 | ||||
|     environment: | ||||
|       - MYSQL_ROOT_PASSWORD=KigYBNCT9IU5XyB3ehzMLFWyI | ||||
|       - MYSQL_ROOT_PASSWORD=KigYBNCT9IU5XyB3ehzMLFWyI # for dev testing only, obviously. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user