ref: 7bc27341ac44be2f87444d96b7e10569e8333af1
parent: d5a4d882a221f32721bc9608d14bfbad0583c50b
author: Philip Silva <philip.silva@protonmail.com>
date: Sat Dec 4 21:36:29 EST 2021
Really basic text selection - works only non overloaded pages
--- a/browser/browser.go
+++ b/browser/browser.go
@@ -26,6 +26,7 @@
"os"
"strconv"
"strings"
+ "unicode"
"github.com/mjl-/duit"
@@ -71,6 +72,10 @@
scroller *duitx.Scroll
display *draw.Display
+ selected int
+ dragRect draw.Rectangle
+ fromLabel *duitx.Label
+
colorCache = make(map[draw.Color]*draw.Image)
imageCache = make(map[string]*draw.Image)
)
@@ -423,9 +428,10 @@
return box, true
}
-func (el *Element) Rect() image.Rectangle {
+func (el *Element) Rect() (r image.Rectangle) {
if el == nil {
log.Errorf("Rect: nil element")
+ return
}
return el.rect.Add(el.orig)
}
@@ -665,7 +671,7 @@
}
func (el *Element) Mouse(dui *duit.DUI, self *duit.Kid, m draw.Mouse, origM draw.Mouse, orig image.Point) (r duit.Result) {
- if m.Buttons == 2 {
+ if m.Buttons == 3 {
if el == nil {
log.Infof("inspect nil element")
} else {
@@ -683,8 +689,17 @@
maxX := self.R.Dx()
maxY := self.R.Dy()
border := 5 > x || x > (maxX-5) || 5 > y || y > (maxY-5)
- hover := m.In(el.rect)
- if hover && el.m.Buttons&duit.Button1 == duit.Button1 && m.Buttons&duit.Button1 == 0 && el.click() {
+
+ if l, ok := el.UI.(*Label); ok && l != nil {
+ fromLabel = l.Label
+ }
+ if el.n.Data() == "body" {
+ if el.mouseSelect(dui, self, m, origM, orig) {
+ return duit.Result{
+ Consumed: true,
+ }
+ }
+ } else if el.m.Buttons&1 == 1 && m.Buttons&1 == 0 && el.click() {
return duit.Result{
Consumed: true,
}
@@ -706,6 +721,98 @@
el.m = m
}
return el.UI.Mouse(dui, self, m, origM, orig)
+}
+
+func (el *Element) mouseSelect(dui *duit.DUI, self *duit.Kid, m draw.Mouse, origM draw.Mouse, orig image.Point) (consumed bool) {
+ mouseDrag := m != origM
+ changed := false
+ if mouseDrag {
+ from := origM.Point.Add(orig)
+ to := m.Point.Add(orig)
+ r := draw.Rectangle{
+ draw.Point{from.X, from.Y},
+ draw.Point{to.X, to.Y},
+ }.Canon()
+ var delta image.Point
+ if fromLabel != nil {
+ // make sure the same coordinates are used
+ // (TODO: should be consistent in the first place)
+ delta = r.Min.Sub(fromLabel.Rect().Min)
+ }
+ r = r.Sub(delta)
+ if !rectsSimilar(dragRect, r) {
+ TraverseTree(el, func(ui duit.UI) {
+ l, ok := ui.(*duitx.Label)
+ if !ok {
+ return
+ }
+ sel := l.Rect().Overlaps(r)
+ if sel == l.Selected {
+ return
+ }
+ l.Selected = sel
+ changed = true
+ if sel {
+ selected++
+ } else {
+ selected--
+ }
+ })
+ dragRect = r
+ }
+ if m.Buttons&2 == 2 && el.m.Buttons&2 == 0 {
+ var s string
+ var last *duitx.Label
+ TraverseTree(el, func(ui duit.UI) {
+ l, ok := ui.(*duitx.Label)
+ if ok && l.Selected {
+ if last != nil && l.Rect().Min.Y > last.Rect().Min.Y {
+ s += "\n"
+ }
+ s += l.Text
+ last = l
+ return
+ }
+ })
+ s = strings.TrimSpace(s)
+ s = strings.TrimFunc(s, func(r rune) bool {
+ return !unicode.IsGraphic(r)
+ })
+ s = strings.Map(func(r rune) rune {
+ if unicode.IsSpace(r) && r != '\n' {
+ return ' '
+ }
+ return r
+ }, s)
+ dui.WriteSnarf([]byte(s))
+ }
+ } else if selected > 0 && m.Buttons == 1 {
+ TraverseTree(browser.Website.UI, func(ui duit.UI) {
+ l, ok := ui.(*duitx.Label)
+ if ok && l.Selected {
+ selected--
+ changed = true
+ l.Selected = false
+ }
+ })
+ selected = 0
+ }
+ return changed
+}
+
+func rectsSimilar(r, rr draw.Rectangle) bool {
+ deltas := []float64{
+ math.Abs(float64(r.Min.X - rr.Min.X)),
+ math.Abs(float64(r.Min.Y - rr.Min.Y)),
+ math.Abs(float64(r.Max.X - rr.Max.X)),
+ math.Abs(float64(r.Max.Y - rr.Max.Y)),
+ }
+ for _, d := range deltas {
+ if d > float64(dui.Scale(10)) {
+ return false
+ }
+ }
+ return true
}
func (el *Element) FirstFocus(dui *duit.DUI, self *duit.Kid) *image.Point {
--- a/browser/duitx/label.go
+++ b/browser/duitx/label.go
@@ -21,6 +21,7 @@
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import (
+ "fmt"
"image"
"math"
@@ -28,6 +29,10 @@
"github.com/mjl-/duit"
)
+var (
+ selectedBg *draw.Image
+)
+
// Label draws multiline text in a single font.:
//
// Keys:
@@ -34,11 +39,13 @@
// cmd-c, copy text
// \n, like button1 click, calls the Click function
type Label struct {
- Text string // Text to draw, wrapped at glyph boundary.
- Font *draw.Font `json:"-"` // For drawing text.
- Click func() (e duit.Event) `json:"-"` // Called on button1 click.
+ Text string // Text to draw, wrapped at glyph boundary.
+ Font *draw.Font `json:"-"` // For drawing text.
+ Click func() (e duit.Event) `json:"-"` // Called on button1 click.
+ Selected bool
lines []string
+ orig image.Point
size image.Point
m draw.Mouse
}
@@ -90,12 +97,25 @@
func (ui *Label) Draw(dui *duit.DUI, self *duit.Kid, img *draw.Image, orig image.Point, m draw.Mouse, force bool) {
debugDraw(dui, self)
+ if selectedBg == nil {
+ var err error
+ selectedBg, err = dui.Display.AllocImage(image.Rect(0, 0, 10, 10), draw.ARGB32, true, 0x9acd32ff)
+ if err != nil {
+ panic(fmt.Errorf("%v", err))
+ }
+ }
+
p := orig
font := ui.font(dui)
for _, line := range ui.lines {
- img.String(p, dui.Regular.Normal.Text, image.ZP, font, line)
+ if ui.Selected {
+ img.StringBg(p, dui.Regular.Normal.Text, image.ZP, font, line, selectedBg, image.ZP)
+ } else {
+ img.String(p, dui.Regular.Normal.Text, image.ZP, font, line)
+ }
p.Y += ui.lineHeight(font)
}
+ ui.orig = orig
}
func (ui *Label) Mouse(dui *duit.DUI, self *duit.Kid, m draw.Mouse, origM draw.Mouse, orig image.Point) (r duit.Result) {
@@ -148,4 +168,11 @@
self.Draw = duit.Dirty
}
r.Consumed = e.Consumed || r.Consumed
+}
+
+func (ui *Label) Rect() draw.Rectangle {
+ return draw.Rectangle{
+ ui.orig,
+ ui.orig.Add(ui.size),
+ }
}