Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

app: [js] fix tabbing #116

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/os_js.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,10 @@ func (w *window) keyEvent(e js.Value, ks key.State) {
State: ks,
}
w.w.Event(cmd)

if cmd.Name == key.NameTab {
e.Call("preventDefault")
}
}
}

Expand Down
23 changes: 15 additions & 8 deletions internal/stroke/stroke.go
Original file line number Diff line number Diff line change
Expand Up @@ -677,13 +677,14 @@ func SplitCubic(from, ctrl0, ctrl1, to f32.Point, quads []QuadSegment) []QuadSeg
if h := hull.Dy(); h > l {
l = h
}
approxCubeTo(&quads, 0, l*0.001, from, ctrl0, ctrl1, to)
maxDist := l * 0.001
approxCubeTo(&quads, 0, maxDist*maxDist, from, ctrl0, ctrl1, to)
return quads
}

// approxCubeTo approximates a cubic Bézier by a series of quadratic
// curves.
func approxCubeTo(quads *[]QuadSegment, splits int, maxDist float32, from, ctrl0, ctrl1, to f32.Point) int {
func approxCubeTo(quads *[]QuadSegment, splits int, maxDistSq float32, from, ctrl0, ctrl1, to f32.Point) int {
// The idea is from
// https://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
// where a quadratic approximates a cubic by eliminating its t³ term
Expand All @@ -708,7 +709,11 @@ func approxCubeTo(quads *[]QuadSegment, splits int, maxDist float32, from, ctrl0
// and use the midpoint between the two curves Q1 and Q2 as control point:
//
// C = (3ctrl0 - pen + 3ctrl1 - to)/4
c := ctrl0.Mul(3).Sub(from).Add(ctrl1.Mul(3)).Sub(to).Mul(1.0 / 4.0)
// using, q0 := 3ctrl0 - pen, q1 := 3ctrl1 - to
// C = (q0 + q1)/4
q0 := ctrl0.Mul(3).Sub(from)
q1 := ctrl1.Mul(3).Sub(to)
c := q0.Add(q1).Mul(1.0 / 4.0)
const maxSplits = 32
if splits >= maxSplits {
*quads = append(*quads, QuadSegment{From: from, Ctrl: c, To: to})
Expand All @@ -717,12 +722,14 @@ func approxCubeTo(quads *[]QuadSegment, splits int, maxDist float32, from, ctrl0
// The maximum distance between the cubic P and its approximation Q given t
// can be shown to be
//
// d = sqrt(3)/36*|to - 3ctrl1 + 3ctrl0 - pen|
// d = sqrt(3)/36 * |to - 3ctrl1 + 3ctrl0 - pen|
// reusing, q0 := 3ctrl0 - pen, q1 := 3ctrl1 - to
// d = sqrt(3)/36 * |-q1 + q0|
//
// To save a square root, compare d² with the squared tolerance.
v := to.Sub(ctrl1.Mul(3)).Add(ctrl0.Mul(3)).Sub(from)
v := q0.Sub(q1)
d2 := (v.X*v.X + v.Y*v.Y) * 3 / (36 * 36)
if d2 <= maxDist*maxDist {
if d2 <= maxDistSq {
*quads = append(*quads, QuadSegment{From: from, Ctrl: c, To: to})
return splits
}
Expand All @@ -735,7 +742,7 @@ func approxCubeTo(quads *[]QuadSegment, splits int, maxDist float32, from, ctrl0
c12 := c1.Add(c2.Sub(c1).Mul(t))
c0112 := c01.Add(c12.Sub(c01).Mul(t))
splits++
splits = approxCubeTo(quads, splits, maxDist, from, c0, c01, c0112)
splits = approxCubeTo(quads, splits, maxDist, c0112, c12, c2, to)
splits = approxCubeTo(quads, splits, maxDistSq, from, c0, c01, c0112)
splits = approxCubeTo(quads, splits, maxDistSq, c0112, c12, c2, to)
return splits
}
66 changes: 66 additions & 0 deletions internal/stroke/stroke_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: Unlicense OR MIT

package stroke

import (
"strconv"
"testing"

"gioui.org/internal/f32"
)

func BenchmarkSplitCubic(b *testing.B) {
type scenario struct {
segments int
from, ctrl0, ctrl1, to f32.Point
}

scenarios := []scenario{
{
segments: 4,
from: f32.Pt(0, 0),
ctrl0: f32.Pt(10, 10),
ctrl1: f32.Pt(10, 10),
to: f32.Pt(20, 0),
},
{
segments: 8,
from: f32.Pt(-145.90305, 703.21277),
ctrl0: f32.Pt(-940.20215, 606.05994),
ctrl1: f32.Pt(74.58341, 405.815),
to: f32.Pt(104.35474, -241.543),
},
{
segments: 16,
from: f32.Pt(770.35626, 639.77765),
ctrl0: f32.Pt(735.57135, 545.07837),
ctrl1: f32.Pt(286.7138, 853.7052),
to: f32.Pt(286.7138, 890.5413),
},
{
segments: 33,
from: f32.Pt(0, 0),
ctrl0: f32.Pt(0, 0),
ctrl1: f32.Pt(100, 100),
to: f32.Pt(100, 100),
},
}

for _, s := range scenarios {
s := s
b.Run(strconv.Itoa(s.segments), func(b *testing.B) {
from, ctrl0, ctrl1, to := s.from, s.ctrl0, s.ctrl1, s.to
quads := make([]QuadSegment, s.segments)
b.ResetTimer()
for i := 0; i < b.N; i++ {
quads = SplitCubic(from, ctrl0, ctrl1, to, quads[:0])
}
if len(quads) != s.segments {
// this is just for checking that we are benchmarking similar splits
// when splitting algorithm splits differently, then it's fine to adjust the
// parameters to give appropriate number of segments.
b.Fatalf("expected %d but got %d", s.segments, len(quads))
}
})
}
}
3 changes: 3 additions & 0 deletions widget/editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,9 @@ func (e *Editor) processKey(gtx layout.Context) {
switch ke := ke.(type) {
case key.FocusEvent:
e.focused = ke.Focus
if e.focused {
key.SoftKeyboardOp{Show: true}.Add(gtx.Ops)
}
Comment on lines +322 to +324
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you expand the commit message to say what issue this fixes? Is there a TODO number?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without that change TAB on browser will switch the focus to another input (which is expected!), but the focus is removed, in the next frame.

Currently, on JS, Gio focus/blur one input based on SoftKeyboardOp, even on desktop.

Previously, SoftKeyboardOp is requested using requestFocus, which happens only after clicking into the input. However, when the focus shifts to another input without clicking (which is the tabbing case): it don't triggers requestFocus, then it doesn't use SoftKeyboardOp.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This losing of focus after TAB sounds like a browser-only issue, correct? If so, can it be fixed in os_js.go so Editor doesn't need to know?

// Reset IME state.
e.ime.imeState = imeState{}
case key.Event:
Expand Down