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),
+ }
}
--
⑨