File: shadow.go

package info (click to toggle)
golang-github-gcla-gowid 1.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 1,456 kB
  • sloc: makefile: 4
file content (147 lines) | stat: -rw-r--r-- 4,507 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright 2019-2022 Graham Clark. All rights reserved.  Use of this source
// code is governed by the MIT license that can be found in the LICENSE
// file.

// Package shadow adds a drop shadow effect to a widget.
package shadow

import (
	"fmt"

	"github.com/gcla/gowid"
	tcell "github.com/gdamore/tcell/v2"
)

//======================================================================

type IOffset interface {
	Offset() int
}

type IWidget interface {
	gowid.ICompositeWidget
	IOffset
}

// Widget will render a drop shadow underneath and to the right of the inner widget,
// providing a simple 3D effect.
//
// Offset is the number of lines to extend the drop shadow down - it is
// extended right by 2*Offset because terminal cells aren't square.
//
type Widget struct {
	gowid.IWidget
	offset int // Means y offset, x is 2*y because cells are not squares -
	// we just guess at a reasonable look for a reasonable
	// aspect ratio
	*gowid.Callbacks
	gowid.SubWidgetCallbacks
}

func New(inner gowid.IWidget, offset int) *Widget {
	res := &Widget{
		IWidget: inner,
		offset:  offset,
	}
	res.SubWidgetCallbacks = gowid.SubWidgetCallbacks{CB: &res.Callbacks}
	var _ gowid.ICompositeWidget = res
	var _ IWidget = res
	return res
}

func (w *Widget) String() string {
	return fmt.Sprintf("shadow[%v]", w.SubWidget())
}

func (w *Widget) SubWidget() gowid.IWidget {
	return w.IWidget
}

func (w *Widget) SetSubWidget(wi gowid.IWidget, app gowid.IApp) {
	w.IWidget = wi
	gowid.RunWidgetCallbacks(w, gowid.SubWidgetCB{}, app, w)
}

func (w *Widget) Offset() int {
	return w.offset
}

func (w *Widget) SetOffset(x int, app gowid.IApp) {
	w.offset = x
}

func (w *Widget) UserInput(ev interface{}, size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) bool {
	return UserInput(w, ev, size, focus, app)
}

func (w *Widget) Render(size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) gowid.ICanvas {
	return Render(w, size, focus, app)
}

func (w *Widget) SubWidgetSize(size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) gowid.IRenderSize {
	return SubWidgetSize(w, size, focus, app)
}

func (w *Widget) RenderSize(size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) gowid.IRenderBox {
	return RenderSize(w, size, focus, app)
}

//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

func UserInput(w gowid.ICompositeWidget, ev interface{}, size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) bool {
	subSize := w.SubWidgetSize(size, focus, app)

	if evm, ok := ev.(*tcell.EventMouse); ok {
		ss := w.SubWidget().RenderSize(subSize, focus, app)
		mx, my := evm.Position()
		if my < ss.BoxRows() && my >= 0 && mx < ss.BoxColumns() && mx >= 0 {
			return gowid.UserInputIfSelectable(w.SubWidget(), ev, subSize, focus, app)
		}
	} else {
		return gowid.UserInputIfSelectable(w.SubWidget(), ev, subSize, focus, app)
	}
	return false
}

func Render(w IWidget, size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) gowid.ICanvas {
	newSize := w.SubWidgetSize(size, focus, app)
	innerCanvas := w.SubWidget().Render(newSize, focus, app)

	shadowCanvas := gowid.NewCanvasOfSizeExt(innerCanvas.BoxColumns(), innerCanvas.BoxRows(),
		gowid.MakeCell(' ', gowid.MakeTCellColorExt(tcell.ColorDefault), gowid.MakeTCellColorExt(tcell.ColorBlack), gowid.StyleNone))

	shadowCanvas.ExtendLeft(gowid.EmptyLine(w.Offset() * 2))

	res := gowid.NewCanvasOfSize(shadowCanvas.BoxColumns(), w.Offset())
	res.AppendBelow(shadowCanvas, false, false)
	res.MergeUnder(innerCanvas, 0, 0, false)

	return res
}

func SubWidgetSize(w IOffset, size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) gowid.IRenderSize {
	var newSize gowid.IRenderSize
	switch sz := size.(type) {
	case gowid.IRenderFixed:
		newSize = gowid.RenderFixed{}
	case gowid.IRenderBox:
		newSize = gowid.RenderBox{C: sz.BoxColumns() - (w.Offset() * 2), R: sz.BoxRows() - w.Offset()}
	case gowid.IRenderFlowWith:
		newSize = gowid.RenderFlowWith{C: sz.FlowColumns() - 2}
	default:
		panic(gowid.WidgetSizeError{Widget: w, Size: size})
	}
	return newSize
}

func RenderSize(w IWidget, size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) gowid.IRenderBox {
	ss := w.SubWidgetSize(size, focus, app)
	sdim := w.SubWidget().RenderSize(ss, focus, app)
	return gowid.RenderBox{C: sdim.BoxColumns() + (w.Offset() * 2), R: sdim.BoxRows() + w.Offset()}
}

//======================================================================
// Local Variables:
// mode: Go
// fill-column: 110
// End: